From: Andrew Reynolds Date: Wed, 14 Feb 2018 23:55:23 +0000 (-0600) Subject: Quantifiers subdirectories (#1608) X-Git-Tag: cvc5-1.0.0~5285 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=bf385ca69a958e0939524d8fbcf988c1fb45d131;p=cvc5.git Quantifiers subdirectories (#1608) --- diff --git a/src/Makefile.am b/src/Makefile.am index 4d5c85707..8e516a00d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -360,34 +360,32 @@ libcvc4_la_SOURCES = \ theory/idl/theory_idl.h \ theory/quantifiers/alpha_equivalence.cpp \ theory/quantifiers/alpha_equivalence.h \ - theory/quantifiers/ambqi_builder.cpp \ - theory/quantifiers/ambqi_builder.h \ theory/quantifiers/anti_skolem.cpp \ theory/quantifiers/anti_skolem.h \ - theory/quantifiers/bounded_integers.cpp \ - theory/quantifiers/bounded_integers.h \ theory/quantifiers/bv_inverter.cpp \ theory/quantifiers/bv_inverter.h \ theory/quantifiers/candidate_generator.cpp \ theory/quantifiers/candidate_generator.h \ - theory/quantifiers/ce_guided_conjecture.cpp \ - theory/quantifiers/ce_guided_conjecture.h \ - theory/quantifiers/ce_guided_instantiation.cpp \ - theory/quantifiers/ce_guided_instantiation.h \ - theory/quantifiers/ce_guided_single_inv.cpp \ - theory/quantifiers/ce_guided_single_inv.h \ - theory/quantifiers/ce_guided_pbe.cpp \ - theory/quantifiers/ce_guided_pbe.h \ - theory/quantifiers/ce_guided_single_inv_sol.cpp \ - theory/quantifiers/ce_guided_single_inv_sol.h \ - theory/quantifiers/ceg_instantiator.cpp \ - theory/quantifiers/ceg_instantiator.h \ - theory/quantifiers/ceg_t_instantiator.cpp \ - theory/quantifiers/ceg_t_instantiator.h \ + theory/quantifiers/cegqi/ceg_instantiator.cpp \ + theory/quantifiers/cegqi/ceg_instantiator.h \ + theory/quantifiers/cegqi/ceg_t_instantiator.cpp \ + theory/quantifiers/cegqi/ceg_t_instantiator.h \ + theory/quantifiers/cegqi/inst_strategy_cbqi.cpp \ + theory/quantifiers/cegqi/inst_strategy_cbqi.h \ theory/quantifiers/conjecture_generator.cpp \ theory/quantifiers/conjecture_generator.h \ theory/quantifiers/dynamic_rewrite.cpp \ theory/quantifiers/dynamic_rewrite.h \ + theory/quantifiers/ematching/ho_trigger.cpp \ + theory/quantifiers/ematching/ho_trigger.h \ + theory/quantifiers/ematching/inst_match_generator.cpp \ + theory/quantifiers/ematching/inst_match_generator.h \ + theory/quantifiers/ematching/inst_strategy_e_matching.cpp \ + theory/quantifiers/ematching/inst_strategy_e_matching.h \ + theory/quantifiers/ematching/instantiation_engine.cpp \ + theory/quantifiers/ematching/instantiation_engine.h \ + theory/quantifiers/ematching/trigger.cpp \ + theory/quantifiers/ematching/trigger.h \ theory/quantifiers/equality_query.cpp \ theory/quantifiers/equality_query.h \ theory/quantifiers/equality_infer.cpp \ @@ -396,42 +394,36 @@ libcvc4_la_SOURCES = \ theory/quantifiers/extended_rewrite.h \ theory/quantifiers/first_order_model.cpp \ theory/quantifiers/first_order_model.h \ - theory/quantifiers/full_model_check.cpp \ - theory/quantifiers/full_model_check.h \ + theory/quantifiers/fmf/ambqi_builder.cpp \ + theory/quantifiers/fmf/ambqi_builder.h \ + theory/quantifiers/fmf/bounded_integers.cpp \ + theory/quantifiers/fmf/bounded_integers.h \ + theory/quantifiers/fmf/full_model_check.cpp \ + theory/quantifiers/fmf/full_model_check.h \ + theory/quantifiers/fmf/model_builder.cpp \ + theory/quantifiers/fmf/model_builder.h \ + theory/quantifiers/fmf/model_engine.cpp \ + theory/quantifiers/fmf/model_engine.h \ theory/quantifiers/fun_def_engine.cpp \ theory/quantifiers/fun_def_engine.h \ theory/quantifiers/fun_def_process.cpp \ theory/quantifiers/fun_def_process.h \ theory/quantifiers/global_negate.cpp \ theory/quantifiers/global_negate.h \ - theory/quantifiers/ho_trigger.cpp \ - theory/quantifiers/ho_trigger.h \ theory/quantifiers/instantiate.cpp \ theory/quantifiers/instantiate.h \ theory/quantifiers/inst_match.cpp \ theory/quantifiers/inst_match.h \ theory/quantifiers/inst_match_trie.cpp \ theory/quantifiers/inst_match_trie.h \ - theory/quantifiers/inst_match_generator.cpp \ - theory/quantifiers/inst_match_generator.h \ theory/quantifiers/inst_propagator.cpp \ theory/quantifiers/inst_propagator.h \ - theory/quantifiers/inst_strategy_cbqi.cpp \ - theory/quantifiers/inst_strategy_cbqi.h \ - theory/quantifiers/inst_strategy_e_matching.cpp \ - theory/quantifiers/inst_strategy_e_matching.h \ theory/quantifiers/inst_strategy_enumerative.cpp \ theory/quantifiers/inst_strategy_enumerative.h \ - theory/quantifiers/instantiation_engine.cpp \ - theory/quantifiers/instantiation_engine.h \ theory/quantifiers/local_theory_ext.cpp \ theory/quantifiers/local_theory_ext.h \ theory/quantifiers/macros.cpp \ theory/quantifiers/macros.h \ - theory/quantifiers/model_builder.cpp \ - theory/quantifiers/model_builder.h \ - theory/quantifiers/model_engine.cpp \ - theory/quantifiers/model_engine.h \ theory/quantifiers/quant_conflict_find.cpp \ theory/quantifiers/quant_conflict_find.h \ theory/quantifiers/quant_epr.cpp \ @@ -456,26 +448,36 @@ libcvc4_la_SOURCES = \ theory/quantifiers/single_inv_partition.h \ theory/quantifiers/skolemize.cpp \ theory/quantifiers/skolemize.h \ - theory/quantifiers/sygus_explain.cpp \ - theory/quantifiers/sygus_explain.h \ - theory/quantifiers/sygus_invariance.cpp \ - theory/quantifiers/sygus_invariance.h \ - theory/quantifiers/sygus_grammar_cons.cpp \ - theory/quantifiers/sygus_grammar_cons.h \ - theory/quantifiers/sygus_grammar_norm.cpp \ - theory/quantifiers/sygus_grammar_norm.h \ - theory/quantifiers/sygus_grammar_red.cpp \ - theory/quantifiers/sygus_grammar_red.h \ - theory/quantifiers/sygus_process_conj.cpp \ - theory/quantifiers/sygus_process_conj.h \ + theory/quantifiers/sygus/ce_guided_conjecture.cpp \ + theory/quantifiers/sygus/ce_guided_conjecture.h \ + theory/quantifiers/sygus/ce_guided_instantiation.cpp \ + theory/quantifiers/sygus/ce_guided_instantiation.h \ + theory/quantifiers/sygus/ce_guided_single_inv.cpp \ + theory/quantifiers/sygus/ce_guided_single_inv.h \ + theory/quantifiers/sygus/sygus_pbe.cpp \ + theory/quantifiers/sygus/sygus_pbe.h \ + theory/quantifiers/sygus/ce_guided_single_inv_sol.cpp \ + theory/quantifiers/sygus/ce_guided_single_inv_sol.h \ + theory/quantifiers/sygus/sygus_explain.cpp \ + theory/quantifiers/sygus/sygus_explain.h \ + theory/quantifiers/sygus/sygus_invariance.cpp \ + theory/quantifiers/sygus/sygus_invariance.h \ + theory/quantifiers/sygus/sygus_grammar_cons.cpp \ + theory/quantifiers/sygus/sygus_grammar_cons.h \ + theory/quantifiers/sygus/sygus_grammar_norm.cpp \ + theory/quantifiers/sygus/sygus_grammar_norm.h \ + theory/quantifiers/sygus/sygus_grammar_red.cpp \ + theory/quantifiers/sygus/sygus_grammar_red.h \ + theory/quantifiers/sygus/sygus_process_conj.cpp \ + theory/quantifiers/sygus/sygus_process_conj.h \ + theory/quantifiers/sygus/term_database_sygus.cpp \ + theory/quantifiers/sygus/term_database_sygus.h \ theory/quantifiers/sygus_sampler.cpp \ theory/quantifiers/sygus_sampler.h \ theory/quantifiers/symmetry_breaking.cpp \ theory/quantifiers/symmetry_breaking.h \ theory/quantifiers/term_database.cpp \ theory/quantifiers/term_database.h \ - theory/quantifiers/term_database_sygus.cpp \ - theory/quantifiers/term_database_sygus.h \ theory/quantifiers/term_enumeration.cpp \ theory/quantifiers/term_enumeration.h \ theory/quantifiers/term_util.cpp \ @@ -483,8 +485,6 @@ libcvc4_la_SOURCES = \ theory/quantifiers/theory_quantifiers.cpp \ theory/quantifiers/theory_quantifiers.h \ theory/quantifiers/theory_quantifiers_type_rules.h \ - theory/quantifiers/trigger.cpp \ - theory/quantifiers/trigger.h \ theory/sep/theory_sep.cpp \ theory/sep/theory_sep.h \ theory/sep/theory_sep_rewriter.cpp \ diff --git a/src/smt/smt_engine.cpp b/src/smt/smt_engine.cpp index 7e2f6c38c..8176ba3e9 100644 --- a/src/smt/smt_engine.cpp +++ b/src/smt/smt_engine.cpp @@ -93,7 +93,7 @@ #include "theory/bv/bvintropow2.h" #include "theory/bv/theory_bv_rewriter.h" #include "theory/logic_info.h" -#include "theory/quantifiers/ce_guided_instantiation.h" +#include "theory/quantifiers/sygus/ce_guided_instantiation.h" #include "theory/quantifiers/fun_def_process.h" #include "theory/quantifiers/global_negate.h" #include "theory/quantifiers/macros.h" diff --git a/src/theory/arith/theory_arith_private.cpp b/src/theory/arith/theory_arith_private.cpp index fc0673d21..d7706201d 100644 --- a/src/theory/arith/theory_arith_private.cpp +++ b/src/theory/arith/theory_arith_private.cpp @@ -64,7 +64,7 @@ #include "theory/arith/simplex.h" #include "theory/arith/theory_arith.h" #include "theory/ite_utilities.h" -#include "theory/quantifiers/bounded_integers.h" +#include "theory/quantifiers/fmf/bounded_integers.h" #include "theory/rewriter.h" #include "theory/theory_model.h" #include "theory/valuation.h" diff --git a/src/theory/datatypes/datatypes_sygus.cpp b/src/theory/datatypes/datatypes_sygus.cpp index 91400479f..556b07f7f 100644 --- a/src/theory/datatypes/datatypes_sygus.cpp +++ b/src/theory/datatypes/datatypes_sygus.cpp @@ -22,9 +22,9 @@ #include "printer/printer.h" #include "theory/datatypes/datatypes_rewriter.h" #include "theory/datatypes/theory_datatypes.h" -#include "theory/quantifiers/ce_guided_conjecture.h" -#include "theory/quantifiers/sygus_explain.h" -#include "theory/quantifiers/term_database_sygus.h" +#include "theory/quantifiers/sygus/ce_guided_conjecture.h" +#include "theory/quantifiers/sygus/sygus_explain.h" +#include "theory/quantifiers/sygus/term_database_sygus.h" #include "theory/quantifiers/term_util.h" #include "theory/theory_model.h" diff --git a/src/theory/datatypes/datatypes_sygus.h b/src/theory/datatypes/datatypes_sygus.h index ff2d2a873..2c1f85deb 100644 --- a/src/theory/datatypes/datatypes_sygus.h +++ b/src/theory/datatypes/datatypes_sygus.h @@ -29,7 +29,7 @@ #include "context/context.h" #include "expr/datatype.h" #include "expr/node.h" -#include "theory/quantifiers/ce_guided_conjecture.h" +#include "theory/quantifiers/sygus/ce_guided_conjecture.h" #include "theory/quantifiers/sygus_sampler.h" #include "theory/quantifiers/term_database.h" diff --git a/src/theory/quantifiers/ambqi_builder.cpp b/src/theory/quantifiers/ambqi_builder.cpp deleted file mode 100644 index 0a6df7df5..000000000 --- a/src/theory/quantifiers/ambqi_builder.cpp +++ /dev/null @@ -1,968 +0,0 @@ -/********************* */ -/*! \file ambqi_builder.cpp - ** \verbatim - ** Top contributors (to current version): - ** Morgan Deters, 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 Implementation of abstract MBQI builder - **/ - -#include "theory/quantifiers/ambqi_builder.h" -#include "options/quantifiers_options.h" -#include "theory/quantifiers/instantiate.h" -#include "theory/quantifiers/term_database.h" -#include "theory/quantifiers/term_util.h" - -using namespace std; -using namespace CVC4::kind; -using namespace CVC4::context; - -namespace CVC4 { -namespace theory { -namespace quantifiers { - - -void AbsDef::construct_func( FirstOrderModelAbs * m, std::vector< TNode >& fapps, unsigned depth ) { - d_def.clear(); - Assert( !fapps.empty() ); - if( depth==fapps[0].getNumChildren() ){ - //if( fapps.size()>1 ){ - // for( unsigned i=0; i " << m->getRepresentativeId( fapps[i] ) << std::endl; - // } - //} - //get representative in model for this term - d_value = m->getRepresentativeId( fapps[0] ); - Assert( d_value!=val_none ); - }else{ - TypeNode tn = fapps[0][depth].getType(); - std::map< unsigned, std::vector< TNode > > fapp_child; - - //partition based on evaluations of fapps[1][depth]....fapps[n][depth] - for( unsigned i=0; igetRepresentativeId( fapps[i][depth] ); - Assert( r < 32 ); - fapp_child[r].push_back( fapps[i] ); - } - - //do completion - std::map< unsigned, unsigned > fapp_child_index; - unsigned def = m->d_domain[ tn ]; - unsigned minSize = fapp_child.begin()->second.size(); - unsigned minSizeIndex = fapp_child.begin()->first; - for( std::map< unsigned, std::vector< TNode > >::iterator it = fapp_child.begin(); it != fapp_child.end(); ++it ){ - fapp_child_index[it->first] = ( 1 << it->first ); - def = def & ~( 1 << it->first ); - if( it->second.size()second.size(); - minSizeIndex = it->first; - } - } - fapp_child_index[minSizeIndex] |= def; - d_default = fapp_child_index[minSizeIndex]; - - //construct children - for( std::map< unsigned, std::vector< TNode > >::iterator it = fapp_child.begin(); it != fapp_child.end(); ++it ){ - Trace("abs-model-debug") << "Construct " << it->first << " : " << fapp_child_index[it->first] << " : "; - const RepSet* rs = m->getRepSet(); - debugPrintUInt("abs-model-debug", - rs->getNumRepresentatives(tn), - fapp_child_index[it->first]); - Trace("abs-model-debug") << " : " << it->second.size() << " terms." << std::endl; - d_def[fapp_child_index[it->first]].construct_func( m, it->second, depth+1 ); - } - } -} - -void AbsDef::simplify( FirstOrderModelAbs * m, TNode q, TNode n, unsigned depth ) { - if( d_value==val_none && !d_def.empty() ){ - //process the default - std::map< unsigned, AbsDef >::iterator defd = d_def.find( d_default ); - Assert( defd!=d_def.end() ); - unsigned newDef = d_default; - std::vector< unsigned > to_erase; - defd->second.simplify( m, q, n, depth+1 ); - int defVal = defd->second.d_value; - bool isConstant = ( defVal!=val_none ); - //process each child - for( std::map< unsigned, AbsDef >::iterator it = d_def.begin(); it != d_def.end(); ++it ){ - if( it->first!=d_default ){ - it->second.simplify( m, q, n, depth+1 ); - if( it->second.d_value==defVal && it->second.d_value!=val_none ){ - newDef = newDef | it->first; - to_erase.push_back( it->first ); - }else{ - isConstant = false; - } - } - } - if( !to_erase.empty() ){ - //erase old default - int defVal = defd->second.d_value; - d_def.erase( d_default ); - //set new default - d_default = newDef; - d_def[d_default].construct_def_entry( m, q, n, defVal, depth+1 ); - //erase redundant entries - for( unsigned i=0; igetRepSet(); - unsigned dSize = rs->getNumRepresentatives(tn); - Assert( dSize<32 ); - for( std::map< unsigned, AbsDef >::const_iterator it = d_def.begin(); it != d_def.end(); ++it ){ - for( unsigned i=0; ifirst ); - if( it->first==d_default ){ - Trace(c) << "*"; - } - if( it->second.d_value!=val_none ){ - Trace(c) << " -> V[" << it->second.d_value << "]"; - } - Trace(c) << std::endl; - it->second.debugPrint( c, m, f, depth+1 ); - } - } - } -} - -bool AbsDef::addInstantiations( FirstOrderModelAbs * m, QuantifiersEngine * qe, TNode q, std::vector< Node >& terms, int& inst, unsigned depth ) { - if( inst==0 || !options::fmfOneInstPerRound() ){ - if( d_value==1 ){ - //instantiations are all true : ignore this - return true; - }else{ - if( depth==q[0].getNumChildren() ){ - if (qe->getInstantiate()->addInstantiation(q, terms, true)) - { - Trace("ambqi-inst-debug") << "-> Added instantiation." << std::endl; - inst++; - return true; - }else{ - Trace("ambqi-inst-debug") << "-> Failed to add instantiation." << std::endl; - //we are incomplete - return false; - } - }else{ - bool osuccess = true; - TypeNode tn = m->getVariable( q, depth ).getType(); - for( std::map< unsigned, AbsDef >::iterator it = d_def.begin(); it != d_def.end(); ++it ){ - //get witness term - unsigned index = 0; - bool success; - do { - success = false; - index = getId( it->first, index ); - if( index<32 ){ - const RepSet* rs = m->getRepSet(); - Assert(index < rs->getNumRepresentatives(tn)); - terms[m->d_var_order[q][depth]] = - rs->getRepresentative(tn, index); - if( !it->second.addInstantiations( m, qe, q, terms, inst, depth+1 ) && inst==0 ){ - //if we are incomplete, and have not yet added an instantiation, keep trying - index++; - Trace("ambqi-inst-debug") << "At depth " << depth << ", failed branch, no instantiations and incomplete, increment index : " << index << std::endl; - }else{ - success = true; - } - } - }while( !qe->inConflict() && !success && index<32 ); - //mark if we are incomplete - osuccess = osuccess && success; - } - return osuccess; - } - } - }else{ - return true; - } -} - -void AbsDef::construct_entry( std::vector< unsigned >& entry, std::vector< bool >& entry_def, int v, unsigned depth ) { - if( depth==entry.size() ){ - d_value = v; - }else{ - d_def[entry[depth]].construct_entry( entry, entry_def, v, depth+1 ); - if( entry_def[depth] ){ - d_default = entry[depth]; - } - } -} - -void AbsDef::get_defs( unsigned u, std::vector< AbsDef * >& defs ) { - for( std::map< unsigned, AbsDef >::iterator it = d_def.begin(); it != d_def.end(); ++it ){ - if( ( u & it->first )!=0 ){ - Assert( (u & it->first)==u ); - defs.push_back( &it->second ); - } - } -} - -void AbsDef::construct_normalize( FirstOrderModelAbs * m, TNode q, std::vector< AbsDef * >& defs, unsigned depth ) { - if( depth==q[0].getNumChildren() ){ - Assert( defs.size()==1 ); - d_value = defs[0]->d_value; - }else{ - TypeNode tn = m->getVariable( q, depth ).getType(); - unsigned def = m->d_domain[tn]; - for( unsigned i=0; i::iterator itd = defs[i]->d_def.begin(); itd != defs[i]->d_def.end(); ++itd ){ - if( isSimple( itd->first ) && ( def & itd->first )!=0 ){ - def &= ~( itd->first ); - //process this value - std::vector< AbsDef * > cdefs; - for( unsigned j=0; jget_defs( itd->first, cdefs ); - } - d_def[itd->first].construct_normalize( m, q, cdefs, depth+1 ); - if( def==0 ){ - d_default = itd->first; - break; - } - } - } - if( def==0 ){ - break; - } - } - if( def!=0 ){ - d_default = def; - //process the default - std::vector< AbsDef * > cdefs; - for( unsigned j=0; jget_defs( d_default, cdefs ); - } - d_def[d_default].construct_normalize( m, q, cdefs, depth+1 ); - } - } -} - -void AbsDef::construct_def_entry( FirstOrderModelAbs * m, TNode q, TNode n, int v, unsigned depth ) { - d_value = v; - if( depthgetVariable( q, depth ).getType(); - unsigned dom = m->d_domain[tn] ; - d_def[dom].construct_def_entry( m, q, n, v, depth+1 ); - d_default = dom; - } -} - -void AbsDef::apply_ucompose( FirstOrderModelAbs * m, TNode q, - std::vector< unsigned >& entry, std::vector< bool >& entry_def, - std::vector< int >& terms, std::map< unsigned, int >& vchildren, - AbsDef * a, unsigned depth ) { - if( depth==terms.size() ){ - if( Trace.isOn("ambqi-check-debug2") ){ - Trace("ambqi-check-debug2") << "Add entry ( "; - const RepSet* rs = m->getRepSet(); - for( unsigned i=0; igetNumRepresentatives(m->getVariable(q, i).getType()); - debugPrintUInt( "ambqi-check-debug2", dSize, entry[i] ); - Trace("ambqi-check-debug2") << " "; - } - Trace("ambqi-check-debug2") << ")" << std::endl; - } - a->construct_entry( entry, entry_def, d_value ); - }else{ - unsigned id; - if( terms[depth]==val_none ){ - //a variable - std::map< unsigned, int >::iterator itv = vchildren.find( depth ); - Assert( itv!=vchildren.end() ); - unsigned prev_v = entry[itv->second]; - bool prev_vd = entry_def[itv->second]; - for( std::map< unsigned, AbsDef >::iterator it = d_def.begin(); it != d_def.end(); ++it ){ - entry[itv->second] = it->first & prev_v; - entry_def[itv->second] = ( it->first==d_default ) && prev_vd; - if( entry[itv->second]!=0 ){ - it->second.apply_ucompose( m, q, entry, entry_def, terms, vchildren, a, depth+1 ); - } - } - entry[itv->second] = prev_v; - entry_def[itv->second] = prev_vd; - }else{ - id = (unsigned)terms[depth]; - Assert( id<32 ); - unsigned fid = 1 << id; - std::map< unsigned, AbsDef >::iterator it = d_def.find( fid ); - if( it!=d_def.end() ){ - it->second.apply_ucompose( m, q, entry, entry_def, terms, vchildren, a, depth+1 ); - }else{ - d_def[d_default].apply_ucompose( m, q, entry, entry_def, terms, vchildren, a, depth+1 ); - } - } - } -} - -void AbsDef::construct_var_eq( FirstOrderModelAbs * m, TNode q, unsigned v1, unsigned v2, int curr, int currv, unsigned depth ) { - if( depth==q[0].getNumChildren() ){ - Assert( currv!=val_none ); - d_value = currv; - }else{ - TypeNode tn = m->getVariable( q, depth ).getType(); - unsigned dom = m->d_domain[tn]; - int vindex = depth==v1 ? 0 : ( depth==v2 ? 1 : val_none ); - if( vindex==val_none ){ - d_def[dom].construct_var_eq( m, q, v1, v2, curr, currv, depth+1 ); - d_default = dom; - }else{ - Assert( currv==val_none ); - if( curr==val_none ){ - unsigned numReps = m->getRepSet()->getNumRepresentatives(tn); - Assert( numReps < 32 ); - for( unsigned i=0; igetVariable( q, depth ).getType(); - if( v==depth ){ - unsigned numReps = m->getRepSet()->getNumRepresentatives(tn); - Assert( numReps>0 && numReps < 32 ); - for( unsigned i=0; id_domain[tn]; - d_def[dom].construct_var( m, q, v, currv, depth+1 ); - d_default = dom; - } - } -} - -void AbsDef::construct_compose( FirstOrderModelAbs * m, TNode q, TNode n, AbsDef * f, - std::map< unsigned, AbsDef * >& children, - std::map< unsigned, int >& bchildren, std::map< unsigned, int >& vchildren, - std::vector< unsigned >& entry, std::vector< bool >& entry_def ) { - const RepSet* rs = m->getRepSet(); - if( n.getKind()==OR || n.getKind()==AND ){ - // short circuiting - for( std::map< unsigned, AbsDef * >::iterator it = children.begin(); it != children.end(); ++it ){ - if( ( it->second->d_value==0 && n.getKind()==AND ) || - ( it->second->d_value==1 && n.getKind()==OR ) ){ - //std::cout << "Short circuit " << it->second->d_value << " " << entry.size() << "/" << q[0].getNumChildren() << std::endl; - unsigned count = q[0].getNumChildren() - entry.size(); - for( unsigned i=0; id_domain[m->getVariable( q, entry.size() ).getType()] ); - entry_def.push_back( true ); - } - construct_entry( entry, entry_def, it->second->d_value ); - for( unsigned i=0; i values; - values.resize( n.getNumChildren(), val_none ); - for( std::map< unsigned, AbsDef * >::iterator it = children.begin(); it != children.end(); ++it ){ - values[it->first] = it->second->d_value; - } - for( std::map< unsigned, int >::iterator it = bchildren.begin(); it != bchildren.end(); ++it ){ - values[it->first] = it->second; - } - //look up value(s) - f->apply_ucompose( m, q, entry, entry_def, values, vchildren, this ); - }else{ - bool incomplete = false; - //we are composing with an interpreted function - std::vector< TNode > values; - values.resize( n.getNumChildren(), TNode::null() ); - for( std::map< unsigned, AbsDef * >::iterator it = children.begin(); it != children.end(); ++it ){ - Trace("ambqi-check-debug2") << "composite : " << it->first << " : " << it->second->d_value; - if( it->second->d_value>=0 ){ - if (it->second->d_value - >= (int)rs->getNumRepresentatives(n[it->first].getType())) - { - std::cout << it->second->d_value << " " << n[it->first] << " " - << n[it->first].getType() << " " - << rs->getNumRepresentatives(n[it->first].getType()) - << std::endl; - } - Assert(it->second->d_value - < (int)rs->getNumRepresentatives(n[it->first].getType())); - values[it->first] = rs->getRepresentative(n[it->first].getType(), - it->second->d_value); - }else{ - incomplete = true; - } - Trace("ambqi-check-debug2") << " ->> " << values[it->first] << std::endl; - } - for( std::map< unsigned, int >::iterator it = bchildren.begin(); it != bchildren.end(); ++it ){ - Trace("ambqi-check-debug2") << " basic : " << it->first << " : " << it->second; - if( it->second>=0 ){ - Assert(it->second - < (int)rs->getNumRepresentatives(n[it->first].getType())); - values[it->first] = - rs->getRepresentative(n[it->first].getType(), it->second); - }else{ - incomplete = true; - } - Trace("ambqi-check-debug2") << " ->> " << values[it->first] << std::endl; - } - Assert( vchildren.empty() ); - if( incomplete ){ - Trace("ambqi-check-debug2") << "Construct incomplete entry." << std::endl; - - //if a child is unknown, we must return unknown - construct_entry( entry, entry_def, val_unk ); - }else{ - if( Trace.isOn("ambqi-check-debug2") ){ - for( unsigned i=0; imkNode( n.getKind(), values ); - vv = Rewriter::rewrite( vv ); - int v = m->getRepresentativeId( vv ); - construct_entry( entry, entry_def, v ); - } - } - }else{ - //take product of arguments - TypeNode tn = m->getVariable( q, entry.size() ).getType(); - Assert( m->isValidType( tn ) ); - unsigned def = m->d_domain[tn]; - if( Trace.isOn("ambqi-check-debug2") ){ - for( unsigned i=0; i::iterator it = children.begin(); it != children.end(); ++it ){ - Assert( it->second!=NULL ); - //process each child - for( std::map< unsigned, AbsDef >::iterator itd = it->second->d_def.begin(); itd != it->second->d_def.end(); ++itd ){ - if( itd->first!=it->second->d_default && ( def & itd->first )!=0 ){ - def &= ~( itd->first ); - //process this value - std::map< unsigned, AbsDef * > cchildren; - for( std::map< unsigned, AbsDef * >::iterator it2 = children.begin(); it2 != children.end(); ++it2 ){ - Assert( it2->second!=NULL ); - std::map< unsigned, AbsDef >::iterator itdf = it2->second->d_def.find( itd->first ); - if( itdf!=it2->second->d_def.end() ){ - cchildren[it2->first] = &itdf->second; - }else{ - Assert( it2->second->getDefault()!=NULL ); - cchildren[it2->first] = it2->second->getDefault(); - } - } - if( Trace.isOn("ambqi-check-debug2") ){ - for( unsigned i=0; igetNumRepresentatives(tn), - itd->first); - Trace("ambqi-check-debug2") << " " << children.size() << " " << cchildren.size() << std::endl; - } - entry.push_back( itd->first ); - entry_def.push_back( def==0 ); - construct_compose( m, q, n, f, cchildren, bchildren, vchildren, entry, entry_def ); - entry_def.pop_back(); - entry.pop_back(); - if( def==0 ){ - break; - } - } - } - if( def==0 ){ - break; - } - } - if( def!=0 ){ - if( Trace.isOn("ambqi-check-debug2") ){ - for( unsigned i=0; i cdchildren; - for( std::map< unsigned, AbsDef * >::iterator it = children.begin(); it != children.end(); ++it ){ - Assert( it->second->getDefault()!=NULL ); - cdchildren[it->first] = it->second->getDefault(); - } - if( Trace.isOn("ambqi-check-debug2") ){ - for( unsigned i=0; igetNumRepresentatives(tn), def); - Trace("ambqi-check-debug2") << " " << children.size() << " " << cdchildren.size() << std::endl; - } - entry.push_back( def ); - entry_def.push_back( true ); - construct_compose( m, q, n, f, cdchildren, bchildren, vchildren, entry, entry_def ); - entry_def.pop_back(); - entry.pop_back(); - } - } -} - -bool AbsDef::construct( FirstOrderModelAbs * m, TNode q, TNode n, AbsDef * f, - std::map< unsigned, AbsDef * >& children, - std::map< unsigned, int >& bchildren, std::map< unsigned, int >& vchildren, - int varChCount ) { - if( Trace.isOn("ambqi-check-debug3") ){ - for( unsigned i=0; i::iterator it = bchildren.begin(); it !=bchildren.end(); ++it ){ - if( ( it->second==0 && n.getKind()==AND ) || - ( it->second==1 && n.getKind()==OR ) ){ - construct_def_entry( m, q, q[0], it->second ); - return true; - } - } - } - Trace("ambqi-check-debug2") << "Construct compose..." << std::endl; - std::vector< unsigned > entry; - std::vector< bool > entry_def; - if( f && varChCount>0 ){ - AbsDef unorm; - unorm.construct_compose( m, q, n, f, children, bchildren, vchildren, entry, entry_def ); - //normalize - std::vector< AbsDef* > defs; - defs.push_back( &unorm ); - construct_normalize( m, q, defs ); - }else{ - construct_compose( m, q, n, f, children, bchildren, vchildren, entry, entry_def ); - } - Assert( is_normalized() ); - //if( !is_normalized() ){ - // std::cout << "NON NORMALIZED DEFINITION" << std::endl; - // exit( 10 ); - //} - return true; - }else if( varChCount==1 && ( n.getKind()==EQUAL && !n[0].getType().isBoolean() ) ){ - Trace("ambqi-check-debug2") << "Expand variable child..." << std::endl; - //expand the variable based on its finite domain - AbsDef a; - a.construct_var( m, q, vchildren.begin()->second, val_none ); - children[vchildren.begin()->first] = &a; - vchildren.clear(); - std::vector< unsigned > entry; - std::vector< bool > entry_def; - Trace("ambqi-check-debug2") << "Construct compose with variable..." << std::endl; - construct_compose( m, q, n, f, children, bchildren, vchildren, entry, entry_def ); - return true; - }else if( varChCount==2 && ( n.getKind()==EQUAL && !n[0].getType().isBoolean() ) ){ - Trace("ambqi-check-debug2") << "Construct variable equality..." << std::endl; - //efficient expansion of the equality - construct_var_eq( m, q, vchildren[0], vchildren[1], val_none, val_none ); - return true; - }else{ - return false; - } -} - -void AbsDef::negate() { - for( std::map< unsigned, AbsDef >::iterator it = d_def.begin(); it != d_def.end(); ++it ){ - it->second.negate(); - } - if( d_value==0 ){ - d_value = 1; - }else if( d_value==1 ){ - d_value = 0; - } -} - -Node AbsDef::getFunctionValue( FirstOrderModelAbs * m, TNode op, std::vector< Node >& vars, unsigned depth ) { - const RepSet* rs = m->getRepSet(); - if( depth==vars.size() ){ - TypeNode tn = op.getType(); - if( tn.getNumChildren()>0 ){ - tn = tn[tn.getNumChildren() - 1]; - } - if( d_value>=0 ){ - Assert(d_value < (int)rs->getNumRepresentatives(tn)); - if( tn.isBoolean() ){ - return NodeManager::currentNM()->mkConst( d_value==1 ); - }else{ - return rs->getRepresentative(tn, d_value); - } - }else{ - return Node::null(); - } - }else{ - TypeNode tn = vars[depth].getType(); - Node curr; - curr = d_def[d_default].getFunctionValue( m, op, vars, depth+1 ); - for( std::map< unsigned, AbsDef >::iterator it = d_def.begin(); it != d_def.end(); ++it ){ - if( it->first!=d_default ){ - unsigned id = getId( it->first ); - Assert(id < rs->getNumRepresentatives(tn)); - TNode n = rs->getRepresentative(tn, id); - Node fv = it->second.getFunctionValue( m, op, vars, depth+1 ); - if( !curr.isNull() && !fv.isNull() ){ - curr = NodeManager::currentNM()->mkNode( ITE, vars[depth].eqNode( n ), fv, curr ); - }else{ - curr = Node::null(); - } - } - } - return curr; - } -} - -bool AbsDef::isSimple( unsigned n ) { - return (n & (n - 1))==0; -} - -unsigned AbsDef::getId( unsigned n, unsigned start, unsigned end ) { - Assert( n!=0 ); - while( (n & ( 1 << start )) == 0 ){ - start++; - if( start==end ){ - return start; - } - } - return start; -} - -Node AbsDef::evaluate( FirstOrderModelAbs * m, TypeNode retTyp, std::vector< Node >& args ) { - std::vector< unsigned > iargs; - for( unsigned i=0; igetRepresentativeId( args[i] ); - iargs.push_back( v ); - } - return evaluate( m, retTyp, iargs, 0 ); -} - -Node AbsDef::evaluate( FirstOrderModelAbs * m, TypeNode retTyp, std::vector< unsigned >& iargs, unsigned depth ) { - if( d_value!=val_none ){ - if( d_value==val_unk ){ - return Node::null(); - }else{ - const RepSet* rs = m->getRepSet(); - Assert(d_value >= 0 && d_value < (int)rs->getNumRepresentatives(retTyp)); - return rs->getRepresentative(retTyp, d_value); - } - }else{ - std::map< unsigned, AbsDef >::iterator it = d_def.find( iargs[depth] ); - if( it==d_def.end() ){ - return d_def[d_default].evaluate( m, retTyp, iargs, depth+1 ); - }else{ - return it->second.evaluate( m, retTyp, iargs, depth+1 ); - } - } -} - -bool AbsDef::is_normalized() { - for( std::map< unsigned, AbsDef >::iterator it1 = d_def.begin(); it1 != d_def.end(); ++it1 ){ - if( !it1->second.is_normalized() ){ - return false; - } - for( std::map< unsigned, AbsDef >::iterator it2 = d_def.begin(); it2 != d_def.end(); ++it2 ){ - if( it1->first!=it2->first && (( it1->first & it2->first )!=0) ){ - return false; - } - } - } - return true; -} - -AbsMbqiBuilder::AbsMbqiBuilder( context::Context* c, QuantifiersEngine* qe ) : -QModelBuilder( c, qe ){ - d_true = NodeManager::currentNM()->mkConst( true ); - d_false = NodeManager::currentNM()->mkConst( false ); -} - - -//------------------------model construction---------------------------- - -bool AbsMbqiBuilder::processBuildModel(TheoryModel* m) { - Trace("ambqi-debug") << "process build model " << std::endl; - FirstOrderModel* f = (FirstOrderModel*)m; - FirstOrderModelAbs* fm = f->asFirstOrderModelAbs(); - RepSet* rs = m->getRepSetPtr(); - fm->initialize(); - //process representatives - fm->d_rep_id.clear(); - fm->d_domain.clear(); - - //initialize boolean sort - TypeNode b = d_true.getType(); - rs->d_type_reps[b].clear(); - rs->d_type_reps[b].push_back(d_false); - rs->d_type_reps[b].push_back(d_true); - fm->d_rep_id[d_false] = 0; - fm->d_rep_id[d_true] = 1; - - //initialize unintpreted sorts - Trace("ambqi-model") << std::endl << "Making representatives..." << std::endl; - for (std::map >::iterator it = - rs->d_type_reps.begin(); - it != rs->d_type_reps.end(); - ++it) - { - if( it->first.isSort() ){ - Assert( !it->second.empty() ); - //set the domain - fm->d_domain[it->first] = 0; - Trace("ambqi-model") << "Representatives for " << it->first << " : " << std::endl; - for( unsigned i=0; isecond.size(); i++ ){ - if( i<32 ){ - fm->d_domain[it->first] |= ( 1 << i ); - } - Trace("ambqi-model") << i << " : " << it->second[i] << std::endl; - fm->d_rep_id[it->second[i]] = i; - } - if( it->second.size()>=32 ){ - fm->d_domain.erase( it->first ); - } - } - } - - Trace("ambqi-model") << std::endl << "Making function definitions..." << std::endl; - //construct the models for functions - for( std::map::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ) { - Node f = it->first; - Trace("ambqi-model-debug") << "Building Model for " << f << std::endl; - //reset the model - it->second->clear(); - //get all (non-redundant) f-applications - std::vector< TNode > fapps; - Trace("ambqi-model-debug") << "Initial terms: " << std::endl; - std::map< Node, std::vector< Node > >::iterator itut = fm->d_uf_terms.find( f ); - if( itut!=fm->d_uf_terms.end() ){ - for( size_t i=0; isecond.size(); i++ ){ - Node n = itut->second[i]; - // only consider unique up to congruence (in model equality engine)? - Trace("ambqi-model-debug") << " " << n << " -> " << fm->getRepresentativeId( n ) << std::endl; - fapps.push_back( n ); - } - } - if( fapps.empty() ){ - //choose arbitrary value - Node mbt = fm->getModelBasisOpTerm(f); - Trace("ambqi-model-debug") << "Initial terms empty, add " << mbt << std::endl; - fapps.push_back( mbt ); - } - bool fValid = true; - for( unsigned i=0; id_domain.find( fapps[0][i].getType() )==fm->d_domain.end() ){ - Trace("ambqi-model") << "Interpretation of " << f << " is not valid."; - Trace("ambqi-model") << " (domain for " << fapps[0][i].getType() << " is too large)." << std::endl; - fValid = false; - break; - } - } - fm->d_models_valid[f] = fValid; - if( fValid ){ - //construct the ambqi model - it->second->construct_func( fm, fapps ); - Trace("ambqi-model-debug") << "Interpretation of " << f << " : " << std::endl; - it->second->debugPrint("ambqi-model-debug", fm, fapps[0] ); - Trace("ambqi-model-debug") << "Simplifying " << f << "..." << std::endl; - it->second->simplify( fm, TNode::null(), fapps[0] ); - Trace("ambqi-model") << "(Simplified) interpretation of " << f << " : " << std::endl; - it->second->debugPrint("ambqi-model", fm, fapps[0] ); - -/* - if( Debug.isOn("ambqi-model-debug") ){ - for( size_t i=0; id_uf_terms[f].size(); i++ ){ - Node e = it->second->evaluate_n( fm, fm->d_uf_terms[f][i] ); - Debug("ambqi-model-debug") << fm->d_uf_terms[f][i] << " evaluates to " << e << std::endl; - Assert( fm->areEqual( e, fm->d_uf_terms[f][i] ) ); - } - } -*/ - } - } - Trace("ambqi-model") << "Construct model representation..." << std::endl; - //make function values - for( std::map::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ) { - if( it->first.getType().getNumChildren()>1 ){ - Trace("ambqi-model") << "Construct for " << it->first << "..." << std::endl; - Node f_def = fm->getFunctionValue( it->first, "$x" ); - m->assignFunctionDefinition( it->first, f_def ); - } - } - Assert( d_addedLemmas==0 ); - return TheoryEngineModelBuilder::processBuildModel( m ); -} - - -//--------------------model checking--------------------------------------- - -//do exhaustive instantiation -int AbsMbqiBuilder::doExhaustiveInstantiation( FirstOrderModel * fm, Node q, int effort ) { - Trace("ambqi-check") << "Exhaustive instantiation " << q << " " << effort << std::endl; - if (effort==0) { - FirstOrderModelAbs * fma = fm->asFirstOrderModelAbs(); - bool quantValid = true; - for( unsigned i=0; iisValidType( q[0][i].getType() ) ){ - quantValid = false; - Trace("ambqi-inst") << "Interpretation of " << q << " is not valid because of type " << q[0][i].getType() << std::endl; - break; - } - } - if( quantValid ){ - Trace("ambqi-check") << "Compute interpretation..." << std::endl; - AbsDef ad; - doCheck( fma, q, ad, q[1] ); - //now process entries - Trace("ambqi-inst-debug") << "...Current : " << d_addedLemmas << std::endl; - Trace("ambqi-inst") << "Interpretation of " << q << " is : " << std::endl; - ad.debugPrint( "ambqi-inst", fma, q[0] ); - Trace("ambqi-inst") << std::endl; - Trace("ambqi-check") << "Add instantiations..." << std::endl; - int lem = 0; - quantValid = ad.addInstantiations( fma, d_qe, q, lem ); - Trace("ambqi-inst") << "...Added " << lem << " lemmas." << std::endl; - if( lem>0 ){ - //if we were incomplete but added at least one lemma, we are ok - quantValid = true; - } - d_addedLemmas += lem; - Trace("ambqi-inst-debug") << "...Total : " << d_addedLemmas << std::endl; - } - return quantValid ? 1 : 0; - }else{ - return 1; - } -} - -bool AbsMbqiBuilder::doCheck( FirstOrderModelAbs * m, TNode q, AbsDef & ad, TNode n ) { - Assert( n.getKind()!=FORALL ); - if( n.getKind()==NOT && n[0].getKind()!=FORALL ){ - doCheck( m, q, ad, n[0] ); - ad.negate(); - return true; - }else{ - std::map< unsigned, AbsDef > children; - std::map< unsigned, int > bchildren; - std::map< unsigned, int > vchildren; - int varChCount = 0; - for( unsigned i=0; id_var_index[q][ m->getVariableId( q, n[i] ) ]; - //vchildren[i] = m->getVariableId( q, n[i] ); - }else if( m->hasTerm( n[i] ) ){ - bchildren[i] = m->getRepresentativeId( n[i] ); - }else{ - if( !doCheck( m, q, children[i], n[i] ) ){ - bchildren[i] = AbsDef::val_unk; - children.erase( i ); - } - } - } - //convert to pointers - std::map< unsigned, AbsDef * > pchildren; - for( std::map< unsigned, AbsDef >::iterator it = children.begin(); it != children.end(); ++it ){ - pchildren[it->first] = &it->second; - } - //construct the interpretation - Trace("ambqi-check-debug") << "Compute Interpretation of " << n << " " << n.getKind() << std::endl; - if( n.getKind() == APPLY_UF || n.getKind() == VARIABLE || n.getKind() == SKOLEM ){ - Node op; - if( n.getKind() == APPLY_UF ){ - op = n.getOperator(); - }else{ - op = n; - } - //uninterpreted compose - if( m->d_models_valid[op] ){ - ad.construct( m, q, n, m->d_models[op], pchildren, bchildren, vchildren, varChCount ); - }else{ - Trace("ambqi-check-debug") << "** Cannot produce interpretation for " << n << " (no function model)" << std::endl; - return false; - } - }else if( !ad.construct( m, q, n, NULL, pchildren, bchildren, vchildren, varChCount ) ){ - Trace("ambqi-check-debug") << "** Cannot produce interpretation for " << n << " (variables are children of interpreted symbol)" << std::endl; - return false; - } - Trace("ambqi-check-try") << "Interpretation for " << n << " is : " << std::endl; - ad.debugPrint("ambqi-check-try", m, q[0] ); - ad.simplify( m, q, q[0] ); - Trace("ambqi-check-debug") << "(Simplified) Interpretation for " << n << " is : " << std::endl; - ad.debugPrint("ambqi-check-debug", m, q[0] ); - Trace("ambqi-check-debug") << std::endl; - return true; - } -} - -}/* namespace CVC4::theory::quantifiers */ -}/* namespace CVC4::theory */ -}/* namespace CVC4 */ diff --git a/src/theory/quantifiers/ambqi_builder.h b/src/theory/quantifiers/ambqi_builder.h deleted file mode 100644 index c451f0489..000000000 --- a/src/theory/quantifiers/ambqi_builder.h +++ /dev/null @@ -1,105 +0,0 @@ -/********************* */ -/*! \file ambqi_builder.h - ** \verbatim - ** Top contributors (to current version): - ** Morgan Deters, Tim King, 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 Abstract MBQI model builder class - **/ - -#include "cvc4_private.h" - -#ifndef ABSTRACT_MBQI_BUILDER -#define ABSTRACT_MBQI_BUILDER - -#include "theory/quantifiers/model_builder.h" -#include "theory/quantifiers/first_order_model.h" - -namespace CVC4 { -namespace theory { -namespace quantifiers { - -class FirstOrderModelAbs; - -//representiation of function and term interpretations -class AbsDef -{ -private: - bool addInstantiations( FirstOrderModelAbs * m, QuantifiersEngine * qe, TNode q, std::vector< Node >& terms, int& inst, unsigned depth ); - void construct_compose( FirstOrderModelAbs * m, TNode q, TNode n, AbsDef * f, - std::map< unsigned, AbsDef * >& children, - std::map< unsigned, int >& bchildren, std::map< unsigned, int >& vchildren, - std::vector< unsigned >& entry, std::vector< bool >& entry_def ); - void construct_entry( std::vector< unsigned >& entry, std::vector< bool >& entry_def, int v, unsigned depth = 0 ); - void construct_def_entry( FirstOrderModelAbs * m, TNode q, TNode n, int v, unsigned depth = 0 ); - void apply_ucompose( FirstOrderModelAbs * m, TNode q, - std::vector< unsigned >& entry, std::vector< bool >& entry_def, std::vector< int >& terms, - std::map< unsigned, int >& vchildren, AbsDef * a, unsigned depth = 0 ); - void construct_var_eq( FirstOrderModelAbs * m, TNode q, unsigned v1, unsigned v2, int curr, int currv, unsigned depth = 0 ); - void construct_var( FirstOrderModelAbs * m, TNode q, unsigned v, int currv, unsigned depth = 0 ); - void get_defs( unsigned u, std::vector< AbsDef * >& defs ); - void construct_normalize( FirstOrderModelAbs * m, TNode q, std::vector< AbsDef * >& defs, unsigned depth = 0 ); -public: - enum { - val_none = -1, - val_unk = -2, - }; - AbsDef() : d_default( 0 ), d_value( -1 ){} - std::map< unsigned, AbsDef > d_def; - unsigned d_default; - int d_value; - - void clear() { d_def.clear(); d_default = 0; d_value = -1; } - AbsDef * getDefault() { return &d_def[d_default]; } - void construct_func( FirstOrderModelAbs * m, std::vector< TNode >& fapps, unsigned depth = 0 ); - void debugPrintUInt( const char * c, unsigned dSize, unsigned u ) const; - void debugPrint( const char * c, FirstOrderModelAbs * m, TNode f, unsigned depth = 0 ) const; - void simplify( FirstOrderModelAbs * m, TNode q, TNode n, unsigned depth = 0 ); - int addInstantiations( FirstOrderModelAbs * m, QuantifiersEngine * qe, Node q, int& inst ){ - std::vector< Node > terms; - terms.resize( q[0].getNumChildren() ); - return addInstantiations( m, qe, q, terms, inst, 0 ); - } - bool construct( FirstOrderModelAbs * m, TNode q, TNode n, AbsDef * f, - std::map< unsigned, AbsDef * >& children, - std::map< unsigned, int >& bchildren, - std::map< unsigned, int >& vchildren, - int varChCount ); - void negate(); - Node getFunctionValue( FirstOrderModelAbs * m, TNode op, std::vector< Node >& vars, unsigned depth = 0 ); - static bool isSimple( unsigned n ); - static unsigned getId( unsigned n, unsigned start=0, unsigned end=32 ); - Node evaluate( FirstOrderModelAbs * m, TypeNode retType, std::vector< Node >& args ); - Node evaluate( FirstOrderModelAbs * m, TypeNode retType, std::vector< unsigned >& iargs, unsigned depth = 0 ); - //for debugging - bool is_normalized(); -}; - -class AbsMbqiBuilder : public QModelBuilder -{ - friend class AbsDef; -private: - Node d_true; - Node d_false; - bool doCheck( FirstOrderModelAbs * m, TNode q, AbsDef & ad, TNode n ); -public: - AbsMbqiBuilder( context::Context* c, QuantifiersEngine* qe ); - - //process build model - bool processBuildModel(TheoryModel* m) override; - //do exhaustive instantiation - int doExhaustiveInstantiation(FirstOrderModel* fm, - Node q, - int effort) override; -}; - -} -} -} - -#endif diff --git a/src/theory/quantifiers/bounded_integers.cpp b/src/theory/quantifiers/bounded_integers.cpp deleted file mode 100644 index 963aba612..000000000 --- a/src/theory/quantifiers/bounded_integers.cpp +++ /dev/null @@ -1,893 +0,0 @@ -/********************* */ -/*! \file bounded_integers.cpp - ** \verbatim - ** Top contributors (to current version): - ** Andrew Reynolds, Morgan Deters, 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 Bounded integers module - ** - ** This class manages integer bounds for quantifiers - **/ - -#include "theory/quantifiers/bounded_integers.h" -#include "options/quantifiers_options.h" -#include "theory/arith/arith_msum.h" -#include "theory/quantifiers/first_order_model.h" -#include "theory/quantifiers/model_engine.h" -#include "theory/quantifiers/term_enumeration.h" -#include "theory/quantifiers/term_util.h" -#include "theory/theory_engine.h" - -using namespace CVC4; -using namespace std; -using namespace CVC4::theory; -using namespace CVC4::theory::quantifiers; -using namespace CVC4::kind; - - -BoundedIntegers::IntRangeModel::IntRangeModel(BoundedIntegers * bi, Node r, context::Context* c, context::Context* u, bool isProxy) : d_bi(bi), - d_range(r), d_curr_max(-1), d_lit_to_range(u), d_range_assertions(c), d_has_range(c,false), d_curr_range(c,-1), d_ranges_proxied(u) { - if( options::fmfBoundLazy() ){ - d_proxy_range = isProxy ? r : NodeManager::currentNM()->mkSkolem( "pbir", r.getType() ); - }else{ - d_proxy_range = r; - } - if( !isProxy ){ - Trace("bound-int") << "Introduce proxy " << d_proxy_range << " for " << d_range << std::endl; - } -} - -void BoundedIntegers::IntRangeModel::initialize() { - //add initial split lemma - Node ltr = NodeManager::currentNM()->mkNode( LT, d_proxy_range, NodeManager::currentNM()->mkConst( Rational(0) ) ); - ltr = Rewriter::rewrite( ltr ); - Trace("bound-int-lemma") << " *** bound int: initial split on " << ltr << std::endl; - d_bi->getQuantifiersEngine()->getOutputChannel().split( ltr ); - Node ltr_lit = ltr.getKind()==NOT ? ltr[0] : ltr; - d_range_literal[-1] = ltr_lit; - d_lit_to_range[ltr_lit] = -1; - d_lit_to_pol[ltr_lit] = ltr.getKind()!=NOT; - //register with bounded integers - Trace("bound-int-debug") << "Literal " << ltr_lit << " is literal for " << d_range << std::endl; - d_bi->addLiteralFromRange(ltr_lit, d_range); -} - -void BoundedIntegers::IntRangeModel::assertNode(Node n) { - bool pol = n.getKind()!=NOT; - Node nlit = n.getKind()==NOT ? n[0] : n; - if( d_lit_to_range.find( nlit )!=d_lit_to_range.end() ){ - int vrange = d_lit_to_range[nlit]; - Trace("bound-int-assert") << "With polarity = " << pol << " (req "<< d_lit_to_pol[nlit] << ")"; - Trace("bound-int-assert") << ", found literal " << nlit; - Trace("bound-int-assert") << ", it is bound literal " << vrange << " for " << d_range << std::endl; - d_range_assertions[nlit] = (pol==d_lit_to_pol[nlit]); - if( pol!=d_lit_to_pol[nlit] ){ - //check if we need a new split? - if( !d_has_range ){ - bool needsRange = true; - for( NodeIntMap::iterator it = d_lit_to_range.begin(); it != d_lit_to_range.end(); ++it ){ - if( d_range_assertions.find( (*it).first )==d_range_assertions.end() ){ - Trace("bound-int-debug") << "Does not need range because of " << (*it).first << std::endl; - needsRange = false; - break; - } - } - if( needsRange ){ - allocateRange(); - } - } - }else{ - if (!d_has_range || vrangemkNode( LEQ, d_proxy_range, NodeManager::currentNM()->mkConst( Rational(newBound) ) ); - ltr = Rewriter::rewrite( ltr ); - Trace("bound-int-lemma") << " *** bound int: split on " << ltr << std::endl; - d_bi->getQuantifiersEngine()->getOutputChannel().split( ltr ); - Node ltr_lit = ltr.getKind()==NOT ? ltr[0] : ltr; - d_range_literal[newBound] = ltr_lit; - d_lit_to_range[ltr_lit] = newBound; - d_lit_to_pol[ltr_lit] = ltr.getKind()!=NOT; - //register with bounded integers - d_bi->addLiteralFromRange(ltr_lit, d_range); -} - -Node BoundedIntegers::IntRangeModel::getNextDecisionRequest() { - //request the current cardinality as a decision literal, if not already asserted - for( NodeIntMap::iterator it = d_lit_to_range.begin(); it != d_lit_to_range.end(); ++it ){ - int i = (*it).second; - if( !d_has_range || imkNode( EQUAL, d_range_literal[curr].negate(), - NodeManager::currentNM()->mkNode( LEQ, d_range, NodeManager::currentNM()->mkConst( Rational(curr) ) ) ); - Trace("bound-int-lemma") << "*** bound int : proxy lemma : " << lem << std::endl; - d_bi->getQuantifiersEngine()->addLemma( lem ); - return true; - } - } - return false; -} - - - - - -BoundedIntegers::BoundedIntegers(context::Context* c, QuantifiersEngine* qe) : -QuantifiersModule(qe), d_assertions(c){ - -} - -BoundedIntegers::~BoundedIntegers() { - for( std::map< Node, RangeModel * >::iterator it = d_rms.begin(); it != d_rms.end(); ++it ){ - delete it->second; - } -} - -void BoundedIntegers::presolve() { - d_bnd_it.clear(); -} - -bool BoundedIntegers::isBound( Node f, Node v ) { - return std::find( d_set[f].begin(), d_set[f].end(), v )!=d_set[f].end(); -} - -bool BoundedIntegers::hasNonBoundVar( Node f, Node b, std::map< Node, bool >& visited ) { - if( visited.find( b )==visited.end() ){ - visited[b] = true; - if( b.getKind()==BOUND_VARIABLE ){ - if( !isBound( f, b ) ){ - return true; - } - }else{ - for( unsigned i=0; i visited; - return hasNonBoundVar( f, b, visited ); -} - -bool BoundedIntegers::processEqDisjunct( Node q, Node n, Node& v, std::vector< Node >& v_cases ) { - if( n.getKind()==EQUAL ){ - for( unsigned i=0; i<2; i++ ){ - Node t = n[i]; - if( !hasNonBoundVar( q, n[1-i] ) ){ - if( t==v ){ - v_cases.push_back( n[1-i] ); - return true; - }else if( v.isNull() && t.getKind()==BOUND_VARIABLE ){ - v = t; - v_cases.push_back( n[1-i] ); - return true; - } - } - } - } - return false; -} - -void BoundedIntegers::processMatchBoundVars( Node q, Node n, std::vector< Node >& bvs, std::map< Node, bool >& visited ){ - if( visited.find( n )==visited.end() ){ - visited[n] = true; - if( n.getKind()==BOUND_VARIABLE && !isBound( q, n ) ){ - bvs.push_back( n ); - //injective operators - }else if( n.getKind()==kind::APPLY_CONSTRUCTOR ){ - for( unsigned i=0; i& bound_lit_type_map, - std::map< int, std::map< Node, Node > >& bound_lit_map, - std::map< int, std::map< Node, bool > >& bound_lit_pol_map, - std::map< int, std::map< Node, Node > >& bound_int_range_term, - std::map< Node, std::vector< Node > >& bound_fixed_set ){ - if( n.getKind()==OR || n.getKind()==AND ){ - if( (n.getKind()==OR)==pol ){ - for( unsigned i=0; i v_cases; - bool success = true; - for( unsigned i=0; i v_cases; - if( processEqDisjunct( q, n, v, v_cases ) ){ - if( !isBound( q, v ) ){ - bound_lit_type_map[v] = BOUND_FIXED_SET; - bound_lit_map[3][v] = n; - bound_lit_pol_map[3][v] = pol; - Assert( v_cases.size()==1 ); - bound_fixed_set[v].clear(); - bound_fixed_set[v].push_back( v_cases[0] ); - } - } - } - }else if( n.getKind()==NOT ){ - process( q, n[0], !pol, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term, bound_fixed_set ); - }else if( n.getKind()==GEQ ){ - if( n[0].getType().isInteger() ){ - std::map< Node, Node > msum; - if (ArithMSum::getMonomialSumLit(n, msum)) - { - Trace("bound-int-debug") << "literal (polarity = " << pol << ") " << n << " is monomial sum : " << std::endl; - ArithMSum::debugPrintMonomialSum(msum, "bound-int-debug"); - for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){ - if ( !it->first.isNull() && it->first.getKind()==BOUND_VARIABLE && !isBound( q, it->first ) ){ - //if not bound in another way - if( bound_lit_type_map.find( it->first )==bound_lit_type_map.end() || bound_lit_type_map[it->first] == BOUND_INT_RANGE ){ - Node veq; - if (ArithMSum::isolate(it->first, msum, veq, GEQ) != 0) - { - Node n1 = veq[0]; - Node n2 = veq[1]; - if(pol){ - //flip - n1 = veq[1]; - n2 = veq[0]; - if( n1.getKind()==BOUND_VARIABLE ){ - n2 = ArithMSum::offset(n2, 1); - }else{ - n1 = ArithMSum::offset(n1, -1); - } - veq = NodeManager::currentNM()->mkNode( GEQ, n1, n2 ); - } - Trace("bound-int-debug") << "Isolated for " << it->first << " : (" << n1 << " >= " << n2 << ")" << std::endl; - Node t = n1==it->first ? n2 : n1; - if( !hasNonBoundVar( q, t ) ) { - Trace("bound-int-debug") << "The bound is relevant." << std::endl; - int loru = n1==it->first ? 0 : 1; - bound_lit_type_map[it->first] = BOUND_INT_RANGE; - bound_int_range_term[loru][it->first] = t; - bound_lit_map[loru][it->first] = n; - bound_lit_pol_map[loru][it->first] = pol; - }else{ - Trace("bound-int-debug") << "The term " << t << " has non-bound variable." << std::endl; - } - } - } - } - } - } - } - }else if( n.getKind()==MEMBER ){ - if( !pol && !hasNonBoundVar( q, n[1] ) ){ - std::vector< Node > bound_vars; - std::map< Node, bool > visited; - processMatchBoundVars( q, n[0], bound_vars, visited ); - for( unsigned i=0; iproxyCurrentRange() ){ - addedLemma = true; - } - } - Trace("bint-engine") << " addedLemma = " << addedLemma << std::endl; - } -} - - -void BoundedIntegers::addLiteralFromRange( Node lit, Node r ) { - d_lit_to_ranges[lit].push_back(r); - //check if it is already asserted? - if(d_assertions.find(lit)!=d_assertions.end()){ - d_rms[r]->assertNode( d_assertions[lit] ? lit : lit.negate() ); - } -} - -void BoundedIntegers::setBoundedVar( Node q, Node v, unsigned bound_type ) { - d_bound_type[q][v] = bound_type; - d_set_nums[q][v] = d_set[q].size(); - d_set[q].push_back( v ); - Trace("bound-int-var") << "Bound variable #" << d_set_nums[q][v] << " : " << v << std::endl; -} - -void BoundedIntegers::preRegisterQuantifier( Node f ) { - //this needs to be done at preregister since it affects e.g. QuantDSplit's preregister - Trace("bound-int") << "preRegister quantifier " << f << std::endl; - - bool success; - do{ - std::map< Node, unsigned > bound_lit_type_map; - std::map< int, std::map< Node, Node > > bound_lit_map; - std::map< int, std::map< Node, bool > > bound_lit_pol_map; - std::map< int, std::map< Node, Node > > bound_int_range_term; - std::map< Node, std::vector< Node > > bound_fixed_set; - success = false; - process( f, f[1], true, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term, bound_fixed_set ); - //for( std::map< Node, Node >::iterator it = d_bounds[0][f].begin(); it != d_bounds[0][f].end(); ++it ){ - for( std::map< Node, unsigned >::iterator it = bound_lit_type_map.begin(); it != bound_lit_type_map.end(); ++it ){ - Node v = it->first; - if( !isBound( f, v ) ){ - bool setBoundVar = false; - if( it->second==BOUND_INT_RANGE ){ - //must have both - if( bound_lit_map[0].find( v )!=bound_lit_map[0].end() && bound_lit_map[1].find( v )!=bound_lit_map[1].end() ){ - setBoundedVar( f, v, BOUND_INT_RANGE ); - setBoundVar = true; - for( unsigned b=0; b<2; b++ ){ - //set the bounds - Assert( bound_int_range_term[b].find( v )!=bound_int_range_term[b].end() ); - d_bounds[b][f][v] = bound_int_range_term[b][v]; - } - if( options::fmfBoundMinMode()==FMF_BOUND_MIN_ALL || options::fmfBoundMinMode()==FMF_BOUND_MIN_INT_RANGE ){ - Node r = NodeManager::currentNM()->mkNode( MINUS, d_bounds[1][f][v], d_bounds[0][f][v] ); - d_range[f][v] = Rewriter::rewrite( r ); - } - Trace("bound-int") << "Variable " << v << " is bound because of int range literals " << bound_lit_map[0][v] << " and " << bound_lit_map[1][v] << std::endl; - } - }else if( it->second==BOUND_SET_MEMBER ){ - setBoundedVar( f, v, BOUND_SET_MEMBER ); - setBoundVar = true; - d_setm_range[f][v] = bound_lit_map[2][v][1]; - d_setm_range_lit[f][v] = bound_lit_map[2][v]; - if( options::fmfBoundMinMode()==FMF_BOUND_MIN_ALL || options::fmfBoundMinMode()==FMF_BOUND_MIN_SET_CARD ){ - d_range[f][v] = NodeManager::currentNM()->mkNode( CARD, d_setm_range[f][v] ); - } - Trace("bound-int") << "Variable " << v << " is bound because of set membership literal " << bound_lit_map[2][v] << std::endl; - }else if( it->second==BOUND_FIXED_SET ){ - setBoundedVar( f, v, BOUND_FIXED_SET ); - setBoundVar = true; - for( unsigned i=0; isecond!=BOUND_INT_RANGE ); - } - } - } - } - } - if( !success ){ - //resort to setting a finite bound on a variable - for( unsigned i=0; igetTermEnumeration()->mayComplete(tn)) - { - success = true; - setBoundedVar( f, f[0][i], BOUND_FINITE ); - break; - } - } - } - } - }while( success ); - - if( Trace.isOn("bound-int") ){ - Trace("bound-int") << "Bounds are : " << std::endl; - for( unsigned i=0; i::iterator itr = d_range[f].find( v ); - if( itr != d_range[f].end() ){ - Node r = itr->second; - Assert( !r.isNull() ); - bool isProxy = false; - if( r.hasBoundVar() ){ - //introduce a new bound - Node new_range = NodeManager::currentNM()->mkSkolem( "bir", r.getType(), "bound for term" ); - d_nground_range[f][v] = r; - d_range[f][v] = new_range; - r = new_range; - isProxy = true; - } - if( !r.isConst() ){ - if( std::find(d_ranges.begin(), d_ranges.end(), r)==d_ranges.end() ){ - Trace("bound-int") << "For " << v << ", bounded Integer Module will try to minimize : " << r << std::endl; - d_ranges.push_back( r ); - d_rms[r] = new IntRangeModel( this, r, d_quantEngine->getSatContext(), d_quantEngine->getUserContext(), isProxy ); - d_rms[r]->initialize(); - } - } - } - } - } -} - -void BoundedIntegers::registerQuantifier( Node q ) { - -} - -void BoundedIntegers::assertNode( Node n ) { - Trace("bound-int-assert") << "Assert " << n << std::endl; - Node nlit = n.getKind()==NOT ? n[0] : n; - if( d_lit_to_ranges.find(nlit)!=d_lit_to_ranges.end() ){ - Trace("bound-int-assert") << "This is the bounding literal for " << d_lit_to_ranges[nlit].size() << " ranges." << std::endl; - for( unsigned i=0; iassertNode( n ); - } - } - d_assertions[nlit] = n.getKind()!=NOT; -} - -Node BoundedIntegers::getNextDecisionRequest( unsigned& priority ) { - Trace("bound-int-dec-debug") << "bi: Get next decision request?" << std::endl; - for( unsigned i=0; igetNextDecisionRequest(); - if (!d.isNull()) { - bool polLit = d.getKind()!=NOT; - Node lit = d.getKind()==NOT ? d[0] : d; - bool value; - if( d_quantEngine->getValuation().hasSatValue( lit, value ) ) { - if( value==polLit ){ - Trace("bound-int-dec-debug") << "...already asserted properly." << std::endl; - //already true, we're already fine - }else{ - Trace("bound-int-dec-debug") << "...already asserted with wrong polarity, re-assert." << std::endl; - assertNode( d.negate() ); - i--; - } - }else{ - priority = 1; - Trace("bound-int-dec") << "Bounded Integers : Decide " << d << std::endl; - return d; - } - } - } - Trace("bound-int-dec-debug") << "No decision request." << std::endl; - return Node::null(); -} - -unsigned BoundedIntegers::getBoundVarType( Node q, Node v ) { - std::map< Node, unsigned >::iterator it = d_bound_type[q].find( v ); - if( it==d_bound_type[q].end() ){ - return BOUND_NONE; - }else{ - return it->second; - } -} - -void BoundedIntegers::getBounds( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ) { - l = d_bounds[0][f][v]; - u = d_bounds[1][f][v]; - if( d_nground_range[f].find(v)!=d_nground_range[f].end() ){ - //get the substitution - std::vector< Node > vars; - std::vector< Node > subs; - if( getRsiSubsitution( f, v, vars, subs, rsi ) ){ - u = u.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); - l = l.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); - }else{ - u = Node::null(); - l = Node::null(); - } - } -} - -void BoundedIntegers::getBoundValues( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ) { - getBounds( f, v, rsi, l, u ); - Trace("bound-int-rsi") << "Get value in model for..." << l << " and " << u << std::endl; - if( !l.isNull() ){ - l = d_quantEngine->getModel()->getValue( l ); - } - if( !u.isNull() ){ - u = d_quantEngine->getModel()->getValue( u ); - } - Trace("bound-int-rsi") << "Value is " << l << " ... " << u << std::endl; - return; -} - -bool BoundedIntegers::isGroundRange( Node q, Node v ) { - if( isBoundVar(q,v) ){ - if( d_bound_type[q][v]==BOUND_INT_RANGE ){ - return !getLowerBound(q,v).hasBoundVar() && !getUpperBound(q,v).hasBoundVar(); - }else if( d_bound_type[q][v]==BOUND_SET_MEMBER ){ - return !d_setm_range[q][v].hasBoundVar(); - }else if( d_bound_type[q][v]==BOUND_FIXED_SET ){ - return !d_fixed_set_ngr_range[q][v].empty(); - } - } - return false; -} - -Node BoundedIntegers::getSetRange( Node q, Node v, RepSetIterator * rsi ) { - Node sr = d_setm_range[q][v]; - if( d_nground_range[q].find(v)!=d_nground_range[q].end() ){ - //get the substitution - std::vector< Node > vars; - std::vector< Node > subs; - if( getRsiSubsitution( q, v, vars, subs, rsi ) ){ - sr = sr.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); - }else{ - sr = Node::null(); - } - } - return sr; -} - -Node BoundedIntegers::getSetRangeValue( Node q, Node v, RepSetIterator * rsi ) { - Node sr = getSetRange( q, v, rsi ); - if( !sr.isNull() ){ - Trace("bound-int-rsi") << "Get value in model for..." << sr << std::endl; - sr = d_quantEngine->getModel()->getValue( sr ); - //if non-constant, then sr does not occur in the model, we fail - if( !sr.isConst() ){ - return Node::null(); - } - Trace("bound-int-rsi") << "Value is " << sr << std::endl; - //as heuristic, map to term model - if( sr.getKind()!=EMPTYSET ){ - std::map< Node, Node > val_to_term; - while( sr.getKind()==UNION ){ - Assert( sr[1].getKind()==kind::SINGLETON ); - val_to_term[ sr[1][0] ] = sr[1][0]; - sr = sr[0]; - } - Assert( sr.getKind()==kind::SINGLETON ); - val_to_term[ sr[0] ] = sr[0]; - //must look back at assertions, not term database (theory of sets introduces extraneous terms internally) - Theory* theory = d_quantEngine->getTheoryEngine()->theoryOf( THEORY_SETS ); - if( theory ){ - context::CDList::const_iterator it = theory->facts_begin(), it_end = theory->facts_end(); - for( unsigned i = 0; it != it_end; ++ it, ++i ){ - Node lit = (*it).assertion; - if( lit.getKind()==kind::MEMBER ){ - Node vr = d_quantEngine->getModel()->getValue( lit[0] ); - Trace("bound-int-rsi-debug") << "....membership for " << lit << " ==> " << vr << std::endl; - Trace("bound-int-rsi-debug") << " " << (val_to_term.find( vr )!=val_to_term.end()) << " " << d_quantEngine->getEqualityQuery()->areEqual( d_setm_range_lit[q][v][1], lit[1] ) << std::endl; - if( val_to_term.find( vr )!=val_to_term.end() ){ - if( d_quantEngine->getEqualityQuery()->areEqual( d_setm_range_lit[q][v][1], lit[1] ) ){ - Trace("bound-int-rsi") << " Map value to term : " << vr << " -> " << lit[0] << std::endl; - val_to_term[ vr ] = lit[0]; - } - } - } - } - } - //rebuild value - Node nsr; - for( std::map< Node, Node >::iterator it = val_to_term.begin(); it != val_to_term.end(); ++it ){ - Node nv = NodeManager::currentNM()->mkNode( kind::SINGLETON, it->second ); - if( nsr.isNull() ){ - nsr = nv; - }else{ - nsr = NodeManager::currentNM()->mkNode( kind::UNION, nsr, nv ); - } - } - Trace("bound-int-rsi") << "...reconstructed " << nsr << std::endl; - return nsr; - - /* - Node lit = d_setm_range_lit[q][v]; - Trace("bound-int-rsi-debug") << "Bounded from lit " << lit << std::endl; - Node f = d_quantEngine->getTermDatabase()->getMatchOperator( lit ); - TermArgTrie * ta = d_quantEngine->getTermDatabase()->getTermArgTrie( f ); - if( ta ){ - Trace("bound-int-rsi-debug") << "Got term index for " << f << std::endl; - for( std::map< TNode, TermArgTrie >::iterator it = ta->d_data.begin(); it != ta->d_data.end(); ++it ){ - - } - - } - */ - } - - } - return sr; -} - -bool BoundedIntegers::getRsiSubsitution( Node q, Node v, std::vector< Node >& vars, std::vector< Node >& subs, RepSetIterator * rsi ) { - - Trace("bound-int-rsi") << "Get bound value in model of variable " << v << std::endl; - Assert( d_set_nums[q].find( v )!=d_set_nums[q].end() ); - int vindex = d_set_nums[q][v]; - Assert( d_set_nums[q][v]==vindex ); - Trace("bound-int-rsi-debug") << " index order is " << vindex << std::endl; - //must take substitution for all variables that are iterating at higher level - for( int i=0; igetVariableOrder( i ); - Assert( q[0][v]==d_set[q][i] ); - Node t = rsi->getCurrentTerm(v, true); - Trace("bound-int-rsi") << "term : " << t << std::endl; - vars.push_back( d_set[q][i] ); - subs.push_back( t ); - } - - //check if it has been instantiated - if( !vars.empty() && !d_bnd_it[q][v].hasInstantiated(subs) ){ - if( d_bound_type[q][v]==BOUND_INT_RANGE || d_bound_type[q][v]==BOUND_SET_MEMBER ){ - //must add the lemma - Node nn = d_nground_range[q][v]; - nn = nn.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); - Node lem = NodeManager::currentNM()->mkNode( LEQ, nn, d_range[q][v] ); - Trace("bound-int-lemma") << "*** Add lemma to minimize instantiated non-ground term " << lem << std::endl; - d_quantEngine->getOutputChannel().lemma(lem, false, true); - } - return false; - }else{ - return true; - } -} - -Node BoundedIntegers::matchBoundVar( Node v, Node t, Node e ){ - if( t==v ){ - return e; - }else if( t.getKind()==kind::APPLY_CONSTRUCTOR ){ - if( e.getKind()==kind::APPLY_CONSTRUCTOR ){ - if( t.getOperator() != e.getOperator() ) { - return Node::null(); - } - } - const Datatype& dt = Datatype::datatypeOf( t.getOperator().toExpr() ); - unsigned index = Datatype::indexOf( t.getOperator().toExpr() ); - for( unsigned i=0; imkNode( kind::APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[index].getSelectorInternal( e.getType().toType(), i ) ), e ); - u = matchBoundVar( v, t[i], se ); - } - if( !u.isNull() ){ - return u; - } - } - } - return Node::null(); -} - -bool BoundedIntegers::getBoundElements( RepSetIterator * rsi, bool initial, Node q, Node v, std::vector< Node >& elements ) { - if( initial || !isGroundRange( q, v ) ){ - elements.clear(); - unsigned bvt = getBoundVarType( q, v ); - if( bvt==BOUND_INT_RANGE ){ - Node l, u; - getBoundValues( q, v, rsi, l, u ); - if( l.isNull() || u.isNull() ){ - Trace("bound-int-warn") << "WARNING: Could not find integer bounds in model for " << v << " in " << q << std::endl; - //failed, abort the iterator - return false; - }else{ - Trace("bound-int-rsi") << "Can limit bounds of " << v << " to " << l << "..." << u << std::endl; - Node range = Rewriter::rewrite( NodeManager::currentNM()->mkNode( MINUS, u, l ) ); - Node ra = Rewriter::rewrite( NodeManager::currentNM()->mkNode( LEQ, range, NodeManager::currentNM()->mkConst( Rational( 9999 ) ) ) ); - Node tl = l; - Node tu = u; - getBounds( q, v, rsi, tl, tu ); - Assert( !tl.isNull() && !tu.isNull() ); - if( ra==d_quantEngine->getTermUtil()->d_true ){ - long rr = range.getConst().getNumerator().getLong()+1; - Trace("bound-int-rsi") << "Actual bound range is " << rr << std::endl; - for( unsigned k=0; kmkNode(PLUS, tl, NodeManager::currentNM()->mkConst( Rational(k) ) ); - t = Rewriter::rewrite( t ); - elements.push_back( t ); - } - return true; - }else{ - Trace("fmf-incomplete") << "Incomplete because of integer quantification, bounds are too big for " << v << "." << std::endl; - return false; - } - } - }else if( bvt==BOUND_SET_MEMBER ){ - Node srv = getSetRangeValue( q, v, rsi ); - if( srv.isNull() ){ - Trace("bound-int-warn") << "WARNING: Could not find set bound in model for " << v << " in " << q << std::endl; - return false; - }else{ - Trace("bound-int-rsi") << "Bounded by set membership : " << srv << std::endl; - if( srv.getKind()!=EMPTYSET ){ - //collect the elements - while( srv.getKind()==UNION ){ - Assert( srv[1].getKind()==kind::SINGLETON ); - elements.push_back( srv[1][0] ); - srv = srv[0]; - } - Assert( srv.getKind()==kind::SINGLETON ); - elements.push_back( srv[0] ); - //check if we need to do matching, for literals like ( tuple( v ) in S ) - Node t = d_setm_range_lit[q][v][0]; - if( t!=v ){ - std::vector< Node > elements_tmp; - elements_tmp.insert( elements_tmp.end(), elements.begin(), elements.end() ); - elements.clear(); - for( unsigned i=0; i u - Node u = matchBoundVar( v, t, elements_tmp[i] ); - Trace("bound-int-rsi-debug") << " unification : " << elements_tmp[i] << " = " << t << " yields " << v << " -> " << u << std::endl; - if( !u.isNull() ){ - elements.push_back( u ); - } - } - } - } - return true; - } - }else if( bvt==BOUND_FIXED_SET ){ - std::map< Node, std::vector< Node > >::iterator it = d_fixed_set_gr_range[q].find( v ); - if( it!=d_fixed_set_gr_range[q].end() ){ - for( unsigned i=0; isecond.size(); i++ ){ - elements.push_back( it->second[i] ); - } - } - it = d_fixed_set_ngr_range[q].find( v ); - if( it!=d_fixed_set_ngr_range[q].end() ){ - std::vector< Node > vars; - std::vector< Node > subs; - if( getRsiSubsitution( q, v, vars, subs, rsi ) ){ - for( unsigned i=0; isecond.size(); i++ ){ - Node t = it->second[i].substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); - elements.push_back( t ); - } - return true; - }else{ - return false; - } - }else{ - return true; - } - }else{ - return false; - } - }else{ - //no change required - return true; - } -} - diff --git a/src/theory/quantifiers/bounded_integers.h b/src/theory/quantifiers/bounded_integers.h deleted file mode 100644 index 99d77a8e7..000000000 --- a/src/theory/quantifiers/bounded_integers.h +++ /dev/null @@ -1,181 +0,0 @@ -/********************* */ -/*! \file bounded_integers.h - ** \verbatim - ** Top contributors (to current version): - ** Morgan Deters, 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 - ** - ** [[ Add lengthier description here ]] - ** \todo document this file -**/ - -#include "cvc4_private.h" - -#ifndef __CVC4__BOUNDED_INTEGERS_H -#define __CVC4__BOUNDED_INTEGERS_H - - -#include "theory/quantifiers_engine.h" - -#include "context/context.h" -#include "context/context_mm.h" - -namespace CVC4 { -namespace theory { - -class RepSetIterator; - -namespace quantifiers { - - -class BoundedIntegers : public QuantifiersModule -{ - typedef context::CDHashMap NodeBoolMap; - typedef context::CDHashMap NodeIntMap; - typedef context::CDHashMap NodeNodeMap; - typedef context::CDHashMap IntBoolMap; -public: - enum { - BOUND_FINITE, - BOUND_INT_RANGE, - BOUND_SET_MEMBER, - BOUND_FIXED_SET, - BOUND_NONE - }; -private: - //for determining bounds - bool isBound( Node f, Node v ); - bool hasNonBoundVar( Node f, Node b, std::map< Node, bool >& visited ); - bool hasNonBoundVar( Node f, Node b ); - //bound type - std::map< Node, std::map< Node, unsigned > > d_bound_type; - std::map< Node, std::vector< Node > > d_set; - std::map< Node, std::map< Node, int > > d_set_nums; - std::map< Node, std::map< Node, Node > > d_range; - std::map< Node, std::map< Node, Node > > d_nground_range; - //integer lower/upper bounds - std::map< Node, std::map< Node, Node > > d_bounds[2]; - //set membership range - std::map< Node, std::map< Node, Node > > d_setm_range; - std::map< Node, std::map< Node, Node > > d_setm_range_lit; - //fixed finite set range - std::map< Node, std::map< Node, std::vector< Node > > > d_fixed_set_gr_range; - std::map< Node, std::map< Node, std::vector< Node > > > d_fixed_set_ngr_range; - void hasFreeVar( Node f, Node n ); - void process( Node q, Node n, bool pol, - std::map< Node, unsigned >& bound_lit_type_map, - std::map< int, std::map< Node, Node > >& bound_lit_map, - std::map< int, std::map< Node, bool > >& bound_lit_pol_map, - std::map< int, std::map< Node, Node > >& bound_int_range_term, - std::map< Node, std::vector< Node > >& bound_fixed_set ); - bool processEqDisjunct( Node q, Node n, Node& v, std::vector< Node >& v_cases ); - void processMatchBoundVars( Node q, Node n, std::vector< Node >& bvs, std::map< Node, bool >& visited ); - std::vector< Node > d_bound_quants; -private: - class RangeModel { - public: - RangeModel(){} - virtual ~RangeModel(){} - virtual void initialize() = 0; - virtual void assertNode(Node n) = 0; - virtual Node getNextDecisionRequest() = 0; - virtual bool proxyCurrentRange() = 0; - }; - class IntRangeModel : public RangeModel { - private: - BoundedIntegers * d_bi; - void allocateRange(); - Node d_proxy_range; - public: - IntRangeModel( BoundedIntegers * bi, Node r, context::Context* c, context::Context* u, bool isProxy); - virtual ~IntRangeModel(){} - Node d_range; - int d_curr_max; - std::map< int, Node > d_range_literal; - std::map< Node, bool > d_lit_to_pol; - NodeIntMap d_lit_to_range; - NodeBoolMap d_range_assertions; - context::CDO< bool > d_has_range; - context::CDO< int > d_curr_range; - IntBoolMap d_ranges_proxied; - void initialize(); - void assertNode(Node n); - Node getNextDecisionRequest(); - bool proxyCurrentRange(); - }; -private: - //information for minimizing ranges - std::vector< Node > d_ranges; - //map to range model objects - std::map< Node, RangeModel * > d_rms; - //literal to range - std::map< Node, std::vector< Node > > d_lit_to_ranges; - //list of currently asserted arithmetic literals - NodeBoolMap d_assertions; -private: - //class to store whether bounding lemmas have been added - class BoundInstTrie - { - public: - std::map< Node, BoundInstTrie > d_children; - bool hasInstantiated( std::vector< Node > & vals, int index = 0, bool madeNew = false ){ - if( index>=(int)vals.size() ){ - return !madeNew; - }else{ - Node n = vals[index]; - if( d_children.find(n)==d_children.end() ){ - madeNew = true; - } - return d_children[n].hasInstantiated(vals,index+1,madeNew); - } - } - }; - std::map< Node, std::map< Node, BoundInstTrie > > d_bnd_it; -private: - void addLiteralFromRange( Node lit, Node r ); - - void setBoundedVar( Node f, Node v, unsigned bound_type ); -public: - BoundedIntegers( context::Context* c, QuantifiersEngine* qe ); - virtual ~BoundedIntegers(); - - void presolve(); - bool needsCheck( Theory::Effort e ); - void check(Theory::Effort e, QEffort quant_e); - void registerQuantifier( Node q ); - void preRegisterQuantifier( Node q ); - void assertNode( Node n ); - Node getNextDecisionRequest( unsigned& priority ); - bool isBoundVar( Node q, Node v ) { return std::find( d_set[q].begin(), d_set[q].end(), v )!=d_set[q].end(); } - unsigned getBoundVarType( Node q, Node v ); - unsigned getNumBoundVars( Node q ) { return d_set[q].size(); } - Node getBoundVar( Node q, int i ) { return d_set[q][i]; } -private: - //for integer range - Node getLowerBound( Node q, Node v ){ return d_bounds[0][q][v]; } - Node getUpperBound( Node q, Node v ){ return d_bounds[1][q][v]; } - void getBounds( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ); - void getBoundValues( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ); - bool isGroundRange(Node f, Node v); - //for set range - Node getSetRange( Node q, Node v, RepSetIterator * rsi ); - Node getSetRangeValue( Node q, Node v, RepSetIterator * rsi ); - Node matchBoundVar( Node v, Node t, Node e ); - - bool getRsiSubsitution( Node q, Node v, std::vector< Node >& vars, std::vector< Node >& subs, RepSetIterator * rsi ); -public: - bool getBoundElements( RepSetIterator * rsi, bool initial, Node q, Node v, std::vector< Node >& elements ); - - /** Identify this module */ - std::string identify() const { return "BoundedIntegers"; } -}; - -} -} -} - -#endif diff --git a/src/theory/quantifiers/ce_guided_conjecture.cpp b/src/theory/quantifiers/ce_guided_conjecture.cpp deleted file mode 100644 index 0ce9b7c72..000000000 --- a/src/theory/quantifiers/ce_guided_conjecture.cpp +++ /dev/null @@ -1,894 +0,0 @@ -/********************* */ -/*! \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/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/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/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; ipreSimplify(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; igetTemplate(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; imkSkolem( "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() ); - //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; kgetQuantAttributes()->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; igetOutputChannel().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; imkNode( 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[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; igetSkolemize()->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 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 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; kmkNode( 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 "; - 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; imkNode(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; igetTermDatabaseSygus() - ->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 sols; - std::vector 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(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::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& sol_map, - bool singleInvocation) -{ - NodeManager* nm = NodeManager::currentNM(); - TermDbSygus* sygusDb = d_qe->getTermDatabaseSygus(); - std::vector sols; - std::vector 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(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& sols, - std::vector& 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 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& vals, - std::vector& 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 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()) - { - 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 vars; - std::vector 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/ce_guided_conjecture.h b/src/theory/quantifiers/ce_guided_conjecture.h deleted file mode 100644 index ebcecbe0f..000000000 --- a/src/theory/quantifiers/ce_guided_conjecture.h +++ /dev/null @@ -1,283 +0,0 @@ -/********************* */ -/*! \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 - -#include "theory/quantifiers/ce_guided_pbe.h" -#include "theory/quantifiers/ce_guided_single_inv.h" -#include "theory/quantifiers/sygus_grammar_cons.h" -#include "theory/quantifiers/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& 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& vals, - std::vector& 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 d_ceg_si; - /** program by examples utility */ - std::unique_ptr d_ceg_pbe; - /** utility for static preprocessing and analysis of conjectures */ - std::unique_ptr d_ceg_proc; - /** grammar utility */ - std::unique_ptr 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 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; ig(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& sols, - std::vector& 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 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 d_cegis_sample_refine; -}; - -} /* namespace CVC4::theory::quantifiers */ -} /* namespace CVC4::theory */ -} /* namespace CVC4 */ - -#endif diff --git a/src/theory/quantifiers/ce_guided_instantiation.cpp b/src/theory/quantifiers/ce_guided_instantiation.cpp deleted file mode 100644 index ea2a2d13a..000000000 --- a/src/theory/quantifiers/ce_guided_instantiation.cpp +++ /dev/null @@ -1,388 +0,0 @@ -/********************* */ -/*! \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/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/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; iaddLemma( 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; jaddLemma( 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; jaddLemma( 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 " << 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; jmkNode( 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; iaddLemma( 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; iaddLemma( 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; igetTermUtil()->d_false ){ - std::vector< Node > msu; - std::vector< Node > mexp; - msu.insert( msu.end(), ms.begin(), ms.end() ); - for( unsigned k=0; kisAssigned() ) - { - d_conj->printSynthSolution( out, d_last_inst_si ); - } - else - { - Assert( false ); - } -} - -void CegInstantiation::getSynthSolutions(std::map& 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/ce_guided_instantiation.h b/src/theory/quantifiers/ce_guided_instantiation.h deleted file mode 100644 index 2dc74dc72..000000000 --- a/src/theory/quantifiers/ce_guided_instantiation.h +++ /dev/null @@ -1,90 +0,0 @@ -/********************* */ -/*! \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/ce_guided_conjecture.h" -#include "theory/quantifiers_engine.h" - -namespace CVC4 { -namespace theory { -namespace quantifiers { - -class CegInstantiation : public QuantifiersModule -{ - typedef context::CDHashMap 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& 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/ce_guided_pbe.cpp b/src/theory/quantifiers/ce_guided_pbe.cpp deleted file mode 100644 index 7f339be5f..000000000 --- a/src/theory/quantifiers/ce_guided_pbe.cpp +++ /dev/null @@ -1,2460 +0,0 @@ -/********************* */ -/*! \file ce_guided_pbe.cpp - ** \verbatim - ** Top contributors (to current version): - ** Andrew Reynolds - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2016 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief utility for processing programming by examples synthesis conjectures - ** - **/ -#include "theory/quantifiers/ce_guided_pbe.h" - -#include "expr/datatype.h" -#include "options/quantifiers_options.h" -#include "theory/quantifiers/term_database_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& vals, bool pol = true ){ - if( Trace.isOn(c) ){ - for( unsigned i=0; i() : !vals[i].getConst() ) ? "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(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(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(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& candidates, - std::vector& lemmas) -{ - Trace("sygus-pbe") << "Initialize PBE : " << n << std::endl; - - for( unsigned i=0; i visited; - collectExamples( n, visited, true, true ); - - for( unsigned i=0; i " << 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; igetTermDatabaseSygus()->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 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& 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::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 > >::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& ex) { - e = d_tds->getSynthFunForEnumerator(e); - Assert(!e.isNull()); - std::map > >::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 >::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::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::iterator itx = d_examples_invalid.find(e); - if (itx == d_examples_invalid.end()) { - std::map > >::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::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::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::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(tn.toType()).getDatatype(); - Assert(dt.isSygus()); - - std::map > cop_to_strat; - std::map cop_to_cindex; - std::map > cop_to_child_templ; - std::map > cop_to_child_templ_arg; - std::map > cop_to_carg_list; - std::map > cop_to_child_types; - std::map > 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 utchildren; - utchildren.push_back(cop); - std::vector sks; - std::vector 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 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 templ_injection; - std::vector vs; - std::vector ss; - std::map templ_var_index; - for (unsigned k = 0, sksize = sks.size(); k < sksize; k++) - { - Assert(sks[k].getType().isDatatype()); - const Datatype& cdt = - static_cast(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 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 > assoc_combine; - std::vector assoc_waiting; - int assoc_last_valid_index = -1; - for (std::pair& 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::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& 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& ac = assoc_combine[k]; - Assert(!ac.empty()); - std::vector 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(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 >& 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& childTypes = cop_to_child_types[cop]; - Assert(cop_to_carg_list.find(cop) != cop_to_carg_list.end()); - std::vector& cargList = cop_to_carg_list[cop]; - - std::vector 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(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& lemmas ) { - for( unsigned i=0; i::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; jsecond.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 > visited; - std::map > needs_cons; - staticLearnRedundantOps(c, - d_cinfo[c].getRootEnumerator(), - role_equal, - visited, - needs_cons, - 0, - false); - // now, check the needs_cons map - for (std::pair >& nce : needs_cons) - { - Node em = nce.first; - const Datatype& dt = - static_cast(em.getType().toType()).getDatatype(); - for (std::pair& 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 >& visited, - std::map >& 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::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 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(cindex)] = false; - for (std::pair& 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::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(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::iterator it = d_cinfo.find( v ); - if( it!=d_cinfo.end() ){ - for( unsigned j=0; jsecond.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()) - { - 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 " << enum_values[i] << std::endl; - unsigned sz = d_tds->getSygusTermSize( enum_values[i] ); - if( i==0 || sz& 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()) - { - 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::iterator itc = d_cinfo.find(c); - Assert(itc != d_cinfo.end()); - TypeNode xtn = x.getType(); - Node bv = d_tds->sygusToBuiltin(v, xtn); - std::map > >::iterator itx = - d_examples.find(c); - std::map >::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 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 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; ssecond.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::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::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& results, - EnumInfo& ei, - std::vector& 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 >::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 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; id_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::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; id_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; id_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 " << curr_index << std::endl; - unsigned i = curr_index; - while( i " << i << std::endl; - curr_index = i; - } - - // verify it is correct - unsigned j = start(); - for( unsigned k=0; kj ); - for( unsigned k=(j+1); k::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::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 )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::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 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::iterator itet; - std::map::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 >::iterator itx = - d_examples_out.find(c); - Assert(itx != d_examples_out.end()); - std::vector 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 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 > incr; - bool isPrefix = nrole == role_string_prefix; - std::map total_inc; - std::vector inc_strs; - std::map >::iterator itx = d_examples_out.find(c); - Assert(itx != d_examples_out.end()); - // make the value of the examples - std::vector 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::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 look_ahead_solved_children; - std::vector 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::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& cenum = etis->d_cenum[sc]; - - // update the context - std::vector prev; - if (strat == strat_ITE && sc > 0) - { - std::map::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::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& te_pair = etis->d_cenum[i]; - Node te = te_pair.first; - std::map::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 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 > possible_cond; - std::map solved_cond; // stores branch - einfo_child.d_term_trie.getLeaves( - this, x.d_vals, true, possible_cond); - - std::map >::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 > 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 >::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 >::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& 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; id_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; i0 ){ - 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; id_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& vals, - std::vector& ex_vals) -{ - bool isPrefix = d_has_string_pos == role_string_prefix; - String dummy; - for( unsigned i=0; id_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(); - 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() ); - } - }else{ - // irrelevant, add dummy - ex_vals.push_back( dummy ); - } - } -} - -bool CegConjecturePbe::UnifContext::getStringIncrement( - CegConjecturePbe* pbe, - bool isPrefix, - const std::vector& ex_vals, - const std::vector& vals, - std::vector& inc, - unsigned& tot) -{ - for( unsigned j=0; jd_true ){ - // example is active in this context - Assert( vals[j].isConst() ); - String mystr = vals[j].getConst(); - 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& ex_vals, - const std::vector& vals) -{ - for( unsigned j=0; jd_true ){ - // example is active in this context - Assert( vals[j].isConst() ); - String mystr = vals[j].getConst(); - 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/ce_guided_pbe.h b/src/theory/quantifiers/ce_guided_pbe.h deleted file mode 100644 index ce1f2bf5e..000000000 --- a/src/theory/quantifiers/ce_guided_pbe.h +++ /dev/null @@ -1,802 +0,0 @@ -/********************* */ -/*! \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& candidates, - std::vector& lemmas); - /** get candidate list - * Adds all active enumerators associated with functions-to-synthesize in - * candidates to clist. - */ - void getCandidateList(std::vector& candidates, - std::vector& 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& enums, - std::vector& enum_values, - std::vector& candidates, - std::vector& candidate_values, - std::vector& 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& 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 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& 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 > 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 d_children; - /** helper function for above functions */ - Node addTermInternal(CegConjecturePbe* pbe, - Node t, - std::vector& vals, - bool pol, - std::vector& subsumed, - bool spol, - IndexFilter* f, - unsigned index, - int status, - bool checkExistsOnly, - bool checkSubsume); - /** helper function for above functions */ - void getLeavesInternal(CegConjecturePbe* pbe, - std::vector& vals, - bool pol, - std::map >& 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& 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 d_enum_slave; - /** values we have enumerated */ - std::vector 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 > d_enum_vals_res; - std::vector d_enum_subsume; - std::map 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 > d_cenum; - /** the arguments for the (templated) solution */ - std::vector 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 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 d_enum; - /** map from node roles to strategy nodes */ - std::map 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 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& templ_var_index, - std::map& 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& 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 >& visited, - std::map >& 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 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& 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 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& 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& vals, - std::vector& 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& ex_vals, - const std::vector& vals, - std::vector& inc, - unsigned& tot); - /** returns true if ex_vals[i] = vals[i] for all active indices i. */ - bool isStringSolved(CegConjecturePbe* pbe, - const std::vector& ex_vals, - const std::vector& 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 > 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 > 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/ce_guided_single_inv.cpp b/src/theory/quantifiers/ce_guided_single_inv.cpp deleted file mode 100644 index b2b4fe18c..000000000 --- a/src/theory/quantifiers/ce_guided_single_inv.cpp +++ /dev/null @@ -1,1004 +0,0 @@ -/********************* */ -/*! \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/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; imkSkolem( 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; iinit( 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 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 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; jmkNode( 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 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 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; jgetFunctionForFirstOrderVariable(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::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; imkNode( 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; id_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; id_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( indexsecond.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& vars ) { - return d_trie.constructFormula( vars ); -} - - -void DetTrace::print( const char* c ) { - for( unsigned i=0; i& 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 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 " << 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 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 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; imkSkolem( "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; jmkConst( 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_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; imkSkolem( "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 >::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; isecond.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()==( 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() ){ - 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; isecond.find( pv )!=it->second.end() ); - Node pvs = it->second[pv]; - Assert( d_vars.size()==dt.d_curr.size() ); - Node pvsr = Rewriter::rewrite( pvs.substitute( d_vars.begin(), d_vars.end(), dt.d_curr.begin(), dt.d_curr.end() ) ); - next.push_back( pvsr ); - } - if( dt.increment( loc, next ) ){ - Trace("cegqi-inv-debug2") << "dtrace : success increment" << std::endl; - return 0; - }else{ - // looped - Trace("cegqi-inv-debug2") << "dtrace : looped" << std::endl; - return 1; - } - } - }else{ - //TODO - } - return -1; -} - -int TransitionInference::initializeTrace( DetTrace& dt, bool fwd ) { - Trace("cegqi-inv-debug2") << "Initialize trace" << std::endl; - int index = fwd ? 1 : -1; - if( d_com[index].d_conjuncts.size()==1 ){ - return initializeTrace( dt, d_com[index].d_conjuncts[0], fwd ); - }else{ - return -1; - } -} - -int TransitionInference::incrementTrace( DetTrace& dt, bool fwd ) { - if( d_com[0].d_conjuncts.size()==1 ){ - return incrementTrace( dt, d_com[0].d_conjuncts[0], fwd ); - }else{ - return -1; - } -} - -Node TransitionInference::constructFormulaTrace( DetTrace& dt ) { - return dt.constructFormula( d_vars ); -} - -} //namespace CVC4 - diff --git a/src/theory/quantifiers/ce_guided_single_inv.h b/src/theory/quantifiers/ce_guided_single_inv.h deleted file mode 100644 index 888e078af..000000000 --- a/src/theory/quantifiers/ce_guided_single_inv.h +++ /dev/null @@ -1,248 +0,0 @@ -/********************* */ -/*! \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/ce_guided_single_inv_sol.h" -#include "theory/quantifiers/inst_match_trie.h" -#include "theory/quantifiers/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& indices, unsigned i, - unsigned index, std::map& 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 d_single_inv_arg_sk; - // list of variables/skolems for each program - std::vector d_single_inv_var; - std::vector d_single_inv_sk; - std::map d_single_inv_sk_index; - // program to solution index - std::map 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 d_lemmas_produced; - std::vector > d_inst; - - private: - std::vector 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::const_iterator location = d_trans_pre.find(prog); - return location->second; - } - - Node getTransPost(Node prog) const { - std::map::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::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::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/ce_guided_single_inv_sol.cpp b/src/theory/quantifiers/ce_guided_single_inv_sol.cpp deleted file mode 100644 index 74408a7c3..000000000 --- a/src/theory/quantifiers/ce_guided_single_inv_sol.cpp +++ /dev/null @@ -1,1512 +0,0 @@ -/********************* */ -/*! \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/ce_guided_single_inv_sol.h" - -#include "expr/datatype.h" -#include "options/quantifiers_options.h" -#include "theory/quantifiers/ce_guided_instantiation.h" -#include "theory/quantifiers/ce_guided_single_inv.h" -#include "theory/quantifiers/first_order_model.h" -#include "theory/quantifiers/quantifiers_attributes.h" -#include "theory/quantifiers/term_database_sygus.h" -#include "theory/quantifiers/term_enumeration.h" -#include "theory/quantifiers/term_util.h" -#include "theory/quantifiers/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(); -} - -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::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; isecond; - 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 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; imkNode( ITE, n0[0], n2, n1 ); - }else if( n0.getKind()==AND || n0.getKind()==OR ){ - std::vector< Node > children; - for( unsigned i=1; imkNode( 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; imkNode( 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& 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; igetTermDatabaseSygus()->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; imkNode( 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::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 final_children; - for( unsigned i=0; i 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; ji || 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; imkNode( 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 >::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::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; imkNode( 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 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 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; igetTermDatabaseSygus()->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 *** 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; igetTermDatabaseSygus()->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; igetArgType( 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; isecond.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::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& 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().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().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::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(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(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& s, - std::vector& 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 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& args, - int index_exc, - int index_start) -{ - Assert(st.isDatatype()); - const Datatype& dt = static_cast(st.toType()).getDatatype(); - Assert(dt.isSygus()); - std::map > kgens; - std::vector 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 sigma; - std::vector 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::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 var_count; - std::map 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/ce_guided_single_inv_sol.h b/src/theory/quantifiers/ce_guided_single_inv_sol.h deleted file mode 100644 index 7043e1ecf..000000000 --- a/src/theory/quantifiers/ce_guided_single_inv_sol.h +++ /dev/null @@ -1,191 +0,0 @@ -/********************* */ -/*! \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 > d_builtin_const_to_sygus; - /** sorted list of constants, per type */ - std::map > d_const_list; - /** number of positive constants, per type */ - std::map d_const_list_pos; - /** list of constructor indices whose operators are identity functions */ - std::map > 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 > 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& s, - std::vector& 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& args, - int index_exc = -1, - int index_start = 0); -}; - - -} -} -} - -#endif diff --git a/src/theory/quantifiers/ceg_instantiator.cpp b/src/theory/quantifiers/ceg_instantiator.cpp deleted file mode 100644 index 0e8b229a3..000000000 --- a/src/theory/quantifiers/ceg_instantiator.cpp +++ /dev/null @@ -1,1337 +0,0 @@ -/********************* */ -/*! \file ceg_instantiator.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 Implementation of counterexample-guided quantifier instantiation - **/ - -#include "theory/quantifiers/ceg_instantiator.h" -#include "theory/quantifiers/ceg_t_instantiator.h" - -#include "options/quantifiers_options.h" -#include "smt/term_formula_removal.h" -#include "theory/arith/arith_msum.h" -#include "theory/quantifiers/first_order_model.h" -#include "theory/quantifiers/quantifiers_rewriter.h" -#include "theory/quantifiers/term_database.h" -#include "theory/quantifiers/term_enumeration.h" -#include "theory/quantifiers/term_util.h" -#include "theory/quantifiers/trigger.h" -#include "theory/theory_engine.h" - -using namespace std; -using namespace CVC4::kind; -using namespace CVC4::context; - -namespace CVC4 { -namespace theory { -namespace quantifiers { - -std::ostream& operator<<(std::ostream& os, CegInstEffort e) -{ - switch (e) - { - case CEG_INST_EFFORT_NONE: os << "?"; break; - case CEG_INST_EFFORT_STANDARD: os << "STANDARD"; break; - case CEG_INST_EFFORT_STANDARD_MV: os << "STANDARD_MV"; break; - case CEG_INST_EFFORT_FULL: os << "FULL"; break; - default: Unreachable(); - } - return os; -} - -std::ostream& operator<<(std::ostream& os, CegInstPhase phase) -{ - switch (phase) - { - case CEG_INST_PHASE_NONE: os << "?"; break; - case CEG_INST_PHASE_EQC: os << "eqc"; break; - case CEG_INST_PHASE_EQUAL: os << "eq"; break; - case CEG_INST_PHASE_ASSERTION: os << "as"; break; - case CEG_INST_PHASE_MVALUE: os << "mv"; break; - default: Unreachable(); - } - return os; -} - -CegInstantiator::CegInstantiator(QuantifiersEngine* qe, - CegqiOutput* out, - bool use_vts_delta, - bool use_vts_inf) - : d_qe(qe), - d_out(out), - d_use_vts_delta(use_vts_delta), - d_use_vts_inf(use_vts_inf), - d_is_nested_quant(false), - d_effort(CEG_INST_EFFORT_NONE) -{ -} - -CegInstantiator::~CegInstantiator() { - for (std::pair inst : d_instantiator) - { - delete inst.second; - } - for (std::pair instp : d_tipp) - { - delete instp.second; - } -} - -void CegInstantiator::computeProgVars( Node n ){ - if( d_prog_var.find( n )==d_prog_var.end() ){ - d_prog_var[n].clear(); - if (n.getKind() == kind::CHOICE) - { - Assert(d_prog_var.find(n[0][0]) == d_prog_var.end()); - d_prog_var[n[0][0]].clear(); - } - if (d_vars_set.find(n) != d_vars_set.end()) - { - d_prog_var[n].insert(n); - }else if( !d_out->isEligibleForInstantiation( n ) ){ - d_inelig.insert(n); - return; - } - for( unsigned i=0; i& visited) -{ - if (visited.find(tn) == visited.end()) - { - visited[tn] = true; - TheoryId tid = Theory::theoryOf(tn); - registerTheoryId(tid); - if (tn.isDatatype()) - { - const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); - for (unsigned i = 0; i < dt.getNumConstructors(); i++) - { - for (unsigned j = 0; j < dt[i].getNumArgs(); j++) - { - registerTheoryIds( - TypeNode::fromType( - ((SelectorType)dt[i][j].getType()).getRangeType()), - visited); - } - } - } - } -} - -void CegInstantiator::registerTheoryId(TheoryId tid) -{ - if (std::find(d_tids.begin(), d_tids.end(), tid) == d_tids.end()) - { - // setup any theory-specific preprocessors here - if (tid == THEORY_BV) - { - d_tipp[tid] = new BvInstantiatorPreprocess; - } - d_tids.push_back(tid); - } -} - -void CegInstantiator::registerVariable(Node v, bool is_aux) -{ - Assert(std::find(d_vars.begin(), d_vars.end(), v) == d_vars.end()); - Assert(std::find(d_aux_vars.begin(), d_aux_vars.end(), v) - == d_aux_vars.end()); - if (!is_aux) - { - d_vars.push_back(v); - d_vars_set.insert(v); - } - else - { - d_aux_vars.push_back(v); - } - TypeNode vtn = v.getType(); - Trace("cbqi-proc-debug") << "Collect theory ids from type " << vtn << " of " - << v << std::endl; - // collect relevant theories for this variable - std::map visited; - registerTheoryIds(vtn, visited); -} - -void CegInstantiator::deactivateInstantiationVariable(Node v) -{ - d_curr_subs_proc.erase( v ); - d_curr_index.erase( v ); - d_curr_iphase.erase(v); -} - -bool CegInstantiator::constructInstantiation(SolvedForm& sf, unsigned i) -{ - if( i==d_vars.size() ){ - //solved for all variables, now construct instantiation - bool needsPostprocess = - sf.d_vars.size() > d_input_vars.size() || !d_var_order_index.empty(); - std::vector< Instantiator * > pp_inst; - std::map< Instantiator *, Node > pp_inst_to_var; - std::vector< Node > lemmas; - for( std::map< Node, Instantiator * >::iterator ita = d_active_instantiators.begin(); ita != d_active_instantiators.end(); ++ita ){ - if (ita->second->needsPostProcessInstantiationForVariable( - this, sf, ita->first, d_effort)) - { - needsPostprocess = true; - pp_inst_to_var[ ita->second ] = ita->first; - } - } - if( needsPostprocess ){ - //must make copy so that backtracking reverts sf - SolvedForm sf_tmp = sf; - bool postProcessSuccess = true; - for( std::map< Instantiator *, Node >::iterator itp = pp_inst_to_var.begin(); itp != pp_inst_to_var.end(); ++itp ){ - if (!itp->first->postProcessInstantiationForVariable( - this, sf_tmp, itp->second, d_effort, lemmas)) - { - postProcessSuccess = false; - break; - } - } - if( postProcessSuccess ){ - return doAddInstantiation( sf_tmp.d_vars, sf_tmp.d_subs, lemmas ); - }else{ - return false; - } - }else{ - return doAddInstantiation( sf.d_vars, sf.d_subs, lemmas ); - } - }else{ - //Node v = d_single_inv_map_to_prog[d_single_inv[0][i]]; - bool is_cv = false; - Node pv; - if( d_stack_vars.empty() ){ - pv = d_vars[i]; - }else{ - pv = d_stack_vars.back(); - is_cv = true; - d_stack_vars.pop_back(); - } - activateInstantiationVariable(pv, i); - - //get the instantiator object - Instantiator * vinst = NULL; - std::map< Node, Instantiator * >::iterator itin = d_instantiator.find( pv ); - if( itin!=d_instantiator.end() ){ - vinst = itin->second; - } - Assert( vinst!=NULL ); - d_active_instantiators[pv] = vinst; - vinst->reset(this, sf, pv, d_effort); - - TypeNode pvtn = pv.getType(); - TypeNode pvtnb = pvtn.getBaseType(); - Node pvr = pv; - if( d_qe->getMasterEqualityEngine()->hasTerm( pv ) ){ - pvr = d_qe->getMasterEqualityEngine()->getRepresentative( pv ); - } - Trace("cbqi-inst-debug") << "[Find instantiation for " << pv << "], rep=" << pvr << ", instantiator is " << vinst->identify() << std::endl; - Node pv_value; - if( options::cbqiModel() ){ - pv_value = getModelValue( pv ); - Trace("cbqi-bound2") << "...M( " << pv << " ) = " << pv_value << std::endl; - } - - // if d_effort is full, we must choose at least one model value - if ((i + 1) < d_vars.size() || d_effort != CEG_INST_EFFORT_FULL) - { - //[1] easy case : pv is in the equivalence class as another term not containing pv - Trace("cbqi-inst-debug") << "[1] try based on equivalence class." << std::endl; - d_curr_iphase[pv] = CEG_INST_PHASE_EQC; - std::map< Node, std::vector< Node > >::iterator it_eqc = d_curr_eqc.find( pvr ); - if( it_eqc!=d_curr_eqc.end() ){ - //std::vector< Node > eq_candidates; - Trace("cbqi-inst-debug2") << "...eqc has size " << it_eqc->second.size() << std::endl; - for( unsigned k=0; ksecond.size(); k++ ){ - Node n = it_eqc->second[k]; - if( n!=pv ){ - Trace("cbqi-inst-debug") << "...try based on equal term " << n << std::endl; - //must be an eligible term - if( isEligible( n ) ){ - Node ns; - TermProperties pv_prop; //coefficient of pv in the equality we solve (null is 1) - bool proc = false; - if( !d_prog_var[n].empty() ){ - ns = applySubstitution( pvtn, n, sf, pv_prop, false ); - if( !ns.isNull() ){ - computeProgVars( ns ); - //substituted version must be new and cannot contain pv - proc = d_prog_var[ns].find( pv )==d_prog_var[ns].end(); - } - }else{ - ns = n; - proc = true; - } - if( proc ){ - if (vinst->processEqualTerm( - this, sf, pv, pv_prop, ns, d_effort)) - { - return true; - } - // Do not consider more than one equal term, - // this helps non-monotonic strategies that may encounter - // duplicate instantiations. - break; - } - } - } - } - if (vinst->processEqualTerms(this, sf, pv, it_eqc->second, d_effort)) - { - return true; - } - }else{ - Trace("cbqi-inst-debug2") << "...eqc not found." << std::endl; - } - - //[2] : we can solve an equality for pv - /// iterate over equivalence classes to find cases where we can solve for - /// the variable - if (vinst->hasProcessEquality(this, sf, pv, d_effort)) - { - Trace("cbqi-inst-debug") << "[2] try based on solving equalities." << std::endl; - d_curr_iphase[pv] = CEG_INST_PHASE_EQUAL; - for( unsigned k=0; k >::iterator it_reqc = d_curr_eqc.find( r ); - std::vector< Node > lhs; - std::vector< bool > lhs_v; - std::vector< TermProperties > lhs_prop; - Assert( it_reqc!=d_curr_eqc.end() ); - for( unsigned kk=0; kksecond.size(); kk++ ){ - Node n = it_reqc->second[kk]; - Trace("cbqi-inst-debug2") << "...look at term " << n << std::endl; - //must be an eligible term - if( isEligible( n ) ){ - Node ns; - TermProperties pv_prop; - if( !d_prog_var[n].empty() ){ - ns = applySubstitution( pvtn, n, sf, pv_prop ); - if( !ns.isNull() ){ - computeProgVars( ns ); - } - }else{ - ns = n; - } - if( !ns.isNull() ){ - bool hasVar = d_prog_var[ns].find( pv )!=d_prog_var[ns].end(); - Trace("cbqi-inst-debug2") << "... " << ns << " has var " << pv << " : " << hasVar << std::endl; - std::vector< TermProperties > term_props; - std::vector< Node > terms; - term_props.push_back( pv_prop ); - terms.push_back( ns ); - for( unsigned j=0; jhasProcessAssertion(this, sf, pv, d_effort)) - { - Trace("cbqi-inst-debug") << "[3] try based on assertions." << std::endl; - d_curr_iphase[pv] = CEG_INST_PHASE_ASSERTION; - std::unordered_set< Node, NodeHashFunction > lits; - //unsigned rmax = Theory::theoryOf( pv )==Theory::theoryOf( pv.getType() ) ? 1 : 2; - for( unsigned r=0; r<2; r++ ){ - TheoryId tid = r==0 ? Theory::theoryOf( pvtn ) : THEORY_UF; - Trace("cbqi-inst-debug2") << " look at assertions of " << tid << std::endl; - std::map< TheoryId, std::vector< Node > >::iterator ita = d_curr_asserts.find( tid ); - if( ita!=d_curr_asserts.end() ){ - for (unsigned j = 0; jsecond.size(); j++) { - Node lit = ita->second[j]; - if( lits.find(lit)==lits.end() ){ - lits.insert(lit); - Node plit; - if (options::cbqiRepeatLit() || !isSolvedAssertion(lit)) - { - plit = - vinst->hasProcessAssertion(this, sf, pv, lit, d_effort); - } - if (!plit.isNull()) { - Trace("cbqi-inst-debug2") << " look at " << lit; - if (plit != lit) { - Trace("cbqi-inst-debug2") << "...processed to : " << plit; - } - Trace("cbqi-inst-debug2") << std::endl; - // apply substitutions - Node slit = applySubstitutionToLiteral(plit, sf); - if( !slit.isNull() ){ - // check if contains pv - if( hasVariable( slit, pv ) ){ - Trace("cbqi-inst-debug") << "...try based on literal " - << slit << "," << std::endl; - Trace("cbqi-inst-debug") << "...from " << lit - << std::endl; - if (vinst->processAssertion( - this, sf, pv, slit, lit, d_effort)) - { - return true; - } - } - } - } - } - } - } - } - if (vinst->processAssertions(this, sf, pv, d_effort)) - { - return true; - } - } - } - - //[4] resort to using value in model. We do so if: - // - if the instantiator uses model values at this effort, or - // - if we are solving for a subfield of a datatype (is_cv). - if ((vinst->useModelValue(this, sf, pv, d_effort) || is_cv) - && vinst->allowModelValue(this, sf, pv, d_effort)) - { -#ifdef CVC4_ASSERTIONS - if( pvtn.isReal() && options::cbqiNestedQE() && !options::cbqiAll() ){ - Trace("cbqi-warn") << "Had to resort to model value." << std::endl; - Assert( false ); - } -#endif - Node mv = getModelValue( pv ); - TermProperties pv_prop_m; - Trace("cbqi-inst-debug") << "[4] " << i << "...try model value " << mv << std::endl; - d_curr_iphase[pv] = CEG_INST_PHASE_MVALUE; - CegInstEffort prev = d_effort; - if (d_effort < CEG_INST_EFFORT_STANDARD_MV) - { - // update the effort level to indicate we have used a model value - d_effort = CEG_INST_EFFORT_STANDARD_MV; - } - if (constructInstantiationInc(pv, mv, pv_prop_m, sf)) - { - return true; - } - d_effort = prev; - } - Trace("cbqi-inst-debug") << "[No instantiation found for " << pv << "]" << std::endl; - if( is_cv ){ - d_stack_vars.push_back( pv ); - } - d_active_instantiators.erase( pv ); - deactivateInstantiationVariable(pv); - return false; - } -} - -void CegInstantiator::pushStackVariable( Node v ) { - d_stack_vars.push_back( v ); -} - -void CegInstantiator::popStackVariable() { - Assert( !d_stack_vars.empty() ); - d_stack_vars.pop_back(); -} - -bool CegInstantiator::constructInstantiationInc(Node pv, - Node n, - TermProperties& pv_prop, - SolvedForm& sf, - bool revertOnSuccess) -{ - Node cnode = pv_prop.getCacheNode(); - if( d_curr_subs_proc[pv][n].find( cnode )==d_curr_subs_proc[pv][n].end() ){ - d_curr_subs_proc[pv][n][cnode] = true; - if( Trace.isOn("cbqi-inst-debug") ){ - for( unsigned j=0; j " << n << std::endl; - Assert( n.getType().isSubtypeOf( pv.getType() ) ); - } - //must ensure variables have been computed for n - computeProgVars( n ); - Assert( d_inelig.find( n )==d_inelig.end() ); - - //substitute into previous substitutions, when applicable - std::vector< Node > a_var; - a_var.push_back( pv ); - std::vector< Node > a_subs; - a_subs.push_back( n ); - std::vector< TermProperties > a_prop; - a_prop.push_back( pv_prop ); - std::vector< Node > a_non_basic; - if( !pv_prop.isBasic() ){ - a_non_basic.push_back( pv ); - } - bool success = true; - std::map< int, Node > prev_subs; - std::map< int, TermProperties > prev_prop; - std::map< int, Node > prev_sym_subs; - std::vector< Node > new_non_basic; - Trace("cbqi-inst-debug2") << "Applying substitutions to previous substitution terms..." << std::endl; - for( unsigned j=0; j n } - - // based on the above substitution, we have: - // x = sf.d_subs[j] { pv_prop.getModifiedTerm( pv ) -> n } - // is equivalent to - // a_pv_prop.getModifiedTerm( x ) = new_subs - - // thus we must compose substitutions: - // a_pv_prop.getModifiedTerm( sf.d_props[j].getModifiedTerm( sf.d_vars[j] ) ) = new_subs - - prev_prop[j] = sf.d_props[j]; - bool prev_basic = sf.d_props[j].isBasic(); - - // now compose the property - sf.d_props[j].composeProperty( a_pv_prop ); - - // if previously was basic, becomes non-basic - if( prev_basic && !sf.d_props[j].isBasic() ){ - Assert( std::find( sf.d_non_basic.begin(), sf.d_non_basic.end(), sf.d_vars[j] )==sf.d_non_basic.end() ); - new_non_basic.push_back( sf.d_vars[j] ); - sf.d_non_basic.push_back( sf.d_vars[j] ); - } - } - if( sf.d_subs[j]!=prev_subs[j] ){ - computeProgVars( sf.d_subs[j] ); - Assert( d_inelig.find( sf.d_subs[j] )==d_inelig.end() ); - } - Trace("cbqi-inst-debug2") << "Subs " << j << " " << sf.d_subs[j] << std::endl; - }else{ - Trace("cbqi-inst-debug2") << "...failed to apply substitution to " << sf.d_subs[j] << std::endl; - success = false; - break; - } - }else{ - Trace("cbqi-inst-debug2") << "Skip " << j << " " << sf.d_subs[j] << std::endl; - } - } - if( success ){ - Trace("cbqi-inst-debug2") << "Adding to vectors..." << std::endl; - sf.push_back( pv, n, pv_prop ); - Trace("cbqi-inst-debug2") << "Recurse..." << std::endl; - unsigned i = d_curr_index[pv]; - success = constructInstantiation(sf, d_stack_vars.empty() ? i + 1 : i); - if (!success || revertOnSuccess) - { - Trace("cbqi-inst-debug2") << "Removing from vectors..." << std::endl; - sf.pop_back( pv, n, pv_prop ); - } - } - if (success && !revertOnSuccess) - { - return true; - }else{ - Trace("cbqi-inst-debug2") << "Revert substitutions..." << std::endl; - //revert substitution information - for( std::map< int, Node >::iterator it = prev_subs.begin(); it != prev_subs.end(); it++ ){ - sf.d_subs[it->first] = it->second; - } - for( std::map< int, TermProperties >::iterator it = prev_prop.begin(); it != prev_prop.end(); it++ ){ - sf.d_props[it->first] = it->second; - } - for( unsigned i=0; i& vars, std::vector< Node >& subs, std::vector< Node >& lemmas ) { - if (vars.size() > d_input_vars.size() || !d_var_order_index.empty()) - { - Trace("cbqi-inst-debug") << "Reconstructing instantiations...." << std::endl; - std::map< Node, Node > subs_map; - for( unsigned i=0; i::iterator it = subs_map.find(d_input_vars[i]); - Assert( it!=subs_map.end() ); - Node n = it->second; - Trace("cbqi-inst-debug") << " " << d_input_vars[i] << " -> " << n - << std::endl; - Assert(n.getType().isSubtypeOf(d_input_vars[i].getType())); - subs.push_back( n ); - } - } - if (Trace.isOn("cbqi-inst")) - { - Trace("cbqi-inst") << "Ceg Instantiator produced : " << std::endl; - for (unsigned i = 0, size = d_input_vars.size(); i < size; ++i) - { - Node v = d_input_vars[i]; - Trace("cbqi-inst") << i << " (" << d_curr_iphase[v] << ") : " - << v << " -> " << subs[i] << std::endl; - Assert(subs[i].getType().isSubtypeOf(v.getType())); - } - } - Trace("cbqi-inst-debug") << "Do the instantiation...." << std::endl; - bool ret = d_out->doAddInstantiation( subs ); - for( unsigned i=0; iaddLemma( lemmas[i] ); - } - return ret; -} - -bool CegInstantiator::canApplyBasicSubstitution( Node n, std::vector< Node >& non_basic ){ - Assert( d_prog_var.find( n )!=d_prog_var.end() ); - if( !non_basic.empty() ){ - for (std::unordered_set::iterator it = - d_prog_var[n].begin(); - it != d_prog_var[n].end(); - ++it) - { - if (std::find(non_basic.begin(), non_basic.end(), *it) != non_basic.end()) - { - return false; - } - } - } - return true; -} - -Node CegInstantiator::applySubstitution( TypeNode tn, Node n, std::vector< Node >& vars, std::vector< Node >& subs, std::vector< TermProperties >& prop, - std::vector< Node >& non_basic, TermProperties& pv_prop, bool try_coeff ) { - computeProgVars( n ); - Assert( n==Rewriter::rewrite( n ) ); - bool is_basic = canApplyBasicSubstitution( n, non_basic ); - if( Trace.isOn("cegqi-si-apply-subs-debug") ){ - Trace("cegqi-si-apply-subs-debug") << "is_basic = " << is_basic << " " << tn << std::endl; - for( unsigned i=0; i " << subs[i] << " types : " << vars[i].getType() << " -> " << subs[i].getType() << std::endl; - Assert( subs[i].getType().isSubtypeOf( vars[i].getType() ) ); - } - } - Node nret; - if( is_basic ){ - nret = n.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); - }else{ - if( !tn.isInteger() ){ - //can do basic substitution instead with divisions - std::vector< Node > nvars; - std::vector< Node > nsubs; - for( unsigned i=0; imkNode( MULT, subs[i], NodeManager::currentNM()->mkConst( Rational(1)/prop[i].d_coeff.getConst() ) ); - nn = NodeManager::currentNM()->mkNode( kind::TO_INTEGER, nn ); - nn = Rewriter::rewrite( nn ); - nsubs.push_back( nn ); - }else{ - nsubs.push_back( subs[i] ); - } - } - nret = n.substitute( vars.begin(), vars.end(), nsubs.begin(), nsubs.end() ); - }else if( try_coeff ){ - //must convert to monomial representation - std::map< Node, Node > msum; - if (ArithMSum::getMonomialSum(n, msum)) - { - std::map< Node, Node > msum_coeff; - std::map< Node, Node > msum_term; - for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){ - //check if in substitution - std::vector< Node >::iterator its = std::find( vars.begin(), vars.end(), it->first ); - if( its!=vars.end() ){ - int index = its-vars.begin(); - if( prop[index].d_coeff.isNull() ){ - //apply substitution - msum_term[it->first] = subs[index]; - }else{ - //apply substitution, multiply to ensure no divisibility conflict - msum_term[it->first] = subs[index]; - //relative coefficient - msum_coeff[it->first] = prop[index].d_coeff; - if( pv_prop.d_coeff.isNull() ){ - pv_prop.d_coeff = prop[index].d_coeff; - }else{ - pv_prop.d_coeff = NodeManager::currentNM()->mkNode( MULT, pv_prop.d_coeff, prop[index].d_coeff ); - } - } - }else{ - msum_term[it->first] = it->first; - } - } - //make sum with normalized coefficient - if( !pv_prop.d_coeff.isNull() ){ - pv_prop.d_coeff = Rewriter::rewrite( pv_prop.d_coeff ); - Trace("cegqi-si-apply-subs-debug") << "Combined coeff : " << pv_prop.d_coeff << std::endl; - std::vector< Node > children; - for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){ - Node c_coeff; - if( !msum_coeff[it->first].isNull() ){ - c_coeff = Rewriter::rewrite( NodeManager::currentNM()->mkConst( pv_prop.d_coeff.getConst() / msum_coeff[it->first].getConst() ) ); - }else{ - c_coeff = pv_prop.d_coeff; - } - if( !it->second.isNull() ){ - c_coeff = NodeManager::currentNM()->mkNode( MULT, c_coeff, it->second ); - } - Assert( !c_coeff.isNull() ); - Node c; - if( msum_term[it->first].isNull() ){ - c = c_coeff; - }else{ - c = NodeManager::currentNM()->mkNode( MULT, c_coeff, msum_term[it->first] ); - } - children.push_back( c ); - Trace("cegqi-si-apply-subs-debug") << "Add child : " << c << std::endl; - } - Node nretc = children.size()==1 ? children[0] : NodeManager::currentNM()->mkNode( PLUS, children ); - nretc = Rewriter::rewrite( nretc ); - //ensure that nret does not contain vars - if( !TermUtil::containsTerms( nretc, vars ) ){ - //result is ( nret / pv_prop.d_coeff ) - nret = nretc; - }else{ - Trace("cegqi-si-apply-subs-debug") << "Failed, since result " << nretc << " contains free variable." << std::endl; - } - }else{ - //implies that we have a monomial that has a free variable - Trace("cegqi-si-apply-subs-debug") << "Failed to find coefficient during substitution, implies monomial with free variable." << std::endl; - } - }else{ - Trace("cegqi-si-apply-subs-debug") << "Failed to find monomial sum " << n << std::endl; - } - } - } - if( n!=nret && !nret.isNull() ){ - nret = Rewriter::rewrite( nret ); - } - return nret; -} - -Node CegInstantiator::applySubstitutionToLiteral( Node lit, std::vector< Node >& vars, std::vector< Node >& subs, - std::vector< TermProperties >& prop, std::vector< Node >& non_basic ) { - computeProgVars( lit ); - bool is_basic = canApplyBasicSubstitution( lit, non_basic ); - Node lret; - if( is_basic ){ - lret = lit.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); - }else{ - Node atom = lit.getKind()==NOT ? lit[0] : lit; - bool pol = lit.getKind()!=NOT; - //arithmetic inequalities and disequalities - if( atom.getKind()==GEQ || ( atom.getKind()==EQUAL && !pol && atom[0].getType().isReal() ) ){ - Assert( atom.getKind()!=GEQ || atom[1].isConst() ); - Node atom_lhs; - Node atom_rhs; - if( atom.getKind()==GEQ ){ - atom_lhs = atom[0]; - atom_rhs = atom[1]; - }else{ - atom_lhs = NodeManager::currentNM()->mkNode( MINUS, atom[0], atom[1] ); - atom_lhs = Rewriter::rewrite( atom_lhs ); - atom_rhs = getQuantifiersEngine()->getTermUtil()->d_zero; - } - //must be an eligible term - if( isEligible( atom_lhs ) ){ - //apply substitution to LHS of atom - TermProperties atom_lhs_prop; - atom_lhs = applySubstitution( NodeManager::currentNM()->realType(), atom_lhs, vars, subs, prop, non_basic, atom_lhs_prop ); - if( !atom_lhs.isNull() ){ - if( !atom_lhs_prop.d_coeff.isNull() ){ - atom_rhs = Rewriter::rewrite( NodeManager::currentNM()->mkNode( MULT, atom_lhs_prop.d_coeff, atom_rhs ) ); - } - lret = NodeManager::currentNM()->mkNode( atom.getKind(), atom_lhs, atom_rhs ); - if( !pol ){ - lret = lret.negate(); - } - } - } - }else{ - // don't know how to apply substitution to literal - } - } - if( lit!=lret && !lret.isNull() ){ - lret = Rewriter::rewrite( lret ); - } - return lret; -} - -bool CegInstantiator::check() { - if( d_qe->getTheoryEngine()->needCheck() ){ - Trace("cbqi-engine") << " CEGQI instantiator : wait until all ground theories are finished." << std::endl; - return false; - } - processAssertions(); - for( unsigned r=0; r<2; r++ ){ - d_effort = r == 0 ? CEG_INST_EFFORT_STANDARD : CEG_INST_EFFORT_FULL; - SolvedForm sf; - d_stack_vars.clear(); - d_bound_var_index.clear(); - d_solved_asserts.clear(); - //try to add an instantiation - if (constructInstantiation(sf, 0)) - { - return true; - } - } - Trace("cbqi-engine") << " WARNING : unable to find CEGQI single invocation instantiation." << std::endl; - return false; -} - -void collectPresolveEqTerms( Node n, std::map< Node, std::vector< Node > >& teq ) { - if( n.getKind()==FORALL || n.getKind()==EXISTS ){ - //do nothing - }else{ - if( n.getKind()==EQUAL ){ - for( unsigned i=0; i<2; i++ ){ - std::map< Node, std::vector< Node > >::iterator it = teq.find( n[i] ); - if( it!=teq.end() ){ - Node nn = n[ i==0 ? 1 : 0 ]; - if( std::find( it->second.begin(), it->second.end(), nn )==it->second.end() ){ - it->second.push_back( nn ); - Trace("cbqi-presolve") << " - " << n[i] << " = " << nn << std::endl; - } - } - } - } - for( unsigned i=0; i& vars, std::vector< Node >& terms, - std::map< Node, std::vector< Node > >& teq, Node f, std::vector< Node >& conj ) { - if( conj.size()<1000 ){ - if( terms.size()==f[0].getNumChildren() ){ - Node c = f[1].substitute( vars.begin(), vars.end(), terms.begin(), terms.end() ); - conj.push_back( c ); - }else{ - unsigned i = terms.size(); - Node v = f[0][i]; - terms.push_back( Node::null() ); - for( unsigned j=0; j ps_vars; - std::map< Node, std::vector< Node > > teq; - for( unsigned i=0; i terms; - std::vector< Node > conj; - getPresolveEqConjuncts( ps_vars, terms, teq, q, conj ); - - if( !conj.empty() ){ - Node lem = conj.size()==1 ? conj[0] : NodeManager::currentNM()->mkNode( AND, conj ); - Node g = NodeManager::currentNM()->mkSkolem( "g", NodeManager::currentNM()->booleanType() ); - lem = NodeManager::currentNM()->mkNode( OR, g, lem ); - Trace("cbqi-presolve-debug") << "Presolve lemma : " << lem << std::endl; - d_qe->getOutputChannel().lemma( lem, false, true ); - } - } -} - -void CegInstantiator::processAssertions() { - Trace("cbqi-proc") << "--- Process assertions, #var = " << d_vars.size() << ", #aux-var = " << d_aux_vars.size() << std::endl; - d_curr_asserts.clear(); - d_curr_eqc.clear(); - d_curr_type_eqc.clear(); - - // must use master equality engine to avoid value instantiations - eq::EqualityEngine* ee = d_qe->getMasterEqualityEngine(); - //to eliminate identified illegal terms - std::map< Node, Node > aux_subs; - - //for each variable - for( unsigned i=0; ihasTerm( pv ) ){ - Node pvr = ee->getRepresentative( pv ); - if( d_curr_eqc.find( pvr )==d_curr_eqc.end() ){ - Trace("cbqi-proc") << "Collect equivalence class " << pvr << std::endl; - eq::EqClassIterator eqc_i = eq::EqClassIterator( pvr, ee ); - while( !eqc_i.isFinished() ){ - d_curr_eqc[pvr].push_back( *eqc_i ); - ++eqc_i; - } - } - } - } - //collect assertions for relevant theories - for( unsigned i=0; igetTheoryEngine()->theoryOf( tid ); - if( theory && d_qe->getTheoryEngine()->isTheoryEnabled(tid) ){ - Trace("cbqi-proc") << "Collect assertions from theory " << tid << std::endl; - d_curr_asserts[tid].clear(); - //collect all assertions from theory - for( context::CDList::const_iterator it = theory->facts_begin(); it != theory->facts_end(); ++ it) { - Node lit = (*it).assertion; - Node atom = lit.getKind()==NOT ? lit[0] : lit; - if( d_is_nested_quant || std::find( d_ce_atoms.begin(), d_ce_atoms.end(), atom )!=d_ce_atoms.end() ){ - d_curr_asserts[tid].push_back( lit ); - Trace("cbqi-proc-debug") << "...add : " << lit << std::endl; - }else{ - Trace("cbqi-proc") << "...do not consider literal " << tid << " : " << lit << " since it is not part of CE body." << std::endl; - } - if( lit.getKind()==EQUAL ){ - std::map< Node, std::map< Node, Node > >::iterator itae = d_aux_eq.find( lit ); - if( itae!=d_aux_eq.end() ){ - for( std::map< Node, Node >::iterator itae2 = itae->second.begin(); itae2 != itae->second.end(); ++itae2 ){ - aux_subs[ itae2->first ] = itae2->second; - Trace("cbqi-proc") << "......add substitution : " << itae2->first << " -> " << itae2->second << std::endl; - } - } - }else if( atom.getKind()==BOOLEAN_TERM_VARIABLE ){ - if( std::find( d_aux_vars.begin(), d_aux_vars.end(), atom )!=d_aux_vars.end() ){ - Node val = NodeManager::currentNM()->mkConst( lit.getKind()!=NOT ); - aux_subs[ atom ] = val; - Trace("cbqi-proc") << "......add substitution : " << atom << " -> " << val << std::endl; - } - } - } - } - } - //collect equivalence classes that correspond to relevant theories - Trace("cbqi-proc-debug") << "...collect typed equivalence classes" << std::endl; - eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( ee ); - while( !eqcs_i.isFinished() ){ - Node r = *eqcs_i; - TypeNode rtn = r.getType(); - TheoryId tid = Theory::theoryOf( rtn ); - //if we care about the theory of this eqc - if( std::find( d_tids.begin(), d_tids.end(), tid )!=d_tids.end() ){ - if( rtn.isInteger() || rtn.isReal() ){ - rtn = rtn.getBaseType(); - } - Trace("cbqi-proc-debug") << "...type eqc: " << r << std::endl; - d_curr_type_eqc[rtn].push_back( r ); - if( d_curr_eqc.find( r )==d_curr_eqc.end() ){ - Trace("cbqi-proc") << "Collect equivalence class " << r << std::endl; - Trace("cbqi-proc-debug") << " "; - eq::EqClassIterator eqc_i = eq::EqClassIterator( r, ee ); - while( !eqc_i.isFinished() ){ - Trace("cbqi-proc-debug") << *eqc_i << " "; - d_curr_eqc[r].push_back( *eqc_i ); - ++eqc_i; - } - Trace("cbqi-proc-debug") << std::endl; - } - } - ++eqcs_i; - } - //construct substitution from auxiliary variable equalities (if e.g. ITE removal was applied to CE body of quantified formula) - std::vector< Node > subs_lhs; - std::vector< Node > subs_rhs; - for( unsigned i=0; i::iterator it = aux_subs.find( r ); - if( it!=aux_subs.end() ){ - addToAuxVarSubstitution( subs_lhs, subs_rhs, r, it->second ); - }else{ - Trace("cbqi-proc") << "....no substitution found for auxiliary variable " << r << "!!! type is " << r.getType() << std::endl; - Assert( false ); - } - } - - //apply substitutions to everything, if necessary - if( !subs_lhs.empty() ){ - Trace("cbqi-proc") << "Applying substitution : " << std::endl; - for( unsigned i=0; i " << subs_rhs[i] << std::endl; - } - for( std::map< TheoryId, std::vector< Node > >::iterator it = d_curr_asserts.begin(); it != d_curr_asserts.end(); ++it ){ - for( unsigned i=0; isecond.size(); i++ ){ - Node lit = it->second[i]; - lit = lit.substitute( subs_lhs.begin(), subs_lhs.end(), subs_rhs.begin(), subs_rhs.end() ); - lit = Rewriter::rewrite( lit ); - it->second[i] = lit; - } - } - for( std::map< Node, std::vector< Node > >::iterator it = d_curr_eqc.begin(); it != d_curr_eqc.end(); ++it ){ - for( unsigned i=0; isecond.size(); i++ ){ - Node n = it->second[i]; - n = n.substitute( subs_lhs.begin(), subs_lhs.end(), subs_rhs.begin(), subs_rhs.end() ); - n = Rewriter::rewrite( n ); - it->second[i] = n; - } - } - } - - //remove unecessary assertions - for( std::map< TheoryId, std::vector< Node > >::iterator it = d_curr_asserts.begin(); it != d_curr_asserts.end(); ++it ){ - std::vector< Node > akeep; - for( unsigned i=0; isecond.size(); i++ ){ - Node n = it->second[i]; - //must be an eligible term - if( isEligible( n ) ){ - //must contain at least one variable - if( !d_prog_var[n].empty() ){ - Trace("cbqi-proc") << "...literal[" << it->first << "] : " << n << std::endl; - akeep.push_back( n ); - }else{ - Trace("cbqi-proc") << "...remove literal from " << it->first << " : " << n << " since it contains no relevant variables." << std::endl; - } - }else{ - Trace("cbqi-proc") << "...remove literal from " << it->first << " : " << n << " since it contains ineligible terms." << std::endl; - } - } - it->second.clear(); - it->second.insert( it->second.end(), akeep.begin(), akeep.end() ); - } - - //remove duplicate terms from eqc - for( std::map< Node, std::vector< Node > >::iterator it = d_curr_eqc.begin(); it != d_curr_eqc.end(); ++it ){ - std::vector< Node > new_eqc; - for( unsigned i=0; isecond.size(); i++ ){ - if( std::find( new_eqc.begin(), new_eqc.end(), it->second[i] )==new_eqc.end() ){ - new_eqc.push_back( it->second[i] ); - } - } - it->second.clear(); - it->second.insert( it->second.end(), new_eqc.begin(), new_eqc.end() ); - } -} - -void CegInstantiator::addToAuxVarSubstitution( std::vector< Node >& subs_lhs, std::vector< Node >& subs_rhs, Node l, Node r ) { - r = r.substitute( subs_lhs.begin(), subs_lhs.end(), subs_rhs.begin(), subs_rhs.end() ); - - std::vector< Node > cl; - cl.push_back( l ); - std::vector< Node > cr; - cr.push_back( r ); - for( unsigned i=0; igetModel()->getValue( n ); -} - -Node CegInstantiator::getBoundVariable(TypeNode tn) -{ - unsigned index = 0; - std::unordered_map::iterator itb = - d_bound_var_index.find(tn); - if (itb != d_bound_var_index.end()) - { - index = itb->second; - } - d_bound_var_index[tn] = index + 1; - while (index >= d_bound_var[tn].size()) - { - std::stringstream ss; - ss << "x" << index; - Node x = NodeManager::currentNM()->mkBoundVar(ss.str(), tn); - d_bound_var[tn].push_back(x); - } - return d_bound_var[tn][index]; -} - -bool CegInstantiator::isSolvedAssertion(Node n) const -{ - return d_solved_asserts.find(n) != d_solved_asserts.end(); -} - -void CegInstantiator::markSolved(Node n, bool solved) -{ - if (solved) - { - d_solved_asserts.insert(n); - } - else if (isSolvedAssertion(n)) - { - d_solved_asserts.erase(n); - } -} - -void CegInstantiator::collectCeAtoms( Node n, std::map< Node, bool >& visited ) { - if( n.getKind()==FORALL ){ - d_is_nested_quant = true; - }else if( visited.find( n )==visited.end() ){ - visited[n] = true; - if( TermUtil::isBoolConnectiveTerm( n ) ){ - for( unsigned i=0; i& lems, std::vector< Node >& ce_vars ) { - Trace("cbqi-reg") << "Register counterexample lemma..." << std::endl; - d_input_vars.clear(); - d_input_vars.insert(d_input_vars.end(), ce_vars.begin(), ce_vars.end()); - - //Assert( d_vars.empty() ); - d_vars.clear(); - registerTheoryId(THEORY_UF); - for (unsigned i = 0; i < ce_vars.size(); i++) - { - Trace("cbqi-reg") << " register input variable : " << ce_vars[i] << std::endl; - registerVariable(ce_vars[i]); - } - - // preprocess with all relevant instantiator preprocessors - Trace("cbqi-debug") << "Preprocess based on theory-specific methods..." - << std::endl; - std::vector pvars; - pvars.insert(pvars.end(), d_vars.begin(), d_vars.end()); - for (std::pair& p : d_tipp) - { - p.second->registerCounterexampleLemma(lems, pvars); - } - // must register variables generated by preprocessors - Trace("cbqi-debug") << "Register variables from theory-specific methods " - << d_input_vars.size() << " " << pvars.size() << " ..." - << std::endl; - for (unsigned i = d_input_vars.size(), size = pvars.size(); i < size; ++i) - { - Trace("cbqi-reg") << " register theory preprocess variable : " << pvars[i] - << std::endl; - registerVariable(pvars[i]); - } - - //remove ITEs - IteSkolemMap iteSkolemMap; - d_qe->getTheoryEngine()->getTermFormulaRemover()->run(lems, iteSkolemMap); - //Assert( d_aux_vars.empty() ); - d_aux_vars.clear(); - d_aux_eq.clear(); - for(IteSkolemMap::iterator i = iteSkolemMap.begin(); i != iteSkolemMap.end(); ++i) { - Trace("cbqi-reg") << " register aux variable : " << i->first << std::endl; - registerVariable(i->first, true); - } - for( unsigned i=0; i voo; - bool doSort = false; - std::vector vars; - std::map > tvars; - for (unsigned i = 0, size = d_vars.size(); i < size; i++) - { - voo[d_vars[i]] = i; - d_var_order_index.push_back(0); - TypeNode tn = d_vars[i].getType(); - if (tn.isInteger()) - { - doSort = true; - tvars[tn].push_back(d_vars[i]); - } - else - { - vars.push_back(d_vars[i]); - } - } - if (doSort) - { - Trace("cbqi-debug") << "Sort variables based on ordering." << std::endl; - for (std::pair >& vs : tvars) - { - vars.insert(vars.end(), vs.second.begin(), vs.second.end()); - } - - Trace("cbqi-debug") << "Consider variables in this order : " << std::endl; - for (unsigned i = 0; i < vars.size(); i++) - { - d_var_order_index[voo[vars[i]]] = i; - Trace("cbqi-debug") << " " << vars[i] << " : " << vars[i].getType() - << ", index was : " << voo[vars[i]] << std::endl; - d_vars[i] = vars[i]; - } - Trace("cbqi-debug") << std::endl; - } - else - { - d_var_order_index.clear(); - } - } - - //collect atoms from all lemmas: we will only do bounds coming from original body - d_is_nested_quant = false; - std::map< Node, bool > visited; - for( unsigned i=0; igetTermEnumeration()->isClosedEnumerableType(tn); -} - -bool Instantiator::processEqualTerm(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - TermProperties& pv_prop, - Node n, - CegInstEffort effort) -{ - pv_prop.d_type = 0; - return ci->constructInstantiationInc(pv, n, pv_prop, sf); -} - -} /* CVC4::theory::quantifiers namespace */ -} /* CVC4::theory namespace */ -} /* CVC4 namespace */ diff --git a/src/theory/quantifiers/ceg_instantiator.h b/src/theory/quantifiers/ceg_instantiator.h deleted file mode 100644 index 03983fe1a..000000000 --- a/src/theory/quantifiers/ceg_instantiator.h +++ /dev/null @@ -1,783 +0,0 @@ -/********************* */ -/*! \file ceg_instantiator.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 quantifier instantiation - **/ - - -#include "cvc4_private.h" - -#ifndef __CVC4__THEORY__QUANTIFIERS__CEG_INSTANTIATOR_H -#define __CVC4__THEORY__QUANTIFIERS__CEG_INSTANTIATOR_H - -#include "theory/quantifiers_engine.h" -#include "util/statistics_registry.h" - -namespace CVC4 { -namespace theory { - -namespace arith { - class TheoryArith; -} - -namespace quantifiers { - -class CegqiOutput { -public: - virtual ~CegqiOutput() {} - virtual bool doAddInstantiation( std::vector< Node >& subs ) = 0; - virtual bool isEligibleForInstantiation( Node n ) = 0; - virtual bool addLemma( Node lem ) = 0; -}; - -class Instantiator; -class InstantiatorPreprocess; - -/** Term Properties - * - * Stores properties for a variable to solve for in counterexample-guided - * instantiation. - * - * For LIA, this includes the coefficient of the variable, and the bound type - * for the variable. - */ -class TermProperties { -public: - TermProperties() : d_type(0) {} - virtual ~TermProperties() {} - - // type of property for a term - // for arithmetic this corresponds to bound type (0:equal, 1:upper bound, -1:lower bound) - int d_type; - // for arithmetic - Node d_coeff; - // get cache node - // we consider terms + TermProperties that are unique up to their cache node - // (see constructInstantiationInc) - virtual Node getCacheNode() const { return d_coeff; } - // is non-basic - virtual bool isBasic() const { return d_coeff.isNull(); } - // get modified term - virtual Node getModifiedTerm( Node pv ) const { - if( !d_coeff.isNull() ){ - return NodeManager::currentNM()->mkNode( kind::MULT, d_coeff, pv ); - }else{ - return pv; - } - } - // compose property, should be such that: - // p.getModifiedTerm( this.getModifiedTerm( x ) ) = this_updated.getModifiedTerm( x ) - virtual void composeProperty( TermProperties& p ){ - if( !p.d_coeff.isNull() ){ - if( d_coeff.isNull() ){ - d_coeff = p.d_coeff; - }else{ - d_coeff = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::MULT, d_coeff, p.d_coeff ) ); - } - } - } -}; - -/** Solved form - * This specifies a substitution: - * { d_props[i].getModifiedTerm(d_vars[i]) -> d_subs[i] | i = 0...|d_vars| } - */ -class SolvedForm { -public: - // list of variables - std::vector< Node > d_vars; - // list of terms that they are substituted to - std::vector< Node > d_subs; - // properties for each variable - std::vector< TermProperties > d_props; - // the variables that have non-basic information regarding how they are substituted - // an example is for linear arithmetic, we store "substitution with coefficients". - std::vector d_non_basic; - // push the substitution pv_prop.getModifiedTerm(pv) -> n - void push_back( Node pv, Node n, TermProperties& pv_prop ){ - d_vars.push_back( pv ); - d_subs.push_back( n ); - d_props.push_back( pv_prop ); - if( !pv_prop.isBasic() ){ - d_non_basic.push_back( pv ); - // update theta value - Node new_theta = getTheta(); - if( new_theta.isNull() ){ - new_theta = pv_prop.d_coeff; - }else{ - new_theta = NodeManager::currentNM()->mkNode( kind::MULT, new_theta, pv_prop.d_coeff ); - new_theta = Rewriter::rewrite( new_theta ); - } - d_theta.push_back( new_theta ); - } - } - // pop the substitution pv_prop.getModifiedTerm(pv) -> n - void pop_back( Node pv, Node n, TermProperties& pv_prop ){ - d_vars.pop_back(); - d_subs.pop_back(); - d_props.pop_back(); - if( !pv_prop.isBasic() ){ - d_non_basic.pop_back(); - // update theta value - d_theta.pop_back(); - } - } - // is this solved form empty? - bool empty() { return d_vars.empty(); } -public: - // theta values (for LIA, see Section 4 of Reynolds/King/Kuncak FMSD 2017) - std::vector< Node > d_theta; - // get the current value for theta (for LIA, see Section 4 of Reynolds/King/Kuncak FMSD 2017) - Node getTheta() { - if( d_theta.empty() ){ - return Node::null(); - }else{ - return d_theta[d_theta.size()-1]; - } - } -}; - -/** instantiation effort levels - * - * This effort is used to stratify the construction of - * instantiations for some theories that may result to - * using model value instantiations. - */ -enum CegInstEffort -{ - // uninitialized - CEG_INST_EFFORT_NONE, - // standard effort level - CEG_INST_EFFORT_STANDARD, - // standard effort level, but we have used model values - CEG_INST_EFFORT_STANDARD_MV, - // full effort level - CEG_INST_EFFORT_FULL -}; - -std::ostream& operator<<(std::ostream& os, CegInstEffort e); - -/** instantiation phase for variables - * - * This indicates the phase in which we constructed - * a substitution for individual variables. - */ -enum CegInstPhase -{ - // uninitialized - CEG_INST_PHASE_NONE, - // instantiation constructed during traversal of equivalence classes - CEG_INST_PHASE_EQC, - // instantiation constructed during solving equalities - CEG_INST_PHASE_EQUAL, - // instantiation constructed by looking at theory assertions - CEG_INST_PHASE_ASSERTION, - // instantiation constructed by querying model value - CEG_INST_PHASE_MVALUE, -}; - -std::ostream& operator<<(std::ostream& os, CegInstPhase phase); - -/** Ceg instantiator - * - * This class manages counterexample-guided quantifier instantiation - * for a single quantified formula. - * - * For details on counterexample-guided quantifier instantiation - * (for linear arithmetic), see Reynolds/King/Kuncak FMSD 2017. - */ -class CegInstantiator { - public: - CegInstantiator(QuantifiersEngine* qe, - CegqiOutput* out, - bool use_vts_delta = true, - bool use_vts_inf = true); - virtual ~CegInstantiator(); - /** check - * This adds instantiations based on the state of d_vars in current context - * on the output channel d_out of this class. - */ - bool check(); - /** presolve for quantified formula - * - * This initializes formulas that help static learning of the quantifier-free - * solver. It is only called if the option --cbqi-prereg-inst is used. - */ - void presolve(Node q); - /** Register the counterexample lemma - * - * lems : contains the conjuncts of the counterexample lemma of the - * quantified formula we are processing. The counterexample - * lemma is the formula { ~phi[e/x] } in Figure 1 of Reynolds - * et al. FMSD 2017. - * ce_vars : contains the variables e. Notice these are variables of - * INST_CONSTANT kind, since we do not permit bound - * variables in assertions. - * - * This method may modify the set of lemmas lems based on: - * - ITE removal, - * - Theory-specific preprocessing of instantiation lemmas. - * It may also introduce new variables to ce_vars if necessary. - */ - void registerCounterexampleLemma(std::vector& lems, - std::vector& ce_vars); - /** get the output channel of this class */ - CegqiOutput* getOutput() { return d_out; } - //------------------------------interface for instantiators - /** get quantifiers engine */ - QuantifiersEngine* getQuantifiersEngine() { return d_qe; } - /** push stack variable - * This adds a new variable to solve for in the stack - * of variables we are processing. This stack is only - * used for datatypes, where e.g. the DtInstantiator - * solving for a list x may push the stack "variables" - * head(x) and tail(x). - */ - void pushStackVariable(Node v); - /** pop stack variable */ - void popStackVariable(); - /** construct instantiation increment - * - * Adds the substitution { pv_prop.getModifiedTerm(pv) -> n } to the current - * instantiation, specified by sf. - * - * This function returns true if a call to - * QuantifiersEngine::addInstantiation(...) - * was successfully made in a recursive call. - * - * The solved form sf is reverted to its original state if - * this function returns false, or - * revertOnSuccess is true and this function returns true. - */ - bool constructInstantiationInc(Node pv, - Node n, - TermProperties& pv_prop, - SolvedForm& sf, - bool revertOnSuccess = false); - /** get the current model value of term n */ - Node getModelValue(Node n); - /** get bound variable for type - * - * This gets the next (canonical) bound variable of - * type tn. This can be used for instance when - * constructing instantiations that involve choice expressions. - */ - Node getBoundVariable(TypeNode tn); - /** has this assertion been marked as solved? */ - bool isSolvedAssertion(Node n) const; - /** marked solved */ - void markSolved(Node n, bool solved = true); - //------------------------------end interface for instantiators - - /** - * Get the number of atoms in the counterexample lemma of the quantified - * formula we are processing with this class. - */ - unsigned getNumCEAtoms() { return d_ce_atoms.size(); } - /** - * Get the i^th atom of the counterexample lemma of the quantified - * formula we are processing with this class. - */ - Node getCEAtom(unsigned i) { return d_ce_atoms[i]; } - /** is n a term that is eligible for instantiation? */ - bool isEligible(Node n); - /** does n have variable pv? */ - bool hasVariable(Node n, Node pv); - /** are we using delta for LRA virtual term substitution? */ - bool useVtsDelta() { return d_use_vts_delta; } - /** are we using infinity for LRA virtual term substitution? */ - bool useVtsInfinity() { return d_use_vts_inf; } - /** are we processing a nested quantified formula? */ - bool hasNestedQuantification() { return d_is_nested_quant; } - private: - /** quantified formula associated with this instantiator */ - QuantifiersEngine* d_qe; - /** output channel of this instantiator */ - CegqiOutput* d_out; - /** whether we are using delta for virtual term substitution - * (for quantified LRA). - */ - bool d_use_vts_delta; - /** whether we are using infinity for virtual term substitution - * (for quantified LRA). - */ - bool d_use_vts_inf; - - //-------------------------------globally cached - /** cache from nodes to the set of variables it contains - * (from the quantified formula we are instantiating). - */ - std::unordered_map, - NodeHashFunction> - d_prog_var; - /** cache of the set of terms that we have established are - * ineligible for instantiation. - */ - std::unordered_set d_inelig; - /** ensures n is in d_prog_var and d_inelig. */ - void computeProgVars(Node n); - //-------------------------------end globally cached - - //-------------------------------cached per round - /** current assertions per theory */ - std::map > d_curr_asserts; - /** map from representatives to the terms in their equivalence class */ - std::map > d_curr_eqc; - /** map from types to representatives of that type */ - std::map > d_curr_type_eqc; - /** solved asserts */ - std::unordered_set d_solved_asserts; - /** process assertions - * This is called once at the beginning of check to - * set up all necessary information for constructing instantiations, - * such as the above data structures. - */ - void processAssertions(); - /** add to auxiliary variable substitution - * This adds the substitution l -> r to the auxiliary - * variable substitution subs_lhs -> subs_rhs, and serializes - * it (applies it to existing substitutions). - */ - void addToAuxVarSubstitution(std::vector& subs_lhs, - std::vector& subs_rhs, - Node l, - Node r); - /** cache bound variables for type returned - * by getBoundVariable(...). - */ - std::unordered_map, TypeNodeHashFunction> - d_bound_var; - /** current index of bound variables for type. - * The next call to getBoundVariable(...) for - * type tn returns the d_bound_var_index[tn]^th - * element of d_bound_var[tn], or a fresh variable - * if not in bounds. - */ - std::unordered_map - d_bound_var_index; - //-------------------------------end cached per round - - //-------------------------------data per theory - /** relevant theory ids - * A list of theory ids that contain at least one - * constraint in the body of the quantified formula we - * are processing. - */ - std::vector d_tids; - /** map from theory ids to instantiator preprocessors */ - std::map d_tipp; - /** registers all theory ids associated with type tn - * - * This recursively calls registerTheoryId for typeOf(tn') for - * all parameters and datatype subfields of type tn. - * visited stores the types we have already visited. - */ - void registerTheoryIds(TypeNode tn, std::map& visited); - /** register theory id tid - * - * This is called when the quantified formula we are processing - * with this class involves theory tid. In this case, we will - * construct instantiations based on the assertion list of this theory. - */ - void registerTheoryId(TheoryId tid); - //-------------------------------end data per theory - - //-------------------------------the variables - /** the variables we are instantiating - * - * This is a superset of the variables for the instantiations we are - * generating and sending on the output channel of this class. - */ - std::vector d_vars; - /** set form of d_vars */ - std::unordered_set d_vars_set; - /** index of variables reported in instantiation */ - std::vector d_var_order_index; - /** number of input variables - * - * These are the variables, in order, for the instantiations we are generating - * and sending on the output channel of this class. - */ - std::vector d_input_vars; - /** literals to equalities for aux vars - * This stores entries of the form - * L -> ( k -> t ) - * where - * k is a variable in d_aux_vars, - * L is a literal that if asserted implies that our - * instantiation should map { k -> t }. - * For example, if a term of the form - * ite( C, t1, t2 ) - * was replaced by k, we get this (top-level) assertion: - * ite( C, k=t1, k=t2 ) - * The vector d_aux_eq contains the exact form of - * the literals in the above constraint that they would - * appear in assertions, meaning d_aux_eq may contain: - * t1=k -> ( k -> t1 ) - * t2=k -> ( k -> t2 ) - * where t1=k and t2=k are the rewritten form of - * k=t1 and k=t2 respectively. - */ - std::map > d_aux_eq; - /** auxiliary variables - * These variables include the result of removing ITE - * terms from the quantified formula we are processing. - * These variables must be eliminated from constraints - * as a preprocess step to check(). - */ - std::vector d_aux_vars; - /** register variable */ - void registerVariable(Node v, bool is_aux = false); - //-------------------------------the variables - - //-------------------------------quantified formula info - /** are we processing a nested quantified formula? */ - bool d_is_nested_quant; - /** the atoms of the CE lemma */ - std::vector d_ce_atoms; - /** collect atoms */ - void collectCeAtoms(Node n, std::map& visited); - //-------------------------------end quantified formula info - - //-------------------------------current state - /** the current effort level of the instantiator - * This indicates the effort Instantiator objects - * will put into the terms they return. - */ - CegInstEffort d_effort; - /** for each variable, the instantiator used for that variable */ - std::map d_active_instantiators; - /** map from variables to the index in the prefix of the quantified - * formula we are processing. - */ - std::map d_curr_index; - /** map from variables to the phase in which we instantiated them */ - std::map d_curr_iphase; - /** cache of current substitutions tried between activate/deactivate */ - std::map > > d_curr_subs_proc; - /** stack of temporary variables we are solving for, - * e.g. subfields of datatypes. - */ - std::vector d_stack_vars; - /** activate instantiation variable v at index - * - * This is called when variable (inst constant) v is activated - * for the quantified formula we are processing. - * This method initializes the instantiator class for - * that variable if necessary, where this class is - * determined by the type of v. It also initializes - * the cache of values we have tried substituting for v - * (in d_curr_subs_proc), and stores its index. - */ - void activateInstantiationVariable(Node v, unsigned index); - /** deactivate instantiation variable - * - * This is called when variable (inst constant) v is deactivated - * for the quantified formula we are processing. - */ - void deactivateInstantiationVariable(Node v); - //-------------------------------end current state - - //---------------------------------for applying substitutions - /** can use basic substitution */ - bool canApplyBasicSubstitution( Node n, std::vector< Node >& non_basic ); - /** apply substitution - * We wish to process the substitution: - * ( pv = n * sf ) - * where pv is a variable with type tn, and * denotes application of substitution. - * The return value "ret" and pv_prop is such that the above equality is equivalent to - * ( pv_prop.getModifiedTerm(pv) = ret ) - */ - Node applySubstitution( TypeNode tn, Node n, SolvedForm& sf, TermProperties& pv_prop, bool try_coeff = true ) { - return applySubstitution( tn, n, sf.d_vars, sf.d_subs, sf.d_props, sf.d_non_basic, pv_prop, try_coeff ); - } - /** apply substitution, with solved form expanded to subs/prop/non_basic/vars */ - Node applySubstitution( TypeNode tn, Node n, std::vector< Node >& vars, std::vector< Node >& subs, std::vector< TermProperties >& prop, - std::vector< Node >& non_basic, TermProperties& pv_prop, bool try_coeff = true ); - /** apply substitution to literal lit - * The return value is equivalent to ( lit * sf ) - * where * denotes application of substitution. - */ - Node applySubstitutionToLiteral( Node lit, SolvedForm& sf ) { - return applySubstitutionToLiteral( lit, sf.d_vars, sf.d_subs, sf.d_props, sf.d_non_basic ); - } - /** apply substitution to literal lit, with solved form expanded to subs/prop/non_basic/vars */ - Node applySubstitutionToLiteral( Node lit, std::vector< Node >& vars, std::vector< Node >& subs, std::vector< TermProperties >& prop, - std::vector< Node >& non_basic ); - //---------------------------------end for applying substitutions - - /** map from variables to their instantiators */ - std::map d_instantiator; - - /** construct instantiation - * This method constructs the current instantiation, where we - * are currently processing the i^th variable in d_vars. - * It returns true if a successful call to the output channel's - * doAddInstantiation was made. - */ - bool constructInstantiation(SolvedForm& sf, unsigned i); - /** do add instantiation - * This method is called by the above function after we finalize the - * variables/substitution and auxiliary lemmas. - * It returns true if a successful call to the output channel's - * doAddInstantiation was made. - */ - bool doAddInstantiation(std::vector& vars, - std::vector& subs, - std::vector& lemmas); -}; - -/** Instantiator class - * - * This is a virtual class that is used for methods for constructing - * substitutions for individual variables in counterexample-guided - * instantiation techniques. - * - * This class contains a set of interface functions below, which are called - * based on a fixed instantiation method implemented by CegInstantiator. - * In these calls, the Instantiator in turn makes calls to methods in - * CegInstanatior (primarily constructInstantiationInc). - */ -class Instantiator { -public: - Instantiator( QuantifiersEngine * qe, TypeNode tn ); - virtual ~Instantiator(){} - /** reset - * This is called once, prior to any of the below methods are called. - * This function sets up any initial information necessary for constructing - * instantiations for pv based on the current context. - */ - virtual void reset(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) - { - } - - /** process equal term - * - * This method is called when the entailment: - * E |= pv_prop.getModifiedTerm(pv) = n - * holds in the current context E, and n is eligible for instantiation. - * - * Returns true if an instantiation was successfully added via a call to - * CegInstantiator::constructInstantiationInc. - */ - virtual bool processEqualTerm(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - TermProperties& pv_prop, - Node n, - CegInstEffort effort); - /** process equal terms - * - * This method is called after process equal term, where eqc is the list of - * eligible terms in the equivalence class of pv. - * - * Returns true if an instantiation was successfully added via a call to - * CegInstantiator::constructInstantiationInc. - */ - virtual bool processEqualTerms(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - std::vector& eqc, - CegInstEffort effort) - { - return false; - } - - /** whether the instantiator implements processEquality */ - virtual bool hasProcessEquality(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) - { - return false; - } - /** process equality - * The input is such that term_props.size() = terms.size() = 2 - * This method is called when the entailment: - * E ^ term_props[0].getModifiedTerm(x0) = - * terms[0] ^ term_props[1].getModifiedTerm(x1) = terms[1] |= x0 = x1 - * holds in current context E for fresh variables xi, terms[i] are eligible, - * and at least one terms[i] contains pv for i = 0,1. - * Notice in the basic case, E |= terms[0] = terms[1]. - * - * Returns true if an instantiation was successfully added via a call to - * CegInstantiator::constructInstantiationInc. - */ - virtual bool processEquality(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - std::vector& term_props, - std::vector& terms, - CegInstEffort effort) - { - return false; - } - - /** whether the instantiator implements processAssertion for any literal */ - virtual bool hasProcessAssertion(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) - { - return false; - } - /** has process assertion - * - * This method is called when the entailment: - * E |= lit - * holds in current context E. Typically, lit belongs to the list of current - * assertions. - * - * This method is used to determine whether the instantiator implements - * processAssertion for literal lit. - * If this method returns null, then this intantiator does not handle the - * literal lit. Otherwise, this method returns a literal lit' such that: - * (1) lit' is true in the current model, - * (2) lit' implies lit. - * where typically lit' = lit. - */ - virtual Node hasProcessAssertion(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - Node lit, - CegInstEffort effort) - { - return Node::null(); - } - /** process assertion - * This method processes the assertion slit for variable pv. - * lit : the substituted form (under sf) of a literal returned by - * hasProcessAssertion - * alit : the asserted literal, given as input to hasProcessAssertion - * - * Returns true if an instantiation was successfully added via a call to - * CegInstantiator::constructInstantiationInc. - */ - virtual bool processAssertion(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - Node lit, - Node alit, - CegInstEffort effort) - { - return false; - } - /** process assertions - * - * Called after processAssertion is called for each literal asserted to the - * instantiator. - * - * Returns true if an instantiation was successfully added via a call to - * CegInstantiator::constructInstantiationInc. - */ - virtual bool processAssertions(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) - { - return false; - } - - /** do we use the model value as instantiation for pv? - * This method returns true if we use model value instantiations - * at the same effort level as those determined by this instantiator. - */ - virtual bool useModelValue(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) - { - return effort > CEG_INST_EFFORT_STANDARD; - } - /** do we allow the model value as instantiation for pv? */ - virtual bool allowModelValue(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) - { - return d_closed_enum_type; - } - - /** do we need to postprocess the solved form for pv? */ - virtual bool needsPostProcessInstantiationForVariable(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) - { - return false; - } - /** postprocess the solved form for pv - * - * This method returns true if we successfully postprocessed the solved form. - * lemmas is a set of lemmas we wish to return along with the instantiation. - */ - virtual bool postProcessInstantiationForVariable(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort, - std::vector& lemmas) - { - return true; - } - - /** Identify this module (for debugging) */ - virtual std::string identify() const { return "Default"; } - protected: - /** the type of the variable we are instantiating */ - TypeNode d_type; - /** whether d_type is a closed enumerable type */ - bool d_closed_enum_type; -}; - -class ModelValueInstantiator : public Instantiator { -public: - ModelValueInstantiator( QuantifiersEngine * qe, TypeNode tn ) : Instantiator( qe, tn ){} - virtual ~ModelValueInstantiator(){} - bool useModelValue(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) - { - return true; - } - std::string identify() const { return "ModelValue"; } -}; - -/** instantiator preprocess - * - * This class implements techniques for preprocessing the counterexample lemma - * generated for counterexample-guided quantifier instantiation. - */ -class InstantiatorPreprocess -{ - public: - InstantiatorPreprocess() {} - virtual ~InstantiatorPreprocess() {} - /** register counterexample lemma - * This implements theory-specific preprocessing and registration - * of counterexample lemmas, with the same contract as - * CegInstantiation::registerCounterexampleLemma. - */ - virtual void registerCounterexampleLemma(std::vector& lems, - std::vector& ce_vars) - { - } -}; - -} /* CVC4::theory::quantifiers namespace */ -} /* CVC4::theory namespace */ -} /* CVC4 namespace */ - -#endif diff --git a/src/theory/quantifiers/ceg_t_instantiator.cpp b/src/theory/quantifiers/ceg_t_instantiator.cpp deleted file mode 100644 index e617819d7..000000000 --- a/src/theory/quantifiers/ceg_t_instantiator.cpp +++ /dev/null @@ -1,1990 +0,0 @@ -/********************* */ -/*! \file ceg_t_instantiator.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 theory-specific counterexample-guided quantifier instantiation - **/ - -#include "theory/quantifiers/ceg_t_instantiator.h" - -#include "options/quantifiers_options.h" -#include "theory/quantifiers/first_order_model.h" -#include "theory/quantifiers/term_database.h" -#include "theory/quantifiers/term_util.h" -#include "theory/quantifiers/quantifiers_rewriter.h" -#include "theory/quantifiers/trigger.h" - -#include "theory/arith/arith_msum.h" -#include "theory/arith/partial_model.h" -#include "theory/arith/theory_arith.h" -#include "theory/arith/theory_arith_private.h" -#include "theory/bv/theory_bv_utils.h" -#include "util/bitvector.h" -#include "util/random.h" - -#include -#include - -using namespace std; -using namespace CVC4::kind; -using namespace CVC4::context; - -namespace CVC4 { -namespace theory { -namespace quantifiers { - -struct BvLinearAttributeId {}; -using BvLinearAttribute = expr::Attribute; - -Node ArithInstantiator::getModelBasedProjectionValue( CegInstantiator * ci, Node e, Node t, bool isLower, Node c, Node me, Node mt, Node theta, Node inf_coeff, Node delta_coeff ) { - Node val = t; - Trace("cegqi-arith-bound2") << "Value : " << val << std::endl; - Assert( !e.getType().isInteger() || t.getType().isInteger() ); - Assert( !e.getType().isInteger() || mt.getType().isInteger() ); - //add rho value - //get the value of c*e - Node ceValue = me; - Node new_theta = theta; - if( !c.isNull() ){ - Assert( c.getType().isInteger() ); - ceValue = NodeManager::currentNM()->mkNode( MULT, ceValue, c ); - ceValue = Rewriter::rewrite( ceValue ); - if( new_theta.isNull() ){ - new_theta = c; - }else{ - new_theta = NodeManager::currentNM()->mkNode( MULT, new_theta, c ); - new_theta = Rewriter::rewrite( new_theta ); - } - Trace("cegqi-arith-bound2") << "...c*e = " << ceValue << std::endl; - Trace("cegqi-arith-bound2") << "...theta = " << new_theta << std::endl; - } - if( !new_theta.isNull() && e.getType().isInteger() ){ - Node rho; - //if( !mt.getType().isInteger() ){ - //round up/down - //mt = NodeManager::currentNM()->mkNode( - //} - if( isLower ){ - rho = NodeManager::currentNM()->mkNode( MINUS, ceValue, mt ); - }else{ - rho = NodeManager::currentNM()->mkNode( MINUS, mt, ceValue ); - } - rho = Rewriter::rewrite( rho ); - Trace("cegqi-arith-bound2") << "...rho = " << me << " - " << mt << " = " << rho << std::endl; - Trace("cegqi-arith-bound2") << "..." << rho << " mod " << new_theta << " = "; - rho = NodeManager::currentNM()->mkNode( INTS_MODULUS_TOTAL, rho, new_theta ); - rho = Rewriter::rewrite( rho ); - Trace("cegqi-arith-bound2") << rho << std::endl; - Kind rk = isLower ? PLUS : MINUS; - val = NodeManager::currentNM()->mkNode( rk, val, rho ); - val = Rewriter::rewrite( val ); - Trace("cegqi-arith-bound2") << "(after rho) : " << val << std::endl; - } - if( !inf_coeff.isNull() ){ - Assert( !d_vts_sym[0].isNull() ); - val = NodeManager::currentNM()->mkNode( PLUS, val, NodeManager::currentNM()->mkNode( MULT, inf_coeff, d_vts_sym[0] ) ); - val = Rewriter::rewrite( val ); - } - if( !delta_coeff.isNull() ){ - //create delta here if necessary - val = NodeManager::currentNM()->mkNode( PLUS, val, NodeManager::currentNM()->mkNode( MULT, delta_coeff, ci->getQuantifiersEngine()->getTermUtil()->getVtsDelta() ) ); - val = Rewriter::rewrite( val ); - } - return val; -} - -//this isolates the atom into solved form -// veq_c * pv <> val + vts_coeff_delta * delta + vts_coeff_inf * inf -// ensures val is Int if pv is Int, and val does not contain vts symbols -int ArithInstantiator::solve_arith( CegInstantiator * ci, Node pv, Node atom, Node& veq_c, Node& val, Node& vts_coeff_inf, Node& vts_coeff_delta ) { - int ires = 0; - Trace("cegqi-arith-debug") << "isolate for " << pv << " in " << atom << std::endl; - std::map< Node, Node > msum; - if (ArithMSum::getMonomialSumLit(atom, msum)) - { - Trace("cegqi-arith-debug") << "got monomial sum: " << std::endl; - if( Trace.isOn("cegqi-arith-debug") ){ - ArithMSum::debugPrintMonomialSum(msum, "cegqi-arith-debug"); - } - TypeNode pvtn = pv.getType(); - //remove vts symbols from polynomial - Node vts_coeff[2]; - for( unsigned t=0; t<2; t++ ){ - if( !d_vts_sym[t].isNull() ){ - std::map< Node, Node >::iterator itminf = msum.find( d_vts_sym[t] ); - if( itminf!=msum.end() ){ - vts_coeff[t] = itminf->second; - if( vts_coeff[t].isNull() ){ - vts_coeff[t] = NodeManager::currentNM()->mkConst( Rational( 1 ) ); - } - //negate if coefficient on variable is positive - std::map< Node, Node >::iterator itv = msum.find( pv ); - if( itv!=msum.end() ){ - //multiply by the coefficient we will isolate for - if( itv->second.isNull() ){ - vts_coeff[t] = ArithMSum::negate(vts_coeff[t]); - }else{ - if( !pvtn.isInteger() ){ - vts_coeff[t] = NodeManager::currentNM()->mkNode( MULT, NodeManager::currentNM()->mkConst( Rational(-1) / itv->second.getConst() ), vts_coeff[t] ); - vts_coeff[t] = Rewriter::rewrite( vts_coeff[t] ); - }else if( itv->second.getConst().sgn()==1 ){ - vts_coeff[t] = ArithMSum::negate(vts_coeff[t]); - } - } - } - Trace("cegqi-arith-debug") << "vts[" << t << "] coefficient is " << vts_coeff[t] << std::endl; - msum.erase( d_vts_sym[t] ); - } - } - } - - ires = ArithMSum::isolate(pv, msum, veq_c, val, atom.getKind()); - if( ires!=0 ){ - Node realPart; - if( Trace.isOn("cegqi-arith-debug") ){ - Trace("cegqi-arith-debug") << "Isolate : "; - if( !veq_c.isNull() ){ - Trace("cegqi-arith-debug") << veq_c << " * "; - } - Trace("cegqi-arith-debug") << pv << " " << atom.getKind() << " " << val << std::endl; - } - if( options::cbqiAll() ){ - // when not pure LIA/LRA, we must check whether the lhs contains pv - if( TermUtil::containsTerm( val, pv ) ){ - Trace("cegqi-arith-debug") << "fail : contains bad term" << std::endl; - return 0; - } - } - if( pvtn.isInteger() && ( ( !veq_c.isNull() && !veq_c.getType().isInteger() ) || !val.getType().isInteger() ) ){ - //redo, split integer/non-integer parts - bool useCoeff = false; - Integer coeff = ci->getQuantifiersEngine()->getTermUtil()->d_one.getConst().getNumerator(); - for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){ - if( it->first.isNull() || it->first.getType().isInteger() ){ - if( !it->second.isNull() ){ - coeff = coeff.lcm( it->second.getConst().getDenominator() ); - useCoeff = true; - } - } - } - //multiply everything by this coefficient - Node rcoeff = NodeManager::currentNM()->mkConst( Rational( coeff ) ); - std::vector< Node > real_part; - for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){ - if( useCoeff ){ - if( it->second.isNull() ){ - msum[it->first] = rcoeff; - }else{ - msum[it->first] = Rewriter::rewrite( NodeManager::currentNM()->mkNode( MULT, it->second, rcoeff ) ); - } - } - if( !it->first.isNull() && !it->first.getType().isInteger() ){ - real_part.push_back( msum[it->first].isNull() ? it->first : NodeManager::currentNM()->mkNode( MULT, msum[it->first], it->first ) ); - } - } - //remove delta TODO: check this - vts_coeff[1] = Node::null(); - //multiply inf - if( !vts_coeff[0].isNull() ){ - vts_coeff[0] = Rewriter::rewrite( NodeManager::currentNM()->mkNode( MULT, rcoeff, vts_coeff[0] ) ); - } - realPart = real_part.empty() ? ci->getQuantifiersEngine()->getTermUtil()->d_zero : ( real_part.size()==1 ? real_part[0] : NodeManager::currentNM()->mkNode( PLUS, real_part ) ); - Assert( ci->getOutput()->isEligibleForInstantiation( realPart ) ); - //re-isolate - Trace("cegqi-arith-debug") << "Re-isolate..." << std::endl; - ires = ArithMSum::isolate(pv, msum, veq_c, val, atom.getKind()); - Trace("cegqi-arith-debug") << "Isolate for mixed Int/Real : " << veq_c << " * " << pv << " " << atom.getKind() << " " << val << std::endl; - Trace("cegqi-arith-debug") << " real part : " << realPart << std::endl; - if( ires!=0 ){ - int ires_use = ( msum[pv].isNull() || msum[pv].getConst().sgn()==1 ) ? 1 : -1; - val = Rewriter::rewrite( NodeManager::currentNM()->mkNode( ires_use==-1 ? PLUS : MINUS, - NodeManager::currentNM()->mkNode( ires_use==-1 ? MINUS : PLUS, val, realPart ), - NodeManager::currentNM()->mkNode( TO_INTEGER, realPart ) ) ); //TODO: round up for upper bounds? - Trace("cegqi-arith-debug") << "result : " << val << std::endl; - Assert( val.getType().isInteger() ); - } - } - } - vts_coeff_inf = vts_coeff[0]; - vts_coeff_delta = vts_coeff[1]; - Trace("cegqi-arith-debug") << "Return " << veq_c << " * " << pv << " " << atom.getKind() << " " << val << ", vts = (" << vts_coeff_inf << ", " << vts_coeff_delta << ")" << std::endl; - }else{ - Trace("cegqi-arith-debug") << "fail : could not get monomial sum" << std::endl; - } - return ires; -} - -void ArithInstantiator::reset(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) -{ - d_vts_sym[0] = ci->getQuantifiersEngine()->getTermUtil()->getVtsInfinity( d_type, false, false ); - d_vts_sym[1] = ci->getQuantifiersEngine()->getTermUtil()->getVtsDelta( false, false ); - for( unsigned i=0; i<2; i++ ){ - d_mbp_bounds[i].clear(); - d_mbp_coeff[i].clear(); - for( unsigned j=0; j<2; j++ ){ - d_mbp_vts_coeff[i][j].clear(); - } - d_mbp_lit[i].clear(); - } -} - -bool ArithInstantiator::processEquality(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - std::vector& term_props, - std::vector& terms, - CegInstEffort effort) -{ - Node eq_lhs = terms[0]; - Node eq_rhs = terms[1]; - Node lhs_coeff = term_props[0].d_coeff; - Node rhs_coeff = term_props[1].d_coeff; - //make the same coefficient - if( rhs_coeff!=lhs_coeff ){ - if( !rhs_coeff.isNull() ){ - Trace("cegqi-arith-debug") << "...mult lhs by " << rhs_coeff << std::endl; - eq_lhs = NodeManager::currentNM()->mkNode( MULT, rhs_coeff, eq_lhs ); - eq_lhs = Rewriter::rewrite( eq_lhs ); - } - if( !lhs_coeff.isNull() ){ - Trace("cegqi-arith-debug") << "...mult rhs by " << lhs_coeff << std::endl; - eq_rhs = NodeManager::currentNM()->mkNode( MULT, lhs_coeff, eq_rhs ); - eq_rhs = Rewriter::rewrite( eq_rhs ); - } - } - Node eq = eq_lhs.eqNode( eq_rhs ); - eq = Rewriter::rewrite( eq ); - Node val; - TermProperties pv_prop; - Node vts_coeff_inf; - Node vts_coeff_delta; - //isolate pv in the equality - int ires = solve_arith( ci, pv, eq, pv_prop.d_coeff, val, vts_coeff_inf, vts_coeff_delta ); - if( ires!=0 ){ - pv_prop.d_type = 0; - if (ci->constructInstantiationInc(pv, val, pv_prop, sf)) - { - return true; - } - } - - return false; -} - -Node ArithInstantiator::hasProcessAssertion(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - Node lit, - CegInstEffort effort) -{ - Node atom = lit.getKind()==NOT ? lit[0] : lit; - bool pol = lit.getKind()!=NOT; - //arithmetic inequalities and disequalities - if (atom.getKind() == GEQ || - (atom.getKind() == EQUAL && !pol && atom[0].getType().isReal())) { - return lit; - } else { - return Node::null(); - } -} - -bool ArithInstantiator::processAssertion(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - Node lit, - Node alit, - CegInstEffort effort) -{ - Node atom = lit.getKind()==NOT ? lit[0] : lit; - bool pol = lit.getKind()!=NOT; - //arithmetic inequalities and disequalities - Assert( atom.getKind()==GEQ || ( atom.getKind()==EQUAL && !pol && atom[0].getType().isReal() ) ); - // get model value for pv - Node pv_value = ci->getModelValue( pv ); - //cannot contain infinity? - Node vts_coeff_inf; - Node vts_coeff_delta; - Node val; - TermProperties pv_prop; - //isolate pv in the inequality - int ires = solve_arith( ci, pv, atom, pv_prop.d_coeff, val, vts_coeff_inf, vts_coeff_delta ); - if( ires!=0 ){ - //disequalities are either strict upper or lower bounds - unsigned rmax = ( atom.getKind()==GEQ || options::cbqiModel() ) ? 1 : 2; - for( unsigned r=0; rmkNode( PLUS, val, NodeManager::currentNM()->mkConst( Rational( uires ) ) ); - uval = Rewriter::rewrite( uval ); - }else{ - Assert( d_type.isReal() ); - //now is strict inequality - uires = uires*2; - } - } - }else{ - bool is_upper; - if( options::cbqiModel() ){ - // disequality is a disjunction : only consider the bound in the direction of the model - //first check if there is an infinity... - if( !vts_coeff_inf.isNull() ){ - //coefficient or val won't make a difference, just compare with zero - Trace("cegqi-arith-debug") << "Disequality : check infinity polarity " << vts_coeff_inf << std::endl; - Assert( vts_coeff_inf.isConst() ); - is_upper = ( vts_coeff_inf.getConst().sgn()==1 ); - }else{ - Node rhs_value = ci->getModelValue( val ); - Node lhs_value = pv_prop.getModifiedTerm( pv_value ); - if( !pv_prop.isBasic() ){ - lhs_value = pv_prop.getModifiedTerm( pv_value ); - lhs_value = Rewriter::rewrite( lhs_value ); - } - Trace("cegqi-arith-debug") << "Disequality : check model values " << lhs_value << " " << rhs_value << std::endl; - Assert( lhs_value!=rhs_value ); - Node cmp = NodeManager::currentNM()->mkNode( GEQ, lhs_value, rhs_value ); - cmp = Rewriter::rewrite( cmp ); - Assert( cmp.isConst() ); - is_upper = ( cmp!=ci->getQuantifiersEngine()->getTermUtil()->d_true ); - } - }else{ - is_upper = (r==0); - } - Assert( atom.getKind()==EQUAL && !pol ); - if( d_type.isInteger() ){ - uires = is_upper ? -1 : 1; - uval = NodeManager::currentNM()->mkNode( PLUS, val, NodeManager::currentNM()->mkConst( Rational( uires ) ) ); - uval = Rewriter::rewrite( uval ); - }else{ - Assert( d_type.isReal() ); - uires = is_upper ? -2 : 2; - } - } - if( Trace.isOn("cegqi-arith-bound-inf") ){ - Node pvmod = pv_prop.getModifiedTerm( pv ); - Trace("cegqi-arith-bound-inf") << "From " << lit << ", got : "; - Trace("cegqi-arith-bound-inf") << pvmod << " -> " << uval << ", styp = " << uires << std::endl; - } - //take into account delta - if( ci->useVtsDelta() && ( uires==2 || uires==-2 ) ){ - if( options::cbqiModel() ){ - Node delta_coeff = NodeManager::currentNM()->mkConst( Rational( uires > 0 ? 1 : -1 ) ); - if( vts_coeff_delta.isNull() ){ - vts_coeff_delta = delta_coeff; - }else{ - vts_coeff_delta = NodeManager::currentNM()->mkNode( PLUS, vts_coeff_delta, delta_coeff ); - vts_coeff_delta = Rewriter::rewrite( vts_coeff_delta ); - } - }else{ - Node delta = ci->getQuantifiersEngine()->getTermUtil()->getVtsDelta(); - uval = NodeManager::currentNM()->mkNode( uires==2 ? PLUS : MINUS, uval, delta ); - uval = Rewriter::rewrite( uval ); - } - } - if( options::cbqiModel() ){ - //just store bounds, will choose based on tighest bound - unsigned index = uires>0 ? 0 : 1; - d_mbp_bounds[index].push_back( uval ); - d_mbp_coeff[index].push_back( pv_prop.d_coeff ); - Trace("cegqi-arith-debug") << "Store bound " << index << " " << uval << " " << pv_prop.d_coeff << " " << vts_coeff_inf << " " << vts_coeff_delta << " " << lit << std::endl; - for( unsigned t=0; t<2; t++ ){ - d_mbp_vts_coeff[index][t].push_back( t==0 ? vts_coeff_inf : vts_coeff_delta ); - } - d_mbp_lit[index].push_back( lit ); - }else{ - //try this bound - pv_prop.d_type = uires>0 ? 1 : -1; - if (ci->constructInstantiationInc(pv, uval, pv_prop, sf)) - { - return true; - } - } - } - } - - - return false; -} - -bool ArithInstantiator::processAssertions(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) -{ - if (options::cbqiModel()) { - bool use_inf = ci->useVtsInfinity() && ( d_type.isInteger() ? options::cbqiUseInfInt() : options::cbqiUseInfReal() ); - bool upper_first = false; - if( options::cbqiMinBounds() ){ - upper_first = d_mbp_bounds[1].size() t_values[3]; - Node zero = ci->getQuantifiersEngine()->getTermUtil()->d_zero; - Node one = ci->getQuantifiersEngine()->getTermUtil()->d_one; - Node pv_value = ci->getModelValue( pv ); - //try optimal bounds - for( unsigned r=0; r<2; r++ ){ - int rr = upper_first ? (1-r) : r; - best_used[rr] = -1; - if( d_mbp_bounds[rr].empty() ){ - if( use_inf ){ - Trace("cegqi-arith-bound") << "No " << ( rr==0 ? "lower" : "upper" ) << " bounds for " << pv << " (type=" << d_type << ")" << std::endl; - //no bounds, we do +- infinity - Node val = ci->getQuantifiersEngine()->getTermUtil()->getVtsInfinity( d_type ); - //TODO : rho value for infinity? - if( rr==0 ){ - val = NodeManager::currentNM()->mkNode( UMINUS, val ); - val = Rewriter::rewrite( val ); - } - TermProperties pv_prop_no_bound; - if (ci->constructInstantiationInc(pv, val, pv_prop_no_bound, sf)) - { - return true; - } - } - }else{ - Trace("cegqi-arith-bound") << ( rr==0 ? "Lower" : "Upper" ) << " bounds for " << pv << " (type=" << d_type << ") : " << std::endl; - int best = -1; - Node best_bound_value[3]; - for( unsigned j=0; jgetModelValue( d_mbp_bounds[rr][j] ); - t_values[rr][j] = t_value; - value[1] = t_value; - Trace("cegqi-arith-bound") << value[1]; - }else{ - value[2] = d_mbp_vts_coeff[rr][1][j]; - if( !value[2].isNull() ){ - Trace("cegqi-arith-bound") << " + ( " << value[2] << " * DELTA )"; - }else{ - value[2] = zero; - } - } - //multiply by coefficient - if( value[t]!=zero && !d_mbp_coeff[rr][j].isNull() ){ - Assert( d_mbp_coeff[rr][j].isConst() ); - value[t] = NodeManager::currentNM()->mkNode( MULT, NodeManager::currentNM()->mkConst( Rational(1) / d_mbp_coeff[rr][j].getConst() ), value[t] ); - value[t] = Rewriter::rewrite( value[t] ); - } - //check if new best - if( best!=-1 ){ - Assert( !value[t].isNull() && !best_bound_value[t].isNull() ); - if( value[t]!=best_bound_value[t] ){ - Kind k = rr==0 ? GEQ : LEQ; - Node cmp_bound = NodeManager::currentNM()->mkNode( k, value[t], best_bound_value[t] ); - cmp_bound = Rewriter::rewrite( cmp_bound ); - if( cmp_bound!=ci->getQuantifiersEngine()->getTermUtil()->d_true ){ - new_best = false; - break; - } - } - } - } - Trace("cegqi-arith-bound") << std::endl; - if( new_best ){ - for( unsigned t=0; t<3; t++ ){ - best_bound_value[t] = value[t]; - } - best = j; - } - } - if( best!=-1 ){ - Trace("cegqi-arith-bound") << "...best bound is " << best << " : "; - if( best_bound_value[0]!=zero ){ - Trace("cegqi-arith-bound") << "( " << best_bound_value[0] << " * INF ) + "; - } - Trace("cegqi-arith-bound") << best_bound_value[1]; - if( best_bound_value[2]!=zero ){ - Trace("cegqi-arith-bound") << " + ( " << best_bound_value[2] << " * DELTA )"; - } - Trace("cegqi-arith-bound") << std::endl; - best_used[rr] = best; - //if using cbqiMidpoint, only add the instance based on one bound if the bound is non-strict - if (!options::cbqiMidpoint() || d_type.isInteger() - || (ci->useVtsDelta() && d_mbp_vts_coeff[rr][1][best].isNull())) - { - Node val = d_mbp_bounds[rr][best]; - val = getModelBasedProjectionValue( ci, pv, val, rr==0, d_mbp_coeff[rr][best], pv_value, t_values[rr][best], sf.getTheta(), - d_mbp_vts_coeff[rr][0][best], d_mbp_vts_coeff[rr][1][best] ); - if( !val.isNull() ){ - TermProperties pv_prop_bound; - pv_prop_bound.d_coeff = d_mbp_coeff[rr][best]; - pv_prop_bound.d_type = rr==0 ? 1 : -1; - if (ci->constructInstantiationInc(pv, val, pv_prop_bound, sf)) - { - return true; - } - } - } - } - } - } - //if not using infinity, use model value of zero - if( !use_inf && d_mbp_bounds[0].empty() && d_mbp_bounds[1].empty() ){ - Node val = zero; - TermProperties pv_prop_zero; - Node theta = sf.getTheta(); - val = getModelBasedProjectionValue( ci, pv, val, true, pv_prop_zero.d_coeff, pv_value, zero, sf.getTheta(), Node::null(), Node::null() ); - if( !val.isNull() ){ - if (ci->constructInstantiationInc(pv, val, pv_prop_zero, sf)) - { - return true; - } - } - } - if( options::cbqiMidpoint() && !d_type.isInteger() ){ - Node vals[2]; - bool bothBounds = true; - Trace("cegqi-arith-bound") << "Try midpoint of bounds..." << std::endl; - for( unsigned rr=0; rr<2; rr++ ){ - int best = best_used[rr]; - if( best==-1 ){ - bothBounds = false; - }else{ - vals[rr] = d_mbp_bounds[rr][best]; - vals[rr] = getModelBasedProjectionValue( ci, pv, vals[rr], rr==0, Node::null(), pv_value, t_values[rr][best], sf.getTheta(), - d_mbp_vts_coeff[rr][0][best], Node::null() ); - } - Trace("cegqi-arith-bound") << "Bound : " << vals[rr] << std::endl; - } - Node val; - if( bothBounds ){ - Assert( !vals[0].isNull() && !vals[1].isNull() ); - if( vals[0]==vals[1] ){ - val = vals[0]; - }else{ - val = NodeManager::currentNM()->mkNode( MULT, NodeManager::currentNM()->mkNode( PLUS, vals[0], vals[1] ), - NodeManager::currentNM()->mkConst( Rational(1)/Rational(2) ) ); - val = Rewriter::rewrite( val ); - } - }else{ - if( !vals[0].isNull() ){ - val = NodeManager::currentNM()->mkNode( PLUS, vals[0], one ); - val = Rewriter::rewrite( val ); - }else if( !vals[1].isNull() ){ - val = NodeManager::currentNM()->mkNode( MINUS, vals[1], one ); - val = Rewriter::rewrite( val ); - } - } - Trace("cegqi-arith-bound") << "Midpoint value : " << val << std::endl; - if( !val.isNull() ){ - TermProperties pv_prop_midpoint; - if (ci->constructInstantiationInc(pv, val, pv_prop_midpoint, sf)) - { - return true; - } - } - } - //generally should not make it to this point FIXME: write proper assertion - //Assert( ( ci->hasNestedQuantification() && !options::cbqiNestedQE() ) || options::cbqiAll() ); - - if( options::cbqiNopt() ){ - //try non-optimal bounds (heuristic, may help when nested quantification) ? - Trace("cegqi-arith-bound") << "Try non-optimal bounds..." << std::endl; - for( unsigned r=0; r<2; r++ ){ - int rr = upper_first ? (1-r) : r; - for( unsigned j=0; jconstructInstantiationInc( - pv, val, pv_prop_nopt_bound, sf)) - { - return true; - } - } - } - } - } - } - } - return false; -} - -bool ArithInstantiator::needsPostProcessInstantiationForVariable( - CegInstantiator* ci, SolvedForm& sf, Node pv, CegInstEffort effort) -{ - return std::find( sf.d_non_basic.begin(), sf.d_non_basic.end(), pv )!=sf.d_non_basic.end(); -} - -bool ArithInstantiator::postProcessInstantiationForVariable( - CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort, - std::vector& lemmas) -{ - Assert( std::find( sf.d_non_basic.begin(), sf.d_non_basic.end(), pv )!=sf.d_non_basic.end() ); - Assert( std::find( sf.d_vars.begin(), sf.d_vars.end(), pv )!=sf.d_vars.end() ); - unsigned index = std::find( sf.d_vars.begin(), sf.d_vars.end(), pv )-sf.d_vars.begin(); - Assert( !sf.d_props[index].isBasic() ); - Node eq_lhs = sf.d_props[index].getModifiedTerm( sf.d_vars[index] ); - if( Trace.isOn("cegqi-arith-debug") ){ - Trace("cegqi-arith-debug") << "Normalize substitution for "; - Trace("cegqi-arith-debug") << eq_lhs << " = " << sf.d_subs[index] << std::endl; - } - Assert( sf.d_vars[index].getType().isInteger() ); - //must ensure that divisibility constraints are met - //solve updated rewritten equality for vars[index], if coefficient is one, then we are successful - Node eq_rhs = sf.d_subs[index]; - Node eq = eq_lhs.eqNode( eq_rhs ); - eq = Rewriter::rewrite( eq ); - Trace("cegqi-arith-debug") << "...equality is " << eq << std::endl; - std::map< Node, Node > msum; - if (ArithMSum::getMonomialSumLit(eq, msum)) - { - Node veq; - if (ArithMSum::isolate(sf.d_vars[index], msum, veq, EQUAL, true) != 0) - { - Node veq_c; - if( veq[0]!=sf.d_vars[index] ){ - Node veq_v; - if (ArithMSum::getMonomial(veq[0], veq_c, veq_v)) - { - Assert( veq_v==sf.d_vars[index] ); - } - } - sf.d_subs[index] = veq[1]; - if( !veq_c.isNull() ){ - sf.d_subs[index] = NodeManager::currentNM()->mkNode( INTS_DIVISION_TOTAL, veq[1], veq_c ); - Trace("cegqi-arith-debug") << "...bound type is : " << sf.d_props[index].d_type << std::endl; - //intger division rounding up if from a lower bound - if( sf.d_props[index].d_type==1 && options::cbqiRoundUpLowerLia() ){ - sf.d_subs[index] = NodeManager::currentNM()->mkNode( PLUS, sf.d_subs[index], - NodeManager::currentNM()->mkNode( ITE, - NodeManager::currentNM()->mkNode( EQUAL, - NodeManager::currentNM()->mkNode( INTS_MODULUS_TOTAL, veq[1], veq_c ), - ci->getQuantifiersEngine()->getTermUtil()->d_zero ), - ci->getQuantifiersEngine()->getTermUtil()->d_zero, ci->getQuantifiersEngine()->getTermUtil()->d_one ) - ); - } - } - Trace("cegqi-arith-debug") << "...normalize integers : " << sf.d_vars[index] << " -> " << sf.d_subs[index] << std::endl; - }else{ - Trace("cegqi-arith-debug") << "...failed to isolate." << std::endl; - return false; - } - }else{ - Trace("cegqi-arith-debug") << "...failed to get monomial sum." << std::endl; - return false; - } - return true; -} - -void DtInstantiator::reset(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) -{ -} - -Node DtInstantiator::solve_dt( Node v, Node a, Node b, Node sa, Node sb ) { - Trace("cegqi-arith-debug2") << "Solve dt : " << v << " " << a << " " << b << " " << sa << " " << sb << std::endl; - Node ret; - if( !a.isNull() && a==v ){ - ret = sb; - }else if( !b.isNull() && b==v ){ - ret = sa; - }else if( !a.isNull() && a.getKind()==APPLY_CONSTRUCTOR ){ - if( !b.isNull() && b.getKind()==APPLY_CONSTRUCTOR ){ - if( a.getOperator()==b.getOperator() ){ - for( unsigned i=0; imkNode( APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[cindex].getSelectorInternal( tn.toType(), i ) ), sb ); - Node s = solve_dt( v, a[i], Node::null(), sa[i], nn ); - if( !s.isNull() ){ - return s; - } - } - } - }else if( !b.isNull() && b.getKind()==APPLY_CONSTRUCTOR ){ - return solve_dt( v, b, a, sb, sa ); - } - if( !ret.isNull() ){ - //ensure does not contain - if( TermUtil::containsTerm( ret, v ) ){ - ret = Node::null(); - } - } - return ret; -} - -bool DtInstantiator::processEqualTerms(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - std::vector& eqc, - CegInstEffort effort) -{ - Trace("cegqi-dt-debug") << "try based on constructors in equivalence class." - << std::endl; - // look in equivalence class for a constructor - for( unsigned k=0; k children; - children.push_back( n.getOperator() ); - const Datatype& dt = ((DatatypeType)(d_type).toType()).getDatatype(); - unsigned cindex = Datatype::indexOf( n.getOperator().toExpr() ); - //now must solve for selectors applied to pv - for( unsigned j=0; jmkNode( APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[cindex].getSelectorInternal( d_type.toType(), j ) ), pv ); - ci->pushStackVariable( c ); - children.push_back( c ); - } - Node val = NodeManager::currentNM()->mkNode( kind::APPLY_CONSTRUCTOR, children ); - TermProperties pv_prop_dt; - if (ci->constructInstantiationInc(pv, val, pv_prop_dt, sf)) - { - return true; - }else{ - //cleanup - for( unsigned j=0; jpopStackVariable(); - } - break; - } - } - } - return false; -} - -bool DtInstantiator::processEquality(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - std::vector& term_props, - std::vector& terms, - CegInstEffort effort) -{ - Node val = solve_dt( pv, terms[0], terms[1], terms[0], terms[1] ); - if( !val.isNull() ){ - TermProperties pv_prop; - if (ci->constructInstantiationInc(pv, val, pv_prop, sf)) - { - return true; - } - } - return false; -} - -void EprInstantiator::reset(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) -{ - d_equal_terms.clear(); -} - -bool EprInstantiator::processEqualTerm(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - TermProperties& pv_prop, - Node n, - CegInstEffort effort) -{ - if( options::quantEprMatching() ){ - Assert( pv_prop.isBasic() ); - d_equal_terms.push_back( n ); - return false; - }else{ - pv_prop.d_type = 0; - return ci->constructInstantiationInc(pv, n, pv_prop, sf); - } -} - -void EprInstantiator::computeMatchScore( CegInstantiator * ci, Node pv, Node catom, std::vector< Node >& arg_reps, TermArgTrie * tat, unsigned index, std::map< Node, int >& match_score ) { - if( index==catom.getNumChildren() ){ - Assert( tat->hasNodeData() ); - Node gcatom = tat->getNodeData(); - Trace("cegqi-epr") << "Matched : " << catom << " and " << gcatom << std::endl; - for( unsigned i=0; i::iterator it = tat->d_data.find( arg_reps[index] ); - if( it!=tat->d_data.end() ){ - computeMatchScore( ci, pv, catom, arg_reps, &it->second, index+1, match_score ); - } - } -} - -void EprInstantiator::computeMatchScore( CegInstantiator * ci, Node pv, Node catom, Node eqc, std::map< Node, int >& match_score ) { - if( inst::Trigger::isAtomicTrigger( catom ) && TermUtil::containsTerm( catom, pv ) ){ - Trace("cegqi-epr") << "Find matches for " << catom << "..." << std::endl; - std::vector< Node > arg_reps; - for( unsigned j=0; jgetQuantifiersEngine()->getMasterEqualityEngine()->getRepresentative( catom[j] ) ); - } - if( ci->getQuantifiersEngine()->getMasterEqualityEngine()->hasTerm( eqc ) ){ - Node rep = ci->getQuantifiersEngine()->getMasterEqualityEngine()->getRepresentative( eqc ); - Node op = ci->getQuantifiersEngine()->getTermDatabase()->getMatchOperator( catom ); - TermArgTrie * tat = ci->getQuantifiersEngine()->getTermDatabase()->getTermArgTrie( rep, op ); - Trace("cegqi-epr") << "EPR instantiation match term : " << catom << ", check ground terms=" << (tat!=NULL) << std::endl; - if( tat ){ - computeMatchScore( ci, pv, catom, arg_reps, tat, 0, match_score ); - } - } - } -} - -struct sortEqTermsMatch { - std::map< Node, int > d_match_score; - bool operator() (Node i, Node j) { - int match_score_i = d_match_score[i]; - int match_score_j = d_match_score[j]; - return match_score_i>match_score_j || ( match_score_i==match_score_j && i& eqc, - CegInstEffort effort) -{ - if( options::quantEprMatching() ){ - //heuristic for best matching constant - sortEqTermsMatch setm; - for( unsigned i=0; igetNumCEAtoms(); i++ ){ - Node catom = ci->getCEAtom( i ); - computeMatchScore( ci, pv, catom, catom, setm.d_match_score ); - } - //sort by match score - std::sort( d_equal_terms.begin(), d_equal_terms.end(), setm ); - TermProperties pv_prop; - pv_prop.d_type = 0; - for( unsigned i=0; iconstructInstantiationInc(pv, d_equal_terms[i], pv_prop, sf)) - { - return true; - } - } - } - return false; -} - -// this class can be used to query the model value through the CegInstaniator class -class CegInstantiatorBvInverterQuery : public BvInverterQuery -{ - public: - CegInstantiatorBvInverterQuery(CegInstantiator* ci) - : BvInverterQuery(), d_ci(ci) - { - } - ~CegInstantiatorBvInverterQuery() {} - /** return the model value of n */ - Node getModelValue( Node n ) { - return d_ci->getModelValue( n ); - } - /** get bound variable of type tn */ - Node getBoundVariable(TypeNode tn) { return d_ci->getBoundVariable(tn); } - protected: - // pointer to class that is able to query model values - CegInstantiator * d_ci; -}; - -BvInstantiator::BvInstantiator(QuantifiersEngine* qe, TypeNode tn) - : Instantiator(qe, tn), d_tried_assertion_inst(false) -{ - // get the global inverter utility - // this must be global since we need to: - // * process Skolem functions properly across multiple variables within the same quantifier - // * cache Skolem variables uniformly across multiple quantified formulas - d_inverter = qe->getBvInverter(); -} - -BvInstantiator::~BvInstantiator(){ - -} -void BvInstantiator::reset(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) -{ - d_inst_id_counter = 0; - d_var_to_inst_id.clear(); - d_inst_id_to_term.clear(); - d_inst_id_to_alit.clear(); - d_var_to_curr_inst_id.clear(); - d_alit_to_model_slack.clear(); - d_tried_assertion_inst = false; -} - -void BvInstantiator::processLiteral(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - Node lit, - Node alit, - CegInstEffort effort) -{ - Assert(d_inverter != NULL); - // find path to pv - std::vector path; - Node sv = d_inverter->getSolveVariable(pv.getType()); - Node pvs = ci->getModelValue(pv); - Trace("cegqi-bv") << "Get path to pv : " << lit << std::endl; - Node slit = d_inverter->getPathToPv(lit, pv, sv, pvs, path); - if (!slit.isNull()) - { - CegInstantiatorBvInverterQuery m(ci); - unsigned iid = d_inst_id_counter; - Trace("cegqi-bv") << "Solve lit to bv inverter : " << slit << std::endl; - Node inst = d_inverter->solveBvLit(sv, slit, path, &m); - if (!inst.isNull()) - { - inst = Rewriter::rewrite(inst); - if (inst.isConst() || !ci->hasNestedQuantification()) - { - Trace("cegqi-bv") << "...solved form is " << inst << std::endl; - // store information for id and increment - d_var_to_inst_id[pv].push_back(iid); - d_inst_id_to_term[iid] = inst; - d_inst_id_to_alit[iid] = alit; - d_inst_id_counter++; - } - } - else - { - Trace("cegqi-bv") << "...failed to solve." << std::endl; - } - } -} - -Node BvInstantiator::hasProcessAssertion(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - Node lit, - CegInstEffort effort) -{ - if (effort == CEG_INST_EFFORT_FULL) - { - // always use model values at full effort - return Node::null(); - } - Node atom = lit.getKind() == NOT ? lit[0] : lit; - bool pol = lit.getKind() != NOT; - Kind k = atom.getKind(); - if (k != EQUAL && k != BITVECTOR_ULT && k != BITVECTOR_SLT) - { - // others are unhandled - return Node::null(); - } - else if (!atom[0].getType().isBitVector()) - { - return Node::null(); - } - else if (options::cbqiBvIneqMode() == CBQI_BV_INEQ_KEEP - || (pol && k == EQUAL)) - { - return lit; - } - NodeManager* nm = NodeManager::currentNM(); - Node s = atom[0]; - Node t = atom[1]; - - Node sm = ci->getModelValue(s); - Node tm = ci->getModelValue(t); - Assert(!sm.isNull() && sm.isConst()); - Assert(!tm.isNull() && tm.isConst()); - Trace("cegqi-bv") << "Model value: " << std::endl; - Trace("cegqi-bv") << " " << s << " " << k << " " << t << " is " - << std::endl; - Trace("cegqi-bv") << " " << sm << " <> " << tm << std::endl; - - Node ret; - if (options::cbqiBvIneqMode() == CBQI_BV_INEQ_EQ_SLACK) - { - // if using slack, we convert constraints to a positive equality based on - // the current model M, e.g.: - // (not) s ~ t ---> s = t + ( s^M - t^M ) - if (sm != tm) - { - Node slack = Rewriter::rewrite(nm->mkNode(BITVECTOR_SUB, sm, tm)); - Assert(slack.isConst()); - // remember the slack value for the asserted literal - d_alit_to_model_slack[lit] = slack; - ret = nm->mkNode(EQUAL, s, nm->mkNode(BITVECTOR_PLUS, t, slack)); - Trace("cegqi-bv") << "Slack is " << slack << std::endl; - } - else - { - ret = s.eqNode(t); - } - } else { - // turn disequality into an inequality - // e.g. s != t becomes s < t or t < s - if (k == EQUAL) - { - if (Random::getRandom().pickWithProb(0.5)) - { - std::swap(s, t); - } - pol = true; - } - // otherwise, we optimistically solve for the boundary point of an - // inequality, for example: - // for s < t, we solve s+1 = t - // for ~( s < t ), we solve s = t - // notice that this equality does not necessarily hold in the model, and - // hence the corresponding instantiation strategy is not guaranteed to be - // monotonic. - if (!pol) - { - ret = s.eqNode(t); - } else { - Node bv_one = bv::utils::mkOne(bv::utils::getSize(s)); - ret = nm->mkNode(BITVECTOR_PLUS, s, bv_one).eqNode(t); - } - } - Trace("cegqi-bv") << "Process " << lit << " as " << ret << std::endl; - return ret; -} - -bool BvInstantiator::processAssertion(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - Node lit, - Node alit, - CegInstEffort effort) -{ - // if option enabled, use approach for word-level inversion for BV instantiation - if( options::cbqiBv() ){ - // get the best rewritten form of lit for solving for pv - // this should remove instances of non-invertible operators, and "linearize" lit with respect to pv as much as possible - Node rlit = rewriteAssertionForSolvePv(ci, pv, lit); - if( Trace.isOn("cegqi-bv") ){ - Trace("cegqi-bv") << "BvInstantiator::processAssertion : solve " << pv << " in " << lit << std::endl; - if( lit!=rlit ){ - Trace("cegqi-bv") << "...rewritten to " << rlit << std::endl; - } - } - if (!rlit.isNull()) - { - processLiteral(ci, sf, pv, rlit, alit, effort); - } - } - return false; -} - -bool BvInstantiator::useModelValue(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) -{ - return !d_tried_assertion_inst - && (effort < CEG_INST_EFFORT_FULL || options::cbqiFullEffort()); -} - -bool BvInstantiator::processAssertions(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) -{ - std::unordered_map< Node, std::vector< unsigned >, NodeHashFunction >::iterator iti = d_var_to_inst_id.find( pv ); - if( iti!=d_var_to_inst_id.end() ){ - Trace("cegqi-bv") << "BvInstantiator::processAssertions for " << pv << std::endl; - // if interleaving, do not do inversion half the time - if (!options::cbqiBvInterleaveValue() || Random::getRandom().pickWithProb(0.5)) - { - bool firstVar = sf.empty(); - // get inst id list - if (Trace.isOn("cegqi-bv")) - { - Trace("cegqi-bv") << " " << iti->second.size() - << " candidate instantiations for " << pv << " : " - << std::endl; - if (firstVar) - { - Trace("cegqi-bv") << " ...this is the first variable" << std::endl; - } - } - // until we have a model-preserving selection function for BV, this must - // be heuristic (we only pick one literal) - // hence we randomize the list - // this helps robustness, since picking the same literal every time may - // lead to a stream of value instantiations, whereas with randomization - // we may find an invertible literal that leads to a useful instantiation. - std::random_shuffle(iti->second.begin(), iti->second.end()); - - if (Trace.isOn("cegqi-bv")) - { - for (unsigned j = 0, size = iti->second.size(); j < size; j++) - { - unsigned inst_id = iti->second[j]; - Assert(d_inst_id_to_term.find(inst_id) != d_inst_id_to_term.end()); - Node inst_term = d_inst_id_to_term[inst_id]; - Assert(d_inst_id_to_alit.find(inst_id) != d_inst_id_to_alit.end()); - Node alit = d_inst_id_to_alit[inst_id]; - - // get the slack value introduced for the asserted literal - Node curr_slack_val; - std::unordered_map::iterator itms = - d_alit_to_model_slack.find(alit); - if (itms != d_alit_to_model_slack.end()) - { - curr_slack_val = itms->second; - } - - // debug printing - Trace("cegqi-bv") << " [" << j << "] : "; - Trace("cegqi-bv") << inst_term << std::endl; - if (!curr_slack_val.isNull()) { - Trace("cegqi-bv") << " ...with slack value : " << curr_slack_val - << std::endl; - } - Trace("cegqi-bv-debug") << " ...from : " << alit << std::endl; - Trace("cegqi-bv") << std::endl; - } - } - - // Now, try all instantiation ids we want to try - // Typically we try only one, otherwise worst-case performance - // for constructing instantiations is exponential on the number of - // variables in this quantifier prefix. - bool ret = false; - bool tryMultipleInst = firstVar && options::cbqiMultiInst(); - bool revertOnSuccess = tryMultipleInst; - for (unsigned j = 0, size = iti->second.size(); j < size; j++) - { - unsigned inst_id = iti->second[j]; - Assert(d_inst_id_to_term.find(inst_id) != d_inst_id_to_term.end()); - Node inst_term = d_inst_id_to_term[inst_id]; - Node alit = d_inst_id_to_alit[inst_id]; - // try instantiation pv -> inst_term - TermProperties pv_prop_bv; - Trace("cegqi-bv") << "*** try " << pv << " -> " << inst_term - << std::endl; - d_var_to_curr_inst_id[pv] = inst_id; - d_tried_assertion_inst = true; - ci->markSolved(alit); - if (ci->constructInstantiationInc( - pv, inst_term, pv_prop_bv, sf, revertOnSuccess)) - { - ret = true; - } - ci->markSolved(alit, false); - // we are done unless we try multiple instances - if (!tryMultipleInst) - { - break; - } - } - if (ret) - { - return true; - } - Trace("cegqi-bv") << "...failed to add instantiation for " << pv - << std::endl; - d_var_to_curr_inst_id.erase(pv); - } else { - Trace("cegqi-bv") << "...do not do instantiation for " << pv - << " (skip, based on heuristic)" << std::endl; - } - } - - return false; -} - -Node BvInstantiator::rewriteAssertionForSolvePv(CegInstantiator* ci, - Node pv, - Node lit) -{ - // result of rewriting the visited term - std::stack > visited; - visited.push(std::unordered_map()); - // whether the visited term contains pv - std::unordered_map visited_contains_pv; - std::unordered_map::iterator it; - std::unordered_map curr_subs; - std::stack > visit; - TNode cur; - visit.push(std::stack()); - visit.top().push(lit); - do { - cur = visit.top().top(); - visit.top().pop(); - it = visited.top().find(cur); - - if (it == visited.top().end()) - { - std::unordered_map::iterator itc = - curr_subs.find(cur); - if (itc != curr_subs.end()) - { - visited.top()[cur] = itc->second; - } - else - { - if (cur.getKind() == CHOICE) - { - // must replace variables of choice functions - // with new variables to avoid variable - // capture when considering substitutions - // with multiple literals. - Node bv = ci->getBoundVariable(cur[0][0].getType()); - // should not have captured variables - Assert(curr_subs.find(cur[0][0]) == curr_subs.end()); - curr_subs[cur[0][0]] = bv; - // we cannot cache the results of subterms - // of this choice expression since we are - // now in the context { cur[0][0] -> bv }, - // hence we push a context here - visited.push(std::unordered_map()); - visit.push(std::stack()); - } - visited.top()[cur] = Node::null(); - visit.top().push(cur); - for (unsigned i = 0; i < cur.getNumChildren(); i++) - { - visit.top().push(cur[i]); - } - } - } else if (it->second.isNull()) { - Node ret; - bool childChanged = false; - std::vector children; - if (cur.getMetaKind() == kind::metakind::PARAMETERIZED) { - children.push_back(cur.getOperator()); - } - bool contains_pv = ( cur==pv ); - for (unsigned i = 0; i < cur.getNumChildren(); i++) { - it = visited.top().find(cur[i]); - Assert(it != visited.top().end()); - Assert(!it->second.isNull()); - childChanged = childChanged || cur[i] != it->second; - children.push_back(it->second); - contains_pv = contains_pv || visited_contains_pv[cur[i]]; - } - // careful that rewrites above do not affect whether this term contains pv - visited_contains_pv[cur] = contains_pv; - - // rewrite the term - ret = rewriteTermForSolvePv(pv, cur, children, visited_contains_pv); - - // return original if the above function does not produce a result - if (ret.isNull()) { - if (childChanged) { - ret = NodeManager::currentNM()->mkNode(cur.getKind(), children); - }else{ - ret = cur; - } - } - - /* We need to update contains_pv also for rewritten nodes, since - * the normalizePv* functions rely on the information if pv occurs in a - * rewritten node or not. */ - if (ret != cur) - { - contains_pv = (ret == pv); - for (unsigned i = 0, size = ret.getNumChildren(); i < size; ++i) - { - contains_pv = contains_pv || visited_contains_pv[ret[i]]; - } - visited_contains_pv[ret] = contains_pv; - } - - // if was choice, pop context - if (cur.getKind() == CHOICE) - { - Assert(curr_subs.find(cur[0][0]) != curr_subs.end()); - curr_subs.erase(cur[0][0]); - visited.pop(); - visit.pop(); - Assert(visited.size() == visit.size()); - Assert(!visit.empty()); - } - - visited.top()[cur] = ret; - } - } while (!visit.top().empty()); - Assert(visited.size() == 1); - Assert(visited.top().find(lit) != visited.top().end()); - Assert(!visited.top().find(lit)->second.isNull()); - - Node result = visited.top()[lit]; - - if (Trace.isOn("cegqi-bv-nl")) - { - std::vector trace_visit; - std::unordered_set trace_visited; - - trace_visit.push_back(result); - do - { - cur = trace_visit.back(); - trace_visit.pop_back(); - - if (trace_visited.find(cur) == trace_visited.end()) - { - trace_visited.insert(cur); - trace_visit.insert(trace_visit.end(), cur.begin(), cur.end()); - } - else if (cur == pv) - { - Trace("cegqi-bv-nl") - << "NONLINEAR LITERAL for " << pv << " : " << lit << std::endl; - } - } while (!trace_visit.empty()); - } - - return result; -} - -/** - * Determine coefficient of pv in term n, where n has the form pv, -pv, pv * t, - * or -pv * t. Extracting the coefficient of multiplications only succeeds if - * the multiplication are normalized with normalizePvMult. - * - * If sucessful it returns - * 1 if n == pv, - * -1 if n == -pv, - * t if n == pv * t, - * -t if n == -pv * t. - * If n is not a linear term, a null node is returned. - */ -static Node getPvCoeff(TNode pv, TNode n) -{ - bool neg = false; - Node coeff; - - if (n.getKind() == BITVECTOR_NEG) - { - neg = true; - n = n[0]; - } - - if (n == pv) - { - coeff = bv::utils::mkOne(bv::utils::getSize(pv)); - } - /* All multiplications are normalized to pv * (t1 * t2). */ - else if (n.getKind() == BITVECTOR_MULT && n.getAttribute(BvLinearAttribute())) - { - Assert(n.getNumChildren() == 2); - Assert(n[0] == pv); - coeff = n[1]; - } - else /* n is in no form to extract the coefficient for pv */ - { - Trace("cegqi-bv-nl") << "Cannot extract coefficient for " << pv << " in " - << n << std::endl; - return Node::null(); - } - Assert(!coeff.isNull()); - - if (neg) return NodeManager::currentNM()->mkNode(BITVECTOR_NEG, coeff); - return coeff; -} - -/** - * Normalizes the children of a BITVECTOR_MULT w.r.t. pv. contains_pv marks - * terms in which pv occurs. - * For example, - * - * a * -pv * b * c - * - * is rewritten to - * - * pv * -(a * b * c) - * - * Returns the normalized node if the resulting term is linear w.r.t. pv and - * a null node otherwise. If pv does not occur in children it returns a - * multiplication over children. - */ -static Node normalizePvMult( - TNode pv, - const std::vector& children, - std::unordered_map& contains_pv) -{ - bool neg, neg_coeff = false; - bool found_pv = false; - NodeManager* nm; - NodeBuilder<> nb(BITVECTOR_MULT); - BvLinearAttribute is_linear; - - nm = NodeManager::currentNM(); - for (TNode nc : children) - { - if (!contains_pv[nc]) - { - nb << nc; - continue; - } - - neg = false; - if (nc.getKind() == BITVECTOR_NEG) - { - neg = true; - nc = nc[0]; - } - - if (!found_pv && nc == pv) - { - found_pv = true; - neg_coeff = neg; - continue; - } - else if (!found_pv && nc.getKind() == BITVECTOR_MULT - && nc.getAttribute(is_linear)) - { - Assert(nc.getNumChildren() == 2); - Assert(nc[0] == pv); - Assert(!contains_pv[nc[1]]); - found_pv = true; - neg_coeff = neg; - nb << nc[1]; - continue; - } - return Node::null(); /* non-linear */ - } - Assert(nb.getNumChildren() > 0); - - Node coeff = (nb.getNumChildren() == 1) ? nb[0] : nb.constructNode(); - if (neg_coeff) - { - coeff = nm->mkNode(BITVECTOR_NEG, coeff); - } - coeff = Rewriter::rewrite(coeff); - unsigned size_coeff = bv::utils::getSize(coeff); - Node zero = bv::utils::mkZero(size_coeff); - if (coeff == zero) - { - return zero; - } - Node result; - if (found_pv) - { - if (coeff == bv::utils::mkOne(size_coeff)) - { - return pv; - } - result = nm->mkNode(BITVECTOR_MULT, pv, coeff); - contains_pv[result] = true; - result.setAttribute(is_linear, true); - } - else - { - result = coeff; - } - return result; -} - -#ifdef CVC4_ASSERTIONS -static bool isLinearPlus( - TNode n, - TNode pv, - std::unordered_map& contains_pv) -{ - Node coeff; - Assert(n.getAttribute(BvLinearAttribute())); - Assert(n.getNumChildren() == 2); - if (n[0] != pv) - { - Assert(n[0].getKind() == BITVECTOR_MULT); - Assert(n[0].getNumChildren() == 2); - Assert(n[0][0] == pv); - Assert(!contains_pv[n[0][1]]); - } - Assert(!contains_pv[n[1]]); - coeff = getPvCoeff(pv, n[0]); - Assert(!coeff.isNull()); - Assert(!contains_pv[coeff]); - return true; -} -#endif - -/** - * Normalizes the children of a BITVECTOR_PLUS w.r.t. pv. contains_pv marks - * terms in which pv occurs. - * For example, - * - * a * pv + b + c * -pv - * - * is rewritten to - * - * pv * (a - c) + b - * - * Returns the normalized node if the resulting term is linear w.r.t. pv and - * a null node otherwise. If pv does not occur in children it returns an - * addition over children. - */ -static Node normalizePvPlus( - Node pv, - const std::vector& children, - std::unordered_map& contains_pv) -{ - NodeManager* nm; - NodeBuilder<> nb_c(BITVECTOR_PLUS); - NodeBuilder<> nb_l(BITVECTOR_PLUS); - BvLinearAttribute is_linear; - bool neg; - - nm = NodeManager::currentNM(); - for (TNode nc : children) - { - if (!contains_pv[nc]) - { - nb_l << nc; - continue; - } - - neg = false; - if (nc.getKind() == BITVECTOR_NEG) - { - neg = true; - nc = nc[0]; - } - - if (nc == pv - || (nc.getKind() == BITVECTOR_MULT && nc.getAttribute(is_linear))) - { - Node coeff = getPvCoeff(pv, nc); - Assert(!coeff.isNull()); - if (neg) - { - coeff = nm->mkNode(BITVECTOR_NEG, coeff); - } - nb_c << coeff; - continue; - } - else if (nc.getKind() == BITVECTOR_PLUS && nc.getAttribute(is_linear)) - { - Assert(isLinearPlus(nc, pv, contains_pv)); - Node coeff = getPvCoeff(pv, nc[0]); - Assert(!coeff.isNull()); - Node leaf = nc[1]; - if (neg) - { - coeff = nm->mkNode(BITVECTOR_NEG, coeff); - leaf = nm->mkNode(BITVECTOR_NEG, leaf); - } - nb_c << coeff; - nb_l << leaf; - continue; - } - /* can't collect coefficients of 'pv' in 'cur' -> non-linear */ - return Node::null(); - } - Assert(nb_c.getNumChildren() > 0 || nb_l.getNumChildren() > 0); - - Node pv_mult_coeffs, result; - if (nb_c.getNumChildren() > 0) - { - Node coeffs = (nb_c.getNumChildren() == 1) ? nb_c[0] : nb_c.constructNode(); - coeffs = Rewriter::rewrite(coeffs); - result = pv_mult_coeffs = normalizePvMult(pv, {pv, coeffs}, contains_pv); - } - - if (nb_l.getNumChildren() > 0) - { - Node leafs = (nb_l.getNumChildren() == 1) ? nb_l[0] : nb_l.constructNode(); - leafs = Rewriter::rewrite(leafs); - Node zero = bv::utils::mkZero(bv::utils::getSize(pv)); - /* pv * 0 + t --> t */ - if (pv_mult_coeffs.isNull() || pv_mult_coeffs == zero) - { - result = leafs; - } - else - { - result = nm->mkNode(BITVECTOR_PLUS, pv_mult_coeffs, leafs); - contains_pv[result] = true; - result.setAttribute(is_linear, true); - } - } - Assert(!result.isNull()); - return result; -} - -/** - * Linearize an equality w.r.t. pv such that pv only occurs once. contains_pv - * marks terms in which pv occurs. - * For example, equality - * - * -pv * a + b = c + pv - * - * rewrites to - * - * pv * (-a - 1) = c - b. - */ -static Node normalizePvEqual( - Node pv, - const std::vector& children, - std::unordered_map& contains_pv) -{ - Assert(children.size() == 2); - - NodeManager* nm = NodeManager::currentNM(); - BvLinearAttribute is_linear; - Node coeffs[2], leafs[2]; - bool neg; - TNode child; - - for (unsigned i = 0; i < 2; ++i) - { - child = children[i]; - neg = false; - if (child.getKind() == BITVECTOR_NEG) - { - neg = true; - child = child[0]; - } - if (child.getAttribute(is_linear) || child == pv) - { - if (child.getKind() == BITVECTOR_PLUS) - { - Assert(isLinearPlus(child, pv, contains_pv)); - coeffs[i] = getPvCoeff(pv, child[0]); - leafs[i] = child[1]; - } - else - { - Assert(child.getKind() == BITVECTOR_MULT || child == pv); - coeffs[i] = getPvCoeff(pv, child); - } - } - if (neg) - { - if (!coeffs[i].isNull()) - { - coeffs[i] = nm->mkNode(BITVECTOR_NEG, coeffs[i]); - } - if (!leafs[i].isNull()) - { - leafs[i] = nm->mkNode(BITVECTOR_NEG, leafs[i]); - } - } - } - - if (coeffs[0].isNull() || coeffs[1].isNull()) - { - return Node::null(); - } - - Node coeff = nm->mkNode(BITVECTOR_SUB, coeffs[0], coeffs[1]); - coeff = Rewriter::rewrite(coeff); - std::vector mult_children = {pv, coeff}; - Node lhs = normalizePvMult(pv, mult_children, contains_pv); - - Node rhs; - if (!leafs[0].isNull() && !leafs[1].isNull()) - { - rhs = nm->mkNode(BITVECTOR_SUB, leafs[1], leafs[0]); - } - else if (!leafs[0].isNull()) - { - rhs = nm->mkNode(BITVECTOR_NEG, leafs[0]); - } - else if (!leafs[1].isNull()) - { - rhs = leafs[1]; - } - else - { - rhs = bv::utils::mkZero(bv::utils::getSize(pv)); - } - rhs = Rewriter::rewrite(rhs); - - if (lhs == rhs) - { - return bv::utils::mkTrue(); - } - - Node result = lhs.eqNode(rhs); - contains_pv[result] = true; - return result; -} - -Node BvInstantiator::rewriteTermForSolvePv( - Node pv, - Node n, - std::vector& children, - std::unordered_map& contains_pv) -{ - NodeManager* nm = NodeManager::currentNM(); - - // [1] rewrite cases of non-invertible operators - - if (n.getKind() == EQUAL) - { - TNode lhs = children[0]; - TNode rhs = children[1]; - - /* rewrite: x * x = x -> x < 2 */ - if ((lhs == pv && rhs.getKind() == BITVECTOR_MULT && rhs[0] == pv - && rhs[1] == pv) - || (rhs == pv && lhs.getKind() == BITVECTOR_MULT && lhs[0] == pv - && lhs[1] == pv)) - { - return nm->mkNode( - BITVECTOR_ULT, - pv, - bv::utils::mkConst(BitVector(bv::utils::getSize(pv), Integer(2)))); - } - - if (options::cbqiBvLinearize() && contains_pv[lhs] && contains_pv[rhs]) - { - Node result = normalizePvEqual(pv, children, contains_pv); - if (!result.isNull()) - { - Trace("cegqi-bv-nl") - << "Normalize " << n << " to " << result << std::endl; - } - else - { - Trace("cegqi-bv-nl") - << "Nonlinear " << n.getKind() << " " << n << std::endl; - } - return result; - } - } - else if (n.getKind() == BITVECTOR_MULT || n.getKind() == BITVECTOR_PLUS) - { - if (options::cbqiBvLinearize() && contains_pv[n]) - { - Node result; - if (n.getKind() == BITVECTOR_MULT) - { - result = normalizePvMult(pv, children, contains_pv); - } - else - { - result = normalizePvPlus(pv, children, contains_pv); - } - if (!result.isNull()) - { - Trace("cegqi-bv-nl") - << "Normalize " << n << " to " << result << std::endl; - return result; - } - else - { - Trace("cegqi-bv-nl") - << "Nonlinear " << n.getKind() << " " << n << std::endl; - } - } - } - - // [2] try to rewrite non-linear literals -> linear literals - - return Node::null(); -} - -/** sort bv extract interval - * - * This sorts lists of bitvector extract terms where - * ((_ extract i1 i2) t) < ((_ extract j1 j2) t) - * if i1>j1 or i1=j1 and i2>j2. - */ -struct SortBvExtractInterval -{ - bool operator()(Node i, Node j) - { - Assert(i.getKind() == BITVECTOR_EXTRACT); - Assert(j.getKind() == BITVECTOR_EXTRACT); - BitVectorExtract ie = i.getOperator().getConst(); - BitVectorExtract je = j.getOperator().getConst(); - if (ie.high > je.high) - { - return true; - } - else if (ie.high == je.high) - { - Assert(ie.low != je.low); - return ie.low > je.low; - } - return false; - } -}; - -void BvInstantiatorPreprocess::registerCounterexampleLemma( - std::vector& lems, std::vector& ce_vars) -{ - // new variables - std::vector vars; - // new lemmas - std::vector new_lems; - - if (options::cbqiBvRmExtract()) - { - NodeManager* nm = NodeManager::currentNM(); - Trace("cegqi-bv-pp") << "-----remove extracts..." << std::endl; - // map from terms to bitvector extracts applied to that term - std::map > extract_map; - std::unordered_set visited; - for (unsigned i = 0, size = lems.size(); i < size; i++) - { - Trace("cegqi-bv-pp-debug2") << "Register ce lemma # " << i << " : " - << lems[i] << std::endl; - collectExtracts(lems[i], extract_map, visited); - } - for (std::pair >& es : extract_map) - { - // sort based on the extract start position - std::vector& curr_vec = es.second; - - SortBvExtractInterval sbei; - std::sort(curr_vec.begin(), curr_vec.end(), sbei); - - unsigned width = es.first.getType().getBitVectorSize(); - - // list of points b such that: - // b>0 and we must start a segment at (b-1) or b==0 - std::vector boundaries; - boundaries.push_back(width); - boundaries.push_back(0); - - Trace("cegqi-bv-pp") << "For term " << es.first << " : " << std::endl; - for (unsigned i = 0, size = curr_vec.size(); i < size; i++) - { - Trace("cegqi-bv-pp") << " " << i << " : " << curr_vec[i] << std::endl; - BitVectorExtract e = - curr_vec[i].getOperator().getConst(); - if (std::find(boundaries.begin(), boundaries.end(), e.high + 1) - == boundaries.end()) - { - boundaries.push_back(e.high + 1); - } - if (std::find(boundaries.begin(), boundaries.end(), e.low) - == boundaries.end()) - { - boundaries.push_back(e.low); - } - } - std::sort(boundaries.rbegin(), boundaries.rend()); - - // make the extract variables - std::vector children; - for (unsigned i = 1; i < boundaries.size(); i++) - { - Assert(boundaries[i - 1] > 0); - Node ex = bv::utils::mkExtract( - es.first, boundaries[i - 1] - 1, boundaries[i]); - Node var = - nm->mkSkolem("ek", - ex.getType(), - "variable to represent disjoint extract region"); - children.push_back(var); - vars.push_back(var); - } - - Node conc = nm->mkNode(kind::BITVECTOR_CONCAT, children); - Assert(conc.getType() == es.first.getType()); - Node eq_lem = conc.eqNode(es.first); - Trace("cegqi-bv-pp") << "Introduced : " << eq_lem << std::endl; - new_lems.push_back(eq_lem); - Trace("cegqi-bv-pp") << "...finished processing extracts for term " - << es.first << std::endl; - } - Trace("cegqi-bv-pp") << "-----done remove extracts" << std::endl; - } - - if (!vars.empty()) - { - // could try applying subs -> vars here - // in practice, this led to worse performance - - Trace("cegqi-bv-pp") << "Adding " << new_lems.size() << " lemmas..." - << std::endl; - lems.insert(lems.end(), new_lems.begin(), new_lems.end()); - Trace("cegqi-bv-pp") << "Adding " << vars.size() << " variables..." - << std::endl; - ce_vars.insert(ce_vars.end(), vars.begin(), vars.end()); - } -} - -void BvInstantiatorPreprocess::collectExtracts( - Node lem, - std::map >& extract_map, - std::unordered_set& visited) -{ - std::vector visit; - TNode cur; - visit.push_back(lem); - do - { - cur = visit.back(); - visit.pop_back(); - if (visited.find(cur) == visited.end()) - { - visited.insert(cur); - if (cur.getKind() != FORALL) - { - if (cur.getKind() == BITVECTOR_EXTRACT) - { - extract_map[cur[0]].push_back(cur); - } - - for (const Node& nc : cur) - { - visit.push_back(nc); - } - } - } - } while (!visit.empty()); -} - -} /* CVC4::theory::quantifiers namespace */ -} /* CVC4::theory namespace */ -} /* CVC4 namespace */ diff --git a/src/theory/quantifiers/ceg_t_instantiator.h b/src/theory/quantifiers/ceg_t_instantiator.h deleted file mode 100644 index 95295d214..000000000 --- a/src/theory/quantifiers/ceg_t_instantiator.h +++ /dev/null @@ -1,331 +0,0 @@ -/********************* */ -/*! \file ceg_t_instantiator.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 theory-specific counterexample-guided quantifier instantiation - **/ - - -#include "cvc4_private.h" - -#ifndef __CVC4__CEG_T_INSTANTIATOR_H -#define __CVC4__CEG_T_INSTANTIATOR_H - -#include "theory/quantifiers/bv_inverter.h" -#include "theory/quantifiers/ceg_instantiator.h" - -#include - -namespace CVC4 { -namespace theory { -namespace quantifiers { - -/** TODO (#1367) : document these classes, also move to separate files. */ - -class ArithInstantiator : public Instantiator { - public: - ArithInstantiator( QuantifiersEngine * qe, TypeNode tn ) : Instantiator( qe, tn ){} - virtual ~ArithInstantiator(){} - virtual void reset(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) override; - virtual bool hasProcessEquality(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) override - { - return true; - } - virtual bool processEquality(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - std::vector& term_props, - std::vector& terms, - CegInstEffort effort) override; - virtual bool hasProcessAssertion(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) override - { - return true; - } - virtual Node hasProcessAssertion(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - Node lit, - CegInstEffort effort) override; - virtual bool processAssertion(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - Node lit, - Node alit, - CegInstEffort effort) override; - virtual bool processAssertions(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) override; - virtual bool needsPostProcessInstantiationForVariable( - CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) override; - virtual bool postProcessInstantiationForVariable( - CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort, - std::vector& lemmas) override; - virtual std::string identify() const override { return "Arith"; } - private: - Node d_vts_sym[2]; - std::vector d_mbp_bounds[2]; - std::vector d_mbp_coeff[2]; - std::vector d_mbp_vts_coeff[2][2]; - std::vector d_mbp_lit[2]; - int solve_arith(CegInstantiator* ci, - Node v, - Node atom, - Node& veq_c, - Node& val, - Node& vts_coeff_inf, - Node& vts_coeff_delta); - Node getModelBasedProjectionValue(CegInstantiator* ci, - Node e, - Node t, - bool isLower, - Node c, - Node me, - Node mt, - Node theta, - Node inf_coeff, - Node delta_coeff); -}; - -class DtInstantiator : public Instantiator { -public: - DtInstantiator( QuantifiersEngine * qe, TypeNode tn ) : Instantiator( qe, tn ){} - virtual ~DtInstantiator(){} - virtual void reset(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) override; - virtual bool processEqualTerms(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - std::vector& eqc, - CegInstEffort effort) override; - virtual bool hasProcessEquality(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) override - { - return true; - } - virtual bool processEquality(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - std::vector& term_props, - std::vector& terms, - CegInstEffort effort) override; - virtual std::string identify() const override { return "Dt"; } - private: - Node solve_dt(Node v, Node a, Node b, Node sa, Node sb); -}; - -class TermArgTrie; - -class EprInstantiator : public Instantiator { - public: - EprInstantiator( QuantifiersEngine * qe, TypeNode tn ) : Instantiator( qe, tn ){} - virtual ~EprInstantiator(){} - virtual void reset(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) override; - virtual bool processEqualTerm(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - TermProperties& pv_prop, - Node n, - CegInstEffort effort) override; - virtual bool processEqualTerms(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - std::vector& eqc, - CegInstEffort effort) override; - virtual std::string identify() const override { return "Epr"; } - private: - std::vector d_equal_terms; - void computeMatchScore(CegInstantiator* ci, - Node pv, - Node catom, - std::vector& arg_reps, - TermArgTrie* tat, - unsigned index, - std::map& match_score); - void computeMatchScore(CegInstantiator* ci, - Node pv, - Node catom, - Node eqc, - std::map& match_score); -}; - -/** Bitvector instantiator - * - * This implements an approach for counterexample-guided instantiation - * for bit-vector variables based on word-level inversions. - * It is enabled by --cbqi-bv. - */ -class BvInstantiator : public Instantiator { - public: - BvInstantiator(QuantifiersEngine* qe, TypeNode tn); - ~BvInstantiator() override; - void reset(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) override; - bool hasProcessAssertion(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) override - { - return true; - } - Node hasProcessAssertion(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - Node lit, - CegInstEffort effort) override; - bool processAssertion(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - Node lit, - Node alit, - CegInstEffort effort) override; - bool processAssertions(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) override; - /** use model value - * - * We allow model values if we have not already tried an assertion, - * and only at levels below full if cbqiFullEffort is false. - */ - bool useModelValue(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - CegInstEffort effort) override; - std::string identify() const override { return "Bv"; } - - private: - // point to the bv inverter class - BvInverter * d_inverter; - unsigned d_inst_id_counter; - /** information about solved forms */ - std::unordered_map< Node, std::vector< unsigned >, NodeHashFunction > d_var_to_inst_id; - std::unordered_map< unsigned, Node > d_inst_id_to_term; - std::unordered_map d_inst_id_to_alit; - // variable to current id we are processing - std::unordered_map< Node, unsigned, NodeHashFunction > d_var_to_curr_inst_id; - /** the amount of slack we added for asserted literals */ - std::unordered_map d_alit_to_model_slack; - /** whether we have tried an instantiation based on assertion in this round */ - bool d_tried_assertion_inst; - /** rewrite assertion for solve pv - * returns a literal that is equivalent to lit that leads to best solved form for pv - */ - Node rewriteAssertionForSolvePv(CegInstantiator* ci, Node pv, Node lit); - /** rewrite term for solve pv - * This is a helper function for rewriteAssertionForSolvePv. - * If this returns non-null value ret, then this indicates - * that n should be rewritten to ret. It is called as - * a "post-rewrite", that is, after the children of n - * have been rewritten and stored in the vector children. - * - * contains_pv stores whether certain nodes contain pv. - * where we guarantee that all subterms of terms in children - * appear in the domain of contains_pv. - */ - Node rewriteTermForSolvePv( - Node pv, - Node n, - std::vector& children, - std::unordered_map& contains_pv); - /** process literal, called from processAssertion - * lit is the literal to solve for pv that has been rewritten according to - * internal rules here. - * alit is the asserted literal that lit is derived from. - */ - void processLiteral(CegInstantiator* ci, - SolvedForm& sf, - Node pv, - Node lit, - Node alit, - CegInstEffort effort); -}; - -/** Bitvector instantiator preprocess - * - * This class implements preprocess techniques that are helpful for - * counterexample-guided instantiation, such as introducing variables - * that refer to disjoint bit-vector extracts. - */ -class BvInstantiatorPreprocess : public InstantiatorPreprocess -{ - public: - BvInstantiatorPreprocess() {} - ~BvInstantiatorPreprocess() override {} - /** register counterexample lemma - * - * This method modifies the contents of lems based on the extract terms - * it contains when the option --cbqi-bv-rm-extract is enabled. It introduces - * a dummy equality so that segments of terms t under extracts can be solved - * independently. - * - * For example: - * P[ ((extract 7 4) t), ((extract 3 0) t)] - * becomes: - * P[((extract 7 4) t), ((extract 3 0) t)] ^ - * t = concat( x74, x30 ) - * where x74 and x30 are fresh variables of type BV_4. - * - * Another example: - * P[ ((extract 7 3) t), ((extract 4 0) t)] - * becomes: - * P[((extract 7 4) t), ((extract 3 0) t)] ^ - * t = concat( x75, x44, x30 ) - * where x75, x44 and x30 are fresh variables of type BV_3, BV_1, and BV_4 - * respectively. - * - * Notice we leave the original conjecture alone. This is done for performance - * since the added equalities ensure we are able to construct the proper - * solved forms for variables in t and for the intermediate variables above. - */ - void registerCounterexampleLemma(std::vector& lems, - std::vector& ce_vars) override; - - private: - /** collect extracts - * - * This method collects all extract terms in lem - * and stores them in d_extract_map. - * visited is the terms we've already visited. - */ - void collectExtracts(Node lem, - std::map >& extract_map, - std::unordered_set& visited); -}; - -} /* CVC4::theory::quantifiers namespace */ -} /* CVC4::theory namespace */ -} /* CVC4 namespace */ - -#endif diff --git a/src/theory/quantifiers/cegqi/ceg_instantiator.cpp b/src/theory/quantifiers/cegqi/ceg_instantiator.cpp new file mode 100644 index 000000000..d7d46bb4b --- /dev/null +++ b/src/theory/quantifiers/cegqi/ceg_instantiator.cpp @@ -0,0 +1,1337 @@ +/********************* */ +/*! \file ceg_instantiator.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 Implementation of counterexample-guided quantifier instantiation + **/ + +#include "theory/quantifiers/cegqi/ceg_instantiator.h" +#include "theory/quantifiers/cegqi/ceg_t_instantiator.h" + +#include "options/quantifiers_options.h" +#include "smt/term_formula_removal.h" +#include "theory/arith/arith_msum.h" +#include "theory/quantifiers/first_order_model.h" +#include "theory/quantifiers/quantifiers_rewriter.h" +#include "theory/quantifiers/term_database.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 std; +using namespace CVC4::kind; +using namespace CVC4::context; + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +std::ostream& operator<<(std::ostream& os, CegInstEffort e) +{ + switch (e) + { + case CEG_INST_EFFORT_NONE: os << "?"; break; + case CEG_INST_EFFORT_STANDARD: os << "STANDARD"; break; + case CEG_INST_EFFORT_STANDARD_MV: os << "STANDARD_MV"; break; + case CEG_INST_EFFORT_FULL: os << "FULL"; break; + default: Unreachable(); + } + return os; +} + +std::ostream& operator<<(std::ostream& os, CegInstPhase phase) +{ + switch (phase) + { + case CEG_INST_PHASE_NONE: os << "?"; break; + case CEG_INST_PHASE_EQC: os << "eqc"; break; + case CEG_INST_PHASE_EQUAL: os << "eq"; break; + case CEG_INST_PHASE_ASSERTION: os << "as"; break; + case CEG_INST_PHASE_MVALUE: os << "mv"; break; + default: Unreachable(); + } + return os; +} + +CegInstantiator::CegInstantiator(QuantifiersEngine* qe, + CegqiOutput* out, + bool use_vts_delta, + bool use_vts_inf) + : d_qe(qe), + d_out(out), + d_use_vts_delta(use_vts_delta), + d_use_vts_inf(use_vts_inf), + d_is_nested_quant(false), + d_effort(CEG_INST_EFFORT_NONE) +{ +} + +CegInstantiator::~CegInstantiator() { + for (std::pair inst : d_instantiator) + { + delete inst.second; + } + for (std::pair instp : d_tipp) + { + delete instp.second; + } +} + +void CegInstantiator::computeProgVars( Node n ){ + if( d_prog_var.find( n )==d_prog_var.end() ){ + d_prog_var[n].clear(); + if (n.getKind() == kind::CHOICE) + { + Assert(d_prog_var.find(n[0][0]) == d_prog_var.end()); + d_prog_var[n[0][0]].clear(); + } + if (d_vars_set.find(n) != d_vars_set.end()) + { + d_prog_var[n].insert(n); + }else if( !d_out->isEligibleForInstantiation( n ) ){ + d_inelig.insert(n); + return; + } + for( unsigned i=0; i& visited) +{ + if (visited.find(tn) == visited.end()) + { + visited[tn] = true; + TheoryId tid = Theory::theoryOf(tn); + registerTheoryId(tid); + if (tn.isDatatype()) + { + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + for (unsigned i = 0; i < dt.getNumConstructors(); i++) + { + for (unsigned j = 0; j < dt[i].getNumArgs(); j++) + { + registerTheoryIds( + TypeNode::fromType( + ((SelectorType)dt[i][j].getType()).getRangeType()), + visited); + } + } + } + } +} + +void CegInstantiator::registerTheoryId(TheoryId tid) +{ + if (std::find(d_tids.begin(), d_tids.end(), tid) == d_tids.end()) + { + // setup any theory-specific preprocessors here + if (tid == THEORY_BV) + { + d_tipp[tid] = new BvInstantiatorPreprocess; + } + d_tids.push_back(tid); + } +} + +void CegInstantiator::registerVariable(Node v, bool is_aux) +{ + Assert(std::find(d_vars.begin(), d_vars.end(), v) == d_vars.end()); + Assert(std::find(d_aux_vars.begin(), d_aux_vars.end(), v) + == d_aux_vars.end()); + if (!is_aux) + { + d_vars.push_back(v); + d_vars_set.insert(v); + } + else + { + d_aux_vars.push_back(v); + } + TypeNode vtn = v.getType(); + Trace("cbqi-proc-debug") << "Collect theory ids from type " << vtn << " of " + << v << std::endl; + // collect relevant theories for this variable + std::map visited; + registerTheoryIds(vtn, visited); +} + +void CegInstantiator::deactivateInstantiationVariable(Node v) +{ + d_curr_subs_proc.erase( v ); + d_curr_index.erase( v ); + d_curr_iphase.erase(v); +} + +bool CegInstantiator::constructInstantiation(SolvedForm& sf, unsigned i) +{ + if( i==d_vars.size() ){ + //solved for all variables, now construct instantiation + bool needsPostprocess = + sf.d_vars.size() > d_input_vars.size() || !d_var_order_index.empty(); + std::vector< Instantiator * > pp_inst; + std::map< Instantiator *, Node > pp_inst_to_var; + std::vector< Node > lemmas; + for( std::map< Node, Instantiator * >::iterator ita = d_active_instantiators.begin(); ita != d_active_instantiators.end(); ++ita ){ + if (ita->second->needsPostProcessInstantiationForVariable( + this, sf, ita->first, d_effort)) + { + needsPostprocess = true; + pp_inst_to_var[ ita->second ] = ita->first; + } + } + if( needsPostprocess ){ + //must make copy so that backtracking reverts sf + SolvedForm sf_tmp = sf; + bool postProcessSuccess = true; + for( std::map< Instantiator *, Node >::iterator itp = pp_inst_to_var.begin(); itp != pp_inst_to_var.end(); ++itp ){ + if (!itp->first->postProcessInstantiationForVariable( + this, sf_tmp, itp->second, d_effort, lemmas)) + { + postProcessSuccess = false; + break; + } + } + if( postProcessSuccess ){ + return doAddInstantiation( sf_tmp.d_vars, sf_tmp.d_subs, lemmas ); + }else{ + return false; + } + }else{ + return doAddInstantiation( sf.d_vars, sf.d_subs, lemmas ); + } + }else{ + //Node v = d_single_inv_map_to_prog[d_single_inv[0][i]]; + bool is_cv = false; + Node pv; + if( d_stack_vars.empty() ){ + pv = d_vars[i]; + }else{ + pv = d_stack_vars.back(); + is_cv = true; + d_stack_vars.pop_back(); + } + activateInstantiationVariable(pv, i); + + //get the instantiator object + Instantiator * vinst = NULL; + std::map< Node, Instantiator * >::iterator itin = d_instantiator.find( pv ); + if( itin!=d_instantiator.end() ){ + vinst = itin->second; + } + Assert( vinst!=NULL ); + d_active_instantiators[pv] = vinst; + vinst->reset(this, sf, pv, d_effort); + + TypeNode pvtn = pv.getType(); + TypeNode pvtnb = pvtn.getBaseType(); + Node pvr = pv; + if( d_qe->getMasterEqualityEngine()->hasTerm( pv ) ){ + pvr = d_qe->getMasterEqualityEngine()->getRepresentative( pv ); + } + Trace("cbqi-inst-debug") << "[Find instantiation for " << pv << "], rep=" << pvr << ", instantiator is " << vinst->identify() << std::endl; + Node pv_value; + if( options::cbqiModel() ){ + pv_value = getModelValue( pv ); + Trace("cbqi-bound2") << "...M( " << pv << " ) = " << pv_value << std::endl; + } + + // if d_effort is full, we must choose at least one model value + if ((i + 1) < d_vars.size() || d_effort != CEG_INST_EFFORT_FULL) + { + //[1] easy case : pv is in the equivalence class as another term not containing pv + Trace("cbqi-inst-debug") << "[1] try based on equivalence class." << std::endl; + d_curr_iphase[pv] = CEG_INST_PHASE_EQC; + std::map< Node, std::vector< Node > >::iterator it_eqc = d_curr_eqc.find( pvr ); + if( it_eqc!=d_curr_eqc.end() ){ + //std::vector< Node > eq_candidates; + Trace("cbqi-inst-debug2") << "...eqc has size " << it_eqc->second.size() << std::endl; + for( unsigned k=0; ksecond.size(); k++ ){ + Node n = it_eqc->second[k]; + if( n!=pv ){ + Trace("cbqi-inst-debug") << "...try based on equal term " << n << std::endl; + //must be an eligible term + if( isEligible( n ) ){ + Node ns; + TermProperties pv_prop; //coefficient of pv in the equality we solve (null is 1) + bool proc = false; + if( !d_prog_var[n].empty() ){ + ns = applySubstitution( pvtn, n, sf, pv_prop, false ); + if( !ns.isNull() ){ + computeProgVars( ns ); + //substituted version must be new and cannot contain pv + proc = d_prog_var[ns].find( pv )==d_prog_var[ns].end(); + } + }else{ + ns = n; + proc = true; + } + if( proc ){ + if (vinst->processEqualTerm( + this, sf, pv, pv_prop, ns, d_effort)) + { + return true; + } + // Do not consider more than one equal term, + // this helps non-monotonic strategies that may encounter + // duplicate instantiations. + break; + } + } + } + } + if (vinst->processEqualTerms(this, sf, pv, it_eqc->second, d_effort)) + { + return true; + } + }else{ + Trace("cbqi-inst-debug2") << "...eqc not found." << std::endl; + } + + //[2] : we can solve an equality for pv + /// iterate over equivalence classes to find cases where we can solve for + /// the variable + if (vinst->hasProcessEquality(this, sf, pv, d_effort)) + { + Trace("cbqi-inst-debug") << "[2] try based on solving equalities." << std::endl; + d_curr_iphase[pv] = CEG_INST_PHASE_EQUAL; + for( unsigned k=0; k >::iterator it_reqc = d_curr_eqc.find( r ); + std::vector< Node > lhs; + std::vector< bool > lhs_v; + std::vector< TermProperties > lhs_prop; + Assert( it_reqc!=d_curr_eqc.end() ); + for( unsigned kk=0; kksecond.size(); kk++ ){ + Node n = it_reqc->second[kk]; + Trace("cbqi-inst-debug2") << "...look at term " << n << std::endl; + //must be an eligible term + if( isEligible( n ) ){ + Node ns; + TermProperties pv_prop; + if( !d_prog_var[n].empty() ){ + ns = applySubstitution( pvtn, n, sf, pv_prop ); + if( !ns.isNull() ){ + computeProgVars( ns ); + } + }else{ + ns = n; + } + if( !ns.isNull() ){ + bool hasVar = d_prog_var[ns].find( pv )!=d_prog_var[ns].end(); + Trace("cbqi-inst-debug2") << "... " << ns << " has var " << pv << " : " << hasVar << std::endl; + std::vector< TermProperties > term_props; + std::vector< Node > terms; + term_props.push_back( pv_prop ); + terms.push_back( ns ); + for( unsigned j=0; jhasProcessAssertion(this, sf, pv, d_effort)) + { + Trace("cbqi-inst-debug") << "[3] try based on assertions." << std::endl; + d_curr_iphase[pv] = CEG_INST_PHASE_ASSERTION; + std::unordered_set< Node, NodeHashFunction > lits; + //unsigned rmax = Theory::theoryOf( pv )==Theory::theoryOf( pv.getType() ) ? 1 : 2; + for( unsigned r=0; r<2; r++ ){ + TheoryId tid = r==0 ? Theory::theoryOf( pvtn ) : THEORY_UF; + Trace("cbqi-inst-debug2") << " look at assertions of " << tid << std::endl; + std::map< TheoryId, std::vector< Node > >::iterator ita = d_curr_asserts.find( tid ); + if( ita!=d_curr_asserts.end() ){ + for (unsigned j = 0; jsecond.size(); j++) { + Node lit = ita->second[j]; + if( lits.find(lit)==lits.end() ){ + lits.insert(lit); + Node plit; + if (options::cbqiRepeatLit() || !isSolvedAssertion(lit)) + { + plit = + vinst->hasProcessAssertion(this, sf, pv, lit, d_effort); + } + if (!plit.isNull()) { + Trace("cbqi-inst-debug2") << " look at " << lit; + if (plit != lit) { + Trace("cbqi-inst-debug2") << "...processed to : " << plit; + } + Trace("cbqi-inst-debug2") << std::endl; + // apply substitutions + Node slit = applySubstitutionToLiteral(plit, sf); + if( !slit.isNull() ){ + // check if contains pv + if( hasVariable( slit, pv ) ){ + Trace("cbqi-inst-debug") << "...try based on literal " + << slit << "," << std::endl; + Trace("cbqi-inst-debug") << "...from " << lit + << std::endl; + if (vinst->processAssertion( + this, sf, pv, slit, lit, d_effort)) + { + return true; + } + } + } + } + } + } + } + } + if (vinst->processAssertions(this, sf, pv, d_effort)) + { + return true; + } + } + } + + //[4] resort to using value in model. We do so if: + // - if the instantiator uses model values at this effort, or + // - if we are solving for a subfield of a datatype (is_cv). + if ((vinst->useModelValue(this, sf, pv, d_effort) || is_cv) + && vinst->allowModelValue(this, sf, pv, d_effort)) + { +#ifdef CVC4_ASSERTIONS + if( pvtn.isReal() && options::cbqiNestedQE() && !options::cbqiAll() ){ + Trace("cbqi-warn") << "Had to resort to model value." << std::endl; + Assert( false ); + } +#endif + Node mv = getModelValue( pv ); + TermProperties pv_prop_m; + Trace("cbqi-inst-debug") << "[4] " << i << "...try model value " << mv << std::endl; + d_curr_iphase[pv] = CEG_INST_PHASE_MVALUE; + CegInstEffort prev = d_effort; + if (d_effort < CEG_INST_EFFORT_STANDARD_MV) + { + // update the effort level to indicate we have used a model value + d_effort = CEG_INST_EFFORT_STANDARD_MV; + } + if (constructInstantiationInc(pv, mv, pv_prop_m, sf)) + { + return true; + } + d_effort = prev; + } + Trace("cbqi-inst-debug") << "[No instantiation found for " << pv << "]" << std::endl; + if( is_cv ){ + d_stack_vars.push_back( pv ); + } + d_active_instantiators.erase( pv ); + deactivateInstantiationVariable(pv); + return false; + } +} + +void CegInstantiator::pushStackVariable( Node v ) { + d_stack_vars.push_back( v ); +} + +void CegInstantiator::popStackVariable() { + Assert( !d_stack_vars.empty() ); + d_stack_vars.pop_back(); +} + +bool CegInstantiator::constructInstantiationInc(Node pv, + Node n, + TermProperties& pv_prop, + SolvedForm& sf, + bool revertOnSuccess) +{ + Node cnode = pv_prop.getCacheNode(); + if( d_curr_subs_proc[pv][n].find( cnode )==d_curr_subs_proc[pv][n].end() ){ + d_curr_subs_proc[pv][n][cnode] = true; + if( Trace.isOn("cbqi-inst-debug") ){ + for( unsigned j=0; j " << n << std::endl; + Assert( n.getType().isSubtypeOf( pv.getType() ) ); + } + //must ensure variables have been computed for n + computeProgVars( n ); + Assert( d_inelig.find( n )==d_inelig.end() ); + + //substitute into previous substitutions, when applicable + std::vector< Node > a_var; + a_var.push_back( pv ); + std::vector< Node > a_subs; + a_subs.push_back( n ); + std::vector< TermProperties > a_prop; + a_prop.push_back( pv_prop ); + std::vector< Node > a_non_basic; + if( !pv_prop.isBasic() ){ + a_non_basic.push_back( pv ); + } + bool success = true; + std::map< int, Node > prev_subs; + std::map< int, TermProperties > prev_prop; + std::map< int, Node > prev_sym_subs; + std::vector< Node > new_non_basic; + Trace("cbqi-inst-debug2") << "Applying substitutions to previous substitution terms..." << std::endl; + for( unsigned j=0; j n } + + // based on the above substitution, we have: + // x = sf.d_subs[j] { pv_prop.getModifiedTerm( pv ) -> n } + // is equivalent to + // a_pv_prop.getModifiedTerm( x ) = new_subs + + // thus we must compose substitutions: + // a_pv_prop.getModifiedTerm( sf.d_props[j].getModifiedTerm( sf.d_vars[j] ) ) = new_subs + + prev_prop[j] = sf.d_props[j]; + bool prev_basic = sf.d_props[j].isBasic(); + + // now compose the property + sf.d_props[j].composeProperty( a_pv_prop ); + + // if previously was basic, becomes non-basic + if( prev_basic && !sf.d_props[j].isBasic() ){ + Assert( std::find( sf.d_non_basic.begin(), sf.d_non_basic.end(), sf.d_vars[j] )==sf.d_non_basic.end() ); + new_non_basic.push_back( sf.d_vars[j] ); + sf.d_non_basic.push_back( sf.d_vars[j] ); + } + } + if( sf.d_subs[j]!=prev_subs[j] ){ + computeProgVars( sf.d_subs[j] ); + Assert( d_inelig.find( sf.d_subs[j] )==d_inelig.end() ); + } + Trace("cbqi-inst-debug2") << "Subs " << j << " " << sf.d_subs[j] << std::endl; + }else{ + Trace("cbqi-inst-debug2") << "...failed to apply substitution to " << sf.d_subs[j] << std::endl; + success = false; + break; + } + }else{ + Trace("cbqi-inst-debug2") << "Skip " << j << " " << sf.d_subs[j] << std::endl; + } + } + if( success ){ + Trace("cbqi-inst-debug2") << "Adding to vectors..." << std::endl; + sf.push_back( pv, n, pv_prop ); + Trace("cbqi-inst-debug2") << "Recurse..." << std::endl; + unsigned i = d_curr_index[pv]; + success = constructInstantiation(sf, d_stack_vars.empty() ? i + 1 : i); + if (!success || revertOnSuccess) + { + Trace("cbqi-inst-debug2") << "Removing from vectors..." << std::endl; + sf.pop_back( pv, n, pv_prop ); + } + } + if (success && !revertOnSuccess) + { + return true; + }else{ + Trace("cbqi-inst-debug2") << "Revert substitutions..." << std::endl; + //revert substitution information + for( std::map< int, Node >::iterator it = prev_subs.begin(); it != prev_subs.end(); it++ ){ + sf.d_subs[it->first] = it->second; + } + for( std::map< int, TermProperties >::iterator it = prev_prop.begin(); it != prev_prop.end(); it++ ){ + sf.d_props[it->first] = it->second; + } + for( unsigned i=0; i& vars, std::vector< Node >& subs, std::vector< Node >& lemmas ) { + if (vars.size() > d_input_vars.size() || !d_var_order_index.empty()) + { + Trace("cbqi-inst-debug") << "Reconstructing instantiations...." << std::endl; + std::map< Node, Node > subs_map; + for( unsigned i=0; i::iterator it = subs_map.find(d_input_vars[i]); + Assert( it!=subs_map.end() ); + Node n = it->second; + Trace("cbqi-inst-debug") << " " << d_input_vars[i] << " -> " << n + << std::endl; + Assert(n.getType().isSubtypeOf(d_input_vars[i].getType())); + subs.push_back( n ); + } + } + if (Trace.isOn("cbqi-inst")) + { + Trace("cbqi-inst") << "Ceg Instantiator produced : " << std::endl; + for (unsigned i = 0, size = d_input_vars.size(); i < size; ++i) + { + Node v = d_input_vars[i]; + Trace("cbqi-inst") << i << " (" << d_curr_iphase[v] << ") : " + << v << " -> " << subs[i] << std::endl; + Assert(subs[i].getType().isSubtypeOf(v.getType())); + } + } + Trace("cbqi-inst-debug") << "Do the instantiation...." << std::endl; + bool ret = d_out->doAddInstantiation( subs ); + for( unsigned i=0; iaddLemma( lemmas[i] ); + } + return ret; +} + +bool CegInstantiator::canApplyBasicSubstitution( Node n, std::vector< Node >& non_basic ){ + Assert( d_prog_var.find( n )!=d_prog_var.end() ); + if( !non_basic.empty() ){ + for (std::unordered_set::iterator it = + d_prog_var[n].begin(); + it != d_prog_var[n].end(); + ++it) + { + if (std::find(non_basic.begin(), non_basic.end(), *it) != non_basic.end()) + { + return false; + } + } + } + return true; +} + +Node CegInstantiator::applySubstitution( TypeNode tn, Node n, std::vector< Node >& vars, std::vector< Node >& subs, std::vector< TermProperties >& prop, + std::vector< Node >& non_basic, TermProperties& pv_prop, bool try_coeff ) { + computeProgVars( n ); + Assert( n==Rewriter::rewrite( n ) ); + bool is_basic = canApplyBasicSubstitution( n, non_basic ); + if( Trace.isOn("cegqi-si-apply-subs-debug") ){ + Trace("cegqi-si-apply-subs-debug") << "is_basic = " << is_basic << " " << tn << std::endl; + for( unsigned i=0; i " << subs[i] << " types : " << vars[i].getType() << " -> " << subs[i].getType() << std::endl; + Assert( subs[i].getType().isSubtypeOf( vars[i].getType() ) ); + } + } + Node nret; + if( is_basic ){ + nret = n.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + }else{ + if( !tn.isInteger() ){ + //can do basic substitution instead with divisions + std::vector< Node > nvars; + std::vector< Node > nsubs; + for( unsigned i=0; imkNode( MULT, subs[i], NodeManager::currentNM()->mkConst( Rational(1)/prop[i].d_coeff.getConst() ) ); + nn = NodeManager::currentNM()->mkNode( kind::TO_INTEGER, nn ); + nn = Rewriter::rewrite( nn ); + nsubs.push_back( nn ); + }else{ + nsubs.push_back( subs[i] ); + } + } + nret = n.substitute( vars.begin(), vars.end(), nsubs.begin(), nsubs.end() ); + }else if( try_coeff ){ + //must convert to monomial representation + std::map< Node, Node > msum; + if (ArithMSum::getMonomialSum(n, msum)) + { + std::map< Node, Node > msum_coeff; + std::map< Node, Node > msum_term; + for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){ + //check if in substitution + std::vector< Node >::iterator its = std::find( vars.begin(), vars.end(), it->first ); + if( its!=vars.end() ){ + int index = its-vars.begin(); + if( prop[index].d_coeff.isNull() ){ + //apply substitution + msum_term[it->first] = subs[index]; + }else{ + //apply substitution, multiply to ensure no divisibility conflict + msum_term[it->first] = subs[index]; + //relative coefficient + msum_coeff[it->first] = prop[index].d_coeff; + if( pv_prop.d_coeff.isNull() ){ + pv_prop.d_coeff = prop[index].d_coeff; + }else{ + pv_prop.d_coeff = NodeManager::currentNM()->mkNode( MULT, pv_prop.d_coeff, prop[index].d_coeff ); + } + } + }else{ + msum_term[it->first] = it->first; + } + } + //make sum with normalized coefficient + if( !pv_prop.d_coeff.isNull() ){ + pv_prop.d_coeff = Rewriter::rewrite( pv_prop.d_coeff ); + Trace("cegqi-si-apply-subs-debug") << "Combined coeff : " << pv_prop.d_coeff << std::endl; + std::vector< Node > children; + for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){ + Node c_coeff; + if( !msum_coeff[it->first].isNull() ){ + c_coeff = Rewriter::rewrite( NodeManager::currentNM()->mkConst( pv_prop.d_coeff.getConst() / msum_coeff[it->first].getConst() ) ); + }else{ + c_coeff = pv_prop.d_coeff; + } + if( !it->second.isNull() ){ + c_coeff = NodeManager::currentNM()->mkNode( MULT, c_coeff, it->second ); + } + Assert( !c_coeff.isNull() ); + Node c; + if( msum_term[it->first].isNull() ){ + c = c_coeff; + }else{ + c = NodeManager::currentNM()->mkNode( MULT, c_coeff, msum_term[it->first] ); + } + children.push_back( c ); + Trace("cegqi-si-apply-subs-debug") << "Add child : " << c << std::endl; + } + Node nretc = children.size()==1 ? children[0] : NodeManager::currentNM()->mkNode( PLUS, children ); + nretc = Rewriter::rewrite( nretc ); + //ensure that nret does not contain vars + if( !TermUtil::containsTerms( nretc, vars ) ){ + //result is ( nret / pv_prop.d_coeff ) + nret = nretc; + }else{ + Trace("cegqi-si-apply-subs-debug") << "Failed, since result " << nretc << " contains free variable." << std::endl; + } + }else{ + //implies that we have a monomial that has a free variable + Trace("cegqi-si-apply-subs-debug") << "Failed to find coefficient during substitution, implies monomial with free variable." << std::endl; + } + }else{ + Trace("cegqi-si-apply-subs-debug") << "Failed to find monomial sum " << n << std::endl; + } + } + } + if( n!=nret && !nret.isNull() ){ + nret = Rewriter::rewrite( nret ); + } + return nret; +} + +Node CegInstantiator::applySubstitutionToLiteral( Node lit, std::vector< Node >& vars, std::vector< Node >& subs, + std::vector< TermProperties >& prop, std::vector< Node >& non_basic ) { + computeProgVars( lit ); + bool is_basic = canApplyBasicSubstitution( lit, non_basic ); + Node lret; + if( is_basic ){ + lret = lit.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + }else{ + Node atom = lit.getKind()==NOT ? lit[0] : lit; + bool pol = lit.getKind()!=NOT; + //arithmetic inequalities and disequalities + if( atom.getKind()==GEQ || ( atom.getKind()==EQUAL && !pol && atom[0].getType().isReal() ) ){ + Assert( atom.getKind()!=GEQ || atom[1].isConst() ); + Node atom_lhs; + Node atom_rhs; + if( atom.getKind()==GEQ ){ + atom_lhs = atom[0]; + atom_rhs = atom[1]; + }else{ + atom_lhs = NodeManager::currentNM()->mkNode( MINUS, atom[0], atom[1] ); + atom_lhs = Rewriter::rewrite( atom_lhs ); + atom_rhs = getQuantifiersEngine()->getTermUtil()->d_zero; + } + //must be an eligible term + if( isEligible( atom_lhs ) ){ + //apply substitution to LHS of atom + TermProperties atom_lhs_prop; + atom_lhs = applySubstitution( NodeManager::currentNM()->realType(), atom_lhs, vars, subs, prop, non_basic, atom_lhs_prop ); + if( !atom_lhs.isNull() ){ + if( !atom_lhs_prop.d_coeff.isNull() ){ + atom_rhs = Rewriter::rewrite( NodeManager::currentNM()->mkNode( MULT, atom_lhs_prop.d_coeff, atom_rhs ) ); + } + lret = NodeManager::currentNM()->mkNode( atom.getKind(), atom_lhs, atom_rhs ); + if( !pol ){ + lret = lret.negate(); + } + } + } + }else{ + // don't know how to apply substitution to literal + } + } + if( lit!=lret && !lret.isNull() ){ + lret = Rewriter::rewrite( lret ); + } + return lret; +} + +bool CegInstantiator::check() { + if( d_qe->getTheoryEngine()->needCheck() ){ + Trace("cbqi-engine") << " CEGQI instantiator : wait until all ground theories are finished." << std::endl; + return false; + } + processAssertions(); + for( unsigned r=0; r<2; r++ ){ + d_effort = r == 0 ? CEG_INST_EFFORT_STANDARD : CEG_INST_EFFORT_FULL; + SolvedForm sf; + d_stack_vars.clear(); + d_bound_var_index.clear(); + d_solved_asserts.clear(); + //try to add an instantiation + if (constructInstantiation(sf, 0)) + { + return true; + } + } + Trace("cbqi-engine") << " WARNING : unable to find CEGQI single invocation instantiation." << std::endl; + return false; +} + +void collectPresolveEqTerms( Node n, std::map< Node, std::vector< Node > >& teq ) { + if( n.getKind()==FORALL || n.getKind()==EXISTS ){ + //do nothing + }else{ + if( n.getKind()==EQUAL ){ + for( unsigned i=0; i<2; i++ ){ + std::map< Node, std::vector< Node > >::iterator it = teq.find( n[i] ); + if( it!=teq.end() ){ + Node nn = n[ i==0 ? 1 : 0 ]; + if( std::find( it->second.begin(), it->second.end(), nn )==it->second.end() ){ + it->second.push_back( nn ); + Trace("cbqi-presolve") << " - " << n[i] << " = " << nn << std::endl; + } + } + } + } + for( unsigned i=0; i& vars, std::vector< Node >& terms, + std::map< Node, std::vector< Node > >& teq, Node f, std::vector< Node >& conj ) { + if( conj.size()<1000 ){ + if( terms.size()==f[0].getNumChildren() ){ + Node c = f[1].substitute( vars.begin(), vars.end(), terms.begin(), terms.end() ); + conj.push_back( c ); + }else{ + unsigned i = terms.size(); + Node v = f[0][i]; + terms.push_back( Node::null() ); + for( unsigned j=0; j ps_vars; + std::map< Node, std::vector< Node > > teq; + for( unsigned i=0; i terms; + std::vector< Node > conj; + getPresolveEqConjuncts( ps_vars, terms, teq, q, conj ); + + if( !conj.empty() ){ + Node lem = conj.size()==1 ? conj[0] : NodeManager::currentNM()->mkNode( AND, conj ); + Node g = NodeManager::currentNM()->mkSkolem( "g", NodeManager::currentNM()->booleanType() ); + lem = NodeManager::currentNM()->mkNode( OR, g, lem ); + Trace("cbqi-presolve-debug") << "Presolve lemma : " << lem << std::endl; + d_qe->getOutputChannel().lemma( lem, false, true ); + } + } +} + +void CegInstantiator::processAssertions() { + Trace("cbqi-proc") << "--- Process assertions, #var = " << d_vars.size() << ", #aux-var = " << d_aux_vars.size() << std::endl; + d_curr_asserts.clear(); + d_curr_eqc.clear(); + d_curr_type_eqc.clear(); + + // must use master equality engine to avoid value instantiations + eq::EqualityEngine* ee = d_qe->getMasterEqualityEngine(); + //to eliminate identified illegal terms + std::map< Node, Node > aux_subs; + + //for each variable + for( unsigned i=0; ihasTerm( pv ) ){ + Node pvr = ee->getRepresentative( pv ); + if( d_curr_eqc.find( pvr )==d_curr_eqc.end() ){ + Trace("cbqi-proc") << "Collect equivalence class " << pvr << std::endl; + eq::EqClassIterator eqc_i = eq::EqClassIterator( pvr, ee ); + while( !eqc_i.isFinished() ){ + d_curr_eqc[pvr].push_back( *eqc_i ); + ++eqc_i; + } + } + } + } + //collect assertions for relevant theories + for( unsigned i=0; igetTheoryEngine()->theoryOf( tid ); + if( theory && d_qe->getTheoryEngine()->isTheoryEnabled(tid) ){ + Trace("cbqi-proc") << "Collect assertions from theory " << tid << std::endl; + d_curr_asserts[tid].clear(); + //collect all assertions from theory + for( context::CDList::const_iterator it = theory->facts_begin(); it != theory->facts_end(); ++ it) { + Node lit = (*it).assertion; + Node atom = lit.getKind()==NOT ? lit[0] : lit; + if( d_is_nested_quant || std::find( d_ce_atoms.begin(), d_ce_atoms.end(), atom )!=d_ce_atoms.end() ){ + d_curr_asserts[tid].push_back( lit ); + Trace("cbqi-proc-debug") << "...add : " << lit << std::endl; + }else{ + Trace("cbqi-proc") << "...do not consider literal " << tid << " : " << lit << " since it is not part of CE body." << std::endl; + } + if( lit.getKind()==EQUAL ){ + std::map< Node, std::map< Node, Node > >::iterator itae = d_aux_eq.find( lit ); + if( itae!=d_aux_eq.end() ){ + for( std::map< Node, Node >::iterator itae2 = itae->second.begin(); itae2 != itae->second.end(); ++itae2 ){ + aux_subs[ itae2->first ] = itae2->second; + Trace("cbqi-proc") << "......add substitution : " << itae2->first << " -> " << itae2->second << std::endl; + } + } + }else if( atom.getKind()==BOOLEAN_TERM_VARIABLE ){ + if( std::find( d_aux_vars.begin(), d_aux_vars.end(), atom )!=d_aux_vars.end() ){ + Node val = NodeManager::currentNM()->mkConst( lit.getKind()!=NOT ); + aux_subs[ atom ] = val; + Trace("cbqi-proc") << "......add substitution : " << atom << " -> " << val << std::endl; + } + } + } + } + } + //collect equivalence classes that correspond to relevant theories + Trace("cbqi-proc-debug") << "...collect typed equivalence classes" << std::endl; + eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( ee ); + while( !eqcs_i.isFinished() ){ + Node r = *eqcs_i; + TypeNode rtn = r.getType(); + TheoryId tid = Theory::theoryOf( rtn ); + //if we care about the theory of this eqc + if( std::find( d_tids.begin(), d_tids.end(), tid )!=d_tids.end() ){ + if( rtn.isInteger() || rtn.isReal() ){ + rtn = rtn.getBaseType(); + } + Trace("cbqi-proc-debug") << "...type eqc: " << r << std::endl; + d_curr_type_eqc[rtn].push_back( r ); + if( d_curr_eqc.find( r )==d_curr_eqc.end() ){ + Trace("cbqi-proc") << "Collect equivalence class " << r << std::endl; + Trace("cbqi-proc-debug") << " "; + eq::EqClassIterator eqc_i = eq::EqClassIterator( r, ee ); + while( !eqc_i.isFinished() ){ + Trace("cbqi-proc-debug") << *eqc_i << " "; + d_curr_eqc[r].push_back( *eqc_i ); + ++eqc_i; + } + Trace("cbqi-proc-debug") << std::endl; + } + } + ++eqcs_i; + } + //construct substitution from auxiliary variable equalities (if e.g. ITE removal was applied to CE body of quantified formula) + std::vector< Node > subs_lhs; + std::vector< Node > subs_rhs; + for( unsigned i=0; i::iterator it = aux_subs.find( r ); + if( it!=aux_subs.end() ){ + addToAuxVarSubstitution( subs_lhs, subs_rhs, r, it->second ); + }else{ + Trace("cbqi-proc") << "....no substitution found for auxiliary variable " << r << "!!! type is " << r.getType() << std::endl; + Assert( false ); + } + } + + //apply substitutions to everything, if necessary + if( !subs_lhs.empty() ){ + Trace("cbqi-proc") << "Applying substitution : " << std::endl; + for( unsigned i=0; i " << subs_rhs[i] << std::endl; + } + for( std::map< TheoryId, std::vector< Node > >::iterator it = d_curr_asserts.begin(); it != d_curr_asserts.end(); ++it ){ + for( unsigned i=0; isecond.size(); i++ ){ + Node lit = it->second[i]; + lit = lit.substitute( subs_lhs.begin(), subs_lhs.end(), subs_rhs.begin(), subs_rhs.end() ); + lit = Rewriter::rewrite( lit ); + it->second[i] = lit; + } + } + for( std::map< Node, std::vector< Node > >::iterator it = d_curr_eqc.begin(); it != d_curr_eqc.end(); ++it ){ + for( unsigned i=0; isecond.size(); i++ ){ + Node n = it->second[i]; + n = n.substitute( subs_lhs.begin(), subs_lhs.end(), subs_rhs.begin(), subs_rhs.end() ); + n = Rewriter::rewrite( n ); + it->second[i] = n; + } + } + } + + //remove unecessary assertions + for( std::map< TheoryId, std::vector< Node > >::iterator it = d_curr_asserts.begin(); it != d_curr_asserts.end(); ++it ){ + std::vector< Node > akeep; + for( unsigned i=0; isecond.size(); i++ ){ + Node n = it->second[i]; + //must be an eligible term + if( isEligible( n ) ){ + //must contain at least one variable + if( !d_prog_var[n].empty() ){ + Trace("cbqi-proc") << "...literal[" << it->first << "] : " << n << std::endl; + akeep.push_back( n ); + }else{ + Trace("cbqi-proc") << "...remove literal from " << it->first << " : " << n << " since it contains no relevant variables." << std::endl; + } + }else{ + Trace("cbqi-proc") << "...remove literal from " << it->first << " : " << n << " since it contains ineligible terms." << std::endl; + } + } + it->second.clear(); + it->second.insert( it->second.end(), akeep.begin(), akeep.end() ); + } + + //remove duplicate terms from eqc + for( std::map< Node, std::vector< Node > >::iterator it = d_curr_eqc.begin(); it != d_curr_eqc.end(); ++it ){ + std::vector< Node > new_eqc; + for( unsigned i=0; isecond.size(); i++ ){ + if( std::find( new_eqc.begin(), new_eqc.end(), it->second[i] )==new_eqc.end() ){ + new_eqc.push_back( it->second[i] ); + } + } + it->second.clear(); + it->second.insert( it->second.end(), new_eqc.begin(), new_eqc.end() ); + } +} + +void CegInstantiator::addToAuxVarSubstitution( std::vector< Node >& subs_lhs, std::vector< Node >& subs_rhs, Node l, Node r ) { + r = r.substitute( subs_lhs.begin(), subs_lhs.end(), subs_rhs.begin(), subs_rhs.end() ); + + std::vector< Node > cl; + cl.push_back( l ); + std::vector< Node > cr; + cr.push_back( r ); + for( unsigned i=0; igetModel()->getValue( n ); +} + +Node CegInstantiator::getBoundVariable(TypeNode tn) +{ + unsigned index = 0; + std::unordered_map::iterator itb = + d_bound_var_index.find(tn); + if (itb != d_bound_var_index.end()) + { + index = itb->second; + } + d_bound_var_index[tn] = index + 1; + while (index >= d_bound_var[tn].size()) + { + std::stringstream ss; + ss << "x" << index; + Node x = NodeManager::currentNM()->mkBoundVar(ss.str(), tn); + d_bound_var[tn].push_back(x); + } + return d_bound_var[tn][index]; +} + +bool CegInstantiator::isSolvedAssertion(Node n) const +{ + return d_solved_asserts.find(n) != d_solved_asserts.end(); +} + +void CegInstantiator::markSolved(Node n, bool solved) +{ + if (solved) + { + d_solved_asserts.insert(n); + } + else if (isSolvedAssertion(n)) + { + d_solved_asserts.erase(n); + } +} + +void CegInstantiator::collectCeAtoms( Node n, std::map< Node, bool >& visited ) { + if( n.getKind()==FORALL ){ + d_is_nested_quant = true; + }else if( visited.find( n )==visited.end() ){ + visited[n] = true; + if( TermUtil::isBoolConnectiveTerm( n ) ){ + for( unsigned i=0; i& lems, std::vector< Node >& ce_vars ) { + Trace("cbqi-reg") << "Register counterexample lemma..." << std::endl; + d_input_vars.clear(); + d_input_vars.insert(d_input_vars.end(), ce_vars.begin(), ce_vars.end()); + + //Assert( d_vars.empty() ); + d_vars.clear(); + registerTheoryId(THEORY_UF); + for (unsigned i = 0; i < ce_vars.size(); i++) + { + Trace("cbqi-reg") << " register input variable : " << ce_vars[i] << std::endl; + registerVariable(ce_vars[i]); + } + + // preprocess with all relevant instantiator preprocessors + Trace("cbqi-debug") << "Preprocess based on theory-specific methods..." + << std::endl; + std::vector pvars; + pvars.insert(pvars.end(), d_vars.begin(), d_vars.end()); + for (std::pair& p : d_tipp) + { + p.second->registerCounterexampleLemma(lems, pvars); + } + // must register variables generated by preprocessors + Trace("cbqi-debug") << "Register variables from theory-specific methods " + << d_input_vars.size() << " " << pvars.size() << " ..." + << std::endl; + for (unsigned i = d_input_vars.size(), size = pvars.size(); i < size; ++i) + { + Trace("cbqi-reg") << " register theory preprocess variable : " << pvars[i] + << std::endl; + registerVariable(pvars[i]); + } + + //remove ITEs + IteSkolemMap iteSkolemMap; + d_qe->getTheoryEngine()->getTermFormulaRemover()->run(lems, iteSkolemMap); + //Assert( d_aux_vars.empty() ); + d_aux_vars.clear(); + d_aux_eq.clear(); + for(IteSkolemMap::iterator i = iteSkolemMap.begin(); i != iteSkolemMap.end(); ++i) { + Trace("cbqi-reg") << " register aux variable : " << i->first << std::endl; + registerVariable(i->first, true); + } + for( unsigned i=0; i voo; + bool doSort = false; + std::vector vars; + std::map > tvars; + for (unsigned i = 0, size = d_vars.size(); i < size; i++) + { + voo[d_vars[i]] = i; + d_var_order_index.push_back(0); + TypeNode tn = d_vars[i].getType(); + if (tn.isInteger()) + { + doSort = true; + tvars[tn].push_back(d_vars[i]); + } + else + { + vars.push_back(d_vars[i]); + } + } + if (doSort) + { + Trace("cbqi-debug") << "Sort variables based on ordering." << std::endl; + for (std::pair >& vs : tvars) + { + vars.insert(vars.end(), vs.second.begin(), vs.second.end()); + } + + Trace("cbqi-debug") << "Consider variables in this order : " << std::endl; + for (unsigned i = 0; i < vars.size(); i++) + { + d_var_order_index[voo[vars[i]]] = i; + Trace("cbqi-debug") << " " << vars[i] << " : " << vars[i].getType() + << ", index was : " << voo[vars[i]] << std::endl; + d_vars[i] = vars[i]; + } + Trace("cbqi-debug") << std::endl; + } + else + { + d_var_order_index.clear(); + } + } + + //collect atoms from all lemmas: we will only do bounds coming from original body + d_is_nested_quant = false; + std::map< Node, bool > visited; + for( unsigned i=0; igetTermEnumeration()->isClosedEnumerableType(tn); +} + +bool Instantiator::processEqualTerm(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + TermProperties& pv_prop, + Node n, + CegInstEffort effort) +{ + pv_prop.d_type = 0; + return ci->constructInstantiationInc(pv, n, pv_prop, sf); +} + +} /* CVC4::theory::quantifiers namespace */ +} /* CVC4::theory namespace */ +} /* CVC4 namespace */ diff --git a/src/theory/quantifiers/cegqi/ceg_instantiator.h b/src/theory/quantifiers/cegqi/ceg_instantiator.h new file mode 100644 index 000000000..03983fe1a --- /dev/null +++ b/src/theory/quantifiers/cegqi/ceg_instantiator.h @@ -0,0 +1,783 @@ +/********************* */ +/*! \file ceg_instantiator.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 quantifier instantiation + **/ + + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__QUANTIFIERS__CEG_INSTANTIATOR_H +#define __CVC4__THEORY__QUANTIFIERS__CEG_INSTANTIATOR_H + +#include "theory/quantifiers_engine.h" +#include "util/statistics_registry.h" + +namespace CVC4 { +namespace theory { + +namespace arith { + class TheoryArith; +} + +namespace quantifiers { + +class CegqiOutput { +public: + virtual ~CegqiOutput() {} + virtual bool doAddInstantiation( std::vector< Node >& subs ) = 0; + virtual bool isEligibleForInstantiation( Node n ) = 0; + virtual bool addLemma( Node lem ) = 0; +}; + +class Instantiator; +class InstantiatorPreprocess; + +/** Term Properties + * + * Stores properties for a variable to solve for in counterexample-guided + * instantiation. + * + * For LIA, this includes the coefficient of the variable, and the bound type + * for the variable. + */ +class TermProperties { +public: + TermProperties() : d_type(0) {} + virtual ~TermProperties() {} + + // type of property for a term + // for arithmetic this corresponds to bound type (0:equal, 1:upper bound, -1:lower bound) + int d_type; + // for arithmetic + Node d_coeff; + // get cache node + // we consider terms + TermProperties that are unique up to their cache node + // (see constructInstantiationInc) + virtual Node getCacheNode() const { return d_coeff; } + // is non-basic + virtual bool isBasic() const { return d_coeff.isNull(); } + // get modified term + virtual Node getModifiedTerm( Node pv ) const { + if( !d_coeff.isNull() ){ + return NodeManager::currentNM()->mkNode( kind::MULT, d_coeff, pv ); + }else{ + return pv; + } + } + // compose property, should be such that: + // p.getModifiedTerm( this.getModifiedTerm( x ) ) = this_updated.getModifiedTerm( x ) + virtual void composeProperty( TermProperties& p ){ + if( !p.d_coeff.isNull() ){ + if( d_coeff.isNull() ){ + d_coeff = p.d_coeff; + }else{ + d_coeff = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::MULT, d_coeff, p.d_coeff ) ); + } + } + } +}; + +/** Solved form + * This specifies a substitution: + * { d_props[i].getModifiedTerm(d_vars[i]) -> d_subs[i] | i = 0...|d_vars| } + */ +class SolvedForm { +public: + // list of variables + std::vector< Node > d_vars; + // list of terms that they are substituted to + std::vector< Node > d_subs; + // properties for each variable + std::vector< TermProperties > d_props; + // the variables that have non-basic information regarding how they are substituted + // an example is for linear arithmetic, we store "substitution with coefficients". + std::vector d_non_basic; + // push the substitution pv_prop.getModifiedTerm(pv) -> n + void push_back( Node pv, Node n, TermProperties& pv_prop ){ + d_vars.push_back( pv ); + d_subs.push_back( n ); + d_props.push_back( pv_prop ); + if( !pv_prop.isBasic() ){ + d_non_basic.push_back( pv ); + // update theta value + Node new_theta = getTheta(); + if( new_theta.isNull() ){ + new_theta = pv_prop.d_coeff; + }else{ + new_theta = NodeManager::currentNM()->mkNode( kind::MULT, new_theta, pv_prop.d_coeff ); + new_theta = Rewriter::rewrite( new_theta ); + } + d_theta.push_back( new_theta ); + } + } + // pop the substitution pv_prop.getModifiedTerm(pv) -> n + void pop_back( Node pv, Node n, TermProperties& pv_prop ){ + d_vars.pop_back(); + d_subs.pop_back(); + d_props.pop_back(); + if( !pv_prop.isBasic() ){ + d_non_basic.pop_back(); + // update theta value + d_theta.pop_back(); + } + } + // is this solved form empty? + bool empty() { return d_vars.empty(); } +public: + // theta values (for LIA, see Section 4 of Reynolds/King/Kuncak FMSD 2017) + std::vector< Node > d_theta; + // get the current value for theta (for LIA, see Section 4 of Reynolds/King/Kuncak FMSD 2017) + Node getTheta() { + if( d_theta.empty() ){ + return Node::null(); + }else{ + return d_theta[d_theta.size()-1]; + } + } +}; + +/** instantiation effort levels + * + * This effort is used to stratify the construction of + * instantiations for some theories that may result to + * using model value instantiations. + */ +enum CegInstEffort +{ + // uninitialized + CEG_INST_EFFORT_NONE, + // standard effort level + CEG_INST_EFFORT_STANDARD, + // standard effort level, but we have used model values + CEG_INST_EFFORT_STANDARD_MV, + // full effort level + CEG_INST_EFFORT_FULL +}; + +std::ostream& operator<<(std::ostream& os, CegInstEffort e); + +/** instantiation phase for variables + * + * This indicates the phase in which we constructed + * a substitution for individual variables. + */ +enum CegInstPhase +{ + // uninitialized + CEG_INST_PHASE_NONE, + // instantiation constructed during traversal of equivalence classes + CEG_INST_PHASE_EQC, + // instantiation constructed during solving equalities + CEG_INST_PHASE_EQUAL, + // instantiation constructed by looking at theory assertions + CEG_INST_PHASE_ASSERTION, + // instantiation constructed by querying model value + CEG_INST_PHASE_MVALUE, +}; + +std::ostream& operator<<(std::ostream& os, CegInstPhase phase); + +/** Ceg instantiator + * + * This class manages counterexample-guided quantifier instantiation + * for a single quantified formula. + * + * For details on counterexample-guided quantifier instantiation + * (for linear arithmetic), see Reynolds/King/Kuncak FMSD 2017. + */ +class CegInstantiator { + public: + CegInstantiator(QuantifiersEngine* qe, + CegqiOutput* out, + bool use_vts_delta = true, + bool use_vts_inf = true); + virtual ~CegInstantiator(); + /** check + * This adds instantiations based on the state of d_vars in current context + * on the output channel d_out of this class. + */ + bool check(); + /** presolve for quantified formula + * + * This initializes formulas that help static learning of the quantifier-free + * solver. It is only called if the option --cbqi-prereg-inst is used. + */ + void presolve(Node q); + /** Register the counterexample lemma + * + * lems : contains the conjuncts of the counterexample lemma of the + * quantified formula we are processing. The counterexample + * lemma is the formula { ~phi[e/x] } in Figure 1 of Reynolds + * et al. FMSD 2017. + * ce_vars : contains the variables e. Notice these are variables of + * INST_CONSTANT kind, since we do not permit bound + * variables in assertions. + * + * This method may modify the set of lemmas lems based on: + * - ITE removal, + * - Theory-specific preprocessing of instantiation lemmas. + * It may also introduce new variables to ce_vars if necessary. + */ + void registerCounterexampleLemma(std::vector& lems, + std::vector& ce_vars); + /** get the output channel of this class */ + CegqiOutput* getOutput() { return d_out; } + //------------------------------interface for instantiators + /** get quantifiers engine */ + QuantifiersEngine* getQuantifiersEngine() { return d_qe; } + /** push stack variable + * This adds a new variable to solve for in the stack + * of variables we are processing. This stack is only + * used for datatypes, where e.g. the DtInstantiator + * solving for a list x may push the stack "variables" + * head(x) and tail(x). + */ + void pushStackVariable(Node v); + /** pop stack variable */ + void popStackVariable(); + /** construct instantiation increment + * + * Adds the substitution { pv_prop.getModifiedTerm(pv) -> n } to the current + * instantiation, specified by sf. + * + * This function returns true if a call to + * QuantifiersEngine::addInstantiation(...) + * was successfully made in a recursive call. + * + * The solved form sf is reverted to its original state if + * this function returns false, or + * revertOnSuccess is true and this function returns true. + */ + bool constructInstantiationInc(Node pv, + Node n, + TermProperties& pv_prop, + SolvedForm& sf, + bool revertOnSuccess = false); + /** get the current model value of term n */ + Node getModelValue(Node n); + /** get bound variable for type + * + * This gets the next (canonical) bound variable of + * type tn. This can be used for instance when + * constructing instantiations that involve choice expressions. + */ + Node getBoundVariable(TypeNode tn); + /** has this assertion been marked as solved? */ + bool isSolvedAssertion(Node n) const; + /** marked solved */ + void markSolved(Node n, bool solved = true); + //------------------------------end interface for instantiators + + /** + * Get the number of atoms in the counterexample lemma of the quantified + * formula we are processing with this class. + */ + unsigned getNumCEAtoms() { return d_ce_atoms.size(); } + /** + * Get the i^th atom of the counterexample lemma of the quantified + * formula we are processing with this class. + */ + Node getCEAtom(unsigned i) { return d_ce_atoms[i]; } + /** is n a term that is eligible for instantiation? */ + bool isEligible(Node n); + /** does n have variable pv? */ + bool hasVariable(Node n, Node pv); + /** are we using delta for LRA virtual term substitution? */ + bool useVtsDelta() { return d_use_vts_delta; } + /** are we using infinity for LRA virtual term substitution? */ + bool useVtsInfinity() { return d_use_vts_inf; } + /** are we processing a nested quantified formula? */ + bool hasNestedQuantification() { return d_is_nested_quant; } + private: + /** quantified formula associated with this instantiator */ + QuantifiersEngine* d_qe; + /** output channel of this instantiator */ + CegqiOutput* d_out; + /** whether we are using delta for virtual term substitution + * (for quantified LRA). + */ + bool d_use_vts_delta; + /** whether we are using infinity for virtual term substitution + * (for quantified LRA). + */ + bool d_use_vts_inf; + + //-------------------------------globally cached + /** cache from nodes to the set of variables it contains + * (from the quantified formula we are instantiating). + */ + std::unordered_map, + NodeHashFunction> + d_prog_var; + /** cache of the set of terms that we have established are + * ineligible for instantiation. + */ + std::unordered_set d_inelig; + /** ensures n is in d_prog_var and d_inelig. */ + void computeProgVars(Node n); + //-------------------------------end globally cached + + //-------------------------------cached per round + /** current assertions per theory */ + std::map > d_curr_asserts; + /** map from representatives to the terms in their equivalence class */ + std::map > d_curr_eqc; + /** map from types to representatives of that type */ + std::map > d_curr_type_eqc; + /** solved asserts */ + std::unordered_set d_solved_asserts; + /** process assertions + * This is called once at the beginning of check to + * set up all necessary information for constructing instantiations, + * such as the above data structures. + */ + void processAssertions(); + /** add to auxiliary variable substitution + * This adds the substitution l -> r to the auxiliary + * variable substitution subs_lhs -> subs_rhs, and serializes + * it (applies it to existing substitutions). + */ + void addToAuxVarSubstitution(std::vector& subs_lhs, + std::vector& subs_rhs, + Node l, + Node r); + /** cache bound variables for type returned + * by getBoundVariable(...). + */ + std::unordered_map, TypeNodeHashFunction> + d_bound_var; + /** current index of bound variables for type. + * The next call to getBoundVariable(...) for + * type tn returns the d_bound_var_index[tn]^th + * element of d_bound_var[tn], or a fresh variable + * if not in bounds. + */ + std::unordered_map + d_bound_var_index; + //-------------------------------end cached per round + + //-------------------------------data per theory + /** relevant theory ids + * A list of theory ids that contain at least one + * constraint in the body of the quantified formula we + * are processing. + */ + std::vector d_tids; + /** map from theory ids to instantiator preprocessors */ + std::map d_tipp; + /** registers all theory ids associated with type tn + * + * This recursively calls registerTheoryId for typeOf(tn') for + * all parameters and datatype subfields of type tn. + * visited stores the types we have already visited. + */ + void registerTheoryIds(TypeNode tn, std::map& visited); + /** register theory id tid + * + * This is called when the quantified formula we are processing + * with this class involves theory tid. In this case, we will + * construct instantiations based on the assertion list of this theory. + */ + void registerTheoryId(TheoryId tid); + //-------------------------------end data per theory + + //-------------------------------the variables + /** the variables we are instantiating + * + * This is a superset of the variables for the instantiations we are + * generating and sending on the output channel of this class. + */ + std::vector d_vars; + /** set form of d_vars */ + std::unordered_set d_vars_set; + /** index of variables reported in instantiation */ + std::vector d_var_order_index; + /** number of input variables + * + * These are the variables, in order, for the instantiations we are generating + * and sending on the output channel of this class. + */ + std::vector d_input_vars; + /** literals to equalities for aux vars + * This stores entries of the form + * L -> ( k -> t ) + * where + * k is a variable in d_aux_vars, + * L is a literal that if asserted implies that our + * instantiation should map { k -> t }. + * For example, if a term of the form + * ite( C, t1, t2 ) + * was replaced by k, we get this (top-level) assertion: + * ite( C, k=t1, k=t2 ) + * The vector d_aux_eq contains the exact form of + * the literals in the above constraint that they would + * appear in assertions, meaning d_aux_eq may contain: + * t1=k -> ( k -> t1 ) + * t2=k -> ( k -> t2 ) + * where t1=k and t2=k are the rewritten form of + * k=t1 and k=t2 respectively. + */ + std::map > d_aux_eq; + /** auxiliary variables + * These variables include the result of removing ITE + * terms from the quantified formula we are processing. + * These variables must be eliminated from constraints + * as a preprocess step to check(). + */ + std::vector d_aux_vars; + /** register variable */ + void registerVariable(Node v, bool is_aux = false); + //-------------------------------the variables + + //-------------------------------quantified formula info + /** are we processing a nested quantified formula? */ + bool d_is_nested_quant; + /** the atoms of the CE lemma */ + std::vector d_ce_atoms; + /** collect atoms */ + void collectCeAtoms(Node n, std::map& visited); + //-------------------------------end quantified formula info + + //-------------------------------current state + /** the current effort level of the instantiator + * This indicates the effort Instantiator objects + * will put into the terms they return. + */ + CegInstEffort d_effort; + /** for each variable, the instantiator used for that variable */ + std::map d_active_instantiators; + /** map from variables to the index in the prefix of the quantified + * formula we are processing. + */ + std::map d_curr_index; + /** map from variables to the phase in which we instantiated them */ + std::map d_curr_iphase; + /** cache of current substitutions tried between activate/deactivate */ + std::map > > d_curr_subs_proc; + /** stack of temporary variables we are solving for, + * e.g. subfields of datatypes. + */ + std::vector d_stack_vars; + /** activate instantiation variable v at index + * + * This is called when variable (inst constant) v is activated + * for the quantified formula we are processing. + * This method initializes the instantiator class for + * that variable if necessary, where this class is + * determined by the type of v. It also initializes + * the cache of values we have tried substituting for v + * (in d_curr_subs_proc), and stores its index. + */ + void activateInstantiationVariable(Node v, unsigned index); + /** deactivate instantiation variable + * + * This is called when variable (inst constant) v is deactivated + * for the quantified formula we are processing. + */ + void deactivateInstantiationVariable(Node v); + //-------------------------------end current state + + //---------------------------------for applying substitutions + /** can use basic substitution */ + bool canApplyBasicSubstitution( Node n, std::vector< Node >& non_basic ); + /** apply substitution + * We wish to process the substitution: + * ( pv = n * sf ) + * where pv is a variable with type tn, and * denotes application of substitution. + * The return value "ret" and pv_prop is such that the above equality is equivalent to + * ( pv_prop.getModifiedTerm(pv) = ret ) + */ + Node applySubstitution( TypeNode tn, Node n, SolvedForm& sf, TermProperties& pv_prop, bool try_coeff = true ) { + return applySubstitution( tn, n, sf.d_vars, sf.d_subs, sf.d_props, sf.d_non_basic, pv_prop, try_coeff ); + } + /** apply substitution, with solved form expanded to subs/prop/non_basic/vars */ + Node applySubstitution( TypeNode tn, Node n, std::vector< Node >& vars, std::vector< Node >& subs, std::vector< TermProperties >& prop, + std::vector< Node >& non_basic, TermProperties& pv_prop, bool try_coeff = true ); + /** apply substitution to literal lit + * The return value is equivalent to ( lit * sf ) + * where * denotes application of substitution. + */ + Node applySubstitutionToLiteral( Node lit, SolvedForm& sf ) { + return applySubstitutionToLiteral( lit, sf.d_vars, sf.d_subs, sf.d_props, sf.d_non_basic ); + } + /** apply substitution to literal lit, with solved form expanded to subs/prop/non_basic/vars */ + Node applySubstitutionToLiteral( Node lit, std::vector< Node >& vars, std::vector< Node >& subs, std::vector< TermProperties >& prop, + std::vector< Node >& non_basic ); + //---------------------------------end for applying substitutions + + /** map from variables to their instantiators */ + std::map d_instantiator; + + /** construct instantiation + * This method constructs the current instantiation, where we + * are currently processing the i^th variable in d_vars. + * It returns true if a successful call to the output channel's + * doAddInstantiation was made. + */ + bool constructInstantiation(SolvedForm& sf, unsigned i); + /** do add instantiation + * This method is called by the above function after we finalize the + * variables/substitution and auxiliary lemmas. + * It returns true if a successful call to the output channel's + * doAddInstantiation was made. + */ + bool doAddInstantiation(std::vector& vars, + std::vector& subs, + std::vector& lemmas); +}; + +/** Instantiator class + * + * This is a virtual class that is used for methods for constructing + * substitutions for individual variables in counterexample-guided + * instantiation techniques. + * + * This class contains a set of interface functions below, which are called + * based on a fixed instantiation method implemented by CegInstantiator. + * In these calls, the Instantiator in turn makes calls to methods in + * CegInstanatior (primarily constructInstantiationInc). + */ +class Instantiator { +public: + Instantiator( QuantifiersEngine * qe, TypeNode tn ); + virtual ~Instantiator(){} + /** reset + * This is called once, prior to any of the below methods are called. + * This function sets up any initial information necessary for constructing + * instantiations for pv based on the current context. + */ + virtual void reset(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) + { + } + + /** process equal term + * + * This method is called when the entailment: + * E |= pv_prop.getModifiedTerm(pv) = n + * holds in the current context E, and n is eligible for instantiation. + * + * Returns true if an instantiation was successfully added via a call to + * CegInstantiator::constructInstantiationInc. + */ + virtual bool processEqualTerm(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + TermProperties& pv_prop, + Node n, + CegInstEffort effort); + /** process equal terms + * + * This method is called after process equal term, where eqc is the list of + * eligible terms in the equivalence class of pv. + * + * Returns true if an instantiation was successfully added via a call to + * CegInstantiator::constructInstantiationInc. + */ + virtual bool processEqualTerms(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + std::vector& eqc, + CegInstEffort effort) + { + return false; + } + + /** whether the instantiator implements processEquality */ + virtual bool hasProcessEquality(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) + { + return false; + } + /** process equality + * The input is such that term_props.size() = terms.size() = 2 + * This method is called when the entailment: + * E ^ term_props[0].getModifiedTerm(x0) = + * terms[0] ^ term_props[1].getModifiedTerm(x1) = terms[1] |= x0 = x1 + * holds in current context E for fresh variables xi, terms[i] are eligible, + * and at least one terms[i] contains pv for i = 0,1. + * Notice in the basic case, E |= terms[0] = terms[1]. + * + * Returns true if an instantiation was successfully added via a call to + * CegInstantiator::constructInstantiationInc. + */ + virtual bool processEquality(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + std::vector& term_props, + std::vector& terms, + CegInstEffort effort) + { + return false; + } + + /** whether the instantiator implements processAssertion for any literal */ + virtual bool hasProcessAssertion(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) + { + return false; + } + /** has process assertion + * + * This method is called when the entailment: + * E |= lit + * holds in current context E. Typically, lit belongs to the list of current + * assertions. + * + * This method is used to determine whether the instantiator implements + * processAssertion for literal lit. + * If this method returns null, then this intantiator does not handle the + * literal lit. Otherwise, this method returns a literal lit' such that: + * (1) lit' is true in the current model, + * (2) lit' implies lit. + * where typically lit' = lit. + */ + virtual Node hasProcessAssertion(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + Node lit, + CegInstEffort effort) + { + return Node::null(); + } + /** process assertion + * This method processes the assertion slit for variable pv. + * lit : the substituted form (under sf) of a literal returned by + * hasProcessAssertion + * alit : the asserted literal, given as input to hasProcessAssertion + * + * Returns true if an instantiation was successfully added via a call to + * CegInstantiator::constructInstantiationInc. + */ + virtual bool processAssertion(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + Node lit, + Node alit, + CegInstEffort effort) + { + return false; + } + /** process assertions + * + * Called after processAssertion is called for each literal asserted to the + * instantiator. + * + * Returns true if an instantiation was successfully added via a call to + * CegInstantiator::constructInstantiationInc. + */ + virtual bool processAssertions(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) + { + return false; + } + + /** do we use the model value as instantiation for pv? + * This method returns true if we use model value instantiations + * at the same effort level as those determined by this instantiator. + */ + virtual bool useModelValue(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) + { + return effort > CEG_INST_EFFORT_STANDARD; + } + /** do we allow the model value as instantiation for pv? */ + virtual bool allowModelValue(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) + { + return d_closed_enum_type; + } + + /** do we need to postprocess the solved form for pv? */ + virtual bool needsPostProcessInstantiationForVariable(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) + { + return false; + } + /** postprocess the solved form for pv + * + * This method returns true if we successfully postprocessed the solved form. + * lemmas is a set of lemmas we wish to return along with the instantiation. + */ + virtual bool postProcessInstantiationForVariable(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort, + std::vector& lemmas) + { + return true; + } + + /** Identify this module (for debugging) */ + virtual std::string identify() const { return "Default"; } + protected: + /** the type of the variable we are instantiating */ + TypeNode d_type; + /** whether d_type is a closed enumerable type */ + bool d_closed_enum_type; +}; + +class ModelValueInstantiator : public Instantiator { +public: + ModelValueInstantiator( QuantifiersEngine * qe, TypeNode tn ) : Instantiator( qe, tn ){} + virtual ~ModelValueInstantiator(){} + bool useModelValue(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) + { + return true; + } + std::string identify() const { return "ModelValue"; } +}; + +/** instantiator preprocess + * + * This class implements techniques for preprocessing the counterexample lemma + * generated for counterexample-guided quantifier instantiation. + */ +class InstantiatorPreprocess +{ + public: + InstantiatorPreprocess() {} + virtual ~InstantiatorPreprocess() {} + /** register counterexample lemma + * This implements theory-specific preprocessing and registration + * of counterexample lemmas, with the same contract as + * CegInstantiation::registerCounterexampleLemma. + */ + virtual void registerCounterexampleLemma(std::vector& lems, + std::vector& ce_vars) + { + } +}; + +} /* CVC4::theory::quantifiers namespace */ +} /* CVC4::theory namespace */ +} /* CVC4 namespace */ + +#endif diff --git a/src/theory/quantifiers/cegqi/ceg_t_instantiator.cpp b/src/theory/quantifiers/cegqi/ceg_t_instantiator.cpp new file mode 100644 index 000000000..e6e64201e --- /dev/null +++ b/src/theory/quantifiers/cegqi/ceg_t_instantiator.cpp @@ -0,0 +1,1990 @@ +/********************* */ +/*! \file ceg_t_instantiator.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 theory-specific counterexample-guided quantifier instantiation + **/ + +#include "theory/quantifiers/cegqi/ceg_t_instantiator.h" + +#include "options/quantifiers_options.h" +#include "theory/quantifiers/first_order_model.h" +#include "theory/quantifiers/term_database.h" +#include "theory/quantifiers/term_util.h" +#include "theory/quantifiers/quantifiers_rewriter.h" +#include "theory/quantifiers/ematching/trigger.h" + +#include "theory/arith/arith_msum.h" +#include "theory/arith/partial_model.h" +#include "theory/arith/theory_arith.h" +#include "theory/arith/theory_arith_private.h" +#include "theory/bv/theory_bv_utils.h" +#include "util/bitvector.h" +#include "util/random.h" + +#include +#include + +using namespace std; +using namespace CVC4::kind; +using namespace CVC4::context; + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +struct BvLinearAttributeId {}; +using BvLinearAttribute = expr::Attribute; + +Node ArithInstantiator::getModelBasedProjectionValue( CegInstantiator * ci, Node e, Node t, bool isLower, Node c, Node me, Node mt, Node theta, Node inf_coeff, Node delta_coeff ) { + Node val = t; + Trace("cegqi-arith-bound2") << "Value : " << val << std::endl; + Assert( !e.getType().isInteger() || t.getType().isInteger() ); + Assert( !e.getType().isInteger() || mt.getType().isInteger() ); + //add rho value + //get the value of c*e + Node ceValue = me; + Node new_theta = theta; + if( !c.isNull() ){ + Assert( c.getType().isInteger() ); + ceValue = NodeManager::currentNM()->mkNode( MULT, ceValue, c ); + ceValue = Rewriter::rewrite( ceValue ); + if( new_theta.isNull() ){ + new_theta = c; + }else{ + new_theta = NodeManager::currentNM()->mkNode( MULT, new_theta, c ); + new_theta = Rewriter::rewrite( new_theta ); + } + Trace("cegqi-arith-bound2") << "...c*e = " << ceValue << std::endl; + Trace("cegqi-arith-bound2") << "...theta = " << new_theta << std::endl; + } + if( !new_theta.isNull() && e.getType().isInteger() ){ + Node rho; + //if( !mt.getType().isInteger() ){ + //round up/down + //mt = NodeManager::currentNM()->mkNode( + //} + if( isLower ){ + rho = NodeManager::currentNM()->mkNode( MINUS, ceValue, mt ); + }else{ + rho = NodeManager::currentNM()->mkNode( MINUS, mt, ceValue ); + } + rho = Rewriter::rewrite( rho ); + Trace("cegqi-arith-bound2") << "...rho = " << me << " - " << mt << " = " << rho << std::endl; + Trace("cegqi-arith-bound2") << "..." << rho << " mod " << new_theta << " = "; + rho = NodeManager::currentNM()->mkNode( INTS_MODULUS_TOTAL, rho, new_theta ); + rho = Rewriter::rewrite( rho ); + Trace("cegqi-arith-bound2") << rho << std::endl; + Kind rk = isLower ? PLUS : MINUS; + val = NodeManager::currentNM()->mkNode( rk, val, rho ); + val = Rewriter::rewrite( val ); + Trace("cegqi-arith-bound2") << "(after rho) : " << val << std::endl; + } + if( !inf_coeff.isNull() ){ + Assert( !d_vts_sym[0].isNull() ); + val = NodeManager::currentNM()->mkNode( PLUS, val, NodeManager::currentNM()->mkNode( MULT, inf_coeff, d_vts_sym[0] ) ); + val = Rewriter::rewrite( val ); + } + if( !delta_coeff.isNull() ){ + //create delta here if necessary + val = NodeManager::currentNM()->mkNode( PLUS, val, NodeManager::currentNM()->mkNode( MULT, delta_coeff, ci->getQuantifiersEngine()->getTermUtil()->getVtsDelta() ) ); + val = Rewriter::rewrite( val ); + } + return val; +} + +//this isolates the atom into solved form +// veq_c * pv <> val + vts_coeff_delta * delta + vts_coeff_inf * inf +// ensures val is Int if pv is Int, and val does not contain vts symbols +int ArithInstantiator::solve_arith( CegInstantiator * ci, Node pv, Node atom, Node& veq_c, Node& val, Node& vts_coeff_inf, Node& vts_coeff_delta ) { + int ires = 0; + Trace("cegqi-arith-debug") << "isolate for " << pv << " in " << atom << std::endl; + std::map< Node, Node > msum; + if (ArithMSum::getMonomialSumLit(atom, msum)) + { + Trace("cegqi-arith-debug") << "got monomial sum: " << std::endl; + if( Trace.isOn("cegqi-arith-debug") ){ + ArithMSum::debugPrintMonomialSum(msum, "cegqi-arith-debug"); + } + TypeNode pvtn = pv.getType(); + //remove vts symbols from polynomial + Node vts_coeff[2]; + for( unsigned t=0; t<2; t++ ){ + if( !d_vts_sym[t].isNull() ){ + std::map< Node, Node >::iterator itminf = msum.find( d_vts_sym[t] ); + if( itminf!=msum.end() ){ + vts_coeff[t] = itminf->second; + if( vts_coeff[t].isNull() ){ + vts_coeff[t] = NodeManager::currentNM()->mkConst( Rational( 1 ) ); + } + //negate if coefficient on variable is positive + std::map< Node, Node >::iterator itv = msum.find( pv ); + if( itv!=msum.end() ){ + //multiply by the coefficient we will isolate for + if( itv->second.isNull() ){ + vts_coeff[t] = ArithMSum::negate(vts_coeff[t]); + }else{ + if( !pvtn.isInteger() ){ + vts_coeff[t] = NodeManager::currentNM()->mkNode( MULT, NodeManager::currentNM()->mkConst( Rational(-1) / itv->second.getConst() ), vts_coeff[t] ); + vts_coeff[t] = Rewriter::rewrite( vts_coeff[t] ); + }else if( itv->second.getConst().sgn()==1 ){ + vts_coeff[t] = ArithMSum::negate(vts_coeff[t]); + } + } + } + Trace("cegqi-arith-debug") << "vts[" << t << "] coefficient is " << vts_coeff[t] << std::endl; + msum.erase( d_vts_sym[t] ); + } + } + } + + ires = ArithMSum::isolate(pv, msum, veq_c, val, atom.getKind()); + if( ires!=0 ){ + Node realPart; + if( Trace.isOn("cegqi-arith-debug") ){ + Trace("cegqi-arith-debug") << "Isolate : "; + if( !veq_c.isNull() ){ + Trace("cegqi-arith-debug") << veq_c << " * "; + } + Trace("cegqi-arith-debug") << pv << " " << atom.getKind() << " " << val << std::endl; + } + if( options::cbqiAll() ){ + // when not pure LIA/LRA, we must check whether the lhs contains pv + if( TermUtil::containsTerm( val, pv ) ){ + Trace("cegqi-arith-debug") << "fail : contains bad term" << std::endl; + return 0; + } + } + if( pvtn.isInteger() && ( ( !veq_c.isNull() && !veq_c.getType().isInteger() ) || !val.getType().isInteger() ) ){ + //redo, split integer/non-integer parts + bool useCoeff = false; + Integer coeff = ci->getQuantifiersEngine()->getTermUtil()->d_one.getConst().getNumerator(); + for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){ + if( it->first.isNull() || it->first.getType().isInteger() ){ + if( !it->second.isNull() ){ + coeff = coeff.lcm( it->second.getConst().getDenominator() ); + useCoeff = true; + } + } + } + //multiply everything by this coefficient + Node rcoeff = NodeManager::currentNM()->mkConst( Rational( coeff ) ); + std::vector< Node > real_part; + for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){ + if( useCoeff ){ + if( it->second.isNull() ){ + msum[it->first] = rcoeff; + }else{ + msum[it->first] = Rewriter::rewrite( NodeManager::currentNM()->mkNode( MULT, it->second, rcoeff ) ); + } + } + if( !it->first.isNull() && !it->first.getType().isInteger() ){ + real_part.push_back( msum[it->first].isNull() ? it->first : NodeManager::currentNM()->mkNode( MULT, msum[it->first], it->first ) ); + } + } + //remove delta TODO: check this + vts_coeff[1] = Node::null(); + //multiply inf + if( !vts_coeff[0].isNull() ){ + vts_coeff[0] = Rewriter::rewrite( NodeManager::currentNM()->mkNode( MULT, rcoeff, vts_coeff[0] ) ); + } + realPart = real_part.empty() ? ci->getQuantifiersEngine()->getTermUtil()->d_zero : ( real_part.size()==1 ? real_part[0] : NodeManager::currentNM()->mkNode( PLUS, real_part ) ); + Assert( ci->getOutput()->isEligibleForInstantiation( realPart ) ); + //re-isolate + Trace("cegqi-arith-debug") << "Re-isolate..." << std::endl; + ires = ArithMSum::isolate(pv, msum, veq_c, val, atom.getKind()); + Trace("cegqi-arith-debug") << "Isolate for mixed Int/Real : " << veq_c << " * " << pv << " " << atom.getKind() << " " << val << std::endl; + Trace("cegqi-arith-debug") << " real part : " << realPart << std::endl; + if( ires!=0 ){ + int ires_use = ( msum[pv].isNull() || msum[pv].getConst().sgn()==1 ) ? 1 : -1; + val = Rewriter::rewrite( NodeManager::currentNM()->mkNode( ires_use==-1 ? PLUS : MINUS, + NodeManager::currentNM()->mkNode( ires_use==-1 ? MINUS : PLUS, val, realPart ), + NodeManager::currentNM()->mkNode( TO_INTEGER, realPart ) ) ); //TODO: round up for upper bounds? + Trace("cegqi-arith-debug") << "result : " << val << std::endl; + Assert( val.getType().isInteger() ); + } + } + } + vts_coeff_inf = vts_coeff[0]; + vts_coeff_delta = vts_coeff[1]; + Trace("cegqi-arith-debug") << "Return " << veq_c << " * " << pv << " " << atom.getKind() << " " << val << ", vts = (" << vts_coeff_inf << ", " << vts_coeff_delta << ")" << std::endl; + }else{ + Trace("cegqi-arith-debug") << "fail : could not get monomial sum" << std::endl; + } + return ires; +} + +void ArithInstantiator::reset(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) +{ + d_vts_sym[0] = ci->getQuantifiersEngine()->getTermUtil()->getVtsInfinity( d_type, false, false ); + d_vts_sym[1] = ci->getQuantifiersEngine()->getTermUtil()->getVtsDelta( false, false ); + for( unsigned i=0; i<2; i++ ){ + d_mbp_bounds[i].clear(); + d_mbp_coeff[i].clear(); + for( unsigned j=0; j<2; j++ ){ + d_mbp_vts_coeff[i][j].clear(); + } + d_mbp_lit[i].clear(); + } +} + +bool ArithInstantiator::processEquality(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + std::vector& term_props, + std::vector& terms, + CegInstEffort effort) +{ + Node eq_lhs = terms[0]; + Node eq_rhs = terms[1]; + Node lhs_coeff = term_props[0].d_coeff; + Node rhs_coeff = term_props[1].d_coeff; + //make the same coefficient + if( rhs_coeff!=lhs_coeff ){ + if( !rhs_coeff.isNull() ){ + Trace("cegqi-arith-debug") << "...mult lhs by " << rhs_coeff << std::endl; + eq_lhs = NodeManager::currentNM()->mkNode( MULT, rhs_coeff, eq_lhs ); + eq_lhs = Rewriter::rewrite( eq_lhs ); + } + if( !lhs_coeff.isNull() ){ + Trace("cegqi-arith-debug") << "...mult rhs by " << lhs_coeff << std::endl; + eq_rhs = NodeManager::currentNM()->mkNode( MULT, lhs_coeff, eq_rhs ); + eq_rhs = Rewriter::rewrite( eq_rhs ); + } + } + Node eq = eq_lhs.eqNode( eq_rhs ); + eq = Rewriter::rewrite( eq ); + Node val; + TermProperties pv_prop; + Node vts_coeff_inf; + Node vts_coeff_delta; + //isolate pv in the equality + int ires = solve_arith( ci, pv, eq, pv_prop.d_coeff, val, vts_coeff_inf, vts_coeff_delta ); + if( ires!=0 ){ + pv_prop.d_type = 0; + if (ci->constructInstantiationInc(pv, val, pv_prop, sf)) + { + return true; + } + } + + return false; +} + +Node ArithInstantiator::hasProcessAssertion(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + Node lit, + CegInstEffort effort) +{ + Node atom = lit.getKind()==NOT ? lit[0] : lit; + bool pol = lit.getKind()!=NOT; + //arithmetic inequalities and disequalities + if (atom.getKind() == GEQ || + (atom.getKind() == EQUAL && !pol && atom[0].getType().isReal())) { + return lit; + } else { + return Node::null(); + } +} + +bool ArithInstantiator::processAssertion(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + Node lit, + Node alit, + CegInstEffort effort) +{ + Node atom = lit.getKind()==NOT ? lit[0] : lit; + bool pol = lit.getKind()!=NOT; + //arithmetic inequalities and disequalities + Assert( atom.getKind()==GEQ || ( atom.getKind()==EQUAL && !pol && atom[0].getType().isReal() ) ); + // get model value for pv + Node pv_value = ci->getModelValue( pv ); + //cannot contain infinity? + Node vts_coeff_inf; + Node vts_coeff_delta; + Node val; + TermProperties pv_prop; + //isolate pv in the inequality + int ires = solve_arith( ci, pv, atom, pv_prop.d_coeff, val, vts_coeff_inf, vts_coeff_delta ); + if( ires!=0 ){ + //disequalities are either strict upper or lower bounds + unsigned rmax = ( atom.getKind()==GEQ || options::cbqiModel() ) ? 1 : 2; + for( unsigned r=0; rmkNode( PLUS, val, NodeManager::currentNM()->mkConst( Rational( uires ) ) ); + uval = Rewriter::rewrite( uval ); + }else{ + Assert( d_type.isReal() ); + //now is strict inequality + uires = uires*2; + } + } + }else{ + bool is_upper; + if( options::cbqiModel() ){ + // disequality is a disjunction : only consider the bound in the direction of the model + //first check if there is an infinity... + if( !vts_coeff_inf.isNull() ){ + //coefficient or val won't make a difference, just compare with zero + Trace("cegqi-arith-debug") << "Disequality : check infinity polarity " << vts_coeff_inf << std::endl; + Assert( vts_coeff_inf.isConst() ); + is_upper = ( vts_coeff_inf.getConst().sgn()==1 ); + }else{ + Node rhs_value = ci->getModelValue( val ); + Node lhs_value = pv_prop.getModifiedTerm( pv_value ); + if( !pv_prop.isBasic() ){ + lhs_value = pv_prop.getModifiedTerm( pv_value ); + lhs_value = Rewriter::rewrite( lhs_value ); + } + Trace("cegqi-arith-debug") << "Disequality : check model values " << lhs_value << " " << rhs_value << std::endl; + Assert( lhs_value!=rhs_value ); + Node cmp = NodeManager::currentNM()->mkNode( GEQ, lhs_value, rhs_value ); + cmp = Rewriter::rewrite( cmp ); + Assert( cmp.isConst() ); + is_upper = ( cmp!=ci->getQuantifiersEngine()->getTermUtil()->d_true ); + } + }else{ + is_upper = (r==0); + } + Assert( atom.getKind()==EQUAL && !pol ); + if( d_type.isInteger() ){ + uires = is_upper ? -1 : 1; + uval = NodeManager::currentNM()->mkNode( PLUS, val, NodeManager::currentNM()->mkConst( Rational( uires ) ) ); + uval = Rewriter::rewrite( uval ); + }else{ + Assert( d_type.isReal() ); + uires = is_upper ? -2 : 2; + } + } + if( Trace.isOn("cegqi-arith-bound-inf") ){ + Node pvmod = pv_prop.getModifiedTerm( pv ); + Trace("cegqi-arith-bound-inf") << "From " << lit << ", got : "; + Trace("cegqi-arith-bound-inf") << pvmod << " -> " << uval << ", styp = " << uires << std::endl; + } + //take into account delta + if( ci->useVtsDelta() && ( uires==2 || uires==-2 ) ){ + if( options::cbqiModel() ){ + Node delta_coeff = NodeManager::currentNM()->mkConst( Rational( uires > 0 ? 1 : -1 ) ); + if( vts_coeff_delta.isNull() ){ + vts_coeff_delta = delta_coeff; + }else{ + vts_coeff_delta = NodeManager::currentNM()->mkNode( PLUS, vts_coeff_delta, delta_coeff ); + vts_coeff_delta = Rewriter::rewrite( vts_coeff_delta ); + } + }else{ + Node delta = ci->getQuantifiersEngine()->getTermUtil()->getVtsDelta(); + uval = NodeManager::currentNM()->mkNode( uires==2 ? PLUS : MINUS, uval, delta ); + uval = Rewriter::rewrite( uval ); + } + } + if( options::cbqiModel() ){ + //just store bounds, will choose based on tighest bound + unsigned index = uires>0 ? 0 : 1; + d_mbp_bounds[index].push_back( uval ); + d_mbp_coeff[index].push_back( pv_prop.d_coeff ); + Trace("cegqi-arith-debug") << "Store bound " << index << " " << uval << " " << pv_prop.d_coeff << " " << vts_coeff_inf << " " << vts_coeff_delta << " " << lit << std::endl; + for( unsigned t=0; t<2; t++ ){ + d_mbp_vts_coeff[index][t].push_back( t==0 ? vts_coeff_inf : vts_coeff_delta ); + } + d_mbp_lit[index].push_back( lit ); + }else{ + //try this bound + pv_prop.d_type = uires>0 ? 1 : -1; + if (ci->constructInstantiationInc(pv, uval, pv_prop, sf)) + { + return true; + } + } + } + } + + + return false; +} + +bool ArithInstantiator::processAssertions(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) +{ + if (options::cbqiModel()) { + bool use_inf = ci->useVtsInfinity() && ( d_type.isInteger() ? options::cbqiUseInfInt() : options::cbqiUseInfReal() ); + bool upper_first = false; + if( options::cbqiMinBounds() ){ + upper_first = d_mbp_bounds[1].size() t_values[3]; + Node zero = ci->getQuantifiersEngine()->getTermUtil()->d_zero; + Node one = ci->getQuantifiersEngine()->getTermUtil()->d_one; + Node pv_value = ci->getModelValue( pv ); + //try optimal bounds + for( unsigned r=0; r<2; r++ ){ + int rr = upper_first ? (1-r) : r; + best_used[rr] = -1; + if( d_mbp_bounds[rr].empty() ){ + if( use_inf ){ + Trace("cegqi-arith-bound") << "No " << ( rr==0 ? "lower" : "upper" ) << " bounds for " << pv << " (type=" << d_type << ")" << std::endl; + //no bounds, we do +- infinity + Node val = ci->getQuantifiersEngine()->getTermUtil()->getVtsInfinity( d_type ); + //TODO : rho value for infinity? + if( rr==0 ){ + val = NodeManager::currentNM()->mkNode( UMINUS, val ); + val = Rewriter::rewrite( val ); + } + TermProperties pv_prop_no_bound; + if (ci->constructInstantiationInc(pv, val, pv_prop_no_bound, sf)) + { + return true; + } + } + }else{ + Trace("cegqi-arith-bound") << ( rr==0 ? "Lower" : "Upper" ) << " bounds for " << pv << " (type=" << d_type << ") : " << std::endl; + int best = -1; + Node best_bound_value[3]; + for( unsigned j=0; jgetModelValue( d_mbp_bounds[rr][j] ); + t_values[rr][j] = t_value; + value[1] = t_value; + Trace("cegqi-arith-bound") << value[1]; + }else{ + value[2] = d_mbp_vts_coeff[rr][1][j]; + if( !value[2].isNull() ){ + Trace("cegqi-arith-bound") << " + ( " << value[2] << " * DELTA )"; + }else{ + value[2] = zero; + } + } + //multiply by coefficient + if( value[t]!=zero && !d_mbp_coeff[rr][j].isNull() ){ + Assert( d_mbp_coeff[rr][j].isConst() ); + value[t] = NodeManager::currentNM()->mkNode( MULT, NodeManager::currentNM()->mkConst( Rational(1) / d_mbp_coeff[rr][j].getConst() ), value[t] ); + value[t] = Rewriter::rewrite( value[t] ); + } + //check if new best + if( best!=-1 ){ + Assert( !value[t].isNull() && !best_bound_value[t].isNull() ); + if( value[t]!=best_bound_value[t] ){ + Kind k = rr==0 ? GEQ : LEQ; + Node cmp_bound = NodeManager::currentNM()->mkNode( k, value[t], best_bound_value[t] ); + cmp_bound = Rewriter::rewrite( cmp_bound ); + if( cmp_bound!=ci->getQuantifiersEngine()->getTermUtil()->d_true ){ + new_best = false; + break; + } + } + } + } + Trace("cegqi-arith-bound") << std::endl; + if( new_best ){ + for( unsigned t=0; t<3; t++ ){ + best_bound_value[t] = value[t]; + } + best = j; + } + } + if( best!=-1 ){ + Trace("cegqi-arith-bound") << "...best bound is " << best << " : "; + if( best_bound_value[0]!=zero ){ + Trace("cegqi-arith-bound") << "( " << best_bound_value[0] << " * INF ) + "; + } + Trace("cegqi-arith-bound") << best_bound_value[1]; + if( best_bound_value[2]!=zero ){ + Trace("cegqi-arith-bound") << " + ( " << best_bound_value[2] << " * DELTA )"; + } + Trace("cegqi-arith-bound") << std::endl; + best_used[rr] = best; + //if using cbqiMidpoint, only add the instance based on one bound if the bound is non-strict + if (!options::cbqiMidpoint() || d_type.isInteger() + || (ci->useVtsDelta() && d_mbp_vts_coeff[rr][1][best].isNull())) + { + Node val = d_mbp_bounds[rr][best]; + val = getModelBasedProjectionValue( ci, pv, val, rr==0, d_mbp_coeff[rr][best], pv_value, t_values[rr][best], sf.getTheta(), + d_mbp_vts_coeff[rr][0][best], d_mbp_vts_coeff[rr][1][best] ); + if( !val.isNull() ){ + TermProperties pv_prop_bound; + pv_prop_bound.d_coeff = d_mbp_coeff[rr][best]; + pv_prop_bound.d_type = rr==0 ? 1 : -1; + if (ci->constructInstantiationInc(pv, val, pv_prop_bound, sf)) + { + return true; + } + } + } + } + } + } + //if not using infinity, use model value of zero + if( !use_inf && d_mbp_bounds[0].empty() && d_mbp_bounds[1].empty() ){ + Node val = zero; + TermProperties pv_prop_zero; + Node theta = sf.getTheta(); + val = getModelBasedProjectionValue( ci, pv, val, true, pv_prop_zero.d_coeff, pv_value, zero, sf.getTheta(), Node::null(), Node::null() ); + if( !val.isNull() ){ + if (ci->constructInstantiationInc(pv, val, pv_prop_zero, sf)) + { + return true; + } + } + } + if( options::cbqiMidpoint() && !d_type.isInteger() ){ + Node vals[2]; + bool bothBounds = true; + Trace("cegqi-arith-bound") << "Try midpoint of bounds..." << std::endl; + for( unsigned rr=0; rr<2; rr++ ){ + int best = best_used[rr]; + if( best==-1 ){ + bothBounds = false; + }else{ + vals[rr] = d_mbp_bounds[rr][best]; + vals[rr] = getModelBasedProjectionValue( ci, pv, vals[rr], rr==0, Node::null(), pv_value, t_values[rr][best], sf.getTheta(), + d_mbp_vts_coeff[rr][0][best], Node::null() ); + } + Trace("cegqi-arith-bound") << "Bound : " << vals[rr] << std::endl; + } + Node val; + if( bothBounds ){ + Assert( !vals[0].isNull() && !vals[1].isNull() ); + if( vals[0]==vals[1] ){ + val = vals[0]; + }else{ + val = NodeManager::currentNM()->mkNode( MULT, NodeManager::currentNM()->mkNode( PLUS, vals[0], vals[1] ), + NodeManager::currentNM()->mkConst( Rational(1)/Rational(2) ) ); + val = Rewriter::rewrite( val ); + } + }else{ + if( !vals[0].isNull() ){ + val = NodeManager::currentNM()->mkNode( PLUS, vals[0], one ); + val = Rewriter::rewrite( val ); + }else if( !vals[1].isNull() ){ + val = NodeManager::currentNM()->mkNode( MINUS, vals[1], one ); + val = Rewriter::rewrite( val ); + } + } + Trace("cegqi-arith-bound") << "Midpoint value : " << val << std::endl; + if( !val.isNull() ){ + TermProperties pv_prop_midpoint; + if (ci->constructInstantiationInc(pv, val, pv_prop_midpoint, sf)) + { + return true; + } + } + } + //generally should not make it to this point FIXME: write proper assertion + //Assert( ( ci->hasNestedQuantification() && !options::cbqiNestedQE() ) || options::cbqiAll() ); + + if( options::cbqiNopt() ){ + //try non-optimal bounds (heuristic, may help when nested quantification) ? + Trace("cegqi-arith-bound") << "Try non-optimal bounds..." << std::endl; + for( unsigned r=0; r<2; r++ ){ + int rr = upper_first ? (1-r) : r; + for( unsigned j=0; jconstructInstantiationInc( + pv, val, pv_prop_nopt_bound, sf)) + { + return true; + } + } + } + } + } + } + } + return false; +} + +bool ArithInstantiator::needsPostProcessInstantiationForVariable( + CegInstantiator* ci, SolvedForm& sf, Node pv, CegInstEffort effort) +{ + return std::find( sf.d_non_basic.begin(), sf.d_non_basic.end(), pv )!=sf.d_non_basic.end(); +} + +bool ArithInstantiator::postProcessInstantiationForVariable( + CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort, + std::vector& lemmas) +{ + Assert( std::find( sf.d_non_basic.begin(), sf.d_non_basic.end(), pv )!=sf.d_non_basic.end() ); + Assert( std::find( sf.d_vars.begin(), sf.d_vars.end(), pv )!=sf.d_vars.end() ); + unsigned index = std::find( sf.d_vars.begin(), sf.d_vars.end(), pv )-sf.d_vars.begin(); + Assert( !sf.d_props[index].isBasic() ); + Node eq_lhs = sf.d_props[index].getModifiedTerm( sf.d_vars[index] ); + if( Trace.isOn("cegqi-arith-debug") ){ + Trace("cegqi-arith-debug") << "Normalize substitution for "; + Trace("cegqi-arith-debug") << eq_lhs << " = " << sf.d_subs[index] << std::endl; + } + Assert( sf.d_vars[index].getType().isInteger() ); + //must ensure that divisibility constraints are met + //solve updated rewritten equality for vars[index], if coefficient is one, then we are successful + Node eq_rhs = sf.d_subs[index]; + Node eq = eq_lhs.eqNode( eq_rhs ); + eq = Rewriter::rewrite( eq ); + Trace("cegqi-arith-debug") << "...equality is " << eq << std::endl; + std::map< Node, Node > msum; + if (ArithMSum::getMonomialSumLit(eq, msum)) + { + Node veq; + if (ArithMSum::isolate(sf.d_vars[index], msum, veq, EQUAL, true) != 0) + { + Node veq_c; + if( veq[0]!=sf.d_vars[index] ){ + Node veq_v; + if (ArithMSum::getMonomial(veq[0], veq_c, veq_v)) + { + Assert( veq_v==sf.d_vars[index] ); + } + } + sf.d_subs[index] = veq[1]; + if( !veq_c.isNull() ){ + sf.d_subs[index] = NodeManager::currentNM()->mkNode( INTS_DIVISION_TOTAL, veq[1], veq_c ); + Trace("cegqi-arith-debug") << "...bound type is : " << sf.d_props[index].d_type << std::endl; + //intger division rounding up if from a lower bound + if( sf.d_props[index].d_type==1 && options::cbqiRoundUpLowerLia() ){ + sf.d_subs[index] = NodeManager::currentNM()->mkNode( PLUS, sf.d_subs[index], + NodeManager::currentNM()->mkNode( ITE, + NodeManager::currentNM()->mkNode( EQUAL, + NodeManager::currentNM()->mkNode( INTS_MODULUS_TOTAL, veq[1], veq_c ), + ci->getQuantifiersEngine()->getTermUtil()->d_zero ), + ci->getQuantifiersEngine()->getTermUtil()->d_zero, ci->getQuantifiersEngine()->getTermUtil()->d_one ) + ); + } + } + Trace("cegqi-arith-debug") << "...normalize integers : " << sf.d_vars[index] << " -> " << sf.d_subs[index] << std::endl; + }else{ + Trace("cegqi-arith-debug") << "...failed to isolate." << std::endl; + return false; + } + }else{ + Trace("cegqi-arith-debug") << "...failed to get monomial sum." << std::endl; + return false; + } + return true; +} + +void DtInstantiator::reset(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) +{ +} + +Node DtInstantiator::solve_dt( Node v, Node a, Node b, Node sa, Node sb ) { + Trace("cegqi-arith-debug2") << "Solve dt : " << v << " " << a << " " << b << " " << sa << " " << sb << std::endl; + Node ret; + if( !a.isNull() && a==v ){ + ret = sb; + }else if( !b.isNull() && b==v ){ + ret = sa; + }else if( !a.isNull() && a.getKind()==APPLY_CONSTRUCTOR ){ + if( !b.isNull() && b.getKind()==APPLY_CONSTRUCTOR ){ + if( a.getOperator()==b.getOperator() ){ + for( unsigned i=0; imkNode( APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[cindex].getSelectorInternal( tn.toType(), i ) ), sb ); + Node s = solve_dt( v, a[i], Node::null(), sa[i], nn ); + if( !s.isNull() ){ + return s; + } + } + } + }else if( !b.isNull() && b.getKind()==APPLY_CONSTRUCTOR ){ + return solve_dt( v, b, a, sb, sa ); + } + if( !ret.isNull() ){ + //ensure does not contain + if( TermUtil::containsTerm( ret, v ) ){ + ret = Node::null(); + } + } + return ret; +} + +bool DtInstantiator::processEqualTerms(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + std::vector& eqc, + CegInstEffort effort) +{ + Trace("cegqi-dt-debug") << "try based on constructors in equivalence class." + << std::endl; + // look in equivalence class for a constructor + for( unsigned k=0; k children; + children.push_back( n.getOperator() ); + const Datatype& dt = ((DatatypeType)(d_type).toType()).getDatatype(); + unsigned cindex = Datatype::indexOf( n.getOperator().toExpr() ); + //now must solve for selectors applied to pv + for( unsigned j=0; jmkNode( APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[cindex].getSelectorInternal( d_type.toType(), j ) ), pv ); + ci->pushStackVariable( c ); + children.push_back( c ); + } + Node val = NodeManager::currentNM()->mkNode( kind::APPLY_CONSTRUCTOR, children ); + TermProperties pv_prop_dt; + if (ci->constructInstantiationInc(pv, val, pv_prop_dt, sf)) + { + return true; + }else{ + //cleanup + for( unsigned j=0; jpopStackVariable(); + } + break; + } + } + } + return false; +} + +bool DtInstantiator::processEquality(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + std::vector& term_props, + std::vector& terms, + CegInstEffort effort) +{ + Node val = solve_dt( pv, terms[0], terms[1], terms[0], terms[1] ); + if( !val.isNull() ){ + TermProperties pv_prop; + if (ci->constructInstantiationInc(pv, val, pv_prop, sf)) + { + return true; + } + } + return false; +} + +void EprInstantiator::reset(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) +{ + d_equal_terms.clear(); +} + +bool EprInstantiator::processEqualTerm(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + TermProperties& pv_prop, + Node n, + CegInstEffort effort) +{ + if( options::quantEprMatching() ){ + Assert( pv_prop.isBasic() ); + d_equal_terms.push_back( n ); + return false; + }else{ + pv_prop.d_type = 0; + return ci->constructInstantiationInc(pv, n, pv_prop, sf); + } +} + +void EprInstantiator::computeMatchScore( CegInstantiator * ci, Node pv, Node catom, std::vector< Node >& arg_reps, TermArgTrie * tat, unsigned index, std::map< Node, int >& match_score ) { + if( index==catom.getNumChildren() ){ + Assert( tat->hasNodeData() ); + Node gcatom = tat->getNodeData(); + Trace("cegqi-epr") << "Matched : " << catom << " and " << gcatom << std::endl; + for( unsigned i=0; i::iterator it = tat->d_data.find( arg_reps[index] ); + if( it!=tat->d_data.end() ){ + computeMatchScore( ci, pv, catom, arg_reps, &it->second, index+1, match_score ); + } + } +} + +void EprInstantiator::computeMatchScore( CegInstantiator * ci, Node pv, Node catom, Node eqc, std::map< Node, int >& match_score ) { + if( inst::Trigger::isAtomicTrigger( catom ) && TermUtil::containsTerm( catom, pv ) ){ + Trace("cegqi-epr") << "Find matches for " << catom << "..." << std::endl; + std::vector< Node > arg_reps; + for( unsigned j=0; jgetQuantifiersEngine()->getMasterEqualityEngine()->getRepresentative( catom[j] ) ); + } + if( ci->getQuantifiersEngine()->getMasterEqualityEngine()->hasTerm( eqc ) ){ + Node rep = ci->getQuantifiersEngine()->getMasterEqualityEngine()->getRepresentative( eqc ); + Node op = ci->getQuantifiersEngine()->getTermDatabase()->getMatchOperator( catom ); + TermArgTrie * tat = ci->getQuantifiersEngine()->getTermDatabase()->getTermArgTrie( rep, op ); + Trace("cegqi-epr") << "EPR instantiation match term : " << catom << ", check ground terms=" << (tat!=NULL) << std::endl; + if( tat ){ + computeMatchScore( ci, pv, catom, arg_reps, tat, 0, match_score ); + } + } + } +} + +struct sortEqTermsMatch { + std::map< Node, int > d_match_score; + bool operator() (Node i, Node j) { + int match_score_i = d_match_score[i]; + int match_score_j = d_match_score[j]; + return match_score_i>match_score_j || ( match_score_i==match_score_j && i& eqc, + CegInstEffort effort) +{ + if( options::quantEprMatching() ){ + //heuristic for best matching constant + sortEqTermsMatch setm; + for( unsigned i=0; igetNumCEAtoms(); i++ ){ + Node catom = ci->getCEAtom( i ); + computeMatchScore( ci, pv, catom, catom, setm.d_match_score ); + } + //sort by match score + std::sort( d_equal_terms.begin(), d_equal_terms.end(), setm ); + TermProperties pv_prop; + pv_prop.d_type = 0; + for( unsigned i=0; iconstructInstantiationInc(pv, d_equal_terms[i], pv_prop, sf)) + { + return true; + } + } + } + return false; +} + +// this class can be used to query the model value through the CegInstaniator class +class CegInstantiatorBvInverterQuery : public BvInverterQuery +{ + public: + CegInstantiatorBvInverterQuery(CegInstantiator* ci) + : BvInverterQuery(), d_ci(ci) + { + } + ~CegInstantiatorBvInverterQuery() {} + /** return the model value of n */ + Node getModelValue( Node n ) { + return d_ci->getModelValue( n ); + } + /** get bound variable of type tn */ + Node getBoundVariable(TypeNode tn) { return d_ci->getBoundVariable(tn); } + protected: + // pointer to class that is able to query model values + CegInstantiator * d_ci; +}; + +BvInstantiator::BvInstantiator(QuantifiersEngine* qe, TypeNode tn) + : Instantiator(qe, tn), d_tried_assertion_inst(false) +{ + // get the global inverter utility + // this must be global since we need to: + // * process Skolem functions properly across multiple variables within the same quantifier + // * cache Skolem variables uniformly across multiple quantified formulas + d_inverter = qe->getBvInverter(); +} + +BvInstantiator::~BvInstantiator(){ + +} +void BvInstantiator::reset(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) +{ + d_inst_id_counter = 0; + d_var_to_inst_id.clear(); + d_inst_id_to_term.clear(); + d_inst_id_to_alit.clear(); + d_var_to_curr_inst_id.clear(); + d_alit_to_model_slack.clear(); + d_tried_assertion_inst = false; +} + +void BvInstantiator::processLiteral(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + Node lit, + Node alit, + CegInstEffort effort) +{ + Assert(d_inverter != NULL); + // find path to pv + std::vector path; + Node sv = d_inverter->getSolveVariable(pv.getType()); + Node pvs = ci->getModelValue(pv); + Trace("cegqi-bv") << "Get path to pv : " << lit << std::endl; + Node slit = d_inverter->getPathToPv(lit, pv, sv, pvs, path); + if (!slit.isNull()) + { + CegInstantiatorBvInverterQuery m(ci); + unsigned iid = d_inst_id_counter; + Trace("cegqi-bv") << "Solve lit to bv inverter : " << slit << std::endl; + Node inst = d_inverter->solveBvLit(sv, slit, path, &m); + if (!inst.isNull()) + { + inst = Rewriter::rewrite(inst); + if (inst.isConst() || !ci->hasNestedQuantification()) + { + Trace("cegqi-bv") << "...solved form is " << inst << std::endl; + // store information for id and increment + d_var_to_inst_id[pv].push_back(iid); + d_inst_id_to_term[iid] = inst; + d_inst_id_to_alit[iid] = alit; + d_inst_id_counter++; + } + } + else + { + Trace("cegqi-bv") << "...failed to solve." << std::endl; + } + } +} + +Node BvInstantiator::hasProcessAssertion(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + Node lit, + CegInstEffort effort) +{ + if (effort == CEG_INST_EFFORT_FULL) + { + // always use model values at full effort + return Node::null(); + } + Node atom = lit.getKind() == NOT ? lit[0] : lit; + bool pol = lit.getKind() != NOT; + Kind k = atom.getKind(); + if (k != EQUAL && k != BITVECTOR_ULT && k != BITVECTOR_SLT) + { + // others are unhandled + return Node::null(); + } + else if (!atom[0].getType().isBitVector()) + { + return Node::null(); + } + else if (options::cbqiBvIneqMode() == CBQI_BV_INEQ_KEEP + || (pol && k == EQUAL)) + { + return lit; + } + NodeManager* nm = NodeManager::currentNM(); + Node s = atom[0]; + Node t = atom[1]; + + Node sm = ci->getModelValue(s); + Node tm = ci->getModelValue(t); + Assert(!sm.isNull() && sm.isConst()); + Assert(!tm.isNull() && tm.isConst()); + Trace("cegqi-bv") << "Model value: " << std::endl; + Trace("cegqi-bv") << " " << s << " " << k << " " << t << " is " + << std::endl; + Trace("cegqi-bv") << " " << sm << " <> " << tm << std::endl; + + Node ret; + if (options::cbqiBvIneqMode() == CBQI_BV_INEQ_EQ_SLACK) + { + // if using slack, we convert constraints to a positive equality based on + // the current model M, e.g.: + // (not) s ~ t ---> s = t + ( s^M - t^M ) + if (sm != tm) + { + Node slack = Rewriter::rewrite(nm->mkNode(BITVECTOR_SUB, sm, tm)); + Assert(slack.isConst()); + // remember the slack value for the asserted literal + d_alit_to_model_slack[lit] = slack; + ret = nm->mkNode(EQUAL, s, nm->mkNode(BITVECTOR_PLUS, t, slack)); + Trace("cegqi-bv") << "Slack is " << slack << std::endl; + } + else + { + ret = s.eqNode(t); + } + } else { + // turn disequality into an inequality + // e.g. s != t becomes s < t or t < s + if (k == EQUAL) + { + if (Random::getRandom().pickWithProb(0.5)) + { + std::swap(s, t); + } + pol = true; + } + // otherwise, we optimistically solve for the boundary point of an + // inequality, for example: + // for s < t, we solve s+1 = t + // for ~( s < t ), we solve s = t + // notice that this equality does not necessarily hold in the model, and + // hence the corresponding instantiation strategy is not guaranteed to be + // monotonic. + if (!pol) + { + ret = s.eqNode(t); + } else { + Node bv_one = bv::utils::mkOne(bv::utils::getSize(s)); + ret = nm->mkNode(BITVECTOR_PLUS, s, bv_one).eqNode(t); + } + } + Trace("cegqi-bv") << "Process " << lit << " as " << ret << std::endl; + return ret; +} + +bool BvInstantiator::processAssertion(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + Node lit, + Node alit, + CegInstEffort effort) +{ + // if option enabled, use approach for word-level inversion for BV instantiation + if( options::cbqiBv() ){ + // get the best rewritten form of lit for solving for pv + // this should remove instances of non-invertible operators, and "linearize" lit with respect to pv as much as possible + Node rlit = rewriteAssertionForSolvePv(ci, pv, lit); + if( Trace.isOn("cegqi-bv") ){ + Trace("cegqi-bv") << "BvInstantiator::processAssertion : solve " << pv << " in " << lit << std::endl; + if( lit!=rlit ){ + Trace("cegqi-bv") << "...rewritten to " << rlit << std::endl; + } + } + if (!rlit.isNull()) + { + processLiteral(ci, sf, pv, rlit, alit, effort); + } + } + return false; +} + +bool BvInstantiator::useModelValue(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) +{ + return !d_tried_assertion_inst + && (effort < CEG_INST_EFFORT_FULL || options::cbqiFullEffort()); +} + +bool BvInstantiator::processAssertions(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) +{ + std::unordered_map< Node, std::vector< unsigned >, NodeHashFunction >::iterator iti = d_var_to_inst_id.find( pv ); + if( iti!=d_var_to_inst_id.end() ){ + Trace("cegqi-bv") << "BvInstantiator::processAssertions for " << pv << std::endl; + // if interleaving, do not do inversion half the time + if (!options::cbqiBvInterleaveValue() || Random::getRandom().pickWithProb(0.5)) + { + bool firstVar = sf.empty(); + // get inst id list + if (Trace.isOn("cegqi-bv")) + { + Trace("cegqi-bv") << " " << iti->second.size() + << " candidate instantiations for " << pv << " : " + << std::endl; + if (firstVar) + { + Trace("cegqi-bv") << " ...this is the first variable" << std::endl; + } + } + // until we have a model-preserving selection function for BV, this must + // be heuristic (we only pick one literal) + // hence we randomize the list + // this helps robustness, since picking the same literal every time may + // lead to a stream of value instantiations, whereas with randomization + // we may find an invertible literal that leads to a useful instantiation. + std::random_shuffle(iti->second.begin(), iti->second.end()); + + if (Trace.isOn("cegqi-bv")) + { + for (unsigned j = 0, size = iti->second.size(); j < size; j++) + { + unsigned inst_id = iti->second[j]; + Assert(d_inst_id_to_term.find(inst_id) != d_inst_id_to_term.end()); + Node inst_term = d_inst_id_to_term[inst_id]; + Assert(d_inst_id_to_alit.find(inst_id) != d_inst_id_to_alit.end()); + Node alit = d_inst_id_to_alit[inst_id]; + + // get the slack value introduced for the asserted literal + Node curr_slack_val; + std::unordered_map::iterator itms = + d_alit_to_model_slack.find(alit); + if (itms != d_alit_to_model_slack.end()) + { + curr_slack_val = itms->second; + } + + // debug printing + Trace("cegqi-bv") << " [" << j << "] : "; + Trace("cegqi-bv") << inst_term << std::endl; + if (!curr_slack_val.isNull()) { + Trace("cegqi-bv") << " ...with slack value : " << curr_slack_val + << std::endl; + } + Trace("cegqi-bv-debug") << " ...from : " << alit << std::endl; + Trace("cegqi-bv") << std::endl; + } + } + + // Now, try all instantiation ids we want to try + // Typically we try only one, otherwise worst-case performance + // for constructing instantiations is exponential on the number of + // variables in this quantifier prefix. + bool ret = false; + bool tryMultipleInst = firstVar && options::cbqiMultiInst(); + bool revertOnSuccess = tryMultipleInst; + for (unsigned j = 0, size = iti->second.size(); j < size; j++) + { + unsigned inst_id = iti->second[j]; + Assert(d_inst_id_to_term.find(inst_id) != d_inst_id_to_term.end()); + Node inst_term = d_inst_id_to_term[inst_id]; + Node alit = d_inst_id_to_alit[inst_id]; + // try instantiation pv -> inst_term + TermProperties pv_prop_bv; + Trace("cegqi-bv") << "*** try " << pv << " -> " << inst_term + << std::endl; + d_var_to_curr_inst_id[pv] = inst_id; + d_tried_assertion_inst = true; + ci->markSolved(alit); + if (ci->constructInstantiationInc( + pv, inst_term, pv_prop_bv, sf, revertOnSuccess)) + { + ret = true; + } + ci->markSolved(alit, false); + // we are done unless we try multiple instances + if (!tryMultipleInst) + { + break; + } + } + if (ret) + { + return true; + } + Trace("cegqi-bv") << "...failed to add instantiation for " << pv + << std::endl; + d_var_to_curr_inst_id.erase(pv); + } else { + Trace("cegqi-bv") << "...do not do instantiation for " << pv + << " (skip, based on heuristic)" << std::endl; + } + } + + return false; +} + +Node BvInstantiator::rewriteAssertionForSolvePv(CegInstantiator* ci, + Node pv, + Node lit) +{ + // result of rewriting the visited term + std::stack > visited; + visited.push(std::unordered_map()); + // whether the visited term contains pv + std::unordered_map visited_contains_pv; + std::unordered_map::iterator it; + std::unordered_map curr_subs; + std::stack > visit; + TNode cur; + visit.push(std::stack()); + visit.top().push(lit); + do { + cur = visit.top().top(); + visit.top().pop(); + it = visited.top().find(cur); + + if (it == visited.top().end()) + { + std::unordered_map::iterator itc = + curr_subs.find(cur); + if (itc != curr_subs.end()) + { + visited.top()[cur] = itc->second; + } + else + { + if (cur.getKind() == CHOICE) + { + // must replace variables of choice functions + // with new variables to avoid variable + // capture when considering substitutions + // with multiple literals. + Node bv = ci->getBoundVariable(cur[0][0].getType()); + // should not have captured variables + Assert(curr_subs.find(cur[0][0]) == curr_subs.end()); + curr_subs[cur[0][0]] = bv; + // we cannot cache the results of subterms + // of this choice expression since we are + // now in the context { cur[0][0] -> bv }, + // hence we push a context here + visited.push(std::unordered_map()); + visit.push(std::stack()); + } + visited.top()[cur] = Node::null(); + visit.top().push(cur); + for (unsigned i = 0; i < cur.getNumChildren(); i++) + { + visit.top().push(cur[i]); + } + } + } else if (it->second.isNull()) { + Node ret; + bool childChanged = false; + std::vector children; + if (cur.getMetaKind() == kind::metakind::PARAMETERIZED) { + children.push_back(cur.getOperator()); + } + bool contains_pv = ( cur==pv ); + for (unsigned i = 0; i < cur.getNumChildren(); i++) { + it = visited.top().find(cur[i]); + Assert(it != visited.top().end()); + Assert(!it->second.isNull()); + childChanged = childChanged || cur[i] != it->second; + children.push_back(it->second); + contains_pv = contains_pv || visited_contains_pv[cur[i]]; + } + // careful that rewrites above do not affect whether this term contains pv + visited_contains_pv[cur] = contains_pv; + + // rewrite the term + ret = rewriteTermForSolvePv(pv, cur, children, visited_contains_pv); + + // return original if the above function does not produce a result + if (ret.isNull()) { + if (childChanged) { + ret = NodeManager::currentNM()->mkNode(cur.getKind(), children); + }else{ + ret = cur; + } + } + + /* We need to update contains_pv also for rewritten nodes, since + * the normalizePv* functions rely on the information if pv occurs in a + * rewritten node or not. */ + if (ret != cur) + { + contains_pv = (ret == pv); + for (unsigned i = 0, size = ret.getNumChildren(); i < size; ++i) + { + contains_pv = contains_pv || visited_contains_pv[ret[i]]; + } + visited_contains_pv[ret] = contains_pv; + } + + // if was choice, pop context + if (cur.getKind() == CHOICE) + { + Assert(curr_subs.find(cur[0][0]) != curr_subs.end()); + curr_subs.erase(cur[0][0]); + visited.pop(); + visit.pop(); + Assert(visited.size() == visit.size()); + Assert(!visit.empty()); + } + + visited.top()[cur] = ret; + } + } while (!visit.top().empty()); + Assert(visited.size() == 1); + Assert(visited.top().find(lit) != visited.top().end()); + Assert(!visited.top().find(lit)->second.isNull()); + + Node result = visited.top()[lit]; + + if (Trace.isOn("cegqi-bv-nl")) + { + std::vector trace_visit; + std::unordered_set trace_visited; + + trace_visit.push_back(result); + do + { + cur = trace_visit.back(); + trace_visit.pop_back(); + + if (trace_visited.find(cur) == trace_visited.end()) + { + trace_visited.insert(cur); + trace_visit.insert(trace_visit.end(), cur.begin(), cur.end()); + } + else if (cur == pv) + { + Trace("cegqi-bv-nl") + << "NONLINEAR LITERAL for " << pv << " : " << lit << std::endl; + } + } while (!trace_visit.empty()); + } + + return result; +} + +/** + * Determine coefficient of pv in term n, where n has the form pv, -pv, pv * t, + * or -pv * t. Extracting the coefficient of multiplications only succeeds if + * the multiplication are normalized with normalizePvMult. + * + * If sucessful it returns + * 1 if n == pv, + * -1 if n == -pv, + * t if n == pv * t, + * -t if n == -pv * t. + * If n is not a linear term, a null node is returned. + */ +static Node getPvCoeff(TNode pv, TNode n) +{ + bool neg = false; + Node coeff; + + if (n.getKind() == BITVECTOR_NEG) + { + neg = true; + n = n[0]; + } + + if (n == pv) + { + coeff = bv::utils::mkOne(bv::utils::getSize(pv)); + } + /* All multiplications are normalized to pv * (t1 * t2). */ + else if (n.getKind() == BITVECTOR_MULT && n.getAttribute(BvLinearAttribute())) + { + Assert(n.getNumChildren() == 2); + Assert(n[0] == pv); + coeff = n[1]; + } + else /* n is in no form to extract the coefficient for pv */ + { + Trace("cegqi-bv-nl") << "Cannot extract coefficient for " << pv << " in " + << n << std::endl; + return Node::null(); + } + Assert(!coeff.isNull()); + + if (neg) return NodeManager::currentNM()->mkNode(BITVECTOR_NEG, coeff); + return coeff; +} + +/** + * Normalizes the children of a BITVECTOR_MULT w.r.t. pv. contains_pv marks + * terms in which pv occurs. + * For example, + * + * a * -pv * b * c + * + * is rewritten to + * + * pv * -(a * b * c) + * + * Returns the normalized node if the resulting term is linear w.r.t. pv and + * a null node otherwise. If pv does not occur in children it returns a + * multiplication over children. + */ +static Node normalizePvMult( + TNode pv, + const std::vector& children, + std::unordered_map& contains_pv) +{ + bool neg, neg_coeff = false; + bool found_pv = false; + NodeManager* nm; + NodeBuilder<> nb(BITVECTOR_MULT); + BvLinearAttribute is_linear; + + nm = NodeManager::currentNM(); + for (TNode nc : children) + { + if (!contains_pv[nc]) + { + nb << nc; + continue; + } + + neg = false; + if (nc.getKind() == BITVECTOR_NEG) + { + neg = true; + nc = nc[0]; + } + + if (!found_pv && nc == pv) + { + found_pv = true; + neg_coeff = neg; + continue; + } + else if (!found_pv && nc.getKind() == BITVECTOR_MULT + && nc.getAttribute(is_linear)) + { + Assert(nc.getNumChildren() == 2); + Assert(nc[0] == pv); + Assert(!contains_pv[nc[1]]); + found_pv = true; + neg_coeff = neg; + nb << nc[1]; + continue; + } + return Node::null(); /* non-linear */ + } + Assert(nb.getNumChildren() > 0); + + Node coeff = (nb.getNumChildren() == 1) ? nb[0] : nb.constructNode(); + if (neg_coeff) + { + coeff = nm->mkNode(BITVECTOR_NEG, coeff); + } + coeff = Rewriter::rewrite(coeff); + unsigned size_coeff = bv::utils::getSize(coeff); + Node zero = bv::utils::mkZero(size_coeff); + if (coeff == zero) + { + return zero; + } + Node result; + if (found_pv) + { + if (coeff == bv::utils::mkOne(size_coeff)) + { + return pv; + } + result = nm->mkNode(BITVECTOR_MULT, pv, coeff); + contains_pv[result] = true; + result.setAttribute(is_linear, true); + } + else + { + result = coeff; + } + return result; +} + +#ifdef CVC4_ASSERTIONS +static bool isLinearPlus( + TNode n, + TNode pv, + std::unordered_map& contains_pv) +{ + Node coeff; + Assert(n.getAttribute(BvLinearAttribute())); + Assert(n.getNumChildren() == 2); + if (n[0] != pv) + { + Assert(n[0].getKind() == BITVECTOR_MULT); + Assert(n[0].getNumChildren() == 2); + Assert(n[0][0] == pv); + Assert(!contains_pv[n[0][1]]); + } + Assert(!contains_pv[n[1]]); + coeff = getPvCoeff(pv, n[0]); + Assert(!coeff.isNull()); + Assert(!contains_pv[coeff]); + return true; +} +#endif + +/** + * Normalizes the children of a BITVECTOR_PLUS w.r.t. pv. contains_pv marks + * terms in which pv occurs. + * For example, + * + * a * pv + b + c * -pv + * + * is rewritten to + * + * pv * (a - c) + b + * + * Returns the normalized node if the resulting term is linear w.r.t. pv and + * a null node otherwise. If pv does not occur in children it returns an + * addition over children. + */ +static Node normalizePvPlus( + Node pv, + const std::vector& children, + std::unordered_map& contains_pv) +{ + NodeManager* nm; + NodeBuilder<> nb_c(BITVECTOR_PLUS); + NodeBuilder<> nb_l(BITVECTOR_PLUS); + BvLinearAttribute is_linear; + bool neg; + + nm = NodeManager::currentNM(); + for (TNode nc : children) + { + if (!contains_pv[nc]) + { + nb_l << nc; + continue; + } + + neg = false; + if (nc.getKind() == BITVECTOR_NEG) + { + neg = true; + nc = nc[0]; + } + + if (nc == pv + || (nc.getKind() == BITVECTOR_MULT && nc.getAttribute(is_linear))) + { + Node coeff = getPvCoeff(pv, nc); + Assert(!coeff.isNull()); + if (neg) + { + coeff = nm->mkNode(BITVECTOR_NEG, coeff); + } + nb_c << coeff; + continue; + } + else if (nc.getKind() == BITVECTOR_PLUS && nc.getAttribute(is_linear)) + { + Assert(isLinearPlus(nc, pv, contains_pv)); + Node coeff = getPvCoeff(pv, nc[0]); + Assert(!coeff.isNull()); + Node leaf = nc[1]; + if (neg) + { + coeff = nm->mkNode(BITVECTOR_NEG, coeff); + leaf = nm->mkNode(BITVECTOR_NEG, leaf); + } + nb_c << coeff; + nb_l << leaf; + continue; + } + /* can't collect coefficients of 'pv' in 'cur' -> non-linear */ + return Node::null(); + } + Assert(nb_c.getNumChildren() > 0 || nb_l.getNumChildren() > 0); + + Node pv_mult_coeffs, result; + if (nb_c.getNumChildren() > 0) + { + Node coeffs = (nb_c.getNumChildren() == 1) ? nb_c[0] : nb_c.constructNode(); + coeffs = Rewriter::rewrite(coeffs); + result = pv_mult_coeffs = normalizePvMult(pv, {pv, coeffs}, contains_pv); + } + + if (nb_l.getNumChildren() > 0) + { + Node leafs = (nb_l.getNumChildren() == 1) ? nb_l[0] : nb_l.constructNode(); + leafs = Rewriter::rewrite(leafs); + Node zero = bv::utils::mkZero(bv::utils::getSize(pv)); + /* pv * 0 + t --> t */ + if (pv_mult_coeffs.isNull() || pv_mult_coeffs == zero) + { + result = leafs; + } + else + { + result = nm->mkNode(BITVECTOR_PLUS, pv_mult_coeffs, leafs); + contains_pv[result] = true; + result.setAttribute(is_linear, true); + } + } + Assert(!result.isNull()); + return result; +} + +/** + * Linearize an equality w.r.t. pv such that pv only occurs once. contains_pv + * marks terms in which pv occurs. + * For example, equality + * + * -pv * a + b = c + pv + * + * rewrites to + * + * pv * (-a - 1) = c - b. + */ +static Node normalizePvEqual( + Node pv, + const std::vector& children, + std::unordered_map& contains_pv) +{ + Assert(children.size() == 2); + + NodeManager* nm = NodeManager::currentNM(); + BvLinearAttribute is_linear; + Node coeffs[2], leafs[2]; + bool neg; + TNode child; + + for (unsigned i = 0; i < 2; ++i) + { + child = children[i]; + neg = false; + if (child.getKind() == BITVECTOR_NEG) + { + neg = true; + child = child[0]; + } + if (child.getAttribute(is_linear) || child == pv) + { + if (child.getKind() == BITVECTOR_PLUS) + { + Assert(isLinearPlus(child, pv, contains_pv)); + coeffs[i] = getPvCoeff(pv, child[0]); + leafs[i] = child[1]; + } + else + { + Assert(child.getKind() == BITVECTOR_MULT || child == pv); + coeffs[i] = getPvCoeff(pv, child); + } + } + if (neg) + { + if (!coeffs[i].isNull()) + { + coeffs[i] = nm->mkNode(BITVECTOR_NEG, coeffs[i]); + } + if (!leafs[i].isNull()) + { + leafs[i] = nm->mkNode(BITVECTOR_NEG, leafs[i]); + } + } + } + + if (coeffs[0].isNull() || coeffs[1].isNull()) + { + return Node::null(); + } + + Node coeff = nm->mkNode(BITVECTOR_SUB, coeffs[0], coeffs[1]); + coeff = Rewriter::rewrite(coeff); + std::vector mult_children = {pv, coeff}; + Node lhs = normalizePvMult(pv, mult_children, contains_pv); + + Node rhs; + if (!leafs[0].isNull() && !leafs[1].isNull()) + { + rhs = nm->mkNode(BITVECTOR_SUB, leafs[1], leafs[0]); + } + else if (!leafs[0].isNull()) + { + rhs = nm->mkNode(BITVECTOR_NEG, leafs[0]); + } + else if (!leafs[1].isNull()) + { + rhs = leafs[1]; + } + else + { + rhs = bv::utils::mkZero(bv::utils::getSize(pv)); + } + rhs = Rewriter::rewrite(rhs); + + if (lhs == rhs) + { + return bv::utils::mkTrue(); + } + + Node result = lhs.eqNode(rhs); + contains_pv[result] = true; + return result; +} + +Node BvInstantiator::rewriteTermForSolvePv( + Node pv, + Node n, + std::vector& children, + std::unordered_map& contains_pv) +{ + NodeManager* nm = NodeManager::currentNM(); + + // [1] rewrite cases of non-invertible operators + + if (n.getKind() == EQUAL) + { + TNode lhs = children[0]; + TNode rhs = children[1]; + + /* rewrite: x * x = x -> x < 2 */ + if ((lhs == pv && rhs.getKind() == BITVECTOR_MULT && rhs[0] == pv + && rhs[1] == pv) + || (rhs == pv && lhs.getKind() == BITVECTOR_MULT && lhs[0] == pv + && lhs[1] == pv)) + { + return nm->mkNode( + BITVECTOR_ULT, + pv, + bv::utils::mkConst(BitVector(bv::utils::getSize(pv), Integer(2)))); + } + + if (options::cbqiBvLinearize() && contains_pv[lhs] && contains_pv[rhs]) + { + Node result = normalizePvEqual(pv, children, contains_pv); + if (!result.isNull()) + { + Trace("cegqi-bv-nl") + << "Normalize " << n << " to " << result << std::endl; + } + else + { + Trace("cegqi-bv-nl") + << "Nonlinear " << n.getKind() << " " << n << std::endl; + } + return result; + } + } + else if (n.getKind() == BITVECTOR_MULT || n.getKind() == BITVECTOR_PLUS) + { + if (options::cbqiBvLinearize() && contains_pv[n]) + { + Node result; + if (n.getKind() == BITVECTOR_MULT) + { + result = normalizePvMult(pv, children, contains_pv); + } + else + { + result = normalizePvPlus(pv, children, contains_pv); + } + if (!result.isNull()) + { + Trace("cegqi-bv-nl") + << "Normalize " << n << " to " << result << std::endl; + return result; + } + else + { + Trace("cegqi-bv-nl") + << "Nonlinear " << n.getKind() << " " << n << std::endl; + } + } + } + + // [2] try to rewrite non-linear literals -> linear literals + + return Node::null(); +} + +/** sort bv extract interval + * + * This sorts lists of bitvector extract terms where + * ((_ extract i1 i2) t) < ((_ extract j1 j2) t) + * if i1>j1 or i1=j1 and i2>j2. + */ +struct SortBvExtractInterval +{ + bool operator()(Node i, Node j) + { + Assert(i.getKind() == BITVECTOR_EXTRACT); + Assert(j.getKind() == BITVECTOR_EXTRACT); + BitVectorExtract ie = i.getOperator().getConst(); + BitVectorExtract je = j.getOperator().getConst(); + if (ie.high > je.high) + { + return true; + } + else if (ie.high == je.high) + { + Assert(ie.low != je.low); + return ie.low > je.low; + } + return false; + } +}; + +void BvInstantiatorPreprocess::registerCounterexampleLemma( + std::vector& lems, std::vector& ce_vars) +{ + // new variables + std::vector vars; + // new lemmas + std::vector new_lems; + + if (options::cbqiBvRmExtract()) + { + NodeManager* nm = NodeManager::currentNM(); + Trace("cegqi-bv-pp") << "-----remove extracts..." << std::endl; + // map from terms to bitvector extracts applied to that term + std::map > extract_map; + std::unordered_set visited; + for (unsigned i = 0, size = lems.size(); i < size; i++) + { + Trace("cegqi-bv-pp-debug2") << "Register ce lemma # " << i << " : " + << lems[i] << std::endl; + collectExtracts(lems[i], extract_map, visited); + } + for (std::pair >& es : extract_map) + { + // sort based on the extract start position + std::vector& curr_vec = es.second; + + SortBvExtractInterval sbei; + std::sort(curr_vec.begin(), curr_vec.end(), sbei); + + unsigned width = es.first.getType().getBitVectorSize(); + + // list of points b such that: + // b>0 and we must start a segment at (b-1) or b==0 + std::vector boundaries; + boundaries.push_back(width); + boundaries.push_back(0); + + Trace("cegqi-bv-pp") << "For term " << es.first << " : " << std::endl; + for (unsigned i = 0, size = curr_vec.size(); i < size; i++) + { + Trace("cegqi-bv-pp") << " " << i << " : " << curr_vec[i] << std::endl; + BitVectorExtract e = + curr_vec[i].getOperator().getConst(); + if (std::find(boundaries.begin(), boundaries.end(), e.high + 1) + == boundaries.end()) + { + boundaries.push_back(e.high + 1); + } + if (std::find(boundaries.begin(), boundaries.end(), e.low) + == boundaries.end()) + { + boundaries.push_back(e.low); + } + } + std::sort(boundaries.rbegin(), boundaries.rend()); + + // make the extract variables + std::vector children; + for (unsigned i = 1; i < boundaries.size(); i++) + { + Assert(boundaries[i - 1] > 0); + Node ex = bv::utils::mkExtract( + es.first, boundaries[i - 1] - 1, boundaries[i]); + Node var = + nm->mkSkolem("ek", + ex.getType(), + "variable to represent disjoint extract region"); + children.push_back(var); + vars.push_back(var); + } + + Node conc = nm->mkNode(kind::BITVECTOR_CONCAT, children); + Assert(conc.getType() == es.first.getType()); + Node eq_lem = conc.eqNode(es.first); + Trace("cegqi-bv-pp") << "Introduced : " << eq_lem << std::endl; + new_lems.push_back(eq_lem); + Trace("cegqi-bv-pp") << "...finished processing extracts for term " + << es.first << std::endl; + } + Trace("cegqi-bv-pp") << "-----done remove extracts" << std::endl; + } + + if (!vars.empty()) + { + // could try applying subs -> vars here + // in practice, this led to worse performance + + Trace("cegqi-bv-pp") << "Adding " << new_lems.size() << " lemmas..." + << std::endl; + lems.insert(lems.end(), new_lems.begin(), new_lems.end()); + Trace("cegqi-bv-pp") << "Adding " << vars.size() << " variables..." + << std::endl; + ce_vars.insert(ce_vars.end(), vars.begin(), vars.end()); + } +} + +void BvInstantiatorPreprocess::collectExtracts( + Node lem, + std::map >& extract_map, + std::unordered_set& visited) +{ + std::vector visit; + TNode cur; + visit.push_back(lem); + do + { + cur = visit.back(); + visit.pop_back(); + if (visited.find(cur) == visited.end()) + { + visited.insert(cur); + if (cur.getKind() != FORALL) + { + if (cur.getKind() == BITVECTOR_EXTRACT) + { + extract_map[cur[0]].push_back(cur); + } + + for (const Node& nc : cur) + { + visit.push_back(nc); + } + } + } + } while (!visit.empty()); +} + +} /* CVC4::theory::quantifiers namespace */ +} /* CVC4::theory namespace */ +} /* CVC4 namespace */ diff --git a/src/theory/quantifiers/cegqi/ceg_t_instantiator.h b/src/theory/quantifiers/cegqi/ceg_t_instantiator.h new file mode 100644 index 000000000..b9c7e2024 --- /dev/null +++ b/src/theory/quantifiers/cegqi/ceg_t_instantiator.h @@ -0,0 +1,331 @@ +/********************* */ +/*! \file ceg_t_instantiator.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 theory-specific counterexample-guided quantifier instantiation + **/ + + +#include "cvc4_private.h" + +#ifndef __CVC4__CEG_T_INSTANTIATOR_H +#define __CVC4__CEG_T_INSTANTIATOR_H + +#include "theory/quantifiers/bv_inverter.h" +#include "theory/quantifiers/cegqi/ceg_instantiator.h" + +#include + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +/** TODO (#1367) : document these classes, also move to separate files. */ + +class ArithInstantiator : public Instantiator { + public: + ArithInstantiator( QuantifiersEngine * qe, TypeNode tn ) : Instantiator( qe, tn ){} + virtual ~ArithInstantiator(){} + virtual void reset(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) override; + virtual bool hasProcessEquality(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) override + { + return true; + } + virtual bool processEquality(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + std::vector& term_props, + std::vector& terms, + CegInstEffort effort) override; + virtual bool hasProcessAssertion(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) override + { + return true; + } + virtual Node hasProcessAssertion(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + Node lit, + CegInstEffort effort) override; + virtual bool processAssertion(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + Node lit, + Node alit, + CegInstEffort effort) override; + virtual bool processAssertions(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) override; + virtual bool needsPostProcessInstantiationForVariable( + CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) override; + virtual bool postProcessInstantiationForVariable( + CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort, + std::vector& lemmas) override; + virtual std::string identify() const override { return "Arith"; } + private: + Node d_vts_sym[2]; + std::vector d_mbp_bounds[2]; + std::vector d_mbp_coeff[2]; + std::vector d_mbp_vts_coeff[2][2]; + std::vector d_mbp_lit[2]; + int solve_arith(CegInstantiator* ci, + Node v, + Node atom, + Node& veq_c, + Node& val, + Node& vts_coeff_inf, + Node& vts_coeff_delta); + Node getModelBasedProjectionValue(CegInstantiator* ci, + Node e, + Node t, + bool isLower, + Node c, + Node me, + Node mt, + Node theta, + Node inf_coeff, + Node delta_coeff); +}; + +class DtInstantiator : public Instantiator { +public: + DtInstantiator( QuantifiersEngine * qe, TypeNode tn ) : Instantiator( qe, tn ){} + virtual ~DtInstantiator(){} + virtual void reset(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) override; + virtual bool processEqualTerms(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + std::vector& eqc, + CegInstEffort effort) override; + virtual bool hasProcessEquality(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) override + { + return true; + } + virtual bool processEquality(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + std::vector& term_props, + std::vector& terms, + CegInstEffort effort) override; + virtual std::string identify() const override { return "Dt"; } + private: + Node solve_dt(Node v, Node a, Node b, Node sa, Node sb); +}; + +class TermArgTrie; + +class EprInstantiator : public Instantiator { + public: + EprInstantiator( QuantifiersEngine * qe, TypeNode tn ) : Instantiator( qe, tn ){} + virtual ~EprInstantiator(){} + virtual void reset(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) override; + virtual bool processEqualTerm(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + TermProperties& pv_prop, + Node n, + CegInstEffort effort) override; + virtual bool processEqualTerms(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + std::vector& eqc, + CegInstEffort effort) override; + virtual std::string identify() const override { return "Epr"; } + private: + std::vector d_equal_terms; + void computeMatchScore(CegInstantiator* ci, + Node pv, + Node catom, + std::vector& arg_reps, + TermArgTrie* tat, + unsigned index, + std::map& match_score); + void computeMatchScore(CegInstantiator* ci, + Node pv, + Node catom, + Node eqc, + std::map& match_score); +}; + +/** Bitvector instantiator + * + * This implements an approach for counterexample-guided instantiation + * for bit-vector variables based on word-level inversions. + * It is enabled by --cbqi-bv. + */ +class BvInstantiator : public Instantiator { + public: + BvInstantiator(QuantifiersEngine* qe, TypeNode tn); + ~BvInstantiator() override; + void reset(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) override; + bool hasProcessAssertion(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) override + { + return true; + } + Node hasProcessAssertion(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + Node lit, + CegInstEffort effort) override; + bool processAssertion(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + Node lit, + Node alit, + CegInstEffort effort) override; + bool processAssertions(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) override; + /** use model value + * + * We allow model values if we have not already tried an assertion, + * and only at levels below full if cbqiFullEffort is false. + */ + bool useModelValue(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + CegInstEffort effort) override; + std::string identify() const override { return "Bv"; } + + private: + // point to the bv inverter class + BvInverter * d_inverter; + unsigned d_inst_id_counter; + /** information about solved forms */ + std::unordered_map< Node, std::vector< unsigned >, NodeHashFunction > d_var_to_inst_id; + std::unordered_map< unsigned, Node > d_inst_id_to_term; + std::unordered_map d_inst_id_to_alit; + // variable to current id we are processing + std::unordered_map< Node, unsigned, NodeHashFunction > d_var_to_curr_inst_id; + /** the amount of slack we added for asserted literals */ + std::unordered_map d_alit_to_model_slack; + /** whether we have tried an instantiation based on assertion in this round */ + bool d_tried_assertion_inst; + /** rewrite assertion for solve pv + * returns a literal that is equivalent to lit that leads to best solved form for pv + */ + Node rewriteAssertionForSolvePv(CegInstantiator* ci, Node pv, Node lit); + /** rewrite term for solve pv + * This is a helper function for rewriteAssertionForSolvePv. + * If this returns non-null value ret, then this indicates + * that n should be rewritten to ret. It is called as + * a "post-rewrite", that is, after the children of n + * have been rewritten and stored in the vector children. + * + * contains_pv stores whether certain nodes contain pv. + * where we guarantee that all subterms of terms in children + * appear in the domain of contains_pv. + */ + Node rewriteTermForSolvePv( + Node pv, + Node n, + std::vector& children, + std::unordered_map& contains_pv); + /** process literal, called from processAssertion + * lit is the literal to solve for pv that has been rewritten according to + * internal rules here. + * alit is the asserted literal that lit is derived from. + */ + void processLiteral(CegInstantiator* ci, + SolvedForm& sf, + Node pv, + Node lit, + Node alit, + CegInstEffort effort); +}; + +/** Bitvector instantiator preprocess + * + * This class implements preprocess techniques that are helpful for + * counterexample-guided instantiation, such as introducing variables + * that refer to disjoint bit-vector extracts. + */ +class BvInstantiatorPreprocess : public InstantiatorPreprocess +{ + public: + BvInstantiatorPreprocess() {} + ~BvInstantiatorPreprocess() override {} + /** register counterexample lemma + * + * This method modifies the contents of lems based on the extract terms + * it contains when the option --cbqi-bv-rm-extract is enabled. It introduces + * a dummy equality so that segments of terms t under extracts can be solved + * independently. + * + * For example: + * P[ ((extract 7 4) t), ((extract 3 0) t)] + * becomes: + * P[((extract 7 4) t), ((extract 3 0) t)] ^ + * t = concat( x74, x30 ) + * where x74 and x30 are fresh variables of type BV_4. + * + * Another example: + * P[ ((extract 7 3) t), ((extract 4 0) t)] + * becomes: + * P[((extract 7 4) t), ((extract 3 0) t)] ^ + * t = concat( x75, x44, x30 ) + * where x75, x44 and x30 are fresh variables of type BV_3, BV_1, and BV_4 + * respectively. + * + * Notice we leave the original conjecture alone. This is done for performance + * since the added equalities ensure we are able to construct the proper + * solved forms for variables in t and for the intermediate variables above. + */ + void registerCounterexampleLemma(std::vector& lems, + std::vector& ce_vars) override; + + private: + /** collect extracts + * + * This method collects all extract terms in lem + * and stores them in d_extract_map. + * visited is the terms we've already visited. + */ + void collectExtracts(Node lem, + std::map >& extract_map, + std::unordered_set& visited); +}; + +} /* CVC4::theory::quantifiers namespace */ +} /* CVC4::theory namespace */ +} /* CVC4 namespace */ + +#endif diff --git a/src/theory/quantifiers/cegqi/inst_strategy_cbqi.cpp b/src/theory/quantifiers/cegqi/inst_strategy_cbqi.cpp new file mode 100644 index 000000000..8de3dbfcb --- /dev/null +++ b/src/theory/quantifiers/cegqi/inst_strategy_cbqi.cpp @@ -0,0 +1,828 @@ +/********************* */ +/*! \file inst_strategy_cbqi.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 Implementation of counterexample-guided quantifier instantiation strategies + **/ +#include "theory/quantifiers/cegqi/inst_strategy_cbqi.h" + +#include "options/quantifiers_options.h" +#include "smt/term_formula_removal.h" +#include "theory/arith/partial_model.h" +#include "theory/arith/theory_arith.h" +#include "theory/arith/theory_arith_private.h" +#include "theory/quantifiers/first_order_model.h" +#include "theory/quantifiers/instantiate.h" +#include "theory/quantifiers/quant_epr.h" +#include "theory/quantifiers/quantifiers_attributes.h" +#include "theory/quantifiers/quantifiers_rewriter.h" +#include "theory/quantifiers/term_database.h" +#include "theory/quantifiers/term_util.h" +#include "theory/quantifiers/ematching/trigger.h" +#include "theory/theory_engine.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; +using namespace CVC4::theory::arith; + +#define ARITH_INSTANTIATOR_USE_MINUS_DELTA + +InstStrategyCbqi::InstStrategyCbqi( QuantifiersEngine * qe ) + : QuantifiersModule( qe ), d_added_cbqi_lemma( qe->getUserContext() ), +d_elim_quants( qe->getSatContext() ), +d_nested_qe_waitlist_size( qe->getUserContext() ), +d_nested_qe_waitlist_proc( qe->getUserContext() ) +//, d_added_inst( qe->getUserContext() ) +{ + d_qid_count = 0; +} + +bool InstStrategyCbqi::needsCheck( Theory::Effort e ) { + return e>=Theory::EFFORT_LAST_CALL; +} + +QuantifiersModule::QEffort InstStrategyCbqi::needsModel(Theory::Effort e) +{ + for( unsigned i=0; igetModel()->getNumAssertedQuantifiers(); i++ ){ + Node q = d_quantEngine->getModel()->getAssertedQuantifier( i ); + if( doCbqi( q ) && d_quantEngine->getModel()->isQuantifierActive( q ) ){ + return QEFFORT_STANDARD; + } + } + return QEFFORT_NONE; +} + +bool InstStrategyCbqi::registerCbqiLemma( Node q ) { + if( !hasAddedCbqiLemma( q ) ){ + d_added_cbqi_lemma.insert( q ); + Trace("cbqi-debug") << "Do cbqi for " << q << std::endl; + //add cbqi lemma + //get the counterexample literal + Node ceLit = d_quantEngine->getTermUtil()->getCounterexampleLiteral( q ); + Node ceBody = d_quantEngine->getTermUtil()->getInstConstantBody( q ); + if( !ceBody.isNull() ){ + //add counterexample lemma + Node lem = NodeManager::currentNM()->mkNode( OR, ceLit.negate(), ceBody.negate() ); + //require any decision on cel to be phase=true + d_quantEngine->addRequirePhase( ceLit, true ); + Debug("cbqi-debug") << "Require phase " << ceLit << " = true." << std::endl; + //add counterexample lemma + lem = Rewriter::rewrite( lem ); + Trace("cbqi-lemma") << "Counterexample lemma : " << lem << std::endl; + registerCounterexampleLemma( q, lem ); + + //totality lemmas for EPR + QuantEPR * qepr = d_quantEngine->getQuantEPR(); + if( qepr!=NULL ){ + for( unsigned i=0; iisEPR( tn ) ){ + //add totality lemma + std::map< TypeNode, std::vector< Node > >::iterator itc = qepr->d_consts.find( tn ); + if( itc!=qepr->d_consts.end() ){ + Assert( !itc->second.empty() ); + Node ic = d_quantEngine->getTermUtil()->getInstantiationConstant( q, i ); + std::vector< Node > disj; + for( unsigned j=0; jsecond.size(); j++ ){ + disj.push_back( ic.eqNode( itc->second[j] ) ); + } + Node tlem = disj.size()==1 ? disj[0] : NodeManager::currentNM()->mkNode( kind::OR, disj ); + Trace("cbqi-lemma") << "EPR totality lemma : " << tlem << std::endl; + d_quantEngine->getOutputChannel().lemma( tlem ); + }else{ + Assert( false ); + } + }else{ + Assert( !options::cbqiAll() ); + } + } + } + } + + //compute dependencies between quantified formulas + if( options::cbqiLitDepend() || options::cbqiInnermost() ){ + std::vector< Node > ics; + TermUtil::computeVarContains( q, ics ); + d_parent_quant[q].clear(); + d_children_quant[q].clear(); + std::vector< Node > dep; + for( unsigned i=0; igetTermUtil()->getCounterexampleLiteral( qi ); + dep.push_back( qi ); + dep.push_back( qicel ); + } + } + if( !dep.empty() ){ + Node dep_lemma = NodeManager::currentNM()->mkNode( kind::IMPLIES, ceLit, NodeManager::currentNM()->mkNode( kind::AND, dep ) ); + Trace("cbqi-lemma") << "Counterexample dependency lemma : " << dep_lemma << std::endl; + d_quantEngine->getOutputChannel().lemma( dep_lemma ); + } + } + + //must register all sub-quantifiers of counterexample lemma, register their lemmas + std::vector< Node > quants; + TermUtil::computeQuantContains( lem, quants ); + for( unsigned i=0; igetModel()->getNumAssertedQuantifiers(); i++ ){ + Node q = d_quantEngine->getModel()->getAssertedQuantifier( i ); + //it is not active if it corresponds to a rewrite rule: we will process in rewrite engine + if( doCbqi( q ) ){ + Assert( hasAddedCbqiLemma( q ) ); + if( d_quantEngine->getModel()->isQuantifierActive( q ) ){ + d_active_quant[q] = true; + Debug("cbqi-debug") << "Check quantified formula " << q << "..." << std::endl; + Node cel = d_quantEngine->getTermUtil()->getCounterexampleLiteral( q ); + bool value; + if( d_quantEngine->getValuation().hasSatValue( cel, value ) ){ + Debug("cbqi-debug") << "...CE Literal has value " << value << std::endl; + if( !value ){ + if( d_quantEngine->getValuation().isDecision( cel ) ){ + Trace("cbqi-warn") << "CBQI WARNING: Bad decision on CE Literal." << std::endl; + }else{ + Trace("cbqi") << "Inactive : " << q << std::endl; + d_quantEngine->getModel()->setQuantifierActive( q, false ); + d_cbqi_set_quant_inactive = true; + d_active_quant.erase( q ); + d_elim_quants.insert( q ); + Trace("cbqi-nqe") << "Inactive, waitlist proc/size = " << d_nested_qe_waitlist_proc[q].get() << "/" << d_nested_qe_waitlist_size[q].get() << std::endl; + //process from waitlist + while( d_nested_qe_waitlist_proc[q]=0 ); + Assert( index<(int)d_nested_qe_waitlist[q].size() ); + Node nq = d_nested_qe_waitlist[q][index]; + Node nqeqn = doNestedQENode( d_nested_qe_info[nq].d_q, q, nq, d_nested_qe_info[nq].d_inst_terms, d_nested_qe_info[nq].d_doVts ); + Node dqelem = nq.eqNode( nqeqn ); + Trace("cbqi-lemma") << "Delayed nested quantifier elimination lemma : " << dqelem << std::endl; + d_quantEngine->getOutputChannel().lemma( dqelem ); + d_nested_qe_waitlist_proc[q] = index + 1; + } + } + } + }else{ + Debug("cbqi-debug") << "...CE Literal does not have value " << std::endl; + } + } + } + } + + //refinement: only consider innermost active quantified formulas + if( options::cbqiInnermost() ){ + if( !d_children_quant.empty() && !d_active_quant.empty() ){ + Trace("cbqi-debug") << "Find non-innermost quantifiers..." << std::endl; + std::vector< Node > ninner; + for( std::map< Node, bool >::iterator it = d_active_quant.begin(); it != d_active_quant.end(); ++it ){ + std::map< Node, std::vector< Node > >::iterator itc = d_children_quant.find( it->first ); + if( itc!=d_children_quant.end() ){ + for( unsigned j=0; jsecond.size(); j++ ){ + if( d_active_quant.find( itc->second[j] )!=d_active_quant.end() ){ + Trace("cbqi-debug") << "Do not consider " << it->first << " since it is not innermost (" << itc->second[j] << std::endl; + ninner.push_back( it->first ); + break; + } + } + } + } + Trace("cbqi-debug") << "Found " << ninner.size() << " non-innermost." << std::endl; + for( unsigned i=0; iinConflict() ); + double clSet = 0; + if( Trace.isOn("cbqi-engine") ){ + clSet = double(clock())/double(CLOCKS_PER_SEC); + Trace("cbqi-engine") << "---Cbqi Engine Round, effort = " << e << "---" << std::endl; + } + unsigned lastWaiting = d_quantEngine->getNumLemmasWaiting(); + for( int ee=0; ee<=1; ee++ ){ + //for( unsigned i=0; igetModel()->getNumAssertedQuantifiers(); i++ ){ + // Node q = d_quantEngine->getModel()->getAssertedQuantifier( i ); + // if( doCbqi( q ) && d_quantEngine->getModel()->isQuantifierActive( q ) ){ + for( std::map< Node, bool >::iterator it = d_active_quant.begin(); it != d_active_quant.end(); ++it ){ + Node q = it->first; + Trace("cbqi") << "CBQI : Process quantifier " << q[0] << " at effort " << ee << std::endl; + if( d_nested_qe.find( q )==d_nested_qe.end() ){ + process( q, e, ee ); + if( d_quantEngine->inConflict() ){ + break; + } + }else{ + Trace("cbqi-warn") << "CBQI : Cannot process already eliminated quantified formula " << q << std::endl; + Assert( false ); + } + } + if( d_quantEngine->inConflict() || d_quantEngine->getNumLemmasWaiting()>lastWaiting ){ + break; + } + } + if( Trace.isOn("cbqi-engine") ){ + if( d_quantEngine->getNumLemmasWaiting()>lastWaiting ){ + Trace("cbqi-engine") << "Added lemmas = " << (d_quantEngine->getNumLemmasWaiting()-lastWaiting) << std::endl; + } + double clSet2 = double(clock())/double(CLOCKS_PER_SEC); + Trace("cbqi-engine") << "Finished cbqi engine, time = " << (clSet2-clSet) << std::endl; + } + } +} + +bool InstStrategyCbqi::checkComplete() { + if( ( !options::cbqiSat() && d_cbqi_set_quant_inactive ) || d_incomplete_check ){ + return false; + }else{ + return true; + } +} + +bool InstStrategyCbqi::checkCompleteFor( Node q ) { + std::map< Node, int >::iterator it = d_do_cbqi.find( q ); + if( it!=d_do_cbqi.end() ){ + return it->second>0; + }else{ + return false; + } +} + +Node InstStrategyCbqi::getIdMarkedQuantNode( Node n, std::map< Node, Node >& visited ){ + std::map< Node, Node >::iterator it = visited.find( n ); + if( it==visited.end() ){ + Node ret = n; + if( n.getKind()==FORALL ){ + QAttributes qa; + QuantAttributes::computeQuantAttributes( n, qa ); + if( qa.d_qid_num.isNull() ){ + std::vector< Node > rc; + rc.push_back( n[0] ); + rc.push_back( getIdMarkedQuantNode( n[1], visited ) ); + Node avar = NodeManager::currentNM()->mkSkolem( "id", NodeManager::currentNM()->booleanType() ); + QuantIdNumAttribute ida; + avar.setAttribute(ida,d_qid_count); + d_qid_count++; + std::vector< Node > iplc; + iplc.push_back( NodeManager::currentNM()->mkNode( INST_ATTRIBUTE, avar ) ); + if( n.getNumChildren()==3 ){ + for( unsigned i=0; imkNode( INST_PATTERN_LIST, iplc ) ); + ret = NodeManager::currentNM()->mkNode( FORALL, rc ); + } + }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; imkNode( n.getKind(), children ); + } + } + visited[n] = ret; + return ret; + }else{ + return it->second; + } +} + +void InstStrategyCbqi::preRegisterQuantifier( Node q ) { + if( d_quantEngine->getOwner( q )==NULL && doCbqi( q ) ){ + if( d_do_cbqi[q]==2 ){ + //take full ownership of the quantified formula + d_quantEngine->setOwner( q, this ); + + //mark all nested quantifiers with id + if( options::cbqiNestedQE() ){ + std::map< Node, Node > visited; + Node mq = getIdMarkedQuantNode( q[1], visited ); + if( mq!=q[1] ){ + //do not do cbqi + d_do_cbqi[q] = false; + //instead do reduction + std::vector< Node > qqc; + qqc.push_back( q[0] ); + qqc.push_back( mq ); + if( q.getNumChildren()==3 ){ + qqc.push_back( q[2] ); + } + Node qq = NodeManager::currentNM()->mkNode( FORALL, qqc ); + Node mlem = NodeManager::currentNM()->mkNode( IMPLIES, q, qq ); + Trace("cbqi-lemma") << "Mark quant id lemma : " << mlem << std::endl; + d_quantEngine->getOutputChannel().lemma( mlem ); + } + } + } + } +} + +void InstStrategyCbqi::registerQuantifier( Node q ) { + if( doCbqi( q ) ){ + if( registerCbqiLemma( q ) ){ + Trace("cbqi") << "Registered cbqi lemma for quantifier : " << q << std::endl; + } + } +} + +Node InstStrategyCbqi::doNestedQENode( Node q, Node ceq, Node n, std::vector< Node >& inst_terms, bool doVts ) { + // there is a nested quantified formula (forall y. nq[y,x]) such that + // q is (forall y. nq[y,t]) for ground terms t, + // ceq is (forall y. nq[y,e]) for CE variables e. + // we call this function when we know (forall y. nq[y,e]) is equivalent to quantifier-free formula C[e]. + // in this case, q is equivalent to the quantifier-free formula C[t]. + if( d_nested_qe.find( ceq )==d_nested_qe.end() ){ + d_nested_qe[ceq] = d_quantEngine->getInstantiatedConjunction( ceq ); + Trace("cbqi-nqe") << "CE quantifier elimination : " << std::endl; + Trace("cbqi-nqe") << " " << ceq << std::endl; + Trace("cbqi-nqe") << " " << d_nested_qe[ceq] << std::endl; + //should not contain quantifiers + Assert( !QuantifiersRewriter::containsQuantifiers( d_nested_qe[ceq] ) ); + } + Assert( d_quantEngine->getTermUtil()->d_inst_constants[q].size()==inst_terms.size() ); + //replace inst constants with instantiation + Node ret = d_nested_qe[ceq].substitute( d_quantEngine->getTermUtil()->d_inst_constants[q].begin(), + d_quantEngine->getTermUtil()->d_inst_constants[q].end(), + inst_terms.begin(), inst_terms.end() ); + if( doVts ){ + //do virtual term substitution + ret = Rewriter::rewrite( ret ); + ret = d_quantEngine->getTermUtil()->rewriteVtsSymbols( ret ); + } + Trace("cbqi-nqe") << "Nested quantifier elimination: " << std::endl; + Trace("cbqi-nqe") << " " << n << std::endl; + Trace("cbqi-nqe") << " " << ret << std::endl; + return ret; +} + +Node InstStrategyCbqi::doNestedQERec( Node q, Node n, std::map< Node, Node >& visited, std::vector< Node >& inst_terms, bool doVts ) { + if( visited.find( n )==visited.end() ){ + Node ret = n; + if( n.getKind()==FORALL ){ + QAttributes qa; + QuantAttributes::computeQuantAttributes( n, qa ); + if( !qa.d_qid_num.isNull() ){ + //if it has an id, check whether we have done quantifier elimination for this id + std::map< Node, Node >::iterator it = d_id_to_ce_quant.find( qa.d_qid_num ); + if( it!=d_id_to_ce_quant.end() ){ + Node ceq = it->second; + bool doNestedQe = d_elim_quants.contains( ceq ); + if( doNestedQe ){ + ret = doNestedQENode( q, ceq, n, inst_terms, doVts ); + }else{ + Trace("cbqi-nqe") << "Add to nested qe waitlist : " << std::endl; + Node nr = Rewriter::rewrite( n ); + Trace("cbqi-nqe") << " " << ceq << std::endl; + Trace("cbqi-nqe") << " " << nr << std::endl; + int wlsize = d_nested_qe_waitlist_size[ceq] + 1; + d_nested_qe_waitlist_size[ceq] = wlsize; + if( wlsize<(int)d_nested_qe_waitlist[ceq].size() ){ + d_nested_qe_waitlist[ceq][wlsize] = nr; + }else{ + d_nested_qe_waitlist[ceq].push_back( nr ); + } + d_nested_qe_info[nr].d_q = q; + d_nested_qe_info[nr].d_inst_terms.clear(); + d_nested_qe_info[nr].d_inst_terms.insert( d_nested_qe_info[nr].d_inst_terms.end(), inst_terms.begin(), inst_terms.end() ); + d_nested_qe_info[nr].d_doVts = doVts; + //TODO: ensure this holds by restricting prenex when cbqiNestedQe is true. + Assert( !options::cbqiInnermost() ); + } + } + } + }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; imkNode( n.getKind(), children ); + } + } + visited[n] = ret; + return ret; + }else{ + return n; + } +} + +Node InstStrategyCbqi::doNestedQE( Node q, std::vector< Node >& inst_terms, Node lem, bool doVts ) { + std::map< Node, Node > visited; + return doNestedQERec( q, lem, visited, inst_terms, doVts ); +} + +void InstStrategyCbqi::registerCounterexampleLemma( Node q, Node lem ){ + Trace("cbqi-debug") << "Counterexample lemma : " << lem << std::endl; + d_quantEngine->addLemma( lem, false ); +} + +bool InstStrategyCbqi::hasNonCbqiOperator( Node n, std::map< Node, bool >& visited ){ + if( visited.find( n )==visited.end() ){ + visited[n] = true; + if( n.getKind()!=BOUND_VARIABLE && TermUtil::hasBoundVarAttr( n ) ){ + if( !inst::Trigger::isCbqiKind( n.getKind() ) ){ + Trace("cbqi-debug2") << "Non-cbqi kind : " << n.getKind() << " in " << n << std::endl; + return true; + }else if( n.getKind()==MULT && ( n.getNumChildren()!=2 || !n[0].isConst() ) ){ + Trace("cbqi-debug2") << "Non-linear arithmetic : " << n << std::endl; + return true; + }else if( n.getKind()==FORALL ){ + return hasNonCbqiOperator( n[1], visited ); + }else{ + for( unsigned i=0; i& visited ) { + std::map< TypeNode, int >::iterator itv = visited.find( tn ); + if( itv==visited.end() ){ + visited[tn] = 0; + int ret = -1; + if( tn.isInteger() || tn.isReal() || tn.isBoolean() || tn.isBitVector() ){ + ret = 0; + }else if( tn.isDatatype() ){ + ret = 1; + const Datatype& dt = ((DatatypeType)tn.toType()).getDatatype(); + for( unsigned i=0; igetQuantEPR(); + if( qepr!=NULL ){ + ret = qepr->isEPR( tn ) ? 1 : -1; + } + } + visited[tn] = ret; + return ret; + }else{ + return itv->second; + } +} + +int InstStrategyCbqi::hasNonCbqiVariable( Node q ){ + int hmin = 1; + for( unsigned i=0; i visited; + int handled = isCbqiSort( tn, visited ); + if( handled==-1 ){ + return -1; + }else if( handled::iterator it = d_do_cbqi.find( q ); + if( it==d_do_cbqi.end() ){ + int ret = 2; + if( !d_quantEngine->getQuantAttributes()->isQuantElim( q ) ){ + Assert( !d_quantEngine->getQuantAttributes()->isQuantElimPartial( q ) ); + //if has an instantiation pattern, don't do it + if( q.getNumChildren()==3 && options::eMatching() && options::userPatternsQuant()!=USER_PAT_MODE_IGNORE ){ + for( unsigned i=0; igetQuantAttributes()->isSygus( q ) ){ + ret = 0; + } + if( ret!=0 ){ + //if quantifier has a non-handled variable, then do not use cbqi + //if quantifier has an APPLY_UF term, then do not use cbqi unless EPR + int ncbqiv = hasNonCbqiVariable( q ); + if( ncbqiv==0 || ncbqiv==1 ){ + std::map< Node, bool > visited; + if( hasNonCbqiOperator( q[1], visited ) ){ + if( ncbqiv==1 ){ + //all variables are fully handled, this implies this will be handlable regardless of body (e.g. for EPR) + // so, try but not exclusively + ret = 1; + }else{ + //cannot be handled + ret = 0; + } + } + }else{ + // unhandled variable type + ret = 0; + } + if( ret==0 && options::cbqiAll() ){ + //try but not exclusively + ret = 1; + } + } + } + Trace("cbqi-quant") << "doCbqi " << q << " returned " << ret << std::endl; + d_do_cbqi[q] = ret; + return ret!=0; + }else{ + return it->second!=0; + } +} + +Node InstStrategyCbqi::getNextDecisionRequestProc( Node q, std::map< Node, bool >& proc ) { + if( proc.find( q )==proc.end() ){ + proc[q] = true; + //first check children + std::map< Node, std::vector< Node > >::iterator itc = d_children_quant.find( q ); + if( itc!=d_children_quant.end() ){ + for( unsigned j=0; jsecond.size(); j++ ){ + Node d = getNextDecisionRequestProc( itc->second[j], proc ); + if( !d.isNull() ){ + return d; + } + } + } + //then check self + if( hasAddedCbqiLemma( q ) ){ + Node cel = d_quantEngine->getTermUtil()->getCounterexampleLiteral( q ); + bool value; + if( !d_quantEngine->getValuation().hasSatValue( cel, value ) ){ + Trace("cbqi-dec") << "CBQI: get next decision " << cel << std::endl; + return cel; + } + } + } + return Node::null(); +} + +Node InstStrategyCbqi::getNextDecisionRequest( unsigned& priority ){ + std::map< Node, bool > proc; + //for( unsigned i=0; igetModel()->getNumAssertedQuantifiers(); i++ ){ + // Node q = d_quantEngine->getModel()->getAssertedQuantifier( i ); + for( NodeSet::const_iterator it = d_added_cbqi_lemma.begin(); it != d_added_cbqi_lemma.end(); ++it ){ + Node q = *it; + Node d = getNextDecisionRequestProc( q, proc ); + if( !d.isNull() ){ + priority = 0; + return d; + } + } + return Node::null(); +} + + + +//new implementation + +bool CegqiOutputInstStrategy::doAddInstantiation( std::vector< Node >& subs ) { + return d_out->doAddInstantiation( subs ); +} + +bool CegqiOutputInstStrategy::isEligibleForInstantiation( Node n ) { + return d_out->isEligibleForInstantiation( n ); +} + +bool CegqiOutputInstStrategy::addLemma( Node lem ) { + return d_out->addLemma( lem ); +} + + +InstStrategyCegqi::InstStrategyCegqi( QuantifiersEngine * qe ) + : InstStrategyCbqi( qe ) { + d_out = new CegqiOutputInstStrategy( this ); + d_small_const = NodeManager::currentNM()->mkConst( Rational(1)/Rational(1000000) ); + d_check_vts_lemma_lc = false; +} + +InstStrategyCegqi::~InstStrategyCegqi() +{ + delete d_out; + + for(std::map< Node, CegInstantiator * >::iterator i = d_cinst.begin(), + iend = d_cinst.end(); i != iend; ++i) { + CegInstantiator * instantiator = (*i).second; + delete instantiator; + } + d_cinst.clear(); +} + +void InstStrategyCegqi::processResetInstantiationRound( Theory::Effort effort ) { + d_check_vts_lemma_lc = false; +} + +void InstStrategyCegqi::process( Node q, Theory::Effort effort, int e ) { + if( e==0 ){ + CegInstantiator * cinst = getInstantiator( q ); + Trace("inst-alg") << "-> Run cegqi for " << q << std::endl; + d_curr_quant = q; + if( !cinst->check() ){ + d_incomplete_check = true; + d_check_vts_lemma_lc = true; + } + d_curr_quant = Node::null(); + }else if( e==1 ){ + //minimize the free delta heuristically on demand + if( d_check_vts_lemma_lc ){ + Trace("inst-alg") << "-> Minimize delta heuristic, for " << q << std::endl; + d_check_vts_lemma_lc = false; + d_small_const = NodeManager::currentNM()->mkNode( MULT, d_small_const, d_small_const ); + d_small_const = Rewriter::rewrite( d_small_const ); + //heuristic for now, until we know how to do nested quantification + Node delta = d_quantEngine->getTermUtil()->getVtsDelta( true, false ); + if( !delta.isNull() ){ + Trace("quant-vts-debug") << "Delta lemma for " << d_small_const << std::endl; + Node delta_lem_ub = NodeManager::currentNM()->mkNode( LT, delta, d_small_const ); + d_quantEngine->getOutputChannel().lemma( delta_lem_ub ); + } + std::vector< Node > inf; + d_quantEngine->getTermUtil()->getVtsTerms( inf, true, false, false ); + for( unsigned i=0; imkNode( GT, inf[i], NodeManager::currentNM()->mkConst( Rational(1)/d_small_const.getConst() ) ); + d_quantEngine->getOutputChannel().lemma( inf_lem_lb ); + } + } + } +} + +bool InstStrategyCegqi::doAddInstantiation( std::vector< Node >& subs ) { + Assert( !d_curr_quant.isNull() ); + //if doing partial quantifier elimination, record the instantiation and set the incomplete flag instead of sending instantiation lemma + if( d_quantEngine->getQuantAttributes()->isQuantElimPartial( d_curr_quant ) ){ + d_cbqi_set_quant_inactive = true; + d_incomplete_check = true; + d_quantEngine->getInstantiate()->recordInstantiation( + d_curr_quant, subs, false, false); + return true; + }else{ + //check if we need virtual term substitution (if used delta or infinity) + bool used_vts = d_quantEngine->getTermUtil()->containsVtsTerm( subs, false ); + if (d_quantEngine->getInstantiate()->addInstantiation( + d_curr_quant, subs, false, false, used_vts)) + { + ++(d_quantEngine->d_statistics.d_instantiations_cbqi); + //d_added_inst.insert( d_curr_quant ); + return true; + }else{ + //this should never happen for monotonic selection strategies + Trace("cbqi-warn") << "WARNING: Existing instantiation" << std::endl; + return false; + } + } +} + +bool InstStrategyCegqi::addLemma( Node lem ) { + return d_quantEngine->addLemma( lem ); +} + +bool InstStrategyCegqi::isEligibleForInstantiation( Node n ) { + if( n.getKind()==INST_CONSTANT || n.getKind()==SKOLEM ){ + if( n.getAttribute(VirtualTermSkolemAttribute()) ){ + // virtual terms are allowed + return true; + }else{ + TypeNode tn = n.getType(); + if( tn.isSort() ){ + QuantEPR * qepr = d_quantEngine->getQuantEPR(); + if( qepr!=NULL ){ + //legal if in the finite set of constants of type tn + if( qepr->isEPRConstant( tn, n ) ){ + return true; + } + } + } + //only legal if current quantified formula contains n + return TermUtil::containsTerm( d_curr_quant, n ); + } + }else{ + return true; + } +} + +CegInstantiator * InstStrategyCegqi::getInstantiator( Node q ) { + std::map< Node, CegInstantiator * >::iterator it = d_cinst.find( q ); + if( it==d_cinst.end() ){ + CegInstantiator * cinst = new CegInstantiator( d_quantEngine, d_out, true, true ); + d_cinst[q] = cinst; + return cinst; + }else{ + return it->second; + } +} + +void InstStrategyCegqi::registerQuantifier( Node q ) { + if( doCbqi( q ) ){ + // get the instantiator + if( options::cbqiPreRegInst() ){ + getInstantiator( q ); + } + // register the cbqi lemma + if( registerCbqiLemma( q ) ){ + Trace("cbqi") << "Registered cbqi lemma for quantifier : " << q << std::endl; + } + } +} + +void InstStrategyCegqi::registerCounterexampleLemma( Node q, Node lem ) { + //must register with the instantiator + //must explicitly remove ITEs so that we record dependencies + std::vector< Node > ce_vars; + for( unsigned i=0; igetTermUtil()->getNumInstantiationConstants( q ); i++ ){ + ce_vars.push_back( d_quantEngine->getTermUtil()->getInstantiationConstant( q, i ) ); + } + std::vector< Node > lems; + lems.push_back( lem ); + CegInstantiator * cinst = getInstantiator( q ); + cinst->registerCounterexampleLemma( lems, ce_vars ); + for( unsigned i=0; iaddLemma( lems[i], false ); + } +} + +void InstStrategyCegqi::presolve() { + if( options::cbqiPreRegInst() ){ + for( std::map< Node, CegInstantiator * >::iterator it = d_cinst.begin(); it != d_cinst.end(); ++it ){ + Trace("cbqi-presolve") << "Presolve " << it->first << std::endl; + it->second->presolve( it->first ); + } + } +} + diff --git a/src/theory/quantifiers/cegqi/inst_strategy_cbqi.h b/src/theory/quantifiers/cegqi/inst_strategy_cbqi.h new file mode 100644 index 000000000..3443d2c4d --- /dev/null +++ b/src/theory/quantifiers/cegqi/inst_strategy_cbqi.h @@ -0,0 +1,166 @@ +/********************* */ +/*! \file inst_strategy_cbqi.h + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds, Morgan Deters, 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 quantifier instantiation + **/ + + +#include "cvc4_private.h" + +#ifndef __CVC4__INST_STRATEGY_CBQI_H +#define __CVC4__INST_STRATEGY_CBQI_H + +#include "theory/arith/arithvar.h" +#include "theory/quantifiers/cegqi/ceg_instantiator.h" +#include "theory/quantifiers/ematching/instantiation_engine.h" +#include "util/statistics_registry.h" + +namespace CVC4 { +namespace theory { + +namespace arith { + class TheoryArith; +} + +namespace quantifiers { + +class InstStrategyCbqi : public QuantifiersModule { + typedef context::CDHashSet NodeSet; + typedef context::CDHashMap< Node, int, NodeHashFunction> NodeIntMap; + + protected: + bool d_cbqi_set_quant_inactive; + bool d_incomplete_check; + /** whether we have added cbqi lemma */ + NodeSet d_added_cbqi_lemma; + /** whether we have added cbqi lemma */ + NodeSet d_elim_quants; + /** parent guards */ + std::map< Node, std::vector< Node > > d_parent_quant; + std::map< Node, std::vector< Node > > d_children_quant; + std::map< Node, bool > d_active_quant; + /** whether we have instantiated quantified formulas */ + //NodeSet d_added_inst; + /** whether to do cbqi for this quantified formula 0 : no, 2 : yes, 1 : yes but not exclusively, -1 : heuristically */ + std::map< Node, int > d_do_cbqi; + /** register ce lemma */ + bool registerCbqiLemma( Node q ); + virtual void registerCounterexampleLemma( Node q, Node lem ); + /** has added cbqi lemma */ + bool hasAddedCbqiLemma( Node q ) { return d_added_cbqi_lemma.find( q )!=d_added_cbqi_lemma.end(); } + /** helper functions */ + int hasNonCbqiVariable( Node q ); + bool hasNonCbqiOperator( Node n, std::map< Node, bool >& visited ); + int isCbqiSort( TypeNode tn, std::map< TypeNode, int >& visited ); + /** get next decision request with dependency checking */ + Node getNextDecisionRequestProc( Node q, std::map< Node, bool >& proc ); + /** process functions */ + virtual void processResetInstantiationRound( Theory::Effort effort ) = 0; + virtual void process( Node q, Theory::Effort effort, int e ) = 0; + + protected: + //for identification + uint64_t d_qid_count; + //nested qe map + std::map< Node, Node > d_nested_qe; + //mark ids on quantifiers + Node getIdMarkedQuantNode( Node n, std::map< Node, Node >& visited ); + // id to ce quant + std::map< Node, Node > d_id_to_ce_quant; + std::map< Node, Node > d_ce_quant_to_id; + //do nested quantifier elimination recursive + Node doNestedQENode( Node q, Node ceq, Node n, std::vector< Node >& inst_terms, bool doVts ); + Node doNestedQERec( Node q, Node n, std::map< Node, Node >& visited, std::vector< Node >& inst_terms, bool doVts ); + //elimination information (for delayed elimination) + class NestedQEInfo { + public: + NestedQEInfo() : d_doVts(false){} + ~NestedQEInfo(){} + Node d_q; + std::vector< Node > d_inst_terms; + bool d_doVts; + }; + std::map< Node, NestedQEInfo > d_nested_qe_info; + NodeIntMap d_nested_qe_waitlist_size; + NodeIntMap d_nested_qe_waitlist_proc; + std::map< Node, std::vector< Node > > d_nested_qe_waitlist; + + public: + //do nested quantifier elimination + Node doNestedQE( Node q, std::vector< Node >& inst_terms, Node lem, bool doVts ); + + public: + InstStrategyCbqi( QuantifiersEngine * qe ); + + /** whether to do CBQI for quantifier q */ + bool doCbqi( Node q ); + /** process functions */ + bool needsCheck( Theory::Effort e ); + QEffort needsModel(Theory::Effort e); + void reset_round( Theory::Effort e ); + void check(Theory::Effort e, QEffort quant_e); + bool checkComplete(); + bool checkCompleteFor( Node q ); + void preRegisterQuantifier( Node q ); + void registerQuantifier( Node q ); + /** get next decision request */ + Node getNextDecisionRequest( unsigned& priority ); +}; + +//generalized counterexample guided quantifier instantiation + +class InstStrategyCegqi; + +class CegqiOutputInstStrategy : public CegqiOutput { +public: + CegqiOutputInstStrategy( InstStrategyCegqi * out ) : d_out( out ){} + InstStrategyCegqi * d_out; + bool doAddInstantiation( std::vector< Node >& subs ); + bool isEligibleForInstantiation( Node n ); + bool addLemma( Node lem ); +}; + +class InstStrategyCegqi : public InstStrategyCbqi { + protected: + CegqiOutputInstStrategy * d_out; + std::map< Node, CegInstantiator * > d_cinst; + Node d_small_const; + Node d_curr_quant; + bool d_check_vts_lemma_lc; + /** process functions */ + void processResetInstantiationRound(Theory::Effort effort) override; + void process(Node f, Theory::Effort effort, int e) override; + /** register ce lemma */ + void registerCounterexampleLemma(Node q, Node lem) override; + + public: + InstStrategyCegqi( QuantifiersEngine * qe ); + ~InstStrategyCegqi() override; + + bool doAddInstantiation( std::vector< Node >& subs ); + bool isEligibleForInstantiation( Node n ); + bool addLemma( Node lem ); + /** identify */ + std::string identify() const override { return std::string("Cegqi"); } + + //get instantiator for quantifier + CegInstantiator * getInstantiator( Node q ); + //register quantifier + void registerQuantifier(Node q) override; + //presolve + void presolve() override; +}; + +} +} +} + +#endif diff --git a/src/theory/quantifiers/conjecture_generator.cpp b/src/theory/quantifiers/conjecture_generator.cpp index b7d1fe404..142f9beae 100644 --- a/src/theory/quantifiers/conjecture_generator.cpp +++ b/src/theory/quantifiers/conjecture_generator.cpp @@ -20,7 +20,7 @@ #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/term_enumeration.h" #include "theory/quantifiers/term_util.h" -#include "theory/quantifiers/trigger.h" +#include "theory/quantifiers/ematching/trigger.h" #include "theory/theory_engine.h" using namespace CVC4; diff --git a/src/theory/quantifiers/ematching/ho_trigger.cpp b/src/theory/quantifiers/ematching/ho_trigger.cpp new file mode 100644 index 000000000..0e0955119 --- /dev/null +++ b/src/theory/quantifiers/ematching/ho_trigger.cpp @@ -0,0 +1,475 @@ +/********************* */ +/*! \file ho_trigger.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 higher-order trigger class + **/ + +#include + +#include "theory/quantifiers/ematching/ho_trigger.h" +#include "theory/quantifiers/instantiate.h" +#include "theory/quantifiers/term_database.h" +#include "theory/quantifiers/term_util.h" +#include "theory/quantifiers_engine.h" +#include "theory/theory_engine.h" +#include "theory/uf/equality_engine.h" +#include "theory/uf/theory_uf_rewriter.h" +#include "util/hash.h" + +using namespace CVC4::kind; + +namespace CVC4 { +namespace theory { +namespace inst { + +HigherOrderTrigger::HigherOrderTrigger( + QuantifiersEngine* qe, + Node q, + std::vector& nodes, + std::map >& ho_apps) + : Trigger(qe, q, nodes), d_ho_var_apps(ho_apps) +{ + NodeManager* nm = NodeManager::currentNM(); + // process the higher-order variable applications + for (std::pair >& as : d_ho_var_apps) + { + Node n = as.first; + d_ho_var_list.push_back(n); + TypeNode tn = n.getType(); + Assert(tn.isFunction()); + if (Trace.isOn("ho-quant-trigger")) + { + Trace("ho-quant-trigger") << " have " << as.second.size(); + Trace("ho-quant-trigger") << " patterns with variable operator " << n + << ":" << std::endl; + for (unsigned j = 0; j < as.second.size(); j++) + { + Trace("ho-quant-trigger") << " " << as.second[j] << std::endl; + } + } + if (d_ho_var_types.find(tn) == d_ho_var_types.end()) + { + Trace("ho-quant-trigger") << " type " << tn + << " needs higher-order matching." << std::endl; + d_ho_var_types.insert(tn); + } + // make the bound variable lists + d_ho_var_bvl[n] = nm->getBoundVarListForFunctionType(tn); + for (const Node& nc : d_ho_var_bvl[n]) + { + d_ho_var_bvs[n].push_back(nc); + } + } +} + +HigherOrderTrigger::~HigherOrderTrigger() {} +void HigherOrderTrigger::collectHoVarApplyTerms( + Node q, Node& n, std::map >& apps) +{ + std::vector ns; + ns.push_back(n); + collectHoVarApplyTerms(q, ns, apps); + Assert(ns.size() == 1); + n = ns[0]; +} + +void HigherOrderTrigger::collectHoVarApplyTerms( + Node q, std::vector& ns, std::map >& apps) +{ + std::unordered_map visited; + std::unordered_map::iterator it; + // whether the visited node is a child of a HO_APPLY chain + std::unordered_map withinApply; + std::vector visit; + TNode cur; + for (unsigned i = 0, size = ns.size(); i < size; i++) + { + visit.push_back(ns[i]); + withinApply[ns[i]] = false; + do + { + cur = visit.back(); + visit.pop_back(); + + it = visited.find(cur); + if (it == visited.end()) + { + // do not look in nested quantifiers + if (cur.getKind() == FORALL) + { + visited[cur] = cur; + } + else + { + bool curWithinApply = withinApply[cur]; + visited[cur] = Node::null(); + visit.push_back(cur); + for (unsigned j = 0, size = cur.getNumChildren(); j < size; j++) + { + withinApply[cur[j]] = curWithinApply && j == 0; + visit.push_back(cur[j]); + } + } + } + else if (it->second.isNull()) + { + // carry the conversion + Node ret = cur; + bool childChanged = false; + std::vector children; + if (cur.getMetaKind() == kind::metakind::PARAMETERIZED) + { + children.push_back(cur.getOperator()); + } + for (const Node& nc : cur) + { + it = visited.find(nc); + Assert(it != visited.end()); + Assert(!it->second.isNull()); + childChanged = childChanged || nc != it->second; + children.push_back(it->second); + } + if (childChanged) + { + ret = NodeManager::currentNM()->mkNode(cur.getKind(), children); + } + // now, convert and store the application + if (!withinApply[cur]) + { + TNode op; + if (ret.getKind() == kind::APPLY_UF) + { + // could be a fully applied function variable + op = ret.getOperator(); + } + else if (ret.getKind() == kind::HO_APPLY) + { + op = ret; + while (op.getKind() == kind::HO_APPLY) + { + op = op[0]; + } + } + if (!op.isNull()) + { + if (op.getKind() == kind::INST_CONSTANT) + { + Assert(quantifiers::TermUtil::getInstConstAttr(ret) == q); + Trace("ho-quant-trigger-debug") + << "Ho variable apply term : " << ret << " with head " << op + << std::endl; + if (ret.getKind() == kind::APPLY_UF) + { + Node prev = ret; + // for consistency, convert to HO_APPLY if fully applied + ret = uf::TheoryUfRewriter::getHoApplyForApplyUf(ret); + } + apps[op].push_back(ret); + } + } + } + visited[cur] = ret; + } + } while (!visit.empty()); + + // store the conversion + Assert(visited.find(ns[i]) != visited.end()); + ns[i] = visited[ns[i]]; + } +} + +int HigherOrderTrigger::addInstantiations() +{ + // call the base class implementation + int addedFoLemmas = Trigger::addInstantiations(); + // also adds predicate lemms to force app completion + int addedHoLemmas = addHoTypeMatchPredicateLemmas(); + return addedHoLemmas + addedFoLemmas; +} + +bool HigherOrderTrigger::sendInstantiation(InstMatch& m) +{ + if (options::hoMatching()) + { + // get substitution corresponding to m + std::vector vars; + std::vector subs; + quantifiers::TermUtil* tutil = d_quantEngine->getTermUtil(); + for (unsigned i = 0, size = d_quant[0].getNumChildren(); i < size; i++) + { + subs.push_back(m.d_vals[i]); + vars.push_back(tutil->getInstantiationConstant(d_quant, i)); + } + + Trace("ho-unif-debug") << "Run higher-order unification..." << std::endl; + + // get the substituted form of all variable-operator ho application terms + std::map > ho_var_apps_subs; + for (std::pair >& ha : d_ho_var_apps) + { + TNode var = ha.first; + for (unsigned j = 0, size = ha.second.size(); j < size; j++) + { + TNode app = ha.second[j]; + Node sapp = + app.substitute(vars.begin(), vars.end(), subs.begin(), subs.end()); + ho_var_apps_subs[var].push_back(sapp); + Trace("ho-unif-debug") << " app[" << var << "] : " << app << " -> " + << sapp << std::endl; + } + } + + // compute argument vectors for each variable + d_lchildren.clear(); + d_arg_to_arg_rep.clear(); + d_arg_vector.clear(); + EqualityQuery* eq = d_quantEngine->getEqualityQuery(); + for (std::pair >& ha : ho_var_apps_subs) + { + TNode var = ha.first; + unsigned vnum = var.getAttribute(InstVarNumAttribute()); + Node value = m.d_vals[vnum]; + Trace("ho-unif-debug") << " val[" << var << "] = " << value << std::endl; + + Trace("ho-unif-debug2") << "initialize lambda information..." + << std::endl; + // initialize the lambda children + d_lchildren[vnum].push_back(value); + std::map >::iterator ithb = + d_ho_var_bvs.find(var); + Assert(ithb != d_ho_var_bvs.end()); + d_lchildren[vnum].insert( + d_lchildren[vnum].end(), ithb->second.begin(), ithb->second.end()); + + Trace("ho-unif-debug2") << "compute fixed arguments..." << std::endl; + // compute for each argument if it is only applied to a fixed value modulo + // equality + std::map fixed_vals; + for (unsigned i = 0; i < ha.second.size(); i++) + { + std::vector args; + Node f = uf::TheoryUfRewriter::decomposeHoApply(ha.second[i], args); + // Assert( f==value ); + for (unsigned k = 0, size = args.size(); k < size; k++) + { + Node val = args[k]; + std::map::iterator itf = fixed_vals.find(k); + if (itf == fixed_vals.end()) + { + fixed_vals[k] = val; + } + else if (!itf->second.isNull()) + { + if (!eq->areEqual(itf->second, args[k])) + { + if (!d_quantEngine->getTermDatabase()->isEntailed( + itf->second.eqNode(args[k]), true, eq)) + { + fixed_vals[k] = Node::null(); + } + } + } + } + } + if (Trace.isOn("ho-unif-debug")) + { + for (std::map::iterator itf = fixed_vals.begin(); + itf != fixed_vals.end(); + ++itf) + { + Trace("ho-unif-debug") << " arg[" << var << "][" << itf->first + << "] : " << itf->second << std::endl; + } + } + + // now construct argument vectors + Trace("ho-unif-debug2") << "compute argument vectors..." << std::endl; + std::map arg_to_rep; + for (unsigned index = 0, size = ithb->second.size(); index < size; + index++) + { + Node bv_at_index = ithb->second[index]; + std::map::iterator itf = fixed_vals.find(index); + Trace("ho-unif-debug") << " * arg[" << var << "][" << index << "]"; + if (itf != fixed_vals.end()) + { + if (!itf->second.isNull()) + { + Node r = eq->getRepresentative(itf->second); + std::map::iterator itfr = arg_to_rep.find(r); + if (itfr != arg_to_rep.end()) + { + d_arg_to_arg_rep[vnum][index] = itfr->second; + // function applied to equivalent values at multiple arguments, + // can permute variables + d_arg_vector[vnum][itfr->second].push_back(bv_at_index); + Trace("ho-unif-debug") << " = { self } ++ arg[" << var << "][" + << itfr->second << "]" << std::endl; + } + else + { + arg_to_rep[r] = index; + // function applied to single value, can either use variable or + // value at this argument position + d_arg_vector[vnum][index].push_back(bv_at_index); + d_arg_vector[vnum][index].push_back(itf->second); + if (!options::hoMatchingVarArgPriority()) + { + std::reverse(d_arg_vector[vnum][index].begin(), + d_arg_vector[vnum][index].end()); + } + Trace("ho-unif-debug") << " = { self, " << itf->second << " } " + << std::endl; + } + } + else + { + // function is applied to disequal values, can only use variable at + // this argument position + d_arg_vector[vnum][index].push_back(bv_at_index); + Trace("ho-unif-debug") << " = { self } (disequal)" << std::endl; + } + } + else + { + // argument is irrelevant to matching, assume identity variable + d_arg_vector[vnum][index].push_back(bv_at_index); + Trace("ho-unif-debug") << " = { self } (irrelevant)" << std::endl; + } + } + Trace("ho-unif-debug2") << "finished." << std::endl; + } + + return sendInstantiation(m, 0); + } + else + { + // do not run higher-order matching + return d_quantEngine->getInstantiate()->addInstantiation(d_quant, m); + } +} + +// recursion depth limited by number of arguments of higher order variables +// occurring as pattern operators (very small) +bool HigherOrderTrigger::sendInstantiation(InstMatch& m, unsigned var_index) +{ + if (var_index == d_ho_var_list.size()) + { + // we now have an instantiation to try + return d_quantEngine->getInstantiate()->addInstantiation(d_quant, m); + } + else + { + Node var = d_ho_var_list[var_index]; + unsigned vnum = var.getAttribute(InstVarNumAttribute()); + Assert(vnum < m.d_vals.size()); + Node value = m.d_vals[vnum]; + Assert(d_lchildren[vnum][0] == value); + Assert(d_ho_var_bvl.find(var) != d_ho_var_bvl.end()); + + // now, recurse on arguments to enumerate equivalent matching lambda + // expressions + bool ret = + sendInstantiationArg(m, var_index, vnum, 0, d_ho_var_bvl[var], false); + + // reset the value + m.d_vals[vnum] = value; + + return ret; + } +} + +bool HigherOrderTrigger::sendInstantiationArg(InstMatch& m, + unsigned var_index, + unsigned vnum, + unsigned arg_index, + Node lbvl, + bool arg_changed) +{ + if (arg_index == lbvl.getNumChildren()) + { + // construct the lambda + if (arg_changed) + { + Node body = + NodeManager::currentNM()->mkNode(kind::APPLY_UF, d_lchildren[vnum]); + Node lam = NodeManager::currentNM()->mkNode(kind::LAMBDA, lbvl, body); + m.d_vals[vnum] = lam; + Trace("ho-unif-debug2") << " try " << vnum << " -> " << lam << std::endl; + } + return sendInstantiation(m, var_index + 1); + } + else + { + std::map::iterator itr = + d_arg_to_arg_rep[vnum].find(arg_index); + unsigned rindex = + itr != d_arg_to_arg_rep[vnum].end() ? itr->second : arg_index; + std::map >::iterator itv = + d_arg_vector[vnum].find(rindex); + Assert(itv != d_arg_vector[vnum].end()); + Node prev = lbvl[arg_index]; + bool ret = false; + // try each argument in the vector + for (unsigned i = 0, size = itv->second.size(); i < size; i++) + { + bool new_arg_changed = arg_changed || prev != itv->second[i]; + d_lchildren[vnum][arg_index + 1] = itv->second[i]; + if (sendInstantiationArg( + m, var_index, vnum, arg_index + 1, lbvl, new_arg_changed)) + { + ret = true; + break; + } + } + // clean up + d_lchildren[vnum][arg_index + 1] = prev; + return ret; + } +} + +int HigherOrderTrigger::addHoTypeMatchPredicateLemmas() +{ + unsigned numLemmas = 0; + if (!d_ho_var_types.empty()) + { + // this forces expansion of APPLY_UF terms to curried HO_APPLY chains + unsigned size = d_quantEngine->getTermDatabase()->getNumOperators(); + for (unsigned j = 0; j < size; j++) + { + Node f = d_quantEngine->getTermDatabase()->getOperator(j); + if (f.isVar()) + { + TypeNode tn = f.getType(); + if (d_ho_var_types.find(tn) != d_ho_var_types.end()) + { + Node u = d_quantEngine->getTermUtil()->getHoTypeMatchPredicate(tn); + Node au = NodeManager::currentNM()->mkNode(kind::APPLY_UF, u, f); + if (d_quantEngine->addLemma(au)) + { + // this forces f to be a first-class member of the quantifier-free + // equality engine, + // which in turn forces the quantifier-free theory solver to expand + // it to HO_APPLY + Trace("ho-quant") << "Added ho match predicate lemma : " << au + << std::endl; + numLemmas++; + } + } + } + } + } + return numLemmas; +} + +} /* CVC4::theory::inst namespace */ +} /* CVC4::theory namespace */ +} /* CVC4 namespace */ diff --git a/src/theory/quantifiers/ematching/ho_trigger.h b/src/theory/quantifiers/ematching/ho_trigger.h new file mode 100644 index 000000000..41fec5e5f --- /dev/null +++ b/src/theory/quantifiers/ematching/ho_trigger.h @@ -0,0 +1,278 @@ +/********************* */ +/*! \file ho_trigger.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 higher-order trigger class + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__QUANTIFIERS__HO_TRIGGER_H +#define __CVC4__THEORY__QUANTIFIERS__HO_TRIGGER_H + +#include +#include + +#include "expr/node.h" +#include "options/quantifiers_options.h" +#include "theory/quantifiers/inst_match.h" +#include "theory/quantifiers/ematching/trigger.h" + +namespace CVC4 { +namespace theory { +namespace inst { + +class Trigger; + +/** HigherOrder trigger + * + * This extends the trigger class with techniques that post-process + * instantiations, specified by InstMatch objects, according to a variant of + * Huet's algorithm. For details, see Chapter 16 of the Handbook of Automated + * Reasoning (vol. 2), by Gilles Dowek. + * + * The main difference between HigherOrderTrigger and Trigger is the function + * sendInstantiation(...). Recall that this function is called when its + * underlying IMGenerator generates an InstMatch m using E-matching technique. + * We enumerate additional instantiations based on m, when the domain of m + * contains variables of function type. + * + * Examples below (f, x, y are universal variables): + * + * (EX1): (f x y) matches (k 0 1) in standard E-matching with: + * + * f -> k, x -> 0, y -> 1 + * + * This match is extended to four possible solutions by this class: + * + * f -> \ xy. (k x y), x -> 0, y -> 1 + * f -> \ xy. (k 0 y), x -> 0, y -> 1 + * f -> \ xy. (k x 1), x -> 0, y -> 1 + * f -> \ xy. (k 0 1), x -> 0, y -> 1 + * + * + * (EX2): Similarly, (f x y) matches (k 0 0) with possible solutions: + * + * f -> \ xy. (k x x), x -> 0, y -> 0 + * f -> \ xy. (k y x), x -> 0, y -> 0 + * f -> \ xy. (k 0 x), x -> 0, y -> 0 + * f -> \ xy. (k x y), x -> 0, y -> 0 + * f -> \ xy. (k y y), x -> 0, y -> 0 + * f -> \ xy. (k 0 y), x -> 0, y -> 0 + * f -> \ xy. (k x 0), x -> 0, y -> 0 + * f -> \ xy. (k y 0), x -> 0, y -> 0 + * f -> \ xy. (k 0 0), x -> 0, y -> 0 + * + * + * (EX3): (f x y), (f x z) simultaneously match (k 0 1), (k 0 2) with possible + * solutions: + * + * f -> \ xy. (k x y), x -> 0, y -> 1, z -> 2 + * f -> \ xy. (k 0 y), x -> 0, y -> 1, z -> 2 + * + * + * This class enumerates the lists above until one instantiation of that form is + * successfully added via a call to Instantiate::addInstantiation(...) + * + * + * It also implements a way of forcing APPLY_UF to expand to curried HO_APPLY to + * handle a corner case where there are no matchable ground terms + * (addHoTypeMatchPredicateLemmas). + * + */ +class HigherOrderTrigger : public Trigger +{ + friend class Trigger; + + private: + HigherOrderTrigger(QuantifiersEngine* qe, + Node q, + std::vector& nodes, + std::map >& ho_apps); + virtual ~HigherOrderTrigger(); + + public: + /** Collect higher order var apply terms + * + * Collect all top-level HO_APPLY terms in n whose head is a variable x in + * quantified formula q. Append all such terms in apps[x]. + * This method may modify n so that it is in the expected form required for + * higher-order matching, in particular, APPLY_UF terms with variable + * operators are converted to curried applications of HO_APPLY. + */ + static void collectHoVarApplyTerms(Node q, + Node& n, + std::map >& apps); + /** Collect higher order var apply terms + * + * Same as above, but with multiple terms ns. + */ + static void collectHoVarApplyTerms(Node q, + std::vector& ns, + std::map >& apps); + /** add all available instantiations, based on the current context + * + * Extends Trigger::addInstantiations to also send + * lemmas based on addHoTypeMatchPredicateLemmas. + */ + virtual int addInstantiations() override; + + protected: + /** + * Map from function-typed variables to their applications in the quantified + * formula d_f (member of Trigger). + */ + std::map > d_ho_var_apps; + /** + * List of all function-typed variables that occur as the head of function + * applications in d_f. + */ + std::vector d_ho_var_list; + /** + * Cached bound variables and bound variable list for each variable of + * function type. These are used for constructing lambda terms in + * instantiations. + */ + std::map > d_ho_var_bvs; + std::map d_ho_var_bvl; + /** the set of types of ho variables */ + std::unordered_set d_ho_var_types; + /** add higher-order type predicate lemmas + * + * Adds lemmas of the form P( f ), where P is the predicate + * returned by TermUtil::getHoTypeMatchPredicate( f.getType() ). + * These lemmas force certain functions f of type tn to appear as + * first-class terms in the quantifier-free UF solver, where recall a + * first-class term is one that occurs as an (external) term in its equality + * engine. These functions f are all operators that have at least one + * term f(t1...tn) indexed by TermDabatase and are such that + * f's type is the same as a function-typed variable we + * are considering in this class (in d_ho_var_apps). + * + * TODO: we may eliminate this based on how github issue #1115 is resolved. + */ + int addHoTypeMatchPredicateLemmas(); + /** send instantiation + * + * Sends an instantiation that is equivalent to m via + * Instantiate::addInstantiation(...). This method may modify m based on + * imitations and projections (Huet's algorithm), if m was generated by + * matching ground terms to function applications with variable heads. + * See examples (EX1)-(EX3) above. + */ + bool sendInstantiation(InstMatch& m) override; + + private: + //-------------------- current information about the match + /** + * Map from variable position to the arguments of the lambda we generated + * for that variable. + * + * For example (EX4), take a quantified formula: + * forall x f1 y f2. (...) + * Say we generated the match: + * x -> 0 + * f1 -> k1 + * y -> 1 + * f2 -> k2 + * z -> 0 + * where we matched + * (f1 x y) with (k1 0 1) and + * (f2 x z) with (k2 0 0) + * Then the algorithm implemented by this class may modify the match to: + * x -> 0 + * f1 -> (\ x1 x2. (k1 x1 1)) + * y -> 1 + * f2 -> (\ x1 x2. (k2 0 x1)) + * z -> 0 + * + * In the above (modified) match, the contents of d_lchildren are: + * 1 -> { k1, x1, 1 } + * 3 -> { k2, 0, x1 } + */ + std::map > d_lchildren; + /** map from variable position to the representative variable position. + * Used when two argument positions of a match are mapped to equal terms. + * + * In the above example (EX4), the first and second arguments of + * the match for f2 are equal. Thus, in the above example, + * we have that d_arg_to_arg_rep is: + * 1 -> { 0 -> 0, 1 -> 1 } + * 3 -> { 0 -> 0, 1 -> 0 } + * In other words, the first argument + */ + std::map > d_arg_to_arg_rep; + /** map from representative argument positions to the equivalence class + * of argument positions. In the above example (EX4), d_arg_vector is: + * 1 -> { 0 -> { 0 }, 1 -> { 1 } } + * 3 -> { 0 -> { 0, 1 } } + */ + std::map > > d_arg_vector; + //-------------------- end current information about the match + + /** higher-order pattern unification algorithm + * + * Sends an instantiation that is equivalent to m via + * d_quantEngine->addInstantiation(...), + * based on Huet's algorithm. + * + * This is a helper function of sendInstantiation( m ) above. + * + * var_index is the index of the variable in m that we are currently processing + * i.e. we are processing the var_index^{th} higher-order variable. + * + * For example, say we are processing the match from (EX4) above. + * when var_index = 0,1, we are processing possibilities for + * instantiation of f1,f2 respectively. + */ + bool sendInstantiation(InstMatch& m, unsigned var_index); + /** higher-order pattern unification algorithm + * Sends an instantiation that is equivalent to m via + * d_quantEngine->addInstantiation(...). + * This is a helper function of sendInstantiation( m, var_index ) above. + * + * var_index is the index of the variable in m that we are currently + * processing + * i.e. we are processing the var_index^{th} higher-order variable. + * vnum maps var_index to the actual variable number in m + * arg_index is the argument of the lambda term we are currently considering + * lbvl is the bound variable list associated with the term in m we are + * currently modifying + * arg_changed is whether we have modified m. + * + * For example, say we have modified our match from (EX4) to: + * x -> 0 + * f1 -> (\ x1 x2. (k1 x1 1)) + * y -> 1 + * f2 -> (\ x1 x2. (k2 0 ?)) + * z -> 0 + * That is, we are currently considering possibilities for the second + * argument of the body for f2. + * Then: + * var_index = 1, + * vnum = 3 (f2 is the 3^rd variable of our quantified formula) + * arg_index = 1, + * lbvl is d_ho_var_bvl[f2], and + * arg_changed is true, since we modified at least one previous + * argument of f1 or f2. + */ + bool sendInstantiationArg(InstMatch& m, + unsigned var_index, + unsigned vnum, + unsigned arg_index, + Node lbvl, + bool arg_changed); +}; + +} /* CVC4::theory::inst namespace */ +} /* CVC4::theory namespace */ +} /* CVC4 namespace */ + +#endif /* __CVC4__THEORY__QUANTIFIERS__HO_TRIGGER_H */ diff --git a/src/theory/quantifiers/ematching/inst_match_generator.cpp b/src/theory/quantifiers/ematching/inst_match_generator.cpp new file mode 100644 index 000000000..c36597e3e --- /dev/null +++ b/src/theory/quantifiers/ematching/inst_match_generator.cpp @@ -0,0 +1,1138 @@ +/********************* */ +/*! \file inst_match_generator.cpp + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds, Morgan Deters, 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 + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ +#include "theory/quantifiers/ematching/inst_match_generator.h" + +#include "expr/datatype.h" +#include "options/datatypes_options.h" +#include "options/quantifiers_options.h" +#include "theory/quantifiers/candidate_generator.h" +#include "theory/quantifiers/instantiate.h" +#include "theory/quantifiers/term_database.h" +#include "theory/quantifiers/term_util.h" +#include "theory/quantifiers/ematching/trigger.h" +#include "theory/quantifiers_engine.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; + +namespace CVC4 { +namespace theory { +namespace inst { + +bool IMGenerator::sendInstantiation(Trigger* tparent, InstMatch& m) +{ + return tparent->sendInstantiation(m); +} + +InstMatchGenerator::InstMatchGenerator( Node pat ){ + d_cg = NULL; + d_needsReset = true; + d_active_add = true; + Assert( quantifiers::TermUtil::hasInstConstAttr(pat) ); + d_pattern = pat; + d_match_pattern = pat; + d_match_pattern_type = pat.getType(); + d_next = NULL; + d_independent_gen = false; +} + +InstMatchGenerator::InstMatchGenerator() { + d_cg = NULL; + d_needsReset = true; + d_active_add = true; + d_next = NULL; + d_independent_gen = false; +} + +InstMatchGenerator::~InstMatchGenerator() +{ + for( unsigned i=0; isetActiveAdd(val); + } +} + +int InstMatchGenerator::getActiveScore( QuantifiersEngine * qe ) { + if( d_match_pattern.isNull() ){ + return -1; + }else if( Trigger::isAtomicTrigger( d_match_pattern ) ){ + Node f = qe->getTermDatabase()->getMatchOperator( d_match_pattern ); + unsigned ngt = qe->getTermDatabase()->getNumGroundTerms( f ); + Trace("trigger-active-sel-debug") << "Number of ground terms for " << f << " is " << ngt << std::endl; + return ngt; + }else if( d_match_pattern.getKind()==INST_CONSTANT ){ + TypeNode tn = d_match_pattern.getType(); + unsigned ngtt = qe->getTermDatabase()->getNumTypeGroundTerms( tn ); + Trace("trigger-active-sel-debug") << "Number of ground terms for " << tn << " is " << ngtt << std::endl; + return ngtt; +// }else if( d_match_pattern_getKind()==EQUAL ){ + + }else{ + return -1; + } +} + +void InstMatchGenerator::initialize( Node q, QuantifiersEngine* qe, std::vector< InstMatchGenerator * > & gens ){ + if( !d_pattern.isNull() ){ + Trace("inst-match-gen") << "Initialize, pattern term is " << d_pattern << std::endl; + if( d_match_pattern.getKind()==NOT ){ + //we want to add the children of the NOT + d_match_pattern = d_pattern[0]; + } + if( d_match_pattern.getKind()==EQUAL || d_match_pattern.getKind()==GEQ ){ + //make sure the matching portion of the equality is on the LHS of d_pattern + // and record what d_match_pattern is + for( unsigned i=0; i<2; i++ ){ + if( !quantifiers::TermUtil::hasInstConstAttr(d_match_pattern[i]) || d_match_pattern[i].getKind()==INST_CONSTANT ){ + Node mp = d_match_pattern[1-i]; + Node mpo = d_match_pattern[i]; + if( mp.getKind()!=INST_CONSTANT ){ + if( i==0 ){ + if( d_match_pattern.getKind()==GEQ ){ + d_pattern = NodeManager::currentNM()->mkNode( kind::GT, mp, mpo ); + d_pattern = d_pattern.negate(); + }else{ + d_pattern = NodeManager::currentNM()->mkNode( d_match_pattern.getKind(), mp, mpo ); + } + } + d_eq_class_rel = mpo; + d_match_pattern = mp; + } + break; + } + } + }else if( d_match_pattern.getKind()==APPLY_SELECTOR_TOTAL && d_match_pattern[0].getKind()==INST_CONSTANT && + options::purifyDtTriggers() && !options::dtSharedSelectors() ){ + d_match_pattern = d_match_pattern[0]; + } + d_match_pattern_type = d_match_pattern.getType(); + Trace("inst-match-gen") << "Pattern is " << d_pattern << ", match pattern is " << d_match_pattern << std::endl; + d_match_pattern_op = qe->getTermDatabase()->getMatchOperator( d_match_pattern ); + + //now, collect children of d_match_pattern + if (d_match_pattern.getKind() == INST_CONSTANT) + { + d_children_types.push_back( + d_match_pattern.getAttribute(InstVarNumAttribute())); + } + else + { + for (unsigned i = 0, size = d_match_pattern.getNumChildren(); i < size; + i++) + { + Node qa = quantifiers::TermUtil::getInstConstAttr(d_match_pattern[i]); + if (!qa.isNull()) + { + InstMatchGenerator* cimg = + getInstMatchGenerator(q, d_match_pattern[i]); + if (cimg) + { + d_children.push_back(cimg); + d_children_index.push_back(i); + d_children_types.push_back(-2); + }else{ + if (d_match_pattern[i].getKind() == INST_CONSTANT && qa == q) + { + d_children_types.push_back( + d_match_pattern[i].getAttribute(InstVarNumAttribute())); + } + else + { + d_children_types.push_back(-1); + } + } + } + else + { + d_children_types.push_back(-1); + } + } + } + + //create candidate generator + if( Trigger::isAtomicTrigger( d_match_pattern ) ){ + //we will be scanning lists trying to find d_match_pattern.getOperator() + d_cg = new inst::CandidateGeneratorQE( qe, d_match_pattern ); + //if matching on disequality, inform the candidate generator not to match on eqc + if( d_pattern.getKind()==NOT && d_pattern[0].getKind()==EQUAL ){ + ((inst::CandidateGeneratorQE*)d_cg)->excludeEqc( d_eq_class_rel ); + d_eq_class_rel = Node::null(); + } + }else if( d_match_pattern.getKind()==INST_CONSTANT ){ + if( d_pattern.getKind()==APPLY_SELECTOR_TOTAL ){ + Expr selectorExpr = qe->getTermDatabase()->getMatchOperator( d_pattern ).toExpr(); + size_t selectorIndex = Datatype::cindexOf(selectorExpr); + const Datatype& dt = Datatype::datatypeOf(selectorExpr); + const DatatypeConstructor& c = dt[selectorIndex]; + Node cOp = Node::fromExpr(c.getConstructor()); + Trace("inst-match-gen") << "Purify dt trigger " << d_pattern << ", will match terms of op " << cOp << std::endl; + d_cg = new inst::CandidateGeneratorQE( qe, cOp ); + }else{ + d_cg = new CandidateGeneratorQEAll( qe, d_match_pattern ); + } + }else if( d_match_pattern.getKind()==EQUAL && + d_match_pattern[0].getKind()==INST_CONSTANT && d_match_pattern[1].getKind()==INST_CONSTANT ){ + //we will be producing candidates via literal matching heuristics + if( d_pattern.getKind()!=NOT ){ + //candidates will be all equalities + d_cg = new inst::CandidateGeneratorQELitEq( qe, d_match_pattern ); + }else{ + //candidates will be all disequalities + d_cg = new inst::CandidateGeneratorQELitDeq( qe, d_match_pattern ); + } + }else{ + Trace("inst-match-gen-warn") << "(?) Unknown matching pattern is " << d_match_pattern << std::endl; + } + } + gens.insert( gens.end(), d_children.begin(), d_children.end() ); +} + +/** get match (not modulo equality) */ +int InstMatchGenerator::getMatch( + Node f, Node t, InstMatch& m, QuantifiersEngine* qe, Trigger* tparent) +{ + Trace("matching") << "Matching " << t << " against pattern " << d_match_pattern << " (" + << m << ")" << ", " << d_children.size() << ", pattern is " << d_pattern << std::endl; + Assert( !d_match_pattern.isNull() ); + if (d_cg == nullptr) + { + Trace("matching-fail") << "Internal error for match generator." << std::endl; + return -2; + }else{ + EqualityQuery* q = qe->getEqualityQuery(); + bool success = true; + //save previous match + //InstMatch prev( &m ); + std::vector< int > prev; + //if t is null + Assert( !t.isNull() ); + Assert( !quantifiers::TermUtil::hasInstConstAttr(t) ); + Assert( d_match_pattern.getKind()==INST_CONSTANT || t.getKind()==d_match_pattern.getKind() ); + Assert( !Trigger::isAtomicTrigger( d_match_pattern ) || t.getOperator()==d_match_pattern.getOperator() ); + //first, check if ground arguments are not equal, or a match is in conflict + Trace("matching-debug2") << "Setting immediate matches..." << std::endl; + for (unsigned i = 0, size = d_match_pattern.getNumChildren(); i < size; i++) + { + int ct = d_children_types[i]; + if (ct >= 0) + { + Trace("matching-debug2") << "Setting " << ct << " to " << t[i] << "..." + << std::endl; + bool addToPrev = m.get(ct).isNull(); + if (!m.set(q, ct, t[i])) + { + //match is in conflict + Trace("matching-fail") << "Match fail: " << m.get(ct) << " and " + << t[i] << std::endl; + success = false; + break; + }else if( addToPrev ){ + Trace("matching-debug2") << "Success." << std::endl; + prev.push_back(ct); + } + } + else if (ct == -1) + { + if( !q->areEqual( d_match_pattern[i], t[i] ) ){ + Trace("matching-fail") << "Match fail arg: " << d_match_pattern[i] << " and " << t[i] << std::endl; + //ground arguments are not equal + success = false; + break; + } + } + } + Trace("matching-debug2") << "Done setting immediate matches, success = " << success << "." << std::endl; + //for variable matching + if( d_match_pattern.getKind()==INST_CONSTANT ){ + bool addToPrev = m.get(d_children_types[0]).isNull(); + if (!m.set(q, d_children_types[0], t)) + { + success = false; + }else{ + if( addToPrev ){ + prev.push_back(d_children_types[0]); + } + } + //for relational matching + }else if( !d_eq_class_rel.isNull() && d_eq_class_rel.getKind()==INST_CONSTANT ){ + int v = d_eq_class_rel.getAttribute(InstVarNumAttribute()); + //also must fit match to equivalence class + bool pol = d_pattern.getKind()!=NOT; + Node pat = d_pattern.getKind()==NOT ? d_pattern[0] : d_pattern; + Node t_match; + if( pol ){ + if( pat.getKind()==GT ){ + t_match = NodeManager::currentNM()->mkNode(MINUS, t, qe->getTermUtil()->d_one); + }else{ + t_match = t; + } + }else{ + if( pat.getKind()==EQUAL ){ + if( t.getType().isBoolean() ){ + t_match = NodeManager::currentNM()->mkConst( !q->areEqual( qe->getTermUtil()->d_true, t ) ); + }else{ + Assert( t.getType().isReal() ); + t_match = NodeManager::currentNM()->mkNode(PLUS, t, qe->getTermUtil()->d_one); + } + }else if( pat.getKind()==GEQ ){ + t_match = NodeManager::currentNM()->mkNode(PLUS, t, qe->getTermUtil()->d_one); + }else if( pat.getKind()==GT ){ + t_match = t; + } + } + if( !t_match.isNull() ){ + bool addToPrev = m.get( v ).isNull(); + if (!m.set(q, v, t_match)) + { + success = false; + }else if( addToPrev ){ + prev.push_back( v ); + } + } + } + int ret_val = -1; + if( success ){ + Trace("matching-debug2") << "Reset children..." << std::endl; + //now, fit children into match + //we will be requesting candidates for matching terms for each child + for (unsigned i = 0, size = d_children.size(); i < size; i++) + { + if( !d_children[i]->reset( t[ d_children_index[i] ], qe ) ){ + success = false; + break; + } + } + if( success ){ + Trace("matching-debug2") << "Continue next " << d_next << std::endl; + ret_val = continueNextMatch(f, m, qe, tparent); + } + } + if( ret_val<0 ){ + for (int& pv : prev) + { + m.d_vals[pv] = Node::null(); + } + } + return ret_val; + } +} + +int InstMatchGenerator::continueNextMatch(Node f, + InstMatch& m, + QuantifiersEngine* qe, + Trigger* tparent) +{ + if( d_next!=NULL ){ + return d_next->getNextMatch(f, m, qe, tparent); + }else{ + if( d_active_add ){ + return sendInstantiation(tparent, m) ? 1 : -1; + }else{ + return 1; + } + } +} + +/** reset instantiation round */ +void InstMatchGenerator::resetInstantiationRound( QuantifiersEngine* qe ){ + if( !d_match_pattern.isNull() ){ + Trace("matching-debug2") << this << " reset instantiation round." << std::endl; + d_needsReset = true; + if( d_cg ){ + d_cg->resetInstantiationRound(); + } + } + if( d_next ){ + d_next->resetInstantiationRound( qe ); + } + d_curr_exclude_match.clear(); +} + +bool InstMatchGenerator::reset( Node eqc, QuantifiersEngine* qe ){ + eqc = qe->getEqualityQuery()->getRepresentative( eqc ); + Trace("matching-debug2") << this << " reset " << eqc << "." << std::endl; + if( !d_eq_class_rel.isNull() && d_eq_class_rel.getKind()!=INST_CONSTANT ){ + d_eq_class = d_eq_class_rel; + }else if( !eqc.isNull() ){ + d_eq_class = eqc; + } + //we have a specific equivalence class in mind + //we are producing matches for f(E) ~ t, where E is a non-ground vector of terms, and t is a ground term + //just look in equivalence class of the RHS + d_cg->reset( d_eq_class ); + d_needsReset = false; + + //generate the first candidate preemptively + d_curr_first_candidate = Node::null(); + Node t; + do { + t = d_cg->getNextCandidate(); + if( d_curr_exclude_match.find( t )==d_curr_exclude_match.end() ){ + d_curr_first_candidate = t; + } + }while( !t.isNull() && d_curr_first_candidate.isNull() ); + Trace("matching-summary") << "Reset " << d_match_pattern << " in " << eqc << " returns " << !d_curr_first_candidate.isNull() << "." << std::endl; + + return !d_curr_first_candidate.isNull(); +} + +int InstMatchGenerator::getNextMatch(Node f, + InstMatch& m, + QuantifiersEngine* qe, + Trigger* tparent) +{ + if( d_needsReset ){ + Trace("matching") << "Reset not done yet, must do the reset..." << std::endl; + reset( d_eq_class, qe ); + } + d_curr_matched = Node::null(); + Trace("matching") << this << " " << d_match_pattern << " get next match " << m << " in eq class " << d_eq_class << std::endl; + int success = -1; + Node t = d_curr_first_candidate; + do{ + Trace("matching-debug2") << "Matching candidate : " << t << std::endl; + //if t not null, try to fit it into match m + if( !t.isNull() ){ + if( d_curr_exclude_match.find( t )==d_curr_exclude_match.end() ){ + Assert( t.getType().isComparableTo( d_match_pattern_type ) ); + Trace("matching-summary") << "Try " << d_match_pattern << " : " << t << std::endl; + success = getMatch(f, t, m, qe, tparent); + if( d_independent_gen && success<0 ){ + Assert( d_eq_class.isNull() ); + d_curr_exclude_match[t] = true; + } + } + //get the next candidate term t + if( success<0 ){ + t = d_cg->getNextCandidate(); + }else{ + d_curr_first_candidate = d_cg->getNextCandidate(); + } + } + }while( success<0 && !t.isNull() ); + d_curr_matched = t; + if( success<0 ){ + Trace("matching-summary") << "..." << d_match_pattern << " failed, reset." << std::endl; + Trace("matching") << this << " failed, reset " << d_eq_class << std::endl; + //we failed, must reset + reset( d_eq_class, qe ); + }else{ + Trace("matching-summary") << "..." << d_match_pattern << " success." << std::endl; + } + return success; +} + +int InstMatchGenerator::addInstantiations(Node f, + QuantifiersEngine* qe, + Trigger* tparent) +{ + //try to add instantiation for each match produced + int addedLemmas = 0; + InstMatch m( f ); + while (getNextMatch(f, m, qe, tparent) > 0) + { + if( !d_active_add ){ + if (sendInstantiation(tparent, m)) + { + addedLemmas++; + if( qe->inConflict() ){ + break; + } + } + }else{ + addedLemmas++; + if( qe->inConflict() ){ + break; + } + } + m.clear(); + } + //return number of lemmas added + return addedLemmas; +} + + +InstMatchGenerator* InstMatchGenerator::mkInstMatchGenerator( Node q, Node pat, QuantifiersEngine* qe ) { + std::vector< Node > pats; + pats.push_back( pat ); + std::map< Node, InstMatchGenerator * > pat_map_init; + return mkInstMatchGenerator( q, pats, qe, pat_map_init ); +} + +InstMatchGenerator* InstMatchGenerator::mkInstMatchGeneratorMulti( Node q, std::vector< Node >& pats, QuantifiersEngine* qe ) { + Assert( pats.size()>1 ); + InstMatchGeneratorMultiLinear * imgm = new InstMatchGeneratorMultiLinear( q, pats, qe ); + std::vector< InstMatchGenerator* > gens; + imgm->initialize(q, qe, gens); + Assert( gens.size()==pats.size() ); + std::vector< Node > patsn; + std::map< Node, InstMatchGenerator * > pat_map_init; + for( unsigned i=0; id_match_pattern; + patsn.push_back( pn ); + pat_map_init[pn] = gens[i]; + } + //return mkInstMatchGenerator( q, patsn, qe, pat_map_init ); + imgm->d_next = mkInstMatchGenerator( q, patsn, qe, pat_map_init ); + return imgm; +} + +InstMatchGenerator* InstMatchGenerator::mkInstMatchGenerator( Node q, std::vector< Node >& pats, QuantifiersEngine* qe, + std::map< Node, InstMatchGenerator * >& pat_map_init ) { + size_t pCounter = 0; + InstMatchGenerator* prev = NULL; + InstMatchGenerator* oinit = NULL; + while( pCounter gens; + InstMatchGenerator* init; + std::map< Node, InstMatchGenerator * >::iterator iti = pat_map_init.find( pats[pCounter] ); + if( iti==pat_map_init.end() ){ + init = new InstMatchGenerator(pats[pCounter]); + }else{ + init = iti->second; + } + if(pCounter==0){ + oinit = init; + } + gens.push_back(init); + //chain the resulting match generators together + while (counterd_next = curr; + } + curr->initialize(q, qe, gens); + prev = curr; + counter++; + } + pCounter++; + } + return oinit; +} + +InstMatchGenerator* InstMatchGenerator::getInstMatchGenerator(Node q, Node n) +{ + if (n.getKind() == INST_CONSTANT) + { + return NULL; + } + Trace("var-trigger-debug") << "Is " << n << " a variable trigger?" + << std::endl; + if (Trigger::isBooleanTermTrigger(n)) + { + VarMatchGeneratorBooleanTerm* vmg = + new VarMatchGeneratorBooleanTerm(n[0], n[1]); + Trace("var-trigger") << "Boolean term trigger : " << n << ", var = " << n[0] + << std::endl; + return vmg; + } + Node x; + if (options::purifyTriggers()) + { + x = Trigger::getInversionVariable(n); + } + if (!x.isNull()) + { + Node s = Trigger::getInversion(n, x); + VarMatchGeneratorTermSubs* vmg = new VarMatchGeneratorTermSubs(x, s); + Trace("var-trigger") << "Term substitution trigger : " << n + << ", var = " << x << ", subs = " << s << std::endl; + return vmg; + } + return new InstMatchGenerator(n); +} + +VarMatchGeneratorBooleanTerm::VarMatchGeneratorBooleanTerm( Node var, Node comp ) : + InstMatchGenerator(), d_comp( comp ), d_rm_prev( false ) { + d_children_types.push_back(var.getAttribute(InstVarNumAttribute())); +} + +int VarMatchGeneratorBooleanTerm::getNextMatch(Node q, + InstMatch& m, + QuantifiersEngine* qe, + Trigger* tparent) +{ + int ret_val = -1; + if( !d_eq_class.isNull() ){ + Node s = NodeManager::currentNM()->mkConst(qe->getEqualityQuery()->areEqual( d_eq_class, d_pattern )); + d_eq_class = Node::null(); + d_rm_prev = m.get(d_children_types[0]).isNull(); + if (!m.set(qe->getEqualityQuery(), d_children_types[0], s)) + { + return -1; + }else{ + ret_val = continueNextMatch(q, m, qe, tparent); + if( ret_val>0 ){ + return ret_val; + } + } + } + if( d_rm_prev ){ + m.d_vals[d_children_types[0]] = Node::null(); + d_rm_prev = false; + } + return ret_val; +} + +VarMatchGeneratorTermSubs::VarMatchGeneratorTermSubs( Node var, Node subs ) : + InstMatchGenerator(), d_var( var ), d_subs( subs ), d_rm_prev( false ){ + d_children_types.push_back(d_var.getAttribute(InstVarNumAttribute())); + d_var_type = d_var.getType(); +} + +int VarMatchGeneratorTermSubs::getNextMatch(Node q, + InstMatch& m, + QuantifiersEngine* qe, + Trigger* tparent) +{ + int ret_val = -1; + if( !d_eq_class.isNull() ){ + Trace("var-trigger-matching") << "Matching " << d_eq_class << " against " << d_var << " in " << d_subs << std::endl; + Node s = d_subs.substitute( d_var, d_eq_class ); + s = Rewriter::rewrite( s ); + Trace("var-trigger-matching") << "...got " << s << ", " << s.getKind() << std::endl; + d_eq_class = Node::null(); + //if( s.getType().isSubtypeOf( d_var_type ) ){ + d_rm_prev = m.get(d_children_types[0]).isNull(); + if (!m.set(qe->getEqualityQuery(), d_children_types[0], s)) + { + return -1; + }else{ + ret_val = continueNextMatch(q, m, qe, tparent); + if( ret_val>0 ){ + return ret_val; + } + } + } + if( d_rm_prev ){ + m.d_vals[d_children_types[0]] = Node::null(); + d_rm_prev = false; + } + return -1; +} + +InstMatchGeneratorMultiLinear::InstMatchGeneratorMultiLinear( Node q, std::vector< Node >& pats, QuantifiersEngine* qe ) { + //order patterns to maximize eager matching failures + std::map< Node, std::vector< Node > > var_contains; + qe->getTermUtil()->getVarContains( q, pats, var_contains ); + std::map< Node, std::vector< Node > > var_to_node; + for( std::map< Node, std::vector< Node > >::iterator it = var_contains.begin(); it != var_contains.end(); ++it ){ + for( unsigned i=0; isecond.size(); i++ ){ + var_to_node[ it->second[i] ].push_back( it->first ); + } + } + std::vector< Node > pats_ordered; + std::vector< bool > pats_ordered_independent; + std::map< Node, bool > var_bound; + while( pats_ordered.size()1 ){ + score_2++; + } + } + if( score_index==-1 || score_1>score_max_1 || ( score_1==score_max_1 && score_2>score_max_2 ) ){ + score_index = i; + score_max_1 = score_1; + score_max_2 = score_2; + } + } + } + //update the variable bounds + Node mp = pats[score_index]; + for( unsigned i=0; isetIndependent(); + } + } +} + +int InstMatchGeneratorMultiLinear::resetChildren( QuantifiersEngine* qe ){ + for( unsigned i=0; ireset( Node::null(), qe ) ){ + return -2; + } + } + return 1; +} + +bool InstMatchGeneratorMultiLinear::reset( Node eqc, QuantifiersEngine* qe ) { + Assert( eqc.isNull() ); + if( options::multiTriggerLinear() ){ + return true; + }else{ + return resetChildren( qe )>0; + } +} + +int InstMatchGeneratorMultiLinear::getNextMatch(Node q, + InstMatch& m, + QuantifiersEngine* qe, + Trigger* tparent) +{ + Trace("multi-trigger-linear-debug") << "InstMatchGeneratorMultiLinear::getNextMatch : reset " << std::endl; + if( options::multiTriggerLinear() ){ + //reset everyone + int rc_ret = resetChildren( qe ); + if( rc_ret<0 ){ + return rc_ret; + } + } + Trace("multi-trigger-linear-debug") << "InstMatchGeneratorMultiLinear::getNextMatch : continue match " << std::endl; + Assert( d_next!=NULL ); + int ret_val = continueNextMatch(q, m, qe, tparent); + if( ret_val>0 ){ + Trace("multi-trigger-linear") << "Successful multi-trigger instantiation." << std::endl; + if( options::multiTriggerLinear() ){ + // now, restrict everyone + for( unsigned i=0; igetCurrentMatch(); + Trace("multi-trigger-linear") << " child " << i << " match : " << mi << std::endl; + d_children[i]->excludeMatch( mi ); + } + } + } + return ret_val; +} + + +/** constructors */ +InstMatchGeneratorMulti::InstMatchGeneratorMulti(Node q, + std::vector& pats, + QuantifiersEngine* qe) + : d_quant(q) +{ + Trace("multi-trigger-cache") << "Making smart multi-trigger for " << q << std::endl; + std::map< Node, std::vector< Node > > var_contains; + qe->getTermUtil()->getVarContains( q, pats, var_contains ); + //convert to indicies + for( std::map< Node, std::vector< Node > >::iterator it = var_contains.begin(); it != var_contains.end(); ++it ){ + Trace("multi-trigger-cache") << "Pattern " << it->first << " contains: "; + for( int i=0; i<(int)it->second.size(); i++ ){ + Trace("multi-trigger-cache") << it->second[i] << " "; + int index = it->second[i].getAttribute(InstVarNumAttribute()); + d_var_contains[ it->first ].push_back( index ); + d_var_to_node[ index ].push_back( it->first ); + } + Trace("multi-trigger-cache") << std::endl; + } + for( unsigned i=0; isetActiveAdd(false); + d_children.push_back(img); + //compute unique/shared variables + std::vector< int > unique_vars; + std::map< int, bool > shared_vars; + int numSharedVars = 0; + for( unsigned j=0; j vars; + unsigned index = i==0 ? pats.size()-1 : (i-1); + while( numSharedVars>0 && index!=i ){ + for( std::map< int, bool >::iterator it = shared_vars.begin(); it != shared_vars.end(); ++it ){ + if( it->second ){ + if( std::find( d_var_contains[ pats[index] ].begin(), d_var_contains[ pats[index] ].end(), it->first )!= + d_var_contains[ pats[index] ].end() ){ + vars.push_back( it->first ); + shared_vars[ it->first ] = false; + numSharedVars--; + } + } + } + index = index==0 ? (int)(pats.size()-1) : (index-1); + } + vars.insert( vars.end(), unique_vars.begin(), unique_vars.end() ); + Trace("multi-trigger-cache") << " Index[" << i << "]: "; + for( unsigned j=0; jd_order.insert( d_imtio[i]->d_order.begin(), vars.begin(), vars.end() ); + d_children_trie.push_back( InstMatchTrieOrdered( d_imtio[i] ) ); + } +} + +InstMatchGeneratorMulti::~InstMatchGeneratorMulti() +{ + for( unsigned i=0; i::iterator it = d_imtio.begin(); it != d_imtio.end(); ++it ){ + delete it->second; + } +} + +/** reset instantiation round (call this whenever equivalence classes have changed) */ +void InstMatchGeneratorMulti::resetInstantiationRound( QuantifiersEngine* qe ){ + for( unsigned i=0; iresetInstantiationRound( qe ); + } +} + +/** reset, eqc is the equivalence class to search in (any if eqc=null) */ +bool InstMatchGeneratorMulti::reset( Node eqc, QuantifiersEngine* qe ){ + for( unsigned i=0; ireset( eqc, qe ) ){ + //return false; + } + } + return true; +} + +int InstMatchGeneratorMulti::addInstantiations(Node q, + QuantifiersEngine* qe, + Trigger* tparent) +{ + int addedLemmas = 0; + Trace("multi-trigger-cache") << "Process smart multi trigger" << std::endl; + for( unsigned i=0; i newMatches; + InstMatch m( q ); + while (d_children[i]->getNextMatch(q, m, qe, tparent) > 0) + { + //m.makeRepresentative( qe ); + newMatches.push_back( InstMatch( &m ) ); + m.clear(); + } + Trace("multi-trigger-cache") << "Made " << newMatches.size() << " new matches for index " << i << std::endl; + for( unsigned j=0; jinConflict() ); + if( childIndex==endChildIndex ){ + // m is an instantiation + if (sendInstantiation(tparent, m)) + { + addedLemmas++; + Trace("multi-trigger-cache-debug") << "-> Produced instantiation " << m + << std::endl; + } + }else if( trieIndex<(int)d_children_trie[childIndex].getOrdering()->d_order.size() ){ + int curr_index = d_children_trie[childIndex].getOrdering()->d_order[trieIndex]; + // Node curr_ic = qe->getTermUtil()->getInstantiationConstant( d_quant, + // curr_index ); + Node n = m.get( curr_index ); + if( n.isNull() ){ + // add to InstMatch + for (std::pair& d : tr->d_data) + { + InstMatch mn(&m); + mn.setValue(curr_index, d.first); + processNewInstantiations(qe, + tparent, + mn, + addedLemmas, + &(d.second), + trieIndex + 1, + childIndex, + endChildIndex, + modEq); + if (qe->inConflict()) + { + break; + } + } + } + // shared and set variable, try to merge + std::map::iterator it = tr->d_data.find(n); + if (it != tr->d_data.end()) + { + processNewInstantiations(qe, + tparent, + m, + addedLemmas, + &(it->second), + trieIndex + 1, + childIndex, + endChildIndex, + modEq); + } + if (modEq) + { + // check modulo equality for other possible instantiations + if (qe->getEqualityQuery()->getEngine()->hasTerm(n)) + { + eq::EqClassIterator eqc( + qe->getEqualityQuery()->getEngine()->getRepresentative(n), + qe->getEqualityQuery()->getEngine()); + while (!eqc.isFinished()) + { + Node en = (*eqc); + if (en != n) + { + std::map::iterator itc = tr->d_data.find(en); + if (itc != tr->d_data.end()) + { + processNewInstantiations(qe, + tparent, + m, + addedLemmas, + &(itc->second), + trieIndex + 1, + childIndex, + endChildIndex, + modEq); + if (qe->inConflict()) + { + break; + } + } + } + ++eqc; + } + } + } + }else{ + int newChildIndex = (childIndex+1)%(int)d_children.size(); + processNewInstantiations(qe, + tparent, + m, + addedLemmas, + d_children_trie[newChildIndex].getTrie(), + 0, + newChildIndex, + endChildIndex, + modEq); + } +} + +InstMatchGeneratorSimple::InstMatchGeneratorSimple(Node q, + Node pat, + QuantifiersEngine* qe) + : d_quant(q), d_match_pattern(pat) +{ + if( d_match_pattern.getKind()==NOT ){ + d_match_pattern = d_match_pattern[0]; + d_pol = false; + }else{ + d_pol = true; + } + if( d_match_pattern.getKind()==EQUAL ){ + d_eqc = d_match_pattern[1]; + d_match_pattern = d_match_pattern[0]; + Assert( !quantifiers::TermUtil::hasInstConstAttr( d_eqc ) ); + } + Assert( Trigger::isSimpleTrigger( d_match_pattern ) ); + for( unsigned i=0; igetTermDatabase()->getMatchOperator( d_match_pattern ); +} + +void InstMatchGeneratorSimple::resetInstantiationRound( QuantifiersEngine* qe ) { + +} +int InstMatchGeneratorSimple::addInstantiations(Node q, + QuantifiersEngine* qe, + Trigger* tparent) +{ + int addedLemmas = 0; + quantifiers::TermArgTrie* tat; + if( d_eqc.isNull() ){ + tat = qe->getTermDatabase()->getTermArgTrie( d_op ); + }else{ + if( d_pol ){ + tat = qe->getTermDatabase()->getTermArgTrie( d_eqc, d_op ); + }else{ + Node r = qe->getEqualityQuery()->getRepresentative( d_eqc ); + //iterate over all classes except r + tat = qe->getTermDatabase()->getTermArgTrie( Node::null(), d_op ); + if( tat ){ + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = tat->d_data.begin(); it != tat->d_data.end(); ++it ){ + if( it->first!=r ){ + InstMatch m( q ); + addInstantiations( m, qe, addedLemmas, 0, &(it->second) ); + if( qe->inConflict() ){ + break; + } + } + } + tat = NULL; + } + } + } + Debug("simple-trigger-debug") << "Adding instantiations based on " << tat << " from " << d_op << " " << d_eqc << std::endl; + if( tat ){ + InstMatch m( q ); + addInstantiations( m, qe, addedLemmas, 0, tat ); + } + return addedLemmas; +} + +void InstMatchGeneratorSimple::addInstantiations(InstMatch& m, + QuantifiersEngine* qe, + int& addedLemmas, + unsigned argIndex, + quantifiers::TermArgTrie* tat) +{ + Debug("simple-trigger-debug") << "Add inst " << argIndex << " " << d_match_pattern << std::endl; + if (argIndex == d_match_pattern.getNumChildren()) + { + Assert( !tat->d_data.empty() ); + TNode t = tat->getNodeData(); + Debug("simple-trigger") << "Actual term is " << t << std::endl; + //convert to actual used terms + for( std::map< int, int >::iterator it = d_var_num.begin(); it != d_var_num.end(); ++it ){ + if( it->second>=0 ){ + Debug("simple-trigger") << "...set " << it->second << " " << t[it->first] << std::endl; + m.setValue( it->second, t[it->first] ); + } + } + // we do not need the trigger parent for simple triggers (no post-processing + // required) + if (qe->getInstantiate()->addInstantiation(d_quant, m)) + { + addedLemmas++; + Debug("simple-trigger") << "-> Produced instantiation " << m << std::endl; + } + }else{ + if( d_match_pattern[argIndex].getKind()==INST_CONSTANT ){ + int v = d_var_num[argIndex]; + if( v!=-1 ){ + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = tat->d_data.begin(); it != tat->d_data.end(); ++it ){ + Node t = it->first; + Node prev = m.get( v ); + //using representatives, just check if equal + Assert( t.getType().isComparableTo( d_match_pattern_arg_types[argIndex] ) ); + if( prev.isNull() || prev==t ){ + m.setValue( v, t); + addInstantiations( m, qe, addedLemmas, argIndex+1, &(it->second) ); + m.setValue( v, prev); + if( qe->inConflict() ){ + break; + } + } + } + return; + } + //inst constant from another quantified formula, treat as ground term TODO: remove this? + } + Node r = qe->getEqualityQuery()->getRepresentative( d_match_pattern[argIndex] ); + std::map< TNode, quantifiers::TermArgTrie >::iterator it = tat->d_data.find( r ); + if( it!=tat->d_data.end() ){ + addInstantiations( m, qe, addedLemmas, argIndex+1, &(it->second) ); + } + } +} + +int InstMatchGeneratorSimple::getActiveScore( QuantifiersEngine * qe ) { + Node f = qe->getTermDatabase()->getMatchOperator( d_match_pattern ); + unsigned ngt = qe->getTermDatabase()->getNumGroundTerms( f ); + Trace("trigger-active-sel-debug") << "Number of ground terms for (simple) " << f << " is " << ngt << std::endl; + return ngt; +} + + +}/* CVC4::theory::inst namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/quantifiers/ematching/inst_match_generator.h b/src/theory/quantifiers/ematching/inst_match_generator.h new file mode 100644 index 000000000..6c38db13b --- /dev/null +++ b/src/theory/quantifiers/ematching/inst_match_generator.h @@ -0,0 +1,694 @@ +/********************* */ +/*! \file inst_match_generator.h + ** \verbatim + ** Top contributors (to current version): + ** Morgan Deters, 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 inst match generator class + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__QUANTIFIERS__INST_MATCH_GENERATOR_H +#define __CVC4__THEORY__QUANTIFIERS__INST_MATCH_GENERATOR_H + +#include +#include "theory/quantifiers/inst_match_trie.h" + +namespace CVC4 { +namespace theory { + +class QuantifiersEngine; +namespace quantifiers{ + class TermArgTrie; +} + +namespace inst { + +class Trigger; + +/** IMGenerator class +* +* Virtual base class for generating InstMatch objects, which correspond to +* quantifier instantiations. The main use of this class is as a backend utility +* to Trigger (see theory/quantifiers/ematching/trigger.h). +* +* Some functions below take as argument a pointer to the current quantifiers +* engine, which is used for making various queries about what terms and +* equalities exist in the current context. +* +* Some functions below take a pointer to a parent Trigger object, which is used +* to post-process and finalize the instantiations through +* sendInstantiation(...), where the parent trigger will make calls to +* qe->getInstantiate()->addInstantiation(...). The latter function is the entry +* point in which instantiate lemmas are enqueued to be sent on the output +* channel. +*/ +class IMGenerator { +public: + virtual ~IMGenerator() {} + /** Reset instantiation round. + * + * Called once at beginning of an instantiation round. + */ + virtual void resetInstantiationRound(QuantifiersEngine* qe) {} + /** Reset. + * + * eqc is the equivalence class to search in (any if eqc=null). + * Returns true if this generator can produce instantiations, and false + * otherwise. An example of when it returns false is if it can be determined + * that no appropriate matchable terms occur based on eqc. + */ + virtual bool reset(Node eqc, QuantifiersEngine* qe) { return true; } + /** Get the next match. + * + * Must call reset( eqc ) before this function. This constructs an + * instantiation, which it populates in data structure m, + * based on the current context using the implemented matching algorithm. + * + * q is the quantified formula we are adding instantiations for + * m is the InstMatch object we are generating + * + * Returns a value >0 if an instantiation was successfully generated + */ + virtual int getNextMatch(Node q, + InstMatch& m, + QuantifiersEngine* qe, + Trigger* tparent) + { + return 0; + } + /** add instantiations + * + * This add all available instantiations for q based on the current context + * using the implemented matching algorithm. It typically is implemented as a + * fixed point of getNextMatch above. + * + * It returns the number of instantiations added using calls to calls to + * Instantiate::addInstantiation(...). + */ + virtual int addInstantiations(Node q, + QuantifiersEngine* qe, + Trigger* tparent) + { + return 0; + } + /** get active score + * + * A heuristic value indicating how active this generator is. + */ + virtual int getActiveScore( QuantifiersEngine * qe ) { return 0; } + protected: + /** send instantiation + * + * This method sends instantiation, specified by m, to the parent trigger + * object, which will in turn make a call to + * Instantiate::addInstantiation(...). This method returns true if a + * call to Instantiate::addInstantiation(...) was successfully made, + * indicating that an instantiation was enqueued in the quantifier engine's + * lemma cache. + */ + bool sendInstantiation(Trigger* tparent, InstMatch& m); +};/* class IMGenerator */ + +class CandidateGenerator; + +/** InstMatchGenerator class +* +* This is the default generator class for non-simple single triggers, that is, +* triggers composed of a single term with nested term applications. +* For example, { f( y, f( x, a ) ) } and { f( g( x ), a ) } are non-simple +* triggers. +* +* Handling non-simple triggers is done by constructing a linked list of +* InstMatchGenerator classes (see mkInstMatchGenerator), where each +* InstMatchGenerator has a "d_next" pointer. If d_next is NULL, +* then this is the end of the InstMatchGenerator and the last +* InstMatchGenerator is responsible for finalizing the instantiation. +* +* For (EX1), for the trigger f( y, f( x, a ) ), we construct the linked list: +* +* [ f( y, f( x, a ) ) ] -> [ f( x, a ) ] -> NULL +* +* In a call to getNextMatch, +* if we match against a ground term f( b, c ), then the first InstMatchGenerator +* in this list binds y to b, and tells the InstMatchGenerator [ f( x, a ) ] to +* match f-applications in the equivalence class of c. +* +* CVC4 employs techniques that ensure that the number of instantiations +* is worst-case polynomial wrt the number of ground terms. +* Consider the axiom/pattern/context (EX2) : +* +* axiom : forall x1 x2 x3 x4. F[ x1...x4 ] +* +* trigger : P( f( x1 ), f( x2 ), f( x3 ), f( x4 ) ) +* +* ground context : ~P( a, a, a, a ), a = f( c_1 ) = ... = f( c_100 ) +* +* If E-matching were applied exhaustively, then x1, x2, x3, x4 would be +* instantiated with all combinations of c_1, ... c_100, giving 100^4 +* instantiations. +* +* Instead, we enforce that at most 1 instantiation is produced for a +* ( pattern, ground term ) pair per round. Meaning, only one instantiation is +* generated when matching P( a, a, a, a ) against the generator +* [P( f( x1 ), f( x2 ), f( x3 ), f( x4 ) )]. For details, see Section 3 of +* Reynolds, Vampire 2016. +* +* To enforce these policies, we use a flag "d_active_add" which dictates the +* behavior of the last element in the linked list. If d_active_add is +* true -> a call to getNextMatch(...) returns 1 only if adding the +* instantiation via a call to IMGenerator::sendInstantiation(...) +* successfully enqueues a lemma via a call to +* Instantiate::addInstantiation(...). This call may fail e.g. if we +* have already added the instantiation, or the instantiation is +* entailed. +* false -> a call to getNextMatch(...) returns 1 whenever an m is +* constructed, where typically the caller would use m. +* This is important since a return value >0 signals that the current matched +* terms should be flushed. Consider the above example (EX1), where +* [ f(y,f(x,a)) ] is being matched against f(b,c), +* [ f(x,a) ] is being matched against f(d,a) where c=f(d,a) +* A successfully added instantiation { x->d, y->b } here signals we should +* not produce further instantiations that match f(y,f(x,a)) with f(b,c). +* +* A number of special cases of triggers are covered by this generator (see +* implementation of initialize), including : +* Literal triggers, e.g. x >= a, ~x = y +* Selector triggers, e.g. head( x ) +* Triggers with invertible subterms, e.g. f( x+1 ) +* Variable triggers, e.g. x +* +* All triggers above can be in the context of an equality, e.g. +* { f( y, f( x, a ) ) = b } is a trigger that matches f( y, f( x, a ) ) to +* ground terms in the equivalence class of b. +* { ~f( y, f( x, a ) ) = b } is a trigger that matches f( y, f( x, a ) ) to any +* ground terms not in the equivalence class of b. +*/ +class InstMatchGenerator : public IMGenerator { + public: + /** destructor */ + ~InstMatchGenerator() override; + + /** Reset instantiation round. */ + void resetInstantiationRound(QuantifiersEngine* qe) override; + /** Reset. */ + bool reset(Node eqc, QuantifiersEngine* qe) override; + /** Get the next match. */ + int getNextMatch(Node q, + InstMatch& m, + QuantifiersEngine* qe, + Trigger* tparent) override; + /** Add instantiations. */ + int addInstantiations(Node q, + QuantifiersEngine* qe, + Trigger* tparent) override; + + /** set active add flag (true by default) + * + * If active add is true, we call sendInstantiation in calls to getNextMatch, + * instead of returning the match. This is necessary so that we can ensure + * policies that are dependent upon knowing when instantiations are + * successfully added to the output channel through + * Instantiate::addInstantiation(...). + */ + void setActiveAdd(bool val); + /** Get active score for this inst match generator + * + * See Trigger::getActiveScore for details. + */ + int getActiveScore(QuantifiersEngine* qe) override; + /** exclude match + * + * Exclude matching d_match_pattern with Node n on subsequent calls to + * getNextMatch. + */ + void excludeMatch(Node n) { d_curr_exclude_match[n] = true; } + /** Get current match. + * Returns the term we are currently matching. + */ + Node getCurrentMatch() { return d_curr_matched; } + /** set that this match generator is independent + * + * A match generator is indepndent when this generator fails to produce a + * match in a call to getNextMatch, the overall matching fails. + */ + void setIndependent() { d_independent_gen = true; } + //-------------------------------construction of inst match generators + /** mkInstMatchGenerator for single triggers, calls the method below */ + static InstMatchGenerator* mkInstMatchGenerator(Node q, + Node pat, + QuantifiersEngine* qe); + /** mkInstMatchGenerator for the multi trigger case + * + * This linked list of InstMatchGenerator classes with one + * InstMatchGeneratorMultiLinear at the head and a list of trailing + * InstMatchGenerators corresponding to each unique subterm of pats with + * free variables. + */ + static InstMatchGenerator* mkInstMatchGeneratorMulti(Node q, + std::vector& pats, + QuantifiersEngine* qe); + /** mkInstMatchGenerator + * + * This generates a linked list of InstMatchGenerators for each unique + * subterm of pats with free variables. + * + * q is the quantified formula associated with the generator we are making + * pats is a set of terms we are making InstMatchGenerator nodes for + * qe is a pointer to the quantifiers engine (used for querying necessary + * information during initialization) pat_map_init maps from terms to + * generators we have already made for them. + * + * It calls initialize(...) for all InstMatchGenerators generated by this call. + */ + static InstMatchGenerator* mkInstMatchGenerator( + Node q, + std::vector& pats, + QuantifiersEngine* qe, + std::map& pat_map_init); + //-------------------------------end construction of inst match generators + + protected: + /** constructors + * + * These are intentionally private, and are called during linked list + * construction in mkInstMatchGenerator. + */ + InstMatchGenerator(Node pat); + InstMatchGenerator(); + /** The pattern we are producing matches for. + * + * This term and d_match_pattern can be different since this + * term may involve information regarding phase and (dis)equality entailment, + * or other special cases of Triggers. + * + * For example, for the trigger for P( x ) = false, which matches P( x ) with + * P( t ) in the equivalence class of false, + * P( x ) = false is d_pattern + * P( x ) is d_match_pattern + * Another example, for pure theory triggers like head( x ), we have + * head( x ) is d_pattern + * x is d_match_pattern + * since head( x ) can match any (List) datatype term x. + * + * If null, this is a multi trigger that is merging matches from d_children, + * which is used in InstMatchGeneratorMulti. + */ + Node d_pattern; + /** The match pattern + * This is the term that is matched against ground terms, + * see examples above. + */ + Node d_match_pattern; + /** The current term we are matching. */ + Node d_curr_matched; + /** do we need to call reset on this generator? */ + bool d_needsReset; + /** candidate generator + * This is the back-end utility for InstMatchGenerator. + * It generates a stream of candidate terms to match against d_match_pattern + * below, dependending upon what kind of term we are matching + * (e.g. a matchable term, a variable, a relation, etc.). + */ + CandidateGenerator* d_cg; + /** children generators + * These match generators correspond to the children of the term + * we are matching with this generator. + * For example [ f( x, a ) ] is a child of [ f( y, f( x, a ) ) ] + * in the example (EX1) above. + */ + std::vector d_children; + /** for each child, the index in the term + * For example [ f( x, a ) ] has index 1 in [ f( y, f( x, a ) ) ] + * in the example (EX1) above, indicating it is the 2nd child + * of the term. + */ + std::vector d_children_index; + /** children types + * + * If d_match_pattern is an instantiation constant, then this is a singleton + * vector containing the variable number of the d_match_pattern itself. + * If d_match_patterm is a term of the form f( t1, ..., tn ), then for each + * index i, d_children[i] stores the type of node ti is, where: + * >= 0 : variable (indicates its number), + * -1 : ground term, + * -2 : child term. + */ + std::vector d_children_types; + /** The next generator in the linked list + * that this generator is a part of. + */ + InstMatchGenerator* d_next; + /** The equivalence class we are currently matching in. */ + Node d_eq_class; + /** If non-null, then this is a relational trigger of the form x ~ + * d_eq_class_rel. */ + Node d_eq_class_rel; + /** Excluded matches + * Stores the terms we are not allowed to match. + * These can for instance be specified by the smt2 + * extended syntax (! ... :no-pattern). + */ + std::map d_curr_exclude_match; + /** Current first candidate + * Used in a key fail-quickly optimization which generates + * the first candidate term to match during reset(). + */ + Node d_curr_first_candidate; + /** Indepdendent generate + * If this flag is true, failures to match should be cached. + */ + bool d_independent_gen; + /** active add flag, described above. */ + bool d_active_add; + /** cached value of d_match_pattern.getType() */ + TypeNode d_match_pattern_type; + /** the match operator for d_match_pattern + * + * See TermDatabase::getMatchOperator for details on match operators. + */ + Node d_match_pattern_op; + /** get the match against ground term or formula t. + * + * d_match_pattern and t should have the same shape, that is, + * their match operator (see TermDatabase::getMatchOperator) is the same. + * only valid for use where !d_match_pattern.isNull(). + */ + int getMatch( + Node q, Node t, InstMatch& m, QuantifiersEngine* qe, Trigger* tparent); + /** Initialize this generator. + * + * q is the quantified formula associated with this generator. + * + * This constructs the appropriate information about what + * patterns we are matching and children generators. + * + * It may construct new (child) generators needed to implement + * the matching algorithm for this term. For each new generator + * constructed in this way, it adds them to gens. + */ + void initialize(Node q, + QuantifiersEngine* qe, + std::vector& gens); + /** Continue next match + * + * This is called during getNextMatch when the current generator in the linked + * list succesfully satisfies its matching constraint. This function either + * calls getNextMatch of the next element in the linked list, or finalizes the + * match (calling sendInstantiation(...) if active add is true, or returning a + * value >0 if active add is false). Its return value has the same semantics + * as getNextMatch. + */ + int continueNextMatch(Node q, + InstMatch& m, + QuantifiersEngine* qe, + Trigger* tparent); + /** Get inst match generator + * + * Gets the InstMatchGenerator that implements the + * appropriate matching algorithm for n within q + * within a linked list of InstMatchGenerators. + */ + static InstMatchGenerator* getInstMatchGenerator(Node q, Node n); +};/* class InstMatchGenerator */ + +/** match generator for Boolean term ITEs +* This handles the special case of triggers that look like ite( x, BV1, BV0 ). +*/ +class VarMatchGeneratorBooleanTerm : public InstMatchGenerator { +public: + VarMatchGeneratorBooleanTerm( Node var, Node comp ); + + /** Reset */ + bool reset(Node eqc, QuantifiersEngine* qe) override + { + d_eq_class = eqc; + return true; + } + /** Get the next match. */ + int getNextMatch(Node q, + InstMatch& m, + QuantifiersEngine* qe, + Trigger* tparent) override; + + private: + /** stores the true branch of the Boolean ITE */ + Node d_comp; + /** stores whether we have written a value for var in the current match. */ + bool d_rm_prev; +}; + +/** match generator for purified terms +* This handles the special case of invertible terms like x+1 (see +* Trigger::getTermInversionVariable). +*/ +class VarMatchGeneratorTermSubs : public InstMatchGenerator { +public: + VarMatchGeneratorTermSubs( Node var, Node subs ); + + /** Reset */ + bool reset(Node eqc, QuantifiersEngine* qe) override + { + d_eq_class = eqc; + return true; + } + /** Get the next match. */ + int getNextMatch(Node q, + InstMatch& m, + QuantifiersEngine* qe, + Trigger* tparent) override; + + private: + /** variable we are matching (x in the example x+1). */ + TNode d_var; + /** cache of d_var.getType() */ + TypeNode d_var_type; + /** The substitution for what we match (x-1 in the example x+1). */ + Node d_subs; + /** stores whether we have written a value for d_var in the current match. */ + bool d_rm_prev; +}; + +/** InstMatchGeneratorMultiLinear class +* +* This is the default implementation of multi-triggers. +* +* Handling multi-triggers using this class is done by constructing a linked +* list of InstMatchGenerator classes (see mkInstMatchGeneratorMulti), with one +* InstMatchGeneratorMultiLinear at the head and a list of trailing +* InstMatchGenerators. +* +* CVC4 employs techniques that ensure that the number of instantiations +* is worst-case polynomial wrt the number of ground terms, where this class +* lifts this policy to multi-triggers. In particular consider +* +* multi-trigger : { f( x1 ), f( x2 ), f( x3 ), f( x4 ) } +* +* For this multi-trigger, we insist that for each i=1...4, and each ground term +* t, there is at most 1 instantiation added as a result of matching +* ( f( x1 ), f( x2 ), f( x3 ), f( x4 ) ) +* against ground terms of the form +* ( s_1, s_2, s_3, s_4 ) where t = s_i for i=1,...,4. +* Meaning we add instantiations for the multi-trigger +* ( f( x1 ), f( x2 ), f( x3 ), f( x4 ) ) based on matching pairwise against: +* ( f( c_i11 ), f( c_i21 ), f( c_i31 ), f( c_i41 ) ) +* ( f( c_i12 ), f( c_i22 ), f( c_i32 ), f( c_i42 ) ) +* ( f( c_i13 ), f( c_i23 ), f( c_i33 ), f( c_i43 ) ) +* Where we require c_i{jk} != c_i{jl} for each i=1...4, k != l. +* +* For example, we disallow adding instantiations based on matching against both +* ( f( c_1 ), f( c_2 ), f( c_4 ), f( c_6 ) ) and +* ( f( c_1 ), f( c_3 ), f( c_5 ), f( c_7 ) ) +* against ( f( x1 ), f( x2 ), f( x3 ), f( x4 ) ), since f( c_1 ) is matched +* against f( x1 ) twice. +* +* This policy can be disabled by --no-multi-trigger-linear. +* +*/ +class InstMatchGeneratorMultiLinear : public InstMatchGenerator { + friend class InstMatchGenerator; + + public: + /** Reset. */ + bool reset(Node eqc, QuantifiersEngine* qe) override; + /** Get the next match. */ + int getNextMatch(Node q, + InstMatch& m, + QuantifiersEngine* qe, + Trigger* tparent) override; + + protected: + /** reset the children of this generator */ + int resetChildren(QuantifiersEngine* qe); + /** constructor */ + InstMatchGeneratorMultiLinear(Node q, + std::vector& pats, + QuantifiersEngine* qe); +};/* class InstMatchGeneratorMulti */ + +/** InstMatchGeneratorMulti +* +* This is an earlier implementation of multi-triggers +* that is enabled by --multi-trigger-cache. +* This generator takes the product of instantiations +* found by single trigger matching, and does +* not have the guarantee that the number of +* instantiations is polynomial wrt the number of +* ground terms. +*/ +class InstMatchGeneratorMulti : public IMGenerator { + public: + /** constructors */ + InstMatchGeneratorMulti(Node q, + std::vector& pats, + QuantifiersEngine* qe); + /** destructor */ + ~InstMatchGeneratorMulti() override; + + /** Reset instantiation round. */ + void resetInstantiationRound(QuantifiersEngine* qe) override; + /** Reset. */ + bool reset(Node eqc, QuantifiersEngine* qe) override; + /** Add instantiations. */ + int addInstantiations(Node q, + QuantifiersEngine* qe, + Trigger* tparent) override; + + private: + /** indexed trie */ + typedef std::pair< std::pair< int, int >, InstMatchTrie* > IndexedTrie; + /** process new match + * + * Called during addInstantiations(...). + * Indicates we produced a match m for child fromChildIndex + * addedLemmas is how many instantiations we succesfully send + * via IMGenerator::sendInstantiation(...) calls. + */ + void processNewMatch(QuantifiersEngine* qe, + Trigger* tparent, + InstMatch& m, + int fromChildIndex, + int& addedLemmas); + /** helper for process new match + * tr is the inst match trie (term index) we are currently traversing. + * trieIndex is depth we are in trie tr. + * childIndex is the index of the term in the multi trigger we are currently + * processing. + * endChildIndex is the index of the term in the multi trigger that generated + * a new term, and hence we will end when the product + * computed by this function returns to. + * modEq is whether we are matching modulo equality. + */ + void processNewInstantiations(QuantifiersEngine* qe, + Trigger* tparent, + InstMatch& m, + int& addedLemmas, + InstMatchTrie* tr, + int trieIndex, + int childIndex, + int endChildIndex, + bool modEq); + /** Map from pattern nodes to indices of variables they contain. */ + std::map< Node, std::vector< int > > d_var_contains; + /** Map from variable indices to pattern nodes that contain them. */ + std::map< int, std::vector< Node > > d_var_to_node; + /** quantified formula we are producing matches for */ + Node d_quant; + /** children generators + * These are inst match generators for each term in the multi trigger. + */ + std::vector< InstMatchGenerator* > d_children; + /** variable orderings + * Stores a heuristically determined variable ordering (unique + * variables first) for each term in the multi trigger. + */ + std::map< unsigned, InstMatchTrie::ImtIndexOrder* > d_imtio; + /** inst match tries for each child node + * This data structure stores all InstMatch objects generated + * by matching for each term in the multi trigger. + */ + std::vector< InstMatchTrieOrdered > d_children_trie; +};/* class InstMatchGeneratorMulti */ + +/** InstMatchGeneratorSimple class +* +* This is the default generator class for simple single triggers. +* For example, { f( x, a ) }, { f( x, x ) } and { f( x, y ) } are simple single +* triggers. In practice, around 70-90% of triggers are simple single triggers. +* +* Notice that simple triggers also can have an attached polarity. +* For example, { P( x ) = false }, { f( x, y ) = a } and { ~f( a, x ) = b } are +* simple single triggers. +* +* The implementation traverses the term indices in TermDatabase for adding +* instantiations, which is more efficient than the techniques required for +* handling non-simple single triggers. +* +* In contrast to other instantiation generators, it does not call +* IMGenerator::sendInstantiation and for performance reasons instead calls +* qe->getInstantiate()->addInstantiation(...) directly. +*/ +class InstMatchGeneratorSimple : public IMGenerator { + public: + /** constructors */ + InstMatchGeneratorSimple(Node q, Node pat, QuantifiersEngine* qe); + + /** Reset instantiation round. */ + void resetInstantiationRound(QuantifiersEngine* qe) override; + /** Add instantiations. */ + int addInstantiations(Node q, + QuantifiersEngine* qe, + Trigger* tparent) override; + /** Get active score. */ + int getActiveScore(QuantifiersEngine* qe) override; + + private: + /** quantified formula for the trigger term */ + Node d_quant; + /** the trigger term */ + Node d_match_pattern; + /** equivalence class polarity information + * + * This stores the required polarity/equivalence class with this trigger. + * If d_eqc is non-null, we only produce matches { x->t } such that + * our context does not entail + * ( d_match_pattern*{ x->t } = d_eqc) if d_pol = true, or + * ( d_match_pattern*{ x->t } != d_eqc) if d_pol = false. + * where * denotes application of substitution. + */ + bool d_pol; + Node d_eqc; + /** Match pattern arg types. + * Cached values of d_match_pattern[i].getType(). + */ + std::vector< TypeNode > d_match_pattern_arg_types; + /** The match operator d_match_pattern (see TermDb::getMatchOperator). */ + Node d_op; + /** Map from child number to variable index. */ + std::map< int, int > d_var_num; + /** add instantiations, helper function. + * + * m is the current match we are building, + * addedLemmas is the number of lemmas we have added via calls to + * qe->getInstantiate()->aaddInstantiation(...), + * argIndex is the argument index in d_match_pattern we are currently + * matching, + * tat is the term index we are currently traversing. + */ + void addInstantiations(InstMatch& m, + QuantifiersEngine* qe, + int& addedLemmas, + unsigned argIndex, + quantifiers::TermArgTrie* tat); +};/* class InstMatchGeneratorSimple */ +} +} +} + +#endif diff --git a/src/theory/quantifiers/ematching/inst_strategy_e_matching.cpp b/src/theory/quantifiers/ematching/inst_strategy_e_matching.cpp new file mode 100644 index 000000000..c2c1425f0 --- /dev/null +++ b/src/theory/quantifiers/ematching/inst_strategy_e_matching.cpp @@ -0,0 +1,613 @@ +/********************* */ +/*! \file inst_strategy_e_matching.cpp + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds, Morgan Deters, 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 Implementation of e matching instantiation strategies + **/ + +#include "theory/quantifiers/ematching/inst_strategy_e_matching.h" +#include "theory/quantifiers/ematching/inst_match_generator.h" +#include "theory/quantifiers/quant_relevance.h" +#include "theory/quantifiers/quantifiers_attributes.h" +#include "theory/quantifiers/term_database.h" +#include "theory/quantifiers/term_util.h" +#include "theory/theory_engine.h" + +using namespace std; + +namespace CVC4 { + +using namespace kind; +using namespace context; + +namespace theory { + +using namespace inst; + +namespace quantifiers { + +//priority levels : +//1 : user patterns (when user-pat!={resort,ignore}), auto-gen patterns (for non-user pattern quantifiers, or when user-pat={resort,ignore}) +//2 : user patterns (when user-pat=resort), auto gen patterns (for user pattern quantifiers when user-pat=use) + +// user-pat=interleave alternates between use and resort + +struct sortQuantifiersForSymbol { + QuantifiersEngine* d_qe; + std::map< Node, Node > d_op_map; + bool operator() (Node i, Node j) { + int nqfsi = d_qe->getQuantifierRelevance()->getNumQuantifiersForSymbol( d_op_map[i] ); + int nqfsj = d_qe->getQuantifierRelevance()->getNumQuantifiersForSymbol( d_op_map[j] ); + if( nqfsinqfsj ){ + return false; + }else{ + return false; + } + } +}; + +struct sortTriggers { + bool operator() (Node i, Node j) { + int wi = Trigger::getTriggerWeight( i ); + int wj = Trigger::getTriggerWeight( j ); + if( wi==wj ){ + return i >::iterator it = d_user_gen.begin(); it != d_user_gen.end(); ++it ){ + for( unsigned i=0; isecond.size(); i++ ){ + it->second[i]->resetInstantiationRound(); + it->second[i]->reset( Node::null() ); + } + } + Trace("inst-alg-debug") << "done reset user triggers" << std::endl; +} + +int InstStrategyUserPatterns::process( Node f, Theory::Effort effort, int e ){ + if( e==0 ){ + return STATUS_UNFINISHED; + }else{ + int peffort = d_quantEngine->getInstUserPatMode()==USER_PAT_MODE_RESORT ? 2 : 1; + if( e User-provided instantiate " << f << "..." << std::endl; + if( d_quantEngine->getInstUserPatMode()==USER_PAT_MODE_RESORT ){ + for( unsigned i=0; idebugPrint("process-trigger"); + Trace("process-trigger") << "..." << std::endl; + int numInst = d_user_gen[f][i]->addInstantiations(); + Trace("process-trigger") << " Done, numInst = " << numInst << "." << std::endl; + d_quantEngine->d_statistics.d_instantiations_user_patterns += numInst; + if( d_user_gen[f][i]->isMultiTrigger() ){ + d_quantEngine->d_statistics.d_multi_trigger_instantiations += numInst; + } + if( d_quantEngine->inConflict() ){ + break; + } + } + } + } + } + return STATUS_UNKNOWN; +} + +void InstStrategyUserPatterns::addUserPattern( Node q, Node pat ){ + Assert( pat.getKind()==INST_PATTERN ); + //add to generators + bool usable = true; + std::vector< Node > nodes; + for( unsigned i=0; igetInstUserPatMode()==USER_PAT_MODE_RESORT ){ + d_user_gen_wait[q].push_back( nodes ); + }else{ + Trigger * t = Trigger::mkTrigger( d_quantEngine, q, nodes, true, Trigger::TR_MAKE_NEW ); + if( t ){ + d_user_gen[q].push_back( t ); + }else{ + Trace("trigger-warn") << "Failed to construct trigger : " << pat << " due to variable mismatch" << std::endl; + } + } + } +} + +InstStrategyAutoGenTriggers::InstStrategyAutoGenTriggers( QuantifiersEngine* qe ) : InstStrategy( qe ){ + //how to select trigger terms + d_tr_strategy = options::triggerSelMode(); + //whether to select new triggers during the search + if( options::incrementTriggers() ){ + d_regenerate_frequency = 3; + d_regenerate = true; + }else{ + d_regenerate_frequency = 1; + d_regenerate = false; + } +} + +void InstStrategyAutoGenTriggers::processResetInstantiationRound( Theory::Effort effort ){ + Trace("inst-alg-debug") << "reset auto-gen triggers" << std::endl; + //reset triggers + for( unsigned r=0; r<2; r++ ){ + for( std::map< Node, std::map< Trigger*, bool > >::iterator it = d_auto_gen_trigger[r].begin(); it != d_auto_gen_trigger[r].end(); ++it ){ + for( std::map< Trigger*, bool >::iterator itt = it->second.begin(); itt != it->second.end(); ++itt ){ + itt->first->resetInstantiationRound(); + itt->first->reset( Node::null() ); + } + } + } + d_processed_trigger.clear(); + Trace("inst-alg-debug") << "done reset auto-gen triggers" << std::endl; +} + +int InstStrategyAutoGenTriggers::process( Node f, Theory::Effort effort, int e ){ + UserPatMode upMode = d_quantEngine->getInstUserPatMode(); + if( hasUserPatterns( f ) && upMode==USER_PAT_MODE_TRUST ){ + return STATUS_UNKNOWN; + }else{ + int peffort = ( hasUserPatterns( f ) && upMode!=USER_PAT_MODE_IGNORE && upMode!=USER_PAT_MODE_RESORT ) ? 2 : 1; + if( e Auto-gen instantiate " << f << "..." << std::endl; + bool gen = false; + if( e==peffort ){ + if( d_counter.find( f )==d_counter.end() ){ + d_counter[f] = 0; + gen = true; + }else{ + d_counter[f]++; + gen = d_regenerate && d_counter[f]%d_regenerate_frequency==0; + } + }else{ + gen = true; + } + if( gen ){ + generateTriggers( f ); + if( d_counter[f]==0 && d_auto_gen_trigger[0][f].empty() && d_auto_gen_trigger[1][f].empty() && f.getNumChildren()==2 ){ + Trace("trigger-warn") << "Could not find trigger for " << f << std::endl; + } + } + + //if( e==4 ){ + // d_processed_trigger.clear(); + // d_quantEngine->getEqualityQuery()->setLiberal( true ); + //} + if( options::triggerActiveSelMode()!=TRIGGER_ACTIVE_SEL_ALL ){ + int max_score = -1; + Trigger * max_trigger = NULL; + for( std::map< Trigger*, bool >::iterator itt = d_auto_gen_trigger[0][f].begin(); itt != d_auto_gen_trigger[0][f].end(); ++itt ){ + int score = itt->first->getActiveScore(); + if( options::triggerActiveSelMode()==TRIGGER_ACTIVE_SEL_MIN ){ + if( score>=0 && ( scorefirst; + } + }else{ + if( score>max_score ){ + max_score = score; + max_trigger = itt->first; + } + } + d_auto_gen_trigger[0][f][itt->first] = false; + } + if( max_trigger!=NULL ){ + d_auto_gen_trigger[0][f][max_trigger] = true; + } + } + + bool hasInst = false; + for( unsigned r=0; r<2; r++ ){ + for( std::map< Trigger*, bool >::iterator itt = d_auto_gen_trigger[r][f].begin(); itt != d_auto_gen_trigger[r][f].end(); ++itt ){ + Trigger* tr = itt->first; + if( tr ){ + bool processTrigger = itt->second; + if( processTrigger && d_processed_trigger[f].find( tr )==d_processed_trigger[f].end() ){ + d_processed_trigger[f][tr] = true; + Trace("process-trigger") << " Process "; + tr->debugPrint("process-trigger"); + Trace("process-trigger") << "..." << std::endl; + int numInst = tr->addInstantiations(); + hasInst = numInst>0 || hasInst; + Trace("process-trigger") << " Done, numInst = " << numInst << "." << std::endl; + d_quantEngine->d_statistics.d_instantiations_auto_gen += numInst; + if( r==1 ){ + d_quantEngine->d_statistics.d_multi_trigger_instantiations += numInst; + } + if( d_quantEngine->inConflict() ){ + break; + } + } + } + } + if( d_quantEngine->inConflict() || ( hasInst && options::multiTriggerPriority() ) ){ + break; + } + } + //if( e==4 ){ + // d_quantEngine->getEqualityQuery()->setLiberal( false ); + //} + return STATUS_UNKNOWN; + } + } +} + +void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ + Trace("auto-gen-trigger-debug") << "Generate triggers for " << f << ", #var=" << f[0].getNumChildren() << "..." << std::endl; + if( d_patTerms[0].find( f )==d_patTerms[0].end() ){ + //determine all possible pattern terms based on trigger term selection strategy d_tr_strategy + d_patTerms[0][f].clear(); + d_patTerms[1][f].clear(); + bool ntrivTriggers = options::relationalTriggers(); + std::vector< Node > patTermsF; + std::map< Node, inst::TriggerTermInfo > tinfo; + //well-defined function: can assume LHS is only trigger + if( options::quantFunWellDefined() ){ + Node hd = QuantAttributes::getFunDefHead( f ); + if( !hd.isNull() ){ + hd = d_quantEngine->getTermUtil() + ->substituteBoundVariablesToInstConstants(hd, f); + patTermsF.push_back( hd ); + tinfo[hd].init( f, hd ); + } + } + //otherwise, use algorithm for collecting pattern terms + if( patTermsF.empty() ){ + Node bd = d_quantEngine->getTermUtil()->getInstConstantBody( f ); + Trigger::collectPatTerms( f, bd, patTermsF, d_tr_strategy, d_user_no_gen[f], tinfo, true ); + if( ntrivTriggers ){ + sortTriggers st; + std::sort( patTermsF.begin(), patTermsF.end(), st ); + } + if( Trace.isOn("auto-gen-trigger-debug") ){ + Trace("auto-gen-trigger-debug") << "Collected pat terms for " << bd << ", no-patterns : " << d_user_no_gen[f].size() << std::endl; + for( unsigned i=0; i vcMap; + std::map< Node, bool > rmPatTermsF; + int last_weight = -1; + for( unsigned i=0; ilast_weight ){ + Trace("auto-gen-trigger-debug") << "...exclude expendible non-trivial trigger : " << patTermsF[i] << std::endl; + rmPatTermsF[patTermsF[i]] = true; + }else{ + last_weight = curr_w; + } + } + d_num_trigger_vars[f] = vcMap.size(); + if( d_num_trigger_vars[f]>0 && d_num_trigger_vars[f] vcs[2]; + for( unsigned i=0; igetTermUtil()->getInstantiationConstant( f, i ); + vcs[ vcMap.find( ic )==vcMap.end() ? 0 : 1 ].push_back( f[0][i] ); + } + for( unsigned i=0; i<2; i++ ){ + d_vc_partition[i][f] = NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, vcs[i] ); + } + }else{ + return; + } + } + for( unsigned i=0; i patTerms; + for( int i=0; i<(int)d_patTerms[r][f].size(); i++ ){ + if( r==1 || d_single_trigger_gen.find( d_patTerms[r][f][i] )==d_single_trigger_gen.end() ){ + patTerms.push_back( d_patTerms[r][f][i] ); + } + } + if( !patTerms.empty() ){ + Trace("auto-gen-trigger") << "Generate trigger for " << f << std::endl; + //sort terms based on relevance + if( options::relevantTriggers() ){ + sortQuantifiersForSymbol sqfs; + sqfs.d_qe = d_quantEngine; + for( unsigned i=0; igetQuantifierRelevance()->getNumQuantifiersForSymbol( d_pat_to_mpat[patTerms[i]].getOperator() ) << ")" << std::endl; + } + } + //now, generate the trigger... + Trigger* tr = NULL; + if( d_is_single_trigger[ patTerms[0] ] ){ + tr = Trigger::mkTrigger( d_quantEngine, f, patTerms[0], false, Trigger::TR_RETURN_NULL, d_num_trigger_vars[f] ); + d_single_trigger_gen[ patTerms[0] ] = true; + }else{ + //only generate multi trigger if option set, or if no single triggers exist + if( !d_patTerms[0][f].empty() ){ + if( options::multiTriggerWhenSingle() ){ + Trace("multi-trigger-debug") << "Resort to choosing multi-triggers..." << std::endl; + }else{ + return; + } + } + //if we are re-generating triggers, shuffle based on some method + if( d_made_multi_trigger[f] ){ + std::random_shuffle( patTerms.begin(), patTerms.end() ); //shuffle randomly + }else{ + d_made_multi_trigger[f] = true; + } + //will possibly want to get an old trigger + tr = Trigger::mkTrigger( d_quantEngine, f, patTerms, false, Trigger::TR_GET_OLD, d_num_trigger_vars[f] ); + } + if( tr ){ + addTrigger( tr, f ); + //if we are generating additional triggers... + if( !tr->isMultiTrigger() ){ + unsigned index = 0; + if( indexgetQuantifierRelevance()->getNumQuantifiersForSymbol( patTerms[0].getOperator() ); + } + index++; + bool success = true; + while( success && indexgetQuantifierRelevance()->getNumQuantifiersForSymbol( patTerms[index].getOperator() )<=nqfs_curr ){ + d_single_trigger_gen[ patTerms[index] ] = true; + Trigger* tr2 = Trigger::mkTrigger( d_quantEngine, f, patTerms[index], false, Trigger::TR_RETURN_NULL, d_num_trigger_vars[f] ); + addTrigger( tr2, f ); + success = true; + } + index++; + } + //Notice() << "done check add additional" << std::endl; + } + } + } + } + } +} + +void InstStrategyAutoGenTriggers::addPatternToPool( Node q, Node pat, unsigned num_fv, Node mpat ) { + d_pat_to_mpat[pat] = mpat; + unsigned num_vars = options::partialTriggers() ? d_num_trigger_vars[q] : q[0].getNumChildren(); + if( num_fv==num_vars && ( options::pureThTriggers() || !Trigger::isPureTheoryTrigger( pat ) ) ){ + d_patTerms[0][q].push_back( pat ); + d_is_single_trigger[ pat ] = true; + }else{ + d_patTerms[1][q].push_back( pat ); + d_is_single_trigger[ pat ] = false; + } +} + + +void InstStrategyAutoGenTriggers::addTrigger( inst::Trigger * tr, Node q ) { + if( tr ){ + if( d_num_trigger_vars[q]getTermUtil()->substituteInstConstantsToBoundVariables( + tr->getInstPattern(), q); + Node ipl = NodeManager::currentNM()->mkNode(INST_PATTERN_LIST, pat); + Node qq = NodeManager::currentNM()->mkNode( FORALL, d_vc_partition[1][q], NodeManager::currentNM()->mkNode( FORALL, d_vc_partition[0][q], q[1] ), ipl ); + Trace("auto-gen-trigger-partial") << "Make partially specified user pattern: " << std::endl; + Trace("auto-gen-trigger-partial") << " " << qq << std::endl; + Node lem = NodeManager::currentNM()->mkNode( OR, q.negate(), qq ); + d_quantEngine->addLemma( lem ); + }else{ + unsigned tindex; + if( tr->isMultiTrigger() ){ + //disable all other multi triggers + for( std::map< Trigger*, bool >::iterator it = d_auto_gen_trigger[1][q].begin(); it != d_auto_gen_trigger[1][q].end(); ++it ){ + d_auto_gen_trigger[1][q][ it->first ] = false; + } + tindex = 1; + }else{ + tindex = 0; + } + //making it during an instantiation round, so must reset + if( d_auto_gen_trigger[tindex][q].find( tr )==d_auto_gen_trigger[tindex][q].end() ){ + tr->resetInstantiationRound(); + tr->reset( Node::null() ); + } + d_auto_gen_trigger[tindex][q][tr] = true; + } + } +} + +bool InstStrategyAutoGenTriggers::hasUserPatterns( Node q ) { + if( q.getNumChildren()==3 ){ + std::map< Node, bool >::iterator it = d_hasUserPatterns.find( q ); + if( it==d_hasUserPatterns.end() ){ + bool hasPat = false; + for( unsigned i=0; isecond; + } + }else{ + return false; + } +} + +void InstStrategyAutoGenTriggers::addUserNoPattern( Node q, Node pat ) { + Assert( pat.getKind()==INST_NO_PATTERN && pat.getNumChildren()==1 ); + if( std::find( d_user_no_gen[q].begin(), d_user_no_gen[q].end(), pat[0] )==d_user_no_gen[q].end() ){ + Trace("user-pat") << "Add user no-pattern: " << pat[0] << " for " << q << std::endl; + d_user_no_gen[q].push_back( pat[0] ); + } +} + +/* TODO? +bool InstStrategyLocalTheoryExt::isLocalTheoryExt( Node f ) { + std::map< Node, bool >::iterator itq = d_quant.find( f ); + if( itq==d_quant.end() ){ + //generate triggers + Node bd = d_quantEngine->getTermUtil()->getInstConstantBody( f ); + std::vector< Node > vars; + std::vector< Node > patTerms; + bool ret = Trigger::isLocalTheoryExt( bd, vars, patTerms ); + if( ret ){ + d_quant[f] = ret; + //add all variables to trigger that don't already occur + for( unsigned i=0; igetTermUtil()->getInstantiationConstant( f, i ); + if( std::find( vars.begin(), vars.end(), x )==vars.end() ){ + patTerms.push_back( x ); + } + } + Trace("local-t-ext") << "Local theory extensions trigger for " << f << " : " << std::endl; + for( unsigned i=0; isecond; + } +} +*/ + +} /* CVC4::theory::quantifiers namespace */ +} /* CVC4::theory namespace */ +} /* CVC4 namespace */ diff --git a/src/theory/quantifiers/ematching/inst_strategy_e_matching.h b/src/theory/quantifiers/ematching/inst_strategy_e_matching.h new file mode 100644 index 000000000..8f11dfedf --- /dev/null +++ b/src/theory/quantifiers/ematching/inst_strategy_e_matching.h @@ -0,0 +1,119 @@ +/********************* */ +/*! \file inst_strategy_e_matching.h + ** \verbatim + ** Top contributors (to current version): + ** Morgan Deters, 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 E matching instantiation strategies + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__INST_STRATEGY_E_MATCHING_H +#define __CVC4__INST_STRATEGY_E_MATCHING_H + +#include "context/context.h" +#include "context/context_mm.h" +#include "theory/quantifiers/ematching/instantiation_engine.h" +#include "theory/quantifiers/ematching/trigger.h" +#include "theory/quantifiers_engine.h" +#include "util/statistics_registry.h" +#include "options/quantifiers_options.h" + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +//instantiation strategies + +class InstStrategyUserPatterns : public InstStrategy{ +private: + /** explicitly provided patterns */ + std::map< Node, std::vector< inst::Trigger* > > d_user_gen; + /** waiting to be generated patterns */ + std::map< Node, std::vector< std::vector< Node > > > d_user_gen_wait; + /** counter for quantifiers */ + std::map< Node, int > d_counter; + /** process functions */ + void processResetInstantiationRound( Theory::Effort effort ); + int process( Node f, Theory::Effort effort, int e ); +public: + InstStrategyUserPatterns( QuantifiersEngine* ie ) : + InstStrategy( ie ){} + ~InstStrategyUserPatterns(){} +public: + /** add pattern */ + void addUserPattern( Node q, Node pat ); + /** get num patterns */ + int getNumUserGenerators( Node q ) { return (int)d_user_gen[q].size(); } + /** get user pattern */ + inst::Trigger* getUserGenerator( Node q, int i ) { return d_user_gen[q][ i ]; } + /** identify */ + std::string identify() const { return std::string("UserPatterns"); } +};/* class InstStrategyUserPatterns */ + +class InstStrategyAutoGenTriggers : public InstStrategy { +public: + enum { + RELEVANCE_NONE, + RELEVANCE_DEFAULT, + }; +private: + /** trigger generation strategy */ + TriggerSelMode d_tr_strategy; + /** regeneration */ + bool d_regenerate; + int d_regenerate_frequency; + /** (single,multi) triggers for each quantifier */ + std::map< Node, std::map< inst::Trigger*, bool > > d_auto_gen_trigger[2]; + std::map< Node, int > d_counter; + /** single, multi triggers for each quantifier */ + std::map< Node, std::vector< Node > > d_patTerms[2]; + std::map< Node, std::map< Node, bool > > d_patReqPol; + /** information about triggers */ + std::map< Node, bool > d_is_single_trigger; + std::map< Node, bool > d_single_trigger_gen; + std::map< Node, bool > d_made_multi_trigger; + //processed trigger this round + std::map< Node, std::map< inst::Trigger*, bool > > d_processed_trigger; + //instantiation no patterns + std::map< Node, std::vector< Node > > d_user_no_gen; + // number of trigger variables per quantifier + std::map< Node, unsigned > d_num_trigger_vars; + std::map< Node, Node > d_vc_partition[2]; + std::map< Node, Node > d_pat_to_mpat; +private: + /** process functions */ + void processResetInstantiationRound( Theory::Effort effort ); + int process( Node q, Theory::Effort effort, int e ); + /** generate triggers */ + void generateTriggers( Node q ); + void addPatternToPool( Node q, Node pat, unsigned num_fv, Node mpat ); + void addTrigger( inst::Trigger * tr, Node f ); + /** has user patterns */ + bool hasUserPatterns( Node q ); + /** has user patterns */ + std::map< Node, bool > d_hasUserPatterns; +public: + InstStrategyAutoGenTriggers( QuantifiersEngine* qe ); + ~InstStrategyAutoGenTriggers(){} +public: + /** get auto-generated trigger */ + inst::Trigger* getAutoGenTrigger( Node q ); + /** identify */ + std::string identify() const { return std::string("AutoGenTriggers"); } + /** add pattern */ + void addUserNoPattern( Node q, Node pat ); +};/* class InstStrategyAutoGenTriggers */ + + +} +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif diff --git a/src/theory/quantifiers/ematching/instantiation_engine.cpp b/src/theory/quantifiers/ematching/instantiation_engine.cpp new file mode 100644 index 000000000..184add8c3 --- /dev/null +++ b/src/theory/quantifiers/ematching/instantiation_engine.cpp @@ -0,0 +1,209 @@ +/********************* */ +/*! \file instantiation_engine.cpp + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds, Morgan Deters, 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 Implementation of instantiation engine class + **/ + +#include "theory/quantifiers/ematching/instantiation_engine.h" + +#include "options/quantifiers_options.h" +#include "theory/quantifiers/first_order_model.h" +#include "theory/quantifiers/ematching/inst_strategy_e_matching.h" +#include "theory/quantifiers/term_database.h" +#include "theory/quantifiers/term_util.h" +#include "theory/quantifiers/ematching/trigger.h" +#include "theory/theory_engine.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; +using namespace CVC4::theory::inst; + +InstantiationEngine::InstantiationEngine(QuantifiersEngine* qe) + : QuantifiersModule(qe), + d_instStrategies(), + d_isup(), + d_i_ag(), + d_quants() { + if (options::eMatching()) { + // these are the instantiation strategies for E-matching + // user-provided patterns + if (options::userPatternsQuant() != USER_PAT_MODE_IGNORE) { + d_isup.reset(new InstStrategyUserPatterns(d_quantEngine)); + d_instStrategies.push_back(d_isup.get()); + } + + // auto-generated patterns + d_i_ag.reset(new InstStrategyAutoGenTriggers(d_quantEngine)); + d_instStrategies.push_back(d_i_ag.get()); + } +} + +InstantiationEngine::~InstantiationEngine() {} + +void InstantiationEngine::presolve() { + for( unsigned i=0; ipresolve(); + } +} + +void InstantiationEngine::doInstantiationRound( Theory::Effort effort ){ + unsigned lastWaiting = d_quantEngine->getNumLemmasWaiting(); + //iterate over an internal effort level e + int e = 0; + int eLimit = effort==Theory::EFFORT_LAST_CALL ? 10 : 2; + bool finished = false; + //while unfinished, try effort level=0,1,2.... + while( !finished && e<=eLimit ){ + Debug("inst-engine") << "IE: Prepare instantiation (" << e << ")." << std::endl; + finished = true; + //instantiate each quantifier + for( unsigned i=0; igetRelevance( q )==-1 ? e - 1 : e; + int e_use = e; + if( e_use>=0 ){ + Trace("inst-engine-debug") << "inst-engine : " << q << std::endl; + //check each instantiation strategy + for( unsigned j=0; jidentify() << " " << e_use << std::endl; + int quantStatus = is->process( q, effort, e_use ); + Trace("inst-engine-debug") << " -> status is " << quantStatus << ", conflict=" << d_quantEngine->inConflict() << std::endl; + if( d_quantEngine->inConflict() ){ + return; + }else if( quantStatus==InstStrategy::STATUS_UNFINISHED ){ + finished = false; + } + } + } + } + //do not consider another level if already added lemma at this level + if( d_quantEngine->getNumLemmasWaiting()>lastWaiting ){ + finished = true; + } + e++; + } +} + +bool InstantiationEngine::needsCheck( Theory::Effort e ){ + return d_quantEngine->getInstWhenNeedsCheck( e ); +} + +void InstantiationEngine::reset_round( Theory::Effort e ){ + //if not, proceed to instantiation round + //reset the instantiation strategies + for( unsigned i=0; iprocessResetInstantiationRound( e ); + } +} + +void InstantiationEngine::check(Theory::Effort e, QEffort quant_e) +{ + CodeTimer codeTimer(d_quantEngine->d_statistics.d_ematching_time); + if (quant_e == QEFFORT_STANDARD) + { + double clSet = 0; + if( Trace.isOn("inst-engine") ){ + clSet = double(clock())/double(CLOCKS_PER_SEC); + Trace("inst-engine") << "---Instantiation Engine Round, effort = " << e << "---" << std::endl; + } + //collect all active quantified formulas belonging to this + bool quantActive = false; + d_quants.clear(); + for( unsigned i=0; igetModel()->getNumAssertedQuantifiers(); i++ ){ + Node q = d_quantEngine->getModel()->getAssertedQuantifier( i, true ); + if( d_quantEngine->hasOwnership( q, this ) && d_quantEngine->getModel()->isQuantifierActive( q ) ){ + quantActive = true; + d_quants.push_back( q ); + } + } + Trace("inst-engine-debug") << "InstEngine: check: # asserted quantifiers " << d_quants.size() << "/"; + Trace("inst-engine-debug") << d_quantEngine->getModel()->getNumAssertedQuantifiers() << " " << quantActive << std::endl; + if( quantActive ){ + unsigned lastWaiting = d_quantEngine->getNumLemmasWaiting(); + doInstantiationRound( e ); + if( d_quantEngine->inConflict() ){ + Assert( d_quantEngine->getNumLemmasWaiting()>lastWaiting ); + Trace("inst-engine") << "Conflict, added lemmas = " << (d_quantEngine->getNumLemmasWaiting()-lastWaiting) << std::endl; + }else if( d_quantEngine->hasAddedLemma() ){ + Trace("inst-engine") << "Added lemmas = " << (d_quantEngine->getNumLemmasWaiting()-lastWaiting) << std::endl; + } + }else{ + d_quants.clear(); + } + if( Trace.isOn("inst-engine") ){ + double clSet2 = double(clock())/double(CLOCKS_PER_SEC); + Trace("inst-engine") << "Finished instantiation engine, time = " << (clSet2-clSet) << std::endl; + } + } +} + +bool InstantiationEngine::checkCompleteFor( Node q ) { + //TODO? + return false; +} + +void InstantiationEngine::preRegisterQuantifier( Node q ) { + if( options::strictTriggers() && q.getNumChildren()==3 ){ + //if strict triggers, take ownership of this quantified formula + bool hasPat = false; + for( unsigned i=0; isetOwner( q, this, 1 ); + } + } +} + +void InstantiationEngine::registerQuantifier( Node f ){ + if( d_quantEngine->hasOwnership( f, this ) ){ + //for( unsigned i=0; iregisterQuantifier( f ); + //} + //take into account user patterns + if( f.getNumChildren()==3 ){ + Node subsPat = + d_quantEngine->getTermUtil()->substituteBoundVariablesToInstConstants( + f[2], f); + //add patterns + for( int i=0; i<(int)subsPat.getNumChildren(); i++ ){ + //Notice() << "Add pattern " << subsPat[i] << " for " << f << std::endl; + if( subsPat[i].getKind()==INST_PATTERN ){ + addUserPattern( f, subsPat[i] ); + }else if( subsPat[i].getKind()==INST_NO_PATTERN ){ + addUserNoPattern( f, subsPat[i] ); + } + } + } + } +} + +void InstantiationEngine::addUserPattern(Node q, Node pat) { + if (d_isup) { + d_isup->addUserPattern(q, pat); + } +} + +void InstantiationEngine::addUserNoPattern(Node q, Node pat) { + if (d_i_ag) { + d_i_ag->addUserNoPattern(q, pat); + } +} diff --git a/src/theory/quantifiers/ematching/instantiation_engine.h b/src/theory/quantifiers/ematching/instantiation_engine.h new file mode 100644 index 000000000..18b5ea19c --- /dev/null +++ b/src/theory/quantifiers/ematching/instantiation_engine.h @@ -0,0 +1,98 @@ +/********************* */ +/*! \file instantiation_engine.h + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds, Morgan Deters, 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 Instantiation Engine classes + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__QUANTIFIERS__INSTANTIATION_ENGINE_H +#define __CVC4__THEORY__QUANTIFIERS__INSTANTIATION_ENGINE_H + +#include + +#include "theory/quantifiers_engine.h" +#include "theory/quantifiers/theory_quantifiers.h" + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +class InstStrategyUserPatterns; +class InstStrategyAutoGenTriggers; +class InstStrategyFreeVariable; + +/** instantiation strategy class */ +class InstStrategy { +public: + enum Status { + STATUS_UNFINISHED, + STATUS_UNKNOWN, + };/* enum Status */ +protected: + /** reference to the instantiation engine */ + QuantifiersEngine* d_quantEngine; +public: + InstStrategy( QuantifiersEngine* qe ) : d_quantEngine( qe ){} + virtual ~InstStrategy(){} + /** presolve */ + virtual void presolve() {} + /** reset instantiation */ + virtual void processResetInstantiationRound( Theory::Effort effort ) = 0; + /** process method, returns a status */ + virtual int process( Node f, Theory::Effort effort, int e ) = 0; + /** identify */ + virtual std::string identify() const { return std::string("Unknown"); } + /** register quantifier */ + //virtual void registerQuantifier( Node q ) {} +};/* class InstStrategy */ + +class InstantiationEngine : public QuantifiersModule { + private: + /** instantiation strategies */ + std::vector d_instStrategies; + /** user-pattern instantiation strategy */ + std::unique_ptr d_isup; + /** auto gen triggers; only kept for destructor cleanup */ + std::unique_ptr d_i_ag; + + typedef context::CDHashMap BoolMap; + /** current processing quantified formulas */ + std::vector d_quants; + + /** is the engine incomplete for this quantifier */ + bool isIncomplete(Node q); + /** do instantiation round */ + void doInstantiationRound(Theory::Effort effort); + + public: + InstantiationEngine(QuantifiersEngine* qe); + ~InstantiationEngine(); + void presolve(); + bool needsCheck(Theory::Effort e); + void reset_round(Theory::Effort e); + void check(Theory::Effort e, QEffort quant_e); + bool checkCompleteFor(Node q); + void preRegisterQuantifier(Node q); + void registerQuantifier(Node q); + Node explain(TNode n) { return Node::null(); } + /** add user pattern */ + void addUserPattern(Node q, Node pat); + void addUserNoPattern(Node q, Node pat); + /** Identify this module */ + std::string identify() const { return "InstEngine"; } +}; /* class InstantiationEngine */ + +}/* CVC4::theory::quantifiers namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__QUANTIFIERS__INSTANTIATION_ENGINE_H */ diff --git a/src/theory/quantifiers/ematching/trigger.cpp b/src/theory/quantifiers/ematching/trigger.cpp new file mode 100644 index 000000000..b73c3368d --- /dev/null +++ b/src/theory/quantifiers/ematching/trigger.cpp @@ -0,0 +1,969 @@ +/********************* */ +/*! \file trigger.cpp + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds, Morgan Deters, 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 Implementation of trigger class + **/ + +#include "theory/quantifiers/ematching/trigger.h" + +#include "theory/arith/arith_msum.h" +#include "theory/quantifiers/candidate_generator.h" +#include "theory/quantifiers/ematching/ho_trigger.h" +#include "theory/quantifiers/ematching/inst_match_generator.h" +#include "theory/quantifiers/instantiate.h" +#include "theory/quantifiers/term_database.h" +#include "theory/quantifiers/term_util.h" +#include "theory/quantifiers_engine.h" +#include "theory/theory_engine.h" +#include "theory/uf/equality_engine.h" +#include "util/hash.h" + +using namespace std; +using namespace CVC4::kind; +using namespace CVC4::context; + +namespace CVC4 { +namespace theory { +namespace inst { + +void TriggerTermInfo::init( Node q, Node n, int reqPol, Node reqPolEq ){ + if( d_fv.empty() ){ + quantifiers::TermUtil::getVarContainsNode( q, n, d_fv ); + } + if( d_reqPol==0 ){ + d_reqPol = reqPol; + d_reqPolEq = reqPolEq; + }else{ + //determined a ground (dis)equality must hold or else q is a tautology? + } + d_weight = Trigger::getTriggerWeight(n); +} + +/** trigger class constructor */ +Trigger::Trigger(QuantifiersEngine* qe, Node q, std::vector& nodes) + : d_quantEngine(qe), d_quant(q) +{ + d_nodes.insert( d_nodes.begin(), nodes.begin(), nodes.end() ); + Trace("trigger") << "Trigger for " << q << ": " << std::endl; + for( unsigned i=0; id_statistics.d_triggers); + }else{ + ++(qe->d_statistics.d_simple_triggers); + } + }else{ + Trace("multi-trigger") << "Trigger for " << q << ": " << std::endl; + for( unsigned i=0; id_statistics.d_multi_triggers); + } + + // Notice() << "Trigger : " << (*this) << " for " << q << std::endl; + Trace("trigger-debug") << "Finished making trigger." << std::endl; +} + +Trigger::~Trigger() { + delete d_mg; +} + +void Trigger::resetInstantiationRound(){ + d_mg->resetInstantiationRound( d_quantEngine ); +} + +void Trigger::reset( Node eqc ){ + d_mg->reset( eqc, d_quantEngine ); +} + +Node Trigger::getInstPattern(){ + return NodeManager::currentNM()->mkNode( INST_PATTERN, d_nodes ); +} + +int Trigger::addInstantiations() +{ + int addedLemmas = d_mg->addInstantiations(d_quant, d_quantEngine, this); + if( addedLemmas>0 ){ + if (Debug.isOn("inst-trigger")) + { + Debug("inst-trigger") << "Added " << addedLemmas + << " lemmas, trigger was "; + for (unsigned i = 0; i < d_nodes.size(); i++) + { + Debug("inst-trigger") << d_nodes[i] << " "; + } + Debug("inst-trigger") << std::endl; + } + } + return addedLemmas; +} + +bool Trigger::sendInstantiation(InstMatch& m) +{ + return d_quantEngine->getInstantiate()->addInstantiation(d_quant, m); +} + +bool Trigger::mkTriggerTerms( Node q, std::vector< Node >& nodes, unsigned n_vars, std::vector< Node >& trNodes ) { + //only take nodes that contribute variables to the trigger when added + std::vector< Node > temp; + temp.insert( temp.begin(), nodes.begin(), nodes.end() ); + std::map< Node, bool > vars; + std::map< Node, std::vector< Node > > patterns; + size_t varCount = 0; + std::map< Node, std::vector< Node > > varContains; + quantifiers::TermUtil::getVarContains( q, temp, varContains ); + for( unsigned i=0; i& nodes, bool keepAll, int trOption, unsigned use_n_vars ){ + std::vector< Node > trNodes; + if( !keepAll ){ + unsigned n_vars = use_n_vars==0 ? f[0].getNumChildren() : use_n_vars; + if( !mkTriggerTerms( f, nodes, n_vars, trNodes ) ){ + return NULL; + } + }else{ + trNodes.insert( trNodes.begin(), nodes.begin(), nodes.end() ); + } + + //check for duplicate? + if( trOption!=TR_MAKE_NEW ){ + Trigger* t = qe->getTriggerDatabase()->getTrigger( trNodes ); + if( t ){ + if( trOption==TR_GET_OLD ){ + //just return old trigger + return t; + }else{ + return NULL; + } + } + } + + // check if higher-order + Trace("trigger-debug") << "Collect higher-order variable triggers..." + << std::endl; + std::map > ho_apps; + HigherOrderTrigger::collectHoVarApplyTerms(f, trNodes, ho_apps); + Trace("trigger") << "...got " << ho_apps.size() + << " higher-order applications." << std::endl; + Trigger* t; + if (!ho_apps.empty()) + { + t = new HigherOrderTrigger(qe, f, trNodes, ho_apps); + } + else + { + t = new Trigger(qe, f, trNodes); + } + + qe->getTriggerDatabase()->addTrigger( trNodes, t ); + return t; +} + +Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, Node n, bool keepAll, int trOption, unsigned use_n_vars ){ + std::vector< Node > nodes; + nodes.push_back( n ); + return mkTrigger( qe, f, nodes, keepAll, trOption, use_n_vars ); +} + +bool Trigger::isUsable( Node n, Node q ){ + if( quantifiers::TermUtil::getInstConstAttr(n)==q ){ + if( isAtomicTrigger( n ) ){ + for( unsigned i=0; i coeffs; + if( isBooleanTermTrigger( n ) ){ + return true; + }else if( options::purifyTriggers() ){ + Node x = getInversionVariable( n ); + if( !x.isNull() ){ + return true; + } + } + } + return false; + }else{ + return true; + } +} + +Node Trigger::getIsUsableEq( Node q, Node n ) { + Assert( isRelationalTrigger( n ) ); + for( unsigned i=0; i<2; i++) { + if( isUsableEqTerms( q, n[i], n[1-i] ) ){ + if( i==1 && n.getKind()==EQUAL && !quantifiers::TermUtil::hasInstConstAttr(n[0]) ){ + return NodeManager::currentNM()->mkNode( n.getKind(), n[1], n[0] ); + }else{ + return n; + } + } + } + return Node::null(); +} + +bool Trigger::isUsableEqTerms( Node q, Node n1, Node n2 ) { + if( n1.getKind()==INST_CONSTANT ){ + if( options::relationalTriggers() ){ + if( !quantifiers::TermUtil::hasInstConstAttr(n2) ){ + return true; + }else if( n2.getKind()==INST_CONSTANT ){ + return true; + } + } + }else if( isUsableAtomicTrigger( n1, q ) ){ + if( options::relationalTriggers() && n2.getKind()==INST_CONSTANT && !quantifiers::TermUtil::containsTerm( n1, n2 ) ){ + return true; + }else if( !quantifiers::TermUtil::hasInstConstAttr(n2) ){ + return true; + } + } + return false; +} + +Node Trigger::getIsUsableTrigger( Node n, Node q ) { + bool pol = true; + Trace("trigger-debug") << "Is " << n << " a usable trigger?" << std::endl; + if( n.getKind()==NOT ){ + pol = !pol; + n = n[0]; + } + if( n.getKind()==INST_CONSTANT ){ + return pol ? n : NodeManager::currentNM()->mkNode( EQUAL, n, NodeManager::currentNM()->mkConst( true ) ).notNode(); + }else if( isRelationalTrigger( n ) ){ + Node rtr = getIsUsableEq( q, n ); + if( rtr.isNull() && n[0].getType().isReal() ){ + //try to solve relation + std::map< Node, Node > m; + if (ArithMSum::getMonomialSumLit(n, m)) + { + for( std::map< Node, Node >::iterator it = m.begin(); it!=m.end(); ++it ){ + bool trySolve = false; + if( !it->first.isNull() ){ + if( it->first.getKind()==INST_CONSTANT ){ + trySolve = options::relationalTriggers(); + }else if( isUsableTrigger( it->first, q ) ){ + trySolve = true; + } + } + if( trySolve ){ + Trace("trigger-debug") << "Try to solve for " << it->first << std::endl; + Node veq; + if (ArithMSum::isolate(it->first, m, veq, n.getKind()) != 0) + { + rtr = getIsUsableEq( q, veq ); + } + //either all solves will succeed or all solves will fail + break; + } + } + } + } + if( !rtr.isNull() ){ + Trace("relational-trigger") << "Relational trigger : " << std::endl; + Trace("relational-trigger") << " " << rtr << " (from " << n << ")" << std::endl; + Trace("relational-trigger") << " in quantifier " << q << std::endl; + Node rtr2 = pol ? rtr : rtr.negate(); + Trace("relational-trigger") << " return : " << rtr2 << std::endl; + return rtr2; + } + }else{ + Trace("trigger-debug") << n << " usable : " << ( quantifiers::TermUtil::getInstConstAttr(n)==q ) << " " << isAtomicTrigger( n ) << " " << isUsable( n, q ) << std::endl; + if( isUsableAtomicTrigger( n, q ) ){ + return pol ? n : NodeManager::currentNM()->mkNode( EQUAL, n, NodeManager::currentNM()->mkConst( true ) ).notNode(); + } + } + return Node::null(); +} + +bool Trigger::isUsableAtomicTrigger( Node n, Node q ) { + return quantifiers::TermUtil::getInstConstAttr( n )==q && isAtomicTrigger( n ) && isUsable( n, q ); +} + +bool Trigger::isUsableTrigger( Node n, Node q ){ + Node nu = getIsUsableTrigger( n, q ); + return !nu.isNull(); +} + +bool Trigger::isAtomicTrigger( Node n ){ + return isAtomicTriggerKind( n.getKind() ); +} + +bool Trigger::isAtomicTriggerKind( Kind k ) { + return k == APPLY_UF || k == SELECT || k == STORE || k == APPLY_CONSTRUCTOR + || k == APPLY_SELECTOR_TOTAL || k == APPLY_TESTER || k == UNION + || k == INTERSECTION || k == SUBSET || k == SETMINUS || k == MEMBER + || k == SINGLETON || k == SEP_PTO || k == BITVECTOR_TO_NAT + || k == INT_TO_BITVECTOR || k == HO_APPLY; +} + +bool Trigger::isRelationalTrigger( Node n ) { + return isRelationalTriggerKind( n.getKind() ); +} + +bool Trigger::isRelationalTriggerKind( Kind k ) { + return k==EQUAL || k==GEQ; +} + +bool Trigger::isCbqiKind( Kind k ) { + if( quantifiers::TermUtil::isBoolConnective( k ) || k==PLUS || k==GEQ || k==EQUAL || k==MULT ){ + return true; + }else{ + //CBQI typically works for satisfaction-complete theories + TheoryId t = kindToTheoryId( k ); + return t == THEORY_BV || t == THEORY_DATATYPES || t == THEORY_BOOL; + } +} + +bool Trigger::isSimpleTrigger( Node n ){ + Node t = n.getKind()==NOT ? n[0] : n; + if( t.getKind()==EQUAL ){ + if( !quantifiers::TermUtil::hasInstConstAttr( t[1] ) ){ + t = t[0]; + } + } + if( isAtomicTrigger( t ) ){ + for( unsigned i=0; i >& visited, std::map< Node, TriggerTermInfo >& tinfo, + quantifiers::TriggerSelMode tstrt, std::vector< Node >& exclude, std::vector< Node >& added, + bool pol, bool hasPol, bool epol, bool hasEPol, bool knowIsUsable ){ + std::map< Node, std::vector< Node > >::iterator itv = visited.find( n ); + if( itv==visited.end() ){ + visited[ n ].clear(); + Trace("auto-gen-trigger-debug2") << "Collect pat terms " << n << " " << pol << " " << hasPol << " " << epol << " " << hasEPol << std::endl; + if( n.getKind()!=FORALL && n.getKind()!=INST_CONSTANT ){ + Node nu; + bool nu_single = false; + if( knowIsUsable ){ + nu = n; + }else if( n.getKind()!=NOT && std::find( exclude.begin(), exclude.end(), n )==exclude.end() ){ + nu = getIsUsableTrigger( n, q ); + if( !nu.isNull() && nu!=n ){ + collectPatTerms2( q, nu, visited, tinfo, tstrt, exclude, added, pol, hasPol, epol, hasEPol, true ); + // copy to n + visited[n].insert( visited[n].end(), added.begin(), added.end() ); + return; + } + } + if( !nu.isNull() ){ + Assert( nu==n ); + Assert( nu.getKind()!=NOT ); + Trace("auto-gen-trigger-debug2") << "...found usable trigger : " << nu << std::endl; + Node reqEq; + if( nu.getKind()==EQUAL ){ + if( isAtomicTrigger( nu[0] ) && !quantifiers::TermUtil::hasInstConstAttr(nu[1]) ){ + if( hasPol ){ + reqEq = nu[1]; + } + nu = nu[0]; + } + } + Assert( reqEq.isNull() || !quantifiers::TermUtil::hasInstConstAttr( reqEq ) ); + Assert( isUsableTrigger( nu, q ) ); + //tinfo.find( nu )==tinfo.end() + Trace("auto-gen-trigger-debug2") << "...add usable trigger : " << nu << std::endl; + tinfo[ nu ].init( q, nu, hasEPol ? ( epol ? 1 : -1 ) : 0, reqEq ); + nu_single = tinfo[ nu ].d_fv.size()==q[0].getNumChildren(); + } + Node nrec = nu.isNull() ? n : nu; + std::vector< Node > added2; + for( unsigned i=0; i= tinfo[added2[i]].d_weight) + { + // discard if we added a subterm as a trigger with all + // variables that nu has + Trace("auto-gen-trigger-debug2") + << "......subsumes parent " << tinfo[nu].d_weight << " " + << tinfo[added2[i]].d_weight << "." << std::endl; + rm_nu = true; + } + } + if( std::find( added.begin(), added.end(), added2[i] )==added.end() ){ + added.push_back( added2[i] ); + } + } + } + } + if( rm_nu && ( tstrt==quantifiers::TRIGGER_SEL_MIN || ( tstrt==quantifiers::TRIGGER_SEL_MIN_SINGLE_ALL && nu_single ) ) ){ + tinfo.erase( nu ); + }else{ + if( std::find( added.begin(), added.end(), nu )==added.end() ){ + added.push_back( nu ); + } + } + visited[n].insert( visited[n].end(), added.begin(), added.end() ); + } + } + }else{ + for( unsigned i=0; isecond.size(); ++i ){ + Node t = itv->second[i]; + if( std::find( added.begin(), added.end(), t )==added.end() ){ + added.push_back( t ); + } + } + } +} + +bool Trigger::isBooleanTermTrigger( Node n ) { + if( n.getKind()==ITE ){ + //check for boolean term converted to ITE + if( n[0].getKind()==INST_CONSTANT && + n[1].getKind()==CONST_BITVECTOR && + n[2].getKind()==CONST_BITVECTOR ){ + if( ((BitVectorType)n[1].getType().toType()).getSize()==1 && + n[1].getConst().toInteger()==1 && + n[2].getConst().toInteger()==0 ){ + return true; + } + } + } + return false; +} + +bool Trigger::isPureTheoryTrigger( Node n ) { + if( n.getKind()==APPLY_UF || n.getKind()==VARIABLE || n.getKind()==SKOLEM ){ //|| !quantifiers::TermUtil::hasInstConstAttr( n ) ){ + return false; + }else{ + for( unsigned i=0; i& vars, std::vector< Node >& patTerms ) { + if( !n.getType().isBoolean() && n.getKind()==APPLY_UF ){ + if( std::find( patTerms.begin(), patTerms.end(), n )==patTerms.end() ){ + bool hasVar = false; + for( unsigned i=0; i& patTerms, quantifiers::TriggerSelMode tstrt, std::vector< Node >& exclude, + std::map< Node, TriggerTermInfo >& tinfo, bool filterInst ){ + std::map< Node, std::vector< Node > > visited; + if( filterInst ){ + //immediately do not consider any term t for which another term is an instance of t + std::vector< Node > patTerms2; + std::map< Node, TriggerTermInfo > tinfo2; + collectPatTerms( q, n, patTerms2, quantifiers::TRIGGER_SEL_ALL, exclude, tinfo2, false ); + std::vector< Node > temp; + temp.insert( temp.begin(), patTerms2.begin(), patTerms2.end() ); + filterTriggerInstances(temp); + if (Trace.isOn("trigger-filter-instance")) + { + if (temp.size() != patTerms2.size()) + { + Trace("trigger-filter-instance") << "Filtered an instance: " + << std::endl; + Trace("trigger-filter-instance") << "Old: "; + for (unsigned i = 0; i < patTerms2.size(); i++) + { + Trace("trigger-filter-instance") << patTerms2[i] << " "; + } + Trace("trigger-filter-instance") << std::endl << "New: "; + for (unsigned i = 0; i < temp.size(); i++) + { + Trace("trigger-filter-instance") << temp[i] << " "; + } + Trace("trigger-filter-instance") << std::endl; + } + } + if( tstrt==quantifiers::TRIGGER_SEL_ALL ){ + for( unsigned i=0; i added; + collectPatTerms2( q, n, visited, tinfo, tstrt, exclude, added, true, true, false, true ); + for( std::map< Node, TriggerTermInfo >::iterator it = tinfo.begin(); it != tinfo.end(); ++it ){ + patTerms.push_back( it->first ); + } +} + +int Trigger::isTriggerInstanceOf(Node n1, + Node n2, + std::vector& fv1, + std::vector& fv2) +{ + Assert(n1 != n2); + int status = 0; + std::unordered_set subs_vars; + std::unordered_set, + PairHashFunction > + visited; + std::vector > visit; + std::pair cur; + TNode cur1; + TNode cur2; + visit.push_back(std::pair(n1, n2)); + do + { + cur = visit.back(); + visit.pop_back(); + if (visited.find(cur) == visited.end()) + { + visited.insert(cur); + cur1 = cur.first; + cur2 = cur.second; + Assert(cur1 != cur2); + // recurse if they have the same operator + if (cur1.hasOperator() && cur2.hasOperator() + && cur1.getNumChildren() == cur2.getNumChildren() + && cur1.getOperator() == cur2.getOperator() + && cur1.getOperator().getKind()!=INST_CONSTANT) + { + visit.push_back(std::pair(cur1, cur2)); + for (unsigned i = 0, size = cur1.getNumChildren(); i < size; i++) + { + if (cur1[i] != cur2[i]) + { + visit.push_back(std::pair(cur1[i], cur2[i])); + } + else if (cur1[i].getKind() == INST_CONSTANT) + { + if (subs_vars.find(cur1[i]) != subs_vars.end()) + { + return 0; + } + // the variable must map to itself in the substitution + subs_vars.insert(cur1[i]); + } + } + } + else + { + bool success = false; + // check if we are in a unifiable instance case + // must be only this case + for (unsigned r = 0; r < 2; r++) + { + if (status == 0 || ((status == 1) == (r == 0))) + { + TNode curi = r == 0 ? cur1 : cur2; + if (curi.getKind() == INST_CONSTANT + && subs_vars.find(curi) == subs_vars.end()) + { + TNode curj = r == 0 ? cur2 : cur1; + // RHS must be a simple trigger + if (getTriggerWeight(curj) == 0) + { + // must occur in the free variables in the other + std::vector& free_vars = r == 0 ? fv2 : fv1; + if (std::find(free_vars.begin(), free_vars.end(), curi) + != free_vars.end()) + { + status = r == 0 ? 1 : -1; + subs_vars.insert(curi); + success = true; + break; + } + } + } + } + } + if (!success) + { + return 0; + } + } + } + } while (!visit.empty()); + return status; +} + +void Trigger::filterTriggerInstances(std::vector& nodes) +{ + std::map > fvs; + for (unsigned i = 0, size = nodes.size(); i < size; i++) + { + quantifiers::TermUtil::computeVarContains(nodes[i], fvs[i]); + } + std::vector active; + active.resize(nodes.size(), true); + for (unsigned i = 0, size = nodes.size(); i < size; i++) + { + std::vector& fvsi = fvs[i]; + if (active[i]) + { + for (unsigned j = i + 1, size2 = nodes.size(); j < size2; j++) + { + if (active[j]) + { + int result = isTriggerInstanceOf(nodes[i], nodes[j], fvsi, fvs[j]); + if (result == 1) + { + Trace("filter-instances") << nodes[j] << " is an instance of " + << nodes[i] << std::endl; + active[i] = false; + break; + } + else if (result == -1) + { + Trace("filter-instances") << nodes[i] << " is an instance of " + << nodes[j] << std::endl; + active[j] = false; + } + } + } + } + } + std::vector temp; + for (unsigned i = 0; i < nodes.size(); i++) + { + if (active[i]) + { + temp.push_back(nodes[i]); + } + } + nodes.clear(); + nodes.insert(nodes.begin(), temp.begin(), temp.end()); +} + +Node Trigger::getInversionVariable( Node n ) { + if( n.getKind()==INST_CONSTANT ){ + return n; + }else if( n.getKind()==PLUS || n.getKind()==MULT ){ + Node ret; + for( unsigned i=0; i(); + if( r!=Rational(-1) && r!=Rational(1) ){ + Trace("var-trigger-debug") << "No : not integer coefficient " << n << std::endl; + return Node::null(); + } + } + */ + } + } + return ret; + }else{ + Trace("var-trigger-debug") << "No : unsupported operator " << n << "." << std::endl; + } + return Node::null(); +} + +Node Trigger::getInversion( Node n, Node x ) { + if( n.getKind()==INST_CONSTANT ){ + return x; + }else if( n.getKind()==PLUS || n.getKind()==MULT ){ + int cindex = -1; + for( unsigned i=0; imkNode( MINUS, x, n[i] ); + }else if( n.getKind()==MULT ){ + Assert( n[i].isConst() ); + if( x.getType().isInteger() ){ + Node coeff = NodeManager::currentNM()->mkConst( n[i].getConst().abs() ); + if( !n[i].getConst().abs().isOne() ){ + x = NodeManager::currentNM()->mkNode( INTS_DIVISION_TOTAL, x, coeff ); + } + if( n[i].getConst().sgn()<0 ){ + x = NodeManager::currentNM()->mkNode( UMINUS, x ); + } + }else{ + Node coeff = NodeManager::currentNM()->mkConst( Rational(1) / n[i].getConst() ); + x = NodeManager::currentNM()->mkNode( MULT, x, coeff ); + } + } + x = Rewriter::rewrite( x ); + }else{ + Assert( cindex==-1 ); + cindex = i; + } + } + Assert( cindex!=-1 ); + return getInversion( n[cindex], x ); + } + return Node::null(); +} + +void Trigger::getTriggerVariables(Node n, Node q, std::vector& t_vars) +{ + std::vector< Node > patTerms; + std::map< Node, TriggerTermInfo > tinfo; + // collect all patterns from n + std::vector< Node > exclude; + collectPatTerms(q, n, patTerms, quantifiers::TRIGGER_SEL_ALL, exclude, tinfo); + //collect all variables from all patterns in patTerms, add to t_vars + for( unsigned i=0; igetActiveScore( d_quantEngine ); +} + +TriggerTrie::TriggerTrie() +{} + +TriggerTrie::~TriggerTrie() { + for(std::map< TNode, TriggerTrie* >::iterator i = d_children.begin(), iend = d_children.end(); + i != iend; ++i) { + TriggerTrie* current = (*i).second; + delete current; + } + d_children.clear(); + + for( unsigned i=0; i& nodes) +{ + std::vector temp; + temp.insert(temp.begin(), nodes.begin(), nodes.end()); + std::sort(temp.begin(), temp.end()); + TriggerTrie* tt = this; + for (const Node& n : temp) + { + std::map::iterator itt = tt->d_children.find(n); + if (itt == tt->d_children.end()) + { + return NULL; + } + else + { + tt = itt->second; + } + } + return tt->d_tr.empty() ? NULL : tt->d_tr[0]; +} + +void TriggerTrie::addTrigger(std::vector& nodes, inst::Trigger* t) +{ + std::vector temp; + temp.insert(temp.begin(), nodes.begin(), nodes.end()); + std::sort(temp.begin(), temp.end()); + TriggerTrie* tt = this; + for (const Node& n : temp) + { + std::map::iterator itt = tt->d_children.find(n); + if (itt == tt->d_children.end()) + { + TriggerTrie* ttn = new TriggerTrie; + tt->d_children[n] = ttn; + tt = ttn; + } + else + { + tt = itt->second; + } + } + tt->d_tr.push_back(t); +} + +}/* CVC4::theory::inst namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/quantifiers/ematching/trigger.h b/src/theory/quantifiers/ematching/trigger.h new file mode 100644 index 000000000..e91a87e58 --- /dev/null +++ b/src/theory/quantifiers/ematching/trigger.h @@ -0,0 +1,476 @@ +/********************* */ +/*! \file trigger.h + ** \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 trigger class + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__QUANTIFIERS__TRIGGER_H +#define __CVC4__THEORY__QUANTIFIERS__TRIGGER_H + +#include + +#include "expr/node.h" +#include "theory/quantifiers/inst_match.h" +#include "options/quantifiers_options.h" + +namespace CVC4 { +namespace theory { + +class QuantifiersEngine; + +namespace inst { + +class IMGenerator; +class InstMatchGenerator; + +/** Information about a node used in a trigger. +* +* This information includes: +* 1. the free variables of the node, and +* 2. information about which terms are useful for matching. +* +* As an example, consider the trigger +* { f( x ), g( y ), P( y, z ) } +* for quantified formula +* forall xy. ( f( x ) != b => ( P( x, g( y ) ) V P( y, z ) ) ) +* +* Notice that it is only useful to match f( x ) to f-applications not in the +* equivalence class of b, and P( y, z ) to P-applications not in the equivalence +* class of true, as such instances will always be entailed by the ground +* equalities and disequalities the current context. Entailed instances are +* typically not helpful, and are discarded in Instantiate::addInstantiation(...) +* unless the option --no-inst-no-entail is enabled. For more details, see page +* 10 of "Congruence Closure with Free Variables", Barbosa et al., TACAS 2017. +* +* This example is referenced for each of the functions below. +*/ +class TriggerTermInfo { + public: + TriggerTermInfo() : d_reqPol(0), d_weight(0) {} + ~TriggerTermInfo() {} + /** The free variables in the node + * + * In the trigger term info for f( x ) in the above example, d_fv = { x } + * In the trigger term info for g( y ) in the above example, d_fv = { y } + * In the trigger term info for P( y, z ) in the above example, d_fv = { y, z } + */ + std::vector d_fv; + /** Required polarity: 1 for equality, -1 for disequality, 0 for none + * + * In the trigger term info for f( x ) in the above example, d_reqPol = -1 + * In the trigger term info for g( y ) in the above example, d_reqPol = 0 + * In the trigger term info for P( y, z ) in the above example, d_reqPol = 1 + */ + int d_reqPol; + /** Required polarity equal term + * + * If d_reqPolEq is not null, + * if d_reqPol = 1, then this trigger term must be matched to terms in the + * equivalence class of d_reqPolEq, + * if d_reqPol = -1, then this trigger term must be matched to terms *not* in + * the equivalence class of d_reqPolEq. + * + * This information is typically chosen by analyzing the entailed equalities + * and disequalities of quantified formulas. + * In the trigger term info for f( x ) in the above example, d_reqPolEq = b + * In the trigger term info for g( y ) in the above example, + * d_reqPolEq = Node::null() + * In the trigger term info for P( y, z ) in the above example, + * d_reqPolEq = false + */ + Node d_reqPolEq; + /** the weight of the trigger (see Trigger::getTriggerWeight). */ + int d_weight; + /** Initialize this information class (can be called more than once). + * q is the quantified formula that n is a trigger term for + * n is the trigger term + * reqPol/reqPolEq are described above + */ + void init(Node q, Node n, int reqPol = 0, Node reqPolEq = Node::null()); +}; + +/** A collection of nodes representing a trigger. +* +* This class encapsulates all implementations of E-matching in CVC4. +* Its primary use is as a utility of the quantifiers module InstantiationEngine +* (see theory/quantifiers/ematching/instantiation_engine.h) which uses Trigger to make +* appropriate calls to Instantiate::addInstantiation(...) +* (see theory/instantiate.h) for the instantiate utility of the quantifiers +* engine (d_quantEngine) associated with this trigger. These calls +* queue instantiation lemmas to the output channel of TheoryQuantifiers during +* a full effort check. +* +* Concretely, a Trigger* t is used in the following way during a full effort +* check. Assume that t is associated with quantified formula q (see field d_f). +* We call : +* +* // setup initial information +* t->resetInstantiationRound(); +* // will produce instantiations based on matching with all terms +* t->reset( Node::null() ); +* // add all instantiations based on E-matching with this trigger and the +* // current context +* t->addInstantiations(); +* +* This will result in (a set of) calls to +* Instantiate::addInstantiation(q, m1)...Instantiate::addInstantiation(q, mn), +* where m1...mn are InstMatch objects. These calls add the corresponding +* instantiation lemma for (q,mi) on the output channel associated with +* d_quantEngine. +* +* The Trigger class is wrapper around an underlying IMGenerator class, which +* implements various forms of E-matching for its set of nodes (d_nodes), which +* is refered to in the literature as a "trigger". A trigger is a set of terms +* whose free variables are the bound variables of a quantified formula q, +* and that is used to guide instantiations for q (for example, see "Efficient +* E-Matching for SMT Solvers" by de Moura et al). +* +* For example of an instantiation lemma produced by E-matching : +* +* quantified formula : forall x. P( x ) +* trigger : P( x ) +* ground context : ~P( a ) +* +* Then E-matching matches P( x ) and P( a ), resulting in the match { x -> a } +* which is used to generate the instantiation lemma : +* (forall x. P( x )) => P( a ) +* +* Terms that are provided as input to a Trigger class via mkTrigger +* should be in "instantiation constant form", see TermUtil::getInstConstantNode. +* Say we have quantified formula q whose AST is the Node +* (FORALL +* (BOUND_VAR_LIST x) +* (NOT (P x)) +* (INST_PATTERN_LIST (INST_PATTERN (P x)))) +* then TermUtil::getInstConstantNode( q, (P x) ) = (P IC) where +* IC = TermUtil::getInstantiationConstant( q, i ). +* Trigger expects as input (P IC) to represent the Trigger (P x). This form +* ensures that references to bound variables are unique to quantified formulas, +* which is required to ensure the correctness of instantiation lemmas we +* generate. +* +*/ +class Trigger { + friend class IMGenerator; + + public: + virtual ~Trigger(); + /** get the generator associated with this trigger */ + IMGenerator* getGenerator() { return d_mg; } + /** Reset instantiation round. + * + * Called once at beginning of an instantiation round. + */ + void resetInstantiationRound(); + /** Reset the trigger. + * + * eqc is the equivalence class from which to match ground terms. If eqc is + * Node::null(), then we match ground terms from all equivalence classes. + */ + void reset( Node eqc ); + /** add all available instantiations, based on the current context + * + * This function makes the appropriate calls to d_qe->addInstantiation(...) + * based on the current ground terms and equalities in the current context, + * via queries to functions in d_qe. This calls the addInstantiations function + * of the underlying match generator. It can be extended to + * produce instantiations beyond what is produced by the match generator + * (for example, see theory/quantifiers/ematching/ho_trigger.h). + */ + virtual int addInstantiations(); + /** Return whether this is a multi-trigger. */ + bool isMultiTrigger() { return d_nodes.size()>1; } + /** Get instantiation pattern list associated with this trigger. + * + * An instantiation pattern list is the node representation of a trigger, in + * particular, it is the third argument of quantified formulas which have user + * (! ... :pattern) attributes. + */ + Node getInstPattern(); + /* A heuristic value indicating how active this generator is. + * + * This returns the number of ground terms for the match operators in terms + * from d_nodes. This score is only used with the experimental option + * --trigger-active-sel. + */ + int getActiveScore(); + /** print debug information for the trigger */ + void debugPrint(const char* c) + { + Trace(c) << "TRIGGER( "; + for (int i = 0; i < (int)d_nodes.size(); i++) + { + if (i > 0) + { + Trace(c) << ", "; + } + Trace(c) << d_nodes[i]; + } + Trace(c) << " )"; + } + /** mkTrigger method + * + * This makes an instance of a trigger object. + * qe : pointer to the quantifier engine; + * q : the quantified formula we are making a trigger for + * nodes : the nodes comprising the (multi-)trigger + * keepAll: don't remove unneeded patterns; + * trOption : policy for dealing with triggers that already exist + * (see below) + * use_n_vars : number of variables that should be bound by the trigger + * typically, the number of quantified variables in q. + */ + enum{ + TR_MAKE_NEW, //make new trigger even if it already may exist + TR_GET_OLD, //return a previous trigger if it had already been created + TR_RETURN_NULL //return null if a duplicate is found + }; + static Trigger* mkTrigger(QuantifiersEngine* qe, + Node q, + std::vector& nodes, + bool keepAll = true, + int trOption = TR_MAKE_NEW, + unsigned use_n_vars = 0); + /** single trigger version that calls the above function */ + static Trigger* mkTrigger(QuantifiersEngine* qe, + Node q, + Node n, + bool keepAll = true, + int trOption = TR_MAKE_NEW, + unsigned use_n_vars = 0); + /** make trigger terms + * + * This takes a set of eligible trigger terms and stores a subset of them in + * trNodes, such that : + * (1) the terms in trNodes contain at least n_vars of the quantified + * variables in quantified formula q, and + * (2) the set trNodes is minimal, i.e. removing one term from trNodes + * always violates (1). + */ + static bool mkTriggerTerms(Node q, + std::vector& nodes, + unsigned n_vars, + std::vector& trNodes); + /** collect pattern terms + * + * This collects all terms that are eligible for triggers for quantified + * formula q in term n and adds them to patTerms. + * tstrt : the selection strategy (see options/quantifiers_mode.h), + * exclude : a set of terms that *cannot* be selected as triggers, + * tinfo : stores the result of the collection, mapping terms to the + * information they are associated with, + * filterInst : flag that when true, we discard terms that have instances + * in the vector we are returning, e.g. we do not return f( x ) if we are + * also returning f( f( x ) ). TODO: revisit this (issue #1211) + */ + static void collectPatTerms( Node q, Node n, std::vector< Node >& patTerms, quantifiers::TriggerSelMode tstrt, + std::vector< Node >& exclude, std::map< Node, TriggerTermInfo >& tinfo, + bool filterInst = false ); + + /** Is n a usable trigger in quantified formula q? + * + * A usable trigger is one that is matchable and contains free variables only + * from q. + */ + static bool isUsableTrigger( Node n, Node q ); + /** get is usable trigger + * + * Return the associated node of n that is a usable trigger in quantified + * formula q. This may be different than n in several cases : + * (1) Polarity information is explicitly converted to equalities, e.g. + * getIsUsableTrigger( (not (P x )), q ) may return (= (P x) false) + * (2) Relational triggers are put into solved form, e.g. + * getIsUsableTrigger( (= (+ x a) 5), q ) may return (= x (- 5 a)). + */ + static Node getIsUsableTrigger( Node n, Node q ); + /** Is n a usable atomic trigger? + * + * A usable atomic trigger is a term that is both a useable trigger and an + * atomic trigger. + */ + static bool isUsableAtomicTrigger( Node n, Node q ); + /** is n an atomic trigger? + * + * An atomic trigger is one whose kind is an atomic trigger kind. + */ + static bool isAtomicTrigger( Node n ); + /** Is k an atomic trigger kind? + * + * An atomic trigger kind is one for which term indexing/matching is possible. + */ + static bool isAtomicTriggerKind( Kind k ); + /** is n a relational trigger, e.g. like x >= a ? */ + static bool isRelationalTrigger( Node n ); + /** Is k a relational trigger kind? */ + static bool isRelationalTriggerKind( Kind k ); + /** Is k a kind for which counterexample-guided instantiation is possible? + * + * This typically corresponds to kinds that correspond to operators that + * have total interpretations and are a part of the signature of + * satisfaction complete theories (see Reynolds et al., CAV 2015). + */ + static bool isCbqiKind( Kind k ); + /** is n a simple trigger (see inst_match_generator.h)? */ + static bool isSimpleTrigger( Node n ); + /** is n a Boolean term trigger, e.g. ite( x, BV1, BV0 )? */ + static bool isBooleanTermTrigger( Node n ); + /** is n a pure theory trigger, e.g. head( x )? */ + static bool isPureTheoryTrigger( Node n ); + /** get trigger weight + * + * Returns 0 for triggers that are easy to process and 1 otherwise. + * A trigger is easy to process if it is an atomic trigger, or a relational + * trigger of the form x ~ g for ~ \in { =, >=, > }. + */ + static int getTriggerWeight( Node n ); + /** Returns whether n is a trigger term with a local theory extension + * property from Bansal et al., CAV 2015. + */ + static bool isLocalTheoryExt( Node n, std::vector< Node >& vars, + std::vector< Node >& patTerms ); + /** get the variable associated with an inversion for n + * + * A term n with an inversion variable x has the following property : + * There exists a closed function f such that for all terms t + * |= (n = t) <=> (x = f(t)) + * For example, getInversionVariable( x+1 ) returns x since for all terms t, + * |= x+1 = t <=> x = (\y. y-1)(t) + * We call f the inversion function for n. + */ + static Node getInversionVariable( Node n ); + /** Get the body of the inversion function for n whose argument is y. + * e.g. getInversion( x+1, y ) returns y-1 + */ + static Node getInversion(Node n, Node y); + /** get all variables that E-matching can instantiate from a subterm n. + * + * This returns the union of all free variables in usable triggers that are + * subterms of n. + */ + static void getTriggerVariables(Node n, Node f, std::vector& t_vars); + + protected: + /** trigger constructor, intentionally protected (use Trigger::mkTrigger). */ + Trigger(QuantifiersEngine* ie, Node q, std::vector& nodes); + /** is subterm of trigger usable (helper function for isUsableTrigger) */ + static bool isUsable( Node n, Node q ); + /** returns an equality that is equivalent to the equality eq and + * is a usable trigger for q if one exists, otherwise returns Node::null(). + */ + static Node getIsUsableEq( Node q, Node eq ); + /** returns whether n1 == n2 is a usable (relational) trigger for q. */ + static bool isUsableEqTerms( Node q, Node n1, Node n2 ); + /** recursive helper function for collectPatTerms + * + * This collects the usable trigger terms in the subterm n of the body of + * quantified formula q. + * visited : cache of the trigger terms collected for each visited node, + * tinfo : cache of trigger term info for each visited node, + * tstrat : the selection strategy (see options/quantifiers_mode.h) + * exclude : a set of terms that *cannot* be selected as triggers + * pol/hasPol : the polarity of node n in q + * (see QuantPhaseReq theory/quantifiers/quant_util.h) + * epol/hasEPol : the entailed polarity of node n in q + * (see QuantPhaseReq theory/quantifiers/quant_util.h) + * knowIsUsable : whether we know that n is a usable trigger. + * + * We add the triggers we collected recursively in n into added. + */ + static void collectPatTerms2( Node q, Node n, std::map< Node, std::vector< Node > >& visited, std::map< Node, TriggerTermInfo >& tinfo, + quantifiers::TriggerSelMode tstrt, std::vector< Node >& exclude, std::vector< Node >& added, + bool pol, bool hasPol, bool epol, bool hasEPol, bool knowIsUsable = false ); + + /** filter all nodes that have trigger instances + * + * This is used during collectModelInfo to filter certain trigger terms, + * stored in nodes. This updates nodes so that no pairs of distinct nodes + * (i,j) is such that i is a trigger instance of j or vice versa (see below). + */ + static void filterTriggerInstances(std::vector& nodes); + + /** is instance of + * + * We say a term t is an trigger instance of term s if + * (1) t = s * { x1 -> t1 ... xn -> tn } + * (2) { x1, ..., xn } are a subset of FV( t ). + * For example, f( g( h( x, x ) ) ) and f( g( x ) ) are instances of f( x ), + * but f( g( y ) ) and g( x ) are not instances of f( x ). + * + * When this method returns -1, n1 is an instance of n2, + * When this method returns 1, n1 is an instance of n2. + * + * The motivation for this method is to discard triggers s that are less + * restrictive (criteria (1)) and serve to bind the same variables (criteria + * (2)) as another trigger t. This often helps avoiding matching loops. + */ + static int isTriggerInstanceOf(Node n1, + Node n2, + std::vector& fv1, + std::vector& fv2); + + /** add an instantiation (called by InstMatchGenerator) + * + * This calls Instantiate::addInstantiation(...) for instantiations + * associated with m. Typically, m is associated with a single instantiation, + * but in some cases (e.g. higher-order) we may modify m before calling + * Instantiate::addInstantiation(...). + */ + virtual bool sendInstantiation(InstMatch& m); + /** The nodes comprising this trigger. */ + std::vector< Node > d_nodes; + /** The quantifiers engine associated with this trigger. */ + QuantifiersEngine* d_quantEngine; + /** The quantified formula this trigger is for. */ + Node d_quant; + /** match generator + * + * This is the back-end utility that implements the underlying matching + * algorithm associated with this trigger. + */ + IMGenerator* d_mg; +}; /* class Trigger */ + +/** A trie of triggers. +* +* This class is used to cache all Trigger objects that are generated in the +* current context. We index Triggers in this data structure based on the +* value of Trigger::d_nodes. When a Trigger is added to this data structure, +* this Trie assumes responsibility for deleting it. +*/ +class TriggerTrie { +public: + TriggerTrie(); + ~TriggerTrie(); + /** get trigger + * This returns a Trigger t that is indexed by nodes, + * or NULL otherwise. + */ + Trigger* getTrigger(std::vector& nodes); + /** add trigger + * This adds t to the trie, indexed by nodes. + * In typical use cases, nodes is t->d_nodes. + */ + void addTrigger(std::vector& nodes, Trigger* t); + + private: + /** The trigger at this node in the trie. */ + std::vector d_tr; + /** The children of this node in the trie. */ + std::map< TNode, TriggerTrie* > d_children; +};/* class inst::Trigger::TriggerTrie */ + +}/* CVC4::theory::inst namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__QUANTIFIERS__TRIGGER_H */ diff --git a/src/theory/quantifiers/first_order_model.cpp b/src/theory/quantifiers/first_order_model.cpp index e7070b469..e608b25b3 100644 --- a/src/theory/quantifiers/first_order_model.cpp +++ b/src/theory/quantifiers/first_order_model.cpp @@ -15,10 +15,10 @@ #include "theory/quantifiers/first_order_model.h" #include "options/base_options.h" #include "options/quantifiers_options.h" -#include "theory/quantifiers/ambqi_builder.h" -#include "theory/quantifiers/bounded_integers.h" -#include "theory/quantifiers/full_model_check.h" -#include "theory/quantifiers/model_engine.h" +#include "theory/quantifiers/fmf/ambqi_builder.h" +#include "theory/quantifiers/fmf/bounded_integers.h" +#include "theory/quantifiers/fmf/full_model_check.h" +#include "theory/quantifiers/fmf/model_engine.h" #include "theory/quantifiers/quantifiers_attributes.h" #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/term_enumeration.h" diff --git a/src/theory/quantifiers/fmf/ambqi_builder.cpp b/src/theory/quantifiers/fmf/ambqi_builder.cpp new file mode 100644 index 000000000..6bb73f8a9 --- /dev/null +++ b/src/theory/quantifiers/fmf/ambqi_builder.cpp @@ -0,0 +1,968 @@ +/********************* */ +/*! \file ambqi_builder.cpp + ** \verbatim + ** Top contributors (to current version): + ** Morgan Deters, 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 Implementation of abstract MBQI builder + **/ + +#include "theory/quantifiers/fmf/ambqi_builder.h" +#include "options/quantifiers_options.h" +#include "theory/quantifiers/instantiate.h" +#include "theory/quantifiers/term_database.h" +#include "theory/quantifiers/term_util.h" + +using namespace std; +using namespace CVC4::kind; +using namespace CVC4::context; + +namespace CVC4 { +namespace theory { +namespace quantifiers { + + +void AbsDef::construct_func( FirstOrderModelAbs * m, std::vector< TNode >& fapps, unsigned depth ) { + d_def.clear(); + Assert( !fapps.empty() ); + if( depth==fapps[0].getNumChildren() ){ + //if( fapps.size()>1 ){ + // for( unsigned i=0; i " << m->getRepresentativeId( fapps[i] ) << std::endl; + // } + //} + //get representative in model for this term + d_value = m->getRepresentativeId( fapps[0] ); + Assert( d_value!=val_none ); + }else{ + TypeNode tn = fapps[0][depth].getType(); + std::map< unsigned, std::vector< TNode > > fapp_child; + + //partition based on evaluations of fapps[1][depth]....fapps[n][depth] + for( unsigned i=0; igetRepresentativeId( fapps[i][depth] ); + Assert( r < 32 ); + fapp_child[r].push_back( fapps[i] ); + } + + //do completion + std::map< unsigned, unsigned > fapp_child_index; + unsigned def = m->d_domain[ tn ]; + unsigned minSize = fapp_child.begin()->second.size(); + unsigned minSizeIndex = fapp_child.begin()->first; + for( std::map< unsigned, std::vector< TNode > >::iterator it = fapp_child.begin(); it != fapp_child.end(); ++it ){ + fapp_child_index[it->first] = ( 1 << it->first ); + def = def & ~( 1 << it->first ); + if( it->second.size()second.size(); + minSizeIndex = it->first; + } + } + fapp_child_index[minSizeIndex] |= def; + d_default = fapp_child_index[minSizeIndex]; + + //construct children + for( std::map< unsigned, std::vector< TNode > >::iterator it = fapp_child.begin(); it != fapp_child.end(); ++it ){ + Trace("abs-model-debug") << "Construct " << it->first << " : " << fapp_child_index[it->first] << " : "; + const RepSet* rs = m->getRepSet(); + debugPrintUInt("abs-model-debug", + rs->getNumRepresentatives(tn), + fapp_child_index[it->first]); + Trace("abs-model-debug") << " : " << it->second.size() << " terms." << std::endl; + d_def[fapp_child_index[it->first]].construct_func( m, it->second, depth+1 ); + } + } +} + +void AbsDef::simplify( FirstOrderModelAbs * m, TNode q, TNode n, unsigned depth ) { + if( d_value==val_none && !d_def.empty() ){ + //process the default + std::map< unsigned, AbsDef >::iterator defd = d_def.find( d_default ); + Assert( defd!=d_def.end() ); + unsigned newDef = d_default; + std::vector< unsigned > to_erase; + defd->second.simplify( m, q, n, depth+1 ); + int defVal = defd->second.d_value; + bool isConstant = ( defVal!=val_none ); + //process each child + for( std::map< unsigned, AbsDef >::iterator it = d_def.begin(); it != d_def.end(); ++it ){ + if( it->first!=d_default ){ + it->second.simplify( m, q, n, depth+1 ); + if( it->second.d_value==defVal && it->second.d_value!=val_none ){ + newDef = newDef | it->first; + to_erase.push_back( it->first ); + }else{ + isConstant = false; + } + } + } + if( !to_erase.empty() ){ + //erase old default + int defVal = defd->second.d_value; + d_def.erase( d_default ); + //set new default + d_default = newDef; + d_def[d_default].construct_def_entry( m, q, n, defVal, depth+1 ); + //erase redundant entries + for( unsigned i=0; igetRepSet(); + unsigned dSize = rs->getNumRepresentatives(tn); + Assert( dSize<32 ); + for( std::map< unsigned, AbsDef >::const_iterator it = d_def.begin(); it != d_def.end(); ++it ){ + for( unsigned i=0; ifirst ); + if( it->first==d_default ){ + Trace(c) << "*"; + } + if( it->second.d_value!=val_none ){ + Trace(c) << " -> V[" << it->second.d_value << "]"; + } + Trace(c) << std::endl; + it->second.debugPrint( c, m, f, depth+1 ); + } + } + } +} + +bool AbsDef::addInstantiations( FirstOrderModelAbs * m, QuantifiersEngine * qe, TNode q, std::vector< Node >& terms, int& inst, unsigned depth ) { + if( inst==0 || !options::fmfOneInstPerRound() ){ + if( d_value==1 ){ + //instantiations are all true : ignore this + return true; + }else{ + if( depth==q[0].getNumChildren() ){ + if (qe->getInstantiate()->addInstantiation(q, terms, true)) + { + Trace("ambqi-inst-debug") << "-> Added instantiation." << std::endl; + inst++; + return true; + }else{ + Trace("ambqi-inst-debug") << "-> Failed to add instantiation." << std::endl; + //we are incomplete + return false; + } + }else{ + bool osuccess = true; + TypeNode tn = m->getVariable( q, depth ).getType(); + for( std::map< unsigned, AbsDef >::iterator it = d_def.begin(); it != d_def.end(); ++it ){ + //get witness term + unsigned index = 0; + bool success; + do { + success = false; + index = getId( it->first, index ); + if( index<32 ){ + const RepSet* rs = m->getRepSet(); + Assert(index < rs->getNumRepresentatives(tn)); + terms[m->d_var_order[q][depth]] = + rs->getRepresentative(tn, index); + if( !it->second.addInstantiations( m, qe, q, terms, inst, depth+1 ) && inst==0 ){ + //if we are incomplete, and have not yet added an instantiation, keep trying + index++; + Trace("ambqi-inst-debug") << "At depth " << depth << ", failed branch, no instantiations and incomplete, increment index : " << index << std::endl; + }else{ + success = true; + } + } + }while( !qe->inConflict() && !success && index<32 ); + //mark if we are incomplete + osuccess = osuccess && success; + } + return osuccess; + } + } + }else{ + return true; + } +} + +void AbsDef::construct_entry( std::vector< unsigned >& entry, std::vector< bool >& entry_def, int v, unsigned depth ) { + if( depth==entry.size() ){ + d_value = v; + }else{ + d_def[entry[depth]].construct_entry( entry, entry_def, v, depth+1 ); + if( entry_def[depth] ){ + d_default = entry[depth]; + } + } +} + +void AbsDef::get_defs( unsigned u, std::vector< AbsDef * >& defs ) { + for( std::map< unsigned, AbsDef >::iterator it = d_def.begin(); it != d_def.end(); ++it ){ + if( ( u & it->first )!=0 ){ + Assert( (u & it->first)==u ); + defs.push_back( &it->second ); + } + } +} + +void AbsDef::construct_normalize( FirstOrderModelAbs * m, TNode q, std::vector< AbsDef * >& defs, unsigned depth ) { + if( depth==q[0].getNumChildren() ){ + Assert( defs.size()==1 ); + d_value = defs[0]->d_value; + }else{ + TypeNode tn = m->getVariable( q, depth ).getType(); + unsigned def = m->d_domain[tn]; + for( unsigned i=0; i::iterator itd = defs[i]->d_def.begin(); itd != defs[i]->d_def.end(); ++itd ){ + if( isSimple( itd->first ) && ( def & itd->first )!=0 ){ + def &= ~( itd->first ); + //process this value + std::vector< AbsDef * > cdefs; + for( unsigned j=0; jget_defs( itd->first, cdefs ); + } + d_def[itd->first].construct_normalize( m, q, cdefs, depth+1 ); + if( def==0 ){ + d_default = itd->first; + break; + } + } + } + if( def==0 ){ + break; + } + } + if( def!=0 ){ + d_default = def; + //process the default + std::vector< AbsDef * > cdefs; + for( unsigned j=0; jget_defs( d_default, cdefs ); + } + d_def[d_default].construct_normalize( m, q, cdefs, depth+1 ); + } + } +} + +void AbsDef::construct_def_entry( FirstOrderModelAbs * m, TNode q, TNode n, int v, unsigned depth ) { + d_value = v; + if( depthgetVariable( q, depth ).getType(); + unsigned dom = m->d_domain[tn] ; + d_def[dom].construct_def_entry( m, q, n, v, depth+1 ); + d_default = dom; + } +} + +void AbsDef::apply_ucompose( FirstOrderModelAbs * m, TNode q, + std::vector< unsigned >& entry, std::vector< bool >& entry_def, + std::vector< int >& terms, std::map< unsigned, int >& vchildren, + AbsDef * a, unsigned depth ) { + if( depth==terms.size() ){ + if( Trace.isOn("ambqi-check-debug2") ){ + Trace("ambqi-check-debug2") << "Add entry ( "; + const RepSet* rs = m->getRepSet(); + for( unsigned i=0; igetNumRepresentatives(m->getVariable(q, i).getType()); + debugPrintUInt( "ambqi-check-debug2", dSize, entry[i] ); + Trace("ambqi-check-debug2") << " "; + } + Trace("ambqi-check-debug2") << ")" << std::endl; + } + a->construct_entry( entry, entry_def, d_value ); + }else{ + unsigned id; + if( terms[depth]==val_none ){ + //a variable + std::map< unsigned, int >::iterator itv = vchildren.find( depth ); + Assert( itv!=vchildren.end() ); + unsigned prev_v = entry[itv->second]; + bool prev_vd = entry_def[itv->second]; + for( std::map< unsigned, AbsDef >::iterator it = d_def.begin(); it != d_def.end(); ++it ){ + entry[itv->second] = it->first & prev_v; + entry_def[itv->second] = ( it->first==d_default ) && prev_vd; + if( entry[itv->second]!=0 ){ + it->second.apply_ucompose( m, q, entry, entry_def, terms, vchildren, a, depth+1 ); + } + } + entry[itv->second] = prev_v; + entry_def[itv->second] = prev_vd; + }else{ + id = (unsigned)terms[depth]; + Assert( id<32 ); + unsigned fid = 1 << id; + std::map< unsigned, AbsDef >::iterator it = d_def.find( fid ); + if( it!=d_def.end() ){ + it->second.apply_ucompose( m, q, entry, entry_def, terms, vchildren, a, depth+1 ); + }else{ + d_def[d_default].apply_ucompose( m, q, entry, entry_def, terms, vchildren, a, depth+1 ); + } + } + } +} + +void AbsDef::construct_var_eq( FirstOrderModelAbs * m, TNode q, unsigned v1, unsigned v2, int curr, int currv, unsigned depth ) { + if( depth==q[0].getNumChildren() ){ + Assert( currv!=val_none ); + d_value = currv; + }else{ + TypeNode tn = m->getVariable( q, depth ).getType(); + unsigned dom = m->d_domain[tn]; + int vindex = depth==v1 ? 0 : ( depth==v2 ? 1 : val_none ); + if( vindex==val_none ){ + d_def[dom].construct_var_eq( m, q, v1, v2, curr, currv, depth+1 ); + d_default = dom; + }else{ + Assert( currv==val_none ); + if( curr==val_none ){ + unsigned numReps = m->getRepSet()->getNumRepresentatives(tn); + Assert( numReps < 32 ); + for( unsigned i=0; igetVariable( q, depth ).getType(); + if( v==depth ){ + unsigned numReps = m->getRepSet()->getNumRepresentatives(tn); + Assert( numReps>0 && numReps < 32 ); + for( unsigned i=0; id_domain[tn]; + d_def[dom].construct_var( m, q, v, currv, depth+1 ); + d_default = dom; + } + } +} + +void AbsDef::construct_compose( FirstOrderModelAbs * m, TNode q, TNode n, AbsDef * f, + std::map< unsigned, AbsDef * >& children, + std::map< unsigned, int >& bchildren, std::map< unsigned, int >& vchildren, + std::vector< unsigned >& entry, std::vector< bool >& entry_def ) { + const RepSet* rs = m->getRepSet(); + if( n.getKind()==OR || n.getKind()==AND ){ + // short circuiting + for( std::map< unsigned, AbsDef * >::iterator it = children.begin(); it != children.end(); ++it ){ + if( ( it->second->d_value==0 && n.getKind()==AND ) || + ( it->second->d_value==1 && n.getKind()==OR ) ){ + //std::cout << "Short circuit " << it->second->d_value << " " << entry.size() << "/" << q[0].getNumChildren() << std::endl; + unsigned count = q[0].getNumChildren() - entry.size(); + for( unsigned i=0; id_domain[m->getVariable( q, entry.size() ).getType()] ); + entry_def.push_back( true ); + } + construct_entry( entry, entry_def, it->second->d_value ); + for( unsigned i=0; i values; + values.resize( n.getNumChildren(), val_none ); + for( std::map< unsigned, AbsDef * >::iterator it = children.begin(); it != children.end(); ++it ){ + values[it->first] = it->second->d_value; + } + for( std::map< unsigned, int >::iterator it = bchildren.begin(); it != bchildren.end(); ++it ){ + values[it->first] = it->second; + } + //look up value(s) + f->apply_ucompose( m, q, entry, entry_def, values, vchildren, this ); + }else{ + bool incomplete = false; + //we are composing with an interpreted function + std::vector< TNode > values; + values.resize( n.getNumChildren(), TNode::null() ); + for( std::map< unsigned, AbsDef * >::iterator it = children.begin(); it != children.end(); ++it ){ + Trace("ambqi-check-debug2") << "composite : " << it->first << " : " << it->second->d_value; + if( it->second->d_value>=0 ){ + if (it->second->d_value + >= (int)rs->getNumRepresentatives(n[it->first].getType())) + { + std::cout << it->second->d_value << " " << n[it->first] << " " + << n[it->first].getType() << " " + << rs->getNumRepresentatives(n[it->first].getType()) + << std::endl; + } + Assert(it->second->d_value + < (int)rs->getNumRepresentatives(n[it->first].getType())); + values[it->first] = rs->getRepresentative(n[it->first].getType(), + it->second->d_value); + }else{ + incomplete = true; + } + Trace("ambqi-check-debug2") << " ->> " << values[it->first] << std::endl; + } + for( std::map< unsigned, int >::iterator it = bchildren.begin(); it != bchildren.end(); ++it ){ + Trace("ambqi-check-debug2") << " basic : " << it->first << " : " << it->second; + if( it->second>=0 ){ + Assert(it->second + < (int)rs->getNumRepresentatives(n[it->first].getType())); + values[it->first] = + rs->getRepresentative(n[it->first].getType(), it->second); + }else{ + incomplete = true; + } + Trace("ambqi-check-debug2") << " ->> " << values[it->first] << std::endl; + } + Assert( vchildren.empty() ); + if( incomplete ){ + Trace("ambqi-check-debug2") << "Construct incomplete entry." << std::endl; + + //if a child is unknown, we must return unknown + construct_entry( entry, entry_def, val_unk ); + }else{ + if( Trace.isOn("ambqi-check-debug2") ){ + for( unsigned i=0; imkNode( n.getKind(), values ); + vv = Rewriter::rewrite( vv ); + int v = m->getRepresentativeId( vv ); + construct_entry( entry, entry_def, v ); + } + } + }else{ + //take product of arguments + TypeNode tn = m->getVariable( q, entry.size() ).getType(); + Assert( m->isValidType( tn ) ); + unsigned def = m->d_domain[tn]; + if( Trace.isOn("ambqi-check-debug2") ){ + for( unsigned i=0; i::iterator it = children.begin(); it != children.end(); ++it ){ + Assert( it->second!=NULL ); + //process each child + for( std::map< unsigned, AbsDef >::iterator itd = it->second->d_def.begin(); itd != it->second->d_def.end(); ++itd ){ + if( itd->first!=it->second->d_default && ( def & itd->first )!=0 ){ + def &= ~( itd->first ); + //process this value + std::map< unsigned, AbsDef * > cchildren; + for( std::map< unsigned, AbsDef * >::iterator it2 = children.begin(); it2 != children.end(); ++it2 ){ + Assert( it2->second!=NULL ); + std::map< unsigned, AbsDef >::iterator itdf = it2->second->d_def.find( itd->first ); + if( itdf!=it2->second->d_def.end() ){ + cchildren[it2->first] = &itdf->second; + }else{ + Assert( it2->second->getDefault()!=NULL ); + cchildren[it2->first] = it2->second->getDefault(); + } + } + if( Trace.isOn("ambqi-check-debug2") ){ + for( unsigned i=0; igetNumRepresentatives(tn), + itd->first); + Trace("ambqi-check-debug2") << " " << children.size() << " " << cchildren.size() << std::endl; + } + entry.push_back( itd->first ); + entry_def.push_back( def==0 ); + construct_compose( m, q, n, f, cchildren, bchildren, vchildren, entry, entry_def ); + entry_def.pop_back(); + entry.pop_back(); + if( def==0 ){ + break; + } + } + } + if( def==0 ){ + break; + } + } + if( def!=0 ){ + if( Trace.isOn("ambqi-check-debug2") ){ + for( unsigned i=0; i cdchildren; + for( std::map< unsigned, AbsDef * >::iterator it = children.begin(); it != children.end(); ++it ){ + Assert( it->second->getDefault()!=NULL ); + cdchildren[it->first] = it->second->getDefault(); + } + if( Trace.isOn("ambqi-check-debug2") ){ + for( unsigned i=0; igetNumRepresentatives(tn), def); + Trace("ambqi-check-debug2") << " " << children.size() << " " << cdchildren.size() << std::endl; + } + entry.push_back( def ); + entry_def.push_back( true ); + construct_compose( m, q, n, f, cdchildren, bchildren, vchildren, entry, entry_def ); + entry_def.pop_back(); + entry.pop_back(); + } + } +} + +bool AbsDef::construct( FirstOrderModelAbs * m, TNode q, TNode n, AbsDef * f, + std::map< unsigned, AbsDef * >& children, + std::map< unsigned, int >& bchildren, std::map< unsigned, int >& vchildren, + int varChCount ) { + if( Trace.isOn("ambqi-check-debug3") ){ + for( unsigned i=0; i::iterator it = bchildren.begin(); it !=bchildren.end(); ++it ){ + if( ( it->second==0 && n.getKind()==AND ) || + ( it->second==1 && n.getKind()==OR ) ){ + construct_def_entry( m, q, q[0], it->second ); + return true; + } + } + } + Trace("ambqi-check-debug2") << "Construct compose..." << std::endl; + std::vector< unsigned > entry; + std::vector< bool > entry_def; + if( f && varChCount>0 ){ + AbsDef unorm; + unorm.construct_compose( m, q, n, f, children, bchildren, vchildren, entry, entry_def ); + //normalize + std::vector< AbsDef* > defs; + defs.push_back( &unorm ); + construct_normalize( m, q, defs ); + }else{ + construct_compose( m, q, n, f, children, bchildren, vchildren, entry, entry_def ); + } + Assert( is_normalized() ); + //if( !is_normalized() ){ + // std::cout << "NON NORMALIZED DEFINITION" << std::endl; + // exit( 10 ); + //} + return true; + }else if( varChCount==1 && ( n.getKind()==EQUAL && !n[0].getType().isBoolean() ) ){ + Trace("ambqi-check-debug2") << "Expand variable child..." << std::endl; + //expand the variable based on its finite domain + AbsDef a; + a.construct_var( m, q, vchildren.begin()->second, val_none ); + children[vchildren.begin()->first] = &a; + vchildren.clear(); + std::vector< unsigned > entry; + std::vector< bool > entry_def; + Trace("ambqi-check-debug2") << "Construct compose with variable..." << std::endl; + construct_compose( m, q, n, f, children, bchildren, vchildren, entry, entry_def ); + return true; + }else if( varChCount==2 && ( n.getKind()==EQUAL && !n[0].getType().isBoolean() ) ){ + Trace("ambqi-check-debug2") << "Construct variable equality..." << std::endl; + //efficient expansion of the equality + construct_var_eq( m, q, vchildren[0], vchildren[1], val_none, val_none ); + return true; + }else{ + return false; + } +} + +void AbsDef::negate() { + for( std::map< unsigned, AbsDef >::iterator it = d_def.begin(); it != d_def.end(); ++it ){ + it->second.negate(); + } + if( d_value==0 ){ + d_value = 1; + }else if( d_value==1 ){ + d_value = 0; + } +} + +Node AbsDef::getFunctionValue( FirstOrderModelAbs * m, TNode op, std::vector< Node >& vars, unsigned depth ) { + const RepSet* rs = m->getRepSet(); + if( depth==vars.size() ){ + TypeNode tn = op.getType(); + if( tn.getNumChildren()>0 ){ + tn = tn[tn.getNumChildren() - 1]; + } + if( d_value>=0 ){ + Assert(d_value < (int)rs->getNumRepresentatives(tn)); + if( tn.isBoolean() ){ + return NodeManager::currentNM()->mkConst( d_value==1 ); + }else{ + return rs->getRepresentative(tn, d_value); + } + }else{ + return Node::null(); + } + }else{ + TypeNode tn = vars[depth].getType(); + Node curr; + curr = d_def[d_default].getFunctionValue( m, op, vars, depth+1 ); + for( std::map< unsigned, AbsDef >::iterator it = d_def.begin(); it != d_def.end(); ++it ){ + if( it->first!=d_default ){ + unsigned id = getId( it->first ); + Assert(id < rs->getNumRepresentatives(tn)); + TNode n = rs->getRepresentative(tn, id); + Node fv = it->second.getFunctionValue( m, op, vars, depth+1 ); + if( !curr.isNull() && !fv.isNull() ){ + curr = NodeManager::currentNM()->mkNode( ITE, vars[depth].eqNode( n ), fv, curr ); + }else{ + curr = Node::null(); + } + } + } + return curr; + } +} + +bool AbsDef::isSimple( unsigned n ) { + return (n & (n - 1))==0; +} + +unsigned AbsDef::getId( unsigned n, unsigned start, unsigned end ) { + Assert( n!=0 ); + while( (n & ( 1 << start )) == 0 ){ + start++; + if( start==end ){ + return start; + } + } + return start; +} + +Node AbsDef::evaluate( FirstOrderModelAbs * m, TypeNode retTyp, std::vector< Node >& args ) { + std::vector< unsigned > iargs; + for( unsigned i=0; igetRepresentativeId( args[i] ); + iargs.push_back( v ); + } + return evaluate( m, retTyp, iargs, 0 ); +} + +Node AbsDef::evaluate( FirstOrderModelAbs * m, TypeNode retTyp, std::vector< unsigned >& iargs, unsigned depth ) { + if( d_value!=val_none ){ + if( d_value==val_unk ){ + return Node::null(); + }else{ + const RepSet* rs = m->getRepSet(); + Assert(d_value >= 0 && d_value < (int)rs->getNumRepresentatives(retTyp)); + return rs->getRepresentative(retTyp, d_value); + } + }else{ + std::map< unsigned, AbsDef >::iterator it = d_def.find( iargs[depth] ); + if( it==d_def.end() ){ + return d_def[d_default].evaluate( m, retTyp, iargs, depth+1 ); + }else{ + return it->second.evaluate( m, retTyp, iargs, depth+1 ); + } + } +} + +bool AbsDef::is_normalized() { + for( std::map< unsigned, AbsDef >::iterator it1 = d_def.begin(); it1 != d_def.end(); ++it1 ){ + if( !it1->second.is_normalized() ){ + return false; + } + for( std::map< unsigned, AbsDef >::iterator it2 = d_def.begin(); it2 != d_def.end(); ++it2 ){ + if( it1->first!=it2->first && (( it1->first & it2->first )!=0) ){ + return false; + } + } + } + return true; +} + +AbsMbqiBuilder::AbsMbqiBuilder( context::Context* c, QuantifiersEngine* qe ) : +QModelBuilder( c, qe ){ + d_true = NodeManager::currentNM()->mkConst( true ); + d_false = NodeManager::currentNM()->mkConst( false ); +} + + +//------------------------model construction---------------------------- + +bool AbsMbqiBuilder::processBuildModel(TheoryModel* m) { + Trace("ambqi-debug") << "process build model " << std::endl; + FirstOrderModel* f = (FirstOrderModel*)m; + FirstOrderModelAbs* fm = f->asFirstOrderModelAbs(); + RepSet* rs = m->getRepSetPtr(); + fm->initialize(); + //process representatives + fm->d_rep_id.clear(); + fm->d_domain.clear(); + + //initialize boolean sort + TypeNode b = d_true.getType(); + rs->d_type_reps[b].clear(); + rs->d_type_reps[b].push_back(d_false); + rs->d_type_reps[b].push_back(d_true); + fm->d_rep_id[d_false] = 0; + fm->d_rep_id[d_true] = 1; + + //initialize unintpreted sorts + Trace("ambqi-model") << std::endl << "Making representatives..." << std::endl; + for (std::map >::iterator it = + rs->d_type_reps.begin(); + it != rs->d_type_reps.end(); + ++it) + { + if( it->first.isSort() ){ + Assert( !it->second.empty() ); + //set the domain + fm->d_domain[it->first] = 0; + Trace("ambqi-model") << "Representatives for " << it->first << " : " << std::endl; + for( unsigned i=0; isecond.size(); i++ ){ + if( i<32 ){ + fm->d_domain[it->first] |= ( 1 << i ); + } + Trace("ambqi-model") << i << " : " << it->second[i] << std::endl; + fm->d_rep_id[it->second[i]] = i; + } + if( it->second.size()>=32 ){ + fm->d_domain.erase( it->first ); + } + } + } + + Trace("ambqi-model") << std::endl << "Making function definitions..." << std::endl; + //construct the models for functions + for( std::map::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ) { + Node f = it->first; + Trace("ambqi-model-debug") << "Building Model for " << f << std::endl; + //reset the model + it->second->clear(); + //get all (non-redundant) f-applications + std::vector< TNode > fapps; + Trace("ambqi-model-debug") << "Initial terms: " << std::endl; + std::map< Node, std::vector< Node > >::iterator itut = fm->d_uf_terms.find( f ); + if( itut!=fm->d_uf_terms.end() ){ + for( size_t i=0; isecond.size(); i++ ){ + Node n = itut->second[i]; + // only consider unique up to congruence (in model equality engine)? + Trace("ambqi-model-debug") << " " << n << " -> " << fm->getRepresentativeId( n ) << std::endl; + fapps.push_back( n ); + } + } + if( fapps.empty() ){ + //choose arbitrary value + Node mbt = fm->getModelBasisOpTerm(f); + Trace("ambqi-model-debug") << "Initial terms empty, add " << mbt << std::endl; + fapps.push_back( mbt ); + } + bool fValid = true; + for( unsigned i=0; id_domain.find( fapps[0][i].getType() )==fm->d_domain.end() ){ + Trace("ambqi-model") << "Interpretation of " << f << " is not valid."; + Trace("ambqi-model") << " (domain for " << fapps[0][i].getType() << " is too large)." << std::endl; + fValid = false; + break; + } + } + fm->d_models_valid[f] = fValid; + if( fValid ){ + //construct the ambqi model + it->second->construct_func( fm, fapps ); + Trace("ambqi-model-debug") << "Interpretation of " << f << " : " << std::endl; + it->second->debugPrint("ambqi-model-debug", fm, fapps[0] ); + Trace("ambqi-model-debug") << "Simplifying " << f << "..." << std::endl; + it->second->simplify( fm, TNode::null(), fapps[0] ); + Trace("ambqi-model") << "(Simplified) interpretation of " << f << " : " << std::endl; + it->second->debugPrint("ambqi-model", fm, fapps[0] ); + +/* + if( Debug.isOn("ambqi-model-debug") ){ + for( size_t i=0; id_uf_terms[f].size(); i++ ){ + Node e = it->second->evaluate_n( fm, fm->d_uf_terms[f][i] ); + Debug("ambqi-model-debug") << fm->d_uf_terms[f][i] << " evaluates to " << e << std::endl; + Assert( fm->areEqual( e, fm->d_uf_terms[f][i] ) ); + } + } +*/ + } + } + Trace("ambqi-model") << "Construct model representation..." << std::endl; + //make function values + for( std::map::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ) { + if( it->first.getType().getNumChildren()>1 ){ + Trace("ambqi-model") << "Construct for " << it->first << "..." << std::endl; + Node f_def = fm->getFunctionValue( it->first, "$x" ); + m->assignFunctionDefinition( it->first, f_def ); + } + } + Assert( d_addedLemmas==0 ); + return TheoryEngineModelBuilder::processBuildModel( m ); +} + + +//--------------------model checking--------------------------------------- + +//do exhaustive instantiation +int AbsMbqiBuilder::doExhaustiveInstantiation( FirstOrderModel * fm, Node q, int effort ) { + Trace("ambqi-check") << "Exhaustive instantiation " << q << " " << effort << std::endl; + if (effort==0) { + FirstOrderModelAbs * fma = fm->asFirstOrderModelAbs(); + bool quantValid = true; + for( unsigned i=0; iisValidType( q[0][i].getType() ) ){ + quantValid = false; + Trace("ambqi-inst") << "Interpretation of " << q << " is not valid because of type " << q[0][i].getType() << std::endl; + break; + } + } + if( quantValid ){ + Trace("ambqi-check") << "Compute interpretation..." << std::endl; + AbsDef ad; + doCheck( fma, q, ad, q[1] ); + //now process entries + Trace("ambqi-inst-debug") << "...Current : " << d_addedLemmas << std::endl; + Trace("ambqi-inst") << "Interpretation of " << q << " is : " << std::endl; + ad.debugPrint( "ambqi-inst", fma, q[0] ); + Trace("ambqi-inst") << std::endl; + Trace("ambqi-check") << "Add instantiations..." << std::endl; + int lem = 0; + quantValid = ad.addInstantiations( fma, d_qe, q, lem ); + Trace("ambqi-inst") << "...Added " << lem << " lemmas." << std::endl; + if( lem>0 ){ + //if we were incomplete but added at least one lemma, we are ok + quantValid = true; + } + d_addedLemmas += lem; + Trace("ambqi-inst-debug") << "...Total : " << d_addedLemmas << std::endl; + } + return quantValid ? 1 : 0; + }else{ + return 1; + } +} + +bool AbsMbqiBuilder::doCheck( FirstOrderModelAbs * m, TNode q, AbsDef & ad, TNode n ) { + Assert( n.getKind()!=FORALL ); + if( n.getKind()==NOT && n[0].getKind()!=FORALL ){ + doCheck( m, q, ad, n[0] ); + ad.negate(); + return true; + }else{ + std::map< unsigned, AbsDef > children; + std::map< unsigned, int > bchildren; + std::map< unsigned, int > vchildren; + int varChCount = 0; + for( unsigned i=0; id_var_index[q][ m->getVariableId( q, n[i] ) ]; + //vchildren[i] = m->getVariableId( q, n[i] ); + }else if( m->hasTerm( n[i] ) ){ + bchildren[i] = m->getRepresentativeId( n[i] ); + }else{ + if( !doCheck( m, q, children[i], n[i] ) ){ + bchildren[i] = AbsDef::val_unk; + children.erase( i ); + } + } + } + //convert to pointers + std::map< unsigned, AbsDef * > pchildren; + for( std::map< unsigned, AbsDef >::iterator it = children.begin(); it != children.end(); ++it ){ + pchildren[it->first] = &it->second; + } + //construct the interpretation + Trace("ambqi-check-debug") << "Compute Interpretation of " << n << " " << n.getKind() << std::endl; + if( n.getKind() == APPLY_UF || n.getKind() == VARIABLE || n.getKind() == SKOLEM ){ + Node op; + if( n.getKind() == APPLY_UF ){ + op = n.getOperator(); + }else{ + op = n; + } + //uninterpreted compose + if( m->d_models_valid[op] ){ + ad.construct( m, q, n, m->d_models[op], pchildren, bchildren, vchildren, varChCount ); + }else{ + Trace("ambqi-check-debug") << "** Cannot produce interpretation for " << n << " (no function model)" << std::endl; + return false; + } + }else if( !ad.construct( m, q, n, NULL, pchildren, bchildren, vchildren, varChCount ) ){ + Trace("ambqi-check-debug") << "** Cannot produce interpretation for " << n << " (variables are children of interpreted symbol)" << std::endl; + return false; + } + Trace("ambqi-check-try") << "Interpretation for " << n << " is : " << std::endl; + ad.debugPrint("ambqi-check-try", m, q[0] ); + ad.simplify( m, q, q[0] ); + Trace("ambqi-check-debug") << "(Simplified) Interpretation for " << n << " is : " << std::endl; + ad.debugPrint("ambqi-check-debug", m, q[0] ); + Trace("ambqi-check-debug") << std::endl; + return true; + } +} + +}/* namespace CVC4::theory::quantifiers */ +}/* namespace CVC4::theory */ +}/* namespace CVC4 */ diff --git a/src/theory/quantifiers/fmf/ambqi_builder.h b/src/theory/quantifiers/fmf/ambqi_builder.h new file mode 100644 index 000000000..914da14b4 --- /dev/null +++ b/src/theory/quantifiers/fmf/ambqi_builder.h @@ -0,0 +1,105 @@ +/********************* */ +/*! \file ambqi_builder.h + ** \verbatim + ** Top contributors (to current version): + ** Morgan Deters, Tim King, 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 Abstract MBQI model builder class + **/ + +#include "cvc4_private.h" + +#ifndef ABSTRACT_MBQI_BUILDER +#define ABSTRACT_MBQI_BUILDER + +#include "theory/quantifiers/fmf/model_builder.h" +#include "theory/quantifiers/first_order_model.h" + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +class FirstOrderModelAbs; + +//representiation of function and term interpretations +class AbsDef +{ +private: + bool addInstantiations( FirstOrderModelAbs * m, QuantifiersEngine * qe, TNode q, std::vector< Node >& terms, int& inst, unsigned depth ); + void construct_compose( FirstOrderModelAbs * m, TNode q, TNode n, AbsDef * f, + std::map< unsigned, AbsDef * >& children, + std::map< unsigned, int >& bchildren, std::map< unsigned, int >& vchildren, + std::vector< unsigned >& entry, std::vector< bool >& entry_def ); + void construct_entry( std::vector< unsigned >& entry, std::vector< bool >& entry_def, int v, unsigned depth = 0 ); + void construct_def_entry( FirstOrderModelAbs * m, TNode q, TNode n, int v, unsigned depth = 0 ); + void apply_ucompose( FirstOrderModelAbs * m, TNode q, + std::vector< unsigned >& entry, std::vector< bool >& entry_def, std::vector< int >& terms, + std::map< unsigned, int >& vchildren, AbsDef * a, unsigned depth = 0 ); + void construct_var_eq( FirstOrderModelAbs * m, TNode q, unsigned v1, unsigned v2, int curr, int currv, unsigned depth = 0 ); + void construct_var( FirstOrderModelAbs * m, TNode q, unsigned v, int currv, unsigned depth = 0 ); + void get_defs( unsigned u, std::vector< AbsDef * >& defs ); + void construct_normalize( FirstOrderModelAbs * m, TNode q, std::vector< AbsDef * >& defs, unsigned depth = 0 ); +public: + enum { + val_none = -1, + val_unk = -2, + }; + AbsDef() : d_default( 0 ), d_value( -1 ){} + std::map< unsigned, AbsDef > d_def; + unsigned d_default; + int d_value; + + void clear() { d_def.clear(); d_default = 0; d_value = -1; } + AbsDef * getDefault() { return &d_def[d_default]; } + void construct_func( FirstOrderModelAbs * m, std::vector< TNode >& fapps, unsigned depth = 0 ); + void debugPrintUInt( const char * c, unsigned dSize, unsigned u ) const; + void debugPrint( const char * c, FirstOrderModelAbs * m, TNode f, unsigned depth = 0 ) const; + void simplify( FirstOrderModelAbs * m, TNode q, TNode n, unsigned depth = 0 ); + int addInstantiations( FirstOrderModelAbs * m, QuantifiersEngine * qe, Node q, int& inst ){ + std::vector< Node > terms; + terms.resize( q[0].getNumChildren() ); + return addInstantiations( m, qe, q, terms, inst, 0 ); + } + bool construct( FirstOrderModelAbs * m, TNode q, TNode n, AbsDef * f, + std::map< unsigned, AbsDef * >& children, + std::map< unsigned, int >& bchildren, + std::map< unsigned, int >& vchildren, + int varChCount ); + void negate(); + Node getFunctionValue( FirstOrderModelAbs * m, TNode op, std::vector< Node >& vars, unsigned depth = 0 ); + static bool isSimple( unsigned n ); + static unsigned getId( unsigned n, unsigned start=0, unsigned end=32 ); + Node evaluate( FirstOrderModelAbs * m, TypeNode retType, std::vector< Node >& args ); + Node evaluate( FirstOrderModelAbs * m, TypeNode retType, std::vector< unsigned >& iargs, unsigned depth = 0 ); + //for debugging + bool is_normalized(); +}; + +class AbsMbqiBuilder : public QModelBuilder +{ + friend class AbsDef; +private: + Node d_true; + Node d_false; + bool doCheck( FirstOrderModelAbs * m, TNode q, AbsDef & ad, TNode n ); +public: + AbsMbqiBuilder( context::Context* c, QuantifiersEngine* qe ); + + //process build model + bool processBuildModel(TheoryModel* m) override; + //do exhaustive instantiation + int doExhaustiveInstantiation(FirstOrderModel* fm, + Node q, + int effort) override; +}; + +} +} +} + +#endif diff --git a/src/theory/quantifiers/fmf/bounded_integers.cpp b/src/theory/quantifiers/fmf/bounded_integers.cpp new file mode 100644 index 000000000..b1e9c2a34 --- /dev/null +++ b/src/theory/quantifiers/fmf/bounded_integers.cpp @@ -0,0 +1,893 @@ +/********************* */ +/*! \file bounded_integers.cpp + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds, Morgan Deters, 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 Bounded integers module + ** + ** This class manages integer bounds for quantifiers + **/ + +#include "theory/quantifiers/fmf/bounded_integers.h" +#include "options/quantifiers_options.h" +#include "theory/arith/arith_msum.h" +#include "theory/quantifiers/first_order_model.h" +#include "theory/quantifiers/fmf/model_engine.h" +#include "theory/quantifiers/term_enumeration.h" +#include "theory/quantifiers/term_util.h" +#include "theory/theory_engine.h" + +using namespace CVC4; +using namespace std; +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; +using namespace CVC4::kind; + + +BoundedIntegers::IntRangeModel::IntRangeModel(BoundedIntegers * bi, Node r, context::Context* c, context::Context* u, bool isProxy) : d_bi(bi), + d_range(r), d_curr_max(-1), d_lit_to_range(u), d_range_assertions(c), d_has_range(c,false), d_curr_range(c,-1), d_ranges_proxied(u) { + if( options::fmfBoundLazy() ){ + d_proxy_range = isProxy ? r : NodeManager::currentNM()->mkSkolem( "pbir", r.getType() ); + }else{ + d_proxy_range = r; + } + if( !isProxy ){ + Trace("bound-int") << "Introduce proxy " << d_proxy_range << " for " << d_range << std::endl; + } +} + +void BoundedIntegers::IntRangeModel::initialize() { + //add initial split lemma + Node ltr = NodeManager::currentNM()->mkNode( LT, d_proxy_range, NodeManager::currentNM()->mkConst( Rational(0) ) ); + ltr = Rewriter::rewrite( ltr ); + Trace("bound-int-lemma") << " *** bound int: initial split on " << ltr << std::endl; + d_bi->getQuantifiersEngine()->getOutputChannel().split( ltr ); + Node ltr_lit = ltr.getKind()==NOT ? ltr[0] : ltr; + d_range_literal[-1] = ltr_lit; + d_lit_to_range[ltr_lit] = -1; + d_lit_to_pol[ltr_lit] = ltr.getKind()!=NOT; + //register with bounded integers + Trace("bound-int-debug") << "Literal " << ltr_lit << " is literal for " << d_range << std::endl; + d_bi->addLiteralFromRange(ltr_lit, d_range); +} + +void BoundedIntegers::IntRangeModel::assertNode(Node n) { + bool pol = n.getKind()!=NOT; + Node nlit = n.getKind()==NOT ? n[0] : n; + if( d_lit_to_range.find( nlit )!=d_lit_to_range.end() ){ + int vrange = d_lit_to_range[nlit]; + Trace("bound-int-assert") << "With polarity = " << pol << " (req "<< d_lit_to_pol[nlit] << ")"; + Trace("bound-int-assert") << ", found literal " << nlit; + Trace("bound-int-assert") << ", it is bound literal " << vrange << " for " << d_range << std::endl; + d_range_assertions[nlit] = (pol==d_lit_to_pol[nlit]); + if( pol!=d_lit_to_pol[nlit] ){ + //check if we need a new split? + if( !d_has_range ){ + bool needsRange = true; + for( NodeIntMap::iterator it = d_lit_to_range.begin(); it != d_lit_to_range.end(); ++it ){ + if( d_range_assertions.find( (*it).first )==d_range_assertions.end() ){ + Trace("bound-int-debug") << "Does not need range because of " << (*it).first << std::endl; + needsRange = false; + break; + } + } + if( needsRange ){ + allocateRange(); + } + } + }else{ + if (!d_has_range || vrangemkNode( LEQ, d_proxy_range, NodeManager::currentNM()->mkConst( Rational(newBound) ) ); + ltr = Rewriter::rewrite( ltr ); + Trace("bound-int-lemma") << " *** bound int: split on " << ltr << std::endl; + d_bi->getQuantifiersEngine()->getOutputChannel().split( ltr ); + Node ltr_lit = ltr.getKind()==NOT ? ltr[0] : ltr; + d_range_literal[newBound] = ltr_lit; + d_lit_to_range[ltr_lit] = newBound; + d_lit_to_pol[ltr_lit] = ltr.getKind()!=NOT; + //register with bounded integers + d_bi->addLiteralFromRange(ltr_lit, d_range); +} + +Node BoundedIntegers::IntRangeModel::getNextDecisionRequest() { + //request the current cardinality as a decision literal, if not already asserted + for( NodeIntMap::iterator it = d_lit_to_range.begin(); it != d_lit_to_range.end(); ++it ){ + int i = (*it).second; + if( !d_has_range || imkNode( EQUAL, d_range_literal[curr].negate(), + NodeManager::currentNM()->mkNode( LEQ, d_range, NodeManager::currentNM()->mkConst( Rational(curr) ) ) ); + Trace("bound-int-lemma") << "*** bound int : proxy lemma : " << lem << std::endl; + d_bi->getQuantifiersEngine()->addLemma( lem ); + return true; + } + } + return false; +} + + + + + +BoundedIntegers::BoundedIntegers(context::Context* c, QuantifiersEngine* qe) : +QuantifiersModule(qe), d_assertions(c){ + +} + +BoundedIntegers::~BoundedIntegers() { + for( std::map< Node, RangeModel * >::iterator it = d_rms.begin(); it != d_rms.end(); ++it ){ + delete it->second; + } +} + +void BoundedIntegers::presolve() { + d_bnd_it.clear(); +} + +bool BoundedIntegers::isBound( Node f, Node v ) { + return std::find( d_set[f].begin(), d_set[f].end(), v )!=d_set[f].end(); +} + +bool BoundedIntegers::hasNonBoundVar( Node f, Node b, std::map< Node, bool >& visited ) { + if( visited.find( b )==visited.end() ){ + visited[b] = true; + if( b.getKind()==BOUND_VARIABLE ){ + if( !isBound( f, b ) ){ + return true; + } + }else{ + for( unsigned i=0; i visited; + return hasNonBoundVar( f, b, visited ); +} + +bool BoundedIntegers::processEqDisjunct( Node q, Node n, Node& v, std::vector< Node >& v_cases ) { + if( n.getKind()==EQUAL ){ + for( unsigned i=0; i<2; i++ ){ + Node t = n[i]; + if( !hasNonBoundVar( q, n[1-i] ) ){ + if( t==v ){ + v_cases.push_back( n[1-i] ); + return true; + }else if( v.isNull() && t.getKind()==BOUND_VARIABLE ){ + v = t; + v_cases.push_back( n[1-i] ); + return true; + } + } + } + } + return false; +} + +void BoundedIntegers::processMatchBoundVars( Node q, Node n, std::vector< Node >& bvs, std::map< Node, bool >& visited ){ + if( visited.find( n )==visited.end() ){ + visited[n] = true; + if( n.getKind()==BOUND_VARIABLE && !isBound( q, n ) ){ + bvs.push_back( n ); + //injective operators + }else if( n.getKind()==kind::APPLY_CONSTRUCTOR ){ + for( unsigned i=0; i& bound_lit_type_map, + std::map< int, std::map< Node, Node > >& bound_lit_map, + std::map< int, std::map< Node, bool > >& bound_lit_pol_map, + std::map< int, std::map< Node, Node > >& bound_int_range_term, + std::map< Node, std::vector< Node > >& bound_fixed_set ){ + if( n.getKind()==OR || n.getKind()==AND ){ + if( (n.getKind()==OR)==pol ){ + for( unsigned i=0; i v_cases; + bool success = true; + for( unsigned i=0; i v_cases; + if( processEqDisjunct( q, n, v, v_cases ) ){ + if( !isBound( q, v ) ){ + bound_lit_type_map[v] = BOUND_FIXED_SET; + bound_lit_map[3][v] = n; + bound_lit_pol_map[3][v] = pol; + Assert( v_cases.size()==1 ); + bound_fixed_set[v].clear(); + bound_fixed_set[v].push_back( v_cases[0] ); + } + } + } + }else if( n.getKind()==NOT ){ + process( q, n[0], !pol, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term, bound_fixed_set ); + }else if( n.getKind()==GEQ ){ + if( n[0].getType().isInteger() ){ + std::map< Node, Node > msum; + if (ArithMSum::getMonomialSumLit(n, msum)) + { + Trace("bound-int-debug") << "literal (polarity = " << pol << ") " << n << " is monomial sum : " << std::endl; + ArithMSum::debugPrintMonomialSum(msum, "bound-int-debug"); + for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){ + if ( !it->first.isNull() && it->first.getKind()==BOUND_VARIABLE && !isBound( q, it->first ) ){ + //if not bound in another way + if( bound_lit_type_map.find( it->first )==bound_lit_type_map.end() || bound_lit_type_map[it->first] == BOUND_INT_RANGE ){ + Node veq; + if (ArithMSum::isolate(it->first, msum, veq, GEQ) != 0) + { + Node n1 = veq[0]; + Node n2 = veq[1]; + if(pol){ + //flip + n1 = veq[1]; + n2 = veq[0]; + if( n1.getKind()==BOUND_VARIABLE ){ + n2 = ArithMSum::offset(n2, 1); + }else{ + n1 = ArithMSum::offset(n1, -1); + } + veq = NodeManager::currentNM()->mkNode( GEQ, n1, n2 ); + } + Trace("bound-int-debug") << "Isolated for " << it->first << " : (" << n1 << " >= " << n2 << ")" << std::endl; + Node t = n1==it->first ? n2 : n1; + if( !hasNonBoundVar( q, t ) ) { + Trace("bound-int-debug") << "The bound is relevant." << std::endl; + int loru = n1==it->first ? 0 : 1; + bound_lit_type_map[it->first] = BOUND_INT_RANGE; + bound_int_range_term[loru][it->first] = t; + bound_lit_map[loru][it->first] = n; + bound_lit_pol_map[loru][it->first] = pol; + }else{ + Trace("bound-int-debug") << "The term " << t << " has non-bound variable." << std::endl; + } + } + } + } + } + } + } + }else if( n.getKind()==MEMBER ){ + if( !pol && !hasNonBoundVar( q, n[1] ) ){ + std::vector< Node > bound_vars; + std::map< Node, bool > visited; + processMatchBoundVars( q, n[0], bound_vars, visited ); + for( unsigned i=0; iproxyCurrentRange() ){ + addedLemma = true; + } + } + Trace("bint-engine") << " addedLemma = " << addedLemma << std::endl; + } +} + + +void BoundedIntegers::addLiteralFromRange( Node lit, Node r ) { + d_lit_to_ranges[lit].push_back(r); + //check if it is already asserted? + if(d_assertions.find(lit)!=d_assertions.end()){ + d_rms[r]->assertNode( d_assertions[lit] ? lit : lit.negate() ); + } +} + +void BoundedIntegers::setBoundedVar( Node q, Node v, unsigned bound_type ) { + d_bound_type[q][v] = bound_type; + d_set_nums[q][v] = d_set[q].size(); + d_set[q].push_back( v ); + Trace("bound-int-var") << "Bound variable #" << d_set_nums[q][v] << " : " << v << std::endl; +} + +void BoundedIntegers::preRegisterQuantifier( Node f ) { + //this needs to be done at preregister since it affects e.g. QuantDSplit's preregister + Trace("bound-int") << "preRegister quantifier " << f << std::endl; + + bool success; + do{ + std::map< Node, unsigned > bound_lit_type_map; + std::map< int, std::map< Node, Node > > bound_lit_map; + std::map< int, std::map< Node, bool > > bound_lit_pol_map; + std::map< int, std::map< Node, Node > > bound_int_range_term; + std::map< Node, std::vector< Node > > bound_fixed_set; + success = false; + process( f, f[1], true, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term, bound_fixed_set ); + //for( std::map< Node, Node >::iterator it = d_bounds[0][f].begin(); it != d_bounds[0][f].end(); ++it ){ + for( std::map< Node, unsigned >::iterator it = bound_lit_type_map.begin(); it != bound_lit_type_map.end(); ++it ){ + Node v = it->first; + if( !isBound( f, v ) ){ + bool setBoundVar = false; + if( it->second==BOUND_INT_RANGE ){ + //must have both + if( bound_lit_map[0].find( v )!=bound_lit_map[0].end() && bound_lit_map[1].find( v )!=bound_lit_map[1].end() ){ + setBoundedVar( f, v, BOUND_INT_RANGE ); + setBoundVar = true; + for( unsigned b=0; b<2; b++ ){ + //set the bounds + Assert( bound_int_range_term[b].find( v )!=bound_int_range_term[b].end() ); + d_bounds[b][f][v] = bound_int_range_term[b][v]; + } + if( options::fmfBoundMinMode()==FMF_BOUND_MIN_ALL || options::fmfBoundMinMode()==FMF_BOUND_MIN_INT_RANGE ){ + Node r = NodeManager::currentNM()->mkNode( MINUS, d_bounds[1][f][v], d_bounds[0][f][v] ); + d_range[f][v] = Rewriter::rewrite( r ); + } + Trace("bound-int") << "Variable " << v << " is bound because of int range literals " << bound_lit_map[0][v] << " and " << bound_lit_map[1][v] << std::endl; + } + }else if( it->second==BOUND_SET_MEMBER ){ + setBoundedVar( f, v, BOUND_SET_MEMBER ); + setBoundVar = true; + d_setm_range[f][v] = bound_lit_map[2][v][1]; + d_setm_range_lit[f][v] = bound_lit_map[2][v]; + if( options::fmfBoundMinMode()==FMF_BOUND_MIN_ALL || options::fmfBoundMinMode()==FMF_BOUND_MIN_SET_CARD ){ + d_range[f][v] = NodeManager::currentNM()->mkNode( CARD, d_setm_range[f][v] ); + } + Trace("bound-int") << "Variable " << v << " is bound because of set membership literal " << bound_lit_map[2][v] << std::endl; + }else if( it->second==BOUND_FIXED_SET ){ + setBoundedVar( f, v, BOUND_FIXED_SET ); + setBoundVar = true; + for( unsigned i=0; isecond!=BOUND_INT_RANGE ); + } + } + } + } + } + if( !success ){ + //resort to setting a finite bound on a variable + for( unsigned i=0; igetTermEnumeration()->mayComplete(tn)) + { + success = true; + setBoundedVar( f, f[0][i], BOUND_FINITE ); + break; + } + } + } + } + }while( success ); + + if( Trace.isOn("bound-int") ){ + Trace("bound-int") << "Bounds are : " << std::endl; + for( unsigned i=0; i::iterator itr = d_range[f].find( v ); + if( itr != d_range[f].end() ){ + Node r = itr->second; + Assert( !r.isNull() ); + bool isProxy = false; + if( r.hasBoundVar() ){ + //introduce a new bound + Node new_range = NodeManager::currentNM()->mkSkolem( "bir", r.getType(), "bound for term" ); + d_nground_range[f][v] = r; + d_range[f][v] = new_range; + r = new_range; + isProxy = true; + } + if( !r.isConst() ){ + if( std::find(d_ranges.begin(), d_ranges.end(), r)==d_ranges.end() ){ + Trace("bound-int") << "For " << v << ", bounded Integer Module will try to minimize : " << r << std::endl; + d_ranges.push_back( r ); + d_rms[r] = new IntRangeModel( this, r, d_quantEngine->getSatContext(), d_quantEngine->getUserContext(), isProxy ); + d_rms[r]->initialize(); + } + } + } + } + } +} + +void BoundedIntegers::registerQuantifier( Node q ) { + +} + +void BoundedIntegers::assertNode( Node n ) { + Trace("bound-int-assert") << "Assert " << n << std::endl; + Node nlit = n.getKind()==NOT ? n[0] : n; + if( d_lit_to_ranges.find(nlit)!=d_lit_to_ranges.end() ){ + Trace("bound-int-assert") << "This is the bounding literal for " << d_lit_to_ranges[nlit].size() << " ranges." << std::endl; + for( unsigned i=0; iassertNode( n ); + } + } + d_assertions[nlit] = n.getKind()!=NOT; +} + +Node BoundedIntegers::getNextDecisionRequest( unsigned& priority ) { + Trace("bound-int-dec-debug") << "bi: Get next decision request?" << std::endl; + for( unsigned i=0; igetNextDecisionRequest(); + if (!d.isNull()) { + bool polLit = d.getKind()!=NOT; + Node lit = d.getKind()==NOT ? d[0] : d; + bool value; + if( d_quantEngine->getValuation().hasSatValue( lit, value ) ) { + if( value==polLit ){ + Trace("bound-int-dec-debug") << "...already asserted properly." << std::endl; + //already true, we're already fine + }else{ + Trace("bound-int-dec-debug") << "...already asserted with wrong polarity, re-assert." << std::endl; + assertNode( d.negate() ); + i--; + } + }else{ + priority = 1; + Trace("bound-int-dec") << "Bounded Integers : Decide " << d << std::endl; + return d; + } + } + } + Trace("bound-int-dec-debug") << "No decision request." << std::endl; + return Node::null(); +} + +unsigned BoundedIntegers::getBoundVarType( Node q, Node v ) { + std::map< Node, unsigned >::iterator it = d_bound_type[q].find( v ); + if( it==d_bound_type[q].end() ){ + return BOUND_NONE; + }else{ + return it->second; + } +} + +void BoundedIntegers::getBounds( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ) { + l = d_bounds[0][f][v]; + u = d_bounds[1][f][v]; + if( d_nground_range[f].find(v)!=d_nground_range[f].end() ){ + //get the substitution + std::vector< Node > vars; + std::vector< Node > subs; + if( getRsiSubsitution( f, v, vars, subs, rsi ) ){ + u = u.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + l = l.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + }else{ + u = Node::null(); + l = Node::null(); + } + } +} + +void BoundedIntegers::getBoundValues( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ) { + getBounds( f, v, rsi, l, u ); + Trace("bound-int-rsi") << "Get value in model for..." << l << " and " << u << std::endl; + if( !l.isNull() ){ + l = d_quantEngine->getModel()->getValue( l ); + } + if( !u.isNull() ){ + u = d_quantEngine->getModel()->getValue( u ); + } + Trace("bound-int-rsi") << "Value is " << l << " ... " << u << std::endl; + return; +} + +bool BoundedIntegers::isGroundRange( Node q, Node v ) { + if( isBoundVar(q,v) ){ + if( d_bound_type[q][v]==BOUND_INT_RANGE ){ + return !getLowerBound(q,v).hasBoundVar() && !getUpperBound(q,v).hasBoundVar(); + }else if( d_bound_type[q][v]==BOUND_SET_MEMBER ){ + return !d_setm_range[q][v].hasBoundVar(); + }else if( d_bound_type[q][v]==BOUND_FIXED_SET ){ + return !d_fixed_set_ngr_range[q][v].empty(); + } + } + return false; +} + +Node BoundedIntegers::getSetRange( Node q, Node v, RepSetIterator * rsi ) { + Node sr = d_setm_range[q][v]; + if( d_nground_range[q].find(v)!=d_nground_range[q].end() ){ + //get the substitution + std::vector< Node > vars; + std::vector< Node > subs; + if( getRsiSubsitution( q, v, vars, subs, rsi ) ){ + sr = sr.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + }else{ + sr = Node::null(); + } + } + return sr; +} + +Node BoundedIntegers::getSetRangeValue( Node q, Node v, RepSetIterator * rsi ) { + Node sr = getSetRange( q, v, rsi ); + if( !sr.isNull() ){ + Trace("bound-int-rsi") << "Get value in model for..." << sr << std::endl; + sr = d_quantEngine->getModel()->getValue( sr ); + //if non-constant, then sr does not occur in the model, we fail + if( !sr.isConst() ){ + return Node::null(); + } + Trace("bound-int-rsi") << "Value is " << sr << std::endl; + //as heuristic, map to term model + if( sr.getKind()!=EMPTYSET ){ + std::map< Node, Node > val_to_term; + while( sr.getKind()==UNION ){ + Assert( sr[1].getKind()==kind::SINGLETON ); + val_to_term[ sr[1][0] ] = sr[1][0]; + sr = sr[0]; + } + Assert( sr.getKind()==kind::SINGLETON ); + val_to_term[ sr[0] ] = sr[0]; + //must look back at assertions, not term database (theory of sets introduces extraneous terms internally) + Theory* theory = d_quantEngine->getTheoryEngine()->theoryOf( THEORY_SETS ); + if( theory ){ + context::CDList::const_iterator it = theory->facts_begin(), it_end = theory->facts_end(); + for( unsigned i = 0; it != it_end; ++ it, ++i ){ + Node lit = (*it).assertion; + if( lit.getKind()==kind::MEMBER ){ + Node vr = d_quantEngine->getModel()->getValue( lit[0] ); + Trace("bound-int-rsi-debug") << "....membership for " << lit << " ==> " << vr << std::endl; + Trace("bound-int-rsi-debug") << " " << (val_to_term.find( vr )!=val_to_term.end()) << " " << d_quantEngine->getEqualityQuery()->areEqual( d_setm_range_lit[q][v][1], lit[1] ) << std::endl; + if( val_to_term.find( vr )!=val_to_term.end() ){ + if( d_quantEngine->getEqualityQuery()->areEqual( d_setm_range_lit[q][v][1], lit[1] ) ){ + Trace("bound-int-rsi") << " Map value to term : " << vr << " -> " << lit[0] << std::endl; + val_to_term[ vr ] = lit[0]; + } + } + } + } + } + //rebuild value + Node nsr; + for( std::map< Node, Node >::iterator it = val_to_term.begin(); it != val_to_term.end(); ++it ){ + Node nv = NodeManager::currentNM()->mkNode( kind::SINGLETON, it->second ); + if( nsr.isNull() ){ + nsr = nv; + }else{ + nsr = NodeManager::currentNM()->mkNode( kind::UNION, nsr, nv ); + } + } + Trace("bound-int-rsi") << "...reconstructed " << nsr << std::endl; + return nsr; + + /* + Node lit = d_setm_range_lit[q][v]; + Trace("bound-int-rsi-debug") << "Bounded from lit " << lit << std::endl; + Node f = d_quantEngine->getTermDatabase()->getMatchOperator( lit ); + TermArgTrie * ta = d_quantEngine->getTermDatabase()->getTermArgTrie( f ); + if( ta ){ + Trace("bound-int-rsi-debug") << "Got term index for " << f << std::endl; + for( std::map< TNode, TermArgTrie >::iterator it = ta->d_data.begin(); it != ta->d_data.end(); ++it ){ + + } + + } + */ + } + + } + return sr; +} + +bool BoundedIntegers::getRsiSubsitution( Node q, Node v, std::vector< Node >& vars, std::vector< Node >& subs, RepSetIterator * rsi ) { + + Trace("bound-int-rsi") << "Get bound value in model of variable " << v << std::endl; + Assert( d_set_nums[q].find( v )!=d_set_nums[q].end() ); + int vindex = d_set_nums[q][v]; + Assert( d_set_nums[q][v]==vindex ); + Trace("bound-int-rsi-debug") << " index order is " << vindex << std::endl; + //must take substitution for all variables that are iterating at higher level + for( int i=0; igetVariableOrder( i ); + Assert( q[0][v]==d_set[q][i] ); + Node t = rsi->getCurrentTerm(v, true); + Trace("bound-int-rsi") << "term : " << t << std::endl; + vars.push_back( d_set[q][i] ); + subs.push_back( t ); + } + + //check if it has been instantiated + if( !vars.empty() && !d_bnd_it[q][v].hasInstantiated(subs) ){ + if( d_bound_type[q][v]==BOUND_INT_RANGE || d_bound_type[q][v]==BOUND_SET_MEMBER ){ + //must add the lemma + Node nn = d_nground_range[q][v]; + nn = nn.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + Node lem = NodeManager::currentNM()->mkNode( LEQ, nn, d_range[q][v] ); + Trace("bound-int-lemma") << "*** Add lemma to minimize instantiated non-ground term " << lem << std::endl; + d_quantEngine->getOutputChannel().lemma(lem, false, true); + } + return false; + }else{ + return true; + } +} + +Node BoundedIntegers::matchBoundVar( Node v, Node t, Node e ){ + if( t==v ){ + return e; + }else if( t.getKind()==kind::APPLY_CONSTRUCTOR ){ + if( e.getKind()==kind::APPLY_CONSTRUCTOR ){ + if( t.getOperator() != e.getOperator() ) { + return Node::null(); + } + } + const Datatype& dt = Datatype::datatypeOf( t.getOperator().toExpr() ); + unsigned index = Datatype::indexOf( t.getOperator().toExpr() ); + for( unsigned i=0; imkNode( kind::APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[index].getSelectorInternal( e.getType().toType(), i ) ), e ); + u = matchBoundVar( v, t[i], se ); + } + if( !u.isNull() ){ + return u; + } + } + } + return Node::null(); +} + +bool BoundedIntegers::getBoundElements( RepSetIterator * rsi, bool initial, Node q, Node v, std::vector< Node >& elements ) { + if( initial || !isGroundRange( q, v ) ){ + elements.clear(); + unsigned bvt = getBoundVarType( q, v ); + if( bvt==BOUND_INT_RANGE ){ + Node l, u; + getBoundValues( q, v, rsi, l, u ); + if( l.isNull() || u.isNull() ){ + Trace("bound-int-warn") << "WARNING: Could not find integer bounds in model for " << v << " in " << q << std::endl; + //failed, abort the iterator + return false; + }else{ + Trace("bound-int-rsi") << "Can limit bounds of " << v << " to " << l << "..." << u << std::endl; + Node range = Rewriter::rewrite( NodeManager::currentNM()->mkNode( MINUS, u, l ) ); + Node ra = Rewriter::rewrite( NodeManager::currentNM()->mkNode( LEQ, range, NodeManager::currentNM()->mkConst( Rational( 9999 ) ) ) ); + Node tl = l; + Node tu = u; + getBounds( q, v, rsi, tl, tu ); + Assert( !tl.isNull() && !tu.isNull() ); + if( ra==d_quantEngine->getTermUtil()->d_true ){ + long rr = range.getConst().getNumerator().getLong()+1; + Trace("bound-int-rsi") << "Actual bound range is " << rr << std::endl; + for( unsigned k=0; kmkNode(PLUS, tl, NodeManager::currentNM()->mkConst( Rational(k) ) ); + t = Rewriter::rewrite( t ); + elements.push_back( t ); + } + return true; + }else{ + Trace("fmf-incomplete") << "Incomplete because of integer quantification, bounds are too big for " << v << "." << std::endl; + return false; + } + } + }else if( bvt==BOUND_SET_MEMBER ){ + Node srv = getSetRangeValue( q, v, rsi ); + if( srv.isNull() ){ + Trace("bound-int-warn") << "WARNING: Could not find set bound in model for " << v << " in " << q << std::endl; + return false; + }else{ + Trace("bound-int-rsi") << "Bounded by set membership : " << srv << std::endl; + if( srv.getKind()!=EMPTYSET ){ + //collect the elements + while( srv.getKind()==UNION ){ + Assert( srv[1].getKind()==kind::SINGLETON ); + elements.push_back( srv[1][0] ); + srv = srv[0]; + } + Assert( srv.getKind()==kind::SINGLETON ); + elements.push_back( srv[0] ); + //check if we need to do matching, for literals like ( tuple( v ) in S ) + Node t = d_setm_range_lit[q][v][0]; + if( t!=v ){ + std::vector< Node > elements_tmp; + elements_tmp.insert( elements_tmp.end(), elements.begin(), elements.end() ); + elements.clear(); + for( unsigned i=0; i u + Node u = matchBoundVar( v, t, elements_tmp[i] ); + Trace("bound-int-rsi-debug") << " unification : " << elements_tmp[i] << " = " << t << " yields " << v << " -> " << u << std::endl; + if( !u.isNull() ){ + elements.push_back( u ); + } + } + } + } + return true; + } + }else if( bvt==BOUND_FIXED_SET ){ + std::map< Node, std::vector< Node > >::iterator it = d_fixed_set_gr_range[q].find( v ); + if( it!=d_fixed_set_gr_range[q].end() ){ + for( unsigned i=0; isecond.size(); i++ ){ + elements.push_back( it->second[i] ); + } + } + it = d_fixed_set_ngr_range[q].find( v ); + if( it!=d_fixed_set_ngr_range[q].end() ){ + std::vector< Node > vars; + std::vector< Node > subs; + if( getRsiSubsitution( q, v, vars, subs, rsi ) ){ + for( unsigned i=0; isecond.size(); i++ ){ + Node t = it->second[i].substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + elements.push_back( t ); + } + return true; + }else{ + return false; + } + }else{ + return true; + } + }else{ + return false; + } + }else{ + //no change required + return true; + } +} + diff --git a/src/theory/quantifiers/fmf/bounded_integers.h b/src/theory/quantifiers/fmf/bounded_integers.h new file mode 100644 index 000000000..99d77a8e7 --- /dev/null +++ b/src/theory/quantifiers/fmf/bounded_integers.h @@ -0,0 +1,181 @@ +/********************* */ +/*! \file bounded_integers.h + ** \verbatim + ** Top contributors (to current version): + ** Morgan Deters, 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 + ** + ** [[ Add lengthier description here ]] + ** \todo document this file +**/ + +#include "cvc4_private.h" + +#ifndef __CVC4__BOUNDED_INTEGERS_H +#define __CVC4__BOUNDED_INTEGERS_H + + +#include "theory/quantifiers_engine.h" + +#include "context/context.h" +#include "context/context_mm.h" + +namespace CVC4 { +namespace theory { + +class RepSetIterator; + +namespace quantifiers { + + +class BoundedIntegers : public QuantifiersModule +{ + typedef context::CDHashMap NodeBoolMap; + typedef context::CDHashMap NodeIntMap; + typedef context::CDHashMap NodeNodeMap; + typedef context::CDHashMap IntBoolMap; +public: + enum { + BOUND_FINITE, + BOUND_INT_RANGE, + BOUND_SET_MEMBER, + BOUND_FIXED_SET, + BOUND_NONE + }; +private: + //for determining bounds + bool isBound( Node f, Node v ); + bool hasNonBoundVar( Node f, Node b, std::map< Node, bool >& visited ); + bool hasNonBoundVar( Node f, Node b ); + //bound type + std::map< Node, std::map< Node, unsigned > > d_bound_type; + std::map< Node, std::vector< Node > > d_set; + std::map< Node, std::map< Node, int > > d_set_nums; + std::map< Node, std::map< Node, Node > > d_range; + std::map< Node, std::map< Node, Node > > d_nground_range; + //integer lower/upper bounds + std::map< Node, std::map< Node, Node > > d_bounds[2]; + //set membership range + std::map< Node, std::map< Node, Node > > d_setm_range; + std::map< Node, std::map< Node, Node > > d_setm_range_lit; + //fixed finite set range + std::map< Node, std::map< Node, std::vector< Node > > > d_fixed_set_gr_range; + std::map< Node, std::map< Node, std::vector< Node > > > d_fixed_set_ngr_range; + void hasFreeVar( Node f, Node n ); + void process( Node q, Node n, bool pol, + std::map< Node, unsigned >& bound_lit_type_map, + std::map< int, std::map< Node, Node > >& bound_lit_map, + std::map< int, std::map< Node, bool > >& bound_lit_pol_map, + std::map< int, std::map< Node, Node > >& bound_int_range_term, + std::map< Node, std::vector< Node > >& bound_fixed_set ); + bool processEqDisjunct( Node q, Node n, Node& v, std::vector< Node >& v_cases ); + void processMatchBoundVars( Node q, Node n, std::vector< Node >& bvs, std::map< Node, bool >& visited ); + std::vector< Node > d_bound_quants; +private: + class RangeModel { + public: + RangeModel(){} + virtual ~RangeModel(){} + virtual void initialize() = 0; + virtual void assertNode(Node n) = 0; + virtual Node getNextDecisionRequest() = 0; + virtual bool proxyCurrentRange() = 0; + }; + class IntRangeModel : public RangeModel { + private: + BoundedIntegers * d_bi; + void allocateRange(); + Node d_proxy_range; + public: + IntRangeModel( BoundedIntegers * bi, Node r, context::Context* c, context::Context* u, bool isProxy); + virtual ~IntRangeModel(){} + Node d_range; + int d_curr_max; + std::map< int, Node > d_range_literal; + std::map< Node, bool > d_lit_to_pol; + NodeIntMap d_lit_to_range; + NodeBoolMap d_range_assertions; + context::CDO< bool > d_has_range; + context::CDO< int > d_curr_range; + IntBoolMap d_ranges_proxied; + void initialize(); + void assertNode(Node n); + Node getNextDecisionRequest(); + bool proxyCurrentRange(); + }; +private: + //information for minimizing ranges + std::vector< Node > d_ranges; + //map to range model objects + std::map< Node, RangeModel * > d_rms; + //literal to range + std::map< Node, std::vector< Node > > d_lit_to_ranges; + //list of currently asserted arithmetic literals + NodeBoolMap d_assertions; +private: + //class to store whether bounding lemmas have been added + class BoundInstTrie + { + public: + std::map< Node, BoundInstTrie > d_children; + bool hasInstantiated( std::vector< Node > & vals, int index = 0, bool madeNew = false ){ + if( index>=(int)vals.size() ){ + return !madeNew; + }else{ + Node n = vals[index]; + if( d_children.find(n)==d_children.end() ){ + madeNew = true; + } + return d_children[n].hasInstantiated(vals,index+1,madeNew); + } + } + }; + std::map< Node, std::map< Node, BoundInstTrie > > d_bnd_it; +private: + void addLiteralFromRange( Node lit, Node r ); + + void setBoundedVar( Node f, Node v, unsigned bound_type ); +public: + BoundedIntegers( context::Context* c, QuantifiersEngine* qe ); + virtual ~BoundedIntegers(); + + void presolve(); + bool needsCheck( Theory::Effort e ); + void check(Theory::Effort e, QEffort quant_e); + void registerQuantifier( Node q ); + void preRegisterQuantifier( Node q ); + void assertNode( Node n ); + Node getNextDecisionRequest( unsigned& priority ); + bool isBoundVar( Node q, Node v ) { return std::find( d_set[q].begin(), d_set[q].end(), v )!=d_set[q].end(); } + unsigned getBoundVarType( Node q, Node v ); + unsigned getNumBoundVars( Node q ) { return d_set[q].size(); } + Node getBoundVar( Node q, int i ) { return d_set[q][i]; } +private: + //for integer range + Node getLowerBound( Node q, Node v ){ return d_bounds[0][q][v]; } + Node getUpperBound( Node q, Node v ){ return d_bounds[1][q][v]; } + void getBounds( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ); + void getBoundValues( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ); + bool isGroundRange(Node f, Node v); + //for set range + Node getSetRange( Node q, Node v, RepSetIterator * rsi ); + Node getSetRangeValue( Node q, Node v, RepSetIterator * rsi ); + Node matchBoundVar( Node v, Node t, Node e ); + + bool getRsiSubsitution( Node q, Node v, std::vector< Node >& vars, std::vector< Node >& subs, RepSetIterator * rsi ); +public: + bool getBoundElements( RepSetIterator * rsi, bool initial, Node q, Node v, std::vector< Node >& elements ); + + /** Identify this module */ + std::string identify() const { return "BoundedIntegers"; } +}; + +} +} +} + +#endif diff --git a/src/theory/quantifiers/fmf/full_model_check.cpp b/src/theory/quantifiers/fmf/full_model_check.cpp new file mode 100644 index 000000000..d6957b210 --- /dev/null +++ b/src/theory/quantifiers/fmf/full_model_check.cpp @@ -0,0 +1,1518 @@ +/********************* */ +/*! \file full_model_check.cpp + ** \verbatim + ** Top contributors (to current version): + ** Morgan Deters, 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 Implementation of full model check class + **/ + +#include "theory/quantifiers/fmf/full_model_check.h" +#include "options/quantifiers_options.h" +#include "options/uf_options.h" +#include "theory/quantifiers/first_order_model.h" +#include "theory/quantifiers/instantiate.h" +#include "theory/quantifiers/term_database.h" +#include "theory/quantifiers/term_util.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; +using namespace CVC4::theory::inst; +using namespace CVC4::theory::quantifiers::fmcheck; + +struct ModelBasisArgSort +{ + std::vector< Node > d_terms; + // number of arguments that are model-basis terms + std::unordered_map d_mba_count; + bool operator() (int i,int j) { + return (d_mba_count[d_terms[i]] < d_mba_count[d_terms[j]]); + } +}; + + +struct ConstRationalSort +{ + std::vector< Node > d_terms; + bool operator() (int i, int j) { + return (d_terms[i].getConst() < d_terms[j].getConst() ); + } +}; + + +bool EntryTrie::hasGeneralization( FirstOrderModelFmc * m, Node c, int index ) { + if (index==(int)c.getNumChildren()) { + return d_data!=-1; + }else{ + TypeNode tn = c[index].getType(); + Node st = m->getStar(tn); + if(d_child.find(st)!=d_child.end()) { + if( d_child[st].hasGeneralization(m, c, index+1) ){ + return true; + } + } + if( c[index]!=st && d_child.find( c[index] )!=d_child.end() ){ + if( d_child[ c[index] ].hasGeneralization(m, c, index+1) ){ + return true; + } + } + if( c[index].getType().isSort() ){ + //for star: check if all children are defined and have generalizations + if( c[index]==st ){ ///options::fmfFmcCoverSimplify() + //check if all children exist and are complete + unsigned num_child_def = + d_child.size() - (d_child.find(st) != d_child.end() ? 1 : 0); + if (num_child_def == m->getRepSet()->getNumRepresentatives(tn)) + { + bool complete = true; + for ( std::map::iterator it = d_child.begin(); it != d_child.end(); ++it ){ + if( !m->isStar(it->first) ){ + if( !it->second.hasGeneralization(m, c, index+1) ){ + complete = false; + break; + } + } + } + if( complete ){ + return true; + } + } + } + } + + return false; + } +} + +int EntryTrie::getGeneralizationIndex( FirstOrderModelFmc * m, std::vector & inst, int index ) { + Debug("fmc-entry-trie") << "Get generalization index " << inst.size() << " " << index << std::endl; + if (index==(int)inst.size()) { + return d_data; + }else{ + int minIndex = -1; + if( options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL && inst[index].getType().isInteger() ){ + for( std::map::iterator it = d_child.begin(); it != d_child.end(); ++it ){ + //if( !m->isInterval( it->first ) ){ + // std::cout << "Not an interval during getGenIndex " << it->first << std::endl; + // exit( 11 ); + //} + //check if it is in the range + if( m->isInRange(inst[index], it->first ) ){ + int gindex = it->second.getGeneralizationIndex(m, inst, index+1); + if( minIndex==-1 || (gindex!=-1 && gindexgetStar(inst[index].getType()); + if(d_child.find(st)!=d_child.end()) { + minIndex = d_child[st].getGeneralizationIndex(m, inst, index+1); + } + Node cc = inst[index]; + if( cc!=st && d_child.find( cc )!=d_child.end() ){ + int gindex = d_child[ cc ].getGeneralizationIndex(m, inst, index+1); + if (minIndex==-1 || (gindex!=-1 && gindex & compat, std::vector & gen, int index, bool is_gen ) { + if (index==(int)c.getNumChildren()) { + if( d_data!=-1) { + if( is_gen ){ + gen.push_back(d_data); + } + compat.push_back(d_data); + } + }else{ + if (m->isStar(c[index])) { + for ( std::map::iterator it = d_child.begin(); it != d_child.end(); ++it ){ + it->second.getEntries(m, c, compat, gen, index+1, is_gen ); + } + }else{ + Node st = m->getStar(c[index].getType()); + if(d_child.find(st)!=d_child.end()) { + d_child[st].getEntries(m, c, compat, gen, index+1, false); + } + if( d_child.find( c[index] )!=d_child.end() ){ + d_child[ c[index] ].getEntries(m, c, compat, gen, index+1, is_gen); + } + } + + } +} + +void EntryTrie::collectIndices(Node c, int index, std::vector< int >& indices ) { + if (index==(int)c.getNumChildren()) { + if( d_data!=-1 ){ + indices.push_back( d_data ); + } + }else{ + for ( std::map::iterator it = d_child.begin(); it != d_child.end(); ++it ){ + it->second.collectIndices(c, index+1, indices ); + } + } +} + +bool EntryTrie::isComplete(FirstOrderModelFmc * m, Node c, int index) { + if( d_complete==-1 ){ + d_complete = 1; + if (index<(int)c.getNumChildren()) { + Node st = m->getStar(c[index].getType()); + if(d_child.find(st)!=d_child.end()) { + if (!d_child[st].isComplete(m, c, index+1)) { + d_complete = 0; + } + }else{ + d_complete = 0; + } + } + } + return d_complete==1; +} + +bool Def::addEntry( FirstOrderModelFmc * m, Node c, Node v) { + if (d_et.hasGeneralization(m, c)) { + Trace("fmc-debug") << "Already has generalization, skip." << std::endl; + return false; + } + int newIndex = (int)d_cond.size(); + if (!d_has_simplified) { + std::vector compat; + std::vector gen; + d_et.getEntries(m, c, compat, gen); + for( unsigned i=0; i& inst ) { + int gindex = d_et.getGeneralizationIndex(m, inst); + if (gindex!=-1) { + return d_value[gindex]; + }else{ + Trace("fmc-warn") << "Warning : evaluation came up null!" << std::endl; + return Node::null(); + } +} + +int Def::getGeneralizationIndex( FirstOrderModelFmc * m, std::vector& inst ) { + return d_et.getGeneralizationIndex(m, inst); +} + +void Def::basic_simplify( FirstOrderModelFmc * m ) { + d_has_simplified = true; + std::vector< Node > cond; + cond.insert( cond.end(), d_cond.begin(), d_cond.end() ); + d_cond.clear(); + std::vector< Node > value; + value.insert( value.end(), d_value.begin(), d_value.end() ); + d_value.clear(); + d_et.reset(); + for (unsigned i=0; i cond; + cond.insert( cond.end(), d_cond.begin(), d_cond.end() ); + d_cond.clear(); + std::vector< Node > value; + value.insert( value.end(), d_value.begin(), d_value.end() ); + d_value.clear(); + d_et.reset(); + d_has_simplified = false; + //change the last to all star + std::vector< Node > nc; + nc.push_back( cc.getOperator() ); + for( unsigned j=0; j< cc.getNumChildren(); j++){ + nc.push_back(m->getStarElement(cc[j].getType())); + } + cond[cond.size()-1] = NodeManager::currentNM()->mkNode( APPLY_UF, nc ); + //re-add the entries + for (unsigned i=0; idebugPrintCond(tr, d_cond[i], true); + Trace(tr) << " -> "; + m->debugPrint(tr, d_value[i]); + Trace(tr) << std::endl; + } +} + + +FullModelChecker::FullModelChecker(context::Context* c, QuantifiersEngine* qe) : +QModelBuilder( c, qe ){ + d_true = NodeManager::currentNM()->mkConst(true); + d_false = NodeManager::currentNM()->mkConst(false); +} + +bool FullModelChecker::preProcessBuildModel(TheoryModel* m) { + //standard pre-process + if( !preProcessBuildModelStd( m ) ){ + return false; + } + + FirstOrderModelFmc * fm = ((FirstOrderModelFmc*)m)->asFirstOrderModelFmc(); + Trace("fmc") << "---Full Model Check preprocess() " << std::endl; + d_preinitialized_types.clear(); + //traverse equality engine + eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( fm->d_equalityEngine ); + while( !eqcs_i.isFinished() ){ + TypeNode tr = (*eqcs_i).getType(); + d_preinitialized_types[tr] = true; + ++eqcs_i; + } + + //must ensure model basis terms exists in model for each relevant type + fm->initialize(); + for( std::map::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ) { + Node op = it->first; + TypeNode tno = op.getType(); + for( unsigned i=0; igetNumAssertedQuantifiers(); i++ ){ + Node q = fm->getAssertedQuantifier( i ); + //make sure all types are set + for( unsigned j=0; jasFirstOrderModelFmc(); + Trace("fmc") << "---Full Model Check reset() " << std::endl; + d_quant_models.clear(); + d_rep_ids.clear(); + d_star_insts.clear(); + //process representatives + RepSet* rs = fm->getRepSetPtr(); + for (std::map >::iterator it = + rs->d_type_reps.begin(); + it != rs->d_type_reps.end(); + ++it) + { + if( it->first.isSort() ){ + Trace("fmc") << "Cardinality( " << it->first << " )" << " = " << it->second.size() << std::endl; + for( size_t a=0; asecond.size(); a++ ){ + Node r = fm->getRepresentative( it->second[a] ); + if( Trace.isOn("fmc-model-debug") ){ + std::vector< Node > eqc; + d_qe->getEqualityQuery()->getEquivalenceClass( r, eqc ); + Trace("fmc-model-debug") << " " << (it->second[a]==r); + Trace("fmc-model-debug") << " : " << it->second[a] << " : " << r << " : "; + //Trace("fmc-model-debug") << r2 << " : " << ir << " : "; + Trace("fmc-model-debug") << " {"; + for( size_t i=0; ifirst][r] = (int)a; + } + Trace("fmc-model-debug") << std::endl; + } + } + + //now, make models + for( std::map::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ) { + Node op = it->first; + //reset the model + fm->d_models[op]->reset(); + + std::vector< Node > add_conds; + std::vector< Node > add_values; + bool needsDefault = true; + std::map< Node, std::vector< Node > >::iterator itut = fm->d_uf_terms.find( op ); + if( itut!=fm->d_uf_terms.end() ){ + Trace("fmc-model-debug") << itut->second.size() << " model values for " << op << " ... " << std::endl; + for( size_t i=0; isecond.size(); i++ ){ + Node n = itut->second[i]; + // only consider unique up to congruence (in model equality engine)? + add_conds.push_back( n ); + add_values.push_back( n ); + Node r = fm->getRepresentative(n); + Trace("fmc-model-debug") << n << " -> " << r << std::endl; + //AlwaysAssert( fm->areEqual( itut->second[i], r ) ); + } + }else{ + Trace("fmc-model-debug") << "No model values for " << op << " ... " << std::endl; + } + Trace("fmc-model-debug") << std::endl; + //possibly get default + if( needsDefault ){ + Node nmb = fm->getModelBasisOpTerm(op); + //add default value if necessary + if( fm->hasTerm( nmb ) ){ + Trace("fmc-model-debug") << "Add default " << nmb << std::endl; + add_conds.push_back( nmb ); + add_values.push_back( nmb ); + }else{ + Node vmb = getSomeDomainElement(fm, nmb.getType()); + Trace("fmc-model-debug") << "Add default to default representative " << nmb << " "; + Trace("fmc-model-debug") + << fm->getRepSet()->getNumRepresentatives(nmb.getType()) + << std::endl; + add_conds.push_back( nmb ); + add_values.push_back( vmb ); + } + } + + std::vector< Node > conds; + std::vector< Node > values; + std::vector< Node > entry_conds; + //get the entries for the model + for( size_t i=0; i children; + std::vector< Node > entry_children; + children.push_back(op); + entry_children.push_back(op); + bool hasNonStar = false; + for( unsigned i=0; igetRepresentative( c[i] ); + children.push_back(ri); + bool isStar = false; + if( options::mbqiMode()!=quantifiers::MBQI_FMC_INTERVAL || !ri.getType().isInteger() ){ + if (fm->isModelBasisTerm(ri) ) { + ri = fm->getStar( ri.getType() ); + isStar = true; + }else{ + hasNonStar = true; + } + } + if( !isStar && !ri.isConst() ){ + Trace("fmc-warn") << "Warning : model for " << op << " has non-constant argument in model " << ri << " (from " << c[i] << ")" << std::endl; + Assert( false ); + } + entry_children.push_back(ri); + } + Node n = NodeManager::currentNM()->mkNode( APPLY_UF, children ); + Node nv = fm->getRepresentative( v ); + if( !nv.isConst() ){ + Trace("fmc-warn") << "Warning : model for " << op << " has non-constant value in model " << nv << std::endl; + Assert( false ); + } + Node en = (useSimpleModels() && hasNonStar) ? n : NodeManager::currentNM()->mkNode( APPLY_UF, entry_children ); + if( std::find(conds.begin(), conds.end(), n )==conds.end() ){ + Trace("fmc-model-debug") << "- add " << n << " -> " << nv << " (entry is " << en << ")" << std::endl; + conds.push_back(n); + values.push_back(nv); + entry_conds.push_back(en); + } + else { + Trace("fmc-model-debug") << "Already have entry for : " << n << " -> " << nv << " (entry is " << en << ")" << std::endl; + } + } + + + //sort based on # default arguments + std::vector< int > indices; + ModelBasisArgSort mbas; + for (int i=0; i<(int)conds.size(); i++) { + mbas.d_terms.push_back(conds[i]); + mbas.d_mba_count[conds[i]] = fm->getModelBasisArg(conds[i]); + indices.push_back(i); + } + std::sort( indices.begin(), indices.end(), mbas ); + + for (int i=0; i<(int)indices.size(); i++) { + fm->d_models[op]->addEntry(fm, entry_conds[indices[i]], values[indices[i]]); + } + + + if( options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL ){ + convertIntervalModel( fm, op ); + } + + Trace("fmc-model-simplify") << "Before simplification : " << std::endl; + fm->d_models[op]->debugPrint("fmc-model-simplify", op, this); + Trace("fmc-model-simplify") << std::endl; + + Trace("fmc-model-simplify") << "Simplifying " << op << "..." << std::endl; + fm->d_models[op]->simplify( this, fm ); + + fm->d_models[op]->debugPrint("fmc-model", op, this); + Trace("fmc-model") << std::endl; + + //for debugging + /* + for( size_t i=0; id_uf_terms[op].size(); i++ ){ + std::vector< Node > inst; + for( unsigned j=0; jd_uf_terms[op][i].getNumChildren(); j++ ){ + Node r = fm->getRepresentative( fm->d_uf_terms[op][i][j] ); + inst.push_back( r ); + } + Node ev = fm->d_models[op]->evaluate( fm, inst ); + Trace("fmc-model-debug") << ".....Checking eval( " << fm->d_uf_terms[op][i] << " ) = " << ev << std::endl; + AlwaysAssert( fm->areEqual( ev, fm->d_uf_terms[op][i] ) ); + } + */ + } + Assert( d_addedLemmas==0 ); + + //make function values + for( std::map::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ){ + Node f_def = getFunctionValue( fm, it->first, "$x" ); + m->assignFunctionDefinition( it->first, f_def ); + } + return TheoryEngineModelBuilder::processBuildModel( m ); +} + +void FullModelChecker::preInitializeType( FirstOrderModelFmc * fm, TypeNode tn ){ + if( d_preinitialized_types.find( tn )==d_preinitialized_types.end() ){ + d_preinitialized_types[tn] = true; + if (!tn.isFunction() || options::ufHo()) + { + Node mb = fm->getModelBasisTerm(tn); + if (!mb.isConst()) + { + Trace("fmc") << "...add model basis term to EE of model " << mb << " " + << tn << std::endl; + fm->d_equalityEngine->addTerm(mb); + } + } + } +} + +void FullModelChecker::debugPrintCond(const char * tr, Node n, bool dispStar) { + Trace(tr) << "("; + for( unsigned j=0; j0 ) Trace(tr) << ", "; + debugPrint(tr, n[j], dispStar); + } + Trace(tr) << ")"; +} + +void FullModelChecker::debugPrint(const char * tr, Node n, bool dispStar) { + FirstOrderModelFmc * fm = (FirstOrderModelFmc *)d_qe->getModel(); + if( n.isNull() ){ + Trace(tr) << "null"; + } + else if(fm->isStar(n) && dispStar) { + Trace(tr) << "*"; + } + else if(fm->isInterval(n)) { + debugPrint(tr, n[0], dispStar ); + Trace(tr) << "..."; + debugPrint(tr, n[1], dispStar ); + }else{ + TypeNode tn = n.getType(); + if( tn.isSort() && d_rep_ids.find(tn)!=d_rep_ids.end() ){ + if (d_rep_ids[tn].find(n)!=d_rep_ids[tn].end()) { + Trace(tr) << d_rep_ids[tn][n]; + }else{ + Trace(tr) << n; + } + }else{ + Trace(tr) << n; + } + } +} + + +int FullModelChecker::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ) { + Trace("fmc") << "Full model check " << f << ", effort = " << effort << "..." << std::endl; + Assert( !d_qe->inConflict() ); + if( optUseModel() ){ + FirstOrderModelFmc * fmfmc = fm->asFirstOrderModelFmc(); + if (effort==0) { + //register the quantifier + if (d_quant_cond.find(f)==d_quant_cond.end()) { + std::vector< TypeNode > types; + for(unsigned i=0; imkFunctionType( types, NodeManager::currentNM()->booleanType() ); + Node op = NodeManager::currentNM()->mkSkolem( "qfmc", typ, "op created for full-model checking" ); + d_quant_cond[f] = op; + } + + if( options::mbqiMode()==MBQI_NONE ){ + //just exhaustive instantiate + Node c = mkCondDefault( fmfmc, f ); + d_quant_models[f].addEntry( fmfmc, c, d_false ); + return exhaustiveInstantiate( fmfmc, f, c, -1); + }else{ + //model check the quantifier + doCheck(fmfmc, f, d_quant_models[f], f[1]); + Trace("fmc") << "Definition for quantifier " << f << " is : " << std::endl; + Assert( !d_quant_models[f].d_cond.empty() ); + d_quant_models[f].debugPrint("fmc", Node::null(), this); + Trace("fmc") << std::endl; + + //consider all entries going to non-true + for (unsigned i=0; i inst; + for (unsigned j=0; jisStar(d_quant_models[f].d_cond[i][j])) { + hasStar = true; + inst.push_back(fmfmc->getModelBasisTerm(d_quant_models[f].d_cond[i][j].getType())); + }else if( fmfmc->isInterval(d_quant_models[f].d_cond[i][j])){ + hasStar = true; + //if interval, find a sample point + if( fmfmc->isStar(d_quant_models[f].d_cond[i][j][0]) ){ + if( fmfmc->isStar(d_quant_models[f].d_cond[i][j][1]) ){ + inst.push_back(fmfmc->getModelBasisTerm(d_quant_models[f].d_cond[i][j][1].getType())); + }else{ + Node nn = NodeManager::currentNM()->mkNode( MINUS, d_quant_models[f].d_cond[i][j][1], + NodeManager::currentNM()->mkConst( Rational(1) ) ); + nn = Rewriter::rewrite( nn ); + inst.push_back( nn ); + } + }else{ + inst.push_back(d_quant_models[f].d_cond[i][j][0]); + } + }else{ + inst.push_back(d_quant_models[f].d_cond[i][j]); + } + } + bool addInst = true; + if( hasStar ){ + //try obvious (specified by inst) + Node ev = d_quant_models[f].evaluate(fmfmc, inst); + if (ev==d_true) { + addInst = false; + Trace("fmc-debug") << "...do not instantiate, evaluation was " << ev << std::endl; + } + }else{ + //for debugging + if (Trace.isOn("fmc-test-inst")) { + Node ev = d_quant_models[f].evaluate(fmfmc, inst); + if( ev==d_true ){ + std::cout << "WARNING: instantiation was true! " << f << " " << d_quant_models[f].d_cond[i] << std::endl; + exit(0); + }else{ + Trace("fmc-test-inst") << "...instantiation evaluated to false." << std::endl; + } + } + } + if( addInst ){ + if( options::fmfBound() ){ + std::vector< Node > cond; + cond.push_back(d_quant_cond[f]); + cond.insert( cond.end(), inst.begin(), inst.end() ); + //need to do exhaustive instantiate algorithm to set things properly (should only add one instance) + Node c = mkCond( cond ); + unsigned prevInst = d_addedLemmas; + exhaustiveInstantiate( fmfmc, f, c, -1 ); + if( d_addedLemmas==prevInst ){ + d_star_insts[f].push_back(i); + } + }else{ + //just add the instance + d_triedLemmas++; + if (d_qe->getInstantiate()->addInstantiation(f, inst, true)) + { + Trace("fmc-debug-inst") << "** Added instantiation." << std::endl; + d_addedLemmas++; + if( d_qe->inConflict() || options::fmfOneInstPerRound() ){ + break; + } + }else{ + Trace("fmc-debug-inst") << "** Instantiation was duplicate." << std::endl; + //this can happen if evaluation is unknown, or if we are generalizing a star that already has a value + //if( !hasStar && d_quant_models[f].d_value[i]==d_false ){ + // Trace("fmc-warn") << "**** FMC warning: inconsistent duplicate instantiation." << std::endl; + //} + //this assertion can happen if two instantiations from this round are identical + // (0,1)->false (1,0)->false for forall xy. f( x, y ) = f( y, x ) + //Assert( hasStar || d_quant_models[f].d_value[i]!=d_false ); + //might try it next effort level + d_star_insts[f].push_back(i); + } + } + }else{ + Trace("fmc-debug-inst") << "** Instantiation was already true." << std::endl; + //might try it next effort level + d_star_insts[f].push_back(i); + } + } + } + } + }else{ + if (!d_star_insts[f].empty()) { + Trace("fmc-exh") << "Exhaustive instantiate " << f << std::endl; + Trace("fmc-exh") << "Definition was : " << std::endl; + d_quant_models[f].debugPrint("fmc-exh", Node::null(), this); + Trace("fmc-exh") << std::endl; + Def temp; + //simplify the exceptions? + for( int i=(d_star_insts[f].size()-1); i>=0; i--) { + //get witness for d_star_insts[f][i] + int j = d_star_insts[f][i]; + if( temp.addEntry(fmfmc, d_quant_models[f].d_cond[j], d_quant_models[f].d_value[j] ) ){ + if( !exhaustiveInstantiate(fmfmc, f, d_quant_models[f].d_cond[j], j ) ){ + //something went wrong, resort to exhaustive instantiation + return 0; + } + } + } + } + } + return 1; + }else{ + return 0; + } +} + +/** Representative bound fmc entry + * + * This bound information corresponds to one + * entry in a term definition (see terminology in + * Chapter 5 of Finite Model Finding for + * Satisfiability Modulo Theories thesis). + * For example, a term definition for the body + * of a quantified formula: + * forall xyz. P( x, y, z ) + * may be: + * ( 0, 0, 0 ) -> false + * ( *, 1, 2 ) -> false + * ( *, *, * ) -> true + * Indicating that the quantified formula evaluates + * to false in the current model for x=0, y=0, z=0, + * or y=1, z=2 for any x, and evaluates to true + * otherwise. + * This class is used if we wish + * to iterate over all values corresponding to one + * of these entries. For example, for the second entry: + * (*, 1, 2 ) + * we iterate over all values of x, but only {1} + * for y and {2} for z. + */ +class RepBoundFmcEntry : public QRepBoundExt +{ + public: + RepBoundFmcEntry(QuantifiersEngine* qe, Node e, FirstOrderModelFmc* f) + : QRepBoundExt(qe), d_entry(e), d_fm(f) + { + } + ~RepBoundFmcEntry() {} + /** set bound */ + virtual RepSetIterator::RsiEnumType setBound( + Node owner, unsigned i, std::vector& elements) override + { + if (d_fm->isInterval(d_entry[i])) + { + // explicitly add the interval? + } + else if (d_fm->isStar(d_entry[i])) + { + // must add the full range + } + else + { + // only need to consider the single point + elements.push_back(d_entry[i]); + return RepSetIterator::ENUM_DEFAULT; + } + return QRepBoundExt::setBound(owner, i, elements); + } + + private: + /** the entry for this bound */ + Node d_entry; + /** the model builder associated with this bound */ + FirstOrderModelFmc* d_fm; +}; + +bool FullModelChecker::exhaustiveInstantiate(FirstOrderModelFmc * fm, Node f, Node c, int c_index) { + Trace("fmc-exh") << "----Exhaustive instantiate based on index " << c_index << " : " << c << " "; + debugPrintCond("fmc-exh", c, true); + Trace("fmc-exh")<< std::endl; + RepBoundFmcEntry rbfe(d_qe, c, fm); + RepSetIterator riter(d_qe->getModel()->getRepSet(), &rbfe); + Trace("fmc-exh-debug") << "Set quantifier..." << std::endl; + //initialize + if (riter.setQuantifier(f)) + { + Trace("fmc-exh-debug") << "Set element domains..." << std::endl; + int addedLemmas = 0; + //now do full iteration + while( !riter.isFinished() ){ + d_triedLemmas++; + Trace("fmc-exh-debug") << "Inst : "; + std::vector< Node > ev_inst; + std::vector< Node > inst; + for (unsigned i = 0; i < riter.getNumTerms(); i++) + { + Node rr = riter.getCurrentTerm( i ); + Node r = rr; + //if( r.getType().isSort() ){ + r = fm->getRepresentative( r ); + //}else{ + // r = fm->getCurrentModelValue( r ); + //} + debugPrint("fmc-exh-debug", r); + Trace("fmc-exh-debug") << " (term : " << rr << ")"; + ev_inst.push_back( r ); + inst.push_back( rr ); + } + int ev_index = d_quant_models[f].getGeneralizationIndex(fm, ev_inst); + Trace("fmc-exh-debug") << ", index = " << ev_index << " / " << d_quant_models[f].d_value.size(); + Node ev = ev_index==-1 ? Node::null() : d_quant_models[f].d_value[ev_index]; + if (ev!=d_true) { + Trace("fmc-exh-debug") << ", add!"; + //add as instantiation + if (d_qe->getInstantiate()->addInstantiation(f, inst, true)) + { + Trace("fmc-exh-debug") << " ...success."; + addedLemmas++; + if( d_qe->inConflict() || options::fmfOneInstPerRound() ){ + break; + } + }else{ + Trace("fmc-exh-debug") << ", failed."; + } + }else{ + Trace("fmc-exh-debug") << ", already true"; + } + Trace("fmc-exh-debug") << std::endl; + int index = riter.increment(); + Trace("fmc-exh-debug") << "Incremented index " << index << std::endl; + if( !riter.isFinished() ){ + if (index>=0 && riter.d_index[index]>0 && addedLemmas>0 && riter.d_enum_type[index]==RepSetIterator::ENUM_BOUND_INT ) { + Trace("fmc-exh-debug") << "Since this is a range enumeration, skip to the next..." << std::endl; + riter.incrementAtIndex(index - 1); + } + } + } + d_addedLemmas += addedLemmas; + Trace("fmc-exh") << "----Finished Exhaustive instantiate, lemmas = " << addedLemmas << ", incomplete=" << riter.isIncomplete() << std::endl; + return addedLemmas>0 || !riter.isIncomplete(); + }else{ + Trace("fmc-exh") << "----Finished Exhaustive instantiate, failed." << std::endl; + return !riter.isIncomplete(); + } +} + +void FullModelChecker::doCheck(FirstOrderModelFmc * fm, Node f, Def & d, Node n ) { + Trace("fmc-debug") << "Check " << n << " " << n.getKind() << std::endl; + //first check if it is a bounding literal + if( n.hasAttribute(BoundIntLitAttribute()) ){ + Trace("fmc-debug") << "It is a bounding literal, polarity = " << n.getAttribute(BoundIntLitAttribute()) << std::endl; + d.addEntry(fm, mkCondDefault(fm, f), n.getAttribute(BoundIntLitAttribute())==1 ? d_false : d_true ); + }else if( n.getKind() == kind::BOUND_VARIABLE ){ + Trace("fmc-debug") << "Add default entry..." << std::endl; + d.addEntry(fm, mkCondDefault(fm, f), n); + } + else if( n.getKind() == kind::NOT ){ + //just do directly + doCheck( fm, f, d, n[0] ); + doNegate( d ); + } + else if( n.getKind() == kind::FORALL ){ + d.addEntry(fm, mkCondDefault(fm, f), Node::null()); + } + else if( n.getType().isArray() ){ + //Trace("fmc-warn") << "WARNING : ARRAYS : Can't process base array " << r << std::endl; + //Trace("fmc-warn") << " Default value was : " << odefaultValue << std::endl; + //Trace("fmc-debug") << "Can't process base array " << r << std::endl; + //can't process this array + d.reset(); + d.addEntry(fm, mkCondDefault(fm, f), Node::null()); + } + else if( n.getNumChildren()==0 ){ + Node r = n; + if( !n.isConst() ){ + if( !fm->hasTerm(n) ){ + r = getSomeDomainElement(fm, n.getType() ); + } + r = fm->getRepresentative( r ); + } + Trace("fmc-debug") << "Add constant entry..." << std::endl; + d.addEntry(fm, mkCondDefault(fm, f), r); + } + else{ + std::vector< int > var_ch; + std::vector< Def > children; + for( int i=0; i<(int)n.getNumChildren(); i++) { + Def dc; + doCheck(fm, f, dc, n[i]); + children.push_back(dc); + if( n[i].getKind() == kind::BOUND_VARIABLE ){ + var_ch.push_back(i); + } + } + + if( n.getKind()==APPLY_UF ){ + Trace("fmc-debug") << "Do uninterpreted compose " << n << std::endl; + //uninterpreted compose + doUninterpretedCompose( fm, f, d, n.getOperator(), children ); + /* + } else if( n.getKind()==SELECT ){ + Trace("fmc-debug") << "Do select compose " << n << std::endl; + std::vector< Def > children2; + children2.push_back( children[1] ); + std::vector< Node > cond; + mkCondDefaultVec(fm, f, cond); + std::vector< Node > val; + doUninterpretedCompose(fm, f, d, children[0], children2, 0, cond, val ); + */ + } else { + if( !var_ch.empty() ){ + if( n.getKind()==EQUAL && !n[0].getType().isBoolean() ){ + if( var_ch.size()==2 ){ + Trace("fmc-debug") << "Do variable equality " << n << std::endl; + doVariableEquality( fm, f, d, n ); + }else{ + Trace("fmc-debug") << "Do variable relation " << n << std::endl; + doVariableRelation( fm, f, d, var_ch[0]==0 ? children[1] : children[0], var_ch[0]==0 ? n[0] : n[1] ); + } + }else{ + Trace("fmc-warn") << "Don't know how to check " << n << std::endl; + d.addEntry(fm, mkCondDefault(fm, f), Node::null()); + } + }else{ + Trace("fmc-debug") << "Do interpreted compose " << n << std::endl; + std::vector< Node > cond; + mkCondDefaultVec(fm, f, cond); + std::vector< Node > val; + //interpreted compose + doInterpretedCompose( fm, f, d, n, children, 0, cond, val ); + } + } + Trace("fmc-debug") << "Simplify the definition..." << std::endl; + d.debugPrint("fmc-debug", Node::null(), this); + d.simplify(this, fm); + Trace("fmc-debug") << "Done simplifying" << std::endl; + } + Trace("fmc-debug") << "Definition for " << n << " is : " << std::endl; + d.debugPrint("fmc-debug", Node::null(), this); + Trace("fmc-debug") << std::endl; +} + +void FullModelChecker::doNegate( Def & dc ) { + for (unsigned i=0; i cond; + mkCondDefaultVec(fm, f, cond); + if (eq[0]==eq[1]){ + d.addEntry(fm, mkCond(cond), d_true); + }else{ + TypeNode tn = eq[0].getType(); + if( tn.isSort() ){ + int j = fm->getVariableId(f, eq[0]); + int k = fm->getVariableId(f, eq[1]); + const RepSet* rs = fm->getRepSet(); + if (!rs->hasType(tn)) + { + getSomeDomainElement( fm, tn ); //to verify the type is initialized + } + unsigned nreps = rs->getNumRepresentatives(tn); + for (unsigned i = 0; i < nreps; i++) + { + Node r = fm->getRepresentative(rs->getRepresentative(tn, i)); + cond[j+1] = r; + cond[k+1] = r; + d.addEntry( fm, mkCond(cond), d_true); + } + d.addEntry( fm, mkCondDefault(fm, f), d_false); + }else{ + d.addEntry( fm, mkCondDefault(fm, f), Node::null()); + } + } +} + +void FullModelChecker::doVariableRelation( FirstOrderModelFmc * fm, Node f, Def & d, Def & dc, Node v) { + int j = fm->getVariableId(f, v); + for (unsigned i=0; iisStar(dc.d_cond[i][j])) { + std::vector cond; + mkCondVec(dc.d_cond[i],cond); + cond[j+1] = val; + d.addEntry(fm, mkCond(cond), d_true); + cond[j+1] = fm->getStar(val.getType()); + d.addEntry(fm, mkCond(cond), d_false); + }else{ + d.addEntry( fm, dc.d_cond[i], d_false); + } + }else{ + d.addEntry( fm, dc.d_cond[i], d_true); + } + } + } +} + +void FullModelChecker::doUninterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, Node op, std::vector< Def > & dc ) { + Trace("fmc-uf-debug") << "Definition : " << std::endl; + fm->d_models[op]->debugPrint("fmc-uf-debug", op, this); + Trace("fmc-uf-debug") << std::endl; + + std::vector< Node > cond; + mkCondDefaultVec(fm, f, cond); + std::vector< Node > val; + doUninterpretedCompose( fm, f, d, *fm->d_models[op], dc, 0, cond, val); +} + +void FullModelChecker::doUninterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, + Def & df, std::vector< Def > & dc, int index, + std::vector< Node > & cond, std::vector & val ) { + Trace("fmc-uf-process") << "process at " << index << std::endl; + for( unsigned i=1; i entries; + doUninterpretedCompose2( fm, f, entries, 0, cond, val, df.d_et); + if( entries.empty() ){ + d.addEntry(fm, mkCond(cond), Node::null()); + }else{ + //add them to the definition + for( unsigned e=0; e new_cond; + new_cond.insert(new_cond.end(), cond.begin(), cond.end()); + if( doMeet(fm, new_cond, dc[index].d_cond[i]) ){ + Trace("fmc-uf-process") << "index " << i << " succeeded meet." << std::endl; + val.push_back(dc[index].d_value[i]); + doUninterpretedCompose(fm, f, d, df, dc, index+1, new_cond, val); + val.pop_back(); + }else{ + Trace("fmc-uf-process") << "index " << i << " failed meet." << std::endl; + } + } + } + } +} + +void FullModelChecker::doUninterpretedCompose2( FirstOrderModelFmc * fm, Node f, + std::map< int, Node > & entries, int index, + std::vector< Node > & cond, std::vector< Node > & val, + EntryTrie & curr ) { + Trace("fmc-uf-process") << "compose " << index << " / " << val.size() << std::endl; + for( unsigned i=1; i index[" << curr.d_data << "]" << std::endl; + entries[curr.d_data] = c; + }else{ + Node v = val[index]; + Trace("fmc-uf-process") << "Process " << v << std::endl; + bool bind_var = false; + if( !v.isNull() && v.getKind()==kind::BOUND_VARIABLE ){ + int j = fm->getVariableId(f, v); + Trace("fmc-uf-process") << v << " is variable #" << j << std::endl; + if (!fm->isStar(cond[j+1]) && !fm->isInterval(cond[j+1])) { + v = cond[j+1]; + }else{ + bind_var = true; + } + } + if (bind_var) { + Trace("fmc-uf-process") << "bind variable..." << std::endl; + int j = fm->getVariableId(f, v); + if( fm->isStar(cond[j+1]) ){ + for (std::map::iterator it = curr.d_child.begin(); it != curr.d_child.end(); ++it) { + cond[j+1] = it->first; + doUninterpretedCompose2(fm, f, entries, index+1, cond, val, it->second); + } + cond[j+1] = fm->getStar(v.getType()); + }else{ + Node orig = cond[j+1]; + for (std::map::iterator it = curr.d_child.begin(); it != curr.d_child.end(); ++it) { + Node nw = doIntervalMeet( fm, it->first, orig ); + if( !nw.isNull() ){ + cond[j+1] = nw; + doUninterpretedCompose2(fm, f, entries, index+1, cond, val, it->second); + } + } + cond[j+1] = orig; + } + }else{ + if( !v.isNull() ){ + if( options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL && v.getType().isInteger() ){ + for (std::map::iterator it = curr.d_child.begin(); it != curr.d_child.end(); ++it) { + if( fm->isInRange( v, it->first ) ){ + doUninterpretedCompose2(fm, f, entries, index+1, cond, val, it->second); + } + } + }else{ + if (curr.d_child.find(v)!=curr.d_child.end()) { + Trace("fmc-uf-process") << "follow value..." << std::endl; + doUninterpretedCompose2(fm, f, entries, index+1, cond, val, curr.d_child[v]); + } + Node st = fm->getStarElement(v.getType()); + if (curr.d_child.find(st)!=curr.d_child.end()) { + Trace("fmc-uf-process") << "follow star..." << std::endl; + doUninterpretedCompose2(fm, f, entries, index+1, cond, val, curr.d_child[st]); + } + } + } + } + } +} + +void FullModelChecker::doInterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, Node n, + std::vector< Def > & dc, int index, + std::vector< Node > & cond, std::vector & val ) { + Trace("fmc-if-process") << "int compose " << index << " / " << dc.size() << std::endl; + for( unsigned i=1; i new_cond; + new_cond.insert(new_cond.end(), cond.begin(), cond.end()); + if( doMeet(fm, new_cond, dc[index].d_cond[i]) ){ + bool process = true; + if (vtn.isBoolean()) { + //short circuit + if( (n.getKind()==OR && dc[index].d_value[i]==d_true) || + (n.getKind()==AND && dc[index].d_value[i]==d_false) ){ + Node c = mkCond(new_cond); + d.addEntry(fm, c, dc[index].d_value[i]); + process = false; + } + } + if (process) { + val.push_back(dc[index].d_value[i]); + doInterpretedCompose(fm, f, d, n, dc, index+1, new_cond, val); + val.pop_back(); + } + } + } + } + } +} + +int FullModelChecker::isCompat( FirstOrderModelFmc * fm, std::vector< Node > & cond, Node c ) { + Trace("fmc-debug3") << "isCompat " << c << std::endl; + Assert(cond.size()==c.getNumChildren()+1); + for (unsigned i=1; iisStar(cond[i]) && !fm->isStar(c[i-1]) ) { + return 0; + } + } + } + return 1; +} + +bool FullModelChecker::doMeet( FirstOrderModelFmc * fm, std::vector< Node > & cond, Node c ) { + Trace("fmc-debug3") << "doMeet " << c << std::endl; + Assert(cond.size()==c.getNumChildren()+1); + for (unsigned i=1; iisStar(cond[i]) ){ + cond[i] = c[i-1]; + }else if( !fm->isStar(c[i-1]) ){ + return false; + } + } + } + } + return true; +} + +Node FullModelChecker::doIntervalMeet( FirstOrderModelFmc * fm, Node i1, Node i2, bool mk ) { + Trace("fmc-debug2") << "Interval meet : " << i1 << " " << i2 << " " << mk << std::endl; + if( fm->isStar( i1 ) ){ + return i2; + }else if( fm->isStar( i2 ) ){ + return i1; + }else if( !fm->isInterval( i1 ) && fm->isInterval( i2 ) ){ + return doIntervalMeet( fm, i2, i1, mk ); + }else if( !fm->isInterval( i2 ) ){ + if( fm->isInterval( i1 ) ){ + if( fm->isInRange( i2, i1 ) ){ + return i2; + } + }else if( i1==i2 ){ + return i1; + } + return Node::null(); + }else{ + Node b[2]; + for( unsigned j=0; j<2; j++ ){ + Node b1 = i1[j]; + Node b2 = i2[j]; + if( fm->isStar( b1 ) ){ + b[j] = b2; + }else if( fm->isStar( b2 ) ){ + b[j] = b1; + }else if( b1.getConst() < b2.getConst() ){ + b[j] = j==0 ? b2 : b1; + }else{ + b[j] = j==0 ? b1 : b2; + } + } + if( fm->isStar( b[0] ) || fm->isStar( b[1] ) || b[0].getConst() < b[1].getConst() ){ + return mk ? fm->getInterval( b[0], b[1] ) : i1; + }else{ + return Node::null(); + } + } +} + +Node FullModelChecker::mkCond( std::vector< Node > & cond ) { + return NodeManager::currentNM()->mkNode(APPLY_UF, cond); +} + +Node FullModelChecker::mkCondDefault( FirstOrderModelFmc * fm, Node f) { + std::vector< Node > cond; + mkCondDefaultVec(fm, f, cond); + return mkCond(cond); +} + +void FullModelChecker::mkCondDefaultVec( FirstOrderModelFmc * fm, Node f, std::vector< Node > & cond ) { + Trace("fmc-debug") << "Make default vec, intervals = " << (options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL) << std::endl; + //get function symbol for f + cond.push_back(d_quant_cond[f]); + for (unsigned i=0; igetStarElement( f[0][i].getType() ); + Assert( ts.getType()==f[0][i].getType() ); + cond.push_back(ts); + } +} + +void FullModelChecker::mkCondVec( Node n, std::vector< Node > & cond ) { + cond.push_back(n.getOperator()); + for( unsigned i=0; imkFunctionType( a.getType(), NodeManager::currentNM()->booleanType() ); + Node op = NodeManager::currentNM()->mkSkolem( "fmc", typ, "op created for full-model checking" ); + d_array_cond[a.getType()] = op; + } + std::vector< Node > cond; + cond.push_back(d_array_cond[a.getType()]); + cond.push_back(a); + d_array_term_cond[a] = NodeManager::currentNM()->mkNode(APPLY_UF, cond ); + } + return d_array_term_cond[a]; +} + +Node FullModelChecker::evaluateInterpreted( Node n, std::vector< Node > & vals ) { + if( n.getKind()==EQUAL && !n[0].getType().isBoolean() ){ + if (!vals[0].isNull() && !vals[1].isNull()) { + return vals[0]==vals[1] ? d_true : d_false; + }else{ + return Node::null(); + } + }else if( n.getKind()==ITE ){ + if( vals[0]==d_true ){ + return vals[1]; + }else if( vals[0]==d_false ){ + return vals[2]; + }else{ + return vals[1]==vals[2] ? vals[1] : Node::null(); + } + }else if( n.getKind()==AND || n.getKind()==OR ){ + bool isNull = false; + for (unsigned i=0; i children; + if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ + children.push_back( n.getOperator() ); + } + for (unsigned i=0; imkNode(n.getKind(), children); + Trace("fmc-eval") << "Evaluate " << nc << " to "; + nc = Rewriter::rewrite(nc); + Trace("fmc-eval") << nc << std::endl; + return nc; + } +} + +Node FullModelChecker::getSomeDomainElement( FirstOrderModelFmc * fm, TypeNode tn ) { + bool addRepId = !fm->getRepSet()->hasType(tn); + Node de = fm->getSomeDomainElement(tn); + if( addRepId ){ + d_rep_ids[tn][de] = 0; + } + return de; +} + +Node FullModelChecker::getFunctionValue(FirstOrderModelFmc * fm, Node op, const char* argPrefix ) { + return fm->getFunctionValue(op, argPrefix); +} + + +bool FullModelChecker::useSimpleModels() { + return options::fmfFmcSimple(); +} + +void FullModelChecker::convertIntervalModel( FirstOrderModelFmc * fm, Node op ){ + Trace("fmc-interval-model") << "Changing to interval model, Before : " << std::endl; + fm->d_models[op]->debugPrint("fmc-interval-model", op, this); + Trace("fmc-interval-model") << std::endl; + std::vector< int > indices; + for( int i=0; i<(int)fm->d_models[op]->d_cond.size(); i++ ){ + indices.push_back( i ); + } + std::map< int, std::map< int, Node > > changed_vals; + makeIntervalModel( fm, op, indices, 0, changed_vals ); + + std::vector< Node > conds; + std::vector< Node > values; + for( unsigned i=0; id_models[op]->d_cond.size(); i++ ){ + if( changed_vals.find(i)==changed_vals.end() ){ + conds.push_back( fm->d_models[op]->d_cond[i] ); + }else{ + std::vector< Node > children; + children.push_back( op ); + for( unsigned j=0; jd_models[op]->d_cond[i].getNumChildren(); j++ ){ + if( changed_vals[i].find(j)==changed_vals[i].end() ){ + children.push_back( fm->d_models[op]->d_cond[i][j] ); + }else{ + children.push_back( changed_vals[i][j] ); + } + } + Node nc = NodeManager::currentNM()->mkNode( APPLY_UF, children ); + conds.push_back( nc ); + Trace("fmc-interval-model") << "Interval : Entry #" << i << " changed to "; + debugPrintCond("fmc-interval-model", nc, true ); + Trace("fmc-interval-model") << std::endl; + } + values.push_back( fm->d_models[op]->d_value[i] ); + } + fm->d_models[op]->reset(); + for( unsigned i=0; id_models[op]->addEntry(fm, conds[i], values[i] ); + } +} + +void FullModelChecker::makeIntervalModel( FirstOrderModelFmc * fm, Node op, std::vector< int > & indices, int index, + std::map< int, std::map< int, Node > >& changed_vals ) { + if( index==(int)fm->d_models[op]->d_cond[0].getNumChildren() ){ + makeIntervalModel2( fm, op, indices, 0, changed_vals ); + }else{ + TypeNode tn = fm->d_models[op]->d_cond[0][index].getType(); + if( tn.isInteger() ){ + makeIntervalModel(fm,op,indices,index+1, changed_vals ); + }else{ + std::map< Node, std::vector< int > > new_indices; + for( unsigned i=0; id_models[op]->d_cond[indices[i]][index]; + new_indices[v].push_back( indices[i] ); + } + + for( std::map< Node, std::vector< int > >::iterator it = new_indices.begin(); it != new_indices.end(); ++it ){ + makeIntervalModel( fm, op, it->second, index+1, changed_vals ); + } + } + } +} + +void FullModelChecker::makeIntervalModel2( FirstOrderModelFmc * fm, Node op, std::vector< int > & indices, int index, + std::map< int, std::map< int, Node > >& changed_vals ) { + Debug("fmc-interval-model-debug") << "Process " << index << " with indicies : "; + for( unsigned i=0; id_models[op]->d_cond[0].getNumChildren() ){ + TypeNode tn = fm->d_models[op]->d_cond[0][index].getType(); + if( tn.isInteger() ){ + std::map< Node, std::vector< int > > new_indices; + for( unsigned i=0; id_models[op]->d_cond[indices[i]][index]; + new_indices[v].push_back( indices[i] ); + if( !v.isConst() ){ + Trace("fmc-warn") << "WARNING: for interval, model has non-constant : " << v << std::endl; + Trace("fmc-warn") << "From condition : " << fm->d_models[op]->d_cond[indices[i]] << std::endl; + } + } + + std::vector< Node > values; + for( std::map< Node, std::vector< int > >::iterator it = new_indices.begin(); it != new_indices.end(); ++it ){ + makeIntervalModel2( fm, op, it->second, index+1, changed_vals ); + values.push_back( it->first ); + } + + if( tn.isInteger() ){ + //sort values by size + ConstRationalSort crs; + std::vector< int > sindices; + for( unsigned i=0; igetStar( tn ); + for( int i=(int)(sindices.size()-1); i>=0; i-- ){ + Node lb = fm->getStar( tn ); + if( i>0 ){ + lb = values[sindices[i]]; + } + Node interval = fm->getInterval( lb, ub ); + for( unsigned j=0; j d_child; + int d_data; + void reset() { d_data = -1; d_child.clear(); d_complete = -1; } + void addEntry( FirstOrderModelFmc * m, Node c, Node v, int data, int index = 0 ); + bool hasGeneralization( FirstOrderModelFmc * m, Node c, int index = 0 ); + int getGeneralizationIndex( FirstOrderModelFmc * m, std::vector & inst, int index = 0 ); + void getEntries( FirstOrderModelFmc * m, Node c, std::vector & compat, std::vector & gen, int index = 0, bool is_gen = true ); + + void collectIndices(Node c, int index, std::vector< int >& indices ); + bool isComplete(FirstOrderModelFmc * m, Node c, int index); +};/* class EntryTrie */ + + +class Def +{ +public: + EntryTrie d_et; + //cond is APPLY_UF whose arguments are returned by FullModelChecker::getRepresentative + std::vector< Node > d_cond; + //value is returned by FullModelChecker::getRepresentative + std::vector< Node > d_value; + void basic_simplify( FirstOrderModelFmc * m ); +private: + enum { + status_unk, + status_redundant, + status_non_redundant + }; + std::vector< int > d_status; + bool d_has_simplified; +public: + Def() : d_has_simplified(false){} + void reset() { + d_et.reset(); + d_cond.clear(); + d_value.clear(); + d_status.clear(); + d_has_simplified = false; + } + bool addEntry( FirstOrderModelFmc * m, Node c, Node v); + Node evaluate( FirstOrderModelFmc * m, std::vector& inst ); + int getGeneralizationIndex( FirstOrderModelFmc * m, std::vector& inst ); + void simplify( FullModelChecker * mc, FirstOrderModelFmc * m ); + void debugPrint(const char * tr, Node op, FullModelChecker * m); +};/* class Def */ + + +class FullModelChecker : public QModelBuilder +{ +protected: + Node d_true; + Node d_false; + std::map > d_rep_ids; + std::map d_quant_models; + std::map d_quant_cond; + std::map< TypeNode, Node > d_array_cond; + std::map< Node, Node > d_array_term_cond; + std::map< Node, std::vector< int > > d_star_insts; + std::map< TypeNode, bool > d_preinitialized_types; + void preInitializeType( FirstOrderModelFmc * fm, TypeNode tn ); + Node normalizeArgReps(FirstOrderModelFmc * fm, Node op, Node n); + bool exhaustiveInstantiate(FirstOrderModelFmc * fm, Node f, Node c, int c_index); +protected: + void makeIntervalModel2( FirstOrderModelFmc * fm, Node op, std::vector< int > & indices, int index, + std::map< int, std::map< int, Node > >& changed_vals ); + void makeIntervalModel( FirstOrderModelFmc * fm, Node op, std::vector< int > & indices, int index, + std::map< int, std::map< int, Node > >& changed_vals ); + void convertIntervalModel( FirstOrderModelFmc * fm, Node op ); +private: + void doCheck(FirstOrderModelFmc * fm, Node f, Def & d, Node n ); + + void doNegate( Def & dc ); + void doVariableEquality( FirstOrderModelFmc * fm, Node f, Def & d, Node eq ); + void doVariableRelation( FirstOrderModelFmc * fm, Node f, Def & d, Def & dc, Node v); + void doUninterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, Node n, std::vector< Def > & dc ); + + void doUninterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, + Def & df, std::vector< Def > & dc, int index, + std::vector< Node > & cond, std::vector & val ); + void doUninterpretedCompose2( FirstOrderModelFmc * fm, Node f, + std::map< int, Node > & entries, int index, + std::vector< Node > & cond, std::vector< Node > & val, + EntryTrie & curr); + + void doInterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, Node n, + std::vector< Def > & dc, int index, + std::vector< Node > & cond, std::vector & val ); + int isCompat( FirstOrderModelFmc * fm, std::vector< Node > & cond, Node c ); + Node doIntervalMeet( FirstOrderModelFmc * fm, Node i1, Node i2, bool mk = true ); + bool doMeet( FirstOrderModelFmc * fm, std::vector< Node > & cond, Node c ); + Node mkCond( std::vector< Node > & cond ); + Node mkCondDefault( FirstOrderModelFmc * fm, Node f ); + void mkCondDefaultVec( FirstOrderModelFmc * fm, Node f, std::vector< Node > & cond ); + void mkCondVec( Node n, std::vector< Node > & cond ); + Node mkArrayCond( Node a ); + Node evaluateInterpreted( Node n, std::vector< Node > & vals ); + Node getSomeDomainElement( FirstOrderModelFmc * fm, TypeNode tn ); +public: + FullModelChecker( context::Context* c, QuantifiersEngine* qe ); + + void debugPrintCond(const char * tr, Node n, bool dispStar = false); + void debugPrint(const char * tr, Node n, bool dispStar = false); + + int doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ); + + Node getFunctionValue(FirstOrderModelFmc * fm, Node op, const char* argPrefix ); + + /** process build model */ + bool preProcessBuildModel(TheoryModel* m); + bool processBuildModel(TheoryModel* m); + + bool useSimpleModels(); +};/* class FullModelChecker */ + +}/* CVC4::theory::quantifiers::fmcheck namespace */ +}/* CVC4::theory::quantifiers namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__QUANTIFIERS__FULL_MODEL_CHECK_H */ diff --git a/src/theory/quantifiers/fmf/model_builder.cpp b/src/theory/quantifiers/fmf/model_builder.cpp new file mode 100644 index 000000000..9e7961172 --- /dev/null +++ b/src/theory/quantifiers/fmf/model_builder.cpp @@ -0,0 +1,808 @@ +/********************* */ +/*! \file model_builder.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 Implementation of model builder class + **/ + +#include "theory/quantifiers/fmf/model_builder.h" + +#include "options/quantifiers_options.h" +#include "theory/quantifiers/first_order_model.h" +#include "theory/quantifiers/instantiate.h" +#include "theory/quantifiers/fmf/model_engine.h" +#include "theory/quantifiers/quantifiers_attributes.h" +#include "theory/quantifiers/term_database.h" +#include "theory/quantifiers/term_util.h" +#include "theory/quantifiers/ematching/trigger.h" +#include "theory/theory_engine.h" +#include "theory/uf/equality_engine.h" +#include "theory/uf/theory_uf.h" +#include "theory/uf/theory_uf_model.h" +#include "theory/uf/theory_uf_strong_solver.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; + +QModelBuilder::QModelBuilder(context::Context* c, QuantifiersEngine* qe) + : TheoryEngineModelBuilder(qe->getTheoryEngine()), + d_qe(qe), + d_addedLemmas(0), + d_triedLemmas(0) {} + +bool QModelBuilder::optUseModel() { + return options::mbqiMode()!=MBQI_NONE || options::fmfBound(); +} + +bool QModelBuilder::preProcessBuildModel(TheoryModel* m) { + return preProcessBuildModelStd( m ); +} + +bool QModelBuilder::preProcessBuildModelStd(TheoryModel* m) { + d_addedLemmas = 0; + d_triedLemmas = 0; + if( options::fmfEmptySorts() || options::fmfFunWellDefinedRelevant() ){ + FirstOrderModel * fm = (FirstOrderModel*)m; + //traverse equality engine + std::map< TypeNode, bool > eqc_usort; + eq::EqClassesIterator eqcs_i = + eq::EqClassesIterator(fm->getEqualityEngine()); + while( !eqcs_i.isFinished() ){ + TypeNode tr = (*eqcs_i).getType(); + eqc_usort[tr] = true; + ++eqcs_i; + } + //look at quantified formulas + for( unsigned i=0; igetNumAssertedQuantifiers(); i++ ){ + Node q = fm->getAssertedQuantifier( i, true ); + if( fm->isQuantifierActive( q ) ){ + //check if any of these quantified formulas can be set inactive + if( options::fmfEmptySorts() ){ + for( unsigned i=0; isetQuantifierActive( q, false ); + } + } + }else if( options::fmfFunWellDefinedRelevant() ){ + if( q[0].getNumChildren()==1 ){ + TypeNode tn = q[0][0].getType(); + if( tn.getAttribute(AbsTypeFunDefAttribute()) ){ + //Trace("model-engine-debug2") << "...possible irrelevant function def : " << q << ", #rr = " << d_quantEngine->getModel()->d_rep_set.getNumRelevantGroundReps( tn ) << std::endl; + //we are allowed to assume the introduced type is empty + if( eqc_usort.find( tn )==eqc_usort.end() ){ + Trace("model-engine-debug") << "Irrelevant function definition : " << q << std::endl; + fm->setQuantifierActive( q, false ); + } + } + } + } + } + } + } + return true; +} + +void QModelBuilder::debugModel( TheoryModel* m ){ + //debug the model: cycle through all instantiations for all quantifiers, report ones that are not true + if( Trace.isOn("quant-check-model") ){ + FirstOrderModel* fm = (FirstOrderModel*)m; + Trace("quant-check-model") << "Testing quantifier instantiations..." << std::endl; + int tests = 0; + int bad = 0; + for( unsigned i=0; igetNumAssertedQuantifiers(); i++ ){ + Node f = fm->getAssertedQuantifier( i ); + std::vector< Node > vars; + for( unsigned j=0; jgetModel()->getRepSet(), &qrbe); + if( riter.setQuantifier( f ) ){ + while( !riter.isFinished() ){ + tests++; + std::vector< Node > terms; + for (unsigned k = 0; k < riter.getNumTerms(); k++) + { + terms.push_back( riter.getCurrentTerm( k ) ); + } + Node n = d_qe->getInstantiate()->getInstantiation(f, vars, terms); + Node val = fm->getValue( n ); + if (val != d_qe->getTermUtil()->d_true) + { + Trace("quant-check-model") << "******* Instantiation " << n << " for " << std::endl; + Trace("quant-check-model") << " " << f << std::endl; + Trace("quant-check-model") << " Evaluates to " << val << std::endl; + bad++; + } + riter.increment(); + } + Trace("quant-check-model") << "Tested " << tests << " instantiations"; + if( bad>0 ){ + Trace("quant-check-model") << ", " << bad << " failed" << std::endl; + } + Trace("quant-check-model") << "." << std::endl; + }else{ + if( riter.isIncomplete() ){ + Trace("quant-check-model") << "Warning: Could not test quantifier " << f << std::endl; + } + } + } + } +} + +bool TermArgBasisTrie::addTerm(FirstOrderModel* fm, Node n, unsigned argIndex) +{ + if (argIndex < n.getNumChildren()) + { + Node r; + if( n[ argIndex ].getAttribute(ModelBasisAttribute()) ){ + r = n[ argIndex ]; + }else{ + r = fm->getRepresentative( n[ argIndex ] ); + } + std::map< Node, TermArgBasisTrie >::iterator it = d_data.find( r ); + if( it==d_data.end() ){ + d_data[r].addTerm(fm, n, argIndex + 1); + return true; + }else{ + return it->second.addTerm(fm, n, argIndex + 1); + } + }else{ + return false; + } +} + +QModelBuilderIG::QModelBuilderIG(context::Context* c, QuantifiersEngine* qe) + : QModelBuilder(c, qe), + d_basisNoMatch(c), + d_didInstGen(false), + d_numQuantSat(0), + d_numQuantInstGen(0), + d_numQuantNoInstGen(0), + d_numQuantNoSelForm(0), + d_instGenMatches(0) {} + +/* +Node QModelBuilderIG::getCurrentUfModelValue( FirstOrderModel* fm, Node n, std::vector< Node > & args, bool partial ) { + return n; +} +*/ + +bool QModelBuilderIG::processBuildModel( TheoryModel* m ) { + FirstOrderModel* f = (FirstOrderModel*)m; + FirstOrderModelIG* fm = f->asFirstOrderModelIG(); + Trace("model-engine-debug") << "Process build model " << optUseModel() << std::endl; + d_didInstGen = false; + //reset the internal information + reset( fm ); + //only construct first order model if optUseModel() is true + if( optUseModel() ){ + Trace("model-engine-debug") << "Initializing " << fm->getNumAssertedQuantifiers() << " quantifiers..." << std::endl; + //check if any quantifiers are un-initialized + for( unsigned i=0; igetNumAssertedQuantifiers(); i++ ){ + Node q = fm->getAssertedQuantifier( i ); + if( d_qe->getModel()->isQuantifierActive( q ) ){ + int lems = initializeQuantifier(q, q, f); + d_statistics.d_init_inst_gen_lemmas += lems; + d_addedLemmas += lems; + if( d_qe->inConflict() ){ + break; + } + } + } + if( d_addedLemmas>0 ){ + Trace("model-engine") << "Initialize, Added Lemmas = " << d_addedLemmas << std::endl; + return false; + }else{ + Assert( !d_qe->inConflict() ); + //initialize model + fm->initialize(); + //analyze the functions + Trace("model-engine-debug") << "Analyzing model..." << std::endl; + analyzeModel( fm ); + //analyze the quantifiers + Trace("model-engine-debug") << "Analyzing quantifiers..." << std::endl; + d_uf_prefs.clear(); + for( unsigned i=0; igetNumAssertedQuantifiers(); i++ ){ + Node q = fm->getAssertedQuantifier( i ); + analyzeQuantifier( fm, q ); + } + + //if applicable, find exceptions to model via inst-gen + if( options::fmfInstGen() ){ + d_didInstGen = true; + d_instGenMatches = 0; + d_numQuantSat = 0; + d_numQuantInstGen = 0; + d_numQuantNoInstGen = 0; + d_numQuantNoSelForm = 0; + //now, see if we know that any exceptions via InstGen exist + Trace("model-engine-debug") << "Perform InstGen techniques for quantifiers..." << std::endl; + for( unsigned i=0; igetNumAssertedQuantifiers(); i++ ){ + Node f = fm->getAssertedQuantifier( i ); + if( d_qe->getModel()->isQuantifierActive( f ) ){ + int lems = doInstGen( fm, f ); + d_statistics.d_inst_gen_lemmas += lems; + d_addedLemmas += lems; + //temporary + if( lems>0 ){ + d_numQuantInstGen++; + }else if( hasInstGen( f ) ){ + d_numQuantNoInstGen++; + }else{ + d_numQuantNoSelForm++; + } + if( d_qe->inConflict() || ( options::fmfInstGenOneQuantPerRound() && lems>0 ) ){ + break; + } + }else{ + d_numQuantSat++; + } + } + Trace("model-engine-debug") << "Quantifiers sat/ig/n-ig/null " << d_numQuantSat << " / " << d_numQuantInstGen << " / "; + Trace("model-engine-debug") << d_numQuantNoInstGen << " / " << d_numQuantNoSelForm << std::endl; + Trace("model-engine-debug") << "Inst-gen # matches examined = " << d_instGenMatches << std::endl; + if( Trace.isOn("model-engine") ){ + if( d_addedLemmas>0 ){ + Trace("model-engine") << "InstGen, added lemmas = " << d_addedLemmas << std::endl; + }else{ + Trace("model-engine") << "No InstGen lemmas..." << std::endl; + } + } + } + //construct the model if necessary + if( d_addedLemmas==0 ){ + //if no immediate exceptions, build the model + // this model will be an approximation that will need to be tested via exhaustive instantiation + Trace("model-engine-debug") << "Building model..." << std::endl; + //build model for UF + for( std::map< Node, uf::UfModelTree >::iterator it = fm->d_uf_model_tree.begin(); it != fm->d_uf_model_tree.end(); ++it ){ + Trace("model-engine-debug-uf") << "Building model for " << it->first << "..." << std::endl; + constructModelUf( fm, it->first ); + } + Trace("model-engine-debug") << "Done building models." << std::endl; + }else{ + return false; + } + } + } + //update models + for( std::map< Node, uf::UfModelTree >::iterator it = fm->d_uf_model_tree.begin(); it != fm->d_uf_model_tree.end(); ++it ){ + it->second.update( fm ); + Trace("model-func") << "QModelBuilder: Make function value from tree " << it->first << std::endl; + //construct function values + Node f_def = it->second.getFunctionValue( "$x" ); + fm->assignFunctionDefinition( it->first, f_def ); + } + Assert( d_addedLemmas==0 ); + return TheoryEngineModelBuilder::processBuildModel( m ); +} + +int QModelBuilderIG::initializeQuantifier(Node f, Node fp, FirstOrderModel* fm) +{ + if( d_quant_basis_match_added.find( f )==d_quant_basis_match_added.end() ){ + //create the basis match if necessary + if( d_quant_basis_match.find( f )==d_quant_basis_match.end() ){ + Trace("inst-fmf-init") << "Initialize " << f << std::endl; + //add the model basis instantiation + // This will help produce the necessary information for model completion. + // We do this by extending distinguish ground assertions (those + // containing terms with "model basis" attribute) to hold for all cases. + + ////first, check if any variables are required to be equal + //for( std::map< Node, bool >::iterator it = d_quantEngine->d_phase_reqs[f].begin(); + // it != d_quantEngine->d_phase_reqs[f].end(); ++it ){ + // Node n = it->first; + // if( n.getKind()==EQUAL && n[0].getKind()==INST_CONSTANT && n[1].getKind()==INST_CONSTANT ){ + // Notice() << "Unhandled phase req: " << n << std::endl; + // } + //} + d_quant_basis_match[f] = InstMatch( f ); + for (unsigned j = 0; j < f[0].getNumChildren(); j++) + { + Node t = fm->getModelBasisTerm(f[0][j].getType()); + //calculate the basis match for f + d_quant_basis_match[f].setValue( j, t ); + } + ++(d_statistics.d_num_quants_init); + } + //try to add it + Trace("inst-fmf-init") << "Init: try to add match " << d_quant_basis_match[f] << std::endl; + //add model basis instantiation + if (d_qe->getInstantiate()->addInstantiation(fp, d_quant_basis_match[f])) + { + d_quant_basis_match_added[f] = true; + return 1; + }else{ + //shouldn't happen usually, but will occur if x != y is a required literal for f. + //Notice() << "No model basis for " << f << std::endl; + d_quant_basis_match_added[f] = false; + } + } + return 0; +} + +void QModelBuilderIG::analyzeModel( FirstOrderModel* fm ){ + FirstOrderModelIG* fmig = fm->asFirstOrderModelIG(); + d_uf_model_constructed.clear(); + //determine if any functions are constant + for( std::map< Node, uf::UfModelTree >::iterator it = fmig->d_uf_model_tree.begin(); it != fmig->d_uf_model_tree.end(); ++it ){ + Node op = it->first; + TermArgBasisTrie tabt; + std::map< Node, std::vector< Node > >::iterator itut = fmig->d_uf_terms.find( op ); + if( itut!=fmig->d_uf_terms.end() ){ + for( size_t i=0; isecond.size(); i++ ){ + Node n = fmig->d_uf_terms[op][i]; + //for calculating if op is constant + Node v = fmig->getRepresentative( n ); + if( i==0 ){ + d_uf_prefs[op].d_const_val = v; + }else if( v!=d_uf_prefs[op].d_const_val ){ + d_uf_prefs[op].d_const_val = Node::null(); + break; + } + //for calculating terms that we don't need to consider + //if( d_qe->getTermDatabase()->isTermActive( n ) || n.getAttribute(ModelBasisArgAttribute())!=0 ){ + if( d_basisNoMatch.find( n )==d_basisNoMatch.end() ){ + //need to consider if it is not congruent modulo model basis + if( !tabt.addTerm( fmig, n ) ){ + d_basisNoMatch[n] = true; + } + } + } + } + if( !d_uf_prefs[op].d_const_val.isNull() ){ + fmig->d_uf_model_gen[op].setDefaultValue( d_uf_prefs[op].d_const_val ); + fmig->d_uf_model_gen[op].makeModel( fmig, it->second ); + Debug("fmf-model-cons") << "Function " << op << " is the constant function "; + fmig->printRepresentativeDebug( "fmf-model-cons", d_uf_prefs[op].d_const_val ); + Debug("fmf-model-cons") << std::endl; + d_uf_model_constructed[op] = true; + }else{ + d_uf_model_constructed[op] = false; + } + } +} + +bool QModelBuilderIG::hasConstantDefinition( Node n ){ + Node lit = n.getKind()==NOT ? n[0] : n; + if( lit.getKind()==APPLY_UF ){ + Node op = lit.getOperator(); + if( !d_uf_prefs[op].d_const_val.isNull() ){ + return true; + } + } + return false; +} + +QModelBuilderIG::Statistics::Statistics(): + d_num_quants_init("QModelBuilderIG::Number_Quantifiers", 0), + d_num_partial_quants_init("QModelBuilderIG::Number_Partial_Quantifiers", 0), + d_init_inst_gen_lemmas("QModelBuilderIG::Initialize_Inst_Gen_Lemmas", 0 ), + d_inst_gen_lemmas("QModelBuilderIG::Inst_Gen_Lemmas", 0 ), + d_eval_formulas("QModelBuilderIG::Eval_Formulas", 0 ), + d_eval_uf_terms("QModelBuilderIG::Eval_Uf_Terms", 0 ), + d_eval_lits("QModelBuilderIG::Eval_Lits", 0 ), + d_eval_lits_unknown("QModelBuilderIG::Eval_Lits_Unknown", 0 ) +{ + smtStatisticsRegistry()->registerStat(&d_num_quants_init); + smtStatisticsRegistry()->registerStat(&d_num_partial_quants_init); + smtStatisticsRegistry()->registerStat(&d_init_inst_gen_lemmas); + smtStatisticsRegistry()->registerStat(&d_inst_gen_lemmas); + smtStatisticsRegistry()->registerStat(&d_eval_formulas); + smtStatisticsRegistry()->registerStat(&d_eval_uf_terms); + smtStatisticsRegistry()->registerStat(&d_eval_lits); + smtStatisticsRegistry()->registerStat(&d_eval_lits_unknown); +} + +QModelBuilderIG::Statistics::~Statistics(){ + smtStatisticsRegistry()->unregisterStat(&d_num_quants_init); + smtStatisticsRegistry()->unregisterStat(&d_num_partial_quants_init); + smtStatisticsRegistry()->unregisterStat(&d_init_inst_gen_lemmas); + smtStatisticsRegistry()->unregisterStat(&d_inst_gen_lemmas); + smtStatisticsRegistry()->unregisterStat(&d_eval_formulas); + smtStatisticsRegistry()->unregisterStat(&d_eval_uf_terms); + smtStatisticsRegistry()->unregisterStat(&d_eval_lits); + smtStatisticsRegistry()->unregisterStat(&d_eval_lits_unknown); +} + +//do exhaustive instantiation +int QModelBuilderIG::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ) { + if( optUseModel() ){ + QRepBoundExt qrbe(d_qe); + RepSetIterator riter(d_qe->getModel()->getRepSet(), &qrbe); + if( riter.setQuantifier( f ) ){ + FirstOrderModelIG * fmig = (FirstOrderModelIG*)d_qe->getModel(); + Debug("inst-fmf-ei") << "Reset evaluate..." << std::endl; + fmig->resetEvaluate(); + Debug("inst-fmf-ei") << "Begin instantiation..." << std::endl; + EqualityQuery* qy = d_qe->getEqualityQuery(); + Instantiate* inst = d_qe->getInstantiate(); + TermUtil* util = d_qe->getTermUtil(); + while( !riter.isFinished() && ( d_addedLemmas==0 || !options::fmfOneInstPerRound() ) ){ + d_triedLemmas++; + if( Debug.isOn("inst-fmf-ei-debug") ){ + for( int i=0; i<(int)riter.d_index.size(); i++ ){ + Debug("inst-fmf-ei-debug") << i << " : " << riter.d_index[i] << " : " << riter.getCurrentTerm( i ) << std::endl; + } + } + int eval = 0; + int depIndex; + //see if instantiation is already true in current model + if( Debug.isOn("fmf-model-eval") ){ + Debug("fmf-model-eval") << "Evaluating "; + riter.debugPrintSmall("fmf-model-eval"); + Debug("fmf-model-eval") << "Done calculating terms." << std::endl; + } + //if evaluate(...)==1, then the instantiation is already true in the model + // depIndex is the index of the least significant variable that this evaluation relies upon + depIndex = riter.getNumTerms()-1; + Debug("fmf-model-eval") << "We will evaluate " + << util->getInstConstantBody(f) << std::endl; + eval = fmig->evaluate(util->getInstConstantBody(f), depIndex, &riter); + if( eval==1 ){ + Debug("fmf-model-eval") << " Returned success with depIndex = " << depIndex << std::endl; + }else{ + Debug("fmf-model-eval") << " Returned " << (eval==-1 ? "failure" : "unknown") << ", depIndex = " << depIndex << std::endl; + } + if( eval==1 ){ + //instantiation is already true -> skip + riter.incrementAtIndex(depIndex); + }else{ + //instantiation was not shown to be true, construct the match + InstMatch m( f ); + for (unsigned i = 0; i < riter.getNumTerms(); i++) + { + m.set(qy, i, riter.getCurrentTerm(i)); + } + Debug("fmf-model-eval") << "* Add instantiation " << m << std::endl; + //add as instantiation + if (inst->addInstantiation(f, m, true)) + { + d_addedLemmas++; + if( d_qe->inConflict() ){ + break; + } + //if the instantiation is show to be false, and we wish to skip multiple instantiations at once + if( eval==-1 ){ + riter.incrementAtIndex(depIndex); + }else{ + riter.increment(); + } + }else{ + Debug("fmf-model-eval") << "* Failed Add instantiation " << m << std::endl; + riter.increment(); + } + } + } + //print debugging information + if( fmig ){ + d_statistics.d_eval_formulas += fmig->d_eval_formulas; + d_statistics.d_eval_uf_terms += fmig->d_eval_uf_terms; + d_statistics.d_eval_lits += fmig->d_eval_lits; + d_statistics.d_eval_lits_unknown += fmig->d_eval_lits_unknown; + } + Trace("inst-fmf-ei") << "For " << f << ", finished: " << std::endl; + Trace("inst-fmf-ei") << " Inst Tried: " << d_triedLemmas << std::endl; + Trace("inst-fmf-ei") << " Inst Added: " << d_addedLemmas << std::endl; + if( d_addedLemmas>1000 ){ + Trace("model-engine-warn") << "WARNING: many instantiations produced for " << f << ": " << std::endl; + Trace("model-engine-warn") << " Inst Tried: " << d_triedLemmas << std::endl; + Trace("model-engine-warn") << " Inst Added: " << d_addedLemmas << std::endl; + Trace("model-engine-warn") << std::endl; + } + } + //if the iterator is incomplete, we will return unknown instead of sat if no instantiations are added this round + return riter.isIncomplete() ? -1 : 1; + }else{ + return 0; + } +} + + + +void QModelBuilderDefault::reset( FirstOrderModel* fm ){ + d_quant_selection_lit.clear(); + d_quant_selection_lit_candidates.clear(); + d_quant_selection_lit_terms.clear(); + d_term_selection_lit.clear(); + d_op_selection_terms.clear(); +} + + +int QModelBuilderDefault::getSelectionScore( std::vector< Node >& uf_terms ) { + /* + size_t maxChildren = 0; + for( size_t i=0; imaxChildren ){ + maxChildren = uf_terms[i].getNumChildren(); + } + } + //TODO: look at how many entries they have? + return (int)maxChildren; + */ + return 0; +} + +void QModelBuilderDefault::analyzeQuantifier( FirstOrderModel* fm, Node f ){ + if( d_qe->getModel()->isQuantifierActive( f ) ){ + FirstOrderModelIG* fmig = fm->asFirstOrderModelIG(); + Debug("fmf-model-prefs") << "Analyze quantifier " << f << std::endl; + //the pro/con preferences for this quantifier + std::vector< Node > pro_con[2]; + //the terms in the selection literal we choose + std::vector< Node > selectionLitTerms; + Trace("inst-gen-debug-quant") << "Inst-gen analyze " << f << std::endl; + //for each asserted quantifier f, + // - determine selection literals + // - check which function/predicates have good and bad definitions for satisfying f + if( d_phase_reqs.find( f )==d_phase_reqs.end() ){ + d_phase_reqs[f].initialize( d_qe->getTermUtil()->getInstConstantBody( f ), true ); + } + int selectLitScore = -1; + for( std::map< Node, bool >::iterator it = d_phase_reqs[f].d_phase_reqs.begin(); it != d_phase_reqs[f].d_phase_reqs.end(); ++it ){ + //the literal n is phase-required for quantifier f + Node n = it->first; + Node gn = fm->getModelBasis(f, n); + Debug("fmf-model-req") << " Req: " << n << " -> " << it->second << std::endl; + bool value; + //if the corresponding ground abstraction literal has a SAT value + if( d_qe->getValuation().hasSatValue( gn, value ) ){ + //collect the non-ground uf terms that this literal contains + // and compute if all of the symbols in this literal have + // constant definitions. + bool isConst = true; + std::vector< Node > uf_terms; + if( TermUtil::hasInstConstAttr(n) ){ + isConst = false; + if( gn.getKind()==APPLY_UF ){ + uf_terms.push_back( gn ); + isConst = hasConstantDefinition( gn ); + }else if( gn.getKind()==EQUAL ){ + isConst = true; + for( int j=0; j<2; j++ ){ + if( TermUtil::hasInstConstAttr(n[j]) ){ + if( n[j].getKind()==APPLY_UF && + fmig->d_uf_model_tree.find( gn[j].getOperator() )!=fmig->d_uf_model_tree.end() ){ + uf_terms.push_back( gn[j] ); + isConst = isConst && hasConstantDefinition( gn[j] ); + }else{ + isConst = false; + } + } + } + } + } + //check if the value in the SAT solver matches the preference according to the quantifier + int pref = 0; + if( value!=it->second ){ + //we have a possible selection literal + bool selectLit = d_quant_selection_lit[f].isNull(); + bool selectLitConstraints = true; + //it is a constantly defined selection literal : the quantifier is sat + if( isConst ){ + selectLit = selectLit || d_qe->getModel()->isQuantifierActive( f ); + d_qe->getModel()->setQuantifierActive( f, false ); + //check if choosing this literal would add any additional constraints to default definitions + selectLitConstraints = false; + for( int j=0; j<(int)uf_terms.size(); j++ ){ + Node op = uf_terms[j].getOperator(); + if( d_uf_prefs[op].d_reconsiderModel ){ + selectLitConstraints = true; + } + } + if( !selectLitConstraints ){ + selectLit = true; + } + } + //also check if it is naturally a better literal + if( !selectLit ){ + int score = getSelectionScore( uf_terms ); + //Trace("inst-gen-debug") << "Check " << score << " < " << selectLitScore << std::endl; + selectLit = scoregetModel()->isQuantifierActive( f ) ){ + Debug("fmf-model-prefs") << " It is " << ( pref==1 ? "pro" : "con" ); + Debug("fmf-model-prefs") << " the definition of " << n << std::endl; + for( int j=0; j<(int)uf_terms.size(); j++ ){ + pro_con[ pref==1 ? 0 : 1 ].push_back( uf_terms[j] ); + } + } + } + } + //process information about selection literal for f + if( !d_quant_selection_lit[f].isNull() ){ + d_quant_selection_lit_terms[f].insert( d_quant_selection_lit_terms[f].begin(), selectionLitTerms.begin(), selectionLitTerms.end() ); + for( int i=0; i<(int)selectionLitTerms.size(); i++ ){ + d_term_selection_lit[ selectionLitTerms[i] ] = d_quant_selection_lit[f]; + d_op_selection_terms[ selectionLitTerms[i].getOperator() ].push_back( selectionLitTerms[i] ); + } + }else{ + Trace("inst-gen-warn") << "WARNING: " << f << " has no selection literals" << std::endl; + } + //process information about requirements and preferences of quantifier f + if( !d_qe->getModel()->isQuantifierActive( f ) ){ + Debug("fmf-model-prefs") << " * Constant SAT due to definition of ops: "; + for( int i=0; i<(int)selectionLitTerms.size(); i++ ){ + Debug("fmf-model-prefs") << selectionLitTerms[i] << " "; + d_uf_prefs[ selectionLitTerms[i].getOperator() ].d_reconsiderModel = false; + } + Debug("fmf-model-prefs") << std::endl; + }else{ + //note quantifier's value preferences to models + for( int k=0; k<2; k++ ){ + for( int j=0; j<(int)pro_con[k].size(); j++ ){ + Node op = pro_con[k][j].getOperator(); + Node r = fmig->getRepresentative( pro_con[k][j] ); + d_uf_prefs[op].setValuePreference( f, pro_con[k][j], r, k==0 ); + } + } + } + } +} + +int QModelBuilderDefault::doInstGen( FirstOrderModel* fm, Node f ){ + int addedLemmas = 0; + //we wish to add all known exceptions to our selection literal for f. this will help to refine our current model. + //This step is advantageous over exhaustive instantiation, since we are adding instantiations that involve model basis terms, + // effectively acting as partial instantiations instead of pointwise instantiations. + if( !d_quant_selection_lit[f].isNull() ){ + Trace("inst-gen") << "Do Inst-Gen for " << f << std::endl; + for( size_t i=0; i tr_terms; + if( lit.getKind()==APPLY_UF ){ + //only match predicates that are contrary to this one, use literal matching + Node eq = NodeManager::currentNM()->mkNode( + EQUAL, lit, NodeManager::currentNM()->mkConst(!phase)); + tr_terms.push_back( eq ); + }else if( lit.getKind()==EQUAL ){ + //collect trigger terms + for( int j=0; j<2; j++ ){ + if( TermUtil::hasInstConstAttr(lit[j]) ){ + if( lit[j].getKind()==APPLY_UF ){ + tr_terms.push_back( lit[j] ); + }else{ + tr_terms.clear(); + break; + } + } + } + if( tr_terms.size()==1 && !phase ){ + //equality between a function and a ground term, use literal matching + tr_terms.clear(); + tr_terms.push_back( lit ); + } + } + //if applicable, try to add exceptions here + if( !tr_terms.empty() ){ + //make a trigger for these terms, add instantiations + inst::Trigger* tr = inst::Trigger::mkTrigger( d_qe, f, tr_terms, true, inst::Trigger::TR_MAKE_NEW ); + //Notice() << "Trigger = " << (*tr) << std::endl; + tr->resetInstantiationRound(); + tr->reset( Node::null() ); + //d_qe->d_optInstMakeRepresentative = false; + //d_qe->d_optMatchIgnoreModelBasis = true; + addedLemmas += tr->addInstantiations(); + } + } + } + return addedLemmas; +} + +void QModelBuilderDefault::constructModelUf( FirstOrderModel* fm, Node op ){ + FirstOrderModelIG* fmig = fm->asFirstOrderModelIG(); + if( optReconsiderFuncConstants() ){ + //reconsider constant functions that weren't necessary + if( d_uf_model_constructed[op] ){ + if( d_uf_prefs[op].d_reconsiderModel ){ + //if we are allowed to reconsider default value, then see if the default value can be improved + Node v = d_uf_prefs[op].d_const_val; + if( d_uf_prefs[op].d_value_pro_con[0][v].empty() ){ + Debug("fmf-model-cons-debug") << "Consider changing the default value for " << op << std::endl; + fmig->d_uf_model_tree[op].clear(); + fmig->d_uf_model_gen[op].clear(); + d_uf_model_constructed[op] = false; + } + } + } + } + if( !d_uf_model_constructed[op] ){ + //construct the model for the uninterpretted function/predicate + bool setDefaultVal = true; + Node defaultTerm = fmig->getModelBasisOpTerm(op); + Trace("fmf-model-cons") << "Construct model for " << op << "..." << std::endl; + //set the values in the model + std::map< Node, std::vector< Node > >::iterator itut = fmig->d_uf_terms.find( op ); + if( itut!=fmig->d_uf_terms.end() ){ + for( size_t i=0; isecond.size(); i++ ){ + Node n = itut->second[i]; + // only consider unique up to congruence (in model equality engine)? + Node v = fmig->getRepresentative( n ); + Trace("fmf-model-cons") << "Set term " << n << " : " + << fmig->getRepSet()->getIndexFor(v) << " " << v + << std::endl; + //if this assertion did not help the model, just consider it ground + //set n = v in the model tree + //set it as ground value + fmig->d_uf_model_gen[op].setValue( fm, n, v ); + if( fmig->d_uf_model_gen[op].optUsePartialDefaults() ){ + //also set as default value if necessary + if( n.hasAttribute(ModelBasisArgAttribute()) && n.getAttribute(ModelBasisArgAttribute())!=0 ){ + Trace("fmf-model-cons") << " Set as default." << std::endl; + fmig->d_uf_model_gen[op].setValue( fm, n, v, false ); + if( n==defaultTerm ){ + //incidentally already set, we will not need to find a default value + setDefaultVal = false; + } + } + }else{ + if( n==defaultTerm ){ + fmig->d_uf_model_gen[op].setValue( fm, n, v, false ); + //incidentally already set, we will not need to find a default value + setDefaultVal = false; + } + } + } + } + //set the overall default value if not set already (is this necessary??) + if( setDefaultVal ){ + Trace("fmf-model-cons") << " Choose default value..." << std::endl; + //chose defaultVal based on heuristic, currently the best ratio of "pro" responses + Node defaultVal = d_uf_prefs[op].getBestDefaultValue( defaultTerm, fm ); + if( defaultVal.isNull() ){ + if (!fmig->getRepSet()->hasType(defaultTerm.getType())) + { + Node mbt = fmig->getModelBasisTerm(defaultTerm.getType()); + fmig->getRepSetPtr()->d_type_reps[defaultTerm.getType()].push_back( + mbt); + } + defaultVal = + fmig->getRepSet()->getRepresentative(defaultTerm.getType(), 0); + } + Assert( !defaultVal.isNull() ); + Trace("fmf-model-cons") + << "Set default term : " << fmig->getRepSet()->getIndexFor(defaultVal) + << std::endl; + fmig->d_uf_model_gen[op].setValue( fm, defaultTerm, defaultVal, false ); + } + Debug("fmf-model-cons") << " Making model..."; + fmig->d_uf_model_gen[op].makeModel( fm, fmig->d_uf_model_tree[op] ); + d_uf_model_constructed[op] = true; + Debug("fmf-model-cons") << " Finished constructing model for " << op << "." << std::endl; + } +} diff --git a/src/theory/quantifiers/fmf/model_builder.h b/src/theory/quantifiers/fmf/model_builder.h new file mode 100644 index 000000000..4eb592b3e --- /dev/null +++ b/src/theory/quantifiers/fmf/model_builder.h @@ -0,0 +1,202 @@ +/********************* */ +/*! \file model_builder.h + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds, Morgan Deters, 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 Model Builder class + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__QUANTIFIERS__MODEL_BUILDER_H +#define __CVC4__THEORY__QUANTIFIERS__MODEL_BUILDER_H + +#include "theory/quantifiers_engine.h" +#include "theory/theory_model_builder.h" +#include "theory/uf/theory_uf_model.h" + +namespace CVC4 { +namespace theory { +namespace quantifiers { + + +class QModelBuilder : public TheoryEngineModelBuilder +{ +protected: + //quantifiers engine + QuantifiersEngine* d_qe; + bool preProcessBuildModel(TheoryModel* m); //must call preProcessBuildModelStd + bool preProcessBuildModelStd(TheoryModel* m); + /** number of lemmas generated while building model */ + unsigned d_addedLemmas; + unsigned d_triedLemmas; +public: + QModelBuilder( context::Context* c, QuantifiersEngine* qe ); + + //do exhaustive instantiation + // 0 : failed, but resorting to true exhaustive instantiation may work + // >0 : success + // <0 : failed + virtual int doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ) { return false; } + //whether to construct model + virtual bool optUseModel(); + /** exist instantiation ? */ + virtual bool existsInstantiation( Node f, InstMatch& m, bool modEq = true, bool modInst = false ) { return false; } + //debug model + virtual void debugModel( TheoryModel* m ); + //statistics + unsigned getNumAddedLemmas() { return d_addedLemmas; } + unsigned getNumTriedLemmas() { return d_triedLemmas; } +}; + + + + + +class TermArgBasisTrie { +public: + /** the data */ + std::map< Node, TermArgBasisTrie > d_data; + /** add term to the trie */ + bool addTerm(FirstOrderModel* fm, Node n, unsigned argIndex = 0); +};/* class TermArgBasisTrie */ + +/** model builder class + * This class is capable of building candidate models based on the current quantified formulas + * that are asserted. Use: + * (1) call QModelBuilder::buildModel( m, false );, where m is a FirstOrderModel + * (2) if candidate model is determined to be a real model, + then call QModelBuilder::buildModel( m, true ); + */ +class QModelBuilderIG : public QModelBuilder +{ + typedef context::CDHashMap BoolMap; + + protected: + BoolMap d_basisNoMatch; + //map from operators to model preference data + std::map< Node, uf::UfModelPreferenceData > d_uf_prefs; + //built model uf + std::map< Node, bool > d_uf_model_constructed; + //whether inst gen was done + bool d_didInstGen; + /** process build model */ + virtual bool processBuildModel( TheoryModel* m ); + + protected: + //reset + virtual void reset( FirstOrderModel* fm ) = 0; + //initialize quantifiers, return number of lemmas produced + virtual int initializeQuantifier(Node f, Node fp, FirstOrderModel* fm); + //analyze model + virtual void analyzeModel( FirstOrderModel* fm ); + //analyze quantifiers + virtual void analyzeQuantifier( FirstOrderModel* fm, Node f ) = 0; + //do InstGen techniques for quantifier, return number of lemmas produced + virtual int doInstGen( FirstOrderModel* fm, Node f ) = 0; + //theory-specific build models + virtual void constructModelUf( FirstOrderModel* fm, Node op ) = 0; + + protected: + //map from quantifiers to if are SAT + //std::map< Node, bool > d_quant_sat; + //which quantifiers have been initialized + std::map< Node, bool > d_quant_basis_match_added; + //map from quantifiers to model basis match + std::map< Node, InstMatch > d_quant_basis_match; + + protected: // helper functions + /** term has constant definition */ + bool hasConstantDefinition( Node n ); + + public: + QModelBuilderIG( context::Context* c, QuantifiersEngine* qe ); + + public: + /** statistics class */ + class Statistics { + public: + IntStat d_num_quants_init; + IntStat d_num_partial_quants_init; + IntStat d_init_inst_gen_lemmas; + IntStat d_inst_gen_lemmas; + IntStat d_eval_formulas; + IntStat d_eval_uf_terms; + IntStat d_eval_lits; + IntStat d_eval_lits_unknown; + Statistics(); + ~Statistics(); + }; + Statistics d_statistics; + // is term selected + virtual bool isTermSelected( Node n ) { return false; } + /** quantifier has inst-gen definition */ + virtual bool hasInstGen( Node f ) = 0; + /** did inst gen this round? */ + bool didInstGen() { return d_didInstGen; } + // is quantifier active? + bool isQuantifierActive( Node f ); + //do exhaustive instantiation + int doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ); + + //temporary stats + int d_numQuantSat; + int d_numQuantInstGen; + int d_numQuantNoInstGen; + int d_numQuantNoSelForm; + //temporary stat + int d_instGenMatches; +};/* class QModelBuilder */ + + +class QModelBuilderDefault : public QModelBuilderIG +{ + private: /// information for (old) InstGen + // map from quantifiers to their selection literals + std::map< Node, Node > d_quant_selection_lit; + std::map< Node, std::vector< Node > > d_quant_selection_lit_candidates; + //map from quantifiers to their selection literal terms + std::map< Node, std::vector< Node > > d_quant_selection_lit_terms; + //map from terms to the selection literals they exist in + std::map< Node, Node > d_term_selection_lit; + //map from operators to terms that appear in selection literals + std::map< Node, std::vector< Node > > d_op_selection_terms; + //get selection score + int getSelectionScore( std::vector< Node >& uf_terms ); + + protected: + //reset + void reset(FirstOrderModel* fm) override; + //analyze quantifier + void analyzeQuantifier(FirstOrderModel* fm, Node f) override; + //do InstGen techniques for quantifier, return number of lemmas produced + int doInstGen(FirstOrderModel* fm, Node f) override; + //theory-specific build models + void constructModelUf(FirstOrderModel* fm, Node op) override; + + protected: + std::map< Node, QuantPhaseReq > d_phase_reqs; + + public: + QModelBuilderDefault( context::Context* c, QuantifiersEngine* qe ) : QModelBuilderIG( c, qe ){} + + //options + bool optReconsiderFuncConstants() { return true; } + //has inst gen + bool hasInstGen(Node f) override + { + return !d_quant_selection_lit[f].isNull(); + } +}; + +}/* CVC4::theory::quantifiers namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__QUANTIFIERS__MODEL_BUILDER_H */ diff --git a/src/theory/quantifiers/fmf/model_engine.cpp b/src/theory/quantifiers/fmf/model_engine.cpp new file mode 100644 index 000000000..3f3c21907 --- /dev/null +++ b/src/theory/quantifiers/fmf/model_engine.cpp @@ -0,0 +1,342 @@ +/********************* */ +/*! \file model_engine.cpp + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds, Morgan Deters, 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 Implementation of model engine class + **/ + +#include "theory/quantifiers/fmf/model_engine.h" + +#include "options/quantifiers_options.h" +#include "theory/quantifiers/fmf/ambqi_builder.h" +#include "theory/quantifiers/first_order_model.h" +#include "theory/quantifiers/fmf/full_model_check.h" +#include "theory/quantifiers/instantiate.h" +#include "theory/quantifiers/quantifiers_attributes.h" +#include "theory/quantifiers/term_database.h" +#include "theory/quantifiers/term_util.h" +#include "theory/theory_engine.h" +#include "theory/uf/equality_engine.h" +#include "theory/uf/theory_uf.h" +#include "theory/uf/theory_uf_strong_solver.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; +using namespace CVC4::theory::inst; + +//Model Engine constructor +ModelEngine::ModelEngine( context::Context* c, QuantifiersEngine* qe ) : +QuantifiersModule( qe ), +d_incomplete_check(true), +d_addedLemmas(0), +d_triedLemmas(0), +d_totalLemmas(0) +{ + +} + +ModelEngine::~ModelEngine() { + +} + +bool ModelEngine::needsCheck( Theory::Effort e ) { + return e==Theory::EFFORT_LAST_CALL; +} + +QuantifiersModule::QEffort ModelEngine::needsModel(Theory::Effort e) +{ + if( options::mbqiInterleave() ){ + return QEFFORT_STANDARD; + }else{ + return QEFFORT_MODEL; + } +} + +void ModelEngine::reset_round( Theory::Effort e ) { + d_incomplete_check = true; +} +void ModelEngine::check(Theory::Effort e, QEffort quant_e) +{ + bool doCheck = false; + if( options::mbqiInterleave() ){ + doCheck = quant_e == QEFFORT_STANDARD && d_quantEngine->hasAddedLemma(); + } + if( !doCheck ){ + doCheck = quant_e == QEFFORT_MODEL; + } + if( doCheck ){ + Assert( !d_quantEngine->inConflict() ); + int addedLemmas = 0; + FirstOrderModel* fm = d_quantEngine->getModel(); + + //the following will test that the model satisfies all asserted universal quantifiers by + // (model-based) exhaustive instantiation. + double clSet = 0; + if( Trace.isOn("model-engine") ){ + Trace("model-engine") << "---Model Engine Round---" << std::endl; + clSet = double(clock())/double(CLOCKS_PER_SEC); + } + + Trace("model-engine-debug") << "Verify uf ss is minimal..." << std::endl; + //let the strong solver verify that the model is minimal + //for debugging, this will if there are terms in the model that the strong solver was not notified of + uf::StrongSolverTheoryUF * ufss = ((uf::TheoryUF*)d_quantEngine->getTheoryEngine()->theoryOf( THEORY_UF ))->getStrongSolver(); + if( !ufss || ufss->debugModel( fm ) ){ + Trace("model-engine-debug") << "Check model..." << std::endl; + d_incomplete_check = false; + //print debug + if( Trace.isOn("fmf-model-complete") ){ + Trace("fmf-model-complete") << std::endl; + debugPrint("fmf-model-complete"); + } + //successfully built an acceptable model, now check it + addedLemmas += checkModel(); + }else{ + addedLemmas++; + } + + if( Trace.isOn("model-engine") ){ + double clSet2 = double(clock())/double(CLOCKS_PER_SEC); + Trace("model-engine") << "Finished model engine, time = " << (clSet2-clSet) << std::endl; + } + + if( addedLemmas==0 ){ + Trace("model-engine-debug") << "No lemmas added, incomplete = " << ( d_incomplete_check || !d_incomplete_quants.empty() ) << std::endl; + //CVC4 will answer SAT or unknown + if( Trace.isOn("fmf-consistent") ){ + Trace("fmf-consistent") << std::endl; + debugPrint("fmf-consistent"); + } + } + } +} + +bool ModelEngine::checkComplete() { + return !d_incomplete_check; +} + +bool ModelEngine::checkCompleteFor( Node q ) { + return std::find( d_incomplete_quants.begin(), d_incomplete_quants.end(), q )==d_incomplete_quants.end(); +} + +void ModelEngine::registerQuantifier( Node f ){ + if( Trace.isOn("fmf-warn") ){ + bool canHandle = true; + for( unsigned i=0; igetModel(); + + //flatten the representatives + //Trace("model-engine-debug") << "Flattening representatives...." << std::endl; + // d_quantEngine->getEqualityQuery()->flattenRepresentatives( + // fm->getRepSet()->d_type_reps ); + + //for debugging, setup + for (std::map >::iterator it = + fm->getRepSetPtr()->d_type_reps.begin(); + it != fm->getRepSetPtr()->d_type_reps.end(); + ++it) + { + if( it->first.isSort() ){ + Trace("model-engine") << "Cardinality( " << it->first << " )" << " = " << it->second.size() << std::endl; + Trace("model-engine-debug") << " Reps : "; + for( size_t i=0; isecond.size(); i++ ){ + Trace("model-engine-debug") << it->second[i] << " "; + } + Trace("model-engine-debug") << std::endl; + Trace("model-engine-debug") << " Term reps : "; + for( size_t i=0; isecond.size(); i++ ){ + Node r = d_quantEngine->getInternalRepresentative( it->second[i], Node::null(), 0 ); + Trace("model-engine-debug") << r << " "; + } + Trace("model-engine-debug") << std::endl; + Node mbt = fm->getModelBasisTerm(it->first); + Trace("model-engine-debug") << " Basis term : " << mbt << std::endl; + } + } + + d_triedLemmas = 0; + d_addedLemmas = 0; + d_totalLemmas = 0; + //for statistics + if( Trace.isOn("model-engine") ){ + for( unsigned i=0; igetNumAssertedQuantifiers(); i++ ){ + Node f = fm->getAssertedQuantifier( i ); + if( d_quantEngine->getModel()->isQuantifierActive( f ) && d_quantEngine->hasOwnership( f, this ) ){ + int totalInst = 1; + for( unsigned j=0; jgetRepSet()->hasType(tn)) + { + totalInst = + totalInst * (int)fm->getRepSet()->getNumRepresentatives(tn); + } + } + d_totalLemmas += totalInst; + } + } + } + + Trace("model-engine-debug") << "Do exhaustive instantiation..." << std::endl; + // FMC uses two sub-effort levels + int e_max = options::mbqiMode()==MBQI_FMC || options::mbqiMode()==MBQI_FMC_INTERVAL ? 2 : ( options::mbqiMode()==MBQI_TRUST ? 0 : 1 ); + for( int e=0; egetNumAssertedQuantifiers(); i++ ){ + Node q = fm->getAssertedQuantifier( i, true ); + Trace("fmf-exh-inst") << "-> Exhaustive instantiate " << q << ", effort = " << e << "..." << std::endl; + //determine if we should check this quantifier + if( d_quantEngine->getModel()->isQuantifierActive( q ) && d_quantEngine->hasOwnership( q, this ) ){ + exhaustiveInstantiate( q, e ); + if( d_quantEngine->inConflict() || ( optOneQuantPerRound() && d_addedLemmas>0 ) ){ + break; + } + }else{ + Trace("fmf-exh-inst") << "-> Inactive : " << q << std::endl; + } + } + if( d_addedLemmas>0 ){ + break; + }else{ + Assert( !d_quantEngine->inConflict() ); + } + } + + //print debug information + if( d_quantEngine->inConflict() ){ + Trace("model-engine") << "Conflict, added lemmas = "; + }else{ + Trace("model-engine") << "Added Lemmas = "; + } + Trace("model-engine") << d_addedLemmas << " / " << d_triedLemmas << " / "; + Trace("model-engine") << d_totalLemmas << std::endl; + return d_addedLemmas; +} + + + +void ModelEngine::exhaustiveInstantiate( Node f, int effort ){ + //first check if the builder can do the exhaustive instantiation + quantifiers::QModelBuilder * mb = d_quantEngine->getModelBuilder(); + unsigned prev_alem = mb->getNumAddedLemmas(); + unsigned prev_tlem = mb->getNumTriedLemmas(); + int retEi = mb->doExhaustiveInstantiation( d_quantEngine->getModel(), f, effort ); + if( retEi!=0 ){ + if( retEi<0 ){ + Trace("fmf-exh-inst") << "-> Builder determined complete instantiation was impossible." << std::endl; + d_incomplete_quants.push_back( f ); + }else{ + Trace("fmf-exh-inst") << "-> Builder determined instantiation(s)." << std::endl; + } + d_triedLemmas += mb->getNumTriedLemmas()-prev_tlem; + d_addedLemmas += mb->getNumAddedLemmas()-prev_alem; + d_quantEngine->d_statistics.d_instantiations_fmf_mbqi += mb->getNumAddedLemmas(); + }else{ + if( Trace.isOn("fmf-exh-inst-debug") ){ + Trace("fmf-exh-inst-debug") << " Instantiation Constants: "; + for( size_t i=0; igetTermUtil()->getInstantiationConstant( f, i ) << " "; + } + Trace("fmf-exh-inst-debug") << std::endl; + } + //create a rep set iterator and iterate over the (relevant) domain of the quantifier + QRepBoundExt qrbe(d_quantEngine); + RepSetIterator riter(d_quantEngine->getModel()->getRepSet(), &qrbe); + if( riter.setQuantifier( f ) ){ + Trace("fmf-exh-inst") << "...exhaustive instantiation set, incomplete=" << riter.isIncomplete() << "..." << std::endl; + if( !riter.isIncomplete() ){ + int triedLemmas = 0; + int addedLemmas = 0; + EqualityQuery* qy = d_quantEngine->getEqualityQuery(); + Instantiate* inst = d_quantEngine->getInstantiate(); + while( !riter.isFinished() && ( addedLemmas==0 || !options::fmfOneInstPerRound() ) ){ + //instantiation was not shown to be true, construct the match + InstMatch m( f ); + for (unsigned i = 0; i < riter.getNumTerms(); i++) + { + m.set(qy, i, riter.getCurrentTerm(i)); + } + Debug("fmf-model-eval") << "* Add instantiation " << m << std::endl; + triedLemmas++; + //add as instantiation + if (inst->addInstantiation(f, m, true)) + { + addedLemmas++; + if( d_quantEngine->inConflict() ){ + break; + } + }else{ + Debug("fmf-model-eval") << "* Failed Add instantiation " << m << std::endl; + } + riter.increment(); + } + d_addedLemmas += addedLemmas; + d_triedLemmas += triedLemmas; + d_quantEngine->d_statistics.d_instantiations_fmf_exh += addedLemmas; + } + }else{ + Trace("fmf-exh-inst") << "...exhaustive instantiation did set, incomplete=" << riter.isIncomplete() << "..." << std::endl; + } + //if the iterator is incomplete, we will return unknown instead of sat if no instantiations are added this round + if( riter.isIncomplete() ){ + d_incomplete_quants.push_back( f ); + } + } +} + +void ModelEngine::debugPrint( const char* c ){ + Trace( c ) << "Quantifiers: " << std::endl; + for( unsigned i=0; igetModel()->getNumAssertedQuantifiers(); i++ ){ + Node q = d_quantEngine->getModel()->getAssertedQuantifier( i ); + if( d_quantEngine->hasOwnership( q, this ) ){ + Trace( c ) << " "; + if( !d_quantEngine->getModel()->isQuantifierActive( q ) ){ + Trace( c ) << "*Inactive* "; + }else{ + Trace( c ) << " "; + } + Trace( c ) << q << std::endl; + } + } + //d_quantEngine->getModel()->debugPrint( c ); +} + diff --git a/src/theory/quantifiers/fmf/model_engine.h b/src/theory/quantifiers/fmf/model_engine.h new file mode 100644 index 000000000..090374744 --- /dev/null +++ b/src/theory/quantifiers/fmf/model_engine.h @@ -0,0 +1,70 @@ +/********************* */ +/*! \file model_engine.h + ** \verbatim + ** Top contributors (to current version): + ** Morgan Deters, 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 Model Engine class + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__QUANTIFIERS__MODEL_ENGINE_H +#define __CVC4__THEORY__QUANTIFIERS__MODEL_ENGINE_H + +#include "theory/quantifiers_engine.h" +#include "theory/quantifiers/fmf/model_builder.h" +#include "theory/theory_model.h" + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +class ModelEngine : public QuantifiersModule +{ + friend class RepSetIterator; +private: + //options + bool optOneQuantPerRound(); +private: + //check model + int checkModel(); + //exhaustively instantiate quantifier (possibly using mbqi) + void exhaustiveInstantiate( Node f, int effort = 0 ); +private: + //temporary statistics + //is the exhaustive instantiation incomplete? + bool d_incomplete_check; + // set of quantified formulas for which check was incomplete + std::vector< Node > d_incomplete_quants; + int d_addedLemmas; + int d_triedLemmas; + int d_totalLemmas; +public: + ModelEngine( context::Context* c, QuantifiersEngine* qe ); + virtual ~ModelEngine(); +public: + bool needsCheck( Theory::Effort e ); + QEffort needsModel(Theory::Effort e); + void reset_round( Theory::Effort e ); + void check(Theory::Effort e, QEffort quant_e); + bool checkComplete(); + bool checkCompleteFor( Node q ); + void registerQuantifier( Node f ); + void assertNode( Node f ); + Node explain(TNode n){ return Node::null(); } + void debugPrint( const char* c ); + /** Identify this module */ + std::string identify() const { return "ModelEngine"; } +};/* class ModelEngine */ + +}/* CVC4::theory::quantifiers namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__QUANTIFIERS__MODEL_ENGINE_H */ diff --git a/src/theory/quantifiers/full_model_check.cpp b/src/theory/quantifiers/full_model_check.cpp deleted file mode 100644 index 4da23ea96..000000000 --- a/src/theory/quantifiers/full_model_check.cpp +++ /dev/null @@ -1,1518 +0,0 @@ -/********************* */ -/*! \file full_model_check.cpp - ** \verbatim - ** Top contributors (to current version): - ** Morgan Deters, 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 Implementation of full model check class - **/ - -#include "theory/quantifiers/full_model_check.h" -#include "options/quantifiers_options.h" -#include "options/uf_options.h" -#include "theory/quantifiers/first_order_model.h" -#include "theory/quantifiers/instantiate.h" -#include "theory/quantifiers/term_database.h" -#include "theory/quantifiers/term_util.h" - -using namespace std; -using namespace CVC4; -using namespace CVC4::kind; -using namespace CVC4::context; -using namespace CVC4::theory; -using namespace CVC4::theory::quantifiers; -using namespace CVC4::theory::inst; -using namespace CVC4::theory::quantifiers::fmcheck; - -struct ModelBasisArgSort -{ - std::vector< Node > d_terms; - // number of arguments that are model-basis terms - std::unordered_map d_mba_count; - bool operator() (int i,int j) { - return (d_mba_count[d_terms[i]] < d_mba_count[d_terms[j]]); - } -}; - - -struct ConstRationalSort -{ - std::vector< Node > d_terms; - bool operator() (int i, int j) { - return (d_terms[i].getConst() < d_terms[j].getConst() ); - } -}; - - -bool EntryTrie::hasGeneralization( FirstOrderModelFmc * m, Node c, int index ) { - if (index==(int)c.getNumChildren()) { - return d_data!=-1; - }else{ - TypeNode tn = c[index].getType(); - Node st = m->getStar(tn); - if(d_child.find(st)!=d_child.end()) { - if( d_child[st].hasGeneralization(m, c, index+1) ){ - return true; - } - } - if( c[index]!=st && d_child.find( c[index] )!=d_child.end() ){ - if( d_child[ c[index] ].hasGeneralization(m, c, index+1) ){ - return true; - } - } - if( c[index].getType().isSort() ){ - //for star: check if all children are defined and have generalizations - if( c[index]==st ){ ///options::fmfFmcCoverSimplify() - //check if all children exist and are complete - unsigned num_child_def = - d_child.size() - (d_child.find(st) != d_child.end() ? 1 : 0); - if (num_child_def == m->getRepSet()->getNumRepresentatives(tn)) - { - bool complete = true; - for ( std::map::iterator it = d_child.begin(); it != d_child.end(); ++it ){ - if( !m->isStar(it->first) ){ - if( !it->second.hasGeneralization(m, c, index+1) ){ - complete = false; - break; - } - } - } - if( complete ){ - return true; - } - } - } - } - - return false; - } -} - -int EntryTrie::getGeneralizationIndex( FirstOrderModelFmc * m, std::vector & inst, int index ) { - Debug("fmc-entry-trie") << "Get generalization index " << inst.size() << " " << index << std::endl; - if (index==(int)inst.size()) { - return d_data; - }else{ - int minIndex = -1; - if( options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL && inst[index].getType().isInteger() ){ - for( std::map::iterator it = d_child.begin(); it != d_child.end(); ++it ){ - //if( !m->isInterval( it->first ) ){ - // std::cout << "Not an interval during getGenIndex " << it->first << std::endl; - // exit( 11 ); - //} - //check if it is in the range - if( m->isInRange(inst[index], it->first ) ){ - int gindex = it->second.getGeneralizationIndex(m, inst, index+1); - if( minIndex==-1 || (gindex!=-1 && gindexgetStar(inst[index].getType()); - if(d_child.find(st)!=d_child.end()) { - minIndex = d_child[st].getGeneralizationIndex(m, inst, index+1); - } - Node cc = inst[index]; - if( cc!=st && d_child.find( cc )!=d_child.end() ){ - int gindex = d_child[ cc ].getGeneralizationIndex(m, inst, index+1); - if (minIndex==-1 || (gindex!=-1 && gindex & compat, std::vector & gen, int index, bool is_gen ) { - if (index==(int)c.getNumChildren()) { - if( d_data!=-1) { - if( is_gen ){ - gen.push_back(d_data); - } - compat.push_back(d_data); - } - }else{ - if (m->isStar(c[index])) { - for ( std::map::iterator it = d_child.begin(); it != d_child.end(); ++it ){ - it->second.getEntries(m, c, compat, gen, index+1, is_gen ); - } - }else{ - Node st = m->getStar(c[index].getType()); - if(d_child.find(st)!=d_child.end()) { - d_child[st].getEntries(m, c, compat, gen, index+1, false); - } - if( d_child.find( c[index] )!=d_child.end() ){ - d_child[ c[index] ].getEntries(m, c, compat, gen, index+1, is_gen); - } - } - - } -} - -void EntryTrie::collectIndices(Node c, int index, std::vector< int >& indices ) { - if (index==(int)c.getNumChildren()) { - if( d_data!=-1 ){ - indices.push_back( d_data ); - } - }else{ - for ( std::map::iterator it = d_child.begin(); it != d_child.end(); ++it ){ - it->second.collectIndices(c, index+1, indices ); - } - } -} - -bool EntryTrie::isComplete(FirstOrderModelFmc * m, Node c, int index) { - if( d_complete==-1 ){ - d_complete = 1; - if (index<(int)c.getNumChildren()) { - Node st = m->getStar(c[index].getType()); - if(d_child.find(st)!=d_child.end()) { - if (!d_child[st].isComplete(m, c, index+1)) { - d_complete = 0; - } - }else{ - d_complete = 0; - } - } - } - return d_complete==1; -} - -bool Def::addEntry( FirstOrderModelFmc * m, Node c, Node v) { - if (d_et.hasGeneralization(m, c)) { - Trace("fmc-debug") << "Already has generalization, skip." << std::endl; - return false; - } - int newIndex = (int)d_cond.size(); - if (!d_has_simplified) { - std::vector compat; - std::vector gen; - d_et.getEntries(m, c, compat, gen); - for( unsigned i=0; i& inst ) { - int gindex = d_et.getGeneralizationIndex(m, inst); - if (gindex!=-1) { - return d_value[gindex]; - }else{ - Trace("fmc-warn") << "Warning : evaluation came up null!" << std::endl; - return Node::null(); - } -} - -int Def::getGeneralizationIndex( FirstOrderModelFmc * m, std::vector& inst ) { - return d_et.getGeneralizationIndex(m, inst); -} - -void Def::basic_simplify( FirstOrderModelFmc * m ) { - d_has_simplified = true; - std::vector< Node > cond; - cond.insert( cond.end(), d_cond.begin(), d_cond.end() ); - d_cond.clear(); - std::vector< Node > value; - value.insert( value.end(), d_value.begin(), d_value.end() ); - d_value.clear(); - d_et.reset(); - for (unsigned i=0; i cond; - cond.insert( cond.end(), d_cond.begin(), d_cond.end() ); - d_cond.clear(); - std::vector< Node > value; - value.insert( value.end(), d_value.begin(), d_value.end() ); - d_value.clear(); - d_et.reset(); - d_has_simplified = false; - //change the last to all star - std::vector< Node > nc; - nc.push_back( cc.getOperator() ); - for( unsigned j=0; j< cc.getNumChildren(); j++){ - nc.push_back(m->getStarElement(cc[j].getType())); - } - cond[cond.size()-1] = NodeManager::currentNM()->mkNode( APPLY_UF, nc ); - //re-add the entries - for (unsigned i=0; idebugPrintCond(tr, d_cond[i], true); - Trace(tr) << " -> "; - m->debugPrint(tr, d_value[i]); - Trace(tr) << std::endl; - } -} - - -FullModelChecker::FullModelChecker(context::Context* c, QuantifiersEngine* qe) : -QModelBuilder( c, qe ){ - d_true = NodeManager::currentNM()->mkConst(true); - d_false = NodeManager::currentNM()->mkConst(false); -} - -bool FullModelChecker::preProcessBuildModel(TheoryModel* m) { - //standard pre-process - if( !preProcessBuildModelStd( m ) ){ - return false; - } - - FirstOrderModelFmc * fm = ((FirstOrderModelFmc*)m)->asFirstOrderModelFmc(); - Trace("fmc") << "---Full Model Check preprocess() " << std::endl; - d_preinitialized_types.clear(); - //traverse equality engine - eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( fm->d_equalityEngine ); - while( !eqcs_i.isFinished() ){ - TypeNode tr = (*eqcs_i).getType(); - d_preinitialized_types[tr] = true; - ++eqcs_i; - } - - //must ensure model basis terms exists in model for each relevant type - fm->initialize(); - for( std::map::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ) { - Node op = it->first; - TypeNode tno = op.getType(); - for( unsigned i=0; igetNumAssertedQuantifiers(); i++ ){ - Node q = fm->getAssertedQuantifier( i ); - //make sure all types are set - for( unsigned j=0; jasFirstOrderModelFmc(); - Trace("fmc") << "---Full Model Check reset() " << std::endl; - d_quant_models.clear(); - d_rep_ids.clear(); - d_star_insts.clear(); - //process representatives - RepSet* rs = fm->getRepSetPtr(); - for (std::map >::iterator it = - rs->d_type_reps.begin(); - it != rs->d_type_reps.end(); - ++it) - { - if( it->first.isSort() ){ - Trace("fmc") << "Cardinality( " << it->first << " )" << " = " << it->second.size() << std::endl; - for( size_t a=0; asecond.size(); a++ ){ - Node r = fm->getRepresentative( it->second[a] ); - if( Trace.isOn("fmc-model-debug") ){ - std::vector< Node > eqc; - d_qe->getEqualityQuery()->getEquivalenceClass( r, eqc ); - Trace("fmc-model-debug") << " " << (it->second[a]==r); - Trace("fmc-model-debug") << " : " << it->second[a] << " : " << r << " : "; - //Trace("fmc-model-debug") << r2 << " : " << ir << " : "; - Trace("fmc-model-debug") << " {"; - for( size_t i=0; ifirst][r] = (int)a; - } - Trace("fmc-model-debug") << std::endl; - } - } - - //now, make models - for( std::map::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ) { - Node op = it->first; - //reset the model - fm->d_models[op]->reset(); - - std::vector< Node > add_conds; - std::vector< Node > add_values; - bool needsDefault = true; - std::map< Node, std::vector< Node > >::iterator itut = fm->d_uf_terms.find( op ); - if( itut!=fm->d_uf_terms.end() ){ - Trace("fmc-model-debug") << itut->second.size() << " model values for " << op << " ... " << std::endl; - for( size_t i=0; isecond.size(); i++ ){ - Node n = itut->second[i]; - // only consider unique up to congruence (in model equality engine)? - add_conds.push_back( n ); - add_values.push_back( n ); - Node r = fm->getRepresentative(n); - Trace("fmc-model-debug") << n << " -> " << r << std::endl; - //AlwaysAssert( fm->areEqual( itut->second[i], r ) ); - } - }else{ - Trace("fmc-model-debug") << "No model values for " << op << " ... " << std::endl; - } - Trace("fmc-model-debug") << std::endl; - //possibly get default - if( needsDefault ){ - Node nmb = fm->getModelBasisOpTerm(op); - //add default value if necessary - if( fm->hasTerm( nmb ) ){ - Trace("fmc-model-debug") << "Add default " << nmb << std::endl; - add_conds.push_back( nmb ); - add_values.push_back( nmb ); - }else{ - Node vmb = getSomeDomainElement(fm, nmb.getType()); - Trace("fmc-model-debug") << "Add default to default representative " << nmb << " "; - Trace("fmc-model-debug") - << fm->getRepSet()->getNumRepresentatives(nmb.getType()) - << std::endl; - add_conds.push_back( nmb ); - add_values.push_back( vmb ); - } - } - - std::vector< Node > conds; - std::vector< Node > values; - std::vector< Node > entry_conds; - //get the entries for the model - for( size_t i=0; i children; - std::vector< Node > entry_children; - children.push_back(op); - entry_children.push_back(op); - bool hasNonStar = false; - for( unsigned i=0; igetRepresentative( c[i] ); - children.push_back(ri); - bool isStar = false; - if( options::mbqiMode()!=quantifiers::MBQI_FMC_INTERVAL || !ri.getType().isInteger() ){ - if (fm->isModelBasisTerm(ri) ) { - ri = fm->getStar( ri.getType() ); - isStar = true; - }else{ - hasNonStar = true; - } - } - if( !isStar && !ri.isConst() ){ - Trace("fmc-warn") << "Warning : model for " << op << " has non-constant argument in model " << ri << " (from " << c[i] << ")" << std::endl; - Assert( false ); - } - entry_children.push_back(ri); - } - Node n = NodeManager::currentNM()->mkNode( APPLY_UF, children ); - Node nv = fm->getRepresentative( v ); - if( !nv.isConst() ){ - Trace("fmc-warn") << "Warning : model for " << op << " has non-constant value in model " << nv << std::endl; - Assert( false ); - } - Node en = (useSimpleModels() && hasNonStar) ? n : NodeManager::currentNM()->mkNode( APPLY_UF, entry_children ); - if( std::find(conds.begin(), conds.end(), n )==conds.end() ){ - Trace("fmc-model-debug") << "- add " << n << " -> " << nv << " (entry is " << en << ")" << std::endl; - conds.push_back(n); - values.push_back(nv); - entry_conds.push_back(en); - } - else { - Trace("fmc-model-debug") << "Already have entry for : " << n << " -> " << nv << " (entry is " << en << ")" << std::endl; - } - } - - - //sort based on # default arguments - std::vector< int > indices; - ModelBasisArgSort mbas; - for (int i=0; i<(int)conds.size(); i++) { - mbas.d_terms.push_back(conds[i]); - mbas.d_mba_count[conds[i]] = fm->getModelBasisArg(conds[i]); - indices.push_back(i); - } - std::sort( indices.begin(), indices.end(), mbas ); - - for (int i=0; i<(int)indices.size(); i++) { - fm->d_models[op]->addEntry(fm, entry_conds[indices[i]], values[indices[i]]); - } - - - if( options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL ){ - convertIntervalModel( fm, op ); - } - - Trace("fmc-model-simplify") << "Before simplification : " << std::endl; - fm->d_models[op]->debugPrint("fmc-model-simplify", op, this); - Trace("fmc-model-simplify") << std::endl; - - Trace("fmc-model-simplify") << "Simplifying " << op << "..." << std::endl; - fm->d_models[op]->simplify( this, fm ); - - fm->d_models[op]->debugPrint("fmc-model", op, this); - Trace("fmc-model") << std::endl; - - //for debugging - /* - for( size_t i=0; id_uf_terms[op].size(); i++ ){ - std::vector< Node > inst; - for( unsigned j=0; jd_uf_terms[op][i].getNumChildren(); j++ ){ - Node r = fm->getRepresentative( fm->d_uf_terms[op][i][j] ); - inst.push_back( r ); - } - Node ev = fm->d_models[op]->evaluate( fm, inst ); - Trace("fmc-model-debug") << ".....Checking eval( " << fm->d_uf_terms[op][i] << " ) = " << ev << std::endl; - AlwaysAssert( fm->areEqual( ev, fm->d_uf_terms[op][i] ) ); - } - */ - } - Assert( d_addedLemmas==0 ); - - //make function values - for( std::map::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ){ - Node f_def = getFunctionValue( fm, it->first, "$x" ); - m->assignFunctionDefinition( it->first, f_def ); - } - return TheoryEngineModelBuilder::processBuildModel( m ); -} - -void FullModelChecker::preInitializeType( FirstOrderModelFmc * fm, TypeNode tn ){ - if( d_preinitialized_types.find( tn )==d_preinitialized_types.end() ){ - d_preinitialized_types[tn] = true; - if (!tn.isFunction() || options::ufHo()) - { - Node mb = fm->getModelBasisTerm(tn); - if (!mb.isConst()) - { - Trace("fmc") << "...add model basis term to EE of model " << mb << " " - << tn << std::endl; - fm->d_equalityEngine->addTerm(mb); - } - } - } -} - -void FullModelChecker::debugPrintCond(const char * tr, Node n, bool dispStar) { - Trace(tr) << "("; - for( unsigned j=0; j0 ) Trace(tr) << ", "; - debugPrint(tr, n[j], dispStar); - } - Trace(tr) << ")"; -} - -void FullModelChecker::debugPrint(const char * tr, Node n, bool dispStar) { - FirstOrderModelFmc * fm = (FirstOrderModelFmc *)d_qe->getModel(); - if( n.isNull() ){ - Trace(tr) << "null"; - } - else if(fm->isStar(n) && dispStar) { - Trace(tr) << "*"; - } - else if(fm->isInterval(n)) { - debugPrint(tr, n[0], dispStar ); - Trace(tr) << "..."; - debugPrint(tr, n[1], dispStar ); - }else{ - TypeNode tn = n.getType(); - if( tn.isSort() && d_rep_ids.find(tn)!=d_rep_ids.end() ){ - if (d_rep_ids[tn].find(n)!=d_rep_ids[tn].end()) { - Trace(tr) << d_rep_ids[tn][n]; - }else{ - Trace(tr) << n; - } - }else{ - Trace(tr) << n; - } - } -} - - -int FullModelChecker::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ) { - Trace("fmc") << "Full model check " << f << ", effort = " << effort << "..." << std::endl; - Assert( !d_qe->inConflict() ); - if( optUseModel() ){ - FirstOrderModelFmc * fmfmc = fm->asFirstOrderModelFmc(); - if (effort==0) { - //register the quantifier - if (d_quant_cond.find(f)==d_quant_cond.end()) { - std::vector< TypeNode > types; - for(unsigned i=0; imkFunctionType( types, NodeManager::currentNM()->booleanType() ); - Node op = NodeManager::currentNM()->mkSkolem( "qfmc", typ, "op created for full-model checking" ); - d_quant_cond[f] = op; - } - - if( options::mbqiMode()==MBQI_NONE ){ - //just exhaustive instantiate - Node c = mkCondDefault( fmfmc, f ); - d_quant_models[f].addEntry( fmfmc, c, d_false ); - return exhaustiveInstantiate( fmfmc, f, c, -1); - }else{ - //model check the quantifier - doCheck(fmfmc, f, d_quant_models[f], f[1]); - Trace("fmc") << "Definition for quantifier " << f << " is : " << std::endl; - Assert( !d_quant_models[f].d_cond.empty() ); - d_quant_models[f].debugPrint("fmc", Node::null(), this); - Trace("fmc") << std::endl; - - //consider all entries going to non-true - for (unsigned i=0; i inst; - for (unsigned j=0; jisStar(d_quant_models[f].d_cond[i][j])) { - hasStar = true; - inst.push_back(fmfmc->getModelBasisTerm(d_quant_models[f].d_cond[i][j].getType())); - }else if( fmfmc->isInterval(d_quant_models[f].d_cond[i][j])){ - hasStar = true; - //if interval, find a sample point - if( fmfmc->isStar(d_quant_models[f].d_cond[i][j][0]) ){ - if( fmfmc->isStar(d_quant_models[f].d_cond[i][j][1]) ){ - inst.push_back(fmfmc->getModelBasisTerm(d_quant_models[f].d_cond[i][j][1].getType())); - }else{ - Node nn = NodeManager::currentNM()->mkNode( MINUS, d_quant_models[f].d_cond[i][j][1], - NodeManager::currentNM()->mkConst( Rational(1) ) ); - nn = Rewriter::rewrite( nn ); - inst.push_back( nn ); - } - }else{ - inst.push_back(d_quant_models[f].d_cond[i][j][0]); - } - }else{ - inst.push_back(d_quant_models[f].d_cond[i][j]); - } - } - bool addInst = true; - if( hasStar ){ - //try obvious (specified by inst) - Node ev = d_quant_models[f].evaluate(fmfmc, inst); - if (ev==d_true) { - addInst = false; - Trace("fmc-debug") << "...do not instantiate, evaluation was " << ev << std::endl; - } - }else{ - //for debugging - if (Trace.isOn("fmc-test-inst")) { - Node ev = d_quant_models[f].evaluate(fmfmc, inst); - if( ev==d_true ){ - std::cout << "WARNING: instantiation was true! " << f << " " << d_quant_models[f].d_cond[i] << std::endl; - exit(0); - }else{ - Trace("fmc-test-inst") << "...instantiation evaluated to false." << std::endl; - } - } - } - if( addInst ){ - if( options::fmfBound() ){ - std::vector< Node > cond; - cond.push_back(d_quant_cond[f]); - cond.insert( cond.end(), inst.begin(), inst.end() ); - //need to do exhaustive instantiate algorithm to set things properly (should only add one instance) - Node c = mkCond( cond ); - unsigned prevInst = d_addedLemmas; - exhaustiveInstantiate( fmfmc, f, c, -1 ); - if( d_addedLemmas==prevInst ){ - d_star_insts[f].push_back(i); - } - }else{ - //just add the instance - d_triedLemmas++; - if (d_qe->getInstantiate()->addInstantiation(f, inst, true)) - { - Trace("fmc-debug-inst") << "** Added instantiation." << std::endl; - d_addedLemmas++; - if( d_qe->inConflict() || options::fmfOneInstPerRound() ){ - break; - } - }else{ - Trace("fmc-debug-inst") << "** Instantiation was duplicate." << std::endl; - //this can happen if evaluation is unknown, or if we are generalizing a star that already has a value - //if( !hasStar && d_quant_models[f].d_value[i]==d_false ){ - // Trace("fmc-warn") << "**** FMC warning: inconsistent duplicate instantiation." << std::endl; - //} - //this assertion can happen if two instantiations from this round are identical - // (0,1)->false (1,0)->false for forall xy. f( x, y ) = f( y, x ) - //Assert( hasStar || d_quant_models[f].d_value[i]!=d_false ); - //might try it next effort level - d_star_insts[f].push_back(i); - } - } - }else{ - Trace("fmc-debug-inst") << "** Instantiation was already true." << std::endl; - //might try it next effort level - d_star_insts[f].push_back(i); - } - } - } - } - }else{ - if (!d_star_insts[f].empty()) { - Trace("fmc-exh") << "Exhaustive instantiate " << f << std::endl; - Trace("fmc-exh") << "Definition was : " << std::endl; - d_quant_models[f].debugPrint("fmc-exh", Node::null(), this); - Trace("fmc-exh") << std::endl; - Def temp; - //simplify the exceptions? - for( int i=(d_star_insts[f].size()-1); i>=0; i--) { - //get witness for d_star_insts[f][i] - int j = d_star_insts[f][i]; - if( temp.addEntry(fmfmc, d_quant_models[f].d_cond[j], d_quant_models[f].d_value[j] ) ){ - if( !exhaustiveInstantiate(fmfmc, f, d_quant_models[f].d_cond[j], j ) ){ - //something went wrong, resort to exhaustive instantiation - return 0; - } - } - } - } - } - return 1; - }else{ - return 0; - } -} - -/** Representative bound fmc entry - * - * This bound information corresponds to one - * entry in a term definition (see terminology in - * Chapter 5 of Finite Model Finding for - * Satisfiability Modulo Theories thesis). - * For example, a term definition for the body - * of a quantified formula: - * forall xyz. P( x, y, z ) - * may be: - * ( 0, 0, 0 ) -> false - * ( *, 1, 2 ) -> false - * ( *, *, * ) -> true - * Indicating that the quantified formula evaluates - * to false in the current model for x=0, y=0, z=0, - * or y=1, z=2 for any x, and evaluates to true - * otherwise. - * This class is used if we wish - * to iterate over all values corresponding to one - * of these entries. For example, for the second entry: - * (*, 1, 2 ) - * we iterate over all values of x, but only {1} - * for y and {2} for z. - */ -class RepBoundFmcEntry : public QRepBoundExt -{ - public: - RepBoundFmcEntry(QuantifiersEngine* qe, Node e, FirstOrderModelFmc* f) - : QRepBoundExt(qe), d_entry(e), d_fm(f) - { - } - ~RepBoundFmcEntry() {} - /** set bound */ - virtual RepSetIterator::RsiEnumType setBound( - Node owner, unsigned i, std::vector& elements) override - { - if (d_fm->isInterval(d_entry[i])) - { - // explicitly add the interval? - } - else if (d_fm->isStar(d_entry[i])) - { - // must add the full range - } - else - { - // only need to consider the single point - elements.push_back(d_entry[i]); - return RepSetIterator::ENUM_DEFAULT; - } - return QRepBoundExt::setBound(owner, i, elements); - } - - private: - /** the entry for this bound */ - Node d_entry; - /** the model builder associated with this bound */ - FirstOrderModelFmc* d_fm; -}; - -bool FullModelChecker::exhaustiveInstantiate(FirstOrderModelFmc * fm, Node f, Node c, int c_index) { - Trace("fmc-exh") << "----Exhaustive instantiate based on index " << c_index << " : " << c << " "; - debugPrintCond("fmc-exh", c, true); - Trace("fmc-exh")<< std::endl; - RepBoundFmcEntry rbfe(d_qe, c, fm); - RepSetIterator riter(d_qe->getModel()->getRepSet(), &rbfe); - Trace("fmc-exh-debug") << "Set quantifier..." << std::endl; - //initialize - if (riter.setQuantifier(f)) - { - Trace("fmc-exh-debug") << "Set element domains..." << std::endl; - int addedLemmas = 0; - //now do full iteration - while( !riter.isFinished() ){ - d_triedLemmas++; - Trace("fmc-exh-debug") << "Inst : "; - std::vector< Node > ev_inst; - std::vector< Node > inst; - for (unsigned i = 0; i < riter.getNumTerms(); i++) - { - Node rr = riter.getCurrentTerm( i ); - Node r = rr; - //if( r.getType().isSort() ){ - r = fm->getRepresentative( r ); - //}else{ - // r = fm->getCurrentModelValue( r ); - //} - debugPrint("fmc-exh-debug", r); - Trace("fmc-exh-debug") << " (term : " << rr << ")"; - ev_inst.push_back( r ); - inst.push_back( rr ); - } - int ev_index = d_quant_models[f].getGeneralizationIndex(fm, ev_inst); - Trace("fmc-exh-debug") << ", index = " << ev_index << " / " << d_quant_models[f].d_value.size(); - Node ev = ev_index==-1 ? Node::null() : d_quant_models[f].d_value[ev_index]; - if (ev!=d_true) { - Trace("fmc-exh-debug") << ", add!"; - //add as instantiation - if (d_qe->getInstantiate()->addInstantiation(f, inst, true)) - { - Trace("fmc-exh-debug") << " ...success."; - addedLemmas++; - if( d_qe->inConflict() || options::fmfOneInstPerRound() ){ - break; - } - }else{ - Trace("fmc-exh-debug") << ", failed."; - } - }else{ - Trace("fmc-exh-debug") << ", already true"; - } - Trace("fmc-exh-debug") << std::endl; - int index = riter.increment(); - Trace("fmc-exh-debug") << "Incremented index " << index << std::endl; - if( !riter.isFinished() ){ - if (index>=0 && riter.d_index[index]>0 && addedLemmas>0 && riter.d_enum_type[index]==RepSetIterator::ENUM_BOUND_INT ) { - Trace("fmc-exh-debug") << "Since this is a range enumeration, skip to the next..." << std::endl; - riter.incrementAtIndex(index - 1); - } - } - } - d_addedLemmas += addedLemmas; - Trace("fmc-exh") << "----Finished Exhaustive instantiate, lemmas = " << addedLemmas << ", incomplete=" << riter.isIncomplete() << std::endl; - return addedLemmas>0 || !riter.isIncomplete(); - }else{ - Trace("fmc-exh") << "----Finished Exhaustive instantiate, failed." << std::endl; - return !riter.isIncomplete(); - } -} - -void FullModelChecker::doCheck(FirstOrderModelFmc * fm, Node f, Def & d, Node n ) { - Trace("fmc-debug") << "Check " << n << " " << n.getKind() << std::endl; - //first check if it is a bounding literal - if( n.hasAttribute(BoundIntLitAttribute()) ){ - Trace("fmc-debug") << "It is a bounding literal, polarity = " << n.getAttribute(BoundIntLitAttribute()) << std::endl; - d.addEntry(fm, mkCondDefault(fm, f), n.getAttribute(BoundIntLitAttribute())==1 ? d_false : d_true ); - }else if( n.getKind() == kind::BOUND_VARIABLE ){ - Trace("fmc-debug") << "Add default entry..." << std::endl; - d.addEntry(fm, mkCondDefault(fm, f), n); - } - else if( n.getKind() == kind::NOT ){ - //just do directly - doCheck( fm, f, d, n[0] ); - doNegate( d ); - } - else if( n.getKind() == kind::FORALL ){ - d.addEntry(fm, mkCondDefault(fm, f), Node::null()); - } - else if( n.getType().isArray() ){ - //Trace("fmc-warn") << "WARNING : ARRAYS : Can't process base array " << r << std::endl; - //Trace("fmc-warn") << " Default value was : " << odefaultValue << std::endl; - //Trace("fmc-debug") << "Can't process base array " << r << std::endl; - //can't process this array - d.reset(); - d.addEntry(fm, mkCondDefault(fm, f), Node::null()); - } - else if( n.getNumChildren()==0 ){ - Node r = n; - if( !n.isConst() ){ - if( !fm->hasTerm(n) ){ - r = getSomeDomainElement(fm, n.getType() ); - } - r = fm->getRepresentative( r ); - } - Trace("fmc-debug") << "Add constant entry..." << std::endl; - d.addEntry(fm, mkCondDefault(fm, f), r); - } - else{ - std::vector< int > var_ch; - std::vector< Def > children; - for( int i=0; i<(int)n.getNumChildren(); i++) { - Def dc; - doCheck(fm, f, dc, n[i]); - children.push_back(dc); - if( n[i].getKind() == kind::BOUND_VARIABLE ){ - var_ch.push_back(i); - } - } - - if( n.getKind()==APPLY_UF ){ - Trace("fmc-debug") << "Do uninterpreted compose " << n << std::endl; - //uninterpreted compose - doUninterpretedCompose( fm, f, d, n.getOperator(), children ); - /* - } else if( n.getKind()==SELECT ){ - Trace("fmc-debug") << "Do select compose " << n << std::endl; - std::vector< Def > children2; - children2.push_back( children[1] ); - std::vector< Node > cond; - mkCondDefaultVec(fm, f, cond); - std::vector< Node > val; - doUninterpretedCompose(fm, f, d, children[0], children2, 0, cond, val ); - */ - } else { - if( !var_ch.empty() ){ - if( n.getKind()==EQUAL && !n[0].getType().isBoolean() ){ - if( var_ch.size()==2 ){ - Trace("fmc-debug") << "Do variable equality " << n << std::endl; - doVariableEquality( fm, f, d, n ); - }else{ - Trace("fmc-debug") << "Do variable relation " << n << std::endl; - doVariableRelation( fm, f, d, var_ch[0]==0 ? children[1] : children[0], var_ch[0]==0 ? n[0] : n[1] ); - } - }else{ - Trace("fmc-warn") << "Don't know how to check " << n << std::endl; - d.addEntry(fm, mkCondDefault(fm, f), Node::null()); - } - }else{ - Trace("fmc-debug") << "Do interpreted compose " << n << std::endl; - std::vector< Node > cond; - mkCondDefaultVec(fm, f, cond); - std::vector< Node > val; - //interpreted compose - doInterpretedCompose( fm, f, d, n, children, 0, cond, val ); - } - } - Trace("fmc-debug") << "Simplify the definition..." << std::endl; - d.debugPrint("fmc-debug", Node::null(), this); - d.simplify(this, fm); - Trace("fmc-debug") << "Done simplifying" << std::endl; - } - Trace("fmc-debug") << "Definition for " << n << " is : " << std::endl; - d.debugPrint("fmc-debug", Node::null(), this); - Trace("fmc-debug") << std::endl; -} - -void FullModelChecker::doNegate( Def & dc ) { - for (unsigned i=0; i cond; - mkCondDefaultVec(fm, f, cond); - if (eq[0]==eq[1]){ - d.addEntry(fm, mkCond(cond), d_true); - }else{ - TypeNode tn = eq[0].getType(); - if( tn.isSort() ){ - int j = fm->getVariableId(f, eq[0]); - int k = fm->getVariableId(f, eq[1]); - const RepSet* rs = fm->getRepSet(); - if (!rs->hasType(tn)) - { - getSomeDomainElement( fm, tn ); //to verify the type is initialized - } - unsigned nreps = rs->getNumRepresentatives(tn); - for (unsigned i = 0; i < nreps; i++) - { - Node r = fm->getRepresentative(rs->getRepresentative(tn, i)); - cond[j+1] = r; - cond[k+1] = r; - d.addEntry( fm, mkCond(cond), d_true); - } - d.addEntry( fm, mkCondDefault(fm, f), d_false); - }else{ - d.addEntry( fm, mkCondDefault(fm, f), Node::null()); - } - } -} - -void FullModelChecker::doVariableRelation( FirstOrderModelFmc * fm, Node f, Def & d, Def & dc, Node v) { - int j = fm->getVariableId(f, v); - for (unsigned i=0; iisStar(dc.d_cond[i][j])) { - std::vector cond; - mkCondVec(dc.d_cond[i],cond); - cond[j+1] = val; - d.addEntry(fm, mkCond(cond), d_true); - cond[j+1] = fm->getStar(val.getType()); - d.addEntry(fm, mkCond(cond), d_false); - }else{ - d.addEntry( fm, dc.d_cond[i], d_false); - } - }else{ - d.addEntry( fm, dc.d_cond[i], d_true); - } - } - } -} - -void FullModelChecker::doUninterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, Node op, std::vector< Def > & dc ) { - Trace("fmc-uf-debug") << "Definition : " << std::endl; - fm->d_models[op]->debugPrint("fmc-uf-debug", op, this); - Trace("fmc-uf-debug") << std::endl; - - std::vector< Node > cond; - mkCondDefaultVec(fm, f, cond); - std::vector< Node > val; - doUninterpretedCompose( fm, f, d, *fm->d_models[op], dc, 0, cond, val); -} - -void FullModelChecker::doUninterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, - Def & df, std::vector< Def > & dc, int index, - std::vector< Node > & cond, std::vector & val ) { - Trace("fmc-uf-process") << "process at " << index << std::endl; - for( unsigned i=1; i entries; - doUninterpretedCompose2( fm, f, entries, 0, cond, val, df.d_et); - if( entries.empty() ){ - d.addEntry(fm, mkCond(cond), Node::null()); - }else{ - //add them to the definition - for( unsigned e=0; e new_cond; - new_cond.insert(new_cond.end(), cond.begin(), cond.end()); - if( doMeet(fm, new_cond, dc[index].d_cond[i]) ){ - Trace("fmc-uf-process") << "index " << i << " succeeded meet." << std::endl; - val.push_back(dc[index].d_value[i]); - doUninterpretedCompose(fm, f, d, df, dc, index+1, new_cond, val); - val.pop_back(); - }else{ - Trace("fmc-uf-process") << "index " << i << " failed meet." << std::endl; - } - } - } - } -} - -void FullModelChecker::doUninterpretedCompose2( FirstOrderModelFmc * fm, Node f, - std::map< int, Node > & entries, int index, - std::vector< Node > & cond, std::vector< Node > & val, - EntryTrie & curr ) { - Trace("fmc-uf-process") << "compose " << index << " / " << val.size() << std::endl; - for( unsigned i=1; i index[" << curr.d_data << "]" << std::endl; - entries[curr.d_data] = c; - }else{ - Node v = val[index]; - Trace("fmc-uf-process") << "Process " << v << std::endl; - bool bind_var = false; - if( !v.isNull() && v.getKind()==kind::BOUND_VARIABLE ){ - int j = fm->getVariableId(f, v); - Trace("fmc-uf-process") << v << " is variable #" << j << std::endl; - if (!fm->isStar(cond[j+1]) && !fm->isInterval(cond[j+1])) { - v = cond[j+1]; - }else{ - bind_var = true; - } - } - if (bind_var) { - Trace("fmc-uf-process") << "bind variable..." << std::endl; - int j = fm->getVariableId(f, v); - if( fm->isStar(cond[j+1]) ){ - for (std::map::iterator it = curr.d_child.begin(); it != curr.d_child.end(); ++it) { - cond[j+1] = it->first; - doUninterpretedCompose2(fm, f, entries, index+1, cond, val, it->second); - } - cond[j+1] = fm->getStar(v.getType()); - }else{ - Node orig = cond[j+1]; - for (std::map::iterator it = curr.d_child.begin(); it != curr.d_child.end(); ++it) { - Node nw = doIntervalMeet( fm, it->first, orig ); - if( !nw.isNull() ){ - cond[j+1] = nw; - doUninterpretedCompose2(fm, f, entries, index+1, cond, val, it->second); - } - } - cond[j+1] = orig; - } - }else{ - if( !v.isNull() ){ - if( options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL && v.getType().isInteger() ){ - for (std::map::iterator it = curr.d_child.begin(); it != curr.d_child.end(); ++it) { - if( fm->isInRange( v, it->first ) ){ - doUninterpretedCompose2(fm, f, entries, index+1, cond, val, it->second); - } - } - }else{ - if (curr.d_child.find(v)!=curr.d_child.end()) { - Trace("fmc-uf-process") << "follow value..." << std::endl; - doUninterpretedCompose2(fm, f, entries, index+1, cond, val, curr.d_child[v]); - } - Node st = fm->getStarElement(v.getType()); - if (curr.d_child.find(st)!=curr.d_child.end()) { - Trace("fmc-uf-process") << "follow star..." << std::endl; - doUninterpretedCompose2(fm, f, entries, index+1, cond, val, curr.d_child[st]); - } - } - } - } - } -} - -void FullModelChecker::doInterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, Node n, - std::vector< Def > & dc, int index, - std::vector< Node > & cond, std::vector & val ) { - Trace("fmc-if-process") << "int compose " << index << " / " << dc.size() << std::endl; - for( unsigned i=1; i new_cond; - new_cond.insert(new_cond.end(), cond.begin(), cond.end()); - if( doMeet(fm, new_cond, dc[index].d_cond[i]) ){ - bool process = true; - if (vtn.isBoolean()) { - //short circuit - if( (n.getKind()==OR && dc[index].d_value[i]==d_true) || - (n.getKind()==AND && dc[index].d_value[i]==d_false) ){ - Node c = mkCond(new_cond); - d.addEntry(fm, c, dc[index].d_value[i]); - process = false; - } - } - if (process) { - val.push_back(dc[index].d_value[i]); - doInterpretedCompose(fm, f, d, n, dc, index+1, new_cond, val); - val.pop_back(); - } - } - } - } - } -} - -int FullModelChecker::isCompat( FirstOrderModelFmc * fm, std::vector< Node > & cond, Node c ) { - Trace("fmc-debug3") << "isCompat " << c << std::endl; - Assert(cond.size()==c.getNumChildren()+1); - for (unsigned i=1; iisStar(cond[i]) && !fm->isStar(c[i-1]) ) { - return 0; - } - } - } - return 1; -} - -bool FullModelChecker::doMeet( FirstOrderModelFmc * fm, std::vector< Node > & cond, Node c ) { - Trace("fmc-debug3") << "doMeet " << c << std::endl; - Assert(cond.size()==c.getNumChildren()+1); - for (unsigned i=1; iisStar(cond[i]) ){ - cond[i] = c[i-1]; - }else if( !fm->isStar(c[i-1]) ){ - return false; - } - } - } - } - return true; -} - -Node FullModelChecker::doIntervalMeet( FirstOrderModelFmc * fm, Node i1, Node i2, bool mk ) { - Trace("fmc-debug2") << "Interval meet : " << i1 << " " << i2 << " " << mk << std::endl; - if( fm->isStar( i1 ) ){ - return i2; - }else if( fm->isStar( i2 ) ){ - return i1; - }else if( !fm->isInterval( i1 ) && fm->isInterval( i2 ) ){ - return doIntervalMeet( fm, i2, i1, mk ); - }else if( !fm->isInterval( i2 ) ){ - if( fm->isInterval( i1 ) ){ - if( fm->isInRange( i2, i1 ) ){ - return i2; - } - }else if( i1==i2 ){ - return i1; - } - return Node::null(); - }else{ - Node b[2]; - for( unsigned j=0; j<2; j++ ){ - Node b1 = i1[j]; - Node b2 = i2[j]; - if( fm->isStar( b1 ) ){ - b[j] = b2; - }else if( fm->isStar( b2 ) ){ - b[j] = b1; - }else if( b1.getConst() < b2.getConst() ){ - b[j] = j==0 ? b2 : b1; - }else{ - b[j] = j==0 ? b1 : b2; - } - } - if( fm->isStar( b[0] ) || fm->isStar( b[1] ) || b[0].getConst() < b[1].getConst() ){ - return mk ? fm->getInterval( b[0], b[1] ) : i1; - }else{ - return Node::null(); - } - } -} - -Node FullModelChecker::mkCond( std::vector< Node > & cond ) { - return NodeManager::currentNM()->mkNode(APPLY_UF, cond); -} - -Node FullModelChecker::mkCondDefault( FirstOrderModelFmc * fm, Node f) { - std::vector< Node > cond; - mkCondDefaultVec(fm, f, cond); - return mkCond(cond); -} - -void FullModelChecker::mkCondDefaultVec( FirstOrderModelFmc * fm, Node f, std::vector< Node > & cond ) { - Trace("fmc-debug") << "Make default vec, intervals = " << (options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL) << std::endl; - //get function symbol for f - cond.push_back(d_quant_cond[f]); - for (unsigned i=0; igetStarElement( f[0][i].getType() ); - Assert( ts.getType()==f[0][i].getType() ); - cond.push_back(ts); - } -} - -void FullModelChecker::mkCondVec( Node n, std::vector< Node > & cond ) { - cond.push_back(n.getOperator()); - for( unsigned i=0; imkFunctionType( a.getType(), NodeManager::currentNM()->booleanType() ); - Node op = NodeManager::currentNM()->mkSkolem( "fmc", typ, "op created for full-model checking" ); - d_array_cond[a.getType()] = op; - } - std::vector< Node > cond; - cond.push_back(d_array_cond[a.getType()]); - cond.push_back(a); - d_array_term_cond[a] = NodeManager::currentNM()->mkNode(APPLY_UF, cond ); - } - return d_array_term_cond[a]; -} - -Node FullModelChecker::evaluateInterpreted( Node n, std::vector< Node > & vals ) { - if( n.getKind()==EQUAL && !n[0].getType().isBoolean() ){ - if (!vals[0].isNull() && !vals[1].isNull()) { - return vals[0]==vals[1] ? d_true : d_false; - }else{ - return Node::null(); - } - }else if( n.getKind()==ITE ){ - if( vals[0]==d_true ){ - return vals[1]; - }else if( vals[0]==d_false ){ - return vals[2]; - }else{ - return vals[1]==vals[2] ? vals[1] : Node::null(); - } - }else if( n.getKind()==AND || n.getKind()==OR ){ - bool isNull = false; - for (unsigned i=0; i children; - if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ - children.push_back( n.getOperator() ); - } - for (unsigned i=0; imkNode(n.getKind(), children); - Trace("fmc-eval") << "Evaluate " << nc << " to "; - nc = Rewriter::rewrite(nc); - Trace("fmc-eval") << nc << std::endl; - return nc; - } -} - -Node FullModelChecker::getSomeDomainElement( FirstOrderModelFmc * fm, TypeNode tn ) { - bool addRepId = !fm->getRepSet()->hasType(tn); - Node de = fm->getSomeDomainElement(tn); - if( addRepId ){ - d_rep_ids[tn][de] = 0; - } - return de; -} - -Node FullModelChecker::getFunctionValue(FirstOrderModelFmc * fm, Node op, const char* argPrefix ) { - return fm->getFunctionValue(op, argPrefix); -} - - -bool FullModelChecker::useSimpleModels() { - return options::fmfFmcSimple(); -} - -void FullModelChecker::convertIntervalModel( FirstOrderModelFmc * fm, Node op ){ - Trace("fmc-interval-model") << "Changing to interval model, Before : " << std::endl; - fm->d_models[op]->debugPrint("fmc-interval-model", op, this); - Trace("fmc-interval-model") << std::endl; - std::vector< int > indices; - for( int i=0; i<(int)fm->d_models[op]->d_cond.size(); i++ ){ - indices.push_back( i ); - } - std::map< int, std::map< int, Node > > changed_vals; - makeIntervalModel( fm, op, indices, 0, changed_vals ); - - std::vector< Node > conds; - std::vector< Node > values; - for( unsigned i=0; id_models[op]->d_cond.size(); i++ ){ - if( changed_vals.find(i)==changed_vals.end() ){ - conds.push_back( fm->d_models[op]->d_cond[i] ); - }else{ - std::vector< Node > children; - children.push_back( op ); - for( unsigned j=0; jd_models[op]->d_cond[i].getNumChildren(); j++ ){ - if( changed_vals[i].find(j)==changed_vals[i].end() ){ - children.push_back( fm->d_models[op]->d_cond[i][j] ); - }else{ - children.push_back( changed_vals[i][j] ); - } - } - Node nc = NodeManager::currentNM()->mkNode( APPLY_UF, children ); - conds.push_back( nc ); - Trace("fmc-interval-model") << "Interval : Entry #" << i << " changed to "; - debugPrintCond("fmc-interval-model", nc, true ); - Trace("fmc-interval-model") << std::endl; - } - values.push_back( fm->d_models[op]->d_value[i] ); - } - fm->d_models[op]->reset(); - for( unsigned i=0; id_models[op]->addEntry(fm, conds[i], values[i] ); - } -} - -void FullModelChecker::makeIntervalModel( FirstOrderModelFmc * fm, Node op, std::vector< int > & indices, int index, - std::map< int, std::map< int, Node > >& changed_vals ) { - if( index==(int)fm->d_models[op]->d_cond[0].getNumChildren() ){ - makeIntervalModel2( fm, op, indices, 0, changed_vals ); - }else{ - TypeNode tn = fm->d_models[op]->d_cond[0][index].getType(); - if( tn.isInteger() ){ - makeIntervalModel(fm,op,indices,index+1, changed_vals ); - }else{ - std::map< Node, std::vector< int > > new_indices; - for( unsigned i=0; id_models[op]->d_cond[indices[i]][index]; - new_indices[v].push_back( indices[i] ); - } - - for( std::map< Node, std::vector< int > >::iterator it = new_indices.begin(); it != new_indices.end(); ++it ){ - makeIntervalModel( fm, op, it->second, index+1, changed_vals ); - } - } - } -} - -void FullModelChecker::makeIntervalModel2( FirstOrderModelFmc * fm, Node op, std::vector< int > & indices, int index, - std::map< int, std::map< int, Node > >& changed_vals ) { - Debug("fmc-interval-model-debug") << "Process " << index << " with indicies : "; - for( unsigned i=0; id_models[op]->d_cond[0].getNumChildren() ){ - TypeNode tn = fm->d_models[op]->d_cond[0][index].getType(); - if( tn.isInteger() ){ - std::map< Node, std::vector< int > > new_indices; - for( unsigned i=0; id_models[op]->d_cond[indices[i]][index]; - new_indices[v].push_back( indices[i] ); - if( !v.isConst() ){ - Trace("fmc-warn") << "WARNING: for interval, model has non-constant : " << v << std::endl; - Trace("fmc-warn") << "From condition : " << fm->d_models[op]->d_cond[indices[i]] << std::endl; - } - } - - std::vector< Node > values; - for( std::map< Node, std::vector< int > >::iterator it = new_indices.begin(); it != new_indices.end(); ++it ){ - makeIntervalModel2( fm, op, it->second, index+1, changed_vals ); - values.push_back( it->first ); - } - - if( tn.isInteger() ){ - //sort values by size - ConstRationalSort crs; - std::vector< int > sindices; - for( unsigned i=0; igetStar( tn ); - for( int i=(int)(sindices.size()-1); i>=0; i-- ){ - Node lb = fm->getStar( tn ); - if( i>0 ){ - lb = values[sindices[i]]; - } - Node interval = fm->getInterval( lb, ub ); - for( unsigned j=0; j d_child; - int d_data; - void reset() { d_data = -1; d_child.clear(); d_complete = -1; } - void addEntry( FirstOrderModelFmc * m, Node c, Node v, int data, int index = 0 ); - bool hasGeneralization( FirstOrderModelFmc * m, Node c, int index = 0 ); - int getGeneralizationIndex( FirstOrderModelFmc * m, std::vector & inst, int index = 0 ); - void getEntries( FirstOrderModelFmc * m, Node c, std::vector & compat, std::vector & gen, int index = 0, bool is_gen = true ); - - void collectIndices(Node c, int index, std::vector< int >& indices ); - bool isComplete(FirstOrderModelFmc * m, Node c, int index); -};/* class EntryTrie */ - - -class Def -{ -public: - EntryTrie d_et; - //cond is APPLY_UF whose arguments are returned by FullModelChecker::getRepresentative - std::vector< Node > d_cond; - //value is returned by FullModelChecker::getRepresentative - std::vector< Node > d_value; - void basic_simplify( FirstOrderModelFmc * m ); -private: - enum { - status_unk, - status_redundant, - status_non_redundant - }; - std::vector< int > d_status; - bool d_has_simplified; -public: - Def() : d_has_simplified(false){} - void reset() { - d_et.reset(); - d_cond.clear(); - d_value.clear(); - d_status.clear(); - d_has_simplified = false; - } - bool addEntry( FirstOrderModelFmc * m, Node c, Node v); - Node evaluate( FirstOrderModelFmc * m, std::vector& inst ); - int getGeneralizationIndex( FirstOrderModelFmc * m, std::vector& inst ); - void simplify( FullModelChecker * mc, FirstOrderModelFmc * m ); - void debugPrint(const char * tr, Node op, FullModelChecker * m); -};/* class Def */ - - -class FullModelChecker : public QModelBuilder -{ -protected: - Node d_true; - Node d_false; - std::map > d_rep_ids; - std::map d_quant_models; - std::map d_quant_cond; - std::map< TypeNode, Node > d_array_cond; - std::map< Node, Node > d_array_term_cond; - std::map< Node, std::vector< int > > d_star_insts; - std::map< TypeNode, bool > d_preinitialized_types; - void preInitializeType( FirstOrderModelFmc * fm, TypeNode tn ); - Node normalizeArgReps(FirstOrderModelFmc * fm, Node op, Node n); - bool exhaustiveInstantiate(FirstOrderModelFmc * fm, Node f, Node c, int c_index); -protected: - void makeIntervalModel2( FirstOrderModelFmc * fm, Node op, std::vector< int > & indices, int index, - std::map< int, std::map< int, Node > >& changed_vals ); - void makeIntervalModel( FirstOrderModelFmc * fm, Node op, std::vector< int > & indices, int index, - std::map< int, std::map< int, Node > >& changed_vals ); - void convertIntervalModel( FirstOrderModelFmc * fm, Node op ); -private: - void doCheck(FirstOrderModelFmc * fm, Node f, Def & d, Node n ); - - void doNegate( Def & dc ); - void doVariableEquality( FirstOrderModelFmc * fm, Node f, Def & d, Node eq ); - void doVariableRelation( FirstOrderModelFmc * fm, Node f, Def & d, Def & dc, Node v); - void doUninterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, Node n, std::vector< Def > & dc ); - - void doUninterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, - Def & df, std::vector< Def > & dc, int index, - std::vector< Node > & cond, std::vector & val ); - void doUninterpretedCompose2( FirstOrderModelFmc * fm, Node f, - std::map< int, Node > & entries, int index, - std::vector< Node > & cond, std::vector< Node > & val, - EntryTrie & curr); - - void doInterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, Node n, - std::vector< Def > & dc, int index, - std::vector< Node > & cond, std::vector & val ); - int isCompat( FirstOrderModelFmc * fm, std::vector< Node > & cond, Node c ); - Node doIntervalMeet( FirstOrderModelFmc * fm, Node i1, Node i2, bool mk = true ); - bool doMeet( FirstOrderModelFmc * fm, std::vector< Node > & cond, Node c ); - Node mkCond( std::vector< Node > & cond ); - Node mkCondDefault( FirstOrderModelFmc * fm, Node f ); - void mkCondDefaultVec( FirstOrderModelFmc * fm, Node f, std::vector< Node > & cond ); - void mkCondVec( Node n, std::vector< Node > & cond ); - Node mkArrayCond( Node a ); - Node evaluateInterpreted( Node n, std::vector< Node > & vals ); - Node getSomeDomainElement( FirstOrderModelFmc * fm, TypeNode tn ); -public: - FullModelChecker( context::Context* c, QuantifiersEngine* qe ); - - void debugPrintCond(const char * tr, Node n, bool dispStar = false); - void debugPrint(const char * tr, Node n, bool dispStar = false); - - int doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ); - - Node getFunctionValue(FirstOrderModelFmc * fm, Node op, const char* argPrefix ); - - /** process build model */ - bool preProcessBuildModel(TheoryModel* m); - bool processBuildModel(TheoryModel* m); - - bool useSimpleModels(); -};/* class FullModelChecker */ - -}/* CVC4::theory::quantifiers::fmcheck namespace */ -}/* CVC4::theory::quantifiers namespace */ -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ - -#endif /* __CVC4__THEORY__QUANTIFIERS__FULL_MODEL_CHECK_H */ diff --git a/src/theory/quantifiers/ho_trigger.cpp b/src/theory/quantifiers/ho_trigger.cpp deleted file mode 100644 index 6456fb2f6..000000000 --- a/src/theory/quantifiers/ho_trigger.cpp +++ /dev/null @@ -1,475 +0,0 @@ -/********************* */ -/*! \file ho_trigger.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 higher-order trigger class - **/ - -#include - -#include "theory/quantifiers/ho_trigger.h" -#include "theory/quantifiers/instantiate.h" -#include "theory/quantifiers/term_database.h" -#include "theory/quantifiers/term_util.h" -#include "theory/quantifiers_engine.h" -#include "theory/theory_engine.h" -#include "theory/uf/equality_engine.h" -#include "theory/uf/theory_uf_rewriter.h" -#include "util/hash.h" - -using namespace CVC4::kind; - -namespace CVC4 { -namespace theory { -namespace inst { - -HigherOrderTrigger::HigherOrderTrigger( - QuantifiersEngine* qe, - Node q, - std::vector& nodes, - std::map >& ho_apps) - : Trigger(qe, q, nodes), d_ho_var_apps(ho_apps) -{ - NodeManager* nm = NodeManager::currentNM(); - // process the higher-order variable applications - for (std::pair >& as : d_ho_var_apps) - { - Node n = as.first; - d_ho_var_list.push_back(n); - TypeNode tn = n.getType(); - Assert(tn.isFunction()); - if (Trace.isOn("ho-quant-trigger")) - { - Trace("ho-quant-trigger") << " have " << as.second.size(); - Trace("ho-quant-trigger") << " patterns with variable operator " << n - << ":" << std::endl; - for (unsigned j = 0; j < as.second.size(); j++) - { - Trace("ho-quant-trigger") << " " << as.second[j] << std::endl; - } - } - if (d_ho_var_types.find(tn) == d_ho_var_types.end()) - { - Trace("ho-quant-trigger") << " type " << tn - << " needs higher-order matching." << std::endl; - d_ho_var_types.insert(tn); - } - // make the bound variable lists - d_ho_var_bvl[n] = nm->getBoundVarListForFunctionType(tn); - for (const Node& nc : d_ho_var_bvl[n]) - { - d_ho_var_bvs[n].push_back(nc); - } - } -} - -HigherOrderTrigger::~HigherOrderTrigger() {} -void HigherOrderTrigger::collectHoVarApplyTerms( - Node q, Node& n, std::map >& apps) -{ - std::vector ns; - ns.push_back(n); - collectHoVarApplyTerms(q, ns, apps); - Assert(ns.size() == 1); - n = ns[0]; -} - -void HigherOrderTrigger::collectHoVarApplyTerms( - Node q, std::vector& ns, std::map >& apps) -{ - std::unordered_map visited; - std::unordered_map::iterator it; - // whether the visited node is a child of a HO_APPLY chain - std::unordered_map withinApply; - std::vector visit; - TNode cur; - for (unsigned i = 0, size = ns.size(); i < size; i++) - { - visit.push_back(ns[i]); - withinApply[ns[i]] = false; - do - { - cur = visit.back(); - visit.pop_back(); - - it = visited.find(cur); - if (it == visited.end()) - { - // do not look in nested quantifiers - if (cur.getKind() == FORALL) - { - visited[cur] = cur; - } - else - { - bool curWithinApply = withinApply[cur]; - visited[cur] = Node::null(); - visit.push_back(cur); - for (unsigned j = 0, size = cur.getNumChildren(); j < size; j++) - { - withinApply[cur[j]] = curWithinApply && j == 0; - visit.push_back(cur[j]); - } - } - } - else if (it->second.isNull()) - { - // carry the conversion - Node ret = cur; - bool childChanged = false; - std::vector children; - if (cur.getMetaKind() == kind::metakind::PARAMETERIZED) - { - children.push_back(cur.getOperator()); - } - for (const Node& nc : cur) - { - it = visited.find(nc); - Assert(it != visited.end()); - Assert(!it->second.isNull()); - childChanged = childChanged || nc != it->second; - children.push_back(it->second); - } - if (childChanged) - { - ret = NodeManager::currentNM()->mkNode(cur.getKind(), children); - } - // now, convert and store the application - if (!withinApply[cur]) - { - TNode op; - if (ret.getKind() == kind::APPLY_UF) - { - // could be a fully applied function variable - op = ret.getOperator(); - } - else if (ret.getKind() == kind::HO_APPLY) - { - op = ret; - while (op.getKind() == kind::HO_APPLY) - { - op = op[0]; - } - } - if (!op.isNull()) - { - if (op.getKind() == kind::INST_CONSTANT) - { - Assert(quantifiers::TermUtil::getInstConstAttr(ret) == q); - Trace("ho-quant-trigger-debug") - << "Ho variable apply term : " << ret << " with head " << op - << std::endl; - if (ret.getKind() == kind::APPLY_UF) - { - Node prev = ret; - // for consistency, convert to HO_APPLY if fully applied - ret = uf::TheoryUfRewriter::getHoApplyForApplyUf(ret); - } - apps[op].push_back(ret); - } - } - } - visited[cur] = ret; - } - } while (!visit.empty()); - - // store the conversion - Assert(visited.find(ns[i]) != visited.end()); - ns[i] = visited[ns[i]]; - } -} - -int HigherOrderTrigger::addInstantiations() -{ - // call the base class implementation - int addedFoLemmas = Trigger::addInstantiations(); - // also adds predicate lemms to force app completion - int addedHoLemmas = addHoTypeMatchPredicateLemmas(); - return addedHoLemmas + addedFoLemmas; -} - -bool HigherOrderTrigger::sendInstantiation(InstMatch& m) -{ - if (options::hoMatching()) - { - // get substitution corresponding to m - std::vector vars; - std::vector subs; - quantifiers::TermUtil* tutil = d_quantEngine->getTermUtil(); - for (unsigned i = 0, size = d_quant[0].getNumChildren(); i < size; i++) - { - subs.push_back(m.d_vals[i]); - vars.push_back(tutil->getInstantiationConstant(d_quant, i)); - } - - Trace("ho-unif-debug") << "Run higher-order unification..." << std::endl; - - // get the substituted form of all variable-operator ho application terms - std::map > ho_var_apps_subs; - for (std::pair >& ha : d_ho_var_apps) - { - TNode var = ha.first; - for (unsigned j = 0, size = ha.second.size(); j < size; j++) - { - TNode app = ha.second[j]; - Node sapp = - app.substitute(vars.begin(), vars.end(), subs.begin(), subs.end()); - ho_var_apps_subs[var].push_back(sapp); - Trace("ho-unif-debug") << " app[" << var << "] : " << app << " -> " - << sapp << std::endl; - } - } - - // compute argument vectors for each variable - d_lchildren.clear(); - d_arg_to_arg_rep.clear(); - d_arg_vector.clear(); - EqualityQuery* eq = d_quantEngine->getEqualityQuery(); - for (std::pair >& ha : ho_var_apps_subs) - { - TNode var = ha.first; - unsigned vnum = var.getAttribute(InstVarNumAttribute()); - Node value = m.d_vals[vnum]; - Trace("ho-unif-debug") << " val[" << var << "] = " << value << std::endl; - - Trace("ho-unif-debug2") << "initialize lambda information..." - << std::endl; - // initialize the lambda children - d_lchildren[vnum].push_back(value); - std::map >::iterator ithb = - d_ho_var_bvs.find(var); - Assert(ithb != d_ho_var_bvs.end()); - d_lchildren[vnum].insert( - d_lchildren[vnum].end(), ithb->second.begin(), ithb->second.end()); - - Trace("ho-unif-debug2") << "compute fixed arguments..." << std::endl; - // compute for each argument if it is only applied to a fixed value modulo - // equality - std::map fixed_vals; - for (unsigned i = 0; i < ha.second.size(); i++) - { - std::vector args; - Node f = uf::TheoryUfRewriter::decomposeHoApply(ha.second[i], args); - // Assert( f==value ); - for (unsigned k = 0, size = args.size(); k < size; k++) - { - Node val = args[k]; - std::map::iterator itf = fixed_vals.find(k); - if (itf == fixed_vals.end()) - { - fixed_vals[k] = val; - } - else if (!itf->second.isNull()) - { - if (!eq->areEqual(itf->second, args[k])) - { - if (!d_quantEngine->getTermDatabase()->isEntailed( - itf->second.eqNode(args[k]), true, eq)) - { - fixed_vals[k] = Node::null(); - } - } - } - } - } - if (Trace.isOn("ho-unif-debug")) - { - for (std::map::iterator itf = fixed_vals.begin(); - itf != fixed_vals.end(); - ++itf) - { - Trace("ho-unif-debug") << " arg[" << var << "][" << itf->first - << "] : " << itf->second << std::endl; - } - } - - // now construct argument vectors - Trace("ho-unif-debug2") << "compute argument vectors..." << std::endl; - std::map arg_to_rep; - for (unsigned index = 0, size = ithb->second.size(); index < size; - index++) - { - Node bv_at_index = ithb->second[index]; - std::map::iterator itf = fixed_vals.find(index); - Trace("ho-unif-debug") << " * arg[" << var << "][" << index << "]"; - if (itf != fixed_vals.end()) - { - if (!itf->second.isNull()) - { - Node r = eq->getRepresentative(itf->second); - std::map::iterator itfr = arg_to_rep.find(r); - if (itfr != arg_to_rep.end()) - { - d_arg_to_arg_rep[vnum][index] = itfr->second; - // function applied to equivalent values at multiple arguments, - // can permute variables - d_arg_vector[vnum][itfr->second].push_back(bv_at_index); - Trace("ho-unif-debug") << " = { self } ++ arg[" << var << "][" - << itfr->second << "]" << std::endl; - } - else - { - arg_to_rep[r] = index; - // function applied to single value, can either use variable or - // value at this argument position - d_arg_vector[vnum][index].push_back(bv_at_index); - d_arg_vector[vnum][index].push_back(itf->second); - if (!options::hoMatchingVarArgPriority()) - { - std::reverse(d_arg_vector[vnum][index].begin(), - d_arg_vector[vnum][index].end()); - } - Trace("ho-unif-debug") << " = { self, " << itf->second << " } " - << std::endl; - } - } - else - { - // function is applied to disequal values, can only use variable at - // this argument position - d_arg_vector[vnum][index].push_back(bv_at_index); - Trace("ho-unif-debug") << " = { self } (disequal)" << std::endl; - } - } - else - { - // argument is irrelevant to matching, assume identity variable - d_arg_vector[vnum][index].push_back(bv_at_index); - Trace("ho-unif-debug") << " = { self } (irrelevant)" << std::endl; - } - } - Trace("ho-unif-debug2") << "finished." << std::endl; - } - - return sendInstantiation(m, 0); - } - else - { - // do not run higher-order matching - return d_quantEngine->getInstantiate()->addInstantiation(d_quant, m); - } -} - -// recursion depth limited by number of arguments of higher order variables -// occurring as pattern operators (very small) -bool HigherOrderTrigger::sendInstantiation(InstMatch& m, unsigned var_index) -{ - if (var_index == d_ho_var_list.size()) - { - // we now have an instantiation to try - return d_quantEngine->getInstantiate()->addInstantiation(d_quant, m); - } - else - { - Node var = d_ho_var_list[var_index]; - unsigned vnum = var.getAttribute(InstVarNumAttribute()); - Assert(vnum < m.d_vals.size()); - Node value = m.d_vals[vnum]; - Assert(d_lchildren[vnum][0] == value); - Assert(d_ho_var_bvl.find(var) != d_ho_var_bvl.end()); - - // now, recurse on arguments to enumerate equivalent matching lambda - // expressions - bool ret = - sendInstantiationArg(m, var_index, vnum, 0, d_ho_var_bvl[var], false); - - // reset the value - m.d_vals[vnum] = value; - - return ret; - } -} - -bool HigherOrderTrigger::sendInstantiationArg(InstMatch& m, - unsigned var_index, - unsigned vnum, - unsigned arg_index, - Node lbvl, - bool arg_changed) -{ - if (arg_index == lbvl.getNumChildren()) - { - // construct the lambda - if (arg_changed) - { - Node body = - NodeManager::currentNM()->mkNode(kind::APPLY_UF, d_lchildren[vnum]); - Node lam = NodeManager::currentNM()->mkNode(kind::LAMBDA, lbvl, body); - m.d_vals[vnum] = lam; - Trace("ho-unif-debug2") << " try " << vnum << " -> " << lam << std::endl; - } - return sendInstantiation(m, var_index + 1); - } - else - { - std::map::iterator itr = - d_arg_to_arg_rep[vnum].find(arg_index); - unsigned rindex = - itr != d_arg_to_arg_rep[vnum].end() ? itr->second : arg_index; - std::map >::iterator itv = - d_arg_vector[vnum].find(rindex); - Assert(itv != d_arg_vector[vnum].end()); - Node prev = lbvl[arg_index]; - bool ret = false; - // try each argument in the vector - for (unsigned i = 0, size = itv->second.size(); i < size; i++) - { - bool new_arg_changed = arg_changed || prev != itv->second[i]; - d_lchildren[vnum][arg_index + 1] = itv->second[i]; - if (sendInstantiationArg( - m, var_index, vnum, arg_index + 1, lbvl, new_arg_changed)) - { - ret = true; - break; - } - } - // clean up - d_lchildren[vnum][arg_index + 1] = prev; - return ret; - } -} - -int HigherOrderTrigger::addHoTypeMatchPredicateLemmas() -{ - unsigned numLemmas = 0; - if (!d_ho_var_types.empty()) - { - // this forces expansion of APPLY_UF terms to curried HO_APPLY chains - unsigned size = d_quantEngine->getTermDatabase()->getNumOperators(); - for (unsigned j = 0; j < size; j++) - { - Node f = d_quantEngine->getTermDatabase()->getOperator(j); - if (f.isVar()) - { - TypeNode tn = f.getType(); - if (d_ho_var_types.find(tn) != d_ho_var_types.end()) - { - Node u = d_quantEngine->getTermUtil()->getHoTypeMatchPredicate(tn); - Node au = NodeManager::currentNM()->mkNode(kind::APPLY_UF, u, f); - if (d_quantEngine->addLemma(au)) - { - // this forces f to be a first-class member of the quantifier-free - // equality engine, - // which in turn forces the quantifier-free theory solver to expand - // it to HO_APPLY - Trace("ho-quant") << "Added ho match predicate lemma : " << au - << std::endl; - numLemmas++; - } - } - } - } - } - return numLemmas; -} - -} /* CVC4::theory::inst namespace */ -} /* CVC4::theory namespace */ -} /* CVC4 namespace */ diff --git a/src/theory/quantifiers/ho_trigger.h b/src/theory/quantifiers/ho_trigger.h deleted file mode 100644 index 87f7fe07f..000000000 --- a/src/theory/quantifiers/ho_trigger.h +++ /dev/null @@ -1,278 +0,0 @@ -/********************* */ -/*! \file ho_trigger.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 higher-order trigger class - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__THEORY__QUANTIFIERS__HO_TRIGGER_H -#define __CVC4__THEORY__QUANTIFIERS__HO_TRIGGER_H - -#include -#include - -#include "expr/node.h" -#include "options/quantifiers_options.h" -#include "theory/quantifiers/inst_match.h" -#include "theory/quantifiers/trigger.h" - -namespace CVC4 { -namespace theory { -namespace inst { - -class Trigger; - -/** HigherOrder trigger - * - * This extends the trigger class with techniques that post-process - * instantiations, specified by InstMatch objects, according to a variant of - * Huet's algorithm. For details, see Chapter 16 of the Handbook of Automated - * Reasoning (vol. 2), by Gilles Dowek. - * - * The main difference between HigherOrderTrigger and Trigger is the function - * sendInstantiation(...). Recall that this function is called when its - * underlying IMGenerator generates an InstMatch m using E-matching technique. - * We enumerate additional instantiations based on m, when the domain of m - * contains variables of function type. - * - * Examples below (f, x, y are universal variables): - * - * (EX1): (f x y) matches (k 0 1) in standard E-matching with: - * - * f -> k, x -> 0, y -> 1 - * - * This match is extended to four possible solutions by this class: - * - * f -> \ xy. (k x y), x -> 0, y -> 1 - * f -> \ xy. (k 0 y), x -> 0, y -> 1 - * f -> \ xy. (k x 1), x -> 0, y -> 1 - * f -> \ xy. (k 0 1), x -> 0, y -> 1 - * - * - * (EX2): Similarly, (f x y) matches (k 0 0) with possible solutions: - * - * f -> \ xy. (k x x), x -> 0, y -> 0 - * f -> \ xy. (k y x), x -> 0, y -> 0 - * f -> \ xy. (k 0 x), x -> 0, y -> 0 - * f -> \ xy. (k x y), x -> 0, y -> 0 - * f -> \ xy. (k y y), x -> 0, y -> 0 - * f -> \ xy. (k 0 y), x -> 0, y -> 0 - * f -> \ xy. (k x 0), x -> 0, y -> 0 - * f -> \ xy. (k y 0), x -> 0, y -> 0 - * f -> \ xy. (k 0 0), x -> 0, y -> 0 - * - * - * (EX3): (f x y), (f x z) simultaneously match (k 0 1), (k 0 2) with possible - * solutions: - * - * f -> \ xy. (k x y), x -> 0, y -> 1, z -> 2 - * f -> \ xy. (k 0 y), x -> 0, y -> 1, z -> 2 - * - * - * This class enumerates the lists above until one instantiation of that form is - * successfully added via a call to Instantiate::addInstantiation(...) - * - * - * It also implements a way of forcing APPLY_UF to expand to curried HO_APPLY to - * handle a corner case where there are no matchable ground terms - * (addHoTypeMatchPredicateLemmas). - * - */ -class HigherOrderTrigger : public Trigger -{ - friend class Trigger; - - private: - HigherOrderTrigger(QuantifiersEngine* qe, - Node q, - std::vector& nodes, - std::map >& ho_apps); - virtual ~HigherOrderTrigger(); - - public: - /** Collect higher order var apply terms - * - * Collect all top-level HO_APPLY terms in n whose head is a variable x in - * quantified formula q. Append all such terms in apps[x]. - * This method may modify n so that it is in the expected form required for - * higher-order matching, in particular, APPLY_UF terms with variable - * operators are converted to curried applications of HO_APPLY. - */ - static void collectHoVarApplyTerms(Node q, - Node& n, - std::map >& apps); - /** Collect higher order var apply terms - * - * Same as above, but with multiple terms ns. - */ - static void collectHoVarApplyTerms(Node q, - std::vector& ns, - std::map >& apps); - /** add all available instantiations, based on the current context - * - * Extends Trigger::addInstantiations to also send - * lemmas based on addHoTypeMatchPredicateLemmas. - */ - virtual int addInstantiations() override; - - protected: - /** - * Map from function-typed variables to their applications in the quantified - * formula d_f (member of Trigger). - */ - std::map > d_ho_var_apps; - /** - * List of all function-typed variables that occur as the head of function - * applications in d_f. - */ - std::vector d_ho_var_list; - /** - * Cached bound variables and bound variable list for each variable of - * function type. These are used for constructing lambda terms in - * instantiations. - */ - std::map > d_ho_var_bvs; - std::map d_ho_var_bvl; - /** the set of types of ho variables */ - std::unordered_set d_ho_var_types; - /** add higher-order type predicate lemmas - * - * Adds lemmas of the form P( f ), where P is the predicate - * returned by TermUtil::getHoTypeMatchPredicate( f.getType() ). - * These lemmas force certain functions f of type tn to appear as - * first-class terms in the quantifier-free UF solver, where recall a - * first-class term is one that occurs as an (external) term in its equality - * engine. These functions f are all operators that have at least one - * term f(t1...tn) indexed by TermDabatase and are such that - * f's type is the same as a function-typed variable we - * are considering in this class (in d_ho_var_apps). - * - * TODO: we may eliminate this based on how github issue #1115 is resolved. - */ - int addHoTypeMatchPredicateLemmas(); - /** send instantiation - * - * Sends an instantiation that is equivalent to m via - * Instantiate::addInstantiation(...). This method may modify m based on - * imitations and projections (Huet's algorithm), if m was generated by - * matching ground terms to function applications with variable heads. - * See examples (EX1)-(EX3) above. - */ - bool sendInstantiation(InstMatch& m) override; - - private: - //-------------------- current information about the match - /** - * Map from variable position to the arguments of the lambda we generated - * for that variable. - * - * For example (EX4), take a quantified formula: - * forall x f1 y f2. (...) - * Say we generated the match: - * x -> 0 - * f1 -> k1 - * y -> 1 - * f2 -> k2 - * z -> 0 - * where we matched - * (f1 x y) with (k1 0 1) and - * (f2 x z) with (k2 0 0) - * Then the algorithm implemented by this class may modify the match to: - * x -> 0 - * f1 -> (\ x1 x2. (k1 x1 1)) - * y -> 1 - * f2 -> (\ x1 x2. (k2 0 x1)) - * z -> 0 - * - * In the above (modified) match, the contents of d_lchildren are: - * 1 -> { k1, x1, 1 } - * 3 -> { k2, 0, x1 } - */ - std::map > d_lchildren; - /** map from variable position to the representative variable position. - * Used when two argument positions of a match are mapped to equal terms. - * - * In the above example (EX4), the first and second arguments of - * the match for f2 are equal. Thus, in the above example, - * we have that d_arg_to_arg_rep is: - * 1 -> { 0 -> 0, 1 -> 1 } - * 3 -> { 0 -> 0, 1 -> 0 } - * In other words, the first argument - */ - std::map > d_arg_to_arg_rep; - /** map from representative argument positions to the equivalence class - * of argument positions. In the above example (EX4), d_arg_vector is: - * 1 -> { 0 -> { 0 }, 1 -> { 1 } } - * 3 -> { 0 -> { 0, 1 } } - */ - std::map > > d_arg_vector; - //-------------------- end current information about the match - - /** higher-order pattern unification algorithm - * - * Sends an instantiation that is equivalent to m via - * d_quantEngine->addInstantiation(...), - * based on Huet's algorithm. - * - * This is a helper function of sendInstantiation( m ) above. - * - * var_index is the index of the variable in m that we are currently processing - * i.e. we are processing the var_index^{th} higher-order variable. - * - * For example, say we are processing the match from (EX4) above. - * when var_index = 0,1, we are processing possibilities for - * instantiation of f1,f2 respectively. - */ - bool sendInstantiation(InstMatch& m, unsigned var_index); - /** higher-order pattern unification algorithm - * Sends an instantiation that is equivalent to m via - * d_quantEngine->addInstantiation(...). - * This is a helper function of sendInstantiation( m, var_index ) above. - * - * var_index is the index of the variable in m that we are currently - * processing - * i.e. we are processing the var_index^{th} higher-order variable. - * vnum maps var_index to the actual variable number in m - * arg_index is the argument of the lambda term we are currently considering - * lbvl is the bound variable list associated with the term in m we are - * currently modifying - * arg_changed is whether we have modified m. - * - * For example, say we have modified our match from (EX4) to: - * x -> 0 - * f1 -> (\ x1 x2. (k1 x1 1)) - * y -> 1 - * f2 -> (\ x1 x2. (k2 0 ?)) - * z -> 0 - * That is, we are currently considering possibilities for the second - * argument of the body for f2. - * Then: - * var_index = 1, - * vnum = 3 (f2 is the 3^rd variable of our quantified formula) - * arg_index = 1, - * lbvl is d_ho_var_bvl[f2], and - * arg_changed is true, since we modified at least one previous - * argument of f1 or f2. - */ - bool sendInstantiationArg(InstMatch& m, - unsigned var_index, - unsigned vnum, - unsigned arg_index, - Node lbvl, - bool arg_changed); -}; - -} /* CVC4::theory::inst namespace */ -} /* CVC4::theory namespace */ -} /* CVC4 namespace */ - -#endif /* __CVC4__THEORY__QUANTIFIERS__HO_TRIGGER_H */ diff --git a/src/theory/quantifiers/inst_match_generator.cpp b/src/theory/quantifiers/inst_match_generator.cpp deleted file mode 100644 index 46ae8aa84..000000000 --- a/src/theory/quantifiers/inst_match_generator.cpp +++ /dev/null @@ -1,1138 +0,0 @@ -/********************* */ -/*! \file inst_match_generator.cpp - ** \verbatim - ** Top contributors (to current version): - ** Andrew Reynolds, Morgan Deters, 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 - ** - ** [[ Add lengthier description here ]] - ** \todo document this file - **/ -#include "theory/quantifiers/inst_match_generator.h" - -#include "expr/datatype.h" -#include "options/datatypes_options.h" -#include "options/quantifiers_options.h" -#include "theory/quantifiers/candidate_generator.h" -#include "theory/quantifiers/instantiate.h" -#include "theory/quantifiers/term_database.h" -#include "theory/quantifiers/term_util.h" -#include "theory/quantifiers/trigger.h" -#include "theory/quantifiers_engine.h" - -using namespace std; -using namespace CVC4; -using namespace CVC4::kind; -using namespace CVC4::context; -using namespace CVC4::theory; - -namespace CVC4 { -namespace theory { -namespace inst { - -bool IMGenerator::sendInstantiation(Trigger* tparent, InstMatch& m) -{ - return tparent->sendInstantiation(m); -} - -InstMatchGenerator::InstMatchGenerator( Node pat ){ - d_cg = NULL; - d_needsReset = true; - d_active_add = true; - Assert( quantifiers::TermUtil::hasInstConstAttr(pat) ); - d_pattern = pat; - d_match_pattern = pat; - d_match_pattern_type = pat.getType(); - d_next = NULL; - d_independent_gen = false; -} - -InstMatchGenerator::InstMatchGenerator() { - d_cg = NULL; - d_needsReset = true; - d_active_add = true; - d_next = NULL; - d_independent_gen = false; -} - -InstMatchGenerator::~InstMatchGenerator() -{ - for( unsigned i=0; isetActiveAdd(val); - } -} - -int InstMatchGenerator::getActiveScore( QuantifiersEngine * qe ) { - if( d_match_pattern.isNull() ){ - return -1; - }else if( Trigger::isAtomicTrigger( d_match_pattern ) ){ - Node f = qe->getTermDatabase()->getMatchOperator( d_match_pattern ); - unsigned ngt = qe->getTermDatabase()->getNumGroundTerms( f ); - Trace("trigger-active-sel-debug") << "Number of ground terms for " << f << " is " << ngt << std::endl; - return ngt; - }else if( d_match_pattern.getKind()==INST_CONSTANT ){ - TypeNode tn = d_match_pattern.getType(); - unsigned ngtt = qe->getTermDatabase()->getNumTypeGroundTerms( tn ); - Trace("trigger-active-sel-debug") << "Number of ground terms for " << tn << " is " << ngtt << std::endl; - return ngtt; -// }else if( d_match_pattern_getKind()==EQUAL ){ - - }else{ - return -1; - } -} - -void InstMatchGenerator::initialize( Node q, QuantifiersEngine* qe, std::vector< InstMatchGenerator * > & gens ){ - if( !d_pattern.isNull() ){ - Trace("inst-match-gen") << "Initialize, pattern term is " << d_pattern << std::endl; - if( d_match_pattern.getKind()==NOT ){ - //we want to add the children of the NOT - d_match_pattern = d_pattern[0]; - } - if( d_match_pattern.getKind()==EQUAL || d_match_pattern.getKind()==GEQ ){ - //make sure the matching portion of the equality is on the LHS of d_pattern - // and record what d_match_pattern is - for( unsigned i=0; i<2; i++ ){ - if( !quantifiers::TermUtil::hasInstConstAttr(d_match_pattern[i]) || d_match_pattern[i].getKind()==INST_CONSTANT ){ - Node mp = d_match_pattern[1-i]; - Node mpo = d_match_pattern[i]; - if( mp.getKind()!=INST_CONSTANT ){ - if( i==0 ){ - if( d_match_pattern.getKind()==GEQ ){ - d_pattern = NodeManager::currentNM()->mkNode( kind::GT, mp, mpo ); - d_pattern = d_pattern.negate(); - }else{ - d_pattern = NodeManager::currentNM()->mkNode( d_match_pattern.getKind(), mp, mpo ); - } - } - d_eq_class_rel = mpo; - d_match_pattern = mp; - } - break; - } - } - }else if( d_match_pattern.getKind()==APPLY_SELECTOR_TOTAL && d_match_pattern[0].getKind()==INST_CONSTANT && - options::purifyDtTriggers() && !options::dtSharedSelectors() ){ - d_match_pattern = d_match_pattern[0]; - } - d_match_pattern_type = d_match_pattern.getType(); - Trace("inst-match-gen") << "Pattern is " << d_pattern << ", match pattern is " << d_match_pattern << std::endl; - d_match_pattern_op = qe->getTermDatabase()->getMatchOperator( d_match_pattern ); - - //now, collect children of d_match_pattern - if (d_match_pattern.getKind() == INST_CONSTANT) - { - d_children_types.push_back( - d_match_pattern.getAttribute(InstVarNumAttribute())); - } - else - { - for (unsigned i = 0, size = d_match_pattern.getNumChildren(); i < size; - i++) - { - Node qa = quantifiers::TermUtil::getInstConstAttr(d_match_pattern[i]); - if (!qa.isNull()) - { - InstMatchGenerator* cimg = - getInstMatchGenerator(q, d_match_pattern[i]); - if (cimg) - { - d_children.push_back(cimg); - d_children_index.push_back(i); - d_children_types.push_back(-2); - }else{ - if (d_match_pattern[i].getKind() == INST_CONSTANT && qa == q) - { - d_children_types.push_back( - d_match_pattern[i].getAttribute(InstVarNumAttribute())); - } - else - { - d_children_types.push_back(-1); - } - } - } - else - { - d_children_types.push_back(-1); - } - } - } - - //create candidate generator - if( Trigger::isAtomicTrigger( d_match_pattern ) ){ - //we will be scanning lists trying to find d_match_pattern.getOperator() - d_cg = new inst::CandidateGeneratorQE( qe, d_match_pattern ); - //if matching on disequality, inform the candidate generator not to match on eqc - if( d_pattern.getKind()==NOT && d_pattern[0].getKind()==EQUAL ){ - ((inst::CandidateGeneratorQE*)d_cg)->excludeEqc( d_eq_class_rel ); - d_eq_class_rel = Node::null(); - } - }else if( d_match_pattern.getKind()==INST_CONSTANT ){ - if( d_pattern.getKind()==APPLY_SELECTOR_TOTAL ){ - Expr selectorExpr = qe->getTermDatabase()->getMatchOperator( d_pattern ).toExpr(); - size_t selectorIndex = Datatype::cindexOf(selectorExpr); - const Datatype& dt = Datatype::datatypeOf(selectorExpr); - const DatatypeConstructor& c = dt[selectorIndex]; - Node cOp = Node::fromExpr(c.getConstructor()); - Trace("inst-match-gen") << "Purify dt trigger " << d_pattern << ", will match terms of op " << cOp << std::endl; - d_cg = new inst::CandidateGeneratorQE( qe, cOp ); - }else{ - d_cg = new CandidateGeneratorQEAll( qe, d_match_pattern ); - } - }else if( d_match_pattern.getKind()==EQUAL && - d_match_pattern[0].getKind()==INST_CONSTANT && d_match_pattern[1].getKind()==INST_CONSTANT ){ - //we will be producing candidates via literal matching heuristics - if( d_pattern.getKind()!=NOT ){ - //candidates will be all equalities - d_cg = new inst::CandidateGeneratorQELitEq( qe, d_match_pattern ); - }else{ - //candidates will be all disequalities - d_cg = new inst::CandidateGeneratorQELitDeq( qe, d_match_pattern ); - } - }else{ - Trace("inst-match-gen-warn") << "(?) Unknown matching pattern is " << d_match_pattern << std::endl; - } - } - gens.insert( gens.end(), d_children.begin(), d_children.end() ); -} - -/** get match (not modulo equality) */ -int InstMatchGenerator::getMatch( - Node f, Node t, InstMatch& m, QuantifiersEngine* qe, Trigger* tparent) -{ - Trace("matching") << "Matching " << t << " against pattern " << d_match_pattern << " (" - << m << ")" << ", " << d_children.size() << ", pattern is " << d_pattern << std::endl; - Assert( !d_match_pattern.isNull() ); - if (d_cg == nullptr) - { - Trace("matching-fail") << "Internal error for match generator." << std::endl; - return -2; - }else{ - EqualityQuery* q = qe->getEqualityQuery(); - bool success = true; - //save previous match - //InstMatch prev( &m ); - std::vector< int > prev; - //if t is null - Assert( !t.isNull() ); - Assert( !quantifiers::TermUtil::hasInstConstAttr(t) ); - Assert( d_match_pattern.getKind()==INST_CONSTANT || t.getKind()==d_match_pattern.getKind() ); - Assert( !Trigger::isAtomicTrigger( d_match_pattern ) || t.getOperator()==d_match_pattern.getOperator() ); - //first, check if ground arguments are not equal, or a match is in conflict - Trace("matching-debug2") << "Setting immediate matches..." << std::endl; - for (unsigned i = 0, size = d_match_pattern.getNumChildren(); i < size; i++) - { - int ct = d_children_types[i]; - if (ct >= 0) - { - Trace("matching-debug2") << "Setting " << ct << " to " << t[i] << "..." - << std::endl; - bool addToPrev = m.get(ct).isNull(); - if (!m.set(q, ct, t[i])) - { - //match is in conflict - Trace("matching-fail") << "Match fail: " << m.get(ct) << " and " - << t[i] << std::endl; - success = false; - break; - }else if( addToPrev ){ - Trace("matching-debug2") << "Success." << std::endl; - prev.push_back(ct); - } - } - else if (ct == -1) - { - if( !q->areEqual( d_match_pattern[i], t[i] ) ){ - Trace("matching-fail") << "Match fail arg: " << d_match_pattern[i] << " and " << t[i] << std::endl; - //ground arguments are not equal - success = false; - break; - } - } - } - Trace("matching-debug2") << "Done setting immediate matches, success = " << success << "." << std::endl; - //for variable matching - if( d_match_pattern.getKind()==INST_CONSTANT ){ - bool addToPrev = m.get(d_children_types[0]).isNull(); - if (!m.set(q, d_children_types[0], t)) - { - success = false; - }else{ - if( addToPrev ){ - prev.push_back(d_children_types[0]); - } - } - //for relational matching - }else if( !d_eq_class_rel.isNull() && d_eq_class_rel.getKind()==INST_CONSTANT ){ - int v = d_eq_class_rel.getAttribute(InstVarNumAttribute()); - //also must fit match to equivalence class - bool pol = d_pattern.getKind()!=NOT; - Node pat = d_pattern.getKind()==NOT ? d_pattern[0] : d_pattern; - Node t_match; - if( pol ){ - if( pat.getKind()==GT ){ - t_match = NodeManager::currentNM()->mkNode(MINUS, t, qe->getTermUtil()->d_one); - }else{ - t_match = t; - } - }else{ - if( pat.getKind()==EQUAL ){ - if( t.getType().isBoolean() ){ - t_match = NodeManager::currentNM()->mkConst( !q->areEqual( qe->getTermUtil()->d_true, t ) ); - }else{ - Assert( t.getType().isReal() ); - t_match = NodeManager::currentNM()->mkNode(PLUS, t, qe->getTermUtil()->d_one); - } - }else if( pat.getKind()==GEQ ){ - t_match = NodeManager::currentNM()->mkNode(PLUS, t, qe->getTermUtil()->d_one); - }else if( pat.getKind()==GT ){ - t_match = t; - } - } - if( !t_match.isNull() ){ - bool addToPrev = m.get( v ).isNull(); - if (!m.set(q, v, t_match)) - { - success = false; - }else if( addToPrev ){ - prev.push_back( v ); - } - } - } - int ret_val = -1; - if( success ){ - Trace("matching-debug2") << "Reset children..." << std::endl; - //now, fit children into match - //we will be requesting candidates for matching terms for each child - for (unsigned i = 0, size = d_children.size(); i < size; i++) - { - if( !d_children[i]->reset( t[ d_children_index[i] ], qe ) ){ - success = false; - break; - } - } - if( success ){ - Trace("matching-debug2") << "Continue next " << d_next << std::endl; - ret_val = continueNextMatch(f, m, qe, tparent); - } - } - if( ret_val<0 ){ - for (int& pv : prev) - { - m.d_vals[pv] = Node::null(); - } - } - return ret_val; - } -} - -int InstMatchGenerator::continueNextMatch(Node f, - InstMatch& m, - QuantifiersEngine* qe, - Trigger* tparent) -{ - if( d_next!=NULL ){ - return d_next->getNextMatch(f, m, qe, tparent); - }else{ - if( d_active_add ){ - return sendInstantiation(tparent, m) ? 1 : -1; - }else{ - return 1; - } - } -} - -/** reset instantiation round */ -void InstMatchGenerator::resetInstantiationRound( QuantifiersEngine* qe ){ - if( !d_match_pattern.isNull() ){ - Trace("matching-debug2") << this << " reset instantiation round." << std::endl; - d_needsReset = true; - if( d_cg ){ - d_cg->resetInstantiationRound(); - } - } - if( d_next ){ - d_next->resetInstantiationRound( qe ); - } - d_curr_exclude_match.clear(); -} - -bool InstMatchGenerator::reset( Node eqc, QuantifiersEngine* qe ){ - eqc = qe->getEqualityQuery()->getRepresentative( eqc ); - Trace("matching-debug2") << this << " reset " << eqc << "." << std::endl; - if( !d_eq_class_rel.isNull() && d_eq_class_rel.getKind()!=INST_CONSTANT ){ - d_eq_class = d_eq_class_rel; - }else if( !eqc.isNull() ){ - d_eq_class = eqc; - } - //we have a specific equivalence class in mind - //we are producing matches for f(E) ~ t, where E is a non-ground vector of terms, and t is a ground term - //just look in equivalence class of the RHS - d_cg->reset( d_eq_class ); - d_needsReset = false; - - //generate the first candidate preemptively - d_curr_first_candidate = Node::null(); - Node t; - do { - t = d_cg->getNextCandidate(); - if( d_curr_exclude_match.find( t )==d_curr_exclude_match.end() ){ - d_curr_first_candidate = t; - } - }while( !t.isNull() && d_curr_first_candidate.isNull() ); - Trace("matching-summary") << "Reset " << d_match_pattern << " in " << eqc << " returns " << !d_curr_first_candidate.isNull() << "." << std::endl; - - return !d_curr_first_candidate.isNull(); -} - -int InstMatchGenerator::getNextMatch(Node f, - InstMatch& m, - QuantifiersEngine* qe, - Trigger* tparent) -{ - if( d_needsReset ){ - Trace("matching") << "Reset not done yet, must do the reset..." << std::endl; - reset( d_eq_class, qe ); - } - d_curr_matched = Node::null(); - Trace("matching") << this << " " << d_match_pattern << " get next match " << m << " in eq class " << d_eq_class << std::endl; - int success = -1; - Node t = d_curr_first_candidate; - do{ - Trace("matching-debug2") << "Matching candidate : " << t << std::endl; - //if t not null, try to fit it into match m - if( !t.isNull() ){ - if( d_curr_exclude_match.find( t )==d_curr_exclude_match.end() ){ - Assert( t.getType().isComparableTo( d_match_pattern_type ) ); - Trace("matching-summary") << "Try " << d_match_pattern << " : " << t << std::endl; - success = getMatch(f, t, m, qe, tparent); - if( d_independent_gen && success<0 ){ - Assert( d_eq_class.isNull() ); - d_curr_exclude_match[t] = true; - } - } - //get the next candidate term t - if( success<0 ){ - t = d_cg->getNextCandidate(); - }else{ - d_curr_first_candidate = d_cg->getNextCandidate(); - } - } - }while( success<0 && !t.isNull() ); - d_curr_matched = t; - if( success<0 ){ - Trace("matching-summary") << "..." << d_match_pattern << " failed, reset." << std::endl; - Trace("matching") << this << " failed, reset " << d_eq_class << std::endl; - //we failed, must reset - reset( d_eq_class, qe ); - }else{ - Trace("matching-summary") << "..." << d_match_pattern << " success." << std::endl; - } - return success; -} - -int InstMatchGenerator::addInstantiations(Node f, - QuantifiersEngine* qe, - Trigger* tparent) -{ - //try to add instantiation for each match produced - int addedLemmas = 0; - InstMatch m( f ); - while (getNextMatch(f, m, qe, tparent) > 0) - { - if( !d_active_add ){ - if (sendInstantiation(tparent, m)) - { - addedLemmas++; - if( qe->inConflict() ){ - break; - } - } - }else{ - addedLemmas++; - if( qe->inConflict() ){ - break; - } - } - m.clear(); - } - //return number of lemmas added - return addedLemmas; -} - - -InstMatchGenerator* InstMatchGenerator::mkInstMatchGenerator( Node q, Node pat, QuantifiersEngine* qe ) { - std::vector< Node > pats; - pats.push_back( pat ); - std::map< Node, InstMatchGenerator * > pat_map_init; - return mkInstMatchGenerator( q, pats, qe, pat_map_init ); -} - -InstMatchGenerator* InstMatchGenerator::mkInstMatchGeneratorMulti( Node q, std::vector< Node >& pats, QuantifiersEngine* qe ) { - Assert( pats.size()>1 ); - InstMatchGeneratorMultiLinear * imgm = new InstMatchGeneratorMultiLinear( q, pats, qe ); - std::vector< InstMatchGenerator* > gens; - imgm->initialize(q, qe, gens); - Assert( gens.size()==pats.size() ); - std::vector< Node > patsn; - std::map< Node, InstMatchGenerator * > pat_map_init; - for( unsigned i=0; id_match_pattern; - patsn.push_back( pn ); - pat_map_init[pn] = gens[i]; - } - //return mkInstMatchGenerator( q, patsn, qe, pat_map_init ); - imgm->d_next = mkInstMatchGenerator( q, patsn, qe, pat_map_init ); - return imgm; -} - -InstMatchGenerator* InstMatchGenerator::mkInstMatchGenerator( Node q, std::vector< Node >& pats, QuantifiersEngine* qe, - std::map< Node, InstMatchGenerator * >& pat_map_init ) { - size_t pCounter = 0; - InstMatchGenerator* prev = NULL; - InstMatchGenerator* oinit = NULL; - while( pCounter gens; - InstMatchGenerator* init; - std::map< Node, InstMatchGenerator * >::iterator iti = pat_map_init.find( pats[pCounter] ); - if( iti==pat_map_init.end() ){ - init = new InstMatchGenerator(pats[pCounter]); - }else{ - init = iti->second; - } - if(pCounter==0){ - oinit = init; - } - gens.push_back(init); - //chain the resulting match generators together - while (counterd_next = curr; - } - curr->initialize(q, qe, gens); - prev = curr; - counter++; - } - pCounter++; - } - return oinit; -} - -InstMatchGenerator* InstMatchGenerator::getInstMatchGenerator(Node q, Node n) -{ - if (n.getKind() == INST_CONSTANT) - { - return NULL; - } - Trace("var-trigger-debug") << "Is " << n << " a variable trigger?" - << std::endl; - if (Trigger::isBooleanTermTrigger(n)) - { - VarMatchGeneratorBooleanTerm* vmg = - new VarMatchGeneratorBooleanTerm(n[0], n[1]); - Trace("var-trigger") << "Boolean term trigger : " << n << ", var = " << n[0] - << std::endl; - return vmg; - } - Node x; - if (options::purifyTriggers()) - { - x = Trigger::getInversionVariable(n); - } - if (!x.isNull()) - { - Node s = Trigger::getInversion(n, x); - VarMatchGeneratorTermSubs* vmg = new VarMatchGeneratorTermSubs(x, s); - Trace("var-trigger") << "Term substitution trigger : " << n - << ", var = " << x << ", subs = " << s << std::endl; - return vmg; - } - return new InstMatchGenerator(n); -} - -VarMatchGeneratorBooleanTerm::VarMatchGeneratorBooleanTerm( Node var, Node comp ) : - InstMatchGenerator(), d_comp( comp ), d_rm_prev( false ) { - d_children_types.push_back(var.getAttribute(InstVarNumAttribute())); -} - -int VarMatchGeneratorBooleanTerm::getNextMatch(Node q, - InstMatch& m, - QuantifiersEngine* qe, - Trigger* tparent) -{ - int ret_val = -1; - if( !d_eq_class.isNull() ){ - Node s = NodeManager::currentNM()->mkConst(qe->getEqualityQuery()->areEqual( d_eq_class, d_pattern )); - d_eq_class = Node::null(); - d_rm_prev = m.get(d_children_types[0]).isNull(); - if (!m.set(qe->getEqualityQuery(), d_children_types[0], s)) - { - return -1; - }else{ - ret_val = continueNextMatch(q, m, qe, tparent); - if( ret_val>0 ){ - return ret_val; - } - } - } - if( d_rm_prev ){ - m.d_vals[d_children_types[0]] = Node::null(); - d_rm_prev = false; - } - return ret_val; -} - -VarMatchGeneratorTermSubs::VarMatchGeneratorTermSubs( Node var, Node subs ) : - InstMatchGenerator(), d_var( var ), d_subs( subs ), d_rm_prev( false ){ - d_children_types.push_back(d_var.getAttribute(InstVarNumAttribute())); - d_var_type = d_var.getType(); -} - -int VarMatchGeneratorTermSubs::getNextMatch(Node q, - InstMatch& m, - QuantifiersEngine* qe, - Trigger* tparent) -{ - int ret_val = -1; - if( !d_eq_class.isNull() ){ - Trace("var-trigger-matching") << "Matching " << d_eq_class << " against " << d_var << " in " << d_subs << std::endl; - Node s = d_subs.substitute( d_var, d_eq_class ); - s = Rewriter::rewrite( s ); - Trace("var-trigger-matching") << "...got " << s << ", " << s.getKind() << std::endl; - d_eq_class = Node::null(); - //if( s.getType().isSubtypeOf( d_var_type ) ){ - d_rm_prev = m.get(d_children_types[0]).isNull(); - if (!m.set(qe->getEqualityQuery(), d_children_types[0], s)) - { - return -1; - }else{ - ret_val = continueNextMatch(q, m, qe, tparent); - if( ret_val>0 ){ - return ret_val; - } - } - } - if( d_rm_prev ){ - m.d_vals[d_children_types[0]] = Node::null(); - d_rm_prev = false; - } - return -1; -} - -InstMatchGeneratorMultiLinear::InstMatchGeneratorMultiLinear( Node q, std::vector< Node >& pats, QuantifiersEngine* qe ) { - //order patterns to maximize eager matching failures - std::map< Node, std::vector< Node > > var_contains; - qe->getTermUtil()->getVarContains( q, pats, var_contains ); - std::map< Node, std::vector< Node > > var_to_node; - for( std::map< Node, std::vector< Node > >::iterator it = var_contains.begin(); it != var_contains.end(); ++it ){ - for( unsigned i=0; isecond.size(); i++ ){ - var_to_node[ it->second[i] ].push_back( it->first ); - } - } - std::vector< Node > pats_ordered; - std::vector< bool > pats_ordered_independent; - std::map< Node, bool > var_bound; - while( pats_ordered.size()1 ){ - score_2++; - } - } - if( score_index==-1 || score_1>score_max_1 || ( score_1==score_max_1 && score_2>score_max_2 ) ){ - score_index = i; - score_max_1 = score_1; - score_max_2 = score_2; - } - } - } - //update the variable bounds - Node mp = pats[score_index]; - for( unsigned i=0; isetIndependent(); - } - } -} - -int InstMatchGeneratorMultiLinear::resetChildren( QuantifiersEngine* qe ){ - for( unsigned i=0; ireset( Node::null(), qe ) ){ - return -2; - } - } - return 1; -} - -bool InstMatchGeneratorMultiLinear::reset( Node eqc, QuantifiersEngine* qe ) { - Assert( eqc.isNull() ); - if( options::multiTriggerLinear() ){ - return true; - }else{ - return resetChildren( qe )>0; - } -} - -int InstMatchGeneratorMultiLinear::getNextMatch(Node q, - InstMatch& m, - QuantifiersEngine* qe, - Trigger* tparent) -{ - Trace("multi-trigger-linear-debug") << "InstMatchGeneratorMultiLinear::getNextMatch : reset " << std::endl; - if( options::multiTriggerLinear() ){ - //reset everyone - int rc_ret = resetChildren( qe ); - if( rc_ret<0 ){ - return rc_ret; - } - } - Trace("multi-trigger-linear-debug") << "InstMatchGeneratorMultiLinear::getNextMatch : continue match " << std::endl; - Assert( d_next!=NULL ); - int ret_val = continueNextMatch(q, m, qe, tparent); - if( ret_val>0 ){ - Trace("multi-trigger-linear") << "Successful multi-trigger instantiation." << std::endl; - if( options::multiTriggerLinear() ){ - // now, restrict everyone - for( unsigned i=0; igetCurrentMatch(); - Trace("multi-trigger-linear") << " child " << i << " match : " << mi << std::endl; - d_children[i]->excludeMatch( mi ); - } - } - } - return ret_val; -} - - -/** constructors */ -InstMatchGeneratorMulti::InstMatchGeneratorMulti(Node q, - std::vector& pats, - QuantifiersEngine* qe) - : d_quant(q) -{ - Trace("multi-trigger-cache") << "Making smart multi-trigger for " << q << std::endl; - std::map< Node, std::vector< Node > > var_contains; - qe->getTermUtil()->getVarContains( q, pats, var_contains ); - //convert to indicies - for( std::map< Node, std::vector< Node > >::iterator it = var_contains.begin(); it != var_contains.end(); ++it ){ - Trace("multi-trigger-cache") << "Pattern " << it->first << " contains: "; - for( int i=0; i<(int)it->second.size(); i++ ){ - Trace("multi-trigger-cache") << it->second[i] << " "; - int index = it->second[i].getAttribute(InstVarNumAttribute()); - d_var_contains[ it->first ].push_back( index ); - d_var_to_node[ index ].push_back( it->first ); - } - Trace("multi-trigger-cache") << std::endl; - } - for( unsigned i=0; isetActiveAdd(false); - d_children.push_back(img); - //compute unique/shared variables - std::vector< int > unique_vars; - std::map< int, bool > shared_vars; - int numSharedVars = 0; - for( unsigned j=0; j vars; - unsigned index = i==0 ? pats.size()-1 : (i-1); - while( numSharedVars>0 && index!=i ){ - for( std::map< int, bool >::iterator it = shared_vars.begin(); it != shared_vars.end(); ++it ){ - if( it->second ){ - if( std::find( d_var_contains[ pats[index] ].begin(), d_var_contains[ pats[index] ].end(), it->first )!= - d_var_contains[ pats[index] ].end() ){ - vars.push_back( it->first ); - shared_vars[ it->first ] = false; - numSharedVars--; - } - } - } - index = index==0 ? (int)(pats.size()-1) : (index-1); - } - vars.insert( vars.end(), unique_vars.begin(), unique_vars.end() ); - Trace("multi-trigger-cache") << " Index[" << i << "]: "; - for( unsigned j=0; jd_order.insert( d_imtio[i]->d_order.begin(), vars.begin(), vars.end() ); - d_children_trie.push_back( InstMatchTrieOrdered( d_imtio[i] ) ); - } -} - -InstMatchGeneratorMulti::~InstMatchGeneratorMulti() -{ - for( unsigned i=0; i::iterator it = d_imtio.begin(); it != d_imtio.end(); ++it ){ - delete it->second; - } -} - -/** reset instantiation round (call this whenever equivalence classes have changed) */ -void InstMatchGeneratorMulti::resetInstantiationRound( QuantifiersEngine* qe ){ - for( unsigned i=0; iresetInstantiationRound( qe ); - } -} - -/** reset, eqc is the equivalence class to search in (any if eqc=null) */ -bool InstMatchGeneratorMulti::reset( Node eqc, QuantifiersEngine* qe ){ - for( unsigned i=0; ireset( eqc, qe ) ){ - //return false; - } - } - return true; -} - -int InstMatchGeneratorMulti::addInstantiations(Node q, - QuantifiersEngine* qe, - Trigger* tparent) -{ - int addedLemmas = 0; - Trace("multi-trigger-cache") << "Process smart multi trigger" << std::endl; - for( unsigned i=0; i newMatches; - InstMatch m( q ); - while (d_children[i]->getNextMatch(q, m, qe, tparent) > 0) - { - //m.makeRepresentative( qe ); - newMatches.push_back( InstMatch( &m ) ); - m.clear(); - } - Trace("multi-trigger-cache") << "Made " << newMatches.size() << " new matches for index " << i << std::endl; - for( unsigned j=0; jinConflict() ); - if( childIndex==endChildIndex ){ - // m is an instantiation - if (sendInstantiation(tparent, m)) - { - addedLemmas++; - Trace("multi-trigger-cache-debug") << "-> Produced instantiation " << m - << std::endl; - } - }else if( trieIndex<(int)d_children_trie[childIndex].getOrdering()->d_order.size() ){ - int curr_index = d_children_trie[childIndex].getOrdering()->d_order[trieIndex]; - // Node curr_ic = qe->getTermUtil()->getInstantiationConstant( d_quant, - // curr_index ); - Node n = m.get( curr_index ); - if( n.isNull() ){ - // add to InstMatch - for (std::pair& d : tr->d_data) - { - InstMatch mn(&m); - mn.setValue(curr_index, d.first); - processNewInstantiations(qe, - tparent, - mn, - addedLemmas, - &(d.second), - trieIndex + 1, - childIndex, - endChildIndex, - modEq); - if (qe->inConflict()) - { - break; - } - } - } - // shared and set variable, try to merge - std::map::iterator it = tr->d_data.find(n); - if (it != tr->d_data.end()) - { - processNewInstantiations(qe, - tparent, - m, - addedLemmas, - &(it->second), - trieIndex + 1, - childIndex, - endChildIndex, - modEq); - } - if (modEq) - { - // check modulo equality for other possible instantiations - if (qe->getEqualityQuery()->getEngine()->hasTerm(n)) - { - eq::EqClassIterator eqc( - qe->getEqualityQuery()->getEngine()->getRepresentative(n), - qe->getEqualityQuery()->getEngine()); - while (!eqc.isFinished()) - { - Node en = (*eqc); - if (en != n) - { - std::map::iterator itc = tr->d_data.find(en); - if (itc != tr->d_data.end()) - { - processNewInstantiations(qe, - tparent, - m, - addedLemmas, - &(itc->second), - trieIndex + 1, - childIndex, - endChildIndex, - modEq); - if (qe->inConflict()) - { - break; - } - } - } - ++eqc; - } - } - } - }else{ - int newChildIndex = (childIndex+1)%(int)d_children.size(); - processNewInstantiations(qe, - tparent, - m, - addedLemmas, - d_children_trie[newChildIndex].getTrie(), - 0, - newChildIndex, - endChildIndex, - modEq); - } -} - -InstMatchGeneratorSimple::InstMatchGeneratorSimple(Node q, - Node pat, - QuantifiersEngine* qe) - : d_quant(q), d_match_pattern(pat) -{ - if( d_match_pattern.getKind()==NOT ){ - d_match_pattern = d_match_pattern[0]; - d_pol = false; - }else{ - d_pol = true; - } - if( d_match_pattern.getKind()==EQUAL ){ - d_eqc = d_match_pattern[1]; - d_match_pattern = d_match_pattern[0]; - Assert( !quantifiers::TermUtil::hasInstConstAttr( d_eqc ) ); - } - Assert( Trigger::isSimpleTrigger( d_match_pattern ) ); - for( unsigned i=0; igetTermDatabase()->getMatchOperator( d_match_pattern ); -} - -void InstMatchGeneratorSimple::resetInstantiationRound( QuantifiersEngine* qe ) { - -} -int InstMatchGeneratorSimple::addInstantiations(Node q, - QuantifiersEngine* qe, - Trigger* tparent) -{ - int addedLemmas = 0; - quantifiers::TermArgTrie* tat; - if( d_eqc.isNull() ){ - tat = qe->getTermDatabase()->getTermArgTrie( d_op ); - }else{ - if( d_pol ){ - tat = qe->getTermDatabase()->getTermArgTrie( d_eqc, d_op ); - }else{ - Node r = qe->getEqualityQuery()->getRepresentative( d_eqc ); - //iterate over all classes except r - tat = qe->getTermDatabase()->getTermArgTrie( Node::null(), d_op ); - if( tat ){ - for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = tat->d_data.begin(); it != tat->d_data.end(); ++it ){ - if( it->first!=r ){ - InstMatch m( q ); - addInstantiations( m, qe, addedLemmas, 0, &(it->second) ); - if( qe->inConflict() ){ - break; - } - } - } - tat = NULL; - } - } - } - Debug("simple-trigger-debug") << "Adding instantiations based on " << tat << " from " << d_op << " " << d_eqc << std::endl; - if( tat ){ - InstMatch m( q ); - addInstantiations( m, qe, addedLemmas, 0, tat ); - } - return addedLemmas; -} - -void InstMatchGeneratorSimple::addInstantiations(InstMatch& m, - QuantifiersEngine* qe, - int& addedLemmas, - unsigned argIndex, - quantifiers::TermArgTrie* tat) -{ - Debug("simple-trigger-debug") << "Add inst " << argIndex << " " << d_match_pattern << std::endl; - if (argIndex == d_match_pattern.getNumChildren()) - { - Assert( !tat->d_data.empty() ); - TNode t = tat->getNodeData(); - Debug("simple-trigger") << "Actual term is " << t << std::endl; - //convert to actual used terms - for( std::map< int, int >::iterator it = d_var_num.begin(); it != d_var_num.end(); ++it ){ - if( it->second>=0 ){ - Debug("simple-trigger") << "...set " << it->second << " " << t[it->first] << std::endl; - m.setValue( it->second, t[it->first] ); - } - } - // we do not need the trigger parent for simple triggers (no post-processing - // required) - if (qe->getInstantiate()->addInstantiation(d_quant, m)) - { - addedLemmas++; - Debug("simple-trigger") << "-> Produced instantiation " << m << std::endl; - } - }else{ - if( d_match_pattern[argIndex].getKind()==INST_CONSTANT ){ - int v = d_var_num[argIndex]; - if( v!=-1 ){ - for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = tat->d_data.begin(); it != tat->d_data.end(); ++it ){ - Node t = it->first; - Node prev = m.get( v ); - //using representatives, just check if equal - Assert( t.getType().isComparableTo( d_match_pattern_arg_types[argIndex] ) ); - if( prev.isNull() || prev==t ){ - m.setValue( v, t); - addInstantiations( m, qe, addedLemmas, argIndex+1, &(it->second) ); - m.setValue( v, prev); - if( qe->inConflict() ){ - break; - } - } - } - return; - } - //inst constant from another quantified formula, treat as ground term TODO: remove this? - } - Node r = qe->getEqualityQuery()->getRepresentative( d_match_pattern[argIndex] ); - std::map< TNode, quantifiers::TermArgTrie >::iterator it = tat->d_data.find( r ); - if( it!=tat->d_data.end() ){ - addInstantiations( m, qe, addedLemmas, argIndex+1, &(it->second) ); - } - } -} - -int InstMatchGeneratorSimple::getActiveScore( QuantifiersEngine * qe ) { - Node f = qe->getTermDatabase()->getMatchOperator( d_match_pattern ); - unsigned ngt = qe->getTermDatabase()->getNumGroundTerms( f ); - Trace("trigger-active-sel-debug") << "Number of ground terms for (simple) " << f << " is " << ngt << std::endl; - return ngt; -} - - -}/* CVC4::theory::inst namespace */ -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ diff --git a/src/theory/quantifiers/inst_match_generator.h b/src/theory/quantifiers/inst_match_generator.h deleted file mode 100644 index 1903a0f95..000000000 --- a/src/theory/quantifiers/inst_match_generator.h +++ /dev/null @@ -1,694 +0,0 @@ -/********************* */ -/*! \file inst_match_generator.h - ** \verbatim - ** Top contributors (to current version): - ** Morgan Deters, 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 inst match generator class - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__THEORY__QUANTIFIERS__INST_MATCH_GENERATOR_H -#define __CVC4__THEORY__QUANTIFIERS__INST_MATCH_GENERATOR_H - -#include -#include "theory/quantifiers/inst_match_trie.h" - -namespace CVC4 { -namespace theory { - -class QuantifiersEngine; -namespace quantifiers{ - class TermArgTrie; -} - -namespace inst { - -class Trigger; - -/** IMGenerator class -* -* Virtual base class for generating InstMatch objects, which correspond to -* quantifier instantiations. The main use of this class is as a backend utility -* to Trigger (see theory/quantifiers/trigger.h). -* -* Some functions below take as argument a pointer to the current quantifiers -* engine, which is used for making various queries about what terms and -* equalities exist in the current context. -* -* Some functions below take a pointer to a parent Trigger object, which is used -* to post-process and finalize the instantiations through -* sendInstantiation(...), where the parent trigger will make calls to -* qe->getInstantiate()->addInstantiation(...). The latter function is the entry -* point in which instantiate lemmas are enqueued to be sent on the output -* channel. -*/ -class IMGenerator { -public: - virtual ~IMGenerator() {} - /** Reset instantiation round. - * - * Called once at beginning of an instantiation round. - */ - virtual void resetInstantiationRound(QuantifiersEngine* qe) {} - /** Reset. - * - * eqc is the equivalence class to search in (any if eqc=null). - * Returns true if this generator can produce instantiations, and false - * otherwise. An example of when it returns false is if it can be determined - * that no appropriate matchable terms occur based on eqc. - */ - virtual bool reset(Node eqc, QuantifiersEngine* qe) { return true; } - /** Get the next match. - * - * Must call reset( eqc ) before this function. This constructs an - * instantiation, which it populates in data structure m, - * based on the current context using the implemented matching algorithm. - * - * q is the quantified formula we are adding instantiations for - * m is the InstMatch object we are generating - * - * Returns a value >0 if an instantiation was successfully generated - */ - virtual int getNextMatch(Node q, - InstMatch& m, - QuantifiersEngine* qe, - Trigger* tparent) - { - return 0; - } - /** add instantiations - * - * This add all available instantiations for q based on the current context - * using the implemented matching algorithm. It typically is implemented as a - * fixed point of getNextMatch above. - * - * It returns the number of instantiations added using calls to calls to - * Instantiate::addInstantiation(...). - */ - virtual int addInstantiations(Node q, - QuantifiersEngine* qe, - Trigger* tparent) - { - return 0; - } - /** get active score - * - * A heuristic value indicating how active this generator is. - */ - virtual int getActiveScore( QuantifiersEngine * qe ) { return 0; } - protected: - /** send instantiation - * - * This method sends instantiation, specified by m, to the parent trigger - * object, which will in turn make a call to - * Instantiate::addInstantiation(...). This method returns true if a - * call to Instantiate::addInstantiation(...) was successfully made, - * indicating that an instantiation was enqueued in the quantifier engine's - * lemma cache. - */ - bool sendInstantiation(Trigger* tparent, InstMatch& m); -};/* class IMGenerator */ - -class CandidateGenerator; - -/** InstMatchGenerator class -* -* This is the default generator class for non-simple single triggers, that is, -* triggers composed of a single term with nested term applications. -* For example, { f( y, f( x, a ) ) } and { f( g( x ), a ) } are non-simple -* triggers. -* -* Handling non-simple triggers is done by constructing a linked list of -* InstMatchGenerator classes (see mkInstMatchGenerator), where each -* InstMatchGenerator has a "d_next" pointer. If d_next is NULL, -* then this is the end of the InstMatchGenerator and the last -* InstMatchGenerator is responsible for finalizing the instantiation. -* -* For (EX1), for the trigger f( y, f( x, a ) ), we construct the linked list: -* -* [ f( y, f( x, a ) ) ] -> [ f( x, a ) ] -> NULL -* -* In a call to getNextMatch, -* if we match against a ground term f( b, c ), then the first InstMatchGenerator -* in this list binds y to b, and tells the InstMatchGenerator [ f( x, a ) ] to -* match f-applications in the equivalence class of c. -* -* CVC4 employs techniques that ensure that the number of instantiations -* is worst-case polynomial wrt the number of ground terms. -* Consider the axiom/pattern/context (EX2) : -* -* axiom : forall x1 x2 x3 x4. F[ x1...x4 ] -* -* trigger : P( f( x1 ), f( x2 ), f( x3 ), f( x4 ) ) -* -* ground context : ~P( a, a, a, a ), a = f( c_1 ) = ... = f( c_100 ) -* -* If E-matching were applied exhaustively, then x1, x2, x3, x4 would be -* instantiated with all combinations of c_1, ... c_100, giving 100^4 -* instantiations. -* -* Instead, we enforce that at most 1 instantiation is produced for a -* ( pattern, ground term ) pair per round. Meaning, only one instantiation is -* generated when matching P( a, a, a, a ) against the generator -* [P( f( x1 ), f( x2 ), f( x3 ), f( x4 ) )]. For details, see Section 3 of -* Reynolds, Vampire 2016. -* -* To enforce these policies, we use a flag "d_active_add" which dictates the -* behavior of the last element in the linked list. If d_active_add is -* true -> a call to getNextMatch(...) returns 1 only if adding the -* instantiation via a call to IMGenerator::sendInstantiation(...) -* successfully enqueues a lemma via a call to -* Instantiate::addInstantiation(...). This call may fail e.g. if we -* have already added the instantiation, or the instantiation is -* entailed. -* false -> a call to getNextMatch(...) returns 1 whenever an m is -* constructed, where typically the caller would use m. -* This is important since a return value >0 signals that the current matched -* terms should be flushed. Consider the above example (EX1), where -* [ f(y,f(x,a)) ] is being matched against f(b,c), -* [ f(x,a) ] is being matched against f(d,a) where c=f(d,a) -* A successfully added instantiation { x->d, y->b } here signals we should -* not produce further instantiations that match f(y,f(x,a)) with f(b,c). -* -* A number of special cases of triggers are covered by this generator (see -* implementation of initialize), including : -* Literal triggers, e.g. x >= a, ~x = y -* Selector triggers, e.g. head( x ) -* Triggers with invertible subterms, e.g. f( x+1 ) -* Variable triggers, e.g. x -* -* All triggers above can be in the context of an equality, e.g. -* { f( y, f( x, a ) ) = b } is a trigger that matches f( y, f( x, a ) ) to -* ground terms in the equivalence class of b. -* { ~f( y, f( x, a ) ) = b } is a trigger that matches f( y, f( x, a ) ) to any -* ground terms not in the equivalence class of b. -*/ -class InstMatchGenerator : public IMGenerator { - public: - /** destructor */ - ~InstMatchGenerator() override; - - /** Reset instantiation round. */ - void resetInstantiationRound(QuantifiersEngine* qe) override; - /** Reset. */ - bool reset(Node eqc, QuantifiersEngine* qe) override; - /** Get the next match. */ - int getNextMatch(Node q, - InstMatch& m, - QuantifiersEngine* qe, - Trigger* tparent) override; - /** Add instantiations. */ - int addInstantiations(Node q, - QuantifiersEngine* qe, - Trigger* tparent) override; - - /** set active add flag (true by default) - * - * If active add is true, we call sendInstantiation in calls to getNextMatch, - * instead of returning the match. This is necessary so that we can ensure - * policies that are dependent upon knowing when instantiations are - * successfully added to the output channel through - * Instantiate::addInstantiation(...). - */ - void setActiveAdd(bool val); - /** Get active score for this inst match generator - * - * See Trigger::getActiveScore for details. - */ - int getActiveScore(QuantifiersEngine* qe) override; - /** exclude match - * - * Exclude matching d_match_pattern with Node n on subsequent calls to - * getNextMatch. - */ - void excludeMatch(Node n) { d_curr_exclude_match[n] = true; } - /** Get current match. - * Returns the term we are currently matching. - */ - Node getCurrentMatch() { return d_curr_matched; } - /** set that this match generator is independent - * - * A match generator is indepndent when this generator fails to produce a - * match in a call to getNextMatch, the overall matching fails. - */ - void setIndependent() { d_independent_gen = true; } - //-------------------------------construction of inst match generators - /** mkInstMatchGenerator for single triggers, calls the method below */ - static InstMatchGenerator* mkInstMatchGenerator(Node q, - Node pat, - QuantifiersEngine* qe); - /** mkInstMatchGenerator for the multi trigger case - * - * This linked list of InstMatchGenerator classes with one - * InstMatchGeneratorMultiLinear at the head and a list of trailing - * InstMatchGenerators corresponding to each unique subterm of pats with - * free variables. - */ - static InstMatchGenerator* mkInstMatchGeneratorMulti(Node q, - std::vector& pats, - QuantifiersEngine* qe); - /** mkInstMatchGenerator - * - * This generates a linked list of InstMatchGenerators for each unique - * subterm of pats with free variables. - * - * q is the quantified formula associated with the generator we are making - * pats is a set of terms we are making InstMatchGenerator nodes for - * qe is a pointer to the quantifiers engine (used for querying necessary - * information during initialization) pat_map_init maps from terms to - * generators we have already made for them. - * - * It calls initialize(...) for all InstMatchGenerators generated by this call. - */ - static InstMatchGenerator* mkInstMatchGenerator( - Node q, - std::vector& pats, - QuantifiersEngine* qe, - std::map& pat_map_init); - //-------------------------------end construction of inst match generators - - protected: - /** constructors - * - * These are intentionally private, and are called during linked list - * construction in mkInstMatchGenerator. - */ - InstMatchGenerator(Node pat); - InstMatchGenerator(); - /** The pattern we are producing matches for. - * - * This term and d_match_pattern can be different since this - * term may involve information regarding phase and (dis)equality entailment, - * or other special cases of Triggers. - * - * For example, for the trigger for P( x ) = false, which matches P( x ) with - * P( t ) in the equivalence class of false, - * P( x ) = false is d_pattern - * P( x ) is d_match_pattern - * Another example, for pure theory triggers like head( x ), we have - * head( x ) is d_pattern - * x is d_match_pattern - * since head( x ) can match any (List) datatype term x. - * - * If null, this is a multi trigger that is merging matches from d_children, - * which is used in InstMatchGeneratorMulti. - */ - Node d_pattern; - /** The match pattern - * This is the term that is matched against ground terms, - * see examples above. - */ - Node d_match_pattern; - /** The current term we are matching. */ - Node d_curr_matched; - /** do we need to call reset on this generator? */ - bool d_needsReset; - /** candidate generator - * This is the back-end utility for InstMatchGenerator. - * It generates a stream of candidate terms to match against d_match_pattern - * below, dependending upon what kind of term we are matching - * (e.g. a matchable term, a variable, a relation, etc.). - */ - CandidateGenerator* d_cg; - /** children generators - * These match generators correspond to the children of the term - * we are matching with this generator. - * For example [ f( x, a ) ] is a child of [ f( y, f( x, a ) ) ] - * in the example (EX1) above. - */ - std::vector d_children; - /** for each child, the index in the term - * For example [ f( x, a ) ] has index 1 in [ f( y, f( x, a ) ) ] - * in the example (EX1) above, indicating it is the 2nd child - * of the term. - */ - std::vector d_children_index; - /** children types - * - * If d_match_pattern is an instantiation constant, then this is a singleton - * vector containing the variable number of the d_match_pattern itself. - * If d_match_patterm is a term of the form f( t1, ..., tn ), then for each - * index i, d_children[i] stores the type of node ti is, where: - * >= 0 : variable (indicates its number), - * -1 : ground term, - * -2 : child term. - */ - std::vector d_children_types; - /** The next generator in the linked list - * that this generator is a part of. - */ - InstMatchGenerator* d_next; - /** The equivalence class we are currently matching in. */ - Node d_eq_class; - /** If non-null, then this is a relational trigger of the form x ~ - * d_eq_class_rel. */ - Node d_eq_class_rel; - /** Excluded matches - * Stores the terms we are not allowed to match. - * These can for instance be specified by the smt2 - * extended syntax (! ... :no-pattern). - */ - std::map d_curr_exclude_match; - /** Current first candidate - * Used in a key fail-quickly optimization which generates - * the first candidate term to match during reset(). - */ - Node d_curr_first_candidate; - /** Indepdendent generate - * If this flag is true, failures to match should be cached. - */ - bool d_independent_gen; - /** active add flag, described above. */ - bool d_active_add; - /** cached value of d_match_pattern.getType() */ - TypeNode d_match_pattern_type; - /** the match operator for d_match_pattern - * - * See TermDatabase::getMatchOperator for details on match operators. - */ - Node d_match_pattern_op; - /** get the match against ground term or formula t. - * - * d_match_pattern and t should have the same shape, that is, - * their match operator (see TermDatabase::getMatchOperator) is the same. - * only valid for use where !d_match_pattern.isNull(). - */ - int getMatch( - Node q, Node t, InstMatch& m, QuantifiersEngine* qe, Trigger* tparent); - /** Initialize this generator. - * - * q is the quantified formula associated with this generator. - * - * This constructs the appropriate information about what - * patterns we are matching and children generators. - * - * It may construct new (child) generators needed to implement - * the matching algorithm for this term. For each new generator - * constructed in this way, it adds them to gens. - */ - void initialize(Node q, - QuantifiersEngine* qe, - std::vector& gens); - /** Continue next match - * - * This is called during getNextMatch when the current generator in the linked - * list succesfully satisfies its matching constraint. This function either - * calls getNextMatch of the next element in the linked list, or finalizes the - * match (calling sendInstantiation(...) if active add is true, or returning a - * value >0 if active add is false). Its return value has the same semantics - * as getNextMatch. - */ - int continueNextMatch(Node q, - InstMatch& m, - QuantifiersEngine* qe, - Trigger* tparent); - /** Get inst match generator - * - * Gets the InstMatchGenerator that implements the - * appropriate matching algorithm for n within q - * within a linked list of InstMatchGenerators. - */ - static InstMatchGenerator* getInstMatchGenerator(Node q, Node n); -};/* class InstMatchGenerator */ - -/** match generator for Boolean term ITEs -* This handles the special case of triggers that look like ite( x, BV1, BV0 ). -*/ -class VarMatchGeneratorBooleanTerm : public InstMatchGenerator { -public: - VarMatchGeneratorBooleanTerm( Node var, Node comp ); - - /** Reset */ - bool reset(Node eqc, QuantifiersEngine* qe) override - { - d_eq_class = eqc; - return true; - } - /** Get the next match. */ - int getNextMatch(Node q, - InstMatch& m, - QuantifiersEngine* qe, - Trigger* tparent) override; - - private: - /** stores the true branch of the Boolean ITE */ - Node d_comp; - /** stores whether we have written a value for var in the current match. */ - bool d_rm_prev; -}; - -/** match generator for purified terms -* This handles the special case of invertible terms like x+1 (see -* Trigger::getTermInversionVariable). -*/ -class VarMatchGeneratorTermSubs : public InstMatchGenerator { -public: - VarMatchGeneratorTermSubs( Node var, Node subs ); - - /** Reset */ - bool reset(Node eqc, QuantifiersEngine* qe) override - { - d_eq_class = eqc; - return true; - } - /** Get the next match. */ - int getNextMatch(Node q, - InstMatch& m, - QuantifiersEngine* qe, - Trigger* tparent) override; - - private: - /** variable we are matching (x in the example x+1). */ - TNode d_var; - /** cache of d_var.getType() */ - TypeNode d_var_type; - /** The substitution for what we match (x-1 in the example x+1). */ - Node d_subs; - /** stores whether we have written a value for d_var in the current match. */ - bool d_rm_prev; -}; - -/** InstMatchGeneratorMultiLinear class -* -* This is the default implementation of multi-triggers. -* -* Handling multi-triggers using this class is done by constructing a linked -* list of InstMatchGenerator classes (see mkInstMatchGeneratorMulti), with one -* InstMatchGeneratorMultiLinear at the head and a list of trailing -* InstMatchGenerators. -* -* CVC4 employs techniques that ensure that the number of instantiations -* is worst-case polynomial wrt the number of ground terms, where this class -* lifts this policy to multi-triggers. In particular consider -* -* multi-trigger : { f( x1 ), f( x2 ), f( x3 ), f( x4 ) } -* -* For this multi-trigger, we insist that for each i=1...4, and each ground term -* t, there is at most 1 instantiation added as a result of matching -* ( f( x1 ), f( x2 ), f( x3 ), f( x4 ) ) -* against ground terms of the form -* ( s_1, s_2, s_3, s_4 ) where t = s_i for i=1,...,4. -* Meaning we add instantiations for the multi-trigger -* ( f( x1 ), f( x2 ), f( x3 ), f( x4 ) ) based on matching pairwise against: -* ( f( c_i11 ), f( c_i21 ), f( c_i31 ), f( c_i41 ) ) -* ( f( c_i12 ), f( c_i22 ), f( c_i32 ), f( c_i42 ) ) -* ( f( c_i13 ), f( c_i23 ), f( c_i33 ), f( c_i43 ) ) -* Where we require c_i{jk} != c_i{jl} for each i=1...4, k != l. -* -* For example, we disallow adding instantiations based on matching against both -* ( f( c_1 ), f( c_2 ), f( c_4 ), f( c_6 ) ) and -* ( f( c_1 ), f( c_3 ), f( c_5 ), f( c_7 ) ) -* against ( f( x1 ), f( x2 ), f( x3 ), f( x4 ) ), since f( c_1 ) is matched -* against f( x1 ) twice. -* -* This policy can be disabled by --no-multi-trigger-linear. -* -*/ -class InstMatchGeneratorMultiLinear : public InstMatchGenerator { - friend class InstMatchGenerator; - - public: - /** Reset. */ - bool reset(Node eqc, QuantifiersEngine* qe) override; - /** Get the next match. */ - int getNextMatch(Node q, - InstMatch& m, - QuantifiersEngine* qe, - Trigger* tparent) override; - - protected: - /** reset the children of this generator */ - int resetChildren(QuantifiersEngine* qe); - /** constructor */ - InstMatchGeneratorMultiLinear(Node q, - std::vector& pats, - QuantifiersEngine* qe); -};/* class InstMatchGeneratorMulti */ - -/** InstMatchGeneratorMulti -* -* This is an earlier implementation of multi-triggers -* that is enabled by --multi-trigger-cache. -* This generator takes the product of instantiations -* found by single trigger matching, and does -* not have the guarantee that the number of -* instantiations is polynomial wrt the number of -* ground terms. -*/ -class InstMatchGeneratorMulti : public IMGenerator { - public: - /** constructors */ - InstMatchGeneratorMulti(Node q, - std::vector& pats, - QuantifiersEngine* qe); - /** destructor */ - ~InstMatchGeneratorMulti() override; - - /** Reset instantiation round. */ - void resetInstantiationRound(QuantifiersEngine* qe) override; - /** Reset. */ - bool reset(Node eqc, QuantifiersEngine* qe) override; - /** Add instantiations. */ - int addInstantiations(Node q, - QuantifiersEngine* qe, - Trigger* tparent) override; - - private: - /** indexed trie */ - typedef std::pair< std::pair< int, int >, InstMatchTrie* > IndexedTrie; - /** process new match - * - * Called during addInstantiations(...). - * Indicates we produced a match m for child fromChildIndex - * addedLemmas is how many instantiations we succesfully send - * via IMGenerator::sendInstantiation(...) calls. - */ - void processNewMatch(QuantifiersEngine* qe, - Trigger* tparent, - InstMatch& m, - int fromChildIndex, - int& addedLemmas); - /** helper for process new match - * tr is the inst match trie (term index) we are currently traversing. - * trieIndex is depth we are in trie tr. - * childIndex is the index of the term in the multi trigger we are currently - * processing. - * endChildIndex is the index of the term in the multi trigger that generated - * a new term, and hence we will end when the product - * computed by this function returns to. - * modEq is whether we are matching modulo equality. - */ - void processNewInstantiations(QuantifiersEngine* qe, - Trigger* tparent, - InstMatch& m, - int& addedLemmas, - InstMatchTrie* tr, - int trieIndex, - int childIndex, - int endChildIndex, - bool modEq); - /** Map from pattern nodes to indices of variables they contain. */ - std::map< Node, std::vector< int > > d_var_contains; - /** Map from variable indices to pattern nodes that contain them. */ - std::map< int, std::vector< Node > > d_var_to_node; - /** quantified formula we are producing matches for */ - Node d_quant; - /** children generators - * These are inst match generators for each term in the multi trigger. - */ - std::vector< InstMatchGenerator* > d_children; - /** variable orderings - * Stores a heuristically determined variable ordering (unique - * variables first) for each term in the multi trigger. - */ - std::map< unsigned, InstMatchTrie::ImtIndexOrder* > d_imtio; - /** inst match tries for each child node - * This data structure stores all InstMatch objects generated - * by matching for each term in the multi trigger. - */ - std::vector< InstMatchTrieOrdered > d_children_trie; -};/* class InstMatchGeneratorMulti */ - -/** InstMatchGeneratorSimple class -* -* This is the default generator class for simple single triggers. -* For example, { f( x, a ) }, { f( x, x ) } and { f( x, y ) } are simple single -* triggers. In practice, around 70-90% of triggers are simple single triggers. -* -* Notice that simple triggers also can have an attached polarity. -* For example, { P( x ) = false }, { f( x, y ) = a } and { ~f( a, x ) = b } are -* simple single triggers. -* -* The implementation traverses the term indices in TermDatabase for adding -* instantiations, which is more efficient than the techniques required for -* handling non-simple single triggers. -* -* In contrast to other instantiation generators, it does not call -* IMGenerator::sendInstantiation and for performance reasons instead calls -* qe->getInstantiate()->addInstantiation(...) directly. -*/ -class InstMatchGeneratorSimple : public IMGenerator { - public: - /** constructors */ - InstMatchGeneratorSimple(Node q, Node pat, QuantifiersEngine* qe); - - /** Reset instantiation round. */ - void resetInstantiationRound(QuantifiersEngine* qe) override; - /** Add instantiations. */ - int addInstantiations(Node q, - QuantifiersEngine* qe, - Trigger* tparent) override; - /** Get active score. */ - int getActiveScore(QuantifiersEngine* qe) override; - - private: - /** quantified formula for the trigger term */ - Node d_quant; - /** the trigger term */ - Node d_match_pattern; - /** equivalence class polarity information - * - * This stores the required polarity/equivalence class with this trigger. - * If d_eqc is non-null, we only produce matches { x->t } such that - * our context does not entail - * ( d_match_pattern*{ x->t } = d_eqc) if d_pol = true, or - * ( d_match_pattern*{ x->t } != d_eqc) if d_pol = false. - * where * denotes application of substitution. - */ - bool d_pol; - Node d_eqc; - /** Match pattern arg types. - * Cached values of d_match_pattern[i].getType(). - */ - std::vector< TypeNode > d_match_pattern_arg_types; - /** The match operator d_match_pattern (see TermDb::getMatchOperator). */ - Node d_op; - /** Map from child number to variable index. */ - std::map< int, int > d_var_num; - /** add instantiations, helper function. - * - * m is the current match we are building, - * addedLemmas is the number of lemmas we have added via calls to - * qe->getInstantiate()->aaddInstantiation(...), - * argIndex is the argument index in d_match_pattern we are currently - * matching, - * tat is the term index we are currently traversing. - */ - void addInstantiations(InstMatch& m, - QuantifiersEngine* qe, - int& addedLemmas, - unsigned argIndex, - quantifiers::TermArgTrie* tat); -};/* class InstMatchGeneratorSimple */ -} -} -} - -#endif diff --git a/src/theory/quantifiers/inst_strategy_cbqi.cpp b/src/theory/quantifiers/inst_strategy_cbqi.cpp deleted file mode 100644 index 70dc9c4e9..000000000 --- a/src/theory/quantifiers/inst_strategy_cbqi.cpp +++ /dev/null @@ -1,828 +0,0 @@ -/********************* */ -/*! \file inst_strategy_cbqi.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 Implementation of counterexample-guided quantifier instantiation strategies - **/ -#include "theory/quantifiers/inst_strategy_cbqi.h" - -#include "options/quantifiers_options.h" -#include "smt/term_formula_removal.h" -#include "theory/arith/partial_model.h" -#include "theory/arith/theory_arith.h" -#include "theory/arith/theory_arith_private.h" -#include "theory/quantifiers/first_order_model.h" -#include "theory/quantifiers/instantiate.h" -#include "theory/quantifiers/quant_epr.h" -#include "theory/quantifiers/quantifiers_attributes.h" -#include "theory/quantifiers/quantifiers_rewriter.h" -#include "theory/quantifiers/term_database.h" -#include "theory/quantifiers/term_util.h" -#include "theory/quantifiers/trigger.h" -#include "theory/theory_engine.h" - -using namespace std; -using namespace CVC4; -using namespace CVC4::kind; -using namespace CVC4::context; -using namespace CVC4::theory; -using namespace CVC4::theory::quantifiers; -using namespace CVC4::theory::arith; - -#define ARITH_INSTANTIATOR_USE_MINUS_DELTA - -InstStrategyCbqi::InstStrategyCbqi( QuantifiersEngine * qe ) - : QuantifiersModule( qe ), d_added_cbqi_lemma( qe->getUserContext() ), -d_elim_quants( qe->getSatContext() ), -d_nested_qe_waitlist_size( qe->getUserContext() ), -d_nested_qe_waitlist_proc( qe->getUserContext() ) -//, d_added_inst( qe->getUserContext() ) -{ - d_qid_count = 0; -} - -bool InstStrategyCbqi::needsCheck( Theory::Effort e ) { - return e>=Theory::EFFORT_LAST_CALL; -} - -QuantifiersModule::QEffort InstStrategyCbqi::needsModel(Theory::Effort e) -{ - for( unsigned i=0; igetModel()->getNumAssertedQuantifiers(); i++ ){ - Node q = d_quantEngine->getModel()->getAssertedQuantifier( i ); - if( doCbqi( q ) && d_quantEngine->getModel()->isQuantifierActive( q ) ){ - return QEFFORT_STANDARD; - } - } - return QEFFORT_NONE; -} - -bool InstStrategyCbqi::registerCbqiLemma( Node q ) { - if( !hasAddedCbqiLemma( q ) ){ - d_added_cbqi_lemma.insert( q ); - Trace("cbqi-debug") << "Do cbqi for " << q << std::endl; - //add cbqi lemma - //get the counterexample literal - Node ceLit = d_quantEngine->getTermUtil()->getCounterexampleLiteral( q ); - Node ceBody = d_quantEngine->getTermUtil()->getInstConstantBody( q ); - if( !ceBody.isNull() ){ - //add counterexample lemma - Node lem = NodeManager::currentNM()->mkNode( OR, ceLit.negate(), ceBody.negate() ); - //require any decision on cel to be phase=true - d_quantEngine->addRequirePhase( ceLit, true ); - Debug("cbqi-debug") << "Require phase " << ceLit << " = true." << std::endl; - //add counterexample lemma - lem = Rewriter::rewrite( lem ); - Trace("cbqi-lemma") << "Counterexample lemma : " << lem << std::endl; - registerCounterexampleLemma( q, lem ); - - //totality lemmas for EPR - QuantEPR * qepr = d_quantEngine->getQuantEPR(); - if( qepr!=NULL ){ - for( unsigned i=0; iisEPR( tn ) ){ - //add totality lemma - std::map< TypeNode, std::vector< Node > >::iterator itc = qepr->d_consts.find( tn ); - if( itc!=qepr->d_consts.end() ){ - Assert( !itc->second.empty() ); - Node ic = d_quantEngine->getTermUtil()->getInstantiationConstant( q, i ); - std::vector< Node > disj; - for( unsigned j=0; jsecond.size(); j++ ){ - disj.push_back( ic.eqNode( itc->second[j] ) ); - } - Node tlem = disj.size()==1 ? disj[0] : NodeManager::currentNM()->mkNode( kind::OR, disj ); - Trace("cbqi-lemma") << "EPR totality lemma : " << tlem << std::endl; - d_quantEngine->getOutputChannel().lemma( tlem ); - }else{ - Assert( false ); - } - }else{ - Assert( !options::cbqiAll() ); - } - } - } - } - - //compute dependencies between quantified formulas - if( options::cbqiLitDepend() || options::cbqiInnermost() ){ - std::vector< Node > ics; - TermUtil::computeVarContains( q, ics ); - d_parent_quant[q].clear(); - d_children_quant[q].clear(); - std::vector< Node > dep; - for( unsigned i=0; igetTermUtil()->getCounterexampleLiteral( qi ); - dep.push_back( qi ); - dep.push_back( qicel ); - } - } - if( !dep.empty() ){ - Node dep_lemma = NodeManager::currentNM()->mkNode( kind::IMPLIES, ceLit, NodeManager::currentNM()->mkNode( kind::AND, dep ) ); - Trace("cbqi-lemma") << "Counterexample dependency lemma : " << dep_lemma << std::endl; - d_quantEngine->getOutputChannel().lemma( dep_lemma ); - } - } - - //must register all sub-quantifiers of counterexample lemma, register their lemmas - std::vector< Node > quants; - TermUtil::computeQuantContains( lem, quants ); - for( unsigned i=0; igetModel()->getNumAssertedQuantifiers(); i++ ){ - Node q = d_quantEngine->getModel()->getAssertedQuantifier( i ); - //it is not active if it corresponds to a rewrite rule: we will process in rewrite engine - if( doCbqi( q ) ){ - Assert( hasAddedCbqiLemma( q ) ); - if( d_quantEngine->getModel()->isQuantifierActive( q ) ){ - d_active_quant[q] = true; - Debug("cbqi-debug") << "Check quantified formula " << q << "..." << std::endl; - Node cel = d_quantEngine->getTermUtil()->getCounterexampleLiteral( q ); - bool value; - if( d_quantEngine->getValuation().hasSatValue( cel, value ) ){ - Debug("cbqi-debug") << "...CE Literal has value " << value << std::endl; - if( !value ){ - if( d_quantEngine->getValuation().isDecision( cel ) ){ - Trace("cbqi-warn") << "CBQI WARNING: Bad decision on CE Literal." << std::endl; - }else{ - Trace("cbqi") << "Inactive : " << q << std::endl; - d_quantEngine->getModel()->setQuantifierActive( q, false ); - d_cbqi_set_quant_inactive = true; - d_active_quant.erase( q ); - d_elim_quants.insert( q ); - Trace("cbqi-nqe") << "Inactive, waitlist proc/size = " << d_nested_qe_waitlist_proc[q].get() << "/" << d_nested_qe_waitlist_size[q].get() << std::endl; - //process from waitlist - while( d_nested_qe_waitlist_proc[q]=0 ); - Assert( index<(int)d_nested_qe_waitlist[q].size() ); - Node nq = d_nested_qe_waitlist[q][index]; - Node nqeqn = doNestedQENode( d_nested_qe_info[nq].d_q, q, nq, d_nested_qe_info[nq].d_inst_terms, d_nested_qe_info[nq].d_doVts ); - Node dqelem = nq.eqNode( nqeqn ); - Trace("cbqi-lemma") << "Delayed nested quantifier elimination lemma : " << dqelem << std::endl; - d_quantEngine->getOutputChannel().lemma( dqelem ); - d_nested_qe_waitlist_proc[q] = index + 1; - } - } - } - }else{ - Debug("cbqi-debug") << "...CE Literal does not have value " << std::endl; - } - } - } - } - - //refinement: only consider innermost active quantified formulas - if( options::cbqiInnermost() ){ - if( !d_children_quant.empty() && !d_active_quant.empty() ){ - Trace("cbqi-debug") << "Find non-innermost quantifiers..." << std::endl; - std::vector< Node > ninner; - for( std::map< Node, bool >::iterator it = d_active_quant.begin(); it != d_active_quant.end(); ++it ){ - std::map< Node, std::vector< Node > >::iterator itc = d_children_quant.find( it->first ); - if( itc!=d_children_quant.end() ){ - for( unsigned j=0; jsecond.size(); j++ ){ - if( d_active_quant.find( itc->second[j] )!=d_active_quant.end() ){ - Trace("cbqi-debug") << "Do not consider " << it->first << " since it is not innermost (" << itc->second[j] << std::endl; - ninner.push_back( it->first ); - break; - } - } - } - } - Trace("cbqi-debug") << "Found " << ninner.size() << " non-innermost." << std::endl; - for( unsigned i=0; iinConflict() ); - double clSet = 0; - if( Trace.isOn("cbqi-engine") ){ - clSet = double(clock())/double(CLOCKS_PER_SEC); - Trace("cbqi-engine") << "---Cbqi Engine Round, effort = " << e << "---" << std::endl; - } - unsigned lastWaiting = d_quantEngine->getNumLemmasWaiting(); - for( int ee=0; ee<=1; ee++ ){ - //for( unsigned i=0; igetModel()->getNumAssertedQuantifiers(); i++ ){ - // Node q = d_quantEngine->getModel()->getAssertedQuantifier( i ); - // if( doCbqi( q ) && d_quantEngine->getModel()->isQuantifierActive( q ) ){ - for( std::map< Node, bool >::iterator it = d_active_quant.begin(); it != d_active_quant.end(); ++it ){ - Node q = it->first; - Trace("cbqi") << "CBQI : Process quantifier " << q[0] << " at effort " << ee << std::endl; - if( d_nested_qe.find( q )==d_nested_qe.end() ){ - process( q, e, ee ); - if( d_quantEngine->inConflict() ){ - break; - } - }else{ - Trace("cbqi-warn") << "CBQI : Cannot process already eliminated quantified formula " << q << std::endl; - Assert( false ); - } - } - if( d_quantEngine->inConflict() || d_quantEngine->getNumLemmasWaiting()>lastWaiting ){ - break; - } - } - if( Trace.isOn("cbqi-engine") ){ - if( d_quantEngine->getNumLemmasWaiting()>lastWaiting ){ - Trace("cbqi-engine") << "Added lemmas = " << (d_quantEngine->getNumLemmasWaiting()-lastWaiting) << std::endl; - } - double clSet2 = double(clock())/double(CLOCKS_PER_SEC); - Trace("cbqi-engine") << "Finished cbqi engine, time = " << (clSet2-clSet) << std::endl; - } - } -} - -bool InstStrategyCbqi::checkComplete() { - if( ( !options::cbqiSat() && d_cbqi_set_quant_inactive ) || d_incomplete_check ){ - return false; - }else{ - return true; - } -} - -bool InstStrategyCbqi::checkCompleteFor( Node q ) { - std::map< Node, int >::iterator it = d_do_cbqi.find( q ); - if( it!=d_do_cbqi.end() ){ - return it->second>0; - }else{ - return false; - } -} - -Node InstStrategyCbqi::getIdMarkedQuantNode( Node n, std::map< Node, Node >& visited ){ - std::map< Node, Node >::iterator it = visited.find( n ); - if( it==visited.end() ){ - Node ret = n; - if( n.getKind()==FORALL ){ - QAttributes qa; - QuantAttributes::computeQuantAttributes( n, qa ); - if( qa.d_qid_num.isNull() ){ - std::vector< Node > rc; - rc.push_back( n[0] ); - rc.push_back( getIdMarkedQuantNode( n[1], visited ) ); - Node avar = NodeManager::currentNM()->mkSkolem( "id", NodeManager::currentNM()->booleanType() ); - QuantIdNumAttribute ida; - avar.setAttribute(ida,d_qid_count); - d_qid_count++; - std::vector< Node > iplc; - iplc.push_back( NodeManager::currentNM()->mkNode( INST_ATTRIBUTE, avar ) ); - if( n.getNumChildren()==3 ){ - for( unsigned i=0; imkNode( INST_PATTERN_LIST, iplc ) ); - ret = NodeManager::currentNM()->mkNode( FORALL, rc ); - } - }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; imkNode( n.getKind(), children ); - } - } - visited[n] = ret; - return ret; - }else{ - return it->second; - } -} - -void InstStrategyCbqi::preRegisterQuantifier( Node q ) { - if( d_quantEngine->getOwner( q )==NULL && doCbqi( q ) ){ - if( d_do_cbqi[q]==2 ){ - //take full ownership of the quantified formula - d_quantEngine->setOwner( q, this ); - - //mark all nested quantifiers with id - if( options::cbqiNestedQE() ){ - std::map< Node, Node > visited; - Node mq = getIdMarkedQuantNode( q[1], visited ); - if( mq!=q[1] ){ - //do not do cbqi - d_do_cbqi[q] = false; - //instead do reduction - std::vector< Node > qqc; - qqc.push_back( q[0] ); - qqc.push_back( mq ); - if( q.getNumChildren()==3 ){ - qqc.push_back( q[2] ); - } - Node qq = NodeManager::currentNM()->mkNode( FORALL, qqc ); - Node mlem = NodeManager::currentNM()->mkNode( IMPLIES, q, qq ); - Trace("cbqi-lemma") << "Mark quant id lemma : " << mlem << std::endl; - d_quantEngine->getOutputChannel().lemma( mlem ); - } - } - } - } -} - -void InstStrategyCbqi::registerQuantifier( Node q ) { - if( doCbqi( q ) ){ - if( registerCbqiLemma( q ) ){ - Trace("cbqi") << "Registered cbqi lemma for quantifier : " << q << std::endl; - } - } -} - -Node InstStrategyCbqi::doNestedQENode( Node q, Node ceq, Node n, std::vector< Node >& inst_terms, bool doVts ) { - // there is a nested quantified formula (forall y. nq[y,x]) such that - // q is (forall y. nq[y,t]) for ground terms t, - // ceq is (forall y. nq[y,e]) for CE variables e. - // we call this function when we know (forall y. nq[y,e]) is equivalent to quantifier-free formula C[e]. - // in this case, q is equivalent to the quantifier-free formula C[t]. - if( d_nested_qe.find( ceq )==d_nested_qe.end() ){ - d_nested_qe[ceq] = d_quantEngine->getInstantiatedConjunction( ceq ); - Trace("cbqi-nqe") << "CE quantifier elimination : " << std::endl; - Trace("cbqi-nqe") << " " << ceq << std::endl; - Trace("cbqi-nqe") << " " << d_nested_qe[ceq] << std::endl; - //should not contain quantifiers - Assert( !QuantifiersRewriter::containsQuantifiers( d_nested_qe[ceq] ) ); - } - Assert( d_quantEngine->getTermUtil()->d_inst_constants[q].size()==inst_terms.size() ); - //replace inst constants with instantiation - Node ret = d_nested_qe[ceq].substitute( d_quantEngine->getTermUtil()->d_inst_constants[q].begin(), - d_quantEngine->getTermUtil()->d_inst_constants[q].end(), - inst_terms.begin(), inst_terms.end() ); - if( doVts ){ - //do virtual term substitution - ret = Rewriter::rewrite( ret ); - ret = d_quantEngine->getTermUtil()->rewriteVtsSymbols( ret ); - } - Trace("cbqi-nqe") << "Nested quantifier elimination: " << std::endl; - Trace("cbqi-nqe") << " " << n << std::endl; - Trace("cbqi-nqe") << " " << ret << std::endl; - return ret; -} - -Node InstStrategyCbqi::doNestedQERec( Node q, Node n, std::map< Node, Node >& visited, std::vector< Node >& inst_terms, bool doVts ) { - if( visited.find( n )==visited.end() ){ - Node ret = n; - if( n.getKind()==FORALL ){ - QAttributes qa; - QuantAttributes::computeQuantAttributes( n, qa ); - if( !qa.d_qid_num.isNull() ){ - //if it has an id, check whether we have done quantifier elimination for this id - std::map< Node, Node >::iterator it = d_id_to_ce_quant.find( qa.d_qid_num ); - if( it!=d_id_to_ce_quant.end() ){ - Node ceq = it->second; - bool doNestedQe = d_elim_quants.contains( ceq ); - if( doNestedQe ){ - ret = doNestedQENode( q, ceq, n, inst_terms, doVts ); - }else{ - Trace("cbqi-nqe") << "Add to nested qe waitlist : " << std::endl; - Node nr = Rewriter::rewrite( n ); - Trace("cbqi-nqe") << " " << ceq << std::endl; - Trace("cbqi-nqe") << " " << nr << std::endl; - int wlsize = d_nested_qe_waitlist_size[ceq] + 1; - d_nested_qe_waitlist_size[ceq] = wlsize; - if( wlsize<(int)d_nested_qe_waitlist[ceq].size() ){ - d_nested_qe_waitlist[ceq][wlsize] = nr; - }else{ - d_nested_qe_waitlist[ceq].push_back( nr ); - } - d_nested_qe_info[nr].d_q = q; - d_nested_qe_info[nr].d_inst_terms.clear(); - d_nested_qe_info[nr].d_inst_terms.insert( d_nested_qe_info[nr].d_inst_terms.end(), inst_terms.begin(), inst_terms.end() ); - d_nested_qe_info[nr].d_doVts = doVts; - //TODO: ensure this holds by restricting prenex when cbqiNestedQe is true. - Assert( !options::cbqiInnermost() ); - } - } - } - }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; imkNode( n.getKind(), children ); - } - } - visited[n] = ret; - return ret; - }else{ - return n; - } -} - -Node InstStrategyCbqi::doNestedQE( Node q, std::vector< Node >& inst_terms, Node lem, bool doVts ) { - std::map< Node, Node > visited; - return doNestedQERec( q, lem, visited, inst_terms, doVts ); -} - -void InstStrategyCbqi::registerCounterexampleLemma( Node q, Node lem ){ - Trace("cbqi-debug") << "Counterexample lemma : " << lem << std::endl; - d_quantEngine->addLemma( lem, false ); -} - -bool InstStrategyCbqi::hasNonCbqiOperator( Node n, std::map< Node, bool >& visited ){ - if( visited.find( n )==visited.end() ){ - visited[n] = true; - if( n.getKind()!=BOUND_VARIABLE && TermUtil::hasBoundVarAttr( n ) ){ - if( !inst::Trigger::isCbqiKind( n.getKind() ) ){ - Trace("cbqi-debug2") << "Non-cbqi kind : " << n.getKind() << " in " << n << std::endl; - return true; - }else if( n.getKind()==MULT && ( n.getNumChildren()!=2 || !n[0].isConst() ) ){ - Trace("cbqi-debug2") << "Non-linear arithmetic : " << n << std::endl; - return true; - }else if( n.getKind()==FORALL ){ - return hasNonCbqiOperator( n[1], visited ); - }else{ - for( unsigned i=0; i& visited ) { - std::map< TypeNode, int >::iterator itv = visited.find( tn ); - if( itv==visited.end() ){ - visited[tn] = 0; - int ret = -1; - if( tn.isInteger() || tn.isReal() || tn.isBoolean() || tn.isBitVector() ){ - ret = 0; - }else if( tn.isDatatype() ){ - ret = 1; - const Datatype& dt = ((DatatypeType)tn.toType()).getDatatype(); - for( unsigned i=0; igetQuantEPR(); - if( qepr!=NULL ){ - ret = qepr->isEPR( tn ) ? 1 : -1; - } - } - visited[tn] = ret; - return ret; - }else{ - return itv->second; - } -} - -int InstStrategyCbqi::hasNonCbqiVariable( Node q ){ - int hmin = 1; - for( unsigned i=0; i visited; - int handled = isCbqiSort( tn, visited ); - if( handled==-1 ){ - return -1; - }else if( handled::iterator it = d_do_cbqi.find( q ); - if( it==d_do_cbqi.end() ){ - int ret = 2; - if( !d_quantEngine->getQuantAttributes()->isQuantElim( q ) ){ - Assert( !d_quantEngine->getQuantAttributes()->isQuantElimPartial( q ) ); - //if has an instantiation pattern, don't do it - if( q.getNumChildren()==3 && options::eMatching() && options::userPatternsQuant()!=USER_PAT_MODE_IGNORE ){ - for( unsigned i=0; igetQuantAttributes()->isSygus( q ) ){ - ret = 0; - } - if( ret!=0 ){ - //if quantifier has a non-handled variable, then do not use cbqi - //if quantifier has an APPLY_UF term, then do not use cbqi unless EPR - int ncbqiv = hasNonCbqiVariable( q ); - if( ncbqiv==0 || ncbqiv==1 ){ - std::map< Node, bool > visited; - if( hasNonCbqiOperator( q[1], visited ) ){ - if( ncbqiv==1 ){ - //all variables are fully handled, this implies this will be handlable regardless of body (e.g. for EPR) - // so, try but not exclusively - ret = 1; - }else{ - //cannot be handled - ret = 0; - } - } - }else{ - // unhandled variable type - ret = 0; - } - if( ret==0 && options::cbqiAll() ){ - //try but not exclusively - ret = 1; - } - } - } - Trace("cbqi-quant") << "doCbqi " << q << " returned " << ret << std::endl; - d_do_cbqi[q] = ret; - return ret!=0; - }else{ - return it->second!=0; - } -} - -Node InstStrategyCbqi::getNextDecisionRequestProc( Node q, std::map< Node, bool >& proc ) { - if( proc.find( q )==proc.end() ){ - proc[q] = true; - //first check children - std::map< Node, std::vector< Node > >::iterator itc = d_children_quant.find( q ); - if( itc!=d_children_quant.end() ){ - for( unsigned j=0; jsecond.size(); j++ ){ - Node d = getNextDecisionRequestProc( itc->second[j], proc ); - if( !d.isNull() ){ - return d; - } - } - } - //then check self - if( hasAddedCbqiLemma( q ) ){ - Node cel = d_quantEngine->getTermUtil()->getCounterexampleLiteral( q ); - bool value; - if( !d_quantEngine->getValuation().hasSatValue( cel, value ) ){ - Trace("cbqi-dec") << "CBQI: get next decision " << cel << std::endl; - return cel; - } - } - } - return Node::null(); -} - -Node InstStrategyCbqi::getNextDecisionRequest( unsigned& priority ){ - std::map< Node, bool > proc; - //for( unsigned i=0; igetModel()->getNumAssertedQuantifiers(); i++ ){ - // Node q = d_quantEngine->getModel()->getAssertedQuantifier( i ); - for( NodeSet::const_iterator it = d_added_cbqi_lemma.begin(); it != d_added_cbqi_lemma.end(); ++it ){ - Node q = *it; - Node d = getNextDecisionRequestProc( q, proc ); - if( !d.isNull() ){ - priority = 0; - return d; - } - } - return Node::null(); -} - - - -//new implementation - -bool CegqiOutputInstStrategy::doAddInstantiation( std::vector< Node >& subs ) { - return d_out->doAddInstantiation( subs ); -} - -bool CegqiOutputInstStrategy::isEligibleForInstantiation( Node n ) { - return d_out->isEligibleForInstantiation( n ); -} - -bool CegqiOutputInstStrategy::addLemma( Node lem ) { - return d_out->addLemma( lem ); -} - - -InstStrategyCegqi::InstStrategyCegqi( QuantifiersEngine * qe ) - : InstStrategyCbqi( qe ) { - d_out = new CegqiOutputInstStrategy( this ); - d_small_const = NodeManager::currentNM()->mkConst( Rational(1)/Rational(1000000) ); - d_check_vts_lemma_lc = false; -} - -InstStrategyCegqi::~InstStrategyCegqi() -{ - delete d_out; - - for(std::map< Node, CegInstantiator * >::iterator i = d_cinst.begin(), - iend = d_cinst.end(); i != iend; ++i) { - CegInstantiator * instantiator = (*i).second; - delete instantiator; - } - d_cinst.clear(); -} - -void InstStrategyCegqi::processResetInstantiationRound( Theory::Effort effort ) { - d_check_vts_lemma_lc = false; -} - -void InstStrategyCegqi::process( Node q, Theory::Effort effort, int e ) { - if( e==0 ){ - CegInstantiator * cinst = getInstantiator( q ); - Trace("inst-alg") << "-> Run cegqi for " << q << std::endl; - d_curr_quant = q; - if( !cinst->check() ){ - d_incomplete_check = true; - d_check_vts_lemma_lc = true; - } - d_curr_quant = Node::null(); - }else if( e==1 ){ - //minimize the free delta heuristically on demand - if( d_check_vts_lemma_lc ){ - Trace("inst-alg") << "-> Minimize delta heuristic, for " << q << std::endl; - d_check_vts_lemma_lc = false; - d_small_const = NodeManager::currentNM()->mkNode( MULT, d_small_const, d_small_const ); - d_small_const = Rewriter::rewrite( d_small_const ); - //heuristic for now, until we know how to do nested quantification - Node delta = d_quantEngine->getTermUtil()->getVtsDelta( true, false ); - if( !delta.isNull() ){ - Trace("quant-vts-debug") << "Delta lemma for " << d_small_const << std::endl; - Node delta_lem_ub = NodeManager::currentNM()->mkNode( LT, delta, d_small_const ); - d_quantEngine->getOutputChannel().lemma( delta_lem_ub ); - } - std::vector< Node > inf; - d_quantEngine->getTermUtil()->getVtsTerms( inf, true, false, false ); - for( unsigned i=0; imkNode( GT, inf[i], NodeManager::currentNM()->mkConst( Rational(1)/d_small_const.getConst() ) ); - d_quantEngine->getOutputChannel().lemma( inf_lem_lb ); - } - } - } -} - -bool InstStrategyCegqi::doAddInstantiation( std::vector< Node >& subs ) { - Assert( !d_curr_quant.isNull() ); - //if doing partial quantifier elimination, record the instantiation and set the incomplete flag instead of sending instantiation lemma - if( d_quantEngine->getQuantAttributes()->isQuantElimPartial( d_curr_quant ) ){ - d_cbqi_set_quant_inactive = true; - d_incomplete_check = true; - d_quantEngine->getInstantiate()->recordInstantiation( - d_curr_quant, subs, false, false); - return true; - }else{ - //check if we need virtual term substitution (if used delta or infinity) - bool used_vts = d_quantEngine->getTermUtil()->containsVtsTerm( subs, false ); - if (d_quantEngine->getInstantiate()->addInstantiation( - d_curr_quant, subs, false, false, used_vts)) - { - ++(d_quantEngine->d_statistics.d_instantiations_cbqi); - //d_added_inst.insert( d_curr_quant ); - return true; - }else{ - //this should never happen for monotonic selection strategies - Trace("cbqi-warn") << "WARNING: Existing instantiation" << std::endl; - return false; - } - } -} - -bool InstStrategyCegqi::addLemma( Node lem ) { - return d_quantEngine->addLemma( lem ); -} - -bool InstStrategyCegqi::isEligibleForInstantiation( Node n ) { - if( n.getKind()==INST_CONSTANT || n.getKind()==SKOLEM ){ - if( n.getAttribute(VirtualTermSkolemAttribute()) ){ - // virtual terms are allowed - return true; - }else{ - TypeNode tn = n.getType(); - if( tn.isSort() ){ - QuantEPR * qepr = d_quantEngine->getQuantEPR(); - if( qepr!=NULL ){ - //legal if in the finite set of constants of type tn - if( qepr->isEPRConstant( tn, n ) ){ - return true; - } - } - } - //only legal if current quantified formula contains n - return TermUtil::containsTerm( d_curr_quant, n ); - } - }else{ - return true; - } -} - -CegInstantiator * InstStrategyCegqi::getInstantiator( Node q ) { - std::map< Node, CegInstantiator * >::iterator it = d_cinst.find( q ); - if( it==d_cinst.end() ){ - CegInstantiator * cinst = new CegInstantiator( d_quantEngine, d_out, true, true ); - d_cinst[q] = cinst; - return cinst; - }else{ - return it->second; - } -} - -void InstStrategyCegqi::registerQuantifier( Node q ) { - if( doCbqi( q ) ){ - // get the instantiator - if( options::cbqiPreRegInst() ){ - getInstantiator( q ); - } - // register the cbqi lemma - if( registerCbqiLemma( q ) ){ - Trace("cbqi") << "Registered cbqi lemma for quantifier : " << q << std::endl; - } - } -} - -void InstStrategyCegqi::registerCounterexampleLemma( Node q, Node lem ) { - //must register with the instantiator - //must explicitly remove ITEs so that we record dependencies - std::vector< Node > ce_vars; - for( unsigned i=0; igetTermUtil()->getNumInstantiationConstants( q ); i++ ){ - ce_vars.push_back( d_quantEngine->getTermUtil()->getInstantiationConstant( q, i ) ); - } - std::vector< Node > lems; - lems.push_back( lem ); - CegInstantiator * cinst = getInstantiator( q ); - cinst->registerCounterexampleLemma( lems, ce_vars ); - for( unsigned i=0; iaddLemma( lems[i], false ); - } -} - -void InstStrategyCegqi::presolve() { - if( options::cbqiPreRegInst() ){ - for( std::map< Node, CegInstantiator * >::iterator it = d_cinst.begin(); it != d_cinst.end(); ++it ){ - Trace("cbqi-presolve") << "Presolve " << it->first << std::endl; - it->second->presolve( it->first ); - } - } -} - diff --git a/src/theory/quantifiers/inst_strategy_cbqi.h b/src/theory/quantifiers/inst_strategy_cbqi.h deleted file mode 100644 index 26591c678..000000000 --- a/src/theory/quantifiers/inst_strategy_cbqi.h +++ /dev/null @@ -1,166 +0,0 @@ -/********************* */ -/*! \file inst_strategy_cbqi.h - ** \verbatim - ** Top contributors (to current version): - ** Andrew Reynolds, Morgan Deters, 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 quantifier instantiation - **/ - - -#include "cvc4_private.h" - -#ifndef __CVC4__INST_STRATEGY_CBQI_H -#define __CVC4__INST_STRATEGY_CBQI_H - -#include "theory/arith/arithvar.h" -#include "theory/quantifiers/ceg_instantiator.h" -#include "theory/quantifiers/instantiation_engine.h" -#include "util/statistics_registry.h" - -namespace CVC4 { -namespace theory { - -namespace arith { - class TheoryArith; -} - -namespace quantifiers { - -class InstStrategyCbqi : public QuantifiersModule { - typedef context::CDHashSet NodeSet; - typedef context::CDHashMap< Node, int, NodeHashFunction> NodeIntMap; - - protected: - bool d_cbqi_set_quant_inactive; - bool d_incomplete_check; - /** whether we have added cbqi lemma */ - NodeSet d_added_cbqi_lemma; - /** whether we have added cbqi lemma */ - NodeSet d_elim_quants; - /** parent guards */ - std::map< Node, std::vector< Node > > d_parent_quant; - std::map< Node, std::vector< Node > > d_children_quant; - std::map< Node, bool > d_active_quant; - /** whether we have instantiated quantified formulas */ - //NodeSet d_added_inst; - /** whether to do cbqi for this quantified formula 0 : no, 2 : yes, 1 : yes but not exclusively, -1 : heuristically */ - std::map< Node, int > d_do_cbqi; - /** register ce lemma */ - bool registerCbqiLemma( Node q ); - virtual void registerCounterexampleLemma( Node q, Node lem ); - /** has added cbqi lemma */ - bool hasAddedCbqiLemma( Node q ) { return d_added_cbqi_lemma.find( q )!=d_added_cbqi_lemma.end(); } - /** helper functions */ - int hasNonCbqiVariable( Node q ); - bool hasNonCbqiOperator( Node n, std::map< Node, bool >& visited ); - int isCbqiSort( TypeNode tn, std::map< TypeNode, int >& visited ); - /** get next decision request with dependency checking */ - Node getNextDecisionRequestProc( Node q, std::map< Node, bool >& proc ); - /** process functions */ - virtual void processResetInstantiationRound( Theory::Effort effort ) = 0; - virtual void process( Node q, Theory::Effort effort, int e ) = 0; - - protected: - //for identification - uint64_t d_qid_count; - //nested qe map - std::map< Node, Node > d_nested_qe; - //mark ids on quantifiers - Node getIdMarkedQuantNode( Node n, std::map< Node, Node >& visited ); - // id to ce quant - std::map< Node, Node > d_id_to_ce_quant; - std::map< Node, Node > d_ce_quant_to_id; - //do nested quantifier elimination recursive - Node doNestedQENode( Node q, Node ceq, Node n, std::vector< Node >& inst_terms, bool doVts ); - Node doNestedQERec( Node q, Node n, std::map< Node, Node >& visited, std::vector< Node >& inst_terms, bool doVts ); - //elimination information (for delayed elimination) - class NestedQEInfo { - public: - NestedQEInfo() : d_doVts(false){} - ~NestedQEInfo(){} - Node d_q; - std::vector< Node > d_inst_terms; - bool d_doVts; - }; - std::map< Node, NestedQEInfo > d_nested_qe_info; - NodeIntMap d_nested_qe_waitlist_size; - NodeIntMap d_nested_qe_waitlist_proc; - std::map< Node, std::vector< Node > > d_nested_qe_waitlist; - - public: - //do nested quantifier elimination - Node doNestedQE( Node q, std::vector< Node >& inst_terms, Node lem, bool doVts ); - - public: - InstStrategyCbqi( QuantifiersEngine * qe ); - - /** whether to do CBQI for quantifier q */ - bool doCbqi( Node q ); - /** process functions */ - bool needsCheck( Theory::Effort e ); - QEffort needsModel(Theory::Effort e); - void reset_round( Theory::Effort e ); - void check(Theory::Effort e, QEffort quant_e); - bool checkComplete(); - bool checkCompleteFor( Node q ); - void preRegisterQuantifier( Node q ); - void registerQuantifier( Node q ); - /** get next decision request */ - Node getNextDecisionRequest( unsigned& priority ); -}; - -//generalized counterexample guided quantifier instantiation - -class InstStrategyCegqi; - -class CegqiOutputInstStrategy : public CegqiOutput { -public: - CegqiOutputInstStrategy( InstStrategyCegqi * out ) : d_out( out ){} - InstStrategyCegqi * d_out; - bool doAddInstantiation( std::vector< Node >& subs ); - bool isEligibleForInstantiation( Node n ); - bool addLemma( Node lem ); -}; - -class InstStrategyCegqi : public InstStrategyCbqi { - protected: - CegqiOutputInstStrategy * d_out; - std::map< Node, CegInstantiator * > d_cinst; - Node d_small_const; - Node d_curr_quant; - bool d_check_vts_lemma_lc; - /** process functions */ - void processResetInstantiationRound(Theory::Effort effort) override; - void process(Node f, Theory::Effort effort, int e) override; - /** register ce lemma */ - void registerCounterexampleLemma(Node q, Node lem) override; - - public: - InstStrategyCegqi( QuantifiersEngine * qe ); - ~InstStrategyCegqi() override; - - bool doAddInstantiation( std::vector< Node >& subs ); - bool isEligibleForInstantiation( Node n ); - bool addLemma( Node lem ); - /** identify */ - std::string identify() const override { return std::string("Cegqi"); } - - //get instantiator for quantifier - CegInstantiator * getInstantiator( Node q ); - //register quantifier - void registerQuantifier(Node q) override; - //presolve - void presolve() override; -}; - -} -} -} - -#endif diff --git a/src/theory/quantifiers/inst_strategy_e_matching.cpp b/src/theory/quantifiers/inst_strategy_e_matching.cpp deleted file mode 100644 index c39df58c6..000000000 --- a/src/theory/quantifiers/inst_strategy_e_matching.cpp +++ /dev/null @@ -1,613 +0,0 @@ -/********************* */ -/*! \file inst_strategy_e_matching.cpp - ** \verbatim - ** Top contributors (to current version): - ** Andrew Reynolds, Morgan Deters, 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 Implementation of e matching instantiation strategies - **/ - -#include "theory/quantifiers/inst_strategy_e_matching.h" -#include "theory/quantifiers/inst_match_generator.h" -#include "theory/quantifiers/quant_relevance.h" -#include "theory/quantifiers/quantifiers_attributes.h" -#include "theory/quantifiers/term_database.h" -#include "theory/quantifiers/term_util.h" -#include "theory/theory_engine.h" - -using namespace std; - -namespace CVC4 { - -using namespace kind; -using namespace context; - -namespace theory { - -using namespace inst; - -namespace quantifiers { - -//priority levels : -//1 : user patterns (when user-pat!={resort,ignore}), auto-gen patterns (for non-user pattern quantifiers, or when user-pat={resort,ignore}) -//2 : user patterns (when user-pat=resort), auto gen patterns (for user pattern quantifiers when user-pat=use) - -// user-pat=interleave alternates between use and resort - -struct sortQuantifiersForSymbol { - QuantifiersEngine* d_qe; - std::map< Node, Node > d_op_map; - bool operator() (Node i, Node j) { - int nqfsi = d_qe->getQuantifierRelevance()->getNumQuantifiersForSymbol( d_op_map[i] ); - int nqfsj = d_qe->getQuantifierRelevance()->getNumQuantifiersForSymbol( d_op_map[j] ); - if( nqfsinqfsj ){ - return false; - }else{ - return false; - } - } -}; - -struct sortTriggers { - bool operator() (Node i, Node j) { - int wi = Trigger::getTriggerWeight( i ); - int wj = Trigger::getTriggerWeight( j ); - if( wi==wj ){ - return i >::iterator it = d_user_gen.begin(); it != d_user_gen.end(); ++it ){ - for( unsigned i=0; isecond.size(); i++ ){ - it->second[i]->resetInstantiationRound(); - it->second[i]->reset( Node::null() ); - } - } - Trace("inst-alg-debug") << "done reset user triggers" << std::endl; -} - -int InstStrategyUserPatterns::process( Node f, Theory::Effort effort, int e ){ - if( e==0 ){ - return STATUS_UNFINISHED; - }else{ - int peffort = d_quantEngine->getInstUserPatMode()==USER_PAT_MODE_RESORT ? 2 : 1; - if( e User-provided instantiate " << f << "..." << std::endl; - if( d_quantEngine->getInstUserPatMode()==USER_PAT_MODE_RESORT ){ - for( unsigned i=0; idebugPrint("process-trigger"); - Trace("process-trigger") << "..." << std::endl; - int numInst = d_user_gen[f][i]->addInstantiations(); - Trace("process-trigger") << " Done, numInst = " << numInst << "." << std::endl; - d_quantEngine->d_statistics.d_instantiations_user_patterns += numInst; - if( d_user_gen[f][i]->isMultiTrigger() ){ - d_quantEngine->d_statistics.d_multi_trigger_instantiations += numInst; - } - if( d_quantEngine->inConflict() ){ - break; - } - } - } - } - } - return STATUS_UNKNOWN; -} - -void InstStrategyUserPatterns::addUserPattern( Node q, Node pat ){ - Assert( pat.getKind()==INST_PATTERN ); - //add to generators - bool usable = true; - std::vector< Node > nodes; - for( unsigned i=0; igetInstUserPatMode()==USER_PAT_MODE_RESORT ){ - d_user_gen_wait[q].push_back( nodes ); - }else{ - Trigger * t = Trigger::mkTrigger( d_quantEngine, q, nodes, true, Trigger::TR_MAKE_NEW ); - if( t ){ - d_user_gen[q].push_back( t ); - }else{ - Trace("trigger-warn") << "Failed to construct trigger : " << pat << " due to variable mismatch" << std::endl; - } - } - } -} - -InstStrategyAutoGenTriggers::InstStrategyAutoGenTriggers( QuantifiersEngine* qe ) : InstStrategy( qe ){ - //how to select trigger terms - d_tr_strategy = options::triggerSelMode(); - //whether to select new triggers during the search - if( options::incrementTriggers() ){ - d_regenerate_frequency = 3; - d_regenerate = true; - }else{ - d_regenerate_frequency = 1; - d_regenerate = false; - } -} - -void InstStrategyAutoGenTriggers::processResetInstantiationRound( Theory::Effort effort ){ - Trace("inst-alg-debug") << "reset auto-gen triggers" << std::endl; - //reset triggers - for( unsigned r=0; r<2; r++ ){ - for( std::map< Node, std::map< Trigger*, bool > >::iterator it = d_auto_gen_trigger[r].begin(); it != d_auto_gen_trigger[r].end(); ++it ){ - for( std::map< Trigger*, bool >::iterator itt = it->second.begin(); itt != it->second.end(); ++itt ){ - itt->first->resetInstantiationRound(); - itt->first->reset( Node::null() ); - } - } - } - d_processed_trigger.clear(); - Trace("inst-alg-debug") << "done reset auto-gen triggers" << std::endl; -} - -int InstStrategyAutoGenTriggers::process( Node f, Theory::Effort effort, int e ){ - UserPatMode upMode = d_quantEngine->getInstUserPatMode(); - if( hasUserPatterns( f ) && upMode==USER_PAT_MODE_TRUST ){ - return STATUS_UNKNOWN; - }else{ - int peffort = ( hasUserPatterns( f ) && upMode!=USER_PAT_MODE_IGNORE && upMode!=USER_PAT_MODE_RESORT ) ? 2 : 1; - if( e Auto-gen instantiate " << f << "..." << std::endl; - bool gen = false; - if( e==peffort ){ - if( d_counter.find( f )==d_counter.end() ){ - d_counter[f] = 0; - gen = true; - }else{ - d_counter[f]++; - gen = d_regenerate && d_counter[f]%d_regenerate_frequency==0; - } - }else{ - gen = true; - } - if( gen ){ - generateTriggers( f ); - if( d_counter[f]==0 && d_auto_gen_trigger[0][f].empty() && d_auto_gen_trigger[1][f].empty() && f.getNumChildren()==2 ){ - Trace("trigger-warn") << "Could not find trigger for " << f << std::endl; - } - } - - //if( e==4 ){ - // d_processed_trigger.clear(); - // d_quantEngine->getEqualityQuery()->setLiberal( true ); - //} - if( options::triggerActiveSelMode()!=TRIGGER_ACTIVE_SEL_ALL ){ - int max_score = -1; - Trigger * max_trigger = NULL; - for( std::map< Trigger*, bool >::iterator itt = d_auto_gen_trigger[0][f].begin(); itt != d_auto_gen_trigger[0][f].end(); ++itt ){ - int score = itt->first->getActiveScore(); - if( options::triggerActiveSelMode()==TRIGGER_ACTIVE_SEL_MIN ){ - if( score>=0 && ( scorefirst; - } - }else{ - if( score>max_score ){ - max_score = score; - max_trigger = itt->first; - } - } - d_auto_gen_trigger[0][f][itt->first] = false; - } - if( max_trigger!=NULL ){ - d_auto_gen_trigger[0][f][max_trigger] = true; - } - } - - bool hasInst = false; - for( unsigned r=0; r<2; r++ ){ - for( std::map< Trigger*, bool >::iterator itt = d_auto_gen_trigger[r][f].begin(); itt != d_auto_gen_trigger[r][f].end(); ++itt ){ - Trigger* tr = itt->first; - if( tr ){ - bool processTrigger = itt->second; - if( processTrigger && d_processed_trigger[f].find( tr )==d_processed_trigger[f].end() ){ - d_processed_trigger[f][tr] = true; - Trace("process-trigger") << " Process "; - tr->debugPrint("process-trigger"); - Trace("process-trigger") << "..." << std::endl; - int numInst = tr->addInstantiations(); - hasInst = numInst>0 || hasInst; - Trace("process-trigger") << " Done, numInst = " << numInst << "." << std::endl; - d_quantEngine->d_statistics.d_instantiations_auto_gen += numInst; - if( r==1 ){ - d_quantEngine->d_statistics.d_multi_trigger_instantiations += numInst; - } - if( d_quantEngine->inConflict() ){ - break; - } - } - } - } - if( d_quantEngine->inConflict() || ( hasInst && options::multiTriggerPriority() ) ){ - break; - } - } - //if( e==4 ){ - // d_quantEngine->getEqualityQuery()->setLiberal( false ); - //} - return STATUS_UNKNOWN; - } - } -} - -void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ - Trace("auto-gen-trigger-debug") << "Generate triggers for " << f << ", #var=" << f[0].getNumChildren() << "..." << std::endl; - if( d_patTerms[0].find( f )==d_patTerms[0].end() ){ - //determine all possible pattern terms based on trigger term selection strategy d_tr_strategy - d_patTerms[0][f].clear(); - d_patTerms[1][f].clear(); - bool ntrivTriggers = options::relationalTriggers(); - std::vector< Node > patTermsF; - std::map< Node, inst::TriggerTermInfo > tinfo; - //well-defined function: can assume LHS is only trigger - if( options::quantFunWellDefined() ){ - Node hd = QuantAttributes::getFunDefHead( f ); - if( !hd.isNull() ){ - hd = d_quantEngine->getTermUtil() - ->substituteBoundVariablesToInstConstants(hd, f); - patTermsF.push_back( hd ); - tinfo[hd].init( f, hd ); - } - } - //otherwise, use algorithm for collecting pattern terms - if( patTermsF.empty() ){ - Node bd = d_quantEngine->getTermUtil()->getInstConstantBody( f ); - Trigger::collectPatTerms( f, bd, patTermsF, d_tr_strategy, d_user_no_gen[f], tinfo, true ); - if( ntrivTriggers ){ - sortTriggers st; - std::sort( patTermsF.begin(), patTermsF.end(), st ); - } - if( Trace.isOn("auto-gen-trigger-debug") ){ - Trace("auto-gen-trigger-debug") << "Collected pat terms for " << bd << ", no-patterns : " << d_user_no_gen[f].size() << std::endl; - for( unsigned i=0; i vcMap; - std::map< Node, bool > rmPatTermsF; - int last_weight = -1; - for( unsigned i=0; ilast_weight ){ - Trace("auto-gen-trigger-debug") << "...exclude expendible non-trivial trigger : " << patTermsF[i] << std::endl; - rmPatTermsF[patTermsF[i]] = true; - }else{ - last_weight = curr_w; - } - } - d_num_trigger_vars[f] = vcMap.size(); - if( d_num_trigger_vars[f]>0 && d_num_trigger_vars[f] vcs[2]; - for( unsigned i=0; igetTermUtil()->getInstantiationConstant( f, i ); - vcs[ vcMap.find( ic )==vcMap.end() ? 0 : 1 ].push_back( f[0][i] ); - } - for( unsigned i=0; i<2; i++ ){ - d_vc_partition[i][f] = NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, vcs[i] ); - } - }else{ - return; - } - } - for( unsigned i=0; i patTerms; - for( int i=0; i<(int)d_patTerms[r][f].size(); i++ ){ - if( r==1 || d_single_trigger_gen.find( d_patTerms[r][f][i] )==d_single_trigger_gen.end() ){ - patTerms.push_back( d_patTerms[r][f][i] ); - } - } - if( !patTerms.empty() ){ - Trace("auto-gen-trigger") << "Generate trigger for " << f << std::endl; - //sort terms based on relevance - if( options::relevantTriggers() ){ - sortQuantifiersForSymbol sqfs; - sqfs.d_qe = d_quantEngine; - for( unsigned i=0; igetQuantifierRelevance()->getNumQuantifiersForSymbol( d_pat_to_mpat[patTerms[i]].getOperator() ) << ")" << std::endl; - } - } - //now, generate the trigger... - Trigger* tr = NULL; - if( d_is_single_trigger[ patTerms[0] ] ){ - tr = Trigger::mkTrigger( d_quantEngine, f, patTerms[0], false, Trigger::TR_RETURN_NULL, d_num_trigger_vars[f] ); - d_single_trigger_gen[ patTerms[0] ] = true; - }else{ - //only generate multi trigger if option set, or if no single triggers exist - if( !d_patTerms[0][f].empty() ){ - if( options::multiTriggerWhenSingle() ){ - Trace("multi-trigger-debug") << "Resort to choosing multi-triggers..." << std::endl; - }else{ - return; - } - } - //if we are re-generating triggers, shuffle based on some method - if( d_made_multi_trigger[f] ){ - std::random_shuffle( patTerms.begin(), patTerms.end() ); //shuffle randomly - }else{ - d_made_multi_trigger[f] = true; - } - //will possibly want to get an old trigger - tr = Trigger::mkTrigger( d_quantEngine, f, patTerms, false, Trigger::TR_GET_OLD, d_num_trigger_vars[f] ); - } - if( tr ){ - addTrigger( tr, f ); - //if we are generating additional triggers... - if( !tr->isMultiTrigger() ){ - unsigned index = 0; - if( indexgetQuantifierRelevance()->getNumQuantifiersForSymbol( patTerms[0].getOperator() ); - } - index++; - bool success = true; - while( success && indexgetQuantifierRelevance()->getNumQuantifiersForSymbol( patTerms[index].getOperator() )<=nqfs_curr ){ - d_single_trigger_gen[ patTerms[index] ] = true; - Trigger* tr2 = Trigger::mkTrigger( d_quantEngine, f, patTerms[index], false, Trigger::TR_RETURN_NULL, d_num_trigger_vars[f] ); - addTrigger( tr2, f ); - success = true; - } - index++; - } - //Notice() << "done check add additional" << std::endl; - } - } - } - } - } -} - -void InstStrategyAutoGenTriggers::addPatternToPool( Node q, Node pat, unsigned num_fv, Node mpat ) { - d_pat_to_mpat[pat] = mpat; - unsigned num_vars = options::partialTriggers() ? d_num_trigger_vars[q] : q[0].getNumChildren(); - if( num_fv==num_vars && ( options::pureThTriggers() || !Trigger::isPureTheoryTrigger( pat ) ) ){ - d_patTerms[0][q].push_back( pat ); - d_is_single_trigger[ pat ] = true; - }else{ - d_patTerms[1][q].push_back( pat ); - d_is_single_trigger[ pat ] = false; - } -} - - -void InstStrategyAutoGenTriggers::addTrigger( inst::Trigger * tr, Node q ) { - if( tr ){ - if( d_num_trigger_vars[q]getTermUtil()->substituteInstConstantsToBoundVariables( - tr->getInstPattern(), q); - Node ipl = NodeManager::currentNM()->mkNode(INST_PATTERN_LIST, pat); - Node qq = NodeManager::currentNM()->mkNode( FORALL, d_vc_partition[1][q], NodeManager::currentNM()->mkNode( FORALL, d_vc_partition[0][q], q[1] ), ipl ); - Trace("auto-gen-trigger-partial") << "Make partially specified user pattern: " << std::endl; - Trace("auto-gen-trigger-partial") << " " << qq << std::endl; - Node lem = NodeManager::currentNM()->mkNode( OR, q.negate(), qq ); - d_quantEngine->addLemma( lem ); - }else{ - unsigned tindex; - if( tr->isMultiTrigger() ){ - //disable all other multi triggers - for( std::map< Trigger*, bool >::iterator it = d_auto_gen_trigger[1][q].begin(); it != d_auto_gen_trigger[1][q].end(); ++it ){ - d_auto_gen_trigger[1][q][ it->first ] = false; - } - tindex = 1; - }else{ - tindex = 0; - } - //making it during an instantiation round, so must reset - if( d_auto_gen_trigger[tindex][q].find( tr )==d_auto_gen_trigger[tindex][q].end() ){ - tr->resetInstantiationRound(); - tr->reset( Node::null() ); - } - d_auto_gen_trigger[tindex][q][tr] = true; - } - } -} - -bool InstStrategyAutoGenTriggers::hasUserPatterns( Node q ) { - if( q.getNumChildren()==3 ){ - std::map< Node, bool >::iterator it = d_hasUserPatterns.find( q ); - if( it==d_hasUserPatterns.end() ){ - bool hasPat = false; - for( unsigned i=0; isecond; - } - }else{ - return false; - } -} - -void InstStrategyAutoGenTriggers::addUserNoPattern( Node q, Node pat ) { - Assert( pat.getKind()==INST_NO_PATTERN && pat.getNumChildren()==1 ); - if( std::find( d_user_no_gen[q].begin(), d_user_no_gen[q].end(), pat[0] )==d_user_no_gen[q].end() ){ - Trace("user-pat") << "Add user no-pattern: " << pat[0] << " for " << q << std::endl; - d_user_no_gen[q].push_back( pat[0] ); - } -} - -/* TODO? -bool InstStrategyLocalTheoryExt::isLocalTheoryExt( Node f ) { - std::map< Node, bool >::iterator itq = d_quant.find( f ); - if( itq==d_quant.end() ){ - //generate triggers - Node bd = d_quantEngine->getTermUtil()->getInstConstantBody( f ); - std::vector< Node > vars; - std::vector< Node > patTerms; - bool ret = Trigger::isLocalTheoryExt( bd, vars, patTerms ); - if( ret ){ - d_quant[f] = ret; - //add all variables to trigger that don't already occur - for( unsigned i=0; igetTermUtil()->getInstantiationConstant( f, i ); - if( std::find( vars.begin(), vars.end(), x )==vars.end() ){ - patTerms.push_back( x ); - } - } - Trace("local-t-ext") << "Local theory extensions trigger for " << f << " : " << std::endl; - for( unsigned i=0; isecond; - } -} -*/ - -} /* CVC4::theory::quantifiers namespace */ -} /* CVC4::theory namespace */ -} /* CVC4 namespace */ diff --git a/src/theory/quantifiers/inst_strategy_e_matching.h b/src/theory/quantifiers/inst_strategy_e_matching.h deleted file mode 100644 index 1a0ec9b44..000000000 --- a/src/theory/quantifiers/inst_strategy_e_matching.h +++ /dev/null @@ -1,119 +0,0 @@ -/********************* */ -/*! \file inst_strategy_e_matching.h - ** \verbatim - ** Top contributors (to current version): - ** Morgan Deters, 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 E matching instantiation strategies - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__INST_STRATEGY_E_MATCHING_H -#define __CVC4__INST_STRATEGY_E_MATCHING_H - -#include "context/context.h" -#include "context/context_mm.h" -#include "theory/quantifiers/instantiation_engine.h" -#include "theory/quantifiers/trigger.h" -#include "theory/quantifiers_engine.h" -#include "util/statistics_registry.h" -#include "options/quantifiers_options.h" - -namespace CVC4 { -namespace theory { -namespace quantifiers { - -//instantiation strategies - -class InstStrategyUserPatterns : public InstStrategy{ -private: - /** explicitly provided patterns */ - std::map< Node, std::vector< inst::Trigger* > > d_user_gen; - /** waiting to be generated patterns */ - std::map< Node, std::vector< std::vector< Node > > > d_user_gen_wait; - /** counter for quantifiers */ - std::map< Node, int > d_counter; - /** process functions */ - void processResetInstantiationRound( Theory::Effort effort ); - int process( Node f, Theory::Effort effort, int e ); -public: - InstStrategyUserPatterns( QuantifiersEngine* ie ) : - InstStrategy( ie ){} - ~InstStrategyUserPatterns(){} -public: - /** add pattern */ - void addUserPattern( Node q, Node pat ); - /** get num patterns */ - int getNumUserGenerators( Node q ) { return (int)d_user_gen[q].size(); } - /** get user pattern */ - inst::Trigger* getUserGenerator( Node q, int i ) { return d_user_gen[q][ i ]; } - /** identify */ - std::string identify() const { return std::string("UserPatterns"); } -};/* class InstStrategyUserPatterns */ - -class InstStrategyAutoGenTriggers : public InstStrategy { -public: - enum { - RELEVANCE_NONE, - RELEVANCE_DEFAULT, - }; -private: - /** trigger generation strategy */ - TriggerSelMode d_tr_strategy; - /** regeneration */ - bool d_regenerate; - int d_regenerate_frequency; - /** (single,multi) triggers for each quantifier */ - std::map< Node, std::map< inst::Trigger*, bool > > d_auto_gen_trigger[2]; - std::map< Node, int > d_counter; - /** single, multi triggers for each quantifier */ - std::map< Node, std::vector< Node > > d_patTerms[2]; - std::map< Node, std::map< Node, bool > > d_patReqPol; - /** information about triggers */ - std::map< Node, bool > d_is_single_trigger; - std::map< Node, bool > d_single_trigger_gen; - std::map< Node, bool > d_made_multi_trigger; - //processed trigger this round - std::map< Node, std::map< inst::Trigger*, bool > > d_processed_trigger; - //instantiation no patterns - std::map< Node, std::vector< Node > > d_user_no_gen; - // number of trigger variables per quantifier - std::map< Node, unsigned > d_num_trigger_vars; - std::map< Node, Node > d_vc_partition[2]; - std::map< Node, Node > d_pat_to_mpat; -private: - /** process functions */ - void processResetInstantiationRound( Theory::Effort effort ); - int process( Node q, Theory::Effort effort, int e ); - /** generate triggers */ - void generateTriggers( Node q ); - void addPatternToPool( Node q, Node pat, unsigned num_fv, Node mpat ); - void addTrigger( inst::Trigger * tr, Node f ); - /** has user patterns */ - bool hasUserPatterns( Node q ); - /** has user patterns */ - std::map< Node, bool > d_hasUserPatterns; -public: - InstStrategyAutoGenTriggers( QuantifiersEngine* qe ); - ~InstStrategyAutoGenTriggers(){} -public: - /** get auto-generated trigger */ - inst::Trigger* getAutoGenTrigger( Node q ); - /** identify */ - std::string identify() const { return std::string("AutoGenTriggers"); } - /** add pattern */ - void addUserNoPattern( Node q, Node pat ); -};/* class InstStrategyAutoGenTriggers */ - - -} -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ - -#endif diff --git a/src/theory/quantifiers/instantiate.cpp b/src/theory/quantifiers/instantiate.cpp index e04217b16..810ceee4f 100644 --- a/src/theory/quantifiers/instantiate.cpp +++ b/src/theory/quantifiers/instantiate.cpp @@ -17,7 +17,7 @@ #include "options/quantifiers_options.h" #include "smt/smt_statistics_registry.h" #include "theory/quantifiers/first_order_model.h" -#include "theory/quantifiers/inst_strategy_cbqi.h" +#include "theory/quantifiers/cegqi/inst_strategy_cbqi.h" #include "theory/quantifiers/quantifiers_attributes.h" #include "theory/quantifiers/quantifiers_rewriter.h" #include "theory/quantifiers/term_database.h" diff --git a/src/theory/quantifiers/instantiation_engine.cpp b/src/theory/quantifiers/instantiation_engine.cpp deleted file mode 100644 index 0c847b597..000000000 --- a/src/theory/quantifiers/instantiation_engine.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/********************* */ -/*! \file instantiation_engine.cpp - ** \verbatim - ** Top contributors (to current version): - ** Andrew Reynolds, Morgan Deters, 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 Implementation of instantiation engine class - **/ - -#include "theory/quantifiers/instantiation_engine.h" - -#include "options/quantifiers_options.h" -#include "theory/quantifiers/first_order_model.h" -#include "theory/quantifiers/inst_strategy_e_matching.h" -#include "theory/quantifiers/term_database.h" -#include "theory/quantifiers/term_util.h" -#include "theory/quantifiers/trigger.h" -#include "theory/theory_engine.h" - -using namespace std; -using namespace CVC4; -using namespace CVC4::kind; -using namespace CVC4::context; -using namespace CVC4::theory; -using namespace CVC4::theory::quantifiers; -using namespace CVC4::theory::inst; - -InstantiationEngine::InstantiationEngine(QuantifiersEngine* qe) - : QuantifiersModule(qe), - d_instStrategies(), - d_isup(), - d_i_ag(), - d_quants() { - if (options::eMatching()) { - // these are the instantiation strategies for E-matching - // user-provided patterns - if (options::userPatternsQuant() != USER_PAT_MODE_IGNORE) { - d_isup.reset(new InstStrategyUserPatterns(d_quantEngine)); - d_instStrategies.push_back(d_isup.get()); - } - - // auto-generated patterns - d_i_ag.reset(new InstStrategyAutoGenTriggers(d_quantEngine)); - d_instStrategies.push_back(d_i_ag.get()); - } -} - -InstantiationEngine::~InstantiationEngine() {} - -void InstantiationEngine::presolve() { - for( unsigned i=0; ipresolve(); - } -} - -void InstantiationEngine::doInstantiationRound( Theory::Effort effort ){ - unsigned lastWaiting = d_quantEngine->getNumLemmasWaiting(); - //iterate over an internal effort level e - int e = 0; - int eLimit = effort==Theory::EFFORT_LAST_CALL ? 10 : 2; - bool finished = false; - //while unfinished, try effort level=0,1,2.... - while( !finished && e<=eLimit ){ - Debug("inst-engine") << "IE: Prepare instantiation (" << e << ")." << std::endl; - finished = true; - //instantiate each quantifier - for( unsigned i=0; igetRelevance( q )==-1 ? e - 1 : e; - int e_use = e; - if( e_use>=0 ){ - Trace("inst-engine-debug") << "inst-engine : " << q << std::endl; - //check each instantiation strategy - for( unsigned j=0; jidentify() << " " << e_use << std::endl; - int quantStatus = is->process( q, effort, e_use ); - Trace("inst-engine-debug") << " -> status is " << quantStatus << ", conflict=" << d_quantEngine->inConflict() << std::endl; - if( d_quantEngine->inConflict() ){ - return; - }else if( quantStatus==InstStrategy::STATUS_UNFINISHED ){ - finished = false; - } - } - } - } - //do not consider another level if already added lemma at this level - if( d_quantEngine->getNumLemmasWaiting()>lastWaiting ){ - finished = true; - } - e++; - } -} - -bool InstantiationEngine::needsCheck( Theory::Effort e ){ - return d_quantEngine->getInstWhenNeedsCheck( e ); -} - -void InstantiationEngine::reset_round( Theory::Effort e ){ - //if not, proceed to instantiation round - //reset the instantiation strategies - for( unsigned i=0; iprocessResetInstantiationRound( e ); - } -} - -void InstantiationEngine::check(Theory::Effort e, QEffort quant_e) -{ - CodeTimer codeTimer(d_quantEngine->d_statistics.d_ematching_time); - if (quant_e == QEFFORT_STANDARD) - { - double clSet = 0; - if( Trace.isOn("inst-engine") ){ - clSet = double(clock())/double(CLOCKS_PER_SEC); - Trace("inst-engine") << "---Instantiation Engine Round, effort = " << e << "---" << std::endl; - } - //collect all active quantified formulas belonging to this - bool quantActive = false; - d_quants.clear(); - for( unsigned i=0; igetModel()->getNumAssertedQuantifiers(); i++ ){ - Node q = d_quantEngine->getModel()->getAssertedQuantifier( i, true ); - if( d_quantEngine->hasOwnership( q, this ) && d_quantEngine->getModel()->isQuantifierActive( q ) ){ - quantActive = true; - d_quants.push_back( q ); - } - } - Trace("inst-engine-debug") << "InstEngine: check: # asserted quantifiers " << d_quants.size() << "/"; - Trace("inst-engine-debug") << d_quantEngine->getModel()->getNumAssertedQuantifiers() << " " << quantActive << std::endl; - if( quantActive ){ - unsigned lastWaiting = d_quantEngine->getNumLemmasWaiting(); - doInstantiationRound( e ); - if( d_quantEngine->inConflict() ){ - Assert( d_quantEngine->getNumLemmasWaiting()>lastWaiting ); - Trace("inst-engine") << "Conflict, added lemmas = " << (d_quantEngine->getNumLemmasWaiting()-lastWaiting) << std::endl; - }else if( d_quantEngine->hasAddedLemma() ){ - Trace("inst-engine") << "Added lemmas = " << (d_quantEngine->getNumLemmasWaiting()-lastWaiting) << std::endl; - } - }else{ - d_quants.clear(); - } - if( Trace.isOn("inst-engine") ){ - double clSet2 = double(clock())/double(CLOCKS_PER_SEC); - Trace("inst-engine") << "Finished instantiation engine, time = " << (clSet2-clSet) << std::endl; - } - } -} - -bool InstantiationEngine::checkCompleteFor( Node q ) { - //TODO? - return false; -} - -void InstantiationEngine::preRegisterQuantifier( Node q ) { - if( options::strictTriggers() && q.getNumChildren()==3 ){ - //if strict triggers, take ownership of this quantified formula - bool hasPat = false; - for( unsigned i=0; isetOwner( q, this, 1 ); - } - } -} - -void InstantiationEngine::registerQuantifier( Node f ){ - if( d_quantEngine->hasOwnership( f, this ) ){ - //for( unsigned i=0; iregisterQuantifier( f ); - //} - //take into account user patterns - if( f.getNumChildren()==3 ){ - Node subsPat = - d_quantEngine->getTermUtil()->substituteBoundVariablesToInstConstants( - f[2], f); - //add patterns - for( int i=0; i<(int)subsPat.getNumChildren(); i++ ){ - //Notice() << "Add pattern " << subsPat[i] << " for " << f << std::endl; - if( subsPat[i].getKind()==INST_PATTERN ){ - addUserPattern( f, subsPat[i] ); - }else if( subsPat[i].getKind()==INST_NO_PATTERN ){ - addUserNoPattern( f, subsPat[i] ); - } - } - } - } -} - -void InstantiationEngine::addUserPattern(Node q, Node pat) { - if (d_isup) { - d_isup->addUserPattern(q, pat); - } -} - -void InstantiationEngine::addUserNoPattern(Node q, Node pat) { - if (d_i_ag) { - d_i_ag->addUserNoPattern(q, pat); - } -} diff --git a/src/theory/quantifiers/instantiation_engine.h b/src/theory/quantifiers/instantiation_engine.h deleted file mode 100644 index 18b5ea19c..000000000 --- a/src/theory/quantifiers/instantiation_engine.h +++ /dev/null @@ -1,98 +0,0 @@ -/********************* */ -/*! \file instantiation_engine.h - ** \verbatim - ** Top contributors (to current version): - ** Andrew Reynolds, Morgan Deters, 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 Instantiation Engine classes - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__THEORY__QUANTIFIERS__INSTANTIATION_ENGINE_H -#define __CVC4__THEORY__QUANTIFIERS__INSTANTIATION_ENGINE_H - -#include - -#include "theory/quantifiers_engine.h" -#include "theory/quantifiers/theory_quantifiers.h" - -namespace CVC4 { -namespace theory { -namespace quantifiers { - -class InstStrategyUserPatterns; -class InstStrategyAutoGenTriggers; -class InstStrategyFreeVariable; - -/** instantiation strategy class */ -class InstStrategy { -public: - enum Status { - STATUS_UNFINISHED, - STATUS_UNKNOWN, - };/* enum Status */ -protected: - /** reference to the instantiation engine */ - QuantifiersEngine* d_quantEngine; -public: - InstStrategy( QuantifiersEngine* qe ) : d_quantEngine( qe ){} - virtual ~InstStrategy(){} - /** presolve */ - virtual void presolve() {} - /** reset instantiation */ - virtual void processResetInstantiationRound( Theory::Effort effort ) = 0; - /** process method, returns a status */ - virtual int process( Node f, Theory::Effort effort, int e ) = 0; - /** identify */ - virtual std::string identify() const { return std::string("Unknown"); } - /** register quantifier */ - //virtual void registerQuantifier( Node q ) {} -};/* class InstStrategy */ - -class InstantiationEngine : public QuantifiersModule { - private: - /** instantiation strategies */ - std::vector d_instStrategies; - /** user-pattern instantiation strategy */ - std::unique_ptr d_isup; - /** auto gen triggers; only kept for destructor cleanup */ - std::unique_ptr d_i_ag; - - typedef context::CDHashMap BoolMap; - /** current processing quantified formulas */ - std::vector d_quants; - - /** is the engine incomplete for this quantifier */ - bool isIncomplete(Node q); - /** do instantiation round */ - void doInstantiationRound(Theory::Effort effort); - - public: - InstantiationEngine(QuantifiersEngine* qe); - ~InstantiationEngine(); - void presolve(); - bool needsCheck(Theory::Effort e); - void reset_round(Theory::Effort e); - void check(Theory::Effort e, QEffort quant_e); - bool checkCompleteFor(Node q); - void preRegisterQuantifier(Node q); - void registerQuantifier(Node q); - Node explain(TNode n) { return Node::null(); } - /** add user pattern */ - void addUserPattern(Node q, Node pat); - void addUserNoPattern(Node q, Node pat); - /** Identify this module */ - std::string identify() const { return "InstEngine"; } -}; /* class InstantiationEngine */ - -}/* CVC4::theory::quantifiers namespace */ -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ - -#endif /* __CVC4__THEORY__QUANTIFIERS__INSTANTIATION_ENGINE_H */ diff --git a/src/theory/quantifiers/macros.cpp b/src/theory/quantifiers/macros.cpp index b84499ee4..cafd6e579 100644 --- a/src/theory/quantifiers/macros.cpp +++ b/src/theory/quantifiers/macros.cpp @@ -25,7 +25,7 @@ #include "smt/smt_engine_scope.h" #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/term_util.h" -#include "theory/quantifiers/trigger.h" +#include "theory/quantifiers/ematching/trigger.h" #include "theory/rewriter.h" using namespace CVC4; diff --git a/src/theory/quantifiers/model_builder.cpp b/src/theory/quantifiers/model_builder.cpp deleted file mode 100644 index fbd122bd6..000000000 --- a/src/theory/quantifiers/model_builder.cpp +++ /dev/null @@ -1,808 +0,0 @@ -/********************* */ -/*! \file model_builder.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 Implementation of model builder class - **/ - -#include "theory/quantifiers/model_builder.h" - -#include "options/quantifiers_options.h" -#include "theory/quantifiers/first_order_model.h" -#include "theory/quantifiers/instantiate.h" -#include "theory/quantifiers/model_engine.h" -#include "theory/quantifiers/quantifiers_attributes.h" -#include "theory/quantifiers/term_database.h" -#include "theory/quantifiers/term_util.h" -#include "theory/quantifiers/trigger.h" -#include "theory/theory_engine.h" -#include "theory/uf/equality_engine.h" -#include "theory/uf/theory_uf.h" -#include "theory/uf/theory_uf_model.h" -#include "theory/uf/theory_uf_strong_solver.h" - -using namespace std; -using namespace CVC4; -using namespace CVC4::kind; -using namespace CVC4::context; -using namespace CVC4::theory; -using namespace CVC4::theory::quantifiers; - -QModelBuilder::QModelBuilder(context::Context* c, QuantifiersEngine* qe) - : TheoryEngineModelBuilder(qe->getTheoryEngine()), - d_qe(qe), - d_addedLemmas(0), - d_triedLemmas(0) {} - -bool QModelBuilder::optUseModel() { - return options::mbqiMode()!=MBQI_NONE || options::fmfBound(); -} - -bool QModelBuilder::preProcessBuildModel(TheoryModel* m) { - return preProcessBuildModelStd( m ); -} - -bool QModelBuilder::preProcessBuildModelStd(TheoryModel* m) { - d_addedLemmas = 0; - d_triedLemmas = 0; - if( options::fmfEmptySorts() || options::fmfFunWellDefinedRelevant() ){ - FirstOrderModel * fm = (FirstOrderModel*)m; - //traverse equality engine - std::map< TypeNode, bool > eqc_usort; - eq::EqClassesIterator eqcs_i = - eq::EqClassesIterator(fm->getEqualityEngine()); - while( !eqcs_i.isFinished() ){ - TypeNode tr = (*eqcs_i).getType(); - eqc_usort[tr] = true; - ++eqcs_i; - } - //look at quantified formulas - for( unsigned i=0; igetNumAssertedQuantifiers(); i++ ){ - Node q = fm->getAssertedQuantifier( i, true ); - if( fm->isQuantifierActive( q ) ){ - //check if any of these quantified formulas can be set inactive - if( options::fmfEmptySorts() ){ - for( unsigned i=0; isetQuantifierActive( q, false ); - } - } - }else if( options::fmfFunWellDefinedRelevant() ){ - if( q[0].getNumChildren()==1 ){ - TypeNode tn = q[0][0].getType(); - if( tn.getAttribute(AbsTypeFunDefAttribute()) ){ - //Trace("model-engine-debug2") << "...possible irrelevant function def : " << q << ", #rr = " << d_quantEngine->getModel()->d_rep_set.getNumRelevantGroundReps( tn ) << std::endl; - //we are allowed to assume the introduced type is empty - if( eqc_usort.find( tn )==eqc_usort.end() ){ - Trace("model-engine-debug") << "Irrelevant function definition : " << q << std::endl; - fm->setQuantifierActive( q, false ); - } - } - } - } - } - } - } - return true; -} - -void QModelBuilder::debugModel( TheoryModel* m ){ - //debug the model: cycle through all instantiations for all quantifiers, report ones that are not true - if( Trace.isOn("quant-check-model") ){ - FirstOrderModel* fm = (FirstOrderModel*)m; - Trace("quant-check-model") << "Testing quantifier instantiations..." << std::endl; - int tests = 0; - int bad = 0; - for( unsigned i=0; igetNumAssertedQuantifiers(); i++ ){ - Node f = fm->getAssertedQuantifier( i ); - std::vector< Node > vars; - for( unsigned j=0; jgetModel()->getRepSet(), &qrbe); - if( riter.setQuantifier( f ) ){ - while( !riter.isFinished() ){ - tests++; - std::vector< Node > terms; - for (unsigned k = 0; k < riter.getNumTerms(); k++) - { - terms.push_back( riter.getCurrentTerm( k ) ); - } - Node n = d_qe->getInstantiate()->getInstantiation(f, vars, terms); - Node val = fm->getValue( n ); - if (val != d_qe->getTermUtil()->d_true) - { - Trace("quant-check-model") << "******* Instantiation " << n << " for " << std::endl; - Trace("quant-check-model") << " " << f << std::endl; - Trace("quant-check-model") << " Evaluates to " << val << std::endl; - bad++; - } - riter.increment(); - } - Trace("quant-check-model") << "Tested " << tests << " instantiations"; - if( bad>0 ){ - Trace("quant-check-model") << ", " << bad << " failed" << std::endl; - } - Trace("quant-check-model") << "." << std::endl; - }else{ - if( riter.isIncomplete() ){ - Trace("quant-check-model") << "Warning: Could not test quantifier " << f << std::endl; - } - } - } - } -} - -bool TermArgBasisTrie::addTerm(FirstOrderModel* fm, Node n, unsigned argIndex) -{ - if (argIndex < n.getNumChildren()) - { - Node r; - if( n[ argIndex ].getAttribute(ModelBasisAttribute()) ){ - r = n[ argIndex ]; - }else{ - r = fm->getRepresentative( n[ argIndex ] ); - } - std::map< Node, TermArgBasisTrie >::iterator it = d_data.find( r ); - if( it==d_data.end() ){ - d_data[r].addTerm(fm, n, argIndex + 1); - return true; - }else{ - return it->second.addTerm(fm, n, argIndex + 1); - } - }else{ - return false; - } -} - -QModelBuilderIG::QModelBuilderIG(context::Context* c, QuantifiersEngine* qe) - : QModelBuilder(c, qe), - d_basisNoMatch(c), - d_didInstGen(false), - d_numQuantSat(0), - d_numQuantInstGen(0), - d_numQuantNoInstGen(0), - d_numQuantNoSelForm(0), - d_instGenMatches(0) {} - -/* -Node QModelBuilderIG::getCurrentUfModelValue( FirstOrderModel* fm, Node n, std::vector< Node > & args, bool partial ) { - return n; -} -*/ - -bool QModelBuilderIG::processBuildModel( TheoryModel* m ) { - FirstOrderModel* f = (FirstOrderModel*)m; - FirstOrderModelIG* fm = f->asFirstOrderModelIG(); - Trace("model-engine-debug") << "Process build model " << optUseModel() << std::endl; - d_didInstGen = false; - //reset the internal information - reset( fm ); - //only construct first order model if optUseModel() is true - if( optUseModel() ){ - Trace("model-engine-debug") << "Initializing " << fm->getNumAssertedQuantifiers() << " quantifiers..." << std::endl; - //check if any quantifiers are un-initialized - for( unsigned i=0; igetNumAssertedQuantifiers(); i++ ){ - Node q = fm->getAssertedQuantifier( i ); - if( d_qe->getModel()->isQuantifierActive( q ) ){ - int lems = initializeQuantifier(q, q, f); - d_statistics.d_init_inst_gen_lemmas += lems; - d_addedLemmas += lems; - if( d_qe->inConflict() ){ - break; - } - } - } - if( d_addedLemmas>0 ){ - Trace("model-engine") << "Initialize, Added Lemmas = " << d_addedLemmas << std::endl; - return false; - }else{ - Assert( !d_qe->inConflict() ); - //initialize model - fm->initialize(); - //analyze the functions - Trace("model-engine-debug") << "Analyzing model..." << std::endl; - analyzeModel( fm ); - //analyze the quantifiers - Trace("model-engine-debug") << "Analyzing quantifiers..." << std::endl; - d_uf_prefs.clear(); - for( unsigned i=0; igetNumAssertedQuantifiers(); i++ ){ - Node q = fm->getAssertedQuantifier( i ); - analyzeQuantifier( fm, q ); - } - - //if applicable, find exceptions to model via inst-gen - if( options::fmfInstGen() ){ - d_didInstGen = true; - d_instGenMatches = 0; - d_numQuantSat = 0; - d_numQuantInstGen = 0; - d_numQuantNoInstGen = 0; - d_numQuantNoSelForm = 0; - //now, see if we know that any exceptions via InstGen exist - Trace("model-engine-debug") << "Perform InstGen techniques for quantifiers..." << std::endl; - for( unsigned i=0; igetNumAssertedQuantifiers(); i++ ){ - Node f = fm->getAssertedQuantifier( i ); - if( d_qe->getModel()->isQuantifierActive( f ) ){ - int lems = doInstGen( fm, f ); - d_statistics.d_inst_gen_lemmas += lems; - d_addedLemmas += lems; - //temporary - if( lems>0 ){ - d_numQuantInstGen++; - }else if( hasInstGen( f ) ){ - d_numQuantNoInstGen++; - }else{ - d_numQuantNoSelForm++; - } - if( d_qe->inConflict() || ( options::fmfInstGenOneQuantPerRound() && lems>0 ) ){ - break; - } - }else{ - d_numQuantSat++; - } - } - Trace("model-engine-debug") << "Quantifiers sat/ig/n-ig/null " << d_numQuantSat << " / " << d_numQuantInstGen << " / "; - Trace("model-engine-debug") << d_numQuantNoInstGen << " / " << d_numQuantNoSelForm << std::endl; - Trace("model-engine-debug") << "Inst-gen # matches examined = " << d_instGenMatches << std::endl; - if( Trace.isOn("model-engine") ){ - if( d_addedLemmas>0 ){ - Trace("model-engine") << "InstGen, added lemmas = " << d_addedLemmas << std::endl; - }else{ - Trace("model-engine") << "No InstGen lemmas..." << std::endl; - } - } - } - //construct the model if necessary - if( d_addedLemmas==0 ){ - //if no immediate exceptions, build the model - // this model will be an approximation that will need to be tested via exhaustive instantiation - Trace("model-engine-debug") << "Building model..." << std::endl; - //build model for UF - for( std::map< Node, uf::UfModelTree >::iterator it = fm->d_uf_model_tree.begin(); it != fm->d_uf_model_tree.end(); ++it ){ - Trace("model-engine-debug-uf") << "Building model for " << it->first << "..." << std::endl; - constructModelUf( fm, it->first ); - } - Trace("model-engine-debug") << "Done building models." << std::endl; - }else{ - return false; - } - } - } - //update models - for( std::map< Node, uf::UfModelTree >::iterator it = fm->d_uf_model_tree.begin(); it != fm->d_uf_model_tree.end(); ++it ){ - it->second.update( fm ); - Trace("model-func") << "QModelBuilder: Make function value from tree " << it->first << std::endl; - //construct function values - Node f_def = it->second.getFunctionValue( "$x" ); - fm->assignFunctionDefinition( it->first, f_def ); - } - Assert( d_addedLemmas==0 ); - return TheoryEngineModelBuilder::processBuildModel( m ); -} - -int QModelBuilderIG::initializeQuantifier(Node f, Node fp, FirstOrderModel* fm) -{ - if( d_quant_basis_match_added.find( f )==d_quant_basis_match_added.end() ){ - //create the basis match if necessary - if( d_quant_basis_match.find( f )==d_quant_basis_match.end() ){ - Trace("inst-fmf-init") << "Initialize " << f << std::endl; - //add the model basis instantiation - // This will help produce the necessary information for model completion. - // We do this by extending distinguish ground assertions (those - // containing terms with "model basis" attribute) to hold for all cases. - - ////first, check if any variables are required to be equal - //for( std::map< Node, bool >::iterator it = d_quantEngine->d_phase_reqs[f].begin(); - // it != d_quantEngine->d_phase_reqs[f].end(); ++it ){ - // Node n = it->first; - // if( n.getKind()==EQUAL && n[0].getKind()==INST_CONSTANT && n[1].getKind()==INST_CONSTANT ){ - // Notice() << "Unhandled phase req: " << n << std::endl; - // } - //} - d_quant_basis_match[f] = InstMatch( f ); - for (unsigned j = 0; j < f[0].getNumChildren(); j++) - { - Node t = fm->getModelBasisTerm(f[0][j].getType()); - //calculate the basis match for f - d_quant_basis_match[f].setValue( j, t ); - } - ++(d_statistics.d_num_quants_init); - } - //try to add it - Trace("inst-fmf-init") << "Init: try to add match " << d_quant_basis_match[f] << std::endl; - //add model basis instantiation - if (d_qe->getInstantiate()->addInstantiation(fp, d_quant_basis_match[f])) - { - d_quant_basis_match_added[f] = true; - return 1; - }else{ - //shouldn't happen usually, but will occur if x != y is a required literal for f. - //Notice() << "No model basis for " << f << std::endl; - d_quant_basis_match_added[f] = false; - } - } - return 0; -} - -void QModelBuilderIG::analyzeModel( FirstOrderModel* fm ){ - FirstOrderModelIG* fmig = fm->asFirstOrderModelIG(); - d_uf_model_constructed.clear(); - //determine if any functions are constant - for( std::map< Node, uf::UfModelTree >::iterator it = fmig->d_uf_model_tree.begin(); it != fmig->d_uf_model_tree.end(); ++it ){ - Node op = it->first; - TermArgBasisTrie tabt; - std::map< Node, std::vector< Node > >::iterator itut = fmig->d_uf_terms.find( op ); - if( itut!=fmig->d_uf_terms.end() ){ - for( size_t i=0; isecond.size(); i++ ){ - Node n = fmig->d_uf_terms[op][i]; - //for calculating if op is constant - Node v = fmig->getRepresentative( n ); - if( i==0 ){ - d_uf_prefs[op].d_const_val = v; - }else if( v!=d_uf_prefs[op].d_const_val ){ - d_uf_prefs[op].d_const_val = Node::null(); - break; - } - //for calculating terms that we don't need to consider - //if( d_qe->getTermDatabase()->isTermActive( n ) || n.getAttribute(ModelBasisArgAttribute())!=0 ){ - if( d_basisNoMatch.find( n )==d_basisNoMatch.end() ){ - //need to consider if it is not congruent modulo model basis - if( !tabt.addTerm( fmig, n ) ){ - d_basisNoMatch[n] = true; - } - } - } - } - if( !d_uf_prefs[op].d_const_val.isNull() ){ - fmig->d_uf_model_gen[op].setDefaultValue( d_uf_prefs[op].d_const_val ); - fmig->d_uf_model_gen[op].makeModel( fmig, it->second ); - Debug("fmf-model-cons") << "Function " << op << " is the constant function "; - fmig->printRepresentativeDebug( "fmf-model-cons", d_uf_prefs[op].d_const_val ); - Debug("fmf-model-cons") << std::endl; - d_uf_model_constructed[op] = true; - }else{ - d_uf_model_constructed[op] = false; - } - } -} - -bool QModelBuilderIG::hasConstantDefinition( Node n ){ - Node lit = n.getKind()==NOT ? n[0] : n; - if( lit.getKind()==APPLY_UF ){ - Node op = lit.getOperator(); - if( !d_uf_prefs[op].d_const_val.isNull() ){ - return true; - } - } - return false; -} - -QModelBuilderIG::Statistics::Statistics(): - d_num_quants_init("QModelBuilderIG::Number_Quantifiers", 0), - d_num_partial_quants_init("QModelBuilderIG::Number_Partial_Quantifiers", 0), - d_init_inst_gen_lemmas("QModelBuilderIG::Initialize_Inst_Gen_Lemmas", 0 ), - d_inst_gen_lemmas("QModelBuilderIG::Inst_Gen_Lemmas", 0 ), - d_eval_formulas("QModelBuilderIG::Eval_Formulas", 0 ), - d_eval_uf_terms("QModelBuilderIG::Eval_Uf_Terms", 0 ), - d_eval_lits("QModelBuilderIG::Eval_Lits", 0 ), - d_eval_lits_unknown("QModelBuilderIG::Eval_Lits_Unknown", 0 ) -{ - smtStatisticsRegistry()->registerStat(&d_num_quants_init); - smtStatisticsRegistry()->registerStat(&d_num_partial_quants_init); - smtStatisticsRegistry()->registerStat(&d_init_inst_gen_lemmas); - smtStatisticsRegistry()->registerStat(&d_inst_gen_lemmas); - smtStatisticsRegistry()->registerStat(&d_eval_formulas); - smtStatisticsRegistry()->registerStat(&d_eval_uf_terms); - smtStatisticsRegistry()->registerStat(&d_eval_lits); - smtStatisticsRegistry()->registerStat(&d_eval_lits_unknown); -} - -QModelBuilderIG::Statistics::~Statistics(){ - smtStatisticsRegistry()->unregisterStat(&d_num_quants_init); - smtStatisticsRegistry()->unregisterStat(&d_num_partial_quants_init); - smtStatisticsRegistry()->unregisterStat(&d_init_inst_gen_lemmas); - smtStatisticsRegistry()->unregisterStat(&d_inst_gen_lemmas); - smtStatisticsRegistry()->unregisterStat(&d_eval_formulas); - smtStatisticsRegistry()->unregisterStat(&d_eval_uf_terms); - smtStatisticsRegistry()->unregisterStat(&d_eval_lits); - smtStatisticsRegistry()->unregisterStat(&d_eval_lits_unknown); -} - -//do exhaustive instantiation -int QModelBuilderIG::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ) { - if( optUseModel() ){ - QRepBoundExt qrbe(d_qe); - RepSetIterator riter(d_qe->getModel()->getRepSet(), &qrbe); - if( riter.setQuantifier( f ) ){ - FirstOrderModelIG * fmig = (FirstOrderModelIG*)d_qe->getModel(); - Debug("inst-fmf-ei") << "Reset evaluate..." << std::endl; - fmig->resetEvaluate(); - Debug("inst-fmf-ei") << "Begin instantiation..." << std::endl; - EqualityQuery* qy = d_qe->getEqualityQuery(); - Instantiate* inst = d_qe->getInstantiate(); - TermUtil* util = d_qe->getTermUtil(); - while( !riter.isFinished() && ( d_addedLemmas==0 || !options::fmfOneInstPerRound() ) ){ - d_triedLemmas++; - if( Debug.isOn("inst-fmf-ei-debug") ){ - for( int i=0; i<(int)riter.d_index.size(); i++ ){ - Debug("inst-fmf-ei-debug") << i << " : " << riter.d_index[i] << " : " << riter.getCurrentTerm( i ) << std::endl; - } - } - int eval = 0; - int depIndex; - //see if instantiation is already true in current model - if( Debug.isOn("fmf-model-eval") ){ - Debug("fmf-model-eval") << "Evaluating "; - riter.debugPrintSmall("fmf-model-eval"); - Debug("fmf-model-eval") << "Done calculating terms." << std::endl; - } - //if evaluate(...)==1, then the instantiation is already true in the model - // depIndex is the index of the least significant variable that this evaluation relies upon - depIndex = riter.getNumTerms()-1; - Debug("fmf-model-eval") << "We will evaluate " - << util->getInstConstantBody(f) << std::endl; - eval = fmig->evaluate(util->getInstConstantBody(f), depIndex, &riter); - if( eval==1 ){ - Debug("fmf-model-eval") << " Returned success with depIndex = " << depIndex << std::endl; - }else{ - Debug("fmf-model-eval") << " Returned " << (eval==-1 ? "failure" : "unknown") << ", depIndex = " << depIndex << std::endl; - } - if( eval==1 ){ - //instantiation is already true -> skip - riter.incrementAtIndex(depIndex); - }else{ - //instantiation was not shown to be true, construct the match - InstMatch m( f ); - for (unsigned i = 0; i < riter.getNumTerms(); i++) - { - m.set(qy, i, riter.getCurrentTerm(i)); - } - Debug("fmf-model-eval") << "* Add instantiation " << m << std::endl; - //add as instantiation - if (inst->addInstantiation(f, m, true)) - { - d_addedLemmas++; - if( d_qe->inConflict() ){ - break; - } - //if the instantiation is show to be false, and we wish to skip multiple instantiations at once - if( eval==-1 ){ - riter.incrementAtIndex(depIndex); - }else{ - riter.increment(); - } - }else{ - Debug("fmf-model-eval") << "* Failed Add instantiation " << m << std::endl; - riter.increment(); - } - } - } - //print debugging information - if( fmig ){ - d_statistics.d_eval_formulas += fmig->d_eval_formulas; - d_statistics.d_eval_uf_terms += fmig->d_eval_uf_terms; - d_statistics.d_eval_lits += fmig->d_eval_lits; - d_statistics.d_eval_lits_unknown += fmig->d_eval_lits_unknown; - } - Trace("inst-fmf-ei") << "For " << f << ", finished: " << std::endl; - Trace("inst-fmf-ei") << " Inst Tried: " << d_triedLemmas << std::endl; - Trace("inst-fmf-ei") << " Inst Added: " << d_addedLemmas << std::endl; - if( d_addedLemmas>1000 ){ - Trace("model-engine-warn") << "WARNING: many instantiations produced for " << f << ": " << std::endl; - Trace("model-engine-warn") << " Inst Tried: " << d_triedLemmas << std::endl; - Trace("model-engine-warn") << " Inst Added: " << d_addedLemmas << std::endl; - Trace("model-engine-warn") << std::endl; - } - } - //if the iterator is incomplete, we will return unknown instead of sat if no instantiations are added this round - return riter.isIncomplete() ? -1 : 1; - }else{ - return 0; - } -} - - - -void QModelBuilderDefault::reset( FirstOrderModel* fm ){ - d_quant_selection_lit.clear(); - d_quant_selection_lit_candidates.clear(); - d_quant_selection_lit_terms.clear(); - d_term_selection_lit.clear(); - d_op_selection_terms.clear(); -} - - -int QModelBuilderDefault::getSelectionScore( std::vector< Node >& uf_terms ) { - /* - size_t maxChildren = 0; - for( size_t i=0; imaxChildren ){ - maxChildren = uf_terms[i].getNumChildren(); - } - } - //TODO: look at how many entries they have? - return (int)maxChildren; - */ - return 0; -} - -void QModelBuilderDefault::analyzeQuantifier( FirstOrderModel* fm, Node f ){ - if( d_qe->getModel()->isQuantifierActive( f ) ){ - FirstOrderModelIG* fmig = fm->asFirstOrderModelIG(); - Debug("fmf-model-prefs") << "Analyze quantifier " << f << std::endl; - //the pro/con preferences for this quantifier - std::vector< Node > pro_con[2]; - //the terms in the selection literal we choose - std::vector< Node > selectionLitTerms; - Trace("inst-gen-debug-quant") << "Inst-gen analyze " << f << std::endl; - //for each asserted quantifier f, - // - determine selection literals - // - check which function/predicates have good and bad definitions for satisfying f - if( d_phase_reqs.find( f )==d_phase_reqs.end() ){ - d_phase_reqs[f].initialize( d_qe->getTermUtil()->getInstConstantBody( f ), true ); - } - int selectLitScore = -1; - for( std::map< Node, bool >::iterator it = d_phase_reqs[f].d_phase_reqs.begin(); it != d_phase_reqs[f].d_phase_reqs.end(); ++it ){ - //the literal n is phase-required for quantifier f - Node n = it->first; - Node gn = fm->getModelBasis(f, n); - Debug("fmf-model-req") << " Req: " << n << " -> " << it->second << std::endl; - bool value; - //if the corresponding ground abstraction literal has a SAT value - if( d_qe->getValuation().hasSatValue( gn, value ) ){ - //collect the non-ground uf terms that this literal contains - // and compute if all of the symbols in this literal have - // constant definitions. - bool isConst = true; - std::vector< Node > uf_terms; - if( TermUtil::hasInstConstAttr(n) ){ - isConst = false; - if( gn.getKind()==APPLY_UF ){ - uf_terms.push_back( gn ); - isConst = hasConstantDefinition( gn ); - }else if( gn.getKind()==EQUAL ){ - isConst = true; - for( int j=0; j<2; j++ ){ - if( TermUtil::hasInstConstAttr(n[j]) ){ - if( n[j].getKind()==APPLY_UF && - fmig->d_uf_model_tree.find( gn[j].getOperator() )!=fmig->d_uf_model_tree.end() ){ - uf_terms.push_back( gn[j] ); - isConst = isConst && hasConstantDefinition( gn[j] ); - }else{ - isConst = false; - } - } - } - } - } - //check if the value in the SAT solver matches the preference according to the quantifier - int pref = 0; - if( value!=it->second ){ - //we have a possible selection literal - bool selectLit = d_quant_selection_lit[f].isNull(); - bool selectLitConstraints = true; - //it is a constantly defined selection literal : the quantifier is sat - if( isConst ){ - selectLit = selectLit || d_qe->getModel()->isQuantifierActive( f ); - d_qe->getModel()->setQuantifierActive( f, false ); - //check if choosing this literal would add any additional constraints to default definitions - selectLitConstraints = false; - for( int j=0; j<(int)uf_terms.size(); j++ ){ - Node op = uf_terms[j].getOperator(); - if( d_uf_prefs[op].d_reconsiderModel ){ - selectLitConstraints = true; - } - } - if( !selectLitConstraints ){ - selectLit = true; - } - } - //also check if it is naturally a better literal - if( !selectLit ){ - int score = getSelectionScore( uf_terms ); - //Trace("inst-gen-debug") << "Check " << score << " < " << selectLitScore << std::endl; - selectLit = scoregetModel()->isQuantifierActive( f ) ){ - Debug("fmf-model-prefs") << " It is " << ( pref==1 ? "pro" : "con" ); - Debug("fmf-model-prefs") << " the definition of " << n << std::endl; - for( int j=0; j<(int)uf_terms.size(); j++ ){ - pro_con[ pref==1 ? 0 : 1 ].push_back( uf_terms[j] ); - } - } - } - } - //process information about selection literal for f - if( !d_quant_selection_lit[f].isNull() ){ - d_quant_selection_lit_terms[f].insert( d_quant_selection_lit_terms[f].begin(), selectionLitTerms.begin(), selectionLitTerms.end() ); - for( int i=0; i<(int)selectionLitTerms.size(); i++ ){ - d_term_selection_lit[ selectionLitTerms[i] ] = d_quant_selection_lit[f]; - d_op_selection_terms[ selectionLitTerms[i].getOperator() ].push_back( selectionLitTerms[i] ); - } - }else{ - Trace("inst-gen-warn") << "WARNING: " << f << " has no selection literals" << std::endl; - } - //process information about requirements and preferences of quantifier f - if( !d_qe->getModel()->isQuantifierActive( f ) ){ - Debug("fmf-model-prefs") << " * Constant SAT due to definition of ops: "; - for( int i=0; i<(int)selectionLitTerms.size(); i++ ){ - Debug("fmf-model-prefs") << selectionLitTerms[i] << " "; - d_uf_prefs[ selectionLitTerms[i].getOperator() ].d_reconsiderModel = false; - } - Debug("fmf-model-prefs") << std::endl; - }else{ - //note quantifier's value preferences to models - for( int k=0; k<2; k++ ){ - for( int j=0; j<(int)pro_con[k].size(); j++ ){ - Node op = pro_con[k][j].getOperator(); - Node r = fmig->getRepresentative( pro_con[k][j] ); - d_uf_prefs[op].setValuePreference( f, pro_con[k][j], r, k==0 ); - } - } - } - } -} - -int QModelBuilderDefault::doInstGen( FirstOrderModel* fm, Node f ){ - int addedLemmas = 0; - //we wish to add all known exceptions to our selection literal for f. this will help to refine our current model. - //This step is advantageous over exhaustive instantiation, since we are adding instantiations that involve model basis terms, - // effectively acting as partial instantiations instead of pointwise instantiations. - if( !d_quant_selection_lit[f].isNull() ){ - Trace("inst-gen") << "Do Inst-Gen for " << f << std::endl; - for( size_t i=0; i tr_terms; - if( lit.getKind()==APPLY_UF ){ - //only match predicates that are contrary to this one, use literal matching - Node eq = NodeManager::currentNM()->mkNode( - EQUAL, lit, NodeManager::currentNM()->mkConst(!phase)); - tr_terms.push_back( eq ); - }else if( lit.getKind()==EQUAL ){ - //collect trigger terms - for( int j=0; j<2; j++ ){ - if( TermUtil::hasInstConstAttr(lit[j]) ){ - if( lit[j].getKind()==APPLY_UF ){ - tr_terms.push_back( lit[j] ); - }else{ - tr_terms.clear(); - break; - } - } - } - if( tr_terms.size()==1 && !phase ){ - //equality between a function and a ground term, use literal matching - tr_terms.clear(); - tr_terms.push_back( lit ); - } - } - //if applicable, try to add exceptions here - if( !tr_terms.empty() ){ - //make a trigger for these terms, add instantiations - inst::Trigger* tr = inst::Trigger::mkTrigger( d_qe, f, tr_terms, true, inst::Trigger::TR_MAKE_NEW ); - //Notice() << "Trigger = " << (*tr) << std::endl; - tr->resetInstantiationRound(); - tr->reset( Node::null() ); - //d_qe->d_optInstMakeRepresentative = false; - //d_qe->d_optMatchIgnoreModelBasis = true; - addedLemmas += tr->addInstantiations(); - } - } - } - return addedLemmas; -} - -void QModelBuilderDefault::constructModelUf( FirstOrderModel* fm, Node op ){ - FirstOrderModelIG* fmig = fm->asFirstOrderModelIG(); - if( optReconsiderFuncConstants() ){ - //reconsider constant functions that weren't necessary - if( d_uf_model_constructed[op] ){ - if( d_uf_prefs[op].d_reconsiderModel ){ - //if we are allowed to reconsider default value, then see if the default value can be improved - Node v = d_uf_prefs[op].d_const_val; - if( d_uf_prefs[op].d_value_pro_con[0][v].empty() ){ - Debug("fmf-model-cons-debug") << "Consider changing the default value for " << op << std::endl; - fmig->d_uf_model_tree[op].clear(); - fmig->d_uf_model_gen[op].clear(); - d_uf_model_constructed[op] = false; - } - } - } - } - if( !d_uf_model_constructed[op] ){ - //construct the model for the uninterpretted function/predicate - bool setDefaultVal = true; - Node defaultTerm = fmig->getModelBasisOpTerm(op); - Trace("fmf-model-cons") << "Construct model for " << op << "..." << std::endl; - //set the values in the model - std::map< Node, std::vector< Node > >::iterator itut = fmig->d_uf_terms.find( op ); - if( itut!=fmig->d_uf_terms.end() ){ - for( size_t i=0; isecond.size(); i++ ){ - Node n = itut->second[i]; - // only consider unique up to congruence (in model equality engine)? - Node v = fmig->getRepresentative( n ); - Trace("fmf-model-cons") << "Set term " << n << " : " - << fmig->getRepSet()->getIndexFor(v) << " " << v - << std::endl; - //if this assertion did not help the model, just consider it ground - //set n = v in the model tree - //set it as ground value - fmig->d_uf_model_gen[op].setValue( fm, n, v ); - if( fmig->d_uf_model_gen[op].optUsePartialDefaults() ){ - //also set as default value if necessary - if( n.hasAttribute(ModelBasisArgAttribute()) && n.getAttribute(ModelBasisArgAttribute())!=0 ){ - Trace("fmf-model-cons") << " Set as default." << std::endl; - fmig->d_uf_model_gen[op].setValue( fm, n, v, false ); - if( n==defaultTerm ){ - //incidentally already set, we will not need to find a default value - setDefaultVal = false; - } - } - }else{ - if( n==defaultTerm ){ - fmig->d_uf_model_gen[op].setValue( fm, n, v, false ); - //incidentally already set, we will not need to find a default value - setDefaultVal = false; - } - } - } - } - //set the overall default value if not set already (is this necessary??) - if( setDefaultVal ){ - Trace("fmf-model-cons") << " Choose default value..." << std::endl; - //chose defaultVal based on heuristic, currently the best ratio of "pro" responses - Node defaultVal = d_uf_prefs[op].getBestDefaultValue( defaultTerm, fm ); - if( defaultVal.isNull() ){ - if (!fmig->getRepSet()->hasType(defaultTerm.getType())) - { - Node mbt = fmig->getModelBasisTerm(defaultTerm.getType()); - fmig->getRepSetPtr()->d_type_reps[defaultTerm.getType()].push_back( - mbt); - } - defaultVal = - fmig->getRepSet()->getRepresentative(defaultTerm.getType(), 0); - } - Assert( !defaultVal.isNull() ); - Trace("fmf-model-cons") - << "Set default term : " << fmig->getRepSet()->getIndexFor(defaultVal) - << std::endl; - fmig->d_uf_model_gen[op].setValue( fm, defaultTerm, defaultVal, false ); - } - Debug("fmf-model-cons") << " Making model..."; - fmig->d_uf_model_gen[op].makeModel( fm, fmig->d_uf_model_tree[op] ); - d_uf_model_constructed[op] = true; - Debug("fmf-model-cons") << " Finished constructing model for " << op << "." << std::endl; - } -} diff --git a/src/theory/quantifiers/model_builder.h b/src/theory/quantifiers/model_builder.h deleted file mode 100644 index 4eb592b3e..000000000 --- a/src/theory/quantifiers/model_builder.h +++ /dev/null @@ -1,202 +0,0 @@ -/********************* */ -/*! \file model_builder.h - ** \verbatim - ** Top contributors (to current version): - ** Andrew Reynolds, Morgan Deters, 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 Model Builder class - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__THEORY__QUANTIFIERS__MODEL_BUILDER_H -#define __CVC4__THEORY__QUANTIFIERS__MODEL_BUILDER_H - -#include "theory/quantifiers_engine.h" -#include "theory/theory_model_builder.h" -#include "theory/uf/theory_uf_model.h" - -namespace CVC4 { -namespace theory { -namespace quantifiers { - - -class QModelBuilder : public TheoryEngineModelBuilder -{ -protected: - //quantifiers engine - QuantifiersEngine* d_qe; - bool preProcessBuildModel(TheoryModel* m); //must call preProcessBuildModelStd - bool preProcessBuildModelStd(TheoryModel* m); - /** number of lemmas generated while building model */ - unsigned d_addedLemmas; - unsigned d_triedLemmas; -public: - QModelBuilder( context::Context* c, QuantifiersEngine* qe ); - - //do exhaustive instantiation - // 0 : failed, but resorting to true exhaustive instantiation may work - // >0 : success - // <0 : failed - virtual int doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ) { return false; } - //whether to construct model - virtual bool optUseModel(); - /** exist instantiation ? */ - virtual bool existsInstantiation( Node f, InstMatch& m, bool modEq = true, bool modInst = false ) { return false; } - //debug model - virtual void debugModel( TheoryModel* m ); - //statistics - unsigned getNumAddedLemmas() { return d_addedLemmas; } - unsigned getNumTriedLemmas() { return d_triedLemmas; } -}; - - - - - -class TermArgBasisTrie { -public: - /** the data */ - std::map< Node, TermArgBasisTrie > d_data; - /** add term to the trie */ - bool addTerm(FirstOrderModel* fm, Node n, unsigned argIndex = 0); -};/* class TermArgBasisTrie */ - -/** model builder class - * This class is capable of building candidate models based on the current quantified formulas - * that are asserted. Use: - * (1) call QModelBuilder::buildModel( m, false );, where m is a FirstOrderModel - * (2) if candidate model is determined to be a real model, - then call QModelBuilder::buildModel( m, true ); - */ -class QModelBuilderIG : public QModelBuilder -{ - typedef context::CDHashMap BoolMap; - - protected: - BoolMap d_basisNoMatch; - //map from operators to model preference data - std::map< Node, uf::UfModelPreferenceData > d_uf_prefs; - //built model uf - std::map< Node, bool > d_uf_model_constructed; - //whether inst gen was done - bool d_didInstGen; - /** process build model */ - virtual bool processBuildModel( TheoryModel* m ); - - protected: - //reset - virtual void reset( FirstOrderModel* fm ) = 0; - //initialize quantifiers, return number of lemmas produced - virtual int initializeQuantifier(Node f, Node fp, FirstOrderModel* fm); - //analyze model - virtual void analyzeModel( FirstOrderModel* fm ); - //analyze quantifiers - virtual void analyzeQuantifier( FirstOrderModel* fm, Node f ) = 0; - //do InstGen techniques for quantifier, return number of lemmas produced - virtual int doInstGen( FirstOrderModel* fm, Node f ) = 0; - //theory-specific build models - virtual void constructModelUf( FirstOrderModel* fm, Node op ) = 0; - - protected: - //map from quantifiers to if are SAT - //std::map< Node, bool > d_quant_sat; - //which quantifiers have been initialized - std::map< Node, bool > d_quant_basis_match_added; - //map from quantifiers to model basis match - std::map< Node, InstMatch > d_quant_basis_match; - - protected: // helper functions - /** term has constant definition */ - bool hasConstantDefinition( Node n ); - - public: - QModelBuilderIG( context::Context* c, QuantifiersEngine* qe ); - - public: - /** statistics class */ - class Statistics { - public: - IntStat d_num_quants_init; - IntStat d_num_partial_quants_init; - IntStat d_init_inst_gen_lemmas; - IntStat d_inst_gen_lemmas; - IntStat d_eval_formulas; - IntStat d_eval_uf_terms; - IntStat d_eval_lits; - IntStat d_eval_lits_unknown; - Statistics(); - ~Statistics(); - }; - Statistics d_statistics; - // is term selected - virtual bool isTermSelected( Node n ) { return false; } - /** quantifier has inst-gen definition */ - virtual bool hasInstGen( Node f ) = 0; - /** did inst gen this round? */ - bool didInstGen() { return d_didInstGen; } - // is quantifier active? - bool isQuantifierActive( Node f ); - //do exhaustive instantiation - int doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ); - - //temporary stats - int d_numQuantSat; - int d_numQuantInstGen; - int d_numQuantNoInstGen; - int d_numQuantNoSelForm; - //temporary stat - int d_instGenMatches; -};/* class QModelBuilder */ - - -class QModelBuilderDefault : public QModelBuilderIG -{ - private: /// information for (old) InstGen - // map from quantifiers to their selection literals - std::map< Node, Node > d_quant_selection_lit; - std::map< Node, std::vector< Node > > d_quant_selection_lit_candidates; - //map from quantifiers to their selection literal terms - std::map< Node, std::vector< Node > > d_quant_selection_lit_terms; - //map from terms to the selection literals they exist in - std::map< Node, Node > d_term_selection_lit; - //map from operators to terms that appear in selection literals - std::map< Node, std::vector< Node > > d_op_selection_terms; - //get selection score - int getSelectionScore( std::vector< Node >& uf_terms ); - - protected: - //reset - void reset(FirstOrderModel* fm) override; - //analyze quantifier - void analyzeQuantifier(FirstOrderModel* fm, Node f) override; - //do InstGen techniques for quantifier, return number of lemmas produced - int doInstGen(FirstOrderModel* fm, Node f) override; - //theory-specific build models - void constructModelUf(FirstOrderModel* fm, Node op) override; - - protected: - std::map< Node, QuantPhaseReq > d_phase_reqs; - - public: - QModelBuilderDefault( context::Context* c, QuantifiersEngine* qe ) : QModelBuilderIG( c, qe ){} - - //options - bool optReconsiderFuncConstants() { return true; } - //has inst gen - bool hasInstGen(Node f) override - { - return !d_quant_selection_lit[f].isNull(); - } -}; - -}/* CVC4::theory::quantifiers namespace */ -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ - -#endif /* __CVC4__THEORY__QUANTIFIERS__MODEL_BUILDER_H */ diff --git a/src/theory/quantifiers/model_engine.cpp b/src/theory/quantifiers/model_engine.cpp deleted file mode 100644 index fe6e3945b..000000000 --- a/src/theory/quantifiers/model_engine.cpp +++ /dev/null @@ -1,342 +0,0 @@ -/********************* */ -/*! \file model_engine.cpp - ** \verbatim - ** Top contributors (to current version): - ** Andrew Reynolds, Morgan Deters, 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 Implementation of model engine class - **/ - -#include "theory/quantifiers/model_engine.h" - -#include "options/quantifiers_options.h" -#include "theory/quantifiers/ambqi_builder.h" -#include "theory/quantifiers/first_order_model.h" -#include "theory/quantifiers/full_model_check.h" -#include "theory/quantifiers/instantiate.h" -#include "theory/quantifiers/quantifiers_attributes.h" -#include "theory/quantifiers/term_database.h" -#include "theory/quantifiers/term_util.h" -#include "theory/theory_engine.h" -#include "theory/uf/equality_engine.h" -#include "theory/uf/theory_uf.h" -#include "theory/uf/theory_uf_strong_solver.h" - -using namespace std; -using namespace CVC4; -using namespace CVC4::kind; -using namespace CVC4::context; -using namespace CVC4::theory; -using namespace CVC4::theory::quantifiers; -using namespace CVC4::theory::inst; - -//Model Engine constructor -ModelEngine::ModelEngine( context::Context* c, QuantifiersEngine* qe ) : -QuantifiersModule( qe ), -d_incomplete_check(true), -d_addedLemmas(0), -d_triedLemmas(0), -d_totalLemmas(0) -{ - -} - -ModelEngine::~ModelEngine() { - -} - -bool ModelEngine::needsCheck( Theory::Effort e ) { - return e==Theory::EFFORT_LAST_CALL; -} - -QuantifiersModule::QEffort ModelEngine::needsModel(Theory::Effort e) -{ - if( options::mbqiInterleave() ){ - return QEFFORT_STANDARD; - }else{ - return QEFFORT_MODEL; - } -} - -void ModelEngine::reset_round( Theory::Effort e ) { - d_incomplete_check = true; -} -void ModelEngine::check(Theory::Effort e, QEffort quant_e) -{ - bool doCheck = false; - if( options::mbqiInterleave() ){ - doCheck = quant_e == QEFFORT_STANDARD && d_quantEngine->hasAddedLemma(); - } - if( !doCheck ){ - doCheck = quant_e == QEFFORT_MODEL; - } - if( doCheck ){ - Assert( !d_quantEngine->inConflict() ); - int addedLemmas = 0; - FirstOrderModel* fm = d_quantEngine->getModel(); - - //the following will test that the model satisfies all asserted universal quantifiers by - // (model-based) exhaustive instantiation. - double clSet = 0; - if( Trace.isOn("model-engine") ){ - Trace("model-engine") << "---Model Engine Round---" << std::endl; - clSet = double(clock())/double(CLOCKS_PER_SEC); - } - - Trace("model-engine-debug") << "Verify uf ss is minimal..." << std::endl; - //let the strong solver verify that the model is minimal - //for debugging, this will if there are terms in the model that the strong solver was not notified of - uf::StrongSolverTheoryUF * ufss = ((uf::TheoryUF*)d_quantEngine->getTheoryEngine()->theoryOf( THEORY_UF ))->getStrongSolver(); - if( !ufss || ufss->debugModel( fm ) ){ - Trace("model-engine-debug") << "Check model..." << std::endl; - d_incomplete_check = false; - //print debug - if( Trace.isOn("fmf-model-complete") ){ - Trace("fmf-model-complete") << std::endl; - debugPrint("fmf-model-complete"); - } - //successfully built an acceptable model, now check it - addedLemmas += checkModel(); - }else{ - addedLemmas++; - } - - if( Trace.isOn("model-engine") ){ - double clSet2 = double(clock())/double(CLOCKS_PER_SEC); - Trace("model-engine") << "Finished model engine, time = " << (clSet2-clSet) << std::endl; - } - - if( addedLemmas==0 ){ - Trace("model-engine-debug") << "No lemmas added, incomplete = " << ( d_incomplete_check || !d_incomplete_quants.empty() ) << std::endl; - //CVC4 will answer SAT or unknown - if( Trace.isOn("fmf-consistent") ){ - Trace("fmf-consistent") << std::endl; - debugPrint("fmf-consistent"); - } - } - } -} - -bool ModelEngine::checkComplete() { - return !d_incomplete_check; -} - -bool ModelEngine::checkCompleteFor( Node q ) { - return std::find( d_incomplete_quants.begin(), d_incomplete_quants.end(), q )==d_incomplete_quants.end(); -} - -void ModelEngine::registerQuantifier( Node f ){ - if( Trace.isOn("fmf-warn") ){ - bool canHandle = true; - for( unsigned i=0; igetModel(); - - //flatten the representatives - //Trace("model-engine-debug") << "Flattening representatives...." << std::endl; - // d_quantEngine->getEqualityQuery()->flattenRepresentatives( - // fm->getRepSet()->d_type_reps ); - - //for debugging, setup - for (std::map >::iterator it = - fm->getRepSetPtr()->d_type_reps.begin(); - it != fm->getRepSetPtr()->d_type_reps.end(); - ++it) - { - if( it->first.isSort() ){ - Trace("model-engine") << "Cardinality( " << it->first << " )" << " = " << it->second.size() << std::endl; - Trace("model-engine-debug") << " Reps : "; - for( size_t i=0; isecond.size(); i++ ){ - Trace("model-engine-debug") << it->second[i] << " "; - } - Trace("model-engine-debug") << std::endl; - Trace("model-engine-debug") << " Term reps : "; - for( size_t i=0; isecond.size(); i++ ){ - Node r = d_quantEngine->getInternalRepresentative( it->second[i], Node::null(), 0 ); - Trace("model-engine-debug") << r << " "; - } - Trace("model-engine-debug") << std::endl; - Node mbt = fm->getModelBasisTerm(it->first); - Trace("model-engine-debug") << " Basis term : " << mbt << std::endl; - } - } - - d_triedLemmas = 0; - d_addedLemmas = 0; - d_totalLemmas = 0; - //for statistics - if( Trace.isOn("model-engine") ){ - for( unsigned i=0; igetNumAssertedQuantifiers(); i++ ){ - Node f = fm->getAssertedQuantifier( i ); - if( d_quantEngine->getModel()->isQuantifierActive( f ) && d_quantEngine->hasOwnership( f, this ) ){ - int totalInst = 1; - for( unsigned j=0; jgetRepSet()->hasType(tn)) - { - totalInst = - totalInst * (int)fm->getRepSet()->getNumRepresentatives(tn); - } - } - d_totalLemmas += totalInst; - } - } - } - - Trace("model-engine-debug") << "Do exhaustive instantiation..." << std::endl; - // FMC uses two sub-effort levels - int e_max = options::mbqiMode()==MBQI_FMC || options::mbqiMode()==MBQI_FMC_INTERVAL ? 2 : ( options::mbqiMode()==MBQI_TRUST ? 0 : 1 ); - for( int e=0; egetNumAssertedQuantifiers(); i++ ){ - Node q = fm->getAssertedQuantifier( i, true ); - Trace("fmf-exh-inst") << "-> Exhaustive instantiate " << q << ", effort = " << e << "..." << std::endl; - //determine if we should check this quantifier - if( d_quantEngine->getModel()->isQuantifierActive( q ) && d_quantEngine->hasOwnership( q, this ) ){ - exhaustiveInstantiate( q, e ); - if( d_quantEngine->inConflict() || ( optOneQuantPerRound() && d_addedLemmas>0 ) ){ - break; - } - }else{ - Trace("fmf-exh-inst") << "-> Inactive : " << q << std::endl; - } - } - if( d_addedLemmas>0 ){ - break; - }else{ - Assert( !d_quantEngine->inConflict() ); - } - } - - //print debug information - if( d_quantEngine->inConflict() ){ - Trace("model-engine") << "Conflict, added lemmas = "; - }else{ - Trace("model-engine") << "Added Lemmas = "; - } - Trace("model-engine") << d_addedLemmas << " / " << d_triedLemmas << " / "; - Trace("model-engine") << d_totalLemmas << std::endl; - return d_addedLemmas; -} - - - -void ModelEngine::exhaustiveInstantiate( Node f, int effort ){ - //first check if the builder can do the exhaustive instantiation - quantifiers::QModelBuilder * mb = d_quantEngine->getModelBuilder(); - unsigned prev_alem = mb->getNumAddedLemmas(); - unsigned prev_tlem = mb->getNumTriedLemmas(); - int retEi = mb->doExhaustiveInstantiation( d_quantEngine->getModel(), f, effort ); - if( retEi!=0 ){ - if( retEi<0 ){ - Trace("fmf-exh-inst") << "-> Builder determined complete instantiation was impossible." << std::endl; - d_incomplete_quants.push_back( f ); - }else{ - Trace("fmf-exh-inst") << "-> Builder determined instantiation(s)." << std::endl; - } - d_triedLemmas += mb->getNumTriedLemmas()-prev_tlem; - d_addedLemmas += mb->getNumAddedLemmas()-prev_alem; - d_quantEngine->d_statistics.d_instantiations_fmf_mbqi += mb->getNumAddedLemmas(); - }else{ - if( Trace.isOn("fmf-exh-inst-debug") ){ - Trace("fmf-exh-inst-debug") << " Instantiation Constants: "; - for( size_t i=0; igetTermUtil()->getInstantiationConstant( f, i ) << " "; - } - Trace("fmf-exh-inst-debug") << std::endl; - } - //create a rep set iterator and iterate over the (relevant) domain of the quantifier - QRepBoundExt qrbe(d_quantEngine); - RepSetIterator riter(d_quantEngine->getModel()->getRepSet(), &qrbe); - if( riter.setQuantifier( f ) ){ - Trace("fmf-exh-inst") << "...exhaustive instantiation set, incomplete=" << riter.isIncomplete() << "..." << std::endl; - if( !riter.isIncomplete() ){ - int triedLemmas = 0; - int addedLemmas = 0; - EqualityQuery* qy = d_quantEngine->getEqualityQuery(); - Instantiate* inst = d_quantEngine->getInstantiate(); - while( !riter.isFinished() && ( addedLemmas==0 || !options::fmfOneInstPerRound() ) ){ - //instantiation was not shown to be true, construct the match - InstMatch m( f ); - for (unsigned i = 0; i < riter.getNumTerms(); i++) - { - m.set(qy, i, riter.getCurrentTerm(i)); - } - Debug("fmf-model-eval") << "* Add instantiation " << m << std::endl; - triedLemmas++; - //add as instantiation - if (inst->addInstantiation(f, m, true)) - { - addedLemmas++; - if( d_quantEngine->inConflict() ){ - break; - } - }else{ - Debug("fmf-model-eval") << "* Failed Add instantiation " << m << std::endl; - } - riter.increment(); - } - d_addedLemmas += addedLemmas; - d_triedLemmas += triedLemmas; - d_quantEngine->d_statistics.d_instantiations_fmf_exh += addedLemmas; - } - }else{ - Trace("fmf-exh-inst") << "...exhaustive instantiation did set, incomplete=" << riter.isIncomplete() << "..." << std::endl; - } - //if the iterator is incomplete, we will return unknown instead of sat if no instantiations are added this round - if( riter.isIncomplete() ){ - d_incomplete_quants.push_back( f ); - } - } -} - -void ModelEngine::debugPrint( const char* c ){ - Trace( c ) << "Quantifiers: " << std::endl; - for( unsigned i=0; igetModel()->getNumAssertedQuantifiers(); i++ ){ - Node q = d_quantEngine->getModel()->getAssertedQuantifier( i ); - if( d_quantEngine->hasOwnership( q, this ) ){ - Trace( c ) << " "; - if( !d_quantEngine->getModel()->isQuantifierActive( q ) ){ - Trace( c ) << "*Inactive* "; - }else{ - Trace( c ) << " "; - } - Trace( c ) << q << std::endl; - } - } - //d_quantEngine->getModel()->debugPrint( c ); -} - diff --git a/src/theory/quantifiers/model_engine.h b/src/theory/quantifiers/model_engine.h deleted file mode 100644 index 840ff4242..000000000 --- a/src/theory/quantifiers/model_engine.h +++ /dev/null @@ -1,70 +0,0 @@ -/********************* */ -/*! \file model_engine.h - ** \verbatim - ** Top contributors (to current version): - ** Morgan Deters, 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 Model Engine class - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__THEORY__QUANTIFIERS__MODEL_ENGINE_H -#define __CVC4__THEORY__QUANTIFIERS__MODEL_ENGINE_H - -#include "theory/quantifiers_engine.h" -#include "theory/quantifiers/model_builder.h" -#include "theory/theory_model.h" - -namespace CVC4 { -namespace theory { -namespace quantifiers { - -class ModelEngine : public QuantifiersModule -{ - friend class RepSetIterator; -private: - //options - bool optOneQuantPerRound(); -private: - //check model - int checkModel(); - //exhaustively instantiate quantifier (possibly using mbqi) - void exhaustiveInstantiate( Node f, int effort = 0 ); -private: - //temporary statistics - //is the exhaustive instantiation incomplete? - bool d_incomplete_check; - // set of quantified formulas for which check was incomplete - std::vector< Node > d_incomplete_quants; - int d_addedLemmas; - int d_triedLemmas; - int d_totalLemmas; -public: - ModelEngine( context::Context* c, QuantifiersEngine* qe ); - virtual ~ModelEngine(); -public: - bool needsCheck( Theory::Effort e ); - QEffort needsModel(Theory::Effort e); - void reset_round( Theory::Effort e ); - void check(Theory::Effort e, QEffort quant_e); - bool checkComplete(); - bool checkCompleteFor( Node q ); - void registerQuantifier( Node f ); - void assertNode( Node f ); - Node explain(TNode n){ return Node::null(); } - void debugPrint( const char* c ); - /** Identify this module */ - std::string identify() const { return "ModelEngine"; } -};/* class ModelEngine */ - -}/* CVC4::theory::quantifiers namespace */ -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ - -#endif /* __CVC4__THEORY__QUANTIFIERS__MODEL_ENGINE_H */ diff --git a/src/theory/quantifiers/quant_conflict_find.cpp b/src/theory/quantifiers/quant_conflict_find.cpp index 23e2ad721..efc2f3064 100644 --- a/src/theory/quantifiers/quant_conflict_find.cpp +++ b/src/theory/quantifiers/quant_conflict_find.cpp @@ -24,7 +24,7 @@ #include "theory/quantifiers/quant_util.h" #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/term_util.h" -#include "theory/quantifiers/trigger.h" +#include "theory/quantifiers/ematching/trigger.h" #include "theory/theory_engine.h" using namespace CVC4::kind; diff --git a/src/theory/quantifiers/quantifiers_attributes.cpp b/src/theory/quantifiers/quantifiers_attributes.cpp index 92d42d578..d80a7cf82 100644 --- a/src/theory/quantifiers/quantifiers_attributes.cpp +++ b/src/theory/quantifiers/quantifiers_attributes.cpp @@ -16,7 +16,7 @@ #include "theory/quantifiers_engine.h" #include "options/quantifiers_options.h" -#include "theory/quantifiers/ce_guided_instantiation.h" +#include "theory/quantifiers/sygus/ce_guided_instantiation.h" #include "theory/quantifiers/fun_def_engine.h" #include "theory/quantifiers/rewrite_engine.h" #include "theory/quantifiers/term_util.h" diff --git a/src/theory/quantifiers/quantifiers_rewriter.cpp b/src/theory/quantifiers/quantifiers_rewriter.cpp index 472316cae..5586c04fb 100644 --- a/src/theory/quantifiers/quantifiers_rewriter.cpp +++ b/src/theory/quantifiers/quantifiers_rewriter.cpp @@ -21,7 +21,7 @@ #include "theory/quantifiers/skolemize.h" #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/term_util.h" -#include "theory/quantifiers/trigger.h" +#include "theory/quantifiers/ematching/trigger.h" using namespace std; using namespace CVC4::kind; diff --git a/src/theory/quantifiers/rewrite_engine.cpp b/src/theory/quantifiers/rewrite_engine.cpp index 922a8cce6..7f78eb049 100644 --- a/src/theory/quantifiers/rewrite_engine.cpp +++ b/src/theory/quantifiers/rewrite_engine.cpp @@ -18,9 +18,9 @@ #include "options/quantifiers_options.h" #include "theory/quantifiers/first_order_model.h" -#include "theory/quantifiers/inst_match_generator.h" +#include "theory/quantifiers/ematching/inst_match_generator.h" #include "theory/quantifiers/instantiate.h" -#include "theory/quantifiers/model_engine.h" +#include "theory/quantifiers/fmf/model_engine.h" #include "theory/quantifiers/quant_conflict_find.h" #include "theory/quantifiers/quant_util.h" #include "theory/quantifiers/quantifiers_attributes.h" diff --git a/src/theory/quantifiers/rewrite_engine.h b/src/theory/quantifiers/rewrite_engine.h index cfca96259..2253ac1da 100644 --- a/src/theory/quantifiers/rewrite_engine.h +++ b/src/theory/quantifiers/rewrite_engine.h @@ -20,7 +20,7 @@ #include "context/context.h" #include "context/context_mm.h" -#include "theory/quantifiers/trigger.h" +#include "theory/quantifiers/ematching/trigger.h" #include "theory/quantifiers/quant_conflict_find.h" #include "theory/quantifiers_engine.h" 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; ipreSimplify(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; igetTemplate(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; imkSkolem( "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() ); + //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; kgetQuantAttributes()->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; igetOutputChannel().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; imkNode( 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[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; igetSkolemize()->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 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 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; kmkNode( 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 "; + 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; imkNode(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; igetTermDatabaseSygus() + ->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 sols; + std::vector 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(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::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& sol_map, + bool singleInvocation) +{ + NodeManager* nm = NodeManager::currentNM(); + TermDbSygus* sygusDb = d_qe->getTermDatabaseSygus(); + std::vector sols; + std::vector 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(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& sols, + std::vector& 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 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& vals, + std::vector& 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 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()) + { + 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 vars; + std::vector 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 + +#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& 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& vals, + std::vector& 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 d_ceg_si; + /** program by examples utility */ + std::unique_ptr d_ceg_pbe; + /** utility for static preprocessing and analysis of conjectures */ + std::unique_ptr d_ceg_proc; + /** grammar utility */ + std::unique_ptr 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 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; ig(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& sols, + std::vector& 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 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 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; iaddLemma( 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; jaddLemma( 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; jaddLemma( 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 " << 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; jmkNode( 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; iaddLemma( 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; iaddLemma( 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; igetTermUtil()->d_false ){ + std::vector< Node > msu; + std::vector< Node > mexp; + msu.insert( msu.end(), ms.begin(), ms.end() ); + for( unsigned k=0; kisAssigned() ) + { + d_conj->printSynthSolution( out, d_last_inst_si ); + } + else + { + Assert( false ); + } +} + +void CegInstantiation::getSynthSolutions(std::map& 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 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& 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; imkSkolem( 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; iinit( 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 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 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; jmkNode( 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 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 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; jgetFunctionForFirstOrderVariable(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::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; imkNode( 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; id_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; id_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( indexsecond.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& vars ) { + return d_trie.constructFormula( vars ); +} + + +void DetTrace::print( const char* c ) { + for( unsigned i=0; i& 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 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 " << 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 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 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; imkSkolem( "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; jmkConst( 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_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; imkSkolem( "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 >::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; isecond.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()==( 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() ){ + 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; isecond.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& indices, unsigned i, + unsigned index, std::map& 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 d_single_inv_arg_sk; + // list of variables/skolems for each program + std::vector d_single_inv_var; + std::vector d_single_inv_sk; + std::map d_single_inv_sk_index; + // program to solution index + std::map 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 d_lemmas_produced; + std::vector > d_inst; + + private: + std::vector 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::const_iterator location = d_trans_pre.find(prog); + return location->second; + } + + Node getTransPost(Node prog) const { + std::map::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::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::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(); +} + +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::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; isecond; + 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 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; imkNode( ITE, n0[0], n2, n1 ); + }else if( n0.getKind()==AND || n0.getKind()==OR ){ + std::vector< Node > children; + for( unsigned i=1; imkNode( 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; imkNode( 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& 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; igetTermDatabaseSygus()->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; imkNode( 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::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 final_children; + for( unsigned i=0; i 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; ji || 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; imkNode( 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 >::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::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; imkNode( 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 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 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; igetTermDatabaseSygus()->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 *** 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; igetTermDatabaseSygus()->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; igetArgType( 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; isecond.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::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& 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().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().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::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(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(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& s, + std::vector& 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 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& args, + int index_exc, + int index_start) +{ + Assert(st.isDatatype()); + const Datatype& dt = static_cast(st.toType()).getDatatype(); + Assert(dt.isSygus()); + std::map > kgens; + std::vector 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 sigma; + std::vector 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::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 var_count; + std::map 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 > d_builtin_const_to_sygus; + /** sorted list of constants, per type */ + std::map > d_const_list; + /** number of positive constants, per type */ + std::map d_const_list_pos; + /** list of constructor indices whose operators are identity functions */ + std::map > 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 > 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& s, + std::vector& 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& 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 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 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& exp) +{ + std::map cexc; + getExplanationForConstantEquality(n, vn, exp, cexc); +} + +void SygusExplain::getExplanationForConstantEquality( + Node n, Node vn, std::vector& exp, std::map& 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 cexc; + return getExplanationForConstantEquality(n, vn, cexc); +} + +Node SygusExplain::getExplanationForConstantEquality( + Node n, Node vn, std::map& cexc) +{ + std::vector 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& exp, + std::map& 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 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& exp, + SygusInvarianceTest& et, + Node vnr, + unsigned& sz) +{ + // naive : + // return getExplanationForConstantEquality( n, vn, exp ); + + // set up the recursion object + std::map 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& exp, + SygusInvarianceTest& et) +{ + int sz = -1; + std::map 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 + +#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 d_term; + /** stack of children of active terms + * Notice that these may be modified with calls to replaceChild(...). + */ + std::vector > d_children; + /** stack the kind of active terms */ + std::vector d_kind; + /** stack of whether the active terms had an operator */ + std::vector d_has_op; + /** stack of positions that were pushed via calls to push(...) */ + std::vector 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& 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& exp, + std::map& cexc); + /** returns the conjunction of exp computed in the above function */ + Node getExplanationForConstantEquality(Node n, + Node vn, + std::map& 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& exp, + SygusInvarianceTest& et, + Node vnr, + unsigned& sz); + void getExplanationFor(Node n, + Node vn, + std::vector& 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& exp, + std::map& 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 + +#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 visited; + std::unordered_map::iterator it; + std::stack visit; + TNode cur; + visit.push(n); + do { + cur = visit.top(); + visit.pop(); + it = visited.find(cur); + if (it == visited.end()) { + visited[cur] = true; + // is this a constant? + if( cur.isConst() ){ + TypeNode tn = cur.getType(); + Node c = cur; + if( tn.isReal() ){ + c = NodeManager::currentNM()->mkConst( c.getConst().abs() ); + } + if( std::find( consts[tn].begin(), consts[tn].end(), c )==consts[tn].end() ){ + Trace("cegqi-debug") << "...consider const : " << c << std::endl; + consts[tn].push_back( c ); + } + } + // recurse + for (unsigned i = 0; i < cur.getNumChildren(); i++) { + visit.push(cur[i]); + } + } + } while (!visit.empty()); +} + + + +Node CegGrammarConstructor::process( Node q, std::map< Node, Node >& templates, std::map< Node, Node >& templates_arg ) { + // convert to deep embedding and finalize single invocation here + // now, construct the grammar + Trace("cegqi") << "CegConjecture : convert to deep embedding..." << std::endl; + std::map< TypeNode, std::vector< Node > > extra_cons; + if( options::sygusAddConstGrammar() ){ + Trace("cegqi") << "CegConjecture : collect constants..." << std::endl; + collectTerms( q[1], extra_cons ); + } + + std::vector< Node > qchildren; + std::map< Node, Node > synth_fun_vars; + std::vector< Node > ebvl; + Node qbody_subs = q[1]; + for( unsigned i=0; i arg_irrelevant; + d_parent->getProcess()->getIrrelevantArgs(sf, arg_irrelevant); + std::unordered_set term_irrelevant; + // convert to term + for (std::unordered_set::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; jmkBoundVar( sfvl[j].getType() ) ); + } + std::vector< Node > subsfn_children; + subsfn_children.push_back( sf ); + subsfn_children.insert( subsfn_children.end(), schildren.begin(), schildren.end() ); + Node subsfn = NodeManager::currentNM()->mkNode( kind::APPLY_UF, subsfn_children ); + TNode subsf = subsfn; + Trace("cegqi-debug") << " substitute arg : " << templ_arg << " -> " << subsf << std::endl; + templ = templ.substitute( templ_arg, subsf ); + // substitute lambda arguments + templ = templ.substitute( schildren.begin(), schildren.end(), largs.begin(), largs.end() ); + Node subsn = NodeManager::currentNM()->mkNode( kind::LAMBDA, NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, largs ), templ ); + TNode var = sf; + TNode subs = subsn; + Trace("cegqi-debug") << " substitute : " << var << " -> " << subs << std::endl; + qbody_subs = qbody_subs.substitute( var, subs ); + Trace("cegqi-debug") << " body is now : " << qbody_subs << std::endl; + } + } + d_qe->getTermDatabaseSygus()->registerSygusType( tn ); + // check grammar restrictions + if( !d_qe->getTermDatabaseSygus()->sygusToBuiltinType( tn ).isBoolean() ){ + if( !d_qe->getTermDatabaseSygus()->hasKind( tn, ITE ) ){ + d_has_ite = false; + } + } + Assert( tn.isDatatype() ); + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + Assert( dt.isSygus() ); + if( !dt.getSygusAllowAll() ){ + d_is_syntax_restricted = true; + } + + // ev is the first-order variable corresponding to this synth fun + std::stringstream ssf; + ssf << "f" << sf; + Node ev = NodeManager::currentNM()->mkBoundVar( ssf.str(), tn ); + ebvl.push_back( ev ); + synth_fun_vars[sf] = ev; + Trace("cegqi") << "...embedding synth fun : " << sf << " -> " << ev << std::endl; + } + qchildren.push_back( NodeManager::currentNM()->mkNode( kind::BOUND_VAR_LIST, ebvl ) ); + if( qbody_subs!=q[1] ){ + Trace("cegqi") << "...rewriting : " << qbody_subs << std::endl; + qbody_subs = Rewriter::rewrite( qbody_subs ); + Trace("cegqi") << "...got : " << qbody_subs << std::endl; + } + qchildren.push_back( convertToEmbedding( qbody_subs, synth_fun_vars ) ); + if( q.getNumChildren()==3 ){ + qchildren.push_back( q[2] ); + } + return NodeManager::currentNM()->mkNode( kind::FORALL, qchildren ); +} + +Node CegGrammarConstructor::convertToEmbedding( Node n, std::map< Node, Node >& synth_fun_vars ){ + std::unordered_map visited; + std::unordered_map::iterator it; + std::stack visit; + TNode cur; + visit.push(n); + do { + cur = visit.top(); + visit.pop(); + it = visited.find(cur); + if (it == visited.end()) { + visited[cur] = Node::null(); + visit.push(cur); + for (unsigned i = 0; i < cur.getNumChildren(); i++) { + visit.push(cur[i]); + } + } else if (it->second.isNull()) { + Node ret = cur; + Kind ret_k = cur.getKind(); + Node op; + bool childChanged = false; + std::vector children; + // get the potential operator + if( cur.getNumChildren()>0 ){ + if( cur.getKind()==kind::APPLY_UF ){ + op = cur.getOperator(); + } + }else{ + op = cur; + } + // is the operator a synth function? + if( !op.isNull() ){ + std::map< Node, Node >::iterator its = synth_fun_vars.find( op ); + if( its!=synth_fun_vars.end() ){ + Assert( its->second.getType().isDatatype() ); + // will make into an application of an evaluation function + const Datatype& dt = ((DatatypeType)its->second.getType().toType()).getDatatype(); + Assert( dt.isSygus() ); + children.push_back( Node::fromExpr( dt.getSygusEvaluationFunc() ) ); + children.push_back( its->second ); + childChanged = true; + ret_k = kind::APPLY_UF; + } + } + if( !childChanged ){ + // otherwise, we apply the previous operator + if( cur.getMetaKind() == kind::metakind::PARAMETERIZED ){ + children.push_back( cur.getOperator() ); + } + } + for (unsigned i = 0; i < cur.getNumChildren(); i++) { + it = visited.find(cur[i]); + Assert(it != visited.end()); + Assert(!it->second.isNull()); + childChanged = childChanged || cur[i] != it->second; + children.push_back(it->second); + } + if (childChanged) { + ret = NodeManager::currentNM()->mkNode(ret_k, children); + } + visited[cur] = ret; + } + } while (!visit.empty()); + Assert(visited.find(n) != visited.end()); + Assert(!visited.find(n)->second.isNull()); + return visited[n]; +} + + +TypeNode CegGrammarConstructor::mkUnresolvedType(const std::string& name, std::set& unres) { + TypeNode unresolved = NodeManager::currentNM()->mkSort(name, ExprManager::SORT_FLAG_PLACEHOLDER); + unres.insert( unresolved.toType() ); + return unresolved; +} + +void CegGrammarConstructor::mkSygusConstantsForType( TypeNode type, std::vector& ops ) { + if (type.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 >& extra_cons, + std::unordered_set& term_irrelevant, + std::vector& datatypes, + std::set& unres) +{ + Trace("sygus-grammar-def") << "Construct default grammar for " << fun << " " + << range << std::endl; + // collect the variables + std::vector sygus_vars; + if( !bvl.isNull() ){ + for( unsigned i=0; i > 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 unres_types; + std::map< TypeNode, Type > type_to_unres; + for( unsigned i=0; i()); + //make unresolved type + Type unres_t = mkUnresolvedType(dname, unres).toType(); + unres_types.push_back(unres_t); + type_to_unres[types[i]] = unres_t; + sygus_to_builtin[unres_t] = types[i].toType(); + } + for( unsigned i=0; i cnames; + std::vector > cargs; + Type unres_t = unres_types[i]; + //add variables + for( unsigned j=0; j() ); + } + } + //add constants + std::vector< Node > consts; + mkSygusConstantsForType( types[i], consts ); + std::map< TypeNode, std::vector< Node > >::iterator itec = extra_cons.find( types[i] ); + if( itec!=extra_cons.end() ){ + //consts.insert( consts.end(), itec->second.begin(), itec->second.end() ); + for( unsigned j=0; jsecond.size(); j++ ){ + if( std::find( consts.begin(), consts.end(), itec->second[j] )==consts.end() ){ + consts.push_back( itec->second[j] ); + } + } + } + for( unsigned j=0; j() ); + } + //ITE + CVC4::Kind k = kind::ITE; + Trace("sygus-grammar-def") << "...add for " << k << std::endl; + ops[i].push_back(NodeManager::currentNM()->operatorOf(k).toExpr()); + cnames.push_back( kind::kindToString(k) ); + cargs.push_back( std::vector< CVC4::Type >() ); + cargs.back().push_back(unres_bt); + cargs.back().push_back(unres_t); + cargs.back().push_back(unres_t); + + if (types[i].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()); + 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 ops_pos_int; + std::vector cnames_pos_int; + std::vector> 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()); + /* 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()); + 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()); + 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() ); + for( unsigned j=0; j() ); + //Assert( type_to_unres.find(arg_type)!=type_to_unres.end() ); + cargs.back().push_back( type_to_unres[arg_type] ); + } + } + Trace("sygus-grammar-def") << "...make datatype " << datatypes[i] << std::endl; + datatypes[i].setSygus( types[i].toType(), bvl.toExpr(), true, true ); + for( unsigned j=0; jbooleanType(); + datatypes.push_back(Datatype(dbname)); + ops.push_back(std::vector()); + std::vector cnames; + std::vector > cargs; + Trace("sygus-grammar-def") << "Make grammar for " << btype << " " << datatypes.back() << std::endl; + //add variables + for( unsigned i=0; i() ); + } + } + //add constants if no variables and no connected types + if( ops.back().empty() && types.empty() ){ + std::vector< Node > consts; + mkSygusConstantsForType( btype, consts ); + for( unsigned j=0; j() ); + } + } + //add operators + for( unsigned i=0; i<3; i++ ){ + CVC4::Kind k = i==0 ? kind::NOT : ( i==1 ? kind::AND : kind::OR ); + Trace("sygus-grammar-def") << "...add for " << k << std::endl; + ops.back().push_back(NodeManager::currentNM()->operatorOf(k).toExpr()); + cnames.push_back(kind::kindToString(k)); + cargs.push_back( std::vector< CVC4::Type >() ); + if( k==kind::NOT ){ + cargs.back().push_back(unres_bt); + }else if( k==kind::AND || k==kind::OR ){ + cargs.back().push_back(unres_bt); + cargs.back().push_back(unres_bt); + } + } + //add predicates for types + for( unsigned i=0; ioperatorOf(k).toExpr()); + std::stringstream ss; + ss << kind::kindToString(k) << "_" << types[i]; + cnames.push_back(ss.str()); + cargs.push_back( std::vector< CVC4::Type >() ); + cargs.back().push_back(unres_types[i]); + cargs.back().push_back(unres_types[i]); + //type specific predicates + if (types[i].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() ); + cargs.back().push_back(unres_types[i]); + } + } + } + if( range==btype ){ + startIndex = datatypes.size()-1; + } + Trace("sygus-grammar-def") << "...make datatype " << datatypes.back() << std::endl; + datatypes.back().setSygus( btype.toType(), bvl.toExpr(), true, true ); + for( unsigned j=0; j0 ){ + CVC4::Datatype tmp_dt = datatypes[0]; + datatypes[0] = datatypes[startIndex]; + datatypes[startIndex] = tmp_dt; + } +} + +TypeNode CegGrammarConstructor::mkSygusDefaultType( + TypeNode range, + Node bvl, + const std::string& fun, + std::map >& extra_cons, + std::unordered_set& 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 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 types = NodeManager::currentNM()->toExprManager()->mkMutualDatatypeTypes(datatypes, unres); + Assert( types.size()==datatypes.size() ); + return TypeNode::fromType( types[0] ); +} + +TypeNode CegGrammarConstructor::mkSygusTemplateTypeRec( Node templ, Node templ_arg, TypeNode templ_arg_sygus_type, Node bvl, + const std::string& fun, unsigned& tcount ) { + if( templ==templ_arg ){ + //Assert( templ_arg.getType()==sygusToBuiltinType( templ_arg_sygus_type ) ); + return templ_arg_sygus_type; + }else{ + tcount++; + std::set unres; + std::vector< CVC4::Datatype > datatypes; + std::stringstream ssd; + ssd << fun << "_templ_" << tcount; + std::string dbname = ssd.str(); + datatypes.push_back(Datatype(dbname)); + Node op; + std::vector< Type > argTypes; + if( templ.getNumChildren()==0 ){ + // TODO : can short circuit to this case when !TermUtil::containsTerm( templ, templ_arg ) + op = templ; + }else{ + Assert( templ.hasOperator() ); + op = templ.getOperator(); + // make constructor taking arguments types from children + for( unsigned i=0; i types = NodeManager::currentNM()->toExprManager()->mkMutualDatatypeTypes(datatypes, unres); + Assert( types.size()==1 ); + return TypeNode::fromType( types[0] ); + } +} + +TypeNode CegGrammarConstructor::mkSygusTemplateType( Node templ, Node templ_arg, TypeNode templ_arg_sygus_type, Node bvl, + const std::string& fun ) { + unsigned tcount = 0; + return mkSygusTemplateTypeRec( templ, templ_arg, templ_arg_sygus_type, bvl, fun, tcount ); +} + +}/* namespace CVC4::theory::quantifiers */ +}/* namespace CVC4::theory */ +}/* namespace CVC4 */ 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& templates, + std::map& 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 >& extra_cons, + std::unordered_set& 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 > extra_cons; + std::unordered_set 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& unres); + // make the builtin constants for type type that should be included in a sygus grammar + static void mkSygusConstantsForType( TypeNode type, std::vector& 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 >& extra_cons, + std::unordered_set& term_irrelevant, + std::vector& datatypes, + std::set& 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 // for std::iota + +using namespace CVC4::kind; + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +bool OpPosTrie::getOrMakeType(TypeNode tn, + TypeNode& unres_tn, + const std::vector& 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()); + 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(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& op_pos) +{ + std::vector 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& op_pos) +{ + NodeManager* nm = NodeManager::currentNM(); + std::vector 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 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 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()); + 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()); + 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()); + to.d_cons_args_t.back().push_back( + sygus_norm->normalizeSygusRec(to.d_tn, dt, d_elem_pos).toType()); +} + +std::map 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::inferTransf( + TypeNode tn, const Datatype& dt, const std::vector& 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 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(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 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(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(new TransfChain(chain_op_pos, elem_pos)); + } + return nullptr; +} + +TypeNode SygusGrammarNorm::normalizeSygusRec(TypeNode tn, + const Datatype& dt, + std::vector& 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 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(tn.toType()).getDatatype(); + std::vector 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 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 +#include +#include +#include + +#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& 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 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 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 d_ops; + /* Names for each constructor. */ + std::vector d_cons_names; + /* Print callbacks for each constructor */ + std::vector> d_pc; + /* Weights for each constructor */ + std::vector d_weight; + /* List of argument types for each constructor */ + std::vector> 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& 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& indices) : d_drop_indices(indices) + { + } + /** build type */ + void buildType(SygusGrammarNorm* sygus_norm, + TypeObject& to, + const Datatype& dt, + std::vector& op_pos) override; + + private: + std::vector 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& 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& 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 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> 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> 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 d_dt_all; + /* Types to be resolved */ + std::set d_unres_t_all; + /* Associates type nodes with OpPosTries */ + std::map d_tries; + /* Map of type nodes into their identity operators (\lambda x. x) */ + static std::map 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& 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 inferTransf(TypeNode tn, + const Datatype& dt, + const std::vector& 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(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 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 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::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& indices) +{ + const Datatype& dt = static_cast(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& pre, + std::vector& 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 +#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& 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 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 d_gen_terms; + /** + * Map from the rewritten form of generic terms for constructors of the + * registered type to their corresponding constructor index. + */ + std::map 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& pre, + std::vector& 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& exo, + std::vector& 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 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 +#include + +#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 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 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& exo, + std::vector& 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 d_exo; + /** The set of I/O pair indices i such that + * contains( out_i, nvn[in_i] ) ---> false + */ + std::vector 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& vals, bool pol = true ){ + if( Trace.isOn(c) ){ + for( unsigned i=0; i() : !vals[i].getConst() ) ? "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(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(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(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& candidates, + std::vector& lemmas) +{ + Trace("sygus-pbe") << "Initialize PBE : " << n << std::endl; + + for( unsigned i=0; i visited; + collectExamples( n, visited, true, true ); + + for( unsigned i=0; i " << 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; igetTermDatabaseSygus()->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 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& 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::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 > >::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& ex) { + e = d_tds->getSynthFunForEnumerator(e); + Assert(!e.isNull()); + std::map > >::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 >::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::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::iterator itx = d_examples_invalid.find(e); + if (itx == d_examples_invalid.end()) { + std::map > >::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::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::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::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(tn.toType()).getDatatype(); + Assert(dt.isSygus()); + + std::map > cop_to_strat; + std::map cop_to_cindex; + std::map > cop_to_child_templ; + std::map > cop_to_child_templ_arg; + std::map > cop_to_carg_list; + std::map > cop_to_child_types; + std::map > 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 utchildren; + utchildren.push_back(cop); + std::vector sks; + std::vector 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 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 templ_injection; + std::vector vs; + std::vector ss; + std::map templ_var_index; + for (unsigned k = 0, sksize = sks.size(); k < sksize; k++) + { + Assert(sks[k].getType().isDatatype()); + const Datatype& cdt = + static_cast(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 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 > assoc_combine; + std::vector assoc_waiting; + int assoc_last_valid_index = -1; + for (std::pair& 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::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& 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& ac = assoc_combine[k]; + Assert(!ac.empty()); + std::vector 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(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 >& 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& childTypes = cop_to_child_types[cop]; + Assert(cop_to_carg_list.find(cop) != cop_to_carg_list.end()); + std::vector& cargList = cop_to_carg_list[cop]; + + std::vector 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(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& lemmas ) { + for( unsigned i=0; i::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; jsecond.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 > visited; + std::map > needs_cons; + staticLearnRedundantOps(c, + d_cinfo[c].getRootEnumerator(), + role_equal, + visited, + needs_cons, + 0, + false); + // now, check the needs_cons map + for (std::pair >& nce : needs_cons) + { + Node em = nce.first; + const Datatype& dt = + static_cast(em.getType().toType()).getDatatype(); + for (std::pair& 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 >& visited, + std::map >& 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::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 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(cindex)] = false; + for (std::pair& 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::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(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::iterator it = d_cinfo.find( v ); + if( it!=d_cinfo.end() ){ + for( unsigned j=0; jsecond.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()) + { + 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 " << enum_values[i] << std::endl; + unsigned sz = d_tds->getSygusTermSize( enum_values[i] ); + if( i==0 || sz& 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()) + { + 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::iterator itc = d_cinfo.find(c); + Assert(itc != d_cinfo.end()); + TypeNode xtn = x.getType(); + Node bv = d_tds->sygusToBuiltin(v, xtn); + std::map > >::iterator itx = + d_examples.find(c); + std::map >::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 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 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; ssecond.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::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::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& results, + EnumInfo& ei, + std::vector& 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 >::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 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; id_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::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; id_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; id_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 " << curr_index << std::endl; + unsigned i = curr_index; + while( i " << i << std::endl; + curr_index = i; + } + + // verify it is correct + unsigned j = start(); + for( unsigned k=0; kj ); + for( unsigned k=(j+1); k::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::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 )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::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 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::iterator itet; + std::map::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 >::iterator itx = + d_examples_out.find(c); + Assert(itx != d_examples_out.end()); + std::vector 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 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 > incr; + bool isPrefix = nrole == role_string_prefix; + std::map total_inc; + std::vector inc_strs; + std::map >::iterator itx = d_examples_out.find(c); + Assert(itx != d_examples_out.end()); + // make the value of the examples + std::vector 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::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 look_ahead_solved_children; + std::vector 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::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& cenum = etis->d_cenum[sc]; + + // update the context + std::vector prev; + if (strat == strat_ITE && sc > 0) + { + std::map::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::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& te_pair = etis->d_cenum[i]; + Node te = te_pair.first; + std::map::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 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 > possible_cond; + std::map solved_cond; // stores branch + einfo_child.d_term_trie.getLeaves( + this, x.d_vals, true, possible_cond); + + std::map >::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 > 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 >::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 >::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& 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; id_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; i0 ){ + 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; id_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& vals, + std::vector& ex_vals) +{ + bool isPrefix = d_has_string_pos == role_string_prefix; + String dummy; + for( unsigned i=0; id_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(); + 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() ); + } + }else{ + // irrelevant, add dummy + ex_vals.push_back( dummy ); + } + } +} + +bool CegConjecturePbe::UnifContext::getStringIncrement( + CegConjecturePbe* pbe, + bool isPrefix, + const std::vector& ex_vals, + const std::vector& vals, + std::vector& inc, + unsigned& tot) +{ + for( unsigned j=0; jd_true ){ + // example is active in this context + Assert( vals[j].isConst() ); + String mystr = vals[j].getConst(); + 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& ex_vals, + const std::vector& vals) +{ + for( unsigned j=0; jd_true ){ + // example is active in this context + Assert( vals[j].isConst() ); + String mystr = vals[j].getConst(); + 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& candidates, + std::vector& lemmas); + /** get candidate list + * Adds all active enumerators associated with functions-to-synthesize in + * candidates to clist. + */ + void getCandidateList(std::vector& candidates, + std::vector& 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& enums, + std::vector& enum_values, + std::vector& candidates, + std::vector& candidate_values, + std::vector& 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& 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 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& 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 > 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 d_children; + /** helper function for above functions */ + Node addTermInternal(CegConjecturePbe* pbe, + Node t, + std::vector& vals, + bool pol, + std::vector& subsumed, + bool spol, + IndexFilter* f, + unsigned index, + int status, + bool checkExistsOnly, + bool checkSubsume); + /** helper function for above functions */ + void getLeavesInternal(CegConjecturePbe* pbe, + std::vector& vals, + bool pol, + std::map >& 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& 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 d_enum_slave; + /** values we have enumerated */ + std::vector 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 > d_enum_vals_res; + std::vector d_enum_subsume; + std::map 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 > d_cenum; + /** the arguments for the (templated) solution */ + std::vector 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 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 d_enum; + /** map from node roles to strategy nodes */ + std::map 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 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& templ_var_index, + std::map& 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& 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 >& visited, + std::map >& 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 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& 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 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& 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& vals, + std::vector& 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& ex_vals, + const std::vector& vals, + std::vector& inc, + unsigned& tot); + /** returns true if ex_vals[i] = vals[i] for all active indices i. */ + bool isStringSolved(CegConjecturePbe* pbe, + const std::vector& ex_vals, + const std::vector& 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 > 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 > 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 + +#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 + type_to_init_deq_id; + std::vector argTypes = + static_cast(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& n_arg_map) +{ + std::vector vars; + std::vector subs; + for (std::unordered_map::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::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& term_to_arg_carry, + std::unordered_map, + NodeHashFunction>& free_vars) +{ + std::unordered_map visited; + std::unordered_map::iterator it; + std::stack visit; + TNode cur; + visit.push(n); + do + { + cur = visit.top(); + visit.pop(); + it = visited.find(cur); + if (it == visited.end()) + { + // 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::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 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& 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 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::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& ns, + std::vector& ks, + Node nf, + std::unordered_set& synth_fv, + std::unordered_map, + 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 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 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::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, + NodeHashFunction>::iterator itf = free_vars.find(nn); + Assert(itf != free_vars.end()); + for (std::unordered_set::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 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 term_to_arg_carry; + // map of terms to (unprocessed) arguments where it occurs + std::unordered_map, 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 arg_list; + // now look at the terms for unprocessed arguments + for (std::unordered_map, 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, 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, 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& 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 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 conjuncts; + getComponentVector(AND, base, conjuncts); + + // process the conjunctions + for (std::map::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& 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::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& args) +{ + std::map::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& 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 synth_fv_n = synth_fv; + std::unordered_map 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, + NodeHashFunction> + free_vars; + getFreeVariables(nf, synth_fv_n, free_vars); + // get free variables in each application + std::vector ns; + std::vector ks; + for (std::unordered_map::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::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& synth_fv, + std::unordered_map& defs) +{ + std::unordered_map visited; + std::unordered_map::iterator it; + std::stack 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 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& synth_fv, + std::unordered_map, + NodeHashFunction>& free_vars) +{ + // first must compute free variables in each subterm of n, + // as well as contains_synth_fun + std::unordered_map visited; + std::unordered_map::iterator it; + std::stack 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& 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 +#include +#include +#include + +#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& ns, + std::vector& ks, + Node nf, + std::unordered_set& synth_fv, + std::unordered_map, + 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& args); + + private: + /** the synth fun associated with this */ + Node d_synth_fun; + /** properties of each argument */ + std::vector 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 d_arg_vars; + /** map from d_arg_vars to the argument # + * they represent. + */ + std::unordered_map 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& 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& term_to_arg_carry, + std::unordered_map, + 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& 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& 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& 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& 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& synth_fv, + std::unordered_map& 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& synth_fv, + std::unordered_map, + NodeHashFunction>& free_vars); + /** for each synth-fun, information that is specific to this conjecture */ + std::map d_sf_info; + + /** get component vector */ + void getComponentVector(Kind k, Node n, std::vector& 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 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& var_count, + std::map& 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& pre) +{ + std::map 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= 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->firstgetArgType( 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::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 (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( cindexgetTermUtil()->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; jmkSkolem( "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::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::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::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& mts) +{ + for (std::map::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_depthsecond ){ + 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::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; isecond; + } +} + +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( cindex0 ){ + ret = 1; + for( unsigned i=0; isecond; + } +} + +unsigned TermDbSygus::getSelectorWeight(TypeNode tn, Node sel) +{ + std::map >::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(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::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 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().isNegativeOne()) + { + mon[ro].push_back( nc ); + changed = true; + }else{ + if( !n[r][i].isConst() || !n[r][i].getConst().isZero() ){ + mon[r].push_back( n[r][i] ); + } + } + } + }else{ + if (ArithMSum::getMonomial(n[r], c, nc) + && c.getConst().isNegativeOne()) + { + mon[ro].push_back( nc ); + changed = true; + }else{ + if( !n[r].isConst() || !n[r].getConst().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 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()); + 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 eval_children; + eval_children.push_back( Node::fromExpr( dt.getSygusEvaluationFunc() ) ); + eval_children.push_back( n ); + //for each evaluation + for( unsigned i=start; isecond.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 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; imkNode( 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 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 children; + for( unsigned i=0; imkNode( 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& visited) +{ + std::unordered_map::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; imkNode( ret.getKind(), children ); + } + ret = getExtRewriter()->extendedRewrite(ret); + } + visited[n] = ret; + return ret; + }else{ + return it->second; + } +} + +Node TermDbSygus::evaluateWithUnfolding( Node n ) { + std::unordered_map 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 + +#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& 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& 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& var_count, + std::map& pre); + /** same as above, but with empty var_count */ + Node mkGeneric(const Datatype& dt, int c, std::map& 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 d_syexp; + /** sygus explanation */ + std::unique_ptr d_ext_rw; + /** mapping from enumerator terms to the conjecture they are associated with + */ + std::map d_enum_to_conjecture; + /** mapping from enumerator terms to the function-to-synthesize they are + * associated with + */ + std::map 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 d_enum_to_active_guard; + + //-----------------------------conversion from sygus to builtin + /** cache for sygusToBuiltin */ + std::map > 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 > d_fv[2]; + /** Maps free variables to the domain type they are associated with in d_fv */ + std::map d_fv_stype; + /** Maps free variables to their index in d_fv. */ + std::map d_fv_num; + /** recursive helper for hasFreeVar, visited stores nodes we have visited. */ + bool hasFreeVar(Node n, std::map& 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 d_register; // stores sygus -> builtin type + std::map > d_var_list; + std::map > d_arg_kind; + std::map > d_kinds; + std::map > d_arg_const; + std::map > d_consts; + std::map > d_ops; + std::map > d_arg_ops; + std::map > d_semantic_skolem; + // grammar information + // root -> type -> _ + std::map > d_min_type_depth; + // std::map< TypeNode, std::map< Node, std::map< std::map< int, bool > > > + // d_consider_const; + // type -> cons -> _ + std::map d_min_term_size; + std::map > d_min_cons_term_size; + /** a cache for getSelectorWeight */ + std::map > 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 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& visited); + Node evaluateWithUnfolding( Node n ); +}; + +}/* CVC4::theory::quantifiers namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__QUANTIFIERS__TERM_DATABASE_H */ diff --git a/src/theory/quantifiers/sygus_explain.cpp b/src/theory/quantifiers/sygus_explain.cpp deleted file mode 100644 index 4ae4d4391..000000000 --- a/src/theory/quantifiers/sygus_explain.cpp +++ /dev/null @@ -1,301 +0,0 @@ -/********************* */ -/*! \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_explain.h" - -#include "theory/datatypes/datatypes_rewriter.h" -#include "theory/quantifiers/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 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 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& exp) -{ - std::map cexc; - getExplanationForConstantEquality(n, vn, exp, cexc); -} - -void SygusExplain::getExplanationForConstantEquality( - Node n, Node vn, std::vector& exp, std::map& 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 cexc; - return getExplanationForConstantEquality(n, vn, cexc); -} - -Node SygusExplain::getExplanationForConstantEquality( - Node n, Node vn, std::map& cexc) -{ - std::vector 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& exp, - std::map& 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 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& exp, - SygusInvarianceTest& et, - Node vnr, - unsigned& sz) -{ - // naive : - // return getExplanationForConstantEquality( n, vn, exp ); - - // set up the recursion object - std::map 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& exp, - SygusInvarianceTest& et) -{ - int sz = -1; - std::map 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_explain.h b/src/theory/quantifiers/sygus_explain.h deleted file mode 100644 index aa2ca0dd0..000000000 --- a/src/theory/quantifiers/sygus_explain.h +++ /dev/null @@ -1,222 +0,0 @@ -/********************* */ -/*! \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 - -#include "expr/node.h" -#include "theory/quantifiers/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 d_term; - /** stack of children of active terms - * Notice that these may be modified with calls to replaceChild(...). - */ - std::vector > d_children; - /** stack the kind of active terms */ - std::vector d_kind; - /** stack of whether the active terms had an operator */ - std::vector d_has_op; - /** stack of positions that were pushed via calls to push(...) */ - std::vector 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& 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& exp, - std::map& cexc); - /** returns the conjunction of exp computed in the above function */ - Node getExplanationForConstantEquality(Node n, - Node vn, - std::map& 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& exp, - SygusInvarianceTest& et, - Node vnr, - unsigned& sz); - void getExplanationFor(Node n, - Node vn, - std::vector& 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& exp, - std::map& 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_grammar_cons.cpp b/src/theory/quantifiers/sygus_grammar_cons.cpp deleted file mode 100644 index 092880047..000000000 --- a/src/theory/quantifiers/sygus_grammar_cons.cpp +++ /dev/null @@ -1,693 +0,0 @@ -/********************* */ -/*! \file sygus_grammar_cons.cpp - ** \verbatim - ** Top contributors (to current version): - ** Andrew Reynolds - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief implementation of class for constructing inductive datatypes that correspond to - ** grammars that encode syntactic restrictions for SyGuS. - **/ -#include "theory/quantifiers/sygus_grammar_cons.h" - -#include - -#include "expr/datatype.h" -#include "options/quantifiers_options.h" -#include "theory/quantifiers/ce_guided_conjecture.h" -#include "theory/quantifiers/sygus_process_conj.h" -#include "theory/quantifiers/sygus_grammar_norm.h" -#include "theory/quantifiers/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 visited; - std::unordered_map::iterator it; - std::stack visit; - TNode cur; - visit.push(n); - do { - cur = visit.top(); - visit.pop(); - it = visited.find(cur); - if (it == visited.end()) { - visited[cur] = true; - // is this a constant? - if( cur.isConst() ){ - TypeNode tn = cur.getType(); - Node c = cur; - if( tn.isReal() ){ - c = NodeManager::currentNM()->mkConst( c.getConst().abs() ); - } - if( std::find( consts[tn].begin(), consts[tn].end(), c )==consts[tn].end() ){ - Trace("cegqi-debug") << "...consider const : " << c << std::endl; - consts[tn].push_back( c ); - } - } - // recurse - for (unsigned i = 0; i < cur.getNumChildren(); i++) { - visit.push(cur[i]); - } - } - } while (!visit.empty()); -} - - - -Node CegGrammarConstructor::process( Node q, std::map< Node, Node >& templates, std::map< Node, Node >& templates_arg ) { - // convert to deep embedding and finalize single invocation here - // now, construct the grammar - Trace("cegqi") << "CegConjecture : convert to deep embedding..." << std::endl; - std::map< TypeNode, std::vector< Node > > extra_cons; - if( options::sygusAddConstGrammar() ){ - Trace("cegqi") << "CegConjecture : collect constants..." << std::endl; - collectTerms( q[1], extra_cons ); - } - - std::vector< Node > qchildren; - std::map< Node, Node > synth_fun_vars; - std::vector< Node > ebvl; - Node qbody_subs = q[1]; - for( unsigned i=0; i arg_irrelevant; - d_parent->getProcess()->getIrrelevantArgs(sf, arg_irrelevant); - std::unordered_set term_irrelevant; - // convert to term - for (std::unordered_set::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; jmkBoundVar( sfvl[j].getType() ) ); - } - std::vector< Node > subsfn_children; - subsfn_children.push_back( sf ); - subsfn_children.insert( subsfn_children.end(), schildren.begin(), schildren.end() ); - Node subsfn = NodeManager::currentNM()->mkNode( kind::APPLY_UF, subsfn_children ); - TNode subsf = subsfn; - Trace("cegqi-debug") << " substitute arg : " << templ_arg << " -> " << subsf << std::endl; - templ = templ.substitute( templ_arg, subsf ); - // substitute lambda arguments - templ = templ.substitute( schildren.begin(), schildren.end(), largs.begin(), largs.end() ); - Node subsn = NodeManager::currentNM()->mkNode( kind::LAMBDA, NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, largs ), templ ); - TNode var = sf; - TNode subs = subsn; - Trace("cegqi-debug") << " substitute : " << var << " -> " << subs << std::endl; - qbody_subs = qbody_subs.substitute( var, subs ); - Trace("cegqi-debug") << " body is now : " << qbody_subs << std::endl; - } - } - d_qe->getTermDatabaseSygus()->registerSygusType( tn ); - // check grammar restrictions - if( !d_qe->getTermDatabaseSygus()->sygusToBuiltinType( tn ).isBoolean() ){ - if( !d_qe->getTermDatabaseSygus()->hasKind( tn, ITE ) ){ - d_has_ite = false; - } - } - Assert( tn.isDatatype() ); - const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); - Assert( dt.isSygus() ); - if( !dt.getSygusAllowAll() ){ - d_is_syntax_restricted = true; - } - - // ev is the first-order variable corresponding to this synth fun - std::stringstream ssf; - ssf << "f" << sf; - Node ev = NodeManager::currentNM()->mkBoundVar( ssf.str(), tn ); - ebvl.push_back( ev ); - synth_fun_vars[sf] = ev; - Trace("cegqi") << "...embedding synth fun : " << sf << " -> " << ev << std::endl; - } - qchildren.push_back( NodeManager::currentNM()->mkNode( kind::BOUND_VAR_LIST, ebvl ) ); - if( qbody_subs!=q[1] ){ - Trace("cegqi") << "...rewriting : " << qbody_subs << std::endl; - qbody_subs = Rewriter::rewrite( qbody_subs ); - Trace("cegqi") << "...got : " << qbody_subs << std::endl; - } - qchildren.push_back( convertToEmbedding( qbody_subs, synth_fun_vars ) ); - if( q.getNumChildren()==3 ){ - qchildren.push_back( q[2] ); - } - return NodeManager::currentNM()->mkNode( kind::FORALL, qchildren ); -} - -Node CegGrammarConstructor::convertToEmbedding( Node n, std::map< Node, Node >& synth_fun_vars ){ - std::unordered_map visited; - std::unordered_map::iterator it; - std::stack visit; - TNode cur; - visit.push(n); - do { - cur = visit.top(); - visit.pop(); - it = visited.find(cur); - if (it == visited.end()) { - visited[cur] = Node::null(); - visit.push(cur); - for (unsigned i = 0; i < cur.getNumChildren(); i++) { - visit.push(cur[i]); - } - } else if (it->second.isNull()) { - Node ret = cur; - Kind ret_k = cur.getKind(); - Node op; - bool childChanged = false; - std::vector children; - // get the potential operator - if( cur.getNumChildren()>0 ){ - if( cur.getKind()==kind::APPLY_UF ){ - op = cur.getOperator(); - } - }else{ - op = cur; - } - // is the operator a synth function? - if( !op.isNull() ){ - std::map< Node, Node >::iterator its = synth_fun_vars.find( op ); - if( its!=synth_fun_vars.end() ){ - Assert( its->second.getType().isDatatype() ); - // will make into an application of an evaluation function - const Datatype& dt = ((DatatypeType)its->second.getType().toType()).getDatatype(); - Assert( dt.isSygus() ); - children.push_back( Node::fromExpr( dt.getSygusEvaluationFunc() ) ); - children.push_back( its->second ); - childChanged = true; - ret_k = kind::APPLY_UF; - } - } - if( !childChanged ){ - // otherwise, we apply the previous operator - if( cur.getMetaKind() == kind::metakind::PARAMETERIZED ){ - children.push_back( cur.getOperator() ); - } - } - for (unsigned i = 0; i < cur.getNumChildren(); i++) { - it = visited.find(cur[i]); - Assert(it != visited.end()); - Assert(!it->second.isNull()); - childChanged = childChanged || cur[i] != it->second; - children.push_back(it->second); - } - if (childChanged) { - ret = NodeManager::currentNM()->mkNode(ret_k, children); - } - visited[cur] = ret; - } - } while (!visit.empty()); - Assert(visited.find(n) != visited.end()); - Assert(!visited.find(n)->second.isNull()); - return visited[n]; -} - - -TypeNode CegGrammarConstructor::mkUnresolvedType(const std::string& name, std::set& unres) { - TypeNode unresolved = NodeManager::currentNM()->mkSort(name, ExprManager::SORT_FLAG_PLACEHOLDER); - unres.insert( unresolved.toType() ); - return unresolved; -} - -void CegGrammarConstructor::mkSygusConstantsForType( TypeNode type, std::vector& ops ) { - if (type.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 >& extra_cons, - std::unordered_set& term_irrelevant, - std::vector& datatypes, - std::set& unres) -{ - Trace("sygus-grammar-def") << "Construct default grammar for " << fun << " " - << range << std::endl; - // collect the variables - std::vector sygus_vars; - if( !bvl.isNull() ){ - for( unsigned i=0; i > 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 unres_types; - std::map< TypeNode, Type > type_to_unres; - for( unsigned i=0; i()); - //make unresolved type - Type unres_t = mkUnresolvedType(dname, unres).toType(); - unres_types.push_back(unres_t); - type_to_unres[types[i]] = unres_t; - sygus_to_builtin[unres_t] = types[i].toType(); - } - for( unsigned i=0; i cnames; - std::vector > cargs; - Type unres_t = unres_types[i]; - //add variables - for( unsigned j=0; j() ); - } - } - //add constants - std::vector< Node > consts; - mkSygusConstantsForType( types[i], consts ); - std::map< TypeNode, std::vector< Node > >::iterator itec = extra_cons.find( types[i] ); - if( itec!=extra_cons.end() ){ - //consts.insert( consts.end(), itec->second.begin(), itec->second.end() ); - for( unsigned j=0; jsecond.size(); j++ ){ - if( std::find( consts.begin(), consts.end(), itec->second[j] )==consts.end() ){ - consts.push_back( itec->second[j] ); - } - } - } - for( unsigned j=0; j() ); - } - //ITE - CVC4::Kind k = kind::ITE; - Trace("sygus-grammar-def") << "...add for " << k << std::endl; - ops[i].push_back(NodeManager::currentNM()->operatorOf(k).toExpr()); - cnames.push_back( kind::kindToString(k) ); - cargs.push_back( std::vector< CVC4::Type >() ); - cargs.back().push_back(unres_bt); - cargs.back().push_back(unres_t); - cargs.back().push_back(unres_t); - - if (types[i].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()); - 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 ops_pos_int; - std::vector cnames_pos_int; - std::vector> 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()); - /* 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()); - 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()); - 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() ); - for( unsigned j=0; j() ); - //Assert( type_to_unres.find(arg_type)!=type_to_unres.end() ); - cargs.back().push_back( type_to_unres[arg_type] ); - } - } - Trace("sygus-grammar-def") << "...make datatype " << datatypes[i] << std::endl; - datatypes[i].setSygus( types[i].toType(), bvl.toExpr(), true, true ); - for( unsigned j=0; jbooleanType(); - datatypes.push_back(Datatype(dbname)); - ops.push_back(std::vector()); - std::vector cnames; - std::vector > cargs; - Trace("sygus-grammar-def") << "Make grammar for " << btype << " " << datatypes.back() << std::endl; - //add variables - for( unsigned i=0; i() ); - } - } - //add constants if no variables and no connected types - if( ops.back().empty() && types.empty() ){ - std::vector< Node > consts; - mkSygusConstantsForType( btype, consts ); - for( unsigned j=0; j() ); - } - } - //add operators - for( unsigned i=0; i<3; i++ ){ - CVC4::Kind k = i==0 ? kind::NOT : ( i==1 ? kind::AND : kind::OR ); - Trace("sygus-grammar-def") << "...add for " << k << std::endl; - ops.back().push_back(NodeManager::currentNM()->operatorOf(k).toExpr()); - cnames.push_back(kind::kindToString(k)); - cargs.push_back( std::vector< CVC4::Type >() ); - if( k==kind::NOT ){ - cargs.back().push_back(unres_bt); - }else if( k==kind::AND || k==kind::OR ){ - cargs.back().push_back(unres_bt); - cargs.back().push_back(unres_bt); - } - } - //add predicates for types - for( unsigned i=0; ioperatorOf(k).toExpr()); - std::stringstream ss; - ss << kind::kindToString(k) << "_" << types[i]; - cnames.push_back(ss.str()); - cargs.push_back( std::vector< CVC4::Type >() ); - cargs.back().push_back(unres_types[i]); - cargs.back().push_back(unres_types[i]); - //type specific predicates - if (types[i].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() ); - cargs.back().push_back(unres_types[i]); - } - } - } - if( range==btype ){ - startIndex = datatypes.size()-1; - } - Trace("sygus-grammar-def") << "...make datatype " << datatypes.back() << std::endl; - datatypes.back().setSygus( btype.toType(), bvl.toExpr(), true, true ); - for( unsigned j=0; j0 ){ - CVC4::Datatype tmp_dt = datatypes[0]; - datatypes[0] = datatypes[startIndex]; - datatypes[startIndex] = tmp_dt; - } -} - -TypeNode CegGrammarConstructor::mkSygusDefaultType( - TypeNode range, - Node bvl, - const std::string& fun, - std::map >& extra_cons, - std::unordered_set& 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 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 types = NodeManager::currentNM()->toExprManager()->mkMutualDatatypeTypes(datatypes, unres); - Assert( types.size()==datatypes.size() ); - return TypeNode::fromType( types[0] ); -} - -TypeNode CegGrammarConstructor::mkSygusTemplateTypeRec( Node templ, Node templ_arg, TypeNode templ_arg_sygus_type, Node bvl, - const std::string& fun, unsigned& tcount ) { - if( templ==templ_arg ){ - //Assert( templ_arg.getType()==sygusToBuiltinType( templ_arg_sygus_type ) ); - return templ_arg_sygus_type; - }else{ - tcount++; - std::set unres; - std::vector< CVC4::Datatype > datatypes; - std::stringstream ssd; - ssd << fun << "_templ_" << tcount; - std::string dbname = ssd.str(); - datatypes.push_back(Datatype(dbname)); - Node op; - std::vector< Type > argTypes; - if( templ.getNumChildren()==0 ){ - // TODO : can short circuit to this case when !TermUtil::containsTerm( templ, templ_arg ) - op = templ; - }else{ - Assert( templ.hasOperator() ); - op = templ.getOperator(); - // make constructor taking arguments types from children - for( unsigned i=0; i types = NodeManager::currentNM()->toExprManager()->mkMutualDatatypeTypes(datatypes, unres); - Assert( types.size()==1 ); - return TypeNode::fromType( types[0] ); - } -} - -TypeNode CegGrammarConstructor::mkSygusTemplateType( Node templ, Node templ_arg, TypeNode templ_arg_sygus_type, Node bvl, - const std::string& fun ) { - unsigned tcount = 0; - return mkSygusTemplateTypeRec( templ, templ_arg, templ_arg_sygus_type, bvl, fun, tcount ); -} - -}/* namespace CVC4::theory::quantifiers */ -}/* namespace CVC4::theory */ -}/* namespace CVC4 */ diff --git a/src/theory/quantifiers/sygus_grammar_cons.h b/src/theory/quantifiers/sygus_grammar_cons.h deleted file mode 100644 index 4e486f88f..000000000 --- a/src/theory/quantifiers/sygus_grammar_cons.h +++ /dev/null @@ -1,131 +0,0 @@ -/********************* */ -/*! \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& templates, - std::map& 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 >& extra_cons, - std::unordered_set& 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 > extra_cons; - std::unordered_set 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& unres); - // make the builtin constants for type type that should be included in a sygus grammar - static void mkSygusConstantsForType( TypeNode type, std::vector& 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 >& extra_cons, - std::unordered_set& term_irrelevant, - std::vector& datatypes, - std::set& 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_grammar_norm.cpp b/src/theory/quantifiers/sygus_grammar_norm.cpp deleted file mode 100644 index 6776aca15..000000000 --- a/src/theory/quantifiers/sygus_grammar_norm.cpp +++ /dev/null @@ -1,492 +0,0 @@ -/********************* */ -/*! \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_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/ce_guided_conjecture.h" -#include "theory/quantifiers/sygus_grammar_red.h" -#include "theory/quantifiers/term_database_sygus.h" -#include "theory/quantifiers/term_util.h" - -#include // for std::iota - -using namespace CVC4::kind; - -namespace CVC4 { -namespace theory { -namespace quantifiers { - -bool OpPosTrie::getOrMakeType(TypeNode tn, - TypeNode& unres_tn, - const std::vector& 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()); - 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(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& op_pos) -{ - std::vector 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& op_pos) -{ - NodeManager* nm = NodeManager::currentNM(); - std::vector 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 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 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()); - 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()); - 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()); - to.d_cons_args_t.back().push_back( - sygus_norm->normalizeSygusRec(to.d_tn, dt, d_elem_pos).toType()); -} - -std::map 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::inferTransf( - TypeNode tn, const Datatype& dt, const std::vector& 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 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(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 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(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(new TransfChain(chain_op_pos, elem_pos)); - } - return nullptr; -} - -TypeNode SygusGrammarNorm::normalizeSygusRec(TypeNode tn, - const Datatype& dt, - std::vector& 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 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(tn.toType()).getDatatype(); - std::vector 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 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_grammar_norm.h b/src/theory/quantifiers/sygus_grammar_norm.h deleted file mode 100644 index f72a83e5a..000000000 --- a/src/theory/quantifiers/sygus_grammar_norm.h +++ /dev/null @@ -1,455 +0,0 @@ -/********************* */ -/*! \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 -#include -#include -#include - -#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& 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 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 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 d_ops; - /* Names for each constructor. */ - std::vector d_cons_names; - /* Print callbacks for each constructor */ - std::vector> d_pc; - /* Weights for each constructor */ - std::vector d_weight; - /* List of argument types for each constructor */ - std::vector> 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& 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& indices) : d_drop_indices(indices) - { - } - /** build type */ - void buildType(SygusGrammarNorm* sygus_norm, - TypeObject& to, - const Datatype& dt, - std::vector& op_pos) override; - - private: - std::vector 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& 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& 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 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> 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> 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 d_dt_all; - /* Types to be resolved */ - std::set d_unres_t_all; - /* Associates type nodes with OpPosTries */ - std::map d_tries; - /* Map of type nodes into their identity operators (\lambda x. x) */ - static std::map 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& 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 inferTransf(TypeNode tn, - const Datatype& dt, - const std::vector& op_pos); -}; /* class SygusGrammarNorm */ - -} // namespace quantifiers -} // namespace theory -} // namespace CVC4 - -#endif diff --git a/src/theory/quantifiers/sygus_grammar_red.cpp b/src/theory/quantifiers/sygus_grammar_red.cpp deleted file mode 100644 index 056fc455a..000000000 --- a/src/theory/quantifiers/sygus_grammar_red.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/********************* */ -/*! \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_grammar_red.h" - -#include "options/quantifiers_options.h" -#include "theory/quantifiers/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(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 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 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::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& indices) -{ - const Datatype& dt = static_cast(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& pre, - std::vector& 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_grammar_red.h b/src/theory/quantifiers/sygus_grammar_red.h deleted file mode 100644 index d0484aa57..000000000 --- a/src/theory/quantifiers/sygus_grammar_red.h +++ /dev/null @@ -1,119 +0,0 @@ -/********************* */ -/*! \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 -#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& 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 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 d_gen_terms; - /** - * Map from the rewritten form of generic terms for constructors of the - * registered type to their corresponding constructor index. - */ - std::map 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& pre, - std::vector& 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_invariance.cpp b/src/theory/quantifiers/sygus_invariance.cpp deleted file mode 100644 index 1fd6bc7cb..000000000 --- a/src/theory/quantifiers/sygus_invariance.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/********************* */ -/*! \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_invariance.h" - -#include "theory/quantifiers/ce_guided_conjecture.h" -#include "theory/quantifiers/ce_guided_pbe.h" -#include "theory/quantifiers/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& exo, - std::vector& 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 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_invariance.h b/src/theory/quantifiers/sygus_invariance.h deleted file mode 100644 index a43e38719..000000000 --- a/src/theory/quantifiers/sygus_invariance.h +++ /dev/null @@ -1,276 +0,0 @@ -/********************* */ -/*! \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 -#include - -#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 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 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& exo, - std::vector& 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 d_exo; - /** The set of I/O pair indices i such that - * contains( out_i, nvn[in_i] ) ---> false - */ - std::vector 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_process_conj.cpp b/src/theory/quantifiers/sygus_process_conj.cpp deleted file mode 100644 index 4c0e992e0..000000000 --- a/src/theory/quantifiers/sygus_process_conj.cpp +++ /dev/null @@ -1,798 +0,0 @@ -/********************* */ -/*! \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_process_conj.h" - -#include - -#include "expr/datatype.h" -#include "theory/quantifiers/term_database_sygus.h" -#include "theory/quantifiers/term_util.h" - -using namespace CVC4::kind; -using namespace std; - -namespace CVC4 { -namespace theory { -namespace quantifiers { - -void CegConjectureProcessFun::init(Node f) -{ - d_synth_fun = f; - Assert(f.getType().isFunction()); - - // initialize the arguments - std::unordered_map - type_to_init_deq_id; - std::vector argTypes = - static_cast(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& n_arg_map) -{ - std::vector vars; - std::vector subs; - for (std::unordered_map::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::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& term_to_arg_carry, - std::unordered_map, - NodeHashFunction>& free_vars) -{ - std::unordered_map visited; - std::unordered_map::iterator it; - std::stack visit; - TNode cur; - visit.push(n); - do - { - cur = visit.top(); - visit.pop(); - it = visited.find(cur); - if (it == visited.end()) - { - // 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::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 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& 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 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::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& ns, - std::vector& ks, - Node nf, - std::unordered_set& synth_fv, - std::unordered_map, - 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 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 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::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, - NodeHashFunction>::iterator itf = free_vars.find(nn); - Assert(itf != free_vars.end()); - for (std::unordered_set::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 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 term_to_arg_carry; - // map of terms to (unprocessed) arguments where it occurs - std::unordered_map, 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 arg_list; - // now look at the terms for unprocessed arguments - for (std::unordered_map, 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, 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, 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& 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 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 conjuncts; - getComponentVector(AND, base, conjuncts); - - // process the conjunctions - for (std::map::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& 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::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& args) -{ - std::map::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& 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 synth_fv_n = synth_fv; - std::unordered_map 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, - NodeHashFunction> - free_vars; - getFreeVariables(nf, synth_fv_n, free_vars); - // get free variables in each application - std::vector ns; - std::vector ks; - for (std::unordered_map::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::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& synth_fv, - std::unordered_map& defs) -{ - std::unordered_map visited; - std::unordered_map::iterator it; - std::stack 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 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& synth_fv, - std::unordered_map, - NodeHashFunction>& free_vars) -{ - // first must compute free variables in each subterm of n, - // as well as contains_synth_fun - std::unordered_map visited; - std::unordered_map::iterator it; - std::stack 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& 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_process_conj.h b/src/theory/quantifiers/sygus_process_conj.h deleted file mode 100644 index 0b9a25532..000000000 --- a/src/theory/quantifiers/sygus_process_conj.h +++ /dev/null @@ -1,365 +0,0 @@ -/********************* */ -/*! \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 -#include -#include -#include - -#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& ns, - std::vector& ks, - Node nf, - std::unordered_set& synth_fv, - std::unordered_map, - 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& args); - - private: - /** the synth fun associated with this */ - Node d_synth_fun; - /** properties of each argument */ - std::vector 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 d_arg_vars; - /** map from d_arg_vars to the argument # - * they represent. - */ - std::unordered_map 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& 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& term_to_arg_carry, - std::unordered_map, - 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& 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& 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& 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& 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& synth_fv, - std::unordered_map& 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& synth_fv, - std::unordered_map, - NodeHashFunction>& free_vars); - /** for each synth-fun, information that is specific to this conjecture */ - std::map d_sf_info; - - /** get component vector */ - void getComponentVector(Kind k, Node n, std::vector& args); -}; - -} /* namespace CVC4::theory::quantifiers */ -} /* namespace CVC4::theory */ -} /* namespace CVC4 */ - -#endif diff --git a/src/theory/quantifiers/sygus_sampler.h b/src/theory/quantifiers/sygus_sampler.h index 60c2af22a..abc9232af 100644 --- a/src/theory/quantifiers/sygus_sampler.h +++ b/src/theory/quantifiers/sygus_sampler.h @@ -19,7 +19,7 @@ #include #include "theory/quantifiers/dynamic_rewrite.h" -#include "theory/quantifiers/term_database_sygus.h" +#include "theory/quantifiers/sygus/term_database_sygus.h" namespace CVC4 { namespace theory { diff --git a/src/theory/quantifiers/term_database.cpp b/src/theory/quantifiers/term_database.cpp index 19cdc68e5..8e22b2ced 100644 --- a/src/theory/quantifiers/term_database.cpp +++ b/src/theory/quantifiers/term_database.cpp @@ -19,7 +19,7 @@ #include "options/uf_options.h" #include "theory/quantifiers/quantifiers_attributes.h" #include "theory/quantifiers/term_util.h" -#include "theory/quantifiers/trigger.h" +#include "theory/quantifiers/ematching/trigger.h" #include "theory/quantifiers_engine.h" #include "theory/theory_engine.h" diff --git a/src/theory/quantifiers/term_database_sygus.cpp b/src/theory/quantifiers/term_database_sygus.cpp deleted file mode 100644 index 4c80108f1..000000000 --- a/src/theory/quantifiers/term_database_sygus.cpp +++ /dev/null @@ -1,1487 +0,0 @@ -/********************* */ -/*! \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/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 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& var_count, - std::map& 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& pre) -{ - std::map 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= 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->firstgetArgType( 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::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 (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( cindexgetTermUtil()->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; jmkSkolem( "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::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::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::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& mts) -{ - for (std::map::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_depthsecond ){ - 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::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; isecond; - } -} - -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( cindex0 ){ - ret = 1; - for( unsigned i=0; isecond; - } -} - -unsigned TermDbSygus::getSelectorWeight(TypeNode tn, Node sel) -{ - std::map >::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(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::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 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().isNegativeOne()) - { - mon[ro].push_back( nc ); - changed = true; - }else{ - if( !n[r][i].isConst() || !n[r][i].getConst().isZero() ){ - mon[r].push_back( n[r][i] ); - } - } - } - }else{ - if (ArithMSum::getMonomial(n[r], c, nc) - && c.getConst().isNegativeOne()) - { - mon[ro].push_back( nc ); - changed = true; - }else{ - if( !n[r].isConst() || !n[r].getConst().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 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()); - 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 eval_children; - eval_children.push_back( Node::fromExpr( dt.getSygusEvaluationFunc() ) ); - eval_children.push_back( n ); - //for each evaluation - for( unsigned i=start; isecond.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 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; imkNode( 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 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 children; - for( unsigned i=0; imkNode( 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& visited) -{ - std::unordered_map::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; imkNode( ret.getKind(), children ); - } - ret = getExtRewriter()->extendedRewrite(ret); - } - visited[n] = ret; - return ret; - }else{ - return it->second; - } -} - -Node TermDbSygus::evaluateWithUnfolding( Node n ) { - std::unordered_map visited; - return evaluateWithUnfolding( n, visited ); -} - -}/* CVC4::theory::quantifiers namespace */ -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ - diff --git a/src/theory/quantifiers/term_database_sygus.h b/src/theory/quantifiers/term_database_sygus.h deleted file mode 100644 index b9af26b6e..000000000 --- a/src/theory/quantifiers/term_database_sygus.h +++ /dev/null @@ -1,286 +0,0 @@ -/********************* */ -/*! \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 - -#include "theory/quantifiers/extended_rewrite.h" -#include "theory/quantifiers/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& 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& 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& var_count, - std::map& pre); - /** same as above, but with empty var_count */ - Node mkGeneric(const Datatype& dt, int c, std::map& 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 d_syexp; - /** sygus explanation */ - std::unique_ptr d_ext_rw; - /** mapping from enumerator terms to the conjecture they are associated with - */ - std::map d_enum_to_conjecture; - /** mapping from enumerator terms to the function-to-synthesize they are - * associated with - */ - std::map 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 d_enum_to_active_guard; - - //-----------------------------conversion from sygus to builtin - /** cache for sygusToBuiltin */ - std::map > 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 > d_fv[2]; - /** Maps free variables to the domain type they are associated with in d_fv */ - std::map d_fv_stype; - /** Maps free variables to their index in d_fv. */ - std::map d_fv_num; - /** recursive helper for hasFreeVar, visited stores nodes we have visited. */ - bool hasFreeVar(Node n, std::map& 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 d_register; // stores sygus -> builtin type - std::map > d_var_list; - std::map > d_arg_kind; - std::map > d_kinds; - std::map > d_arg_const; - std::map > d_consts; - std::map > d_ops; - std::map > d_arg_ops; - std::map > d_semantic_skolem; - // grammar information - // root -> type -> _ - std::map > d_min_type_depth; - // std::map< TypeNode, std::map< Node, std::map< std::map< int, bool > > > - // d_consider_const; - // type -> cons -> _ - std::map d_min_term_size; - std::map > d_min_cons_term_size; - /** a cache for getSelectorWeight */ - std::map > 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 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& visited); - Node evaluateWithUnfolding( Node n ); -}; - -}/* CVC4::theory::quantifiers namespace */ -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ - -#endif /* __CVC4__THEORY__QUANTIFIERS__TERM_DATABASE_H */ diff --git a/src/theory/quantifiers/theory_quantifiers.cpp b/src/theory/quantifiers/theory_quantifiers.cpp index a278274c3..f4e44ff2f 100644 --- a/src/theory/quantifiers/theory_quantifiers.cpp +++ b/src/theory/quantifiers/theory_quantifiers.cpp @@ -20,8 +20,8 @@ #include "base/cvc4_assert.h" #include "expr/kind.h" #include "options/quantifiers_options.h" -#include "theory/quantifiers/instantiation_engine.h" -#include "theory/quantifiers/model_engine.h" +#include "theory/quantifiers/ematching/instantiation_engine.h" +#include "theory/quantifiers/fmf/model_engine.h" #include "theory/quantifiers/quantifiers_attributes.h" #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/term_util.h" diff --git a/src/theory/quantifiers/trigger.cpp b/src/theory/quantifiers/trigger.cpp deleted file mode 100644 index 609b6e461..000000000 --- a/src/theory/quantifiers/trigger.cpp +++ /dev/null @@ -1,969 +0,0 @@ -/********************* */ -/*! \file trigger.cpp - ** \verbatim - ** Top contributors (to current version): - ** Andrew Reynolds, Morgan Deters, 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 Implementation of trigger class - **/ - -#include "theory/quantifiers/trigger.h" - -#include "theory/arith/arith_msum.h" -#include "theory/quantifiers/candidate_generator.h" -#include "theory/quantifiers/ho_trigger.h" -#include "theory/quantifiers/inst_match_generator.h" -#include "theory/quantifiers/instantiate.h" -#include "theory/quantifiers/term_database.h" -#include "theory/quantifiers/term_util.h" -#include "theory/quantifiers_engine.h" -#include "theory/theory_engine.h" -#include "theory/uf/equality_engine.h" -#include "util/hash.h" - -using namespace std; -using namespace CVC4::kind; -using namespace CVC4::context; - -namespace CVC4 { -namespace theory { -namespace inst { - -void TriggerTermInfo::init( Node q, Node n, int reqPol, Node reqPolEq ){ - if( d_fv.empty() ){ - quantifiers::TermUtil::getVarContainsNode( q, n, d_fv ); - } - if( d_reqPol==0 ){ - d_reqPol = reqPol; - d_reqPolEq = reqPolEq; - }else{ - //determined a ground (dis)equality must hold or else q is a tautology? - } - d_weight = Trigger::getTriggerWeight(n); -} - -/** trigger class constructor */ -Trigger::Trigger(QuantifiersEngine* qe, Node q, std::vector& nodes) - : d_quantEngine(qe), d_quant(q) -{ - d_nodes.insert( d_nodes.begin(), nodes.begin(), nodes.end() ); - Trace("trigger") << "Trigger for " << q << ": " << std::endl; - for( unsigned i=0; id_statistics.d_triggers); - }else{ - ++(qe->d_statistics.d_simple_triggers); - } - }else{ - Trace("multi-trigger") << "Trigger for " << q << ": " << std::endl; - for( unsigned i=0; id_statistics.d_multi_triggers); - } - - // Notice() << "Trigger : " << (*this) << " for " << q << std::endl; - Trace("trigger-debug") << "Finished making trigger." << std::endl; -} - -Trigger::~Trigger() { - delete d_mg; -} - -void Trigger::resetInstantiationRound(){ - d_mg->resetInstantiationRound( d_quantEngine ); -} - -void Trigger::reset( Node eqc ){ - d_mg->reset( eqc, d_quantEngine ); -} - -Node Trigger::getInstPattern(){ - return NodeManager::currentNM()->mkNode( INST_PATTERN, d_nodes ); -} - -int Trigger::addInstantiations() -{ - int addedLemmas = d_mg->addInstantiations(d_quant, d_quantEngine, this); - if( addedLemmas>0 ){ - if (Debug.isOn("inst-trigger")) - { - Debug("inst-trigger") << "Added " << addedLemmas - << " lemmas, trigger was "; - for (unsigned i = 0; i < d_nodes.size(); i++) - { - Debug("inst-trigger") << d_nodes[i] << " "; - } - Debug("inst-trigger") << std::endl; - } - } - return addedLemmas; -} - -bool Trigger::sendInstantiation(InstMatch& m) -{ - return d_quantEngine->getInstantiate()->addInstantiation(d_quant, m); -} - -bool Trigger::mkTriggerTerms( Node q, std::vector< Node >& nodes, unsigned n_vars, std::vector< Node >& trNodes ) { - //only take nodes that contribute variables to the trigger when added - std::vector< Node > temp; - temp.insert( temp.begin(), nodes.begin(), nodes.end() ); - std::map< Node, bool > vars; - std::map< Node, std::vector< Node > > patterns; - size_t varCount = 0; - std::map< Node, std::vector< Node > > varContains; - quantifiers::TermUtil::getVarContains( q, temp, varContains ); - for( unsigned i=0; i& nodes, bool keepAll, int trOption, unsigned use_n_vars ){ - std::vector< Node > trNodes; - if( !keepAll ){ - unsigned n_vars = use_n_vars==0 ? f[0].getNumChildren() : use_n_vars; - if( !mkTriggerTerms( f, nodes, n_vars, trNodes ) ){ - return NULL; - } - }else{ - trNodes.insert( trNodes.begin(), nodes.begin(), nodes.end() ); - } - - //check for duplicate? - if( trOption!=TR_MAKE_NEW ){ - Trigger* t = qe->getTriggerDatabase()->getTrigger( trNodes ); - if( t ){ - if( trOption==TR_GET_OLD ){ - //just return old trigger - return t; - }else{ - return NULL; - } - } - } - - // check if higher-order - Trace("trigger-debug") << "Collect higher-order variable triggers..." - << std::endl; - std::map > ho_apps; - HigherOrderTrigger::collectHoVarApplyTerms(f, trNodes, ho_apps); - Trace("trigger") << "...got " << ho_apps.size() - << " higher-order applications." << std::endl; - Trigger* t; - if (!ho_apps.empty()) - { - t = new HigherOrderTrigger(qe, f, trNodes, ho_apps); - } - else - { - t = new Trigger(qe, f, trNodes); - } - - qe->getTriggerDatabase()->addTrigger( trNodes, t ); - return t; -} - -Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, Node n, bool keepAll, int trOption, unsigned use_n_vars ){ - std::vector< Node > nodes; - nodes.push_back( n ); - return mkTrigger( qe, f, nodes, keepAll, trOption, use_n_vars ); -} - -bool Trigger::isUsable( Node n, Node q ){ - if( quantifiers::TermUtil::getInstConstAttr(n)==q ){ - if( isAtomicTrigger( n ) ){ - for( unsigned i=0; i coeffs; - if( isBooleanTermTrigger( n ) ){ - return true; - }else if( options::purifyTriggers() ){ - Node x = getInversionVariable( n ); - if( !x.isNull() ){ - return true; - } - } - } - return false; - }else{ - return true; - } -} - -Node Trigger::getIsUsableEq( Node q, Node n ) { - Assert( isRelationalTrigger( n ) ); - for( unsigned i=0; i<2; i++) { - if( isUsableEqTerms( q, n[i], n[1-i] ) ){ - if( i==1 && n.getKind()==EQUAL && !quantifiers::TermUtil::hasInstConstAttr(n[0]) ){ - return NodeManager::currentNM()->mkNode( n.getKind(), n[1], n[0] ); - }else{ - return n; - } - } - } - return Node::null(); -} - -bool Trigger::isUsableEqTerms( Node q, Node n1, Node n2 ) { - if( n1.getKind()==INST_CONSTANT ){ - if( options::relationalTriggers() ){ - if( !quantifiers::TermUtil::hasInstConstAttr(n2) ){ - return true; - }else if( n2.getKind()==INST_CONSTANT ){ - return true; - } - } - }else if( isUsableAtomicTrigger( n1, q ) ){ - if( options::relationalTriggers() && n2.getKind()==INST_CONSTANT && !quantifiers::TermUtil::containsTerm( n1, n2 ) ){ - return true; - }else if( !quantifiers::TermUtil::hasInstConstAttr(n2) ){ - return true; - } - } - return false; -} - -Node Trigger::getIsUsableTrigger( Node n, Node q ) { - bool pol = true; - Trace("trigger-debug") << "Is " << n << " a usable trigger?" << std::endl; - if( n.getKind()==NOT ){ - pol = !pol; - n = n[0]; - } - if( n.getKind()==INST_CONSTANT ){ - return pol ? n : NodeManager::currentNM()->mkNode( EQUAL, n, NodeManager::currentNM()->mkConst( true ) ).notNode(); - }else if( isRelationalTrigger( n ) ){ - Node rtr = getIsUsableEq( q, n ); - if( rtr.isNull() && n[0].getType().isReal() ){ - //try to solve relation - std::map< Node, Node > m; - if (ArithMSum::getMonomialSumLit(n, m)) - { - for( std::map< Node, Node >::iterator it = m.begin(); it!=m.end(); ++it ){ - bool trySolve = false; - if( !it->first.isNull() ){ - if( it->first.getKind()==INST_CONSTANT ){ - trySolve = options::relationalTriggers(); - }else if( isUsableTrigger( it->first, q ) ){ - trySolve = true; - } - } - if( trySolve ){ - Trace("trigger-debug") << "Try to solve for " << it->first << std::endl; - Node veq; - if (ArithMSum::isolate(it->first, m, veq, n.getKind()) != 0) - { - rtr = getIsUsableEq( q, veq ); - } - //either all solves will succeed or all solves will fail - break; - } - } - } - } - if( !rtr.isNull() ){ - Trace("relational-trigger") << "Relational trigger : " << std::endl; - Trace("relational-trigger") << " " << rtr << " (from " << n << ")" << std::endl; - Trace("relational-trigger") << " in quantifier " << q << std::endl; - Node rtr2 = pol ? rtr : rtr.negate(); - Trace("relational-trigger") << " return : " << rtr2 << std::endl; - return rtr2; - } - }else{ - Trace("trigger-debug") << n << " usable : " << ( quantifiers::TermUtil::getInstConstAttr(n)==q ) << " " << isAtomicTrigger( n ) << " " << isUsable( n, q ) << std::endl; - if( isUsableAtomicTrigger( n, q ) ){ - return pol ? n : NodeManager::currentNM()->mkNode( EQUAL, n, NodeManager::currentNM()->mkConst( true ) ).notNode(); - } - } - return Node::null(); -} - -bool Trigger::isUsableAtomicTrigger( Node n, Node q ) { - return quantifiers::TermUtil::getInstConstAttr( n )==q && isAtomicTrigger( n ) && isUsable( n, q ); -} - -bool Trigger::isUsableTrigger( Node n, Node q ){ - Node nu = getIsUsableTrigger( n, q ); - return !nu.isNull(); -} - -bool Trigger::isAtomicTrigger( Node n ){ - return isAtomicTriggerKind( n.getKind() ); -} - -bool Trigger::isAtomicTriggerKind( Kind k ) { - return k == APPLY_UF || k == SELECT || k == STORE || k == APPLY_CONSTRUCTOR - || k == APPLY_SELECTOR_TOTAL || k == APPLY_TESTER || k == UNION - || k == INTERSECTION || k == SUBSET || k == SETMINUS || k == MEMBER - || k == SINGLETON || k == SEP_PTO || k == BITVECTOR_TO_NAT - || k == INT_TO_BITVECTOR || k == HO_APPLY; -} - -bool Trigger::isRelationalTrigger( Node n ) { - return isRelationalTriggerKind( n.getKind() ); -} - -bool Trigger::isRelationalTriggerKind( Kind k ) { - return k==EQUAL || k==GEQ; -} - -bool Trigger::isCbqiKind( Kind k ) { - if( quantifiers::TermUtil::isBoolConnective( k ) || k==PLUS || k==GEQ || k==EQUAL || k==MULT ){ - return true; - }else{ - //CBQI typically works for satisfaction-complete theories - TheoryId t = kindToTheoryId( k ); - return t == THEORY_BV || t == THEORY_DATATYPES || t == THEORY_BOOL; - } -} - -bool Trigger::isSimpleTrigger( Node n ){ - Node t = n.getKind()==NOT ? n[0] : n; - if( t.getKind()==EQUAL ){ - if( !quantifiers::TermUtil::hasInstConstAttr( t[1] ) ){ - t = t[0]; - } - } - if( isAtomicTrigger( t ) ){ - for( unsigned i=0; i >& visited, std::map< Node, TriggerTermInfo >& tinfo, - quantifiers::TriggerSelMode tstrt, std::vector< Node >& exclude, std::vector< Node >& added, - bool pol, bool hasPol, bool epol, bool hasEPol, bool knowIsUsable ){ - std::map< Node, std::vector< Node > >::iterator itv = visited.find( n ); - if( itv==visited.end() ){ - visited[ n ].clear(); - Trace("auto-gen-trigger-debug2") << "Collect pat terms " << n << " " << pol << " " << hasPol << " " << epol << " " << hasEPol << std::endl; - if( n.getKind()!=FORALL && n.getKind()!=INST_CONSTANT ){ - Node nu; - bool nu_single = false; - if( knowIsUsable ){ - nu = n; - }else if( n.getKind()!=NOT && std::find( exclude.begin(), exclude.end(), n )==exclude.end() ){ - nu = getIsUsableTrigger( n, q ); - if( !nu.isNull() && nu!=n ){ - collectPatTerms2( q, nu, visited, tinfo, tstrt, exclude, added, pol, hasPol, epol, hasEPol, true ); - // copy to n - visited[n].insert( visited[n].end(), added.begin(), added.end() ); - return; - } - } - if( !nu.isNull() ){ - Assert( nu==n ); - Assert( nu.getKind()!=NOT ); - Trace("auto-gen-trigger-debug2") << "...found usable trigger : " << nu << std::endl; - Node reqEq; - if( nu.getKind()==EQUAL ){ - if( isAtomicTrigger( nu[0] ) && !quantifiers::TermUtil::hasInstConstAttr(nu[1]) ){ - if( hasPol ){ - reqEq = nu[1]; - } - nu = nu[0]; - } - } - Assert( reqEq.isNull() || !quantifiers::TermUtil::hasInstConstAttr( reqEq ) ); - Assert( isUsableTrigger( nu, q ) ); - //tinfo.find( nu )==tinfo.end() - Trace("auto-gen-trigger-debug2") << "...add usable trigger : " << nu << std::endl; - tinfo[ nu ].init( q, nu, hasEPol ? ( epol ? 1 : -1 ) : 0, reqEq ); - nu_single = tinfo[ nu ].d_fv.size()==q[0].getNumChildren(); - } - Node nrec = nu.isNull() ? n : nu; - std::vector< Node > added2; - for( unsigned i=0; i= tinfo[added2[i]].d_weight) - { - // discard if we added a subterm as a trigger with all - // variables that nu has - Trace("auto-gen-trigger-debug2") - << "......subsumes parent " << tinfo[nu].d_weight << " " - << tinfo[added2[i]].d_weight << "." << std::endl; - rm_nu = true; - } - } - if( std::find( added.begin(), added.end(), added2[i] )==added.end() ){ - added.push_back( added2[i] ); - } - } - } - } - if( rm_nu && ( tstrt==quantifiers::TRIGGER_SEL_MIN || ( tstrt==quantifiers::TRIGGER_SEL_MIN_SINGLE_ALL && nu_single ) ) ){ - tinfo.erase( nu ); - }else{ - if( std::find( added.begin(), added.end(), nu )==added.end() ){ - added.push_back( nu ); - } - } - visited[n].insert( visited[n].end(), added.begin(), added.end() ); - } - } - }else{ - for( unsigned i=0; isecond.size(); ++i ){ - Node t = itv->second[i]; - if( std::find( added.begin(), added.end(), t )==added.end() ){ - added.push_back( t ); - } - } - } -} - -bool Trigger::isBooleanTermTrigger( Node n ) { - if( n.getKind()==ITE ){ - //check for boolean term converted to ITE - if( n[0].getKind()==INST_CONSTANT && - n[1].getKind()==CONST_BITVECTOR && - n[2].getKind()==CONST_BITVECTOR ){ - if( ((BitVectorType)n[1].getType().toType()).getSize()==1 && - n[1].getConst().toInteger()==1 && - n[2].getConst().toInteger()==0 ){ - return true; - } - } - } - return false; -} - -bool Trigger::isPureTheoryTrigger( Node n ) { - if( n.getKind()==APPLY_UF || n.getKind()==VARIABLE || n.getKind()==SKOLEM ){ //|| !quantifiers::TermUtil::hasInstConstAttr( n ) ){ - return false; - }else{ - for( unsigned i=0; i& vars, std::vector< Node >& patTerms ) { - if( !n.getType().isBoolean() && n.getKind()==APPLY_UF ){ - if( std::find( patTerms.begin(), patTerms.end(), n )==patTerms.end() ){ - bool hasVar = false; - for( unsigned i=0; i& patTerms, quantifiers::TriggerSelMode tstrt, std::vector< Node >& exclude, - std::map< Node, TriggerTermInfo >& tinfo, bool filterInst ){ - std::map< Node, std::vector< Node > > visited; - if( filterInst ){ - //immediately do not consider any term t for which another term is an instance of t - std::vector< Node > patTerms2; - std::map< Node, TriggerTermInfo > tinfo2; - collectPatTerms( q, n, patTerms2, quantifiers::TRIGGER_SEL_ALL, exclude, tinfo2, false ); - std::vector< Node > temp; - temp.insert( temp.begin(), patTerms2.begin(), patTerms2.end() ); - filterTriggerInstances(temp); - if (Trace.isOn("trigger-filter-instance")) - { - if (temp.size() != patTerms2.size()) - { - Trace("trigger-filter-instance") << "Filtered an instance: " - << std::endl; - Trace("trigger-filter-instance") << "Old: "; - for (unsigned i = 0; i < patTerms2.size(); i++) - { - Trace("trigger-filter-instance") << patTerms2[i] << " "; - } - Trace("trigger-filter-instance") << std::endl << "New: "; - for (unsigned i = 0; i < temp.size(); i++) - { - Trace("trigger-filter-instance") << temp[i] << " "; - } - Trace("trigger-filter-instance") << std::endl; - } - } - if( tstrt==quantifiers::TRIGGER_SEL_ALL ){ - for( unsigned i=0; i added; - collectPatTerms2( q, n, visited, tinfo, tstrt, exclude, added, true, true, false, true ); - for( std::map< Node, TriggerTermInfo >::iterator it = tinfo.begin(); it != tinfo.end(); ++it ){ - patTerms.push_back( it->first ); - } -} - -int Trigger::isTriggerInstanceOf(Node n1, - Node n2, - std::vector& fv1, - std::vector& fv2) -{ - Assert(n1 != n2); - int status = 0; - std::unordered_set subs_vars; - std::unordered_set, - PairHashFunction > - visited; - std::vector > visit; - std::pair cur; - TNode cur1; - TNode cur2; - visit.push_back(std::pair(n1, n2)); - do - { - cur = visit.back(); - visit.pop_back(); - if (visited.find(cur) == visited.end()) - { - visited.insert(cur); - cur1 = cur.first; - cur2 = cur.second; - Assert(cur1 != cur2); - // recurse if they have the same operator - if (cur1.hasOperator() && cur2.hasOperator() - && cur1.getNumChildren() == cur2.getNumChildren() - && cur1.getOperator() == cur2.getOperator() - && cur1.getOperator().getKind()!=INST_CONSTANT) - { - visit.push_back(std::pair(cur1, cur2)); - for (unsigned i = 0, size = cur1.getNumChildren(); i < size; i++) - { - if (cur1[i] != cur2[i]) - { - visit.push_back(std::pair(cur1[i], cur2[i])); - } - else if (cur1[i].getKind() == INST_CONSTANT) - { - if (subs_vars.find(cur1[i]) != subs_vars.end()) - { - return 0; - } - // the variable must map to itself in the substitution - subs_vars.insert(cur1[i]); - } - } - } - else - { - bool success = false; - // check if we are in a unifiable instance case - // must be only this case - for (unsigned r = 0; r < 2; r++) - { - if (status == 0 || ((status == 1) == (r == 0))) - { - TNode curi = r == 0 ? cur1 : cur2; - if (curi.getKind() == INST_CONSTANT - && subs_vars.find(curi) == subs_vars.end()) - { - TNode curj = r == 0 ? cur2 : cur1; - // RHS must be a simple trigger - if (getTriggerWeight(curj) == 0) - { - // must occur in the free variables in the other - std::vector& free_vars = r == 0 ? fv2 : fv1; - if (std::find(free_vars.begin(), free_vars.end(), curi) - != free_vars.end()) - { - status = r == 0 ? 1 : -1; - subs_vars.insert(curi); - success = true; - break; - } - } - } - } - } - if (!success) - { - return 0; - } - } - } - } while (!visit.empty()); - return status; -} - -void Trigger::filterTriggerInstances(std::vector& nodes) -{ - std::map > fvs; - for (unsigned i = 0, size = nodes.size(); i < size; i++) - { - quantifiers::TermUtil::computeVarContains(nodes[i], fvs[i]); - } - std::vector active; - active.resize(nodes.size(), true); - for (unsigned i = 0, size = nodes.size(); i < size; i++) - { - std::vector& fvsi = fvs[i]; - if (active[i]) - { - for (unsigned j = i + 1, size2 = nodes.size(); j < size2; j++) - { - if (active[j]) - { - int result = isTriggerInstanceOf(nodes[i], nodes[j], fvsi, fvs[j]); - if (result == 1) - { - Trace("filter-instances") << nodes[j] << " is an instance of " - << nodes[i] << std::endl; - active[i] = false; - break; - } - else if (result == -1) - { - Trace("filter-instances") << nodes[i] << " is an instance of " - << nodes[j] << std::endl; - active[j] = false; - } - } - } - } - } - std::vector temp; - for (unsigned i = 0; i < nodes.size(); i++) - { - if (active[i]) - { - temp.push_back(nodes[i]); - } - } - nodes.clear(); - nodes.insert(nodes.begin(), temp.begin(), temp.end()); -} - -Node Trigger::getInversionVariable( Node n ) { - if( n.getKind()==INST_CONSTANT ){ - return n; - }else if( n.getKind()==PLUS || n.getKind()==MULT ){ - Node ret; - for( unsigned i=0; i(); - if( r!=Rational(-1) && r!=Rational(1) ){ - Trace("var-trigger-debug") << "No : not integer coefficient " << n << std::endl; - return Node::null(); - } - } - */ - } - } - return ret; - }else{ - Trace("var-trigger-debug") << "No : unsupported operator " << n << "." << std::endl; - } - return Node::null(); -} - -Node Trigger::getInversion( Node n, Node x ) { - if( n.getKind()==INST_CONSTANT ){ - return x; - }else if( n.getKind()==PLUS || n.getKind()==MULT ){ - int cindex = -1; - for( unsigned i=0; imkNode( MINUS, x, n[i] ); - }else if( n.getKind()==MULT ){ - Assert( n[i].isConst() ); - if( x.getType().isInteger() ){ - Node coeff = NodeManager::currentNM()->mkConst( n[i].getConst().abs() ); - if( !n[i].getConst().abs().isOne() ){ - x = NodeManager::currentNM()->mkNode( INTS_DIVISION_TOTAL, x, coeff ); - } - if( n[i].getConst().sgn()<0 ){ - x = NodeManager::currentNM()->mkNode( UMINUS, x ); - } - }else{ - Node coeff = NodeManager::currentNM()->mkConst( Rational(1) / n[i].getConst() ); - x = NodeManager::currentNM()->mkNode( MULT, x, coeff ); - } - } - x = Rewriter::rewrite( x ); - }else{ - Assert( cindex==-1 ); - cindex = i; - } - } - Assert( cindex!=-1 ); - return getInversion( n[cindex], x ); - } - return Node::null(); -} - -void Trigger::getTriggerVariables(Node n, Node q, std::vector& t_vars) -{ - std::vector< Node > patTerms; - std::map< Node, TriggerTermInfo > tinfo; - // collect all patterns from n - std::vector< Node > exclude; - collectPatTerms(q, n, patTerms, quantifiers::TRIGGER_SEL_ALL, exclude, tinfo); - //collect all variables from all patterns in patTerms, add to t_vars - for( unsigned i=0; igetActiveScore( d_quantEngine ); -} - -TriggerTrie::TriggerTrie() -{} - -TriggerTrie::~TriggerTrie() { - for(std::map< TNode, TriggerTrie* >::iterator i = d_children.begin(), iend = d_children.end(); - i != iend; ++i) { - TriggerTrie* current = (*i).second; - delete current; - } - d_children.clear(); - - for( unsigned i=0; i& nodes) -{ - std::vector temp; - temp.insert(temp.begin(), nodes.begin(), nodes.end()); - std::sort(temp.begin(), temp.end()); - TriggerTrie* tt = this; - for (const Node& n : temp) - { - std::map::iterator itt = tt->d_children.find(n); - if (itt == tt->d_children.end()) - { - return NULL; - } - else - { - tt = itt->second; - } - } - return tt->d_tr.empty() ? NULL : tt->d_tr[0]; -} - -void TriggerTrie::addTrigger(std::vector& nodes, inst::Trigger* t) -{ - std::vector temp; - temp.insert(temp.begin(), nodes.begin(), nodes.end()); - std::sort(temp.begin(), temp.end()); - TriggerTrie* tt = this; - for (const Node& n : temp) - { - std::map::iterator itt = tt->d_children.find(n); - if (itt == tt->d_children.end()) - { - TriggerTrie* ttn = new TriggerTrie; - tt->d_children[n] = ttn; - tt = ttn; - } - else - { - tt = itt->second; - } - } - tt->d_tr.push_back(t); -} - -}/* CVC4::theory::inst namespace */ -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ diff --git a/src/theory/quantifiers/trigger.h b/src/theory/quantifiers/trigger.h deleted file mode 100644 index e897c0b06..000000000 --- a/src/theory/quantifiers/trigger.h +++ /dev/null @@ -1,476 +0,0 @@ -/********************* */ -/*! \file trigger.h - ** \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 trigger class - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__THEORY__QUANTIFIERS__TRIGGER_H -#define __CVC4__THEORY__QUANTIFIERS__TRIGGER_H - -#include - -#include "expr/node.h" -#include "theory/quantifiers/inst_match.h" -#include "options/quantifiers_options.h" - -namespace CVC4 { -namespace theory { - -class QuantifiersEngine; - -namespace inst { - -class IMGenerator; -class InstMatchGenerator; - -/** Information about a node used in a trigger. -* -* This information includes: -* 1. the free variables of the node, and -* 2. information about which terms are useful for matching. -* -* As an example, consider the trigger -* { f( x ), g( y ), P( y, z ) } -* for quantified formula -* forall xy. ( f( x ) != b => ( P( x, g( y ) ) V P( y, z ) ) ) -* -* Notice that it is only useful to match f( x ) to f-applications not in the -* equivalence class of b, and P( y, z ) to P-applications not in the equivalence -* class of true, as such instances will always be entailed by the ground -* equalities and disequalities the current context. Entailed instances are -* typically not helpful, and are discarded in Instantiate::addInstantiation(...) -* unless the option --no-inst-no-entail is enabled. For more details, see page -* 10 of "Congruence Closure with Free Variables", Barbosa et al., TACAS 2017. -* -* This example is referenced for each of the functions below. -*/ -class TriggerTermInfo { - public: - TriggerTermInfo() : d_reqPol(0), d_weight(0) {} - ~TriggerTermInfo() {} - /** The free variables in the node - * - * In the trigger term info for f( x ) in the above example, d_fv = { x } - * In the trigger term info for g( y ) in the above example, d_fv = { y } - * In the trigger term info for P( y, z ) in the above example, d_fv = { y, z } - */ - std::vector d_fv; - /** Required polarity: 1 for equality, -1 for disequality, 0 for none - * - * In the trigger term info for f( x ) in the above example, d_reqPol = -1 - * In the trigger term info for g( y ) in the above example, d_reqPol = 0 - * In the trigger term info for P( y, z ) in the above example, d_reqPol = 1 - */ - int d_reqPol; - /** Required polarity equal term - * - * If d_reqPolEq is not null, - * if d_reqPol = 1, then this trigger term must be matched to terms in the - * equivalence class of d_reqPolEq, - * if d_reqPol = -1, then this trigger term must be matched to terms *not* in - * the equivalence class of d_reqPolEq. - * - * This information is typically chosen by analyzing the entailed equalities - * and disequalities of quantified formulas. - * In the trigger term info for f( x ) in the above example, d_reqPolEq = b - * In the trigger term info for g( y ) in the above example, - * d_reqPolEq = Node::null() - * In the trigger term info for P( y, z ) in the above example, - * d_reqPolEq = false - */ - Node d_reqPolEq; - /** the weight of the trigger (see Trigger::getTriggerWeight). */ - int d_weight; - /** Initialize this information class (can be called more than once). - * q is the quantified formula that n is a trigger term for - * n is the trigger term - * reqPol/reqPolEq are described above - */ - void init(Node q, Node n, int reqPol = 0, Node reqPolEq = Node::null()); -}; - -/** A collection of nodes representing a trigger. -* -* This class encapsulates all implementations of E-matching in CVC4. -* Its primary use is as a utility of the quantifiers module InstantiationEngine -* (see theory/quantifiers/instantiation_engine.h) which uses Trigger to make -* appropriate calls to Instantiate::addInstantiation(...) -* (see theory/instantiate.h) for the instantiate utility of the quantifiers -* engine (d_quantEngine) associated with this trigger. These calls -* queue instantiation lemmas to the output channel of TheoryQuantifiers during -* a full effort check. -* -* Concretely, a Trigger* t is used in the following way during a full effort -* check. Assume that t is associated with quantified formula q (see field d_f). -* We call : -* -* // setup initial information -* t->resetInstantiationRound(); -* // will produce instantiations based on matching with all terms -* t->reset( Node::null() ); -* // add all instantiations based on E-matching with this trigger and the -* // current context -* t->addInstantiations(); -* -* This will result in (a set of) calls to -* Instantiate::addInstantiation(q, m1)...Instantiate::addInstantiation(q, mn), -* where m1...mn are InstMatch objects. These calls add the corresponding -* instantiation lemma for (q,mi) on the output channel associated with -* d_quantEngine. -* -* The Trigger class is wrapper around an underlying IMGenerator class, which -* implements various forms of E-matching for its set of nodes (d_nodes), which -* is refered to in the literature as a "trigger". A trigger is a set of terms -* whose free variables are the bound variables of a quantified formula q, -* and that is used to guide instantiations for q (for example, see "Efficient -* E-Matching for SMT Solvers" by de Moura et al). -* -* For example of an instantiation lemma produced by E-matching : -* -* quantified formula : forall x. P( x ) -* trigger : P( x ) -* ground context : ~P( a ) -* -* Then E-matching matches P( x ) and P( a ), resulting in the match { x -> a } -* which is used to generate the instantiation lemma : -* (forall x. P( x )) => P( a ) -* -* Terms that are provided as input to a Trigger class via mkTrigger -* should be in "instantiation constant form", see TermUtil::getInstConstantNode. -* Say we have quantified formula q whose AST is the Node -* (FORALL -* (BOUND_VAR_LIST x) -* (NOT (P x)) -* (INST_PATTERN_LIST (INST_PATTERN (P x)))) -* then TermUtil::getInstConstantNode( q, (P x) ) = (P IC) where -* IC = TermUtil::getInstantiationConstant( q, i ). -* Trigger expects as input (P IC) to represent the Trigger (P x). This form -* ensures that references to bound variables are unique to quantified formulas, -* which is required to ensure the correctness of instantiation lemmas we -* generate. -* -*/ -class Trigger { - friend class IMGenerator; - - public: - virtual ~Trigger(); - /** get the generator associated with this trigger */ - IMGenerator* getGenerator() { return d_mg; } - /** Reset instantiation round. - * - * Called once at beginning of an instantiation round. - */ - void resetInstantiationRound(); - /** Reset the trigger. - * - * eqc is the equivalence class from which to match ground terms. If eqc is - * Node::null(), then we match ground terms from all equivalence classes. - */ - void reset( Node eqc ); - /** add all available instantiations, based on the current context - * - * This function makes the appropriate calls to d_qe->addInstantiation(...) - * based on the current ground terms and equalities in the current context, - * via queries to functions in d_qe. This calls the addInstantiations function - * of the underlying match generator. It can be extended to - * produce instantiations beyond what is produced by the match generator - * (for example, see theory/quantifiers/ho_trigger.h). - */ - virtual int addInstantiations(); - /** Return whether this is a multi-trigger. */ - bool isMultiTrigger() { return d_nodes.size()>1; } - /** Get instantiation pattern list associated with this trigger. - * - * An instantiation pattern list is the node representation of a trigger, in - * particular, it is the third argument of quantified formulas which have user - * (! ... :pattern) attributes. - */ - Node getInstPattern(); - /* A heuristic value indicating how active this generator is. - * - * This returns the number of ground terms for the match operators in terms - * from d_nodes. This score is only used with the experimental option - * --trigger-active-sel. - */ - int getActiveScore(); - /** print debug information for the trigger */ - void debugPrint(const char* c) - { - Trace(c) << "TRIGGER( "; - for (int i = 0; i < (int)d_nodes.size(); i++) - { - if (i > 0) - { - Trace(c) << ", "; - } - Trace(c) << d_nodes[i]; - } - Trace(c) << " )"; - } - /** mkTrigger method - * - * This makes an instance of a trigger object. - * qe : pointer to the quantifier engine; - * q : the quantified formula we are making a trigger for - * nodes : the nodes comprising the (multi-)trigger - * keepAll: don't remove unneeded patterns; - * trOption : policy for dealing with triggers that already exist - * (see below) - * use_n_vars : number of variables that should be bound by the trigger - * typically, the number of quantified variables in q. - */ - enum{ - TR_MAKE_NEW, //make new trigger even if it already may exist - TR_GET_OLD, //return a previous trigger if it had already been created - TR_RETURN_NULL //return null if a duplicate is found - }; - static Trigger* mkTrigger(QuantifiersEngine* qe, - Node q, - std::vector& nodes, - bool keepAll = true, - int trOption = TR_MAKE_NEW, - unsigned use_n_vars = 0); - /** single trigger version that calls the above function */ - static Trigger* mkTrigger(QuantifiersEngine* qe, - Node q, - Node n, - bool keepAll = true, - int trOption = TR_MAKE_NEW, - unsigned use_n_vars = 0); - /** make trigger terms - * - * This takes a set of eligible trigger terms and stores a subset of them in - * trNodes, such that : - * (1) the terms in trNodes contain at least n_vars of the quantified - * variables in quantified formula q, and - * (2) the set trNodes is minimal, i.e. removing one term from trNodes - * always violates (1). - */ - static bool mkTriggerTerms(Node q, - std::vector& nodes, - unsigned n_vars, - std::vector& trNodes); - /** collect pattern terms - * - * This collects all terms that are eligible for triggers for quantified - * formula q in term n and adds them to patTerms. - * tstrt : the selection strategy (see options/quantifiers_mode.h), - * exclude : a set of terms that *cannot* be selected as triggers, - * tinfo : stores the result of the collection, mapping terms to the - * information they are associated with, - * filterInst : flag that when true, we discard terms that have instances - * in the vector we are returning, e.g. we do not return f( x ) if we are - * also returning f( f( x ) ). TODO: revisit this (issue #1211) - */ - static void collectPatTerms( Node q, Node n, std::vector< Node >& patTerms, quantifiers::TriggerSelMode tstrt, - std::vector< Node >& exclude, std::map< Node, TriggerTermInfo >& tinfo, - bool filterInst = false ); - - /** Is n a usable trigger in quantified formula q? - * - * A usable trigger is one that is matchable and contains free variables only - * from q. - */ - static bool isUsableTrigger( Node n, Node q ); - /** get is usable trigger - * - * Return the associated node of n that is a usable trigger in quantified - * formula q. This may be different than n in several cases : - * (1) Polarity information is explicitly converted to equalities, e.g. - * getIsUsableTrigger( (not (P x )), q ) may return (= (P x) false) - * (2) Relational triggers are put into solved form, e.g. - * getIsUsableTrigger( (= (+ x a) 5), q ) may return (= x (- 5 a)). - */ - static Node getIsUsableTrigger( Node n, Node q ); - /** Is n a usable atomic trigger? - * - * A usable atomic trigger is a term that is both a useable trigger and an - * atomic trigger. - */ - static bool isUsableAtomicTrigger( Node n, Node q ); - /** is n an atomic trigger? - * - * An atomic trigger is one whose kind is an atomic trigger kind. - */ - static bool isAtomicTrigger( Node n ); - /** Is k an atomic trigger kind? - * - * An atomic trigger kind is one for which term indexing/matching is possible. - */ - static bool isAtomicTriggerKind( Kind k ); - /** is n a relational trigger, e.g. like x >= a ? */ - static bool isRelationalTrigger( Node n ); - /** Is k a relational trigger kind? */ - static bool isRelationalTriggerKind( Kind k ); - /** Is k a kind for which counterexample-guided instantiation is possible? - * - * This typically corresponds to kinds that correspond to operators that - * have total interpretations and are a part of the signature of - * satisfaction complete theories (see Reynolds et al., CAV 2015). - */ - static bool isCbqiKind( Kind k ); - /** is n a simple trigger (see inst_match_generator.h)? */ - static bool isSimpleTrigger( Node n ); - /** is n a Boolean term trigger, e.g. ite( x, BV1, BV0 )? */ - static bool isBooleanTermTrigger( Node n ); - /** is n a pure theory trigger, e.g. head( x )? */ - static bool isPureTheoryTrigger( Node n ); - /** get trigger weight - * - * Returns 0 for triggers that are easy to process and 1 otherwise. - * A trigger is easy to process if it is an atomic trigger, or a relational - * trigger of the form x ~ g for ~ \in { =, >=, > }. - */ - static int getTriggerWeight( Node n ); - /** Returns whether n is a trigger term with a local theory extension - * property from Bansal et al., CAV 2015. - */ - static bool isLocalTheoryExt( Node n, std::vector< Node >& vars, - std::vector< Node >& patTerms ); - /** get the variable associated with an inversion for n - * - * A term n with an inversion variable x has the following property : - * There exists a closed function f such that for all terms t - * |= (n = t) <=> (x = f(t)) - * For example, getInversionVariable( x+1 ) returns x since for all terms t, - * |= x+1 = t <=> x = (\y. y-1)(t) - * We call f the inversion function for n. - */ - static Node getInversionVariable( Node n ); - /** Get the body of the inversion function for n whose argument is y. - * e.g. getInversion( x+1, y ) returns y-1 - */ - static Node getInversion(Node n, Node y); - /** get all variables that E-matching can instantiate from a subterm n. - * - * This returns the union of all free variables in usable triggers that are - * subterms of n. - */ - static void getTriggerVariables(Node n, Node f, std::vector& t_vars); - - protected: - /** trigger constructor, intentionally protected (use Trigger::mkTrigger). */ - Trigger(QuantifiersEngine* ie, Node q, std::vector& nodes); - /** is subterm of trigger usable (helper function for isUsableTrigger) */ - static bool isUsable( Node n, Node q ); - /** returns an equality that is equivalent to the equality eq and - * is a usable trigger for q if one exists, otherwise returns Node::null(). - */ - static Node getIsUsableEq( Node q, Node eq ); - /** returns whether n1 == n2 is a usable (relational) trigger for q. */ - static bool isUsableEqTerms( Node q, Node n1, Node n2 ); - /** recursive helper function for collectPatTerms - * - * This collects the usable trigger terms in the subterm n of the body of - * quantified formula q. - * visited : cache of the trigger terms collected for each visited node, - * tinfo : cache of trigger term info for each visited node, - * tstrat : the selection strategy (see options/quantifiers_mode.h) - * exclude : a set of terms that *cannot* be selected as triggers - * pol/hasPol : the polarity of node n in q - * (see QuantPhaseReq theory/quantifiers/quant_util.h) - * epol/hasEPol : the entailed polarity of node n in q - * (see QuantPhaseReq theory/quantifiers/quant_util.h) - * knowIsUsable : whether we know that n is a usable trigger. - * - * We add the triggers we collected recursively in n into added. - */ - static void collectPatTerms2( Node q, Node n, std::map< Node, std::vector< Node > >& visited, std::map< Node, TriggerTermInfo >& tinfo, - quantifiers::TriggerSelMode tstrt, std::vector< Node >& exclude, std::vector< Node >& added, - bool pol, bool hasPol, bool epol, bool hasEPol, bool knowIsUsable = false ); - - /** filter all nodes that have trigger instances - * - * This is used during collectModelInfo to filter certain trigger terms, - * stored in nodes. This updates nodes so that no pairs of distinct nodes - * (i,j) is such that i is a trigger instance of j or vice versa (see below). - */ - static void filterTriggerInstances(std::vector& nodes); - - /** is instance of - * - * We say a term t is an trigger instance of term s if - * (1) t = s * { x1 -> t1 ... xn -> tn } - * (2) { x1, ..., xn } are a subset of FV( t ). - * For example, f( g( h( x, x ) ) ) and f( g( x ) ) are instances of f( x ), - * but f( g( y ) ) and g( x ) are not instances of f( x ). - * - * When this method returns -1, n1 is an instance of n2, - * When this method returns 1, n1 is an instance of n2. - * - * The motivation for this method is to discard triggers s that are less - * restrictive (criteria (1)) and serve to bind the same variables (criteria - * (2)) as another trigger t. This often helps avoiding matching loops. - */ - static int isTriggerInstanceOf(Node n1, - Node n2, - std::vector& fv1, - std::vector& fv2); - - /** add an instantiation (called by InstMatchGenerator) - * - * This calls Instantiate::addInstantiation(...) for instantiations - * associated with m. Typically, m is associated with a single instantiation, - * but in some cases (e.g. higher-order) we may modify m before calling - * Instantiate::addInstantiation(...). - */ - virtual bool sendInstantiation(InstMatch& m); - /** The nodes comprising this trigger. */ - std::vector< Node > d_nodes; - /** The quantifiers engine associated with this trigger. */ - QuantifiersEngine* d_quantEngine; - /** The quantified formula this trigger is for. */ - Node d_quant; - /** match generator - * - * This is the back-end utility that implements the underlying matching - * algorithm associated with this trigger. - */ - IMGenerator* d_mg; -}; /* class Trigger */ - -/** A trie of triggers. -* -* This class is used to cache all Trigger objects that are generated in the -* current context. We index Triggers in this data structure based on the -* value of Trigger::d_nodes. When a Trigger is added to this data structure, -* this Trie assumes responsibility for deleting it. -*/ -class TriggerTrie { -public: - TriggerTrie(); - ~TriggerTrie(); - /** get trigger - * This returns a Trigger t that is indexed by nodes, - * or NULL otherwise. - */ - Trigger* getTrigger(std::vector& nodes); - /** add trigger - * This adds t to the trie, indexed by nodes. - * In typical use cases, nodes is t->d_nodes. - */ - void addTrigger(std::vector& nodes, Trigger* t); - - private: - /** The trigger at this node in the trie. */ - std::vector d_tr; - /** The children of this node in the trie. */ - std::map< TNode, TriggerTrie* > d_children; -};/* class inst::Trigger::TriggerTrie */ - -}/* CVC4::theory::inst namespace */ -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ - -#endif /* __CVC4__THEORY__QUANTIFIERS__TRIGGER_H */ diff --git a/src/theory/quantifiers_engine.cpp b/src/theory/quantifiers_engine.cpp index a0efe80f9..60f44c256 100644 --- a/src/theory/quantifiers_engine.cpp +++ b/src/theory/quantifiers_engine.cpp @@ -20,25 +20,25 @@ #include "theory/arrays/theory_arrays.h" #include "theory/datatypes/theory_datatypes.h" #include "theory/quantifiers/alpha_equivalence.h" -#include "theory/quantifiers/ambqi_builder.h" +#include "theory/quantifiers/fmf/ambqi_builder.h" #include "theory/quantifiers/anti_skolem.h" -#include "theory/quantifiers/bounded_integers.h" -#include "theory/quantifiers/ce_guided_instantiation.h" -#include "theory/quantifiers/ceg_t_instantiator.h" +#include "theory/quantifiers/fmf/bounded_integers.h" +#include "theory/quantifiers/sygus/ce_guided_instantiation.h" +#include "theory/quantifiers/cegqi/ceg_t_instantiator.h" #include "theory/quantifiers/conjecture_generator.h" #include "theory/quantifiers/equality_infer.h" #include "theory/quantifiers/equality_query.h" #include "theory/quantifiers/first_order_model.h" -#include "theory/quantifiers/full_model_check.h" +#include "theory/quantifiers/fmf/full_model_check.h" #include "theory/quantifiers/fun_def_engine.h" #include "theory/quantifiers/inst_propagator.h" -#include "theory/quantifiers/inst_strategy_cbqi.h" -#include "theory/quantifiers/inst_strategy_e_matching.h" +#include "theory/quantifiers/cegqi/inst_strategy_cbqi.h" +#include "theory/quantifiers/ematching/inst_strategy_e_matching.h" #include "theory/quantifiers/inst_strategy_enumerative.h" #include "theory/quantifiers/instantiate.h" -#include "theory/quantifiers/instantiation_engine.h" +#include "theory/quantifiers/ematching/instantiation_engine.h" #include "theory/quantifiers/local_theory_ext.h" -#include "theory/quantifiers/model_engine.h" +#include "theory/quantifiers/fmf/model_engine.h" #include "theory/quantifiers/quant_conflict_find.h" #include "theory/quantifiers/quant_epr.h" #include "theory/quantifiers/quant_equality_engine.h" @@ -50,10 +50,10 @@ #include "theory/quantifiers/rewrite_engine.h" #include "theory/quantifiers/skolemize.h" #include "theory/quantifiers/term_database.h" -#include "theory/quantifiers/term_database_sygus.h" +#include "theory/quantifiers/sygus/term_database_sygus.h" #include "theory/quantifiers/term_enumeration.h" #include "theory/quantifiers/term_util.h" -#include "theory/quantifiers/trigger.h" +#include "theory/quantifiers/ematching/trigger.h" #include "theory/sep/theory_sep.h" #include "theory/theory_engine.h" #include "theory/uf/equality_engine.h" diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp index edbd768d7..c62996931 100644 --- a/src/theory/theory_engine.cpp +++ b/src/theory/theory_engine.cpp @@ -40,7 +40,7 @@ #include "theory/care_graph.h" #include "theory/ite_utilities.h" #include "theory/quantifiers/first_order_model.h" -#include "theory/quantifiers/model_engine.h" +#include "theory/quantifiers/fmf/model_engine.h" #include "theory/quantifiers/theory_quantifiers.h" #include "theory/quantifiers_engine.h" #include "theory/rewriter.h" diff --git a/test/unit/theory/theory_quantifiers_bv_instantiator_white.h b/test/unit/theory/theory_quantifiers_bv_instantiator_white.h index 1e6578b27..27a676066 100644 --- a/test/unit/theory/theory_quantifiers_bv_instantiator_white.h +++ b/test/unit/theory/theory_quantifiers_bv_instantiator_white.h @@ -22,7 +22,7 @@ #include "theory/rewriter.h" #include "util/bitvector.h" -#include "theory/quantifiers/ceg_t_instantiator.cpp" +#include "theory/quantifiers/cegqi/ceg_t_instantiator.cpp" #include #include