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 \
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 \
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 \
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 \
#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"
#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"
#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"
#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"
+++ /dev/null
-/********************* */
-/*! \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<fapps.size(); i++ ){
- // std::cout << "...." << fapps[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; i<fapps.size(); i++ ){
- unsigned r = m->getRepresentativeId( 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()<minSize ){
- minSize = it->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; i<to_erase.size(); i++ ){
- d_def.erase( to_erase[i] );
- }
- }
- //if constant, propagate the value upwards
- if( isConstant ){
- d_value = defVal;
- }else{
- d_value = val_none;
- }
- }
-}
-
-void AbsDef::debugPrintUInt( const char * c, unsigned dSize, unsigned u ) const{
- for( unsigned i=0; i<dSize; i++ ){
- Trace(c) << ( ( u & ( 1 << i ) )!=0 ? "1" : "0");
- }
- //Trace(c) << "(";
- //for( unsigned i=0; i<32; i++ ){
- // Trace(c) << ( ( u & ( 1 << i ) )!=0 ? "1" : "0");
- //}
- //Trace(c) << ")";
-}
-
-void AbsDef::debugPrint( const char * c, FirstOrderModelAbs * m, TNode f, unsigned depth ) const{
- if( Trace.isOn(c) ){
- if( depth==f.getNumChildren() ){
- for( unsigned i=0; i<depth; i++ ){ Trace(c) << " ";}
- Trace(c) << "V[" << d_value << "]" << std::endl;
- }else{
- TypeNode tn = f[depth].getType();
- const RepSet* rs = m->getRepSet();
- 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; i<depth; i++ ){ Trace(c) << " ";}
- debugPrintUInt( c, dSize, it->first );
- 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<defs.size(); i++ ){
- //process each simple child
- for( std::map< unsigned, AbsDef >::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; j<defs.size(); j++ ){
- defs[j]->get_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; j<defs.size(); j++ ){
- defs[j]->get_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( depth<n.getNumChildren() ){
- TypeNode tn = q.isNull() ? n[depth].getType() : m->getVariable( 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; i<entry.size(); i++ ){
- unsigned dSize =
- rs->getNumRepresentatives(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; i<numReps; i++ ){
- curr = 1 << i;
- d_def[curr].construct_var_eq( m, q, v1, v2, curr, currv, depth+1 );
- }
- d_default = curr;
- }else{
- d_def[curr].construct_var_eq( m, q, v1, v2, curr, 1, depth+1 );
- dom = dom & ~curr;
- d_def[dom].construct_var_eq( m, q, v1, v2, curr, 0, depth+1 );
- d_default = dom;
- }
- }
- }
-}
-
-void AbsDef::construct_var( FirstOrderModelAbs * m, TNode q, unsigned v, int currv, unsigned depth ) {
- if( depth==q[0].getNumChildren() ){
- Assert( currv!=val_none );
- d_value = currv;
- }else{
- TypeNode tn = m->getVariable( q, depth ).getType();
- if( v==depth ){
- unsigned numReps = m->getRepSet()->getNumRepresentatives(tn);
- Assert( numReps>0 && numReps < 32 );
- for( unsigned i=0; i<numReps; i++ ){
- d_def[ 1 << i ].construct_var( m, q, v, i, depth+1 );
- }
- d_default = 1 << (numReps - 1);
- }else{
- unsigned dom = m->d_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; i<count; i++ ){
- entry.push_back( m->d_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<count; i++ ){
- entry.pop_back();
- entry_def.pop_back();
- }
- return;
- }
- }
- }
- if( entry.size()==q[0].getNumChildren() ){
- if( f ){
- if( Trace.isOn("ambqi-check-debug2") ){
- for( unsigned i=0; i<entry.size(); i++ ){ Trace("ambqi-check-debug2") << " "; }
- Trace("ambqi-check-debug2") << "Evaluate uninterpreted function entry..." << std::endl;
- }
- //we are composing with an uninterpreted function
- std::vector< int > 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; i<entry.size(); i++ ){ Trace("ambqi-check-debug2") << " "; }
- Trace("ambqi-check-debug2") << "Evaluate interpreted function entry ( ";
- for( unsigned i=0; i<values.size(); i++ ){
- Assert( !values[i].isNull() );
- Trace("ambqi-check-debug2") << values[i] << " ";
- }
- Trace("ambqi-check-debug2") << ")..." << std::endl;
- }
- //evaluate
- Node vv = NodeManager::currentNM()->mkNode( 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<entry.size(); i++ ){ Trace("ambqi-check-debug2") << " "; }
- Trace("ambqi-check-debug2") << "Take product of arguments" << std::endl;
- }
- for( std::map< unsigned, AbsDef * >::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; i<entry.size(); i++ ){ Trace("ambqi-check-debug2") << " "; }
- Trace("ambqi-check-debug2") << "...process : ";
- debugPrintUInt("ambqi-check-debug2",
- rs->getNumRepresentatives(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<entry.size(); i++ ){ Trace("ambqi-check-debug2") << " "; }
- Trace("ambqi-check-debug2") << "Make default argument" << std::endl;
- }
- std::map< unsigned, AbsDef * > 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; i<entry.size(); i++ ){ Trace("ambqi-check-debug2") << " "; }
- Trace("ambqi-check-debug2") << "...process default : ";
- debugPrintUInt(
- "ambqi-check-debug2", rs->getNumRepresentatives(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<n.getNumChildren(); i++ ){
- Trace("ambqi-check-debug3") << i << " : ";
- Trace("ambqi-check-debug3") << ((children.find( i )!=children.end()) ? "X" : ".");
- if( bchildren.find( i )!=bchildren.end() ){
- Trace("ambqi-check-debug3") << bchildren[i];
- }else{
- Trace("ambqi-check-debug3") << ".";
- }
- if( vchildren.find( i )!=vchildren.end() ){
- Trace("ambqi-check-debug3") << vchildren[i];
- }else{
- Trace("ambqi-check-debug3") << ".";
- }
- Trace("ambqi-check-debug3") << std::endl;
- }
- Trace("ambqi-check-debug3") << "varChCount : " << varChCount << std::endl;
- }
- if( varChCount==0 || f ){
- //short-circuit
- if( n.getKind()==AND || n.getKind()==OR ){
- for( std::map< unsigned, int >::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; i<args.size(); i++ ){
- unsigned v = 1 << m->getRepresentativeId( 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<TypeNode, std::vector<Node> >::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; i<it->second.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<Node, AbsDef * >::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; i<itut->second.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; i<fapps[0].getNumChildren(); i++ ){
- if( fm->d_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; i<fm->d_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<Node, AbsDef * >::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; i<q[0].getNumChildren(); i++ ){
- if( !fma->isValidType( 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; i<n.getNumChildren(); i++ ){
- if( n[i].getKind()==FORALL ){
- bchildren[i] = AbsDef::val_unk;
- }else if( n[i].getKind() == BOUND_VARIABLE ){
- varChCount++;
- vchildren[i] = m->d_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 */
+++ /dev/null
-/********************* */
-/*! \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
+++ /dev/null
-/********************* */
-/*! \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 || vrange<d_curr_range ){
- Trace("bound-int-bound") << "Successfully bound " << d_range << " to " << vrange << std::endl;
- d_curr_range = vrange;
- }
- //set the range
- d_has_range = true;
- }
- }else{
- Message() << "Could not find literal " << nlit << " for range " << d_range << std::endl;
- exit(0);
- }
-}
-
-void BoundedIntegers::IntRangeModel::allocateRange() {
- d_curr_max++;
- int newBound = d_curr_max;
- Trace("bound-int-proc") << "Allocate range bound " << newBound << " for " << d_range << std::endl;
- //TODO: newBound should be chosen in a smarter way
- Node ltr = NodeManager::currentNM()->mkNode( 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 || i<d_curr_range ){
- Node rn = (*it).first;
- Assert( !rn.isNull() );
- if( d_range_assertions.find( rn )==d_range_assertions.end() ){
- if (!d_lit_to_pol[rn]) {
- rn = rn.negate();
- }
- Trace("bound-int-dec-debug") << "For " << d_range << ", make decision " << rn << " to make range " << i << std::endl;
- return rn;
- }
- }
- }
- return Node::null();
-}
-
-bool BoundedIntegers::IntRangeModel::proxyCurrentRange() {
- //Trace("model-engine") << "Range(" << d_range << ") currently is " << d_curr_max.get() << std::endl;
- if( d_range!=d_proxy_range ){
- //int curr = d_curr_range.get();
- int curr = d_curr_max;
- if( d_ranges_proxied.find( curr )==d_ranges_proxied.end() ){
- d_ranges_proxied[curr] = true;
- Assert( d_range_literal.find( curr )!=d_range_literal.end() );
- Node lem = NodeManager::currentNM()->mkNode( 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<b.getNumChildren(); i++ ){
- if( hasNonBoundVar( f, b[i], visited ) ){
- return true;
- }
- }
- }
- }
- return false;
-}
-bool BoundedIntegers::hasNonBoundVar( Node f, Node b ) {
- std::map< Node, bool > 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<n.getNumChildren(); i++ ){
- processMatchBoundVars( q, n[i], bvs, visited );
- }
- }
- }
-}
-
-void BoundedIntegers::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 ){
- if( n.getKind()==OR || n.getKind()==AND ){
- if( (n.getKind()==OR)==pol ){
- for( unsigned i=0; i<n.getNumChildren(); i++) {
- process( q, n[i], pol, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term, bound_fixed_set );
- }
- }else{
- //if we are ( x != t1 ^ ...^ x != tn ), then x can be bound to { t1...tn }
- Node conj = n;
- if( !pol ){
- conj = TermUtil::simpleNegate( conj );
- }
- Trace("bound-int-debug") << "Process possible finite disequality conjunction : " << conj << std::endl;
- Assert( conj.getKind()==AND );
- Node v;
- std::vector< Node > v_cases;
- bool success = true;
- for( unsigned i=0; i<conj.getNumChildren(); i++ ){
- if( conj[i].getKind()==NOT && processEqDisjunct( q, conj[i][0], v, v_cases ) ){
- //continue
- }else{
- Trace("bound-int-debug") << "...failed due to " << conj[i] << std::endl;
- success = false;
- break;
- }
- }
- if( success && !isBound( q, v ) ){
- Trace("bound-int-debug") << "Success with variable " << v << std::endl;
- bound_lit_type_map[v] = BOUND_FIXED_SET;
- bound_lit_map[3][v] = n;
- bound_lit_pol_map[3][v] = pol;
- bound_fixed_set[v].clear();
- bound_fixed_set[v].insert( bound_fixed_set[v].end(), v_cases.begin(), v_cases.end() );
- }
- }
- }else if( n.getKind()==EQUAL ){
- if( !pol ){
- // non-applied DER on x != t, x can be bound to { t }
- Node v;
- std::vector< Node > 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; i<bound_vars.size(); i++ ){
- Node v = bound_vars[i];
- Trace("bound-int-debug") << "literal (polarity = " << pol << ") " << n << " is membership." << std::endl;
- bound_lit_type_map[v] = BOUND_SET_MEMBER;
- bound_lit_map[2][v] = n;
- bound_lit_pol_map[2][v] = pol;
- }
- }
- }else{
- Assert( n.getKind()!=LEQ && n.getKind()!=LT && n.getKind()!=GT );
- }
-}
-
-bool BoundedIntegers::needsCheck( Theory::Effort e ) {
- return e==Theory::EFFORT_LAST_CALL;
-}
-
-void BoundedIntegers::check(Theory::Effort e, QEffort quant_e)
-{
- if (quant_e == QEFFORT_STANDARD)
- {
- Trace("bint-engine") << "---Bounded Integers---" << std::endl;
- bool addedLemma = false;
- //make sure proxies are up-to-date with range
- for( unsigned i=0; i<d_ranges.size(); i++) {
- if( d_rms[d_ranges[i]]->proxyCurrentRange() ){
- 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; i<bound_fixed_set[v].size(); i++ ){
- Node t = bound_fixed_set[v][i];
- if( t.hasBoundVar() ){
- d_fixed_set_ngr_range[f][v].push_back( t );
- }else{
- d_fixed_set_gr_range[f][v].push_back( t );
- }
- }
- Trace("bound-int") << "Variable " << v << " is bound because of disequality conjunction " << bound_lit_map[3][v] << std::endl;
- }
- if( setBoundVar ){
- success = true;
- //set Attributes on literals
- for( unsigned b=0; b<2; b++ ){
- if( bound_lit_map[b].find( v )!=bound_lit_map[b].end() ){
- Assert( bound_lit_pol_map[b].find( v )!=bound_lit_pol_map[b].end() );
- BoundIntLitAttribute bila;
- bound_lit_map[b][v].setAttribute( bila, bound_lit_pol_map[b][v] ? 1 : 0 );
- }else{
- Assert( it->second!=BOUND_INT_RANGE );
- }
- }
- }
- }
- }
- if( !success ){
- //resort to setting a finite bound on a variable
- for( unsigned i=0; i<f[0].getNumChildren(); i++) {
- if( d_bound_type[f].find( f[0][i] )==d_bound_type[f].end() ){
- TypeNode tn = f[0][i].getType();
- if (tn.isSort()
- || d_quantEngine->getTermEnumeration()->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<f[0].getNumChildren(); i++) {
- Node v = f[0][i];
- if( std::find( d_set[f].begin(), d_set[f].end(), v )!=d_set[f].end() ){
- Assert( d_bound_type[f].find( v )!=d_bound_type[f].end() );
- if( d_bound_type[f][v]==BOUND_INT_RANGE ){
- Trace("bound-int") << " " << d_bounds[0][f][v] << " <= " << v << " <= " << d_bounds[1][f][v] << " (range is " << d_range[f][v] << ")" << std::endl;
- }else if( d_bound_type[f][v]==BOUND_SET_MEMBER ){
- if( d_setm_range_lit[f][v][0]==v ){
- Trace("bound-int") << " " << v << " in " << d_setm_range[f][v] << std::endl;
- }else{
- Trace("bound-int") << " " << v << " unifiable in " << d_setm_range_lit[f][v] << std::endl;
- }
- }else if( d_bound_type[f][v]==BOUND_FIXED_SET ){
- Trace("bound-int") << " " << v << " in { ";
- for( unsigned i=0; i<d_fixed_set_ngr_range[f][v].size(); i++ ){
- Trace("bound-int") << d_fixed_set_ngr_range[f][v][i] << " ";
- }
- for( unsigned i=0; i<d_fixed_set_gr_range[f][v].size(); i++ ){
- Trace("bound-int") << d_fixed_set_gr_range[f][v][i] << " ";
- }
- Trace("bound-int") << "}" << std::endl;
- }else if( d_bound_type[f][v]==BOUND_FINITE ){
- Trace("bound-int") << " " << v << " has small finite type." << std::endl;
- }else{
- Trace("bound-int") << " " << v << " has unknown bound." << std::endl;
- Assert( false );
- }
- }else{
- Trace("bound-int") << " " << "*** " << v << " is unbounded." << std::endl;
- }
- }
- }
-
- bool bound_success = true;
- for( unsigned i=0; i<f[0].getNumChildren(); i++) {
- if( d_bound_type[f].find( f[0][i] )==d_bound_type[f].end() ){
- Trace("bound-int-warn") << "Warning : Bounded Integers : Due to quantification on " << f[0][i] << ", could not find bounds for " << f << std::endl;
- bound_success = false;
- break;
- }
- }
-
- if( bound_success ){
- d_bound_quants.push_back( f );
- for( unsigned i=0; i<d_set[f].size(); i++) {
- Node v = d_set[f][i];
- std::map< Node, Node >::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; i<d_lit_to_ranges[nlit].size(); i++) {
- Node r = d_lit_to_ranges[nlit][i];
- Trace("bound-int-assert") << " ...this is a bounding literal for " << r << std::endl;
- d_rms[r]->assertNode( 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; i<d_ranges.size(); i++) {
- Node d = d_rms[d_ranges[i]]->getNextDecisionRequest();
- 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<Assertion>::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; i<vindex; i++) {
- Assert( d_set_nums[q][d_set[q][i]]==i );
- Trace("bound-int-rsi") << "Look up the value for " << d_set[q][i] << " " << i << std::endl;
- int v = rsi->getVariableOrder( 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; i<t.getNumChildren(); i++ ){
- Node u;
- if( e.getKind()==kind::APPLY_CONSTRUCTOR ){
- u = matchBoundVar( v, t[i], e[i] );
- }else{
- Node se = NodeManager::currentNM()->mkNode( kind::APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[index].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<Rational>().getNumerator().getLong()+1;
- Trace("bound-int-rsi") << "Actual bound range is " << rr << std::endl;
- for( unsigned k=0; k<rr; k++ ){
- Node t = NodeManager::currentNM()->mkNode(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<elements_tmp.size(); i++ ){
- //do matching to determine v -> 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; i<it->second.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; i<it->second.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;
- }
-}
-
+++ /dev/null
-/********************* */
-/*! \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<Node, bool, NodeHashFunction> NodeBoolMap;
- typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap;
- typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap;
- typedef context::CDHashMap<int, bool> 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
+++ /dev/null
-/********************* */
-/*! \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; i<n.getNumChildren(); i++ ){
- collectDisjuncts( n[i], d );
- }
- }else{
- d.push_back( n );
- }
-}
-
-CegConjecture::CegConjecture(QuantifiersEngine* qe)
- : d_qe(qe),
- d_ceg_si(new CegConjectureSingleInv(qe, this)),
- d_ceg_pbe(new CegConjecturePbe(qe, this)),
- d_ceg_proc(new CegConjectureProcess(qe)),
- d_ceg_gc(new CegGrammarConstructor(qe, this)),
- d_refine_count(0),
- d_syntax_guided(false) {}
-
-CegConjecture::~CegConjecture() {}
-
-void CegConjecture::assign( Node q ) {
- Assert( d_embed_quant.isNull() );
- Assert( q.getKind()==FORALL );
- Trace("cegqi") << "CegConjecture : assign : " << q << std::endl;
- d_quant = q;
-
- // pre-simplify the quantified formula based on the process utility
- d_simp_quant = d_ceg_proc->preSimplify(d_quant);
-
- std::map< Node, Node > templates;
- std::map< Node, Node > templates_arg;
- //register with single invocation if applicable
- if (d_qe->getQuantAttributes()->isSygus(q))
- {
- d_ceg_si->initialize(d_simp_quant);
- d_simp_quant = d_ceg_si->getSimplifiedConjecture();
- // carry the templates
- for( unsigned i=0; i<q[0].getNumChildren(); i++ ){
- Node v = q[0][i];
- Node templ = d_ceg_si->getTemplate(v);
- if( !templ.isNull() ){
- templates[v] = templ;
- templates_arg[v] = d_ceg_si->getTemplateArg(v);
- }
- }
- }
-
- // post-simplify the quantified formula based on the process utility
- d_simp_quant = d_ceg_proc->postSimplify(d_simp_quant);
-
- // finished simplifying the quantified formula at this point
-
- // convert to deep embedding and finalize single invocation here
- d_embed_quant = d_ceg_gc->process(d_simp_quant, templates, templates_arg);
- Trace("cegqi") << "CegConjecture : converted to embedding : " << d_embed_quant << std::endl;
-
- // we now finalize the single invocation module, based on the syntax restrictions
- if (d_qe->getQuantAttributes()->isSygus(q))
- {
- d_ceg_si->finishInit( d_ceg_gc->isSyntaxRestricted(), d_ceg_gc->hasSyntaxITE() );
- }
-
- Assert( d_candidates.empty() );
- std::vector< Node > vars;
- for( unsigned i=0; i<d_embed_quant[0].getNumChildren(); i++ ){
- vars.push_back( d_embed_quant[0][i] );
- Node e = NodeManager::currentNM()->mkSkolem( "e", d_embed_quant[0][i].getType() );
- d_candidates.push_back( e );
- }
- Trace("cegqi") << "Base quantified formula is : " << d_embed_quant << std::endl;
- //construct base instantiation
- d_base_inst = Rewriter::rewrite(d_qe->getInstantiate()->getInstantiation(
- d_embed_quant, vars, d_candidates));
- Trace("cegqi") << "Base instantiation is : " << d_base_inst << std::endl;
- d_base_body = d_base_inst;
- if (d_base_body.getKind() == NOT && d_base_body[0].getKind() == FORALL)
- {
- for (const Node& v : d_base_body[0][0])
- {
- d_base_vars.push_back(v);
- }
- d_base_body = d_base_body[0][1];
- }
-
- // register this term with sygus database and other utilities that impact
- // the enumerative sygus search
- std::vector< Node > guarded_lemmas;
- if( !isSingleInvocation() ){
- d_ceg_proc->initialize(d_base_inst, d_candidates);
- if( options::sygusPbe() ){
- d_ceg_pbe->initialize(d_base_inst, d_candidates, guarded_lemmas);
- } else {
- for (unsigned i = 0; i < d_candidates.size(); i++) {
- Node e = d_candidates[i];
- d_qe->getTermDatabaseSygus()->registerEnumerator(e, e, this);
- }
- }
- }
-
- if (d_qe->getQuantAttributes()->isSygus(q))
- {
- collectDisjuncts( d_base_inst, d_base_disj );
- Trace("cegqi") << "Conjecture has " << d_base_disj.size() << " disjuncts." << std::endl;
- //store the inner variables for each disjunct
- for( unsigned j=0; j<d_base_disj.size(); j++ ){
- Trace("cegqi") << " " << j << " : " << d_base_disj[j] << std::endl;
- d_inner_vars_disj.push_back( std::vector< Node >() );
- //if the disjunct is an existential, store it
- if( d_base_disj[j].getKind()==NOT && d_base_disj[j][0].getKind()==FORALL ){
- for( unsigned k=0; k<d_base_disj[j][0][0].getNumChildren(); k++ ){
- d_inner_vars.push_back( d_base_disj[j][0][0][k] );
- d_inner_vars_disj[j].push_back( d_base_disj[j][0][0][k] );
- }
- }
- }
- d_syntax_guided = true;
- }
- else if (d_qe->getQuantAttributes()->isSynthesis(q))
- {
- d_syntax_guided = false;
- }else{
- Assert( false );
- }
-
- // initialize the guard
- if( !d_syntax_guided ){
- if( d_nsg_guard.isNull() ){
- d_nsg_guard = Rewriter::rewrite( NodeManager::currentNM()->mkSkolem( "G", NodeManager::currentNM()->booleanType() ) );
- d_nsg_guard = d_qe->getValuation().ensureLiteral( d_nsg_guard );
- AlwaysAssert( !d_nsg_guard.isNull() );
- d_qe->getOutputChannel().requirePhase( d_nsg_guard, true );
- // negated base as a guarded lemma
- guarded_lemmas.push_back( d_base_inst.negate() );
- }
- }else if( d_ceg_si->getGuard().isNull() ){
- std::vector< Node > lems;
- d_ceg_si->getInitialSingleInvLemma( lems );
- for( unsigned i=0; i<lems.size(); i++ ){
- Trace("cegqi-lemma") << "Cegqi::Lemma : single invocation " << i << " : " << lems[i] << std::endl;
- d_qe->getOutputChannel().lemma( lems[i] );
- if( Trace.isOn("cegqi-debug") ){
- Node rlem = Rewriter::rewrite( lems[i] );
- Trace("cegqi-debug") << "...rewritten : " << rlem << std::endl;
- }
- }
- }
- Assert( !getGuard().isNull() );
- Node gneg = getGuard().negate();
- for( unsigned i=0; i<guarded_lemmas.size(); i++ ){
- Node lem = NodeManager::currentNM()->mkNode( OR, gneg, guarded_lemmas[i] );
- Trace("cegqi-lemma") << "Cegqi::Lemma : initial (guarded) lemma : " << lem << std::endl;
- d_qe->getOutputChannel().lemma( lem );
- }
-
- // assign the cegis sampler if applicable
- if (options::cegisSample() != CEGIS_SAMPLE_NONE)
- {
- Trace("cegis-sample") << "Initialize sampler for " << d_base_body << "..."
- << std::endl;
- TypeNode bt = d_base_body.getType();
- d_cegis_sampler.initialize(bt, d_base_vars, options::sygusSamples());
- }
-
- Trace("cegqi") << "...finished, single invocation = " << isSingleInvocation() << std::endl;
-}
-
-Node CegConjecture::getGuard() {
- return !d_syntax_guided ? d_nsg_guard : d_ceg_si->getGuard();
-}
-
-bool CegConjecture::isSingleInvocation() const {
- return d_ceg_si->isSingleInvocation();
-}
-
-bool CegConjecture::needsCheck( std::vector< Node >& lem ) {
- if( isSingleInvocation() && !d_ceg_si->needsCheck() ){
- return false;
- }else{
- bool value;
- Assert( !getGuard().isNull() );
- // non or fully single invocation : look at guard only
- if( d_qe->getValuation().hasSatValue( getGuard(), value ) ) {
- if( !value ){
- Trace("cegqi-engine-debug") << "Conjecture is infeasible." << std::endl;
- return false;
- }
- }else{
- Assert( false );
- }
- return true;
- }
-}
-
-
-void CegConjecture::doSingleInvCheck(std::vector< Node >& lems) {
- if( d_ceg_si!=NULL ){
- d_ceg_si->check(lems);
- }
-}
-
-void CegConjecture::doBasicCheck(std::vector< Node >& lems) {
- std::vector< Node > model_terms;
- std::vector< Node > clist;
- getCandidateList( clist, true );
- Assert( clist.size()==d_quant[0].getNumChildren() );
- getModelValues( clist, model_terms );
- if (d_qe->getInstantiate()->addInstantiation(d_quant, model_terms))
- {
- //record the instantiation
- recordInstantiation( model_terms );
- }else{
- Assert( false );
- }
-}
-
-bool CegConjecture::needsRefinement() {
- return !d_ce_sk.empty();
-}
-
-void CegConjecture::getCandidateList( std::vector< Node >& clist, bool forceOrig ) {
- if( d_ceg_pbe->isPbe() && !forceOrig ){
- d_ceg_pbe->getCandidateList( d_candidates, clist );
- }else{
- clist.insert( clist.end(), d_candidates.begin(), d_candidates.end() );
- }
-}
-
-bool CegConjecture::constructCandidates( std::vector< Node >& clist, std::vector< Node >& model_values, std::vector< Node >& candidate_values,
- std::vector< Node >& lems ) {
- Assert( clist.size()==model_values.size() );
- if( d_ceg_pbe->isPbe() ){
- return d_ceg_pbe->constructCandidates( clist, model_values, d_candidates, candidate_values, lems );
- }else{
- Assert( model_values.size()==d_candidates.size() );
- candidate_values.insert( candidate_values.end(), model_values.begin(), model_values.end() );
- }
- return true;
-}
-
-void CegConjecture::doCheck(std::vector< Node >& lems, std::vector< Node >& model_values) {
- std::vector< Node > clist;
- getCandidateList( clist );
- std::vector< Node > c_model_values;
- Trace("cegqi-check") << "CegConjuncture : check, build candidates..." << std::endl;
- bool constructed_cand = constructCandidates( clist, model_values, c_model_values, lems );
-
- //must get a counterexample to the value of the current candidate
- Node inst;
- if( constructed_cand ){
- if( Trace.isOn("cegqi-check") ){
- Trace("cegqi-check") << "CegConjuncture : check candidate : " << std::endl;
- for( unsigned i=0; i<c_model_values.size(); i++ ){
- Trace("cegqi-check") << " " << i << " : " << d_candidates[i] << " -> " << c_model_values[i] << std::endl;
- }
- }
- Assert( c_model_values.size()==d_candidates.size() );
- inst = d_base_inst.substitute( d_candidates.begin(), d_candidates.end(), c_model_values.begin(), c_model_values.end() );
- }else{
- inst = d_base_inst;
- }
-
- //check whether we will run CEGIS on inner skolem variables
- bool sk_refine = ( !isGround() || d_refine_count==0 ) && ( !d_ceg_pbe->isPbe() || constructed_cand );
- if( sk_refine ){
- if (options::cegisSample() == CEGIS_SAMPLE_TRUST)
- {
- // we have that the current candidate passed a sample test
- // since we trust sampling in this mode, we assert there is no
- // counterexample to the conjecture here.
- NodeManager* nm = NodeManager::currentNM();
- Node lem = nm->mkNode(OR, d_quant.negate(), nm->mkConst(false));
- lem = getStreamGuardedLemma(lem);
- lems.push_back(lem);
- recordInstantiation(c_model_values);
- return;
- }
- Assert( d_ce_sk.empty() );
- d_ce_sk.push_back( std::vector< Node >() );
- }else{
- if( !constructed_cand ){
- return;
- }
- }
-
- std::vector< Node > ic;
- ic.push_back( d_quant.negate() );
- std::vector< Node > d;
- collectDisjuncts( inst, d );
- Assert( d.size()==d_base_disj.size() );
- //immediately skolemize inner existentials
- for( unsigned i=0; i<d.size(); i++ ){
- Node dr = Rewriter::rewrite( d[i] );
- if( dr.getKind()==NOT && dr[0].getKind()==FORALL ){
- if( constructed_cand ){
- ic.push_back(d_qe->getSkolemize()->getSkolemizedBody(dr[0]).negate());
- }
- if( sk_refine ){
- Assert( !isGround() );
- d_ce_sk.back().push_back( dr[0] );
- }
- }else{
- if( constructed_cand ){
- ic.push_back( dr );
- if( !d_inner_vars_disj[i].empty() ){
- Trace("cegqi-debug") << "*** quantified disjunct : " << d[i] << " simplifies to " << dr << std::endl;
- }
- }
- if( sk_refine ){
- d_ce_sk.back().push_back( Node::null() );
- }
- }
- }
- if( constructed_cand ){
- Node lem = NodeManager::currentNM()->mkNode( OR, ic );
- lem = Rewriter::rewrite( lem );
- //eagerly unfold applications of evaluation function
- if( options::sygusDirectEval() ){
- Trace("cegqi-debug") << "pre-unfold counterexample : " << lem << std::endl;
- std::map< Node, Node > visited_n;
- lem = d_qe->getTermDatabaseSygus()->getEagerUnfold( lem, visited_n );
- }
- lem = getStreamGuardedLemma(lem);
- lems.push_back( lem );
- recordInstantiation( c_model_values );
- }
-}
-
-void CegConjecture::doRefine( std::vector< Node >& lems ){
- Assert( lems.empty() );
- Assert( d_ce_sk.size()==1 );
-
- //first, make skolem substitution
- Trace("cegqi-refine") << "doRefine : construct skolem substitution..." << std::endl;
- std::vector< Node > sk_vars;
- std::vector< Node > sk_subs;
- //collect the substitution over all disjuncts
- for( unsigned k=0; k<d_ce_sk[0].size(); k++ ){
- Node ce_q = d_ce_sk[0][k];
- if( !ce_q.isNull() ){
- Assert( !d_inner_vars_disj[k].empty() );
- std::vector<Node> skolems;
- d_qe->getSkolemize()->getSkolemConstants(ce_q, skolems);
- Assert(d_inner_vars_disj[k].size() == skolems.size());
- std::vector< Node > model_values;
- getModelValues(skolems, model_values);
- sk_vars.insert( sk_vars.end(), d_inner_vars_disj[k].begin(), d_inner_vars_disj[k].end() );
- sk_subs.insert( sk_subs.end(), model_values.begin(), model_values.end() );
- }else{
- if( !d_inner_vars_disj[k].empty() ){
- //denegrate case : quantified disjunct was trivially true and does not need to be refined
- //add trivial substitution (in case we need substitution for previous cex's)
- for( unsigned i=0; i<d_inner_vars_disj[k].size(); i++ ){
- sk_vars.push_back( d_inner_vars_disj[k][i] );
- sk_subs.push_back( getModelValue( d_inner_vars_disj[k][i] ) ); // will return dummy value
- }
- }
- }
- }
-
- //for conditional evaluation
- std::vector< Node > lem_c;
- Assert( d_ce_sk[0].size()==d_base_disj.size() );
- std::vector< Node > inst_cond_c;
- Trace("cegqi-refine") << "doRefine : Construct refinement lemma..." << std::endl;
- for( unsigned k=0; k<d_ce_sk[0].size(); k++ ){
- Node ce_q = d_ce_sk[0][k];
- Trace("cegqi-refine-debug") << " For counterexample point, disjunct " << k << " : " << ce_q << " " << d_base_disj[k] << std::endl;
- Node c_disj;
- if( !ce_q.isNull() ){
- Assert( d_base_disj[k].getKind()==kind::NOT && d_base_disj[k][0].getKind()==kind::FORALL );
- c_disj = d_base_disj[k][0][1];
- }else{
- if( d_inner_vars_disj[k].empty() ){
- c_disj = d_base_disj[k].negate();
- }else{
- //denegrate case : quantified disjunct was trivially true and does not need to be refined
- Trace("cegqi-refine-debug") << "*** skip " << d_base_disj[k] << std::endl;
- }
- }
- if( !c_disj.isNull() ){
- //compute the body, inst_cond
- //standard CEGIS refinement : plug in values, assert that d_candidates must satisfy entire specification
- lem_c.push_back( c_disj );
- }
- }
- Assert( sk_vars.size()==sk_subs.size() );
-
- Node base_lem = lem_c.size()==1 ? lem_c[0] : NodeManager::currentNM()->mkNode( AND, lem_c );
-
- Trace("cegqi-refine") << "doRefine : construct and finalize lemmas..." << std::endl;
-
-
- base_lem = base_lem.substitute( sk_vars.begin(), sk_vars.end(), sk_subs.begin(), sk_subs.end() );
- base_lem = Rewriter::rewrite( base_lem );
- d_refinement_lemmas.push_back(base_lem);
-
- Node lem =
- NodeManager::currentNM()->mkNode(OR, getGuard().negate(), base_lem);
- lems.push_back( lem );
-
- d_ce_sk.clear();
-}
-
-void CegConjecture::preregisterConjecture( Node q ) {
- d_ceg_si->preregisterConjecture( q );
-}
-
-void CegConjecture::getModelValues( std::vector< Node >& n, std::vector< Node >& v ) {
- Trace("cegqi-engine") << " * Value is : ";
- for( unsigned i=0; i<n.size(); i++ ){
- Node nv = getModelValue( n[i] );
- v.push_back( nv );
- if( Trace.isOn("cegqi-engine") ){
- TypeNode tn = nv.getType();
- Trace("cegqi-engine") << n[i] << " -> ";
- std::stringstream ss;
- Printer::getPrinter(options::outputLanguage())->toStreamSygus(ss, nv);
- Trace("cegqi-engine") << ss.str() << " ";
- if (Trace.isOn("cegqi-engine-rr"))
- {
- Node bv = d_qe->getTermDatabaseSygus()->sygusToBuiltin(nv, tn);
- bv = Rewriter::rewrite(bv);
- Trace("cegqi-engine-rr") << " -> " << bv << std::endl;
- }
- }
- Assert( !nv.isNull() );
- }
- Trace("cegqi-engine") << std::endl;
-}
-
-Node CegConjecture::getModelValue( Node n ) {
- Trace("cegqi-mv") << "getModelValue for : " << n << std::endl;
- return d_qe->getModel()->getValue( n );
-}
-
-void CegConjecture::debugPrint( const char * c ) {
- Trace(c) << "Synthesis conjecture : " << d_embed_quant << std::endl;
- Trace(c) << " * Candidate program/output symbol : ";
- for( unsigned i=0; i<d_candidates.size(); i++ ){
- Trace(c) << d_candidates[i] << " ";
- }
- Trace(c) << std::endl;
- Trace(c) << " * Candidate ce skolems : ";
- for( unsigned i=0; i<d_ce_sk.size(); i++ ){
- Trace(c) << d_ce_sk[i] << " ";
- }
-}
-
-Node CegConjecture::getCurrentStreamGuard() const {
- if( d_stream_guards.empty() ){
- return Node::null();
- }else{
- return d_stream_guards.back();
- }
-}
-
-Node CegConjecture::getStreamGuardedLemma(Node n) const
-{
- if (options::sygusStream())
- {
- // if we are in streaming mode, we guard with the current stream guard
- Node csg = getCurrentStreamGuard();
- Assert(!csg.isNull());
- return NodeManager::currentNM()->mkNode(kind::OR, csg.negate(), n);
- }
- return n;
-}
-
-Node CegConjecture::getNextDecisionRequest( unsigned& priority ) {
- // first, must try the guard
- // which denotes "this conjecture is feasible"
- Node feasible_guard = getGuard();
- bool value;
- if( !d_qe->getValuation().hasSatValue( feasible_guard, value ) ) {
- priority = 0;
- return feasible_guard;
- }else{
- if( value ){
- // the conjecture is feasible
- if( options::sygusStream() ){
- Assert( !isSingleInvocation() );
- // if we are in sygus streaming mode, then get the "next guard"
- // which denotes "we have not yet generated the next solution to the conjecture"
- Node curr_stream_guard = getCurrentStreamGuard();
- bool needs_new_stream_guard = false;
- if( curr_stream_guard.isNull() ){
- needs_new_stream_guard = true;
- }else{
- // check the polarity of the guard
- if( !d_qe->getValuation().hasSatValue( curr_stream_guard, value ) ) {
- priority = 0;
- return curr_stream_guard;
- }else{
- if( !value ){
- Trace("cegqi-debug") << "getNextDecision : we have a new solution since stream guard was propagated false: " << curr_stream_guard << std::endl;
- // we have generated a solution, print it
- // get the current output stream
- // this output stream should coincide with wherever --dump-synth is output on
- Options& nodeManagerOptions = NodeManager::currentNM()->getOptions();
- printSynthSolution( *nodeManagerOptions.getOut(), false );
- // need to make the next stream guard
- needs_new_stream_guard = true;
-
- // We will not refine the current candidate solution since it is a solution
- // thus, we clear information regarding the current refinement
- d_ce_sk.clear();
- // However, we need to exclude the current solution using an explicit refinement
- // so that we proceed to the next solution.
- std::vector< Node > clist;
- getCandidateList( clist );
- Trace("cegqi-debug") << "getNextDecision : solution was : " << std::endl;
- std::vector< Node > exp;
- for( unsigned i=0; i<clist.size(); i++ ){
- Node cprog = clist[i];
- Node sol = cprog;
- if( !d_cinfo[cprog].d_inst.empty() ){
- sol = d_cinfo[cprog].d_inst.back();
- // add to explanation of exclusion
- d_qe->getTermDatabaseSygus()
- ->getExplain()
- ->getExplanationForConstantEquality(cprog, sol, exp);
- }
- Trace("cegqi-debug") << " " << cprog << " -> " << sol << std::endl;
- }
- Assert( !exp.empty() );
- Node exc_lem = exp.size()==1 ? exp[0] : NodeManager::currentNM()->mkNode( kind::AND, exp );
- exc_lem = exc_lem.negate();
- Trace("cegqi-lemma") << "Cegqi::Lemma : stream exclude current solution : " << exc_lem << std::endl;
- d_qe->getOutputChannel().lemma( exc_lem );
- }
- }
- }
- if( needs_new_stream_guard ){
- // generate a new stream guard
- curr_stream_guard = Rewriter::rewrite( NodeManager::currentNM()->mkSkolem( "G_Stream", NodeManager::currentNM()->booleanType() ) );
- curr_stream_guard = d_qe->getValuation().ensureLiteral( curr_stream_guard );
- AlwaysAssert( !curr_stream_guard.isNull() );
- d_qe->getOutputChannel().requirePhase( curr_stream_guard, true );
- d_stream_guards.push_back( curr_stream_guard );
- Trace("cegqi-debug") << "getNextDecision : allocate new stream guard : " << curr_stream_guard << std::endl;
- // return it as a decision
- priority = 0;
- return curr_stream_guard;
- }
- }
- }else{
- Trace("cegqi-debug") << "getNextDecision : conjecture is infeasible." << std::endl;
- }
- }
- return Node::null();
-}
-
-void CegConjecture::printSynthSolution( std::ostream& out, bool singleInvocation ) {
- Trace("cegqi-debug") << "Printing synth solution..." << std::endl;
- Assert( d_quant[0].getNumChildren()==d_embed_quant[0].getNumChildren() );
- std::vector<Node> sols;
- std::vector<int> statuses;
- getSynthSolutionsInternal(sols, statuses, singleInvocation);
- for (unsigned i = 0, size = d_embed_quant[0].getNumChildren(); i < size; i++)
- {
- Node sol = sols[i];
- if (!sol.isNull())
- {
- Node prog = d_embed_quant[0][i];
- int status = statuses[i];
- TypeNode tn = prog.getType();
- const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
- std::stringstream ss;
- ss << prog;
- std::string f(ss.str());
- f.erase(f.begin());
- out << "(define-fun " << f << " ";
- if( dt.getSygusVarList().isNull() ){
- out << "() ";
- }else{
- out << dt.getSygusVarList() << " ";
- }
- out << dt.getSygusType() << " ";
- if( status==0 ){
- out << sol;
- }else{
- Printer::getPrinter(options::outputLanguage())->toStreamSygus(out, sol);
- }
- out << ")" << std::endl;
- CegInstantiation* cei = d_qe->getCegInstantiation();
- ++(cei->d_statistics.d_solutions);
-
- if (status != 0 && options::sygusRewSynth())
- {
- TermDbSygus* sygusDb = d_qe->getTermDatabaseSygus();
- std::map<Node, SygusSamplerExt>::iterator its = d_sampler.find(prog);
- if (its == d_sampler.end())
- {
- d_sampler[prog].initializeSygusExt(
- d_qe, prog, options::sygusSamples());
- its = d_sampler.find(prog);
- }
- Node solb = sygusDb->sygusToBuiltin(sol, prog.getType());
- Node eq_sol = its->second.registerTerm(solb);
- // eq_sol is a candidate solution that is equivalent to sol
- if (eq_sol != solb)
- {
- ++(cei->d_statistics.d_candidate_rewrites);
- if (!eq_sol.isNull())
- {
- // Terms solb and eq_sol are equivalent under sample points but do
- // not rewrite to the same term. Hence, this indicates a candidate
- // rewrite.
- out << "(candidate-rewrite " << solb << " " << eq_sol << ")"
- << std::endl;
- ++(cei->d_statistics.d_candidate_rewrites_print);
- // debugging information
- if (Trace.isOn("sygus-rr-debug"))
- {
- ExtendedRewriter* er = sygusDb->getExtRewriter();
- Node solbr = er->extendedRewrite(solb);
- Node eq_solr = er->extendedRewrite(eq_sol);
- Trace("sygus-rr-debug")
- << "; candidate #1 ext-rewrites to: " << solbr << std::endl;
- Trace("sygus-rr-debug")
- << "; candidate #2 ext-rewrites to: " << eq_solr << std::endl;
- }
- }
- }
- }
- }
- }
-}
-
-void CegConjecture::getSynthSolutions(std::map<Node, Node>& sol_map,
- bool singleInvocation)
-{
- NodeManager* nm = NodeManager::currentNM();
- TermDbSygus* sygusDb = d_qe->getTermDatabaseSygus();
- std::vector<Node> sols;
- std::vector<int> statuses;
- getSynthSolutionsInternal(sols, statuses, singleInvocation);
- for (unsigned i = 0, size = d_embed_quant[0].getNumChildren(); i < size; i++)
- {
- Node sol = sols[i];
- int status = statuses[i];
- // get the builtin solution
- Node bsol = sol;
- if (status != 0)
- {
- // convert sygus to builtin here
- bsol = sygusDb->sygusToBuiltin(sol, sol.getType());
- }
- // convert to lambda
- TypeNode tn = d_embed_quant[0][i].getType();
- const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
- Node bvl = Node::fromExpr(dt.getSygusVarList());
- if (!bvl.isNull())
- {
- bsol = nm->mkNode(LAMBDA, bvl, bsol);
- }
- // store in map
- Node fvar = d_quant[0][i];
- Assert(fvar.getType() == bsol.getType());
- sol_map[fvar] = bsol;
- }
-}
-
-void CegConjecture::getSynthSolutionsInternal(std::vector<Node>& sols,
- std::vector<int>& statuses,
- bool singleInvocation)
-{
- for (unsigned i = 0, size = d_embed_quant[0].getNumChildren(); i < size; i++)
- {
- Node prog = d_embed_quant[0][i];
- Trace("cegqi-debug") << " get solution for " << prog << std::endl;
- TypeNode tn = prog.getType();
- Assert(tn.isDatatype());
- // get the solution
- Node sol;
- int status = -1;
- if (singleInvocation)
- {
- Assert(d_ceg_si != NULL);
- sol = d_ceg_si->getSolution(i, tn, status, true);
- if (!sol.isNull())
- {
- sol = sol.getKind() == LAMBDA ? sol[1] : sol;
- }
- }
- else
- {
- Node cprog = getCandidate(i);
- if (!d_cinfo[cprog].d_inst.empty())
- {
- // the solution is just the last instantiated term
- sol = d_cinfo[cprog].d_inst.back();
- status = 1;
-
- // check if there was a template
- Node sf = d_quant[0][i];
- Node templ = d_ceg_si->getTemplate(sf);
- if (!templ.isNull())
- {
- Trace("cegqi-inv-debug")
- << sf << " used template : " << templ << std::endl;
- // if it was not embedded into the grammar
- if (!options::sygusTemplEmbedGrammar())
- {
- TNode templa = d_ceg_si->getTemplateArg(sf);
- // make the builtin version of the full solution
- TermDbSygus* sygusDb = d_qe->getTermDatabaseSygus();
- sol = sygusDb->sygusToBuiltin(sol, sol.getType());
- Trace("cegqi-inv") << "Builtin version of solution is : " << sol
- << ", type : " << sol.getType() << std::endl;
- TNode tsol = sol;
- sol = templ.substitute(templa, tsol);
- Trace("cegqi-inv-debug") << "With template : " << sol << std::endl;
- sol = Rewriter::rewrite(sol);
- Trace("cegqi-inv-debug") << "Simplified : " << sol << std::endl;
- // now, reconstruct to the syntax
- sol = d_ceg_si->reconstructToSyntax(sol, tn, status, true);
- sol = sol.getKind() == LAMBDA ? sol[1] : sol;
- Trace("cegqi-inv-debug")
- << "Reconstructed to syntax : " << sol << std::endl;
- }
- else
- {
- Trace("cegqi-inv-debug")
- << "...was embedding into grammar." << std::endl;
- }
- }
- else
- {
- Trace("cegqi-inv-debug")
- << sf << " did not use template" << std::endl;
- }
- }
- else
- {
- Trace("cegqi-warn") << "WARNING : No recorded instantiations for "
- "syntax-guided solution!"
- << std::endl;
- }
- }
- sols.push_back(sol);
- statuses.push_back(status);
- }
-}
-
-Node CegConjecture::getSymmetryBreakingPredicate(
- Node x, Node e, TypeNode tn, unsigned tindex, unsigned depth)
-{
- std::vector<Node> sb_lemmas;
-
- // based on simple preprocessing
- Node ppred =
- d_ceg_proc->getSymmetryBreakingPredicate(x, e, tn, tindex, depth);
- if (!ppred.isNull())
- {
- sb_lemmas.push_back(ppred);
- }
-
- // other static conjecture-dependent symmetry breaking goes here
-
- if (!sb_lemmas.empty())
- {
- return sb_lemmas.size() == 1
- ? sb_lemmas[0]
- : NodeManager::currentNM()->mkNode(kind::AND, sb_lemmas);
- }
- else
- {
- return Node::null();
- }
-}
-
-bool CegConjecture::sampleAddRefinementLemma(std::vector<Node>& vals,
- std::vector<Node>& lems)
-{
- if (Trace.isOn("cegis-sample"))
- {
- Trace("cegis-sample") << "Check sampling for candidate solution"
- << std::endl;
- for (unsigned i = 0, size = vals.size(); i < size; i++)
- {
- Trace("cegis-sample")
- << " " << d_candidates[i] << " -> " << vals[i] << std::endl;
- }
- }
- Assert(vals.size() == d_candidates.size());
- Node sbody = d_base_body.substitute(
- d_candidates.begin(), d_candidates.end(), vals.begin(), vals.end());
- Trace("cegis-sample-debug") << "Sample " << sbody << std::endl;
- // do eager unfolding
- std::map<Node, Node> visited_n;
- sbody = d_qe->getTermDatabaseSygus()->getEagerUnfold(sbody, visited_n);
- Trace("cegis-sample") << "Sample (after unfolding): " << sbody << std::endl;
-
- NodeManager* nm = NodeManager::currentNM();
- for (unsigned i = 0, size = d_cegis_sampler.getNumSamplePoints(); i < size;
- i++)
- {
- if (d_cegis_sample_refine.find(i) == d_cegis_sample_refine.end())
- {
- Node ev = d_cegis_sampler.evaluate(sbody, i);
- Trace("cegis-sample-debug")
- << "...evaluate point #" << i << " to " << ev << std::endl;
- Assert(ev.isConst());
- Assert(ev.getType().isBoolean());
- if (!ev.getConst<bool>())
- {
- Trace("cegis-sample-debug") << "...false for point #" << i << std::endl;
- // mark this as a CEGIS point (no longer sampled)
- d_cegis_sample_refine.insert(i);
- std::vector<Node> vars;
- std::vector<Node> pt;
- d_cegis_sampler.getSamplePoint(i, vars, pt);
- Assert(d_base_vars.size() == pt.size());
- Node rlem = d_base_body.substitute(
- d_base_vars.begin(), d_base_vars.end(), pt.begin(), pt.end());
- rlem = Rewriter::rewrite(rlem);
- if (std::find(
- d_refinement_lemmas.begin(), d_refinement_lemmas.end(), rlem)
- == d_refinement_lemmas.end())
- {
- if (Trace.isOn("cegis-sample"))
- {
- Trace("cegis-sample") << " false for point #" << i << " : ";
- for (const Node& cn : pt)
- {
- Trace("cegis-sample") << cn << " ";
- }
- Trace("cegis-sample") << std::endl;
- }
- Trace("cegqi-engine") << " *** Refine by sampling" << std::endl;
- d_refinement_lemmas.push_back(rlem);
- // if trust, we are not interested in sending out refinement lemmas
- if (options::cegisSample() != CEGIS_SAMPLE_TRUST)
- {
- Node lem = nm->mkNode(OR, getGuard().negate(), rlem);
- lems.push_back(lem);
- }
- return true;
- }
- else
- {
- Trace("cegis-sample-debug") << "...duplicate." << std::endl;
- }
- }
- }
- }
- return false;
-}
-
-}/* namespace CVC4::theory::quantifiers */
-}/* namespace CVC4::theory */
-}/* namespace CVC4 */
+++ /dev/null
-/********************* */
-/*! \file ce_guided_conjecture.h
- ** \verbatim
- ** Top contributors (to current version):
- ** Andrew Reynolds, Tim King
- ** This file is part of the CVC4 project.
- ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
- ** in the top-level source directory) and their institutional affiliations.
- ** All rights reserved. See the file COPYING in the top-level source
- ** directory for licensing information.\endverbatim
- **
- ** \brief class that encapsulates counterexample-guided instantiation
- ** techniques for a single SyGuS synthesis conjecture
- **/
-
-#include "cvc4_private.h"
-
-#ifndef __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_CONJECTURE_H
-#define __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_CONJECTURE_H
-
-#include <memory>
-
-#include "theory/quantifiers/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<Node, Node>& sol_map, bool singleInvocation);
- /** get guard, this is "G" in Figure 3 of Reynolds et al CAV 2015 */
- Node getGuard();
- /** is ground */
- bool isGround() { return d_inner_vars.empty(); }
- /** does this conjecture correspond to a syntax-guided synthesis input */
- bool isSyntaxGuided() const { return d_syntax_guided; }
- /** are we using single invocation techniques */
- bool isSingleInvocation() const;
- /** preregister conjecture
- * This is used as a heuristic for solution reconstruction, so that we
- * remember expressions in the conjecture before preprocessing, since they
- * may be helpful during solution reconstruction (Figure 5 of Reynolds et al CAV 2015)
- */
- void preregisterConjecture( Node q );
- /** assign conjecture q to this class */
- void assign( Node q );
- /** has a conjecture been assigned to this class */
- bool isAssigned() { return !d_embed_quant.isNull(); }
- /** get model values for terms n, store in vector v */
- void getModelValues( std::vector< Node >& n, std::vector< Node >& v );
- /** get model value for term n */
- Node getModelValue( Node n );
-
- //-----------------------------------refinement lemmas
- /** get number of refinement lemmas we have added so far */
- unsigned getNumRefinementLemmas() { return d_refinement_lemmas.size(); }
- /** get refinement lemma
- *
- * If d_embed_quant is forall d. exists y. P( d, y ), then a refinement
- * lemma is one of the form ~P( d_candidates, c ) for some c.
- */
- Node getRefinementLemma( unsigned i ) { return d_refinement_lemmas[i]; }
- /** sample add refinement lemma
- *
- * This function will check if there is a sample point in d_sampler that
- * refutes the candidate solution (d_quant_vars->vals). If so, it adds a
- * refinement lemma to the lists d_refinement_lemmas that corresponds to that
- * sample point, and adds a lemma to lems if cegisSample mode is not trust.
- */
- bool sampleAddRefinementLemma(std::vector<Node>& vals,
- std::vector<Node>& lems);
- //-----------------------------------end refinement lemmas
-
- /** get program by examples utility */
- CegConjecturePbe* getPbe() { return d_ceg_pbe.get(); }
- /** get utility for static preprocessing and analysis of conjectures */
- CegConjectureProcess* getProcess() { return d_ceg_proc.get(); }
- /** get the symmetry breaking predicate for type */
- Node getSymmetryBreakingPredicate(
- Node x, Node e, TypeNode tn, unsigned tindex, unsigned depth);
- /** print out debug information about this conjecture */
- void debugPrint( const char * c );
-private:
- /** reference to quantifier engine */
- QuantifiersEngine * d_qe;
- /** single invocation utility */
- std::unique_ptr<CegConjectureSingleInv> d_ceg_si;
- /** program by examples utility */
- std::unique_ptr<CegConjecturePbe> d_ceg_pbe;
- /** utility for static preprocessing and analysis of conjectures */
- std::unique_ptr<CegConjectureProcess> d_ceg_proc;
- /** grammar utility */
- std::unique_ptr<CegGrammarConstructor> d_ceg_gc;
- /** list of constants for quantified formula
- * The outer Skolems for the negation of d_embed_quant.
- */
- std::vector< Node > d_candidates;
- /** base instantiation
- * If d_embed_quant is forall d. exists y. P( d, y ), then
- * this is the formula exists y. P( d_candidates, y ).
- */
- Node d_base_inst;
- /** If d_base_inst is exists y. P( d, y ), then this is y. */
- std::vector<Node> d_base_vars;
- /**
- * If d_base_inst is exists y. P( d, y ), then this is the formula
- * P( d_candidates, y ).
- */
- Node d_base_body;
- /** expand base inst to disjuncts */
- std::vector< Node > d_base_disj;
- /** list of variables on inner quantification */
- std::vector< Node > d_inner_vars;
- std::vector< std::vector< Node > > d_inner_vars_disj;
- /** current extential quantifeirs whose couterexamples we must refine */
- std::vector< std::vector< Node > > d_ce_sk;
-
- //-----------------------------------refinement lemmas
- /** refinement lemmas */
- std::vector< Node > d_refinement_lemmas;
- //-----------------------------------end refinement lemmas
-
- /** the asserted (negated) conjecture */
- Node d_quant;
- /** (negated) conjecture after simplification */
- Node d_simp_quant;
- /** (negated) conjecture after simplification, conversion to deep embedding */
- Node d_embed_quant;
- /** candidate information */
- class CandidateInfo {
- public:
- CandidateInfo(){}
- /** list of terms we have instantiated candidates with */
- std::vector< Node > d_inst;
- };
- std::map< Node, CandidateInfo > d_cinfo;
- /** number of times we have called doRefine */
- unsigned d_refine_count;
- /** construct candidates */
- bool constructCandidates( std::vector< Node >& clist, std::vector< Node >& model_values,
- std::vector< Node >& candidate_values, std::vector< Node >& lems );
- /** get candidadate */
- Node getCandidate( unsigned int i ) { return d_candidates[i]; }
- /** record instantiation (this is used to construct solutions later) */
- void recordInstantiation( std::vector< Node >& vs ) {
- Assert( vs.size()==d_candidates.size() );
- for( unsigned i=0; i<vs.size(); i++ ){
- d_cinfo[d_candidates[i]].d_inst.push_back( vs[i] );
- }
- }
- /** get synth solutions internal
- *
- * This function constructs the body of solutions for all
- * functions-to-synthesize in this conjecture and stores them in sols, in
- * order. For each solution added to sols, we add an integer indicating what
- * kind of solution n is, where if sols[i] = n, then
- * if status[i] = 0: n is the (builtin term) corresponding to the solution,
- * if status[i] = 1: n is the sygus representation of the solution.
- * We store builtin versions under some conditions (such as when the sygus
- * grammar is being ignored).
- *
- * singleInvocation : set to true if we should consult the single invocation
- * module to get synthesis solutions.
- *
- * For example, for conjecture exists fg. forall x. f(x)>g(x), this function
- * may set ( sols, status ) to ( { x+1, d_x() }, { 1, 0 } ), where d_x() is
- * the sygus datatype constructor corresponding to variable x.
- */
- void getSynthSolutionsInternal(std::vector<Node>& sols,
- std::vector<int>& status,
- bool singleInvocation);
- //-------------------------------- sygus stream
- /** the streaming guards for sygus streaming mode */
- std::vector< Node > d_stream_guards;
- /** get current stream guard */
- Node getCurrentStreamGuard() const;
- /** get stream guarded lemma
- *
- * If sygusStream is enabled, this returns ( G V n ) where G is the guard
- * returned by getCurrentStreamGuard, otherwise this returns n.
- */
- Node getStreamGuardedLemma(Node n) const;
- //-------------------------------- end sygus stream
- //-------------------------------- non-syntax guided (deprecated)
- /** Whether we are syntax-guided (e.g. was the input in SyGuS format).
- * This includes SyGuS inputs where no syntactic restrictions are provided.
- */
- bool d_syntax_guided;
- /** the guard for non-syntax-guided synthesis */
- Node d_nsg_guard;
- //-------------------------------- end non-syntax guided (deprecated)
- /** sygus sampler objects for each program variable
- *
- * This is used for the sygusRewSynth() option to synthesize new candidate
- * rewrite rules.
- */
- std::map<Node, SygusSamplerExt> d_sampler;
- /** sampler object for the option cegisSample()
- *
- * This samples points of the type of the inner variables of the synthesis
- * conjecture (d_base_vars).
- */
- SygusSampler d_cegis_sampler;
- /** cegis sample refine points
- *
- * Stores the list of indices of sample points in d_cegis_sampler we have
- * added as refinement lemmas.
- */
- std::unordered_set<unsigned> d_cegis_sample_refine;
-};
-
-} /* namespace CVC4::theory::quantifiers */
-} /* namespace CVC4::theory */
-} /* namespace CVC4 */
-
-#endif
+++ /dev/null
-/********************* */
-/*! \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; i<lem.size(); i++ ){
- Trace("cegqi-lemma") << "Cegqi::Lemma : check lemma : " << lem[i] << std::endl;
- d_quantEngine->addLemma( lem[i] );
- }
- }
- Trace("cegqi-engine") << "Finished Counterexample Guided Instantiation engine." << std::endl;
- }
-}
-
-void CegInstantiation::registerQuantifier( Node q ) {
- if( d_quantEngine->getOwner( q )==this ){ // && d_eval_axioms.find( q )==d_eval_axioms.end() ){
- if( !d_conj->isAssigned() ){
- Trace("cegqi") << "Register conjecture : " << q << std::endl;
- d_conj->assign( q );
- }else{
- Assert( d_conj->getEmbeddedConjecture()==q );
- }
- }else{
- Trace("cegqi-debug") << "Register quantifier : " << q << std::endl;
- }
-}
-
-Node CegInstantiation::getNextDecisionRequest( unsigned& priority ) {
- if( d_conj->isAssigned() ){
- Node dec_req = d_conj->getNextDecisionRequest( priority );
- if( !dec_req.isNull() ){
- Trace("cegqi-debug") << "CEGQI : Decide next on : " << dec_req << "..." << std::endl;
- return dec_req;
- }
- }
- return Node::null();
-}
-
-void CegInstantiation::checkCegConjecture( CegConjecture * conj ) {
- Node q = conj->getEmbeddedConjecture();
- Node aq = conj->getConjecture();
- if( Trace.isOn("cegqi-engine-debug") ){
- conj->debugPrint("cegqi-engine-debug");
- Trace("cegqi-engine-debug") << std::endl;
- }
-
- if( !conj->needsRefinement() ){
- Trace("cegqi-engine-debug") << "Do conjecture check..." << std::endl;
- if( conj->isSyntaxGuided() ){
- std::vector< Node > clems;
- conj->doSingleInvCheck( clems );
- if( !clems.empty() ){
- d_last_inst_si = true;
- for( unsigned j=0; j<clems.size(); j++ ){
- Trace("cegqi-lemma") << "Cegqi::Lemma : single invocation instantiation : " << clems[j] << std::endl;
- d_quantEngine->addLemma( clems[j] );
- }
- d_statistics.d_cegqi_si_lemmas += clems.size();
- Trace("cegqi-engine") << " ...try single invocation." << std::endl;
- return;
- }
- //ignore return value here
- std::vector< Node > clist;
- conj->getCandidateList( clist );
- std::vector< Node > model_values;
- conj->getModelValues( clist, model_values );
- if( options::sygusDirectEval() ){
- bool addedEvalLemmas = false;
- if( options::sygusCRefEval() ){
- Trace("cegqi-engine") << " *** Do conjecture refinement evaluation..." << std::endl;
- // see if any refinement lemma is refuted by evaluation
- std::vector< Node > cre_lems;
- getCRefEvaluationLemmas( conj, clist, model_values, cre_lems );
- if( !cre_lems.empty() ){
- for( unsigned j=0; j<cre_lems.size(); j++ ){
- Node lem = cre_lems[j];
- if( d_quantEngine->addLemma( lem ) ){
- Trace("cegqi-lemma") << "Cegqi::Lemma : cref evaluation : " << lem << std::endl;
- addedEvalLemmas = true;
- }
- }
- if( addedEvalLemmas ){
- //return;
- }
- }
- }
- Trace("cegqi-engine") << " *** Do direct evaluation..." << std::endl;
- std::vector< Node > eager_terms;
- std::vector< Node > eager_vals;
- std::vector< Node > eager_exps;
- for( unsigned j=0; j<clist.size(); j++ ){
- Trace("cegqi-debug") << " register " << clist[j] << " -> " << model_values[j] << std::endl;
- d_quantEngine->getTermDatabaseSygus()->registerModelValue( clist[j], model_values[j], eager_terms, eager_vals, eager_exps );
- }
- Trace("cegqi-debug") << "...produced " << eager_terms.size() << " eager evaluation lemmas." << std::endl;
- if( !eager_terms.empty() ){
- for( unsigned j=0; j<eager_terms.size(); j++ ){
- Node lem = NodeManager::currentNM()->mkNode( kind::OR, eager_exps[j].negate(), eager_terms[j].eqNode( eager_vals[j] ) );
- if( d_quantEngine->getTheoryEngine()->isTheoryEnabled(THEORY_BV) ){
- //FIXME: hack to incorporate hacks from BV for division by zero (github issue #1156)
- lem = bv::TheoryBVRewriter::eliminateBVSDiv( lem );
- }
- if( d_quantEngine->addLemma( lem ) ){
- Trace("cegqi-lemma") << "Cegqi::Lemma : evaluation : " << lem << std::endl;
- addedEvalLemmas = true;
- }
- }
- }
- if( addedEvalLemmas ){
- return;
- }
- }
-
- Trace("cegqi-engine") << " *** Check candidate phase..." << std::endl;
- std::vector< Node > cclems;
- conj->doCheck( cclems, model_values );
- bool addedLemma = false;
- for( unsigned i=0; i<cclems.size(); i++ ){
- Node lem = cclems[i];
- d_last_inst_si = false;
- Trace("cegqi-lemma") << "Cegqi::Lemma : counterexample : " << lem << std::endl;
- if( d_quantEngine->addLemma( lem ) ){
- ++(d_statistics.d_cegqi_lemmas_ce);
- addedLemma = true;
- }else{
- //this may happen if we eagerly unfold, simplify to true
- if( !options::sygusDirectEval() ){
- Trace("cegqi-warn") << " ...FAILED to add candidate!" << std::endl;
- }else{
- Trace("cegqi-engine-debug") << " ...FAILED to add candidate!" << std::endl;
- }
- }
- }
- if( addedLemma ){
- Trace("cegqi-engine") << " ...check for counterexample." << std::endl;
- }else{
- if( conj->needsRefinement() ){
- //immediately go to refine candidate
- checkCegConjecture( conj );
- return;
- }
- }
- }else{
- Assert( aq==q );
- Trace("cegqi-engine") << " *** Check candidate phase (non-SyGuS)." << std::endl;
- std::vector< Node > lems;
- conj->doBasicCheck(lems);
- Assert(lems.empty());
- }
- }else{
- Trace("cegqi-engine") << " *** Refine candidate phase..." << std::endl;
- std::vector< Node > rlems;
- conj->doRefine( rlems );
- bool addedLemma = false;
- for( unsigned i=0; i<rlems.size(); i++ ){
- Node lem = rlems[i];
- Trace("cegqi-lemma") << "Cegqi::Lemma : candidate refinement : " << lem << std::endl;
- bool res = d_quantEngine->addLemma( lem );
- if( res ){
- ++(d_statistics.d_cegqi_lemmas_refine);
- conj->incrementRefineCount();
- addedLemma = true;
- }else{
- Trace("cegqi-warn") << " ...FAILED to add refinement!" << std::endl;
- }
- }
- if( addedLemma ){
- Trace("cegqi-engine") << " ...refine candidate." << std::endl;
- }
- }
-}
-
-void CegInstantiation::getCRefEvaluationLemmas( CegConjecture * conj, std::vector< Node >& vs, std::vector< Node >& ms, std::vector< Node >& lems ) {
- Trace("sygus-cref-eval") << "Cref eval : conjecture has " << conj->getNumRefinementLemmas() << " refinement lemmas." << std::endl;
- unsigned nlemmas = conj->getNumRefinementLemmas();
- if (nlemmas > 0 || options::cegisSample() != CEGIS_SAMPLE_NONE)
- {
- Assert( vs.size()==ms.size() );
-
- TermDbSygus* tds = d_quantEngine->getTermDatabaseSygus();
- Node nfalse = d_quantEngine->getTermUtil()->d_false;
- Node neg_guard = conj->getGuard().negate();
- for (unsigned i = 0; i <= nlemmas; i++)
- {
- if (i == nlemmas)
- {
- bool addedSample = false;
- // find a new one by sampling, if applicable
- if (options::cegisSample() != CEGIS_SAMPLE_NONE)
- {
- addedSample = conj->sampleAddRefinementLemma(ms, lems);
- }
- if (!addedSample)
- {
- return;
- }
- }
- Node lem;
- std::map< Node, Node > visited;
- std::map< Node, std::vector< Node > > exp;
- lem = conj->getRefinementLemma(i);
- if( !lem.isNull() ){
- std::vector< Node > lem_conj;
- //break into conjunctions
- if( lem.getKind()==kind::AND ){
- for( unsigned i=0; i<lem.getNumChildren(); i++ ){
- lem_conj.push_back( lem[i] );
- }
- }else{
- lem_conj.push_back( lem );
- }
- EvalSygusInvarianceTest vsit;
- for( unsigned j=0; j<lem_conj.size(); j++ ){
- Node lemc = lem_conj[j];
- Trace("sygus-cref-eval") << "Check refinement lemma conjunct " << lemc << " against current model." << std::endl;
- Trace("sygus-cref-eval2") << "Check refinement lemma conjunct " << lemc << " against current model." << std::endl;
- Node cre_lem;
- Node lemcs = lemc.substitute( vs.begin(), vs.end(), ms.begin(), ms.end() );
- Trace("sygus-cref-eval2") << "...under substitution it is : " << lemcs << std::endl;
- Node lemcsu = vsit.doEvaluateWithUnfolding(tds, lemcs);
- Trace("sygus-cref-eval2") << "...after unfolding is : " << lemcsu << std::endl;
- if( lemcsu==d_quantEngine->getTermUtil()->d_false ){
- std::vector< Node > msu;
- std::vector< Node > mexp;
- msu.insert( msu.end(), ms.begin(), ms.end() );
- for( unsigned k=0; k<vs.size(); k++ ){
- vsit.setUpdatedTerm(msu[k]);
- msu[k] = vs[k];
- // substitute for everything except this
- Node sconj =
- lemc.substitute(vs.begin(), vs.end(), msu.begin(), msu.end());
- vsit.init(sconj, vs[k], nfalse);
- // get minimal explanation for this
- Node ut = vsit.getUpdatedTerm();
- Trace("sygus-cref-eval2-debug")
- << " compute min explain of : " << vs[k] << " = " << ut
- << std::endl;
- d_quantEngine->getTermDatabaseSygus()
- ->getExplain()
- ->getExplanationFor(vs[k], ut, mexp, vsit);
- msu[k] = ut;
- }
- if( !mexp.empty() ){
- Node en = mexp.size()==1 ? mexp[0] : NodeManager::currentNM()->mkNode( kind::AND, mexp );
- cre_lem = NodeManager::currentNM()->mkNode( kind::OR, en.negate(), neg_guard );
- }else{
- cre_lem = neg_guard;
- }
- }
- if( !cre_lem.isNull() ){
- if( std::find( lems.begin(), lems.end(), cre_lem )==lems.end() ){
- Trace("sygus-cref-eval") << "...produced lemma : " << cre_lem << std::endl;
- lems.push_back( cre_lem );
- }
- }
- }
- }
- }
- }
-}
-
-void CegInstantiation::printSynthSolution( std::ostream& out ) {
- if( d_conj->isAssigned() )
- {
- d_conj->printSynthSolution( out, d_last_inst_si );
- }
- else
- {
- Assert( false );
- }
-}
-
-void CegInstantiation::getSynthSolutions(std::map<Node, Node>& sol_map)
-{
- if (d_conj->isAssigned())
- {
- d_conj->getSynthSolutions(sol_map, d_last_inst_si);
- }
- else
- {
- Assert(false);
- }
-}
-
-void CegInstantiation::preregisterAssertion( Node n ) {
- //check if it sygus conjecture
- if( QuantAttributes::checkSygusConjecture( n ) ){
- //this is a sygus conjecture
- Trace("cegqi") << "Preregister sygus conjecture : " << n << std::endl;
- d_conj->preregisterConjecture( n );
- }
-}
-
-CegInstantiation::Statistics::Statistics()
- : d_cegqi_lemmas_ce("CegInstantiation::cegqi_lemmas_ce", 0),
- d_cegqi_lemmas_refine("CegInstantiation::cegqi_lemmas_refine", 0),
- d_cegqi_si_lemmas("CegInstantiation::cegqi_lemmas_si", 0),
- d_solutions("CegConjecture::solutions", 0),
- d_candidate_rewrites_print("CegConjecture::candidate_rewrites_print", 0),
- d_candidate_rewrites("CegConjecture::candidate_rewrites", 0)
-
-{
- smtStatisticsRegistry()->registerStat(&d_cegqi_lemmas_ce);
- smtStatisticsRegistry()->registerStat(&d_cegqi_lemmas_refine);
- smtStatisticsRegistry()->registerStat(&d_cegqi_si_lemmas);
- smtStatisticsRegistry()->registerStat(&d_solutions);
- smtStatisticsRegistry()->registerStat(&d_candidate_rewrites_print);
- smtStatisticsRegistry()->registerStat(&d_candidate_rewrites);
-}
-
-CegInstantiation::Statistics::~Statistics(){
- smtStatisticsRegistry()->unregisterStat(&d_cegqi_lemmas_ce);
- smtStatisticsRegistry()->unregisterStat(&d_cegqi_lemmas_refine);
- smtStatisticsRegistry()->unregisterStat(&d_cegqi_si_lemmas);
- smtStatisticsRegistry()->unregisterStat(&d_solutions);
- smtStatisticsRegistry()->unregisterStat(&d_candidate_rewrites_print);
- smtStatisticsRegistry()->unregisterStat(&d_candidate_rewrites);
-}
-
-}/* namespace CVC4::theory::quantifiers */
-}/* namespace CVC4::theory */
-}/* namespace CVC4 */
+++ /dev/null
-/********************* */
-/*! \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<Node, bool, NodeHashFunction> NodeBoolMap;
-private:
- /** the quantified formula stating the synthesis conjecture */
- CegConjecture * d_conj;
- /** last instantiation by single invocation module? */
- bool d_last_inst_si;
-private: //for direct evaluation
- /** get refinement evaluation */
- void getCRefEvaluationLemmas( CegConjecture * conj, std::vector< Node >& vs, std::vector< Node >& ms, std::vector< Node >& lems );
-private:
- /** check conjecture */
- void checkCegConjecture( CegConjecture * conj );
-public:
- CegInstantiation( QuantifiersEngine * qe, context::Context* c );
- ~CegInstantiation();
-public:
- bool needsCheck( Theory::Effort e );
- QEffort needsModel(Theory::Effort e);
- /* Call during quantifier engine's check */
- void check(Theory::Effort e, QEffort quant_e);
- /* Called for new quantifiers */
- void registerQuantifier( Node q );
- /** get the next decision request */
- Node getNextDecisionRequest( unsigned& priority );
- /** Identify this module (for debugging, dynamic configuration, etc..) */
- std::string identify() const { return "CegInstantiation"; }
- /** print solution for synthesis conjectures */
- void printSynthSolution( std::ostream& out );
- /** get synth solutions
- *
- * This function adds entries to sol_map that map functions-to-synthesize
- * with their solutions, for all active conjectures (currently just the one
- * assigned to d_conj). This should be called immediately after the solver
- * answers unsat for sygus input.
- *
- * For details on what is added to sol_map, see
- * CegConjecture::getSynthSolutions.
- */
- void getSynthSolutions(std::map<Node, Node>& sol_map);
- /** preregister assertion (before rewrite) */
- void preregisterAssertion( Node n );
-public:
- class Statistics {
- public:
- IntStat d_cegqi_lemmas_ce;
- IntStat d_cegqi_lemmas_refine;
- IntStat d_cegqi_si_lemmas;
- IntStat d_solutions;
- IntStat d_candidate_rewrites_print;
- IntStat d_candidate_rewrites;
- Statistics();
- ~Statistics();
- };/* class CegInstantiation::Statistics */
- Statistics d_statistics;
-}; /* class CegInstantiation */
-
-} /* namespace CVC4::theory::quantifiers */
-} /* namespace CVC4::theory */
-} /* namespace CVC4 */
-
-#endif
+++ /dev/null
-/********************* */
-/*! \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<ind; i++ ){
- Trace(c) << " ";
- }
- }
-}
-
-void print_val( const char * c, std::vector< Node >& vals, bool pol = true ){
- if( Trace.isOn(c) ){
- for( unsigned i=0; i<vals.size(); i++ ){
- //Trace(c) << ( pol ? vals[i] : !vals[i] );
- Trace(c) << ( ( pol ? vals[i].getConst<bool>() : !vals[i].getConst<bool>() ) ? "1" : "0" );
- }
- }
-}
-
-std::ostream& operator<<(std::ostream& os, EnumRole r)
-{
- switch(r){
- case enum_invalid: os << "INVALID"; break;
- case enum_io: os << "IO"; break;
- case enum_ite_condition: os << "CONDITION"; break;
- case enum_concat_term: os << "CTERM"; break;
- default: os << "enum_" << static_cast<unsigned>(r); break;
- }
- return os;
-}
-
-std::ostream& operator<<(std::ostream& os, NodeRole r)
-{
- switch (r)
- {
- case role_equal: os << "equal"; break;
- case role_string_prefix: os << "string_prefix"; break;
- case role_string_suffix: os << "string_suffix"; break;
- case role_ite_condition: os << "ite_condition"; break;
- default: os << "role_" << static_cast<unsigned>(r); break;
- }
- return os;
-}
-
-EnumRole getEnumeratorRoleForNodeRole(NodeRole r)
-{
- switch (r)
- {
- case role_equal: return enum_io; break;
- case role_string_prefix: return enum_concat_term; break;
- case role_string_suffix: return enum_concat_term; break;
- case role_ite_condition: return enum_ite_condition; break;
- default: break;
- }
- return enum_invalid;
-}
-
-std::ostream& operator<<(std::ostream& os, StrategyType st)
-{
- switch (st)
- {
- case strat_ITE: os << "ITE"; break;
- case strat_CONCAT_PREFIX: os << "CONCAT_PREFIX"; break;
- case strat_CONCAT_SUFFIX: os << "CONCAT_SUFFIX"; break;
- case strat_ID: os << "ID"; break;
- default: os << "strat_" << static_cast<unsigned>(st); break;
- }
- return os;
-}
-
-CegConjecturePbe::CegConjecturePbe(QuantifiersEngine* qe, CegConjecture* p)
- : d_qe(qe),
- d_parent(p){
- d_tds = d_qe->getTermDatabaseSygus();
- d_true = NodeManager::currentNM()->mkConst(true);
- d_false = NodeManager::currentNM()->mkConst(false);
- d_is_pbe = false;
-}
-
-CegConjecturePbe::~CegConjecturePbe() {
-
-}
-
-//--------------------------------- collecting finite input/output domain information
-
-void CegConjecturePbe::collectExamples( Node n, std::map< Node, bool >& visited, bool hasPol, bool pol ) {
- if( visited.find( n )==visited.end() ){
- visited[n] = true;
- Node neval;
- Node n_output;
- if( n.getKind()==APPLY_UF && n.getNumChildren()>0 ){
- neval = n;
- if( hasPol ){
- n_output = !pol ? d_true : d_false;
- }
- }else if( n.getKind()==EQUAL && hasPol && !pol ){
- for( unsigned r=0; r<2; r++ ){
- if( n[r].getKind()==APPLY_UF && n[r].getNumChildren()>0 ){
- neval = n[r];
- if( n[1-r].isConst() ){
- n_output = n[1-r];
- }
- }
- }
- }
- if( !neval.isNull() ){
- if( neval.getKind()==APPLY_UF && neval.getNumChildren()>0 ){
- // is it an evaluation function?
- if( d_examples.find( neval[0] )!=d_examples.end() ){
- std::map< Node, bool >::iterator itx = d_examples_invalid.find( neval[0] );
- if( itx==d_examples_invalid.end() ){
- //collect example
- bool success = true;
- std::vector< Node > ex;
- for( unsigned j=1; j<neval.getNumChildren(); j++ ){
- if( !neval[j].isConst() ){
- success = false;
- break;
- }else{
- ex.push_back( neval[j] );
- }
- }
- if( success ){
- d_examples[neval[0]].push_back( ex );
- d_examples_out[neval[0]].push_back( n_output );
- d_examples_term[neval[0]].push_back( neval );
- if( n_output.isNull() ){
- d_examples_out_invalid[neval[0]] = true;
- }else{
- Assert( n_output.isConst() );
- }
- //finished processing this node
- return;
- }else{
- d_examples_invalid[neval[0]] = true;
- d_examples_out_invalid[neval[0]] = true;
- }
- }
- }
- }
- }
- for( unsigned i=0; i<n.getNumChildren(); i++ ){
- bool newHasPol;
- bool newPol;
- QuantPhaseReq::getPolarity( n, i, hasPol, pol, newHasPol, newPol );
- collectExamples( n[i], visited, newHasPol, newPol );
- }
- }
-}
-
-void CegConjecturePbe::initialize(Node n,
- std::vector<Node>& candidates,
- std::vector<Node>& lemmas)
-{
- Trace("sygus-pbe") << "Initialize PBE : " << n << std::endl;
-
- for( unsigned i=0; i<candidates.size(); i++ ){
- Node v = candidates[i];
- d_examples[v].clear();
- d_examples_out[v].clear();
- d_examples_term[v].clear();
- }
-
- std::map< Node, bool > visited;
- collectExamples( n, visited, true, true );
-
- for( unsigned i=0; i<candidates.size(); i++ ){
- Node v = candidates[i];
- Trace("sygus-pbe") << " examples for " << v << " : ";
- if( d_examples_invalid.find( v )!=d_examples_invalid.end() ){
- Trace("sygus-pbe") << "INVALID" << std::endl;
- }else{
- Trace("sygus-pbe") << std::endl;
- for( unsigned j=0; j<d_examples[v].size(); j++ ){
- Trace("sygus-pbe") << " ";
- for( unsigned k=0; k<d_examples[v][j].size(); k++ ){
- Trace("sygus-pbe") << d_examples[v][j][k] << " ";
- }
- if( !d_examples_out[v][j].isNull() ){
- Trace("sygus-pbe") << " -> " << d_examples_out[v][j];
- }
- Trace("sygus-pbe") << std::endl;
- }
- }
- }
-
- //register candidates
- if( options::sygusUnifCondSol() ){
- if( candidates.size()==1 ){// conditional solutions for multiple function conjectures TODO?
- // collect a pool of types over which we will enumerate terms
- Node c = candidates[0];
- //the candidate must be input/output examples
- if( d_examples_out_invalid.find( c )==d_examples_out_invalid.end() ){
- Assert( d_examples.find( c )!=d_examples.end() );
- Trace("sygus-unif") << "It is input/output examples..." << std::endl;
- TypeNode ctn = c.getType();
- d_cinfo[c].initialize( c );
- // collect the enumerator types / form the strategy
- collectEnumeratorTypes(c, ctn, role_equal);
- // if we have non-trivial strategies, then use pbe
- if( d_cinfo[c].isNonTrivial() ){
- // static learning of redundant constructors
- staticLearnRedundantOps( c, lemmas );
- d_is_pbe = true;
- }
- }
- }
- }
- if( !d_is_pbe ){
- Trace("sygus-unif") << "Do not do PBE optimizations, register..." << std::endl;
- for( unsigned i=0; i<candidates.size(); i++ ){
- d_qe->getTermDatabaseSygus()->registerEnumerator(
- candidates[i], candidates[i], d_parent);
- }
- }
-}
-
-Node CegConjecturePbe::PbeTrie::addPbeExample(TypeNode etn, Node e, Node b,
- CegConjecturePbe* cpbe,
- unsigned index, unsigned ntotal) {
- if (index == ntotal) {
- // lazy child holds the leaf data
- if (d_lazy_child.isNull()) {
- d_lazy_child = b;
- }
- return d_lazy_child;
- } else {
- std::vector<Node> ex;
- if (d_children.empty()) {
- if (d_lazy_child.isNull()) {
- d_lazy_child = b;
- return d_lazy_child;
- } else {
- // evaluate the lazy child
- Assert(cpbe->d_examples.find(e) != cpbe->d_examples.end());
- Assert(index < cpbe->d_examples[e].size());
- ex = cpbe->d_examples[e][index];
- addPbeExampleEval(etn, e, d_lazy_child, ex, cpbe, index, ntotal);
- Assert(!d_children.empty());
- d_lazy_child = Node::null();
- }
- } else {
- Assert(cpbe->d_examples.find(e) != cpbe->d_examples.end());
- Assert(index < cpbe->d_examples[e].size());
- ex = cpbe->d_examples[e][index];
- }
- return addPbeExampleEval(etn, e, b, ex, cpbe, index, ntotal);
- }
-}
-
-Node CegConjecturePbe::PbeTrie::addPbeExampleEval(TypeNode etn, Node e, Node b,
- std::vector<Node>& ex,
- CegConjecturePbe* cpbe,
- unsigned index,
- unsigned ntotal) {
- Node eb = cpbe->d_tds->evaluateBuiltin(etn, b, ex);
- return d_children[eb].addPbeExample(etn, e, b, cpbe, index + 1, ntotal);
-}
-
-bool CegConjecturePbe::hasExamples(Node e) {
- if (isPbe()) {
- e = d_tds->getSynthFunForEnumerator(e);
- Assert(!e.isNull());
- std::map<Node, bool>::iterator itx = d_examples_invalid.find(e);
- if (itx == d_examples_invalid.end()) {
- return d_examples.find(e) != d_examples.end();
- }
- }
- return false;
-}
-
-unsigned CegConjecturePbe::getNumExamples(Node e) {
- e = d_tds->getSynthFunForEnumerator(e);
- Assert(!e.isNull());
- std::map<Node, std::vector<std::vector<Node> > >::iterator it =
- d_examples.find(e);
- if (it != d_examples.end()) {
- return it->second.size();
- } else {
- return 0;
- }
-}
-
-void CegConjecturePbe::getExample(Node e, unsigned i, std::vector<Node>& ex) {
- e = d_tds->getSynthFunForEnumerator(e);
- Assert(!e.isNull());
- std::map<Node, std::vector<std::vector<Node> > >::iterator it =
- d_examples.find(e);
- if (it != d_examples.end()) {
- Assert(i < it->second.size());
- ex.insert(ex.end(), it->second[i].begin(), it->second[i].end());
- } else {
- Assert(false);
- }
-}
-
-Node CegConjecturePbe::getExampleOut(Node e, unsigned i) {
- e = d_tds->getSynthFunForEnumerator(e);
- Assert(!e.isNull());
- std::map<Node, std::vector<Node> >::iterator it = d_examples_out.find(e);
- if (it != d_examples_out.end()) {
- Assert(i < it->second.size());
- return it->second[i];
- } else {
- Assert(false);
- return Node::null();
- }
-}
-
-Node CegConjecturePbe::addSearchVal(TypeNode tn, Node e, Node bvr) {
- Assert(isPbe());
- Assert(!e.isNull());
- e = d_tds->getSynthFunForEnumerator(e);
- Assert(!e.isNull());
- std::map<Node, bool>::iterator itx = d_examples_invalid.find(e);
- if (itx == d_examples_invalid.end()) {
- unsigned nex = d_examples[e].size();
- Node ret = d_pbe_trie[e][tn].addPbeExample(tn, e, bvr, this, 0, nex);
- Assert(ret.getType() == bvr.getType());
- return ret;
- }
- return Node::null();
-}
-
-Node CegConjecturePbe::evaluateBuiltin(TypeNode tn, Node bn, Node e,
- unsigned i) {
- e = d_tds->getSynthFunForEnumerator(e);
- Assert(!e.isNull());
- std::map<Node, bool>::iterator itx = d_examples_invalid.find(e);
- if (itx == d_examples_invalid.end()) {
- std::map<Node, std::vector<std::vector<Node> > >::iterator it =
- d_examples.find(e);
- if (it != d_examples.end()) {
- Assert(i < it->second.size());
- return d_tds->evaluateBuiltin(tn, bn, it->second[i]);
- }
- }
- return Rewriter::rewrite(bn);
-}
-
-// ----------------------------- establishing enumeration types
-
-void CegConjecturePbe::registerEnumerator(
- Node et, Node c, TypeNode tn, EnumRole enum_role, bool inSearch)
-{
- if (d_einfo.find(et) == d_einfo.end())
- {
- Trace("sygus-unif-debug")
- << "...register " << et << " for "
- << ((DatatypeType)tn.toType()).getDatatype().getName();
- Trace("sygus-unif-debug") << ", role = " << enum_role
- << ", in search = " << inSearch << std::endl;
- d_einfo[et].initialize(c, enum_role);
- // if we are actually enumerating this (could be a compound node in the
- // strategy)
- if (inSearch)
- {
- std::map<TypeNode, Node>::iterator itn =
- d_cinfo[c].d_search_enum.find(tn);
- if (itn == d_cinfo[c].d_search_enum.end())
- {
- // use this for the search
- d_cinfo[c].d_search_enum[tn] = et;
- d_cinfo[c].d_esym_list.push_back(et);
- d_einfo[et].d_enum_slave.push_back(et);
- // register measured term with database
- d_qe->getTermDatabaseSygus()->registerEnumerator(et, c, d_parent, true);
- d_einfo[et].d_active_guard =
- d_qe->getTermDatabaseSygus()->getActiveGuardForEnumerator(et);
- }
- else
- {
- Trace("sygus-unif-debug") << "Make " << et << " a slave of "
- << itn->second << std::endl;
- d_einfo[itn->second].d_enum_slave.push_back(et);
- }
- }
- }
-}
-
-void CegConjecturePbe::collectEnumeratorTypes(Node e,
- TypeNode tn,
- NodeRole nrole)
-{
- NodeManager* nm = NodeManager::currentNM();
- if (d_cinfo[e].d_tinfo.find(tn) == d_cinfo[e].d_tinfo.end())
- {
- // register type
- Trace("sygus-unif") << "Register enumerating type : " << tn << std::endl;
- d_cinfo[e].initializeType( tn );
- }
- EnumTypeInfo& eti = d_cinfo[e].d_tinfo[tn];
- std::map<NodeRole, StrategyNode>::iterator itsn = eti.d_snodes.find(nrole);
- if (itsn != eti.d_snodes.end())
- {
- // already initialized
- return;
- }
- StrategyNode& snode = eti.d_snodes[nrole];
-
- // get the enumerator for this
- EnumRole erole = getEnumeratorRoleForNodeRole(nrole);
-
- Node ee;
- std::map<EnumRole, Node>::iterator iten = eti.d_enum.find(erole);
- if (iten == eti.d_enum.end())
- {
- ee = nm->mkSkolem("ee", tn);
- eti.d_enum[erole] = ee;
- Trace("sygus-unif-debug")
- << "...enumerator " << ee << " for "
- << ((DatatypeType)tn.toType()).getDatatype().getName()
- << ", role = " << erole << std::endl;
- }
- else
- {
- ee = iten->second;
- }
-
- // roles that we do not recurse on
- if (nrole == role_ite_condition)
- {
- Trace("sygus-unif-debug") << "...this register (non-io)" << std::endl;
- registerEnumerator(ee, e, tn, erole, true);
- return;
- }
-
- // look at information on how we will construct solutions for this type
- Assert(tn.isDatatype());
- const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
- Assert(dt.isSygus());
-
- std::map<Node, std::vector<StrategyType> > cop_to_strat;
- std::map<Node, unsigned> cop_to_cindex;
- std::map<Node, std::map<unsigned, Node> > cop_to_child_templ;
- std::map<Node, std::map<unsigned, Node> > cop_to_child_templ_arg;
- std::map<Node, std::vector<unsigned> > cop_to_carg_list;
- std::map<Node, std::vector<TypeNode> > cop_to_child_types;
- std::map<Node, std::vector<Node> > cop_to_sks;
-
- // whether we will enumerate the current type
- bool search_this = false;
- for (unsigned j = 0, ncons = dt.getNumConstructors(); j < ncons; j++)
- {
- Node cop = Node::fromExpr(dt[j].getConstructor());
- Node op = Node::fromExpr(dt[j].getSygusOp());
- Trace("sygus-unif-debug") << "--- Infer strategy from " << cop
- << " with sygus op " << op << "..." << std::endl;
-
- // expand the evaluation to see if this constuctor induces a strategy
- std::vector<Node> utchildren;
- utchildren.push_back(cop);
- std::vector<Node> sks;
- std::vector<TypeNode> sktns;
- for (unsigned k = 0, nargs = dt[j].getNumArgs(); k < nargs; k++)
- {
- Type t = dt[j][k].getRangeType();
- TypeNode ttn = TypeNode::fromType(t);
- Node kv = nm->mkSkolem("ut", ttn);
- sks.push_back(kv);
- cop_to_sks[cop].push_back(kv);
- sktns.push_back(ttn);
- utchildren.push_back(kv);
- }
- Node ut = nm->mkNode(APPLY_CONSTRUCTOR, utchildren);
- std::vector<Node> echildren;
- echildren.push_back(Node::fromExpr(dt.getSygusEvaluationFunc()));
- echildren.push_back(ut);
- Node sbvl = Node::fromExpr(dt.getSygusVarList());
- for (const Node& sbv : sbvl)
- {
- echildren.push_back(sbv);
- }
- Node eut = nm->mkNode(APPLY_UF, echildren);
- Trace("sygus-unif-debug2") << " Test evaluation of " << eut << "..."
- << std::endl;
- eut = d_qe->getTermDatabaseSygus()->unfold(eut);
- Trace("sygus-unif-debug2") << " ...got " << eut;
- Trace("sygus-unif-debug2") << ", type : " << eut.getType() << std::endl;
-
- // candidate strategy
- if (eut.getKind() == ITE)
- {
- cop_to_strat[cop].push_back(strat_ITE);
- }
- else if (eut.getKind() == STRING_CONCAT)
- {
- if (nrole != role_string_suffix)
- {
- cop_to_strat[cop].push_back(strat_CONCAT_PREFIX);
- }
- if (nrole != role_string_prefix)
- {
- cop_to_strat[cop].push_back(strat_CONCAT_SUFFIX);
- }
- }
- else if (dt[j].isSygusIdFunc())
- {
- cop_to_strat[cop].push_back(strat_ID);
- }
-
- // the kinds for which there is a strategy
- if (cop_to_strat.find(cop) != cop_to_strat.end())
- {
- // infer an injection from the arguments of the datatype
- std::map<unsigned, unsigned> templ_injection;
- std::vector<Node> vs;
- std::vector<Node> ss;
- std::map<Node, unsigned> templ_var_index;
- for (unsigned k = 0, sksize = sks.size(); k < sksize; k++)
- {
- Assert(sks[k].getType().isDatatype());
- const Datatype& cdt =
- static_cast<DatatypeType>(sks[k].getType().toType()).getDatatype();
- echildren[0] = Node::fromExpr(cdt.getSygusEvaluationFunc());
- echildren[1] = sks[k];
- Trace("sygus-unif-debug2") << "...set eval dt to " << sks[k]
- << std::endl;
- Node esk = nm->mkNode(APPLY_UF, echildren);
- vs.push_back(esk);
- Node tvar = nm->mkSkolem("templ", esk.getType());
- templ_var_index[tvar] = k;
- Trace("sygus-unif-debug2") << "* template inference : looking for "
- << tvar << " for arg " << k << std::endl;
- ss.push_back(tvar);
- Trace("sygus-unif-debug2") << "* substitute : " << esk << " -> " << tvar
- << std::endl;
- }
- eut = eut.substitute(vs.begin(), vs.end(), ss.begin(), ss.end());
- Trace("sygus-unif-debug2") << "Constructor " << j << ", base term is "
- << eut << std::endl;
- std::map<unsigned, Node> test_args;
- if (dt[j].isSygusIdFunc())
- {
- test_args[0] = eut;
- }
- else
- {
- for (unsigned k = 0, size = eut.getNumChildren(); k < size; k++)
- {
- test_args[k] = eut[k];
- }
- }
-
- // TODO : prefix grouping prefix/suffix
- bool isAssoc = TermUtil::isAssoc(eut.getKind());
- Trace("sygus-unif-debug2") << eut.getKind() << " isAssoc = " << isAssoc
- << std::endl;
- std::map<unsigned, std::vector<unsigned> > assoc_combine;
- std::vector<unsigned> assoc_waiting;
- int assoc_last_valid_index = -1;
- for (std::pair<const unsigned, Node>& ta : test_args)
- {
- unsigned k = ta.first;
- Node eut_c = ta.second;
- // success if we can find a injection from args to sygus args
- if (!inferTemplate(k, eut_c, templ_var_index, templ_injection))
- {
- Trace("sygus-unif-debug")
- << "...fail: could not find injection (range)." << std::endl;
- cop_to_strat.erase(cop);
- break;
- }
- std::map<unsigned, unsigned>::iterator itti = templ_injection.find(k);
- if (itti != templ_injection.end())
- {
- // if associative, combine arguments if it is the same variable
- if (isAssoc && assoc_last_valid_index >= 0
- && itti->second == templ_injection[assoc_last_valid_index])
- {
- templ_injection.erase(k);
- assoc_combine[assoc_last_valid_index].push_back(k);
- }
- else
- {
- assoc_last_valid_index = (int)k;
- if (!assoc_waiting.empty())
- {
- assoc_combine[k].insert(assoc_combine[k].end(),
- assoc_waiting.begin(),
- assoc_waiting.end());
- assoc_waiting.clear();
- }
- assoc_combine[k].push_back(k);
- }
- }
- else
- {
- // a ground argument
- if (!isAssoc)
- {
- Trace("sygus-unif-debug")
- << "...fail: could not find injection (functional)."
- << std::endl;
- cop_to_strat.erase(cop);
- break;
- }
- else
- {
- if (assoc_last_valid_index >= 0)
- {
- assoc_combine[assoc_last_valid_index].push_back(k);
- }
- else
- {
- assoc_waiting.push_back(k);
- }
- }
- }
- }
- if (cop_to_strat.find(cop) != cop_to_strat.end())
- {
- // construct the templates
- if (!assoc_waiting.empty())
- {
- // could not find a way to fit some arguments into injection
- cop_to_strat.erase(cop);
- }
- else
- {
- for (std::pair<const unsigned, Node>& ta : test_args)
- {
- unsigned k = ta.first;
- Trace("sygus-unif-debug2") << "- processing argument " << k << "..."
- << std::endl;
- if (templ_injection.find(k) != templ_injection.end())
- {
- unsigned sk_index = templ_injection[k];
- if (std::find(cop_to_carg_list[cop].begin(),
- cop_to_carg_list[cop].end(),
- sk_index)
- == cop_to_carg_list[cop].end())
- {
- cop_to_carg_list[cop].push_back(sk_index);
- }else{
- Trace("sygus-unif-debug") << "...fail: duplicate argument used"
- << std::endl;
- cop_to_strat.erase(cop);
- break;
- }
- // also store the template information, if necessary
- Node teut;
- if (isAssoc)
- {
- std::vector<unsigned>& ac = assoc_combine[k];
- Assert(!ac.empty());
- std::vector<Node> children;
- for (unsigned ack = 0, size_ac = ac.size(); ack < size_ac;
- ack++)
- {
- children.push_back(eut[ac[ack]]);
- }
- teut = children.size() == 1
- ? children[0]
- : nm->mkNode(eut.getKind(), children);
- teut = Rewriter::rewrite(teut);
- }
- else
- {
- teut = ta.second;
- }
-
- if (!teut.isVar())
- {
- cop_to_child_templ[cop][k] = teut;
- cop_to_child_templ_arg[cop][k] = ss[sk_index];
- Trace("sygus-unif-debug")
- << " Arg " << k << " (template : " << teut << " arg "
- << ss[sk_index] << "), index " << sk_index << std::endl;
- }
- else
- {
- Trace("sygus-unif-debug") << " Arg " << k << ", index "
- << sk_index << std::endl;
- Assert(teut == ss[sk_index]);
- }
- }
- else
- {
- Assert(isAssoc);
- }
- }
- }
- }
- }
- if (cop_to_strat.find(cop) == cop_to_strat.end())
- {
- Trace("sygus-unif") << "...constructor " << cop
- << " does not correspond to a strategy." << std::endl;
- search_this = true;
- }
- else
- {
- Trace("sygus-unif") << "-> constructor " << cop
- << " matches strategy for " << eut.getKind() << "..."
- << std::endl;
- // collect children types
- for (unsigned k = 0, size = cop_to_carg_list[cop].size(); k < size; k++)
- {
- TypeNode tn = sktns[cop_to_carg_list[cop][k]];
- Trace("sygus-unif-debug")
- << " Child type " << k << " : "
- << static_cast<DatatypeType>(tn.toType()).getDatatype().getName()
- << std::endl;
- cop_to_child_types[cop].push_back(tn);
- }
- }
- }
-
- // check whether we should also enumerate the current type
- Trace("sygus-unif-debug2") << " register this enumerator..." << std::endl;
- registerEnumerator(ee, e, tn, erole, search_this);
-
- if (cop_to_strat.empty())
- {
- Trace("sygus-unif") << "...consider " << dt.getName() << " a basic type"
- << std::endl;
- }
- else
- {
- for (std::pair<const Node, std::vector<StrategyType> >& cstr : cop_to_strat)
- {
- Node cop = cstr.first;
- Trace("sygus-unif-debug") << "Constructor " << cop << " has "
- << cstr.second.size() << " strategies..."
- << std::endl;
- for (unsigned s = 0, ssize = cstr.second.size(); s < ssize; s++)
- {
- EnumTypeInfoStrat* cons_strat = new EnumTypeInfoStrat;
- StrategyType strat = cstr.second[s];
-
- cons_strat->d_this = strat;
- cons_strat->d_cons = cop;
- Trace("sygus-unif-debug") << "Process strategy #" << s
- << " for operator : " << cop << " : " << strat
- << std::endl;
- Assert(cop_to_child_types.find(cop) != cop_to_child_types.end());
- std::vector<TypeNode>& childTypes = cop_to_child_types[cop];
- Assert(cop_to_carg_list.find(cop) != cop_to_carg_list.end());
- std::vector<unsigned>& cargList = cop_to_carg_list[cop];
-
- std::vector<Node> sol_templ_children;
- sol_templ_children.resize(cop_to_sks[cop].size());
-
- for (unsigned j = 0, csize = childTypes.size(); j < csize; j++)
- {
- // calculate if we should allocate a new enumerator : should be true
- // if we have a new role
- NodeRole nrole_c = nrole;
- if (strat == strat_ITE)
- {
- if (j == 0)
- {
- nrole_c = role_ite_condition;
- }
- }
- else if (strat == strat_CONCAT_PREFIX)
- {
- if ((j + 1) < childTypes.size())
- {
- nrole_c = role_string_prefix;
- }
- }
- else if (strat == strat_CONCAT_SUFFIX)
- {
- if (j > 0)
- {
- nrole_c = role_string_suffix;
- }
- }
- // in all other cases, role is same as parent
-
- // register the child type
- TypeNode ct = childTypes[j];
- Node csk = cop_to_sks[cop][cargList[j]];
- cons_strat->d_sol_templ_args.push_back(csk);
- sol_templ_children[cargList[j]] = csk;
-
- EnumRole erole_c = getEnumeratorRoleForNodeRole(nrole_c);
- // make the enumerator
- Node et;
- if (cop_to_child_templ[cop].find(j) != cop_to_child_templ[cop].end())
- {
- // it is templated, allocate a fresh variable
- et = nm->mkSkolem("et", ct);
- Trace("sygus-unif-debug")
- << "...enumerate " << et << " of type "
- << ((DatatypeType)ct.toType()).getDatatype().getName();
- Trace("sygus-unif-debug")
- << " for arg " << j << " of "
- << ((DatatypeType)tn.toType()).getDatatype().getName()
- << std::endl;
- registerEnumerator(et, e, ct, erole_c, true);
- d_einfo[et].d_template = cop_to_child_templ[cop][j];
- d_einfo[et].d_template_arg = cop_to_child_templ_arg[cop][j];
- Assert(!d_einfo[et].d_template.isNull());
- Assert(!d_einfo[et].d_template_arg.isNull());
- }
- else
- {
- Trace("sygus-unif-debug")
- << "...child type enumerate "
- << ((DatatypeType)ct.toType()).getDatatype().getName()
- << ", node role = " << nrole_c << std::endl;
- collectEnumeratorTypes(e, ct, nrole_c);
- // otherwise use the previous
- Assert(d_cinfo[e].d_tinfo[ct].d_enum.find(erole_c)
- != d_cinfo[e].d_tinfo[ct].d_enum.end());
- et = d_cinfo[e].d_tinfo[ct].d_enum[erole_c];
- }
- Trace("sygus-unif-debug") << "Register child enumerator " << et
- << ", arg " << j << " of " << cop
- << ", role = " << erole_c << std::endl;
- Assert(!et.isNull());
- cons_strat->d_cenum.push_back(std::pair<Node, NodeRole>(et, nrole_c));
- }
- // children that are unused in the strategy can be arbitrary
- for (unsigned j = 0, stsize = sol_templ_children.size(); j < stsize;
- j++)
- {
- if (sol_templ_children[j].isNull())
- {
- sol_templ_children[j] = cop_to_sks[cop][j].getType().mkGroundTerm();
- }
- }
- sol_templ_children.insert(sol_templ_children.begin(), cop);
- cons_strat->d_sol_templ =
- nm->mkNode(APPLY_CONSTRUCTOR, sol_templ_children);
- if (strat == strat_CONCAT_SUFFIX)
- {
- std::reverse(cons_strat->d_cenum.begin(), cons_strat->d_cenum.end());
- std::reverse(cons_strat->d_sol_templ_args.begin(),
- cons_strat->d_sol_templ_args.end());
- }
- if (Trace.isOn("sygus-unif"))
- {
- Trace("sygus-unif") << "Initialized strategy " << strat;
- Trace("sygus-unif") << " for " << ((DatatypeType)tn.toType()).getDatatype().getName() << ", operator " << cop;
- Trace("sygus-unif") << ", #children = " << cons_strat->d_cenum.size()
- << ", solution template = (lambda ( ";
- for (const Node& targ : cons_strat->d_sol_templ_args)
- {
- Trace("sygus-unif") << targ << " ";
- }
- Trace("sygus-unif") << ") " << cons_strat->d_sol_templ << ")";
- Trace("sygus-unif") << std::endl;
- }
- // make the strategy
- snode.d_strats.push_back(cons_strat);
- }
- }
- }
-}
-
-bool CegConjecturePbe::inferTemplate( unsigned k, Node n, std::map< Node, unsigned >& templ_var_index, std::map< unsigned, unsigned >& templ_injection ){
- if( n.getNumChildren()==0 ){
- std::map< Node, unsigned >::iterator itt = templ_var_index.find( n );
- if( itt!=templ_var_index.end() ){
- unsigned kk = itt->second;
- std::map< unsigned, unsigned >::iterator itti = templ_injection.find( k );
- if( itti==templ_injection.end() ){
- Trace("sygus-unif-debug") << "...set template injection " << k << " -> " << kk << std::endl;
- templ_injection[k] = kk;
- }else if( itti->second!=kk ){
- // two distinct variables in this term, we fail
- return false;
- }
- }
- return true;
- }else{
- for( unsigned i=0; i<n.getNumChildren(); i++ ){
- if( !inferTemplate( k, n[i], templ_var_index, templ_injection ) ){
- return false;
- }
- }
- }
- return true;
-}
-
-void CegConjecturePbe::staticLearnRedundantOps( Node c, std::vector< Node >& lemmas ) {
- for( unsigned i=0; i<d_cinfo[c].d_esym_list.size(); i++ ){
- Node e = d_cinfo[c].d_esym_list[i];
- std::map< Node, EnumInfo >::iterator itn = d_einfo.find( e );
- Assert( itn!=d_einfo.end() );
- // see if there is anything we can eliminate
- Trace("sygus-unif") << "* Search enumerator #" << i << " : type " << ((DatatypeType)e.getType().toType()).getDatatype().getName() << " : ";
- Trace("sygus-unif") << e << " has " << itn->second.d_enum_slave.size() << " slaves:" << std::endl;
- for( unsigned j=0; j<itn->second.d_enum_slave.size(); j++ ){
- Node es = itn->second.d_enum_slave[j];
- std::map< Node, EnumInfo >::iterator itns = d_einfo.find( es );
- Assert( itns!=d_einfo.end() );
- Trace("sygus-unif") << " " << es << ", role = " << itns->second.getRole()
- << std::endl;
- }
- }
- Trace("sygus-unif") << std::endl;
- Trace("sygus-unif") << "Strategy for candidate " << c << " is : " << std::endl;
- std::map<Node, std::map<NodeRole, bool> > visited;
- std::map<Node, std::map<unsigned, bool> > needs_cons;
- staticLearnRedundantOps(c,
- d_cinfo[c].getRootEnumerator(),
- role_equal,
- visited,
- needs_cons,
- 0,
- false);
- // now, check the needs_cons map
- for (std::pair<const Node, std::map<unsigned, bool> >& nce : needs_cons)
- {
- Node em = nce.first;
- const Datatype& dt =
- static_cast<DatatypeType>(em.getType().toType()).getDatatype();
- for (std::pair<const unsigned, bool>& nc : nce.second)
- {
- Assert(nc.first < dt.getNumConstructors());
- if (!nc.second)
- {
- Node tst =
- datatypes::DatatypesRewriter::mkTester(em, nc.first, dt).negate();
- if (std::find(lemmas.begin(), lemmas.end(), tst) == lemmas.end())
- {
- Trace("sygus-unif") << "...can exclude based on : " << tst
- << std::endl;
- lemmas.push_back(tst);
- }
- }
- }
- }
-}
-
-void CegConjecturePbe::staticLearnRedundantOps(
- Node c,
- Node e,
- NodeRole nrole,
- std::map<Node, std::map<NodeRole, bool> >& visited,
- std::map<Node, std::map<unsigned, bool> >& needs_cons,
- int ind,
- bool isCond)
-{
- std::map< Node, EnumInfo >::iterator itn = d_einfo.find( e );
- Assert( itn!=d_einfo.end() );
-
- if (visited[e].find(nrole) == visited[e].end()
- || (isCond && !itn->second.isConditional()))
- {
- visited[e][nrole] = true;
- // if conditional
- if (isCond)
- {
- itn->second.setConditional();
- }
- indent("sygus-unif", ind);
- Trace("sygus-unif") << e << " :: node role : " << nrole;
- Trace("sygus-unif")
- << ", type : "
- << ((DatatypeType)e.getType().toType()).getDatatype().getName();
- if (isCond)
- {
- Trace("sygus-unif") << ", conditional";
- }
- Trace("sygus-unif") << ", enum role : " << itn->second.getRole();
-
- if( itn->second.isTemplated() ){
- Trace("sygus-unif") << ", templated : (lambda "
- << itn->second.d_template_arg << " "
- << itn->second.d_template << ")" << std::endl;
- }else{
- Trace("sygus-unif") << std::endl;
- TypeNode etn = e.getType();
-
- // enumerator type info
- std::map< TypeNode, EnumTypeInfo >::iterator itt = d_cinfo[c].d_tinfo.find( etn );
- Assert( itt!=d_cinfo[c].d_tinfo.end() );
- EnumTypeInfo& tinfo = itt->second;
-
- // strategy info
- std::map<NodeRole, StrategyNode>::iterator itsn =
- tinfo.d_snodes.find(nrole);
- Assert(itsn != tinfo.d_snodes.end());
- StrategyNode& snode = itsn->second;
-
- if (snode.d_strats.empty())
- {
- return;
- }
- std::map<unsigned, bool> needs_cons_curr;
- // various strategies
- for (unsigned j = 0, size = snode.d_strats.size(); j < size; j++)
- {
- EnumTypeInfoStrat* etis = snode.d_strats[j];
- StrategyType strat = etis->d_this;
- bool newIsCond = isCond || strat == strat_ITE;
- indent("sygus-unif", ind + 1);
- Trace("sygus-unif") << "Strategy : " << strat
- << ", from cons : " << etis->d_cons << std::endl;
- int cindex = Datatype::indexOf(etis->d_cons.toExpr());
- Assert(cindex != -1);
- needs_cons_curr[static_cast<unsigned>(cindex)] = false;
- for (std::pair<Node, NodeRole>& cec : etis->d_cenum)
- {
- // recurse
- staticLearnRedundantOps(c,
- cec.first,
- cec.second,
- visited,
- needs_cons,
- ind + 2,
- newIsCond);
- }
- }
- // get the master enumerator for the type of this enumerator
- std::map<TypeNode, Node>::iterator itse =
- d_cinfo[c].d_search_enum.find(etn);
- if (itse == d_cinfo[c].d_search_enum.end())
- {
- return;
- }
- Node em = itse->second;
- Assert(!em.isNull());
- // get the current datatype
- const Datatype& dt =
- static_cast<DatatypeType>(etn.toType()).getDatatype();
- // all constructors that are not a part of a strategy are needed
- for (unsigned j = 0, size = dt.getNumConstructors(); j < size; j++)
- {
- if (needs_cons_curr.find(j) == needs_cons_curr.end())
- {
- needs_cons_curr[j] = true;
- }
- }
- // update the constructors that the master enumerator needs
- if (needs_cons.find(em) == needs_cons.end())
- {
- needs_cons[em] = needs_cons_curr;
- }
- else
- {
- for (unsigned j = 0, size = dt.getNumConstructors(); j < size; j++)
- {
- needs_cons[em][j] = needs_cons[em][j] || needs_cons_curr[j];
- }
- }
- }
- }else{
- indent("sygus-unif", ind);
- Trace("sygus-unif") << e << " :: node role : " << nrole << std::endl;
- }
-}
-
-// ------------------------------------------- solution construction from enumeration
-
-void CegConjecturePbe::getCandidateList( std::vector< Node >& candidates, std::vector< Node >& clist ) {
- Valuation& valuation = d_qe->getValuation();
- for( unsigned i=0; i<candidates.size(); i++ ){
- Node v = candidates[i];
- std::map< Node, CandidateInfo >::iterator it = d_cinfo.find( v );
- if( it!=d_cinfo.end() ){
- for( unsigned j=0; j<it->second.d_esym_list.size(); j++ ){
- Node e = it->second.d_esym_list[j];
- std::map< Node, EnumInfo >::iterator it = d_einfo.find( e );
- Assert( it != d_einfo.end() );
- Node gstatus = valuation.getSatValue(it->second.d_active_guard);
- if (!gstatus.isNull() && gstatus.getConst<bool>())
- {
- clist.push_back( e );
- }
- }
- }
- }
-}
-
-bool CegConjecturePbe::constructCandidates( std::vector< Node >& enums, std::vector< Node >& enum_values,
- std::vector< Node >& candidates, std::vector< Node >& candidate_values,
- std::vector< Node >& lems ) {
- Assert( enums.size()==enum_values.size() );
- if( !enums.empty() ){
- unsigned min_term_size = 0;
- std::vector< unsigned > enum_consider;
- Trace("sygus-pbe-enum") << "Register new enumerated values : " << std::endl;
- for( unsigned i=0; i<enums.size(); i++ ){
- Trace("sygus-pbe-enum") << " " << enums[i] << " -> " << enum_values[i] << std::endl;
- unsigned sz = d_tds->getSygusTermSize( enum_values[i] );
- if( i==0 || sz<min_term_size ){
- enum_consider.clear();
- min_term_size = sz;
- enum_consider.push_back( i );
- }else if( sz==min_term_size ){
- enum_consider.push_back( i );
- }
- }
- // only consider the enumerators that are at minimum size (for fairness)
- Trace("sygus-pbe-enum") << "...register " << enum_consider.size() << " / " << enums.size() << std::endl;
- for( unsigned i=0; i<enum_consider.size(); i++ ){
- unsigned j = enum_consider[i];
- addEnumeratedValue( enums[j], enum_values[j], lems );
- }
- }
- for( unsigned i=0; i<candidates.size(); i++ ){
- Node c = candidates[i];
- //build decision tree for candidate
- Node vc = constructSolution( c );
- if( vc.isNull() ){
- return false;
- }else{
- candidate_values.push_back( vc );
- }
- }
- return true;
-}
-
-void CegConjecturePbe::addEnumeratedValue( Node x, Node v, std::vector< Node >& lems ) {
- std::map< Node, EnumInfo >::iterator it = d_einfo.find( x );
- Assert( it != d_einfo.end() );
- Node gstatus = d_qe->getValuation().getSatValue(it->second.d_active_guard);
- if (gstatus.isNull() || !gstatus.getConst<bool>())
- {
- Trace("sygus-pbe-enum-debug") << " ...guard is inactive." << std::endl;
- return;
- }
- Assert(
- std::find(it->second.d_enum_vals.begin(), it->second.d_enum_vals.end(), v)
- == it->second.d_enum_vals.end());
- Node c = it->second.d_parent_candidate;
- // The explanation for why the current value should be excluded in future
- // iterations.
- Node exp_exc;
- if (d_examples_out_invalid.find(c) == d_examples_out_invalid.end())
- {
- std::map<Node, CandidateInfo>::iterator itc = d_cinfo.find(c);
- Assert(itc != d_cinfo.end());
- TypeNode xtn = x.getType();
- Node bv = d_tds->sygusToBuiltin(v, xtn);
- std::map<Node, std::vector<std::vector<Node> > >::iterator itx =
- d_examples.find(c);
- std::map<Node, std::vector<Node> >::iterator itxo = d_examples_out.find(c);
- Assert(itx != d_examples.end());
- Assert(itxo != d_examples_out.end());
- Assert(itx->second.size() == itxo->second.size());
- std::vector<Node> base_results;
- // compte the results
- for (unsigned j = 0, size = itx->second.size(); j < size; j++)
- {
- Node res = d_tds->evaluateBuiltin(xtn, bv, itx->second[j]);
- Trace("sygus-pbe-enum-debug")
- << "...got res = " << res << " from " << bv << std::endl;
- base_results.push_back(res);
- }
- // is it excluded for domain-specific reason?
- std::vector<Node> exp_exc_vec;
- if (getExplanationForEnumeratorExclude(
- c, x, v, base_results, it->second, exp_exc_vec))
- {
- Assert(!exp_exc_vec.empty());
- exp_exc = exp_exc_vec.size() == 1
- ? exp_exc_vec[0]
- : NodeManager::currentNM()->mkNode(AND, exp_exc_vec);
- Trace("sygus-pbe-enum")
- << " ...fail : term is excluded (domain-specific)" << std::endl;
- }
- else
- {
- // notify all slaves
- Assert( !it->second.d_enum_slave.empty() );
- //explanation for why this value should be excluded
- for( unsigned s=0; s<it->second.d_enum_slave.size(); s++ ){
- Node xs = it->second.d_enum_slave[s];
- std::map< Node, EnumInfo >::iterator itv = d_einfo.find( xs );
- Assert( itv!=d_einfo.end() );
- Trace("sygus-pbe-enum") << "Process " << xs << " from " << s << std::endl;
- //bool prevIsCover = false;
- if (itv->second.getRole() == enum_io)
- {
- Trace("sygus-pbe-enum") << " IO-Eval of ";
- //prevIsCover = itv->second.isFeasible();
- }else{
- Trace("sygus-pbe-enum") << "Evaluation of ";
- }
- Trace("sygus-pbe-enum") << xs << " : ";
- //evaluate all input/output examples
- std::vector< Node > results;
- Node templ = itv->second.d_template;
- TNode templ_var = itv->second.d_template_arg;
- std::map< Node, bool > cond_vals;
- for (unsigned j = 0, size = base_results.size(); j < size; j++)
- {
- Node res = base_results[j];
- Assert( res.isConst() );
- if( !templ.isNull() ){
- TNode tres = res;
- res = templ.substitute( templ_var, res );
- res = Rewriter::rewrite( res );
- Assert( res.isConst() );
- }
- Node resb;
- if (itv->second.getRole() == enum_io)
- {
- Node out = itxo->second[j];
- Assert( out.isConst() );
- resb = res==out ? d_true : d_false;
- }else{
- resb = res;
- }
- cond_vals[resb] = true;
- results.push_back( resb );
- if( Trace.isOn("sygus-pbe-enum") ){
- if( resb.getType().isBoolean() ){
- Trace("sygus-pbe-enum") << ( resb==d_true ? "1" : "0" );
- }else{
- Trace("sygus-pbe-enum") << "?";
- }
- }
- }
- bool keep = false;
- if (itv->second.getRole() == enum_io)
- {
- // latter is the degenerate case of no examples
- if (cond_vals.find(d_true) != cond_vals.end() || cond_vals.empty())
- {
- //check subsumbed/subsuming
- std::vector< Node > subsume;
- if( cond_vals.find( d_false )==cond_vals.end() ){
- // it is the entire solution, we are done
- Trace("sygus-pbe-enum") << " ...success, full solution added to PBE pool : " << d_tds->sygusToBuiltin( v ) << std::endl;
- if( !itv->second.isSolved() ){
- itv->second.setSolved( v );
- // it subsumes everything
- itv->second.d_term_trie.clear();
- itv->second.d_term_trie.addTerm( this, v, results, true, subsume );
- }
- keep = true;
- }else{
- Node val = itv->second.d_term_trie.addTerm( this, v, results, true, subsume );
- if( val==v ){
- Trace("sygus-pbe-enum") << " ...success";
- if( !subsume.empty() ){
- itv->second.d_enum_subsume.insert( itv->second.d_enum_subsume.end(), subsume.begin(), subsume.end() );
- Trace("sygus-pbe-enum") << " and subsumed " << subsume.size() << " terms";
- }
- Trace("sygus-pbe-enum") << "! add to PBE pool : " << d_tds->sygusToBuiltin( v ) << std::endl;
- keep = true;
- }else{
- Assert( subsume.empty() );
- Trace("sygus-pbe-enum") << " ...fail : subsumed" << std::endl;
- }
- }
- }else{
- Trace("sygus-pbe-enum") << " ...fail : it does not satisfy examples." << std::endl;
- }
- }else{
- // must be unique up to examples
- Node val = itv->second.d_term_trie.addCond(this, v, results, true);
- if (val == v)
- {
- Trace("sygus-pbe-enum") << " ...success! add to PBE pool : "
- << d_tds->sygusToBuiltin(v) << std::endl;
- keep = true;
- }else{
- Trace("sygus-pbe-enum")
- << " ...fail : term is not unique" << std::endl;
- }
- itc->second.d_cond_count++;
- }
- if( keep ){
- // notify the parent to retry the build of PBE
- itc->second.d_check_sol = true;
- itv->second.addEnumValue( this, v, results );
- }
- }
- }
- }else{
- Trace("sygus-pbe-enum-debug")
- << " ...examples do not have output." << std::endl;
- }
- // exclude this value on subsequent iterations
- Node g = it->second.d_active_guard;
- if (exp_exc.isNull())
- {
- // if we did not already explain why this should be excluded, use default
- exp_exc = d_tds->getExplain()->getExplanationForConstantEquality(x, v);
- }
- Node exlem =
- NodeManager::currentNM()->mkNode(OR, g.negate(), exp_exc.negate());
- Trace("sygus-pbe-enum-lemma")
- << "CegConjecturePbe : enumeration exclude lemma : " << exlem
- << std::endl;
- lems.push_back(exlem);
-}
-
-bool CegConjecturePbe::useStrContainsEnumeratorExclude(Node x, EnumInfo& ei)
-{
- TypeNode xbt = d_tds->sygusToBuiltinType(x.getType());
- if (xbt.isString())
- {
- std::map<Node, bool>::iterator itx = d_use_str_contains_eexc.find(x);
- if (itx != d_use_str_contains_eexc.end())
- {
- return itx->second;
- }
- Trace("sygus-pbe-enum-debug")
- << "Is " << x << " is str.contains exclusion?" << std::endl;
- d_use_str_contains_eexc[x] = true;
- for (const Node& sn : ei.d_enum_slave)
- {
- std::map<Node, EnumInfo>::iterator itv = d_einfo.find(sn);
- EnumRole er = itv->second.getRole();
- if (er != enum_io && er != enum_concat_term)
- {
- Trace("sygus-pbe-enum-debug") << " incompatible slave : " << sn
- << ", role = " << er << std::endl;
- d_use_str_contains_eexc[x] = false;
- return false;
- }
- if (itv->second.isConditional())
- {
- Trace("sygus-pbe-enum-debug")
- << " conditional slave : " << sn << std::endl;
- d_use_str_contains_eexc[x] = false;
- return false;
- }
- }
- Trace("sygus-pbe-enum-debug")
- << "...can use str.contains exclusion." << std::endl;
- return d_use_str_contains_eexc[x];
- }
- return false;
-}
-
-bool CegConjecturePbe::getExplanationForEnumeratorExclude(
- Node c,
- Node x,
- Node v,
- std::vector<Node>& results,
- EnumInfo& ei,
- std::vector<Node>& exp)
-{
- if (useStrContainsEnumeratorExclude(x, ei))
- {
- NodeManager* nm = NodeManager::currentNM();
- // This check whether the example evaluates to something that is larger than
- // the output for some input/output pair. If so, then this term is never
- // useful. We generalize its explanation below.
-
- if (Trace.isOn("sygus-pbe-cterm-debug"))
- {
- Trace("sygus-pbe-enum") << std::endl;
- }
- // check if all examples had longer length that the output
- std::map<Node, std::vector<Node> >::iterator itxo = d_examples_out.find(c);
- Assert(itxo != d_examples_out.end());
- Assert(itxo->second.size() == results.size());
- Trace("sygus-pbe-cterm-debug")
- << "Check enumerator exclusion for " << x << " -> "
- << d_tds->sygusToBuiltin(v) << " based on str.contains." << std::endl;
- std::vector<unsigned> cmp_indices;
- for (unsigned i = 0, size = results.size(); i < size; i++)
- {
- Assert(results[i].isConst());
- Assert(itxo->second[i].isConst());
- Trace("sygus-pbe-cterm-debug")
- << " " << results[i] << " <> " << itxo->second[i];
- Node cont = nm->mkNode(STRING_STRCTN, itxo->second[i], results[i]);
- Node contr = Rewriter::rewrite(cont);
- if (contr == d_false)
- {
- cmp_indices.push_back(i);
- Trace("sygus-pbe-cterm-debug") << "...not contained." << std::endl;
- }
- else
- {
- Trace("sygus-pbe-cterm-debug") << "...contained." << std::endl;
- }
- }
- if (!cmp_indices.empty())
- {
- // we check invariance with respect to a negative contains test
- NegContainsSygusInvarianceTest ncset;
- ncset.init(d_parent, x, itxo->second, cmp_indices);
- // construct the generalized explanation
- d_tds->getExplain()->getExplanationFor(x, v, exp, ncset);
- Trace("sygus-pbe-cterm")
- << "PBE-cterm : enumerator exclude " << d_tds->sygusToBuiltin(v)
- << " due to negative containment." << std::endl;
- return true;
- }
- }
- return false;
-}
-
-
-
-void CegConjecturePbe::EnumInfo::addEnumValue( CegConjecturePbe * pbe, Node v, std::vector< Node >& results ) {
- d_enum_val_to_index[v] = d_enum_vals.size();
- d_enum_vals.push_back( v );
- d_enum_vals_res.push_back( results );
- /*
- if( getRole()==enum_io ){
- // compute
- if( d_enum_total.empty() ){
- d_enum_total = results;
- }else if( !d_enum_total_true ){
- d_enum_total_true = true;
- Assert( d_enum_total.size()==results.size() );
- for( unsigned i=0; i<results.size(); i++ ){
- if( d_enum_total[i]==pbe->d_true || results[i]==pbe->d_true ){
- d_enum_total[i] = pbe->d_true;
- }else{
- d_enum_total[i] = pbe->d_false;
- d_enum_total_true = false;
- }
- }
- }
- }
- */
-}
-
-void CegConjecturePbe::EnumInfo::initialize(Node c, EnumRole role)
-{
- d_parent_candidate = c;
- d_role = role;
-}
-
-void CegConjecturePbe::EnumInfo::setSolved( Node slv ) {
- d_enum_solved = slv;
- //d_enum_total_true = true;
-}
-
-void CegConjecturePbe::CandidateInfo::initialize( Node c ) {
- d_this_candidate = c;
- d_root = c.getType();
-}
-
-void CegConjecturePbe::CandidateInfo::initializeType( TypeNode tn ) {
- d_tinfo[tn].d_this_type = tn;
- d_tinfo[tn].d_parent = this;
-}
-
-Node CegConjecturePbe::CandidateInfo::getRootEnumerator() {
- std::map<EnumRole, Node>::iterator it = d_tinfo[d_root].d_enum.find(enum_io);
- Assert( it!=d_tinfo[d_root].d_enum.end() );
- return it->second;
-}
-
-bool CegConjecturePbe::CandidateInfo::isNonTrivial() {
- //TODO
- return true;
-}
-
-// status : 0 : exact, -1 : vals is subset, 1 : vals is superset
-Node CegConjecturePbe::SubsumeTrie::addTermInternal( CegConjecturePbe * pbe, Node t, std::vector< Node >& vals, bool pol,
- std::vector< Node >& subsumed, bool spol, IndexFilter * f,
- unsigned index, int status, bool checkExistsOnly, bool checkSubsume ) {
- if( index==vals.size() ){
- if( status==0 ){
- // set the term if checkExistsOnly = false
- if( d_term.isNull() && !checkExistsOnly ){
- d_term = t;
- }
- }else if( status==1 ){
- Assert( checkSubsume );
- // found a subsumed term
- if( !d_term.isNull() ){
- subsumed.push_back( d_term );
- if( !checkExistsOnly ){
- // remove it if checkExistsOnly = false
- d_term = Node::null();
- }
- }
- }else{
- Assert( !checkExistsOnly && checkSubsume );
- }
- return d_term;
- }else{
- // the current value
- Assert( pol || ( vals[index].isConst() && vals[index].getType().isBoolean() ) );
- Node cv = pol ? vals[index] : ( vals[index]==pbe->d_true ? pbe->d_false : pbe->d_true );
- // if checkExistsOnly = false, check if the current value is subsumed if checkSubsume = true, if so, don't add
- if( !checkExistsOnly && checkSubsume ){
- std::vector< bool > check_subsumed_by;
- if( status==0 ){
- if( cv==pbe->d_false ){
- check_subsumed_by.push_back( spol );
- }
- }else if( status==-1 ){
- check_subsumed_by.push_back( spol );
- if( cv==pbe->d_false ){
- check_subsumed_by.push_back( !spol );
- }
- }
- // check for subsumed nodes
- for( unsigned i=0; i<check_subsumed_by.size(); i++ ){
- Node csval = check_subsumed_by[i] ? pbe->d_true : pbe->d_false;
- // check if subsumed
- std::map< Node, SubsumeTrie >::iterator itc = d_children.find( csval );
- if( itc!=d_children.end() ){
- unsigned next_index = f ? f->next( index ) : index+1;
- Node ret = itc->second.addTermInternal( pbe, t, vals, pol, subsumed, spol, f, next_index, -1, checkExistsOnly, checkSubsume );
- // ret subsumes t
- if( !ret.isNull() ){
- return ret;
- }
- }
- }
- }
- Node ret;
- std::vector< bool > check_subsume;
- if( status==0 ){
- unsigned next_index = f ? f->next( index ) : index+1;
- if( checkExistsOnly ){
- std::map< Node, SubsumeTrie >::iterator itc = d_children.find( cv );
- if( itc!=d_children.end() ){
- ret = itc->second.addTermInternal( pbe, t, vals, pol, subsumed, spol, f, next_index, 0, checkExistsOnly, checkSubsume );
- }
- }else{
- Assert( spol );
- ret = d_children[cv].addTermInternal( pbe, t, vals, pol, subsumed, spol, f, next_index, 0, checkExistsOnly, checkSubsume );
- if( ret!=t ){
- // we were subsumed by ret, return
- return ret;
- }
- }
- if( checkSubsume ){
- // check for subsuming
- if( cv==pbe->d_true ){
- check_subsume.push_back( !spol );
- }
- }
- }else if( status==1 ){
- Assert( checkSubsume );
- check_subsume.push_back( !spol );
- if( cv==pbe->d_true ){
- check_subsume.push_back( spol );
- }
- }
- if( checkSubsume ){
- // check for subsumed terms
- for( unsigned i=0; i<check_subsume.size(); i++ ){
- Node csval = check_subsume[i] ? pbe->d_true : pbe->d_false;
- std::map< Node, SubsumeTrie >::iterator itc = d_children.find( csval );
- if( itc!=d_children.end() ){
- unsigned next_index = f ? f->next( index ) : index+1;
- itc->second.addTermInternal( pbe, t, vals, pol, subsumed, spol, f, next_index, 1, checkExistsOnly, checkSubsume );
- // clean up
- if( itc->second.isEmpty() ){
- Assert( !checkExistsOnly );
- d_children.erase( csval );
- }
- }
- }
- }
- return ret;
- }
-}
-
-Node CegConjecturePbe::SubsumeTrie::addTerm( CegConjecturePbe * pbe, Node t, std::vector< Node >& vals, bool pol, std::vector< Node >& subsumed, IndexFilter * f ) {
- unsigned start_index = f ? f->start() : 0;
- return addTermInternal( pbe, t, vals, pol, subsumed, true, f, start_index, 0, false, true );
-}
-
-Node CegConjecturePbe::SubsumeTrie::addCond( CegConjecturePbe * pbe, Node c, std::vector< Node >& vals, bool pol, IndexFilter * f ) {
- unsigned start_index = f ? f->start() : 0;
- std::vector< Node > subsumed;
- return addTermInternal( pbe, c, vals, pol, subsumed, true, f, start_index, 0, false, false );
-}
-
-void CegConjecturePbe::SubsumeTrie::getSubsumed( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::vector< Node >& subsumed, IndexFilter * f ){
- unsigned start_index = f ? f->start() : 0;
- addTermInternal( pbe, Node::null(), vals, pol, subsumed, true, f, start_index, 1, true, true );
-}
-
-void CegConjecturePbe::SubsumeTrie::getSubsumedBy( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::vector< Node >& subsumed_by, IndexFilter * f ){
- // flip polarities
- unsigned start_index = f ? f->start() : 0;
- addTermInternal( pbe, Node::null(), vals, !pol, subsumed_by, false, f, start_index, 1, true, true );
-}
-
-void CegConjecturePbe::SubsumeTrie::getLeavesInternal( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::map< int, std::vector< Node > >& v,
- IndexFilter * f, unsigned index, int status ) {
- if( index==vals.size() ){
- Assert( !d_term.isNull() );
- Assert( std::find( v[status].begin(), v[status].end(), d_term )==v[status].end() );
- v[status].push_back( d_term );
- }else{
- Assert( vals[index].isConst() && vals[index].getType().isBoolean() );
- // filter should be for cv
- Assert( f==NULL || vals[index]==( pol ? pbe->d_true : pbe->d_false ) );
- for( std::map< Node, SubsumeTrie >::iterator it = d_children.begin(); it != d_children.end(); ++it ){
- int new_status = status;
- // if the current value is true
- if( vals[index]==( pol ? pbe->d_true : pbe->d_false ) ){
- if( status!=0 ){
- new_status = ( it->first == pbe->d_true ? 1 : -1 );
- if( status!=-2 && new_status!=status ){
- new_status = 0;
- }
- }
- }
- unsigned next_index = f ? f->next( index ) : index+1;
- it->second.getLeavesInternal( pbe, vals, pol, v, f, next_index, new_status );
- }
- }
-}
-
-void CegConjecturePbe::SubsumeTrie::getLeaves( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::map< int, std::vector< Node > >& v, IndexFilter * f ) {
- unsigned start_index = f ? f->start() : 0;
- getLeavesInternal( pbe, vals, pol, v, f, start_index, -2 );
-}
-
-void CegConjecturePbe::IndexFilter::mk( std::vector< Node >& vals, bool pol ) {
- Trace("sygus-pbe-debug") << "Make for : ";
- print_val( "sygus-pbe-debug", vals, pol );
- Trace("sygus-pbe-debug") << std::endl;
- Node poln = NodeManager::currentNM()->mkConst( pol );
-
- unsigned curr_index = 0;
- while( curr_index<vals.size() && vals[curr_index]!=poln ){
- curr_index++;
- }
- d_next[0] = curr_index;
- Trace("sygus-pbe-debug") << "0 -> " << curr_index << std::endl;
- unsigned i = curr_index;
- while( i<vals.size() ){
- while( i<vals.size() && vals[i]!=poln ){
- i++;
- }
- i++;
- d_next[curr_index+1] = i;
- Trace("sygus-pbe-debug") << curr_index+1 << " -> " << i << std::endl;
- curr_index = i;
- }
-
- // verify it is correct
- unsigned j = start();
- for( unsigned k=0; k<j; k++ ){
- AlwaysAssert( vals[k]!=poln );
- }
- Trace("sygus-pbe-debug") << "...start : " << j << std::endl;
- unsigned counter = 0;
- while( j<vals.size() ){
- Trace("sygus-pbe-debug") << "...at : " << j << std::endl;
- AlwaysAssert( vals[j]==poln );
- unsigned jj = next( j );
- AlwaysAssert( jj>j );
- for( unsigned k=(j+1); k<jj; k++ ){
- AlwaysAssert( vals[k]!=poln );
- }
- AlwaysAssert( counter<=vals.size() );
- counter++;
- j = jj;
- }
-
-
-}
-
-unsigned CegConjecturePbe::IndexFilter::start() {
- std::map< unsigned, unsigned >::iterator it = d_next.find( 0 );
- if( it==d_next.end() ){
- return 0;
- }else{
- return it->second;
- }
-}
-
-unsigned CegConjecturePbe::IndexFilter::next( unsigned i ) {
- std::map< unsigned, unsigned >::iterator it = d_next.find( i+1 );
- if( it==d_next.end() ){
- return i+1;
- }else{
- return it->second;
- }
-}
-
-bool CegConjecturePbe::IndexFilter::isEq( std::vector< Node >& vals, Node v ) {
- unsigned index = start();
- while( index<vals.size() ){
- if( vals[index]!=v ){
- return false;
- }
- index = next( index );
- }
- return true;
-}
-
-Node CegConjecturePbe::constructSolution( Node c ){
- std::map< Node, CandidateInfo >::iterator itc = d_cinfo.find( c );
- Assert( itc!=d_cinfo.end() );
- if( !itc->second.d_solution.isNull() ){
- // already has a solution
- return itc->second.d_solution;
- }else{
- // only check if an enumerator updated
- if( itc->second.d_check_sol ){
- Trace("sygus-pbe") << "Construct solution, #iterations = " << itc->second.d_cond_count << std::endl;
- itc->second.d_check_sol = false;
- // try multiple times if we have done multiple conditions, due to non-determinism
- Node vc;
- for( unsigned i=0; i<=itc->second.d_cond_count; i++ ){
- Trace("sygus-pbe-dt") << "ConstructPBE for candidate: " << c << std::endl;
- Node e = itc->second.getRootEnumerator();
- UnifContext x;
- x.initialize( this, c );
- Node vcc = constructSolution(c, e, role_equal, x, 1);
- if( !vcc.isNull() ){
- if( vc.isNull() || ( !vc.isNull() && d_tds->getSygusTermSize( vcc )<d_tds->getSygusTermSize( vc ) ) ){
- Trace("sygus-pbe") << "**** PBE SOLVED : " << c << " = " << vcc << std::endl;
- Trace("sygus-pbe") << "...solved at iteration " << i << std::endl;
- vc = vcc;
- }
- }
- }
- if( !vc.isNull() ){
- itc->second.d_solution = vc;
- return vc;
- }
- Trace("sygus-pbe") << "...failed to solve." << std::endl;
- }
- return Node::null();
- }
-}
-
-Node CegConjecturePbe::constructBestSolvedTerm( std::vector< Node >& solved, UnifContext& x ){
- Assert( !solved.empty() );
- // TODO
- return solved[0];
-}
-
-Node CegConjecturePbe::constructBestStringSolvedTerm( std::vector< Node >& solved, UnifContext& x ) {
- Assert( !solved.empty() );
- // TODO
- return solved[0];
-}
-
-Node CegConjecturePbe::constructBestSolvedConditional( std::vector< Node >& solved, UnifContext& x ){
- Assert( !solved.empty() );
- // TODO
- return solved[0];
-}
-
-Node CegConjecturePbe::constructBestConditional( std::vector< Node >& conds, UnifContext& x ) {
- Assert( !conds.empty() );
- // TODO
- double r = Random::getRandom().pickDouble(0.0, 1.0);
- unsigned cindex = r*conds.size();
- if( cindex>conds.size() ){
- cindex = conds.size() - 1;
- }
- return conds[cindex];
-}
-
-Node CegConjecturePbe::constructBestStringToConcat( std::vector< Node > strs,
- std::map< Node, unsigned > total_inc,
- std::map< Node, std::vector< unsigned > > incr,
- UnifContext& x ) {
- Assert( !strs.empty() );
- std::random_shuffle(strs.begin(), strs.end());
- // prefer one that has incremented by more than 0
- for (const Node& ns : strs)
- {
- if (total_inc[ns] > 0)
- {
- return ns;
- }
- }
- return strs[0];
-}
-
-Node CegConjecturePbe::constructSolution(
- Node c, Node e, NodeRole nrole, UnifContext& x, int ind)
-{
- TypeNode etn = e.getType();
- if (Trace.isOn("sygus-pbe-dt-debug"))
- {
- indent("sygus-pbe-dt-debug", ind);
- Trace("sygus-pbe-dt-debug") << "ConstructPBE: (" << e << ", " << nrole
- << ") for type " << etn << " in context ";
- print_val("sygus-pbe-dt-debug", x.d_vals);
- if (x.d_has_string_pos != role_invalid)
- {
- Trace("sygus-pbe-dt-debug") << ", string context [" << x.d_has_string_pos;
- for (unsigned i = 0, size = x.d_str_pos.size(); i < size; i++)
- {
- Trace("sygus-pbe-dt-debug") << " " << x.d_str_pos[i];
- }
- Trace("sygus-pbe-dt-debug") << "]";
- }
- Trace("sygus-pbe-dt-debug") << std::endl;
- }
- // enumerator type info
- std::map<TypeNode, EnumTypeInfo>::iterator itt = d_cinfo[c].d_tinfo.find(etn);
- Assert(itt != d_cinfo[c].d_tinfo.end());
- EnumTypeInfo& tinfo = itt->second;
-
- // get the enumerator information
- std::map< Node, EnumInfo >::iterator itn = d_einfo.find( e );
- Assert( itn!=d_einfo.end() );
- EnumInfo& einfo = itn->second;
-
- Node ret_dt;
- if (nrole == role_equal)
- {
- if (!x.isReturnValueModified())
- {
- if (einfo.isSolved())
- {
- // this type has a complete solution
- ret_dt = einfo.getSolved();
- indent("sygus-pbe-dt", ind);
- Trace("sygus-pbe-dt") << "return PBE: success : solved "
- << d_tds->sygusToBuiltin(ret_dt) << std::endl;
- Assert(!ret_dt.isNull());
- }
- else
- {
- // could be conditionally solved
- std::vector<Node> subsumed_by;
- einfo.d_term_trie.getSubsumedBy(this, x.d_vals, true, subsumed_by);
- if (!subsumed_by.empty())
- {
- ret_dt = constructBestSolvedTerm(subsumed_by, x);
- indent("sygus-pbe-dt", ind);
- Trace("sygus-pbe-dt") << "return PBE: success : conditionally solved"
- << d_tds->sygusToBuiltin(ret_dt) << std::endl;
- }
- else
- {
- indent("sygus-pbe-dt-debug", ind);
- Trace("sygus-pbe-dt-debug")
- << " ...not currently conditionally solved." << std::endl;
- }
- }
- }
- if (ret_dt.isNull())
- {
- if (d_tds->sygusToBuiltinType(e.getType()).isString())
- {
- // check if a current value that closes all examples
- // get the term enumerator for this type
- bool success = true;
- std::map<Node, EnumInfo>::iterator itet;
- std::map<EnumRole, Node>::iterator itnt =
- tinfo.d_enum.find(enum_concat_term);
- if( itnt != itt->second.d_enum.end() ){
- Node et = itnt->second;
- itet = d_einfo.find( et );
- Assert(itet != d_einfo.end());
- }else{
- success = false;
- }
- if (success)
- {
- // get the current examples
- std::map<Node, std::vector<Node> >::iterator itx =
- d_examples_out.find(c);
- Assert(itx != d_examples_out.end());
- std::vector<String> ex_vals;
- x.getCurrentStrings(this, itx->second, ex_vals);
- Assert(itn->second.d_enum_vals.size()
- == itn->second.d_enum_vals_res.size());
-
- // test each example in the term enumerator for the type
- std::vector<Node> str_solved;
- for (unsigned i = 0, size = itet->second.d_enum_vals.size(); i < size;
- i++)
- {
- if (x.isStringSolved(
- this, ex_vals, itet->second.d_enum_vals_res[i]))
- {
- str_solved.push_back(itet->second.d_enum_vals[i]);
- }
- }
- if (!str_solved.empty())
- {
- ret_dt = constructBestStringSolvedTerm(str_solved, x);
- indent("sygus-pbe-dt", ind);
- Trace("sygus-pbe-dt") << "return PBE: success : string solved "
- << d_tds->sygusToBuiltin(ret_dt) << std::endl;
- }
- else
- {
- indent("sygus-pbe-dt-debug", ind);
- Trace("sygus-pbe-dt-debug") << " ...not currently string solved."
- << std::endl;
- }
- }
- }
- }
- }
- else if (nrole == role_string_prefix || nrole == role_string_suffix)
- {
- // check if each return value is a prefix/suffix of all open examples
- if (!x.isReturnValueModified() || x.d_has_string_pos == nrole)
- {
- std::map<Node, std::vector<unsigned> > incr;
- bool isPrefix = nrole == role_string_prefix;
- std::map<Node, unsigned> total_inc;
- std::vector<Node> inc_strs;
- std::map<Node, std::vector<Node> >::iterator itx = d_examples_out.find(c);
- Assert(itx != d_examples_out.end());
- // make the value of the examples
- std::vector<String> ex_vals;
- x.getCurrentStrings(this, itx->second, ex_vals);
- if (Trace.isOn("sygus-pbe-dt-debug"))
- {
- indent("sygus-pbe-dt-debug", ind);
- Trace("sygus-pbe-dt-debug") << "current strings : " << std::endl;
- for (unsigned i = 0, size = ex_vals.size(); i < size; i++)
- {
- indent("sygus-pbe-dt-debug", ind + 1);
- Trace("sygus-pbe-dt-debug") << ex_vals[i] << std::endl;
- }
- }
-
- // check if there is a value for which is a prefix/suffix of all active
- // examples
- Assert(einfo.d_enum_vals.size() == einfo.d_enum_vals_res.size());
-
- for (unsigned i = 0, size = einfo.d_enum_vals.size(); i < size; i++)
- {
- Node val_t = einfo.d_enum_vals[i];
- indent("sygus-pbe-dt-debug", ind);
- Trace("sygus-pbe-dt-debug") << "increment string values : " << val_t
- << " : ";
- Assert(einfo.d_enum_vals_res[i].size() == itx->second.size());
- unsigned tot = 0;
- bool exsuccess = x.getStringIncrement(this,
- isPrefix,
- ex_vals,
- einfo.d_enum_vals_res[i],
- incr[val_t],
- tot);
- if (!exsuccess)
- {
- incr.erase(val_t);
- Trace("sygus-pbe-dt-debug") << "...fail" << std::endl;
- }
- else
- {
- total_inc[val_t] = tot;
- inc_strs.push_back(val_t);
- Trace("sygus-pbe-dt-debug") << "...success, total increment = " << tot
- << std::endl;
- }
- }
-
- if (!incr.empty())
- {
- ret_dt = constructBestStringToConcat(inc_strs, total_inc, incr, x);
- Assert(!ret_dt.isNull());
- indent("sygus-pbe-dt", ind);
- Trace("sygus-pbe-dt") << "PBE: CONCAT strategy : choose "
- << (isPrefix ? "pre" : "suf") << "fix value "
- << d_tds->sygusToBuiltin(ret_dt) << std::endl;
- // update the context
- bool ret = x.updateStringPosition(this, incr[ret_dt]);
- AlwaysAssert(ret == (total_inc[ret_dt] > 0));
- x.d_has_string_pos = nrole;
- }else{
- indent("sygus-pbe-dt", ind);
- Trace("sygus-pbe-dt") << "PBE: failed CONCAT strategy, no values are "
- << (isPrefix ? "pre" : "suf")
- << "fix of all examples." << std::endl;
- }
- }
- else
- {
- indent("sygus-pbe-dt", ind);
- Trace("sygus-pbe-dt")
- << "PBE: failed CONCAT strategy, prefix/suffix mismatch."
- << std::endl;
- }
- }
- if (ret_dt.isNull() && !einfo.isTemplated())
- {
- // we will try a single strategy
- EnumTypeInfoStrat* etis = nullptr;
- std::map<NodeRole, StrategyNode>::iterator itsn =
- tinfo.d_snodes.find(nrole);
- if (itsn != tinfo.d_snodes.end())
- {
- // strategy info
- StrategyNode& snode = itsn->second;
- if (x.d_visit_role[e].find(nrole) == x.d_visit_role[e].end())
- {
- x.d_visit_role[e][nrole] = true;
- // try a random strategy
- if (snode.d_strats.size() > 1)
- {
- std::random_shuffle(snode.d_strats.begin(), snode.d_strats.end());
- }
- // get an eligible strategy index
- unsigned sindex = 0;
- while (sindex < snode.d_strats.size()
- && !x.isValidStrategy(snode.d_strats[sindex]))
- {
- sindex++;
- }
- // if we found a eligible strategy
- if (sindex < snode.d_strats.size())
- {
- etis = snode.d_strats[sindex];
- }
- }
- }
- if (etis != nullptr)
- {
- StrategyType strat = etis->d_this;
- indent("sygus-pbe-dt", ind + 1);
- Trace("sygus-pbe-dt") << "...try STRATEGY " << strat << "..."
- << std::endl;
-
- std::map<unsigned, Node> look_ahead_solved_children;
- std::vector<Node> dt_children_cons;
- bool success = true;
-
- // for ITE
- Node split_cond_enum;
- int split_cond_res_index = -1;
-
- for (unsigned sc = 0, size = etis->d_cenum.size(); sc < size; sc++)
- {
- indent("sygus-pbe-dt", ind + 1);
- Trace("sygus-pbe-dt") << "construct PBE child #" << sc << "..."
- << std::endl;
- Node rec_c;
- std::map<unsigned, Node>::iterator itla =
- look_ahead_solved_children.find(sc);
- if (itla != look_ahead_solved_children.end())
- {
- rec_c = itla->second;
- indent("sygus-pbe-dt-debug", ind + 1);
- Trace("sygus-pbe-dt-debug") << "ConstructPBE: look ahead solved : "
- << d_tds->sygusToBuiltin(rec_c)
- << std::endl;
- }
- else
- {
- std::pair<Node, NodeRole>& cenum = etis->d_cenum[sc];
-
- // update the context
- std::vector<Node> prev;
- if (strat == strat_ITE && sc > 0)
- {
- std::map<Node, EnumInfo>::iterator itnc =
- d_einfo.find(split_cond_enum);
- Assert(itnc != d_einfo.end());
- Assert(split_cond_res_index >= 0);
- Assert(split_cond_res_index
- < (int)itnc->second.d_enum_vals_res.size());
- prev = x.d_vals;
- bool ret = x.updateContext(
- this,
- itnc->second.d_enum_vals_res[split_cond_res_index],
- sc == 1);
- AlwaysAssert(ret);
- }
-
- // recurse
- if (strat == strat_ITE && sc == 0)
- {
- Node ce = cenum.first;
-
- // register the condition enumerator
- std::map<Node, EnumInfo>::iterator itnc = d_einfo.find(ce);
- Assert(itnc != d_einfo.end());
- EnumInfo& einfo_child = itnc->second;
-
- // only used if the return value is not modified
- if (!x.isReturnValueModified())
- {
- if (x.d_uinfo.find(ce) == x.d_uinfo.end())
- {
- Trace("sygus-pbe-dt-debug2")
- << " reg : PBE: Look for direct solutions for conditional "
- "enumerator "
- << ce << " ... " << std::endl;
- Assert(einfo_child.d_enum_vals.size()
- == einfo_child.d_enum_vals_res.size());
- for (unsigned i = 1; i <= 2; i++)
- {
- std::pair<Node, NodeRole>& te_pair = etis->d_cenum[i];
- Node te = te_pair.first;
- std::map<Node, EnumInfo>::iterator itnt = d_einfo.find(te);
- Assert(itnt != d_einfo.end());
- bool branch_pol = (i == 1);
- // for each condition, get terms that satisfy it in this
- // branch
- for (unsigned k = 0, size = einfo_child.d_enum_vals.size();
- k < size;
- k++)
- {
- Node cond = einfo_child.d_enum_vals[k];
- std::vector<Node> solved;
- itnt->second.d_term_trie.getSubsumedBy(
- this,
- einfo_child.d_enum_vals_res[k],
- branch_pol,
- solved);
- Trace("sygus-pbe-dt-debug2")
- << " reg : PBE: " << d_tds->sygusToBuiltin(cond)
- << " has " << solved.size() << " solutions in branch "
- << i << std::endl;
- if (!solved.empty())
- {
- Node slv = constructBestSolvedTerm(solved, x);
- Trace("sygus-pbe-dt-debug2")
- << " reg : PBE: ..." << d_tds->sygusToBuiltin(slv)
- << " is a solution under branch " << i;
- Trace("sygus-pbe-dt-debug2")
- << " of condition " << d_tds->sygusToBuiltin(cond)
- << std::endl;
- x.d_uinfo[ce].d_look_ahead_sols[cond][i] = slv;
- }
- }
- }
- }
- }
-
- // get the conditionals in the current context : they must be
- // distinguishable
- std::map<int, std::vector<Node> > possible_cond;
- std::map<Node, int> solved_cond; // stores branch
- einfo_child.d_term_trie.getLeaves(
- this, x.d_vals, true, possible_cond);
-
- std::map<int, std::vector<Node> >::iterator itpc =
- possible_cond.find(0);
- if (itpc != possible_cond.end())
- {
- if (Trace.isOn("sygus-pbe-dt-debug"))
- {
- indent("sygus-pbe-dt-debug", ind + 1);
- Trace("sygus-pbe-dt-debug")
- << "PBE : We have " << itpc->second.size()
- << " distinguishable conditionals:" << std::endl;
- for (Node& cond : itpc->second)
- {
- indent("sygus-pbe-dt-debug", ind + 2);
- Trace("sygus-pbe-dt-debug") << d_tds->sygusToBuiltin(cond)
- << std::endl;
- }
- }
-
- // static look ahead conditional : choose conditionals that have
- // solved terms in at least one branch
- // only applicable if we have not modified the return value
- std::map<int, std::vector<Node> > solved_cond;
- if (!x.isReturnValueModified())
- {
- Assert(x.d_uinfo.find(ce) != x.d_uinfo.end());
- int solve_max = 0;
- for (Node& cond : itpc->second)
- {
- std::map<Node, std::map<unsigned, Node> >::iterator itla =
- x.d_uinfo[ce].d_look_ahead_sols.find(cond);
- if (itla != x.d_uinfo[ce].d_look_ahead_sols.end())
- {
- int nsolved = itla->second.size();
- solve_max = nsolved > solve_max ? nsolved : solve_max;
- solved_cond[nsolved].push_back(cond);
- }
- }
- int n = solve_max;
- while (n > 0)
- {
- if (!solved_cond[n].empty())
- {
- rec_c = constructBestSolvedConditional(solved_cond[n], x);
- indent("sygus-pbe-dt", ind + 1);
- Trace("sygus-pbe-dt")
- << "PBE: ITE strategy : choose solved conditional "
- << d_tds->sygusToBuiltin(rec_c) << " with " << n
- << " solved children..." << std::endl;
- std::map<Node, std::map<unsigned, Node> >::iterator itla =
- x.d_uinfo[ce].d_look_ahead_sols.find(rec_c);
- Assert(itla != x.d_uinfo[ce].d_look_ahead_sols.end());
- for (std::pair<const unsigned, Node>& las : itla->second)
- {
- look_ahead_solved_children[las.first] = las.second;
- }
- break;
- }
- n--;
- }
- }
-
- // otherwise, guess a conditional
- if (rec_c.isNull())
- {
- rec_c = constructBestConditional(itpc->second, x);
- Assert(!rec_c.isNull());
- indent("sygus-pbe-dt", ind);
- Trace("sygus-pbe-dt")
- << "PBE: ITE strategy : choose random conditional "
- << d_tds->sygusToBuiltin(rec_c) << std::endl;
- }
- }
- else
- {
- // TODO (#1250) : degenerate case where children have different
- // types?
- indent("sygus-pbe-dt", ind);
- Trace("sygus-pbe-dt") << "return PBE: failed ITE strategy, "
- "cannot find a distinguishable condition"
- << std::endl;
- }
- if( !rec_c.isNull() ){
- Assert(einfo_child.d_enum_val_to_index.find(rec_c)
- != einfo_child.d_enum_val_to_index.end());
- split_cond_res_index = einfo_child.d_enum_val_to_index[rec_c];
- split_cond_enum = ce;
- Assert(split_cond_res_index >= 0);
- Assert(split_cond_res_index
- < (int)einfo_child.d_enum_vals_res.size());
- }
- }
- else
- {
- rec_c = constructSolution(c, cenum.first, cenum.second, x, ind + 2);
- }
-
- // undo update the context
- if (strat == strat_ITE && sc > 0)
- {
- x.d_vals = prev;
- }
- }
- if (!rec_c.isNull())
- {
- dt_children_cons.push_back(rec_c);
- }
- else
- {
- success = false;
- break;
- }
- }
- if (success)
- {
- Assert(dt_children_cons.size() == etis->d_sol_templ_args.size());
- // ret_dt = NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR,
- // dt_children );
- ret_dt = etis->d_sol_templ;
- ret_dt = ret_dt.substitute(etis->d_sol_templ_args.begin(),
- etis->d_sol_templ_args.end(),
- dt_children_cons.begin(),
- dt_children_cons.end());
- indent("sygus-pbe-dt-debug", ind);
- Trace("sygus-pbe-dt-debug")
- << "PBE: success : constructed for strategy " << strat << std::endl;
- }else{
- indent("sygus-pbe-dt-debug", ind);
- Trace("sygus-pbe-dt-debug") << "PBE: failed for strategy " << strat
- << std::endl;
- }
- }
- }
-
- if( !ret_dt.isNull() ){
- Assert( ret_dt.getType()==e.getType() );
- }
- indent("sygus-pbe-dt", ind);
- Trace("sygus-pbe-dt") << "ConstructPBE: returned " << ret_dt << std::endl;
- return ret_dt;
-}
-
-bool CegConjecturePbe::UnifContext::updateContext( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol ) {
- Assert( d_vals.size()==vals.size() );
- bool changed = false;
- Node poln = pol ? pbe->d_true : pbe->d_false;
- for( unsigned i=0; i<vals.size(); i++ ){
- if( vals[i]!=poln ){
- if( d_vals[i]==pbe->d_true ){
- d_vals[i] = pbe->d_false;
- changed = true;
- }
- }
- }
- if (changed)
- {
- d_visit_role.clear();
- }
- return changed;
-}
-
-bool CegConjecturePbe::UnifContext::updateStringPosition( CegConjecturePbe * pbe, std::vector< unsigned >& pos ) {
- Assert( pos.size()==d_str_pos.size() );
- bool changed = false;
- for( unsigned i=0; i<pos.size(); i++ ){
- if( pos[i]>0 ){
- d_str_pos[i] += pos[i];
- changed = true;
- }
- }
- if (changed)
- {
- d_visit_role.clear();
- }
- return changed;
-}
-
-bool CegConjecturePbe::UnifContext::isReturnValueModified() {
- if (d_has_string_pos != role_invalid)
- {
- return true;
- }
- return false;
-}
-
-bool CegConjecturePbe::UnifContext::isValidStrategy(EnumTypeInfoStrat* etis)
-{
- StrategyType st = etis->d_this;
- if (d_has_string_pos == role_string_prefix && st == strat_CONCAT_SUFFIX)
- {
- return false;
- }
- if (d_has_string_pos == role_string_suffix && st == strat_CONCAT_PREFIX)
- {
- return false;
- }
- return true;
-}
-
-void CegConjecturePbe::UnifContext::initialize( CegConjecturePbe * pbe, Node c ) {
- Assert( d_vals.empty() );
- Assert( d_str_pos.empty() );
-
- // initialize with #examples
- Assert( pbe->d_examples.find( c )!=pbe->d_examples.end() );
- unsigned sz = pbe->d_examples[c].size();
- for( unsigned i=0; i<sz; i++ ){
- d_vals.push_back( pbe->d_true );
- }
-
- if( !pbe->d_examples_out[c].empty() ){
- // output type of the examples
- TypeNode exotn = pbe->d_examples_out[c][0].getType();
-
- if( exotn.isString() ){
- for( unsigned i=0; i<sz; i++ ){
- d_str_pos.push_back( 0 );
- }
- }
- }
- d_visit_role.clear();
-}
-
-void CegConjecturePbe::UnifContext::getCurrentStrings(
- CegConjecturePbe* pbe,
- const std::vector<Node>& vals,
- std::vector<String>& ex_vals)
-{
- bool isPrefix = d_has_string_pos == role_string_prefix;
- String dummy;
- for( unsigned i=0; i<vals.size(); i++ ){
- if( d_vals[i]==pbe->d_true ){
- Assert( vals[i].isConst() );
- unsigned pos_value = d_str_pos[i];
- if( pos_value>0 ){
- Assert(d_has_string_pos != role_invalid);
- String s = vals[i].getConst<String>();
- Assert( pos_value<=s.size() );
- ex_vals.push_back( isPrefix ? s.suffix( s.size()-pos_value ) :
- s.prefix( s.size()-pos_value ) );
- }else{
- ex_vals.push_back( vals[i].getConst<String>() );
- }
- }else{
- // irrelevant, add dummy
- ex_vals.push_back( dummy );
- }
- }
-}
-
-bool CegConjecturePbe::UnifContext::getStringIncrement(
- CegConjecturePbe* pbe,
- bool isPrefix,
- const std::vector<String>& ex_vals,
- const std::vector<Node>& vals,
- std::vector<unsigned>& inc,
- unsigned& tot)
-{
- for( unsigned j=0; j<vals.size(); j++ ){
- unsigned ival = 0;
- if( d_vals[j]==pbe->d_true ){
- // example is active in this context
- Assert( vals[j].isConst() );
- String mystr = vals[j].getConst<String>();
- ival = mystr.size();
- if( mystr.size()<=ex_vals[j].size() ){
- if( !( isPrefix ? ex_vals[j].strncmp(mystr, ival) : ex_vals[j].rstrncmp(mystr, ival) ) ){
- Trace("sygus-pbe-dt-debug") << "X";
- return false;
- }
- }else{
- Trace("sygus-pbe-dt-debug") << "X";
- return false;
- }
- }
- Trace("sygus-pbe-dt-debug") << ival;
- tot += ival;
- inc.push_back( ival );
- }
- return true;
-}
-bool CegConjecturePbe::UnifContext::isStringSolved(
- CegConjecturePbe* pbe,
- const std::vector<String>& ex_vals,
- const std::vector<Node>& vals)
-{
- for( unsigned j=0; j<vals.size(); j++ ){
- if( d_vals[j]==pbe->d_true ){
- // example is active in this context
- Assert( vals[j].isConst() );
- String mystr = vals[j].getConst<String>();
- if( ex_vals[j]!=mystr ){
- return false;
- }
- }
- }
- return true;
-}
-
-CegConjecturePbe::StrategyNode::~StrategyNode()
-{
- for (unsigned j = 0, size = d_strats.size(); j < size; j++)
- {
- delete d_strats[j];
- }
- d_strats.clear();
-}
-}
-}
-}
+++ /dev/null
-/********************* */
-/*! \file ce_guided_pbe.h
- ** \verbatim
- ** Top contributors (to current version):
- ** Andrew Reynolds
- ** This file is part of the CVC4 project.
- ** Copyright (c) 2009-2016 by the authors listed in the file AUTHORS
- ** in the top-level source directory) and their institutional affiliations.
- ** All rights reserved. See the file COPYING in the top-level source
- ** directory for licensing information.\endverbatim
- **
- ** \brief utility for processing programming by examples synthesis conjectures
- **/
-
-#include "cvc4_private.h"
-
-#ifndef __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_PBE_H
-#define __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_PBE_H
-
-#include "context/cdhashmap.h"
-#include "theory/quantifiers_engine.h"
-
-namespace CVC4 {
-namespace theory {
-namespace quantifiers {
-
-/** roles for enumerators
- *
- * This indicates the role of an enumerator that is allocated by approaches
- * for synthesis-by-unification (see details below).
- * io : the enumerator should enumerate values that are overall solutions
- * for the function-to-synthesize,
- * ite_condition : the enumerator should enumerate values that are useful
- * in ite conditions in the ITE strategy,
- * concat_term : the enumerator should enumerate values that are used as
- * components of string concatenation solutions.
- */
-enum EnumRole
-{
- enum_invalid,
- enum_io,
- enum_ite_condition,
- enum_concat_term,
-};
-std::ostream& operator<<(std::ostream& os, EnumRole r);
-
-/** roles for strategy nodes
- *
- * This indicates the role of a strategy node, which is a subprocedure of
- * CegConjecturePbe::constructSolution (see details below).
- * equal : the node constructed must be equal to the overall solution for
- * the function-to-synthesize,
- * string_prefix/suffix : the node constructed must be a prefix/suffix
- * of the function-to-synthesize,
- * ite_condition : the node constructed must be a condition that makes some
- * active input examples true and some input examples false.
- */
-enum NodeRole
-{
- role_invalid,
- role_equal,
- role_string_prefix,
- role_string_suffix,
- role_ite_condition,
-};
-std::ostream& operator<<(std::ostream& os, NodeRole r);
-
-/** enumerator role for node role */
-EnumRole getEnumeratorRoleForNodeRole(NodeRole r);
-
-/** strategy types
- *
- * This indicates a strategy for synthesis-by-unification (see details below).
- * ITE : strategy for constructing if-then-else solutions via decision
- * tree learning techniques,
- * CONCAT_PREFIX/SUFFIX : strategy for constructing string concatenation
- * solutions via a divide and conquer approach,
- * ID : identity strategy used for calling strategies on child type through
- * an identity function.
- */
-enum StrategyType
-{
- strat_INVALID,
- strat_ITE,
- strat_CONCAT_PREFIX,
- strat_CONCAT_SUFFIX,
- strat_ID,
-};
-std::ostream& operator<<(std::ostream& os, StrategyType st);
-
-class CegConjecture;
-
-/** CegConjecturePbe
-*
-* This class implements optimizations that target synthesis conjectures
-* that are in Programming-By-Examples (PBE) form.
-*
-* [EX#1] An example of a synthesis conjecture in PBE form is :
-* exists f. forall x.
-* ( x = 0 => f( x ) = 2 ) ^ ( x = 5 => f( x ) = 7 ) ^ ( x = 6 => f( x ) = 8 )
-*
-* We say that the above conjecture has I/O examples (0)->2, (5)->7, (6)->8.
-*
-* Internally, this class does the following for SyGuS inputs:
-*
-* (1) Infers whether the input conjecture is in PBE form or not.
-* (2) Based on this information and on the syntactic restrictions, it
-* devises a strategy for enumerating terms and construction solutions,
-* which is inspired by Alur et al. "Scaling Enumerative Program Synthesis
-* via Divide and Conquer" TACAS 2017. In particular, it may consider
-* strategies for constructing decision trees when the grammar permits ITEs
-* and a strategy for divide-and-conquer string synthesis when the grammar
-* permits string concatenation. This is stored in a set of data structures
-* within d_cinfo.
-* (3) It makes (possibly multiple) calls to
-* TermDatabaseSygus::registerMeasuredTerm(...) based
-* on the strategy, which inform the rest of the system to enumerate values
-* of particular types in the grammar through use of fresh variables which
-* we call "enumerators".
-*
-* Points (1)-(3) happen within a call to CegConjecturePbe::initialize(...).
-*
-* Notice that each enumerator is associated with a single
-* function-to-synthesize, but a function-to-sythesize may be mapped to multiple
-* enumerators. Some public functions of this class expect an enumerator as
-* input, which we map to a function-to-synthesize via
-* TermDatabaseSygus::getSynthFunFor(e).
-*
-* An enumerator is initially "active" but may become inactive if the enumeration
-* exhausts all possible values in the datatype corresponding to syntactic
-* restrictions for it. The search may continue unless all enumerators become
-* inactive.
-*
-* (4) During search, the extension of quantifier-free datatypes procedure for
-* SyGuS datatypes may ask this class whether current candidates can be
-* discarded based on
-* inferring when two candidate solutions are equivalent up to examples.
-* For example, the candidate solutions:
-* f = \x ite( x<0, x+1, x ) and f = \x x
-* are equivalent up to examples on the above conjecture, since they have the
-* same value on the points x = 0,5,6. Hence, we need only consider one of
-* them. The interface for querying this is
-* CegConjecturePbe::addSearchVal(...).
-* For details, see Reynolds et al. SYNT 2017.
-*
-* (5) When the extension of quantifier-free datatypes procedure for SyGuS
-* datatypes terminates with a model, the parent of this class calls
-* CegConjecturePbe::getCandidateList(...), where this class returns the list
-* of active enumerators.
-* (6) The parent class subsequently calls
-* CegConjecturePbe::constructValues(...), which
-* informs this class that new values have been enumerated for active
-* enumerators, as indicated by the current model. This call also requests
-* that based on these
-* newly enumerated values, whether this class is now able to construct a
-* solution based on the high-level strategy (stored in d_c_info).
-*
-* This class is not designed to work in incremental mode, since there is no way
-* to specify incremental problems in SyguS.
-*/
-class CegConjecturePbe {
- public:
- CegConjecturePbe(QuantifiersEngine* qe, CegConjecture* p);
- ~CegConjecturePbe();
-
- /** initialize this class
- *
- * n is the "base instantiation" of the deep-embedding version of
- * the synthesis conjecture under "candidates".
- * (see CegConjecture::d_base_inst)
- *
- * This function may add lemmas to the vector lemmas corresponding
- * to initial lemmas regarding static analysis of enumerators it
- * introduced. For example, we may say that the top-level symbol
- * of an enumerator is not ITE if it is being used to construct
- * return values for decision trees.
- */
- void initialize(Node n,
- std::vector<Node>& candidates,
- std::vector<Node>& lemmas);
- /** get candidate list
- * Adds all active enumerators associated with functions-to-synthesize in
- * candidates to clist.
- */
- void getCandidateList(std::vector<Node>& candidates,
- std::vector<Node>& clist);
- /** construct candidates
- * (1) Indicates that the list of enumerators in "enums" currently have model
- * values "enum_values".
- * (2) Asks whether based on these new enumerated values, we can construct a
- * solution for
- * the functions-to-synthesize in "candidates". If so, this function
- * returns "true" and
- * adds solutions for candidates into "candidate_values".
- * During this class, this class may add auxiliary lemmas to "lems", which the
- * caller should send on the output channel via lemma(...).
- */
- bool constructCandidates(std::vector<Node>& enums,
- std::vector<Node>& enum_values,
- std::vector<Node>& candidates,
- std::vector<Node>& candidate_values,
- std::vector<Node>& lems);
- /** is PBE enabled for any enumerator? */
- bool isPbe() { return d_is_pbe; }
- /** is the enumerator e associated with I/O example pairs? */
- bool hasExamples(Node e);
- /** get number of I/O example pairs for enumerator e */
- unsigned getNumExamples(Node e);
- /** get the input arguments for i^th I/O example for e, which is added to the
- * vector ex */
- void getExample(Node e, unsigned i, std::vector<Node>& ex);
- /** get the output value of the i^th I/O example for enumerator e */
- Node getExampleOut(Node e, unsigned i);
-
- /** add the search val
- * This function is called by the extension of quantifier-free datatypes
- * procedure for SyGuS datatypes when we are considering a value of
- * enumerator e of sygus type tn whose analog in the signature of builtin
- * theory is bvr.
- *
- * For example, bvr = x + 1 when e is the datatype value Plus( x(), One() ) and
- * tn is a sygus datatype that encodes a subsignature of the integers.
- *
- * This returns either:
- * - A SyGuS term whose analog is equivalent to bvr up to examples
- * In the above example,
- * it may return a term t of the form Plus( One(), x() ), such that this
- * function was previously called with t as input.
- * - e, indicating that no previous terms are equivalent to e up to examples.
- */
- Node addSearchVal(TypeNode tn, Node e, Node bvr);
- /** evaluate builtin
- * This returns the evaluation of bn on the i^th example for the
- * function-to-synthesis
- * associated with enumerator e. If there are not at least i examples, it
- * returns the rewritten form of bn.
- * For example, if bn = x+5, e is an enumerator for f in the above example
- * [EX#1], then
- * evaluateBuiltin( tn, bn, e, 0 ) = 7
- * evaluateBuiltin( tn, bn, e, 1 ) = 9
- * evaluateBuiltin( tn, bn, e, 2 ) = 10
- */
- Node evaluateBuiltin(TypeNode tn, Node bn, Node e, unsigned i);
-
- private:
- /** quantifiers engine associated with this class */
- QuantifiersEngine* d_qe;
- /** sygus term database of d_qe */
- quantifiers::TermDbSygus * d_tds;
- /** true and false nodes */
- Node d_true;
- Node d_false;
- /** A reference to the conjecture that owns this class. */
- CegConjecture* d_parent;
- /** is this a PBE conjecture for any function? */
- bool d_is_pbe;
- /** for each candidate variable f (a function-to-synthesize), whether the
- * conjecture is purely PBE for that variable
- * In other words, all occurrences of f are guarded by equalities that
- * constraint its arguments to constants.
- */
- std::map< Node, bool > d_examples_invalid;
- /** for each candidate variable (function-to-synthesize), whether the
- * conjecture is purely PBE for that variable.
- * An example of a conjecture for which d_examples_invalid is false but
- * d_examples_out_invalid is true is:
- * exists f. forall x. ( x = 0 => f( x ) > 2 )
- * another example is:
- * exists f. forall x. ( ( x = 0 => f( x ) = 2 ) V ( x = 3 => f( x ) = 3 ) )
- * since the formula is not a conjunction (the example values are not
- * entailed).
- * However, the domain of f in both cases is finite, which can be used for
- * search space pruning.
- */
- std::map< Node, bool > d_examples_out_invalid;
- /** for each candidate variable (function-to-synthesize), input of I/O
- * examples */
- std::map< Node, std::vector< std::vector< Node > > > d_examples;
- /** for each candidate variable (function-to-synthesize), output of I/O
- * examples */
- std::map< Node, std::vector< Node > > d_examples_out;
- /** the list of example terms
- * For the example [EX#1] above, this is f( 0 ), f( 5 ), f( 6 )
- */
- std::map< Node, std::vector< Node > > d_examples_term;
- /** collect the PBE examples in n
- * This is called on the input conjecture, and will populate the above vectors.
- * hasPol/pol denote the polarity of n in the conjecture.
- */
- void collectExamples( Node n, std::map< Node, bool >& visited, bool hasPol, bool pol );
-
- //--------------------------------- PBE search values
- /** this class is an index of candidate solutions for PBE synthesis */
- class PbeTrie {
- public:
- PbeTrie() {}
- ~PbeTrie() {}
- Node d_lazy_child;
- std::map<Node, PbeTrie> d_children;
- void clear() { d_children.clear(); }
- Node addPbeExample(TypeNode etn, Node e, Node b, CegConjecturePbe* cpbe,
- unsigned index, unsigned ntotal);
-
- private:
- Node addPbeExampleEval(TypeNode etn, Node e, Node b, std::vector<Node>& ex,
- CegConjecturePbe* cpbe, unsigned index,
- unsigned ntotal);
- };
- /** trie of candidate solutions tried
- * This stores information for each (enumerator, type),
- * where type is a type in the grammar of the space of solutions for a subterm
- * of e. This is used for symmetry breaking in quantifier-free reasoning
- * about SyGuS datatypes.
- */
- std::map<Node, std::map<TypeNode, PbeTrie> > d_pbe_trie;
- //--------------------------------- end PBE search values
-
- // -------------------------------- decision tree learning
- // index filter
- class IndexFilter {
- public:
- IndexFilter(){}
- void mk( std::vector< Node >& vals, bool pol = true );
- std::map< unsigned, unsigned > d_next;
- unsigned start();
- unsigned next( unsigned i );
- void clear() { d_next.clear(); }
- bool isEq( std::vector< Node >& vs, Node v );
- };
- // subsumption trie
- class SubsumeTrie {
- public:
- SubsumeTrie(){}
- // adds term to the trie, removes based on subsumption
- Node addTerm( CegConjecturePbe * pbe, Node t, std::vector< Node >& vals, bool pol, std::vector< Node >& subsumed, IndexFilter * f = NULL );
- // adds condition to the trie (does not do subsumption)
- Node addCond( CegConjecturePbe * pbe, Node c, std::vector< Node >& vals, bool pol, IndexFilter * f = NULL );
- // returns the set of terms that are subsets of vals
- void getSubsumed( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::vector< Node >& subsumed, IndexFilter * f = NULL );
- // returns the set of terms that are supersets of vals
- void getSubsumedBy( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::vector< Node >& subsumed_by, IndexFilter * f = NULL );
- // v[-1,1,0] -> children always false, always true, both
- void getLeaves( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::map< int, std::vector< Node > >& v, IndexFilter * f = NULL );
- /** is this trie empty? */
- bool isEmpty() { return d_term.isNull() && d_children.empty(); }
- /** clear this trie */
- void clear() {
- d_term = Node::null();
- d_children.clear();
- }
-
- private:
- /** the term at this node */
- Node d_term;
- /** the children nodes of this trie */
- std::map<Node, SubsumeTrie> d_children;
- /** helper function for above functions */
- Node addTermInternal(CegConjecturePbe* pbe,
- Node t,
- std::vector<Node>& vals,
- bool pol,
- std::vector<Node>& subsumed,
- bool spol,
- IndexFilter* f,
- unsigned index,
- int status,
- bool checkExistsOnly,
- bool checkSubsume);
- /** helper function for above functions */
- void getLeavesInternal(CegConjecturePbe* pbe,
- std::vector<Node>& vals,
- bool pol,
- std::map<int, std::vector<Node> >& v,
- IndexFilter* f,
- unsigned index,
- int status);
- };
- // -------------------------------- end decision tree learning
-
- //------------------------------ representation of a enumeration strategy
-
- /** information about an enumerator
- *
- * We say an enumerator is a master enumerator if it is the variable that
- * we use to enumerate values for its sort. Master enumerators may have
- * (possibly multiple) slave enumerators, stored in d_enum_slave,
- */
- class EnumInfo {
- public:
- EnumInfo() : d_role(enum_io), d_is_conditional(false) {}
- /** initialize this class
- * c is the parent function-to-synthesize
- * role is the "role" the enumerator plays in the high-level strategy,
- * which is one of enum_* above.
- */
- void initialize(Node c, EnumRole role);
- /** is this enumerator associated with a template? */
- bool isTemplated() { return !d_template.isNull(); }
- /** set conditional
- *
- * This flag is set to true if this enumerator may not apply to all
- * input/output examples. For example, if this enumerator is used
- * as an output value beneath a conditional in an instance of strat_ITE,
- * then this enumerator is conditional.
- */
- void setConditional() { d_is_conditional = true; }
- /** is conditional */
- bool isConditional() { return d_is_conditional; }
- void addEnumValue(CegConjecturePbe* pbe,
- Node v,
- std::vector<Node>& results);
- void setSolved(Node slv);
- bool isSolved() { return !d_enum_solved.isNull(); }
- Node getSolved() { return d_enum_solved; }
- EnumRole getRole() { return d_role; }
- Node d_parent_candidate;
- // for template
- Node d_template;
- Node d_template_arg;
-
- Node d_active_guard;
- std::vector<Node> d_enum_slave;
- /** values we have enumerated */
- std::vector<Node> d_enum_vals;
- /**
- * This either stores the values of f( I ) for inputs
- * or the value of f( I ) = O if d_role==enum_io
- */
- std::vector<std::vector<Node> > d_enum_vals_res;
- std::vector<Node> d_enum_subsume;
- std::map<Node, unsigned> d_enum_val_to_index;
- SubsumeTrie d_term_trie;
-
- private:
- /**
- * Whether an enumerated value for this conjecture has solved the entire
- * conjecture.
- */
- Node d_enum_solved;
- /** the role of this enumerator (one of enum_* above). */
- EnumRole d_role;
- /** is this enumerator conditional */
- bool d_is_conditional;
- };
- /** maps enumerators to the information above */
- std::map< Node, EnumInfo > d_einfo;
-
- class CandidateInfo;
-
- /** represents a strategy for a SyGuS datatype type
- *
- * This represents a possible strategy to apply when processing a strategy
- * node in constructSolution. When applying the strategy represented by this
- * class, we may make recursive calls to the children of the strategy,
- * given in d_cenum. If all recursive calls to constructSolution are
- * successful, say:
- * constructSolution( c, d_cenum[1], ... ) = t1,
- * ...,
- * constructSolution( c, d_cenum[n], ... ) = tn,
- * Then, the solution returned by this strategy is
- * d_sol_templ * { d_sol_templ_args -> (t1,...,tn) }
- */
- class EnumTypeInfoStrat {
- public:
- /** the type of strategy this represents */
- StrategyType d_this;
- /** the sygus datatype constructor that induced this strategy
- *
- * For example, this may be a sygus datatype whose sygus operator is ITE,
- * if the strategy type above is strat_ITE.
- */
- Node d_cons;
- /** children of this strategy */
- std::vector<std::pair<Node, NodeRole> > d_cenum;
- /** the arguments for the (templated) solution */
- std::vector<Node> d_sol_templ_args;
- /** the template for the solution */
- Node d_sol_templ;
- };
-
- /** represents a node in the strategy graph
- *
- * It contains a list of possible strategies which are tried during calls
- * to constructSolution.
- */
- class StrategyNode
- {
- public:
- StrategyNode() {}
- ~StrategyNode();
- /** the set of strategies to try at this node in the strategy graph */
- std::vector<EnumTypeInfoStrat*> d_strats;
- };
-
- /** stores enumerators and strategies for a SyGuS datatype type */
- class EnumTypeInfo {
- public:
- EnumTypeInfo() : d_parent( NULL ){}
- /** the parent candidate info (see below) */
- CandidateInfo * d_parent;
- /** the type that this information is for */
- TypeNode d_this_type;
- /** map from enum roles to enumerators for this type */
- std::map<EnumRole, Node> d_enum;
- /** map from node roles to strategy nodes */
- std::map<NodeRole, StrategyNode> d_snodes;
- };
-
- /** stores strategy and enumeration information for a function-to-synthesize
- */
- class CandidateInfo {
- public:
- CandidateInfo() : d_check_sol( false ), d_cond_count( 0 ){}
- Node d_this_candidate;
- /**
- * The root sygus datatype for the function-to-synthesize,
- * which encodes the overall syntactic restrictions on the space
- * of solutions.
- */
- TypeNode d_root;
- /** Info for sygus datatype type occurring in a field of d_root */
- std::map< TypeNode, EnumTypeInfo > d_tinfo;
- /** list of all enumerators for the function-to-synthesize */
- std::vector< Node > d_esym_list;
- /**
- * Maps sygus datatypes to their search enumerator. This is the (single)
- * enumerator of that type that we enumerate values for.
- */
- std::map< TypeNode, Node > d_search_enum;
- bool d_check_sol;
- unsigned d_cond_count;
- Node d_solution;
- void initialize( Node c );
- void initializeType( TypeNode tn );
- Node getRootEnumerator();
- bool isNonTrivial();
- };
- /** maps a function-to-synthesize to the above information */
- std::map< Node, CandidateInfo > d_cinfo;
-
- //------------------------------ representation of an enumeration strategy
- /** add enumerated value
- *
- * We have enumerated the value v for x. This function adds x->v to the
- * relevant data structures that are used for strategy-specific construction
- * of solutions when necessary, and returns a set of lemmas, which are added
- * to the input argument lems. These lemmas are used to rule out models where
- * x = v, to force that a new value is enumerated for x.
- */
- void addEnumeratedValue( Node x, Node v, std::vector< Node >& lems );
- /** domain-specific enumerator exclusion techniques
- *
- * Returns true if the value v for x can be excluded based on a
- * domain-specific exclusion technique like the ones below.
- *
- * c : the candidate variable that x is enumerating for,
- * results : the values of v under the input examples of c,
- * ei : the enumerator information for x,
- * exp : if this function returns true, then exp contains a (possibly
- * generalize) explanation for why v can be excluded.
- */
- bool getExplanationForEnumeratorExclude( Node c, Node x, Node v, std::vector< Node >& results, EnumInfo& ei, std::vector< Node >& exp );
- /** returns true if we can exlude values of x based on negative str.contains
- *
- * Values v for x may be excluded if we realize that the value of v under the
- * substitution for some input example will never be contained in some output
- * example. For details on this technique, see NegContainsSygusInvarianceTest
- * in sygus_invariance.h.
- *
- * This function depends on whether x is being used to enumerate values
- * for any node that is conditional in the strategy graph. For example,
- * nodes that are children of ITE strategy nodes are conditional. If any node
- * is conditional, then this function returns false.
- */
- bool useStrContainsEnumeratorExclude(Node x, EnumInfo& ei);
- /** cache for the above function */
- std::map<Node, bool> d_use_str_contains_eexc;
-
- //------------------------------ strategy registration
- /** collect enumerator types
- *
- * This builds the strategy for enumerated values of type tn for the given
- * role of nrole, for solutions to function-to-synthesize c.
- */
- void collectEnumeratorTypes(Node c, TypeNode tn, NodeRole nrole);
- /** register enumerator
- *
- * This registers that et is an enumerator for function-to-synthesize c
- * of type tn, having enumerator role enum_role.
- *
- * inSearch is whether we will enumerate values based on this enumerator.
- * A strategy node is represented by a (enumerator, node role) pair. Hence,
- * we may use enumerators for which this flag is false to represent strategy
- * nodes that have child strategies.
- */
- void registerEnumerator(
- Node et, Node c, TypeNode tn, EnumRole enum_role, bool inSearch);
- /** infer template */
- bool inferTemplate(unsigned k,
- Node n,
- std::map<Node, unsigned>& templ_var_index,
- std::map<unsigned, unsigned>& templ_injection);
- /** static learn redundant operators
- *
- * This learns static lemmas for pruning enumerative space based on the
- * strategy for the function-to-synthesize c, and stores these into lemmas.
- */
- void staticLearnRedundantOps(Node c, std::vector<Node>& lemmas);
- /** helper for static learn redundant operators
- *
- * (e, nrole) specify the strategy node in the graph we are currently
- * analyzing, visited stores the nodes we have already visited.
- *
- * This method builds the mapping needs_cons, which maps (master) enumerators
- * to a map from the constructors that it needs.
- *
- * ind is the depth in the strategy graph we are at (for debugging).
- *
- * isCond is whether the current enumerator is conditional (beneath a
- * conditional of an strat_ITE strategy).
- */
- void staticLearnRedundantOps(
- Node c,
- Node e,
- NodeRole nrole,
- std::map<Node, std::map<NodeRole, bool> >& visited,
- std::map<Node, std::map<unsigned, bool> >& needs_cons,
- int ind,
- bool isCond);
- //------------------------------ end strategy registration
-
- //------------------------------ constructing solutions
- class UnifContext {
- public:
- UnifContext() : d_has_string_pos(role_invalid) {}
- /** this intiializes this context for function-to-synthesize c */
- void initialize(CegConjecturePbe* pbe, Node c);
-
- //----------for ITE strategy
- /** the value of the context conditional
- *
- * This stores a list of Boolean constants that is the same length of the
- * number of input/output example pairs we are considering. For each i,
- * if d_vals[i] = true, i/o pair #i is active according to this context
- * if d_vals[i] = false, i/o pair #i is inactive according to this context
- */
- std::vector<Node> d_vals;
- /** update the examples
- *
- * if pol=true, this method updates d_vals to d_vals & vals
- * if pol=false, this method updates d_vals to d_vals & ( ~vals )
- */
- bool updateContext(CegConjecturePbe* pbe, std::vector<Node>& vals, bool pol);
- //----------end for ITE strategy
-
- //----------for CONCAT strategies
- /** the position in the strings
- *
- * For each i/o example pair, this stores the length of the current solution
- * for the input of the pair, where the solution for that input is a prefix
- * or
- * suffix of the output of the pair. For example, if our i/o pairs are:
- * f( "abcd" ) = "abcdcd"
- * f( "aa" ) = "aacd"
- * If the solution we have currently constructed is str.++( x1, "c", ... ),
- * then d_str_pos = ( 5, 3 ), where notice that
- * str.++( "abc", "c" ) is a prefix of "abcdcd" and
- * str.++( "aa", "c" ) is a prefix of "aacd".
- */
- std::vector<unsigned> d_str_pos;
- /** has string position
- *
- * Whether the solution positions indicate a prefix or suffix of the output
- * examples. If this is role_invalid, then we have not updated the string
- * position.
- */
- NodeRole d_has_string_pos;
- /** update the string examples
- *
- * This method updates d_str_pos to d_str_pos + pos.
- */
- bool updateStringPosition(CegConjecturePbe* pbe, std::vector<unsigned>& pos);
- /** get current strings
- *
- * This returns the prefix/suffix of the string constants stored in vals
- * of size d_str_pos, and stores the result in ex_vals. For example, if vals
- * is (abcdcd", "aacde") and d_str_pos = ( 5, 3 ), then we add
- * "d" and "de" to ex_vals.
- */
- void getCurrentStrings(CegConjecturePbe* pbe,
- const std::vector<Node>& vals,
- std::vector<String>& ex_vals);
- /** get string increment
- *
- * If this method returns true, then inc and tot are updated such that
- * for all active indices i,
- * vals[i] is a prefix (or suffix if isPrefix=false) of ex_vals[i], and
- * inc[i] = str.len(vals[i])
- * for all inactive indices i, inc[i] = 0
- * We set tot to the sum of inc[i] for i=1,...,n. This indicates the total
- * number of characters incremented across all examples.
- */
- bool getStringIncrement(CegConjecturePbe* pbe,
- bool isPrefix,
- const std::vector<String>& ex_vals,
- const std::vector<Node>& vals,
- std::vector<unsigned>& inc,
- unsigned& tot);
- /** returns true if ex_vals[i] = vals[i] for all active indices i. */
- bool isStringSolved(CegConjecturePbe* pbe,
- const std::vector<String>& ex_vals,
- const std::vector<Node>& vals);
- //----------end for CONCAT strategies
-
- /** is return value modified?
- *
- * This returns true if we are currently in a state where the return value
- * of the solution has been modified, e.g. by a previous node that solved
- * for a prefix.
- */
- bool isReturnValueModified();
- /** returns true if argument is valid strategy in this context */
- bool isValidStrategy(EnumTypeInfoStrat* etis);
- /** visited role
- *
- * This is the current set of enumerator/node role pairs we are currently
- * visiting. This set is cleared when the context is updated.
- */
- std::map<Node, std::map<NodeRole, bool> > d_visit_role;
-
- /** unif context enumerator information */
- class UEnumInfo
- {
- public:
- UEnumInfo() {}
- /** map from conditions and branch positions to a solved node
- *
- * For example, if we have:
- * f( 1 ) = 2 ^ f( 3 ) = 4 ^ f( -1 ) = 1
- * Then, valid entries in this map is:
- * d_look_ahead_sols[x>0][1] = x+1
- * d_look_ahead_sols[x>0][2] = 1
- * For the first entry, notice that for all input examples such that x>0
- * evaluates to true, which are (1) and (3), we have that their output
- * values for x+1 under the substitution that maps x to the input value,
- * resulting in 2 and 4, are equal to the output value for the respective
- * pairs.
- */
- std::map<Node, std::map<unsigned, Node> > d_look_ahead_sols;
- };
- /** map from enumerators to the above info class */
- std::map< Node, UEnumInfo > d_uinfo;
- };
-
- /** construct solution
- *
- * This method tries to construct a solution for function-to-synthesize c
- * based on the strategy stored for c in d_cinfo, which may include
- * synthesis-by-unification approaches for ite and string concatenation terms.
- * These approaches include the work of Alur et al. TACAS 2017.
- * If it cannot construct a solution, it returns the null node.
- */
- Node constructSolution( Node c );
- /** helper function for construct solution.
- *
- * Construct a solution based on enumerator e for function-to-synthesize c
- * with node role nrole in context x.
- *
- * ind is the term depth of the context (for debugging).
- */
- Node constructSolution(
- Node c, Node e, NodeRole nrole, UnifContext& x, int ind);
- /** Heuristically choose the best solved term from solved in context x,
- * currently return the first. */
- Node constructBestSolvedTerm( std::vector< Node >& solved, UnifContext& x );
- /** Heuristically choose the best solved string term from solved in context
- * x, currently return the first. */
- Node constructBestStringSolvedTerm( std::vector< Node >& solved, UnifContext& x );
- /** Heuristically choose the best solved conditional term from solved in
- * context x, currently random */
- Node constructBestSolvedConditional( std::vector< Node >& solved, UnifContext& x );
- /** Heuristically choose the best conditional term from conds in context x,
- * currently random */
- Node constructBestConditional( std::vector< Node >& conds, UnifContext& x );
- /** Heuristically choose the best string to concatenate from strs to the
- * solution in context x, currently random
- * incr stores the vector of indices that are incremented by this solution in
- * example outputs.
- * total_inc[x] is the sum of incr[x] for each x in strs.
- */
- Node constructBestStringToConcat( std::vector< Node > strs,
- std::map< Node, unsigned > total_inc,
- std::map< Node, std::vector< unsigned > > incr,
- UnifContext& x );
- //------------------------------ end constructing solutions
-};
-
-}/* namespace CVC4::theory::quantifiers */
-}/* namespace CVC4::theory */
-}/* namespace CVC4 */
-
-#endif
+++ /dev/null
-/********************* */
-/*! \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; i<d_single_inv[0].getNumChildren(); i++ ){
- std::stringstream ss;
- ss << "k_" << d_single_inv[0][i];
- Node k = NodeManager::currentNM()->mkSkolem( ss.str(), d_single_inv[0][i].getType(), "single invocation function skolem" );
- d_single_inv_var.push_back( d_single_inv[0][i] );
- d_single_inv_sk.push_back( k );
- d_single_inv_sk_index[k] = i;
- }
- inst = d_single_inv[1].substitute( d_single_inv_var.begin(), d_single_inv_var.end(), d_single_inv_sk.begin(), d_single_inv_sk.end() );
- }else{
- inst = d_single_inv;
- }
- inst = TermUtil::simpleNegate( inst );
- Trace("cegqi-si") << "Single invocation initial lemma : " << inst << std::endl;
-
- //register with the instantiator
- Node ginst = NodeManager::currentNM()->mkNode( OR, d_si_guard.negate(), inst );
- lems.push_back( ginst );
- //make and register the instantiator
- if( d_cinst ){
- delete d_cinst;
- }
- d_cinst = new CegInstantiator( d_qe, d_cosi, false, false );
- d_cinst->registerCounterexampleLemma( lems, d_single_inv_sk );
- }
-}
-
-void CegConjectureSingleInv::initialize( Node q ) {
- // can only register one quantified formula with this
- Assert( d_quant.isNull() );
- d_quant = q;
- d_simp_quant = q;
- Trace("cegqi-si") << "CegConjectureSingleInv::initialize : " << q << std::endl;
- // infer single invocation-ness
- std::vector< Node > progs;
- std::map< Node, std::vector< Node > > prog_vars;
- for( unsigned i=0; i<q[0].getNumChildren(); i++ ){
- Node sf = q[0][i];
- progs.push_back( sf );
- Node sfvl = sf.getAttribute(SygusSynthFunVarListAttribute());
- for( unsigned j=0; j<sfvl.getNumChildren(); j++ ){
- prog_vars[sf].push_back( sfvl[j] );
- }
- }
- // compute single invocation partition
- if( options::cegqiSingleInvMode()!=CEGQI_SI_MODE_NONE ){
- Node qq;
- if( q[1].getKind()==NOT && q[1][0].getKind()==FORALL ){
- qq = q[1][0][1];
- }else{
- qq = TermUtil::simpleNegate( q[1] );
- }
- //process the single invocation-ness of the property
- if( !d_sip->init( progs, qq ) ){
- Trace("cegqi-si") << "...not single invocation (type mismatch)" << std::endl;
- }else{
- Trace("cegqi-si") << "- Partitioned to single invocation parts : " << std::endl;
- d_sip->debugPrint( "cegqi-si" );
-
- //map from program to bound variables
- std::vector<Node> funcs;
- d_sip->getFunctions(funcs);
- for (unsigned j = 0, size = funcs.size(); j < size; j++)
- {
- Assert(std::find(progs.begin(), progs.end(), funcs[j]) != progs.end());
- d_prog_to_sol_index[funcs[j]] = j;
- }
-
- //check if it is single invocation
- if (!d_sip->isPurelySingleInvocation())
- {
- if( options::sygusInvTemplMode() != SYGUS_INV_TEMPL_MODE_NONE ){
- //if we are doing invariant templates, then construct the template
- Trace("cegqi-si") << "- Do transition inference..." << std::endl;
- d_ti[q].process( qq );
- Trace("cegqi-inv") << std::endl;
- if( !d_ti[q].d_func.isNull() ){
- // map the program back via non-single invocation map
- Node prog = d_ti[q].d_func;
- std::vector< Node > prog_templ_vars;
- prog_templ_vars.insert( prog_templ_vars.end(), d_ti[q].d_vars.begin(), d_ti[q].d_vars.end() );
- d_trans_pre[prog] = d_ti[q].getComponent( 1 );
- d_trans_post[prog] = d_ti[q].getComponent( -1 );
- Trace("cegqi-inv") << " precondition : " << d_trans_pre[prog] << std::endl;
- Trace("cegqi-inv") << " postcondition : " << d_trans_post[prog] << std::endl;
- std::vector<Node> sivars;
- d_sip->getSingleInvocationVariables(sivars);
- Node invariant = d_sip->getFunctionInvocationFor(prog);
- Assert(!invariant.isNull());
- invariant = invariant.substitute(sivars.begin(),
- sivars.end(),
- prog_templ_vars.begin(),
- prog_templ_vars.end());
- Trace("cegqi-inv") << " invariant : " << invariant << std::endl;
-
- // store simplified version of quantified formula
- d_simp_quant = d_sip->getFullSpecification();
- std::vector< Node > new_bv;
- for (unsigned j = 0, size = sivars.size(); j < size; j++)
- {
- new_bv.push_back(
- NodeManager::currentNM()->mkBoundVar(sivars[j].getType()));
- }
- d_simp_quant = d_simp_quant.substitute(
- sivars.begin(), sivars.end(), new_bv.begin(), new_bv.end());
- Assert( q[1].getKind()==NOT && q[1][0].getKind()==FORALL );
- for( unsigned j=0; j<q[1][0][0].getNumChildren(); j++ ){
- new_bv.push_back( q[1][0][0][j] );
- }
- d_simp_quant = NodeManager::currentNM()->mkNode( kind::FORALL, NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, new_bv ), d_simp_quant ).negate();
- d_simp_quant = Rewriter::rewrite( d_simp_quant );
- d_simp_quant = NodeManager::currentNM()->mkNode( kind::FORALL, q[0], d_simp_quant, q[2] );
- Trace("cegqi-si") << "Rewritten quantifier : " << d_simp_quant << std::endl;
-
- //construct template argument
- d_templ_arg[prog] = NodeManager::currentNM()->mkSkolem( "I", invariant.getType() );
-
- //construct template
- Node templ;
- if( options::sygusInvAutoUnfold() ){
- if( d_ti[q].isComplete() ){
- Trace("cegqi-inv-auto-unfold") << "Automatic deterministic unfolding... " << std::endl;
- // auto-unfold
- DetTrace dt;
- int init_dt = d_ti[q].initializeTrace( dt );
- if( init_dt==0 ){
- Trace("cegqi-inv-auto-unfold") << " Init : ";
- dt.print("cegqi-inv-auto-unfold");
- Trace("cegqi-inv-auto-unfold") << std::endl;
- unsigned counter = 0;
- unsigned status = 0;
- while( counter<100 && status==0 ){
- status = d_ti[q].incrementTrace( dt );
- counter++;
- Trace("cegqi-inv-auto-unfold") << " #" << counter << " : ";
- dt.print("cegqi-inv-auto-unfold");
- Trace("cegqi-inv-auto-unfold") << "...status = " << status << std::endl;
- }
- if( status==1 ){
- // we have a trivial invariant
- templ = d_ti[q].constructFormulaTrace( dt );
- Trace("cegqi-inv") << "By finite deterministic terminating trace, a solution invariant is : " << std::endl;
- Trace("cegqi-inv") << " " << templ << std::endl;
- // FIXME : this should be unnecessary
- templ = NodeManager::currentNM()->mkNode( AND, templ, d_templ_arg[prog] );
- }
- }else{
- Trace("cegqi-inv-auto-unfold") << "...failed initialize." << std::endl;
- }
- }
- }
- if( templ.isNull() ){
- if( options::sygusInvTemplMode() == SYGUS_INV_TEMPL_MODE_PRE ){
- //d_templ[prog] = NodeManager::currentNM()->mkNode( AND, NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], invariant ), d_trans_post[prog] );
- templ = NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], d_templ_arg[prog] );
- }else{
- Assert( options::sygusInvTemplMode() == SYGUS_INV_TEMPL_MODE_POST );
- //d_templ[prog] = NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], NodeManager::currentNM()->mkNode( AND, d_trans_post[prog], invariant ) );
- templ = NodeManager::currentNM()->mkNode( AND, d_trans_post[prog], d_templ_arg[prog] );
- }
- }
- Trace("cegqi-inv") << " template (pre-substitution) : " << templ << std::endl;
- Assert( !templ.isNull() );
- // subsitute the template arguments
- templ = templ.substitute( prog_templ_vars.begin(), prog_templ_vars.end(), prog_vars[prog].begin(), prog_vars[prog].end() );
- Trace("cegqi-inv") << " template : " << templ << std::endl;
- d_templ[prog] = templ;
- }
- }
- }else{
- //we are fully single invocation
- d_single_invocation = true;
- }
- }
- }
-}
-
-void CegConjectureSingleInv::finishInit( bool syntaxRestricted, bool hasItes ) {
- d_has_ites = hasItes;
- // do not do single invocation if grammar is restricted and CEGQI_SI_MODE_ALL is not enabled
- if( options::cegqiSingleInvMode()==CEGQI_SI_MODE_USE && d_single_invocation && syntaxRestricted ){
- d_single_invocation = false;
- Trace("cegqi-si") << "...grammar is restricted, do not use single invocation techniques." << std::endl;
- }
-
- // we now have determined whether we will do single invocation techniques
- if( d_single_invocation ){
- d_single_inv = d_sip->getSingleInvocation();
- d_single_inv = TermUtil::simpleNegate( d_single_inv );
- std::vector<Node> func_vars;
- d_sip->getFunctionVariables(func_vars);
- if (!func_vars.empty())
- {
- Node pbvl = NodeManager::currentNM()->mkNode(BOUND_VAR_LIST, func_vars);
- d_single_inv = NodeManager::currentNM()->mkNode( FORALL, pbvl, d_single_inv );
- }
- //now, introduce the skolems
- std::vector<Node> sivars;
- d_sip->getSingleInvocationVariables(sivars);
- for (unsigned i = 0, size = sivars.size(); i < size; i++)
- {
- Node v = NodeManager::currentNM()->mkSkolem(
- "a", sivars[i].getType(), "single invocation arg");
- d_single_inv_arg_sk.push_back( v );
- }
- d_single_inv = d_single_inv.substitute(sivars.begin(),
- sivars.end(),
- d_single_inv_arg_sk.begin(),
- d_single_inv_arg_sk.end());
- Trace("cegqi-si") << "Single invocation formula is : " << d_single_inv << std::endl;
- if( options::cbqiPreRegInst() && d_single_inv.getKind()==FORALL ){
- //just invoke the presolve now
- d_cinst->presolve( d_single_inv );
- }
- }else{
- d_single_inv = Node::null();
- Trace("cegqi-si") << "Formula is not single invocation." << std::endl;
- if( options::cegqiSingleInvAbort() ){
- Notice() << "Property is not single invocation." << std::endl;
- exit( 1 );
- }
- }
-}
-
-bool CegConjectureSingleInv::doAddInstantiation( std::vector< Node >& subs ){
- Assert( d_single_inv_sk.size()==subs.size() );
- Trace("cegqi-si-inst-debug") << "CegConjectureSingleInv::doAddInstantiation, #vars = ";
- Trace("cegqi-si-inst-debug") << d_single_inv_sk.size() << "..." << std::endl;
- std::stringstream siss;
- if( Trace.isOn("cegqi-si-inst-debug") || Trace.isOn("cegqi-engine") ){
- siss << " * single invocation: " << std::endl;
- for( unsigned j=0; j<d_single_inv_sk.size(); j++ ){
- Node op = d_sip->getFunctionForFirstOrderVariable(d_single_inv[0][j]);
- Assert(!op.isNull());
- siss << " * " << op;
- siss << " (" << d_single_inv_sk[j] << ")";
- siss << " -> " << subs[j] << std::endl;
- }
- }
- Trace("cegqi-si-inst-debug") << siss.str();
-
- bool alreadyExists;
- Node lem;
- if( subs.empty() ){
- Assert( d_single_inv.getKind()!=FORALL );
- alreadyExists = false;
- lem = d_single_inv;
- }else{
- Assert( d_single_inv.getKind()==FORALL );
- if( options::incrementalSolving() ){
- alreadyExists = !d_c_inst_match_trie->addInstMatch( d_qe, d_single_inv, subs, d_qe->getUserContext() );
- }else{
- alreadyExists = !d_inst_match_trie.addInstMatch( d_qe, d_single_inv, subs );
- }
- Trace("cegqi-si-inst-debug") << " * success = " << !alreadyExists << std::endl;
- //Trace("cegqi-si-inst-debug") << siss.str();
- //Trace("cegqi-si-inst-debug") << " * success = " << !alreadyExists << std::endl;
- if( alreadyExists ){
- return false;
- }else{
- Trace("cegqi-engine") << siss.str() << std::endl;
- Assert( d_single_inv_var.size()==subs.size() );
- lem = d_single_inv[1].substitute( d_single_inv_var.begin(), d_single_inv_var.end(), subs.begin(), subs.end() );
- if( d_qe->getTermUtil()->containsVtsTerm( lem ) ){
- Trace("cegqi-engine-debug") << "Rewrite based on vts symbols..." << std::endl;
- lem = d_qe->getTermUtil()->rewriteVtsSymbols( lem );
- }
- }
- }
- Trace("cegqi-engine-debug") << "Rewrite..." << std::endl;
- lem = Rewriter::rewrite( lem );
- Trace("cegqi-si") << "Single invocation lemma : " << lem << std::endl;
- if( std::find( d_lemmas_produced.begin(), d_lemmas_produced.end(), lem )==d_lemmas_produced.end() ){
- d_curr_lemmas.push_back( lem );
- d_lemmas_produced.push_back( lem );
- d_inst.push_back( std::vector< Node >() );
- d_inst.back().insert( d_inst.back().end(), subs.begin(), subs.end() );
- }
- return true;
-}
-
-bool CegConjectureSingleInv::isEligibleForInstantiation( Node n ) {
- return n.getKind()!=SKOLEM || std::find( d_single_inv_arg_sk.begin(), d_single_inv_arg_sk.end(), n )!=d_single_inv_arg_sk.end();
-}
-
-bool CegConjectureSingleInv::addLemma( Node n ) {
- d_curr_lemmas.push_back( n );
- return true;
-}
-
-bool CegConjectureSingleInv::check( std::vector< Node >& lems ) {
- if( !d_single_inv.isNull() ) {
- Trace("cegqi-si-debug") << "CegConjectureSingleInv::check..." << std::endl;
- Trace("cegqi-si-debug") << "CegConjectureSingleInv::check consulting ceg instantiation..." << std::endl;
- d_curr_lemmas.clear();
- Assert( d_cinst!=NULL );
- //call check for instantiator
- d_cinst->check();
- Trace("cegqi-si-debug") << "...returned " << d_curr_lemmas.size() << " lemmas " << std::endl;
- //add lemmas
- lems.insert( lems.end(), d_curr_lemmas.begin(), d_curr_lemmas.end() );
- return !lems.empty();
- }else{
- // not single invocation
- return false;
- }
-}
-
-Node CegConjectureSingleInv::constructSolution( std::vector< unsigned >& indices, unsigned i, unsigned index, std::map< Node, Node >& weak_imp ) {
- Assert( index<d_inst.size() );
- Assert( i<d_inst[index].size() );
- unsigned uindex = indices[index];
- if( index==indices.size()-1 ){
- return d_inst[uindex][i];
- }else{
- Node cond = d_lemmas_produced[uindex];
- //weaken based on unsat core
- std::map< Node, Node >::iterator itw = weak_imp.find( cond );
- if( itw!=weak_imp.end() ){
- cond = itw->second;
- }
- cond = TermUtil::simpleNegate( cond );
- Node ite1 = d_inst[uindex][i];
- Node ite2 = constructSolution( indices, i, index+1, weak_imp );
- return NodeManager::currentNM()->mkNode( ITE, cond, ite1, ite2 );
- }
-}
-
-//TODO: use term size?
-struct sortSiInstanceIndices {
- CegConjectureSingleInv* d_ccsi;
- int d_i;
- bool operator() (unsigned i, unsigned j) {
- if( d_ccsi->d_inst[i][d_i].isConst() && !d_ccsi->d_inst[j][d_i].isConst() ){
- return true;
- }else{
- return false;
- }
- }
-};
-
-
-Node CegConjectureSingleInv::postProcessSolution( Node n ){
- ////remove boolean ITE (not allowed for sygus comp 2015)
- //if( n.getKind()==ITE && n.getType().isBoolean() ){
- // Node n1 = postProcessSolution( n[1] );
- // Node n2 = postProcessSolution( n[2] );
- // return NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, n[0], n1 ),
- // NodeManager::currentNM()->mkNode( AND, n[0].negate(), n2 ) );
- //}else{
- bool childChanged = false;
- Kind k = n.getKind();
- if( n.getKind()==INTS_DIVISION_TOTAL ){
- k = INTS_DIVISION;
- childChanged = true;
- }else if( n.getKind()==INTS_MODULUS_TOTAL ){
- k = INTS_MODULUS;
- childChanged = true;
- }
- std::vector< Node > children;
- for( unsigned i=0; i<n.getNumChildren(); i++ ){
- Node nn = postProcessSolution( n[i] );
- children.push_back( nn );
- childChanged = childChanged || nn!=n[i];
- }
- if( childChanged ){
- if( n.hasOperator() && k==n.getKind() ){
- children.insert( children.begin(), n.getOperator() );
- }
- return NodeManager::currentNM()->mkNode( k, children );
- }else{
- return n;
- }
- //}
-}
-
-
-Node CegConjectureSingleInv::getSolution( unsigned sol_index, TypeNode stn, int& reconstructed, bool rconsSygus ){
- Assert( d_sol!=NULL );
- Assert( !d_lemmas_produced.empty() );
- const Datatype& dt = ((DatatypeType)(stn).toType()).getDatatype();
- Node varList = Node::fromExpr( dt.getSygusVarList() );
- Node prog = d_quant[0][sol_index];
- std::vector< Node > vars;
- Node s;
- if( d_prog_to_sol_index.find( prog )==d_prog_to_sol_index.end() ){
- Trace("csi-sol") << "Get solution for (unconstrained) " << prog << std::endl;
- s = d_qe->getTermEnumeration()->getEnumerateTerm(
- TypeNode::fromType(dt.getSygusType()), 0);
- }else{
- Trace("csi-sol") << "Get solution for " << prog << ", with skolems : ";
- sol_index = d_prog_to_sol_index[prog];
- d_sol->d_varList.clear();
- Assert( d_single_inv_arg_sk.size()==varList.getNumChildren() );
- for( unsigned i=0; i<d_single_inv_arg_sk.size(); i++ ){
- Trace("csi-sol") << d_single_inv_arg_sk[i] << " ";
- vars.push_back( d_single_inv_arg_sk[i] );
- d_sol->d_varList.push_back( varList[i] );
- }
- Trace("csi-sol") << std::endl;
-
- //construct the solution
- Trace("csi-sol") << "Sort solution return values " << sol_index << std::endl;
- bool useUnsatCore = false;
- std::vector< Node > active_lemmas;
- //minimize based on unsat core, if possible
- std::map< Node, Node > weak_imp;
- if( options::cegqiSolMinCore() ){
- if( options::cegqiSolMinInst() ){
- if( d_qe->getUnsatCoreLemmas( active_lemmas, weak_imp ) ){
- useUnsatCore = true;
- }
- }else{
- if( d_qe->getUnsatCoreLemmas( active_lemmas ) ){
- useUnsatCore = true;
- }
- }
- }
- Assert( d_lemmas_produced.size()==d_inst.size() );
- std::vector< unsigned > indices;
- for( unsigned i=0; i<d_lemmas_produced.size(); i++ ){
- bool incl = true;
- if( useUnsatCore ){
- incl = std::find( active_lemmas.begin(), active_lemmas.end(), d_lemmas_produced[i] )!=active_lemmas.end();
- }
- if( incl ){
- Assert( sol_index<d_inst[i].size() );
- indices.push_back( i );
- }
- }
- Trace("csi-sol") << "...included " << indices.size() << " / " << d_lemmas_produced.size() << " instantiations." << std::endl;
- Assert( !indices.empty() );
- //sort indices based on heuristic : currently, do all constant returns first (leads to simpler conditions)
- // TODO : to minimize solution size, put the largest term last
- sortSiInstanceIndices ssii;
- ssii.d_ccsi = this;
- ssii.d_i = sol_index;
- std::sort( indices.begin(), indices.end(), ssii );
- Trace("csi-sol") << "Construct solution" << std::endl;
- s = constructSolution( indices, sol_index, 0, weak_imp );
- Assert( vars.size()==d_sol->d_varList.size() );
- s = s.substitute( vars.begin(), vars.end(), d_sol->d_varList.begin(), d_sol->d_varList.end() );
- }
- d_orig_solution = s;
-
- //simplify the solution
- Trace("csi-sol") << "Solution (pre-simplification): " << d_orig_solution << std::endl;
- s = d_sol->simplifySolution( s, stn );
- Trace("csi-sol") << "Solution (post-simplification): " << s << std::endl;
- return reconstructToSyntax( s, stn, reconstructed, rconsSygus );
-}
-
-Node CegConjectureSingleInv::reconstructToSyntax( Node s, TypeNode stn, int& reconstructed, bool rconsSygus ) {
- d_solution = s;
- const Datatype& dt = ((DatatypeType)(stn).toType()).getDatatype();
-
- //reconstruct the solution into sygus if necessary
- reconstructed = 0;
- if( options::cegqiSingleInvReconstruct() && !dt.getSygusAllowAll() && !stn.isNull() && rconsSygus ){
- d_sol->preregisterConjecture( d_orig_conjecture );
- d_sygus_solution = d_sol->reconstructSolution( s, stn, reconstructed );
- if( reconstructed==1 ){
- Trace("csi-sol") << "Solution (post-reconstruction into Sygus): " << d_sygus_solution << std::endl;
- }
- }else{
- Trace("csi-sol") << "Post-process solution..." << std::endl;
- Node prev = d_solution;
- d_solution = postProcessSolution( d_solution );
- if( prev!=d_solution ){
- Trace("csi-sol") << "Solution (after post process) : " << d_solution << std::endl;
- }
- }
-
-
- if( Trace.isOn("csi-sol") ){
- //debug solution
- if( !d_sol->debugSolution( d_solution ) ){
- Trace("csi-sol") << "WARNING : solution " << d_solution << " contains free constants." << std::endl;
- //exit( 47 );
- }else{
- //exit( 49 );
- }
- }
- if( Trace.isOn("cegqi-stats") ){
- int tsize, itesize;
- tsize = 0;itesize = 0;
- d_sol->debugTermSize( d_orig_solution, tsize, itesize );
- Trace("cegqi-stats") << tsize << " " << itesize << " ";
- tsize = 0;itesize = 0;
- d_sol->debugTermSize( d_solution, tsize, itesize );
- Trace("cegqi-stats") << tsize << " " << itesize << " ";
- if( !d_sygus_solution.isNull() ){
- tsize = 0;itesize = 0;
- d_sol->debugTermSize( d_sygus_solution, tsize, itesize );
- Trace("cegqi-stats") << tsize << " - ";
- }else{
- Trace("cegqi-stats") << "null ";
- }
- Trace("cegqi-stats") << std::endl;
- }
- Node sol;
- if( reconstructed==1 ){
- sol = d_sygus_solution;
- }else if( reconstructed==-1 ){
- return Node::null();
- }else{
- sol = d_solution;
- }
- //make into lambda
- if( !dt.getSygusVarList().isNull() ){
- Node varList = Node::fromExpr( dt.getSygusVarList() );
- return NodeManager::currentNM()->mkNode( LAMBDA, varList, sol );
- }else{
- return sol;
- }
-}
-
-bool CegConjectureSingleInv::needsCheck() {
- if( options::cegqiSingleInvMode()==CEGQI_SI_MODE_ALL_ABORT ){
- if( !d_has_ites ){
- return d_inst.empty();
- }
- }
- return true;
-}
-
-void CegConjectureSingleInv::preregisterConjecture( Node q ) {
- d_orig_conjecture = q;
-}
-
-bool DetTrace::DetTraceTrie::add( Node loc, std::vector< Node >& val, unsigned index ){
- if( index==val.size() ){
- if( d_children.empty() ){
- d_children[loc].clear();
- return true;
- }else{
- return false;
- }
- }else{
- return d_children[val[index]].add( loc, val, index+1 );
- }
-}
-
-Node DetTrace::DetTraceTrie::constructFormula( std::vector< Node >& vars, unsigned index ){
- if( index==vars.size() ){
- return NodeManager::currentNM()->mkConst( true );
- }else{
- std::vector< Node > disj;
- for( std::map< Node, DetTraceTrie >::iterator it = d_children.begin(); it != d_children.end(); ++it ){
- Node eq = vars[index].eqNode( it->first );
- if( index<vars.size()-1 ){
- Node conc = it->second.constructFormula( vars, index+1 );
- disj.push_back( NodeManager::currentNM()->mkNode( kind::AND, eq, conc ) );
- }else{
- disj.push_back( eq );
- }
- }
- Assert( !disj.empty() );
- return disj.size()==1 ? disj[0] : NodeManager::currentNM()->mkNode( kind::OR, disj );
- }
-}
-
-bool DetTrace::increment( Node loc, std::vector< Node >& vals ){
- if( d_trie.add( loc, vals ) ){
- for( unsigned i=0; i<vals.size(); i++ ){
- d_curr[i] = vals[i];
- }
- return true;
- }else{
- return false;
- }
-}
-
-Node DetTrace::constructFormula( std::vector< Node >& vars ) {
- return d_trie.constructFormula( vars );
-}
-
-
-void DetTrace::print( const char* c ) {
- for( unsigned i=0; i<d_curr.size(); i++ ){
- Trace(c) << d_curr[i] << " ";
- }
-}
-
-void TransitionInference::initialize( Node f, std::vector< Node >& vars ) {
- Assert( d_vars.empty() );
- d_func = f;
- d_vars.insert( d_vars.end(), vars.begin(), vars.end() );
-}
-
-
-void TransitionInference::getConstantSubstitution( std::vector< Node >& vars, std::vector< Node >& disjuncts, std::vector< Node >& const_var, std::vector< Node >& const_subs, bool reqPol ) {
- for( unsigned j=0; j<disjuncts.size(); j++ ){
- Node sn;
- if( !const_var.empty() ){
- sn = disjuncts[j].substitute( const_var.begin(), const_var.end(), const_subs.begin(), const_subs.end() );
- sn = Rewriter::rewrite( sn );
- }else{
- sn = disjuncts[j];
- }
- bool slit_pol = sn.getKind()!=NOT;
- Node slit = sn.getKind()==NOT ? sn[0] : sn;
- if( slit.getKind()==EQUAL && slit_pol==reqPol ){
- // check if it is a variable equality
- TNode v;
- Node s;
- for( unsigned r=0; r<2; r++ ){
- if( std::find( vars.begin(), vars.end(), slit[r] )!=vars.end() ){
- if( !TermUtil::containsTerm( slit[1-r], slit[r] ) ){
- v = slit[r];
- s = slit[1-r];
- break;
- }
- }
- }
- if( v.isNull() ){
- //solve for var
- std::map< Node, Node > msum;
- if (ArithMSum::getMonomialSumLit(slit, msum))
- {
- for( std::map< Node, Node >::iterator itm = msum.begin(); itm != msum.end(); ++itm ){
- if( std::find( vars.begin(), vars.end(), itm->first )!=vars.end() ){
- Node veq_c;
- Node val;
- int ires =
- ArithMSum::isolate(itm->first, msum, veq_c, val, EQUAL);
- if( ires!=0 && veq_c.isNull() && !TermUtil::containsTerm( val, itm->first ) ){
- v = itm->first;
- s = val;
- }
- }
- }
- }
- }
- if( !v.isNull() ){
- TNode ts = s;
- for( unsigned k=0; k<const_subs.size(); k++ ){
- const_subs[k] = Rewriter::rewrite( const_subs[k].substitute( v, ts ) );
- }
- Trace("cegqi-inv-debug2") << "...substitution : " << v << " -> " << s << std::endl;
- const_var.push_back( v );
- const_subs.push_back( s );
- }
- }
- }
-}
-
-void TransitionInference::process( Node n ) {
- d_complete = true;
- std::vector< Node > n_check;
- if( n.getKind()==AND ){
- for( unsigned i=0; i<n.getNumChildren(); i++ ){
- n_check.push_back( n[i] );
- }
- }else{
- n_check.push_back( n );
- }
- for( unsigned i=0; i<n_check.size(); i++ ){
- Node nn = n_check[i];
- std::map< Node, bool > visited;
- std::map< bool, Node > terms;
- std::vector< Node > disjuncts;
- Trace("cegqi-inv") << "TransitionInference : Process disjunct : " << nn << std::endl;
- if( processDisjunct( nn, terms, disjuncts, visited, true ) ){
- if( !terms.empty() ){
- Node norm_args;
- int comp_num;
- std::map< bool, Node >::iterator itt = terms.find( false );
- if( itt!=terms.end() ){
- norm_args = itt->second;
- if( terms.find( true )!=terms.end() ){
- comp_num = 0;
- }else{
- comp_num = -1;
- }
- }else{
- norm_args = terms[true];
- comp_num = 1;
- }
- std::vector< Node > subs;
- for( unsigned j=0; j<norm_args.getNumChildren(); j++ ){
- subs.push_back( norm_args[j] );
- }
- Trace("cegqi-inv-debug2") << " normalize based on " << norm_args << std::endl;
- Assert( d_vars.size()==subs.size() );
- for( unsigned j=0; j<disjuncts.size(); j++ ){
- disjuncts[j] = Rewriter::rewrite( disjuncts[j].substitute( subs.begin(), subs.end(), d_vars.begin(), d_vars.end() ) );
- Trace("cegqi-inv-debug2") << " ..." << disjuncts[j] << std::endl;
- }
- std::vector< Node > const_var;
- std::vector< Node > const_subs;
- if( comp_num==0 ){
- //transition
- Assert( terms.find( true )!=terms.end() );
- Node next = terms[true];
- next = Rewriter::rewrite( next.substitute( subs.begin(), subs.end(), d_vars.begin(), d_vars.end() ) );
- Trace("cegqi-inv-debug") << "transition next predicate : " << next << std::endl;
- // normalize the other direction
- std::vector< Node > rvars;
- for( unsigned i=0; i<next.getNumChildren(); i++ ){
- rvars.push_back( next[i] );
- }
- if( d_prime_vars.size()<next.getNumChildren() ){
- for( unsigned i=0; i<next.getNumChildren(); i++ ){
- Node v = NodeManager::currentNM()->mkSkolem( "ir", next[i].getType(), "template inference rev argument" );
- d_prime_vars.push_back( v );
- }
- }
- Trace("cegqi-inv-debug2") << " normalize based on " << next << std::endl;
- Assert( d_vars.size()==subs.size() );
- for( unsigned j=0; j<disjuncts.size(); j++ ){
- disjuncts[j] = Rewriter::rewrite( disjuncts[j].substitute( rvars.begin(), rvars.end(), d_prime_vars.begin(), d_prime_vars.end() ) );
- Trace("cegqi-inv-debug2") << " ..." << disjuncts[j] << std::endl;
- }
- getConstantSubstitution( d_prime_vars, disjuncts, const_var, const_subs, false );
- }else{
- getConstantSubstitution( d_vars, disjuncts, const_var, const_subs, false );
- }
- Node res;
- if( disjuncts.empty() ){
- res = NodeManager::currentNM()->mkConst( false );
- }else if( disjuncts.size()==1 ){
- res = disjuncts[0];
- }else{
- res = NodeManager::currentNM()->mkNode( kind::OR, disjuncts );
- }
- if( !res.hasBoundVar() ){
- Trace("cegqi-inv") << "*** inferred " << ( comp_num==1 ? "pre" : ( comp_num==-1 ? "post" : "trans" ) ) << "-condition : " << res << std::endl;
- d_com[comp_num].d_conjuncts.push_back( res );
- if( !const_var.empty() ){
- bool has_const_eq = const_var.size()==d_vars.size();
- Trace("cegqi-inv") << " with constant substitution, complete = " << has_const_eq << " : " << std::endl;
- for( unsigned i=0; i<const_var.size(); i++ ){
- Trace("cegqi-inv") << " " << const_var[i] << " -> " << const_subs[i] << std::endl;
- if( has_const_eq ){
- d_com[comp_num].d_const_eq[res][const_var[i]] = const_subs[i];
- }
- }
- Trace("cegqi-inv") << "...size = " << const_var.size() << ", #vars = " << d_vars.size() << std::endl;
- }
- }else{
- Trace("cegqi-inv-debug2") << "...failed, free variable." << std::endl;
- d_complete = false;
- }
- }
- }else{
- d_complete = false;
- }
- }
-
- // finalize the components
- for( int i=-1; i<=1; i++ ){
- Node ret;
- if( d_com[i].d_conjuncts.empty() ){
- ret = NodeManager::currentNM()->mkConst( true );
- }else if( d_com[i].d_conjuncts.size()==1 ){
- ret = d_com[i].d_conjuncts[0];
- }else{
- ret = NodeManager::currentNM()->mkNode( kind::AND, d_com[i].d_conjuncts );
- }
- if( i==0 || i==1 ){
- // pre-condition and transition are negated
- ret = TermUtil::simpleNegate( ret );
- }
- d_com[i].d_this = ret;
- }
-}
-
-bool TransitionInference::processDisjunct( Node n, std::map< bool, Node >& terms, std::vector< Node >& disjuncts,
- std::map< Node, bool >& visited, bool topLevel ) {
- if( visited.find( n )==visited.end() ){
- visited[n] = true;
- bool childTopLevel = n.getKind()==OR && topLevel;
- //if another part mentions UF or a free variable, then fail
- bool lit_pol = n.getKind()!=NOT;
- Node lit = n.getKind()==NOT ? n[0] : n;
- if( lit.getKind()==APPLY_UF ){
- Node op = lit.getOperator();
- if( d_func.isNull() ){
- d_func = op;
- Trace("cegqi-inv-debug") << "Use " << op << " with args ";
- for( unsigned i=0; i<lit.getNumChildren(); i++ ){
- Node v = NodeManager::currentNM()->mkSkolem( "i", lit[i].getType(), "template inference argument" );
- d_vars.push_back( v );
- Trace("cegqi-inv-debug") << v << " ";
- }
- Trace("cegqi-inv-debug") << std::endl;
- }
- if( op!=d_func ){
- Trace("cegqi-inv-debug") << "...failed, free function : " << n << std::endl;
- return false;
- }else if( topLevel ){
- if( terms.find( lit_pol )==terms.end() ){
- terms[lit_pol] = lit;
- return true;
- }else{
- Trace("cegqi-inv-debug") << "...failed, repeated inv-app : " << lit << std::endl;
- return false;
- }
- }else{
- Trace("cegqi-inv-debug") << "...failed, non-entailed inv-app : " << lit << std::endl;
- return false;
- }
- }else if( topLevel && !childTopLevel ){
- disjuncts.push_back( n );
- }
- for( unsigned i=0; i<n.getNumChildren(); i++ ){
- if( !processDisjunct( n[i], terms, disjuncts, visited, childTopLevel ) ){
- return false;
- }
- }
- }
- return true;
-}
-
-Node TransitionInference::getComponent( int i ) {
- return d_com[i].d_this;
-}
-
-int TransitionInference::initializeTrace( DetTrace& dt, Node loc, bool fwd ) {
- int index = fwd ? 1 : -1;
- Assert( d_com[index].has( loc ) );
- std::map< Node, std::map< Node, Node > >::iterator it = d_com[index].d_const_eq.find( loc );
- if( it!=d_com[index].d_const_eq.end() ){
- std::vector< Node > next;
- for( unsigned i=0; i<d_vars.size(); i++ ){
- Node v = d_vars[i];
- Assert( it->second.find( v )!=it->second.end() );
- next.push_back( it->second[v] );
- dt.d_curr.push_back( it->second[v] );
- }
- Trace("cegqi-inv-debug2") << "dtrace : initial increment" << std::endl;
- bool ret = dt.increment( loc, next );
- AlwaysAssert( ret );
- return 0;
- }
- return -1;
-}
-
-int TransitionInference::incrementTrace( DetTrace& dt, Node loc, bool fwd ) {
- Assert( d_com[0].has( loc ) );
- // check if it satisfies the pre/post condition
- int check_index = fwd ? -1 : 1;
- Node cc = getComponent( check_index );
- Assert( !cc.isNull() );
- Node ccr = Rewriter::rewrite( cc.substitute( d_vars.begin(), d_vars.end(), dt.d_curr.begin(), dt.d_curr.end() ) );
- if( ccr.isConst() ){
- if( ccr.getConst<bool>()==( fwd ? false : true ) ){
- Trace("cegqi-inv-debug2") << "dtrace : counterexample" << std::endl;
- return 2;
- }
- }
-
-
- // terminates?
- Node c = getComponent( 0 );
- Assert( !c.isNull() );
-
- Assert( d_vars.size()==dt.d_curr.size() );
- Node cr = Rewriter::rewrite( c.substitute( d_vars.begin(), d_vars.end(), dt.d_curr.begin(), dt.d_curr.end() ) );
- if( cr.isConst() ){
- if( !cr.getConst<bool>() ){
- Trace("cegqi-inv-debug2") << "dtrace : terminated" << std::endl;
- return 1;
- }else{
- return -1;
- }
- }
- if( fwd ){
- std::map< Node, std::map< Node, Node > >::iterator it = d_com[0].d_const_eq.find( loc );
- if( it!=d_com[0].d_const_eq.end() ){
- std::vector< Node > next;
- for( unsigned i=0; i<d_prime_vars.size(); i++ ){
- Node pv = d_prime_vars[i];
- Assert( it->second.find( pv )!=it->second.end() );
- Node pvs = it->second[pv];
- Assert( d_vars.size()==dt.d_curr.size() );
- Node pvsr = Rewriter::rewrite( pvs.substitute( d_vars.begin(), d_vars.end(), dt.d_curr.begin(), dt.d_curr.end() ) );
- next.push_back( pvsr );
- }
- if( dt.increment( loc, next ) ){
- Trace("cegqi-inv-debug2") << "dtrace : success increment" << std::endl;
- return 0;
- }else{
- // looped
- Trace("cegqi-inv-debug2") << "dtrace : looped" << std::endl;
- return 1;
- }
- }
- }else{
- //TODO
- }
- return -1;
-}
-
-int TransitionInference::initializeTrace( DetTrace& dt, bool fwd ) {
- Trace("cegqi-inv-debug2") << "Initialize trace" << std::endl;
- int index = fwd ? 1 : -1;
- if( d_com[index].d_conjuncts.size()==1 ){
- return initializeTrace( dt, d_com[index].d_conjuncts[0], fwd );
- }else{
- return -1;
- }
-}
-
-int TransitionInference::incrementTrace( DetTrace& dt, bool fwd ) {
- if( d_com[0].d_conjuncts.size()==1 ){
- return incrementTrace( dt, d_com[0].d_conjuncts[0], fwd );
- }else{
- return -1;
- }
-}
-
-Node TransitionInference::constructFormulaTrace( DetTrace& dt ) {
- return dt.constructFormula( d_vars );
-}
-
-} //namespace CVC4
-
+++ /dev/null
-/********************* */
-/*! \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<unsigned>& indices, unsigned i,
- unsigned index, std::map<Node, Node>& weak_imp);
- Node postProcessSolution(Node n);
- private:
- QuantifiersEngine* d_qe;
- CegConjecture* d_parent;
- // single invocation inference utility
- SingleInvocationPartition* d_sip;
- // transition inference module for each function to synthesize
- std::map< Node, TransitionInference > d_ti;
- // solution reconstruction
- CegConjectureSingleInvSol* d_sol;
- // the instantiator's output channel
- CegqiOutputSingleInv* d_cosi;
- // the instantiator
- CegInstantiator* d_cinst;
-
- // list of skolems for each argument of programs
- std::vector<Node> d_single_inv_arg_sk;
- // list of variables/skolems for each program
- std::vector<Node> d_single_inv_var;
- std::vector<Node> d_single_inv_sk;
- std::map<Node, int> d_single_inv_sk_index;
- // program to solution index
- std::map<Node, unsigned> d_prog_to_sol_index;
- // lemmas produced
- inst::InstMatchTrie d_inst_match_trie;
- inst::CDInstMatchTrie* d_c_inst_match_trie;
- // original conjecture
- Node d_orig_conjecture;
- // solution
- Node d_orig_solution;
- Node d_solution;
- Node d_sygus_solution;
- // whether the grammar for our solution allows ITEs, this tells us when reconstruction is infeasible
- bool d_has_ites;
-
- public:
- // lemmas produced
- std::vector<Node> d_lemmas_produced;
- std::vector<std::vector<Node> > d_inst;
-
- private:
- std::vector<Node> d_curr_lemmas;
- // add instantiation
- bool doAddInstantiation( std::vector< Node >& subs );
- //is eligible for instantiation
- bool isEligibleForInstantiation( Node n );
- // add lemma
- bool addLemma( Node lem );
- // conjecture
- Node d_quant;
- Node d_simp_quant;
- // are we single invocation?
- bool d_single_invocation;
- // single invocation portion of quantified formula
- Node d_single_inv;
- Node d_si_guard;
- // transition relation version per program
- std::map< Node, Node > d_trans_pre;
- std::map< Node, Node > d_trans_post;
- // the template for each function to synthesize
- std::map< Node, Node > d_templ;
- // the template argument for each function to synthesize (occurs in exactly one position of its template)
- std::map< Node, Node > d_templ_arg;
-
- public:
- CegConjectureSingleInv( QuantifiersEngine * qe, CegConjecture * p );
- ~CegConjectureSingleInv();
-
- // get simplified conjecture
- Node getSimplifiedConjecture() { return d_simp_quant; }
- // get single invocation guard
- Node getGuard() { return d_si_guard; }
- public:
- //get the single invocation lemma(s)
- void getInitialSingleInvLemma( std::vector< Node >& lems );
- // initialize this class for synthesis conjecture q
- void initialize( Node q );
- // finish initialize, sets up final decisions about whether to use single invocation techniques
- // syntaxRestricted is whether the syntax for solutions for the initialized conjecture is restricted
- // hasItes is whether the syntax for solutions for the initialized conjecture allows ITEs
- void finishInit( bool syntaxRestricted, bool hasItes );
- //check
- bool check( std::vector< Node >& lems );
- //get solution
- Node getSolution( unsigned sol_index, TypeNode stn, int& reconstructed, bool rconsSygus = true );
- //reconstruct to syntax
- Node reconstructToSyntax( Node s, TypeNode stn, int& reconstructed,
- bool rconsSygus = true );
- // has ites
- bool hasITEs() { return d_has_ites; }
- // is single invocation
- bool isSingleInvocation() const { return !d_single_inv.isNull(); }
- //needs check
- bool needsCheck();
- /** preregister conjecture */
- void preregisterConjecture( Node q );
-
- Node getTransPre(Node prog) const {
- std::map<Node, Node>::const_iterator location = d_trans_pre.find(prog);
- return location->second;
- }
-
- Node getTransPost(Node prog) const {
- std::map<Node, Node>::const_iterator location = d_trans_post.find(prog);
- return location->second;
- }
- // get template for program prog. This returns a term of the form t[x] where x is the template argument (see below)
- Node getTemplate(Node prog) const {
- std::map<Node, Node>::const_iterator tmpl = d_templ.find(prog);
- if( tmpl!=d_templ.end() ){
- return tmpl->second;
- }else{
- return Node::null();
- }
- }
- // get the template argument for program prog.
- // This is a variable which indicates the position of the function/predicate to synthesize.
- Node getTemplateArg(Node prog) const {
- std::map<Node, Node>::const_iterator tmpla = d_templ_arg.find(prog);
- if( tmpla != d_templ_arg.end() ){
- return tmpla->second;
- }else{
- return Node::null();
- }
- }
-};
-
-}/* namespace CVC4::theory::quantifiers */
-}/* namespace CVC4::theory */
-}/* namespace CVC4 */
-
-#endif
+++ /dev/null
-/********************* */
-/*! \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<bool>();
-}
-
-CegConjectureSingleInvSol::CegConjectureSingleInvSol(QuantifiersEngine* qe)
- : d_qe(qe), d_id_count(0), d_root_id() {}
-
-bool CegConjectureSingleInvSol::debugSolution( Node sol ) {
- if( sol.getKind()==SKOLEM ){
- return false;
- }else{
- for( unsigned i=0; i<sol.getNumChildren(); i++ ){
- if( !debugSolution( sol[i] ) ){
- return false;
- }
- }
- return true;
- }
-
-}
-
-void CegConjectureSingleInvSol::debugTermSize( Node sol, int& t_size, int& num_ite ) {
- std::map< Node, int >::iterator it = d_dterm_size.find( sol );
- if( it==d_dterm_size.end() ){
- int prev = t_size;
- int prev_ite = num_ite;
- t_size++;
- if( sol.getKind()==ITE ){
- num_ite++;
- }
- for( unsigned i=0; i<sol.getNumChildren(); i++ ){
- debugTermSize( sol[i], t_size, num_ite );
- }
- d_dterm_size[sol] = t_size-prev;
- d_dterm_ite_size[sol] = num_ite-prev_ite;
- }else{
- t_size += it->second;
- num_ite += d_dterm_ite_size[sol];
- }
-}
-
-
-Node CegConjectureSingleInvSol::pullITEs( Node s ) {
- if( s.getKind()==ITE ){
- bool success;
- do {
- success = false;
- std::vector< Node > conj;
- Node t;
- Node rem;
- if( pullITECondition( s, s, conj, t, rem, 0 ) ){
- Assert( !conj.empty() );
- Node cond = conj.size()==1 ? conj[0] : NodeManager::currentNM()->mkNode( AND, conj );
- Trace("csi-sol-debug") << "For " << s << ", can pull " << cond << " -> " << t << " with remainder " << rem << std::endl;
- t = pullITEs( t );
- rem = pullITEs( rem );
- Trace("csi-pull-ite") << "PI: Rewrite : " << s << std::endl;
- Node prev = s;
- s = NodeManager::currentNM()->mkNode( ITE, TermUtil::simpleNegate( cond ), t, rem );
- Trace("csi-pull-ite") << "PI: Rewrite Now : " << s << std::endl;
- Trace("csi-pull-ite") << "(= " << prev << " " << s << ")" << std::endl;
- success = true;
- }
- }while( success );
- }
- return s;
-}
-
-// pull condition common to all ITE conditions in path of size > 1
-bool CegConjectureSingleInvSol::pullITECondition( Node root, Node n_ite, std::vector< Node >& conj, Node& t, Node& rem, int depth ) {
- Assert( n_ite.getKind()==ITE );
- std::vector< Node > curr_conj;
- std::vector< Node > orig_conj;
- bool isAnd;
- if( n_ite[0].getKind()==AND || n_ite[0].getKind()==OR ){
- isAnd = n_ite[0].getKind()==AND;
- for( unsigned i=0; i<n_ite[0].getNumChildren(); i++ ){
- Node cond = n_ite[0][i];
- orig_conj.push_back( cond );
- if( n_ite[0].getKind()==OR ){
- cond = TermUtil::simpleNegate( cond );
- }
- curr_conj.push_back( cond );
- }
- }else{
- Node neg = n_ite[0].negate();
- if( std::find( conj.begin(), conj.end(), neg )!=conj.end() ){
- //if negation of condition exists, use it
- isAnd = false;
- curr_conj.push_back( neg );
- }else{
- //otherwise, use condition
- isAnd = true;
- curr_conj.push_back( n_ite[0] );
- }
- orig_conj.push_back( n_ite[0] );
- }
- // take intersection with current conditions
- std::vector< Node > new_conj;
- std::vector< Node > prev_conj;
- if( n_ite==root ){
- new_conj.insert( new_conj.end(), curr_conj.begin(), curr_conj.end() );
- Trace("csi-sol-debug") << "Pull ITE root " << n_ite << ", #conj = " << new_conj.size() << std::endl;
- }else{
- for( unsigned i=0; i<curr_conj.size(); i++ ){
- if( std::find( conj.begin(), conj.end(), curr_conj[i] )!=conj.end() ){
- new_conj.push_back( curr_conj[i] );
- }
- }
- Trace("csi-sol-debug") << "Pull ITE " << n_ite << ", #conj = " << conj.size() << " intersect " << curr_conj.size() << " = " << new_conj.size() << std::endl;
- }
- //cannot go further
- if( new_conj.empty() ){
- return false;
- }
- //it is an intersection with current
- conj.clear();
- conj.insert( conj.end(), new_conj.begin(), new_conj.end() );
- //recurse if possible
- Node trec = n_ite[ isAnd ? 2 : 1 ];
- Node tval = n_ite[ isAnd ? 1 : 2 ];
- bool success = false;
- if( trec.getKind()==ITE ){
- prev_conj.insert( prev_conj.end(), conj.begin(), conj.end() );
- success = pullITECondition( root, trec, conj, t, rem, depth+1 );
- }
- if( !success && depth>0 ){
- t = trec;
- rem = trec;
- success = true;
- if( trec.getKind()==ITE ){
- //restore previous state
- conj.clear();
- conj.insert( conj.end(), prev_conj.begin(), prev_conj.end() );
- }
- }
- if( success ){
- //make remainder : strip out conditions in conj
- Assert( !conj.empty() );
- std::vector< Node > cond_c;
- Assert( orig_conj.size()==curr_conj.size() );
- for( unsigned i=0; i<curr_conj.size(); i++ ){
- if( std::find( conj.begin(), conj.end(), curr_conj[i] )==conj.end() ){
- cond_c.push_back( orig_conj[i] );
- }
- }
- if( cond_c.empty() ){
- rem = tval;
- }else{
- Node new_cond = cond_c.size()==1 ? cond_c[0] : NodeManager::currentNM()->mkNode( n_ite[0].getKind(), cond_c );
- rem = NodeManager::currentNM()->mkNode( ITE, new_cond, isAnd ? tval : rem, isAnd ? rem : tval );
- }
- return true;
- }else{
- return false;
- }
-}
-
-Node CegConjectureSingleInvSol::flattenITEs( Node n, bool rec ) {
- Assert( !n.isNull() );
- if( n.getKind()==ITE ){
- Trace("csi-sol-debug") << "Flatten ITE." << std::endl;
- Node ret;
- Node n0 = rec ? flattenITEs( n[0] ) : n[0];
- Node n1 = rec ? flattenITEs( n[1] ) : n[1];
- Node n2 = rec ? flattenITEs( n[2] ) : n[2];
- Assert( !n0.isNull() );
- Assert( !n1.isNull() );
- Assert( !n2.isNull() );
- if( n0.getKind()==NOT ){
- ret = NodeManager::currentNM()->mkNode( ITE, n0[0], n2, n1 );
- }else if( n0.getKind()==AND || n0.getKind()==OR ){
- std::vector< Node > children;
- for( unsigned i=1; i<n0.getNumChildren(); i++ ){
- children.push_back( n0[i] );
- }
- Node rem = children.size()==1 ? children[0] : NodeManager::currentNM()->mkNode( n0.getKind(), children );
- if( n0.getKind()==AND ){
- ret = NodeManager::currentNM()->mkNode( ITE, rem, NodeManager::currentNM()->mkNode( ITE, n0[0], n1, n2 ), n2 );
- }else{
- ret = NodeManager::currentNM()->mkNode( ITE, rem, n1, NodeManager::currentNM()->mkNode( ITE, n0[0], n1, n2 ) );
- }
- }else{
- if( n0.getKind()==ITE ){
- n0 = NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, n0, n1 ),
- NodeManager::currentNM()->mkNode( AND, n0.negate(), n2 ) );
- }else if( n0.getKind()==EQUAL && n0[0].getType().isBoolean() ){
- n0 = NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, n0, n1 ),
- NodeManager::currentNM()->mkNode( AND, n0.negate(), n1.negate() ) );
- }else{
- return NodeManager::currentNM()->mkNode( ITE, n0, n1, n2 );
- }
- ret = NodeManager::currentNM()->mkNode( ITE, n0, n1, n2 );
- }
- Assert( !ret.isNull() );
- return flattenITEs( ret, false );
- }else{
- if( n.getNumChildren()>0 ){
- std::vector< Node > children;
- if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){
- children.push_back( n.getOperator() );
- }
- bool childChanged = false;
- for( unsigned i=0; i<n.getNumChildren(); i++ ){
- Node nc = flattenITEs( n[i] );
- children.push_back( nc );
- childChanged = childChanged || nc!=n[i];
- }
- if( !childChanged ){
- return n;
- }else{
- return NodeManager::currentNM()->mkNode( n.getKind(), children );
- }
- }else{
- return n;
- }
- }
-}
-
-// assign is from literals to booleans
-// union_find is from args to values
-
-bool CegConjectureSingleInvSol::getAssign( bool pol, Node n, std::map< Node, bool >& assign, std::vector< Node >& new_assign, std::vector< Node >& vars,
- std::vector< Node >& new_vars, std::vector< Node >& new_subs ) {
- std::map< Node, bool >::iterator ita = assign.find( n );
- if( ita!=assign.end() ){
- Trace("csi-simp-debug") << "---already assigned, lookup " << pol << " " << ita->second << std::endl;
- return pol==ita->second;
- }else if( n.isConst() ){
- return pol==(n==d_qe->getTermUtil()->d_true);
- }else{
- Trace("csi-simp-debug") << "---assign " << n << " " << pol << std::endl;
- assign[n] = pol;
- new_assign.push_back( n );
- if( ( pol && n.getKind()==AND ) || ( !pol && n.getKind()==OR ) ){
- for( unsigned i=0; i<n.getNumChildren(); i++ ){
- if( !getAssign( pol, n[i], assign, new_assign, vars, new_vars, new_subs ) ){
- return false;
- }
- }
- }else if( n.getKind()==NOT ){
- return getAssign( !pol, n[0], assign, new_assign, vars, new_vars, new_subs );
- }else if( pol && n.getKind()==EQUAL ){
- getAssignEquality( n, vars, new_vars, new_subs );
- }
- }
- return true;
-}
-
-bool CegConjectureSingleInvSol::getAssignEquality( Node eq, std::vector< Node >& vars, std::vector< Node >& new_vars, std::vector< Node >& new_subs ) {
- Assert( eq.getKind()==EQUAL );
- //try to find valid argument
- for( unsigned r=0; r<2; r++ ){
- if( std::find( d_varList.begin(), d_varList.end(), eq[r] )!=d_varList.end() ){
- Assert( std::find( vars.begin(), vars.end(), eq[r] )==vars.end() );
- if( std::find( new_vars.begin(), new_vars.end(), eq[r] )==new_vars.end() ){
- Node eqro = eq[r==0 ? 1 : 0 ];
- if( !d_qe->getTermUtil()->containsTerm( eqro, eq[r] ) ){
- Trace("csi-simp-debug") << "---equality " << eq[r] << " = " << eqro << std::endl;
- new_vars.push_back( eq[r] );
- new_subs.push_back( eqro );
- return true;
- }
- }
- }
- }
- return false;
-}
-
-Node CegConjectureSingleInvSol::simplifySolution( Node sol, TypeNode stn ){
- int tsize, itesize;
- if( Trace.isOn("csi-sol") ){
- tsize = 0;itesize = 0;
- debugTermSize( sol, tsize, itesize );
- Trace("csi-sol") << tsize << " " << itesize << " rewrite..." << std::endl;
- Trace("csi-sol-debug") << "sol : " << sol << "..." << std::endl;
- }
- Node sol0 = Rewriter::rewrite( sol );
- Trace("csi-sol") << "now : " << sol0 << std::endl;
-
- Node curr_sol = sol0;
- Node prev_sol;
- do{
- prev_sol = curr_sol;
- //first, pull ITE conditions
- if( Trace.isOn("csi-sol") ){
- tsize = 0;itesize = 0;
- debugTermSize( curr_sol, tsize, itesize );
- Trace("csi-sol") << tsize << " " << itesize << " pull ITE..." << std::endl;
- Trace("csi-sol-debug") << "sol : " << curr_sol << "..." << std::endl;
- }
- Node sol1 = pullITEs( curr_sol );
- Trace("csi-sol") << "now : " << sol1 << std::endl;
- //do standard rewriting
- if( sol1!=curr_sol ){
- if( Trace.isOn("csi-sol") ){
- tsize = 0;itesize = 0;
- debugTermSize( sol1, tsize, itesize );
- Trace("csi-sol") << tsize << " " << itesize << " rewrite..." << std::endl;
- Trace("csi-sol-debug") << "sol : " << sol1 << "..." << std::endl;
- }
- Node sol2 = Rewriter::rewrite( sol1 );
- Trace("csi-sol") << "now : " << sol2 << std::endl;
- curr_sol = sol2;
- }
- //now do branch analysis
- if( Trace.isOn("csi-sol") ){
- tsize = 0;itesize = 0;
- debugTermSize( curr_sol, tsize, itesize );
- Trace("csi-sol") << tsize << " " << itesize << " simplify solution..." << std::endl;
- Trace("csi-sol-debug") << "sol : " << curr_sol << "..." << std::endl;
- }
- std::map< Node, bool > sassign;
- std::vector< Node > svars;
- std::vector< Node > ssubs;
- Node sol3 = simplifySolutionNode( curr_sol, stn, sassign, svars, ssubs, 0 );
- Trace("csi-sol") << "now : " << sol3 << std::endl;
- if( sol3!=curr_sol ){
- //do standard rewriting again
- if( Trace.isOn("csi-sol" ) ){
- tsize = 0;itesize = 0;
- debugTermSize( sol3, tsize, itesize );
- Trace("csi-sol") << tsize << " " << itesize << " rewrite..." << std::endl;
- }
- Node sol4 = Rewriter::rewrite( sol3 );
- Trace("csi-sol") << "now : " << sol4 << std::endl;
- curr_sol = sol4;
- }
- }while( curr_sol!=prev_sol );
-
- return curr_sol;
-}
-
-Node CegConjectureSingleInvSol::simplifySolutionNode( Node sol, TypeNode stn, std::map< Node, bool >& assign,
- std::vector< Node >& vars, std::vector< Node >& subs, int status ) {
-
- Assert( vars.size()==subs.size() );
- std::map< Node, bool >::iterator ita = assign.find( sol );
- if( ita!=assign.end() ){
- //it is currently assigned a boolean value
- return NodeManager::currentNM()->mkConst( ita->second );
- }else{
- d_qe->getTermDatabaseSygus()->registerSygusType( stn );
- std::map< int, TypeNode > stnc;
- if( !stn.isNull() ){
- int karg = d_qe->getTermDatabaseSygus()->getKindConsNum( stn, sol.getKind() );
- if( karg!=-1 ){
- const Datatype& dt = ((DatatypeType)(stn).toType()).getDatatype();
- if( dt[karg].getNumArgs()==sol.getNumChildren() ){
- for( unsigned i=0; i<dt[karg].getNumArgs(); i++ ){
- stnc[i] = d_qe->getTermDatabaseSygus()->getArgType( dt[karg], i );
- }
- }
- }
- }
-
- if( sol.getKind()==ITE ){
- Trace("csi-simp") << "Simplify ITE " << std::endl;
- std::vector< Node > children;
- for( unsigned r=1; r<=2; r++ ){
- std::vector< Node > new_assign;
- std::vector< Node > new_vars;
- std::vector< Node > new_subs;
- if( getAssign( r==1, sol[0], assign, new_assign, vars, new_vars, new_subs ) ){
- Trace("csi-simp") << "- branch " << r << " led to " << new_assign.size() << " assignments, " << new_vars.size() << " equalities." << std::endl;
- unsigned prev_size = vars.size();
- Node nc = sol[r];
- if( !new_vars.empty() ){
- nc = nc.substitute( new_vars.begin(), new_vars.end(), new_subs.begin(), new_subs.end() );
- vars.insert( vars.end(), new_vars.begin(), new_vars.end() );
- subs.insert( subs.end(), new_subs.begin(), new_subs.end() );
- }
- nc = simplifySolutionNode( nc, stnc[r], assign, vars, subs, 0 );
- children.push_back( nc );
- //clean up substitution
- if( !new_vars.empty() ){
- vars.resize( prev_size );
- subs.resize( prev_size );
- }
- }else{
- Trace("csi-simp") << "- branch " << r << " of " << sol[0] << " is infeasible." << std::endl;
- }
- //clean up assignment
- for( unsigned i=0; i<new_assign.size(); i++ ){
- assign.erase( new_assign[i] );
- }
- }
- if( children.size()==1 || ( children.size()==2 && children[0]==children[1] ) ){
- return children[0];
- }else{
- Assert( children.size()==2 );
- Node ncond = simplifySolutionNode( sol[0], stnc[0], assign, vars, subs, 0 );
- Node ret = NodeManager::currentNM()->mkNode( ITE, ncond, children[0], children[1] );
-
- //expand/flatten if necessary
- Node orig_ret = ret;
- if( !stnc[0].isNull() ){
- d_qe->getTermDatabaseSygus()->registerSygusType( stnc[0] );
- Node prev_ret;
- while( !d_qe->getTermDatabaseSygus()->hasKind( stnc[0], ret[0].getKind() ) && ret!=prev_ret ){
- prev_ret = ret;
- Node exp_c = d_qe->getTermDatabaseSygus()->expandBuiltinTerm( ret[0] );
- if( !exp_c.isNull() ){
- Trace("csi-simp-debug") << "Pre expand to " << ret[0] << " to " << exp_c << std::endl;
- ret = NodeManager::currentNM()->mkNode( ITE, exp_c, ret[1], ret[2] );
- }
- if( !d_qe->getTermDatabaseSygus()->hasKind( stnc[0], ret[0].getKind() ) ){
- Trace("csi-simp-debug") << "Flatten based on " << ret[0] << "." << std::endl;
- ret = flattenITEs( ret, false );
- }
- }
- }
- return ret;
- /*
- if( orig_ret!=ret ){
- Trace("csi-simp") << "Try expanded ITE" << std::endl;
- return ret;//simplifySolutionNode( ret, stn, assign, vars, subs, status );
- }else{
- return ret;
- }
- */
- }
- }else if( sol.getKind()==OR || sol.getKind()==AND ){
- Trace("csi-simp") << "Simplify " << sol.getKind() << std::endl;
- //collect new equalities
- std::map< Node, bool > atoms;
- std::vector< Node > inc;
- std::vector< Node > children;
- std::vector< Node > new_vars;
- std::vector< Node > new_subs;
- Node bc = sol.getKind()==OR ? d_qe->getTermUtil()->d_true : d_qe->getTermUtil()->d_false;
- for( unsigned i=0; i<sol.getNumChildren(); i++ ){
- bool do_exc = false;
- Node c;
- std::map< Node, bool >::iterator ita = assign.find( sol[i] );
- if( ita==assign.end() ){
- c = sol[i];
- }else{
- c = NodeManager::currentNM()->mkConst( ita->second );
- }
- Trace("csi-simp") << " - child " << i << " : " << c << std::endl;
- if( c.isConst() ){
- if( c==bc ){
- Trace("csi-simp") << " ...singularity." << std::endl;
- return bc;
- }else{
- do_exc = true;
- }
- }else{
- Node atom = c.getKind()==NOT ? c[0] : c;
- bool pol = c.getKind()!=NOT;
- std::map< Node, bool >::iterator it = atoms.find( atom );
- if( it==atoms.end() ){
- atoms[atom] = pol;
- if( status==0 && atom.getKind()==EQUAL ){
- if( pol==( sol.getKind()==AND ) ){
- Trace("csi-simp") << " ...equality." << std::endl;
- if( getAssignEquality( atom, vars, new_vars, new_subs ) ){
- children.push_back( sol[i] );
- do_exc = true;
- }
- }
- }
- }else{
- //repeated atom
- if( it->second!=pol ){
- return NodeManager::currentNM()->mkConst( sol.getKind()==OR );
- }else{
- do_exc = true;
- }
- }
- }
- if( !do_exc ){
- inc.push_back( sol[i] );
- }else{
- Trace("csi-simp") << " ...exclude." << std::endl;
- }
- }
- if( !new_vars.empty() ){
- if( !inc.empty() ){
- Node ret = inc.size()==1 ? inc[0] : NodeManager::currentNM()->mkNode( sol.getKind(), inc );
- Trace("csi-simp") << "Base return is : " << ret << std::endl;
- // apply substitution
- ret = ret.substitute( new_vars.begin(), new_vars.end(), new_subs.begin(), new_subs.end() );
- ret = Rewriter::rewrite( ret );
- Trace("csi-simp") << "After substitution : " << ret << std::endl;
- unsigned prev_size = vars.size();
- vars.insert( vars.end(), new_vars.begin(), new_vars.end() );
- subs.insert( subs.end(), new_subs.begin(), new_subs.end() );
- ret = simplifySolutionNode( ret, TypeNode::null(), assign, vars, subs, 1 );
- //clean up substitution
- if( !vars.empty() ){
- vars.resize( prev_size );
- subs.resize( prev_size );
- }
- //Trace("csi-simp") << "After simplification : " << ret << std::endl;
- if( ret.isConst() ){
- if( ret==bc ){
- return bc;
- }
- }else{
- if( ret.getKind()==sol.getKind() ){
- for( unsigned i=0; i<ret.getNumChildren(); i++ ){
- children.push_back( ret[i] );
- }
- }else{
- children.push_back( ret );
- }
- }
- }
- }else{
- //recurse on children
- for( unsigned i=0; i<inc.size(); i++ ){
- Node retc = simplifySolutionNode( inc[i], TypeNode::null(), assign, vars, subs, 0 );
- if( retc.isConst() ){
- if( retc==bc ){
- return bc;
- }
- }else{
- children.push_back( retc );
- }
- }
- }
- // now, remove all equalities that are implied
- std::vector< Node > final_children;
- for( unsigned i=0; i<children.size(); i++ ){
- bool red = false;
- Node atom = children[i].getKind()==NOT ? children[i][0] : children[i];
- bool pol = children[i].getKind()!=NOT;
- if( status==0 && atom.getKind()==EQUAL ){
- if( pol!=( sol.getKind()==AND ) ){
- std::vector< Node > tmp_vars;
- std::vector< Node > tmp_subs;
- if( getAssignEquality( atom, vars, tmp_vars, tmp_subs ) ){
- Trace("csi-simp-debug") << "Check if " << children[i] << " is redundant in " << sol << std::endl;
- for( unsigned j=0; j<children.size(); j++ ){
- if( j!=i && ( j>i || std::find( final_children.begin(), final_children.end(), children[j] )!=final_children.end() ) ){
- Node sj = children[j].substitute( tmp_vars.begin(), tmp_vars.end(), tmp_subs.begin(), tmp_subs.end() );
- sj = Rewriter::rewrite( sj );
- if( sj==( sol.getKind()==AND ? d_qe->getTermUtil()->d_false : d_qe->getTermUtil()->d_true ) ){
- Trace("csi-simp") << "--- " << children[i].negate() << " is implied by " << children[j].negate() << std::endl;
- red = true;
- break;
- }
- }
- }
- if( !red ){
- Trace("csi-simp-debug") << "...is not." << std::endl;
- }
- }
- }
- }
- if( !red ){
- final_children.push_back( children[i] );
- }
- }
- return final_children.size()==0 ? NodeManager::currentNM()->mkConst( sol.getKind()==AND ) :
- ( final_children.size()==1 ? final_children[0] : NodeManager::currentNM()->mkNode( sol.getKind(), final_children ) );
- }else{
- //generic simplification
- std::vector< Node > children;
- if( sol.getMetaKind() == kind::metakind::PARAMETERIZED ){
- children.push_back( sol.getOperator() );
- }
- bool childChanged = false;
- for( unsigned i=0; i<sol.getNumChildren(); i++ ){
- Node nc = simplifySolutionNode( sol[i], stnc[i], assign, vars, subs, 0 );
- childChanged = childChanged || nc!=sol[i];
- children.push_back( nc );
- }
- if( childChanged ){
- return NodeManager::currentNM()->mkNode( sol.getKind(), children );
- }
- }
- return sol;
- }
-}
-
-
-void CegConjectureSingleInvSol::preregisterConjecture( Node q ) {
- Trace("csi-sol") << "Preregister conjecture : " << q << std::endl;
- Node n = q;
- if( n.getKind()==FORALL ){
- n = n[1];
- }
- if( n.getKind()==EXISTS ){
- if( n[0].getNumChildren()==d_varList.size() ){
- std::vector< Node > evars;
- for( unsigned i=0; i<n[0].getNumChildren(); i++ ){
- evars.push_back( n[0][i] );
- }
- n = n[1].substitute( evars.begin(), evars.end(), d_varList.begin(), d_varList.end() );
- }else{
- Trace("csi-sol") << "Not the same number of variables, return." << std::endl;
- return;
- }
- }
- Trace("csi-sol") << "Preregister node for solution reconstruction : " << n << std::endl;
- registerEquivalentTerms( n );
-}
-
-Node CegConjectureSingleInvSol::reconstructSolution( Node sol, TypeNode stn, int& reconstructed ) {
- Trace("csi-rcons") << "Solution (pre-reconstruction) is : " << sol << std::endl;
- int status;
- d_root_id = collectReconstructNodes( sol, stn, status );
- if( status==0 ){
- Node ret = getReconstructedSolution( d_root_id );
- Trace("csi-rcons") << "Sygus solution is : " << ret << std::endl;
- Assert( !ret.isNull() );
- reconstructed = 1;
- return ret;
- }else{
- //Trace("csi-debug-sol") << "Induced solution template is : " << d_templ_solution << std::endl;
- if( Trace.isOn("csi-rcons") ){
- for( std::map< TypeNode, std::map< Node, int > >::iterator it = d_rcons_to_id.begin(); it != d_rcons_to_id.end(); ++it ){
- TypeNode tn = it->first;
- Assert( tn.isDatatype() );
- const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
- Trace("csi-rcons") << "Terms to reconstruct of type " << dt.getName() << " : " << std::endl;
- for( std::map< Node, int >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){
- if( d_reconstruct.find( it2->second )==d_reconstruct.end() ){
- Trace("csi-rcons") << " " << it2->first << std::endl;
- }
- }
- Assert( !it->second.empty() );
- }
- }
- unsigned index = 0;
- std::map< TypeNode, bool > active;
- for( std::map< TypeNode, std::map< Node, int > >::iterator it = d_rcons_to_id.begin(); it != d_rcons_to_id.end(); ++it ){
- active[it->first] = true;
- }
- //enumerate for all types
- do {
- std::vector< TypeNode > to_erase;
- for( std::map< TypeNode, bool >::iterator it = active.begin(); it != active.end(); ++it ){
- TypeNode stn = it->first;
- Node ns = d_qe->getTermEnumeration()->getEnumerateTerm(stn, index);
- if( ns.isNull() ){
- to_erase.push_back( stn );
- }else{
- Node nb = d_qe->getTermDatabaseSygus()->sygusToBuiltin( ns, stn );
- Node nr = Rewriter::rewrite( nb );//d_qe->getTermDatabaseSygus()->getNormalized( stn, nb, false, false );
- Trace("csi-rcons-debug2") << " - try " << ns << " -> " << nr << " for " << stn << " " << nr.getKind() << std::endl;
- std::map< Node, int >::iterator itt = d_rcons_to_id[stn].find( nr );
- if( itt!= d_rcons_to_id[stn].end() ){
- // if it is not already reconstructed
- if( d_reconstruct.find( itt->second )==d_reconstruct.end() ){
- Trace("csi-rcons") << "...reconstructed " << ns << " for term " << nr << std::endl;
- bool do_check = true;//getPathToRoot( itt->second );
- setReconstructed( itt->second, ns );
- if( do_check ){
- Trace("csi-rcons-debug") << "...path to root, try reconstruction." << std::endl;
- d_tmp_fail.clear();
- Node ret = getReconstructedSolution( d_root_id );
- if( !ret.isNull() ){
- Trace("csi-rcons") << "Sygus solution (after enumeration) is : " << ret << std::endl;
- reconstructed = 1;
- return ret;
- }
- }else{
- Trace("csi-rcons-debug") << "...no path to root." << std::endl;
- }
- }
- }
- }
- }
- for( unsigned i=0; i<to_erase.size(); i++ ){
- active.erase( to_erase[i] );
- }
- index++;
- if( index%100==0 ){
- Trace("csi-rcons-stats") << "Tried " << index << " for each type." << std::endl;
- }
- }while( !active.empty() );
-
- // we ran out of elements, return null
- reconstructed = -1;
- Warning() << CommandFailure("Cannot get synth function: reconstruction to syntax failed.");
- return Node::null(); // return sol;
- }
-}
-
-int CegConjectureSingleInvSol::collectReconstructNodes( Node t, TypeNode stn, int& status ) {
- std::map< Node, int >::iterator itri = d_rcons_to_status[stn].find( t );
- if( itri!=d_rcons_to_status[stn].end() ){
- status = itri->second;
- //Trace("csi-rcons-debug") << "-> (cached) " << status << " for " << d_rcons_to_id[stn][t] << std::endl;
- return d_rcons_to_id[stn][t];
- }else{
- status = 1;
- // register the type
- registerType(stn);
- int id = allocate( t, stn );
- d_rcons_to_status[stn][t] = -1;
- TypeNode tn = t.getType();
- Assert( stn.isDatatype() );
- const Datatype& dt = ((DatatypeType)(stn).toType()).getDatatype();
- Assert( dt.isSygus() );
- Trace("csi-rcons-debug") << "Check reconstruct " << t << ", sygus type " << dt.getName() << ", kind " << t.getKind() << ", id : " << id << std::endl;
- int carg = -1;
- int karg = -1;
- // first, do standard minimizations
- Node min_t = d_qe->getTermDatabaseSygus()->minimizeBuiltinTerm( t );
- Trace("csi-rcons-debug") << "Minimized term is : " << min_t << std::endl;
- //check if op is in syntax sort
- carg = d_qe->getTermDatabaseSygus()->getOpConsNum( stn, min_t );
- if( carg!=-1 ){
- Trace("csi-rcons-debug") << " Type has operator." << std::endl;
- d_reconstruct[id] = NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, Node::fromExpr( dt[carg].getConstructor() ) );
- status = 0;
- }else{
- //check if kind is in syntax sort
- karg = d_qe->getTermDatabaseSygus()->getKindConsNum( stn, min_t.getKind() );
- if( karg!=-1 ){
- //collect the children of min_t
- std::vector< Node > tchildren;
- if( min_t.getNumChildren()>dt[karg].getNumArgs() && quantifiers::TermUtil::isAssoc( min_t.getKind() ) && dt[karg].getNumArgs()==2 ){
- tchildren.push_back( min_t[0] );
- std::vector< Node > rem_children;
- for( unsigned i=1; i<min_t.getNumChildren(); i++ ){
- rem_children.push_back( min_t[i] );
- }
- Node t2 = NodeManager::currentNM()->mkNode( min_t.getKind(), rem_children );
- tchildren.push_back( t2 );
- Trace("csi-rcons-debug") << "...split n-ary to binary " << min_t[0] << " " << t2 << "." << std::endl;
- }else{
- for( unsigned i=0; i<min_t.getNumChildren(); i++ ){
- tchildren.push_back( min_t[i] );
- }
- }
- //recurse on the children
- if( tchildren.size()==dt[karg].getNumArgs() ){
- Trace("csi-rcons-debug") << "Type for " << id << " has kind " << min_t.getKind() << ", recurse." << std::endl;
- status = 0;
- Node cons = Node::fromExpr( dt[karg].getConstructor() );
- if( !collectReconstructNodes( id, tchildren, dt[karg], d_reconstruct_op[id][cons], status ) ){
- Trace("csi-rcons-debug") << "...failure for " << id << " " << dt[karg].getName() << std::endl;
- d_reconstruct_op[id].erase( cons );
- status = 1;
- }
- }else{
- Trace("csi-rcons-debug") << "Type for " << id << " has kind " << min_t.getKind() << ", but argument # mismatch." << std::endl;
- }
- }
- if( status!=0 ){
- //try constant reconstruction
- if( min_t.isConst() ){
- Trace("csi-rcons-debug") << "...try constant reconstruction." << std::endl;
- Node min_t_c = builtinToSygusConst(min_t, stn);
- if( !min_t_c.isNull() ){
- Trace("csi-rcons-debug") << " constant reconstruction success for " << id << ", result = " << min_t_c << std::endl;
- d_reconstruct[id] = min_t_c;
- status = 0;
- }
- }
- if( status!=0 ){
- //try identity functions
- for (unsigned ii : d_id_funcs[stn])
- {
- Assert( dt[ii].getNumArgs()==1 );
- //try to directly reconstruct from single argument
- std::vector< Node > tchildren;
- tchildren.push_back( min_t );
- TypeNode stnc = TypeNode::fromType( ((SelectorType)dt[ii][0].getType()).getRangeType() );
- Trace("csi-rcons-debug") << "...try identity function " << dt[ii].getSygusOp() << ", child type is " << stnc << std::endl;
- status = 0;
- Node cons = Node::fromExpr( dt[ii].getConstructor() );
- if( !collectReconstructNodes( id, tchildren, dt[ii], d_reconstruct_op[id][cons], status ) ){
- d_reconstruct_op[id].erase( cons );
- status = 1;
- }else{
- Trace("csi-rcons-debug") << " identity function success for " << id << std::endl;
- break;
- }
- }
- if( status!=0 ){
- //try other options, such as matching against other constructors
- Trace("csi-rcons-debug") << "Try matching for " << id << "." << std::endl;
- bool success;
- int c_index = 0;
- do{
- success = false;
- int index_found;
- std::vector< Node > args;
- if (getMatch(min_t, stn, index_found, args, karg, c_index))
- {
- success = true;
- status = 0;
- Node cons = Node::fromExpr( dt[index_found].getConstructor() );
- Trace("csi-rcons-debug") << "Try alternative for " << id << ", matching " << dt[index_found].getName() << " with children : " << std::endl;
- for( unsigned i=0; i<args.size(); i++ ){
- Trace("csi-rcons-debug") << " " << args[i] << std::endl;
- }
- if( !collectReconstructNodes( id, args, dt[index_found], d_reconstruct_op[id][cons], status ) ){
- d_reconstruct_op[id].erase( cons );
- status = 1;
- }else{
- c_index = index_found+1;
- }
- }
- }while( success && status!=0 );
-
- if( status!=0 ){
- // construct an equivalence class of terms that are equivalent to t
- if( d_rep[id]==id ){
- Trace("csi-rcons-debug") << "Try rewriting for " << id << "." << std::endl;
- //get equivalence class of term
- std::vector< Node > equiv;
- if( tn.isBoolean() ){
- Node curr = min_t;
- Node new_t;
- do{
- new_t = Node::null();
- if( curr.getKind()==EQUAL ){
- if( curr[0].getType().isInteger() || curr[0].getType().isReal() ){
- new_t = NodeManager::currentNM()->mkNode( AND, NodeManager::currentNM()->mkNode( LEQ, curr[0], curr[1] ),
- NodeManager::currentNM()->mkNode( LEQ, curr[1], curr[0] ) );
- }else if( curr[0].getType().isBoolean() ){
- new_t = NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, curr[0], curr[1] ),
- NodeManager::currentNM()->mkNode( AND, curr[0].negate(), curr[1].negate() ) );
- }else{
- new_t = NodeManager::currentNM()->mkNode( NOT, NodeManager::currentNM()->mkNode( NOT, curr ) );
- }
- }else if( curr.getKind()==ITE ){
- new_t = NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, curr[0], curr[1] ),
- NodeManager::currentNM()->mkNode( AND, curr[0].negate(), curr[2] ) );
- }else if( curr.getKind()==OR || curr.getKind()==AND ){
- new_t = TermUtil::simpleNegate( curr ).negate();
- }else if( curr.getKind()==NOT ){
- new_t = TermUtil::simpleNegate( curr[0] );
- }else{
- new_t = NodeManager::currentNM()->mkNode( NOT, NodeManager::currentNM()->mkNode( NOT, curr ) );
- }
- if( !new_t.isNull() ){
- if( new_t!=min_t && std::find( equiv.begin(), equiv.end(), new_t )==equiv.end() ){
- curr = new_t;
- equiv.push_back( new_t );
- }else{
- new_t = Node::null();
- }
- }
- }while( !new_t.isNull() );
- }
- //get decompositions
- for( unsigned i=0; i<dt.getNumConstructors(); i++ ){
- Kind k = d_qe->getTermDatabaseSygus()->getConsNumKind( stn, i );
- getEquivalentTerms( k, min_t, equiv );
- }
- //assign ids to terms
- Trace("csi-rcons-debug") << "Term " << id << " is equivalent to " << equiv.size() << " terms : " << std::endl;
- std::vector< int > equiv_ids;
- for( unsigned i=0; i<equiv.size(); i++ ){
- Trace("csi-rcons-debug") << " " << equiv[i] << std::endl;
- if( d_rcons_to_id[stn].find( equiv[i] )==d_rcons_to_id[stn].end() ){
- int eq_id = allocate( equiv[i], stn );
- d_eqc.erase( eq_id );
- d_rep[eq_id] = id;
- d_eqc[id].push_back( eq_id );
- equiv_ids.push_back( eq_id );
- }else{
- equiv_ids.push_back( -1 );
- }
- }
- // now, try each of them
- for( unsigned i=0; i<equiv.size(); i++ ){
- if( equiv_ids[i]!=-1 ){
- collectReconstructNodes( equiv[i], stn, status );
- //if one succeeds
- if( status==0 ){
- Node rsol = getReconstructedSolution( equiv_ids[i] );
- Assert( !rsol.isNull() );
- //set all members of the equivalence class that this is the reconstructed solution
- setReconstructed( id, rsol );
- break;
- }
- }
- }
- }else{
- Trace("csi-rcons-debug") << "Do not try rewriting for " << id << ", rep = " << d_rep[id] << std::endl;
- }
- }
- }
- }
- }
- }
- if( status!=0 ){
- Trace("csi-rcons-debug") << "-> *** reconstruction required for id " << id << std::endl;
- }else{
- Trace("csi-rcons-debug") << "-> success for " << id << std::endl;
- }
- d_rcons_to_status[stn][t] = status;
- return id;
- }
-}
-
-bool CegConjectureSingleInvSol::collectReconstructNodes( int pid, std::vector< Node >& ts, const DatatypeConstructor& dtc, std::vector< int >& ids, int& status ) {
- Assert( dtc.getNumArgs()==ts.size() );
- for( unsigned i=0; i<ts.size(); i++ ){
- TypeNode cstn = d_qe->getTermDatabaseSygus()->getArgType( dtc, i );
- int cstatus;
- int c_id = collectReconstructNodes( ts[i], cstn, cstatus );
- if( cstatus==-1 ){
- return false;
- }else if( cstatus!=0 ){
- status = 1;
- }
- ids.push_back( c_id );
- }
- for( unsigned i=0; i<ids.size(); i++ ){
- d_parents[ids[i]].push_back( pid );
- }
- return true;
-}
-
- /*
- //flatten ITEs if necessary TODO : carry assignment or move this elsewhere
- if( t.getKind()==ITE ){
- TypeNode cstn = tds->getArgType( dt[karg], 0 );
- tds->registerSygusType( cstn );
- Node prev_t;
- while( !tds->hasKind( cstn, t[0].getKind() ) && t!=prev_t ){
- prev_t = t;
- Node exp_c = tds->expandBuiltinTerm( t[0] );
- if( !exp_c.isNull() ){
- t = NodeManager::currentNM()->mkNode( ITE, exp_c, t[1], t[2] );
- Trace("csi-rcons-debug") << "Pre expand to " << t << std::endl;
- }
- t = flattenITEs( t, false );
- if( t!=prev_t ){
- Trace("csi-rcons-debug") << "Flatten ITE to " << t << std::endl;
- std::map< Node, bool > sassign;
- std::vector< Node > svars;
- std::vector< Node > ssubs;
- t = simplifySolutionNode( t, sassign, svars, ssubs, 0 );
- }
- Assert( t.getKind()==ITE );
- }
- }
- */
-
-
-Node CegConjectureSingleInvSol::CegConjectureSingleInvSol::getReconstructedSolution( int id, bool mod_eq ) {
- std::map< int, Node >::iterator it = d_reconstruct.find( id );
- if( it!=d_reconstruct.end() ){
- return it->second;
- }else{
- if( std::find( d_tmp_fail.begin(), d_tmp_fail.end(), id )!=d_tmp_fail.end() ){
- return Node::null();
- }else{
- // try each child option
- std::map< int, std::map< Node, std::vector< int > > >::iterator ito = d_reconstruct_op.find( id );
- if( ito!=d_reconstruct_op.end() ){
- for( std::map< Node, std::vector< int > >::iterator itt = ito->second.begin(); itt != ito->second.end(); ++itt ){
- std::vector< Node > children;
- children.push_back( itt->first );
- bool success = true;
- for( unsigned i=0; i<itt->second.size(); i++ ){
- Node nc = getReconstructedSolution( itt->second[i] );
- if( nc.isNull() ){
- success = false;
- break;
- }else{
- children.push_back( nc );
- }
- }
- if( success ){
- Node ret = NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, children );
- setReconstructed( id, ret );
- return ret;
- }
- }
- }
- // try terms in the equivalence class of this
- if( mod_eq ){
- int rid = d_rep[id];
- for( unsigned i=0; i<d_eqc[rid].size(); i++ ){
- int tid = d_eqc[rid][i];
- if( tid!=id ){
- Node eret = getReconstructedSolution( tid, false );
- if( !eret.isNull() ){
- setReconstructed( id, eret );
- return eret;
- }
- }
- }
- }
- d_tmp_fail.push_back( id );
- return Node::null();
- }
- }
-}
-
-int CegConjectureSingleInvSol::allocate( Node n, TypeNode stn ) {
- std::map< Node, int >::iterator it = d_rcons_to_id[stn].find( n );
- if( it==d_rcons_to_id[stn].end() ){
- int ret = d_id_count;
- if( Trace.isOn("csi-rcons-debug") ){
- const Datatype& dt = ((DatatypeType)(stn).toType()).getDatatype();
- Trace("csi-rcons-debug") << "id " << ret << " : " << n << " " << dt.getName() << std::endl;
- }
- d_id_node[d_id_count] = n;
- d_id_type[d_id_count] = stn;
- d_rep[d_id_count] = d_id_count;
- d_eqc[d_id_count].push_back( d_id_count );
- d_rcons_to_id[stn][n] = d_id_count;
- d_id_count++;
- return ret;
- }else{
- return it->second;
- }
-}
-
-bool CegConjectureSingleInvSol::getPathToRoot( int id ) {
- if( id==d_root_id ){
- return true;
- }else{
- std::map< int, Node >::iterator it = d_reconstruct.find( id );
- if( it!=d_reconstruct.end() ){
- return false;
- }else{
- int rid = d_rep[id];
- for( unsigned j=0; j<d_parents[rid].size(); j++ ){
- if( getPathToRoot( d_parents[rid][j] ) ){
- return true;
- }
- }
- return false;
- }
- }
-}
-
-void CegConjectureSingleInvSol::setReconstructed( int id, Node n ) {
- //set all equivalent to this as reconstructed
- int rid = d_rep[id];
- for( unsigned i=0; i<d_eqc[rid].size(); i++ ){
- d_reconstruct[d_eqc[rid][i]] = n;
- }
-}
-
-void CegConjectureSingleInvSol::getEquivalentTerms( Kind k, Node n, std::vector< Node >& equiv ) {
- if( k==AND || k==OR ){
- equiv.push_back( NodeManager::currentNM()->mkNode( k, n, n ) );
- equiv.push_back( NodeManager::currentNM()->mkNode( k, n, NodeManager::currentNM()->mkConst( k==AND ) ) );
- }
- //multiplication for integers
- //TODO for bitvectors
- Kind mk = ( k==PLUS || k==MINUS ) ? MULT : UNDEFINED_KIND;
- if( mk!=UNDEFINED_KIND ){
- if( n.getKind()==mk && n[0].isConst() && n[0].getType().isInteger() ){
- bool success = true;
- for( unsigned i=0; i<2; i++ ){
- Node eq;
- if( k==PLUS || k==MINUS ){
- Node oth = NodeManager::currentNM()->mkConst( Rational(i==0 ? 1000 : -1000) );
- eq = i==0 ? NodeManager::currentNM()->mkNode( LEQ, n[0], oth ) : NodeManager::currentNM()->mkNode( GEQ, n[0], oth );
- }
- if( !eq.isNull() ){
- eq = Rewriter::rewrite( eq );
- if( eq!=d_qe->getTermUtil()->d_true ){
- success = false;
- break;
- }
- }
- }
- if( success ){
- Node var = n[1];
- Node rem;
- if( k==PLUS || k==MINUS ){
- int rem_coeff = (int)n[0].getConst<Rational>().getNumerator().getSignedInt();
- if( rem_coeff>0 && k==PLUS ){
- rem_coeff--;
- }else if( rem_coeff<0 && k==MINUS ){
- rem_coeff++;
- }else{
- success = false;
- }
- if( success ){
- rem = NodeManager::currentNM()->mkNode( MULT, NodeManager::currentNM()->mkConst( Rational(rem_coeff) ), var );
- rem = Rewriter::rewrite( rem );
- }
- }
- if( !rem.isNull() ){
- equiv.push_back( NodeManager::currentNM()->mkNode( k, rem, var ) );
- }
- }
- }
- }
- //negative constants
- if( k==MINUS ){
- if( n.isConst() && n.getType().isInteger() && n.getConst<Rational>().getNumerator().strictlyNegative() ){
- Node nn = NodeManager::currentNM()->mkNode( UMINUS, n );
- nn = Rewriter::rewrite( nn );
- equiv.push_back( NodeManager::currentNM()->mkNode( MINUS, NodeManager::currentNM()->mkConst( Rational(0) ), nn ) );
- }
- }
- //inequalities
- if( k==GEQ || k==LEQ || k==LT || k==GT || k==NOT ){
- Node atom = n.getKind()==NOT ? n[0] : n;
- bool pol = n.getKind()!=NOT;
- Kind ak = atom.getKind();
- if( ( ak==GEQ || ak==LEQ || ak==LT || ak==GT ) && ( pol || k!=NOT ) ){
- Node t1 = atom[0];
- Node t2 = atom[1];
- if( !pol ){
- ak = ak==GEQ ? LT : ( ak==LEQ ? GT : ( ak==LT ? GEQ : LEQ ) );
- }
- if( k==NOT ){
- equiv.push_back( NodeManager::currentNM()->mkNode( ak==GEQ ? LT : ( ak==LEQ ? GT : ( ak==LT ? GEQ : LEQ ) ), t1, t2 ).negate() );
- }else if( k==ak ){
- equiv.push_back( NodeManager::currentNM()->mkNode( k, t1, t2 ) );
- }else if( (k==GEQ || k==LEQ)==(ak==GEQ || ak==LEQ) ){
- equiv.push_back( NodeManager::currentNM()->mkNode( k, t2, t1 ) );
- }else if( t1.getType().isInteger() && t2.getType().isInteger() ){
- if( (k==GEQ || k==GT)!=(ak==GEQ || ak==GT) ){
- Node ts = t1;
- t1 = t2;
- t2 = ts;
- ak = ak==GEQ ? LEQ : ( ak==LEQ ? GEQ : ( ak==LT ? GT : LT ) );
- }
- t2 = NodeManager::currentNM()->mkNode( PLUS, t2, NodeManager::currentNM()->mkConst( Rational( (ak==GT || ak==LEQ) ? 1 : -1 ) ) );
- t2 = Rewriter::rewrite( t2 );
- equiv.push_back( NodeManager::currentNM()->mkNode( k, t1, t2 ) );
- }
- }
- }
-
- //based on eqt cache
- std::map< Node, Node >::iterator itet = d_eqt_rep.find( n );
- if( itet!=d_eqt_rep.end() ){
- Node rn = itet->second;
- for( unsigned i=0; i<d_eqt_eqc[rn].size(); i++ ){
- if( d_eqt_eqc[rn][i]!=n && d_eqt_eqc[rn][i].getKind()==k ){
- if( std::find( equiv.begin(), equiv.end(), d_eqt_eqc[rn][i] )==equiv.end() ){
- equiv.push_back( d_eqt_eqc[rn][i] );
- }
- }
- }
- }
-}
-
-void CegConjectureSingleInvSol::registerEquivalentTerms( Node n ) {
- for( unsigned i=0; i<n.getNumChildren(); i++ ){
- registerEquivalentTerms( n[i] );
- }
- Node rn = Rewriter::rewrite( n );
- if( rn!=n ){
- Trace("csi-equiv") << " eq terms : " << n << " " << rn << std::endl;
- d_eqt_rep[n] = rn;
- d_eqt_rep[rn] = rn;
- if( std::find( d_eqt_eqc[rn].begin(), d_eqt_eqc[rn].end(), rn )==d_eqt_eqc[rn].end() ){
- d_eqt_eqc[rn].push_back( rn );
- }
- if( std::find( d_eqt_eqc[rn].begin(), d_eqt_eqc[rn].end(), n )==d_eqt_eqc[rn].end() ){
- d_eqt_eqc[rn].push_back( n );
- }
- }
-}
-
-Node CegConjectureSingleInvSol::builtinToSygusConst(Node c,
- TypeNode tn,
- int rcons_depth)
-{
- std::map<Node, Node>::iterator it = d_builtin_const_to_sygus[tn].find(c);
- if (it != d_builtin_const_to_sygus[tn].end())
- {
- return it->second;
- }
- TermDbSygus* tds = d_qe->getTermDatabaseSygus();
- NodeManager* nm = NodeManager::currentNM();
- Node sc;
- d_builtin_const_to_sygus[tn][c] = sc;
- Assert(c.isConst());
- Assert(tn.isDatatype());
- const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
- Trace("csi-rcons-debug") << "Try to reconstruct " << c << " in "
- << dt.getName() << std::endl;
- Assert(dt.isSygus());
- // if we are not interested in reconstructing constants, or the grammar allows
- // them, return a proxy
- if (!options::cegqiSingleInvReconstructConst() || dt.getSygusAllowConst())
- {
- Node k = nm->mkSkolem("sy", tn, "sygus proxy");
- SygusPrintProxyAttribute spa;
- k.setAttribute(spa, c);
- sc = k;
- }
- else
- {
- int carg = tds->getOpConsNum(tn, c);
- if (carg != -1)
- {
- sc = nm->mkNode(APPLY_CONSTRUCTOR,
- Node::fromExpr(dt[carg].getConstructor()));
- }
- else
- {
- // identity functions
- for (unsigned ii : d_id_funcs[tn])
- {
- Assert(dt[ii].getNumArgs() == 1);
- // try to directly reconstruct from single argument
- TypeNode tnc = tds->getArgType(dt[ii], 0);
- Trace("csi-rcons-debug")
- << "Based on id function " << dt[ii].getSygusOp()
- << ", try reconstructing " << c << " instead in " << tnc
- << std::endl;
- Node n = builtinToSygusConst(c, tnc, rcons_depth);
- if (!n.isNull())
- {
- sc = nm->mkNode(
- APPLY_CONSTRUCTOR, Node::fromExpr(dt[ii].getConstructor()), n);
- break;
- }
- }
- if (sc.isNull())
- {
- if (rcons_depth < 1000)
- {
- // accelerated, recursive reconstruction of constants
- Kind pk = tds->getPlusKind(TypeNode::fromType(dt.getSygusType()));
- if (pk != UNDEFINED_KIND)
- {
- int arg = tds->getKindConsNum(tn, pk);
- if (arg != -1)
- {
- Kind ck =
- tds->getComparisonKind(TypeNode::fromType(dt.getSygusType()));
- Kind pkm =
- tds->getPlusKind(TypeNode::fromType(dt.getSygusType()), true);
- // get types
- Assert(dt[arg].getNumArgs() == 2);
- TypeNode tn1 = tds->getArgType(dt[arg], 0);
- TypeNode tn2 = tds->getArgType(dt[arg], 1);
- // initialize d_const_list for tn1
- registerType(tn1);
- // iterate over all positive constants, largest to smallest
- int start = d_const_list[tn1].size() - 1;
- int end = d_const_list[tn1].size() - d_const_list_pos[tn1];
- for (int i = start; i >= end; --i)
- {
- Node c1 = d_const_list[tn1][i];
- // only consider if smaller than c, and
- if (doCompare(c1, c, ck))
- {
- Node c2 = nm->mkNode(pkm, c, c1);
- c2 = Rewriter::rewrite(c2);
- if (c2.isConst())
- {
- // reconstruct constant on the other side
- Node sc2 = builtinToSygusConst(c2, tn2, rcons_depth + 1);
- if (!sc2.isNull())
- {
- Node sc1 = builtinToSygusConst(c1, tn1, rcons_depth);
- Assert(!sc1.isNull());
- sc = nm->mkNode(APPLY_CONSTRUCTOR,
- Node::fromExpr(dt[arg].getConstructor()),
- sc1,
- sc2);
- break;
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- d_builtin_const_to_sygus[tn][c] = sc;
- return sc;
-}
-
-struct sortConstants
-{
- Kind d_comp_kind;
- bool operator()(Node i, Node j)
- {
- return i != j && doCompare(i, j, d_comp_kind);
- }
-};
-
-void CegConjectureSingleInvSol::registerType(TypeNode tn)
-{
- if (d_const_list_pos.find(tn) != d_const_list_pos.end())
- {
- return;
- }
- d_const_list_pos[tn] = 0;
- Assert(tn.isDatatype());
-
- TermDbSygus* tds = d_qe->getTermDatabaseSygus();
- // ensure it is registered
- tds->registerSygusType(tn);
- const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
- TypeNode btn = TypeNode::fromType(dt.getSygusType());
- // for constant reconstruction
- Kind ck = tds->getComparisonKind(btn);
- Node z = d_qe->getTermUtil()->getTypeValue(btn, 0);
-
- // iterate over constructors
- for (unsigned i = 0, ncons = dt.getNumConstructors(); i < ncons; i++)
- {
- Node n = Node::fromExpr(dt[i].getSygusOp());
- if (n.getKind() != kind::BUILTIN && n.isConst())
- {
- d_const_list[tn].push_back(n);
- if (ck != UNDEFINED_KIND && doCompare(z, n, ck))
- {
- d_const_list_pos[tn]++;
- }
- }
- if (dt[i].isSygusIdFunc())
- {
- d_id_funcs[tn].push_back(i);
- }
- }
- // sort the constant list
- if (!d_const_list[tn].empty())
- {
- if (ck != UNDEFINED_KIND)
- {
- sortConstants sc;
- sc.d_comp_kind = ck;
- std::sort(d_const_list[tn].begin(), d_const_list[tn].end(), sc);
- }
- Trace("csi-rcons") << "Type has " << d_const_list[tn].size()
- << " constants..." << std::endl
- << " ";
- for (unsigned i = 0; i < d_const_list[tn].size(); i++)
- {
- Trace("csi-rcons") << d_const_list[tn][i] << " ";
- }
- Trace("csi-rcons") << std::endl;
- Trace("csi-rcons") << "Of these, " << d_const_list_pos[tn]
- << " are marked as positive." << std::endl;
- }
-}
-
-bool CegConjectureSingleInvSol::getMatch(Node p,
- Node n,
- std::map<int, Node>& s,
- std::vector<int>& new_s)
-{
- TermDbSygus* tds = d_qe->getTermDatabaseSygus();
- if (tds->isFreeVar(p))
- {
- unsigned vnum = tds->getVarNum(p);
- Node prev = s[vnum];
- s[vnum] = n;
- if (prev.isNull())
- {
- new_s.push_back(vnum);
- }
- return prev.isNull() || prev == n;
- }
- if (n.getNumChildren() == 0)
- {
- return p == n;
- }
- if (n.getKind() == p.getKind() && n.getNumChildren() == p.getNumChildren())
- {
- // try both ways?
- unsigned rmax =
- TermUtil::isComm(n.getKind()) && n.getNumChildren() == 2 ? 2 : 1;
- std::vector<int> new_tmp;
- for (unsigned r = 0; r < rmax; r++)
- {
- bool success = true;
- for (unsigned i = 0, size = n.getNumChildren(); i < size; i++)
- {
- int io = r == 0 ? i : (i == 0 ? 1 : 0);
- if (!getMatch(p[i], n[io], s, new_tmp))
- {
- success = false;
- for (unsigned j = 0; j < new_tmp.size(); j++)
- {
- s.erase(new_tmp[j]);
- }
- new_tmp.clear();
- break;
- }
- }
- if (success)
- {
- new_s.insert(new_s.end(), new_tmp.begin(), new_tmp.end());
- return true;
- }
- }
- }
- return false;
-}
-
-bool CegConjectureSingleInvSol::getMatch(Node t,
- TypeNode st,
- int& index_found,
- std::vector<Node>& args,
- int index_exc,
- int index_start)
-{
- Assert(st.isDatatype());
- const Datatype& dt = static_cast<DatatypeType>(st.toType()).getDatatype();
- Assert(dt.isSygus());
- std::map<Kind, std::vector<Node> > kgens;
- std::vector<Node> gens;
- for (unsigned i = index_start, ncons = dt.getNumConstructors(); i < ncons;
- i++)
- {
- if ((int)i != index_exc)
- {
- Node g = getGenericBase(st, dt, i);
- gens.push_back(g);
- kgens[g.getKind()].push_back(g);
- Trace("csi-sol-debug") << "Check generic base : " << g << " from "
- << dt[i].getName() << std::endl;
- if (g.getKind() == t.getKind())
- {
- Trace("csi-sol-debug") << "Possible match ? " << g << " " << t
- << " for " << dt[i].getName() << std::endl;
- std::map<int, Node> sigma;
- std::vector<int> new_s;
- if (getMatch(g, t, sigma, new_s))
- {
- // we found an exact match
- bool msuccess = true;
- for (unsigned j = 0, nargs = dt[i].getNumArgs(); j < nargs; j++)
- {
- if (sigma[j].isNull())
- {
- msuccess = false;
- break;
- }
- else
- {
- args.push_back(sigma[j]);
- }
- }
- if (msuccess)
- {
- index_found = i;
- return true;
- }
- }
- }
- }
- }
- return false;
-}
-
-Node CegConjectureSingleInvSol::getGenericBase(TypeNode tn,
- const Datatype& dt,
- int c)
-{
- std::map<int, Node>::iterator it = d_generic_base[tn].find(c);
- if (it != d_generic_base[tn].end())
- {
- return it->second;
- }
- TermDbSygus* tds = d_qe->getTermDatabaseSygus();
- Assert(tds->isRegistered(tn));
- std::map<TypeNode, int> var_count;
- std::map<int, Node> pre;
- Node g = tds->mkGeneric(dt, c, var_count, pre);
- Trace("csi-sol-debug") << "Generic is " << g << std::endl;
- Node gr = Rewriter::rewrite(g);
- Trace("csi-sol-debug") << "Generic rewritten is " << gr << std::endl;
- d_generic_base[tn][c] = gr;
- return gr;
-}
-}
-}
-}
+++ /dev/null
-/********************* */
-/*! \file ce_guided_single_inv_sol.h
- ** \verbatim
- ** Top contributors (to current version):
- ** Andrew Reynolds, Paul Meng
- ** This file is part of the CVC4 project.
- ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
- ** in the top-level source directory) and their institutional affiliations.
- ** All rights reserved. See the file COPYING in the top-level source
- ** directory for licensing information.\endverbatim
- **
- ** \brief utility for reconstructing solutions for single invocation synthesis conjectures
- **/
-
-#include "cvc4_private.h"
-
-#ifndef __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_SINGLE_INV_SOL_H
-#define __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_SINGLE_INV_SOL_H
-
-#include "context/cdhashmap.h"
-#include "theory/quantifiers_engine.h"
-
-namespace CVC4 {
-namespace theory {
-namespace quantifiers {
-
-
-class CegConjectureSingleInv;
-
-/** CegConjectureSingleInvSol
- *
- * This function implements Figure 5 of "Counterexample-Guided Quantifier
- * Instantiation for Synthesis in SMT", Reynolds et al CAV 2015.
- *
- */
-class CegConjectureSingleInvSol
-{
- friend class CegConjectureSingleInv;
-private:
- QuantifiersEngine * d_qe;
- std::vector< Node > d_varList;
- std::map< Node, int > d_dterm_size;
- std::map< Node, int > d_dterm_ite_size;
-//solution simplification
-private:
- bool debugSolution( Node sol );
- void debugTermSize( Node sol, int& t_size, int& num_ite );
- Node pullITEs( Node n );
- bool pullITECondition( Node root, Node n, std::vector< Node >& conj, Node& t, Node& rem, int depth );
- Node flattenITEs( Node n, bool rec = true );
- bool getAssign( bool pol, Node n, std::map< Node, bool >& assign, std::vector< Node >& new_assign,
- std::vector< Node >& vars, std::vector< Node >& new_vars, std::vector< Node >& new_subs );
- bool getAssignEquality( Node eq, std::vector< Node >& vars, std::vector< Node >& new_vars, std::vector< Node >& new_subs );
- Node simplifySolutionNode( Node sol, TypeNode stn, std::map< Node, bool >& assign,
- std::vector< Node >& vars, std::vector< Node >& subs, int status );
-
- public:
- CegConjectureSingleInvSol(QuantifiersEngine* qe);
- /** simplify solution
- *
- * Returns the simplified version of node sol whose syntax is restricted by
- * the grammar corresponding to sygus datatype stn.
- */
- Node simplifySolution( Node sol, TypeNode stn );
- /** reconstruct solution
- *
- * Returns (if possible) a node that is equivalent to sol those syntax
- * matches the grammar corresponding to sygus datatype stn.
- * The value reconstructed is set to 1 if we successfully return a node,
- * otherwise it is set to -1.
- */
- Node reconstructSolution(Node sol, TypeNode stn, int& reconstructed);
- /** preregister conjecture
- *
- * q : the synthesis conjecture this class is for.
- * This is used as a heuristic to find terms in the original conjecture which
- * may be helpful for using during reconstruction.
- */
- void preregisterConjecture(Node q);
-
- private:
- int d_id_count;
- int d_root_id;
- std::map< int, Node > d_id_node;
- std::map< int, TypeNode > d_id_type;
- std::map< TypeNode, std::map< Node, int > > d_rcons_to_id;
- std::map< TypeNode, std::map< Node, int > > d_rcons_to_status;
-
- std::map< int, std::map< Node, std::vector< int > > > d_reconstruct_op;
- std::map< int, Node > d_reconstruct;
- std::map< int, std::vector< int > > d_parents;
-
- std::map< int, std::vector< int > > d_eqc;
- std::map< int, int > d_rep;
-
- //equivalent terms
- std::map< Node, Node > d_eqt_rep;
- std::map< Node, std::vector< Node > > d_eqt_eqc;
-
- //cache when reconstructing solutions
- std::vector< int > d_tmp_fail;
- // get reconstructed solution
- Node getReconstructedSolution( int id, bool mod_eq = true );
-
- // allocate node with type
- int allocate( Node n, TypeNode stn );
- // term t with sygus type st, returns inducted templated form of t
- int collectReconstructNodes( Node t, TypeNode stn, int& status );
- bool collectReconstructNodes( int pid, std::vector< Node >& ts, const DatatypeConstructor& dtc, std::vector< int >& ids, int& status );
- bool getPathToRoot( int id );
- void setReconstructed( int id, Node n );
- //get equivalent terms to n with top symbol k
- void getEquivalentTerms( Kind k, Node n, std::vector< Node >& equiv );
- //register equivalent terms
- void registerEquivalentTerms( Node n );
- /** builtin to sygus const
- *
- * Returns a sygus term of type tn that encodes the builtin constant c.
- * If the sygus datatype tn allows any constant, this may return a variable
- * with the attribute SygusPrintProxyAttribute that associates it with c.
- *
- * rcons_depth limits the number of recursive calls when doing accelerated
- * constant reconstruction (currently limited to 1000). Notice this is hacky:
- * depending upon order of calls, constant rcons may succeed, e.g. 1001, 999
- * vs. 999, 1001.
- */
- Node builtinToSygusConst(Node c, TypeNode tn, int rcons_depth = 0);
- /** cache for the above function */
- std::map<TypeNode, std::map<Node, Node> > d_builtin_const_to_sygus;
- /** sorted list of constants, per type */
- std::map<TypeNode, std::vector<Node> > d_const_list;
- /** number of positive constants, per type */
- std::map<TypeNode, unsigned> d_const_list_pos;
- /** list of constructor indices whose operators are identity functions */
- std::map<TypeNode, std::vector<int> > d_id_funcs;
- /** initialize the above information for sygus type tn */
- void registerType(TypeNode tn);
- /** get generic base
- *
- * This returns the builtin term that is the analog of an application of the
- * c^th constructor of dt to fresh variables.
- */
- Node getGenericBase(TypeNode tn, const Datatype& dt, int c);
- /** cache for the above function */
- std::map<TypeNode, std::map<int, Node> > d_generic_base;
- /** get match
- *
- * This function attempts to find a substitution for which p = n. If
- * successful, this function returns a substitution in the form of s/new_s,
- * where:
- * s : substitution, where the domain are indices of terms in the sygus
- * term database, and
- * new_s : the members that were added to s on this call.
- * Otherwise, this function returns false and s and new_s are unmodified.
- */
- bool getMatch(Node p,
- Node n,
- std::map<int, Node>& s,
- std::vector<int>& new_s);
- /** get match
- *
- * This function attempts to find a builtin term that is analog to a value
- * of the sygus datatype st that is equivalent to n. If this function returns
- * true, then it has found such a term. Then we set:
- * index_found : updated to the constructor index of the sygus term whose
- * analog to equivalent to n.
- * args : builtin terms corresponding to the match, in order.
- * Otherwise, this function returns false and index_found and args are
- * unmodified.
- * For example, for grammar:
- * A -> 0 | 1 | x | +( A, A )
- * Given input ( 5 + (x+1) ) and A we would return true, where:
- * index_found is set to 3 and args is set to { 5, x+1 }.
- *
- * index_exc : (if applicable) exclude a constructor index of st
- * index_start : start index of constructors of st to try
- */
- bool getMatch(Node n,
- TypeNode st,
- int& index_found,
- std::vector<Node>& args,
- int index_exc = -1,
- int index_start = 0);
-};
-
-
-}
-}
-}
-
-#endif
+++ /dev/null
-/********************* */
-/*! \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<Node, Instantiator*> inst : d_instantiator)
- {
- delete inst.second;
- }
- for (std::pair<TheoryId, InstantiatorPreprocess*> 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<n.getNumChildren(); i++ ){
- computeProgVars( n[i] );
- if( d_inelig.find( n[i] )!=d_inelig.end() ){
- d_inelig.insert(n);
- }
- // all variables in child are contained in this
- d_prog_var[n].insert(d_prog_var[n[i]].begin(), d_prog_var[n[i]].end());
- }
- // selectors applied to program variables are also variables
- if (n.getKind() == APPLY_SELECTOR_TOTAL
- && d_prog_var[n].find(n[0]) != d_prog_var[n].end())
- {
- d_prog_var[n].insert(n);
- }
- if (n.getKind() == kind::CHOICE)
- {
- d_prog_var.erase(n[0][0]);
- }
- }
-}
-
-bool CegInstantiator::isEligible( Node n ) {
- //compute d_subs_fv, which program variables are contained in n, and determines if eligible
- computeProgVars( n );
- return d_inelig.find( n )==d_inelig.end();
-}
-
-bool CegInstantiator::hasVariable( Node n, Node pv ) {
- computeProgVars( n );
- return d_prog_var[n].find( pv )!=d_prog_var[n].end();
-}
-
-void CegInstantiator::activateInstantiationVariable(Node v, unsigned index)
-{
- if( d_instantiator.find( v )==d_instantiator.end() ){
- TypeNode tn = v.getType();
- Instantiator * vinst;
- if( tn.isReal() ){
- vinst = new ArithInstantiator( d_qe, tn );
- }else if( tn.isSort() ){
- Assert( options::quantEpr() );
- vinst = new EprInstantiator( d_qe, tn );
- }else if( tn.isDatatype() ){
- vinst = new DtInstantiator( d_qe, tn );
- }else if( tn.isBitVector() ){
- vinst = new BvInstantiator( d_qe, tn );
- }else if( tn.isBoolean() ){
- vinst = new ModelValueInstantiator( d_qe, tn );
- }else{
- //default
- vinst = new Instantiator( d_qe, tn );
- }
- d_instantiator[v] = vinst;
- }
- d_curr_subs_proc[v].clear();
- d_curr_index[v] = index;
- d_curr_iphase[v] = CEG_INST_PHASE_NONE;
-}
-
-void CegInstantiator::registerTheoryIds(TypeNode tn,
- std::map<TypeNode, bool>& 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<TypeNode, bool> 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; k<it_eqc->second.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<d_curr_type_eqc[pvtnb].size(); k++ ){
- Node r = d_curr_type_eqc[pvtnb][k];
- std::map< Node, std::vector< Node > >::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; kk<it_reqc->second.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; j<lhs.size(); j++ ){
- //if this term or the another has pv in it, try to solve for it
- if( hasVar || lhs_v[j] ){
- Trace("cbqi-inst-debug") << "... " << i << "...try based on equality " << lhs[j] << " = " << ns << std::endl;
- term_props.push_back( lhs_prop[j] );
- terms.push_back( lhs[j] );
- if (vinst->processEquality(
- this, sf, pv, term_props, terms, d_effort))
- {
- return true;
- }
- term_props.pop_back();
- terms.pop_back();
- }
- }
- lhs.push_back( ns );
- lhs_v.push_back( hasVar );
- lhs_prop.push_back( pv_prop );
- }else{
- Trace("cbqi-inst-debug2") << "... term " << n << " is ineligible after substitution." << std::endl;
- }
- }else{
- Trace("cbqi-inst-debug2") << "... term " << n << " is ineligible." << std::endl;
- }
- }
- }
- }
-
- //[3] directly look at assertions
- if (vinst->hasProcessAssertion(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; j<ita->second.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<sf.d_subs.size(); j++ ){
- Trace("cbqi-inst-debug") << " ";
- }
- Trace("cbqi-inst-debug") << sf.d_subs.size() << ": (" << d_curr_iphase[pv]
- << ") ";
- Node mod_pv = pv_prop.getModifiedTerm( pv );
- Trace("cbqi-inst-debug") << mod_pv << " -> " << 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<sf.d_subs.size(); j++ ){
- Trace("cbqi-inst-debug2") << " Apply for " << sf.d_subs[j] << std::endl;
- Assert( d_prog_var.find( sf.d_subs[j] )!=d_prog_var.end() );
- if( d_prog_var[sf.d_subs[j]].find( pv )!=d_prog_var[sf.d_subs[j]].end() ){
- prev_subs[j] = sf.d_subs[j];
- TNode tv = pv;
- TNode ts = n;
- TermProperties a_pv_prop;
- Node new_subs = applySubstitution( sf.d_vars[j].getType(), sf.d_subs[j], a_var, a_subs, a_prop, a_non_basic, a_pv_prop, true );
- if( !new_subs.isNull() ){
- sf.d_subs[j] = new_subs;
- // the substitution apply to this term resulted in a non-basic substitution relationship
- if( !a_pv_prop.isBasic() ){
- // we are processing:
- // sf.d_props[j].getModifiedTerm( sf.d_vars[j] ) = sf.d_subs[j] { pv_prop.getModifiedTerm( pv ) -> 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<new_non_basic.size(); i++ ){
- sf.d_non_basic.pop_back();
- }
- return success;
- }
- }else{
- //already tried this substitution
- return false;
- }
-}
-
-bool CegInstantiator::doAddInstantiation( std::vector< Node >& 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<subs.size(); i++ ){
- subs_map[vars[i]] = subs[i];
- }
- subs.clear();
- for (unsigned i = 0, size = d_input_vars.size(); i < size; ++i)
- {
- std::map<Node, Node>::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; i<lemmas.size(); i++ ){
- d_out->addLemma( 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<Node, NodeHashFunction>::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.size(); i++ ){
- Trace("cegqi-si-apply-subs-debug") << " " << vars[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; i<vars.size(); i++ ){
- if( !prop[i].d_coeff.isNull() ){
- Assert( vars[i].getType().isInteger() );
- Assert( prop[i].d_coeff.isConst() );
- Node nn = NodeManager::currentNM()->mkNode( MULT, subs[i], NodeManager::currentNM()->mkConst( Rational(1)/prop[i].d_coeff.getConst<Rational>() ) );
- 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<Rational>() / msum_coeff[it->first].getConst<Rational>() ) );
- }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<n.getNumChildren(); i++ ){
- collectPresolveEqTerms( n[i], teq );
- }
- }
-}
-
-void getPresolveEqConjuncts( std::vector< Node >& 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<teq[v].size(); j++ ){
- terms[i] = teq[v][j];
- getPresolveEqConjuncts( vars, terms, teq, f, conj );
- }
- terms.pop_back();
- }
- }
-}
-
-void CegInstantiator::presolve( Node q ) {
- //at preregister time, add proxy of obvious instantiations up front, which helps learning during preprocessing
- //only if no nested quantifiers
- if( !QuantifiersRewriter::containsQuantifiers( q[1] ) ){
- std::vector< Node > ps_vars;
- std::map< Node, std::vector< Node > > teq;
- for( unsigned i=0; i<q[0].getNumChildren(); i++ ){
- ps_vars.push_back( q[0][i] );
- teq[q[0][i]].clear();
- }
- collectPresolveEqTerms( q[1], teq );
- std::vector< Node > 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; i<d_vars.size(); i++ ){
- Node pv = d_vars[i];
- TypeNode pvtn = pv.getType();
- Trace("cbqi-proc-debug") << "Collect theory ids from type " << pvtn << " of " << pv << std::endl;
- //collect information about eqc
- if( ee->hasTerm( 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; i<d_tids.size(); i++ ){
- TheoryId tid = d_tids[i];
- Theory* theory = d_qe->getTheoryEngine()->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<Assertion>::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<d_aux_vars.size(); i++ ){
- Node r = d_aux_vars[i];
- std::map< Node, Node >::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_lhs.size(); i++ ){
- Trace("cbqi-proc") << " " << subs_lhs[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; i<it->second.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; i<it->second.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; i<it->second.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; i<it->second.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; i<subs_lhs.size(); i++ ){
- Node nr = subs_rhs[i].substitute( cl.begin(), cl.end(), cr.begin(), cr.end() );
- nr = Rewriter::rewrite( nr );
- subs_rhs[i] = nr;
- }
-
- subs_lhs.push_back( l );
- subs_rhs.push_back( r );
-}
-
-Node CegInstantiator::getModelValue( Node n ) {
- return d_qe->getModel()->getValue( n );
-}
-
-Node CegInstantiator::getBoundVariable(TypeNode tn)
-{
- unsigned index = 0;
- std::unordered_map<TypeNode, unsigned, TypeNodeHashFunction>::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<n.getNumChildren(); i++ ){
- collectCeAtoms( n[i], visited );
- }
- }else{
- if( std::find( d_ce_atoms.begin(), d_ce_atoms.end(), n )==d_ce_atoms.end() ){
- Trace("cbqi-ce-atoms") << "CE atoms : " << n << std::endl;
- d_ce_atoms.push_back( n );
- }
- }
- }
-}
-
-void CegInstantiator::registerCounterexampleLemma( std::vector< Node >& 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<Node> pvars;
- pvars.insert(pvars.end(), d_vars.begin(), d_vars.end());
- for (std::pair<const TheoryId, InstantiatorPreprocess*>& 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<lems.size(); i++ ){
- Trace("cbqi-debug") << "Counterexample lemma (pre-rewrite) " << i << " : " << lems[i] << std::endl;
- Node rlem = lems[i];
- rlem = Rewriter::rewrite( rlem );
- Trace("cbqi-debug") << "Counterexample lemma (post-rewrite) " << i << " : " << rlem << std::endl;
- //record the literals that imply auxiliary variables to be equal to terms
- if( lems[i].getKind()==ITE && rlem.getKind()==ITE ){
- if( lems[i][1].getKind()==EQUAL && lems[i][2].getKind()==EQUAL && lems[i][1][0]==lems[i][2][0] ){
- if( std::find( d_aux_vars.begin(), d_aux_vars.end(), lems[i][1][0] )!=d_aux_vars.end() ){
- Node v = lems[i][1][0];
- for( unsigned r=1; r<=2; r++ ){
- d_aux_eq[rlem[r]][v] = lems[i][r][1];
- Trace("cbqi-debug") << " " << rlem[r] << " implies " << v << " = " << lems[i][r][1] << std::endl;
- }
- }
- }
- }
- /*else if( lems[i].getKind()==EQUAL && lems[i][0].getType().isBoolean() ){
- //Boolean terms
- if( std::find( d_aux_vars.begin(), d_aux_vars.end(), lems[i][0] )!=d_aux_vars.end() ){
- Node v = lems[i][0];
- d_aux_eq[rlem][v] = lems[i][1];
- Trace("cbqi-debug") << " " << rlem << " implies " << v << " = " << lems[i][1] << std::endl;
- }
- }*/
- lems[i] = rlem;
- }
-
- // determine variable order: must do Reals before Ints
- Trace("cbqi-debug") << "Determine variable order..." << std::endl;
- if (!d_vars.empty())
- {
- std::map<Node, unsigned> voo;
- bool doSort = false;
- std::vector<Node> vars;
- std::map<TypeNode, std::vector<Node> > 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<const TypeNode, std::vector<Node> >& 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; i<lems.size(); i++ ){
- collectCeAtoms( lems[i], visited );
- }
-}
-
-
-Instantiator::Instantiator( QuantifiersEngine * qe, TypeNode tn ) : d_type( tn ){
- d_closed_enum_type = qe->getTermEnumeration()->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 */
+++ /dev/null
-/********************* */
-/*! \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<Node> 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<Node>& lems,
- std::vector<Node>& 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<Node,
- std::unordered_set<Node, NodeHashFunction>,
- NodeHashFunction>
- d_prog_var;
- /** cache of the set of terms that we have established are
- * ineligible for instantiation.
- */
- std::unordered_set<Node, NodeHashFunction> 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<TheoryId, std::vector<Node> > d_curr_asserts;
- /** map from representatives to the terms in their equivalence class */
- std::map<Node, std::vector<Node> > d_curr_eqc;
- /** map from types to representatives of that type */
- std::map<TypeNode, std::vector<Node> > d_curr_type_eqc;
- /** solved asserts */
- std::unordered_set<Node, NodeHashFunction> 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<Node>& subs_lhs,
- std::vector<Node>& subs_rhs,
- Node l,
- Node r);
- /** cache bound variables for type returned
- * by getBoundVariable(...).
- */
- std::unordered_map<TypeNode, std::vector<Node>, 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<TypeNode, unsigned, TypeNodeHashFunction>
- 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<TheoryId> d_tids;
- /** map from theory ids to instantiator preprocessors */
- std::map<TheoryId, InstantiatorPreprocess*> 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<TypeNode, bool>& 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<Node> d_vars;
- /** set form of d_vars */
- std::unordered_set<Node, NodeHashFunction> d_vars_set;
- /** index of variables reported in instantiation */
- std::vector<unsigned> 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<Node> 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<Node, std::map<Node, Node> > 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<Node> 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<Node> d_ce_atoms;
- /** collect atoms */
- void collectCeAtoms(Node n, std::map<Node, bool>& 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<Node, Instantiator*> d_active_instantiators;
- /** map from variables to the index in the prefix of the quantified
- * formula we are processing.
- */
- std::map<Node, unsigned> d_curr_index;
- /** map from variables to the phase in which we instantiated them */
- std::map<Node, CegInstPhase> d_curr_iphase;
- /** cache of current substitutions tried between activate/deactivate */
- std::map<Node, std::map<Node, std::map<Node, bool> > > d_curr_subs_proc;
- /** stack of temporary variables we are solving for,
- * e.g. subfields of datatypes.
- */
- std::vector<Node> 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<Node, Instantiator*> 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<Node>& vars,
- std::vector<Node>& subs,
- std::vector<Node>& 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<Node>& 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<TermProperties>& term_props,
- std::vector<Node>& 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<Node>& 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<Node>& lems,
- std::vector<Node>& ce_vars)
- {
- }
-};
-
-} /* CVC4::theory::quantifiers namespace */
-} /* CVC4::theory namespace */
-} /* CVC4 namespace */
-
-#endif
+++ /dev/null
-/********************* */
-/*! \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 <algorithm>
-#include <stack>
-
-using namespace std;
-using namespace CVC4::kind;
-using namespace CVC4::context;
-
-namespace CVC4 {
-namespace theory {
-namespace quantifiers {
-
-struct BvLinearAttributeId {};
-using BvLinearAttribute = expr::Attribute<BvLinearAttributeId, bool>;
-
-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<Rational>() ), vts_coeff[t] );
- vts_coeff[t] = Rewriter::rewrite( vts_coeff[t] );
- }else if( itv->second.getConst<Rational>().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<Rational>().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<Rational>().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<Rational>().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<TermProperties>& term_props,
- std::vector<Node>& 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; r<rmax; r++ ){
- int uires = ires;
- Node uval = val;
- if( atom.getKind()==GEQ ){
- //push negation downwards
- if( !pol ){
- uires = -ires;
- if( d_type.isInteger() ){
- uval = NodeManager::currentNM()->mkNode( 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<Rational>().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()<d_mbp_bounds[0].size();
- }
- int best_used[2];
- std::vector< Node > 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; j<d_mbp_bounds[rr].size(); j++ ){
- Node value[3];
- if( Trace.isOn("cegqi-arith-bound") ){
- Assert( !d_mbp_bounds[rr][j].isNull() );
- Trace("cegqi-arith-bound") << " " << j << ": " << d_mbp_bounds[rr][j];
- if( !d_mbp_vts_coeff[rr][0][j].isNull() ){
- Trace("cegqi-arith-bound") << " (+ " << d_mbp_vts_coeff[rr][0][j] << " * INF)";
- }
- if( !d_mbp_vts_coeff[rr][1][j].isNull() ){
- Trace("cegqi-arith-bound") << " (+ " << d_mbp_vts_coeff[rr][1][j] << " * DELTA)";
- }
- if( !d_mbp_coeff[rr][j].isNull() ){
- Trace("cegqi-arith-bound") << " (div " << d_mbp_coeff[rr][j] << ")";
- }
- Trace("cegqi-arith-bound") << ", value = ";
- }
- t_values[rr].push_back( Node::null() );
- //check if it is better than the current best bound : lexicographic order infinite/finite/infinitesimal parts
- bool new_best = true;
- for( unsigned t=0; t<3; t++ ){
- //get the value
- if( t==0 ){
- value[0] = d_mbp_vts_coeff[rr][0][j];
- if( !value[0].isNull() ){
- Trace("cegqi-arith-bound") << "( " << value[0] << " * INF ) + ";
- }else{
- value[0] = zero;
- }
- }else if( t==1 ){
- Node t_value = ci->getModelValue( 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<Rational>() ), 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; j<d_mbp_bounds[rr].size(); j++ ){
- if( (int)j!=best_used[rr] && ( !options::cbqiMidpoint() || d_mbp_vts_coeff[rr][1][j].isNull() ) ){
- Node val = getModelBasedProjectionValue( ci, pv, d_mbp_bounds[rr][j], rr==0, d_mbp_coeff[rr][j], pv_value, t_values[rr][j], sf.getTheta(),
- d_mbp_vts_coeff[rr][0][j], d_mbp_vts_coeff[rr][1][j] );
- if( !val.isNull() ){
- TermProperties pv_prop_nopt_bound;
- pv_prop_nopt_bound.d_coeff = d_mbp_coeff[rr][j];
- pv_prop_nopt_bound.d_type = rr==0 ? 1 : -1;
- if (ci->constructInstantiationInc(
- 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<Node>& 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; i<a.getNumChildren(); i++ ){
- Node s = solve_dt( v, a[i], b[i], sa[i], sb[i] );
- if( !s.isNull() ){
- return s;
- }
- }
- }
- }else{
- unsigned cindex = Datatype::indexOf( a.getOperator().toExpr() );
- TypeNode tn = a.getType();
- const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
- for( unsigned i=0; i<a.getNumChildren(); i++ ){
- Node nn = NodeManager::currentNM()->mkNode( APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[cindex].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<Node>& 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<eqc.size(); k++ ){
- Node n = eqc[k];
- if( n.getKind()==APPLY_CONSTRUCTOR ){
- Trace("cegqi-dt-debug") << "...try based on constructor term " << n << std::endl;
- std::vector< Node > 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; j<dt[cindex].getNumArgs(); j++ ){
- Node c = NodeManager::currentNM()->mkNode( APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[cindex].getSelectorInternal( d_type.toType(), j ) ), pv );
- ci->pushStackVariable( c );
- children.push_back( c );
- }
- 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; j<dt[cindex].getNumArgs(); j++ ){
- ci->popStackVariable();
- }
- break;
- }
- }
- }
- return false;
-}
-
-bool DtInstantiator::processEquality(CegInstantiator* ci,
- SolvedForm& sf,
- Node pv,
- std::vector<TermProperties>& term_props,
- std::vector<Node>& 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<catom.getNumChildren(); i++ ){
- if( catom[i]==pv ){
- Trace("cegqi-epr") << "...increment " << gcatom[i] << std::endl;
- match_score[gcatom[i]]++;
- }else{
- //recursive matching
- computeMatchScore( ci, pv, catom[i], gcatom[i], match_score );
- }
- }
- }else{
- std::map< TNode, TermArgTrie >::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; j<catom.getNumChildren(); j++ ){
- arg_reps.push_back( ci->getQuantifiersEngine()->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<j );
- }
-};
-
-bool EprInstantiator::processEqualTerms(CegInstantiator* ci,
- SolvedForm& sf,
- Node pv,
- std::vector<Node>& eqc,
- CegInstEffort effort)
-{
- if( options::quantEprMatching() ){
- //heuristic for best matching constant
- sortEqTermsMatch setm;
- for( unsigned i=0; i<ci->getNumCEAtoms(); 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; i<d_equal_terms.size(); i++ ){
- if (ci->constructInstantiationInc(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<unsigned> 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<Node, Node, NodeHashFunction>::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<std::unordered_map<TNode, Node, TNodeHashFunction> > visited;
- visited.push(std::unordered_map<TNode, Node, TNodeHashFunction>());
- // whether the visited term contains pv
- std::unordered_map<TNode, bool, TNodeHashFunction> visited_contains_pv;
- std::unordered_map<TNode, Node, TNodeHashFunction>::iterator it;
- std::unordered_map<TNode, Node, TNodeHashFunction> curr_subs;
- std::stack<std::stack<TNode> > visit;
- TNode cur;
- visit.push(std::stack<TNode>());
- 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<TNode, Node, TNodeHashFunction>::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<TNode, Node, TNodeHashFunction>());
- visit.push(std::stack<TNode>());
- }
- 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<Node> 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<TNode> trace_visit;
- std::unordered_set<TNode, TNodeHashFunction> 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<Node>& children,
- std::unordered_map<TNode, bool, TNodeHashFunction>& 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<TNode, bool, TNodeHashFunction>& 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<Node>& children,
- std::unordered_map<TNode, bool, TNodeHashFunction>& 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<Node>& children,
- std::unordered_map<TNode, bool, TNodeHashFunction>& 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<Node> 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<Node>& children,
- std::unordered_map<TNode, bool, TNodeHashFunction>& 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>();
- BitVectorExtract je = j.getOperator().getConst<BitVectorExtract>();
- 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<Node>& lems, std::vector<Node>& ce_vars)
-{
- // new variables
- std::vector<Node> vars;
- // new lemmas
- std::vector<Node> 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<Node, std::vector<Node> > extract_map;
- std::unordered_set<TNode, TNodeHashFunction> 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<const Node, std::vector<Node> >& es : extract_map)
- {
- // sort based on the extract start position
- std::vector<Node>& 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<unsigned> 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<BitVectorExtract>();
- 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<Node> 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<Node, std::vector<Node> >& extract_map,
- std::unordered_set<TNode, TNodeHashFunction>& visited)
-{
- std::vector<TNode> 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 */
+++ /dev/null
-/********************* */
-/*! \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 <unordered_set>
-
-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<TermProperties>& term_props,
- std::vector<Node>& 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<Node>& lemmas) override;
- virtual std::string identify() const override { return "Arith"; }
- private:
- Node d_vts_sym[2];
- std::vector<Node> d_mbp_bounds[2];
- std::vector<Node> d_mbp_coeff[2];
- std::vector<Node> d_mbp_vts_coeff[2][2];
- std::vector<Node> 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<Node>& 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<TermProperties>& term_props,
- std::vector<Node>& 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<Node>& eqc,
- CegInstEffort effort) override;
- virtual std::string identify() const override { return "Epr"; }
- private:
- std::vector<Node> d_equal_terms;
- void computeMatchScore(CegInstantiator* ci,
- Node pv,
- Node catom,
- std::vector<Node>& arg_reps,
- TermArgTrie* tat,
- unsigned index,
- std::map<Node, int>& match_score);
- void computeMatchScore(CegInstantiator* ci,
- Node pv,
- Node catom,
- Node eqc,
- std::map<Node, int>& 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<unsigned, Node> 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<Node, Node, NodeHashFunction> 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<Node>& children,
- std::unordered_map<TNode, bool, TNodeHashFunction>& 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<Node>& lems,
- std::vector<Node>& 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<Node, std::vector<Node> >& extract_map,
- std::unordered_set<TNode, TNodeHashFunction>& visited);
-};
-
-} /* CVC4::theory::quantifiers namespace */
-} /* CVC4::theory namespace */
-} /* CVC4 namespace */
-
-#endif
--- /dev/null
+/********************* */
+/*! \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<Node, Instantiator*> inst : d_instantiator)
+ {
+ delete inst.second;
+ }
+ for (std::pair<TheoryId, InstantiatorPreprocess*> 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<n.getNumChildren(); i++ ){
+ computeProgVars( n[i] );
+ if( d_inelig.find( n[i] )!=d_inelig.end() ){
+ d_inelig.insert(n);
+ }
+ // all variables in child are contained in this
+ d_prog_var[n].insert(d_prog_var[n[i]].begin(), d_prog_var[n[i]].end());
+ }
+ // selectors applied to program variables are also variables
+ if (n.getKind() == APPLY_SELECTOR_TOTAL
+ && d_prog_var[n].find(n[0]) != d_prog_var[n].end())
+ {
+ d_prog_var[n].insert(n);
+ }
+ if (n.getKind() == kind::CHOICE)
+ {
+ d_prog_var.erase(n[0][0]);
+ }
+ }
+}
+
+bool CegInstantiator::isEligible( Node n ) {
+ //compute d_subs_fv, which program variables are contained in n, and determines if eligible
+ computeProgVars( n );
+ return d_inelig.find( n )==d_inelig.end();
+}
+
+bool CegInstantiator::hasVariable( Node n, Node pv ) {
+ computeProgVars( n );
+ return d_prog_var[n].find( pv )!=d_prog_var[n].end();
+}
+
+void CegInstantiator::activateInstantiationVariable(Node v, unsigned index)
+{
+ if( d_instantiator.find( v )==d_instantiator.end() ){
+ TypeNode tn = v.getType();
+ Instantiator * vinst;
+ if( tn.isReal() ){
+ vinst = new ArithInstantiator( d_qe, tn );
+ }else if( tn.isSort() ){
+ Assert( options::quantEpr() );
+ vinst = new EprInstantiator( d_qe, tn );
+ }else if( tn.isDatatype() ){
+ vinst = new DtInstantiator( d_qe, tn );
+ }else if( tn.isBitVector() ){
+ vinst = new BvInstantiator( d_qe, tn );
+ }else if( tn.isBoolean() ){
+ vinst = new ModelValueInstantiator( d_qe, tn );
+ }else{
+ //default
+ vinst = new Instantiator( d_qe, tn );
+ }
+ d_instantiator[v] = vinst;
+ }
+ d_curr_subs_proc[v].clear();
+ d_curr_index[v] = index;
+ d_curr_iphase[v] = CEG_INST_PHASE_NONE;
+}
+
+void CegInstantiator::registerTheoryIds(TypeNode tn,
+ std::map<TypeNode, bool>& 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<TypeNode, bool> 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; k<it_eqc->second.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<d_curr_type_eqc[pvtnb].size(); k++ ){
+ Node r = d_curr_type_eqc[pvtnb][k];
+ std::map< Node, std::vector< Node > >::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; kk<it_reqc->second.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; j<lhs.size(); j++ ){
+ //if this term or the another has pv in it, try to solve for it
+ if( hasVar || lhs_v[j] ){
+ Trace("cbqi-inst-debug") << "... " << i << "...try based on equality " << lhs[j] << " = " << ns << std::endl;
+ term_props.push_back( lhs_prop[j] );
+ terms.push_back( lhs[j] );
+ if (vinst->processEquality(
+ this, sf, pv, term_props, terms, d_effort))
+ {
+ return true;
+ }
+ term_props.pop_back();
+ terms.pop_back();
+ }
+ }
+ lhs.push_back( ns );
+ lhs_v.push_back( hasVar );
+ lhs_prop.push_back( pv_prop );
+ }else{
+ Trace("cbqi-inst-debug2") << "... term " << n << " is ineligible after substitution." << std::endl;
+ }
+ }else{
+ Trace("cbqi-inst-debug2") << "... term " << n << " is ineligible." << std::endl;
+ }
+ }
+ }
+ }
+
+ //[3] directly look at assertions
+ if (vinst->hasProcessAssertion(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; j<ita->second.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<sf.d_subs.size(); j++ ){
+ Trace("cbqi-inst-debug") << " ";
+ }
+ Trace("cbqi-inst-debug") << sf.d_subs.size() << ": (" << d_curr_iphase[pv]
+ << ") ";
+ Node mod_pv = pv_prop.getModifiedTerm( pv );
+ Trace("cbqi-inst-debug") << mod_pv << " -> " << 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<sf.d_subs.size(); j++ ){
+ Trace("cbqi-inst-debug2") << " Apply for " << sf.d_subs[j] << std::endl;
+ Assert( d_prog_var.find( sf.d_subs[j] )!=d_prog_var.end() );
+ if( d_prog_var[sf.d_subs[j]].find( pv )!=d_prog_var[sf.d_subs[j]].end() ){
+ prev_subs[j] = sf.d_subs[j];
+ TNode tv = pv;
+ TNode ts = n;
+ TermProperties a_pv_prop;
+ Node new_subs = applySubstitution( sf.d_vars[j].getType(), sf.d_subs[j], a_var, a_subs, a_prop, a_non_basic, a_pv_prop, true );
+ if( !new_subs.isNull() ){
+ sf.d_subs[j] = new_subs;
+ // the substitution apply to this term resulted in a non-basic substitution relationship
+ if( !a_pv_prop.isBasic() ){
+ // we are processing:
+ // sf.d_props[j].getModifiedTerm( sf.d_vars[j] ) = sf.d_subs[j] { pv_prop.getModifiedTerm( pv ) -> 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<new_non_basic.size(); i++ ){
+ sf.d_non_basic.pop_back();
+ }
+ return success;
+ }
+ }else{
+ //already tried this substitution
+ return false;
+ }
+}
+
+bool CegInstantiator::doAddInstantiation( std::vector< Node >& 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<subs.size(); i++ ){
+ subs_map[vars[i]] = subs[i];
+ }
+ subs.clear();
+ for (unsigned i = 0, size = d_input_vars.size(); i < size; ++i)
+ {
+ std::map<Node, Node>::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; i<lemmas.size(); i++ ){
+ d_out->addLemma( 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<Node, NodeHashFunction>::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.size(); i++ ){
+ Trace("cegqi-si-apply-subs-debug") << " " << vars[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; i<vars.size(); i++ ){
+ if( !prop[i].d_coeff.isNull() ){
+ Assert( vars[i].getType().isInteger() );
+ Assert( prop[i].d_coeff.isConst() );
+ Node nn = NodeManager::currentNM()->mkNode( MULT, subs[i], NodeManager::currentNM()->mkConst( Rational(1)/prop[i].d_coeff.getConst<Rational>() ) );
+ 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<Rational>() / msum_coeff[it->first].getConst<Rational>() ) );
+ }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<n.getNumChildren(); i++ ){
+ collectPresolveEqTerms( n[i], teq );
+ }
+ }
+}
+
+void getPresolveEqConjuncts( std::vector< Node >& 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<teq[v].size(); j++ ){
+ terms[i] = teq[v][j];
+ getPresolveEqConjuncts( vars, terms, teq, f, conj );
+ }
+ terms.pop_back();
+ }
+ }
+}
+
+void CegInstantiator::presolve( Node q ) {
+ //at preregister time, add proxy of obvious instantiations up front, which helps learning during preprocessing
+ //only if no nested quantifiers
+ if( !QuantifiersRewriter::containsQuantifiers( q[1] ) ){
+ std::vector< Node > ps_vars;
+ std::map< Node, std::vector< Node > > teq;
+ for( unsigned i=0; i<q[0].getNumChildren(); i++ ){
+ ps_vars.push_back( q[0][i] );
+ teq[q[0][i]].clear();
+ }
+ collectPresolveEqTerms( q[1], teq );
+ std::vector< Node > 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; i<d_vars.size(); i++ ){
+ Node pv = d_vars[i];
+ TypeNode pvtn = pv.getType();
+ Trace("cbqi-proc-debug") << "Collect theory ids from type " << pvtn << " of " << pv << std::endl;
+ //collect information about eqc
+ if( ee->hasTerm( 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; i<d_tids.size(); i++ ){
+ TheoryId tid = d_tids[i];
+ Theory* theory = d_qe->getTheoryEngine()->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<Assertion>::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<d_aux_vars.size(); i++ ){
+ Node r = d_aux_vars[i];
+ std::map< Node, Node >::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_lhs.size(); i++ ){
+ Trace("cbqi-proc") << " " << subs_lhs[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; i<it->second.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; i<it->second.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; i<it->second.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; i<it->second.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; i<subs_lhs.size(); i++ ){
+ Node nr = subs_rhs[i].substitute( cl.begin(), cl.end(), cr.begin(), cr.end() );
+ nr = Rewriter::rewrite( nr );
+ subs_rhs[i] = nr;
+ }
+
+ subs_lhs.push_back( l );
+ subs_rhs.push_back( r );
+}
+
+Node CegInstantiator::getModelValue( Node n ) {
+ return d_qe->getModel()->getValue( n );
+}
+
+Node CegInstantiator::getBoundVariable(TypeNode tn)
+{
+ unsigned index = 0;
+ std::unordered_map<TypeNode, unsigned, TypeNodeHashFunction>::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<n.getNumChildren(); i++ ){
+ collectCeAtoms( n[i], visited );
+ }
+ }else{
+ if( std::find( d_ce_atoms.begin(), d_ce_atoms.end(), n )==d_ce_atoms.end() ){
+ Trace("cbqi-ce-atoms") << "CE atoms : " << n << std::endl;
+ d_ce_atoms.push_back( n );
+ }
+ }
+ }
+}
+
+void CegInstantiator::registerCounterexampleLemma( std::vector< Node >& 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<Node> pvars;
+ pvars.insert(pvars.end(), d_vars.begin(), d_vars.end());
+ for (std::pair<const TheoryId, InstantiatorPreprocess*>& 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<lems.size(); i++ ){
+ Trace("cbqi-debug") << "Counterexample lemma (pre-rewrite) " << i << " : " << lems[i] << std::endl;
+ Node rlem = lems[i];
+ rlem = Rewriter::rewrite( rlem );
+ Trace("cbqi-debug") << "Counterexample lemma (post-rewrite) " << i << " : " << rlem << std::endl;
+ //record the literals that imply auxiliary variables to be equal to terms
+ if( lems[i].getKind()==ITE && rlem.getKind()==ITE ){
+ if( lems[i][1].getKind()==EQUAL && lems[i][2].getKind()==EQUAL && lems[i][1][0]==lems[i][2][0] ){
+ if( std::find( d_aux_vars.begin(), d_aux_vars.end(), lems[i][1][0] )!=d_aux_vars.end() ){
+ Node v = lems[i][1][0];
+ for( unsigned r=1; r<=2; r++ ){
+ d_aux_eq[rlem[r]][v] = lems[i][r][1];
+ Trace("cbqi-debug") << " " << rlem[r] << " implies " << v << " = " << lems[i][r][1] << std::endl;
+ }
+ }
+ }
+ }
+ /*else if( lems[i].getKind()==EQUAL && lems[i][0].getType().isBoolean() ){
+ //Boolean terms
+ if( std::find( d_aux_vars.begin(), d_aux_vars.end(), lems[i][0] )!=d_aux_vars.end() ){
+ Node v = lems[i][0];
+ d_aux_eq[rlem][v] = lems[i][1];
+ Trace("cbqi-debug") << " " << rlem << " implies " << v << " = " << lems[i][1] << std::endl;
+ }
+ }*/
+ lems[i] = rlem;
+ }
+
+ // determine variable order: must do Reals before Ints
+ Trace("cbqi-debug") << "Determine variable order..." << std::endl;
+ if (!d_vars.empty())
+ {
+ std::map<Node, unsigned> voo;
+ bool doSort = false;
+ std::vector<Node> vars;
+ std::map<TypeNode, std::vector<Node> > 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<const TypeNode, std::vector<Node> >& 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; i<lems.size(); i++ ){
+ collectCeAtoms( lems[i], visited );
+ }
+}
+
+
+Instantiator::Instantiator( QuantifiersEngine * qe, TypeNode tn ) : d_type( tn ){
+ d_closed_enum_type = qe->getTermEnumeration()->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 */
--- /dev/null
+/********************* */
+/*! \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<Node> 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<Node>& lems,
+ std::vector<Node>& 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<Node,
+ std::unordered_set<Node, NodeHashFunction>,
+ NodeHashFunction>
+ d_prog_var;
+ /** cache of the set of terms that we have established are
+ * ineligible for instantiation.
+ */
+ std::unordered_set<Node, NodeHashFunction> 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<TheoryId, std::vector<Node> > d_curr_asserts;
+ /** map from representatives to the terms in their equivalence class */
+ std::map<Node, std::vector<Node> > d_curr_eqc;
+ /** map from types to representatives of that type */
+ std::map<TypeNode, std::vector<Node> > d_curr_type_eqc;
+ /** solved asserts */
+ std::unordered_set<Node, NodeHashFunction> 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<Node>& subs_lhs,
+ std::vector<Node>& subs_rhs,
+ Node l,
+ Node r);
+ /** cache bound variables for type returned
+ * by getBoundVariable(...).
+ */
+ std::unordered_map<TypeNode, std::vector<Node>, 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<TypeNode, unsigned, TypeNodeHashFunction>
+ 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<TheoryId> d_tids;
+ /** map from theory ids to instantiator preprocessors */
+ std::map<TheoryId, InstantiatorPreprocess*> 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<TypeNode, bool>& 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<Node> d_vars;
+ /** set form of d_vars */
+ std::unordered_set<Node, NodeHashFunction> d_vars_set;
+ /** index of variables reported in instantiation */
+ std::vector<unsigned> 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<Node> 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<Node, std::map<Node, Node> > 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<Node> 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<Node> d_ce_atoms;
+ /** collect atoms */
+ void collectCeAtoms(Node n, std::map<Node, bool>& 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<Node, Instantiator*> d_active_instantiators;
+ /** map from variables to the index in the prefix of the quantified
+ * formula we are processing.
+ */
+ std::map<Node, unsigned> d_curr_index;
+ /** map from variables to the phase in which we instantiated them */
+ std::map<Node, CegInstPhase> d_curr_iphase;
+ /** cache of current substitutions tried between activate/deactivate */
+ std::map<Node, std::map<Node, std::map<Node, bool> > > d_curr_subs_proc;
+ /** stack of temporary variables we are solving for,
+ * e.g. subfields of datatypes.
+ */
+ std::vector<Node> 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<Node, Instantiator*> 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<Node>& vars,
+ std::vector<Node>& subs,
+ std::vector<Node>& 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<Node>& 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<TermProperties>& term_props,
+ std::vector<Node>& 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<Node>& 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<Node>& lems,
+ std::vector<Node>& ce_vars)
+ {
+ }
+};
+
+} /* CVC4::theory::quantifiers namespace */
+} /* CVC4::theory namespace */
+} /* CVC4 namespace */
+
+#endif
--- /dev/null
+/********************* */
+/*! \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 <algorithm>
+#include <stack>
+
+using namespace std;
+using namespace CVC4::kind;
+using namespace CVC4::context;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+struct BvLinearAttributeId {};
+using BvLinearAttribute = expr::Attribute<BvLinearAttributeId, bool>;
+
+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<Rational>() ), vts_coeff[t] );
+ vts_coeff[t] = Rewriter::rewrite( vts_coeff[t] );
+ }else if( itv->second.getConst<Rational>().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<Rational>().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<Rational>().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<Rational>().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<TermProperties>& term_props,
+ std::vector<Node>& 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; r<rmax; r++ ){
+ int uires = ires;
+ Node uval = val;
+ if( atom.getKind()==GEQ ){
+ //push negation downwards
+ if( !pol ){
+ uires = -ires;
+ if( d_type.isInteger() ){
+ uval = NodeManager::currentNM()->mkNode( 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<Rational>().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()<d_mbp_bounds[0].size();
+ }
+ int best_used[2];
+ std::vector< Node > 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; j<d_mbp_bounds[rr].size(); j++ ){
+ Node value[3];
+ if( Trace.isOn("cegqi-arith-bound") ){
+ Assert( !d_mbp_bounds[rr][j].isNull() );
+ Trace("cegqi-arith-bound") << " " << j << ": " << d_mbp_bounds[rr][j];
+ if( !d_mbp_vts_coeff[rr][0][j].isNull() ){
+ Trace("cegqi-arith-bound") << " (+ " << d_mbp_vts_coeff[rr][0][j] << " * INF)";
+ }
+ if( !d_mbp_vts_coeff[rr][1][j].isNull() ){
+ Trace("cegqi-arith-bound") << " (+ " << d_mbp_vts_coeff[rr][1][j] << " * DELTA)";
+ }
+ if( !d_mbp_coeff[rr][j].isNull() ){
+ Trace("cegqi-arith-bound") << " (div " << d_mbp_coeff[rr][j] << ")";
+ }
+ Trace("cegqi-arith-bound") << ", value = ";
+ }
+ t_values[rr].push_back( Node::null() );
+ //check if it is better than the current best bound : lexicographic order infinite/finite/infinitesimal parts
+ bool new_best = true;
+ for( unsigned t=0; t<3; t++ ){
+ //get the value
+ if( t==0 ){
+ value[0] = d_mbp_vts_coeff[rr][0][j];
+ if( !value[0].isNull() ){
+ Trace("cegqi-arith-bound") << "( " << value[0] << " * INF ) + ";
+ }else{
+ value[0] = zero;
+ }
+ }else if( t==1 ){
+ Node t_value = ci->getModelValue( 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<Rational>() ), 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; j<d_mbp_bounds[rr].size(); j++ ){
+ if( (int)j!=best_used[rr] && ( !options::cbqiMidpoint() || d_mbp_vts_coeff[rr][1][j].isNull() ) ){
+ Node val = getModelBasedProjectionValue( ci, pv, d_mbp_bounds[rr][j], rr==0, d_mbp_coeff[rr][j], pv_value, t_values[rr][j], sf.getTheta(),
+ d_mbp_vts_coeff[rr][0][j], d_mbp_vts_coeff[rr][1][j] );
+ if( !val.isNull() ){
+ TermProperties pv_prop_nopt_bound;
+ pv_prop_nopt_bound.d_coeff = d_mbp_coeff[rr][j];
+ pv_prop_nopt_bound.d_type = rr==0 ? 1 : -1;
+ if (ci->constructInstantiationInc(
+ 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<Node>& 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; i<a.getNumChildren(); i++ ){
+ Node s = solve_dt( v, a[i], b[i], sa[i], sb[i] );
+ if( !s.isNull() ){
+ return s;
+ }
+ }
+ }
+ }else{
+ unsigned cindex = Datatype::indexOf( a.getOperator().toExpr() );
+ TypeNode tn = a.getType();
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ for( unsigned i=0; i<a.getNumChildren(); i++ ){
+ Node nn = NodeManager::currentNM()->mkNode( APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[cindex].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<Node>& 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<eqc.size(); k++ ){
+ Node n = eqc[k];
+ if( n.getKind()==APPLY_CONSTRUCTOR ){
+ Trace("cegqi-dt-debug") << "...try based on constructor term " << n << std::endl;
+ std::vector< Node > 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; j<dt[cindex].getNumArgs(); j++ ){
+ Node c = NodeManager::currentNM()->mkNode( APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[cindex].getSelectorInternal( d_type.toType(), j ) ), pv );
+ ci->pushStackVariable( c );
+ children.push_back( c );
+ }
+ 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; j<dt[cindex].getNumArgs(); j++ ){
+ ci->popStackVariable();
+ }
+ break;
+ }
+ }
+ }
+ return false;
+}
+
+bool DtInstantiator::processEquality(CegInstantiator* ci,
+ SolvedForm& sf,
+ Node pv,
+ std::vector<TermProperties>& term_props,
+ std::vector<Node>& 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<catom.getNumChildren(); i++ ){
+ if( catom[i]==pv ){
+ Trace("cegqi-epr") << "...increment " << gcatom[i] << std::endl;
+ match_score[gcatom[i]]++;
+ }else{
+ //recursive matching
+ computeMatchScore( ci, pv, catom[i], gcatom[i], match_score );
+ }
+ }
+ }else{
+ std::map< TNode, TermArgTrie >::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; j<catom.getNumChildren(); j++ ){
+ arg_reps.push_back( ci->getQuantifiersEngine()->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<j );
+ }
+};
+
+bool EprInstantiator::processEqualTerms(CegInstantiator* ci,
+ SolvedForm& sf,
+ Node pv,
+ std::vector<Node>& eqc,
+ CegInstEffort effort)
+{
+ if( options::quantEprMatching() ){
+ //heuristic for best matching constant
+ sortEqTermsMatch setm;
+ for( unsigned i=0; i<ci->getNumCEAtoms(); 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; i<d_equal_terms.size(); i++ ){
+ if (ci->constructInstantiationInc(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<unsigned> 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<Node, Node, NodeHashFunction>::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<std::unordered_map<TNode, Node, TNodeHashFunction> > visited;
+ visited.push(std::unordered_map<TNode, Node, TNodeHashFunction>());
+ // whether the visited term contains pv
+ std::unordered_map<TNode, bool, TNodeHashFunction> visited_contains_pv;
+ std::unordered_map<TNode, Node, TNodeHashFunction>::iterator it;
+ std::unordered_map<TNode, Node, TNodeHashFunction> curr_subs;
+ std::stack<std::stack<TNode> > visit;
+ TNode cur;
+ visit.push(std::stack<TNode>());
+ 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<TNode, Node, TNodeHashFunction>::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<TNode, Node, TNodeHashFunction>());
+ visit.push(std::stack<TNode>());
+ }
+ 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<Node> 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<TNode> trace_visit;
+ std::unordered_set<TNode, TNodeHashFunction> 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<Node>& children,
+ std::unordered_map<TNode, bool, TNodeHashFunction>& 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<TNode, bool, TNodeHashFunction>& 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<Node>& children,
+ std::unordered_map<TNode, bool, TNodeHashFunction>& 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<Node>& children,
+ std::unordered_map<TNode, bool, TNodeHashFunction>& 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<Node> 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<Node>& children,
+ std::unordered_map<TNode, bool, TNodeHashFunction>& 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>();
+ BitVectorExtract je = j.getOperator().getConst<BitVectorExtract>();
+ 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<Node>& lems, std::vector<Node>& ce_vars)
+{
+ // new variables
+ std::vector<Node> vars;
+ // new lemmas
+ std::vector<Node> 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<Node, std::vector<Node> > extract_map;
+ std::unordered_set<TNode, TNodeHashFunction> 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<const Node, std::vector<Node> >& es : extract_map)
+ {
+ // sort based on the extract start position
+ std::vector<Node>& 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<unsigned> 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<BitVectorExtract>();
+ 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<Node> 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<Node, std::vector<Node> >& extract_map,
+ std::unordered_set<TNode, TNodeHashFunction>& visited)
+{
+ std::vector<TNode> 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 */
--- /dev/null
+/********************* */
+/*! \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 <unordered_set>
+
+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<TermProperties>& term_props,
+ std::vector<Node>& 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<Node>& lemmas) override;
+ virtual std::string identify() const override { return "Arith"; }
+ private:
+ Node d_vts_sym[2];
+ std::vector<Node> d_mbp_bounds[2];
+ std::vector<Node> d_mbp_coeff[2];
+ std::vector<Node> d_mbp_vts_coeff[2][2];
+ std::vector<Node> 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<Node>& 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<TermProperties>& term_props,
+ std::vector<Node>& 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<Node>& eqc,
+ CegInstEffort effort) override;
+ virtual std::string identify() const override { return "Epr"; }
+ private:
+ std::vector<Node> d_equal_terms;
+ void computeMatchScore(CegInstantiator* ci,
+ Node pv,
+ Node catom,
+ std::vector<Node>& arg_reps,
+ TermArgTrie* tat,
+ unsigned index,
+ std::map<Node, int>& match_score);
+ void computeMatchScore(CegInstantiator* ci,
+ Node pv,
+ Node catom,
+ Node eqc,
+ std::map<Node, int>& 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<unsigned, Node> 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<Node, Node, NodeHashFunction> 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<Node>& children,
+ std::unordered_map<TNode, bool, TNodeHashFunction>& 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<Node>& lems,
+ std::vector<Node>& 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<Node, std::vector<Node> >& extract_map,
+ std::unordered_set<TNode, TNodeHashFunction>& visited);
+};
+
+} /* CVC4::theory::quantifiers namespace */
+} /* CVC4::theory namespace */
+} /* CVC4 namespace */
+
+#endif
--- /dev/null
+/********************* */
+/*! \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; i<d_quantEngine->getModel()->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; i<q[0].getNumChildren(); i++ ){
+ TypeNode tn = q[0][i].getType();
+ if( tn.isSort() ){
+ if( qepr->isEPR( 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; j<itc->second.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; i<ics.size(); i++ ){
+ Node qi = ics[i].getAttribute(InstConstantAttribute());
+ if( std::find( d_parent_quant[q].begin(), d_parent_quant[q].end(), qi )==d_parent_quant[q].end() ){
+ d_parent_quant[q].push_back( qi );
+ d_children_quant[qi].push_back( q );
+ Assert( hasAddedCbqiLemma( qi ) );
+ Node qicel = d_quantEngine->getTermUtil()->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; i<quants.size(); i++ ){
+ if( doCbqi( quants[i] ) ){
+ registerCbqiLemma( quants[i] );
+ }
+ if( options::cbqiNestedQE() ){
+ //record these as counterexample quantifiers
+ QAttributes qa;
+ QuantAttributes::computeQuantAttributes( quants[i], qa );
+ if( !qa.d_qid_num.isNull() ){
+ d_id_to_ce_quant[ qa.d_qid_num ] = quants[i];
+ d_ce_quant_to_id[ quants[i] ] = qa.d_qid_num;
+ Trace("cbqi-nqe") << "CE quant id = " << qa.d_qid_num << " is " << quants[i] << std::endl;
+ }
+ }
+ }
+ }
+ return true;
+ }else{
+ return false;
+ }
+}
+
+void InstStrategyCbqi::reset_round( Theory::Effort effort ) {
+ d_cbqi_set_quant_inactive = false;
+ d_incomplete_check = false;
+ d_active_quant.clear();
+ //check if any cbqi lemma has not been added yet
+ for( unsigned i=0; i<d_quantEngine->getModel()->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]<d_nested_qe_waitlist_size[q] ){
+ int index = d_nested_qe_waitlist_proc[q];
+ Assert( index>=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; j<itc->second.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; i<ninner.size(); i++ ){
+ Assert( d_active_quant.find( ninner[i] )!=d_active_quant.end() );
+ d_active_quant.erase( ninner[i] );
+ }
+ Assert( !d_active_quant.empty() );
+ Trace("cbqi-debug") << "...done removing." << std::endl;
+ }
+ }
+
+ processResetInstantiationRound( effort );
+}
+
+void InstStrategyCbqi::check(Theory::Effort e, QEffort quant_e)
+{
+ if (quant_e == QEFFORT_STANDARD)
+ {
+ Assert( !d_quantEngine->inConflict() );
+ 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; i<d_quantEngine->getModel()->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; i<n[2].getNumChildren(); i++ ){
+ iplc.push_back( n[2][i] );
+ }
+ }
+ rc.push_back( NodeManager::currentNM()->mkNode( 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; i<n.getNumChildren(); i++ ){
+ Node nc = getIdMarkedQuantNode( n[i], visited );
+ childChanged = childChanged || nc!=n[i];
+ children.push_back( nc );
+ }
+ if( childChanged ){
+ ret = NodeManager::currentNM()->mkNode( 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; i<n.getNumChildren(); i++ ){
+ Node nc = doNestedQERec( q, n[i], visited, inst_terms, doVts );
+ childChanged = childChanged || nc!=n[i];
+ children.push_back( nc );
+ }
+ if( childChanged ){
+ ret = NodeManager::currentNM()->mkNode( 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<n.getNumChildren(); i++ ){
+ if( hasNonCbqiOperator( n[i], visited ) ){
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+// -1 : not cbqi sort, 0 : cbqi sort, 1 : cbqi sort regardless of quantifier body
+int InstStrategyCbqi::isCbqiSort( TypeNode tn, std::map< TypeNode, int >& 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; i<dt.getNumConstructors(); i++ ){
+ for( unsigned j=0; j<dt[i].getNumArgs(); j++ ){
+ TypeNode crange = TypeNode::fromType( ((SelectorType)dt[i][j].getType()).getRangeType() );
+ int cret = isCbqiSort( crange, visited );
+ if( cret==-1 ){
+ visited[tn] = -1;
+ return -1;
+ }else if( cret<ret ){
+ ret = cret;
+ }
+ }
+ }
+ }else if( tn.isSort() ){
+ QuantEPR * qepr = d_quantEngine->getQuantEPR();
+ 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<q[0].getNumChildren(); i++ ){
+ TypeNode tn = q[0][i].getType();
+ std::map< TypeNode, int > visited;
+ int handled = isCbqiSort( tn, visited );
+ if( handled==-1 ){
+ return -1;
+ }else if( handled<hmin ){
+ hmin = handled;
+ }
+ }
+ return hmin;
+}
+
+bool InstStrategyCbqi::doCbqi( Node q ){
+ std::map< Node, int >::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; i<q[2].getNumChildren(); i++ ){
+ if( q[2][i].getKind()==INST_PATTERN ){
+ ret = 0;
+ }
+ }
+ }
+ if( d_quantEngine->getQuantAttributes()->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; j<itc->second.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; i<d_quantEngine->getModel()->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; i<inf.size(); i++ ){
+ Trace("quant-vts-debug") << "Infinity lemma for " << inf[i] << " " << d_small_const << std::endl;
+ Node inf_lem_lb = NodeManager::currentNM()->mkNode( GT, inf[i], NodeManager::currentNM()->mkConst( Rational(1)/d_small_const.getConst<Rational>() ) );
+ 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; i<d_quantEngine->getTermUtil()->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; i<lems.size(); i++ ){
+ Trace("cbqi-debug") << "Counterexample lemma " << i << " : " << lems[i] << std::endl;
+ d_quantEngine->addLemma( 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 );
+ }
+ }
+}
+
--- /dev/null
+/********************* */
+/*! \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<Node, NodeHashFunction> 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
#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;
--- /dev/null
+/********************* */
+/*! \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 <stack>
+
+#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<Node>& nodes,
+ std::map<Node, std::vector<Node> >& 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<const Node, std::vector<Node> >& 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<Node, std::vector<Node> >& apps)
+{
+ std::vector<Node> ns;
+ ns.push_back(n);
+ collectHoVarApplyTerms(q, ns, apps);
+ Assert(ns.size() == 1);
+ n = ns[0];
+}
+
+void HigherOrderTrigger::collectHoVarApplyTerms(
+ Node q, std::vector<Node>& ns, std::map<Node, std::vector<Node> >& apps)
+{
+ std::unordered_map<TNode, Node, TNodeHashFunction> visited;
+ std::unordered_map<TNode, Node, TNodeHashFunction>::iterator it;
+ // whether the visited node is a child of a HO_APPLY chain
+ std::unordered_map<TNode, bool, TNodeHashFunction> withinApply;
+ std::vector<TNode> 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<Node> 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<TNode> vars;
+ std::vector<TNode> 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<TNode, std::vector<Node> > ho_var_apps_subs;
+ for (std::pair<const Node, std::vector<Node> >& 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<const TNode, std::vector<Node> >& 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<TNode, std::vector<Node> >::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<unsigned, Node> fixed_vals;
+ for (unsigned i = 0; i < ha.second.size(); i++)
+ {
+ std::vector<TNode> 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<unsigned, Node>::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<unsigned, Node>::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<Node, unsigned> arg_to_rep;
+ for (unsigned index = 0, size = ithb->second.size(); index < size;
+ index++)
+ {
+ Node bv_at_index = ithb->second[index];
+ std::map<unsigned, Node>::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<Node, unsigned>::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<unsigned, unsigned>::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<unsigned, std::vector<Node> >::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 */
--- /dev/null
+/********************* */
+/*! \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 <map>
+#include <unordered_set>
+
+#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<Node>& nodes,
+ std::map<Node, std::vector<Node> >& 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<Node, std::vector<Node> >& apps);
+ /** Collect higher order var apply terms
+ *
+ * Same as above, but with multiple terms ns.
+ */
+ static void collectHoVarApplyTerms(Node q,
+ std::vector<Node>& ns,
+ std::map<Node, std::vector<Node> >& 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<Node, std::vector<Node> > d_ho_var_apps;
+ /**
+ * List of all function-typed variables that occur as the head of function
+ * applications in d_f.
+ */
+ std::vector<Node> 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<TNode, std::vector<Node> > d_ho_var_bvs;
+ std::map<TNode, Node> d_ho_var_bvl;
+ /** the set of types of ho variables */
+ std::unordered_set<TypeNode, TypeNodeHashFunction> 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<unsigned, std::vector<Node> > 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<unsigned, std::map<unsigned, unsigned> > 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<unsigned, std::map<unsigned, std::vector<Node> > > 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 */
--- /dev/null
+/********************* */
+/*! \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; i<d_children.size(); i++ ){
+ delete d_children[i];
+ }
+ delete d_cg;
+}
+
+void InstMatchGenerator::setActiveAdd(bool val){
+ d_active_add = val;
+ if( d_next!=NULL ){
+ d_next->setActiveAdd(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; i<gens.size(); i++ ){
+ Node pn = gens[i]->d_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<pats.size() ){
+ size_t counter = 0;
+ std::vector< InstMatchGenerator* > 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 (counter<gens.size()) {
+ InstMatchGenerator* curr = gens[counter];
+ if( prev ){
+ prev->d_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; i<it->second.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()<pats.size() ){
+ // score is lexographic ( bound vars, shared vars )
+ int score_max_1 = -1;
+ int score_max_2 = -1;
+ int score_index = -1;
+ for( unsigned i=0; i<pats.size(); i++ ){
+ Node p = pats[i];
+ if( std::find( pats_ordered.begin(), pats_ordered.end(), p )==pats_ordered.end() ){
+ int score_1 = 0;
+ int score_2 = 0;
+ for( unsigned j=0; j<var_contains[p].size(); j++ ){
+ Node v = var_contains[p][j];
+ if( var_bound.find( v )!=var_bound.end() ){
+ score_1++;
+ }else if( var_to_node[v].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; i<var_contains[mp].size(); i++ ){
+ var_bound[var_contains[mp][i]] = true;
+ }
+ pats_ordered.push_back( mp );
+ pats_ordered_independent.push_back( score_max_1==0 );
+ }
+
+ Trace("multi-trigger-linear") << "Make children for linear multi trigger." << std::endl;
+ for( unsigned i=0; i<pats_ordered.size(); i++ ){
+ Trace("multi-trigger-linear") << "...make for " << pats_ordered[i] << std::endl;
+ InstMatchGenerator* cimg = getInstMatchGenerator(q, pats_ordered[i]);
+ Assert( cimg!=NULL );
+ d_children.push_back( cimg );
+ if( i==0 ){ //TODO : improve
+ cimg->setIndependent();
+ }
+ }
+}
+
+int InstMatchGeneratorMultiLinear::resetChildren( QuantifiersEngine* qe ){
+ for( unsigned i=0; i<d_children.size(); i++ ){
+ if( !d_children[i]->reset( 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; i<d_children.size(); i++ ){
+ Node mi = d_children[i]->getCurrentMatch();
+ 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<Node>& 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; i<pats.size(); i++ ){
+ Node n = pats[i];
+ //make the match generator
+ InstMatchGenerator* img =
+ InstMatchGenerator::mkInstMatchGenerator(q, n, qe);
+ img->setActiveAdd(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<d_var_contains[n].size(); j++ ){
+ if( d_var_to_node[ d_var_contains[n][j] ].size()==1 ){
+ Trace("multi-trigger-cache") << "Var " << d_var_contains[n][j] << " is unique to " << pats[i] << std::endl;
+ unique_vars.push_back( d_var_contains[n][j] );
+ }else{
+ shared_vars[ d_var_contains[n][j] ] = true;
+ numSharedVars++;
+ }
+ }
+ //we use the latest shared variables, then unique variables
+ std::vector< int > 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; j<vars.size(); j++ ){
+ Trace("multi-trigger-cache") << vars[j] << " ";
+ }
+ Trace("multi-trigger-cache") << std::endl;
+ //make ordered inst match trie
+ d_imtio[i] = new InstMatchTrie::ImtIndexOrder;
+ d_imtio[i]->d_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<d_children.size(); i++ ){
+ delete d_children[i];
+ }
+ for( std::map< unsigned, InstMatchTrie::ImtIndexOrder* >::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; i<d_children.size(); i++ ){
+ d_children[i]->resetInstantiationRound( 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; i<d_children.size(); i++ ){
+ if( !d_children[i]->reset( 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<d_children.size(); i++ ){
+ Trace("multi-trigger-cache") << "Calculate matches " << i << std::endl;
+ std::vector< InstMatch > 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; j<newMatches.size(); j++ ){
+ Trace("multi-trigger-cache2") << "...processing " << j << " / " << newMatches.size() << ", #lemmas = " << addedLemmas << std::endl;
+ processNewMatch(qe, tparent, newMatches[j], i, addedLemmas);
+ if( qe->inConflict() ){
+ return addedLemmas;
+ }
+ }
+ }
+ return addedLemmas;
+}
+
+void InstMatchGeneratorMulti::processNewMatch(QuantifiersEngine* qe,
+ Trigger* tparent,
+ InstMatch& m,
+ int fromChildIndex,
+ int& addedLemmas)
+{
+ //see if these produce new matches
+ d_children_trie[fromChildIndex].addInstMatch(qe, d_quant, m);
+ //possibly only do the following if we know that new matches will be produced?
+ //the issue is that instantiations are filtered in quantifiers engine, and so there is no guarentee that
+ // we can safely skip the following lines, even when we have already produced this match.
+ Trace("multi-trigger-cache-debug") << "Child " << fromChildIndex << " produced match " << m << std::endl;
+ //process new instantiations
+ int childIndex = (fromChildIndex+1)%(int)d_children.size();
+ processNewInstantiations(qe,
+ tparent,
+ m,
+ addedLemmas,
+ d_children_trie[childIndex].getTrie(),
+ 0,
+ childIndex,
+ fromChildIndex,
+ true);
+}
+
+void InstMatchGeneratorMulti::processNewInstantiations(QuantifiersEngine* qe,
+ Trigger* tparent,
+ InstMatch& m,
+ int& addedLemmas,
+ InstMatchTrie* tr,
+ int trieIndex,
+ int childIndex,
+ int endChildIndex,
+ bool modEq)
+{
+ Assert( !qe->inConflict() );
+ 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<const Node, InstMatchTrie>& 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<Node, InstMatchTrie>::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<Node, InstMatchTrie>::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; i<d_match_pattern.getNumChildren(); i++ ){
+ if( d_match_pattern[i].getKind()==INST_CONSTANT ){
+ if( !options::cbqi() || quantifiers::TermUtil::getInstConstAttr(d_match_pattern[i])==q ){
+ d_var_num[i] = d_match_pattern[i].getAttribute(InstVarNumAttribute());
+ }else{
+ d_var_num[i] = -1;
+ }
+ }
+ d_match_pattern_arg_types.push_back( d_match_pattern[i].getType() );
+ }
+ d_op = qe->getTermDatabase()->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 */
--- /dev/null
+/********************* */
+/*! \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 <map>
+#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<Node>& 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<Node>& pats,
+ QuantifiersEngine* qe,
+ std::map<Node, InstMatchGenerator*>& 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<InstMatchGenerator*> 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<int> 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<int> 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<Node, bool> 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<InstMatchGenerator*>& 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<Node>& 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<Node>& 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
--- /dev/null
+/********************* */
+/*! \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( nqfsi<nqfsj ){
+ return true;
+ }else if( nqfsi>nqfsj ){
+ 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<j;
+ }else{
+ return wi<wj;
+ }
+ }
+};
+
+void InstStrategyUserPatterns::processResetInstantiationRound( Theory::Effort effort ){
+ Trace("inst-alg-debug") << "reset user triggers" << std::endl;
+ //reset triggers
+ for( std::map< Node, std::vector< Trigger* > >::iterator it = d_user_gen.begin(); it != d_user_gen.end(); ++it ){
+ for( unsigned i=0; i<it->second.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<peffort ){
+ return STATUS_UNFINISHED;
+ }else if( e==peffort ){
+ d_counter[f]++;
+
+ Trace("inst-alg") << "-> User-provided instantiate " << f << "..." << std::endl;
+ if( d_quantEngine->getInstUserPatMode()==USER_PAT_MODE_RESORT ){
+ for( unsigned i=0; i<d_user_gen_wait[f].size(); i++ ){
+ Trigger * t = Trigger::mkTrigger( d_quantEngine, f, d_user_gen_wait[f][i], true, Trigger::TR_RETURN_NULL );
+ if( t ){
+ d_user_gen[f].push_back( t );
+ }
+ }
+ d_user_gen_wait[f].clear();
+ }
+
+ for( unsigned i=0; i<d_user_gen[f].size(); i++ ){
+ bool processTrigger = true;
+ if( processTrigger ){
+ Trace("process-trigger") << " Process (user) ";
+ d_user_gen[f][i]->debugPrint("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; i<pat.getNumChildren(); i++ ){
+ Node pat_use = Trigger::getIsUsableTrigger( pat[i], q );
+ if( pat_use.isNull() ){
+ Trace("trigger-warn") << "User-provided trigger is not usable : " << pat << " because of " << pat[i] << std::endl;
+ usable = false;
+ break;
+ }else{
+ nodes.push_back( pat_use );
+ }
+ }
+ if( usable ){
+ Trace("user-pat") << "Add user pattern: " << pat << " for " << q << std::endl;
+ //check match option
+ if( d_quantEngine->getInstUserPatMode()==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<peffort ){
+ return STATUS_UNFINISHED;
+ }else{
+ Trace("inst-alg") << "-> 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 && ( score<max_score || max_score<0 ) ){
+ max_score = score;
+ max_trigger = itt->first;
+ }
+ }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<patTermsF.size(); i++ ){
+ Assert( tinfo.find( patTermsF[i] )!=tinfo.end() );
+ Trace("auto-gen-trigger-debug") << " " << patTermsF[i] << std::endl;
+ Trace("auto-gen-trigger-debug2") << " info = [" << tinfo[patTermsF[i]].d_reqPol << ", " << tinfo[patTermsF[i]].d_reqPolEq << ", " << tinfo[patTermsF[i]].d_fv.size() << "]" << std::endl;
+ }
+ Trace("auto-gen-trigger-debug") << std::endl;
+ }
+ }
+ //sort into single/multi triggers, calculate which terms should not be considered
+ std::map< Node, bool > vcMap;
+ std::map< Node, bool > rmPatTermsF;
+ int last_weight = -1;
+ for( unsigned i=0; i<patTermsF.size(); i++ ){
+ Assert( patTermsF[i].getKind()!=NOT );
+ bool newVar = false;
+ for( unsigned j=0; j<tinfo[ patTermsF[i] ].d_fv.size(); j++ ){
+ if( vcMap.find( tinfo[ patTermsF[i] ].d_fv[j] )==vcMap.end() ){
+ vcMap[tinfo[ patTermsF[i] ].d_fv[j]] = true;
+ newVar = true;
+ }
+ }
+ int curr_w = Trigger::getTriggerWeight( patTermsF[i] );
+ if( ntrivTriggers && !newVar && last_weight!=-1 && curr_w>last_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]<f[0].getNumChildren() ){
+ Trace("auto-gen-trigger-partial") << "Quantified formula : " << f << std::endl;
+ Trace("auto-gen-trigger-partial") << "...does not contain all variables in triggers!!!" << std::endl;
+ if( options::partialTriggers() ){
+ std::vector< Node > vcs[2];
+ for( unsigned i=0; i<f[0].getNumChildren(); i++ ){
+ Node ic = d_quantEngine->getTermUtil()->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<patTermsF.size(); i++ ){
+ Node pat = patTermsF[i];
+ if( rmPatTermsF.find( pat )==rmPatTermsF.end() ){
+ Trace("auto-gen-trigger-debug") << "...processing pattern " << pat << std::endl;
+ Node mpat = pat;
+ //process the pattern: if it has a required polarity, consider it
+ Assert( tinfo.find( pat )!=tinfo.end() );
+ int rpol = tinfo[pat].d_reqPol;
+ Node rpoleq = tinfo[pat].d_reqPolEq;
+ unsigned num_fv = tinfo[pat].d_fv.size();
+ Trace("auto-gen-trigger-debug") << "...required polarity for " << pat << " is " << rpol << ", eq=" << rpoleq << std::endl;
+ if( rpol!=0 ){
+ Assert( rpol==1 || rpol==-1 );
+ if( Trigger::isRelationalTrigger( pat ) ){
+ pat = rpol==-1 ? pat.negate() : pat;
+ }else{
+ Assert( Trigger::isAtomicTrigger( pat ) );
+ if( pat.getType().isBoolean() && rpoleq.isNull() ){
+ if( options::literalMatchMode()==LITERAL_MATCH_USE ){
+ pat = NodeManager::currentNM()->mkNode( EQUAL, pat, NodeManager::currentNM()->mkConst( rpol==-1 ) ).negate();
+ }else if( options::literalMatchMode()!=LITERAL_MATCH_NONE ){
+ pat = NodeManager::currentNM()->mkNode( EQUAL, pat, NodeManager::currentNM()->mkConst( rpol==1 ) );
+ }
+ }else{
+ Assert( !rpoleq.isNull() );
+ if( rpol==-1 ){
+ if( options::literalMatchMode()!=LITERAL_MATCH_NONE ){
+ //all equivalence classes except rpoleq
+ pat = NodeManager::currentNM()->mkNode( EQUAL, pat, rpoleq ).negate();
+ }
+ }else if( rpol==1 ){
+ if( options::literalMatchMode()==LITERAL_MATCH_AGG ){
+ //only equivalence class rpoleq
+ pat = NodeManager::currentNM()->mkNode( EQUAL, pat, rpoleq );
+ }
+ //all equivalence classes that are not disequal to rpoleq TODO?
+ }
+ }
+ }
+ Trace("auto-gen-trigger-debug") << "...got : " << pat << std::endl;
+ }else{
+ if( Trigger::isRelationalTrigger( pat ) ){
+ //consider both polarities
+ addPatternToPool( f, pat.negate(), num_fv, mpat );
+ }
+ }
+ addPatternToPool( f, pat, num_fv, mpat );
+ }
+ }
+ //tinfo not used below this point
+ d_made_multi_trigger[f] = false;
+ Trace("auto-gen-trigger") << "Single trigger pool for " << f << " : " << std::endl;
+ for( unsigned i=0; i<d_patTerms[0][f].size(); i++ ){
+ Trace("auto-gen-trigger") << " " << d_patTerms[0][f][i] << std::endl;
+ }
+ if( !d_patTerms[1][f].empty() ){
+ Trace("auto-gen-trigger") << "Multi-trigger term pool for " << f << " : " << std::endl;
+ for( unsigned i=0; i<d_patTerms[1][f].size(); i++ ){
+ Trace("auto-gen-trigger") << " " << d_patTerms[1][f][i] << std::endl;
+ }
+ }
+ }
+
+ unsigned rmin = d_patTerms[0][f].empty() ? 1 : 0;
+ unsigned rmax = options::multiTriggerWhenSingle() ? 1 : rmin;
+ for( unsigned r=rmin; r<=rmax; r++ ){
+ std::vector< Node > 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; i<patTerms.size(); i++ ){
+ Assert( d_pat_to_mpat.find( patTerms[i] )!=d_pat_to_mpat.end() );
+ Assert( d_pat_to_mpat[patTerms[i]].hasOperator() );
+ sqfs.d_op_map[ patTerms[i] ] = d_pat_to_mpat[patTerms[i]].getOperator();
+ }
+ //sort based on # occurrences (this will cause Trigger to select rarer symbols)
+ std::sort( patTerms.begin(), patTerms.end(), sqfs );
+ Debug("relevant-trigger") << "Terms based on relevance: " << std::endl;
+ for( unsigned i=0; i<patTerms.size(); i++ ){
+ Debug("relevant-trigger") << " " << patTerms[i] << " from " << d_pat_to_mpat[patTerms[i]] << " (";
+ Debug("relevant-trigger") << d_quantEngine->getQuantifierRelevance()->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( index<patTerms.size() ){
+ //Notice() << "check add additional" << std::endl;
+ //check if similar patterns exist, and if so, add them additionally
+ unsigned nqfs_curr = 0;
+ if( options::relevantTriggers() ){
+ nqfs_curr = d_quantEngine->getQuantifierRelevance()->getNumQuantifiersForSymbol( patTerms[0].getOperator() );
+ }
+ index++;
+ bool success = true;
+ while( success && index<patTerms.size() && d_is_single_trigger[ patTerms[index] ] ){
+ success = false;
+ if( !options::relevantTriggers() ||
+ d_quantEngine->getQuantifierRelevance()->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]<q[0].getNumChildren() ){
+ //partial trigger : generate implication to mark user pattern
+ Node pat =
+ d_quantEngine->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; i<q[2].getNumChildren(); i++ ){
+ if( q[2][i].getKind()==INST_PATTERN ){
+ hasPat = true;
+ break;
+ }
+ }
+ d_hasUserPatterns[q] = hasPat;
+ return hasPat;
+ }else{
+ return it->second;
+ }
+ }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; i<f[0].getNumChildren(); i++ ){
+ Node x = d_quantEngine->getTermUtil()->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; i<patTerms.size(); i++ ){
+ Trace("local-t-ext") << " " << patTerms[i] << std::endl;
+ }
+ Trace("local-t-ext") << std::endl;
+ Trigger * tr = Trigger::mkTrigger( d_quantEngine, f, patTerms, true, Trigger::TR_GET_OLD );
+ d_lte_trigger[f] = tr;
+ }else{
+ Trace("local-t-ext") << "No local theory extensions trigger for " << f << "." << std::endl;
+ Trace("local-t-ext-warn") << "WARNING: not local theory extensions : " << f << std::endl;
+ }
+ d_quant[f] = ret;
+ return ret;
+ }else{
+ return itq->second;
+ }
+}
+*/
+
+} /* CVC4::theory::quantifiers namespace */
+} /* CVC4::theory namespace */
+} /* CVC4 namespace */
--- /dev/null
+/********************* */
+/*! \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
--- /dev/null
+/********************* */
+/*! \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; i<d_instStrategies.size(); ++i ){
+ d_instStrategies[i]->presolve();
+ }
+}
+
+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; i<d_quants.size(); i++ ){
+ Node q = d_quants[i];
+ Debug("inst-engine-debug") << "IE: Instantiate " << q << "..." << std::endl;
+ //int e_use = d_quantEngine->getRelevance( 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; j<d_instStrategies.size(); j++ ){
+ InstStrategy* is = d_instStrategies[j];
+ Trace("inst-engine-debug") << "Do " << is->identify() << " " << 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; i<d_instStrategies.size(); ++i ){
+ InstStrategy* is = d_instStrategies[i];
+ is->processResetInstantiationRound( 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; i<d_quantEngine->getModel()->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; i<q[2].getNumChildren(); i++ ){
+ if( q[2][i].getKind()==INST_PATTERN || q[2][i].getKind()==INST_NO_PATTERN ){
+ hasPat = true;
+ break;
+ }
+ }
+ if( hasPat ){
+ d_quantEngine->setOwner( q, this, 1 );
+ }
+ }
+}
+
+void InstantiationEngine::registerQuantifier( Node f ){
+ if( d_quantEngine->hasOwnership( f, this ) ){
+ //for( unsigned i=0; i<d_instStrategies.size(); ++i ){
+ // d_instStrategies[i]->registerQuantifier( 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);
+ }
+}
--- /dev/null
+/********************* */
+/*! \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 <memory>
+
+#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<InstStrategy*> d_instStrategies;
+ /** user-pattern instantiation strategy */
+ std::unique_ptr<InstStrategyUserPatterns> d_isup;
+ /** auto gen triggers; only kept for destructor cleanup */
+ std::unique_ptr<InstStrategyAutoGenTriggers> d_i_ag;
+
+ typedef context::CDHashMap<Node, bool, NodeHashFunction> BoolMap;
+ /** current processing quantified formulas */
+ std::vector<Node> 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 */
--- /dev/null
+/********************* */
+/*! \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<Node>& 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; i<d_nodes.size(); i++ ){
+ Trace("trigger") << " " << d_nodes[i] << std::endl;
+ }
+ if( d_nodes.size()==1 ){
+ if( isSimpleTrigger( d_nodes[0] ) ){
+ d_mg = new InstMatchGeneratorSimple(q, d_nodes[0], qe);
+ }else{
+ d_mg = InstMatchGenerator::mkInstMatchGenerator(q, d_nodes[0], qe);
+ }
+ }else{
+ if( options::multiTriggerCache() ){
+ d_mg = new InstMatchGeneratorMulti(q, d_nodes, qe);
+ }else{
+ d_mg = InstMatchGenerator::mkInstMatchGeneratorMulti(q, d_nodes, qe);
+ }
+ }
+ if( d_nodes.size()==1 ){
+ if( isSimpleTrigger( d_nodes[0] ) ){
+ ++(qe->d_statistics.d_triggers);
+ }else{
+ ++(qe->d_statistics.d_simple_triggers);
+ }
+ }else{
+ Trace("multi-trigger") << "Trigger for " << q << ": " << std::endl;
+ for( unsigned i=0; i<d_nodes.size(); i++ ){
+ Trace("multi-trigger") << " " << d_nodes[i] << std::endl;
+ }
+ ++(qe->d_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<temp.size(); i++ ){
+ bool foundVar = false;
+ for( unsigned j=0; j<varContains[ temp[i] ].size(); j++ ){
+ Node v = varContains[ temp[i] ][j];
+ Assert( quantifiers::TermUtil::getInstConstAttr(v)==q );
+ if( vars.find( v )==vars.end() ){
+ varCount++;
+ vars[ v ] = true;
+ foundVar = true;
+ }
+ }
+ if( foundVar ){
+ trNodes.push_back( temp[i] );
+ for( unsigned j=0; j<varContains[ temp[i] ].size(); j++ ){
+ Node v = varContains[ temp[i] ][j];
+ patterns[ v ].push_back( temp[i] );
+ }
+ }
+ if( varCount==n_vars ){
+ break;
+ }
+ }
+ if( varCount<n_vars ){
+ Trace("trigger-debug") << "Don't consider trigger since it does not contain specified number of variables." << std::endl;
+ for( unsigned i=0; i<nodes.size(); i++) {
+ Trace("trigger-debug") << nodes[i] << " ";
+ }
+ Trace("trigger-debug") << std::endl;
+
+ //do not generate multi-trigger if it does not contain all variables
+ return false;
+ }else{
+ //now, minimize the trigger
+ for( unsigned i=0; i<trNodes.size(); i++ ){
+ bool keepPattern = false;
+ Node n = trNodes[i];
+ for( unsigned j=0; j<varContains[ n ].size(); j++ ){
+ Node v = varContains[ n ][j];
+ if( patterns[v].size()==1 ){
+ keepPattern = true;
+ break;
+ }
+ }
+ if( !keepPattern ){
+ //remove from pattern vector
+ for( unsigned j=0; j<varContains[ n ].size(); j++ ){
+ Node v = varContains[ n ][j];
+ for( unsigned k=0; k<patterns[v].size(); k++ ){
+ if( patterns[v][k]==n ){
+ patterns[v].erase( patterns[v].begin() + k, patterns[v].begin() + k + 1 );
+ break;
+ }
+ }
+ }
+ //remove from trigger nodes
+ trNodes.erase( trNodes.begin() + i, trNodes.begin() + i + 1 );
+ i--;
+ }
+ }
+ }
+ return true;
+}
+
+Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >& 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<Node, std::vector<Node> > 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<n.getNumChildren(); i++ ){
+ if( !isUsable( n[i], q ) ){
+ return false;
+ }
+ }
+ return true;
+ }else if( n.getKind()==INST_CONSTANT ){
+ return true;
+ }else{
+ std::map< Node, Node > 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<t.getNumChildren(); i++ ){
+ if( t[i].getKind()!=INST_CONSTANT && quantifiers::TermUtil::hasInstConstAttr(t[i]) ){
+ return false;
+ }
+ }
+ if( options::purifyDtTriggers() && t.getKind()==APPLY_SELECTOR_TOTAL ){
+ return false;
+ }
+ if (t.getKind() == HO_APPLY && t[0].getKind() == INST_CONSTANT)
+ {
+ return false;
+ }
+ return true;
+ }else{
+ return false;
+ }
+}
+
+//store triggers in reqPol, indicating their polarity (if any) they must appear to falsify the quantified formula
+void Trigger::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 ){
+ 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<nrec.getNumChildren(); i++ ){
+ bool newHasPol, newPol;
+ bool newHasEPol, newEPol;
+ QuantPhaseReq::getPolarity( nrec, i, hasPol, pol, newHasPol, newPol );
+ QuantPhaseReq::getEntailPolarity( nrec, i, hasEPol, epol, newHasEPol, newEPol );
+ collectPatTerms2( q, nrec[i], visited, tinfo, tstrt, exclude, added2, newPol, newHasPol, newEPol, newHasEPol );
+ }
+ // if this is not a usable trigger, don't worry about caching the results, since triggers do not contain non-usable subterms
+ if( !nu.isNull() ){
+ bool rm_nu = false;
+ for( unsigned i=0; i<added2.size(); i++ ){
+ Trace("auto-gen-trigger-debug2") << "..." << nu << " added child " << i << " : " << added2[i] << std::endl;
+ Assert( added2[i]!=nu );
+ // if child was not already removed
+ if( tinfo.find( added2[i] )!=tinfo.end() ){
+ if( tstrt==quantifiers::TRIGGER_SEL_MAX || ( tstrt==quantifiers::TRIGGER_SEL_MIN_SINGLE_MAX && !nu_single ) ){
+ //discard all subterms
+ Trace("auto-gen-trigger-debug2") << "......remove it." << std::endl;
+ visited[ added2[i] ].clear();
+ tinfo.erase( added2[i] );
+ }else{
+ if( tinfo[ nu ].d_fv.size()==tinfo[ added2[i] ].d_fv.size() ){
+ if (tinfo[nu].d_weight >= 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; i<itv->second.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<BitVector>().toInteger()==1 &&
+ n[2].getConst<BitVector>().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<n.getNumChildren(); i++ ){
+ if( !isPureTheoryTrigger( n[i] ) ){
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+int Trigger::getTriggerWeight( Node n ) {
+ if (n.getKind() == APPLY_UF)
+ {
+ return 0;
+ }
+ else if (isAtomicTrigger(n))
+ {
+ return 1;
+ }else{
+ if( options::relationalTriggers() ){
+ if( isRelationalTrigger( n ) ){
+ for( unsigned i=0; i<2; i++ ){
+ if( n[i].getKind()==INST_CONSTANT && !quantifiers::TermUtil::hasInstConstAttr( n[1-i] ) ){
+ return 0;
+ }
+ }
+ }
+ }
+ return 2;
+ }
+}
+
+bool Trigger::isLocalTheoryExt( Node n, std::vector< Node >& 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<n.getNumChildren(); i++ ){
+ if( n[i].getKind()==INST_CONSTANT ){
+ hasVar = true;
+ if( std::find( vars.begin(), vars.end(), n[i] )==vars.end() ){
+ vars.push_back( n[i] );
+ }else{
+ //do not allow duplicate variables
+ return false;
+ }
+ }else{
+ //do not allow nested function applications
+ return false;
+ }
+ }
+ if( hasVar ){
+ patTerms.push_back( n );
+ }
+ }
+ }else{
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ if( !isLocalTheoryExt( n[i], vars, patTerms ) ){
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void Trigger::collectPatTerms( Node q, Node n, std::vector< Node >& 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<temp.size(); i++ ){
+ //copy information
+ tinfo[temp[i]].d_fv.insert( tinfo[temp[i]].d_fv.end(), tinfo2[temp[i]].d_fv.begin(), tinfo2[temp[i]].d_fv.end() );
+ tinfo[temp[i]].d_reqPol = tinfo2[temp[i]].d_reqPol;
+ tinfo[temp[i]].d_reqPolEq = tinfo2[temp[i]].d_reqPolEq;
+ patTerms.push_back( temp[i] );
+ }
+ return;
+ }else{
+ //do not consider terms that have instances
+ for( unsigned i=0; i<patTerms2.size(); i++ ){
+ if( std::find( temp.begin(), temp.end(), patTerms2[i] )==temp.end() ){
+ visited[ patTerms2[i] ].clear();
+ }
+ }
+ }
+ }
+ std::vector< Node > 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<Node>& fv1,
+ std::vector<Node>& fv2)
+{
+ Assert(n1 != n2);
+ int status = 0;
+ std::unordered_set<TNode, TNodeHashFunction> subs_vars;
+ std::unordered_set<std::pair<TNode, TNode>,
+ PairHashFunction<TNode,
+ TNode,
+ TNodeHashFunction,
+ TNodeHashFunction> >
+ visited;
+ std::vector<std::pair<TNode, TNode> > visit;
+ std::pair<TNode, TNode> cur;
+ TNode cur1;
+ TNode cur2;
+ visit.push_back(std::pair<TNode, TNode>(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<TNode, TNode>(cur1, cur2));
+ for (unsigned i = 0, size = cur1.getNumChildren(); i < size; i++)
+ {
+ if (cur1[i] != cur2[i])
+ {
+ visit.push_back(std::pair<TNode, TNode>(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<Node>& 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<Node>& nodes)
+{
+ std::map<unsigned, std::vector<Node> > fvs;
+ for (unsigned i = 0, size = nodes.size(); i < size; i++)
+ {
+ quantifiers::TermUtil::computeVarContains(nodes[i], fvs[i]);
+ }
+ std::vector<bool> active;
+ active.resize(nodes.size(), true);
+ for (unsigned i = 0, size = nodes.size(); i < size; i++)
+ {
+ std::vector<Node>& 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<Node> 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<n.getNumChildren(); i++ ){
+ if( quantifiers::TermUtil::hasInstConstAttr(n[i]) ){
+ if( ret.isNull() ){
+ ret = getInversionVariable( n[i] );
+ if( ret.isNull() ){
+ Trace("var-trigger-debug") << "No : multiple variables " << n << std::endl;
+ return Node::null();
+ }
+ }else{
+ return Node::null();
+ }
+ }else if( n.getKind()==MULT ){
+ if( !n[i].isConst() ){
+ Trace("var-trigger-debug") << "No : non-linear coefficient " << n << std::endl;
+ return Node::null();
+ }
+ /*
+ else if( n.getType().isInteger() ){
+ Rational r = n[i].getConst<Rational>();
+ 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; i<n.getNumChildren(); i++ ){
+ if( !quantifiers::TermUtil::hasInstConstAttr(n[i]) ){
+ if( n.getKind()==PLUS ){
+ x = NodeManager::currentNM()->mkNode( 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<Rational>().abs() );
+ if( !n[i].getConst<Rational>().abs().isOne() ){
+ x = NodeManager::currentNM()->mkNode( INTS_DIVISION_TOTAL, x, coeff );
+ }
+ if( n[i].getConst<Rational>().sgn()<0 ){
+ x = NodeManager::currentNM()->mkNode( UMINUS, x );
+ }
+ }else{
+ Node coeff = NodeManager::currentNM()->mkConst( Rational(1) / n[i].getConst<Rational>() );
+ 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<Node>& 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; i<patTerms.size(); i++ ){
+ quantifiers::TermUtil::getVarContainsNode( q, patTerms[i], t_vars );
+ }
+}
+
+int Trigger::getActiveScore() {
+ return d_mg->getActiveScore( 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<d_tr.size(); i++ ){
+ delete d_tr[i];
+ }
+}
+
+inst::Trigger* TriggerTrie::getTrigger(std::vector<Node>& nodes)
+{
+ std::vector<Node> 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<TNode, TriggerTrie*>::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<Node>& nodes, inst::Trigger* t)
+{
+ std::vector<Node> 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<TNode, TriggerTrie*>::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 */
--- /dev/null
+/********************* */
+/*! \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 <map>
+
+#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<Node> 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<Node>& 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<Node>& nodes,
+ unsigned n_vars,
+ std::vector<Node>& 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<Node>& t_vars);
+
+ protected:
+ /** trigger constructor, intentionally protected (use Trigger::mkTrigger). */
+ Trigger(QuantifiersEngine* ie, Node q, std::vector<Node>& 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<Node>& 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<Node>& fv1,
+ std::vector<Node>& 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<Node>& 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<Node>& nodes, Trigger* t);
+
+ private:
+ /** The trigger at this node in the trie. */
+ std::vector<Trigger*> 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 */
#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"
--- /dev/null
+/********************* */
+/*! \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<fapps.size(); i++ ){
+ // std::cout << "...." << fapps[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; i<fapps.size(); i++ ){
+ unsigned r = m->getRepresentativeId( 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()<minSize ){
+ minSize = it->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; i<to_erase.size(); i++ ){
+ d_def.erase( to_erase[i] );
+ }
+ }
+ //if constant, propagate the value upwards
+ if( isConstant ){
+ d_value = defVal;
+ }else{
+ d_value = val_none;
+ }
+ }
+}
+
+void AbsDef::debugPrintUInt( const char * c, unsigned dSize, unsigned u ) const{
+ for( unsigned i=0; i<dSize; i++ ){
+ Trace(c) << ( ( u & ( 1 << i ) )!=0 ? "1" : "0");
+ }
+ //Trace(c) << "(";
+ //for( unsigned i=0; i<32; i++ ){
+ // Trace(c) << ( ( u & ( 1 << i ) )!=0 ? "1" : "0");
+ //}
+ //Trace(c) << ")";
+}
+
+void AbsDef::debugPrint( const char * c, FirstOrderModelAbs * m, TNode f, unsigned depth ) const{
+ if( Trace.isOn(c) ){
+ if( depth==f.getNumChildren() ){
+ for( unsigned i=0; i<depth; i++ ){ Trace(c) << " ";}
+ Trace(c) << "V[" << d_value << "]" << std::endl;
+ }else{
+ TypeNode tn = f[depth].getType();
+ const RepSet* rs = m->getRepSet();
+ 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; i<depth; i++ ){ Trace(c) << " ";}
+ debugPrintUInt( c, dSize, it->first );
+ 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<defs.size(); i++ ){
+ //process each simple child
+ for( std::map< unsigned, AbsDef >::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; j<defs.size(); j++ ){
+ defs[j]->get_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; j<defs.size(); j++ ){
+ defs[j]->get_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( depth<n.getNumChildren() ){
+ TypeNode tn = q.isNull() ? n[depth].getType() : m->getVariable( 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; i<entry.size(); i++ ){
+ unsigned dSize =
+ rs->getNumRepresentatives(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; i<numReps; i++ ){
+ curr = 1 << i;
+ d_def[curr].construct_var_eq( m, q, v1, v2, curr, currv, depth+1 );
+ }
+ d_default = curr;
+ }else{
+ d_def[curr].construct_var_eq( m, q, v1, v2, curr, 1, depth+1 );
+ dom = dom & ~curr;
+ d_def[dom].construct_var_eq( m, q, v1, v2, curr, 0, depth+1 );
+ d_default = dom;
+ }
+ }
+ }
+}
+
+void AbsDef::construct_var( FirstOrderModelAbs * m, TNode q, unsigned v, int currv, unsigned depth ) {
+ if( depth==q[0].getNumChildren() ){
+ Assert( currv!=val_none );
+ d_value = currv;
+ }else{
+ TypeNode tn = m->getVariable( q, depth ).getType();
+ if( v==depth ){
+ unsigned numReps = m->getRepSet()->getNumRepresentatives(tn);
+ Assert( numReps>0 && numReps < 32 );
+ for( unsigned i=0; i<numReps; i++ ){
+ d_def[ 1 << i ].construct_var( m, q, v, i, depth+1 );
+ }
+ d_default = 1 << (numReps - 1);
+ }else{
+ unsigned dom = m->d_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; i<count; i++ ){
+ entry.push_back( m->d_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<count; i++ ){
+ entry.pop_back();
+ entry_def.pop_back();
+ }
+ return;
+ }
+ }
+ }
+ if( entry.size()==q[0].getNumChildren() ){
+ if( f ){
+ if( Trace.isOn("ambqi-check-debug2") ){
+ for( unsigned i=0; i<entry.size(); i++ ){ Trace("ambqi-check-debug2") << " "; }
+ Trace("ambqi-check-debug2") << "Evaluate uninterpreted function entry..." << std::endl;
+ }
+ //we are composing with an uninterpreted function
+ std::vector< int > 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; i<entry.size(); i++ ){ Trace("ambqi-check-debug2") << " "; }
+ Trace("ambqi-check-debug2") << "Evaluate interpreted function entry ( ";
+ for( unsigned i=0; i<values.size(); i++ ){
+ Assert( !values[i].isNull() );
+ Trace("ambqi-check-debug2") << values[i] << " ";
+ }
+ Trace("ambqi-check-debug2") << ")..." << std::endl;
+ }
+ //evaluate
+ Node vv = NodeManager::currentNM()->mkNode( 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<entry.size(); i++ ){ Trace("ambqi-check-debug2") << " "; }
+ Trace("ambqi-check-debug2") << "Take product of arguments" << std::endl;
+ }
+ for( std::map< unsigned, AbsDef * >::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; i<entry.size(); i++ ){ Trace("ambqi-check-debug2") << " "; }
+ Trace("ambqi-check-debug2") << "...process : ";
+ debugPrintUInt("ambqi-check-debug2",
+ rs->getNumRepresentatives(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<entry.size(); i++ ){ Trace("ambqi-check-debug2") << " "; }
+ Trace("ambqi-check-debug2") << "Make default argument" << std::endl;
+ }
+ std::map< unsigned, AbsDef * > 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; i<entry.size(); i++ ){ Trace("ambqi-check-debug2") << " "; }
+ Trace("ambqi-check-debug2") << "...process default : ";
+ debugPrintUInt(
+ "ambqi-check-debug2", rs->getNumRepresentatives(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<n.getNumChildren(); i++ ){
+ Trace("ambqi-check-debug3") << i << " : ";
+ Trace("ambqi-check-debug3") << ((children.find( i )!=children.end()) ? "X" : ".");
+ if( bchildren.find( i )!=bchildren.end() ){
+ Trace("ambqi-check-debug3") << bchildren[i];
+ }else{
+ Trace("ambqi-check-debug3") << ".";
+ }
+ if( vchildren.find( i )!=vchildren.end() ){
+ Trace("ambqi-check-debug3") << vchildren[i];
+ }else{
+ Trace("ambqi-check-debug3") << ".";
+ }
+ Trace("ambqi-check-debug3") << std::endl;
+ }
+ Trace("ambqi-check-debug3") << "varChCount : " << varChCount << std::endl;
+ }
+ if( varChCount==0 || f ){
+ //short-circuit
+ if( n.getKind()==AND || n.getKind()==OR ){
+ for( std::map< unsigned, int >::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; i<args.size(); i++ ){
+ unsigned v = 1 << m->getRepresentativeId( 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<TypeNode, std::vector<Node> >::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; i<it->second.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<Node, AbsDef * >::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; i<itut->second.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; i<fapps[0].getNumChildren(); i++ ){
+ if( fm->d_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; i<fm->d_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<Node, AbsDef * >::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; i<q[0].getNumChildren(); i++ ){
+ if( !fma->isValidType( 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; i<n.getNumChildren(); i++ ){
+ if( n[i].getKind()==FORALL ){
+ bchildren[i] = AbsDef::val_unk;
+ }else if( n[i].getKind() == BOUND_VARIABLE ){
+ varChCount++;
+ vchildren[i] = m->d_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 */
--- /dev/null
+/********************* */
+/*! \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
--- /dev/null
+/********************* */
+/*! \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 || vrange<d_curr_range ){
+ Trace("bound-int-bound") << "Successfully bound " << d_range << " to " << vrange << std::endl;
+ d_curr_range = vrange;
+ }
+ //set the range
+ d_has_range = true;
+ }
+ }else{
+ Message() << "Could not find literal " << nlit << " for range " << d_range << std::endl;
+ exit(0);
+ }
+}
+
+void BoundedIntegers::IntRangeModel::allocateRange() {
+ d_curr_max++;
+ int newBound = d_curr_max;
+ Trace("bound-int-proc") << "Allocate range bound " << newBound << " for " << d_range << std::endl;
+ //TODO: newBound should be chosen in a smarter way
+ Node ltr = NodeManager::currentNM()->mkNode( 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 || i<d_curr_range ){
+ Node rn = (*it).first;
+ Assert( !rn.isNull() );
+ if( d_range_assertions.find( rn )==d_range_assertions.end() ){
+ if (!d_lit_to_pol[rn]) {
+ rn = rn.negate();
+ }
+ Trace("bound-int-dec-debug") << "For " << d_range << ", make decision " << rn << " to make range " << i << std::endl;
+ return rn;
+ }
+ }
+ }
+ return Node::null();
+}
+
+bool BoundedIntegers::IntRangeModel::proxyCurrentRange() {
+ //Trace("model-engine") << "Range(" << d_range << ") currently is " << d_curr_max.get() << std::endl;
+ if( d_range!=d_proxy_range ){
+ //int curr = d_curr_range.get();
+ int curr = d_curr_max;
+ if( d_ranges_proxied.find( curr )==d_ranges_proxied.end() ){
+ d_ranges_proxied[curr] = true;
+ Assert( d_range_literal.find( curr )!=d_range_literal.end() );
+ Node lem = NodeManager::currentNM()->mkNode( 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<b.getNumChildren(); i++ ){
+ if( hasNonBoundVar( f, b[i], visited ) ){
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+bool BoundedIntegers::hasNonBoundVar( Node f, Node b ) {
+ std::map< Node, bool > 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<n.getNumChildren(); i++ ){
+ processMatchBoundVars( q, n[i], bvs, visited );
+ }
+ }
+ }
+}
+
+void BoundedIntegers::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 ){
+ if( n.getKind()==OR || n.getKind()==AND ){
+ if( (n.getKind()==OR)==pol ){
+ for( unsigned i=0; i<n.getNumChildren(); i++) {
+ process( q, n[i], pol, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term, bound_fixed_set );
+ }
+ }else{
+ //if we are ( x != t1 ^ ...^ x != tn ), then x can be bound to { t1...tn }
+ Node conj = n;
+ if( !pol ){
+ conj = TermUtil::simpleNegate( conj );
+ }
+ Trace("bound-int-debug") << "Process possible finite disequality conjunction : " << conj << std::endl;
+ Assert( conj.getKind()==AND );
+ Node v;
+ std::vector< Node > v_cases;
+ bool success = true;
+ for( unsigned i=0; i<conj.getNumChildren(); i++ ){
+ if( conj[i].getKind()==NOT && processEqDisjunct( q, conj[i][0], v, v_cases ) ){
+ //continue
+ }else{
+ Trace("bound-int-debug") << "...failed due to " << conj[i] << std::endl;
+ success = false;
+ break;
+ }
+ }
+ if( success && !isBound( q, v ) ){
+ Trace("bound-int-debug") << "Success with variable " << v << std::endl;
+ bound_lit_type_map[v] = BOUND_FIXED_SET;
+ bound_lit_map[3][v] = n;
+ bound_lit_pol_map[3][v] = pol;
+ bound_fixed_set[v].clear();
+ bound_fixed_set[v].insert( bound_fixed_set[v].end(), v_cases.begin(), v_cases.end() );
+ }
+ }
+ }else if( n.getKind()==EQUAL ){
+ if( !pol ){
+ // non-applied DER on x != t, x can be bound to { t }
+ Node v;
+ std::vector< Node > 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; i<bound_vars.size(); i++ ){
+ Node v = bound_vars[i];
+ Trace("bound-int-debug") << "literal (polarity = " << pol << ") " << n << " is membership." << std::endl;
+ bound_lit_type_map[v] = BOUND_SET_MEMBER;
+ bound_lit_map[2][v] = n;
+ bound_lit_pol_map[2][v] = pol;
+ }
+ }
+ }else{
+ Assert( n.getKind()!=LEQ && n.getKind()!=LT && n.getKind()!=GT );
+ }
+}
+
+bool BoundedIntegers::needsCheck( Theory::Effort e ) {
+ return e==Theory::EFFORT_LAST_CALL;
+}
+
+void BoundedIntegers::check(Theory::Effort e, QEffort quant_e)
+{
+ if (quant_e == QEFFORT_STANDARD)
+ {
+ Trace("bint-engine") << "---Bounded Integers---" << std::endl;
+ bool addedLemma = false;
+ //make sure proxies are up-to-date with range
+ for( unsigned i=0; i<d_ranges.size(); i++) {
+ if( d_rms[d_ranges[i]]->proxyCurrentRange() ){
+ 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; i<bound_fixed_set[v].size(); i++ ){
+ Node t = bound_fixed_set[v][i];
+ if( t.hasBoundVar() ){
+ d_fixed_set_ngr_range[f][v].push_back( t );
+ }else{
+ d_fixed_set_gr_range[f][v].push_back( t );
+ }
+ }
+ Trace("bound-int") << "Variable " << v << " is bound because of disequality conjunction " << bound_lit_map[3][v] << std::endl;
+ }
+ if( setBoundVar ){
+ success = true;
+ //set Attributes on literals
+ for( unsigned b=0; b<2; b++ ){
+ if( bound_lit_map[b].find( v )!=bound_lit_map[b].end() ){
+ Assert( bound_lit_pol_map[b].find( v )!=bound_lit_pol_map[b].end() );
+ BoundIntLitAttribute bila;
+ bound_lit_map[b][v].setAttribute( bila, bound_lit_pol_map[b][v] ? 1 : 0 );
+ }else{
+ Assert( it->second!=BOUND_INT_RANGE );
+ }
+ }
+ }
+ }
+ }
+ if( !success ){
+ //resort to setting a finite bound on a variable
+ for( unsigned i=0; i<f[0].getNumChildren(); i++) {
+ if( d_bound_type[f].find( f[0][i] )==d_bound_type[f].end() ){
+ TypeNode tn = f[0][i].getType();
+ if (tn.isSort()
+ || d_quantEngine->getTermEnumeration()->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<f[0].getNumChildren(); i++) {
+ Node v = f[0][i];
+ if( std::find( d_set[f].begin(), d_set[f].end(), v )!=d_set[f].end() ){
+ Assert( d_bound_type[f].find( v )!=d_bound_type[f].end() );
+ if( d_bound_type[f][v]==BOUND_INT_RANGE ){
+ Trace("bound-int") << " " << d_bounds[0][f][v] << " <= " << v << " <= " << d_bounds[1][f][v] << " (range is " << d_range[f][v] << ")" << std::endl;
+ }else if( d_bound_type[f][v]==BOUND_SET_MEMBER ){
+ if( d_setm_range_lit[f][v][0]==v ){
+ Trace("bound-int") << " " << v << " in " << d_setm_range[f][v] << std::endl;
+ }else{
+ Trace("bound-int") << " " << v << " unifiable in " << d_setm_range_lit[f][v] << std::endl;
+ }
+ }else if( d_bound_type[f][v]==BOUND_FIXED_SET ){
+ Trace("bound-int") << " " << v << " in { ";
+ for( unsigned i=0; i<d_fixed_set_ngr_range[f][v].size(); i++ ){
+ Trace("bound-int") << d_fixed_set_ngr_range[f][v][i] << " ";
+ }
+ for( unsigned i=0; i<d_fixed_set_gr_range[f][v].size(); i++ ){
+ Trace("bound-int") << d_fixed_set_gr_range[f][v][i] << " ";
+ }
+ Trace("bound-int") << "}" << std::endl;
+ }else if( d_bound_type[f][v]==BOUND_FINITE ){
+ Trace("bound-int") << " " << v << " has small finite type." << std::endl;
+ }else{
+ Trace("bound-int") << " " << v << " has unknown bound." << std::endl;
+ Assert( false );
+ }
+ }else{
+ Trace("bound-int") << " " << "*** " << v << " is unbounded." << std::endl;
+ }
+ }
+ }
+
+ bool bound_success = true;
+ for( unsigned i=0; i<f[0].getNumChildren(); i++) {
+ if( d_bound_type[f].find( f[0][i] )==d_bound_type[f].end() ){
+ Trace("bound-int-warn") << "Warning : Bounded Integers : Due to quantification on " << f[0][i] << ", could not find bounds for " << f << std::endl;
+ bound_success = false;
+ break;
+ }
+ }
+
+ if( bound_success ){
+ d_bound_quants.push_back( f );
+ for( unsigned i=0; i<d_set[f].size(); i++) {
+ Node v = d_set[f][i];
+ std::map< Node, Node >::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; i<d_lit_to_ranges[nlit].size(); i++) {
+ Node r = d_lit_to_ranges[nlit][i];
+ Trace("bound-int-assert") << " ...this is a bounding literal for " << r << std::endl;
+ d_rms[r]->assertNode( 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; i<d_ranges.size(); i++) {
+ Node d = d_rms[d_ranges[i]]->getNextDecisionRequest();
+ 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<Assertion>::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; i<vindex; i++) {
+ Assert( d_set_nums[q][d_set[q][i]]==i );
+ Trace("bound-int-rsi") << "Look up the value for " << d_set[q][i] << " " << i << std::endl;
+ int v = rsi->getVariableOrder( 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; i<t.getNumChildren(); i++ ){
+ Node u;
+ if( e.getKind()==kind::APPLY_CONSTRUCTOR ){
+ u = matchBoundVar( v, t[i], e[i] );
+ }else{
+ Node se = NodeManager::currentNM()->mkNode( kind::APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[index].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<Rational>().getNumerator().getLong()+1;
+ Trace("bound-int-rsi") << "Actual bound range is " << rr << std::endl;
+ for( unsigned k=0; k<rr; k++ ){
+ Node t = NodeManager::currentNM()->mkNode(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<elements_tmp.size(); i++ ){
+ //do matching to determine v -> 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; i<it->second.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; i<it->second.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;
+ }
+}
+
--- /dev/null
+/********************* */
+/*! \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<Node, bool, NodeHashFunction> NodeBoolMap;
+ typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap;
+ typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap;
+ typedef context::CDHashMap<int, bool> 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
--- /dev/null
+/********************* */
+/*! \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<Node, unsigned, NodeHashFunction> 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<Rational>() < d_terms[j].getConst<Rational>() );
+ }
+};
+
+
+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<Node,EntryTrie>::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<Node> & 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<Node,EntryTrie>::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 && gindex<minIndex) ){
+ minIndex = gindex;
+ }
+ }
+ }
+ }else{
+ Node st = m->getStar(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<minIndex) ){
+ minIndex = gindex;
+ }
+ }
+ }
+ return minIndex;
+ }
+}
+
+void EntryTrie::addEntry( FirstOrderModelFmc * m, Node c, Node v, int data, int index ) {
+ if (index==(int)c.getNumChildren()) {
+ if(d_data==-1) {
+ d_data = data;
+ }
+ }
+ else {
+ d_child[ c[index] ].addEntry(m,c,v,data,index+1);
+ if( d_complete==0 ){
+ d_complete = -1;
+ }
+ }
+}
+
+void EntryTrie::getEntries( FirstOrderModelFmc * m, Node c, std::vector<int> & compat, std::vector<int> & 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<Node,EntryTrie>::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<Node,EntryTrie>::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<int> compat;
+ std::vector<int> gen;
+ d_et.getEntries(m, c, compat, gen);
+ for( unsigned i=0; i<compat.size(); i++) {
+ if( d_status[compat[i]]==status_unk ){
+ if( d_value[compat[i]]!=v ){
+ d_status[compat[i]] = status_non_redundant;
+ }
+ }
+ }
+ for( unsigned i=0; i<gen.size(); i++) {
+ if( d_status[gen[i]]==status_unk ){
+ if( d_value[gen[i]]==v ){
+ d_status[gen[i]] = status_redundant;
+ }
+ }
+ }
+ d_status.push_back( status_unk );
+ }
+ d_et.addEntry(m, c, v, newIndex);
+ d_cond.push_back(c);
+ d_value.push_back(v);
+ return true;
+}
+
+Node Def::evaluate( FirstOrderModelFmc * m, std::vector<Node>& 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<Node>& 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<d_status.size(); i++) {
+ if( d_status[i]!=status_redundant ){
+ addEntry(m, cond[i], value[i]);
+ }
+ }
+ d_status.clear();
+}
+
+void Def::simplify(FullModelChecker * mc, FirstOrderModelFmc * m) {
+ Trace("fmc-simplify") << "Simplify definition, #cond = " << d_cond.size() << std::endl;
+ basic_simplify( m );
+ Trace("fmc-simplify") << "post-basic simplify, #cond = " << d_cond.size() << std::endl;
+
+ //check if the last entry is not all star, if so, we can make the last entry all stars
+ if( !d_cond.empty() ){
+ bool last_all_stars = true;
+ Node cc = d_cond[d_cond.size()-1];
+ for( unsigned i=0; i<cc.getNumChildren(); i++ ){
+ if( !m->isInterval(cc[i]) && !m->isStar(cc[i]) ){
+ last_all_stars = false;
+ break;
+ }
+ }
+ if( !last_all_stars ){
+ Trace("fmc-cover-simplify") << "Need to modify last entry to be all stars." << std::endl;
+ Trace("fmc-cover-simplify") << "Before: " << std::endl;
+ debugPrint("fmc-cover-simplify",Node::null(), mc);
+ Trace("fmc-cover-simplify") << std::endl;
+ 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();
+ 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; i<cond.size(); i++) {
+ addEntry(m, cond[i], value[i]);
+ }
+ Trace("fmc-cover-simplify") << "Finished re-adding entries." << std::endl;
+ basic_simplify( m );
+ Trace("fmc-cover-simplify") << "After: " << std::endl;
+ debugPrint("fmc-cover-simplify",Node::null(), mc);
+ Trace("fmc-cover-simplify") << std::endl;
+ }
+ }
+ Trace("fmc-simplify") << "finish simplify, #cond = " << d_cond.size() << std::endl;
+}
+
+void Def::debugPrint(const char * tr, Node op, FullModelChecker * m) {
+ if (!op.isNull()) {
+ Trace(tr) << "Model for " << op << " : " << std::endl;
+ }
+ for( unsigned i=0; i<d_cond.size(); i++) {
+ //print the condition
+ if (!op.isNull()) {
+ Trace(tr) << op;
+ }
+ m->debugPrintCond(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<Node, Def * >::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ) {
+ Node op = it->first;
+ TypeNode tno = op.getType();
+ for( unsigned i=0; i<tno.getNumChildren(); i++) {
+ preInitializeType( fm, tno[i] );
+ }
+ }
+ //do not have to introduce terms for sorts of domains of quantified formulas if we are allowed to assume empty sorts
+ if( !options::fmfEmptySorts() ){
+ for( unsigned i=0; i<fm->getNumAssertedQuantifiers(); i++ ){
+ Node q = fm->getAssertedQuantifier( i );
+ //make sure all types are set
+ for( unsigned j=0; j<q[0].getNumChildren(); j++ ){
+ preInitializeType( fm, q[0][j].getType() );
+ }
+ }
+ }
+ return true;
+}
+
+bool FullModelChecker::processBuildModel(TheoryModel* m){
+ FirstOrderModelFmc * fm = ((FirstOrderModelFmc*)m)->asFirstOrderModelFmc();
+ 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<TypeNode, std::vector<Node> >::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; a<it->second.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; i<eqc.size(); i++ ){
+ Trace("fmc-model-debug") << eqc[i] << ", ";
+ }
+ Trace("fmc-model-debug") << "}" << std::endl;
+ }
+ d_rep_ids[it->first][r] = (int)a;
+ }
+ Trace("fmc-model-debug") << std::endl;
+ }
+ }
+
+ //now, make models
+ for( std::map<Node, Def * >::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; i<itut->second.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<add_conds.size(); i++ ){
+ Node c = add_conds[i];
+ Node v = add_values[i];
+ std::vector< Node > children;
+ std::vector< Node > entry_children;
+ children.push_back(op);
+ entry_children.push_back(op);
+ bool hasNonStar = false;
+ for( unsigned i=0; i<c.getNumChildren(); i++) {
+ Node ri = fm->getRepresentative( 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; i<fm->d_uf_terms[op].size(); i++ ){
+ std::vector< Node > inst;
+ for( unsigned j=0; j<fm->d_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<Node, Def * >::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; j<n.getNumChildren(); j++) {
+ if( j>0 ) 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; i<f[0].getNumChildren(); i++){
+ types.push_back(f[0][i].getType());
+ }
+ TypeNode typ = NodeManager::currentNM()->mkFunctionType( 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<d_quant_models[f].d_cond.size(); i++) {
+ if( d_quant_models[f].d_value[i]!=d_true ) {
+ Trace("fmc-inst") << "Instantiate based on " << d_quant_models[f].d_cond[i] << "..." << std::endl;
+ bool hasStar = false;
+ std::vector< Node > inst;
+ for (unsigned j=0; j<d_quant_models[f].d_cond[i].getNumChildren(); j++) {
+ if (fmfmc->isStar(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<Node>& 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<dc.d_cond.size(); i++) {
+ if (!dc.d_value[i].isNull()) {
+ dc.d_value[i] = dc.d_value[i]==d_true ? d_false : ( dc.d_value[i]==d_false ? d_true : dc.d_value[i] );
+ }
+ }
+}
+
+void FullModelChecker::doVariableEquality( FirstOrderModelFmc * fm, Node f, Def & d, Node eq ) {
+ std::vector<Node> 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; i<dc.d_cond.size(); i++) {
+ Node val = dc.d_value[i];
+ if( val.isNull() ){
+ d.addEntry( fm, dc.d_cond[i], val);
+ }else{
+ if( dc.d_cond[i][j]!=val ){
+ if (fm->isStar(dc.d_cond[i][j])) {
+ std::vector<Node> 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<Node> & val ) {
+ Trace("fmc-uf-process") << "process at " << index << std::endl;
+ for( unsigned i=1; i<cond.size(); i++) {
+ debugPrint("fmc-uf-process", cond[i], true);
+ Trace("fmc-uf-process") << " ";
+ }
+ Trace("fmc-uf-process") << std::endl;
+ if (index==(int)dc.size()) {
+ //we have an entry, now do actual compose
+ std::map< int, Node > 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<df.d_cond.size(); e++ ){
+ if ( entries.find(e)!=entries.end() ){
+ Trace("fmf-uf-process-debug") << "Add entry..." << std::endl;
+ d.addEntry(fm, entries[e], df.d_value[e] );
+ Trace("fmf-uf-process-debug") << "Done add entry." << std::endl;
+ }
+ }
+ }
+ }else{
+ for (unsigned i=0; i<dc[index].d_cond.size(); i++) {
+ if (isCompat(fm, cond, dc[index].d_cond[i])!=0) {
+ std::vector< Node > 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<cond.size(); i++) {
+ debugPrint("fmc-uf-process", cond[i], true);
+ Trace("fmc-uf-process") << " ";
+ }
+ Trace("fmc-uf-process") << std::endl;
+ if (index==(int)val.size()) {
+ Node c = mkCond(cond);
+ Trace("fmc-uf-entry") << "Entry : " << c << " -> 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<Node, EntryTrie>::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<Node, EntryTrie>::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<Node, EntryTrie>::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<Node> & val ) {
+ Trace("fmc-if-process") << "int compose " << index << " / " << dc.size() << std::endl;
+ for( unsigned i=1; i<cond.size(); i++) {
+ debugPrint("fmc-if-process", cond[i], true);
+ Trace("fmc-if-process") << " ";
+ }
+ Trace("fmc-if-process") << std::endl;
+ if ( index==(int)dc.size() ){
+ Node c = mkCond(cond);
+ Node v = evaluateInterpreted(n, val);
+ d.addEntry(fm, c, v);
+ }
+ else {
+ TypeNode vtn = n.getType();
+ for (unsigned i=0; i<dc[index].d_cond.size(); i++) {
+ if (isCompat(fm, cond, dc[index].d_cond[i])!=0) {
+ std::vector< Node > 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; i<cond.size(); i++) {
+ if( options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL && cond[i].getType().isInteger() ){
+ Node iv = doIntervalMeet( fm, cond[i], c[i-1], false );
+ if( iv.isNull() ){
+ return 0;
+ }
+ }else{
+ if( cond[i]!=c[i-1] && !fm->isStar(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; i<cond.size(); i++) {
+ if( cond[i]!=c[i-1] ) {
+ if( options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL && cond[i].getType().isInteger() ){
+ Node iv = doIntervalMeet( fm, cond[i], c[i-1] );
+ if( !iv.isNull() ){
+ cond[i] = iv;
+ }else{
+ return false;
+ }
+ }else{
+ if( fm->isStar(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<Rational>() < b2.getConst<Rational>() ){
+ 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<Rational>() < b[1].getConst<Rational>() ){
+ 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; i<f[0].getNumChildren(); i++) {
+ Node ts = fm->getStarElement( 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; i<n.getNumChildren(); i++ ){
+ cond.push_back( n[i] );
+ }
+}
+
+Node FullModelChecker::mkArrayCond( Node a ) {
+ if( d_array_term_cond.find(a)==d_array_term_cond.end() ){
+ if( d_array_cond.find(a.getType())==d_array_cond.end() ){
+ TypeNode typ = NodeManager::currentNM()->mkFunctionType( 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<vals.size(); i++) {
+ if((vals[i]==d_true && n.getKind()==OR) || (vals[i]==d_false && n.getKind()==AND)) {
+ return vals[i];
+ }else if( vals[i].isNull() ){
+ isNull = true;
+ }
+ }
+ return isNull ? Node::null() : vals[0];
+ }else{
+ std::vector<Node> children;
+ if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){
+ children.push_back( n.getOperator() );
+ }
+ for (unsigned i=0; i<vals.size(); i++) {
+ if( vals[i].isNull() ){
+ return Node::null();
+ }else{
+ children.push_back( vals[i] );
+ }
+ }
+ Node nc = NodeManager::currentNM()->mkNode(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; i<fm->d_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; j<fm->d_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; i<conds.size(); i++ ){
+ fm->d_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; i<indices.size(); i++ ){
+ Node v = fm->d_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; i<indices.size(); i++ ){
+ Debug("fmc-interval-model-debug") << indices[i] << " ";
+ }
+ Debug("fmc-interval-model-debug") << std::endl;
+
+ if( index<(int)fm->d_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; i<indices.size(); i++ ){
+ Node v = fm->d_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; i<values.size(); i++ ){
+ sindices.push_back( (int)i );
+ crs.d_terms.push_back( values[i] );
+ }
+ std::sort( sindices.begin(), sindices.end(), crs );
+
+ Node ub = fm->getStar( 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<new_indices[values[sindices[i]]].size(); j++ ){
+ Debug("fmc-interval-model-debug") << "Change " << new_indices[values[sindices[i]]][j] << ", " << index << " to " << interval << std::endl;
+ changed_vals[new_indices[values[sindices[i]]][j]][index] = interval;
+ }
+ ub = lb;
+ }
+ }
+ }else{
+ makeIntervalModel2( fm, op, indices, index+1, changed_vals );
+ }
+ }
+}
--- /dev/null
+/********************* */
+/*! \file full_model_check.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 Full model check class
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__FULL_MODEL_CHECK_H
+#define __CVC4__THEORY__QUANTIFIERS__FULL_MODEL_CHECK_H
+
+#include "theory/quantifiers/fmf/model_builder.h"
+#include "theory/quantifiers/first_order_model.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+namespace fmcheck {
+
+
+class FirstOrderModelFmc;
+class FullModelChecker;
+
+class EntryTrie
+{
+private:
+ int d_complete;
+public:
+ EntryTrie() : d_complete(-1), d_data(-1){}
+ std::map<Node,EntryTrie> 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<Node> & inst, int index = 0 );
+ void getEntries( FirstOrderModelFmc * m, Node c, std::vector<int> & compat, std::vector<int> & 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<Node>& inst );
+ int getGeneralizationIndex( FirstOrderModelFmc * m, std::vector<Node>& 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<TypeNode, std::map< Node, int > > d_rep_ids;
+ std::map<Node, Def > d_quant_models;
+ std::map<Node, Node > 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<Node> & 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<Node> & 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 */
--- /dev/null
+/********************* */
+/*! \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; i<fm->getNumAssertedQuantifiers(); 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; i<q[0].getNumChildren(); i++ ){
+ TypeNode tn = q[0][i].getType();
+ //we are allowed to assume the type is empty
+ if( tn.isSort() && eqc_usort.find( tn )==eqc_usort.end() ){
+ Trace("model-engine-debug") << "Empty domain quantified formula : " << q << std::endl;
+ fm->setQuantifierActive( 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; i<fm->getNumAssertedQuantifiers(); i++ ){
+ Node f = fm->getAssertedQuantifier( i );
+ std::vector< Node > vars;
+ for( unsigned j=0; j<f[0].getNumChildren(); j++ ){
+ vars.push_back( f[0][j] );
+ }
+ QRepBoundExt qrbe(d_qe);
+ RepSetIterator riter(d_qe->getModel()->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; i<fm->getNumAssertedQuantifiers(); 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; i<fm->getNumAssertedQuantifiers(); 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; i<fm->getNumAssertedQuantifiers(); 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; i<itut->second.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; i<uf_terms.size(); i++ ){
+ if( uf_terms[i].getNumChildren()>maxChildren ){
+ 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 = score<selectLitScore;
+ }
+ //see if we wish to choose this as a selection literal
+ d_quant_selection_lit_candidates[f].push_back( value ? n : n.notNode() );
+ if( selectLit ){
+ selectLitScore = getSelectionScore( uf_terms );
+ Trace("inst-gen-debug") << "Choose selection literal " << gn << std::endl;
+ Trace("inst-gen-debug") << " flags: " << isConst << " " << selectLitConstraints << " " << selectLitScore << std::endl;
+ d_quant_selection_lit[f] = value ? n : n.notNode();
+ selectionLitTerms.clear();
+ selectionLitTerms.insert( selectionLitTerms.begin(), uf_terms.begin(), uf_terms.end() );
+ if( !selectLitConstraints ){
+ break;
+ }
+ }
+ pref = 1;
+ }else{
+ pref = -1;
+ }
+ //if we are not yet SAT, so we will add to preferences
+ if( d_qe->getModel()->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<d_quant_selection_lit_candidates[f].size(); i++ ){
+ bool phase = d_quant_selection_lit_candidates[f][i].getKind()!=NOT;
+ Node lit = d_quant_selection_lit_candidates[f][i].getKind()==NOT ? d_quant_selection_lit_candidates[f][i][0] : d_quant_selection_lit_candidates[f][i];
+ Assert( TermUtil::hasInstConstAttr(lit) );
+ std::vector< Node > 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; i<itut->second.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;
+ }
+}
--- /dev/null
+/********************* */
+/*! \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<Node, bool, NodeHashFunction> 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 */
--- /dev/null
+/********************* */
+/*! \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; i<f[0].getNumChildren(); i++ ){
+ TypeNode tn = f[0][i].getType();
+ if( !tn.isSort() ){
+ if( !tn.getCardinality().isFinite() ){
+ if( tn.isInteger() ){
+ if( !options::fmfBound() ){
+ canHandle = false;
+ }
+ }else{
+ canHandle = false;
+ }
+ }
+ }
+ }
+ if( !canHandle ){
+ Trace("fmf-warn") << "Warning : Model Engine : may not be able to answer SAT because of formula : " << f << std::endl;
+ }
+ }
+}
+
+void ModelEngine::assertNode( Node f ){
+
+}
+
+bool ModelEngine::optOneQuantPerRound(){
+ return options::fmfOneQuantPerRound();
+}
+
+
+int ModelEngine::checkModel(){
+ FirstOrderModel* fm = d_quantEngine->getModel();
+
+ //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<TypeNode, std::vector<Node> >::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; i<it->second.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; i<it->second.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; i<fm->getNumAssertedQuantifiers(); i++ ){
+ Node f = fm->getAssertedQuantifier( i );
+ if( d_quantEngine->getModel()->isQuantifierActive( f ) && d_quantEngine->hasOwnership( f, this ) ){
+ int totalInst = 1;
+ for( unsigned j=0; j<f[0].getNumChildren(); j++ ){
+ TypeNode tn = f[0][j].getType();
+ if (fm->getRepSet()->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; e<e_max; e++) {
+ d_incomplete_quants.clear();
+ for( unsigned i=0; i<fm->getNumAssertedQuantifiers(); 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; i<f[0].getNumChildren(); i++ ){
+ Trace("fmf-exh-inst-debug") << d_quantEngine->getTermUtil()->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; i<d_quantEngine->getModel()->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 );
+}
+
--- /dev/null
+/********************* */
+/*! \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 */
+++ /dev/null
-/********************* */
-/*! \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<Node, unsigned, NodeHashFunction> 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<Rational>() < d_terms[j].getConst<Rational>() );
- }
-};
-
-
-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<Node,EntryTrie>::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<Node> & 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<Node,EntryTrie>::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 && gindex<minIndex) ){
- minIndex = gindex;
- }
- }
- }
- }else{
- Node st = m->getStar(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<minIndex) ){
- minIndex = gindex;
- }
- }
- }
- return minIndex;
- }
-}
-
-void EntryTrie::addEntry( FirstOrderModelFmc * m, Node c, Node v, int data, int index ) {
- if (index==(int)c.getNumChildren()) {
- if(d_data==-1) {
- d_data = data;
- }
- }
- else {
- d_child[ c[index] ].addEntry(m,c,v,data,index+1);
- if( d_complete==0 ){
- d_complete = -1;
- }
- }
-}
-
-void EntryTrie::getEntries( FirstOrderModelFmc * m, Node c, std::vector<int> & compat, std::vector<int> & 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<Node,EntryTrie>::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<Node,EntryTrie>::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<int> compat;
- std::vector<int> gen;
- d_et.getEntries(m, c, compat, gen);
- for( unsigned i=0; i<compat.size(); i++) {
- if( d_status[compat[i]]==status_unk ){
- if( d_value[compat[i]]!=v ){
- d_status[compat[i]] = status_non_redundant;
- }
- }
- }
- for( unsigned i=0; i<gen.size(); i++) {
- if( d_status[gen[i]]==status_unk ){
- if( d_value[gen[i]]==v ){
- d_status[gen[i]] = status_redundant;
- }
- }
- }
- d_status.push_back( status_unk );
- }
- d_et.addEntry(m, c, v, newIndex);
- d_cond.push_back(c);
- d_value.push_back(v);
- return true;
-}
-
-Node Def::evaluate( FirstOrderModelFmc * m, std::vector<Node>& 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<Node>& 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<d_status.size(); i++) {
- if( d_status[i]!=status_redundant ){
- addEntry(m, cond[i], value[i]);
- }
- }
- d_status.clear();
-}
-
-void Def::simplify(FullModelChecker * mc, FirstOrderModelFmc * m) {
- Trace("fmc-simplify") << "Simplify definition, #cond = " << d_cond.size() << std::endl;
- basic_simplify( m );
- Trace("fmc-simplify") << "post-basic simplify, #cond = " << d_cond.size() << std::endl;
-
- //check if the last entry is not all star, if so, we can make the last entry all stars
- if( !d_cond.empty() ){
- bool last_all_stars = true;
- Node cc = d_cond[d_cond.size()-1];
- for( unsigned i=0; i<cc.getNumChildren(); i++ ){
- if( !m->isInterval(cc[i]) && !m->isStar(cc[i]) ){
- last_all_stars = false;
- break;
- }
- }
- if( !last_all_stars ){
- Trace("fmc-cover-simplify") << "Need to modify last entry to be all stars." << std::endl;
- Trace("fmc-cover-simplify") << "Before: " << std::endl;
- debugPrint("fmc-cover-simplify",Node::null(), mc);
- Trace("fmc-cover-simplify") << std::endl;
- 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();
- 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; i<cond.size(); i++) {
- addEntry(m, cond[i], value[i]);
- }
- Trace("fmc-cover-simplify") << "Finished re-adding entries." << std::endl;
- basic_simplify( m );
- Trace("fmc-cover-simplify") << "After: " << std::endl;
- debugPrint("fmc-cover-simplify",Node::null(), mc);
- Trace("fmc-cover-simplify") << std::endl;
- }
- }
- Trace("fmc-simplify") << "finish simplify, #cond = " << d_cond.size() << std::endl;
-}
-
-void Def::debugPrint(const char * tr, Node op, FullModelChecker * m) {
- if (!op.isNull()) {
- Trace(tr) << "Model for " << op << " : " << std::endl;
- }
- for( unsigned i=0; i<d_cond.size(); i++) {
- //print the condition
- if (!op.isNull()) {
- Trace(tr) << op;
- }
- m->debugPrintCond(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<Node, Def * >::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ) {
- Node op = it->first;
- TypeNode tno = op.getType();
- for( unsigned i=0; i<tno.getNumChildren(); i++) {
- preInitializeType( fm, tno[i] );
- }
- }
- //do not have to introduce terms for sorts of domains of quantified formulas if we are allowed to assume empty sorts
- if( !options::fmfEmptySorts() ){
- for( unsigned i=0; i<fm->getNumAssertedQuantifiers(); i++ ){
- Node q = fm->getAssertedQuantifier( i );
- //make sure all types are set
- for( unsigned j=0; j<q[0].getNumChildren(); j++ ){
- preInitializeType( fm, q[0][j].getType() );
- }
- }
- }
- return true;
-}
-
-bool FullModelChecker::processBuildModel(TheoryModel* m){
- FirstOrderModelFmc * fm = ((FirstOrderModelFmc*)m)->asFirstOrderModelFmc();
- 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<TypeNode, std::vector<Node> >::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; a<it->second.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; i<eqc.size(); i++ ){
- Trace("fmc-model-debug") << eqc[i] << ", ";
- }
- Trace("fmc-model-debug") << "}" << std::endl;
- }
- d_rep_ids[it->first][r] = (int)a;
- }
- Trace("fmc-model-debug") << std::endl;
- }
- }
-
- //now, make models
- for( std::map<Node, Def * >::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; i<itut->second.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<add_conds.size(); i++ ){
- Node c = add_conds[i];
- Node v = add_values[i];
- std::vector< Node > children;
- std::vector< Node > entry_children;
- children.push_back(op);
- entry_children.push_back(op);
- bool hasNonStar = false;
- for( unsigned i=0; i<c.getNumChildren(); i++) {
- Node ri = fm->getRepresentative( 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; i<fm->d_uf_terms[op].size(); i++ ){
- std::vector< Node > inst;
- for( unsigned j=0; j<fm->d_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<Node, Def * >::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; j<n.getNumChildren(); j++) {
- if( j>0 ) 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; i<f[0].getNumChildren(); i++){
- types.push_back(f[0][i].getType());
- }
- TypeNode typ = NodeManager::currentNM()->mkFunctionType( 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<d_quant_models[f].d_cond.size(); i++) {
- if( d_quant_models[f].d_value[i]!=d_true ) {
- Trace("fmc-inst") << "Instantiate based on " << d_quant_models[f].d_cond[i] << "..." << std::endl;
- bool hasStar = false;
- std::vector< Node > inst;
- for (unsigned j=0; j<d_quant_models[f].d_cond[i].getNumChildren(); j++) {
- if (fmfmc->isStar(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<Node>& 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<dc.d_cond.size(); i++) {
- if (!dc.d_value[i].isNull()) {
- dc.d_value[i] = dc.d_value[i]==d_true ? d_false : ( dc.d_value[i]==d_false ? d_true : dc.d_value[i] );
- }
- }
-}
-
-void FullModelChecker::doVariableEquality( FirstOrderModelFmc * fm, Node f, Def & d, Node eq ) {
- std::vector<Node> 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; i<dc.d_cond.size(); i++) {
- Node val = dc.d_value[i];
- if( val.isNull() ){
- d.addEntry( fm, dc.d_cond[i], val);
- }else{
- if( dc.d_cond[i][j]!=val ){
- if (fm->isStar(dc.d_cond[i][j])) {
- std::vector<Node> 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<Node> & val ) {
- Trace("fmc-uf-process") << "process at " << index << std::endl;
- for( unsigned i=1; i<cond.size(); i++) {
- debugPrint("fmc-uf-process", cond[i], true);
- Trace("fmc-uf-process") << " ";
- }
- Trace("fmc-uf-process") << std::endl;
- if (index==(int)dc.size()) {
- //we have an entry, now do actual compose
- std::map< int, Node > 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<df.d_cond.size(); e++ ){
- if ( entries.find(e)!=entries.end() ){
- Trace("fmf-uf-process-debug") << "Add entry..." << std::endl;
- d.addEntry(fm, entries[e], df.d_value[e] );
- Trace("fmf-uf-process-debug") << "Done add entry." << std::endl;
- }
- }
- }
- }else{
- for (unsigned i=0; i<dc[index].d_cond.size(); i++) {
- if (isCompat(fm, cond, dc[index].d_cond[i])!=0) {
- std::vector< Node > 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<cond.size(); i++) {
- debugPrint("fmc-uf-process", cond[i], true);
- Trace("fmc-uf-process") << " ";
- }
- Trace("fmc-uf-process") << std::endl;
- if (index==(int)val.size()) {
- Node c = mkCond(cond);
- Trace("fmc-uf-entry") << "Entry : " << c << " -> 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<Node, EntryTrie>::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<Node, EntryTrie>::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<Node, EntryTrie>::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<Node> & val ) {
- Trace("fmc-if-process") << "int compose " << index << " / " << dc.size() << std::endl;
- for( unsigned i=1; i<cond.size(); i++) {
- debugPrint("fmc-if-process", cond[i], true);
- Trace("fmc-if-process") << " ";
- }
- Trace("fmc-if-process") << std::endl;
- if ( index==(int)dc.size() ){
- Node c = mkCond(cond);
- Node v = evaluateInterpreted(n, val);
- d.addEntry(fm, c, v);
- }
- else {
- TypeNode vtn = n.getType();
- for (unsigned i=0; i<dc[index].d_cond.size(); i++) {
- if (isCompat(fm, cond, dc[index].d_cond[i])!=0) {
- std::vector< Node > 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; i<cond.size(); i++) {
- if( options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL && cond[i].getType().isInteger() ){
- Node iv = doIntervalMeet( fm, cond[i], c[i-1], false );
- if( iv.isNull() ){
- return 0;
- }
- }else{
- if( cond[i]!=c[i-1] && !fm->isStar(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; i<cond.size(); i++) {
- if( cond[i]!=c[i-1] ) {
- if( options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL && cond[i].getType().isInteger() ){
- Node iv = doIntervalMeet( fm, cond[i], c[i-1] );
- if( !iv.isNull() ){
- cond[i] = iv;
- }else{
- return false;
- }
- }else{
- if( fm->isStar(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<Rational>() < b2.getConst<Rational>() ){
- 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<Rational>() < b[1].getConst<Rational>() ){
- 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; i<f[0].getNumChildren(); i++) {
- Node ts = fm->getStarElement( 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; i<n.getNumChildren(); i++ ){
- cond.push_back( n[i] );
- }
-}
-
-Node FullModelChecker::mkArrayCond( Node a ) {
- if( d_array_term_cond.find(a)==d_array_term_cond.end() ){
- if( d_array_cond.find(a.getType())==d_array_cond.end() ){
- TypeNode typ = NodeManager::currentNM()->mkFunctionType( 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<vals.size(); i++) {
- if((vals[i]==d_true && n.getKind()==OR) || (vals[i]==d_false && n.getKind()==AND)) {
- return vals[i];
- }else if( vals[i].isNull() ){
- isNull = true;
- }
- }
- return isNull ? Node::null() : vals[0];
- }else{
- std::vector<Node> children;
- if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){
- children.push_back( n.getOperator() );
- }
- for (unsigned i=0; i<vals.size(); i++) {
- if( vals[i].isNull() ){
- return Node::null();
- }else{
- children.push_back( vals[i] );
- }
- }
- Node nc = NodeManager::currentNM()->mkNode(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; i<fm->d_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; j<fm->d_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; i<conds.size(); i++ ){
- fm->d_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; i<indices.size(); i++ ){
- Node v = fm->d_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; i<indices.size(); i++ ){
- Debug("fmc-interval-model-debug") << indices[i] << " ";
- }
- Debug("fmc-interval-model-debug") << std::endl;
-
- if( index<(int)fm->d_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; i<indices.size(); i++ ){
- Node v = fm->d_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; i<values.size(); i++ ){
- sindices.push_back( (int)i );
- crs.d_terms.push_back( values[i] );
- }
- std::sort( sindices.begin(), sindices.end(), crs );
-
- Node ub = fm->getStar( 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<new_indices[values[sindices[i]]].size(); j++ ){
- Debug("fmc-interval-model-debug") << "Change " << new_indices[values[sindices[i]]][j] << ", " << index << " to " << interval << std::endl;
- changed_vals[new_indices[values[sindices[i]]][j]][index] = interval;
- }
- ub = lb;
- }
- }
- }else{
- makeIntervalModel2( fm, op, indices, index+1, changed_vals );
- }
- }
-}
+++ /dev/null
-/********************* */
-/*! \file full_model_check.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 Full model check class
- **/
-
-#include "cvc4_private.h"
-
-#ifndef __CVC4__THEORY__QUANTIFIERS__FULL_MODEL_CHECK_H
-#define __CVC4__THEORY__QUANTIFIERS__FULL_MODEL_CHECK_H
-
-#include "theory/quantifiers/model_builder.h"
-#include "theory/quantifiers/first_order_model.h"
-
-namespace CVC4 {
-namespace theory {
-namespace quantifiers {
-namespace fmcheck {
-
-
-class FirstOrderModelFmc;
-class FullModelChecker;
-
-class EntryTrie
-{
-private:
- int d_complete;
-public:
- EntryTrie() : d_complete(-1), d_data(-1){}
- std::map<Node,EntryTrie> 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<Node> & inst, int index = 0 );
- void getEntries( FirstOrderModelFmc * m, Node c, std::vector<int> & compat, std::vector<int> & 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<Node>& inst );
- int getGeneralizationIndex( FirstOrderModelFmc * m, std::vector<Node>& 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<TypeNode, std::map< Node, int > > d_rep_ids;
- std::map<Node, Def > d_quant_models;
- std::map<Node, Node > 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<Node> & 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<Node> & 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 */
+++ /dev/null
-/********************* */
-/*! \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 <stack>
-
-#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<Node>& nodes,
- std::map<Node, std::vector<Node> >& 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<const Node, std::vector<Node> >& 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<Node, std::vector<Node> >& apps)
-{
- std::vector<Node> ns;
- ns.push_back(n);
- collectHoVarApplyTerms(q, ns, apps);
- Assert(ns.size() == 1);
- n = ns[0];
-}
-
-void HigherOrderTrigger::collectHoVarApplyTerms(
- Node q, std::vector<Node>& ns, std::map<Node, std::vector<Node> >& apps)
-{
- std::unordered_map<TNode, Node, TNodeHashFunction> visited;
- std::unordered_map<TNode, Node, TNodeHashFunction>::iterator it;
- // whether the visited node is a child of a HO_APPLY chain
- std::unordered_map<TNode, bool, TNodeHashFunction> withinApply;
- std::vector<TNode> 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<Node> 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<TNode> vars;
- std::vector<TNode> 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<TNode, std::vector<Node> > ho_var_apps_subs;
- for (std::pair<const Node, std::vector<Node> >& 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<const TNode, std::vector<Node> >& 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<TNode, std::vector<Node> >::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<unsigned, Node> fixed_vals;
- for (unsigned i = 0; i < ha.second.size(); i++)
- {
- std::vector<TNode> 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<unsigned, Node>::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<unsigned, Node>::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<Node, unsigned> arg_to_rep;
- for (unsigned index = 0, size = ithb->second.size(); index < size;
- index++)
- {
- Node bv_at_index = ithb->second[index];
- std::map<unsigned, Node>::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<Node, unsigned>::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<unsigned, unsigned>::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<unsigned, std::vector<Node> >::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 */
+++ /dev/null
-/********************* */
-/*! \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 <map>
-#include <unordered_set>
-
-#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<Node>& nodes,
- std::map<Node, std::vector<Node> >& 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<Node, std::vector<Node> >& apps);
- /** Collect higher order var apply terms
- *
- * Same as above, but with multiple terms ns.
- */
- static void collectHoVarApplyTerms(Node q,
- std::vector<Node>& ns,
- std::map<Node, std::vector<Node> >& 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<Node, std::vector<Node> > d_ho_var_apps;
- /**
- * List of all function-typed variables that occur as the head of function
- * applications in d_f.
- */
- std::vector<Node> 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<TNode, std::vector<Node> > d_ho_var_bvs;
- std::map<TNode, Node> d_ho_var_bvl;
- /** the set of types of ho variables */
- std::unordered_set<TypeNode, TypeNodeHashFunction> 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<unsigned, std::vector<Node> > 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<unsigned, std::map<unsigned, unsigned> > 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<unsigned, std::map<unsigned, std::vector<Node> > > 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 */
+++ /dev/null
-/********************* */
-/*! \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; i<d_children.size(); i++ ){
- delete d_children[i];
- }
- delete d_cg;
-}
-
-void InstMatchGenerator::setActiveAdd(bool val){
- d_active_add = val;
- if( d_next!=NULL ){
- d_next->setActiveAdd(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; i<gens.size(); i++ ){
- Node pn = gens[i]->d_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<pats.size() ){
- size_t counter = 0;
- std::vector< InstMatchGenerator* > 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 (counter<gens.size()) {
- InstMatchGenerator* curr = gens[counter];
- if( prev ){
- prev->d_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; i<it->second.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()<pats.size() ){
- // score is lexographic ( bound vars, shared vars )
- int score_max_1 = -1;
- int score_max_2 = -1;
- int score_index = -1;
- for( unsigned i=0; i<pats.size(); i++ ){
- Node p = pats[i];
- if( std::find( pats_ordered.begin(), pats_ordered.end(), p )==pats_ordered.end() ){
- int score_1 = 0;
- int score_2 = 0;
- for( unsigned j=0; j<var_contains[p].size(); j++ ){
- Node v = var_contains[p][j];
- if( var_bound.find( v )!=var_bound.end() ){
- score_1++;
- }else if( var_to_node[v].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; i<var_contains[mp].size(); i++ ){
- var_bound[var_contains[mp][i]] = true;
- }
- pats_ordered.push_back( mp );
- pats_ordered_independent.push_back( score_max_1==0 );
- }
-
- Trace("multi-trigger-linear") << "Make children for linear multi trigger." << std::endl;
- for( unsigned i=0; i<pats_ordered.size(); i++ ){
- Trace("multi-trigger-linear") << "...make for " << pats_ordered[i] << std::endl;
- InstMatchGenerator* cimg = getInstMatchGenerator(q, pats_ordered[i]);
- Assert( cimg!=NULL );
- d_children.push_back( cimg );
- if( i==0 ){ //TODO : improve
- cimg->setIndependent();
- }
- }
-}
-
-int InstMatchGeneratorMultiLinear::resetChildren( QuantifiersEngine* qe ){
- for( unsigned i=0; i<d_children.size(); i++ ){
- if( !d_children[i]->reset( 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; i<d_children.size(); i++ ){
- Node mi = d_children[i]->getCurrentMatch();
- 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<Node>& 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; i<pats.size(); i++ ){
- Node n = pats[i];
- //make the match generator
- InstMatchGenerator* img =
- InstMatchGenerator::mkInstMatchGenerator(q, n, qe);
- img->setActiveAdd(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<d_var_contains[n].size(); j++ ){
- if( d_var_to_node[ d_var_contains[n][j] ].size()==1 ){
- Trace("multi-trigger-cache") << "Var " << d_var_contains[n][j] << " is unique to " << pats[i] << std::endl;
- unique_vars.push_back( d_var_contains[n][j] );
- }else{
- shared_vars[ d_var_contains[n][j] ] = true;
- numSharedVars++;
- }
- }
- //we use the latest shared variables, then unique variables
- std::vector< int > 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; j<vars.size(); j++ ){
- Trace("multi-trigger-cache") << vars[j] << " ";
- }
- Trace("multi-trigger-cache") << std::endl;
- //make ordered inst match trie
- d_imtio[i] = new InstMatchTrie::ImtIndexOrder;
- d_imtio[i]->d_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<d_children.size(); i++ ){
- delete d_children[i];
- }
- for( std::map< unsigned, InstMatchTrie::ImtIndexOrder* >::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; i<d_children.size(); i++ ){
- d_children[i]->resetInstantiationRound( 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; i<d_children.size(); i++ ){
- if( !d_children[i]->reset( 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<d_children.size(); i++ ){
- Trace("multi-trigger-cache") << "Calculate matches " << i << std::endl;
- std::vector< InstMatch > 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; j<newMatches.size(); j++ ){
- Trace("multi-trigger-cache2") << "...processing " << j << " / " << newMatches.size() << ", #lemmas = " << addedLemmas << std::endl;
- processNewMatch(qe, tparent, newMatches[j], i, addedLemmas);
- if( qe->inConflict() ){
- return addedLemmas;
- }
- }
- }
- return addedLemmas;
-}
-
-void InstMatchGeneratorMulti::processNewMatch(QuantifiersEngine* qe,
- Trigger* tparent,
- InstMatch& m,
- int fromChildIndex,
- int& addedLemmas)
-{
- //see if these produce new matches
- d_children_trie[fromChildIndex].addInstMatch(qe, d_quant, m);
- //possibly only do the following if we know that new matches will be produced?
- //the issue is that instantiations are filtered in quantifiers engine, and so there is no guarentee that
- // we can safely skip the following lines, even when we have already produced this match.
- Trace("multi-trigger-cache-debug") << "Child " << fromChildIndex << " produced match " << m << std::endl;
- //process new instantiations
- int childIndex = (fromChildIndex+1)%(int)d_children.size();
- processNewInstantiations(qe,
- tparent,
- m,
- addedLemmas,
- d_children_trie[childIndex].getTrie(),
- 0,
- childIndex,
- fromChildIndex,
- true);
-}
-
-void InstMatchGeneratorMulti::processNewInstantiations(QuantifiersEngine* qe,
- Trigger* tparent,
- InstMatch& m,
- int& addedLemmas,
- InstMatchTrie* tr,
- int trieIndex,
- int childIndex,
- int endChildIndex,
- bool modEq)
-{
- Assert( !qe->inConflict() );
- 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<const Node, InstMatchTrie>& 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<Node, InstMatchTrie>::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<Node, InstMatchTrie>::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; i<d_match_pattern.getNumChildren(); i++ ){
- if( d_match_pattern[i].getKind()==INST_CONSTANT ){
- if( !options::cbqi() || quantifiers::TermUtil::getInstConstAttr(d_match_pattern[i])==q ){
- d_var_num[i] = d_match_pattern[i].getAttribute(InstVarNumAttribute());
- }else{
- d_var_num[i] = -1;
- }
- }
- d_match_pattern_arg_types.push_back( d_match_pattern[i].getType() );
- }
- d_op = qe->getTermDatabase()->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 */
+++ /dev/null
-/********************* */
-/*! \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 <map>
-#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<Node>& 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<Node>& pats,
- QuantifiersEngine* qe,
- std::map<Node, InstMatchGenerator*>& 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<InstMatchGenerator*> 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<int> 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<int> 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<Node, bool> 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<InstMatchGenerator*>& 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<Node>& 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<Node>& 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
+++ /dev/null
-/********************* */
-/*! \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; i<d_quantEngine->getModel()->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; i<q[0].getNumChildren(); i++ ){
- TypeNode tn = q[0][i].getType();
- if( tn.isSort() ){
- if( qepr->isEPR( 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; j<itc->second.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; i<ics.size(); i++ ){
- Node qi = ics[i].getAttribute(InstConstantAttribute());
- if( std::find( d_parent_quant[q].begin(), d_parent_quant[q].end(), qi )==d_parent_quant[q].end() ){
- d_parent_quant[q].push_back( qi );
- d_children_quant[qi].push_back( q );
- Assert( hasAddedCbqiLemma( qi ) );
- Node qicel = d_quantEngine->getTermUtil()->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; i<quants.size(); i++ ){
- if( doCbqi( quants[i] ) ){
- registerCbqiLemma( quants[i] );
- }
- if( options::cbqiNestedQE() ){
- //record these as counterexample quantifiers
- QAttributes qa;
- QuantAttributes::computeQuantAttributes( quants[i], qa );
- if( !qa.d_qid_num.isNull() ){
- d_id_to_ce_quant[ qa.d_qid_num ] = quants[i];
- d_ce_quant_to_id[ quants[i] ] = qa.d_qid_num;
- Trace("cbqi-nqe") << "CE quant id = " << qa.d_qid_num << " is " << quants[i] << std::endl;
- }
- }
- }
- }
- return true;
- }else{
- return false;
- }
-}
-
-void InstStrategyCbqi::reset_round( Theory::Effort effort ) {
- d_cbqi_set_quant_inactive = false;
- d_incomplete_check = false;
- d_active_quant.clear();
- //check if any cbqi lemma has not been added yet
- for( unsigned i=0; i<d_quantEngine->getModel()->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]<d_nested_qe_waitlist_size[q] ){
- int index = d_nested_qe_waitlist_proc[q];
- Assert( index>=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; j<itc->second.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; i<ninner.size(); i++ ){
- Assert( d_active_quant.find( ninner[i] )!=d_active_quant.end() );
- d_active_quant.erase( ninner[i] );
- }
- Assert( !d_active_quant.empty() );
- Trace("cbqi-debug") << "...done removing." << std::endl;
- }
- }
-
- processResetInstantiationRound( effort );
-}
-
-void InstStrategyCbqi::check(Theory::Effort e, QEffort quant_e)
-{
- if (quant_e == QEFFORT_STANDARD)
- {
- Assert( !d_quantEngine->inConflict() );
- 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; i<d_quantEngine->getModel()->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; i<n[2].getNumChildren(); i++ ){
- iplc.push_back( n[2][i] );
- }
- }
- rc.push_back( NodeManager::currentNM()->mkNode( 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; i<n.getNumChildren(); i++ ){
- Node nc = getIdMarkedQuantNode( n[i], visited );
- childChanged = childChanged || nc!=n[i];
- children.push_back( nc );
- }
- if( childChanged ){
- ret = NodeManager::currentNM()->mkNode( 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; i<n.getNumChildren(); i++ ){
- Node nc = doNestedQERec( q, n[i], visited, inst_terms, doVts );
- childChanged = childChanged || nc!=n[i];
- children.push_back( nc );
- }
- if( childChanged ){
- ret = NodeManager::currentNM()->mkNode( 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<n.getNumChildren(); i++ ){
- if( hasNonCbqiOperator( n[i], visited ) ){
- return true;
- }
- }
- }
- }
- }
- return false;
-}
-
-// -1 : not cbqi sort, 0 : cbqi sort, 1 : cbqi sort regardless of quantifier body
-int InstStrategyCbqi::isCbqiSort( TypeNode tn, std::map< TypeNode, int >& 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; i<dt.getNumConstructors(); i++ ){
- for( unsigned j=0; j<dt[i].getNumArgs(); j++ ){
- TypeNode crange = TypeNode::fromType( ((SelectorType)dt[i][j].getType()).getRangeType() );
- int cret = isCbqiSort( crange, visited );
- if( cret==-1 ){
- visited[tn] = -1;
- return -1;
- }else if( cret<ret ){
- ret = cret;
- }
- }
- }
- }else if( tn.isSort() ){
- QuantEPR * qepr = d_quantEngine->getQuantEPR();
- 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<q[0].getNumChildren(); i++ ){
- TypeNode tn = q[0][i].getType();
- std::map< TypeNode, int > visited;
- int handled = isCbqiSort( tn, visited );
- if( handled==-1 ){
- return -1;
- }else if( handled<hmin ){
- hmin = handled;
- }
- }
- return hmin;
-}
-
-bool InstStrategyCbqi::doCbqi( Node q ){
- std::map< Node, int >::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; i<q[2].getNumChildren(); i++ ){
- if( q[2][i].getKind()==INST_PATTERN ){
- ret = 0;
- }
- }
- }
- if( d_quantEngine->getQuantAttributes()->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; j<itc->second.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; i<d_quantEngine->getModel()->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; i<inf.size(); i++ ){
- Trace("quant-vts-debug") << "Infinity lemma for " << inf[i] << " " << d_small_const << std::endl;
- Node inf_lem_lb = NodeManager::currentNM()->mkNode( GT, inf[i], NodeManager::currentNM()->mkConst( Rational(1)/d_small_const.getConst<Rational>() ) );
- 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; i<d_quantEngine->getTermUtil()->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; i<lems.size(); i++ ){
- Trace("cbqi-debug") << "Counterexample lemma " << i << " : " << lems[i] << std::endl;
- d_quantEngine->addLemma( 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 );
- }
- }
-}
-
+++ /dev/null
-/********************* */
-/*! \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<Node, NodeHashFunction> 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
+++ /dev/null
-/********************* */
-/*! \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( nqfsi<nqfsj ){
- return true;
- }else if( nqfsi>nqfsj ){
- 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<j;
- }else{
- return wi<wj;
- }
- }
-};
-
-void InstStrategyUserPatterns::processResetInstantiationRound( Theory::Effort effort ){
- Trace("inst-alg-debug") << "reset user triggers" << std::endl;
- //reset triggers
- for( std::map< Node, std::vector< Trigger* > >::iterator it = d_user_gen.begin(); it != d_user_gen.end(); ++it ){
- for( unsigned i=0; i<it->second.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<peffort ){
- return STATUS_UNFINISHED;
- }else if( e==peffort ){
- d_counter[f]++;
-
- Trace("inst-alg") << "-> User-provided instantiate " << f << "..." << std::endl;
- if( d_quantEngine->getInstUserPatMode()==USER_PAT_MODE_RESORT ){
- for( unsigned i=0; i<d_user_gen_wait[f].size(); i++ ){
- Trigger * t = Trigger::mkTrigger( d_quantEngine, f, d_user_gen_wait[f][i], true, Trigger::TR_RETURN_NULL );
- if( t ){
- d_user_gen[f].push_back( t );
- }
- }
- d_user_gen_wait[f].clear();
- }
-
- for( unsigned i=0; i<d_user_gen[f].size(); i++ ){
- bool processTrigger = true;
- if( processTrigger ){
- Trace("process-trigger") << " Process (user) ";
- d_user_gen[f][i]->debugPrint("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; i<pat.getNumChildren(); i++ ){
- Node pat_use = Trigger::getIsUsableTrigger( pat[i], q );
- if( pat_use.isNull() ){
- Trace("trigger-warn") << "User-provided trigger is not usable : " << pat << " because of " << pat[i] << std::endl;
- usable = false;
- break;
- }else{
- nodes.push_back( pat_use );
- }
- }
- if( usable ){
- Trace("user-pat") << "Add user pattern: " << pat << " for " << q << std::endl;
- //check match option
- if( d_quantEngine->getInstUserPatMode()==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<peffort ){
- return STATUS_UNFINISHED;
- }else{
- Trace("inst-alg") << "-> 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 && ( score<max_score || max_score<0 ) ){
- max_score = score;
- max_trigger = itt->first;
- }
- }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<patTermsF.size(); i++ ){
- Assert( tinfo.find( patTermsF[i] )!=tinfo.end() );
- Trace("auto-gen-trigger-debug") << " " << patTermsF[i] << std::endl;
- Trace("auto-gen-trigger-debug2") << " info = [" << tinfo[patTermsF[i]].d_reqPol << ", " << tinfo[patTermsF[i]].d_reqPolEq << ", " << tinfo[patTermsF[i]].d_fv.size() << "]" << std::endl;
- }
- Trace("auto-gen-trigger-debug") << std::endl;
- }
- }
- //sort into single/multi triggers, calculate which terms should not be considered
- std::map< Node, bool > vcMap;
- std::map< Node, bool > rmPatTermsF;
- int last_weight = -1;
- for( unsigned i=0; i<patTermsF.size(); i++ ){
- Assert( patTermsF[i].getKind()!=NOT );
- bool newVar = false;
- for( unsigned j=0; j<tinfo[ patTermsF[i] ].d_fv.size(); j++ ){
- if( vcMap.find( tinfo[ patTermsF[i] ].d_fv[j] )==vcMap.end() ){
- vcMap[tinfo[ patTermsF[i] ].d_fv[j]] = true;
- newVar = true;
- }
- }
- int curr_w = Trigger::getTriggerWeight( patTermsF[i] );
- if( ntrivTriggers && !newVar && last_weight!=-1 && curr_w>last_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]<f[0].getNumChildren() ){
- Trace("auto-gen-trigger-partial") << "Quantified formula : " << f << std::endl;
- Trace("auto-gen-trigger-partial") << "...does not contain all variables in triggers!!!" << std::endl;
- if( options::partialTriggers() ){
- std::vector< Node > vcs[2];
- for( unsigned i=0; i<f[0].getNumChildren(); i++ ){
- Node ic = d_quantEngine->getTermUtil()->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<patTermsF.size(); i++ ){
- Node pat = patTermsF[i];
- if( rmPatTermsF.find( pat )==rmPatTermsF.end() ){
- Trace("auto-gen-trigger-debug") << "...processing pattern " << pat << std::endl;
- Node mpat = pat;
- //process the pattern: if it has a required polarity, consider it
- Assert( tinfo.find( pat )!=tinfo.end() );
- int rpol = tinfo[pat].d_reqPol;
- Node rpoleq = tinfo[pat].d_reqPolEq;
- unsigned num_fv = tinfo[pat].d_fv.size();
- Trace("auto-gen-trigger-debug") << "...required polarity for " << pat << " is " << rpol << ", eq=" << rpoleq << std::endl;
- if( rpol!=0 ){
- Assert( rpol==1 || rpol==-1 );
- if( Trigger::isRelationalTrigger( pat ) ){
- pat = rpol==-1 ? pat.negate() : pat;
- }else{
- Assert( Trigger::isAtomicTrigger( pat ) );
- if( pat.getType().isBoolean() && rpoleq.isNull() ){
- if( options::literalMatchMode()==LITERAL_MATCH_USE ){
- pat = NodeManager::currentNM()->mkNode( EQUAL, pat, NodeManager::currentNM()->mkConst( rpol==-1 ) ).negate();
- }else if( options::literalMatchMode()!=LITERAL_MATCH_NONE ){
- pat = NodeManager::currentNM()->mkNode( EQUAL, pat, NodeManager::currentNM()->mkConst( rpol==1 ) );
- }
- }else{
- Assert( !rpoleq.isNull() );
- if( rpol==-1 ){
- if( options::literalMatchMode()!=LITERAL_MATCH_NONE ){
- //all equivalence classes except rpoleq
- pat = NodeManager::currentNM()->mkNode( EQUAL, pat, rpoleq ).negate();
- }
- }else if( rpol==1 ){
- if( options::literalMatchMode()==LITERAL_MATCH_AGG ){
- //only equivalence class rpoleq
- pat = NodeManager::currentNM()->mkNode( EQUAL, pat, rpoleq );
- }
- //all equivalence classes that are not disequal to rpoleq TODO?
- }
- }
- }
- Trace("auto-gen-trigger-debug") << "...got : " << pat << std::endl;
- }else{
- if( Trigger::isRelationalTrigger( pat ) ){
- //consider both polarities
- addPatternToPool( f, pat.negate(), num_fv, mpat );
- }
- }
- addPatternToPool( f, pat, num_fv, mpat );
- }
- }
- //tinfo not used below this point
- d_made_multi_trigger[f] = false;
- Trace("auto-gen-trigger") << "Single trigger pool for " << f << " : " << std::endl;
- for( unsigned i=0; i<d_patTerms[0][f].size(); i++ ){
- Trace("auto-gen-trigger") << " " << d_patTerms[0][f][i] << std::endl;
- }
- if( !d_patTerms[1][f].empty() ){
- Trace("auto-gen-trigger") << "Multi-trigger term pool for " << f << " : " << std::endl;
- for( unsigned i=0; i<d_patTerms[1][f].size(); i++ ){
- Trace("auto-gen-trigger") << " " << d_patTerms[1][f][i] << std::endl;
- }
- }
- }
-
- unsigned rmin = d_patTerms[0][f].empty() ? 1 : 0;
- unsigned rmax = options::multiTriggerWhenSingle() ? 1 : rmin;
- for( unsigned r=rmin; r<=rmax; r++ ){
- std::vector< Node > 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; i<patTerms.size(); i++ ){
- Assert( d_pat_to_mpat.find( patTerms[i] )!=d_pat_to_mpat.end() );
- Assert( d_pat_to_mpat[patTerms[i]].hasOperator() );
- sqfs.d_op_map[ patTerms[i] ] = d_pat_to_mpat[patTerms[i]].getOperator();
- }
- //sort based on # occurrences (this will cause Trigger to select rarer symbols)
- std::sort( patTerms.begin(), patTerms.end(), sqfs );
- Debug("relevant-trigger") << "Terms based on relevance: " << std::endl;
- for( unsigned i=0; i<patTerms.size(); i++ ){
- Debug("relevant-trigger") << " " << patTerms[i] << " from " << d_pat_to_mpat[patTerms[i]] << " (";
- Debug("relevant-trigger") << d_quantEngine->getQuantifierRelevance()->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( index<patTerms.size() ){
- //Notice() << "check add additional" << std::endl;
- //check if similar patterns exist, and if so, add them additionally
- unsigned nqfs_curr = 0;
- if( options::relevantTriggers() ){
- nqfs_curr = d_quantEngine->getQuantifierRelevance()->getNumQuantifiersForSymbol( patTerms[0].getOperator() );
- }
- index++;
- bool success = true;
- while( success && index<patTerms.size() && d_is_single_trigger[ patTerms[index] ] ){
- success = false;
- if( !options::relevantTriggers() ||
- d_quantEngine->getQuantifierRelevance()->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]<q[0].getNumChildren() ){
- //partial trigger : generate implication to mark user pattern
- Node pat =
- d_quantEngine->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; i<q[2].getNumChildren(); i++ ){
- if( q[2][i].getKind()==INST_PATTERN ){
- hasPat = true;
- break;
- }
- }
- d_hasUserPatterns[q] = hasPat;
- return hasPat;
- }else{
- return it->second;
- }
- }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; i<f[0].getNumChildren(); i++ ){
- Node x = d_quantEngine->getTermUtil()->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; i<patTerms.size(); i++ ){
- Trace("local-t-ext") << " " << patTerms[i] << std::endl;
- }
- Trace("local-t-ext") << std::endl;
- Trigger * tr = Trigger::mkTrigger( d_quantEngine, f, patTerms, true, Trigger::TR_GET_OLD );
- d_lte_trigger[f] = tr;
- }else{
- Trace("local-t-ext") << "No local theory extensions trigger for " << f << "." << std::endl;
- Trace("local-t-ext-warn") << "WARNING: not local theory extensions : " << f << std::endl;
- }
- d_quant[f] = ret;
- return ret;
- }else{
- return itq->second;
- }
-}
-*/
-
-} /* CVC4::theory::quantifiers namespace */
-} /* CVC4::theory namespace */
-} /* CVC4 namespace */
+++ /dev/null
-/********************* */
-/*! \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
#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"
+++ /dev/null
-/********************* */
-/*! \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; i<d_instStrategies.size(); ++i ){
- d_instStrategies[i]->presolve();
- }
-}
-
-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; i<d_quants.size(); i++ ){
- Node q = d_quants[i];
- Debug("inst-engine-debug") << "IE: Instantiate " << q << "..." << std::endl;
- //int e_use = d_quantEngine->getRelevance( 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; j<d_instStrategies.size(); j++ ){
- InstStrategy* is = d_instStrategies[j];
- Trace("inst-engine-debug") << "Do " << is->identify() << " " << 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; i<d_instStrategies.size(); ++i ){
- InstStrategy* is = d_instStrategies[i];
- is->processResetInstantiationRound( 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; i<d_quantEngine->getModel()->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; i<q[2].getNumChildren(); i++ ){
- if( q[2][i].getKind()==INST_PATTERN || q[2][i].getKind()==INST_NO_PATTERN ){
- hasPat = true;
- break;
- }
- }
- if( hasPat ){
- d_quantEngine->setOwner( q, this, 1 );
- }
- }
-}
-
-void InstantiationEngine::registerQuantifier( Node f ){
- if( d_quantEngine->hasOwnership( f, this ) ){
- //for( unsigned i=0; i<d_instStrategies.size(); ++i ){
- // d_instStrategies[i]->registerQuantifier( 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);
- }
-}
+++ /dev/null
-/********************* */
-/*! \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 <memory>
-
-#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<InstStrategy*> d_instStrategies;
- /** user-pattern instantiation strategy */
- std::unique_ptr<InstStrategyUserPatterns> d_isup;
- /** auto gen triggers; only kept for destructor cleanup */
- std::unique_ptr<InstStrategyAutoGenTriggers> d_i_ag;
-
- typedef context::CDHashMap<Node, bool, NodeHashFunction> BoolMap;
- /** current processing quantified formulas */
- std::vector<Node> 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 */
#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;
+++ /dev/null
-/********************* */
-/*! \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; i<fm->getNumAssertedQuantifiers(); 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; i<q[0].getNumChildren(); i++ ){
- TypeNode tn = q[0][i].getType();
- //we are allowed to assume the type is empty
- if( tn.isSort() && eqc_usort.find( tn )==eqc_usort.end() ){
- Trace("model-engine-debug") << "Empty domain quantified formula : " << q << std::endl;
- fm->setQuantifierActive( 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; i<fm->getNumAssertedQuantifiers(); i++ ){
- Node f = fm->getAssertedQuantifier( i );
- std::vector< Node > vars;
- for( unsigned j=0; j<f[0].getNumChildren(); j++ ){
- vars.push_back( f[0][j] );
- }
- QRepBoundExt qrbe(d_qe);
- RepSetIterator riter(d_qe->getModel()->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; i<fm->getNumAssertedQuantifiers(); 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; i<fm->getNumAssertedQuantifiers(); 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; i<fm->getNumAssertedQuantifiers(); 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; i<itut->second.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; i<uf_terms.size(); i++ ){
- if( uf_terms[i].getNumChildren()>maxChildren ){
- 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 = score<selectLitScore;
- }
- //see if we wish to choose this as a selection literal
- d_quant_selection_lit_candidates[f].push_back( value ? n : n.notNode() );
- if( selectLit ){
- selectLitScore = getSelectionScore( uf_terms );
- Trace("inst-gen-debug") << "Choose selection literal " << gn << std::endl;
- Trace("inst-gen-debug") << " flags: " << isConst << " " << selectLitConstraints << " " << selectLitScore << std::endl;
- d_quant_selection_lit[f] = value ? n : n.notNode();
- selectionLitTerms.clear();
- selectionLitTerms.insert( selectionLitTerms.begin(), uf_terms.begin(), uf_terms.end() );
- if( !selectLitConstraints ){
- break;
- }
- }
- pref = 1;
- }else{
- pref = -1;
- }
- //if we are not yet SAT, so we will add to preferences
- if( d_qe->getModel()->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<d_quant_selection_lit_candidates[f].size(); i++ ){
- bool phase = d_quant_selection_lit_candidates[f][i].getKind()!=NOT;
- Node lit = d_quant_selection_lit_candidates[f][i].getKind()==NOT ? d_quant_selection_lit_candidates[f][i][0] : d_quant_selection_lit_candidates[f][i];
- Assert( TermUtil::hasInstConstAttr(lit) );
- std::vector< Node > 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; i<itut->second.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;
- }
-}
+++ /dev/null
-/********************* */
-/*! \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<Node, bool, NodeHashFunction> 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 */
+++ /dev/null
-/********************* */
-/*! \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; i<f[0].getNumChildren(); i++ ){
- TypeNode tn = f[0][i].getType();
- if( !tn.isSort() ){
- if( !tn.getCardinality().isFinite() ){
- if( tn.isInteger() ){
- if( !options::fmfBound() ){
- canHandle = false;
- }
- }else{
- canHandle = false;
- }
- }
- }
- }
- if( !canHandle ){
- Trace("fmf-warn") << "Warning : Model Engine : may not be able to answer SAT because of formula : " << f << std::endl;
- }
- }
-}
-
-void ModelEngine::assertNode( Node f ){
-
-}
-
-bool ModelEngine::optOneQuantPerRound(){
- return options::fmfOneQuantPerRound();
-}
-
-
-int ModelEngine::checkModel(){
- FirstOrderModel* fm = d_quantEngine->getModel();
-
- //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<TypeNode, std::vector<Node> >::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; i<it->second.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; i<it->second.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; i<fm->getNumAssertedQuantifiers(); i++ ){
- Node f = fm->getAssertedQuantifier( i );
- if( d_quantEngine->getModel()->isQuantifierActive( f ) && d_quantEngine->hasOwnership( f, this ) ){
- int totalInst = 1;
- for( unsigned j=0; j<f[0].getNumChildren(); j++ ){
- TypeNode tn = f[0][j].getType();
- if (fm->getRepSet()->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; e<e_max; e++) {
- d_incomplete_quants.clear();
- for( unsigned i=0; i<fm->getNumAssertedQuantifiers(); 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; i<f[0].getNumChildren(); i++ ){
- Trace("fmf-exh-inst-debug") << d_quantEngine->getTermUtil()->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; i<d_quantEngine->getModel()->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 );
-}
-
+++ /dev/null
-/********************* */
-/*! \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 */
#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;
#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"
#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;
#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"
#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"
--- /dev/null
+/********************* */
+/*! \file ce_guided_conjecture.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief implementation of class that encapsulates counterexample-guided instantiation
+ ** techniques for a single SyGuS synthesis conjecture
+ **/
+#include "theory/quantifiers/sygus/ce_guided_conjecture.h"
+
+#include "expr/datatype.h"
+#include "options/base_options.h"
+#include "options/quantifiers_options.h"
+#include "printer/printer.h"
+#include "prop/prop_engine.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/quantifiers/sygus/ce_guided_instantiation.h"
+#include "theory/quantifiers/first_order_model.h"
+#include "theory/quantifiers/instantiate.h"
+#include "theory/quantifiers/quantifiers_attributes.h"
+#include "theory/quantifiers/skolemize.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+#include "theory/quantifiers/term_util.h"
+#include "theory/theory_engine.h"
+
+using namespace CVC4::kind;
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+// recursion is not an issue since OR nodes are flattened by the (quantifiers) rewriter
+// this function is for sanity since solution correctness in SyGuS depends on fully miniscoping based on this function
+void collectDisjuncts( Node n, std::vector< Node >& d ) {
+ if( n.getKind()==OR ){
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ collectDisjuncts( n[i], d );
+ }
+ }else{
+ d.push_back( n );
+ }
+}
+
+CegConjecture::CegConjecture(QuantifiersEngine* qe)
+ : d_qe(qe),
+ d_ceg_si(new CegConjectureSingleInv(qe, this)),
+ d_ceg_pbe(new CegConjecturePbe(qe, this)),
+ d_ceg_proc(new CegConjectureProcess(qe)),
+ d_ceg_gc(new CegGrammarConstructor(qe, this)),
+ d_refine_count(0),
+ d_syntax_guided(false) {}
+
+CegConjecture::~CegConjecture() {}
+
+void CegConjecture::assign( Node q ) {
+ Assert( d_embed_quant.isNull() );
+ Assert( q.getKind()==FORALL );
+ Trace("cegqi") << "CegConjecture : assign : " << q << std::endl;
+ d_quant = q;
+
+ // pre-simplify the quantified formula based on the process utility
+ d_simp_quant = d_ceg_proc->preSimplify(d_quant);
+
+ std::map< Node, Node > templates;
+ std::map< Node, Node > templates_arg;
+ //register with single invocation if applicable
+ if (d_qe->getQuantAttributes()->isSygus(q))
+ {
+ d_ceg_si->initialize(d_simp_quant);
+ d_simp_quant = d_ceg_si->getSimplifiedConjecture();
+ // carry the templates
+ for( unsigned i=0; i<q[0].getNumChildren(); i++ ){
+ Node v = q[0][i];
+ Node templ = d_ceg_si->getTemplate(v);
+ if( !templ.isNull() ){
+ templates[v] = templ;
+ templates_arg[v] = d_ceg_si->getTemplateArg(v);
+ }
+ }
+ }
+
+ // post-simplify the quantified formula based on the process utility
+ d_simp_quant = d_ceg_proc->postSimplify(d_simp_quant);
+
+ // finished simplifying the quantified formula at this point
+
+ // convert to deep embedding and finalize single invocation here
+ d_embed_quant = d_ceg_gc->process(d_simp_quant, templates, templates_arg);
+ Trace("cegqi") << "CegConjecture : converted to embedding : " << d_embed_quant << std::endl;
+
+ // we now finalize the single invocation module, based on the syntax restrictions
+ if (d_qe->getQuantAttributes()->isSygus(q))
+ {
+ d_ceg_si->finishInit( d_ceg_gc->isSyntaxRestricted(), d_ceg_gc->hasSyntaxITE() );
+ }
+
+ Assert( d_candidates.empty() );
+ std::vector< Node > vars;
+ for( unsigned i=0; i<d_embed_quant[0].getNumChildren(); i++ ){
+ vars.push_back( d_embed_quant[0][i] );
+ Node e = NodeManager::currentNM()->mkSkolem( "e", d_embed_quant[0][i].getType() );
+ d_candidates.push_back( e );
+ }
+ Trace("cegqi") << "Base quantified formula is : " << d_embed_quant << std::endl;
+ //construct base instantiation
+ d_base_inst = Rewriter::rewrite(d_qe->getInstantiate()->getInstantiation(
+ d_embed_quant, vars, d_candidates));
+ Trace("cegqi") << "Base instantiation is : " << d_base_inst << std::endl;
+ d_base_body = d_base_inst;
+ if (d_base_body.getKind() == NOT && d_base_body[0].getKind() == FORALL)
+ {
+ for (const Node& v : d_base_body[0][0])
+ {
+ d_base_vars.push_back(v);
+ }
+ d_base_body = d_base_body[0][1];
+ }
+
+ // register this term with sygus database and other utilities that impact
+ // the enumerative sygus search
+ std::vector< Node > guarded_lemmas;
+ if( !isSingleInvocation() ){
+ d_ceg_proc->initialize(d_base_inst, d_candidates);
+ if( options::sygusPbe() ){
+ d_ceg_pbe->initialize(d_base_inst, d_candidates, guarded_lemmas);
+ } else {
+ for (unsigned i = 0; i < d_candidates.size(); i++) {
+ Node e = d_candidates[i];
+ d_qe->getTermDatabaseSygus()->registerEnumerator(e, e, this);
+ }
+ }
+ }
+
+ if (d_qe->getQuantAttributes()->isSygus(q))
+ {
+ collectDisjuncts( d_base_inst, d_base_disj );
+ Trace("cegqi") << "Conjecture has " << d_base_disj.size() << " disjuncts." << std::endl;
+ //store the inner variables for each disjunct
+ for( unsigned j=0; j<d_base_disj.size(); j++ ){
+ Trace("cegqi") << " " << j << " : " << d_base_disj[j] << std::endl;
+ d_inner_vars_disj.push_back( std::vector< Node >() );
+ //if the disjunct is an existential, store it
+ if( d_base_disj[j].getKind()==NOT && d_base_disj[j][0].getKind()==FORALL ){
+ for( unsigned k=0; k<d_base_disj[j][0][0].getNumChildren(); k++ ){
+ d_inner_vars.push_back( d_base_disj[j][0][0][k] );
+ d_inner_vars_disj[j].push_back( d_base_disj[j][0][0][k] );
+ }
+ }
+ }
+ d_syntax_guided = true;
+ }
+ else if (d_qe->getQuantAttributes()->isSynthesis(q))
+ {
+ d_syntax_guided = false;
+ }else{
+ Assert( false );
+ }
+
+ // initialize the guard
+ if( !d_syntax_guided ){
+ if( d_nsg_guard.isNull() ){
+ d_nsg_guard = Rewriter::rewrite( NodeManager::currentNM()->mkSkolem( "G", NodeManager::currentNM()->booleanType() ) );
+ d_nsg_guard = d_qe->getValuation().ensureLiteral( d_nsg_guard );
+ AlwaysAssert( !d_nsg_guard.isNull() );
+ d_qe->getOutputChannel().requirePhase( d_nsg_guard, true );
+ // negated base as a guarded lemma
+ guarded_lemmas.push_back( d_base_inst.negate() );
+ }
+ }else if( d_ceg_si->getGuard().isNull() ){
+ std::vector< Node > lems;
+ d_ceg_si->getInitialSingleInvLemma( lems );
+ for( unsigned i=0; i<lems.size(); i++ ){
+ Trace("cegqi-lemma") << "Cegqi::Lemma : single invocation " << i << " : " << lems[i] << std::endl;
+ d_qe->getOutputChannel().lemma( lems[i] );
+ if( Trace.isOn("cegqi-debug") ){
+ Node rlem = Rewriter::rewrite( lems[i] );
+ Trace("cegqi-debug") << "...rewritten : " << rlem << std::endl;
+ }
+ }
+ }
+ Assert( !getGuard().isNull() );
+ Node gneg = getGuard().negate();
+ for( unsigned i=0; i<guarded_lemmas.size(); i++ ){
+ Node lem = NodeManager::currentNM()->mkNode( OR, gneg, guarded_lemmas[i] );
+ Trace("cegqi-lemma") << "Cegqi::Lemma : initial (guarded) lemma : " << lem << std::endl;
+ d_qe->getOutputChannel().lemma( lem );
+ }
+
+ // assign the cegis sampler if applicable
+ if (options::cegisSample() != CEGIS_SAMPLE_NONE)
+ {
+ Trace("cegis-sample") << "Initialize sampler for " << d_base_body << "..."
+ << std::endl;
+ TypeNode bt = d_base_body.getType();
+ d_cegis_sampler.initialize(bt, d_base_vars, options::sygusSamples());
+ }
+
+ Trace("cegqi") << "...finished, single invocation = " << isSingleInvocation() << std::endl;
+}
+
+Node CegConjecture::getGuard() {
+ return !d_syntax_guided ? d_nsg_guard : d_ceg_si->getGuard();
+}
+
+bool CegConjecture::isSingleInvocation() const {
+ return d_ceg_si->isSingleInvocation();
+}
+
+bool CegConjecture::needsCheck( std::vector< Node >& lem ) {
+ if( isSingleInvocation() && !d_ceg_si->needsCheck() ){
+ return false;
+ }else{
+ bool value;
+ Assert( !getGuard().isNull() );
+ // non or fully single invocation : look at guard only
+ if( d_qe->getValuation().hasSatValue( getGuard(), value ) ) {
+ if( !value ){
+ Trace("cegqi-engine-debug") << "Conjecture is infeasible." << std::endl;
+ return false;
+ }
+ }else{
+ Assert( false );
+ }
+ return true;
+ }
+}
+
+
+void CegConjecture::doSingleInvCheck(std::vector< Node >& lems) {
+ if( d_ceg_si!=NULL ){
+ d_ceg_si->check(lems);
+ }
+}
+
+void CegConjecture::doBasicCheck(std::vector< Node >& lems) {
+ std::vector< Node > model_terms;
+ std::vector< Node > clist;
+ getCandidateList( clist, true );
+ Assert( clist.size()==d_quant[0].getNumChildren() );
+ getModelValues( clist, model_terms );
+ if (d_qe->getInstantiate()->addInstantiation(d_quant, model_terms))
+ {
+ //record the instantiation
+ recordInstantiation( model_terms );
+ }else{
+ Assert( false );
+ }
+}
+
+bool CegConjecture::needsRefinement() {
+ return !d_ce_sk.empty();
+}
+
+void CegConjecture::getCandidateList( std::vector< Node >& clist, bool forceOrig ) {
+ if( d_ceg_pbe->isPbe() && !forceOrig ){
+ d_ceg_pbe->getCandidateList( d_candidates, clist );
+ }else{
+ clist.insert( clist.end(), d_candidates.begin(), d_candidates.end() );
+ }
+}
+
+bool CegConjecture::constructCandidates( std::vector< Node >& clist, std::vector< Node >& model_values, std::vector< Node >& candidate_values,
+ std::vector< Node >& lems ) {
+ Assert( clist.size()==model_values.size() );
+ if( d_ceg_pbe->isPbe() ){
+ return d_ceg_pbe->constructCandidates( clist, model_values, d_candidates, candidate_values, lems );
+ }else{
+ Assert( model_values.size()==d_candidates.size() );
+ candidate_values.insert( candidate_values.end(), model_values.begin(), model_values.end() );
+ }
+ return true;
+}
+
+void CegConjecture::doCheck(std::vector< Node >& lems, std::vector< Node >& model_values) {
+ std::vector< Node > clist;
+ getCandidateList( clist );
+ std::vector< Node > c_model_values;
+ Trace("cegqi-check") << "CegConjuncture : check, build candidates..." << std::endl;
+ bool constructed_cand = constructCandidates( clist, model_values, c_model_values, lems );
+
+ //must get a counterexample to the value of the current candidate
+ Node inst;
+ if( constructed_cand ){
+ if( Trace.isOn("cegqi-check") ){
+ Trace("cegqi-check") << "CegConjuncture : check candidate : " << std::endl;
+ for( unsigned i=0; i<c_model_values.size(); i++ ){
+ Trace("cegqi-check") << " " << i << " : " << d_candidates[i] << " -> " << c_model_values[i] << std::endl;
+ }
+ }
+ Assert( c_model_values.size()==d_candidates.size() );
+ inst = d_base_inst.substitute( d_candidates.begin(), d_candidates.end(), c_model_values.begin(), c_model_values.end() );
+ }else{
+ inst = d_base_inst;
+ }
+
+ //check whether we will run CEGIS on inner skolem variables
+ bool sk_refine = ( !isGround() || d_refine_count==0 ) && ( !d_ceg_pbe->isPbe() || constructed_cand );
+ if( sk_refine ){
+ if (options::cegisSample() == CEGIS_SAMPLE_TRUST)
+ {
+ // we have that the current candidate passed a sample test
+ // since we trust sampling in this mode, we assert there is no
+ // counterexample to the conjecture here.
+ NodeManager* nm = NodeManager::currentNM();
+ Node lem = nm->mkNode(OR, d_quant.negate(), nm->mkConst(false));
+ lem = getStreamGuardedLemma(lem);
+ lems.push_back(lem);
+ recordInstantiation(c_model_values);
+ return;
+ }
+ Assert( d_ce_sk.empty() );
+ d_ce_sk.push_back( std::vector< Node >() );
+ }else{
+ if( !constructed_cand ){
+ return;
+ }
+ }
+
+ std::vector< Node > ic;
+ ic.push_back( d_quant.negate() );
+ std::vector< Node > d;
+ collectDisjuncts( inst, d );
+ Assert( d.size()==d_base_disj.size() );
+ //immediately skolemize inner existentials
+ for( unsigned i=0; i<d.size(); i++ ){
+ Node dr = Rewriter::rewrite( d[i] );
+ if( dr.getKind()==NOT && dr[0].getKind()==FORALL ){
+ if( constructed_cand ){
+ ic.push_back(d_qe->getSkolemize()->getSkolemizedBody(dr[0]).negate());
+ }
+ if( sk_refine ){
+ Assert( !isGround() );
+ d_ce_sk.back().push_back( dr[0] );
+ }
+ }else{
+ if( constructed_cand ){
+ ic.push_back( dr );
+ if( !d_inner_vars_disj[i].empty() ){
+ Trace("cegqi-debug") << "*** quantified disjunct : " << d[i] << " simplifies to " << dr << std::endl;
+ }
+ }
+ if( sk_refine ){
+ d_ce_sk.back().push_back( Node::null() );
+ }
+ }
+ }
+ if( constructed_cand ){
+ Node lem = NodeManager::currentNM()->mkNode( OR, ic );
+ lem = Rewriter::rewrite( lem );
+ //eagerly unfold applications of evaluation function
+ if( options::sygusDirectEval() ){
+ Trace("cegqi-debug") << "pre-unfold counterexample : " << lem << std::endl;
+ std::map< Node, Node > visited_n;
+ lem = d_qe->getTermDatabaseSygus()->getEagerUnfold( lem, visited_n );
+ }
+ lem = getStreamGuardedLemma(lem);
+ lems.push_back( lem );
+ recordInstantiation( c_model_values );
+ }
+}
+
+void CegConjecture::doRefine( std::vector< Node >& lems ){
+ Assert( lems.empty() );
+ Assert( d_ce_sk.size()==1 );
+
+ //first, make skolem substitution
+ Trace("cegqi-refine") << "doRefine : construct skolem substitution..." << std::endl;
+ std::vector< Node > sk_vars;
+ std::vector< Node > sk_subs;
+ //collect the substitution over all disjuncts
+ for( unsigned k=0; k<d_ce_sk[0].size(); k++ ){
+ Node ce_q = d_ce_sk[0][k];
+ if( !ce_q.isNull() ){
+ Assert( !d_inner_vars_disj[k].empty() );
+ std::vector<Node> skolems;
+ d_qe->getSkolemize()->getSkolemConstants(ce_q, skolems);
+ Assert(d_inner_vars_disj[k].size() == skolems.size());
+ std::vector< Node > model_values;
+ getModelValues(skolems, model_values);
+ sk_vars.insert( sk_vars.end(), d_inner_vars_disj[k].begin(), d_inner_vars_disj[k].end() );
+ sk_subs.insert( sk_subs.end(), model_values.begin(), model_values.end() );
+ }else{
+ if( !d_inner_vars_disj[k].empty() ){
+ //denegrate case : quantified disjunct was trivially true and does not need to be refined
+ //add trivial substitution (in case we need substitution for previous cex's)
+ for( unsigned i=0; i<d_inner_vars_disj[k].size(); i++ ){
+ sk_vars.push_back( d_inner_vars_disj[k][i] );
+ sk_subs.push_back( getModelValue( d_inner_vars_disj[k][i] ) ); // will return dummy value
+ }
+ }
+ }
+ }
+
+ //for conditional evaluation
+ std::vector< Node > lem_c;
+ Assert( d_ce_sk[0].size()==d_base_disj.size() );
+ std::vector< Node > inst_cond_c;
+ Trace("cegqi-refine") << "doRefine : Construct refinement lemma..." << std::endl;
+ for( unsigned k=0; k<d_ce_sk[0].size(); k++ ){
+ Node ce_q = d_ce_sk[0][k];
+ Trace("cegqi-refine-debug") << " For counterexample point, disjunct " << k << " : " << ce_q << " " << d_base_disj[k] << std::endl;
+ Node c_disj;
+ if( !ce_q.isNull() ){
+ Assert( d_base_disj[k].getKind()==kind::NOT && d_base_disj[k][0].getKind()==kind::FORALL );
+ c_disj = d_base_disj[k][0][1];
+ }else{
+ if( d_inner_vars_disj[k].empty() ){
+ c_disj = d_base_disj[k].negate();
+ }else{
+ //denegrate case : quantified disjunct was trivially true and does not need to be refined
+ Trace("cegqi-refine-debug") << "*** skip " << d_base_disj[k] << std::endl;
+ }
+ }
+ if( !c_disj.isNull() ){
+ //compute the body, inst_cond
+ //standard CEGIS refinement : plug in values, assert that d_candidates must satisfy entire specification
+ lem_c.push_back( c_disj );
+ }
+ }
+ Assert( sk_vars.size()==sk_subs.size() );
+
+ Node base_lem = lem_c.size()==1 ? lem_c[0] : NodeManager::currentNM()->mkNode( AND, lem_c );
+
+ Trace("cegqi-refine") << "doRefine : construct and finalize lemmas..." << std::endl;
+
+
+ base_lem = base_lem.substitute( sk_vars.begin(), sk_vars.end(), sk_subs.begin(), sk_subs.end() );
+ base_lem = Rewriter::rewrite( base_lem );
+ d_refinement_lemmas.push_back(base_lem);
+
+ Node lem =
+ NodeManager::currentNM()->mkNode(OR, getGuard().negate(), base_lem);
+ lems.push_back( lem );
+
+ d_ce_sk.clear();
+}
+
+void CegConjecture::preregisterConjecture( Node q ) {
+ d_ceg_si->preregisterConjecture( q );
+}
+
+void CegConjecture::getModelValues( std::vector< Node >& n, std::vector< Node >& v ) {
+ Trace("cegqi-engine") << " * Value is : ";
+ for( unsigned i=0; i<n.size(); i++ ){
+ Node nv = getModelValue( n[i] );
+ v.push_back( nv );
+ if( Trace.isOn("cegqi-engine") ){
+ TypeNode tn = nv.getType();
+ Trace("cegqi-engine") << n[i] << " -> ";
+ std::stringstream ss;
+ Printer::getPrinter(options::outputLanguage())->toStreamSygus(ss, nv);
+ Trace("cegqi-engine") << ss.str() << " ";
+ if (Trace.isOn("cegqi-engine-rr"))
+ {
+ Node bv = d_qe->getTermDatabaseSygus()->sygusToBuiltin(nv, tn);
+ bv = Rewriter::rewrite(bv);
+ Trace("cegqi-engine-rr") << " -> " << bv << std::endl;
+ }
+ }
+ Assert( !nv.isNull() );
+ }
+ Trace("cegqi-engine") << std::endl;
+}
+
+Node CegConjecture::getModelValue( Node n ) {
+ Trace("cegqi-mv") << "getModelValue for : " << n << std::endl;
+ return d_qe->getModel()->getValue( n );
+}
+
+void CegConjecture::debugPrint( const char * c ) {
+ Trace(c) << "Synthesis conjecture : " << d_embed_quant << std::endl;
+ Trace(c) << " * Candidate program/output symbol : ";
+ for( unsigned i=0; i<d_candidates.size(); i++ ){
+ Trace(c) << d_candidates[i] << " ";
+ }
+ Trace(c) << std::endl;
+ Trace(c) << " * Candidate ce skolems : ";
+ for( unsigned i=0; i<d_ce_sk.size(); i++ ){
+ Trace(c) << d_ce_sk[i] << " ";
+ }
+}
+
+Node CegConjecture::getCurrentStreamGuard() const {
+ if( d_stream_guards.empty() ){
+ return Node::null();
+ }else{
+ return d_stream_guards.back();
+ }
+}
+
+Node CegConjecture::getStreamGuardedLemma(Node n) const
+{
+ if (options::sygusStream())
+ {
+ // if we are in streaming mode, we guard with the current stream guard
+ Node csg = getCurrentStreamGuard();
+ Assert(!csg.isNull());
+ return NodeManager::currentNM()->mkNode(kind::OR, csg.negate(), n);
+ }
+ return n;
+}
+
+Node CegConjecture::getNextDecisionRequest( unsigned& priority ) {
+ // first, must try the guard
+ // which denotes "this conjecture is feasible"
+ Node feasible_guard = getGuard();
+ bool value;
+ if( !d_qe->getValuation().hasSatValue( feasible_guard, value ) ) {
+ priority = 0;
+ return feasible_guard;
+ }else{
+ if( value ){
+ // the conjecture is feasible
+ if( options::sygusStream() ){
+ Assert( !isSingleInvocation() );
+ // if we are in sygus streaming mode, then get the "next guard"
+ // which denotes "we have not yet generated the next solution to the conjecture"
+ Node curr_stream_guard = getCurrentStreamGuard();
+ bool needs_new_stream_guard = false;
+ if( curr_stream_guard.isNull() ){
+ needs_new_stream_guard = true;
+ }else{
+ // check the polarity of the guard
+ if( !d_qe->getValuation().hasSatValue( curr_stream_guard, value ) ) {
+ priority = 0;
+ return curr_stream_guard;
+ }else{
+ if( !value ){
+ Trace("cegqi-debug") << "getNextDecision : we have a new solution since stream guard was propagated false: " << curr_stream_guard << std::endl;
+ // we have generated a solution, print it
+ // get the current output stream
+ // this output stream should coincide with wherever --dump-synth is output on
+ Options& nodeManagerOptions = NodeManager::currentNM()->getOptions();
+ printSynthSolution( *nodeManagerOptions.getOut(), false );
+ // need to make the next stream guard
+ needs_new_stream_guard = true;
+
+ // We will not refine the current candidate solution since it is a solution
+ // thus, we clear information regarding the current refinement
+ d_ce_sk.clear();
+ // However, we need to exclude the current solution using an explicit refinement
+ // so that we proceed to the next solution.
+ std::vector< Node > clist;
+ getCandidateList( clist );
+ Trace("cegqi-debug") << "getNextDecision : solution was : " << std::endl;
+ std::vector< Node > exp;
+ for( unsigned i=0; i<clist.size(); i++ ){
+ Node cprog = clist[i];
+ Node sol = cprog;
+ if( !d_cinfo[cprog].d_inst.empty() ){
+ sol = d_cinfo[cprog].d_inst.back();
+ // add to explanation of exclusion
+ d_qe->getTermDatabaseSygus()
+ ->getExplain()
+ ->getExplanationForConstantEquality(cprog, sol, exp);
+ }
+ Trace("cegqi-debug") << " " << cprog << " -> " << sol << std::endl;
+ }
+ Assert( !exp.empty() );
+ Node exc_lem = exp.size()==1 ? exp[0] : NodeManager::currentNM()->mkNode( kind::AND, exp );
+ exc_lem = exc_lem.negate();
+ Trace("cegqi-lemma") << "Cegqi::Lemma : stream exclude current solution : " << exc_lem << std::endl;
+ d_qe->getOutputChannel().lemma( exc_lem );
+ }
+ }
+ }
+ if( needs_new_stream_guard ){
+ // generate a new stream guard
+ curr_stream_guard = Rewriter::rewrite( NodeManager::currentNM()->mkSkolem( "G_Stream", NodeManager::currentNM()->booleanType() ) );
+ curr_stream_guard = d_qe->getValuation().ensureLiteral( curr_stream_guard );
+ AlwaysAssert( !curr_stream_guard.isNull() );
+ d_qe->getOutputChannel().requirePhase( curr_stream_guard, true );
+ d_stream_guards.push_back( curr_stream_guard );
+ Trace("cegqi-debug") << "getNextDecision : allocate new stream guard : " << curr_stream_guard << std::endl;
+ // return it as a decision
+ priority = 0;
+ return curr_stream_guard;
+ }
+ }
+ }else{
+ Trace("cegqi-debug") << "getNextDecision : conjecture is infeasible." << std::endl;
+ }
+ }
+ return Node::null();
+}
+
+void CegConjecture::printSynthSolution( std::ostream& out, bool singleInvocation ) {
+ Trace("cegqi-debug") << "Printing synth solution..." << std::endl;
+ Assert( d_quant[0].getNumChildren()==d_embed_quant[0].getNumChildren() );
+ std::vector<Node> sols;
+ std::vector<int> statuses;
+ getSynthSolutionsInternal(sols, statuses, singleInvocation);
+ for (unsigned i = 0, size = d_embed_quant[0].getNumChildren(); i < size; i++)
+ {
+ Node sol = sols[i];
+ if (!sol.isNull())
+ {
+ Node prog = d_embed_quant[0][i];
+ int status = statuses[i];
+ TypeNode tn = prog.getType();
+ const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
+ std::stringstream ss;
+ ss << prog;
+ std::string f(ss.str());
+ f.erase(f.begin());
+ out << "(define-fun " << f << " ";
+ if( dt.getSygusVarList().isNull() ){
+ out << "() ";
+ }else{
+ out << dt.getSygusVarList() << " ";
+ }
+ out << dt.getSygusType() << " ";
+ if( status==0 ){
+ out << sol;
+ }else{
+ Printer::getPrinter(options::outputLanguage())->toStreamSygus(out, sol);
+ }
+ out << ")" << std::endl;
+ CegInstantiation* cei = d_qe->getCegInstantiation();
+ ++(cei->d_statistics.d_solutions);
+
+ if (status != 0 && options::sygusRewSynth())
+ {
+ TermDbSygus* sygusDb = d_qe->getTermDatabaseSygus();
+ std::map<Node, SygusSamplerExt>::iterator its = d_sampler.find(prog);
+ if (its == d_sampler.end())
+ {
+ d_sampler[prog].initializeSygusExt(
+ d_qe, prog, options::sygusSamples());
+ its = d_sampler.find(prog);
+ }
+ Node solb = sygusDb->sygusToBuiltin(sol, prog.getType());
+ Node eq_sol = its->second.registerTerm(solb);
+ // eq_sol is a candidate solution that is equivalent to sol
+ if (eq_sol != solb)
+ {
+ ++(cei->d_statistics.d_candidate_rewrites);
+ if (!eq_sol.isNull())
+ {
+ // Terms solb and eq_sol are equivalent under sample points but do
+ // not rewrite to the same term. Hence, this indicates a candidate
+ // rewrite.
+ out << "(candidate-rewrite " << solb << " " << eq_sol << ")"
+ << std::endl;
+ ++(cei->d_statistics.d_candidate_rewrites_print);
+ // debugging information
+ if (Trace.isOn("sygus-rr-debug"))
+ {
+ ExtendedRewriter* er = sygusDb->getExtRewriter();
+ Node solbr = er->extendedRewrite(solb);
+ Node eq_solr = er->extendedRewrite(eq_sol);
+ Trace("sygus-rr-debug")
+ << "; candidate #1 ext-rewrites to: " << solbr << std::endl;
+ Trace("sygus-rr-debug")
+ << "; candidate #2 ext-rewrites to: " << eq_solr << std::endl;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void CegConjecture::getSynthSolutions(std::map<Node, Node>& sol_map,
+ bool singleInvocation)
+{
+ NodeManager* nm = NodeManager::currentNM();
+ TermDbSygus* sygusDb = d_qe->getTermDatabaseSygus();
+ std::vector<Node> sols;
+ std::vector<int> statuses;
+ getSynthSolutionsInternal(sols, statuses, singleInvocation);
+ for (unsigned i = 0, size = d_embed_quant[0].getNumChildren(); i < size; i++)
+ {
+ Node sol = sols[i];
+ int status = statuses[i];
+ // get the builtin solution
+ Node bsol = sol;
+ if (status != 0)
+ {
+ // convert sygus to builtin here
+ bsol = sygusDb->sygusToBuiltin(sol, sol.getType());
+ }
+ // convert to lambda
+ TypeNode tn = d_embed_quant[0][i].getType();
+ const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
+ Node bvl = Node::fromExpr(dt.getSygusVarList());
+ if (!bvl.isNull())
+ {
+ bsol = nm->mkNode(LAMBDA, bvl, bsol);
+ }
+ // store in map
+ Node fvar = d_quant[0][i];
+ Assert(fvar.getType() == bsol.getType());
+ sol_map[fvar] = bsol;
+ }
+}
+
+void CegConjecture::getSynthSolutionsInternal(std::vector<Node>& sols,
+ std::vector<int>& statuses,
+ bool singleInvocation)
+{
+ for (unsigned i = 0, size = d_embed_quant[0].getNumChildren(); i < size; i++)
+ {
+ Node prog = d_embed_quant[0][i];
+ Trace("cegqi-debug") << " get solution for " << prog << std::endl;
+ TypeNode tn = prog.getType();
+ Assert(tn.isDatatype());
+ // get the solution
+ Node sol;
+ int status = -1;
+ if (singleInvocation)
+ {
+ Assert(d_ceg_si != NULL);
+ sol = d_ceg_si->getSolution(i, tn, status, true);
+ if (!sol.isNull())
+ {
+ sol = sol.getKind() == LAMBDA ? sol[1] : sol;
+ }
+ }
+ else
+ {
+ Node cprog = getCandidate(i);
+ if (!d_cinfo[cprog].d_inst.empty())
+ {
+ // the solution is just the last instantiated term
+ sol = d_cinfo[cprog].d_inst.back();
+ status = 1;
+
+ // check if there was a template
+ Node sf = d_quant[0][i];
+ Node templ = d_ceg_si->getTemplate(sf);
+ if (!templ.isNull())
+ {
+ Trace("cegqi-inv-debug")
+ << sf << " used template : " << templ << std::endl;
+ // if it was not embedded into the grammar
+ if (!options::sygusTemplEmbedGrammar())
+ {
+ TNode templa = d_ceg_si->getTemplateArg(sf);
+ // make the builtin version of the full solution
+ TermDbSygus* sygusDb = d_qe->getTermDatabaseSygus();
+ sol = sygusDb->sygusToBuiltin(sol, sol.getType());
+ Trace("cegqi-inv") << "Builtin version of solution is : " << sol
+ << ", type : " << sol.getType() << std::endl;
+ TNode tsol = sol;
+ sol = templ.substitute(templa, tsol);
+ Trace("cegqi-inv-debug") << "With template : " << sol << std::endl;
+ sol = Rewriter::rewrite(sol);
+ Trace("cegqi-inv-debug") << "Simplified : " << sol << std::endl;
+ // now, reconstruct to the syntax
+ sol = d_ceg_si->reconstructToSyntax(sol, tn, status, true);
+ sol = sol.getKind() == LAMBDA ? sol[1] : sol;
+ Trace("cegqi-inv-debug")
+ << "Reconstructed to syntax : " << sol << std::endl;
+ }
+ else
+ {
+ Trace("cegqi-inv-debug")
+ << "...was embedding into grammar." << std::endl;
+ }
+ }
+ else
+ {
+ Trace("cegqi-inv-debug")
+ << sf << " did not use template" << std::endl;
+ }
+ }
+ else
+ {
+ Trace("cegqi-warn") << "WARNING : No recorded instantiations for "
+ "syntax-guided solution!"
+ << std::endl;
+ }
+ }
+ sols.push_back(sol);
+ statuses.push_back(status);
+ }
+}
+
+Node CegConjecture::getSymmetryBreakingPredicate(
+ Node x, Node e, TypeNode tn, unsigned tindex, unsigned depth)
+{
+ std::vector<Node> sb_lemmas;
+
+ // based on simple preprocessing
+ Node ppred =
+ d_ceg_proc->getSymmetryBreakingPredicate(x, e, tn, tindex, depth);
+ if (!ppred.isNull())
+ {
+ sb_lemmas.push_back(ppred);
+ }
+
+ // other static conjecture-dependent symmetry breaking goes here
+
+ if (!sb_lemmas.empty())
+ {
+ return sb_lemmas.size() == 1
+ ? sb_lemmas[0]
+ : NodeManager::currentNM()->mkNode(kind::AND, sb_lemmas);
+ }
+ else
+ {
+ return Node::null();
+ }
+}
+
+bool CegConjecture::sampleAddRefinementLemma(std::vector<Node>& vals,
+ std::vector<Node>& lems)
+{
+ if (Trace.isOn("cegis-sample"))
+ {
+ Trace("cegis-sample") << "Check sampling for candidate solution"
+ << std::endl;
+ for (unsigned i = 0, size = vals.size(); i < size; i++)
+ {
+ Trace("cegis-sample")
+ << " " << d_candidates[i] << " -> " << vals[i] << std::endl;
+ }
+ }
+ Assert(vals.size() == d_candidates.size());
+ Node sbody = d_base_body.substitute(
+ d_candidates.begin(), d_candidates.end(), vals.begin(), vals.end());
+ Trace("cegis-sample-debug") << "Sample " << sbody << std::endl;
+ // do eager unfolding
+ std::map<Node, Node> visited_n;
+ sbody = d_qe->getTermDatabaseSygus()->getEagerUnfold(sbody, visited_n);
+ Trace("cegis-sample") << "Sample (after unfolding): " << sbody << std::endl;
+
+ NodeManager* nm = NodeManager::currentNM();
+ for (unsigned i = 0, size = d_cegis_sampler.getNumSamplePoints(); i < size;
+ i++)
+ {
+ if (d_cegis_sample_refine.find(i) == d_cegis_sample_refine.end())
+ {
+ Node ev = d_cegis_sampler.evaluate(sbody, i);
+ Trace("cegis-sample-debug")
+ << "...evaluate point #" << i << " to " << ev << std::endl;
+ Assert(ev.isConst());
+ Assert(ev.getType().isBoolean());
+ if (!ev.getConst<bool>())
+ {
+ Trace("cegis-sample-debug") << "...false for point #" << i << std::endl;
+ // mark this as a CEGIS point (no longer sampled)
+ d_cegis_sample_refine.insert(i);
+ std::vector<Node> vars;
+ std::vector<Node> pt;
+ d_cegis_sampler.getSamplePoint(i, vars, pt);
+ Assert(d_base_vars.size() == pt.size());
+ Node rlem = d_base_body.substitute(
+ d_base_vars.begin(), d_base_vars.end(), pt.begin(), pt.end());
+ rlem = Rewriter::rewrite(rlem);
+ if (std::find(
+ d_refinement_lemmas.begin(), d_refinement_lemmas.end(), rlem)
+ == d_refinement_lemmas.end())
+ {
+ if (Trace.isOn("cegis-sample"))
+ {
+ Trace("cegis-sample") << " false for point #" << i << " : ";
+ for (const Node& cn : pt)
+ {
+ Trace("cegis-sample") << cn << " ";
+ }
+ Trace("cegis-sample") << std::endl;
+ }
+ Trace("cegqi-engine") << " *** Refine by sampling" << std::endl;
+ d_refinement_lemmas.push_back(rlem);
+ // if trust, we are not interested in sending out refinement lemmas
+ if (options::cegisSample() != CEGIS_SAMPLE_TRUST)
+ {
+ Node lem = nm->mkNode(OR, getGuard().negate(), rlem);
+ lems.push_back(lem);
+ }
+ return true;
+ }
+ else
+ {
+ Trace("cegis-sample-debug") << "...duplicate." << std::endl;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+}/* namespace CVC4::theory::quantifiers */
+}/* namespace CVC4::theory */
+}/* namespace CVC4 */
--- /dev/null
+/********************* */
+/*! \file ce_guided_conjecture.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds, Tim King
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief class that encapsulates counterexample-guided instantiation
+ ** techniques for a single SyGuS synthesis conjecture
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_CONJECTURE_H
+#define __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_CONJECTURE_H
+
+#include <memory>
+
+#include "theory/quantifiers/sygus/sygus_pbe.h"
+#include "theory/quantifiers/sygus/ce_guided_single_inv.h"
+#include "theory/quantifiers/sygus/sygus_grammar_cons.h"
+#include "theory/quantifiers/sygus/sygus_process_conj.h"
+#include "theory/quantifiers/sygus_sampler.h"
+#include "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+/** a synthesis conjecture
+ * This class implements approaches for a synthesis conecjture, given by data
+ * member d_quant.
+ * This includes both approaches for synthesis in Reynolds et al CAV 2015. It
+ * determines which approach and optimizations are applicable to the
+ * conjecture, and has interfaces for implementing them.
+ */
+class CegConjecture {
+public:
+ CegConjecture( QuantifiersEngine * qe );
+ ~CegConjecture();
+ /** get original version of conjecture */
+ Node getConjecture() { return d_quant; }
+ /** get deep embedding version of conjecture */
+ Node getEmbeddedConjecture() { return d_embed_quant; }
+ /** get next decision request */
+ Node getNextDecisionRequest( unsigned& priority );
+
+ //-------------------------------for counterexample-guided check/refine
+ /** increment the number of times we have successfully done candidate
+ * refinement */
+ void incrementRefineCount() { d_refine_count++; }
+ /** whether the conjecture is waiting for a call to doCheck below */
+ bool needsCheck( std::vector< Node >& lem );
+ /** whether the conjecture is waiting for a call to doRefine below */
+ bool needsRefinement();
+ /** get the list of candidates */
+ void getCandidateList( std::vector< Node >& clist, bool forceOrig = false );
+ /** do single invocation check
+ * This updates Gamma for an iteration of step 2 of Figure 1 of Reynolds et al CAV 2015.
+ */
+ void doSingleInvCheck(std::vector< Node >& lems);
+ /** do syntax-guided enumerative check
+ * This is step 2(a) of Figure 3 of Reynolds et al CAV 2015.
+ */
+ void doCheck(std::vector< Node >& lems, std::vector< Node >& model_values);
+ /** do basic check
+ * This is called for non-SyGuS synthesis conjectures
+ */
+ void doBasicCheck(std::vector< Node >& lems);
+ /** do refinement
+ * This is step 2(b) of Figure 3 of Reynolds et al CAV 2015.
+ */
+ void doRefine(std::vector< Node >& lems);
+ //-------------------------------end for counterexample-guided check/refine
+ /**
+ * prints the synthesis solution to output stream out.
+ *
+ * singleInvocation : set to true if we should consult the single invocation
+ * module to get synthesis solutions.
+ */
+ void printSynthSolution( std::ostream& out, bool singleInvocation );
+ /** get synth solutions
+ *
+ * This returns a map from function-to-synthesize variables to their
+ * builtin solution, which has the same type. For example, for synthesis
+ * conjecture exists f. forall x. f( x )>x, this function may return the map
+ * containing the entry:
+ * f -> (lambda x. x+1)
+ *
+ * singleInvocation : set to true if we should consult the single invocation
+ * module to get synthesis solutions.
+ */
+ void getSynthSolutions(std::map<Node, Node>& sol_map, bool singleInvocation);
+ /** get guard, this is "G" in Figure 3 of Reynolds et al CAV 2015 */
+ Node getGuard();
+ /** is ground */
+ bool isGround() { return d_inner_vars.empty(); }
+ /** does this conjecture correspond to a syntax-guided synthesis input */
+ bool isSyntaxGuided() const { return d_syntax_guided; }
+ /** are we using single invocation techniques */
+ bool isSingleInvocation() const;
+ /** preregister conjecture
+ * This is used as a heuristic for solution reconstruction, so that we
+ * remember expressions in the conjecture before preprocessing, since they
+ * may be helpful during solution reconstruction (Figure 5 of Reynolds et al CAV 2015)
+ */
+ void preregisterConjecture( Node q );
+ /** assign conjecture q to this class */
+ void assign( Node q );
+ /** has a conjecture been assigned to this class */
+ bool isAssigned() { return !d_embed_quant.isNull(); }
+ /** get model values for terms n, store in vector v */
+ void getModelValues( std::vector< Node >& n, std::vector< Node >& v );
+ /** get model value for term n */
+ Node getModelValue( Node n );
+
+ //-----------------------------------refinement lemmas
+ /** get number of refinement lemmas we have added so far */
+ unsigned getNumRefinementLemmas() { return d_refinement_lemmas.size(); }
+ /** get refinement lemma
+ *
+ * If d_embed_quant is forall d. exists y. P( d, y ), then a refinement
+ * lemma is one of the form ~P( d_candidates, c ) for some c.
+ */
+ Node getRefinementLemma( unsigned i ) { return d_refinement_lemmas[i]; }
+ /** sample add refinement lemma
+ *
+ * This function will check if there is a sample point in d_sampler that
+ * refutes the candidate solution (d_quant_vars->vals). If so, it adds a
+ * refinement lemma to the lists d_refinement_lemmas that corresponds to that
+ * sample point, and adds a lemma to lems if cegisSample mode is not trust.
+ */
+ bool sampleAddRefinementLemma(std::vector<Node>& vals,
+ std::vector<Node>& lems);
+ //-----------------------------------end refinement lemmas
+
+ /** get program by examples utility */
+ CegConjecturePbe* getPbe() { return d_ceg_pbe.get(); }
+ /** get utility for static preprocessing and analysis of conjectures */
+ CegConjectureProcess* getProcess() { return d_ceg_proc.get(); }
+ /** get the symmetry breaking predicate for type */
+ Node getSymmetryBreakingPredicate(
+ Node x, Node e, TypeNode tn, unsigned tindex, unsigned depth);
+ /** print out debug information about this conjecture */
+ void debugPrint( const char * c );
+private:
+ /** reference to quantifier engine */
+ QuantifiersEngine * d_qe;
+ /** single invocation utility */
+ std::unique_ptr<CegConjectureSingleInv> d_ceg_si;
+ /** program by examples utility */
+ std::unique_ptr<CegConjecturePbe> d_ceg_pbe;
+ /** utility for static preprocessing and analysis of conjectures */
+ std::unique_ptr<CegConjectureProcess> d_ceg_proc;
+ /** grammar utility */
+ std::unique_ptr<CegGrammarConstructor> d_ceg_gc;
+ /** list of constants for quantified formula
+ * The outer Skolems for the negation of d_embed_quant.
+ */
+ std::vector< Node > d_candidates;
+ /** base instantiation
+ * If d_embed_quant is forall d. exists y. P( d, y ), then
+ * this is the formula exists y. P( d_candidates, y ).
+ */
+ Node d_base_inst;
+ /** If d_base_inst is exists y. P( d, y ), then this is y. */
+ std::vector<Node> d_base_vars;
+ /**
+ * If d_base_inst is exists y. P( d, y ), then this is the formula
+ * P( d_candidates, y ).
+ */
+ Node d_base_body;
+ /** expand base inst to disjuncts */
+ std::vector< Node > d_base_disj;
+ /** list of variables on inner quantification */
+ std::vector< Node > d_inner_vars;
+ std::vector< std::vector< Node > > d_inner_vars_disj;
+ /** current extential quantifeirs whose couterexamples we must refine */
+ std::vector< std::vector< Node > > d_ce_sk;
+
+ //-----------------------------------refinement lemmas
+ /** refinement lemmas */
+ std::vector< Node > d_refinement_lemmas;
+ //-----------------------------------end refinement lemmas
+
+ /** the asserted (negated) conjecture */
+ Node d_quant;
+ /** (negated) conjecture after simplification */
+ Node d_simp_quant;
+ /** (negated) conjecture after simplification, conversion to deep embedding */
+ Node d_embed_quant;
+ /** candidate information */
+ class CandidateInfo {
+ public:
+ CandidateInfo(){}
+ /** list of terms we have instantiated candidates with */
+ std::vector< Node > d_inst;
+ };
+ std::map< Node, CandidateInfo > d_cinfo;
+ /** number of times we have called doRefine */
+ unsigned d_refine_count;
+ /** construct candidates */
+ bool constructCandidates( std::vector< Node >& clist, std::vector< Node >& model_values,
+ std::vector< Node >& candidate_values, std::vector< Node >& lems );
+ /** get candidadate */
+ Node getCandidate( unsigned int i ) { return d_candidates[i]; }
+ /** record instantiation (this is used to construct solutions later) */
+ void recordInstantiation( std::vector< Node >& vs ) {
+ Assert( vs.size()==d_candidates.size() );
+ for( unsigned i=0; i<vs.size(); i++ ){
+ d_cinfo[d_candidates[i]].d_inst.push_back( vs[i] );
+ }
+ }
+ /** get synth solutions internal
+ *
+ * This function constructs the body of solutions for all
+ * functions-to-synthesize in this conjecture and stores them in sols, in
+ * order. For each solution added to sols, we add an integer indicating what
+ * kind of solution n is, where if sols[i] = n, then
+ * if status[i] = 0: n is the (builtin term) corresponding to the solution,
+ * if status[i] = 1: n is the sygus representation of the solution.
+ * We store builtin versions under some conditions (such as when the sygus
+ * grammar is being ignored).
+ *
+ * singleInvocation : set to true if we should consult the single invocation
+ * module to get synthesis solutions.
+ *
+ * For example, for conjecture exists fg. forall x. f(x)>g(x), this function
+ * may set ( sols, status ) to ( { x+1, d_x() }, { 1, 0 } ), where d_x() is
+ * the sygus datatype constructor corresponding to variable x.
+ */
+ void getSynthSolutionsInternal(std::vector<Node>& sols,
+ std::vector<int>& status,
+ bool singleInvocation);
+ //-------------------------------- sygus stream
+ /** the streaming guards for sygus streaming mode */
+ std::vector< Node > d_stream_guards;
+ /** get current stream guard */
+ Node getCurrentStreamGuard() const;
+ /** get stream guarded lemma
+ *
+ * If sygusStream is enabled, this returns ( G V n ) where G is the guard
+ * returned by getCurrentStreamGuard, otherwise this returns n.
+ */
+ Node getStreamGuardedLemma(Node n) const;
+ //-------------------------------- end sygus stream
+ //-------------------------------- non-syntax guided (deprecated)
+ /** Whether we are syntax-guided (e.g. was the input in SyGuS format).
+ * This includes SyGuS inputs where no syntactic restrictions are provided.
+ */
+ bool d_syntax_guided;
+ /** the guard for non-syntax-guided synthesis */
+ Node d_nsg_guard;
+ //-------------------------------- end non-syntax guided (deprecated)
+ /** sygus sampler objects for each program variable
+ *
+ * This is used for the sygusRewSynth() option to synthesize new candidate
+ * rewrite rules.
+ */
+ std::map<Node, SygusSamplerExt> d_sampler;
+ /** sampler object for the option cegisSample()
+ *
+ * This samples points of the type of the inner variables of the synthesis
+ * conjecture (d_base_vars).
+ */
+ SygusSampler d_cegis_sampler;
+ /** cegis sample refine points
+ *
+ * Stores the list of indices of sample points in d_cegis_sampler we have
+ * added as refinement lemmas.
+ */
+ std::unordered_set<unsigned> d_cegis_sample_refine;
+};
+
+} /* namespace CVC4::theory::quantifiers */
+} /* namespace CVC4::theory */
+} /* namespace CVC4 */
+
+#endif
--- /dev/null
+/********************* */
+/*! \file ce_guided_instantiation.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds, Tim King, Morgan Deters
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief counterexample guided instantiation class
+ ** This class is the entry point for both synthesis algorithms in Reynolds et al CAV 2015
+ **
+ **/
+#include "theory/quantifiers/sygus/ce_guided_instantiation.h"
+
+#include "options/quantifiers_options.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/theory_engine.h"
+#include "theory/quantifiers/quantifiers_attributes.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+#include "theory/quantifiers/term_util.h"
+//FIXME : remove this include (github issue #1156)
+#include "theory/bv/theory_bv_rewriter.h"
+
+using namespace CVC4::kind;
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+CegInstantiation::CegInstantiation( QuantifiersEngine * qe, context::Context* c ) : QuantifiersModule( qe ){
+ d_conj = new CegConjecture( qe );
+ d_last_inst_si = false;
+}
+
+CegInstantiation::~CegInstantiation(){
+ delete d_conj;
+}
+
+bool CegInstantiation::needsCheck( Theory::Effort e ) {
+ return e>=Theory::EFFORT_LAST_CALL;
+}
+
+QuantifiersModule::QEffort CegInstantiation::needsModel(Theory::Effort e)
+{
+ return d_conj->isSingleInvocation() ? QEFFORT_STANDARD : QEFFORT_MODEL;
+}
+
+void CegInstantiation::check(Theory::Effort e, QEffort quant_e)
+{
+ unsigned echeck =
+ d_conj->isSingleInvocation() ? QEFFORT_STANDARD : QEFFORT_MODEL;
+ if( quant_e==echeck ){
+ Trace("cegqi-engine") << "---Counterexample Guided Instantiation Engine---" << std::endl;
+ Trace("cegqi-engine-debug") << std::endl;
+ bool active = false;
+ bool value;
+ if( d_quantEngine->getValuation().hasSatValue( d_conj->getConjecture(), value ) ) {
+ active = value;
+ }else{
+ Trace("cegqi-engine-debug") << "...no value for quantified formula." << std::endl;
+ }
+ Trace("cegqi-engine-debug") << "Current conjecture status : active : " << active << std::endl;
+ std::vector< Node > lem;
+ if( active && d_conj->needsCheck( lem ) ){
+ checkCegConjecture( d_conj );
+ }else{
+ Trace("cegqi-engine-debug") << "...does not need check." << std::endl;
+ for( unsigned i=0; i<lem.size(); i++ ){
+ Trace("cegqi-lemma") << "Cegqi::Lemma : check lemma : " << lem[i] << std::endl;
+ d_quantEngine->addLemma( lem[i] );
+ }
+ }
+ Trace("cegqi-engine") << "Finished Counterexample Guided Instantiation engine." << std::endl;
+ }
+}
+
+void CegInstantiation::registerQuantifier( Node q ) {
+ if( d_quantEngine->getOwner( q )==this ){ // && d_eval_axioms.find( q )==d_eval_axioms.end() ){
+ if( !d_conj->isAssigned() ){
+ Trace("cegqi") << "Register conjecture : " << q << std::endl;
+ d_conj->assign( q );
+ }else{
+ Assert( d_conj->getEmbeddedConjecture()==q );
+ }
+ }else{
+ Trace("cegqi-debug") << "Register quantifier : " << q << std::endl;
+ }
+}
+
+Node CegInstantiation::getNextDecisionRequest( unsigned& priority ) {
+ if( d_conj->isAssigned() ){
+ Node dec_req = d_conj->getNextDecisionRequest( priority );
+ if( !dec_req.isNull() ){
+ Trace("cegqi-debug") << "CEGQI : Decide next on : " << dec_req << "..." << std::endl;
+ return dec_req;
+ }
+ }
+ return Node::null();
+}
+
+void CegInstantiation::checkCegConjecture( CegConjecture * conj ) {
+ Node q = conj->getEmbeddedConjecture();
+ Node aq = conj->getConjecture();
+ if( Trace.isOn("cegqi-engine-debug") ){
+ conj->debugPrint("cegqi-engine-debug");
+ Trace("cegqi-engine-debug") << std::endl;
+ }
+
+ if( !conj->needsRefinement() ){
+ Trace("cegqi-engine-debug") << "Do conjecture check..." << std::endl;
+ if( conj->isSyntaxGuided() ){
+ std::vector< Node > clems;
+ conj->doSingleInvCheck( clems );
+ if( !clems.empty() ){
+ d_last_inst_si = true;
+ for( unsigned j=0; j<clems.size(); j++ ){
+ Trace("cegqi-lemma") << "Cegqi::Lemma : single invocation instantiation : " << clems[j] << std::endl;
+ d_quantEngine->addLemma( clems[j] );
+ }
+ d_statistics.d_cegqi_si_lemmas += clems.size();
+ Trace("cegqi-engine") << " ...try single invocation." << std::endl;
+ return;
+ }
+ //ignore return value here
+ std::vector< Node > clist;
+ conj->getCandidateList( clist );
+ std::vector< Node > model_values;
+ conj->getModelValues( clist, model_values );
+ if( options::sygusDirectEval() ){
+ bool addedEvalLemmas = false;
+ if( options::sygusCRefEval() ){
+ Trace("cegqi-engine") << " *** Do conjecture refinement evaluation..." << std::endl;
+ // see if any refinement lemma is refuted by evaluation
+ std::vector< Node > cre_lems;
+ getCRefEvaluationLemmas( conj, clist, model_values, cre_lems );
+ if( !cre_lems.empty() ){
+ for( unsigned j=0; j<cre_lems.size(); j++ ){
+ Node lem = cre_lems[j];
+ if( d_quantEngine->addLemma( lem ) ){
+ Trace("cegqi-lemma") << "Cegqi::Lemma : cref evaluation : " << lem << std::endl;
+ addedEvalLemmas = true;
+ }
+ }
+ if( addedEvalLemmas ){
+ //return;
+ }
+ }
+ }
+ Trace("cegqi-engine") << " *** Do direct evaluation..." << std::endl;
+ std::vector< Node > eager_terms;
+ std::vector< Node > eager_vals;
+ std::vector< Node > eager_exps;
+ for( unsigned j=0; j<clist.size(); j++ ){
+ Trace("cegqi-debug") << " register " << clist[j] << " -> " << model_values[j] << std::endl;
+ d_quantEngine->getTermDatabaseSygus()->registerModelValue( clist[j], model_values[j], eager_terms, eager_vals, eager_exps );
+ }
+ Trace("cegqi-debug") << "...produced " << eager_terms.size() << " eager evaluation lemmas." << std::endl;
+ if( !eager_terms.empty() ){
+ for( unsigned j=0; j<eager_terms.size(); j++ ){
+ Node lem = NodeManager::currentNM()->mkNode( kind::OR, eager_exps[j].negate(), eager_terms[j].eqNode( eager_vals[j] ) );
+ if( d_quantEngine->getTheoryEngine()->isTheoryEnabled(THEORY_BV) ){
+ //FIXME: hack to incorporate hacks from BV for division by zero (github issue #1156)
+ lem = bv::TheoryBVRewriter::eliminateBVSDiv( lem );
+ }
+ if( d_quantEngine->addLemma( lem ) ){
+ Trace("cegqi-lemma") << "Cegqi::Lemma : evaluation : " << lem << std::endl;
+ addedEvalLemmas = true;
+ }
+ }
+ }
+ if( addedEvalLemmas ){
+ return;
+ }
+ }
+
+ Trace("cegqi-engine") << " *** Check candidate phase..." << std::endl;
+ std::vector< Node > cclems;
+ conj->doCheck( cclems, model_values );
+ bool addedLemma = false;
+ for( unsigned i=0; i<cclems.size(); i++ ){
+ Node lem = cclems[i];
+ d_last_inst_si = false;
+ Trace("cegqi-lemma") << "Cegqi::Lemma : counterexample : " << lem << std::endl;
+ if( d_quantEngine->addLemma( lem ) ){
+ ++(d_statistics.d_cegqi_lemmas_ce);
+ addedLemma = true;
+ }else{
+ //this may happen if we eagerly unfold, simplify to true
+ if( !options::sygusDirectEval() ){
+ Trace("cegqi-warn") << " ...FAILED to add candidate!" << std::endl;
+ }else{
+ Trace("cegqi-engine-debug") << " ...FAILED to add candidate!" << std::endl;
+ }
+ }
+ }
+ if( addedLemma ){
+ Trace("cegqi-engine") << " ...check for counterexample." << std::endl;
+ }else{
+ if( conj->needsRefinement() ){
+ //immediately go to refine candidate
+ checkCegConjecture( conj );
+ return;
+ }
+ }
+ }else{
+ Assert( aq==q );
+ Trace("cegqi-engine") << " *** Check candidate phase (non-SyGuS)." << std::endl;
+ std::vector< Node > lems;
+ conj->doBasicCheck(lems);
+ Assert(lems.empty());
+ }
+ }else{
+ Trace("cegqi-engine") << " *** Refine candidate phase..." << std::endl;
+ std::vector< Node > rlems;
+ conj->doRefine( rlems );
+ bool addedLemma = false;
+ for( unsigned i=0; i<rlems.size(); i++ ){
+ Node lem = rlems[i];
+ Trace("cegqi-lemma") << "Cegqi::Lemma : candidate refinement : " << lem << std::endl;
+ bool res = d_quantEngine->addLemma( lem );
+ if( res ){
+ ++(d_statistics.d_cegqi_lemmas_refine);
+ conj->incrementRefineCount();
+ addedLemma = true;
+ }else{
+ Trace("cegqi-warn") << " ...FAILED to add refinement!" << std::endl;
+ }
+ }
+ if( addedLemma ){
+ Trace("cegqi-engine") << " ...refine candidate." << std::endl;
+ }
+ }
+}
+
+void CegInstantiation::getCRefEvaluationLemmas( CegConjecture * conj, std::vector< Node >& vs, std::vector< Node >& ms, std::vector< Node >& lems ) {
+ Trace("sygus-cref-eval") << "Cref eval : conjecture has " << conj->getNumRefinementLemmas() << " refinement lemmas." << std::endl;
+ unsigned nlemmas = conj->getNumRefinementLemmas();
+ if (nlemmas > 0 || options::cegisSample() != CEGIS_SAMPLE_NONE)
+ {
+ Assert( vs.size()==ms.size() );
+
+ TermDbSygus* tds = d_quantEngine->getTermDatabaseSygus();
+ Node nfalse = d_quantEngine->getTermUtil()->d_false;
+ Node neg_guard = conj->getGuard().negate();
+ for (unsigned i = 0; i <= nlemmas; i++)
+ {
+ if (i == nlemmas)
+ {
+ bool addedSample = false;
+ // find a new one by sampling, if applicable
+ if (options::cegisSample() != CEGIS_SAMPLE_NONE)
+ {
+ addedSample = conj->sampleAddRefinementLemma(ms, lems);
+ }
+ if (!addedSample)
+ {
+ return;
+ }
+ }
+ Node lem;
+ std::map< Node, Node > visited;
+ std::map< Node, std::vector< Node > > exp;
+ lem = conj->getRefinementLemma(i);
+ if( !lem.isNull() ){
+ std::vector< Node > lem_conj;
+ //break into conjunctions
+ if( lem.getKind()==kind::AND ){
+ for( unsigned i=0; i<lem.getNumChildren(); i++ ){
+ lem_conj.push_back( lem[i] );
+ }
+ }else{
+ lem_conj.push_back( lem );
+ }
+ EvalSygusInvarianceTest vsit;
+ for( unsigned j=0; j<lem_conj.size(); j++ ){
+ Node lemc = lem_conj[j];
+ Trace("sygus-cref-eval") << "Check refinement lemma conjunct " << lemc << " against current model." << std::endl;
+ Trace("sygus-cref-eval2") << "Check refinement lemma conjunct " << lemc << " against current model." << std::endl;
+ Node cre_lem;
+ Node lemcs = lemc.substitute( vs.begin(), vs.end(), ms.begin(), ms.end() );
+ Trace("sygus-cref-eval2") << "...under substitution it is : " << lemcs << std::endl;
+ Node lemcsu = vsit.doEvaluateWithUnfolding(tds, lemcs);
+ Trace("sygus-cref-eval2") << "...after unfolding is : " << lemcsu << std::endl;
+ if( lemcsu==d_quantEngine->getTermUtil()->d_false ){
+ std::vector< Node > msu;
+ std::vector< Node > mexp;
+ msu.insert( msu.end(), ms.begin(), ms.end() );
+ for( unsigned k=0; k<vs.size(); k++ ){
+ vsit.setUpdatedTerm(msu[k]);
+ msu[k] = vs[k];
+ // substitute for everything except this
+ Node sconj =
+ lemc.substitute(vs.begin(), vs.end(), msu.begin(), msu.end());
+ vsit.init(sconj, vs[k], nfalse);
+ // get minimal explanation for this
+ Node ut = vsit.getUpdatedTerm();
+ Trace("sygus-cref-eval2-debug")
+ << " compute min explain of : " << vs[k] << " = " << ut
+ << std::endl;
+ d_quantEngine->getTermDatabaseSygus()
+ ->getExplain()
+ ->getExplanationFor(vs[k], ut, mexp, vsit);
+ msu[k] = ut;
+ }
+ if( !mexp.empty() ){
+ Node en = mexp.size()==1 ? mexp[0] : NodeManager::currentNM()->mkNode( kind::AND, mexp );
+ cre_lem = NodeManager::currentNM()->mkNode( kind::OR, en.negate(), neg_guard );
+ }else{
+ cre_lem = neg_guard;
+ }
+ }
+ if( !cre_lem.isNull() ){
+ if( std::find( lems.begin(), lems.end(), cre_lem )==lems.end() ){
+ Trace("sygus-cref-eval") << "...produced lemma : " << cre_lem << std::endl;
+ lems.push_back( cre_lem );
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void CegInstantiation::printSynthSolution( std::ostream& out ) {
+ if( d_conj->isAssigned() )
+ {
+ d_conj->printSynthSolution( out, d_last_inst_si );
+ }
+ else
+ {
+ Assert( false );
+ }
+}
+
+void CegInstantiation::getSynthSolutions(std::map<Node, Node>& sol_map)
+{
+ if (d_conj->isAssigned())
+ {
+ d_conj->getSynthSolutions(sol_map, d_last_inst_si);
+ }
+ else
+ {
+ Assert(false);
+ }
+}
+
+void CegInstantiation::preregisterAssertion( Node n ) {
+ //check if it sygus conjecture
+ if( QuantAttributes::checkSygusConjecture( n ) ){
+ //this is a sygus conjecture
+ Trace("cegqi") << "Preregister sygus conjecture : " << n << std::endl;
+ d_conj->preregisterConjecture( n );
+ }
+}
+
+CegInstantiation::Statistics::Statistics()
+ : d_cegqi_lemmas_ce("CegInstantiation::cegqi_lemmas_ce", 0),
+ d_cegqi_lemmas_refine("CegInstantiation::cegqi_lemmas_refine", 0),
+ d_cegqi_si_lemmas("CegInstantiation::cegqi_lemmas_si", 0),
+ d_solutions("CegConjecture::solutions", 0),
+ d_candidate_rewrites_print("CegConjecture::candidate_rewrites_print", 0),
+ d_candidate_rewrites("CegConjecture::candidate_rewrites", 0)
+
+{
+ smtStatisticsRegistry()->registerStat(&d_cegqi_lemmas_ce);
+ smtStatisticsRegistry()->registerStat(&d_cegqi_lemmas_refine);
+ smtStatisticsRegistry()->registerStat(&d_cegqi_si_lemmas);
+ smtStatisticsRegistry()->registerStat(&d_solutions);
+ smtStatisticsRegistry()->registerStat(&d_candidate_rewrites_print);
+ smtStatisticsRegistry()->registerStat(&d_candidate_rewrites);
+}
+
+CegInstantiation::Statistics::~Statistics(){
+ smtStatisticsRegistry()->unregisterStat(&d_cegqi_lemmas_ce);
+ smtStatisticsRegistry()->unregisterStat(&d_cegqi_lemmas_refine);
+ smtStatisticsRegistry()->unregisterStat(&d_cegqi_si_lemmas);
+ smtStatisticsRegistry()->unregisterStat(&d_solutions);
+ smtStatisticsRegistry()->unregisterStat(&d_candidate_rewrites_print);
+ smtStatisticsRegistry()->unregisterStat(&d_candidate_rewrites);
+}
+
+}/* namespace CVC4::theory::quantifiers */
+}/* namespace CVC4::theory */
+}/* namespace CVC4 */
--- /dev/null
+/********************* */
+/*! \file ce_guided_instantiation.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds, Tim King
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief counterexample guided instantiation class
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_INSTANTIATION_H
+#define __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_INSTANTIATION_H
+
+#include "context/cdhashmap.h"
+#include "theory/quantifiers/sygus/ce_guided_conjecture.h"
+#include "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+class CegInstantiation : public QuantifiersModule
+{
+ typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap;
+private:
+ /** the quantified formula stating the synthesis conjecture */
+ CegConjecture * d_conj;
+ /** last instantiation by single invocation module? */
+ bool d_last_inst_si;
+private: //for direct evaluation
+ /** get refinement evaluation */
+ void getCRefEvaluationLemmas( CegConjecture * conj, std::vector< Node >& vs, std::vector< Node >& ms, std::vector< Node >& lems );
+private:
+ /** check conjecture */
+ void checkCegConjecture( CegConjecture * conj );
+public:
+ CegInstantiation( QuantifiersEngine * qe, context::Context* c );
+ ~CegInstantiation();
+public:
+ bool needsCheck( Theory::Effort e );
+ QEffort needsModel(Theory::Effort e);
+ /* Call during quantifier engine's check */
+ void check(Theory::Effort e, QEffort quant_e);
+ /* Called for new quantifiers */
+ void registerQuantifier( Node q );
+ /** get the next decision request */
+ Node getNextDecisionRequest( unsigned& priority );
+ /** Identify this module (for debugging, dynamic configuration, etc..) */
+ std::string identify() const { return "CegInstantiation"; }
+ /** print solution for synthesis conjectures */
+ void printSynthSolution( std::ostream& out );
+ /** get synth solutions
+ *
+ * This function adds entries to sol_map that map functions-to-synthesize
+ * with their solutions, for all active conjectures (currently just the one
+ * assigned to d_conj). This should be called immediately after the solver
+ * answers unsat for sygus input.
+ *
+ * For details on what is added to sol_map, see
+ * CegConjecture::getSynthSolutions.
+ */
+ void getSynthSolutions(std::map<Node, Node>& sol_map);
+ /** preregister assertion (before rewrite) */
+ void preregisterAssertion( Node n );
+public:
+ class Statistics {
+ public:
+ IntStat d_cegqi_lemmas_ce;
+ IntStat d_cegqi_lemmas_refine;
+ IntStat d_cegqi_si_lemmas;
+ IntStat d_solutions;
+ IntStat d_candidate_rewrites_print;
+ IntStat d_candidate_rewrites;
+ Statistics();
+ ~Statistics();
+ };/* class CegInstantiation::Statistics */
+ Statistics d_statistics;
+}; /* class CegInstantiation */
+
+} /* namespace CVC4::theory::quantifiers */
+} /* namespace CVC4::theory */
+} /* namespace CVC4 */
+
+#endif
--- /dev/null
+/********************* */
+/*! \file ce_guided_single_inv.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds, Tim King
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief utility for processing single invocation synthesis conjectures
+ **
+ **/
+#include "theory/quantifiers/sygus/ce_guided_single_inv.h"
+
+#include "options/quantifiers_options.h"
+#include "theory/arith/arith_msum.h"
+#include "theory/quantifiers/term_enumeration.h"
+#include "theory/quantifiers/term_util.h"
+
+using namespace CVC4;
+using namespace CVC4::kind;
+using namespace CVC4::theory;
+using namespace CVC4::theory::quantifiers;
+using namespace std;
+
+namespace CVC4 {
+
+bool CegqiOutputSingleInv::doAddInstantiation( std::vector< Node >& subs ) {
+ return d_out->doAddInstantiation( subs );
+}
+
+bool CegqiOutputSingleInv::isEligibleForInstantiation( Node n ) {
+ return d_out->isEligibleForInstantiation( n );
+}
+
+bool CegqiOutputSingleInv::addLemma( Node n ) {
+ return d_out->addLemma( n );
+}
+
+CegConjectureSingleInv::CegConjectureSingleInv(QuantifiersEngine* qe,
+ CegConjecture* p)
+ : d_qe(qe),
+ d_parent(p),
+ d_sip(new SingleInvocationPartition),
+ d_sol(new CegConjectureSingleInvSol(qe)),
+ d_cosi(new CegqiOutputSingleInv(this)),
+ d_cinst(NULL),
+ d_c_inst_match_trie(NULL),
+ d_has_ites(true),
+ d_single_invocation(false) {
+ // third and fourth arguments set to (false,false) until we have solution
+ // reconstruction for delta and infinity
+ d_cinst = new CegInstantiator(d_qe, d_cosi, false, false);
+
+ if (options::incrementalSolving()) {
+ d_c_inst_match_trie = new inst::CDInstMatchTrie(qe->getUserContext());
+ }
+}
+
+CegConjectureSingleInv::~CegConjectureSingleInv() {
+ if (d_c_inst_match_trie) {
+ delete d_c_inst_match_trie;
+ }
+ delete d_cinst;
+ delete d_cosi;
+ delete d_sol; // (new CegConjectureSingleInvSol(qe)),
+ delete d_sip; // d_sip(new SingleInvocationPartition),
+}
+
+void CegConjectureSingleInv::getInitialSingleInvLemma( std::vector< Node >& lems ) {
+ Assert( d_si_guard.isNull() );
+ //single invocation guard
+ d_si_guard = Rewriter::rewrite( NodeManager::currentNM()->mkSkolem( "G", NodeManager::currentNM()->booleanType() ) );
+ d_si_guard = d_qe->getValuation().ensureLiteral( d_si_guard );
+ AlwaysAssert( !d_si_guard.isNull() );
+ d_qe->getOutputChannel().requirePhase( d_si_guard, true );
+
+ if( !d_single_inv.isNull() ) {
+ //make for new var/sk
+ d_single_inv_var.clear();
+ d_single_inv_sk.clear();
+ Node inst;
+ if( d_single_inv.getKind()==FORALL ){
+ for( unsigned i=0; i<d_single_inv[0].getNumChildren(); i++ ){
+ std::stringstream ss;
+ ss << "k_" << d_single_inv[0][i];
+ Node k = NodeManager::currentNM()->mkSkolem( ss.str(), d_single_inv[0][i].getType(), "single invocation function skolem" );
+ d_single_inv_var.push_back( d_single_inv[0][i] );
+ d_single_inv_sk.push_back( k );
+ d_single_inv_sk_index[k] = i;
+ }
+ inst = d_single_inv[1].substitute( d_single_inv_var.begin(), d_single_inv_var.end(), d_single_inv_sk.begin(), d_single_inv_sk.end() );
+ }else{
+ inst = d_single_inv;
+ }
+ inst = TermUtil::simpleNegate( inst );
+ Trace("cegqi-si") << "Single invocation initial lemma : " << inst << std::endl;
+
+ //register with the instantiator
+ Node ginst = NodeManager::currentNM()->mkNode( OR, d_si_guard.negate(), inst );
+ lems.push_back( ginst );
+ //make and register the instantiator
+ if( d_cinst ){
+ delete d_cinst;
+ }
+ d_cinst = new CegInstantiator( d_qe, d_cosi, false, false );
+ d_cinst->registerCounterexampleLemma( lems, d_single_inv_sk );
+ }
+}
+
+void CegConjectureSingleInv::initialize( Node q ) {
+ // can only register one quantified formula with this
+ Assert( d_quant.isNull() );
+ d_quant = q;
+ d_simp_quant = q;
+ Trace("cegqi-si") << "CegConjectureSingleInv::initialize : " << q << std::endl;
+ // infer single invocation-ness
+ std::vector< Node > progs;
+ std::map< Node, std::vector< Node > > prog_vars;
+ for( unsigned i=0; i<q[0].getNumChildren(); i++ ){
+ Node sf = q[0][i];
+ progs.push_back( sf );
+ Node sfvl = sf.getAttribute(SygusSynthFunVarListAttribute());
+ for( unsigned j=0; j<sfvl.getNumChildren(); j++ ){
+ prog_vars[sf].push_back( sfvl[j] );
+ }
+ }
+ // compute single invocation partition
+ if( options::cegqiSingleInvMode()!=CEGQI_SI_MODE_NONE ){
+ Node qq;
+ if( q[1].getKind()==NOT && q[1][0].getKind()==FORALL ){
+ qq = q[1][0][1];
+ }else{
+ qq = TermUtil::simpleNegate( q[1] );
+ }
+ //process the single invocation-ness of the property
+ if( !d_sip->init( progs, qq ) ){
+ Trace("cegqi-si") << "...not single invocation (type mismatch)" << std::endl;
+ }else{
+ Trace("cegqi-si") << "- Partitioned to single invocation parts : " << std::endl;
+ d_sip->debugPrint( "cegqi-si" );
+
+ //map from program to bound variables
+ std::vector<Node> funcs;
+ d_sip->getFunctions(funcs);
+ for (unsigned j = 0, size = funcs.size(); j < size; j++)
+ {
+ Assert(std::find(progs.begin(), progs.end(), funcs[j]) != progs.end());
+ d_prog_to_sol_index[funcs[j]] = j;
+ }
+
+ //check if it is single invocation
+ if (!d_sip->isPurelySingleInvocation())
+ {
+ if( options::sygusInvTemplMode() != SYGUS_INV_TEMPL_MODE_NONE ){
+ //if we are doing invariant templates, then construct the template
+ Trace("cegqi-si") << "- Do transition inference..." << std::endl;
+ d_ti[q].process( qq );
+ Trace("cegqi-inv") << std::endl;
+ if( !d_ti[q].d_func.isNull() ){
+ // map the program back via non-single invocation map
+ Node prog = d_ti[q].d_func;
+ std::vector< Node > prog_templ_vars;
+ prog_templ_vars.insert( prog_templ_vars.end(), d_ti[q].d_vars.begin(), d_ti[q].d_vars.end() );
+ d_trans_pre[prog] = d_ti[q].getComponent( 1 );
+ d_trans_post[prog] = d_ti[q].getComponent( -1 );
+ Trace("cegqi-inv") << " precondition : " << d_trans_pre[prog] << std::endl;
+ Trace("cegqi-inv") << " postcondition : " << d_trans_post[prog] << std::endl;
+ std::vector<Node> sivars;
+ d_sip->getSingleInvocationVariables(sivars);
+ Node invariant = d_sip->getFunctionInvocationFor(prog);
+ Assert(!invariant.isNull());
+ invariant = invariant.substitute(sivars.begin(),
+ sivars.end(),
+ prog_templ_vars.begin(),
+ prog_templ_vars.end());
+ Trace("cegqi-inv") << " invariant : " << invariant << std::endl;
+
+ // store simplified version of quantified formula
+ d_simp_quant = d_sip->getFullSpecification();
+ std::vector< Node > new_bv;
+ for (unsigned j = 0, size = sivars.size(); j < size; j++)
+ {
+ new_bv.push_back(
+ NodeManager::currentNM()->mkBoundVar(sivars[j].getType()));
+ }
+ d_simp_quant = d_simp_quant.substitute(
+ sivars.begin(), sivars.end(), new_bv.begin(), new_bv.end());
+ Assert( q[1].getKind()==NOT && q[1][0].getKind()==FORALL );
+ for( unsigned j=0; j<q[1][0][0].getNumChildren(); j++ ){
+ new_bv.push_back( q[1][0][0][j] );
+ }
+ d_simp_quant = NodeManager::currentNM()->mkNode( kind::FORALL, NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, new_bv ), d_simp_quant ).negate();
+ d_simp_quant = Rewriter::rewrite( d_simp_quant );
+ d_simp_quant = NodeManager::currentNM()->mkNode( kind::FORALL, q[0], d_simp_quant, q[2] );
+ Trace("cegqi-si") << "Rewritten quantifier : " << d_simp_quant << std::endl;
+
+ //construct template argument
+ d_templ_arg[prog] = NodeManager::currentNM()->mkSkolem( "I", invariant.getType() );
+
+ //construct template
+ Node templ;
+ if( options::sygusInvAutoUnfold() ){
+ if( d_ti[q].isComplete() ){
+ Trace("cegqi-inv-auto-unfold") << "Automatic deterministic unfolding... " << std::endl;
+ // auto-unfold
+ DetTrace dt;
+ int init_dt = d_ti[q].initializeTrace( dt );
+ if( init_dt==0 ){
+ Trace("cegqi-inv-auto-unfold") << " Init : ";
+ dt.print("cegqi-inv-auto-unfold");
+ Trace("cegqi-inv-auto-unfold") << std::endl;
+ unsigned counter = 0;
+ unsigned status = 0;
+ while( counter<100 && status==0 ){
+ status = d_ti[q].incrementTrace( dt );
+ counter++;
+ Trace("cegqi-inv-auto-unfold") << " #" << counter << " : ";
+ dt.print("cegqi-inv-auto-unfold");
+ Trace("cegqi-inv-auto-unfold") << "...status = " << status << std::endl;
+ }
+ if( status==1 ){
+ // we have a trivial invariant
+ templ = d_ti[q].constructFormulaTrace( dt );
+ Trace("cegqi-inv") << "By finite deterministic terminating trace, a solution invariant is : " << std::endl;
+ Trace("cegqi-inv") << " " << templ << std::endl;
+ // FIXME : this should be unnecessary
+ templ = NodeManager::currentNM()->mkNode( AND, templ, d_templ_arg[prog] );
+ }
+ }else{
+ Trace("cegqi-inv-auto-unfold") << "...failed initialize." << std::endl;
+ }
+ }
+ }
+ if( templ.isNull() ){
+ if( options::sygusInvTemplMode() == SYGUS_INV_TEMPL_MODE_PRE ){
+ //d_templ[prog] = NodeManager::currentNM()->mkNode( AND, NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], invariant ), d_trans_post[prog] );
+ templ = NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], d_templ_arg[prog] );
+ }else{
+ Assert( options::sygusInvTemplMode() == SYGUS_INV_TEMPL_MODE_POST );
+ //d_templ[prog] = NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], NodeManager::currentNM()->mkNode( AND, d_trans_post[prog], invariant ) );
+ templ = NodeManager::currentNM()->mkNode( AND, d_trans_post[prog], d_templ_arg[prog] );
+ }
+ }
+ Trace("cegqi-inv") << " template (pre-substitution) : " << templ << std::endl;
+ Assert( !templ.isNull() );
+ // subsitute the template arguments
+ templ = templ.substitute( prog_templ_vars.begin(), prog_templ_vars.end(), prog_vars[prog].begin(), prog_vars[prog].end() );
+ Trace("cegqi-inv") << " template : " << templ << std::endl;
+ d_templ[prog] = templ;
+ }
+ }
+ }else{
+ //we are fully single invocation
+ d_single_invocation = true;
+ }
+ }
+ }
+}
+
+void CegConjectureSingleInv::finishInit( bool syntaxRestricted, bool hasItes ) {
+ d_has_ites = hasItes;
+ // do not do single invocation if grammar is restricted and CEGQI_SI_MODE_ALL is not enabled
+ if( options::cegqiSingleInvMode()==CEGQI_SI_MODE_USE && d_single_invocation && syntaxRestricted ){
+ d_single_invocation = false;
+ Trace("cegqi-si") << "...grammar is restricted, do not use single invocation techniques." << std::endl;
+ }
+
+ // we now have determined whether we will do single invocation techniques
+ if( d_single_invocation ){
+ d_single_inv = d_sip->getSingleInvocation();
+ d_single_inv = TermUtil::simpleNegate( d_single_inv );
+ std::vector<Node> func_vars;
+ d_sip->getFunctionVariables(func_vars);
+ if (!func_vars.empty())
+ {
+ Node pbvl = NodeManager::currentNM()->mkNode(BOUND_VAR_LIST, func_vars);
+ d_single_inv = NodeManager::currentNM()->mkNode( FORALL, pbvl, d_single_inv );
+ }
+ //now, introduce the skolems
+ std::vector<Node> sivars;
+ d_sip->getSingleInvocationVariables(sivars);
+ for (unsigned i = 0, size = sivars.size(); i < size; i++)
+ {
+ Node v = NodeManager::currentNM()->mkSkolem(
+ "a", sivars[i].getType(), "single invocation arg");
+ d_single_inv_arg_sk.push_back( v );
+ }
+ d_single_inv = d_single_inv.substitute(sivars.begin(),
+ sivars.end(),
+ d_single_inv_arg_sk.begin(),
+ d_single_inv_arg_sk.end());
+ Trace("cegqi-si") << "Single invocation formula is : " << d_single_inv << std::endl;
+ if( options::cbqiPreRegInst() && d_single_inv.getKind()==FORALL ){
+ //just invoke the presolve now
+ d_cinst->presolve( d_single_inv );
+ }
+ }else{
+ d_single_inv = Node::null();
+ Trace("cegqi-si") << "Formula is not single invocation." << std::endl;
+ if( options::cegqiSingleInvAbort() ){
+ Notice() << "Property is not single invocation." << std::endl;
+ exit( 1 );
+ }
+ }
+}
+
+bool CegConjectureSingleInv::doAddInstantiation( std::vector< Node >& subs ){
+ Assert( d_single_inv_sk.size()==subs.size() );
+ Trace("cegqi-si-inst-debug") << "CegConjectureSingleInv::doAddInstantiation, #vars = ";
+ Trace("cegqi-si-inst-debug") << d_single_inv_sk.size() << "..." << std::endl;
+ std::stringstream siss;
+ if( Trace.isOn("cegqi-si-inst-debug") || Trace.isOn("cegqi-engine") ){
+ siss << " * single invocation: " << std::endl;
+ for( unsigned j=0; j<d_single_inv_sk.size(); j++ ){
+ Node op = d_sip->getFunctionForFirstOrderVariable(d_single_inv[0][j]);
+ Assert(!op.isNull());
+ siss << " * " << op;
+ siss << " (" << d_single_inv_sk[j] << ")";
+ siss << " -> " << subs[j] << std::endl;
+ }
+ }
+ Trace("cegqi-si-inst-debug") << siss.str();
+
+ bool alreadyExists;
+ Node lem;
+ if( subs.empty() ){
+ Assert( d_single_inv.getKind()!=FORALL );
+ alreadyExists = false;
+ lem = d_single_inv;
+ }else{
+ Assert( d_single_inv.getKind()==FORALL );
+ if( options::incrementalSolving() ){
+ alreadyExists = !d_c_inst_match_trie->addInstMatch( d_qe, d_single_inv, subs, d_qe->getUserContext() );
+ }else{
+ alreadyExists = !d_inst_match_trie.addInstMatch( d_qe, d_single_inv, subs );
+ }
+ Trace("cegqi-si-inst-debug") << " * success = " << !alreadyExists << std::endl;
+ //Trace("cegqi-si-inst-debug") << siss.str();
+ //Trace("cegqi-si-inst-debug") << " * success = " << !alreadyExists << std::endl;
+ if( alreadyExists ){
+ return false;
+ }else{
+ Trace("cegqi-engine") << siss.str() << std::endl;
+ Assert( d_single_inv_var.size()==subs.size() );
+ lem = d_single_inv[1].substitute( d_single_inv_var.begin(), d_single_inv_var.end(), subs.begin(), subs.end() );
+ if( d_qe->getTermUtil()->containsVtsTerm( lem ) ){
+ Trace("cegqi-engine-debug") << "Rewrite based on vts symbols..." << std::endl;
+ lem = d_qe->getTermUtil()->rewriteVtsSymbols( lem );
+ }
+ }
+ }
+ Trace("cegqi-engine-debug") << "Rewrite..." << std::endl;
+ lem = Rewriter::rewrite( lem );
+ Trace("cegqi-si") << "Single invocation lemma : " << lem << std::endl;
+ if( std::find( d_lemmas_produced.begin(), d_lemmas_produced.end(), lem )==d_lemmas_produced.end() ){
+ d_curr_lemmas.push_back( lem );
+ d_lemmas_produced.push_back( lem );
+ d_inst.push_back( std::vector< Node >() );
+ d_inst.back().insert( d_inst.back().end(), subs.begin(), subs.end() );
+ }
+ return true;
+}
+
+bool CegConjectureSingleInv::isEligibleForInstantiation( Node n ) {
+ return n.getKind()!=SKOLEM || std::find( d_single_inv_arg_sk.begin(), d_single_inv_arg_sk.end(), n )!=d_single_inv_arg_sk.end();
+}
+
+bool CegConjectureSingleInv::addLemma( Node n ) {
+ d_curr_lemmas.push_back( n );
+ return true;
+}
+
+bool CegConjectureSingleInv::check( std::vector< Node >& lems ) {
+ if( !d_single_inv.isNull() ) {
+ Trace("cegqi-si-debug") << "CegConjectureSingleInv::check..." << std::endl;
+ Trace("cegqi-si-debug") << "CegConjectureSingleInv::check consulting ceg instantiation..." << std::endl;
+ d_curr_lemmas.clear();
+ Assert( d_cinst!=NULL );
+ //call check for instantiator
+ d_cinst->check();
+ Trace("cegqi-si-debug") << "...returned " << d_curr_lemmas.size() << " lemmas " << std::endl;
+ //add lemmas
+ lems.insert( lems.end(), d_curr_lemmas.begin(), d_curr_lemmas.end() );
+ return !lems.empty();
+ }else{
+ // not single invocation
+ return false;
+ }
+}
+
+Node CegConjectureSingleInv::constructSolution( std::vector< unsigned >& indices, unsigned i, unsigned index, std::map< Node, Node >& weak_imp ) {
+ Assert( index<d_inst.size() );
+ Assert( i<d_inst[index].size() );
+ unsigned uindex = indices[index];
+ if( index==indices.size()-1 ){
+ return d_inst[uindex][i];
+ }else{
+ Node cond = d_lemmas_produced[uindex];
+ //weaken based on unsat core
+ std::map< Node, Node >::iterator itw = weak_imp.find( cond );
+ if( itw!=weak_imp.end() ){
+ cond = itw->second;
+ }
+ cond = TermUtil::simpleNegate( cond );
+ Node ite1 = d_inst[uindex][i];
+ Node ite2 = constructSolution( indices, i, index+1, weak_imp );
+ return NodeManager::currentNM()->mkNode( ITE, cond, ite1, ite2 );
+ }
+}
+
+//TODO: use term size?
+struct sortSiInstanceIndices {
+ CegConjectureSingleInv* d_ccsi;
+ int d_i;
+ bool operator() (unsigned i, unsigned j) {
+ if( d_ccsi->d_inst[i][d_i].isConst() && !d_ccsi->d_inst[j][d_i].isConst() ){
+ return true;
+ }else{
+ return false;
+ }
+ }
+};
+
+
+Node CegConjectureSingleInv::postProcessSolution( Node n ){
+ ////remove boolean ITE (not allowed for sygus comp 2015)
+ //if( n.getKind()==ITE && n.getType().isBoolean() ){
+ // Node n1 = postProcessSolution( n[1] );
+ // Node n2 = postProcessSolution( n[2] );
+ // return NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, n[0], n1 ),
+ // NodeManager::currentNM()->mkNode( AND, n[0].negate(), n2 ) );
+ //}else{
+ bool childChanged = false;
+ Kind k = n.getKind();
+ if( n.getKind()==INTS_DIVISION_TOTAL ){
+ k = INTS_DIVISION;
+ childChanged = true;
+ }else if( n.getKind()==INTS_MODULUS_TOTAL ){
+ k = INTS_MODULUS;
+ childChanged = true;
+ }
+ std::vector< Node > children;
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ Node nn = postProcessSolution( n[i] );
+ children.push_back( nn );
+ childChanged = childChanged || nn!=n[i];
+ }
+ if( childChanged ){
+ if( n.hasOperator() && k==n.getKind() ){
+ children.insert( children.begin(), n.getOperator() );
+ }
+ return NodeManager::currentNM()->mkNode( k, children );
+ }else{
+ return n;
+ }
+ //}
+}
+
+
+Node CegConjectureSingleInv::getSolution( unsigned sol_index, TypeNode stn, int& reconstructed, bool rconsSygus ){
+ Assert( d_sol!=NULL );
+ Assert( !d_lemmas_produced.empty() );
+ const Datatype& dt = ((DatatypeType)(stn).toType()).getDatatype();
+ Node varList = Node::fromExpr( dt.getSygusVarList() );
+ Node prog = d_quant[0][sol_index];
+ std::vector< Node > vars;
+ Node s;
+ if( d_prog_to_sol_index.find( prog )==d_prog_to_sol_index.end() ){
+ Trace("csi-sol") << "Get solution for (unconstrained) " << prog << std::endl;
+ s = d_qe->getTermEnumeration()->getEnumerateTerm(
+ TypeNode::fromType(dt.getSygusType()), 0);
+ }else{
+ Trace("csi-sol") << "Get solution for " << prog << ", with skolems : ";
+ sol_index = d_prog_to_sol_index[prog];
+ d_sol->d_varList.clear();
+ Assert( d_single_inv_arg_sk.size()==varList.getNumChildren() );
+ for( unsigned i=0; i<d_single_inv_arg_sk.size(); i++ ){
+ Trace("csi-sol") << d_single_inv_arg_sk[i] << " ";
+ vars.push_back( d_single_inv_arg_sk[i] );
+ d_sol->d_varList.push_back( varList[i] );
+ }
+ Trace("csi-sol") << std::endl;
+
+ //construct the solution
+ Trace("csi-sol") << "Sort solution return values " << sol_index << std::endl;
+ bool useUnsatCore = false;
+ std::vector< Node > active_lemmas;
+ //minimize based on unsat core, if possible
+ std::map< Node, Node > weak_imp;
+ if( options::cegqiSolMinCore() ){
+ if( options::cegqiSolMinInst() ){
+ if( d_qe->getUnsatCoreLemmas( active_lemmas, weak_imp ) ){
+ useUnsatCore = true;
+ }
+ }else{
+ if( d_qe->getUnsatCoreLemmas( active_lemmas ) ){
+ useUnsatCore = true;
+ }
+ }
+ }
+ Assert( d_lemmas_produced.size()==d_inst.size() );
+ std::vector< unsigned > indices;
+ for( unsigned i=0; i<d_lemmas_produced.size(); i++ ){
+ bool incl = true;
+ if( useUnsatCore ){
+ incl = std::find( active_lemmas.begin(), active_lemmas.end(), d_lemmas_produced[i] )!=active_lemmas.end();
+ }
+ if( incl ){
+ Assert( sol_index<d_inst[i].size() );
+ indices.push_back( i );
+ }
+ }
+ Trace("csi-sol") << "...included " << indices.size() << " / " << d_lemmas_produced.size() << " instantiations." << std::endl;
+ Assert( !indices.empty() );
+ //sort indices based on heuristic : currently, do all constant returns first (leads to simpler conditions)
+ // TODO : to minimize solution size, put the largest term last
+ sortSiInstanceIndices ssii;
+ ssii.d_ccsi = this;
+ ssii.d_i = sol_index;
+ std::sort( indices.begin(), indices.end(), ssii );
+ Trace("csi-sol") << "Construct solution" << std::endl;
+ s = constructSolution( indices, sol_index, 0, weak_imp );
+ Assert( vars.size()==d_sol->d_varList.size() );
+ s = s.substitute( vars.begin(), vars.end(), d_sol->d_varList.begin(), d_sol->d_varList.end() );
+ }
+ d_orig_solution = s;
+
+ //simplify the solution
+ Trace("csi-sol") << "Solution (pre-simplification): " << d_orig_solution << std::endl;
+ s = d_sol->simplifySolution( s, stn );
+ Trace("csi-sol") << "Solution (post-simplification): " << s << std::endl;
+ return reconstructToSyntax( s, stn, reconstructed, rconsSygus );
+}
+
+Node CegConjectureSingleInv::reconstructToSyntax( Node s, TypeNode stn, int& reconstructed, bool rconsSygus ) {
+ d_solution = s;
+ const Datatype& dt = ((DatatypeType)(stn).toType()).getDatatype();
+
+ //reconstruct the solution into sygus if necessary
+ reconstructed = 0;
+ if( options::cegqiSingleInvReconstruct() && !dt.getSygusAllowAll() && !stn.isNull() && rconsSygus ){
+ d_sol->preregisterConjecture( d_orig_conjecture );
+ d_sygus_solution = d_sol->reconstructSolution( s, stn, reconstructed );
+ if( reconstructed==1 ){
+ Trace("csi-sol") << "Solution (post-reconstruction into Sygus): " << d_sygus_solution << std::endl;
+ }
+ }else{
+ Trace("csi-sol") << "Post-process solution..." << std::endl;
+ Node prev = d_solution;
+ d_solution = postProcessSolution( d_solution );
+ if( prev!=d_solution ){
+ Trace("csi-sol") << "Solution (after post process) : " << d_solution << std::endl;
+ }
+ }
+
+
+ if( Trace.isOn("csi-sol") ){
+ //debug solution
+ if( !d_sol->debugSolution( d_solution ) ){
+ Trace("csi-sol") << "WARNING : solution " << d_solution << " contains free constants." << std::endl;
+ //exit( 47 );
+ }else{
+ //exit( 49 );
+ }
+ }
+ if( Trace.isOn("cegqi-stats") ){
+ int tsize, itesize;
+ tsize = 0;itesize = 0;
+ d_sol->debugTermSize( d_orig_solution, tsize, itesize );
+ Trace("cegqi-stats") << tsize << " " << itesize << " ";
+ tsize = 0;itesize = 0;
+ d_sol->debugTermSize( d_solution, tsize, itesize );
+ Trace("cegqi-stats") << tsize << " " << itesize << " ";
+ if( !d_sygus_solution.isNull() ){
+ tsize = 0;itesize = 0;
+ d_sol->debugTermSize( d_sygus_solution, tsize, itesize );
+ Trace("cegqi-stats") << tsize << " - ";
+ }else{
+ Trace("cegqi-stats") << "null ";
+ }
+ Trace("cegqi-stats") << std::endl;
+ }
+ Node sol;
+ if( reconstructed==1 ){
+ sol = d_sygus_solution;
+ }else if( reconstructed==-1 ){
+ return Node::null();
+ }else{
+ sol = d_solution;
+ }
+ //make into lambda
+ if( !dt.getSygusVarList().isNull() ){
+ Node varList = Node::fromExpr( dt.getSygusVarList() );
+ return NodeManager::currentNM()->mkNode( LAMBDA, varList, sol );
+ }else{
+ return sol;
+ }
+}
+
+bool CegConjectureSingleInv::needsCheck() {
+ if( options::cegqiSingleInvMode()==CEGQI_SI_MODE_ALL_ABORT ){
+ if( !d_has_ites ){
+ return d_inst.empty();
+ }
+ }
+ return true;
+}
+
+void CegConjectureSingleInv::preregisterConjecture( Node q ) {
+ d_orig_conjecture = q;
+}
+
+bool DetTrace::DetTraceTrie::add( Node loc, std::vector< Node >& val, unsigned index ){
+ if( index==val.size() ){
+ if( d_children.empty() ){
+ d_children[loc].clear();
+ return true;
+ }else{
+ return false;
+ }
+ }else{
+ return d_children[val[index]].add( loc, val, index+1 );
+ }
+}
+
+Node DetTrace::DetTraceTrie::constructFormula( std::vector< Node >& vars, unsigned index ){
+ if( index==vars.size() ){
+ return NodeManager::currentNM()->mkConst( true );
+ }else{
+ std::vector< Node > disj;
+ for( std::map< Node, DetTraceTrie >::iterator it = d_children.begin(); it != d_children.end(); ++it ){
+ Node eq = vars[index].eqNode( it->first );
+ if( index<vars.size()-1 ){
+ Node conc = it->second.constructFormula( vars, index+1 );
+ disj.push_back( NodeManager::currentNM()->mkNode( kind::AND, eq, conc ) );
+ }else{
+ disj.push_back( eq );
+ }
+ }
+ Assert( !disj.empty() );
+ return disj.size()==1 ? disj[0] : NodeManager::currentNM()->mkNode( kind::OR, disj );
+ }
+}
+
+bool DetTrace::increment( Node loc, std::vector< Node >& vals ){
+ if( d_trie.add( loc, vals ) ){
+ for( unsigned i=0; i<vals.size(); i++ ){
+ d_curr[i] = vals[i];
+ }
+ return true;
+ }else{
+ return false;
+ }
+}
+
+Node DetTrace::constructFormula( std::vector< Node >& vars ) {
+ return d_trie.constructFormula( vars );
+}
+
+
+void DetTrace::print( const char* c ) {
+ for( unsigned i=0; i<d_curr.size(); i++ ){
+ Trace(c) << d_curr[i] << " ";
+ }
+}
+
+void TransitionInference::initialize( Node f, std::vector< Node >& vars ) {
+ Assert( d_vars.empty() );
+ d_func = f;
+ d_vars.insert( d_vars.end(), vars.begin(), vars.end() );
+}
+
+
+void TransitionInference::getConstantSubstitution( std::vector< Node >& vars, std::vector< Node >& disjuncts, std::vector< Node >& const_var, std::vector< Node >& const_subs, bool reqPol ) {
+ for( unsigned j=0; j<disjuncts.size(); j++ ){
+ Node sn;
+ if( !const_var.empty() ){
+ sn = disjuncts[j].substitute( const_var.begin(), const_var.end(), const_subs.begin(), const_subs.end() );
+ sn = Rewriter::rewrite( sn );
+ }else{
+ sn = disjuncts[j];
+ }
+ bool slit_pol = sn.getKind()!=NOT;
+ Node slit = sn.getKind()==NOT ? sn[0] : sn;
+ if( slit.getKind()==EQUAL && slit_pol==reqPol ){
+ // check if it is a variable equality
+ TNode v;
+ Node s;
+ for( unsigned r=0; r<2; r++ ){
+ if( std::find( vars.begin(), vars.end(), slit[r] )!=vars.end() ){
+ if( !TermUtil::containsTerm( slit[1-r], slit[r] ) ){
+ v = slit[r];
+ s = slit[1-r];
+ break;
+ }
+ }
+ }
+ if( v.isNull() ){
+ //solve for var
+ std::map< Node, Node > msum;
+ if (ArithMSum::getMonomialSumLit(slit, msum))
+ {
+ for( std::map< Node, Node >::iterator itm = msum.begin(); itm != msum.end(); ++itm ){
+ if( std::find( vars.begin(), vars.end(), itm->first )!=vars.end() ){
+ Node veq_c;
+ Node val;
+ int ires =
+ ArithMSum::isolate(itm->first, msum, veq_c, val, EQUAL);
+ if( ires!=0 && veq_c.isNull() && !TermUtil::containsTerm( val, itm->first ) ){
+ v = itm->first;
+ s = val;
+ }
+ }
+ }
+ }
+ }
+ if( !v.isNull() ){
+ TNode ts = s;
+ for( unsigned k=0; k<const_subs.size(); k++ ){
+ const_subs[k] = Rewriter::rewrite( const_subs[k].substitute( v, ts ) );
+ }
+ Trace("cegqi-inv-debug2") << "...substitution : " << v << " -> " << s << std::endl;
+ const_var.push_back( v );
+ const_subs.push_back( s );
+ }
+ }
+ }
+}
+
+void TransitionInference::process( Node n ) {
+ d_complete = true;
+ std::vector< Node > n_check;
+ if( n.getKind()==AND ){
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ n_check.push_back( n[i] );
+ }
+ }else{
+ n_check.push_back( n );
+ }
+ for( unsigned i=0; i<n_check.size(); i++ ){
+ Node nn = n_check[i];
+ std::map< Node, bool > visited;
+ std::map< bool, Node > terms;
+ std::vector< Node > disjuncts;
+ Trace("cegqi-inv") << "TransitionInference : Process disjunct : " << nn << std::endl;
+ if( processDisjunct( nn, terms, disjuncts, visited, true ) ){
+ if( !terms.empty() ){
+ Node norm_args;
+ int comp_num;
+ std::map< bool, Node >::iterator itt = terms.find( false );
+ if( itt!=terms.end() ){
+ norm_args = itt->second;
+ if( terms.find( true )!=terms.end() ){
+ comp_num = 0;
+ }else{
+ comp_num = -1;
+ }
+ }else{
+ norm_args = terms[true];
+ comp_num = 1;
+ }
+ std::vector< Node > subs;
+ for( unsigned j=0; j<norm_args.getNumChildren(); j++ ){
+ subs.push_back( norm_args[j] );
+ }
+ Trace("cegqi-inv-debug2") << " normalize based on " << norm_args << std::endl;
+ Assert( d_vars.size()==subs.size() );
+ for( unsigned j=0; j<disjuncts.size(); j++ ){
+ disjuncts[j] = Rewriter::rewrite( disjuncts[j].substitute( subs.begin(), subs.end(), d_vars.begin(), d_vars.end() ) );
+ Trace("cegqi-inv-debug2") << " ..." << disjuncts[j] << std::endl;
+ }
+ std::vector< Node > const_var;
+ std::vector< Node > const_subs;
+ if( comp_num==0 ){
+ //transition
+ Assert( terms.find( true )!=terms.end() );
+ Node next = terms[true];
+ next = Rewriter::rewrite( next.substitute( subs.begin(), subs.end(), d_vars.begin(), d_vars.end() ) );
+ Trace("cegqi-inv-debug") << "transition next predicate : " << next << std::endl;
+ // normalize the other direction
+ std::vector< Node > rvars;
+ for( unsigned i=0; i<next.getNumChildren(); i++ ){
+ rvars.push_back( next[i] );
+ }
+ if( d_prime_vars.size()<next.getNumChildren() ){
+ for( unsigned i=0; i<next.getNumChildren(); i++ ){
+ Node v = NodeManager::currentNM()->mkSkolem( "ir", next[i].getType(), "template inference rev argument" );
+ d_prime_vars.push_back( v );
+ }
+ }
+ Trace("cegqi-inv-debug2") << " normalize based on " << next << std::endl;
+ Assert( d_vars.size()==subs.size() );
+ for( unsigned j=0; j<disjuncts.size(); j++ ){
+ disjuncts[j] = Rewriter::rewrite( disjuncts[j].substitute( rvars.begin(), rvars.end(), d_prime_vars.begin(), d_prime_vars.end() ) );
+ Trace("cegqi-inv-debug2") << " ..." << disjuncts[j] << std::endl;
+ }
+ getConstantSubstitution( d_prime_vars, disjuncts, const_var, const_subs, false );
+ }else{
+ getConstantSubstitution( d_vars, disjuncts, const_var, const_subs, false );
+ }
+ Node res;
+ if( disjuncts.empty() ){
+ res = NodeManager::currentNM()->mkConst( false );
+ }else if( disjuncts.size()==1 ){
+ res = disjuncts[0];
+ }else{
+ res = NodeManager::currentNM()->mkNode( kind::OR, disjuncts );
+ }
+ if( !res.hasBoundVar() ){
+ Trace("cegqi-inv") << "*** inferred " << ( comp_num==1 ? "pre" : ( comp_num==-1 ? "post" : "trans" ) ) << "-condition : " << res << std::endl;
+ d_com[comp_num].d_conjuncts.push_back( res );
+ if( !const_var.empty() ){
+ bool has_const_eq = const_var.size()==d_vars.size();
+ Trace("cegqi-inv") << " with constant substitution, complete = " << has_const_eq << " : " << std::endl;
+ for( unsigned i=0; i<const_var.size(); i++ ){
+ Trace("cegqi-inv") << " " << const_var[i] << " -> " << const_subs[i] << std::endl;
+ if( has_const_eq ){
+ d_com[comp_num].d_const_eq[res][const_var[i]] = const_subs[i];
+ }
+ }
+ Trace("cegqi-inv") << "...size = " << const_var.size() << ", #vars = " << d_vars.size() << std::endl;
+ }
+ }else{
+ Trace("cegqi-inv-debug2") << "...failed, free variable." << std::endl;
+ d_complete = false;
+ }
+ }
+ }else{
+ d_complete = false;
+ }
+ }
+
+ // finalize the components
+ for( int i=-1; i<=1; i++ ){
+ Node ret;
+ if( d_com[i].d_conjuncts.empty() ){
+ ret = NodeManager::currentNM()->mkConst( true );
+ }else if( d_com[i].d_conjuncts.size()==1 ){
+ ret = d_com[i].d_conjuncts[0];
+ }else{
+ ret = NodeManager::currentNM()->mkNode( kind::AND, d_com[i].d_conjuncts );
+ }
+ if( i==0 || i==1 ){
+ // pre-condition and transition are negated
+ ret = TermUtil::simpleNegate( ret );
+ }
+ d_com[i].d_this = ret;
+ }
+}
+
+bool TransitionInference::processDisjunct( Node n, std::map< bool, Node >& terms, std::vector< Node >& disjuncts,
+ std::map< Node, bool >& visited, bool topLevel ) {
+ if( visited.find( n )==visited.end() ){
+ visited[n] = true;
+ bool childTopLevel = n.getKind()==OR && topLevel;
+ //if another part mentions UF or a free variable, then fail
+ bool lit_pol = n.getKind()!=NOT;
+ Node lit = n.getKind()==NOT ? n[0] : n;
+ if( lit.getKind()==APPLY_UF ){
+ Node op = lit.getOperator();
+ if( d_func.isNull() ){
+ d_func = op;
+ Trace("cegqi-inv-debug") << "Use " << op << " with args ";
+ for( unsigned i=0; i<lit.getNumChildren(); i++ ){
+ Node v = NodeManager::currentNM()->mkSkolem( "i", lit[i].getType(), "template inference argument" );
+ d_vars.push_back( v );
+ Trace("cegqi-inv-debug") << v << " ";
+ }
+ Trace("cegqi-inv-debug") << std::endl;
+ }
+ if( op!=d_func ){
+ Trace("cegqi-inv-debug") << "...failed, free function : " << n << std::endl;
+ return false;
+ }else if( topLevel ){
+ if( terms.find( lit_pol )==terms.end() ){
+ terms[lit_pol] = lit;
+ return true;
+ }else{
+ Trace("cegqi-inv-debug") << "...failed, repeated inv-app : " << lit << std::endl;
+ return false;
+ }
+ }else{
+ Trace("cegqi-inv-debug") << "...failed, non-entailed inv-app : " << lit << std::endl;
+ return false;
+ }
+ }else if( topLevel && !childTopLevel ){
+ disjuncts.push_back( n );
+ }
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ if( !processDisjunct( n[i], terms, disjuncts, visited, childTopLevel ) ){
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+Node TransitionInference::getComponent( int i ) {
+ return d_com[i].d_this;
+}
+
+int TransitionInference::initializeTrace( DetTrace& dt, Node loc, bool fwd ) {
+ int index = fwd ? 1 : -1;
+ Assert( d_com[index].has( loc ) );
+ std::map< Node, std::map< Node, Node > >::iterator it = d_com[index].d_const_eq.find( loc );
+ if( it!=d_com[index].d_const_eq.end() ){
+ std::vector< Node > next;
+ for( unsigned i=0; i<d_vars.size(); i++ ){
+ Node v = d_vars[i];
+ Assert( it->second.find( v )!=it->second.end() );
+ next.push_back( it->second[v] );
+ dt.d_curr.push_back( it->second[v] );
+ }
+ Trace("cegqi-inv-debug2") << "dtrace : initial increment" << std::endl;
+ bool ret = dt.increment( loc, next );
+ AlwaysAssert( ret );
+ return 0;
+ }
+ return -1;
+}
+
+int TransitionInference::incrementTrace( DetTrace& dt, Node loc, bool fwd ) {
+ Assert( d_com[0].has( loc ) );
+ // check if it satisfies the pre/post condition
+ int check_index = fwd ? -1 : 1;
+ Node cc = getComponent( check_index );
+ Assert( !cc.isNull() );
+ Node ccr = Rewriter::rewrite( cc.substitute( d_vars.begin(), d_vars.end(), dt.d_curr.begin(), dt.d_curr.end() ) );
+ if( ccr.isConst() ){
+ if( ccr.getConst<bool>()==( fwd ? false : true ) ){
+ Trace("cegqi-inv-debug2") << "dtrace : counterexample" << std::endl;
+ return 2;
+ }
+ }
+
+
+ // terminates?
+ Node c = getComponent( 0 );
+ Assert( !c.isNull() );
+
+ Assert( d_vars.size()==dt.d_curr.size() );
+ Node cr = Rewriter::rewrite( c.substitute( d_vars.begin(), d_vars.end(), dt.d_curr.begin(), dt.d_curr.end() ) );
+ if( cr.isConst() ){
+ if( !cr.getConst<bool>() ){
+ Trace("cegqi-inv-debug2") << "dtrace : terminated" << std::endl;
+ return 1;
+ }else{
+ return -1;
+ }
+ }
+ if( fwd ){
+ std::map< Node, std::map< Node, Node > >::iterator it = d_com[0].d_const_eq.find( loc );
+ if( it!=d_com[0].d_const_eq.end() ){
+ std::vector< Node > next;
+ for( unsigned i=0; i<d_prime_vars.size(); i++ ){
+ Node pv = d_prime_vars[i];
+ Assert( it->second.find( pv )!=it->second.end() );
+ Node pvs = it->second[pv];
+ Assert( d_vars.size()==dt.d_curr.size() );
+ Node pvsr = Rewriter::rewrite( pvs.substitute( d_vars.begin(), d_vars.end(), dt.d_curr.begin(), dt.d_curr.end() ) );
+ next.push_back( pvsr );
+ }
+ if( dt.increment( loc, next ) ){
+ Trace("cegqi-inv-debug2") << "dtrace : success increment" << std::endl;
+ return 0;
+ }else{
+ // looped
+ Trace("cegqi-inv-debug2") << "dtrace : looped" << std::endl;
+ return 1;
+ }
+ }
+ }else{
+ //TODO
+ }
+ return -1;
+}
+
+int TransitionInference::initializeTrace( DetTrace& dt, bool fwd ) {
+ Trace("cegqi-inv-debug2") << "Initialize trace" << std::endl;
+ int index = fwd ? 1 : -1;
+ if( d_com[index].d_conjuncts.size()==1 ){
+ return initializeTrace( dt, d_com[index].d_conjuncts[0], fwd );
+ }else{
+ return -1;
+ }
+}
+
+int TransitionInference::incrementTrace( DetTrace& dt, bool fwd ) {
+ if( d_com[0].d_conjuncts.size()==1 ){
+ return incrementTrace( dt, d_com[0].d_conjuncts[0], fwd );
+ }else{
+ return -1;
+ }
+}
+
+Node TransitionInference::constructFormulaTrace( DetTrace& dt ) {
+ return dt.constructFormula( d_vars );
+}
+
+} //namespace CVC4
+
--- /dev/null
+/********************* */
+/*! \file ce_guided_single_inv.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds, Tim King
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief utility for processing single invocation synthesis conjectures
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_SINGLE_INV_H
+#define __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_SINGLE_INV_H
+
+#include "context/cdlist.h"
+#include "theory/quantifiers/sygus/ce_guided_single_inv_sol.h"
+#include "theory/quantifiers/inst_match_trie.h"
+#include "theory/quantifiers/cegqi/inst_strategy_cbqi.h"
+#include "theory/quantifiers/single_inv_partition.h"
+#include "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+class CegConjecture;
+class CegConjectureSingleInv;
+class CegEntailmentInfer;
+
+class CegqiOutputSingleInv : public CegqiOutput {
+public:
+ CegqiOutputSingleInv( CegConjectureSingleInv * out ) : d_out( out ){}
+ virtual ~CegqiOutputSingleInv() {}
+ CegConjectureSingleInv * d_out;
+ bool doAddInstantiation( std::vector< Node >& subs );
+ bool isEligibleForInstantiation( Node n );
+ bool addLemma( Node lem );
+};
+
+class DetTrace {
+private:
+ class DetTraceTrie {
+ public:
+ std::map< Node, DetTraceTrie > d_children;
+ bool add( Node loc, std::vector< Node >& val, unsigned index = 0 );
+ void clear() { d_children.clear(); }
+ Node constructFormula( std::vector< Node >& vars, unsigned index = 0 );
+ };
+ DetTraceTrie d_trie;
+public:
+ std::vector< Node > d_curr;
+ bool increment( Node loc, std::vector< Node >& vals );
+ Node constructFormula( std::vector< Node >& vars );
+ void print( const char* c );
+};
+
+class TransitionInference {
+private:
+ bool processDisjunct( Node n, std::map< bool, Node >& terms, std::vector< Node >& disjuncts, std::map< Node, bool >& visited, bool topLevel );
+ void getConstantSubstitution( std::vector< Node >& vars, std::vector< Node >& disjuncts, std::vector< Node >& const_var, std::vector< Node >& const_subs, bool reqPol );
+ bool d_complete;
+public:
+ TransitionInference() : d_complete( false ) {}
+ std::vector< Node > d_vars;
+ std::vector< Node > d_prime_vars;
+ Node d_func;
+
+ class Component {
+ public:
+ Component(){}
+ Node d_this;
+ std::vector< Node > d_conjuncts;
+ std::map< Node, std::map< Node, Node > > d_const_eq;
+ bool has( Node c ) { return std::find( d_conjuncts.begin(), d_conjuncts.end(), c )!=d_conjuncts.end(); }
+ };
+ std::map< int, Component > d_com;
+
+ void initialize( Node f, std::vector< Node >& vars );
+ void process( Node n );
+ Node getComponent( int i );
+ bool isComplete() { return d_complete; }
+
+ // 0 : success, 1 : terminated, 2 : counterexample, -1 : invalid
+ int initializeTrace( DetTrace& dt, Node loc, bool fwd = true );
+ int incrementTrace( DetTrace& dt, Node loc, bool fwd = true );
+ int initializeTrace( DetTrace& dt, bool fwd = true );
+ int incrementTrace( DetTrace& dt, bool fwd = true );
+ Node constructFormulaTrace( DetTrace& dt );
+};
+
+// this class infers whether a conjecture is single invocation (Reynolds et al CAV 2015), and sets up the
+// counterexample-guided quantifier instantiation utility (d_cinst), and methods for solution
+// reconstruction (d_sol).
+// It also has more advanced techniques for:
+// (1) partitioning a conjecture into single invocation / non-single invocation portions for invariant synthesis,
+// (2) inferring whether the conjecture corresponds to a deterministic transistion system (by utility d_ti).
+// For these techniques, we may generate a template (d_templ) which specifies a restricted
+// solution space. We may in turn embed this template as a SyGuS grammar.
+class CegConjectureSingleInv {
+ private:
+ friend class CegqiOutputSingleInv;
+ //presolve
+ void collectPresolveEqTerms( Node n,
+ std::map< Node, std::vector< Node > >& teq );
+ void getPresolveEqConjuncts( std::vector< Node >& vars,
+ std::vector< Node >& terms,
+ std::map< Node, std::vector< Node > >& teq,
+ Node n, std::vector< Node >& conj );
+ // constructing solution
+ Node constructSolution(std::vector<unsigned>& indices, unsigned i,
+ unsigned index, std::map<Node, Node>& weak_imp);
+ Node postProcessSolution(Node n);
+ private:
+ QuantifiersEngine* d_qe;
+ CegConjecture* d_parent;
+ // single invocation inference utility
+ SingleInvocationPartition* d_sip;
+ // transition inference module for each function to synthesize
+ std::map< Node, TransitionInference > d_ti;
+ // solution reconstruction
+ CegConjectureSingleInvSol* d_sol;
+ // the instantiator's output channel
+ CegqiOutputSingleInv* d_cosi;
+ // the instantiator
+ CegInstantiator* d_cinst;
+
+ // list of skolems for each argument of programs
+ std::vector<Node> d_single_inv_arg_sk;
+ // list of variables/skolems for each program
+ std::vector<Node> d_single_inv_var;
+ std::vector<Node> d_single_inv_sk;
+ std::map<Node, int> d_single_inv_sk_index;
+ // program to solution index
+ std::map<Node, unsigned> d_prog_to_sol_index;
+ // lemmas produced
+ inst::InstMatchTrie d_inst_match_trie;
+ inst::CDInstMatchTrie* d_c_inst_match_trie;
+ // original conjecture
+ Node d_orig_conjecture;
+ // solution
+ Node d_orig_solution;
+ Node d_solution;
+ Node d_sygus_solution;
+ // whether the grammar for our solution allows ITEs, this tells us when reconstruction is infeasible
+ bool d_has_ites;
+
+ public:
+ // lemmas produced
+ std::vector<Node> d_lemmas_produced;
+ std::vector<std::vector<Node> > d_inst;
+
+ private:
+ std::vector<Node> d_curr_lemmas;
+ // add instantiation
+ bool doAddInstantiation( std::vector< Node >& subs );
+ //is eligible for instantiation
+ bool isEligibleForInstantiation( Node n );
+ // add lemma
+ bool addLemma( Node lem );
+ // conjecture
+ Node d_quant;
+ Node d_simp_quant;
+ // are we single invocation?
+ bool d_single_invocation;
+ // single invocation portion of quantified formula
+ Node d_single_inv;
+ Node d_si_guard;
+ // transition relation version per program
+ std::map< Node, Node > d_trans_pre;
+ std::map< Node, Node > d_trans_post;
+ // the template for each function to synthesize
+ std::map< Node, Node > d_templ;
+ // the template argument for each function to synthesize (occurs in exactly one position of its template)
+ std::map< Node, Node > d_templ_arg;
+
+ public:
+ CegConjectureSingleInv( QuantifiersEngine * qe, CegConjecture * p );
+ ~CegConjectureSingleInv();
+
+ // get simplified conjecture
+ Node getSimplifiedConjecture() { return d_simp_quant; }
+ // get single invocation guard
+ Node getGuard() { return d_si_guard; }
+ public:
+ //get the single invocation lemma(s)
+ void getInitialSingleInvLemma( std::vector< Node >& lems );
+ // initialize this class for synthesis conjecture q
+ void initialize( Node q );
+ // finish initialize, sets up final decisions about whether to use single invocation techniques
+ // syntaxRestricted is whether the syntax for solutions for the initialized conjecture is restricted
+ // hasItes is whether the syntax for solutions for the initialized conjecture allows ITEs
+ void finishInit( bool syntaxRestricted, bool hasItes );
+ //check
+ bool check( std::vector< Node >& lems );
+ //get solution
+ Node getSolution( unsigned sol_index, TypeNode stn, int& reconstructed, bool rconsSygus = true );
+ //reconstruct to syntax
+ Node reconstructToSyntax( Node s, TypeNode stn, int& reconstructed,
+ bool rconsSygus = true );
+ // has ites
+ bool hasITEs() { return d_has_ites; }
+ // is single invocation
+ bool isSingleInvocation() const { return !d_single_inv.isNull(); }
+ //needs check
+ bool needsCheck();
+ /** preregister conjecture */
+ void preregisterConjecture( Node q );
+
+ Node getTransPre(Node prog) const {
+ std::map<Node, Node>::const_iterator location = d_trans_pre.find(prog);
+ return location->second;
+ }
+
+ Node getTransPost(Node prog) const {
+ std::map<Node, Node>::const_iterator location = d_trans_post.find(prog);
+ return location->second;
+ }
+ // get template for program prog. This returns a term of the form t[x] where x is the template argument (see below)
+ Node getTemplate(Node prog) const {
+ std::map<Node, Node>::const_iterator tmpl = d_templ.find(prog);
+ if( tmpl!=d_templ.end() ){
+ return tmpl->second;
+ }else{
+ return Node::null();
+ }
+ }
+ // get the template argument for program prog.
+ // This is a variable which indicates the position of the function/predicate to synthesize.
+ Node getTemplateArg(Node prog) const {
+ std::map<Node, Node>::const_iterator tmpla = d_templ_arg.find(prog);
+ if( tmpla != d_templ_arg.end() ){
+ return tmpla->second;
+ }else{
+ return Node::null();
+ }
+ }
+};
+
+}/* namespace CVC4::theory::quantifiers */
+}/* namespace CVC4::theory */
+}/* namespace CVC4 */
+
+#endif
--- /dev/null
+/********************* */
+/*! \file ce_guided_single_inv_sol.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds, Paul Meng, Tim King
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief utility for processing single invocation synthesis conjectures
+ **
+ **/
+#include "theory/quantifiers/sygus/ce_guided_single_inv_sol.h"
+
+#include "expr/datatype.h"
+#include "options/quantifiers_options.h"
+#include "theory/quantifiers/sygus/ce_guided_instantiation.h"
+#include "theory/quantifiers/sygus/ce_guided_single_inv.h"
+#include "theory/quantifiers/first_order_model.h"
+#include "theory/quantifiers/quantifiers_attributes.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+#include "theory/quantifiers/term_enumeration.h"
+#include "theory/quantifiers/term_util.h"
+#include "theory/quantifiers/ematching/trigger.h"
+#include "theory/theory_engine.h"
+
+using namespace CVC4::kind;
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+bool doCompare(Node a, Node b, Kind k)
+{
+ Node com = NodeManager::currentNM()->mkNode(k, a, b);
+ com = Rewriter::rewrite(com);
+ Assert(com.getType().isBoolean());
+ return com.isConst() && com.getConst<bool>();
+}
+
+CegConjectureSingleInvSol::CegConjectureSingleInvSol(QuantifiersEngine* qe)
+ : d_qe(qe), d_id_count(0), d_root_id() {}
+
+bool CegConjectureSingleInvSol::debugSolution( Node sol ) {
+ if( sol.getKind()==SKOLEM ){
+ return false;
+ }else{
+ for( unsigned i=0; i<sol.getNumChildren(); i++ ){
+ if( !debugSolution( sol[i] ) ){
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
+
+void CegConjectureSingleInvSol::debugTermSize( Node sol, int& t_size, int& num_ite ) {
+ std::map< Node, int >::iterator it = d_dterm_size.find( sol );
+ if( it==d_dterm_size.end() ){
+ int prev = t_size;
+ int prev_ite = num_ite;
+ t_size++;
+ if( sol.getKind()==ITE ){
+ num_ite++;
+ }
+ for( unsigned i=0; i<sol.getNumChildren(); i++ ){
+ debugTermSize( sol[i], t_size, num_ite );
+ }
+ d_dterm_size[sol] = t_size-prev;
+ d_dterm_ite_size[sol] = num_ite-prev_ite;
+ }else{
+ t_size += it->second;
+ num_ite += d_dterm_ite_size[sol];
+ }
+}
+
+
+Node CegConjectureSingleInvSol::pullITEs( Node s ) {
+ if( s.getKind()==ITE ){
+ bool success;
+ do {
+ success = false;
+ std::vector< Node > conj;
+ Node t;
+ Node rem;
+ if( pullITECondition( s, s, conj, t, rem, 0 ) ){
+ Assert( !conj.empty() );
+ Node cond = conj.size()==1 ? conj[0] : NodeManager::currentNM()->mkNode( AND, conj );
+ Trace("csi-sol-debug") << "For " << s << ", can pull " << cond << " -> " << t << " with remainder " << rem << std::endl;
+ t = pullITEs( t );
+ rem = pullITEs( rem );
+ Trace("csi-pull-ite") << "PI: Rewrite : " << s << std::endl;
+ Node prev = s;
+ s = NodeManager::currentNM()->mkNode( ITE, TermUtil::simpleNegate( cond ), t, rem );
+ Trace("csi-pull-ite") << "PI: Rewrite Now : " << s << std::endl;
+ Trace("csi-pull-ite") << "(= " << prev << " " << s << ")" << std::endl;
+ success = true;
+ }
+ }while( success );
+ }
+ return s;
+}
+
+// pull condition common to all ITE conditions in path of size > 1
+bool CegConjectureSingleInvSol::pullITECondition( Node root, Node n_ite, std::vector< Node >& conj, Node& t, Node& rem, int depth ) {
+ Assert( n_ite.getKind()==ITE );
+ std::vector< Node > curr_conj;
+ std::vector< Node > orig_conj;
+ bool isAnd;
+ if( n_ite[0].getKind()==AND || n_ite[0].getKind()==OR ){
+ isAnd = n_ite[0].getKind()==AND;
+ for( unsigned i=0; i<n_ite[0].getNumChildren(); i++ ){
+ Node cond = n_ite[0][i];
+ orig_conj.push_back( cond );
+ if( n_ite[0].getKind()==OR ){
+ cond = TermUtil::simpleNegate( cond );
+ }
+ curr_conj.push_back( cond );
+ }
+ }else{
+ Node neg = n_ite[0].negate();
+ if( std::find( conj.begin(), conj.end(), neg )!=conj.end() ){
+ //if negation of condition exists, use it
+ isAnd = false;
+ curr_conj.push_back( neg );
+ }else{
+ //otherwise, use condition
+ isAnd = true;
+ curr_conj.push_back( n_ite[0] );
+ }
+ orig_conj.push_back( n_ite[0] );
+ }
+ // take intersection with current conditions
+ std::vector< Node > new_conj;
+ std::vector< Node > prev_conj;
+ if( n_ite==root ){
+ new_conj.insert( new_conj.end(), curr_conj.begin(), curr_conj.end() );
+ Trace("csi-sol-debug") << "Pull ITE root " << n_ite << ", #conj = " << new_conj.size() << std::endl;
+ }else{
+ for( unsigned i=0; i<curr_conj.size(); i++ ){
+ if( std::find( conj.begin(), conj.end(), curr_conj[i] )!=conj.end() ){
+ new_conj.push_back( curr_conj[i] );
+ }
+ }
+ Trace("csi-sol-debug") << "Pull ITE " << n_ite << ", #conj = " << conj.size() << " intersect " << curr_conj.size() << " = " << new_conj.size() << std::endl;
+ }
+ //cannot go further
+ if( new_conj.empty() ){
+ return false;
+ }
+ //it is an intersection with current
+ conj.clear();
+ conj.insert( conj.end(), new_conj.begin(), new_conj.end() );
+ //recurse if possible
+ Node trec = n_ite[ isAnd ? 2 : 1 ];
+ Node tval = n_ite[ isAnd ? 1 : 2 ];
+ bool success = false;
+ if( trec.getKind()==ITE ){
+ prev_conj.insert( prev_conj.end(), conj.begin(), conj.end() );
+ success = pullITECondition( root, trec, conj, t, rem, depth+1 );
+ }
+ if( !success && depth>0 ){
+ t = trec;
+ rem = trec;
+ success = true;
+ if( trec.getKind()==ITE ){
+ //restore previous state
+ conj.clear();
+ conj.insert( conj.end(), prev_conj.begin(), prev_conj.end() );
+ }
+ }
+ if( success ){
+ //make remainder : strip out conditions in conj
+ Assert( !conj.empty() );
+ std::vector< Node > cond_c;
+ Assert( orig_conj.size()==curr_conj.size() );
+ for( unsigned i=0; i<curr_conj.size(); i++ ){
+ if( std::find( conj.begin(), conj.end(), curr_conj[i] )==conj.end() ){
+ cond_c.push_back( orig_conj[i] );
+ }
+ }
+ if( cond_c.empty() ){
+ rem = tval;
+ }else{
+ Node new_cond = cond_c.size()==1 ? cond_c[0] : NodeManager::currentNM()->mkNode( n_ite[0].getKind(), cond_c );
+ rem = NodeManager::currentNM()->mkNode( ITE, new_cond, isAnd ? tval : rem, isAnd ? rem : tval );
+ }
+ return true;
+ }else{
+ return false;
+ }
+}
+
+Node CegConjectureSingleInvSol::flattenITEs( Node n, bool rec ) {
+ Assert( !n.isNull() );
+ if( n.getKind()==ITE ){
+ Trace("csi-sol-debug") << "Flatten ITE." << std::endl;
+ Node ret;
+ Node n0 = rec ? flattenITEs( n[0] ) : n[0];
+ Node n1 = rec ? flattenITEs( n[1] ) : n[1];
+ Node n2 = rec ? flattenITEs( n[2] ) : n[2];
+ Assert( !n0.isNull() );
+ Assert( !n1.isNull() );
+ Assert( !n2.isNull() );
+ if( n0.getKind()==NOT ){
+ ret = NodeManager::currentNM()->mkNode( ITE, n0[0], n2, n1 );
+ }else if( n0.getKind()==AND || n0.getKind()==OR ){
+ std::vector< Node > children;
+ for( unsigned i=1; i<n0.getNumChildren(); i++ ){
+ children.push_back( n0[i] );
+ }
+ Node rem = children.size()==1 ? children[0] : NodeManager::currentNM()->mkNode( n0.getKind(), children );
+ if( n0.getKind()==AND ){
+ ret = NodeManager::currentNM()->mkNode( ITE, rem, NodeManager::currentNM()->mkNode( ITE, n0[0], n1, n2 ), n2 );
+ }else{
+ ret = NodeManager::currentNM()->mkNode( ITE, rem, n1, NodeManager::currentNM()->mkNode( ITE, n0[0], n1, n2 ) );
+ }
+ }else{
+ if( n0.getKind()==ITE ){
+ n0 = NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, n0, n1 ),
+ NodeManager::currentNM()->mkNode( AND, n0.negate(), n2 ) );
+ }else if( n0.getKind()==EQUAL && n0[0].getType().isBoolean() ){
+ n0 = NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, n0, n1 ),
+ NodeManager::currentNM()->mkNode( AND, n0.negate(), n1.negate() ) );
+ }else{
+ return NodeManager::currentNM()->mkNode( ITE, n0, n1, n2 );
+ }
+ ret = NodeManager::currentNM()->mkNode( ITE, n0, n1, n2 );
+ }
+ Assert( !ret.isNull() );
+ return flattenITEs( ret, false );
+ }else{
+ if( n.getNumChildren()>0 ){
+ std::vector< Node > children;
+ if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){
+ children.push_back( n.getOperator() );
+ }
+ bool childChanged = false;
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ Node nc = flattenITEs( n[i] );
+ children.push_back( nc );
+ childChanged = childChanged || nc!=n[i];
+ }
+ if( !childChanged ){
+ return n;
+ }else{
+ return NodeManager::currentNM()->mkNode( n.getKind(), children );
+ }
+ }else{
+ return n;
+ }
+ }
+}
+
+// assign is from literals to booleans
+// union_find is from args to values
+
+bool CegConjectureSingleInvSol::getAssign( bool pol, Node n, std::map< Node, bool >& assign, std::vector< Node >& new_assign, std::vector< Node >& vars,
+ std::vector< Node >& new_vars, std::vector< Node >& new_subs ) {
+ std::map< Node, bool >::iterator ita = assign.find( n );
+ if( ita!=assign.end() ){
+ Trace("csi-simp-debug") << "---already assigned, lookup " << pol << " " << ita->second << std::endl;
+ return pol==ita->second;
+ }else if( n.isConst() ){
+ return pol==(n==d_qe->getTermUtil()->d_true);
+ }else{
+ Trace("csi-simp-debug") << "---assign " << n << " " << pol << std::endl;
+ assign[n] = pol;
+ new_assign.push_back( n );
+ if( ( pol && n.getKind()==AND ) || ( !pol && n.getKind()==OR ) ){
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ if( !getAssign( pol, n[i], assign, new_assign, vars, new_vars, new_subs ) ){
+ return false;
+ }
+ }
+ }else if( n.getKind()==NOT ){
+ return getAssign( !pol, n[0], assign, new_assign, vars, new_vars, new_subs );
+ }else if( pol && n.getKind()==EQUAL ){
+ getAssignEquality( n, vars, new_vars, new_subs );
+ }
+ }
+ return true;
+}
+
+bool CegConjectureSingleInvSol::getAssignEquality( Node eq, std::vector< Node >& vars, std::vector< Node >& new_vars, std::vector< Node >& new_subs ) {
+ Assert( eq.getKind()==EQUAL );
+ //try to find valid argument
+ for( unsigned r=0; r<2; r++ ){
+ if( std::find( d_varList.begin(), d_varList.end(), eq[r] )!=d_varList.end() ){
+ Assert( std::find( vars.begin(), vars.end(), eq[r] )==vars.end() );
+ if( std::find( new_vars.begin(), new_vars.end(), eq[r] )==new_vars.end() ){
+ Node eqro = eq[r==0 ? 1 : 0 ];
+ if( !d_qe->getTermUtil()->containsTerm( eqro, eq[r] ) ){
+ Trace("csi-simp-debug") << "---equality " << eq[r] << " = " << eqro << std::endl;
+ new_vars.push_back( eq[r] );
+ new_subs.push_back( eqro );
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+Node CegConjectureSingleInvSol::simplifySolution( Node sol, TypeNode stn ){
+ int tsize, itesize;
+ if( Trace.isOn("csi-sol") ){
+ tsize = 0;itesize = 0;
+ debugTermSize( sol, tsize, itesize );
+ Trace("csi-sol") << tsize << " " << itesize << " rewrite..." << std::endl;
+ Trace("csi-sol-debug") << "sol : " << sol << "..." << std::endl;
+ }
+ Node sol0 = Rewriter::rewrite( sol );
+ Trace("csi-sol") << "now : " << sol0 << std::endl;
+
+ Node curr_sol = sol0;
+ Node prev_sol;
+ do{
+ prev_sol = curr_sol;
+ //first, pull ITE conditions
+ if( Trace.isOn("csi-sol") ){
+ tsize = 0;itesize = 0;
+ debugTermSize( curr_sol, tsize, itesize );
+ Trace("csi-sol") << tsize << " " << itesize << " pull ITE..." << std::endl;
+ Trace("csi-sol-debug") << "sol : " << curr_sol << "..." << std::endl;
+ }
+ Node sol1 = pullITEs( curr_sol );
+ Trace("csi-sol") << "now : " << sol1 << std::endl;
+ //do standard rewriting
+ if( sol1!=curr_sol ){
+ if( Trace.isOn("csi-sol") ){
+ tsize = 0;itesize = 0;
+ debugTermSize( sol1, tsize, itesize );
+ Trace("csi-sol") << tsize << " " << itesize << " rewrite..." << std::endl;
+ Trace("csi-sol-debug") << "sol : " << sol1 << "..." << std::endl;
+ }
+ Node sol2 = Rewriter::rewrite( sol1 );
+ Trace("csi-sol") << "now : " << sol2 << std::endl;
+ curr_sol = sol2;
+ }
+ //now do branch analysis
+ if( Trace.isOn("csi-sol") ){
+ tsize = 0;itesize = 0;
+ debugTermSize( curr_sol, tsize, itesize );
+ Trace("csi-sol") << tsize << " " << itesize << " simplify solution..." << std::endl;
+ Trace("csi-sol-debug") << "sol : " << curr_sol << "..." << std::endl;
+ }
+ std::map< Node, bool > sassign;
+ std::vector< Node > svars;
+ std::vector< Node > ssubs;
+ Node sol3 = simplifySolutionNode( curr_sol, stn, sassign, svars, ssubs, 0 );
+ Trace("csi-sol") << "now : " << sol3 << std::endl;
+ if( sol3!=curr_sol ){
+ //do standard rewriting again
+ if( Trace.isOn("csi-sol" ) ){
+ tsize = 0;itesize = 0;
+ debugTermSize( sol3, tsize, itesize );
+ Trace("csi-sol") << tsize << " " << itesize << " rewrite..." << std::endl;
+ }
+ Node sol4 = Rewriter::rewrite( sol3 );
+ Trace("csi-sol") << "now : " << sol4 << std::endl;
+ curr_sol = sol4;
+ }
+ }while( curr_sol!=prev_sol );
+
+ return curr_sol;
+}
+
+Node CegConjectureSingleInvSol::simplifySolutionNode( Node sol, TypeNode stn, std::map< Node, bool >& assign,
+ std::vector< Node >& vars, std::vector< Node >& subs, int status ) {
+
+ Assert( vars.size()==subs.size() );
+ std::map< Node, bool >::iterator ita = assign.find( sol );
+ if( ita!=assign.end() ){
+ //it is currently assigned a boolean value
+ return NodeManager::currentNM()->mkConst( ita->second );
+ }else{
+ d_qe->getTermDatabaseSygus()->registerSygusType( stn );
+ std::map< int, TypeNode > stnc;
+ if( !stn.isNull() ){
+ int karg = d_qe->getTermDatabaseSygus()->getKindConsNum( stn, sol.getKind() );
+ if( karg!=-1 ){
+ const Datatype& dt = ((DatatypeType)(stn).toType()).getDatatype();
+ if( dt[karg].getNumArgs()==sol.getNumChildren() ){
+ for( unsigned i=0; i<dt[karg].getNumArgs(); i++ ){
+ stnc[i] = d_qe->getTermDatabaseSygus()->getArgType( dt[karg], i );
+ }
+ }
+ }
+ }
+
+ if( sol.getKind()==ITE ){
+ Trace("csi-simp") << "Simplify ITE " << std::endl;
+ std::vector< Node > children;
+ for( unsigned r=1; r<=2; r++ ){
+ std::vector< Node > new_assign;
+ std::vector< Node > new_vars;
+ std::vector< Node > new_subs;
+ if( getAssign( r==1, sol[0], assign, new_assign, vars, new_vars, new_subs ) ){
+ Trace("csi-simp") << "- branch " << r << " led to " << new_assign.size() << " assignments, " << new_vars.size() << " equalities." << std::endl;
+ unsigned prev_size = vars.size();
+ Node nc = sol[r];
+ if( !new_vars.empty() ){
+ nc = nc.substitute( new_vars.begin(), new_vars.end(), new_subs.begin(), new_subs.end() );
+ vars.insert( vars.end(), new_vars.begin(), new_vars.end() );
+ subs.insert( subs.end(), new_subs.begin(), new_subs.end() );
+ }
+ nc = simplifySolutionNode( nc, stnc[r], assign, vars, subs, 0 );
+ children.push_back( nc );
+ //clean up substitution
+ if( !new_vars.empty() ){
+ vars.resize( prev_size );
+ subs.resize( prev_size );
+ }
+ }else{
+ Trace("csi-simp") << "- branch " << r << " of " << sol[0] << " is infeasible." << std::endl;
+ }
+ //clean up assignment
+ for( unsigned i=0; i<new_assign.size(); i++ ){
+ assign.erase( new_assign[i] );
+ }
+ }
+ if( children.size()==1 || ( children.size()==2 && children[0]==children[1] ) ){
+ return children[0];
+ }else{
+ Assert( children.size()==2 );
+ Node ncond = simplifySolutionNode( sol[0], stnc[0], assign, vars, subs, 0 );
+ Node ret = NodeManager::currentNM()->mkNode( ITE, ncond, children[0], children[1] );
+
+ //expand/flatten if necessary
+ Node orig_ret = ret;
+ if( !stnc[0].isNull() ){
+ d_qe->getTermDatabaseSygus()->registerSygusType( stnc[0] );
+ Node prev_ret;
+ while( !d_qe->getTermDatabaseSygus()->hasKind( stnc[0], ret[0].getKind() ) && ret!=prev_ret ){
+ prev_ret = ret;
+ Node exp_c = d_qe->getTermDatabaseSygus()->expandBuiltinTerm( ret[0] );
+ if( !exp_c.isNull() ){
+ Trace("csi-simp-debug") << "Pre expand to " << ret[0] << " to " << exp_c << std::endl;
+ ret = NodeManager::currentNM()->mkNode( ITE, exp_c, ret[1], ret[2] );
+ }
+ if( !d_qe->getTermDatabaseSygus()->hasKind( stnc[0], ret[0].getKind() ) ){
+ Trace("csi-simp-debug") << "Flatten based on " << ret[0] << "." << std::endl;
+ ret = flattenITEs( ret, false );
+ }
+ }
+ }
+ return ret;
+ /*
+ if( orig_ret!=ret ){
+ Trace("csi-simp") << "Try expanded ITE" << std::endl;
+ return ret;//simplifySolutionNode( ret, stn, assign, vars, subs, status );
+ }else{
+ return ret;
+ }
+ */
+ }
+ }else if( sol.getKind()==OR || sol.getKind()==AND ){
+ Trace("csi-simp") << "Simplify " << sol.getKind() << std::endl;
+ //collect new equalities
+ std::map< Node, bool > atoms;
+ std::vector< Node > inc;
+ std::vector< Node > children;
+ std::vector< Node > new_vars;
+ std::vector< Node > new_subs;
+ Node bc = sol.getKind()==OR ? d_qe->getTermUtil()->d_true : d_qe->getTermUtil()->d_false;
+ for( unsigned i=0; i<sol.getNumChildren(); i++ ){
+ bool do_exc = false;
+ Node c;
+ std::map< Node, bool >::iterator ita = assign.find( sol[i] );
+ if( ita==assign.end() ){
+ c = sol[i];
+ }else{
+ c = NodeManager::currentNM()->mkConst( ita->second );
+ }
+ Trace("csi-simp") << " - child " << i << " : " << c << std::endl;
+ if( c.isConst() ){
+ if( c==bc ){
+ Trace("csi-simp") << " ...singularity." << std::endl;
+ return bc;
+ }else{
+ do_exc = true;
+ }
+ }else{
+ Node atom = c.getKind()==NOT ? c[0] : c;
+ bool pol = c.getKind()!=NOT;
+ std::map< Node, bool >::iterator it = atoms.find( atom );
+ if( it==atoms.end() ){
+ atoms[atom] = pol;
+ if( status==0 && atom.getKind()==EQUAL ){
+ if( pol==( sol.getKind()==AND ) ){
+ Trace("csi-simp") << " ...equality." << std::endl;
+ if( getAssignEquality( atom, vars, new_vars, new_subs ) ){
+ children.push_back( sol[i] );
+ do_exc = true;
+ }
+ }
+ }
+ }else{
+ //repeated atom
+ if( it->second!=pol ){
+ return NodeManager::currentNM()->mkConst( sol.getKind()==OR );
+ }else{
+ do_exc = true;
+ }
+ }
+ }
+ if( !do_exc ){
+ inc.push_back( sol[i] );
+ }else{
+ Trace("csi-simp") << " ...exclude." << std::endl;
+ }
+ }
+ if( !new_vars.empty() ){
+ if( !inc.empty() ){
+ Node ret = inc.size()==1 ? inc[0] : NodeManager::currentNM()->mkNode( sol.getKind(), inc );
+ Trace("csi-simp") << "Base return is : " << ret << std::endl;
+ // apply substitution
+ ret = ret.substitute( new_vars.begin(), new_vars.end(), new_subs.begin(), new_subs.end() );
+ ret = Rewriter::rewrite( ret );
+ Trace("csi-simp") << "After substitution : " << ret << std::endl;
+ unsigned prev_size = vars.size();
+ vars.insert( vars.end(), new_vars.begin(), new_vars.end() );
+ subs.insert( subs.end(), new_subs.begin(), new_subs.end() );
+ ret = simplifySolutionNode( ret, TypeNode::null(), assign, vars, subs, 1 );
+ //clean up substitution
+ if( !vars.empty() ){
+ vars.resize( prev_size );
+ subs.resize( prev_size );
+ }
+ //Trace("csi-simp") << "After simplification : " << ret << std::endl;
+ if( ret.isConst() ){
+ if( ret==bc ){
+ return bc;
+ }
+ }else{
+ if( ret.getKind()==sol.getKind() ){
+ for( unsigned i=0; i<ret.getNumChildren(); i++ ){
+ children.push_back( ret[i] );
+ }
+ }else{
+ children.push_back( ret );
+ }
+ }
+ }
+ }else{
+ //recurse on children
+ for( unsigned i=0; i<inc.size(); i++ ){
+ Node retc = simplifySolutionNode( inc[i], TypeNode::null(), assign, vars, subs, 0 );
+ if( retc.isConst() ){
+ if( retc==bc ){
+ return bc;
+ }
+ }else{
+ children.push_back( retc );
+ }
+ }
+ }
+ // now, remove all equalities that are implied
+ std::vector< Node > final_children;
+ for( unsigned i=0; i<children.size(); i++ ){
+ bool red = false;
+ Node atom = children[i].getKind()==NOT ? children[i][0] : children[i];
+ bool pol = children[i].getKind()!=NOT;
+ if( status==0 && atom.getKind()==EQUAL ){
+ if( pol!=( sol.getKind()==AND ) ){
+ std::vector< Node > tmp_vars;
+ std::vector< Node > tmp_subs;
+ if( getAssignEquality( atom, vars, tmp_vars, tmp_subs ) ){
+ Trace("csi-simp-debug") << "Check if " << children[i] << " is redundant in " << sol << std::endl;
+ for( unsigned j=0; j<children.size(); j++ ){
+ if( j!=i && ( j>i || std::find( final_children.begin(), final_children.end(), children[j] )!=final_children.end() ) ){
+ Node sj = children[j].substitute( tmp_vars.begin(), tmp_vars.end(), tmp_subs.begin(), tmp_subs.end() );
+ sj = Rewriter::rewrite( sj );
+ if( sj==( sol.getKind()==AND ? d_qe->getTermUtil()->d_false : d_qe->getTermUtil()->d_true ) ){
+ Trace("csi-simp") << "--- " << children[i].negate() << " is implied by " << children[j].negate() << std::endl;
+ red = true;
+ break;
+ }
+ }
+ }
+ if( !red ){
+ Trace("csi-simp-debug") << "...is not." << std::endl;
+ }
+ }
+ }
+ }
+ if( !red ){
+ final_children.push_back( children[i] );
+ }
+ }
+ return final_children.size()==0 ? NodeManager::currentNM()->mkConst( sol.getKind()==AND ) :
+ ( final_children.size()==1 ? final_children[0] : NodeManager::currentNM()->mkNode( sol.getKind(), final_children ) );
+ }else{
+ //generic simplification
+ std::vector< Node > children;
+ if( sol.getMetaKind() == kind::metakind::PARAMETERIZED ){
+ children.push_back( sol.getOperator() );
+ }
+ bool childChanged = false;
+ for( unsigned i=0; i<sol.getNumChildren(); i++ ){
+ Node nc = simplifySolutionNode( sol[i], stnc[i], assign, vars, subs, 0 );
+ childChanged = childChanged || nc!=sol[i];
+ children.push_back( nc );
+ }
+ if( childChanged ){
+ return NodeManager::currentNM()->mkNode( sol.getKind(), children );
+ }
+ }
+ return sol;
+ }
+}
+
+
+void CegConjectureSingleInvSol::preregisterConjecture( Node q ) {
+ Trace("csi-sol") << "Preregister conjecture : " << q << std::endl;
+ Node n = q;
+ if( n.getKind()==FORALL ){
+ n = n[1];
+ }
+ if( n.getKind()==EXISTS ){
+ if( n[0].getNumChildren()==d_varList.size() ){
+ std::vector< Node > evars;
+ for( unsigned i=0; i<n[0].getNumChildren(); i++ ){
+ evars.push_back( n[0][i] );
+ }
+ n = n[1].substitute( evars.begin(), evars.end(), d_varList.begin(), d_varList.end() );
+ }else{
+ Trace("csi-sol") << "Not the same number of variables, return." << std::endl;
+ return;
+ }
+ }
+ Trace("csi-sol") << "Preregister node for solution reconstruction : " << n << std::endl;
+ registerEquivalentTerms( n );
+}
+
+Node CegConjectureSingleInvSol::reconstructSolution( Node sol, TypeNode stn, int& reconstructed ) {
+ Trace("csi-rcons") << "Solution (pre-reconstruction) is : " << sol << std::endl;
+ int status;
+ d_root_id = collectReconstructNodes( sol, stn, status );
+ if( status==0 ){
+ Node ret = getReconstructedSolution( d_root_id );
+ Trace("csi-rcons") << "Sygus solution is : " << ret << std::endl;
+ Assert( !ret.isNull() );
+ reconstructed = 1;
+ return ret;
+ }else{
+ //Trace("csi-debug-sol") << "Induced solution template is : " << d_templ_solution << std::endl;
+ if( Trace.isOn("csi-rcons") ){
+ for( std::map< TypeNode, std::map< Node, int > >::iterator it = d_rcons_to_id.begin(); it != d_rcons_to_id.end(); ++it ){
+ TypeNode tn = it->first;
+ Assert( tn.isDatatype() );
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ Trace("csi-rcons") << "Terms to reconstruct of type " << dt.getName() << " : " << std::endl;
+ for( std::map< Node, int >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){
+ if( d_reconstruct.find( it2->second )==d_reconstruct.end() ){
+ Trace("csi-rcons") << " " << it2->first << std::endl;
+ }
+ }
+ Assert( !it->second.empty() );
+ }
+ }
+ unsigned index = 0;
+ std::map< TypeNode, bool > active;
+ for( std::map< TypeNode, std::map< Node, int > >::iterator it = d_rcons_to_id.begin(); it != d_rcons_to_id.end(); ++it ){
+ active[it->first] = true;
+ }
+ //enumerate for all types
+ do {
+ std::vector< TypeNode > to_erase;
+ for( std::map< TypeNode, bool >::iterator it = active.begin(); it != active.end(); ++it ){
+ TypeNode stn = it->first;
+ Node ns = d_qe->getTermEnumeration()->getEnumerateTerm(stn, index);
+ if( ns.isNull() ){
+ to_erase.push_back( stn );
+ }else{
+ Node nb = d_qe->getTermDatabaseSygus()->sygusToBuiltin( ns, stn );
+ Node nr = Rewriter::rewrite( nb );//d_qe->getTermDatabaseSygus()->getNormalized( stn, nb, false, false );
+ Trace("csi-rcons-debug2") << " - try " << ns << " -> " << nr << " for " << stn << " " << nr.getKind() << std::endl;
+ std::map< Node, int >::iterator itt = d_rcons_to_id[stn].find( nr );
+ if( itt!= d_rcons_to_id[stn].end() ){
+ // if it is not already reconstructed
+ if( d_reconstruct.find( itt->second )==d_reconstruct.end() ){
+ Trace("csi-rcons") << "...reconstructed " << ns << " for term " << nr << std::endl;
+ bool do_check = true;//getPathToRoot( itt->second );
+ setReconstructed( itt->second, ns );
+ if( do_check ){
+ Trace("csi-rcons-debug") << "...path to root, try reconstruction." << std::endl;
+ d_tmp_fail.clear();
+ Node ret = getReconstructedSolution( d_root_id );
+ if( !ret.isNull() ){
+ Trace("csi-rcons") << "Sygus solution (after enumeration) is : " << ret << std::endl;
+ reconstructed = 1;
+ return ret;
+ }
+ }else{
+ Trace("csi-rcons-debug") << "...no path to root." << std::endl;
+ }
+ }
+ }
+ }
+ }
+ for( unsigned i=0; i<to_erase.size(); i++ ){
+ active.erase( to_erase[i] );
+ }
+ index++;
+ if( index%100==0 ){
+ Trace("csi-rcons-stats") << "Tried " << index << " for each type." << std::endl;
+ }
+ }while( !active.empty() );
+
+ // we ran out of elements, return null
+ reconstructed = -1;
+ Warning() << CommandFailure("Cannot get synth function: reconstruction to syntax failed.");
+ return Node::null(); // return sol;
+ }
+}
+
+int CegConjectureSingleInvSol::collectReconstructNodes( Node t, TypeNode stn, int& status ) {
+ std::map< Node, int >::iterator itri = d_rcons_to_status[stn].find( t );
+ if( itri!=d_rcons_to_status[stn].end() ){
+ status = itri->second;
+ //Trace("csi-rcons-debug") << "-> (cached) " << status << " for " << d_rcons_to_id[stn][t] << std::endl;
+ return d_rcons_to_id[stn][t];
+ }else{
+ status = 1;
+ // register the type
+ registerType(stn);
+ int id = allocate( t, stn );
+ d_rcons_to_status[stn][t] = -1;
+ TypeNode tn = t.getType();
+ Assert( stn.isDatatype() );
+ const Datatype& dt = ((DatatypeType)(stn).toType()).getDatatype();
+ Assert( dt.isSygus() );
+ Trace("csi-rcons-debug") << "Check reconstruct " << t << ", sygus type " << dt.getName() << ", kind " << t.getKind() << ", id : " << id << std::endl;
+ int carg = -1;
+ int karg = -1;
+ // first, do standard minimizations
+ Node min_t = d_qe->getTermDatabaseSygus()->minimizeBuiltinTerm( t );
+ Trace("csi-rcons-debug") << "Minimized term is : " << min_t << std::endl;
+ //check if op is in syntax sort
+ carg = d_qe->getTermDatabaseSygus()->getOpConsNum( stn, min_t );
+ if( carg!=-1 ){
+ Trace("csi-rcons-debug") << " Type has operator." << std::endl;
+ d_reconstruct[id] = NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, Node::fromExpr( dt[carg].getConstructor() ) );
+ status = 0;
+ }else{
+ //check if kind is in syntax sort
+ karg = d_qe->getTermDatabaseSygus()->getKindConsNum( stn, min_t.getKind() );
+ if( karg!=-1 ){
+ //collect the children of min_t
+ std::vector< Node > tchildren;
+ if( min_t.getNumChildren()>dt[karg].getNumArgs() && quantifiers::TermUtil::isAssoc( min_t.getKind() ) && dt[karg].getNumArgs()==2 ){
+ tchildren.push_back( min_t[0] );
+ std::vector< Node > rem_children;
+ for( unsigned i=1; i<min_t.getNumChildren(); i++ ){
+ rem_children.push_back( min_t[i] );
+ }
+ Node t2 = NodeManager::currentNM()->mkNode( min_t.getKind(), rem_children );
+ tchildren.push_back( t2 );
+ Trace("csi-rcons-debug") << "...split n-ary to binary " << min_t[0] << " " << t2 << "." << std::endl;
+ }else{
+ for( unsigned i=0; i<min_t.getNumChildren(); i++ ){
+ tchildren.push_back( min_t[i] );
+ }
+ }
+ //recurse on the children
+ if( tchildren.size()==dt[karg].getNumArgs() ){
+ Trace("csi-rcons-debug") << "Type for " << id << " has kind " << min_t.getKind() << ", recurse." << std::endl;
+ status = 0;
+ Node cons = Node::fromExpr( dt[karg].getConstructor() );
+ if( !collectReconstructNodes( id, tchildren, dt[karg], d_reconstruct_op[id][cons], status ) ){
+ Trace("csi-rcons-debug") << "...failure for " << id << " " << dt[karg].getName() << std::endl;
+ d_reconstruct_op[id].erase( cons );
+ status = 1;
+ }
+ }else{
+ Trace("csi-rcons-debug") << "Type for " << id << " has kind " << min_t.getKind() << ", but argument # mismatch." << std::endl;
+ }
+ }
+ if( status!=0 ){
+ //try constant reconstruction
+ if( min_t.isConst() ){
+ Trace("csi-rcons-debug") << "...try constant reconstruction." << std::endl;
+ Node min_t_c = builtinToSygusConst(min_t, stn);
+ if( !min_t_c.isNull() ){
+ Trace("csi-rcons-debug") << " constant reconstruction success for " << id << ", result = " << min_t_c << std::endl;
+ d_reconstruct[id] = min_t_c;
+ status = 0;
+ }
+ }
+ if( status!=0 ){
+ //try identity functions
+ for (unsigned ii : d_id_funcs[stn])
+ {
+ Assert( dt[ii].getNumArgs()==1 );
+ //try to directly reconstruct from single argument
+ std::vector< Node > tchildren;
+ tchildren.push_back( min_t );
+ TypeNode stnc = TypeNode::fromType( ((SelectorType)dt[ii][0].getType()).getRangeType() );
+ Trace("csi-rcons-debug") << "...try identity function " << dt[ii].getSygusOp() << ", child type is " << stnc << std::endl;
+ status = 0;
+ Node cons = Node::fromExpr( dt[ii].getConstructor() );
+ if( !collectReconstructNodes( id, tchildren, dt[ii], d_reconstruct_op[id][cons], status ) ){
+ d_reconstruct_op[id].erase( cons );
+ status = 1;
+ }else{
+ Trace("csi-rcons-debug") << " identity function success for " << id << std::endl;
+ break;
+ }
+ }
+ if( status!=0 ){
+ //try other options, such as matching against other constructors
+ Trace("csi-rcons-debug") << "Try matching for " << id << "." << std::endl;
+ bool success;
+ int c_index = 0;
+ do{
+ success = false;
+ int index_found;
+ std::vector< Node > args;
+ if (getMatch(min_t, stn, index_found, args, karg, c_index))
+ {
+ success = true;
+ status = 0;
+ Node cons = Node::fromExpr( dt[index_found].getConstructor() );
+ Trace("csi-rcons-debug") << "Try alternative for " << id << ", matching " << dt[index_found].getName() << " with children : " << std::endl;
+ for( unsigned i=0; i<args.size(); i++ ){
+ Trace("csi-rcons-debug") << " " << args[i] << std::endl;
+ }
+ if( !collectReconstructNodes( id, args, dt[index_found], d_reconstruct_op[id][cons], status ) ){
+ d_reconstruct_op[id].erase( cons );
+ status = 1;
+ }else{
+ c_index = index_found+1;
+ }
+ }
+ }while( success && status!=0 );
+
+ if( status!=0 ){
+ // construct an equivalence class of terms that are equivalent to t
+ if( d_rep[id]==id ){
+ Trace("csi-rcons-debug") << "Try rewriting for " << id << "." << std::endl;
+ //get equivalence class of term
+ std::vector< Node > equiv;
+ if( tn.isBoolean() ){
+ Node curr = min_t;
+ Node new_t;
+ do{
+ new_t = Node::null();
+ if( curr.getKind()==EQUAL ){
+ if( curr[0].getType().isInteger() || curr[0].getType().isReal() ){
+ new_t = NodeManager::currentNM()->mkNode( AND, NodeManager::currentNM()->mkNode( LEQ, curr[0], curr[1] ),
+ NodeManager::currentNM()->mkNode( LEQ, curr[1], curr[0] ) );
+ }else if( curr[0].getType().isBoolean() ){
+ new_t = NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, curr[0], curr[1] ),
+ NodeManager::currentNM()->mkNode( AND, curr[0].negate(), curr[1].negate() ) );
+ }else{
+ new_t = NodeManager::currentNM()->mkNode( NOT, NodeManager::currentNM()->mkNode( NOT, curr ) );
+ }
+ }else if( curr.getKind()==ITE ){
+ new_t = NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, curr[0], curr[1] ),
+ NodeManager::currentNM()->mkNode( AND, curr[0].negate(), curr[2] ) );
+ }else if( curr.getKind()==OR || curr.getKind()==AND ){
+ new_t = TermUtil::simpleNegate( curr ).negate();
+ }else if( curr.getKind()==NOT ){
+ new_t = TermUtil::simpleNegate( curr[0] );
+ }else{
+ new_t = NodeManager::currentNM()->mkNode( NOT, NodeManager::currentNM()->mkNode( NOT, curr ) );
+ }
+ if( !new_t.isNull() ){
+ if( new_t!=min_t && std::find( equiv.begin(), equiv.end(), new_t )==equiv.end() ){
+ curr = new_t;
+ equiv.push_back( new_t );
+ }else{
+ new_t = Node::null();
+ }
+ }
+ }while( !new_t.isNull() );
+ }
+ //get decompositions
+ for( unsigned i=0; i<dt.getNumConstructors(); i++ ){
+ Kind k = d_qe->getTermDatabaseSygus()->getConsNumKind( stn, i );
+ getEquivalentTerms( k, min_t, equiv );
+ }
+ //assign ids to terms
+ Trace("csi-rcons-debug") << "Term " << id << " is equivalent to " << equiv.size() << " terms : " << std::endl;
+ std::vector< int > equiv_ids;
+ for( unsigned i=0; i<equiv.size(); i++ ){
+ Trace("csi-rcons-debug") << " " << equiv[i] << std::endl;
+ if( d_rcons_to_id[stn].find( equiv[i] )==d_rcons_to_id[stn].end() ){
+ int eq_id = allocate( equiv[i], stn );
+ d_eqc.erase( eq_id );
+ d_rep[eq_id] = id;
+ d_eqc[id].push_back( eq_id );
+ equiv_ids.push_back( eq_id );
+ }else{
+ equiv_ids.push_back( -1 );
+ }
+ }
+ // now, try each of them
+ for( unsigned i=0; i<equiv.size(); i++ ){
+ if( equiv_ids[i]!=-1 ){
+ collectReconstructNodes( equiv[i], stn, status );
+ //if one succeeds
+ if( status==0 ){
+ Node rsol = getReconstructedSolution( equiv_ids[i] );
+ Assert( !rsol.isNull() );
+ //set all members of the equivalence class that this is the reconstructed solution
+ setReconstructed( id, rsol );
+ break;
+ }
+ }
+ }
+ }else{
+ Trace("csi-rcons-debug") << "Do not try rewriting for " << id << ", rep = " << d_rep[id] << std::endl;
+ }
+ }
+ }
+ }
+ }
+ }
+ if( status!=0 ){
+ Trace("csi-rcons-debug") << "-> *** reconstruction required for id " << id << std::endl;
+ }else{
+ Trace("csi-rcons-debug") << "-> success for " << id << std::endl;
+ }
+ d_rcons_to_status[stn][t] = status;
+ return id;
+ }
+}
+
+bool CegConjectureSingleInvSol::collectReconstructNodes( int pid, std::vector< Node >& ts, const DatatypeConstructor& dtc, std::vector< int >& ids, int& status ) {
+ Assert( dtc.getNumArgs()==ts.size() );
+ for( unsigned i=0; i<ts.size(); i++ ){
+ TypeNode cstn = d_qe->getTermDatabaseSygus()->getArgType( dtc, i );
+ int cstatus;
+ int c_id = collectReconstructNodes( ts[i], cstn, cstatus );
+ if( cstatus==-1 ){
+ return false;
+ }else if( cstatus!=0 ){
+ status = 1;
+ }
+ ids.push_back( c_id );
+ }
+ for( unsigned i=0; i<ids.size(); i++ ){
+ d_parents[ids[i]].push_back( pid );
+ }
+ return true;
+}
+
+ /*
+ //flatten ITEs if necessary TODO : carry assignment or move this elsewhere
+ if( t.getKind()==ITE ){
+ TypeNode cstn = tds->getArgType( dt[karg], 0 );
+ tds->registerSygusType( cstn );
+ Node prev_t;
+ while( !tds->hasKind( cstn, t[0].getKind() ) && t!=prev_t ){
+ prev_t = t;
+ Node exp_c = tds->expandBuiltinTerm( t[0] );
+ if( !exp_c.isNull() ){
+ t = NodeManager::currentNM()->mkNode( ITE, exp_c, t[1], t[2] );
+ Trace("csi-rcons-debug") << "Pre expand to " << t << std::endl;
+ }
+ t = flattenITEs( t, false );
+ if( t!=prev_t ){
+ Trace("csi-rcons-debug") << "Flatten ITE to " << t << std::endl;
+ std::map< Node, bool > sassign;
+ std::vector< Node > svars;
+ std::vector< Node > ssubs;
+ t = simplifySolutionNode( t, sassign, svars, ssubs, 0 );
+ }
+ Assert( t.getKind()==ITE );
+ }
+ }
+ */
+
+
+Node CegConjectureSingleInvSol::CegConjectureSingleInvSol::getReconstructedSolution( int id, bool mod_eq ) {
+ std::map< int, Node >::iterator it = d_reconstruct.find( id );
+ if( it!=d_reconstruct.end() ){
+ return it->second;
+ }else{
+ if( std::find( d_tmp_fail.begin(), d_tmp_fail.end(), id )!=d_tmp_fail.end() ){
+ return Node::null();
+ }else{
+ // try each child option
+ std::map< int, std::map< Node, std::vector< int > > >::iterator ito = d_reconstruct_op.find( id );
+ if( ito!=d_reconstruct_op.end() ){
+ for( std::map< Node, std::vector< int > >::iterator itt = ito->second.begin(); itt != ito->second.end(); ++itt ){
+ std::vector< Node > children;
+ children.push_back( itt->first );
+ bool success = true;
+ for( unsigned i=0; i<itt->second.size(); i++ ){
+ Node nc = getReconstructedSolution( itt->second[i] );
+ if( nc.isNull() ){
+ success = false;
+ break;
+ }else{
+ children.push_back( nc );
+ }
+ }
+ if( success ){
+ Node ret = NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, children );
+ setReconstructed( id, ret );
+ return ret;
+ }
+ }
+ }
+ // try terms in the equivalence class of this
+ if( mod_eq ){
+ int rid = d_rep[id];
+ for( unsigned i=0; i<d_eqc[rid].size(); i++ ){
+ int tid = d_eqc[rid][i];
+ if( tid!=id ){
+ Node eret = getReconstructedSolution( tid, false );
+ if( !eret.isNull() ){
+ setReconstructed( id, eret );
+ return eret;
+ }
+ }
+ }
+ }
+ d_tmp_fail.push_back( id );
+ return Node::null();
+ }
+ }
+}
+
+int CegConjectureSingleInvSol::allocate( Node n, TypeNode stn ) {
+ std::map< Node, int >::iterator it = d_rcons_to_id[stn].find( n );
+ if( it==d_rcons_to_id[stn].end() ){
+ int ret = d_id_count;
+ if( Trace.isOn("csi-rcons-debug") ){
+ const Datatype& dt = ((DatatypeType)(stn).toType()).getDatatype();
+ Trace("csi-rcons-debug") << "id " << ret << " : " << n << " " << dt.getName() << std::endl;
+ }
+ d_id_node[d_id_count] = n;
+ d_id_type[d_id_count] = stn;
+ d_rep[d_id_count] = d_id_count;
+ d_eqc[d_id_count].push_back( d_id_count );
+ d_rcons_to_id[stn][n] = d_id_count;
+ d_id_count++;
+ return ret;
+ }else{
+ return it->second;
+ }
+}
+
+bool CegConjectureSingleInvSol::getPathToRoot( int id ) {
+ if( id==d_root_id ){
+ return true;
+ }else{
+ std::map< int, Node >::iterator it = d_reconstruct.find( id );
+ if( it!=d_reconstruct.end() ){
+ return false;
+ }else{
+ int rid = d_rep[id];
+ for( unsigned j=0; j<d_parents[rid].size(); j++ ){
+ if( getPathToRoot( d_parents[rid][j] ) ){
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
+
+void CegConjectureSingleInvSol::setReconstructed( int id, Node n ) {
+ //set all equivalent to this as reconstructed
+ int rid = d_rep[id];
+ for( unsigned i=0; i<d_eqc[rid].size(); i++ ){
+ d_reconstruct[d_eqc[rid][i]] = n;
+ }
+}
+
+void CegConjectureSingleInvSol::getEquivalentTerms( Kind k, Node n, std::vector< Node >& equiv ) {
+ if( k==AND || k==OR ){
+ equiv.push_back( NodeManager::currentNM()->mkNode( k, n, n ) );
+ equiv.push_back( NodeManager::currentNM()->mkNode( k, n, NodeManager::currentNM()->mkConst( k==AND ) ) );
+ }
+ //multiplication for integers
+ //TODO for bitvectors
+ Kind mk = ( k==PLUS || k==MINUS ) ? MULT : UNDEFINED_KIND;
+ if( mk!=UNDEFINED_KIND ){
+ if( n.getKind()==mk && n[0].isConst() && n[0].getType().isInteger() ){
+ bool success = true;
+ for( unsigned i=0; i<2; i++ ){
+ Node eq;
+ if( k==PLUS || k==MINUS ){
+ Node oth = NodeManager::currentNM()->mkConst( Rational(i==0 ? 1000 : -1000) );
+ eq = i==0 ? NodeManager::currentNM()->mkNode( LEQ, n[0], oth ) : NodeManager::currentNM()->mkNode( GEQ, n[0], oth );
+ }
+ if( !eq.isNull() ){
+ eq = Rewriter::rewrite( eq );
+ if( eq!=d_qe->getTermUtil()->d_true ){
+ success = false;
+ break;
+ }
+ }
+ }
+ if( success ){
+ Node var = n[1];
+ Node rem;
+ if( k==PLUS || k==MINUS ){
+ int rem_coeff = (int)n[0].getConst<Rational>().getNumerator().getSignedInt();
+ if( rem_coeff>0 && k==PLUS ){
+ rem_coeff--;
+ }else if( rem_coeff<0 && k==MINUS ){
+ rem_coeff++;
+ }else{
+ success = false;
+ }
+ if( success ){
+ rem = NodeManager::currentNM()->mkNode( MULT, NodeManager::currentNM()->mkConst( Rational(rem_coeff) ), var );
+ rem = Rewriter::rewrite( rem );
+ }
+ }
+ if( !rem.isNull() ){
+ equiv.push_back( NodeManager::currentNM()->mkNode( k, rem, var ) );
+ }
+ }
+ }
+ }
+ //negative constants
+ if( k==MINUS ){
+ if( n.isConst() && n.getType().isInteger() && n.getConst<Rational>().getNumerator().strictlyNegative() ){
+ Node nn = NodeManager::currentNM()->mkNode( UMINUS, n );
+ nn = Rewriter::rewrite( nn );
+ equiv.push_back( NodeManager::currentNM()->mkNode( MINUS, NodeManager::currentNM()->mkConst( Rational(0) ), nn ) );
+ }
+ }
+ //inequalities
+ if( k==GEQ || k==LEQ || k==LT || k==GT || k==NOT ){
+ Node atom = n.getKind()==NOT ? n[0] : n;
+ bool pol = n.getKind()!=NOT;
+ Kind ak = atom.getKind();
+ if( ( ak==GEQ || ak==LEQ || ak==LT || ak==GT ) && ( pol || k!=NOT ) ){
+ Node t1 = atom[0];
+ Node t2 = atom[1];
+ if( !pol ){
+ ak = ak==GEQ ? LT : ( ak==LEQ ? GT : ( ak==LT ? GEQ : LEQ ) );
+ }
+ if( k==NOT ){
+ equiv.push_back( NodeManager::currentNM()->mkNode( ak==GEQ ? LT : ( ak==LEQ ? GT : ( ak==LT ? GEQ : LEQ ) ), t1, t2 ).negate() );
+ }else if( k==ak ){
+ equiv.push_back( NodeManager::currentNM()->mkNode( k, t1, t2 ) );
+ }else if( (k==GEQ || k==LEQ)==(ak==GEQ || ak==LEQ) ){
+ equiv.push_back( NodeManager::currentNM()->mkNode( k, t2, t1 ) );
+ }else if( t1.getType().isInteger() && t2.getType().isInteger() ){
+ if( (k==GEQ || k==GT)!=(ak==GEQ || ak==GT) ){
+ Node ts = t1;
+ t1 = t2;
+ t2 = ts;
+ ak = ak==GEQ ? LEQ : ( ak==LEQ ? GEQ : ( ak==LT ? GT : LT ) );
+ }
+ t2 = NodeManager::currentNM()->mkNode( PLUS, t2, NodeManager::currentNM()->mkConst( Rational( (ak==GT || ak==LEQ) ? 1 : -1 ) ) );
+ t2 = Rewriter::rewrite( t2 );
+ equiv.push_back( NodeManager::currentNM()->mkNode( k, t1, t2 ) );
+ }
+ }
+ }
+
+ //based on eqt cache
+ std::map< Node, Node >::iterator itet = d_eqt_rep.find( n );
+ if( itet!=d_eqt_rep.end() ){
+ Node rn = itet->second;
+ for( unsigned i=0; i<d_eqt_eqc[rn].size(); i++ ){
+ if( d_eqt_eqc[rn][i]!=n && d_eqt_eqc[rn][i].getKind()==k ){
+ if( std::find( equiv.begin(), equiv.end(), d_eqt_eqc[rn][i] )==equiv.end() ){
+ equiv.push_back( d_eqt_eqc[rn][i] );
+ }
+ }
+ }
+ }
+}
+
+void CegConjectureSingleInvSol::registerEquivalentTerms( Node n ) {
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ registerEquivalentTerms( n[i] );
+ }
+ Node rn = Rewriter::rewrite( n );
+ if( rn!=n ){
+ Trace("csi-equiv") << " eq terms : " << n << " " << rn << std::endl;
+ d_eqt_rep[n] = rn;
+ d_eqt_rep[rn] = rn;
+ if( std::find( d_eqt_eqc[rn].begin(), d_eqt_eqc[rn].end(), rn )==d_eqt_eqc[rn].end() ){
+ d_eqt_eqc[rn].push_back( rn );
+ }
+ if( std::find( d_eqt_eqc[rn].begin(), d_eqt_eqc[rn].end(), n )==d_eqt_eqc[rn].end() ){
+ d_eqt_eqc[rn].push_back( n );
+ }
+ }
+}
+
+Node CegConjectureSingleInvSol::builtinToSygusConst(Node c,
+ TypeNode tn,
+ int rcons_depth)
+{
+ std::map<Node, Node>::iterator it = d_builtin_const_to_sygus[tn].find(c);
+ if (it != d_builtin_const_to_sygus[tn].end())
+ {
+ return it->second;
+ }
+ TermDbSygus* tds = d_qe->getTermDatabaseSygus();
+ NodeManager* nm = NodeManager::currentNM();
+ Node sc;
+ d_builtin_const_to_sygus[tn][c] = sc;
+ Assert(c.isConst());
+ Assert(tn.isDatatype());
+ const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
+ Trace("csi-rcons-debug") << "Try to reconstruct " << c << " in "
+ << dt.getName() << std::endl;
+ Assert(dt.isSygus());
+ // if we are not interested in reconstructing constants, or the grammar allows
+ // them, return a proxy
+ if (!options::cegqiSingleInvReconstructConst() || dt.getSygusAllowConst())
+ {
+ Node k = nm->mkSkolem("sy", tn, "sygus proxy");
+ SygusPrintProxyAttribute spa;
+ k.setAttribute(spa, c);
+ sc = k;
+ }
+ else
+ {
+ int carg = tds->getOpConsNum(tn, c);
+ if (carg != -1)
+ {
+ sc = nm->mkNode(APPLY_CONSTRUCTOR,
+ Node::fromExpr(dt[carg].getConstructor()));
+ }
+ else
+ {
+ // identity functions
+ for (unsigned ii : d_id_funcs[tn])
+ {
+ Assert(dt[ii].getNumArgs() == 1);
+ // try to directly reconstruct from single argument
+ TypeNode tnc = tds->getArgType(dt[ii], 0);
+ Trace("csi-rcons-debug")
+ << "Based on id function " << dt[ii].getSygusOp()
+ << ", try reconstructing " << c << " instead in " << tnc
+ << std::endl;
+ Node n = builtinToSygusConst(c, tnc, rcons_depth);
+ if (!n.isNull())
+ {
+ sc = nm->mkNode(
+ APPLY_CONSTRUCTOR, Node::fromExpr(dt[ii].getConstructor()), n);
+ break;
+ }
+ }
+ if (sc.isNull())
+ {
+ if (rcons_depth < 1000)
+ {
+ // accelerated, recursive reconstruction of constants
+ Kind pk = tds->getPlusKind(TypeNode::fromType(dt.getSygusType()));
+ if (pk != UNDEFINED_KIND)
+ {
+ int arg = tds->getKindConsNum(tn, pk);
+ if (arg != -1)
+ {
+ Kind ck =
+ tds->getComparisonKind(TypeNode::fromType(dt.getSygusType()));
+ Kind pkm =
+ tds->getPlusKind(TypeNode::fromType(dt.getSygusType()), true);
+ // get types
+ Assert(dt[arg].getNumArgs() == 2);
+ TypeNode tn1 = tds->getArgType(dt[arg], 0);
+ TypeNode tn2 = tds->getArgType(dt[arg], 1);
+ // initialize d_const_list for tn1
+ registerType(tn1);
+ // iterate over all positive constants, largest to smallest
+ int start = d_const_list[tn1].size() - 1;
+ int end = d_const_list[tn1].size() - d_const_list_pos[tn1];
+ for (int i = start; i >= end; --i)
+ {
+ Node c1 = d_const_list[tn1][i];
+ // only consider if smaller than c, and
+ if (doCompare(c1, c, ck))
+ {
+ Node c2 = nm->mkNode(pkm, c, c1);
+ c2 = Rewriter::rewrite(c2);
+ if (c2.isConst())
+ {
+ // reconstruct constant on the other side
+ Node sc2 = builtinToSygusConst(c2, tn2, rcons_depth + 1);
+ if (!sc2.isNull())
+ {
+ Node sc1 = builtinToSygusConst(c1, tn1, rcons_depth);
+ Assert(!sc1.isNull());
+ sc = nm->mkNode(APPLY_CONSTRUCTOR,
+ Node::fromExpr(dt[arg].getConstructor()),
+ sc1,
+ sc2);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ d_builtin_const_to_sygus[tn][c] = sc;
+ return sc;
+}
+
+struct sortConstants
+{
+ Kind d_comp_kind;
+ bool operator()(Node i, Node j)
+ {
+ return i != j && doCompare(i, j, d_comp_kind);
+ }
+};
+
+void CegConjectureSingleInvSol::registerType(TypeNode tn)
+{
+ if (d_const_list_pos.find(tn) != d_const_list_pos.end())
+ {
+ return;
+ }
+ d_const_list_pos[tn] = 0;
+ Assert(tn.isDatatype());
+
+ TermDbSygus* tds = d_qe->getTermDatabaseSygus();
+ // ensure it is registered
+ tds->registerSygusType(tn);
+ const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
+ TypeNode btn = TypeNode::fromType(dt.getSygusType());
+ // for constant reconstruction
+ Kind ck = tds->getComparisonKind(btn);
+ Node z = d_qe->getTermUtil()->getTypeValue(btn, 0);
+
+ // iterate over constructors
+ for (unsigned i = 0, ncons = dt.getNumConstructors(); i < ncons; i++)
+ {
+ Node n = Node::fromExpr(dt[i].getSygusOp());
+ if (n.getKind() != kind::BUILTIN && n.isConst())
+ {
+ d_const_list[tn].push_back(n);
+ if (ck != UNDEFINED_KIND && doCompare(z, n, ck))
+ {
+ d_const_list_pos[tn]++;
+ }
+ }
+ if (dt[i].isSygusIdFunc())
+ {
+ d_id_funcs[tn].push_back(i);
+ }
+ }
+ // sort the constant list
+ if (!d_const_list[tn].empty())
+ {
+ if (ck != UNDEFINED_KIND)
+ {
+ sortConstants sc;
+ sc.d_comp_kind = ck;
+ std::sort(d_const_list[tn].begin(), d_const_list[tn].end(), sc);
+ }
+ Trace("csi-rcons") << "Type has " << d_const_list[tn].size()
+ << " constants..." << std::endl
+ << " ";
+ for (unsigned i = 0; i < d_const_list[tn].size(); i++)
+ {
+ Trace("csi-rcons") << d_const_list[tn][i] << " ";
+ }
+ Trace("csi-rcons") << std::endl;
+ Trace("csi-rcons") << "Of these, " << d_const_list_pos[tn]
+ << " are marked as positive." << std::endl;
+ }
+}
+
+bool CegConjectureSingleInvSol::getMatch(Node p,
+ Node n,
+ std::map<int, Node>& s,
+ std::vector<int>& new_s)
+{
+ TermDbSygus* tds = d_qe->getTermDatabaseSygus();
+ if (tds->isFreeVar(p))
+ {
+ unsigned vnum = tds->getVarNum(p);
+ Node prev = s[vnum];
+ s[vnum] = n;
+ if (prev.isNull())
+ {
+ new_s.push_back(vnum);
+ }
+ return prev.isNull() || prev == n;
+ }
+ if (n.getNumChildren() == 0)
+ {
+ return p == n;
+ }
+ if (n.getKind() == p.getKind() && n.getNumChildren() == p.getNumChildren())
+ {
+ // try both ways?
+ unsigned rmax =
+ TermUtil::isComm(n.getKind()) && n.getNumChildren() == 2 ? 2 : 1;
+ std::vector<int> new_tmp;
+ for (unsigned r = 0; r < rmax; r++)
+ {
+ bool success = true;
+ for (unsigned i = 0, size = n.getNumChildren(); i < size; i++)
+ {
+ int io = r == 0 ? i : (i == 0 ? 1 : 0);
+ if (!getMatch(p[i], n[io], s, new_tmp))
+ {
+ success = false;
+ for (unsigned j = 0; j < new_tmp.size(); j++)
+ {
+ s.erase(new_tmp[j]);
+ }
+ new_tmp.clear();
+ break;
+ }
+ }
+ if (success)
+ {
+ new_s.insert(new_s.end(), new_tmp.begin(), new_tmp.end());
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool CegConjectureSingleInvSol::getMatch(Node t,
+ TypeNode st,
+ int& index_found,
+ std::vector<Node>& args,
+ int index_exc,
+ int index_start)
+{
+ Assert(st.isDatatype());
+ const Datatype& dt = static_cast<DatatypeType>(st.toType()).getDatatype();
+ Assert(dt.isSygus());
+ std::map<Kind, std::vector<Node> > kgens;
+ std::vector<Node> gens;
+ for (unsigned i = index_start, ncons = dt.getNumConstructors(); i < ncons;
+ i++)
+ {
+ if ((int)i != index_exc)
+ {
+ Node g = getGenericBase(st, dt, i);
+ gens.push_back(g);
+ kgens[g.getKind()].push_back(g);
+ Trace("csi-sol-debug") << "Check generic base : " << g << " from "
+ << dt[i].getName() << std::endl;
+ if (g.getKind() == t.getKind())
+ {
+ Trace("csi-sol-debug") << "Possible match ? " << g << " " << t
+ << " for " << dt[i].getName() << std::endl;
+ std::map<int, Node> sigma;
+ std::vector<int> new_s;
+ if (getMatch(g, t, sigma, new_s))
+ {
+ // we found an exact match
+ bool msuccess = true;
+ for (unsigned j = 0, nargs = dt[i].getNumArgs(); j < nargs; j++)
+ {
+ if (sigma[j].isNull())
+ {
+ msuccess = false;
+ break;
+ }
+ else
+ {
+ args.push_back(sigma[j]);
+ }
+ }
+ if (msuccess)
+ {
+ index_found = i;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+Node CegConjectureSingleInvSol::getGenericBase(TypeNode tn,
+ const Datatype& dt,
+ int c)
+{
+ std::map<int, Node>::iterator it = d_generic_base[tn].find(c);
+ if (it != d_generic_base[tn].end())
+ {
+ return it->second;
+ }
+ TermDbSygus* tds = d_qe->getTermDatabaseSygus();
+ Assert(tds->isRegistered(tn));
+ std::map<TypeNode, int> var_count;
+ std::map<int, Node> pre;
+ Node g = tds->mkGeneric(dt, c, var_count, pre);
+ Trace("csi-sol-debug") << "Generic is " << g << std::endl;
+ Node gr = Rewriter::rewrite(g);
+ Trace("csi-sol-debug") << "Generic rewritten is " << gr << std::endl;
+ d_generic_base[tn][c] = gr;
+ return gr;
+}
+}
+}
+}
--- /dev/null
+/********************* */
+/*! \file ce_guided_single_inv_sol.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds, Paul Meng
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief utility for reconstructing solutions for single invocation synthesis conjectures
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_SINGLE_INV_SOL_H
+#define __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_SINGLE_INV_SOL_H
+
+#include "context/cdhashmap.h"
+#include "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+
+class CegConjectureSingleInv;
+
+/** CegConjectureSingleInvSol
+ *
+ * This function implements Figure 5 of "Counterexample-Guided Quantifier
+ * Instantiation for Synthesis in SMT", Reynolds et al CAV 2015.
+ *
+ */
+class CegConjectureSingleInvSol
+{
+ friend class CegConjectureSingleInv;
+private:
+ QuantifiersEngine * d_qe;
+ std::vector< Node > d_varList;
+ std::map< Node, int > d_dterm_size;
+ std::map< Node, int > d_dterm_ite_size;
+//solution simplification
+private:
+ bool debugSolution( Node sol );
+ void debugTermSize( Node sol, int& t_size, int& num_ite );
+ Node pullITEs( Node n );
+ bool pullITECondition( Node root, Node n, std::vector< Node >& conj, Node& t, Node& rem, int depth );
+ Node flattenITEs( Node n, bool rec = true );
+ bool getAssign( bool pol, Node n, std::map< Node, bool >& assign, std::vector< Node >& new_assign,
+ std::vector< Node >& vars, std::vector< Node >& new_vars, std::vector< Node >& new_subs );
+ bool getAssignEquality( Node eq, std::vector< Node >& vars, std::vector< Node >& new_vars, std::vector< Node >& new_subs );
+ Node simplifySolutionNode( Node sol, TypeNode stn, std::map< Node, bool >& assign,
+ std::vector< Node >& vars, std::vector< Node >& subs, int status );
+
+ public:
+ CegConjectureSingleInvSol(QuantifiersEngine* qe);
+ /** simplify solution
+ *
+ * Returns the simplified version of node sol whose syntax is restricted by
+ * the grammar corresponding to sygus datatype stn.
+ */
+ Node simplifySolution( Node sol, TypeNode stn );
+ /** reconstruct solution
+ *
+ * Returns (if possible) a node that is equivalent to sol those syntax
+ * matches the grammar corresponding to sygus datatype stn.
+ * The value reconstructed is set to 1 if we successfully return a node,
+ * otherwise it is set to -1.
+ */
+ Node reconstructSolution(Node sol, TypeNode stn, int& reconstructed);
+ /** preregister conjecture
+ *
+ * q : the synthesis conjecture this class is for.
+ * This is used as a heuristic to find terms in the original conjecture which
+ * may be helpful for using during reconstruction.
+ */
+ void preregisterConjecture(Node q);
+
+ private:
+ int d_id_count;
+ int d_root_id;
+ std::map< int, Node > d_id_node;
+ std::map< int, TypeNode > d_id_type;
+ std::map< TypeNode, std::map< Node, int > > d_rcons_to_id;
+ std::map< TypeNode, std::map< Node, int > > d_rcons_to_status;
+
+ std::map< int, std::map< Node, std::vector< int > > > d_reconstruct_op;
+ std::map< int, Node > d_reconstruct;
+ std::map< int, std::vector< int > > d_parents;
+
+ std::map< int, std::vector< int > > d_eqc;
+ std::map< int, int > d_rep;
+
+ //equivalent terms
+ std::map< Node, Node > d_eqt_rep;
+ std::map< Node, std::vector< Node > > d_eqt_eqc;
+
+ //cache when reconstructing solutions
+ std::vector< int > d_tmp_fail;
+ // get reconstructed solution
+ Node getReconstructedSolution( int id, bool mod_eq = true );
+
+ // allocate node with type
+ int allocate( Node n, TypeNode stn );
+ // term t with sygus type st, returns inducted templated form of t
+ int collectReconstructNodes( Node t, TypeNode stn, int& status );
+ bool collectReconstructNodes( int pid, std::vector< Node >& ts, const DatatypeConstructor& dtc, std::vector< int >& ids, int& status );
+ bool getPathToRoot( int id );
+ void setReconstructed( int id, Node n );
+ //get equivalent terms to n with top symbol k
+ void getEquivalentTerms( Kind k, Node n, std::vector< Node >& equiv );
+ //register equivalent terms
+ void registerEquivalentTerms( Node n );
+ /** builtin to sygus const
+ *
+ * Returns a sygus term of type tn that encodes the builtin constant c.
+ * If the sygus datatype tn allows any constant, this may return a variable
+ * with the attribute SygusPrintProxyAttribute that associates it with c.
+ *
+ * rcons_depth limits the number of recursive calls when doing accelerated
+ * constant reconstruction (currently limited to 1000). Notice this is hacky:
+ * depending upon order of calls, constant rcons may succeed, e.g. 1001, 999
+ * vs. 999, 1001.
+ */
+ Node builtinToSygusConst(Node c, TypeNode tn, int rcons_depth = 0);
+ /** cache for the above function */
+ std::map<TypeNode, std::map<Node, Node> > d_builtin_const_to_sygus;
+ /** sorted list of constants, per type */
+ std::map<TypeNode, std::vector<Node> > d_const_list;
+ /** number of positive constants, per type */
+ std::map<TypeNode, unsigned> d_const_list_pos;
+ /** list of constructor indices whose operators are identity functions */
+ std::map<TypeNode, std::vector<int> > d_id_funcs;
+ /** initialize the above information for sygus type tn */
+ void registerType(TypeNode tn);
+ /** get generic base
+ *
+ * This returns the builtin term that is the analog of an application of the
+ * c^th constructor of dt to fresh variables.
+ */
+ Node getGenericBase(TypeNode tn, const Datatype& dt, int c);
+ /** cache for the above function */
+ std::map<TypeNode, std::map<int, Node> > d_generic_base;
+ /** get match
+ *
+ * This function attempts to find a substitution for which p = n. If
+ * successful, this function returns a substitution in the form of s/new_s,
+ * where:
+ * s : substitution, where the domain are indices of terms in the sygus
+ * term database, and
+ * new_s : the members that were added to s on this call.
+ * Otherwise, this function returns false and s and new_s are unmodified.
+ */
+ bool getMatch(Node p,
+ Node n,
+ std::map<int, Node>& s,
+ std::vector<int>& new_s);
+ /** get match
+ *
+ * This function attempts to find a builtin term that is analog to a value
+ * of the sygus datatype st that is equivalent to n. If this function returns
+ * true, then it has found such a term. Then we set:
+ * index_found : updated to the constructor index of the sygus term whose
+ * analog to equivalent to n.
+ * args : builtin terms corresponding to the match, in order.
+ * Otherwise, this function returns false and index_found and args are
+ * unmodified.
+ * For example, for grammar:
+ * A -> 0 | 1 | x | +( A, A )
+ * Given input ( 5 + (x+1) ) and A we would return true, where:
+ * index_found is set to 3 and args is set to { 5, x+1 }.
+ *
+ * index_exc : (if applicable) exclude a constructor index of st
+ * index_start : start index of constructors of st to try
+ */
+ bool getMatch(Node n,
+ TypeNode st,
+ int& index_found,
+ std::vector<Node>& args,
+ int index_exc = -1,
+ int index_start = 0);
+};
+
+
+}
+}
+}
+
+#endif
--- /dev/null
+/********************* */
+/*! \file sygus_explain.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief Implementation of techniques for sygus explanations
+ **/
+
+#include "theory/quantifiers/sygus/sygus_explain.h"
+
+#include "theory/datatypes/datatypes_rewriter.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+
+using namespace CVC4::kind;
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+void TermRecBuild::addTerm(Node n)
+{
+ d_term.push_back(n);
+ std::vector<Node> currc;
+ d_kind.push_back(n.getKind());
+ if (n.getMetaKind() == kind::metakind::PARAMETERIZED)
+ {
+ currc.push_back(n.getOperator());
+ d_has_op.push_back(true);
+ }
+ else
+ {
+ d_has_op.push_back(false);
+ }
+ for (unsigned i = 0; i < n.getNumChildren(); i++)
+ {
+ currc.push_back(n[i]);
+ }
+ d_children.push_back(currc);
+}
+
+void TermRecBuild::init(Node n)
+{
+ Assert(d_term.empty());
+ addTerm(n);
+}
+
+void TermRecBuild::push(unsigned p)
+{
+ Assert(!d_term.empty());
+ unsigned curr = d_term.size() - 1;
+ Assert(d_pos.size() == curr);
+ Assert(d_pos.size() + 1 == d_children.size());
+ Assert(p < d_term[curr].getNumChildren());
+ addTerm(d_term[curr][p]);
+ d_pos.push_back(p);
+}
+
+void TermRecBuild::pop()
+{
+ Assert(!d_pos.empty());
+ d_pos.pop_back();
+ d_kind.pop_back();
+ d_has_op.pop_back();
+ d_children.pop_back();
+ d_term.pop_back();
+}
+
+void TermRecBuild::replaceChild(unsigned i, Node r)
+{
+ Assert(!d_term.empty());
+ unsigned curr = d_term.size() - 1;
+ unsigned o = d_has_op[curr] ? 1 : 0;
+ d_children[curr][i + o] = r;
+}
+
+Node TermRecBuild::getChild(unsigned i)
+{
+ unsigned curr = d_term.size() - 1;
+ unsigned o = d_has_op[curr] ? 1 : 0;
+ return d_children[curr][i + o];
+}
+
+Node TermRecBuild::build(unsigned d)
+{
+ Assert(d_pos.size() + 1 == d_term.size());
+ Assert(d < d_term.size());
+ int p = d < d_pos.size() ? d_pos[d] : -2;
+ std::vector<Node> children;
+ unsigned o = d_has_op[d] ? 1 : 0;
+ for (unsigned i = 0; i < d_children[d].size(); i++)
+ {
+ Node nc;
+ if (p + o == i)
+ {
+ nc = build(d + 1);
+ }
+ else
+ {
+ nc = d_children[d][i];
+ }
+ children.push_back(nc);
+ }
+ return NodeManager::currentNM()->mkNode(d_kind[d], children);
+}
+
+void SygusExplain::getExplanationForConstantEquality(Node n,
+ Node vn,
+ std::vector<Node>& exp)
+{
+ std::map<unsigned, bool> cexc;
+ getExplanationForConstantEquality(n, vn, exp, cexc);
+}
+
+void SygusExplain::getExplanationForConstantEquality(
+ Node n, Node vn, std::vector<Node>& exp, std::map<unsigned, bool>& cexc)
+{
+ Assert(vn.getKind() == kind::APPLY_CONSTRUCTOR);
+ Assert(n.getType() == vn.getType());
+ TypeNode tn = n.getType();
+ Assert(tn.isDatatype());
+ const Datatype& dt = ((DatatypeType)tn.toType()).getDatatype();
+ int i = Datatype::indexOf(vn.getOperator().toExpr());
+ Node tst = datatypes::DatatypesRewriter::mkTester(n, i, dt);
+ exp.push_back(tst);
+ for (unsigned j = 0; j < vn.getNumChildren(); j++)
+ {
+ if (cexc.find(j) == cexc.end())
+ {
+ Node sel = NodeManager::currentNM()->mkNode(
+ kind::APPLY_SELECTOR_TOTAL,
+ Node::fromExpr(dt[i].getSelectorInternal(tn.toType(), j)),
+ n);
+ getExplanationForConstantEquality(sel, vn[j], exp);
+ }
+ }
+}
+
+Node SygusExplain::getExplanationForConstantEquality(Node n, Node vn)
+{
+ std::map<unsigned, bool> cexc;
+ return getExplanationForConstantEquality(n, vn, cexc);
+}
+
+Node SygusExplain::getExplanationForConstantEquality(
+ Node n, Node vn, std::map<unsigned, bool>& cexc)
+{
+ std::vector<Node> exp;
+ getExplanationForConstantEquality(n, vn, exp, cexc);
+ Assert(!exp.empty());
+ return exp.size() == 1 ? exp[0]
+ : NodeManager::currentNM()->mkNode(kind::AND, exp);
+}
+
+// we have ( n = vn => eval( n ) = bvr ) ^ vn != vnr , returns exp such that exp
+// => ( eval( n ) = bvr ^ vn != vnr )
+void SygusExplain::getExplanationFor(TermRecBuild& trb,
+ Node n,
+ Node vn,
+ std::vector<Node>& exp,
+ std::map<TypeNode, int>& var_count,
+ SygusInvarianceTest& et,
+ Node vnr,
+ Node& vnr_exp,
+ int& sz)
+{
+ Assert(vnr.isNull() || vn != vnr);
+ Assert(vn.getKind() == APPLY_CONSTRUCTOR);
+ Assert(vnr.isNull() || vnr.getKind() == APPLY_CONSTRUCTOR);
+ Assert(n.getType() == vn.getType());
+ TypeNode ntn = n.getType();
+ std::map<unsigned, bool> cexc;
+ // for each child,
+ // check whether replacing that child by a fresh variable
+ // also satisfies the invariance test.
+ for (unsigned i = 0; i < vn.getNumChildren(); i++)
+ {
+ TypeNode xtn = vn[i].getType();
+ Node x = d_tdb->getFreeVarInc(xtn, var_count);
+ trb.replaceChild(i, x);
+ Node nvn = trb.build();
+ Assert(nvn.getKind() == kind::APPLY_CONSTRUCTOR);
+ if (et.is_invariant(d_tdb, nvn, x))
+ {
+ cexc[i] = true;
+ // we are tracking term size if positive
+ if (sz >= 0)
+ {
+ int s = d_tdb->getSygusTermSize(vn[i]);
+ sz = sz - s;
+ }
+ }
+ else
+ {
+ trb.replaceChild(i, vn[i]);
+ }
+ }
+ const Datatype& dt = ((DatatypeType)ntn.toType()).getDatatype();
+ int cindex = Datatype::indexOf(vn.getOperator().toExpr());
+ Assert(cindex >= 0 && cindex < (int)dt.getNumConstructors());
+ Node tst = datatypes::DatatypesRewriter::mkTester(n, cindex, dt);
+ exp.push_back(tst);
+ // if the operator of vn is different than vnr, then disunification obligation
+ // is met
+ if (!vnr.isNull())
+ {
+ if (vnr.getOperator() != vn.getOperator())
+ {
+ vnr = Node::null();
+ vnr_exp = NodeManager::currentNM()->mkConst(true);
+ }
+ }
+ for (unsigned i = 0; i < vn.getNumChildren(); i++)
+ {
+ Node sel = NodeManager::currentNM()->mkNode(
+ kind::APPLY_SELECTOR_TOTAL,
+ Node::fromExpr(dt[cindex].getSelectorInternal(ntn.toType(), i)),
+ n);
+ Node vnr_c = vnr.isNull() ? vnr : (vn[i] == vnr[i] ? Node::null() : vnr[i]);
+ if (cexc.find(i) == cexc.end())
+ {
+ trb.push(i);
+ Node vnr_exp_c;
+ getExplanationFor(
+ trb, sel, vn[i], exp, var_count, et, vnr_c, vnr_exp_c, sz);
+ trb.pop();
+ if (!vnr_c.isNull())
+ {
+ Assert(!vnr_exp_c.isNull());
+ if (vnr_exp_c.isConst() || vnr_exp.isNull())
+ {
+ // recursively satisfied the disunification obligation
+ if (vnr_exp_c.isConst())
+ {
+ // was successful, don't consider further
+ vnr = Node::null();
+ }
+ vnr_exp = vnr_exp_c;
+ }
+ }
+ }
+ else
+ {
+ // if excluded, we may need to add the explanation for this
+ if (vnr_exp.isNull() && !vnr_c.isNull())
+ {
+ vnr_exp = getExplanationForConstantEquality(sel, vnr[i]);
+ }
+ }
+ }
+}
+
+void SygusExplain::getExplanationFor(Node n,
+ Node vn,
+ std::vector<Node>& exp,
+ SygusInvarianceTest& et,
+ Node vnr,
+ unsigned& sz)
+{
+ // naive :
+ // return getExplanationForConstantEquality( n, vn, exp );
+
+ // set up the recursion object
+ std::map<TypeNode, int> var_count;
+ TermRecBuild trb;
+ trb.init(vn);
+ Node vnr_exp;
+ int sz_use = sz;
+ getExplanationFor(trb, n, vn, exp, var_count, et, vnr, vnr_exp, sz_use);
+ Assert(sz_use >= 0);
+ sz = sz_use;
+ Assert(vnr.isNull() || !vnr_exp.isNull());
+ if (!vnr_exp.isNull() && !vnr_exp.isConst())
+ {
+ exp.push_back(vnr_exp.negate());
+ }
+}
+
+void SygusExplain::getExplanationFor(Node n,
+ Node vn,
+ std::vector<Node>& exp,
+ SygusInvarianceTest& et)
+{
+ int sz = -1;
+ std::map<TypeNode, int> var_count;
+ TermRecBuild trb;
+ trb.init(vn);
+ Node vnr;
+ Node vnr_exp;
+ getExplanationFor(trb, n, vn, exp, var_count, et, vnr, vnr_exp, sz);
+}
+
+} /* CVC4::theory::quantifiers namespace */
+} /* CVC4::theory namespace */
+} /* CVC4 namespace */
--- /dev/null
+/********************* */
+/*! \file sygus_explain.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief sygus explanations
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__SYGUS_EXPLAIN_H
+#define __CVC4__THEORY__QUANTIFIERS__SYGUS_EXPLAIN_H
+
+#include <vector>
+
+#include "expr/node.h"
+#include "theory/quantifiers/sygus/sygus_invariance.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+/** Recursive term builder
+ *
+ * This is a utility used to generate variants
+ * of a term n, where subterms of n can be replaced
+ * by others via calls to replaceChild(...).
+ *
+ * This class maintains a "context", which indicates
+ * a position in term n. Below, we call the subterm of
+ * the initial term n at this position the "active term".
+ *
+ */
+class TermRecBuild
+{
+ public:
+ TermRecBuild() {}
+ /** set the initial term to n
+ *
+ * The context initially empty, that is,
+ * the active term is initially n.
+ */
+ void init(Node n);
+
+ /** push the context
+ *
+ * This updates the context so that the
+ * active term is updated to curr[p], where
+ * curr is the previously active term.
+ */
+ void push(unsigned p);
+
+ /** pop the context */
+ void pop();
+ /** indicates that the i^th child of the active
+ * term should be replaced by r in calls to build().
+ */
+ void replaceChild(unsigned i, Node r);
+ /** get the i^th child of the active term */
+ Node getChild(unsigned i);
+ /** build the (modified) version of the term
+ * we intialized via the call to init().
+ */
+ Node build(unsigned p = 0);
+
+ private:
+ /** stack of active terms */
+ std::vector<Node> d_term;
+ /** stack of children of active terms
+ * Notice that these may be modified with calls to replaceChild(...).
+ */
+ std::vector<std::vector<Node> > d_children;
+ /** stack the kind of active terms */
+ std::vector<Kind> d_kind;
+ /** stack of whether the active terms had an operator */
+ std::vector<bool> d_has_op;
+ /** stack of positions that were pushed via calls to push(...) */
+ std::vector<unsigned> d_pos;
+ /** add term to the context stack */
+ void addTerm(Node n);
+};
+
+/*The SygusExplain utility
+ *
+ * This class is used to produce explanations for refinement lemmas
+ * in the counterexample-guided inductive synthesis (CEGIS) loop.
+ *
+ * When given an invariance test T traverses the AST of a given term,
+ * uses TermRecBuild to replace various subterms by fresh variables and
+ * recheck whether the invariant, as specified by T still holds.
+ * If it does, then we may exclude the explanation for that subterm.
+ *
+ * For example, say we have that the current value of
+ * (datatype) sygus term n is:
+ * (if (gt x 0) 0 0)
+ * where if, gt, x, 0 are datatype constructors.
+ * The explanation returned by getExplanationForConstantEquality
+ * below for n and the above term is:
+ * { ((_ is if) n), ((_ is geq) n.0),
+ * ((_ is x) n.0.0), ((_ is 0) n.0.1),
+ * ((_ is 0) n.1), ((_ is 0) n.2) }
+ *
+ * This class can also return more precise
+ * explanations based on a property that holds for
+ * variants of n. For instance,
+ * say we find that n's builtin analog rewrites to 0:
+ * ite( x>0, 0, 0 ) ----> 0
+ * and we would like to find the minimal explanation for
+ * why the builtin analog of n rewrites to 0.
+ * We use the invariance test EquivSygusInvarianceTest
+ * (see sygus_invariance.h) for doing this.
+ * Using the SygusExplain::getExplanationFor method below,
+ * this will invoke the invariant test to check, e.g.
+ * ite( x>0, 0, y1 ) ----> 0 ? fail
+ * ite( x>0, y2, 0 ) ----> 0 ? fail
+ * ite( y3, 0, 0 ) ----> 0 ? success
+ * where y1, y2, y3 are fresh variables.
+ * Hence the explanation for the condition x>0 is irrelevant.
+ * This gives us the explanation:
+ * { ((_ is if) n), ((_ is 0) n.1), ((_ is 0) n.2) }
+ * indicating that all terms of the form:
+ * (if _ 0 0) have a builtin equivalent that rewrites to 0.
+ *
+ * For details, see Reynolds et al SYNT 2017.
+ *
+ * Below, we let [[exp]]_n denote the term induced by
+ * the explanation exp for n.
+ * For example:
+ * exp = { ((_ is plus) n), ((_ is y) n.1) }
+ * is such that:
+ * [[exp]]_n = (plus w y)
+ * where w is a fresh variable.
+ */
+class SygusExplain
+{
+ public:
+ SygusExplain(TermDbSygus* tdb) : d_tdb(tdb) {}
+ ~SygusExplain() {}
+ /** get explanation for constant equality
+ *
+ * This function constructs an explanation, stored in exp, such that:
+ * - All formulas in exp are of the form ((_ is C) ns), where ns
+ * is a chain of selectors applied to n, and
+ * - exp => ( n = vn )
+ */
+ void getExplanationForConstantEquality(Node n,
+ Node vn,
+ std::vector<Node>& exp);
+ /** returns the conjunction of exp computed in the above function */
+ Node getExplanationForConstantEquality(Node n, Node vn);
+
+ /** get explanation for constant equality
+ * This is identical to the above function except that we
+ * take an additional argument cexc, which says which
+ * children of vn should be excluded from the explanation.
+ *
+ * For example, if vn = plus( plus( x, x ), y ) and cexc is { 0 -> true },
+ * then the following is appended to exp :
+ * { ((_ is plus) n), ((_ is y) n.1) }
+ * where notice that the 0^th argument of vn is excluded.
+ */
+ void getExplanationForConstantEquality(Node n,
+ Node vn,
+ std::vector<Node>& exp,
+ std::map<unsigned, bool>& cexc);
+ /** returns the conjunction of exp computed in the above function */
+ Node getExplanationForConstantEquality(Node n,
+ Node vn,
+ std::map<unsigned, bool>& cexc);
+
+ /** get explanation for
+ *
+ * This function constructs an explanation, stored in exp, such that:
+ * - All formulas in exp are of the form ((_ is C) ns), where ns
+ * is a chain of selectors applied to n, and
+ * - The test et holds for [[exp]]_n, and
+ * - (if applicable) exp => ( n != vnr ).
+ *
+ * This function updates sz to be the term size of [[exp]]_n.
+ */
+ void getExplanationFor(Node n,
+ Node vn,
+ std::vector<Node>& exp,
+ SygusInvarianceTest& et,
+ Node vnr,
+ unsigned& sz);
+ void getExplanationFor(Node n,
+ Node vn,
+ std::vector<Node>& exp,
+ SygusInvarianceTest& et);
+
+ private:
+ /** sygus term database associated with this utility */
+ TermDbSygus* d_tdb;
+ /** Helper function for getExplanationFor
+ * var_count is the number of free variables we have introduced,
+ * per type, for the purposes of generalizing subterms of n.
+ * vnr_exp stores the explanation, if one exists, for
+ * n != vnr. It is only non-null if vnr is non-null.
+ */
+ void getExplanationFor(TermRecBuild& trb,
+ Node n,
+ Node vn,
+ std::vector<Node>& exp,
+ std::map<TypeNode, int>& var_count,
+ SygusInvarianceTest& et,
+ Node vnr,
+ Node& vnr_exp,
+ int& sz);
+};
+
+} /* CVC4::theory::quantifiers namespace */
+} /* CVC4::theory namespace */
+} /* CVC4 namespace */
+
+#endif /* __CVC4__THEORY__QUANTIFIERS__SYGUS_EXPLAIN_H */
--- /dev/null
+/********************* */
+/*! \file sygus_grammar_cons.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief implementation of class for constructing inductive datatypes that correspond to
+ ** grammars that encode syntactic restrictions for SyGuS.
+ **/
+#include "theory/quantifiers/sygus/sygus_grammar_cons.h"
+
+#include <stack>
+
+#include "expr/datatype.h"
+#include "options/quantifiers_options.h"
+#include "theory/quantifiers/sygus/ce_guided_conjecture.h"
+#include "theory/quantifiers/sygus/sygus_process_conj.h"
+#include "theory/quantifiers/sygus/sygus_grammar_norm.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+#include "theory/quantifiers/term_util.h"
+
+using namespace CVC4::kind;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+CegGrammarConstructor::CegGrammarConstructor(QuantifiersEngine* qe,
+ CegConjecture* p)
+ : d_qe(qe), d_parent(p), d_is_syntax_restricted(false), d_has_ite(true)
+{
+}
+
+void CegGrammarConstructor::collectTerms( Node n, std::map< TypeNode, std::vector< Node > >& consts ){
+ std::unordered_map<TNode, bool, TNodeHashFunction> visited;
+ std::unordered_map<TNode, bool, TNodeHashFunction>::iterator it;
+ std::stack<TNode> visit;
+ TNode cur;
+ visit.push(n);
+ do {
+ cur = visit.top();
+ visit.pop();
+ it = visited.find(cur);
+ if (it == visited.end()) {
+ visited[cur] = true;
+ // is this a constant?
+ if( cur.isConst() ){
+ TypeNode tn = cur.getType();
+ Node c = cur;
+ if( tn.isReal() ){
+ c = NodeManager::currentNM()->mkConst( c.getConst<Rational>().abs() );
+ }
+ if( std::find( consts[tn].begin(), consts[tn].end(), c )==consts[tn].end() ){
+ Trace("cegqi-debug") << "...consider const : " << c << std::endl;
+ consts[tn].push_back( c );
+ }
+ }
+ // recurse
+ for (unsigned i = 0; i < cur.getNumChildren(); i++) {
+ visit.push(cur[i]);
+ }
+ }
+ } while (!visit.empty());
+}
+
+
+
+Node CegGrammarConstructor::process( Node q, std::map< Node, Node >& templates, std::map< Node, Node >& templates_arg ) {
+ // convert to deep embedding and finalize single invocation here
+ // now, construct the grammar
+ Trace("cegqi") << "CegConjecture : convert to deep embedding..." << std::endl;
+ std::map< TypeNode, std::vector< Node > > extra_cons;
+ if( options::sygusAddConstGrammar() ){
+ Trace("cegqi") << "CegConjecture : collect constants..." << std::endl;
+ collectTerms( q[1], extra_cons );
+ }
+
+ std::vector< Node > qchildren;
+ std::map< Node, Node > synth_fun_vars;
+ std::vector< Node > ebvl;
+ Node qbody_subs = q[1];
+ for( unsigned i=0; i<q[0].getNumChildren(); i++ ){
+ Node sf = q[0][i];
+ // v encodes the syntactic restrictions (via an inductive datatype) on sf
+ // from the input
+ Node v = sf.getAttribute(SygusSynthGrammarAttribute());
+ Assert(!v.isNull());
+ Node sfvl = sf.getAttribute(SygusSynthFunVarListAttribute());
+ // sfvl may be null for constant synthesis functions
+ Trace("cegqi-debug") << "...sygus var list associated with " << sf << " is " << sfvl << std::endl;
+
+ TypeNode tn;
+ std::stringstream ss;
+ ss << sf;
+ if( v.getType().isDatatype() && ((DatatypeType)v.getType().toType()).getDatatype().isSygus() ){
+ tn = v.getType();
+ }else{
+ // check which arguments are irrelevant
+ std::unordered_set<unsigned> arg_irrelevant;
+ d_parent->getProcess()->getIrrelevantArgs(sf, arg_irrelevant);
+ std::unordered_set<Node, NodeHashFunction> term_irrelevant;
+ // convert to term
+ for (std::unordered_set<unsigned>::iterator ita = arg_irrelevant.begin();
+ ita != arg_irrelevant.end();
+ ++ita)
+ {
+ unsigned arg = *ita;
+ Assert(arg < sfvl.getNumChildren());
+ term_irrelevant.insert(sfvl[arg]);
+ }
+
+ // make the default grammar
+ tn = mkSygusDefaultType(
+ v.getType(), sfvl, ss.str(), extra_cons, term_irrelevant);
+ }
+ // normalize type
+ SygusGrammarNorm sygus_norm(d_qe);
+ tn = sygus_norm.normalizeSygusType(tn, sfvl);
+ // check if there is a template
+ std::map< Node, Node >::iterator itt = templates.find( sf );
+ if( itt!=templates.end() ){
+ Node templ = itt->second;
+ TNode templ_arg = templates_arg[sf];
+ Assert( !templ_arg.isNull() );
+ Trace("cegqi-debug") << "Template for " << sf << " is : " << templ << " with arg " << templ_arg << std::endl;
+ // if there is a template for this argument, make a sygus type on top of it
+ if( options::sygusTemplEmbedGrammar() ){
+ Trace("cegqi-debug") << " embed this template as a grammar..." << std::endl;
+ tn = mkSygusTemplateType( templ, templ_arg, tn, sfvl, ss.str() );
+ }else{
+ // otherwise, apply it as a preprocessing pass
+ Trace("cegqi-debug") << " apply this template as a substituion during preprocess..." << std::endl;
+ std::vector< Node > schildren;
+ std::vector< Node > largs;
+ for( unsigned j=0; j<sfvl.getNumChildren(); j++ ){
+ schildren.push_back( sfvl[j] );
+ largs.push_back( NodeManager::currentNM()->mkBoundVar( sfvl[j].getType() ) );
+ }
+ std::vector< Node > subsfn_children;
+ subsfn_children.push_back( sf );
+ subsfn_children.insert( subsfn_children.end(), schildren.begin(), schildren.end() );
+ Node subsfn = NodeManager::currentNM()->mkNode( kind::APPLY_UF, subsfn_children );
+ TNode subsf = subsfn;
+ Trace("cegqi-debug") << " substitute arg : " << templ_arg << " -> " << subsf << std::endl;
+ templ = templ.substitute( templ_arg, subsf );
+ // substitute lambda arguments
+ templ = templ.substitute( schildren.begin(), schildren.end(), largs.begin(), largs.end() );
+ Node subsn = NodeManager::currentNM()->mkNode( kind::LAMBDA, NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, largs ), templ );
+ TNode var = sf;
+ TNode subs = subsn;
+ Trace("cegqi-debug") << " substitute : " << var << " -> " << subs << std::endl;
+ qbody_subs = qbody_subs.substitute( var, subs );
+ Trace("cegqi-debug") << " body is now : " << qbody_subs << std::endl;
+ }
+ }
+ d_qe->getTermDatabaseSygus()->registerSygusType( tn );
+ // check grammar restrictions
+ if( !d_qe->getTermDatabaseSygus()->sygusToBuiltinType( tn ).isBoolean() ){
+ if( !d_qe->getTermDatabaseSygus()->hasKind( tn, ITE ) ){
+ d_has_ite = false;
+ }
+ }
+ Assert( tn.isDatatype() );
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ Assert( dt.isSygus() );
+ if( !dt.getSygusAllowAll() ){
+ d_is_syntax_restricted = true;
+ }
+
+ // ev is the first-order variable corresponding to this synth fun
+ std::stringstream ssf;
+ ssf << "f" << sf;
+ Node ev = NodeManager::currentNM()->mkBoundVar( ssf.str(), tn );
+ ebvl.push_back( ev );
+ synth_fun_vars[sf] = ev;
+ Trace("cegqi") << "...embedding synth fun : " << sf << " -> " << ev << std::endl;
+ }
+ qchildren.push_back( NodeManager::currentNM()->mkNode( kind::BOUND_VAR_LIST, ebvl ) );
+ if( qbody_subs!=q[1] ){
+ Trace("cegqi") << "...rewriting : " << qbody_subs << std::endl;
+ qbody_subs = Rewriter::rewrite( qbody_subs );
+ Trace("cegqi") << "...got : " << qbody_subs << std::endl;
+ }
+ qchildren.push_back( convertToEmbedding( qbody_subs, synth_fun_vars ) );
+ if( q.getNumChildren()==3 ){
+ qchildren.push_back( q[2] );
+ }
+ return NodeManager::currentNM()->mkNode( kind::FORALL, qchildren );
+}
+
+Node CegGrammarConstructor::convertToEmbedding( Node n, std::map< Node, Node >& synth_fun_vars ){
+ std::unordered_map<TNode, Node, TNodeHashFunction> visited;
+ std::unordered_map<TNode, Node, TNodeHashFunction>::iterator it;
+ std::stack<TNode> visit;
+ TNode cur;
+ visit.push(n);
+ do {
+ cur = visit.top();
+ visit.pop();
+ it = visited.find(cur);
+ if (it == visited.end()) {
+ visited[cur] = Node::null();
+ visit.push(cur);
+ for (unsigned i = 0; i < cur.getNumChildren(); i++) {
+ visit.push(cur[i]);
+ }
+ } else if (it->second.isNull()) {
+ Node ret = cur;
+ Kind ret_k = cur.getKind();
+ Node op;
+ bool childChanged = false;
+ std::vector<Node> children;
+ // get the potential operator
+ if( cur.getNumChildren()>0 ){
+ if( cur.getKind()==kind::APPLY_UF ){
+ op = cur.getOperator();
+ }
+ }else{
+ op = cur;
+ }
+ // is the operator a synth function?
+ if( !op.isNull() ){
+ std::map< Node, Node >::iterator its = synth_fun_vars.find( op );
+ if( its!=synth_fun_vars.end() ){
+ Assert( its->second.getType().isDatatype() );
+ // will make into an application of an evaluation function
+ const Datatype& dt = ((DatatypeType)its->second.getType().toType()).getDatatype();
+ Assert( dt.isSygus() );
+ children.push_back( Node::fromExpr( dt.getSygusEvaluationFunc() ) );
+ children.push_back( its->second );
+ childChanged = true;
+ ret_k = kind::APPLY_UF;
+ }
+ }
+ if( !childChanged ){
+ // otherwise, we apply the previous operator
+ if( cur.getMetaKind() == kind::metakind::PARAMETERIZED ){
+ children.push_back( cur.getOperator() );
+ }
+ }
+ for (unsigned i = 0; i < cur.getNumChildren(); i++) {
+ it = visited.find(cur[i]);
+ Assert(it != visited.end());
+ Assert(!it->second.isNull());
+ childChanged = childChanged || cur[i] != it->second;
+ children.push_back(it->second);
+ }
+ if (childChanged) {
+ ret = NodeManager::currentNM()->mkNode(ret_k, children);
+ }
+ visited[cur] = ret;
+ }
+ } while (!visit.empty());
+ Assert(visited.find(n) != visited.end());
+ Assert(!visited.find(n)->second.isNull());
+ return visited[n];
+}
+
+
+TypeNode CegGrammarConstructor::mkUnresolvedType(const std::string& name, std::set<Type>& unres) {
+ TypeNode unresolved = NodeManager::currentNM()->mkSort(name, ExprManager::SORT_FLAG_PLACEHOLDER);
+ unres.insert( unresolved.toType() );
+ return unresolved;
+}
+
+void CegGrammarConstructor::mkSygusConstantsForType( TypeNode type, std::vector<CVC4::Node>& ops ) {
+ if (type.isReal())
+ {
+ ops.push_back(NodeManager::currentNM()->mkConst(Rational(0)));
+ ops.push_back(NodeManager::currentNM()->mkConst(Rational(1)));
+ }else if( type.isBitVector() ){
+ unsigned sz = ((BitVectorType)type.toType()).getSize();
+ BitVector bval0(sz, (unsigned int)0);
+ ops.push_back( NodeManager::currentNM()->mkConst(bval0) );
+ BitVector bval1(sz, (unsigned int)1);
+ ops.push_back( NodeManager::currentNM()->mkConst(bval1) );
+ }else if( type.isBoolean() ){
+ ops.push_back(NodeManager::currentNM()->mkConst(true));
+ ops.push_back(NodeManager::currentNM()->mkConst(false));
+ }
+ //TODO : others?
+}
+
+void CegGrammarConstructor::collectSygusGrammarTypesFor( TypeNode range, std::vector< TypeNode >& types, std::map< TypeNode, std::vector< DatatypeConstructorArg > >& sels ){
+ if( !range.isBoolean() ){
+ if( std::find( types.begin(), types.end(), range )==types.end() ){
+ Trace("sygus-grammar-def") << "...will make grammar for " << range << std::endl;
+ types.push_back( range );
+ if( range.isDatatype() ){
+ const Datatype& dt = ((DatatypeType)range.toType()).getDatatype();
+ for( unsigned i=0; i<dt.getNumConstructors(); i++ ){
+ for( unsigned j=0; j<dt[i].getNumArgs(); j++ ){
+ TypeNode crange = TypeNode::fromType( ((SelectorType)dt[i][j].getType()).getRangeType() );
+ sels[crange].push_back( dt[i][j] );
+ collectSygusGrammarTypesFor( crange, types, sels );
+ }
+ }
+ }
+ }
+ }
+}
+
+void CegGrammarConstructor::mkSygusDefaultGrammar(
+ TypeNode range,
+ Node bvl,
+ const std::string& fun,
+ std::map<TypeNode, std::vector<Node> >& extra_cons,
+ std::unordered_set<Node, NodeHashFunction>& term_irrelevant,
+ std::vector<CVC4::Datatype>& datatypes,
+ std::set<Type>& unres)
+{
+ Trace("sygus-grammar-def") << "Construct default grammar for " << fun << " "
+ << range << std::endl;
+ // collect the variables
+ std::vector<Node> sygus_vars;
+ if( !bvl.isNull() ){
+ for( unsigned i=0; i<bvl.getNumChildren(); i++ ){
+ if (term_irrelevant.find(bvl[i]) == term_irrelevant.end())
+ {
+ sygus_vars.push_back(bvl[i]);
+ }
+ else
+ {
+ Trace("sygus-grammar-def") << "...synth var " << bvl[i]
+ << " has been marked irrelevant."
+ << std::endl;
+ }
+ }
+ }
+ //if( !range.isBoolean() && !range.isInteger() && !range.isBitVector() && !range.isDatatype() ){
+ // parseError("No default grammar for type.");
+ //}
+ std::vector< std::vector< Expr > > ops;
+ int startIndex = -1;
+ std::map< Type, Type > sygus_to_builtin;
+
+ std::vector< TypeNode > types;
+ std::map< TypeNode, std::vector< DatatypeConstructorArg > > sels;
+ //types for each of the variables of parametric sort
+ for( unsigned i=0; i<sygus_vars.size(); i++ ){
+ collectSygusGrammarTypesFor( sygus_vars[i].getType(), types, sels );
+ }
+ //types connected to range
+ collectSygusGrammarTypesFor( range, types, sels );
+
+ //name of boolean sort
+ std::stringstream ssb;
+ ssb << fun << "_Bool";
+ std::string dbname = ssb.str();
+ Type unres_bt = mkUnresolvedType(ssb.str(), unres).toType();
+
+ std::vector< Type > unres_types;
+ std::map< TypeNode, Type > type_to_unres;
+ for( unsigned i=0; i<types.size(); i++ ){
+ std::stringstream ss;
+ ss << fun << "_" << types[i];
+ std::string dname = ss.str();
+ datatypes.push_back(Datatype(dname));
+ ops.push_back(std::vector< Expr >());
+ //make unresolved type
+ Type unres_t = mkUnresolvedType(dname, unres).toType();
+ unres_types.push_back(unres_t);
+ type_to_unres[types[i]] = unres_t;
+ sygus_to_builtin[unres_t] = types[i].toType();
+ }
+ for( unsigned i=0; i<types.size(); i++ ){
+ Trace("sygus-grammar-def") << "Make grammar for " << types[i] << " " << unres_types[i] << std::endl;
+ std::vector<std::string> cnames;
+ std::vector<std::vector<CVC4::Type> > cargs;
+ Type unres_t = unres_types[i];
+ //add variables
+ for( unsigned j=0; j<sygus_vars.size(); j++ ){
+ if( sygus_vars[j].getType()==types[i] ){
+ std::stringstream ss;
+ ss << sygus_vars[j];
+ Trace("sygus-grammar-def") << "...add for variable " << ss.str() << std::endl;
+ ops[i].push_back( sygus_vars[j].toExpr() );
+ cnames.push_back( ss.str() );
+ cargs.push_back( std::vector< CVC4::Type >() );
+ }
+ }
+ //add constants
+ std::vector< Node > consts;
+ mkSygusConstantsForType( types[i], consts );
+ std::map< TypeNode, std::vector< Node > >::iterator itec = extra_cons.find( types[i] );
+ if( itec!=extra_cons.end() ){
+ //consts.insert( consts.end(), itec->second.begin(), itec->second.end() );
+ for( unsigned j=0; j<itec->second.size(); j++ ){
+ if( std::find( consts.begin(), consts.end(), itec->second[j] )==consts.end() ){
+ consts.push_back( itec->second[j] );
+ }
+ }
+ }
+ for( unsigned j=0; j<consts.size(); j++ ){
+ std::stringstream ss;
+ ss << consts[j];
+ Trace("sygus-grammar-def") << "...add for constant " << ss.str() << std::endl;
+ ops[i].push_back( consts[j].toExpr() );
+ cnames.push_back( ss.str() );
+ cargs.push_back( std::vector< CVC4::Type >() );
+ }
+ //ITE
+ CVC4::Kind k = kind::ITE;
+ Trace("sygus-grammar-def") << "...add for " << k << std::endl;
+ ops[i].push_back(NodeManager::currentNM()->operatorOf(k).toExpr());
+ cnames.push_back( kind::kindToString(k) );
+ cargs.push_back( std::vector< CVC4::Type >() );
+ cargs.back().push_back(unres_bt);
+ cargs.back().push_back(unres_t);
+ cargs.back().push_back(unres_t);
+
+ if (types[i].isReal())
+ {
+ for (unsigned j = 0; j < 2; j++)
+ {
+ Kind k = j == 0 ? PLUS : MINUS;
+ Trace("sygus-grammar-def") << "...add for " << k << std::endl;
+ ops[i].push_back(NodeManager::currentNM()->operatorOf(k).toExpr());
+ cnames.push_back(kind::kindToString(k));
+ cargs.push_back(std::vector<CVC4::Type>());
+ cargs.back().push_back(unres_t);
+ cargs.back().push_back(unres_t);
+ }
+ if (!types[i].isInteger())
+ {
+ Trace("sygus-grammar-def") << "...Dedicate to Real\n";
+ /* Creating type for positive integers */
+ std::stringstream ss;
+ ss << fun << "_PosInt";
+ std::string pos_int_name = ss.str();
+ // make unresolved type
+ Type unres_pos_int_t = mkUnresolvedType(pos_int_name, unres).toType();
+ // make data type
+ datatypes.push_back(Datatype(pos_int_name));
+ /* add placeholders */
+ std::vector<Expr> ops_pos_int;
+ std::vector<std::string> cnames_pos_int;
+ std::vector<std::vector<Type>> cargs_pos_int;
+ /* Add operator 1 */
+ Trace("sygus-grammar-def") << "\t...add for 1 to Pos_Int\n";
+ ops_pos_int.push_back(
+ NodeManager::currentNM()->mkConst(Rational(1)).toExpr());
+ ss << "_1";
+ cnames_pos_int.push_back(ss.str());
+ cargs_pos_int.push_back(std::vector<Type>());
+ /* Add operator PLUS */
+ Kind k = PLUS;
+ Trace("sygus-grammar-def") << "\t...add for PLUS to Pos_Int\n";
+ ops_pos_int.push_back(NodeManager::currentNM()->operatorOf(k).toExpr());
+ cnames_pos_int.push_back(kindToString(k));
+ cargs_pos_int.push_back(std::vector<Type>());
+ cargs_pos_int.back().push_back(unres_pos_int_t);
+ cargs_pos_int.back().push_back(unres_pos_int_t);
+ datatypes.back().setSygus(types[i].toType(), bvl.toExpr(), true, true);
+ for (unsigned j = 0; j < ops_pos_int.size(); j++)
+ {
+ datatypes.back().addSygusConstructor(
+ ops_pos_int[j], cnames_pos_int[j], cargs_pos_int[j]);
+ }
+ Trace("sygus-grammar-def")
+ << "...built datatype " << datatypes.back() << " ";
+ /* Adding division at root */
+ k = DIVISION;
+ Trace("sygus-grammar-def") << "\t...add for " << k << std::endl;
+ ops[i].push_back(NodeManager::currentNM()->operatorOf(k).toExpr());
+ cnames.push_back(kindToString(k));
+ cargs.push_back(std::vector<Type>());
+ cargs.back().push_back(unres_t);
+ cargs.back().push_back(unres_pos_int_t);
+ }
+ }else if( types[i].isDatatype() ){
+ Trace("sygus-grammar-def") << "...add for constructors" << std::endl;
+ const Datatype& dt = ((DatatypeType)types[i].toType()).getDatatype();
+ for( unsigned k=0; k<dt.getNumConstructors(); k++ ){
+ Trace("sygus-grammar-def") << "...for " << dt[k].getName() << std::endl;
+ ops[i].push_back( dt[k].getConstructor() );
+ cnames.push_back( dt[k].getName() );
+ cargs.push_back( std::vector< CVC4::Type >() );
+ for( unsigned j=0; j<dt[k].getNumArgs(); j++ ){
+ TypeNode crange = TypeNode::fromType( ((SelectorType)dt[k][j].getType()).getRangeType() );
+ //Assert( type_to_unres.find(crange)!=type_to_unres.end() );
+ cargs.back().push_back( type_to_unres[crange] );
+ }
+ }
+ }else{
+ std::stringstream sserr;
+ sserr << "No implementation for default Sygus grammar of type " << types[i] << std::endl;
+ //AlwaysAssert( false, sserr.str() );
+ // FIXME
+ AlwaysAssert( false );
+ }
+ //add for all selectors to this type
+ if( !sels[types[i]].empty() ){
+ Trace("sygus-grammar-def") << "...add for selectors" << std::endl;
+ for( unsigned j=0; j<sels[types[i]].size(); j++ ){
+ Trace("sygus-grammar-def") << "...for " << sels[types[i]][j].getName() << std::endl;
+ TypeNode arg_type = TypeNode::fromType( ((SelectorType)sels[types[i]][j].getType()).getDomain() );
+ ops[i].push_back( sels[types[i]][j].getSelector() );
+ cnames.push_back( sels[types[i]][j].getName() );
+ cargs.push_back( std::vector< CVC4::Type >() );
+ //Assert( type_to_unres.find(arg_type)!=type_to_unres.end() );
+ cargs.back().push_back( type_to_unres[arg_type] );
+ }
+ }
+ Trace("sygus-grammar-def") << "...make datatype " << datatypes[i] << std::endl;
+ datatypes[i].setSygus( types[i].toType(), bvl.toExpr(), true, true );
+ for( unsigned j=0; j<ops[i].size(); j++ ){
+ datatypes[i].addSygusConstructor( ops[i][j], cnames[j], cargs[j] );
+ }
+ Trace("sygus-grammar-def")
+ << "...built datatype " << datatypes[i] << " ";
+ //sorts.push_back( types[i] );
+ //set start index if applicable
+ if( types[i]==range ){
+ startIndex = i;
+ }
+ }
+
+ //make Boolean type
+ TypeNode btype = NodeManager::currentNM()->booleanType();
+ datatypes.push_back(Datatype(dbname));
+ ops.push_back(std::vector<Expr>());
+ std::vector<std::string> cnames;
+ std::vector<std::vector< Type > > cargs;
+ Trace("sygus-grammar-def") << "Make grammar for " << btype << " " << datatypes.back() << std::endl;
+ //add variables
+ for( unsigned i=0; i<sygus_vars.size(); i++ ){
+ if( sygus_vars[i].getType().isBoolean() ){
+ std::stringstream ss;
+ ss << sygus_vars[i];
+ Trace("sygus-grammar-def") << "...add for variable " << ss.str() << std::endl;
+ ops.back().push_back( sygus_vars[i].toExpr() );
+ cnames.push_back( ss.str() );
+ cargs.push_back( std::vector< CVC4::Type >() );
+ }
+ }
+ //add constants if no variables and no connected types
+ if( ops.back().empty() && types.empty() ){
+ std::vector< Node > consts;
+ mkSygusConstantsForType( btype, consts );
+ for( unsigned j=0; j<consts.size(); j++ ){
+ std::stringstream ss;
+ ss << consts[j];
+ Trace("sygus-grammar-def") << "...add for constant " << ss.str() << std::endl;
+ ops.back().push_back( consts[j].toExpr() );
+ cnames.push_back( ss.str() );
+ cargs.push_back( std::vector< CVC4::Type >() );
+ }
+ }
+ //add operators
+ for( unsigned i=0; i<3; i++ ){
+ CVC4::Kind k = i==0 ? kind::NOT : ( i==1 ? kind::AND : kind::OR );
+ Trace("sygus-grammar-def") << "...add for " << k << std::endl;
+ ops.back().push_back(NodeManager::currentNM()->operatorOf(k).toExpr());
+ cnames.push_back(kind::kindToString(k));
+ cargs.push_back( std::vector< CVC4::Type >() );
+ if( k==kind::NOT ){
+ cargs.back().push_back(unres_bt);
+ }else if( k==kind::AND || k==kind::OR ){
+ cargs.back().push_back(unres_bt);
+ cargs.back().push_back(unres_bt);
+ }
+ }
+ //add predicates for types
+ for( unsigned i=0; i<types.size(); i++ ){
+ Trace("sygus-grammar-def") << "...add predicates for " << types[i] << std::endl;
+ //add equality per type
+ CVC4::Kind k = kind::EQUAL;
+ Trace("sygus-grammar-def") << "...add for " << k << std::endl;
+ ops.back().push_back(NodeManager::currentNM()->operatorOf(k).toExpr());
+ std::stringstream ss;
+ ss << kind::kindToString(k) << "_" << types[i];
+ cnames.push_back(ss.str());
+ cargs.push_back( std::vector< CVC4::Type >() );
+ cargs.back().push_back(unres_types[i]);
+ cargs.back().push_back(unres_types[i]);
+ //type specific predicates
+ if (types[i].isReal())
+ {
+ CVC4::Kind k = kind::LEQ;
+ Trace("sygus-grammar-def") << "...add for " << k << std::endl;
+ ops.back().push_back(NodeManager::currentNM()->operatorOf(k).toExpr());
+ cnames.push_back(kind::kindToString(k));
+ cargs.push_back( std::vector< CVC4::Type >() );
+ cargs.back().push_back(unres_types[i]);
+ cargs.back().push_back(unres_types[i]);
+ }else if( types[i].isDatatype() ){
+ //add for testers
+ Trace("sygus-grammar-def") << "...add for testers" << std::endl;
+ const Datatype& dt = ((DatatypeType)types[i].toType()).getDatatype();
+ for( unsigned k=0; k<dt.getNumConstructors(); k++ ){
+ Trace("sygus-grammar-def") << "...for " << dt[k].getTesterName() << std::endl;
+ ops.back().push_back(dt[k].getTester());
+ cnames.push_back(dt[k].getTesterName());
+ cargs.push_back( std::vector< CVC4::Type >() );
+ cargs.back().push_back(unres_types[i]);
+ }
+ }
+ }
+ if( range==btype ){
+ startIndex = datatypes.size()-1;
+ }
+ Trace("sygus-grammar-def") << "...make datatype " << datatypes.back() << std::endl;
+ datatypes.back().setSygus( btype.toType(), bvl.toExpr(), true, true );
+ for( unsigned j=0; j<ops.back().size(); j++ ){
+ datatypes.back().addSygusConstructor( ops.back()[j], cnames[j], cargs[j] );
+ }
+ //sorts.push_back( btype );
+ Trace("sygus-grammar-def") << "...finished make default grammar for " << fun << " " << range << std::endl;
+
+ if( startIndex>0 ){
+ CVC4::Datatype tmp_dt = datatypes[0];
+ datatypes[0] = datatypes[startIndex];
+ datatypes[startIndex] = tmp_dt;
+ }
+}
+
+TypeNode CegGrammarConstructor::mkSygusDefaultType(
+ TypeNode range,
+ Node bvl,
+ const std::string& fun,
+ std::map<TypeNode, std::vector<Node> >& extra_cons,
+ std::unordered_set<Node, NodeHashFunction>& term_irrelevant)
+{
+ Trace("sygus-grammar-def") << "*** Make sygus default type " << range << ", make datatypes..." << std::endl;
+ for( std::map< TypeNode, std::vector< Node > >::iterator it = extra_cons.begin(); it != extra_cons.end(); ++it ){
+ Trace("sygus-grammar-def") << " ...using " << it->second.size() << " extra constants for " << it->first << std::endl;
+ }
+ std::set<Type> unres;
+ std::vector< CVC4::Datatype > datatypes;
+ mkSygusDefaultGrammar(
+ range, bvl, fun, extra_cons, term_irrelevant, datatypes, unres);
+ Trace("sygus-grammar-def") << "...made " << datatypes.size() << " datatypes, now make mutual datatype types..." << std::endl;
+ Assert( !datatypes.empty() );
+ std::vector<DatatypeType> types = NodeManager::currentNM()->toExprManager()->mkMutualDatatypeTypes(datatypes, unres);
+ Assert( types.size()==datatypes.size() );
+ return TypeNode::fromType( types[0] );
+}
+
+TypeNode CegGrammarConstructor::mkSygusTemplateTypeRec( Node templ, Node templ_arg, TypeNode templ_arg_sygus_type, Node bvl,
+ const std::string& fun, unsigned& tcount ) {
+ if( templ==templ_arg ){
+ //Assert( templ_arg.getType()==sygusToBuiltinType( templ_arg_sygus_type ) );
+ return templ_arg_sygus_type;
+ }else{
+ tcount++;
+ std::set<Type> unres;
+ std::vector< CVC4::Datatype > datatypes;
+ std::stringstream ssd;
+ ssd << fun << "_templ_" << tcount;
+ std::string dbname = ssd.str();
+ datatypes.push_back(Datatype(dbname));
+ Node op;
+ std::vector< Type > argTypes;
+ if( templ.getNumChildren()==0 ){
+ // TODO : can short circuit to this case when !TermUtil::containsTerm( templ, templ_arg )
+ op = templ;
+ }else{
+ Assert( templ.hasOperator() );
+ op = templ.getOperator();
+ // make constructor taking arguments types from children
+ for( unsigned i=0; i<templ.getNumChildren(); i++ ){
+ //recursion depth bound by the depth of SyGuS template expressions (low)
+ TypeNode tnc = mkSygusTemplateTypeRec( templ[i], templ_arg, templ_arg_sygus_type, bvl, fun, tcount );
+ argTypes.push_back( tnc.toType() );
+ }
+ }
+ std::stringstream ssdc;
+ ssdc << fun << "_templ_cons_" << tcount;
+ std::string cname = ssdc.str();
+ // we have a single sygus constructor that encodes the template
+ datatypes.back().addSygusConstructor( op.toExpr(), cname, argTypes );
+ datatypes.back().setSygus( templ.getType().toType(), bvl.toExpr(), true, true );
+ std::vector<DatatypeType> types = NodeManager::currentNM()->toExprManager()->mkMutualDatatypeTypes(datatypes, unres);
+ Assert( types.size()==1 );
+ return TypeNode::fromType( types[0] );
+ }
+}
+
+TypeNode CegGrammarConstructor::mkSygusTemplateType( Node templ, Node templ_arg, TypeNode templ_arg_sygus_type, Node bvl,
+ const std::string& fun ) {
+ unsigned tcount = 0;
+ return mkSygusTemplateTypeRec( templ, templ_arg, templ_arg_sygus_type, bvl, fun, tcount );
+}
+
+}/* namespace CVC4::theory::quantifiers */
+}/* namespace CVC4::theory */
+}/* namespace CVC4 */
--- /dev/null
+/********************* */
+/*! \file sygus_grammar_cons.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief class for constructing inductive datatypes that correspond to
+ ** grammars that encode syntactic restrictions for SyGuS.
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_CONS_H
+#define __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_CONS_H
+
+#include "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+class CegConjecture;
+
+/** utility for constructing datatypes that correspond to syntactic restrictions,
+* and applying the deep embedding from Section 4 of Reynolds et al CAV 2015.
+*/
+class CegGrammarConstructor
+{
+public:
+ CegGrammarConstructor(QuantifiersEngine* qe, CegConjecture* p);
+ ~CegGrammarConstructor() {}
+ /** process
+ * This converts node q based on its deep embedding
+ * (Section 4 of Reynolds et al CAV 2015).
+ * The syntactic restrictions are associated with
+ * the functions-to-synthesize using the attribute
+ * SygusSynthGrammarAttribute.
+ * The arguments templates and template_args
+ * indicate templates for the function to synthesize,
+ * in particular the solution for the i^th function
+ * to synthesis must be of the form
+ * templates[i]{ templates_arg[i] -> t }
+ * for some t if !templates[i].isNull().
+ */
+ Node process(Node q,
+ std::map<Node, Node>& templates,
+ std::map<Node, Node>& templates_arg);
+ /** is the syntax restricted? */
+ bool isSyntaxRestricted() { return d_is_syntax_restricted; }
+ /** does the syntax allow ITE expressions? */
+ bool hasSyntaxITE() { return d_has_ite; }
+ /** make the default sygus datatype type corresponding to builtin type range
+ * bvl is the set of free variables to include in the grammar
+ * fun is for naming
+ * extra_cons is a set of extra constant symbols to include in the grammar
+ * term_irrelevant is a set of terms that should not be included in the
+ * grammar.
+ */
+ static TypeNode mkSygusDefaultType(
+ TypeNode range,
+ Node bvl,
+ const std::string& fun,
+ std::map<TypeNode, std::vector<Node> >& extra_cons,
+ std::unordered_set<Node, NodeHashFunction>& term_irrelevant);
+ /** make the default sygus datatype type corresponding to builtin type range */
+ static TypeNode mkSygusDefaultType(TypeNode range,
+ Node bvl,
+ const std::string& fun)
+ {
+ std::map<TypeNode, std::vector<Node> > extra_cons;
+ std::unordered_set<Node, NodeHashFunction> term_irrelevant;
+ return mkSygusDefaultType(range, bvl, fun, extra_cons, term_irrelevant);
+ }
+ /** make the sygus datatype type that encodes the solution space (lambda
+ * templ_arg. templ[templ_arg]) where templ_arg
+ * has syntactic restrictions encoded by sygus type templ_arg_sygus_type
+ * bvl is the set of free variables to include in the grammar
+ * fun is for naming
+ */
+ static TypeNode mkSygusTemplateType( Node templ, Node templ_arg, TypeNode templ_arg_sygus_type, Node bvl, const std::string& fun );
+private:
+ /** reference to quantifier engine */
+ QuantifiersEngine * d_qe;
+ /** parent conjecture
+ * This contains global information about the synthesis conjecture.
+ */
+ CegConjecture* d_parent;
+ /** is the syntax restricted? */
+ bool d_is_syntax_restricted;
+ /** does the syntax allow ITE expressions? */
+ bool d_has_ite;
+ /** collect terms */
+ void collectTerms( Node n, std::map< TypeNode, std::vector< Node > >& consts );
+ /** convert node n based on deep embedding (Section 4 of Reynolds et al CAV 2015) */
+ Node convertToEmbedding( Node n, std::map< Node, Node >& synth_fun_vars );
+ //---------------- grammar construction
+ // helper for mkSygusDefaultGrammar (makes unresolved type for mutually recursive datatype construction)
+ static TypeNode mkUnresolvedType(const std::string& name, std::set<Type>& unres);
+ // make the builtin constants for type type that should be included in a sygus grammar
+ static void mkSygusConstantsForType( TypeNode type, std::vector<CVC4::Node>& ops );
+ // collect the list of types that depend on type range
+ static void collectSygusGrammarTypesFor( TypeNode range, std::vector< TypeNode >& types, std::map< TypeNode, std::vector< DatatypeConstructorArg > >& sels );
+ /** helper function for function mkSygusDefaultType
+ * Collects a set of mutually recursive datatypes "datatypes" corresponding to
+ * encoding type "range" to SyGuS.
+ * unres is used for the resulting call to mkMutualDatatypeTypes
+ */
+ static void mkSygusDefaultGrammar(
+ TypeNode range,
+ Node bvl,
+ const std::string& fun,
+ std::map<TypeNode, std::vector<Node> >& extra_cons,
+ std::unordered_set<Node, NodeHashFunction>& term_irrelevant,
+ std::vector<CVC4::Datatype>& datatypes,
+ std::set<Type>& unres);
+ // helper function for mkSygusTemplateType
+ static TypeNode mkSygusTemplateTypeRec( Node templ, Node templ_arg, TypeNode templ_arg_sygus_type, Node bvl,
+ const std::string& fun, unsigned& tcount );
+ //---------------- end grammar construction
+};
+
+} /* namespace CVC4::theory::quantifiers */
+} /* namespace CVC4::theory */
+} /* namespace CVC4 */
+
+#endif
--- /dev/null
+/********************* */
+/*! \file sygus_grammar_norm.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Haniel Barbosa
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief implementation of class for for simplifying SyGuS grammars after they
+ ** are encoded into datatypes.
+ **/
+
+#include "theory/quantifiers/sygus/sygus_grammar_norm.h"
+
+#include "expr/datatype.h"
+#include "options/quantifiers_options.h"
+#include "printer/sygus_print_callback.h"
+#include "smt/smt_engine.h"
+#include "smt/smt_engine_scope.h"
+#include "theory/quantifiers/sygus/ce_guided_conjecture.h"
+#include "theory/quantifiers/sygus/sygus_grammar_red.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+#include "theory/quantifiers/term_util.h"
+
+#include <numeric> // for std::iota
+
+using namespace CVC4::kind;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+bool OpPosTrie::getOrMakeType(TypeNode tn,
+ TypeNode& unres_tn,
+ const std::vector<unsigned>& op_pos,
+ unsigned ind)
+{
+ if (ind == op_pos.size())
+ {
+ /* Found type */
+ if (!d_unres_tn.isNull())
+ {
+ Trace("sygus-grammar-normalize-trie")
+ << "\tFound type " << d_unres_tn << "\n";
+ unres_tn = d_unres_tn;
+ return true;
+ }
+ /* Creating unresolved type */
+ std::stringstream ss;
+ ss << tn << "_";
+ for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
+ {
+ ss << "_" << std::to_string(op_pos[i]);
+ }
+ d_unres_tn = NodeManager::currentNM()->mkSort(
+ ss.str(), ExprManager::SORT_FLAG_PLACEHOLDER);
+ Trace("sygus-grammar-normalize-trie")
+ << "\tCreating type " << d_unres_tn << "\n";
+ unres_tn = d_unres_tn;
+ return false;
+ }
+ /* Go to next node */
+ return d_children[op_pos[ind]].getOrMakeType(tn, unres_tn, op_pos, ind + 1);
+}
+
+void SygusGrammarNorm::TypeObject::addConsInfo(SygusGrammarNorm* sygus_norm,
+ const DatatypeConstructor& cons)
+{
+ Trace("sygus-grammar-normalize") << "...for " << cons.getName() << "\n";
+ /* Recover the sygus operator to not lose reference to the original
+ * operator (NOT, ITE, etc) */
+ Node exp_sop_n = Node::fromExpr(
+ smt::currentSmtEngine()->expandDefinitions(cons.getSygusOp()));
+ d_ops.push_back(Rewriter::rewrite(exp_sop_n));
+ Trace("sygus-grammar-normalize-defs")
+ << "\tOriginal op: " << cons.getSygusOp()
+ << "\n\tExpanded one: " << exp_sop_n
+ << "\n\tRewritten one: " << d_ops.back() << "\n\n";
+ d_cons_names.push_back(cons.getName());
+ d_pc.push_back(cons.getSygusPrintCallback());
+ d_weight.push_back(cons.getWeight());
+ d_cons_args_t.push_back(std::vector<Type>());
+ for (const DatatypeConstructorArg& arg : cons)
+ {
+ /* Collect unresolved type nodes corresponding to the typenode of the
+ * arguments */
+ d_cons_args_t.back().push_back(
+ sygus_norm
+ ->normalizeSygusRec(TypeNode::fromType(
+ static_cast<SelectorType>(arg.getType()).getRangeType()))
+ .toType());
+ }
+}
+
+void SygusGrammarNorm::TypeObject::buildDatatype(SygusGrammarNorm* sygus_norm,
+ const Datatype& dt)
+{
+ /* Use the sygus type to not lose reference to the original types (Bool,
+ * Int, etc) */
+ d_dt.setSygus(dt.getSygusType(),
+ sygus_norm->d_sygus_vars.toExpr(),
+ dt.getSygusAllowConst(),
+ dt.getSygusAllowAll());
+ for (unsigned i = 0, size_d_ops = d_ops.size(); i < size_d_ops; ++i)
+ {
+ d_dt.addSygusConstructor(d_ops[i].toExpr(),
+ d_cons_names[i],
+ d_cons_args_t[i],
+ d_pc[i],
+ d_weight[i]);
+ }
+ Trace("sygus-grammar-normalize") << "...built datatype " << d_dt << " ";
+ /* Add to global accumulators */
+ sygus_norm->d_dt_all.push_back(d_dt);
+ sygus_norm->d_unres_t_all.insert(d_unres_tn.toType());
+ Trace("sygus-grammar-normalize") << "---------------------------------\n";
+}
+
+void SygusGrammarNorm::TransfDrop::buildType(SygusGrammarNorm* sygus_norm,
+ TypeObject& to,
+ const Datatype& dt,
+ std::vector<unsigned>& op_pos)
+{
+ std::vector<unsigned> difference;
+ std::set_difference(op_pos.begin(),
+ op_pos.end(),
+ d_drop_indices.begin(),
+ d_drop_indices.end(),
+ std::back_inserter(difference));
+ op_pos = difference;
+}
+
+/* TODO #1304: have more operators and types. Moreover, have more general ways
+ of finding kind of operator, e.g. if op is (\lambda xy. x + y) this
+ function should realize that it is chainable for integers */
+bool SygusGrammarNorm::TransfChain::isChainable(TypeNode tn, Node op)
+{
+ /* Checks whether operator occurs chainable for its type */
+ if (tn.isInteger() && NodeManager::currentNM()->operatorToKind(op) == PLUS)
+ {
+ return true;
+ }
+ return false;
+}
+
+/* TODO #1304: have more operators and types. Moreover, have more general ways
+ of finding kind of operator, e.g. if op is (\lambda xy. x + y) this
+ function should realize that it is chainable for integers */
+bool SygusGrammarNorm::TransfChain::isId(TypeNode tn, Node op, Node n)
+{
+ if (tn.isInteger() && NodeManager::currentNM()->operatorToKind(op) == PLUS
+ && n == TermUtil::mkTypeValue(tn, 0))
+ {
+ return true;
+ }
+ return false;
+}
+
+void SygusGrammarNorm::TransfChain::buildType(SygusGrammarNorm* sygus_norm,
+ TypeObject& to,
+ const Datatype& dt,
+ std::vector<unsigned>& op_pos)
+{
+ NodeManager* nm = NodeManager::currentNM();
+ std::vector<unsigned> claimed(d_elem_pos);
+ claimed.push_back(d_chain_op_pos);
+ unsigned nb_op_pos = op_pos.size();
+ /* TODO do this properly */
+ /* Remove from op_pos the positions claimed by the transformation */
+ std::sort(op_pos.begin(), op_pos.end());
+ std::sort(claimed.begin(), claimed.end());
+ std::vector<unsigned> difference;
+ std::set_difference(op_pos.begin(),
+ op_pos.end(),
+ claimed.begin(),
+ claimed.end(),
+ std::back_inserter(difference));
+ op_pos = difference;
+ if (Trace.isOn("sygus-grammar-normalize-chain"))
+ {
+ Trace("sygus-grammar-normalize-chain")
+ << "OP at " << d_chain_op_pos << "\n"
+ << d_elem_pos.size() << " d_elem_pos: ";
+ for (unsigned i = 0, size = d_elem_pos.size(); i < size; ++i)
+ {
+ Trace("sygus-grammar-normalize-chain") << d_elem_pos[i] << " ";
+ }
+ Trace("sygus-grammar-normalize-chain")
+ << "\n"
+ << op_pos.size() << " remaining op_pos: ";
+ for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
+ {
+ Trace("sygus-grammar-normalize-chain") << op_pos[i] << " ";
+ }
+ Trace("sygus-grammar-normalize-chain") << "\n";
+ }
+ /* Build identity operator and empty callback */
+ Node iden_op =
+ SygusGrammarNorm::getIdOp(TypeNode::fromType(dt.getSygusType()));
+ /* If all operators are claimed, create a monomial */
+ if (nb_op_pos == d_elem_pos.size() + 1)
+ {
+ Trace("sygus-grammar-normalize-chain")
+ << "\tCreating id type for " << d_elem_pos.back() << "\n";
+ /* creates type for element */
+ std::vector<unsigned> tmp;
+ tmp.push_back(d_elem_pos.back());
+ Type t = sygus_norm->normalizeSygusRec(to.d_tn, dt, tmp).toType();
+ /* consumes element */
+ d_elem_pos.pop_back();
+ /* adds to Root: "type" */
+ to.d_ops.push_back(iden_op);
+ to.d_cons_names.push_back("id");
+ to.d_pc.push_back(printer::SygusEmptyPrintCallback::getEmptyPC());
+ /* Identity operators should not increase the size of terms */
+ to.d_weight.push_back(0);
+ to.d_cons_args_t.push_back(std::vector<Type>());
+ to.d_cons_args_t.back().push_back(t);
+ Trace("sygus-grammar-normalize-chain")
+ << "\tAdding " << t << " to " << to.d_unres_tn << "\n";
+ /* adds to Root: "type + Root" */
+ to.d_ops.push_back(nm->operatorOf(PLUS));
+ to.d_cons_names.push_back(kindToString(PLUS));
+ to.d_pc.push_back(nullptr);
+ to.d_weight.push_back(-1);
+ to.d_cons_args_t.push_back(std::vector<Type>());
+ to.d_cons_args_t.back().push_back(t);
+ to.d_cons_args_t.back().push_back(to.d_unres_tn.toType());
+ Trace("sygus-grammar-normalize-chain")
+ << "\tAdding PLUS to " << to.d_unres_tn << " with arg types "
+ << to.d_unres_tn << " and " << t << "\n";
+ }
+ /* In the initial case if not all operators claimed always creates a next */
+ Assert(nb_op_pos != d_elem_pos.size() + 1 || d_elem_pos.size() > 1);
+ /* TODO #1304: consider case in which CHAIN op has different types than
+ to.d_tn */
+ /* If no more elements to chain, finish */
+ if (d_elem_pos.size() == 0)
+ {
+ return;
+ }
+ /* Creates a type do be added to root representing next step in the chain */
+ /* Add + to elems */
+ d_elem_pos.push_back(d_chain_op_pos);
+ if (Trace.isOn("sygus-grammar-normalize-chain"))
+ {
+ Trace("sygus-grammar-normalize-chain")
+ << "\tCreating type for next entry with sygus_ops ";
+ for (unsigned i = 0, size = d_elem_pos.size(); i < size; ++i)
+ {
+ Trace("sygus-grammar-normalize-chain")
+ << dt[d_elem_pos[i]].getSygusOp() << " ";
+ }
+ Trace("sygus-grammar-normalize-chain") << "\n";
+ }
+ /* adds to Root: (\lambda x. x ) Next */
+ to.d_ops.push_back(iden_op);
+ to.d_cons_names.push_back("id_next");
+ to.d_pc.push_back(printer::SygusEmptyPrintCallback::getEmptyPC());
+ to.d_weight.push_back(0);
+ to.d_cons_args_t.push_back(std::vector<Type>());
+ to.d_cons_args_t.back().push_back(
+ sygus_norm->normalizeSygusRec(to.d_tn, dt, d_elem_pos).toType());
+}
+
+std::map<TypeNode, Node> SygusGrammarNorm::d_tn_to_id = {};
+
+/* Traverse the constructors of dt according to the positions in op_pos. Collect
+ * those that fit the kinds established by to_collect. Remove collected operator
+ * positions from op_pos. Accumulate collected positions in collected
+ *
+ * returns true if collected anything
+ */
+std::unique_ptr<SygusGrammarNorm::Transf> SygusGrammarNorm::inferTransf(
+ TypeNode tn, const Datatype& dt, const std::vector<unsigned>& op_pos)
+{
+ NodeManager* nm = NodeManager::currentNM();
+ TypeNode sygus_tn = TypeNode::fromType(dt.getSygusType());
+ Trace("sygus-gnorm") << "Infer transf for " << dt.getName() << "..."
+ << std::endl;
+ Trace("sygus-gnorm") << " #cons = " << op_pos.size() << " / "
+ << dt.getNumConstructors() << std::endl;
+ // look for redundant constructors to drop
+ if (options::sygusMinGrammar() && dt.getNumConstructors() == op_pos.size())
+ {
+ SygusRedundantCons src;
+ src.initialize(d_qe, tn);
+ std::vector<unsigned> rindices;
+ src.getRedundant(rindices);
+ if (!rindices.empty())
+ {
+ Trace("sygus-gnorm") << "...drop transf, " << rindices.size() << "/"
+ << op_pos.size() << " constructors." << std::endl;
+ Assert(rindices.size() < op_pos.size());
+ return std::unique_ptr<Transf>(new TransfDrop(rindices));
+ }
+ }
+
+ // if normalization option is not enabled, we do not infer the transformations
+ // below
+ if (!options::sygusGrammarNorm())
+ {
+ return nullptr;
+ }
+
+ /* TODO #1304: step 1: look for singleton */
+ /* step 2: look for chain */
+ unsigned chain_op_pos = dt.getNumConstructors();
+ std::vector<unsigned> elem_pos;
+ for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
+ {
+ Assert(op_pos[i] < dt.getNumConstructors());
+ Expr sop = dt[op_pos[i]].getSygusOp();
+ /* Collects a chainable operator such as PLUS */
+ if (sop.getKind() == BUILTIN
+ && TransfChain::isChainable(sygus_tn, Node::fromExpr(sop)))
+ {
+ Assert(nm->operatorToKind(Node::fromExpr(sop)) == PLUS);
+ /* TODO #1304: be robust for this case */
+ /* For now only transforms applications whose arguments have the same type
+ * as the root */
+ bool same_type_plus = true;
+ for (const DatatypeConstructorArg& arg : dt[op_pos[i]])
+ {
+ if (TypeNode::fromType(
+ static_cast<SelectorType>(arg.getType()).getRangeType())
+ != tn)
+ {
+ same_type_plus = false;
+ break;
+ }
+ }
+ if (!same_type_plus)
+ {
+ Trace("sygus-grammar-normalize-infer")
+ << "\tFor OP " << PLUS << " did not collecting sop " << sop
+ << " in position " << op_pos[i] << "\n";
+ continue;
+ }
+ Assert(chain_op_pos == dt.getNumConstructors());
+ Trace("sygus-grammar-normalize-infer")
+ << "\tCollecting chainable OP " << sop << " in position " << op_pos[i]
+ << "\n";
+ chain_op_pos = op_pos[i];
+ continue;
+ }
+ /* TODO #1304: check this for each operator */
+ /* Collects elements that are not the identity (e.g. 0 is the id of PLUS) */
+ if (!TransfChain::isId(sygus_tn, nm->operatorOf(PLUS), Node::fromExpr(sop)))
+ {
+ Trace("sygus-grammar-normalize-infer")
+ << "\tCollecting for NON_ID_ELEMS the sop " << sop
+ << " in position " << op_pos[i] << "\n";
+ elem_pos.push_back(op_pos[i]);
+ }
+ }
+ /* Typenode admits a chain transformation for normalization */
+ if (chain_op_pos != dt.getNumConstructors() && !elem_pos.empty())
+ {
+ Trace("sygus-gnorm") << "...chain transf." << std::endl;
+ Trace("sygus-grammar-normalize-infer")
+ << "\tInfering chain transformation\n";
+ return std::unique_ptr<Transf>(new TransfChain(chain_op_pos, elem_pos));
+ }
+ return nullptr;
+}
+
+TypeNode SygusGrammarNorm::normalizeSygusRec(TypeNode tn,
+ const Datatype& dt,
+ std::vector<unsigned>& op_pos)
+{
+ /* Corresponding type node to tn with the given operator positions. To be
+ * retrieved (if cached) or defined (otherwise) */
+ TypeNode unres_tn;
+ if (Trace.isOn("sygus-grammar-normalize-trie"))
+ {
+ Trace("sygus-grammar-normalize-trie")
+ << "\tRecursing on " << tn << " with op_positions ";
+ for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
+ {
+ Trace("sygus-grammar-normalize-trie") << op_pos[i] << " ";
+ }
+ Trace("sygus-grammar-normalize-trie") << "\n";
+ }
+ /* Checks if unresolved type already created (and returns) or creates it
+ * (and then proceeds to definition) */
+ std::sort(op_pos.begin(), op_pos.end());
+ if (d_tries[tn].getOrMakeType(tn, unres_tn, op_pos))
+ {
+ if (Trace.isOn("sygus-grammar-normalize-trie"))
+ {
+ Trace("sygus-grammar-normalize-trie")
+ << "\tTypenode " << tn << " has already been normalized with op_pos ";
+ for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
+ {
+ Trace("sygus-grammar-normalize-trie") << op_pos[i] << " ";
+ }
+ Trace("sygus-grammar-normalize-trie") << " with tn " << unres_tn << "\n";
+ }
+ return unres_tn;
+ }
+ if (Trace.isOn("sygus-grammar-normalize-trie"))
+ {
+ Trace("sygus-grammar-normalize-trie")
+ << "\tTypenode " << tn << " not yet normalized with op_pos ";
+ for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
+ {
+ Trace("sygus-grammar-normalize-trie") << op_pos[i] << " ";
+ }
+ Trace("sygus-grammar-normalize-trie") << "\n";
+ }
+ /* Creates type object for normalization */
+ TypeObject to(tn, unres_tn);
+
+ /* Determine normalization transformation based on sygus type and given
+ * operators */
+ std::unique_ptr<Transf> transformation = inferTransf(tn, dt, op_pos);
+ /* If a transformation was selected, apply it */
+ if (transformation != nullptr)
+ {
+ transformation->buildType(this, to, dt, op_pos);
+ }
+
+ /* Remaining operators are rebuilt as they are */
+ for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
+ {
+ Assert(op_pos[i] < dt.getNumConstructors());
+ to.addConsInfo(this, dt[op_pos[i]]);
+ }
+ /* Build normalize datatype */
+ if (Trace.isOn("sygus-grammar-normalize"))
+ {
+ Trace("sygus-grammar-normalize") << "\nFor positions ";
+ for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
+ {
+ Trace("sygus-grammar-normalize") << op_pos[i] << " ";
+ }
+ Trace("sygus-grammar-normalize") << " and datatype " << dt << " \n";
+ }
+ to.buildDatatype(this, dt);
+ return to.d_unres_tn;
+}
+
+TypeNode SygusGrammarNorm::normalizeSygusRec(TypeNode tn)
+{
+ /* Collect all operators for normalization */
+ const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
+ std::vector<unsigned> op_pos(dt.getNumConstructors());
+ std::iota(op_pos.begin(), op_pos.end(), 0);
+ return normalizeSygusRec(tn, dt, op_pos);
+}
+
+TypeNode SygusGrammarNorm::normalizeSygusType(TypeNode tn, Node sygus_vars)
+{
+ /* Normalize all types in tn */
+ d_sygus_vars = sygus_vars;
+ normalizeSygusRec(tn);
+ /* Resolve created types */
+ Assert(!d_dt_all.empty() && !d_unres_t_all.empty());
+ if (Trace.isOn("sygus-grammar-normalize-build"))
+ {
+ Trace("sygus-grammar-normalize-build")
+ << "making mutual datatyes with datatypes \n";
+ for (unsigned i = 0, size = d_dt_all.size(); i < size; ++i)
+ {
+ Trace("sygus-grammar-normalize-build") << d_dt_all[i];
+ }
+ Trace("sygus-grammar-normalize-build") << " and unresolved types\n";
+ for (const Type& unres_t : d_unres_t_all)
+ {
+ Trace("sygus-grammar-normalize-build") << unres_t << " ";
+ }
+ Trace("sygus-grammar-normalize-build") << "\n";
+ }
+ Assert(d_dt_all.size() == d_unres_t_all.size());
+ std::vector<DatatypeType> types =
+ NodeManager::currentNM()->toExprManager()->mkMutualDatatypeTypes(
+ d_dt_all, d_unres_t_all);
+ Assert(types.size() == d_dt_all.size());
+ /* Clear accumulators */
+ d_dt_all.clear();
+ d_unres_t_all.clear();
+ /* By construction the normalized type node will be the last one considered */
+ return TypeNode::fromType(types.back());
+}
+
+} // namespace quantifiers
+} // namespace theory
+} // namespace CVC4
--- /dev/null
+/********************* */
+/*! \file sygus_grammar_norm.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Haniel Barbosa
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief class for simplifying SyGuS grammars after they are encoded into
+ ** datatypes.
+ **/
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_NORM_H
+#define __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_NORM_H
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "expr/datatype.h"
+#include "expr/node.h"
+#include "expr/node_manager_attributes.h" // for VarNameAttr
+#include "expr/type.h"
+#include "expr/type_node.h"
+#include "theory/quantifiers/term_util.h"
+#include "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+class SygusGrammarNorm;
+
+/** Operator position trie class
+ *
+ * This data structure stores an unresolved type corresponding to the
+ * normalization of a type. This unresolved type is indexed by the positions of
+ * the construtors of the datatype associated with the original type. The list
+ * of positions represent the operators, associated with the respective
+ * considered constructors, that were used for building the unresolved type.
+ *
+ * Example:
+ *
+ * Let A be a type defined by the grammar "A -> x | 0 | 1 | A + A". In its
+ * datatype representation the operator for "x" is in position 0, for "0" in
+ * position "1" and so on. Consider entries (T, [op_1, ..., op_n]) -> T' to
+ * represent that a type T is normalized with operators [op_1, ..., op_n] into
+ * the type T'. For entries
+ *
+ * (A, [x, 0, 1, +]) -> A1
+ * (A, [x, 1, +]) -> A2
+ * (A, [1, +]) -> A3
+ * (A, [0]) -> AZ
+ * (A, [x]) -> AX
+ * (A, [1]) -> AO
+ *
+ * the OpPosTrie T we build for this type is :
+ *
+ * T[A] :
+ * T[A].d_children[0] : AX
+ * T[A].d_children[0].d_children[1] :
+ * T[A].d_children[0].d_children[1].d_children[2] :
+ * T[A].d_children[0].d_children[1].d_children[2].d_children[3] : A1
+ * T[A].d_children[0].d_children[2] :
+ * T[A].d_children[0].d_children[2].d_children[3] : A2
+ * T[A].d_children[1] : AZ
+ * T[A].d_children[2] : AO
+ * T[A].d_children[2].d_children[4] : A3
+ *
+ * Nodes store the types built for the path of positions up to that point, if
+ * any.
+ */
+class OpPosTrie
+{
+ public:
+ /** type retrieval/addition
+ *
+ * if type indexed by the given operator positions is already in the trie then
+ * unres_t becomes the indexed type and true is returned. Otherwise a new type
+ * is created, indexed by the given positions, and assigned to unres_t, with
+ * false being returned.
+ */
+ bool getOrMakeType(TypeNode tn,
+ TypeNode& unres_tn,
+ const std::vector<unsigned>& op_pos,
+ unsigned ind = 0);
+ /** clear all data from this trie */
+ void clear() { d_children.clear(); }
+
+ private:
+ /** the data (only set for the final node of an inserted path) */
+ TypeNode d_unres_tn;
+ /* the children of the trie node */
+ std::map<unsigned, OpPosTrie> d_children;
+}; /* class OpPosTrie */
+
+/** Utility for normalizing SyGuS grammars to avoid spurious enumerations
+ *
+ * Uses the datatype representation of a SyGuS grammar to identify entries that
+ * can normalized in order to have less possible enumerations. An example is
+ * with integer types, e.g.:
+ *
+ * Int -> x | y | Int + Int | 0 | 1 | ite(Bool, Int, Int)
+ *
+ * becomes
+ *
+ * Int0 -> IntZ | Int1
+ * IntZ -> 0
+ * Int1 -> IntX | IntX + Int1 | Int2
+ * IntX -> x
+ * Int2 -> IntY | IntY + Int2 | Int3
+ * IntY -> y
+ * Int3 -> IntO | IntO + Int3 | Int4
+ * IntO -> 1
+ * Int4 -> IntITE | IntITE + Int4
+ * IntITE -> ite(Bool, Int0, Int0)
+ *
+ * TODO: #1304 normalize more complex grammars
+ *
+ * This class also performs more straightforward normalizations, such as
+ * expanding definitions of functions declared with a "define-fun" command.
+ * These lighweight transformations are always applied, independently of the
+ * normalization option being enabled.
+ */
+class SygusGrammarNorm
+{
+ public:
+ SygusGrammarNorm(QuantifiersEngine* qe)
+ : d_qe(qe), d_tds(d_qe->getTermDatabaseSygus())
+ {
+ }
+ ~SygusGrammarNorm() {}
+ /** creates a normalized typenode from a given one.
+ *
+ * In a normalized typenode all typenodes it contains are normalized.
+ * Normalized typenodes can be structurally identicial to their original
+ * counterparts.
+ *
+ * sygus_vars are the input variables for the function to be synthesized,
+ * which are used as input for the built datatypes.
+ *
+ * This is the function that will resolve all types and datatypes built during
+ * normalization. This operation can only be performed after all types
+ * contained in "tn" have been normalized, since the resolution of datatypes
+ * depends on all types involved being defined.
+ */
+ TypeNode normalizeSygusType(TypeNode tn, Node sygus_vars);
+
+ /* Retrives, or, if none, creates, stores and returns, the node for the
+ * identity operator (\lambda x. x) for the given type node */
+ static inline Node getIdOp(TypeNode tn)
+ {
+ auto it = d_tn_to_id.find(tn);
+ if (it == d_tn_to_id.end())
+ {
+ std::vector<Node> vars = {NodeManager::currentNM()->mkBoundVar(tn)};
+ Node n = NodeManager::currentNM()->mkNode(
+ kind::LAMBDA,
+ NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, vars),
+ vars.back());
+ d_tn_to_id[tn] = n;
+ return n;
+ }
+ return it->second;
+ }
+
+ private:
+ /** Keeps the necessary information for bulding a normalized type:
+ *
+ * the original typenode, from which the datatype representation can be
+ * extracted
+ *
+ * the operators, names, print callbacks and list of argument types for each
+ * constructor
+ *
+ * the unresolved type node used as placeholder for references of the yet to
+ * be built normalized type
+ *
+ * a datatype to represent the structure of the type node for the normalized
+ * type
+ */
+ class TypeObject
+ {
+ public:
+ /* Stores the original type node and the unresolved placeholder. The
+ * datatype for the latter is created with the respective name. */
+ TypeObject(TypeNode src_tn, TypeNode unres_tn)
+ : d_tn(src_tn),
+ d_unres_tn(unres_tn),
+ d_dt(Datatype(unres_tn.getAttribute(expr::VarNameAttr())))
+ {
+ }
+ ~TypeObject() {}
+
+ /** adds information in "cons" (operator, name, print callback, argument
+ * types) as it is into "to"
+ *
+ * A side effect of this procedure is to expand the definitions in the sygus
+ * operator of "cons"
+ *
+ * The types of the arguments of "cons" are recursively normalized
+ */
+ void addConsInfo(SygusGrammarNorm* sygus_norm,
+ const DatatypeConstructor& cons);
+
+ /** builds a datatype with the information in the type object
+ *
+ * "dt" is the datatype of the original typenode. It is necessary for
+ * retrieving ancillary information during the datatype building, such as
+ * its sygus type (e.g. Int)
+ *
+ * The built datatype and its unresolved type are saved in the global
+ * accumulators of "sygus_norm"
+ */
+ void buildDatatype(SygusGrammarNorm* sygus_norm, const Datatype& dt);
+
+ //---------- information stored from original type node
+
+ /* The original typenode this TypeObject is built from */
+ TypeNode d_tn;
+
+ //---------- information to build normalized type node
+
+ /* Operators for each constructor. */
+ std::vector<Node> d_ops;
+ /* Names for each constructor. */
+ std::vector<std::string> d_cons_names;
+ /* Print callbacks for each constructor */
+ std::vector<std::shared_ptr<SygusPrintCallback>> d_pc;
+ /* Weights for each constructor */
+ std::vector<int> d_weight;
+ /* List of argument types for each constructor */
+ std::vector<std::vector<Type>> d_cons_args_t;
+ /* Unresolved type node placeholder */
+ TypeNode d_unres_tn;
+ /* Datatype to represent type's structure */
+ Datatype d_dt;
+ }; /* class TypeObject */
+
+ /** Transformation abstract class
+ *
+ * Classes extending this one will define specif transformationst for building
+ * normalized types based on applications of specific operators
+ */
+ class Transf
+ {
+ public:
+ virtual ~Transf() {}
+
+ /** abstract function for building normalized types
+ *
+ * Builds normalized types for the operators specifed by the positions in
+ * op_pos of constructors from dt. The built types are associated with the
+ * given type object and accumulated in the sygus_norm object, whose
+ * utilities for any extra necessary normalization.
+ */
+ virtual void buildType(SygusGrammarNorm* sygus_norm,
+ TypeObject& to,
+ const Datatype& dt,
+ std::vector<unsigned>& op_pos) = 0;
+ }; /* class Transf */
+
+ /** Drop transformation class
+ *
+ * This class builds a type by dropping a set of redundant constructors,
+ * whose indices are given as input to the constructor of this class.
+ */
+ class TransfDrop : public Transf
+ {
+ public:
+ TransfDrop(const std::vector<unsigned>& indices) : d_drop_indices(indices)
+ {
+ }
+ /** build type */
+ void buildType(SygusGrammarNorm* sygus_norm,
+ TypeObject& to,
+ const Datatype& dt,
+ std::vector<unsigned>& op_pos) override;
+
+ private:
+ std::vector<unsigned> d_drop_indices;
+ };
+
+ /** Chain transformation class
+ *
+ * Determines how to build normalized types by chaining the application of one
+ * of its operators. The resulting type should admit the same terms as the
+ * previous one modulo commutativity, associativity and identity of the
+ * neutral element.
+ *
+ * TODO: #1304:
+ * - define this transformation for more than just PLUS for Int.
+ * - improve the building such that elements that should not be entitled a
+ * "link in the chain" (such as 5 in opposition to variables and 1) do not get
+ * one
+ * - consider the case when operator is applied to different types, e.g.:
+ * A -> B + B | x; B -> 0 | 1
+ * - consider the case in which in which the operator occurs nested in an
+ * argument type of itself, e.g.:
+ * A -> (B + B) + B | x; B -> 0 | 1
+ */
+ class TransfChain : public Transf
+ {
+ public:
+ TransfChain(unsigned chain_op_pos, const std::vector<unsigned>& elem_pos)
+ : d_chain_op_pos(chain_op_pos), d_elem_pos(elem_pos){};
+
+ /** builds types encoding a chain in which each link contains a repetition
+ * of the application of the chain operator over a non-identity element
+ *
+ * Example: when considering, over the integers, the operator "+" and the
+ * elemenst "1", "x" and "y", the built chain is e.g.
+ *
+ * x + ... + x + y + ... + y + 1 + ...+ 1
+ *
+ * whose encoding in types would be e.g.
+ *
+ * A -> AX | AX + A | B
+ * AX -> x
+ * B -> BY | BY + B | C
+ * BY -> y
+ * C -> C1 | C1 + C
+ * C1 -> 1
+ *
+ * ++++++++
+ *
+ * The types composing links in the chain are built recursively by invoking
+ * sygus_norm, which caches results and handles the global normalization, on
+ * the operators not used in a given link, which will lead to recalling this
+ * transformation and so on until all operators originally given are
+ * considered.
+ */
+ void buildType(SygusGrammarNorm* sygus_norm,
+ TypeObject& to,
+ const Datatype& dt,
+ std::vector<unsigned>& op_pos) override;
+
+ /** Whether operator is chainable for the type (e.g. PLUS for Int)
+ *
+ * Since the map this function depends on cannot be built statically, this
+ * function first build maps the first time a type is checked. As a
+ * consequence the list of chainabel operators is hardcoded in the map
+ * building.
+ *
+ * TODO: #1304: Cover more types and operators, make this robust to more
+ * complex grammars
+ */
+ static bool isChainable(TypeNode tn, Node op);
+ /* Whether n is the identity for the chain operator of the type (e.g. 1 is
+ * not the identity 0 for PLUS for Int)
+ *
+ * TODO: #1304: Cover more types, make this robust to more complex grammars
+ */
+ static bool isId(TypeNode tn, Node op, Node n);
+
+ private:
+ /* TODO #1304: this should admit more than one, as well as which elements
+ * are associated with which operator */
+ /* Position of chain operator */
+ unsigned d_chain_op_pos;
+ /* Positions (of constructors in the datatype whose type is being
+ * normalized) of elements the chain operator is applied to */
+ std::vector<unsigned> d_elem_pos;
+ /** Specifies for each type node which are its chainable operators
+ *
+ * For example, for Int the map is {OP -> [+]}
+ *
+ * TODO #1304: consider more operators
+ */
+ static std::map<TypeNode, std::vector<Kind>> d_chain_ops;
+ /** Specifies for each type node and chainable operator its identity
+ *
+ * For example, for Int and PLUS the map is {Int -> {+ -> 0}}
+ *
+ * TODO #1304: consider more operators
+ */
+ static std::map<TypeNode, std::map<Kind, Node>> d_chain_op_id;
+
+ }; /* class TransfChain */
+
+ /** reference to quantifier engine */
+ QuantifiersEngine* d_qe;
+ /** sygus term database associated with this utility */
+ TermDbSygus* d_tds;
+ /** List of variable inputs of function-to-synthesize.
+ *
+ * This information is needed in the construction of each datatype
+ * representation of type nodes contained in the type node being normalized
+ */
+ TNode d_sygus_vars;
+ /* Datatypes to be resolved */
+ std::vector<Datatype> d_dt_all;
+ /* Types to be resolved */
+ std::set<Type> d_unres_t_all;
+ /* Associates type nodes with OpPosTries */
+ std::map<TypeNode, OpPosTrie> d_tries;
+ /* Map of type nodes into their identity operators (\lambda x. x) */
+ static std::map<TypeNode, Node> d_tn_to_id;
+
+ /** recursively traverses a typenode normalizing all of its elements
+ *
+ * "tn" is the typenode to be normalized
+ * "dt" is its datatype representation
+ * "op_pos" is the list of positions of construtors of dt that are being
+ * considered for the normalization
+ *
+ * The result of normalizing tn with the respective constructors is cached
+ * with an OpPosTrie. New types and datatypes created during normalization are
+ * accumulated grobally to be later resolved.
+ *
+ * The normalization occurs following some inferred transformation based on
+ * the sygus type (e.g. Int) of tn, and the operators being considered.
+ *
+ * Example: Let A be the type node encoding the grammar
+ *
+ * Int -> x | y | Int + Int | 0 | 1 | ite(Bool, Int, Int)
+ *
+ * and assume all its datatype constructors are being used for
+ * normalization. The inferred normalization transformation will consider the
+ * non-zero elements {x, y, 1, ite(...)} and the operator {+} to build a chain
+ * of monomials, as seen above. The operator for "0" is rebuilt as is (the
+ * default behaviour of operators not selected for transformations).
+ *
+ * recursion depth is limited by the height of the types, which is small
+ */
+ TypeNode normalizeSygusRec(TypeNode tn,
+ const Datatype& dt,
+ std::vector<unsigned>& op_pos);
+
+ /** wrapper for the above function
+ *
+ * invoked when all operators of "tn" are to be considered for normalization
+ */
+ TypeNode normalizeSygusRec(TypeNode tn);
+
+ /** infers a transformation for normalizing dt when allowed to use the
+ * operators in the positions op_pos.
+ *
+ * TODO: #1304: Infer more complex transformations
+ */
+ std::unique_ptr<Transf> inferTransf(TypeNode tn,
+ const Datatype& dt,
+ const std::vector<unsigned>& op_pos);
+}; /* class SygusGrammarNorm */
+
+} // namespace quantifiers
+} // namespace theory
+} // namespace CVC4
+
+#endif
--- /dev/null
+/********************* */
+/*! \file sygus_grammar_red.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief Implementation of sygus_grammar_red
+ **/
+
+#include "theory/quantifiers/sygus/sygus_grammar_red.h"
+
+#include "options/quantifiers_options.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+#include "theory/quantifiers/term_util.h"
+
+using namespace std;
+using namespace CVC4::kind;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+void SygusRedundantCons::initialize(QuantifiersEngine* qe, TypeNode tn)
+{
+ Assert(qe != nullptr);
+ Trace("sygus-red") << "Compute redundant cons for " << tn << std::endl;
+ d_type = tn;
+ Assert(tn.isDatatype());
+ TermDbSygus* tds = qe->getTermDatabaseSygus();
+ tds->registerSygusType(tn);
+ const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
+ Assert(dt.isSygus());
+ TypeNode btn = TypeNode::fromType(dt.getSygusType());
+ for (unsigned i = 0, ncons = dt.getNumConstructors(); i < ncons; i++)
+ {
+ Trace("sygus-red") << " Is " << dt[i].getName() << " a redundant operator?"
+ << std::endl;
+ std::map<int, Node> pre;
+ Node g = tds->mkGeneric(dt, i, pre);
+ Trace("sygus-red-debug") << " ...pre-rewrite : " << g << std::endl;
+ Assert(g.getNumChildren() == dt[i].getNumArgs());
+ d_gen_terms[i] = g;
+ for (unsigned j = 0, nargs = dt[i].getNumArgs(); j < nargs; j++)
+ {
+ pre[j] = g[j];
+ }
+ std::vector<Node> glist;
+ getGenericList(tds, dt, i, 0, pre, glist);
+ // call the extended rewriter
+ bool red = false;
+ for (const Node& gr : glist)
+ {
+ Trace("sygus-red-debug") << " ...variant : " << gr << std::endl;
+ std::map<Node, unsigned>::iterator itg = d_gen_cons.find(gr);
+ if (itg != d_gen_cons.end() && itg->second != i)
+ {
+ red = true;
+ Trace("sygus-red") << " ......redundant, since a variant of " << g
+ << " and " << d_gen_terms[itg->second]
+ << " both rewrite to " << gr << std::endl;
+ break;
+ }
+ else
+ {
+ d_gen_cons[gr] = i;
+ Trace("sygus-red") << " ......not redundant." << std::endl;
+ }
+ }
+ d_sygus_red_status.push_back(red ? 1 : 0);
+ }
+}
+
+void SygusRedundantCons::getRedundant(std::vector<unsigned>& indices)
+{
+ const Datatype& dt = static_cast<DatatypeType>(d_type.toType()).getDatatype();
+ for (unsigned i = 0, ncons = dt.getNumConstructors(); i < ncons; i++)
+ {
+ if (isRedundant(i))
+ {
+ indices.push_back(i);
+ }
+ }
+}
+
+bool SygusRedundantCons::isRedundant(unsigned i)
+{
+ Assert(i < d_sygus_red_status.size());
+ return d_sygus_red_status[i] == 1;
+}
+
+void SygusRedundantCons::getGenericList(TermDbSygus* tds,
+ const Datatype& dt,
+ unsigned c,
+ unsigned index,
+ std::map<int, Node>& pre,
+ std::vector<Node>& terms)
+{
+ if (index == dt[c].getNumArgs())
+ {
+ Node gt = tds->mkGeneric(dt, c, pre);
+ gt = tds->getExtRewriter()->extendedRewrite(gt);
+ terms.push_back(gt);
+ return;
+ }
+ // with no swap
+ getGenericList(tds, dt, c, index + 1, pre, terms);
+ // swapping is exponential, only use for operators with small # args.
+ if (dt[c].getNumArgs() <= 5)
+ {
+ TypeNode atype = tds->getArgType(dt[c], index);
+ for (unsigned s = index + 1, nargs = dt[c].getNumArgs(); s < nargs; s++)
+ {
+ if (tds->getArgType(dt[c], s) == atype)
+ {
+ // swap s and index
+ Node tmp = pre[s];
+ pre[s] = pre[index];
+ pre[index] = tmp;
+ getGenericList(tds, dt, c, index + 1, pre, terms);
+ // revert
+ tmp = pre[s];
+ pre[s] = pre[index];
+ pre[index] = tmp;
+ }
+ }
+ }
+}
+
+} /* CVC4::theory::quantifiers namespace */
+} /* CVC4::theory namespace */
+} /* CVC4 namespace */
--- /dev/null
+/********************* */
+/*! \file sygus_grammar_red.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief sygus_grammar_red
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_RED_H
+#define __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_RED_H
+
+#include <map>
+#include "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+/** SygusRedundantCons
+ *
+ * This class computes the subset of indices of the constructors of a sygus type
+ * that are redundant. To use this class, first call initialize( qe, tn ),
+ * where tn is a sygus tn. Then, use getRedundant and/or isRedundant to get the
+ * indicies of the constructors of tn that are redundant.
+ */
+class SygusRedundantCons
+{
+ public:
+ SygusRedundantCons() {}
+ ~SygusRedundantCons() {}
+ /** register type tn
+ *
+ * qe : pointer to the quantifiers engine,
+ * tn : the (sygus) type to compute redundant constructors for
+ */
+ void initialize(QuantifiersEngine* qe, TypeNode tn);
+ /** Get the indices of the redundant constructors of the register type */
+ void getRedundant(std::vector<unsigned>& indices);
+ /**
+ * This function returns true if the i^th constructor of the registered type
+ * is redundant.
+ */
+ bool isRedundant(unsigned i);
+
+ private:
+ /** the registered type */
+ TypeNode d_type;
+ /** redundant status
+ *
+ * For each constructor, status indicating whether the constructor is
+ * redundant, where:
+ *
+ * 0 : not redundant,
+ * 1 : redundant since another constructor can be used to construct values for
+ * this constructor.
+ *
+ * For example, for grammar:
+ * A -> C > B | B < C | not D
+ * B -> x | y
+ * C -> 0 | 1 | C+C
+ * D -> B >= C
+ * If A is register with this class, then we store may store { 0, 1, 0 },
+ * noting that the second constructor of A can be simulated with the first.
+ * Notice that the third constructor is not considered redundant.
+ */
+ std::vector<int> d_sygus_red_status;
+ /**
+ * Map from constructor indices to the generic term for that constructor,
+ * where the generic term for a constructor is the (canonical) term returned
+ * by a call to TermDbSygus::mkGeneric.
+ */
+ std::map<unsigned, Node> d_gen_terms;
+ /**
+ * Map from the rewritten form of generic terms for constructors of the
+ * registered type to their corresponding constructor index.
+ */
+ std::map<Node, unsigned> d_gen_cons;
+ /** get generic list
+ *
+ * This function constructs all well-typed variants of a term of the form
+ * op( x1, ..., xn )
+ * where op is the builtin operator for dt[c], and xi = pre[i] for i=1,...,n.
+ *
+ * It constructs a list of terms of the form g * sigma, where sigma
+ * is an automorphism on { x1...xn } such that for all xi -> xj in sigma,
+ * the type for arguments i and j of dt[c] are the same. We store this
+ * list of terms in terms.
+ *
+ * This function recurses on the arguments of g, index is the current argument
+ * we are processing, and pre stores the current arguments of
+ *
+ * For example, for a sygus grammar
+ * A -> and( A, A, B )
+ * B -> false
+ * passing arguments such that g=and( x1, x2, x3 ) to this function will add:
+ * and( x1, x2, x3 ) and and( x2, x1, x3 )
+ * to terms.
+ */
+ void getGenericList(TermDbSygus* tds,
+ const Datatype& dt,
+ unsigned c,
+ unsigned index,
+ std::map<int, Node>& pre,
+ std::vector<Node>& terms);
+};
+
+} /* CVC4::theory::quantifiers namespace */
+} /* CVC4::theory namespace */
+} /* CVC4 namespace */
+
+#endif /* __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_RED_H */
--- /dev/null
+/********************* */
+/*! \file sygus_invariance.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief Implementation of techniques for sygus invariance tests.
+ **/
+
+#include "theory/quantifiers/sygus/sygus_invariance.h"
+
+#include "theory/quantifiers/sygus/ce_guided_conjecture.h"
+#include "theory/quantifiers/sygus/sygus_pbe.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+
+using namespace CVC4::kind;
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+void EvalSygusInvarianceTest::init(Node conj, Node var, Node res)
+{
+ d_conj = conj;
+ d_var = var;
+ d_result = res;
+}
+
+Node EvalSygusInvarianceTest::doEvaluateWithUnfolding(TermDbSygus* tds, Node n)
+{
+ return tds->evaluateWithUnfolding(n, d_visited);
+}
+
+bool EvalSygusInvarianceTest::invariant(TermDbSygus* tds, Node nvn, Node x)
+{
+ TNode tnvn = nvn;
+ Node conj_subs = d_conj.substitute(d_var, tnvn);
+ Node conj_subs_unfold = doEvaluateWithUnfolding(tds, conj_subs);
+ Trace("sygus-cref-eval2-debug")
+ << " ...check unfolding : " << conj_subs_unfold << std::endl;
+ Trace("sygus-cref-eval2-debug") << " ......from : " << conj_subs
+ << std::endl;
+ if (conj_subs_unfold == d_result)
+ {
+ Trace("sygus-cref-eval2") << "Evaluation min explain : " << conj_subs
+ << " still evaluates to " << d_result
+ << " regardless of ";
+ Trace("sygus-cref-eval2") << x << std::endl;
+ return true;
+ }
+ return false;
+}
+
+void EquivSygusInvarianceTest::init(
+ TermDbSygus* tds, TypeNode tn, CegConjecture* aconj, Node e, Node bvr)
+{
+ // compute the current examples
+ d_bvr = bvr;
+ if (aconj->getPbe()->hasExamples(e))
+ {
+ d_conj = aconj;
+ d_enum = e;
+ unsigned nex = aconj->getPbe()->getNumExamples(e);
+ for (unsigned i = 0; i < nex; i++)
+ {
+ d_exo.push_back(d_conj->getPbe()->evaluateBuiltin(tn, bvr, e, i));
+ }
+ }
+}
+
+bool EquivSygusInvarianceTest::invariant(TermDbSygus* tds, Node nvn, Node x)
+{
+ TypeNode tn = nvn.getType();
+ Node nbv = tds->sygusToBuiltin(nvn, tn);
+ Node nbvr = tds->getExtRewriter()->extendedRewrite(nbv);
+ Trace("sygus-sb-mexp-debug") << " min-exp check : " << nbv << " -> " << nbvr
+ << std::endl;
+ bool exc_arg = false;
+ // equivalent / singular up to normalization
+ if (nbvr == d_bvr)
+ {
+ // gives the same result : then the explanation for the child is irrelevant
+ exc_arg = true;
+ Trace("sygus-sb-mexp") << "sb-min-exp : " << tds->sygusToBuiltin(nvn)
+ << " is rewritten to " << nbvr;
+ Trace("sygus-sb-mexp") << " regardless of the content of "
+ << tds->sygusToBuiltin(x) << std::endl;
+ }
+ else
+ {
+ if (nbvr.isVar())
+ {
+ TypeNode xtn = x.getType();
+ if (xtn == tn)
+ {
+ Node bx = tds->sygusToBuiltin(x, xtn);
+ Assert(bx.getType() == nbvr.getType());
+ if (nbvr == bx)
+ {
+ Trace("sygus-sb-mexp") << "sb-min-exp : " << tds->sygusToBuiltin(nvn)
+ << " always rewrites to argument " << nbvr
+ << std::endl;
+ // rewrites to the variable : then the explanation of this is
+ // irrelevant as well
+ exc_arg = true;
+ d_bvr = nbvr;
+ }
+ }
+ }
+ }
+ // equivalent under examples
+ if (!exc_arg)
+ {
+ if (!d_enum.isNull())
+ {
+ bool ex_equiv = true;
+ for (unsigned j = 0; j < d_exo.size(); j++)
+ {
+ Node nbvr_ex = d_conj->getPbe()->evaluateBuiltin(tn, nbvr, d_enum, j);
+ if (nbvr_ex != d_exo[j])
+ {
+ ex_equiv = false;
+ break;
+ }
+ }
+ if (ex_equiv)
+ {
+ Trace("sygus-sb-mexp") << "sb-min-exp : " << tds->sygusToBuiltin(nvn);
+ Trace("sygus-sb-mexp")
+ << " is the same w.r.t. examples regardless of the content of "
+ << tds->sygusToBuiltin(x) << std::endl;
+ exc_arg = true;
+ }
+ }
+ }
+ return exc_arg;
+}
+
+bool DivByZeroSygusInvarianceTest::invariant(TermDbSygus* tds, Node nvn, Node x)
+{
+ TypeNode tn = nvn.getType();
+ Node nbv = tds->sygusToBuiltin(nvn, tn);
+ Node nbvr = tds->getExtRewriter()->extendedRewrite(nbv);
+ if (tds->involvesDivByZero(nbvr))
+ {
+ Trace("sygus-sb-mexp") << "sb-min-exp : " << tds->sygusToBuiltin(nvn)
+ << " involves div-by-zero regardless of "
+ << tds->sygusToBuiltin(x) << std::endl;
+ return true;
+ }
+ return false;
+}
+
+void NegContainsSygusInvarianceTest::init(CegConjecture* conj,
+ Node e,
+ std::vector<Node>& exo,
+ std::vector<unsigned>& ncind)
+{
+ if (conj->getPbe()->hasExamples(e))
+ {
+ Assert(conj->getPbe()->getNumExamples(e) == exo.size());
+ d_enum = e;
+ d_exo.insert(d_exo.end(), exo.begin(), exo.end());
+ d_neg_con_indices.insert(
+ d_neg_con_indices.end(), ncind.begin(), ncind.end());
+ d_conj = conj;
+ }
+}
+
+bool NegContainsSygusInvarianceTest::invariant(TermDbSygus* tds,
+ Node nvn,
+ Node x)
+{
+ if (!d_enum.isNull())
+ {
+ TypeNode tn = nvn.getType();
+ Node nbv = tds->sygusToBuiltin(nvn, tn);
+ Node nbvr = tds->getExtRewriter()->extendedRewrite(nbv);
+ // if for any of the examples, it is not contained, then we can exclude
+ for (unsigned i = 0; i < d_neg_con_indices.size(); i++)
+ {
+ unsigned ii = d_neg_con_indices[i];
+ Assert(ii < d_exo.size());
+ Node nbvre = d_conj->getPbe()->evaluateBuiltin(tn, nbvr, d_enum, ii);
+ Node out = d_exo[ii];
+ Node cont =
+ NodeManager::currentNM()->mkNode(kind::STRING_STRCTN, out, nbvre);
+ Trace("sygus-pbe-cterm-debug") << "Check: " << cont << std::endl;
+ Node contr = Rewriter::rewrite(cont);
+ if (contr == tds->d_false)
+ {
+ if (Trace.isOn("sygus-pbe-cterm"))
+ {
+ Trace("sygus-pbe-cterm")
+ << "PBE-cterm : enumerator : do not consider ";
+ Trace("sygus-pbe-cterm") << nbv << " for any "
+ << tds->sygusToBuiltin(x) << " since "
+ << std::endl;
+ Trace("sygus-pbe-cterm") << " PBE-cterm : for input example : ";
+ std::vector<Node> ex;
+ d_conj->getPbe()->getExample(d_enum, ii, ex);
+ for (unsigned j = 0; j < ex.size(); j++)
+ {
+ Trace("sygus-pbe-cterm") << ex[j] << " ";
+ }
+ Trace("sygus-pbe-cterm") << std::endl;
+ Trace("sygus-pbe-cterm")
+ << " PBE-cterm : this rewrites to : " << nbvre << std::endl;
+ Trace("sygus-pbe-cterm")
+ << " PBE-cterm : and is not in output : " << out << std::endl;
+ }
+ return true;
+ }
+ Trace("sygus-pbe-cterm-debug2")
+ << "...check failed, rewrites to : " << contr << std::endl;
+ }
+ }
+ return false;
+}
+
+} /* CVC4::theory::quantifiers namespace */
+} /* CVC4::theory namespace */
+} /* CVC4 namespace */
--- /dev/null
+/********************* */
+/*! \file sygus_invariance.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief sygus invariance tests
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__SYGUS_INVARIANCE_H
+#define __CVC4__THEORY__QUANTIFIERS__SYGUS_INVARIANCE_H
+
+#include <unordered_map>
+#include <vector>
+
+#include "expr/node.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+class TermDbSygus;
+class CegConjecture;
+
+/* SygusInvarianceTest
+*
+* This class is the standard interface for term generalization
+* in SyGuS. Its interface is a single function is_variant,
+* which is a virtual condition for SyGuS terms.
+*
+* The common use case of invariance tests is when constructing
+* minimal explanations for refinement lemmas in the
+* counterexample-guided inductive synthesis (CEGIS) loop.
+* See sygus_explain.h for more details.
+*/
+class SygusInvarianceTest
+{
+ public:
+ virtual ~SygusInvarianceTest() {}
+
+ /** Is nvn invariant with respect to this test ?
+ *
+ * - nvn is the term to check whether it is invariant.
+ * - x is a variable such that the previous call to
+ * is_invariant (if any) was with term nvn_prev, and
+ * nvn is equal to nvn_prev with some subterm
+ * position replaced by x. This is typically used
+ * for debugging only.
+ */
+ bool is_invariant(TermDbSygus* tds, Node nvn, Node x)
+ {
+ if (invariant(tds, nvn, x))
+ {
+ d_update_nvn = nvn;
+ return true;
+ }
+ return false;
+ }
+ /** get updated term */
+ Node getUpdatedTerm() { return d_update_nvn; }
+ /** set updated term */
+ void setUpdatedTerm(Node n) { d_update_nvn = n; }
+ protected:
+ /** result of the node that satisfies this invariant */
+ Node d_update_nvn;
+ /** check whether nvn[ x ] is invariant */
+ virtual bool invariant(TermDbSygus* tds, Node nvn, Node x) = 0;
+};
+
+/** EquivSygusInvarianceTest
+*
+* This class tests whether a term evaluates via evaluation
+* operators in the deep embedding (Section 4 of Reynolds
+* et al. CAV 2015) to fixed term d_result.
+*
+* For example, consider a SyGuS evaluation function eval
+* for a synthesis conjecture with arguments x and y.
+* Notice that the term t = (mult x y) is such that:
+* eval( t, 0, 1 ) ----> 0
+* This test is invariant on the content of the second
+* argument of t, noting that:
+* eval( (mult x _), 0, 1 ) ----> 0
+* as well, via a call to EvalSygusInvarianceTest::invariant.
+*
+* Another example, t = ite( gt( x, y ), x, y ) is such that:
+* eval( t, 2, 3 ) ----> 3
+* This test is invariant on the second child of t, noting:
+* eval( ite( gt( x, y ), _, y ), 2, 3 ) ----> 3
+*/
+class EvalSygusInvarianceTest : public SygusInvarianceTest
+{
+ public:
+ EvalSygusInvarianceTest() {}
+
+ /** initialize this invariance test
+ * This sets d_conj/d_var/d_result, where
+ * we are checking whether:
+ * d_conj { d_var -> n } ----> d_result.
+ * for terms n.
+ */
+ void init(Node conj, Node var, Node res);
+
+ /** do evaluate with unfolding, using the cache of this class */
+ Node doEvaluateWithUnfolding(TermDbSygus* tds, Node n);
+
+ protected:
+ /** does d_conj{ d_var -> nvn } still rewrite to d_result? */
+ bool invariant(TermDbSygus* tds, Node nvn, Node x);
+
+ private:
+ /** the formula we are evaluating */
+ Node d_conj;
+ /** the variable */
+ TNode d_var;
+ /** the result of the evaluation */
+ Node d_result;
+ /** cache of n -> the simplified form of eval( n ) */
+ std::unordered_map<Node, Node, NodeHashFunction> d_visited;
+};
+
+/** EquivSygusInvarianceTest
+*
+* This class tests whether a builtin version of a
+* sygus term is equivalent up to rewriting to a RHS value bvr.
+*
+* For example,
+*
+* ite( t>0, 0, 0 ) + s*0 ----> 0
+*
+* This test is invariant on the condition t>0 and s, since:
+*
+* ite( _, 0, 0 ) + _*0 ----> 0
+*
+* for any values of _.
+*
+* It also manages the case where the rewriting is invariant
+* wrt a finite set of examples occurring in the conjecture.
+* (EX1) : For example if our input examples are:
+* (x,y,z) = (3,2,4), (5,2,6), (3,2,1)
+* On these examples, we have:
+*
+* ite( x>y, z, 0) ---> 4,6,1
+*
+* which is invariant on the second argument:
+*
+* ite( x>y, z, _) ---> 4,6,1
+*
+* For details, see Reynolds et al SYNT 2017.
+*/
+class EquivSygusInvarianceTest : public SygusInvarianceTest
+{
+ public:
+ EquivSygusInvarianceTest() : d_conj(nullptr) {}
+
+ /** initialize this invariance test
+ * tn is the sygus type for e
+ * aconj/e are used for conjecture-specific symmetry breaking
+ * bvr is the builtin version of the right hand side of the rewrite that we
+ * are checking for invariance
+ */
+ void init(
+ TermDbSygus* tds, TypeNode tn, CegConjecture* aconj, Node e, Node bvr);
+
+ protected:
+ /** checks whether the analog of nvn still rewrites to d_bvr */
+ bool invariant(TermDbSygus* tds, Node nvn, Node x);
+
+ private:
+ /** the conjecture associated with the enumerator d_enum */
+ CegConjecture* d_conj;
+ /** the enumerator associated with the term for which this test is for */
+ Node d_enum;
+ /** the RHS of the evaluation */
+ Node d_bvr;
+ /** the result of the examples
+ * In (EX1), this is (4,6,1)
+ */
+ std::vector<Node> d_exo;
+};
+
+/** DivByZeroSygusInvarianceTest
+ *
+ * This class tests whether a sygus term involves
+ * division by zero.
+ *
+ * For example the test for:
+ * ( x + ( y/0 )*2 )
+ * is invariant on the contents of _ below:
+ * ( _ + ( _/0 )*_ )
+ */
+class DivByZeroSygusInvarianceTest : public SygusInvarianceTest
+{
+ public:
+ DivByZeroSygusInvarianceTest() {}
+
+ protected:
+ /** checks whether nvn involves division by zero. */
+ bool invariant(TermDbSygus* tds, Node nvn, Node x);
+};
+
+/** NegContainsSygusInvarianceTest
+*
+* This class is used to construct a minimal shape of a term that cannot
+* be contained in at least one output of an I/O pair.
+*
+* Say our PBE conjecture is:
+*
+* exists f.
+* f( "abc" ) = "abc abc" ^
+* f( "de" ) = "de de"
+*
+* Then, this class is used when there is a candidate solution t[x1]
+* such that either:
+* contains( "abc abc", t["abc"] ) ---> false or
+* contains( "de de", t["de"] ) ---> false
+*
+* It is used to determine whether certain generalizations of t[x1]
+* are still sufficient to falsify one of the above containments.
+*
+* For example:
+*
+* The test for str.++( x1, "d" ) is invariant on its first argument
+* ...since contains( "abc abc", str.++( _, "d" ) ) ---> false
+* The test for str.replace( "de", x1, "b" ) is invariant on its third argument
+* ...since contains( "abc abc", str.replace( "de", "abc", _ ) ) ---> false
+*/
+class NegContainsSygusInvarianceTest : public SygusInvarianceTest
+{
+ public:
+ NegContainsSygusInvarianceTest() : d_conj(nullptr) {}
+
+ /** initialize this invariance test
+ * cpbe is the conjecture utility.
+ * e is the enumerator which we are reasoning about (associated with a synth
+ * fun).
+ * exo is the list of outputs of the PBE conjecture.
+ * ncind is the set of possible indices of the PBE conjecture to check
+ * invariance of non-containment.
+ * For example, in the above example, when t[x1] = "ab", then this
+ * has the index 1 since contains("de de", "ab") ---> false but not
+ * the index 0 since contains("abc abc","ab") ---> true.
+ */
+ void init(CegConjecture* conj,
+ Node e,
+ std::vector<Node>& exo,
+ std::vector<unsigned>& ncind);
+
+ protected:
+ /** checks if contains( out_i, nvn[in_i] ) --> false for some I/O pair i. */
+ bool invariant(TermDbSygus* tds, Node nvn, Node x);
+
+ private:
+ /** The enumerator whose value we are considering in this invariance test */
+ Node d_enum;
+ /** The output examples for the enumerator */
+ std::vector<Node> d_exo;
+ /** The set of I/O pair indices i such that
+ * contains( out_i, nvn[in_i] ) ---> false
+ */
+ std::vector<unsigned> d_neg_con_indices;
+ /** reference to the conjecture associated with this test */
+ CegConjecture* d_conj;
+};
+
+} /* CVC4::theory::quantifiers namespace */
+} /* CVC4::theory namespace */
+} /* CVC4 namespace */
+
+#endif /* __CVC4__THEORY__QUANTIFIERS__SYGUS_INVARIANCE_H */
--- /dev/null
+/********************* */
+/*! \file ce_guided_pbe.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2016 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief utility for processing programming by examples synthesis conjectures
+ **
+ **/
+#include "theory/quantifiers/sygus/sygus_pbe.h"
+
+#include "expr/datatype.h"
+#include "options/quantifiers_options.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+#include "theory/quantifiers/term_util.h"
+#include "theory/datatypes/datatypes_rewriter.h"
+#include "util/random.h"
+
+using namespace CVC4;
+using namespace CVC4::kind;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+void indent( const char * c, int ind ) {
+ if( Trace.isOn(c) ){
+ for( int i=0; i<ind; i++ ){
+ Trace(c) << " ";
+ }
+ }
+}
+
+void print_val( const char * c, std::vector< Node >& vals, bool pol = true ){
+ if( Trace.isOn(c) ){
+ for( unsigned i=0; i<vals.size(); i++ ){
+ //Trace(c) << ( pol ? vals[i] : !vals[i] );
+ Trace(c) << ( ( pol ? vals[i].getConst<bool>() : !vals[i].getConst<bool>() ) ? "1" : "0" );
+ }
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, EnumRole r)
+{
+ switch(r){
+ case enum_invalid: os << "INVALID"; break;
+ case enum_io: os << "IO"; break;
+ case enum_ite_condition: os << "CONDITION"; break;
+ case enum_concat_term: os << "CTERM"; break;
+ default: os << "enum_" << static_cast<unsigned>(r); break;
+ }
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, NodeRole r)
+{
+ switch (r)
+ {
+ case role_equal: os << "equal"; break;
+ case role_string_prefix: os << "string_prefix"; break;
+ case role_string_suffix: os << "string_suffix"; break;
+ case role_ite_condition: os << "ite_condition"; break;
+ default: os << "role_" << static_cast<unsigned>(r); break;
+ }
+ return os;
+}
+
+EnumRole getEnumeratorRoleForNodeRole(NodeRole r)
+{
+ switch (r)
+ {
+ case role_equal: return enum_io; break;
+ case role_string_prefix: return enum_concat_term; break;
+ case role_string_suffix: return enum_concat_term; break;
+ case role_ite_condition: return enum_ite_condition; break;
+ default: break;
+ }
+ return enum_invalid;
+}
+
+std::ostream& operator<<(std::ostream& os, StrategyType st)
+{
+ switch (st)
+ {
+ case strat_ITE: os << "ITE"; break;
+ case strat_CONCAT_PREFIX: os << "CONCAT_PREFIX"; break;
+ case strat_CONCAT_SUFFIX: os << "CONCAT_SUFFIX"; break;
+ case strat_ID: os << "ID"; break;
+ default: os << "strat_" << static_cast<unsigned>(st); break;
+ }
+ return os;
+}
+
+CegConjecturePbe::CegConjecturePbe(QuantifiersEngine* qe, CegConjecture* p)
+ : d_qe(qe),
+ d_parent(p){
+ d_tds = d_qe->getTermDatabaseSygus();
+ d_true = NodeManager::currentNM()->mkConst(true);
+ d_false = NodeManager::currentNM()->mkConst(false);
+ d_is_pbe = false;
+}
+
+CegConjecturePbe::~CegConjecturePbe() {
+
+}
+
+//--------------------------------- collecting finite input/output domain information
+
+void CegConjecturePbe::collectExamples( Node n, std::map< Node, bool >& visited, bool hasPol, bool pol ) {
+ if( visited.find( n )==visited.end() ){
+ visited[n] = true;
+ Node neval;
+ Node n_output;
+ if( n.getKind()==APPLY_UF && n.getNumChildren()>0 ){
+ neval = n;
+ if( hasPol ){
+ n_output = !pol ? d_true : d_false;
+ }
+ }else if( n.getKind()==EQUAL && hasPol && !pol ){
+ for( unsigned r=0; r<2; r++ ){
+ if( n[r].getKind()==APPLY_UF && n[r].getNumChildren()>0 ){
+ neval = n[r];
+ if( n[1-r].isConst() ){
+ n_output = n[1-r];
+ }
+ }
+ }
+ }
+ if( !neval.isNull() ){
+ if( neval.getKind()==APPLY_UF && neval.getNumChildren()>0 ){
+ // is it an evaluation function?
+ if( d_examples.find( neval[0] )!=d_examples.end() ){
+ std::map< Node, bool >::iterator itx = d_examples_invalid.find( neval[0] );
+ if( itx==d_examples_invalid.end() ){
+ //collect example
+ bool success = true;
+ std::vector< Node > ex;
+ for( unsigned j=1; j<neval.getNumChildren(); j++ ){
+ if( !neval[j].isConst() ){
+ success = false;
+ break;
+ }else{
+ ex.push_back( neval[j] );
+ }
+ }
+ if( success ){
+ d_examples[neval[0]].push_back( ex );
+ d_examples_out[neval[0]].push_back( n_output );
+ d_examples_term[neval[0]].push_back( neval );
+ if( n_output.isNull() ){
+ d_examples_out_invalid[neval[0]] = true;
+ }else{
+ Assert( n_output.isConst() );
+ }
+ //finished processing this node
+ return;
+ }else{
+ d_examples_invalid[neval[0]] = true;
+ d_examples_out_invalid[neval[0]] = true;
+ }
+ }
+ }
+ }
+ }
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ bool newHasPol;
+ bool newPol;
+ QuantPhaseReq::getPolarity( n, i, hasPol, pol, newHasPol, newPol );
+ collectExamples( n[i], visited, newHasPol, newPol );
+ }
+ }
+}
+
+void CegConjecturePbe::initialize(Node n,
+ std::vector<Node>& candidates,
+ std::vector<Node>& lemmas)
+{
+ Trace("sygus-pbe") << "Initialize PBE : " << n << std::endl;
+
+ for( unsigned i=0; i<candidates.size(); i++ ){
+ Node v = candidates[i];
+ d_examples[v].clear();
+ d_examples_out[v].clear();
+ d_examples_term[v].clear();
+ }
+
+ std::map< Node, bool > visited;
+ collectExamples( n, visited, true, true );
+
+ for( unsigned i=0; i<candidates.size(); i++ ){
+ Node v = candidates[i];
+ Trace("sygus-pbe") << " examples for " << v << " : ";
+ if( d_examples_invalid.find( v )!=d_examples_invalid.end() ){
+ Trace("sygus-pbe") << "INVALID" << std::endl;
+ }else{
+ Trace("sygus-pbe") << std::endl;
+ for( unsigned j=0; j<d_examples[v].size(); j++ ){
+ Trace("sygus-pbe") << " ";
+ for( unsigned k=0; k<d_examples[v][j].size(); k++ ){
+ Trace("sygus-pbe") << d_examples[v][j][k] << " ";
+ }
+ if( !d_examples_out[v][j].isNull() ){
+ Trace("sygus-pbe") << " -> " << d_examples_out[v][j];
+ }
+ Trace("sygus-pbe") << std::endl;
+ }
+ }
+ }
+
+ //register candidates
+ if( options::sygusUnifCondSol() ){
+ if( candidates.size()==1 ){// conditional solutions for multiple function conjectures TODO?
+ // collect a pool of types over which we will enumerate terms
+ Node c = candidates[0];
+ //the candidate must be input/output examples
+ if( d_examples_out_invalid.find( c )==d_examples_out_invalid.end() ){
+ Assert( d_examples.find( c )!=d_examples.end() );
+ Trace("sygus-unif") << "It is input/output examples..." << std::endl;
+ TypeNode ctn = c.getType();
+ d_cinfo[c].initialize( c );
+ // collect the enumerator types / form the strategy
+ collectEnumeratorTypes(c, ctn, role_equal);
+ // if we have non-trivial strategies, then use pbe
+ if( d_cinfo[c].isNonTrivial() ){
+ // static learning of redundant constructors
+ staticLearnRedundantOps( c, lemmas );
+ d_is_pbe = true;
+ }
+ }
+ }
+ }
+ if( !d_is_pbe ){
+ Trace("sygus-unif") << "Do not do PBE optimizations, register..." << std::endl;
+ for( unsigned i=0; i<candidates.size(); i++ ){
+ d_qe->getTermDatabaseSygus()->registerEnumerator(
+ candidates[i], candidates[i], d_parent);
+ }
+ }
+}
+
+Node CegConjecturePbe::PbeTrie::addPbeExample(TypeNode etn, Node e, Node b,
+ CegConjecturePbe* cpbe,
+ unsigned index, unsigned ntotal) {
+ if (index == ntotal) {
+ // lazy child holds the leaf data
+ if (d_lazy_child.isNull()) {
+ d_lazy_child = b;
+ }
+ return d_lazy_child;
+ } else {
+ std::vector<Node> ex;
+ if (d_children.empty()) {
+ if (d_lazy_child.isNull()) {
+ d_lazy_child = b;
+ return d_lazy_child;
+ } else {
+ // evaluate the lazy child
+ Assert(cpbe->d_examples.find(e) != cpbe->d_examples.end());
+ Assert(index < cpbe->d_examples[e].size());
+ ex = cpbe->d_examples[e][index];
+ addPbeExampleEval(etn, e, d_lazy_child, ex, cpbe, index, ntotal);
+ Assert(!d_children.empty());
+ d_lazy_child = Node::null();
+ }
+ } else {
+ Assert(cpbe->d_examples.find(e) != cpbe->d_examples.end());
+ Assert(index < cpbe->d_examples[e].size());
+ ex = cpbe->d_examples[e][index];
+ }
+ return addPbeExampleEval(etn, e, b, ex, cpbe, index, ntotal);
+ }
+}
+
+Node CegConjecturePbe::PbeTrie::addPbeExampleEval(TypeNode etn, Node e, Node b,
+ std::vector<Node>& ex,
+ CegConjecturePbe* cpbe,
+ unsigned index,
+ unsigned ntotal) {
+ Node eb = cpbe->d_tds->evaluateBuiltin(etn, b, ex);
+ return d_children[eb].addPbeExample(etn, e, b, cpbe, index + 1, ntotal);
+}
+
+bool CegConjecturePbe::hasExamples(Node e) {
+ if (isPbe()) {
+ e = d_tds->getSynthFunForEnumerator(e);
+ Assert(!e.isNull());
+ std::map<Node, bool>::iterator itx = d_examples_invalid.find(e);
+ if (itx == d_examples_invalid.end()) {
+ return d_examples.find(e) != d_examples.end();
+ }
+ }
+ return false;
+}
+
+unsigned CegConjecturePbe::getNumExamples(Node e) {
+ e = d_tds->getSynthFunForEnumerator(e);
+ Assert(!e.isNull());
+ std::map<Node, std::vector<std::vector<Node> > >::iterator it =
+ d_examples.find(e);
+ if (it != d_examples.end()) {
+ return it->second.size();
+ } else {
+ return 0;
+ }
+}
+
+void CegConjecturePbe::getExample(Node e, unsigned i, std::vector<Node>& ex) {
+ e = d_tds->getSynthFunForEnumerator(e);
+ Assert(!e.isNull());
+ std::map<Node, std::vector<std::vector<Node> > >::iterator it =
+ d_examples.find(e);
+ if (it != d_examples.end()) {
+ Assert(i < it->second.size());
+ ex.insert(ex.end(), it->second[i].begin(), it->second[i].end());
+ } else {
+ Assert(false);
+ }
+}
+
+Node CegConjecturePbe::getExampleOut(Node e, unsigned i) {
+ e = d_tds->getSynthFunForEnumerator(e);
+ Assert(!e.isNull());
+ std::map<Node, std::vector<Node> >::iterator it = d_examples_out.find(e);
+ if (it != d_examples_out.end()) {
+ Assert(i < it->second.size());
+ return it->second[i];
+ } else {
+ Assert(false);
+ return Node::null();
+ }
+}
+
+Node CegConjecturePbe::addSearchVal(TypeNode tn, Node e, Node bvr) {
+ Assert(isPbe());
+ Assert(!e.isNull());
+ e = d_tds->getSynthFunForEnumerator(e);
+ Assert(!e.isNull());
+ std::map<Node, bool>::iterator itx = d_examples_invalid.find(e);
+ if (itx == d_examples_invalid.end()) {
+ unsigned nex = d_examples[e].size();
+ Node ret = d_pbe_trie[e][tn].addPbeExample(tn, e, bvr, this, 0, nex);
+ Assert(ret.getType() == bvr.getType());
+ return ret;
+ }
+ return Node::null();
+}
+
+Node CegConjecturePbe::evaluateBuiltin(TypeNode tn, Node bn, Node e,
+ unsigned i) {
+ e = d_tds->getSynthFunForEnumerator(e);
+ Assert(!e.isNull());
+ std::map<Node, bool>::iterator itx = d_examples_invalid.find(e);
+ if (itx == d_examples_invalid.end()) {
+ std::map<Node, std::vector<std::vector<Node> > >::iterator it =
+ d_examples.find(e);
+ if (it != d_examples.end()) {
+ Assert(i < it->second.size());
+ return d_tds->evaluateBuiltin(tn, bn, it->second[i]);
+ }
+ }
+ return Rewriter::rewrite(bn);
+}
+
+// ----------------------------- establishing enumeration types
+
+void CegConjecturePbe::registerEnumerator(
+ Node et, Node c, TypeNode tn, EnumRole enum_role, bool inSearch)
+{
+ if (d_einfo.find(et) == d_einfo.end())
+ {
+ Trace("sygus-unif-debug")
+ << "...register " << et << " for "
+ << ((DatatypeType)tn.toType()).getDatatype().getName();
+ Trace("sygus-unif-debug") << ", role = " << enum_role
+ << ", in search = " << inSearch << std::endl;
+ d_einfo[et].initialize(c, enum_role);
+ // if we are actually enumerating this (could be a compound node in the
+ // strategy)
+ if (inSearch)
+ {
+ std::map<TypeNode, Node>::iterator itn =
+ d_cinfo[c].d_search_enum.find(tn);
+ if (itn == d_cinfo[c].d_search_enum.end())
+ {
+ // use this for the search
+ d_cinfo[c].d_search_enum[tn] = et;
+ d_cinfo[c].d_esym_list.push_back(et);
+ d_einfo[et].d_enum_slave.push_back(et);
+ // register measured term with database
+ d_qe->getTermDatabaseSygus()->registerEnumerator(et, c, d_parent, true);
+ d_einfo[et].d_active_guard =
+ d_qe->getTermDatabaseSygus()->getActiveGuardForEnumerator(et);
+ }
+ else
+ {
+ Trace("sygus-unif-debug") << "Make " << et << " a slave of "
+ << itn->second << std::endl;
+ d_einfo[itn->second].d_enum_slave.push_back(et);
+ }
+ }
+ }
+}
+
+void CegConjecturePbe::collectEnumeratorTypes(Node e,
+ TypeNode tn,
+ NodeRole nrole)
+{
+ NodeManager* nm = NodeManager::currentNM();
+ if (d_cinfo[e].d_tinfo.find(tn) == d_cinfo[e].d_tinfo.end())
+ {
+ // register type
+ Trace("sygus-unif") << "Register enumerating type : " << tn << std::endl;
+ d_cinfo[e].initializeType( tn );
+ }
+ EnumTypeInfo& eti = d_cinfo[e].d_tinfo[tn];
+ std::map<NodeRole, StrategyNode>::iterator itsn = eti.d_snodes.find(nrole);
+ if (itsn != eti.d_snodes.end())
+ {
+ // already initialized
+ return;
+ }
+ StrategyNode& snode = eti.d_snodes[nrole];
+
+ // get the enumerator for this
+ EnumRole erole = getEnumeratorRoleForNodeRole(nrole);
+
+ Node ee;
+ std::map<EnumRole, Node>::iterator iten = eti.d_enum.find(erole);
+ if (iten == eti.d_enum.end())
+ {
+ ee = nm->mkSkolem("ee", tn);
+ eti.d_enum[erole] = ee;
+ Trace("sygus-unif-debug")
+ << "...enumerator " << ee << " for "
+ << ((DatatypeType)tn.toType()).getDatatype().getName()
+ << ", role = " << erole << std::endl;
+ }
+ else
+ {
+ ee = iten->second;
+ }
+
+ // roles that we do not recurse on
+ if (nrole == role_ite_condition)
+ {
+ Trace("sygus-unif-debug") << "...this register (non-io)" << std::endl;
+ registerEnumerator(ee, e, tn, erole, true);
+ return;
+ }
+
+ // look at information on how we will construct solutions for this type
+ Assert(tn.isDatatype());
+ const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
+ Assert(dt.isSygus());
+
+ std::map<Node, std::vector<StrategyType> > cop_to_strat;
+ std::map<Node, unsigned> cop_to_cindex;
+ std::map<Node, std::map<unsigned, Node> > cop_to_child_templ;
+ std::map<Node, std::map<unsigned, Node> > cop_to_child_templ_arg;
+ std::map<Node, std::vector<unsigned> > cop_to_carg_list;
+ std::map<Node, std::vector<TypeNode> > cop_to_child_types;
+ std::map<Node, std::vector<Node> > cop_to_sks;
+
+ // whether we will enumerate the current type
+ bool search_this = false;
+ for (unsigned j = 0, ncons = dt.getNumConstructors(); j < ncons; j++)
+ {
+ Node cop = Node::fromExpr(dt[j].getConstructor());
+ Node op = Node::fromExpr(dt[j].getSygusOp());
+ Trace("sygus-unif-debug") << "--- Infer strategy from " << cop
+ << " with sygus op " << op << "..." << std::endl;
+
+ // expand the evaluation to see if this constuctor induces a strategy
+ std::vector<Node> utchildren;
+ utchildren.push_back(cop);
+ std::vector<Node> sks;
+ std::vector<TypeNode> sktns;
+ for (unsigned k = 0, nargs = dt[j].getNumArgs(); k < nargs; k++)
+ {
+ Type t = dt[j][k].getRangeType();
+ TypeNode ttn = TypeNode::fromType(t);
+ Node kv = nm->mkSkolem("ut", ttn);
+ sks.push_back(kv);
+ cop_to_sks[cop].push_back(kv);
+ sktns.push_back(ttn);
+ utchildren.push_back(kv);
+ }
+ Node ut = nm->mkNode(APPLY_CONSTRUCTOR, utchildren);
+ std::vector<Node> echildren;
+ echildren.push_back(Node::fromExpr(dt.getSygusEvaluationFunc()));
+ echildren.push_back(ut);
+ Node sbvl = Node::fromExpr(dt.getSygusVarList());
+ for (const Node& sbv : sbvl)
+ {
+ echildren.push_back(sbv);
+ }
+ Node eut = nm->mkNode(APPLY_UF, echildren);
+ Trace("sygus-unif-debug2") << " Test evaluation of " << eut << "..."
+ << std::endl;
+ eut = d_qe->getTermDatabaseSygus()->unfold(eut);
+ Trace("sygus-unif-debug2") << " ...got " << eut;
+ Trace("sygus-unif-debug2") << ", type : " << eut.getType() << std::endl;
+
+ // candidate strategy
+ if (eut.getKind() == ITE)
+ {
+ cop_to_strat[cop].push_back(strat_ITE);
+ }
+ else if (eut.getKind() == STRING_CONCAT)
+ {
+ if (nrole != role_string_suffix)
+ {
+ cop_to_strat[cop].push_back(strat_CONCAT_PREFIX);
+ }
+ if (nrole != role_string_prefix)
+ {
+ cop_to_strat[cop].push_back(strat_CONCAT_SUFFIX);
+ }
+ }
+ else if (dt[j].isSygusIdFunc())
+ {
+ cop_to_strat[cop].push_back(strat_ID);
+ }
+
+ // the kinds for which there is a strategy
+ if (cop_to_strat.find(cop) != cop_to_strat.end())
+ {
+ // infer an injection from the arguments of the datatype
+ std::map<unsigned, unsigned> templ_injection;
+ std::vector<Node> vs;
+ std::vector<Node> ss;
+ std::map<Node, unsigned> templ_var_index;
+ for (unsigned k = 0, sksize = sks.size(); k < sksize; k++)
+ {
+ Assert(sks[k].getType().isDatatype());
+ const Datatype& cdt =
+ static_cast<DatatypeType>(sks[k].getType().toType()).getDatatype();
+ echildren[0] = Node::fromExpr(cdt.getSygusEvaluationFunc());
+ echildren[1] = sks[k];
+ Trace("sygus-unif-debug2") << "...set eval dt to " << sks[k]
+ << std::endl;
+ Node esk = nm->mkNode(APPLY_UF, echildren);
+ vs.push_back(esk);
+ Node tvar = nm->mkSkolem("templ", esk.getType());
+ templ_var_index[tvar] = k;
+ Trace("sygus-unif-debug2") << "* template inference : looking for "
+ << tvar << " for arg " << k << std::endl;
+ ss.push_back(tvar);
+ Trace("sygus-unif-debug2") << "* substitute : " << esk << " -> " << tvar
+ << std::endl;
+ }
+ eut = eut.substitute(vs.begin(), vs.end(), ss.begin(), ss.end());
+ Trace("sygus-unif-debug2") << "Constructor " << j << ", base term is "
+ << eut << std::endl;
+ std::map<unsigned, Node> test_args;
+ if (dt[j].isSygusIdFunc())
+ {
+ test_args[0] = eut;
+ }
+ else
+ {
+ for (unsigned k = 0, size = eut.getNumChildren(); k < size; k++)
+ {
+ test_args[k] = eut[k];
+ }
+ }
+
+ // TODO : prefix grouping prefix/suffix
+ bool isAssoc = TermUtil::isAssoc(eut.getKind());
+ Trace("sygus-unif-debug2") << eut.getKind() << " isAssoc = " << isAssoc
+ << std::endl;
+ std::map<unsigned, std::vector<unsigned> > assoc_combine;
+ std::vector<unsigned> assoc_waiting;
+ int assoc_last_valid_index = -1;
+ for (std::pair<const unsigned, Node>& ta : test_args)
+ {
+ unsigned k = ta.first;
+ Node eut_c = ta.second;
+ // success if we can find a injection from args to sygus args
+ if (!inferTemplate(k, eut_c, templ_var_index, templ_injection))
+ {
+ Trace("sygus-unif-debug")
+ << "...fail: could not find injection (range)." << std::endl;
+ cop_to_strat.erase(cop);
+ break;
+ }
+ std::map<unsigned, unsigned>::iterator itti = templ_injection.find(k);
+ if (itti != templ_injection.end())
+ {
+ // if associative, combine arguments if it is the same variable
+ if (isAssoc && assoc_last_valid_index >= 0
+ && itti->second == templ_injection[assoc_last_valid_index])
+ {
+ templ_injection.erase(k);
+ assoc_combine[assoc_last_valid_index].push_back(k);
+ }
+ else
+ {
+ assoc_last_valid_index = (int)k;
+ if (!assoc_waiting.empty())
+ {
+ assoc_combine[k].insert(assoc_combine[k].end(),
+ assoc_waiting.begin(),
+ assoc_waiting.end());
+ assoc_waiting.clear();
+ }
+ assoc_combine[k].push_back(k);
+ }
+ }
+ else
+ {
+ // a ground argument
+ if (!isAssoc)
+ {
+ Trace("sygus-unif-debug")
+ << "...fail: could not find injection (functional)."
+ << std::endl;
+ cop_to_strat.erase(cop);
+ break;
+ }
+ else
+ {
+ if (assoc_last_valid_index >= 0)
+ {
+ assoc_combine[assoc_last_valid_index].push_back(k);
+ }
+ else
+ {
+ assoc_waiting.push_back(k);
+ }
+ }
+ }
+ }
+ if (cop_to_strat.find(cop) != cop_to_strat.end())
+ {
+ // construct the templates
+ if (!assoc_waiting.empty())
+ {
+ // could not find a way to fit some arguments into injection
+ cop_to_strat.erase(cop);
+ }
+ else
+ {
+ for (std::pair<const unsigned, Node>& ta : test_args)
+ {
+ unsigned k = ta.first;
+ Trace("sygus-unif-debug2") << "- processing argument " << k << "..."
+ << std::endl;
+ if (templ_injection.find(k) != templ_injection.end())
+ {
+ unsigned sk_index = templ_injection[k];
+ if (std::find(cop_to_carg_list[cop].begin(),
+ cop_to_carg_list[cop].end(),
+ sk_index)
+ == cop_to_carg_list[cop].end())
+ {
+ cop_to_carg_list[cop].push_back(sk_index);
+ }else{
+ Trace("sygus-unif-debug") << "...fail: duplicate argument used"
+ << std::endl;
+ cop_to_strat.erase(cop);
+ break;
+ }
+ // also store the template information, if necessary
+ Node teut;
+ if (isAssoc)
+ {
+ std::vector<unsigned>& ac = assoc_combine[k];
+ Assert(!ac.empty());
+ std::vector<Node> children;
+ for (unsigned ack = 0, size_ac = ac.size(); ack < size_ac;
+ ack++)
+ {
+ children.push_back(eut[ac[ack]]);
+ }
+ teut = children.size() == 1
+ ? children[0]
+ : nm->mkNode(eut.getKind(), children);
+ teut = Rewriter::rewrite(teut);
+ }
+ else
+ {
+ teut = ta.second;
+ }
+
+ if (!teut.isVar())
+ {
+ cop_to_child_templ[cop][k] = teut;
+ cop_to_child_templ_arg[cop][k] = ss[sk_index];
+ Trace("sygus-unif-debug")
+ << " Arg " << k << " (template : " << teut << " arg "
+ << ss[sk_index] << "), index " << sk_index << std::endl;
+ }
+ else
+ {
+ Trace("sygus-unif-debug") << " Arg " << k << ", index "
+ << sk_index << std::endl;
+ Assert(teut == ss[sk_index]);
+ }
+ }
+ else
+ {
+ Assert(isAssoc);
+ }
+ }
+ }
+ }
+ }
+ if (cop_to_strat.find(cop) == cop_to_strat.end())
+ {
+ Trace("sygus-unif") << "...constructor " << cop
+ << " does not correspond to a strategy." << std::endl;
+ search_this = true;
+ }
+ else
+ {
+ Trace("sygus-unif") << "-> constructor " << cop
+ << " matches strategy for " << eut.getKind() << "..."
+ << std::endl;
+ // collect children types
+ for (unsigned k = 0, size = cop_to_carg_list[cop].size(); k < size; k++)
+ {
+ TypeNode tn = sktns[cop_to_carg_list[cop][k]];
+ Trace("sygus-unif-debug")
+ << " Child type " << k << " : "
+ << static_cast<DatatypeType>(tn.toType()).getDatatype().getName()
+ << std::endl;
+ cop_to_child_types[cop].push_back(tn);
+ }
+ }
+ }
+
+ // check whether we should also enumerate the current type
+ Trace("sygus-unif-debug2") << " register this enumerator..." << std::endl;
+ registerEnumerator(ee, e, tn, erole, search_this);
+
+ if (cop_to_strat.empty())
+ {
+ Trace("sygus-unif") << "...consider " << dt.getName() << " a basic type"
+ << std::endl;
+ }
+ else
+ {
+ for (std::pair<const Node, std::vector<StrategyType> >& cstr : cop_to_strat)
+ {
+ Node cop = cstr.first;
+ Trace("sygus-unif-debug") << "Constructor " << cop << " has "
+ << cstr.second.size() << " strategies..."
+ << std::endl;
+ for (unsigned s = 0, ssize = cstr.second.size(); s < ssize; s++)
+ {
+ EnumTypeInfoStrat* cons_strat = new EnumTypeInfoStrat;
+ StrategyType strat = cstr.second[s];
+
+ cons_strat->d_this = strat;
+ cons_strat->d_cons = cop;
+ Trace("sygus-unif-debug") << "Process strategy #" << s
+ << " for operator : " << cop << " : " << strat
+ << std::endl;
+ Assert(cop_to_child_types.find(cop) != cop_to_child_types.end());
+ std::vector<TypeNode>& childTypes = cop_to_child_types[cop];
+ Assert(cop_to_carg_list.find(cop) != cop_to_carg_list.end());
+ std::vector<unsigned>& cargList = cop_to_carg_list[cop];
+
+ std::vector<Node> sol_templ_children;
+ sol_templ_children.resize(cop_to_sks[cop].size());
+
+ for (unsigned j = 0, csize = childTypes.size(); j < csize; j++)
+ {
+ // calculate if we should allocate a new enumerator : should be true
+ // if we have a new role
+ NodeRole nrole_c = nrole;
+ if (strat == strat_ITE)
+ {
+ if (j == 0)
+ {
+ nrole_c = role_ite_condition;
+ }
+ }
+ else if (strat == strat_CONCAT_PREFIX)
+ {
+ if ((j + 1) < childTypes.size())
+ {
+ nrole_c = role_string_prefix;
+ }
+ }
+ else if (strat == strat_CONCAT_SUFFIX)
+ {
+ if (j > 0)
+ {
+ nrole_c = role_string_suffix;
+ }
+ }
+ // in all other cases, role is same as parent
+
+ // register the child type
+ TypeNode ct = childTypes[j];
+ Node csk = cop_to_sks[cop][cargList[j]];
+ cons_strat->d_sol_templ_args.push_back(csk);
+ sol_templ_children[cargList[j]] = csk;
+
+ EnumRole erole_c = getEnumeratorRoleForNodeRole(nrole_c);
+ // make the enumerator
+ Node et;
+ if (cop_to_child_templ[cop].find(j) != cop_to_child_templ[cop].end())
+ {
+ // it is templated, allocate a fresh variable
+ et = nm->mkSkolem("et", ct);
+ Trace("sygus-unif-debug")
+ << "...enumerate " << et << " of type "
+ << ((DatatypeType)ct.toType()).getDatatype().getName();
+ Trace("sygus-unif-debug")
+ << " for arg " << j << " of "
+ << ((DatatypeType)tn.toType()).getDatatype().getName()
+ << std::endl;
+ registerEnumerator(et, e, ct, erole_c, true);
+ d_einfo[et].d_template = cop_to_child_templ[cop][j];
+ d_einfo[et].d_template_arg = cop_to_child_templ_arg[cop][j];
+ Assert(!d_einfo[et].d_template.isNull());
+ Assert(!d_einfo[et].d_template_arg.isNull());
+ }
+ else
+ {
+ Trace("sygus-unif-debug")
+ << "...child type enumerate "
+ << ((DatatypeType)ct.toType()).getDatatype().getName()
+ << ", node role = " << nrole_c << std::endl;
+ collectEnumeratorTypes(e, ct, nrole_c);
+ // otherwise use the previous
+ Assert(d_cinfo[e].d_tinfo[ct].d_enum.find(erole_c)
+ != d_cinfo[e].d_tinfo[ct].d_enum.end());
+ et = d_cinfo[e].d_tinfo[ct].d_enum[erole_c];
+ }
+ Trace("sygus-unif-debug") << "Register child enumerator " << et
+ << ", arg " << j << " of " << cop
+ << ", role = " << erole_c << std::endl;
+ Assert(!et.isNull());
+ cons_strat->d_cenum.push_back(std::pair<Node, NodeRole>(et, nrole_c));
+ }
+ // children that are unused in the strategy can be arbitrary
+ for (unsigned j = 0, stsize = sol_templ_children.size(); j < stsize;
+ j++)
+ {
+ if (sol_templ_children[j].isNull())
+ {
+ sol_templ_children[j] = cop_to_sks[cop][j].getType().mkGroundTerm();
+ }
+ }
+ sol_templ_children.insert(sol_templ_children.begin(), cop);
+ cons_strat->d_sol_templ =
+ nm->mkNode(APPLY_CONSTRUCTOR, sol_templ_children);
+ if (strat == strat_CONCAT_SUFFIX)
+ {
+ std::reverse(cons_strat->d_cenum.begin(), cons_strat->d_cenum.end());
+ std::reverse(cons_strat->d_sol_templ_args.begin(),
+ cons_strat->d_sol_templ_args.end());
+ }
+ if (Trace.isOn("sygus-unif"))
+ {
+ Trace("sygus-unif") << "Initialized strategy " << strat;
+ Trace("sygus-unif") << " for " << ((DatatypeType)tn.toType()).getDatatype().getName() << ", operator " << cop;
+ Trace("sygus-unif") << ", #children = " << cons_strat->d_cenum.size()
+ << ", solution template = (lambda ( ";
+ for (const Node& targ : cons_strat->d_sol_templ_args)
+ {
+ Trace("sygus-unif") << targ << " ";
+ }
+ Trace("sygus-unif") << ") " << cons_strat->d_sol_templ << ")";
+ Trace("sygus-unif") << std::endl;
+ }
+ // make the strategy
+ snode.d_strats.push_back(cons_strat);
+ }
+ }
+ }
+}
+
+bool CegConjecturePbe::inferTemplate( unsigned k, Node n, std::map< Node, unsigned >& templ_var_index, std::map< unsigned, unsigned >& templ_injection ){
+ if( n.getNumChildren()==0 ){
+ std::map< Node, unsigned >::iterator itt = templ_var_index.find( n );
+ if( itt!=templ_var_index.end() ){
+ unsigned kk = itt->second;
+ std::map< unsigned, unsigned >::iterator itti = templ_injection.find( k );
+ if( itti==templ_injection.end() ){
+ Trace("sygus-unif-debug") << "...set template injection " << k << " -> " << kk << std::endl;
+ templ_injection[k] = kk;
+ }else if( itti->second!=kk ){
+ // two distinct variables in this term, we fail
+ return false;
+ }
+ }
+ return true;
+ }else{
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ if( !inferTemplate( k, n[i], templ_var_index, templ_injection ) ){
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void CegConjecturePbe::staticLearnRedundantOps( Node c, std::vector< Node >& lemmas ) {
+ for( unsigned i=0; i<d_cinfo[c].d_esym_list.size(); i++ ){
+ Node e = d_cinfo[c].d_esym_list[i];
+ std::map< Node, EnumInfo >::iterator itn = d_einfo.find( e );
+ Assert( itn!=d_einfo.end() );
+ // see if there is anything we can eliminate
+ Trace("sygus-unif") << "* Search enumerator #" << i << " : type " << ((DatatypeType)e.getType().toType()).getDatatype().getName() << " : ";
+ Trace("sygus-unif") << e << " has " << itn->second.d_enum_slave.size() << " slaves:" << std::endl;
+ for( unsigned j=0; j<itn->second.d_enum_slave.size(); j++ ){
+ Node es = itn->second.d_enum_slave[j];
+ std::map< Node, EnumInfo >::iterator itns = d_einfo.find( es );
+ Assert( itns!=d_einfo.end() );
+ Trace("sygus-unif") << " " << es << ", role = " << itns->second.getRole()
+ << std::endl;
+ }
+ }
+ Trace("sygus-unif") << std::endl;
+ Trace("sygus-unif") << "Strategy for candidate " << c << " is : " << std::endl;
+ std::map<Node, std::map<NodeRole, bool> > visited;
+ std::map<Node, std::map<unsigned, bool> > needs_cons;
+ staticLearnRedundantOps(c,
+ d_cinfo[c].getRootEnumerator(),
+ role_equal,
+ visited,
+ needs_cons,
+ 0,
+ false);
+ // now, check the needs_cons map
+ for (std::pair<const Node, std::map<unsigned, bool> >& nce : needs_cons)
+ {
+ Node em = nce.first;
+ const Datatype& dt =
+ static_cast<DatatypeType>(em.getType().toType()).getDatatype();
+ for (std::pair<const unsigned, bool>& nc : nce.second)
+ {
+ Assert(nc.first < dt.getNumConstructors());
+ if (!nc.second)
+ {
+ Node tst =
+ datatypes::DatatypesRewriter::mkTester(em, nc.first, dt).negate();
+ if (std::find(lemmas.begin(), lemmas.end(), tst) == lemmas.end())
+ {
+ Trace("sygus-unif") << "...can exclude based on : " << tst
+ << std::endl;
+ lemmas.push_back(tst);
+ }
+ }
+ }
+ }
+}
+
+void CegConjecturePbe::staticLearnRedundantOps(
+ Node c,
+ Node e,
+ NodeRole nrole,
+ std::map<Node, std::map<NodeRole, bool> >& visited,
+ std::map<Node, std::map<unsigned, bool> >& needs_cons,
+ int ind,
+ bool isCond)
+{
+ std::map< Node, EnumInfo >::iterator itn = d_einfo.find( e );
+ Assert( itn!=d_einfo.end() );
+
+ if (visited[e].find(nrole) == visited[e].end()
+ || (isCond && !itn->second.isConditional()))
+ {
+ visited[e][nrole] = true;
+ // if conditional
+ if (isCond)
+ {
+ itn->second.setConditional();
+ }
+ indent("sygus-unif", ind);
+ Trace("sygus-unif") << e << " :: node role : " << nrole;
+ Trace("sygus-unif")
+ << ", type : "
+ << ((DatatypeType)e.getType().toType()).getDatatype().getName();
+ if (isCond)
+ {
+ Trace("sygus-unif") << ", conditional";
+ }
+ Trace("sygus-unif") << ", enum role : " << itn->second.getRole();
+
+ if( itn->second.isTemplated() ){
+ Trace("sygus-unif") << ", templated : (lambda "
+ << itn->second.d_template_arg << " "
+ << itn->second.d_template << ")" << std::endl;
+ }else{
+ Trace("sygus-unif") << std::endl;
+ TypeNode etn = e.getType();
+
+ // enumerator type info
+ std::map< TypeNode, EnumTypeInfo >::iterator itt = d_cinfo[c].d_tinfo.find( etn );
+ Assert( itt!=d_cinfo[c].d_tinfo.end() );
+ EnumTypeInfo& tinfo = itt->second;
+
+ // strategy info
+ std::map<NodeRole, StrategyNode>::iterator itsn =
+ tinfo.d_snodes.find(nrole);
+ Assert(itsn != tinfo.d_snodes.end());
+ StrategyNode& snode = itsn->second;
+
+ if (snode.d_strats.empty())
+ {
+ return;
+ }
+ std::map<unsigned, bool> needs_cons_curr;
+ // various strategies
+ for (unsigned j = 0, size = snode.d_strats.size(); j < size; j++)
+ {
+ EnumTypeInfoStrat* etis = snode.d_strats[j];
+ StrategyType strat = etis->d_this;
+ bool newIsCond = isCond || strat == strat_ITE;
+ indent("sygus-unif", ind + 1);
+ Trace("sygus-unif") << "Strategy : " << strat
+ << ", from cons : " << etis->d_cons << std::endl;
+ int cindex = Datatype::indexOf(etis->d_cons.toExpr());
+ Assert(cindex != -1);
+ needs_cons_curr[static_cast<unsigned>(cindex)] = false;
+ for (std::pair<Node, NodeRole>& cec : etis->d_cenum)
+ {
+ // recurse
+ staticLearnRedundantOps(c,
+ cec.first,
+ cec.second,
+ visited,
+ needs_cons,
+ ind + 2,
+ newIsCond);
+ }
+ }
+ // get the master enumerator for the type of this enumerator
+ std::map<TypeNode, Node>::iterator itse =
+ d_cinfo[c].d_search_enum.find(etn);
+ if (itse == d_cinfo[c].d_search_enum.end())
+ {
+ return;
+ }
+ Node em = itse->second;
+ Assert(!em.isNull());
+ // get the current datatype
+ const Datatype& dt =
+ static_cast<DatatypeType>(etn.toType()).getDatatype();
+ // all constructors that are not a part of a strategy are needed
+ for (unsigned j = 0, size = dt.getNumConstructors(); j < size; j++)
+ {
+ if (needs_cons_curr.find(j) == needs_cons_curr.end())
+ {
+ needs_cons_curr[j] = true;
+ }
+ }
+ // update the constructors that the master enumerator needs
+ if (needs_cons.find(em) == needs_cons.end())
+ {
+ needs_cons[em] = needs_cons_curr;
+ }
+ else
+ {
+ for (unsigned j = 0, size = dt.getNumConstructors(); j < size; j++)
+ {
+ needs_cons[em][j] = needs_cons[em][j] || needs_cons_curr[j];
+ }
+ }
+ }
+ }else{
+ indent("sygus-unif", ind);
+ Trace("sygus-unif") << e << " :: node role : " << nrole << std::endl;
+ }
+}
+
+// ------------------------------------------- solution construction from enumeration
+
+void CegConjecturePbe::getCandidateList( std::vector< Node >& candidates, std::vector< Node >& clist ) {
+ Valuation& valuation = d_qe->getValuation();
+ for( unsigned i=0; i<candidates.size(); i++ ){
+ Node v = candidates[i];
+ std::map< Node, CandidateInfo >::iterator it = d_cinfo.find( v );
+ if( it!=d_cinfo.end() ){
+ for( unsigned j=0; j<it->second.d_esym_list.size(); j++ ){
+ Node e = it->second.d_esym_list[j];
+ std::map< Node, EnumInfo >::iterator it = d_einfo.find( e );
+ Assert( it != d_einfo.end() );
+ Node gstatus = valuation.getSatValue(it->second.d_active_guard);
+ if (!gstatus.isNull() && gstatus.getConst<bool>())
+ {
+ clist.push_back( e );
+ }
+ }
+ }
+ }
+}
+
+bool CegConjecturePbe::constructCandidates( std::vector< Node >& enums, std::vector< Node >& enum_values,
+ std::vector< Node >& candidates, std::vector< Node >& candidate_values,
+ std::vector< Node >& lems ) {
+ Assert( enums.size()==enum_values.size() );
+ if( !enums.empty() ){
+ unsigned min_term_size = 0;
+ std::vector< unsigned > enum_consider;
+ Trace("sygus-pbe-enum") << "Register new enumerated values : " << std::endl;
+ for( unsigned i=0; i<enums.size(); i++ ){
+ Trace("sygus-pbe-enum") << " " << enums[i] << " -> " << enum_values[i] << std::endl;
+ unsigned sz = d_tds->getSygusTermSize( enum_values[i] );
+ if( i==0 || sz<min_term_size ){
+ enum_consider.clear();
+ min_term_size = sz;
+ enum_consider.push_back( i );
+ }else if( sz==min_term_size ){
+ enum_consider.push_back( i );
+ }
+ }
+ // only consider the enumerators that are at minimum size (for fairness)
+ Trace("sygus-pbe-enum") << "...register " << enum_consider.size() << " / " << enums.size() << std::endl;
+ for( unsigned i=0; i<enum_consider.size(); i++ ){
+ unsigned j = enum_consider[i];
+ addEnumeratedValue( enums[j], enum_values[j], lems );
+ }
+ }
+ for( unsigned i=0; i<candidates.size(); i++ ){
+ Node c = candidates[i];
+ //build decision tree for candidate
+ Node vc = constructSolution( c );
+ if( vc.isNull() ){
+ return false;
+ }else{
+ candidate_values.push_back( vc );
+ }
+ }
+ return true;
+}
+
+void CegConjecturePbe::addEnumeratedValue( Node x, Node v, std::vector< Node >& lems ) {
+ std::map< Node, EnumInfo >::iterator it = d_einfo.find( x );
+ Assert( it != d_einfo.end() );
+ Node gstatus = d_qe->getValuation().getSatValue(it->second.d_active_guard);
+ if (gstatus.isNull() || !gstatus.getConst<bool>())
+ {
+ Trace("sygus-pbe-enum-debug") << " ...guard is inactive." << std::endl;
+ return;
+ }
+ Assert(
+ std::find(it->second.d_enum_vals.begin(), it->second.d_enum_vals.end(), v)
+ == it->second.d_enum_vals.end());
+ Node c = it->second.d_parent_candidate;
+ // The explanation for why the current value should be excluded in future
+ // iterations.
+ Node exp_exc;
+ if (d_examples_out_invalid.find(c) == d_examples_out_invalid.end())
+ {
+ std::map<Node, CandidateInfo>::iterator itc = d_cinfo.find(c);
+ Assert(itc != d_cinfo.end());
+ TypeNode xtn = x.getType();
+ Node bv = d_tds->sygusToBuiltin(v, xtn);
+ std::map<Node, std::vector<std::vector<Node> > >::iterator itx =
+ d_examples.find(c);
+ std::map<Node, std::vector<Node> >::iterator itxo = d_examples_out.find(c);
+ Assert(itx != d_examples.end());
+ Assert(itxo != d_examples_out.end());
+ Assert(itx->second.size() == itxo->second.size());
+ std::vector<Node> base_results;
+ // compte the results
+ for (unsigned j = 0, size = itx->second.size(); j < size; j++)
+ {
+ Node res = d_tds->evaluateBuiltin(xtn, bv, itx->second[j]);
+ Trace("sygus-pbe-enum-debug")
+ << "...got res = " << res << " from " << bv << std::endl;
+ base_results.push_back(res);
+ }
+ // is it excluded for domain-specific reason?
+ std::vector<Node> exp_exc_vec;
+ if (getExplanationForEnumeratorExclude(
+ c, x, v, base_results, it->second, exp_exc_vec))
+ {
+ Assert(!exp_exc_vec.empty());
+ exp_exc = exp_exc_vec.size() == 1
+ ? exp_exc_vec[0]
+ : NodeManager::currentNM()->mkNode(AND, exp_exc_vec);
+ Trace("sygus-pbe-enum")
+ << " ...fail : term is excluded (domain-specific)" << std::endl;
+ }
+ else
+ {
+ // notify all slaves
+ Assert( !it->second.d_enum_slave.empty() );
+ //explanation for why this value should be excluded
+ for( unsigned s=0; s<it->second.d_enum_slave.size(); s++ ){
+ Node xs = it->second.d_enum_slave[s];
+ std::map< Node, EnumInfo >::iterator itv = d_einfo.find( xs );
+ Assert( itv!=d_einfo.end() );
+ Trace("sygus-pbe-enum") << "Process " << xs << " from " << s << std::endl;
+ //bool prevIsCover = false;
+ if (itv->second.getRole() == enum_io)
+ {
+ Trace("sygus-pbe-enum") << " IO-Eval of ";
+ //prevIsCover = itv->second.isFeasible();
+ }else{
+ Trace("sygus-pbe-enum") << "Evaluation of ";
+ }
+ Trace("sygus-pbe-enum") << xs << " : ";
+ //evaluate all input/output examples
+ std::vector< Node > results;
+ Node templ = itv->second.d_template;
+ TNode templ_var = itv->second.d_template_arg;
+ std::map< Node, bool > cond_vals;
+ for (unsigned j = 0, size = base_results.size(); j < size; j++)
+ {
+ Node res = base_results[j];
+ Assert( res.isConst() );
+ if( !templ.isNull() ){
+ TNode tres = res;
+ res = templ.substitute( templ_var, res );
+ res = Rewriter::rewrite( res );
+ Assert( res.isConst() );
+ }
+ Node resb;
+ if (itv->second.getRole() == enum_io)
+ {
+ Node out = itxo->second[j];
+ Assert( out.isConst() );
+ resb = res==out ? d_true : d_false;
+ }else{
+ resb = res;
+ }
+ cond_vals[resb] = true;
+ results.push_back( resb );
+ if( Trace.isOn("sygus-pbe-enum") ){
+ if( resb.getType().isBoolean() ){
+ Trace("sygus-pbe-enum") << ( resb==d_true ? "1" : "0" );
+ }else{
+ Trace("sygus-pbe-enum") << "?";
+ }
+ }
+ }
+ bool keep = false;
+ if (itv->second.getRole() == enum_io)
+ {
+ // latter is the degenerate case of no examples
+ if (cond_vals.find(d_true) != cond_vals.end() || cond_vals.empty())
+ {
+ //check subsumbed/subsuming
+ std::vector< Node > subsume;
+ if( cond_vals.find( d_false )==cond_vals.end() ){
+ // it is the entire solution, we are done
+ Trace("sygus-pbe-enum") << " ...success, full solution added to PBE pool : " << d_tds->sygusToBuiltin( v ) << std::endl;
+ if( !itv->second.isSolved() ){
+ itv->second.setSolved( v );
+ // it subsumes everything
+ itv->second.d_term_trie.clear();
+ itv->second.d_term_trie.addTerm( this, v, results, true, subsume );
+ }
+ keep = true;
+ }else{
+ Node val = itv->second.d_term_trie.addTerm( this, v, results, true, subsume );
+ if( val==v ){
+ Trace("sygus-pbe-enum") << " ...success";
+ if( !subsume.empty() ){
+ itv->second.d_enum_subsume.insert( itv->second.d_enum_subsume.end(), subsume.begin(), subsume.end() );
+ Trace("sygus-pbe-enum") << " and subsumed " << subsume.size() << " terms";
+ }
+ Trace("sygus-pbe-enum") << "! add to PBE pool : " << d_tds->sygusToBuiltin( v ) << std::endl;
+ keep = true;
+ }else{
+ Assert( subsume.empty() );
+ Trace("sygus-pbe-enum") << " ...fail : subsumed" << std::endl;
+ }
+ }
+ }else{
+ Trace("sygus-pbe-enum") << " ...fail : it does not satisfy examples." << std::endl;
+ }
+ }else{
+ // must be unique up to examples
+ Node val = itv->second.d_term_trie.addCond(this, v, results, true);
+ if (val == v)
+ {
+ Trace("sygus-pbe-enum") << " ...success! add to PBE pool : "
+ << d_tds->sygusToBuiltin(v) << std::endl;
+ keep = true;
+ }else{
+ Trace("sygus-pbe-enum")
+ << " ...fail : term is not unique" << std::endl;
+ }
+ itc->second.d_cond_count++;
+ }
+ if( keep ){
+ // notify the parent to retry the build of PBE
+ itc->second.d_check_sol = true;
+ itv->second.addEnumValue( this, v, results );
+ }
+ }
+ }
+ }else{
+ Trace("sygus-pbe-enum-debug")
+ << " ...examples do not have output." << std::endl;
+ }
+ // exclude this value on subsequent iterations
+ Node g = it->second.d_active_guard;
+ if (exp_exc.isNull())
+ {
+ // if we did not already explain why this should be excluded, use default
+ exp_exc = d_tds->getExplain()->getExplanationForConstantEquality(x, v);
+ }
+ Node exlem =
+ NodeManager::currentNM()->mkNode(OR, g.negate(), exp_exc.negate());
+ Trace("sygus-pbe-enum-lemma")
+ << "CegConjecturePbe : enumeration exclude lemma : " << exlem
+ << std::endl;
+ lems.push_back(exlem);
+}
+
+bool CegConjecturePbe::useStrContainsEnumeratorExclude(Node x, EnumInfo& ei)
+{
+ TypeNode xbt = d_tds->sygusToBuiltinType(x.getType());
+ if (xbt.isString())
+ {
+ std::map<Node, bool>::iterator itx = d_use_str_contains_eexc.find(x);
+ if (itx != d_use_str_contains_eexc.end())
+ {
+ return itx->second;
+ }
+ Trace("sygus-pbe-enum-debug")
+ << "Is " << x << " is str.contains exclusion?" << std::endl;
+ d_use_str_contains_eexc[x] = true;
+ for (const Node& sn : ei.d_enum_slave)
+ {
+ std::map<Node, EnumInfo>::iterator itv = d_einfo.find(sn);
+ EnumRole er = itv->second.getRole();
+ if (er != enum_io && er != enum_concat_term)
+ {
+ Trace("sygus-pbe-enum-debug") << " incompatible slave : " << sn
+ << ", role = " << er << std::endl;
+ d_use_str_contains_eexc[x] = false;
+ return false;
+ }
+ if (itv->second.isConditional())
+ {
+ Trace("sygus-pbe-enum-debug")
+ << " conditional slave : " << sn << std::endl;
+ d_use_str_contains_eexc[x] = false;
+ return false;
+ }
+ }
+ Trace("sygus-pbe-enum-debug")
+ << "...can use str.contains exclusion." << std::endl;
+ return d_use_str_contains_eexc[x];
+ }
+ return false;
+}
+
+bool CegConjecturePbe::getExplanationForEnumeratorExclude(
+ Node c,
+ Node x,
+ Node v,
+ std::vector<Node>& results,
+ EnumInfo& ei,
+ std::vector<Node>& exp)
+{
+ if (useStrContainsEnumeratorExclude(x, ei))
+ {
+ NodeManager* nm = NodeManager::currentNM();
+ // This check whether the example evaluates to something that is larger than
+ // the output for some input/output pair. If so, then this term is never
+ // useful. We generalize its explanation below.
+
+ if (Trace.isOn("sygus-pbe-cterm-debug"))
+ {
+ Trace("sygus-pbe-enum") << std::endl;
+ }
+ // check if all examples had longer length that the output
+ std::map<Node, std::vector<Node> >::iterator itxo = d_examples_out.find(c);
+ Assert(itxo != d_examples_out.end());
+ Assert(itxo->second.size() == results.size());
+ Trace("sygus-pbe-cterm-debug")
+ << "Check enumerator exclusion for " << x << " -> "
+ << d_tds->sygusToBuiltin(v) << " based on str.contains." << std::endl;
+ std::vector<unsigned> cmp_indices;
+ for (unsigned i = 0, size = results.size(); i < size; i++)
+ {
+ Assert(results[i].isConst());
+ Assert(itxo->second[i].isConst());
+ Trace("sygus-pbe-cterm-debug")
+ << " " << results[i] << " <> " << itxo->second[i];
+ Node cont = nm->mkNode(STRING_STRCTN, itxo->second[i], results[i]);
+ Node contr = Rewriter::rewrite(cont);
+ if (contr == d_false)
+ {
+ cmp_indices.push_back(i);
+ Trace("sygus-pbe-cterm-debug") << "...not contained." << std::endl;
+ }
+ else
+ {
+ Trace("sygus-pbe-cterm-debug") << "...contained." << std::endl;
+ }
+ }
+ if (!cmp_indices.empty())
+ {
+ // we check invariance with respect to a negative contains test
+ NegContainsSygusInvarianceTest ncset;
+ ncset.init(d_parent, x, itxo->second, cmp_indices);
+ // construct the generalized explanation
+ d_tds->getExplain()->getExplanationFor(x, v, exp, ncset);
+ Trace("sygus-pbe-cterm")
+ << "PBE-cterm : enumerator exclude " << d_tds->sygusToBuiltin(v)
+ << " due to negative containment." << std::endl;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+void CegConjecturePbe::EnumInfo::addEnumValue( CegConjecturePbe * pbe, Node v, std::vector< Node >& results ) {
+ d_enum_val_to_index[v] = d_enum_vals.size();
+ d_enum_vals.push_back( v );
+ d_enum_vals_res.push_back( results );
+ /*
+ if( getRole()==enum_io ){
+ // compute
+ if( d_enum_total.empty() ){
+ d_enum_total = results;
+ }else if( !d_enum_total_true ){
+ d_enum_total_true = true;
+ Assert( d_enum_total.size()==results.size() );
+ for( unsigned i=0; i<results.size(); i++ ){
+ if( d_enum_total[i]==pbe->d_true || results[i]==pbe->d_true ){
+ d_enum_total[i] = pbe->d_true;
+ }else{
+ d_enum_total[i] = pbe->d_false;
+ d_enum_total_true = false;
+ }
+ }
+ }
+ }
+ */
+}
+
+void CegConjecturePbe::EnumInfo::initialize(Node c, EnumRole role)
+{
+ d_parent_candidate = c;
+ d_role = role;
+}
+
+void CegConjecturePbe::EnumInfo::setSolved( Node slv ) {
+ d_enum_solved = slv;
+ //d_enum_total_true = true;
+}
+
+void CegConjecturePbe::CandidateInfo::initialize( Node c ) {
+ d_this_candidate = c;
+ d_root = c.getType();
+}
+
+void CegConjecturePbe::CandidateInfo::initializeType( TypeNode tn ) {
+ d_tinfo[tn].d_this_type = tn;
+ d_tinfo[tn].d_parent = this;
+}
+
+Node CegConjecturePbe::CandidateInfo::getRootEnumerator() {
+ std::map<EnumRole, Node>::iterator it = d_tinfo[d_root].d_enum.find(enum_io);
+ Assert( it!=d_tinfo[d_root].d_enum.end() );
+ return it->second;
+}
+
+bool CegConjecturePbe::CandidateInfo::isNonTrivial() {
+ //TODO
+ return true;
+}
+
+// status : 0 : exact, -1 : vals is subset, 1 : vals is superset
+Node CegConjecturePbe::SubsumeTrie::addTermInternal( CegConjecturePbe * pbe, Node t, std::vector< Node >& vals, bool pol,
+ std::vector< Node >& subsumed, bool spol, IndexFilter * f,
+ unsigned index, int status, bool checkExistsOnly, bool checkSubsume ) {
+ if( index==vals.size() ){
+ if( status==0 ){
+ // set the term if checkExistsOnly = false
+ if( d_term.isNull() && !checkExistsOnly ){
+ d_term = t;
+ }
+ }else if( status==1 ){
+ Assert( checkSubsume );
+ // found a subsumed term
+ if( !d_term.isNull() ){
+ subsumed.push_back( d_term );
+ if( !checkExistsOnly ){
+ // remove it if checkExistsOnly = false
+ d_term = Node::null();
+ }
+ }
+ }else{
+ Assert( !checkExistsOnly && checkSubsume );
+ }
+ return d_term;
+ }else{
+ // the current value
+ Assert( pol || ( vals[index].isConst() && vals[index].getType().isBoolean() ) );
+ Node cv = pol ? vals[index] : ( vals[index]==pbe->d_true ? pbe->d_false : pbe->d_true );
+ // if checkExistsOnly = false, check if the current value is subsumed if checkSubsume = true, if so, don't add
+ if( !checkExistsOnly && checkSubsume ){
+ std::vector< bool > check_subsumed_by;
+ if( status==0 ){
+ if( cv==pbe->d_false ){
+ check_subsumed_by.push_back( spol );
+ }
+ }else if( status==-1 ){
+ check_subsumed_by.push_back( spol );
+ if( cv==pbe->d_false ){
+ check_subsumed_by.push_back( !spol );
+ }
+ }
+ // check for subsumed nodes
+ for( unsigned i=0; i<check_subsumed_by.size(); i++ ){
+ Node csval = check_subsumed_by[i] ? pbe->d_true : pbe->d_false;
+ // check if subsumed
+ std::map< Node, SubsumeTrie >::iterator itc = d_children.find( csval );
+ if( itc!=d_children.end() ){
+ unsigned next_index = f ? f->next( index ) : index+1;
+ Node ret = itc->second.addTermInternal( pbe, t, vals, pol, subsumed, spol, f, next_index, -1, checkExistsOnly, checkSubsume );
+ // ret subsumes t
+ if( !ret.isNull() ){
+ return ret;
+ }
+ }
+ }
+ }
+ Node ret;
+ std::vector< bool > check_subsume;
+ if( status==0 ){
+ unsigned next_index = f ? f->next( index ) : index+1;
+ if( checkExistsOnly ){
+ std::map< Node, SubsumeTrie >::iterator itc = d_children.find( cv );
+ if( itc!=d_children.end() ){
+ ret = itc->second.addTermInternal( pbe, t, vals, pol, subsumed, spol, f, next_index, 0, checkExistsOnly, checkSubsume );
+ }
+ }else{
+ Assert( spol );
+ ret = d_children[cv].addTermInternal( pbe, t, vals, pol, subsumed, spol, f, next_index, 0, checkExistsOnly, checkSubsume );
+ if( ret!=t ){
+ // we were subsumed by ret, return
+ return ret;
+ }
+ }
+ if( checkSubsume ){
+ // check for subsuming
+ if( cv==pbe->d_true ){
+ check_subsume.push_back( !spol );
+ }
+ }
+ }else if( status==1 ){
+ Assert( checkSubsume );
+ check_subsume.push_back( !spol );
+ if( cv==pbe->d_true ){
+ check_subsume.push_back( spol );
+ }
+ }
+ if( checkSubsume ){
+ // check for subsumed terms
+ for( unsigned i=0; i<check_subsume.size(); i++ ){
+ Node csval = check_subsume[i] ? pbe->d_true : pbe->d_false;
+ std::map< Node, SubsumeTrie >::iterator itc = d_children.find( csval );
+ if( itc!=d_children.end() ){
+ unsigned next_index = f ? f->next( index ) : index+1;
+ itc->second.addTermInternal( pbe, t, vals, pol, subsumed, spol, f, next_index, 1, checkExistsOnly, checkSubsume );
+ // clean up
+ if( itc->second.isEmpty() ){
+ Assert( !checkExistsOnly );
+ d_children.erase( csval );
+ }
+ }
+ }
+ }
+ return ret;
+ }
+}
+
+Node CegConjecturePbe::SubsumeTrie::addTerm( CegConjecturePbe * pbe, Node t, std::vector< Node >& vals, bool pol, std::vector< Node >& subsumed, IndexFilter * f ) {
+ unsigned start_index = f ? f->start() : 0;
+ return addTermInternal( pbe, t, vals, pol, subsumed, true, f, start_index, 0, false, true );
+}
+
+Node CegConjecturePbe::SubsumeTrie::addCond( CegConjecturePbe * pbe, Node c, std::vector< Node >& vals, bool pol, IndexFilter * f ) {
+ unsigned start_index = f ? f->start() : 0;
+ std::vector< Node > subsumed;
+ return addTermInternal( pbe, c, vals, pol, subsumed, true, f, start_index, 0, false, false );
+}
+
+void CegConjecturePbe::SubsumeTrie::getSubsumed( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::vector< Node >& subsumed, IndexFilter * f ){
+ unsigned start_index = f ? f->start() : 0;
+ addTermInternal( pbe, Node::null(), vals, pol, subsumed, true, f, start_index, 1, true, true );
+}
+
+void CegConjecturePbe::SubsumeTrie::getSubsumedBy( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::vector< Node >& subsumed_by, IndexFilter * f ){
+ // flip polarities
+ unsigned start_index = f ? f->start() : 0;
+ addTermInternal( pbe, Node::null(), vals, !pol, subsumed_by, false, f, start_index, 1, true, true );
+}
+
+void CegConjecturePbe::SubsumeTrie::getLeavesInternal( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::map< int, std::vector< Node > >& v,
+ IndexFilter * f, unsigned index, int status ) {
+ if( index==vals.size() ){
+ Assert( !d_term.isNull() );
+ Assert( std::find( v[status].begin(), v[status].end(), d_term )==v[status].end() );
+ v[status].push_back( d_term );
+ }else{
+ Assert( vals[index].isConst() && vals[index].getType().isBoolean() );
+ // filter should be for cv
+ Assert( f==NULL || vals[index]==( pol ? pbe->d_true : pbe->d_false ) );
+ for( std::map< Node, SubsumeTrie >::iterator it = d_children.begin(); it != d_children.end(); ++it ){
+ int new_status = status;
+ // if the current value is true
+ if( vals[index]==( pol ? pbe->d_true : pbe->d_false ) ){
+ if( status!=0 ){
+ new_status = ( it->first == pbe->d_true ? 1 : -1 );
+ if( status!=-2 && new_status!=status ){
+ new_status = 0;
+ }
+ }
+ }
+ unsigned next_index = f ? f->next( index ) : index+1;
+ it->second.getLeavesInternal( pbe, vals, pol, v, f, next_index, new_status );
+ }
+ }
+}
+
+void CegConjecturePbe::SubsumeTrie::getLeaves( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::map< int, std::vector< Node > >& v, IndexFilter * f ) {
+ unsigned start_index = f ? f->start() : 0;
+ getLeavesInternal( pbe, vals, pol, v, f, start_index, -2 );
+}
+
+void CegConjecturePbe::IndexFilter::mk( std::vector< Node >& vals, bool pol ) {
+ Trace("sygus-pbe-debug") << "Make for : ";
+ print_val( "sygus-pbe-debug", vals, pol );
+ Trace("sygus-pbe-debug") << std::endl;
+ Node poln = NodeManager::currentNM()->mkConst( pol );
+
+ unsigned curr_index = 0;
+ while( curr_index<vals.size() && vals[curr_index]!=poln ){
+ curr_index++;
+ }
+ d_next[0] = curr_index;
+ Trace("sygus-pbe-debug") << "0 -> " << curr_index << std::endl;
+ unsigned i = curr_index;
+ while( i<vals.size() ){
+ while( i<vals.size() && vals[i]!=poln ){
+ i++;
+ }
+ i++;
+ d_next[curr_index+1] = i;
+ Trace("sygus-pbe-debug") << curr_index+1 << " -> " << i << std::endl;
+ curr_index = i;
+ }
+
+ // verify it is correct
+ unsigned j = start();
+ for( unsigned k=0; k<j; k++ ){
+ AlwaysAssert( vals[k]!=poln );
+ }
+ Trace("sygus-pbe-debug") << "...start : " << j << std::endl;
+ unsigned counter = 0;
+ while( j<vals.size() ){
+ Trace("sygus-pbe-debug") << "...at : " << j << std::endl;
+ AlwaysAssert( vals[j]==poln );
+ unsigned jj = next( j );
+ AlwaysAssert( jj>j );
+ for( unsigned k=(j+1); k<jj; k++ ){
+ AlwaysAssert( vals[k]!=poln );
+ }
+ AlwaysAssert( counter<=vals.size() );
+ counter++;
+ j = jj;
+ }
+
+
+}
+
+unsigned CegConjecturePbe::IndexFilter::start() {
+ std::map< unsigned, unsigned >::iterator it = d_next.find( 0 );
+ if( it==d_next.end() ){
+ return 0;
+ }else{
+ return it->second;
+ }
+}
+
+unsigned CegConjecturePbe::IndexFilter::next( unsigned i ) {
+ std::map< unsigned, unsigned >::iterator it = d_next.find( i+1 );
+ if( it==d_next.end() ){
+ return i+1;
+ }else{
+ return it->second;
+ }
+}
+
+bool CegConjecturePbe::IndexFilter::isEq( std::vector< Node >& vals, Node v ) {
+ unsigned index = start();
+ while( index<vals.size() ){
+ if( vals[index]!=v ){
+ return false;
+ }
+ index = next( index );
+ }
+ return true;
+}
+
+Node CegConjecturePbe::constructSolution( Node c ){
+ std::map< Node, CandidateInfo >::iterator itc = d_cinfo.find( c );
+ Assert( itc!=d_cinfo.end() );
+ if( !itc->second.d_solution.isNull() ){
+ // already has a solution
+ return itc->second.d_solution;
+ }else{
+ // only check if an enumerator updated
+ if( itc->second.d_check_sol ){
+ Trace("sygus-pbe") << "Construct solution, #iterations = " << itc->second.d_cond_count << std::endl;
+ itc->second.d_check_sol = false;
+ // try multiple times if we have done multiple conditions, due to non-determinism
+ Node vc;
+ for( unsigned i=0; i<=itc->second.d_cond_count; i++ ){
+ Trace("sygus-pbe-dt") << "ConstructPBE for candidate: " << c << std::endl;
+ Node e = itc->second.getRootEnumerator();
+ UnifContext x;
+ x.initialize( this, c );
+ Node vcc = constructSolution(c, e, role_equal, x, 1);
+ if( !vcc.isNull() ){
+ if( vc.isNull() || ( !vc.isNull() && d_tds->getSygusTermSize( vcc )<d_tds->getSygusTermSize( vc ) ) ){
+ Trace("sygus-pbe") << "**** PBE SOLVED : " << c << " = " << vcc << std::endl;
+ Trace("sygus-pbe") << "...solved at iteration " << i << std::endl;
+ vc = vcc;
+ }
+ }
+ }
+ if( !vc.isNull() ){
+ itc->second.d_solution = vc;
+ return vc;
+ }
+ Trace("sygus-pbe") << "...failed to solve." << std::endl;
+ }
+ return Node::null();
+ }
+}
+
+Node CegConjecturePbe::constructBestSolvedTerm( std::vector< Node >& solved, UnifContext& x ){
+ Assert( !solved.empty() );
+ // TODO
+ return solved[0];
+}
+
+Node CegConjecturePbe::constructBestStringSolvedTerm( std::vector< Node >& solved, UnifContext& x ) {
+ Assert( !solved.empty() );
+ // TODO
+ return solved[0];
+}
+
+Node CegConjecturePbe::constructBestSolvedConditional( std::vector< Node >& solved, UnifContext& x ){
+ Assert( !solved.empty() );
+ // TODO
+ return solved[0];
+}
+
+Node CegConjecturePbe::constructBestConditional( std::vector< Node >& conds, UnifContext& x ) {
+ Assert( !conds.empty() );
+ // TODO
+ double r = Random::getRandom().pickDouble(0.0, 1.0);
+ unsigned cindex = r*conds.size();
+ if( cindex>conds.size() ){
+ cindex = conds.size() - 1;
+ }
+ return conds[cindex];
+}
+
+Node CegConjecturePbe::constructBestStringToConcat( std::vector< Node > strs,
+ std::map< Node, unsigned > total_inc,
+ std::map< Node, std::vector< unsigned > > incr,
+ UnifContext& x ) {
+ Assert( !strs.empty() );
+ std::random_shuffle(strs.begin(), strs.end());
+ // prefer one that has incremented by more than 0
+ for (const Node& ns : strs)
+ {
+ if (total_inc[ns] > 0)
+ {
+ return ns;
+ }
+ }
+ return strs[0];
+}
+
+Node CegConjecturePbe::constructSolution(
+ Node c, Node e, NodeRole nrole, UnifContext& x, int ind)
+{
+ TypeNode etn = e.getType();
+ if (Trace.isOn("sygus-pbe-dt-debug"))
+ {
+ indent("sygus-pbe-dt-debug", ind);
+ Trace("sygus-pbe-dt-debug") << "ConstructPBE: (" << e << ", " << nrole
+ << ") for type " << etn << " in context ";
+ print_val("sygus-pbe-dt-debug", x.d_vals);
+ if (x.d_has_string_pos != role_invalid)
+ {
+ Trace("sygus-pbe-dt-debug") << ", string context [" << x.d_has_string_pos;
+ for (unsigned i = 0, size = x.d_str_pos.size(); i < size; i++)
+ {
+ Trace("sygus-pbe-dt-debug") << " " << x.d_str_pos[i];
+ }
+ Trace("sygus-pbe-dt-debug") << "]";
+ }
+ Trace("sygus-pbe-dt-debug") << std::endl;
+ }
+ // enumerator type info
+ std::map<TypeNode, EnumTypeInfo>::iterator itt = d_cinfo[c].d_tinfo.find(etn);
+ Assert(itt != d_cinfo[c].d_tinfo.end());
+ EnumTypeInfo& tinfo = itt->second;
+
+ // get the enumerator information
+ std::map< Node, EnumInfo >::iterator itn = d_einfo.find( e );
+ Assert( itn!=d_einfo.end() );
+ EnumInfo& einfo = itn->second;
+
+ Node ret_dt;
+ if (nrole == role_equal)
+ {
+ if (!x.isReturnValueModified())
+ {
+ if (einfo.isSolved())
+ {
+ // this type has a complete solution
+ ret_dt = einfo.getSolved();
+ indent("sygus-pbe-dt", ind);
+ Trace("sygus-pbe-dt") << "return PBE: success : solved "
+ << d_tds->sygusToBuiltin(ret_dt) << std::endl;
+ Assert(!ret_dt.isNull());
+ }
+ else
+ {
+ // could be conditionally solved
+ std::vector<Node> subsumed_by;
+ einfo.d_term_trie.getSubsumedBy(this, x.d_vals, true, subsumed_by);
+ if (!subsumed_by.empty())
+ {
+ ret_dt = constructBestSolvedTerm(subsumed_by, x);
+ indent("sygus-pbe-dt", ind);
+ Trace("sygus-pbe-dt") << "return PBE: success : conditionally solved"
+ << d_tds->sygusToBuiltin(ret_dt) << std::endl;
+ }
+ else
+ {
+ indent("sygus-pbe-dt-debug", ind);
+ Trace("sygus-pbe-dt-debug")
+ << " ...not currently conditionally solved." << std::endl;
+ }
+ }
+ }
+ if (ret_dt.isNull())
+ {
+ if (d_tds->sygusToBuiltinType(e.getType()).isString())
+ {
+ // check if a current value that closes all examples
+ // get the term enumerator for this type
+ bool success = true;
+ std::map<Node, EnumInfo>::iterator itet;
+ std::map<EnumRole, Node>::iterator itnt =
+ tinfo.d_enum.find(enum_concat_term);
+ if( itnt != itt->second.d_enum.end() ){
+ Node et = itnt->second;
+ itet = d_einfo.find( et );
+ Assert(itet != d_einfo.end());
+ }else{
+ success = false;
+ }
+ if (success)
+ {
+ // get the current examples
+ std::map<Node, std::vector<Node> >::iterator itx =
+ d_examples_out.find(c);
+ Assert(itx != d_examples_out.end());
+ std::vector<String> ex_vals;
+ x.getCurrentStrings(this, itx->second, ex_vals);
+ Assert(itn->second.d_enum_vals.size()
+ == itn->second.d_enum_vals_res.size());
+
+ // test each example in the term enumerator for the type
+ std::vector<Node> str_solved;
+ for (unsigned i = 0, size = itet->second.d_enum_vals.size(); i < size;
+ i++)
+ {
+ if (x.isStringSolved(
+ this, ex_vals, itet->second.d_enum_vals_res[i]))
+ {
+ str_solved.push_back(itet->second.d_enum_vals[i]);
+ }
+ }
+ if (!str_solved.empty())
+ {
+ ret_dt = constructBestStringSolvedTerm(str_solved, x);
+ indent("sygus-pbe-dt", ind);
+ Trace("sygus-pbe-dt") << "return PBE: success : string solved "
+ << d_tds->sygusToBuiltin(ret_dt) << std::endl;
+ }
+ else
+ {
+ indent("sygus-pbe-dt-debug", ind);
+ Trace("sygus-pbe-dt-debug") << " ...not currently string solved."
+ << std::endl;
+ }
+ }
+ }
+ }
+ }
+ else if (nrole == role_string_prefix || nrole == role_string_suffix)
+ {
+ // check if each return value is a prefix/suffix of all open examples
+ if (!x.isReturnValueModified() || x.d_has_string_pos == nrole)
+ {
+ std::map<Node, std::vector<unsigned> > incr;
+ bool isPrefix = nrole == role_string_prefix;
+ std::map<Node, unsigned> total_inc;
+ std::vector<Node> inc_strs;
+ std::map<Node, std::vector<Node> >::iterator itx = d_examples_out.find(c);
+ Assert(itx != d_examples_out.end());
+ // make the value of the examples
+ std::vector<String> ex_vals;
+ x.getCurrentStrings(this, itx->second, ex_vals);
+ if (Trace.isOn("sygus-pbe-dt-debug"))
+ {
+ indent("sygus-pbe-dt-debug", ind);
+ Trace("sygus-pbe-dt-debug") << "current strings : " << std::endl;
+ for (unsigned i = 0, size = ex_vals.size(); i < size; i++)
+ {
+ indent("sygus-pbe-dt-debug", ind + 1);
+ Trace("sygus-pbe-dt-debug") << ex_vals[i] << std::endl;
+ }
+ }
+
+ // check if there is a value for which is a prefix/suffix of all active
+ // examples
+ Assert(einfo.d_enum_vals.size() == einfo.d_enum_vals_res.size());
+
+ for (unsigned i = 0, size = einfo.d_enum_vals.size(); i < size; i++)
+ {
+ Node val_t = einfo.d_enum_vals[i];
+ indent("sygus-pbe-dt-debug", ind);
+ Trace("sygus-pbe-dt-debug") << "increment string values : " << val_t
+ << " : ";
+ Assert(einfo.d_enum_vals_res[i].size() == itx->second.size());
+ unsigned tot = 0;
+ bool exsuccess = x.getStringIncrement(this,
+ isPrefix,
+ ex_vals,
+ einfo.d_enum_vals_res[i],
+ incr[val_t],
+ tot);
+ if (!exsuccess)
+ {
+ incr.erase(val_t);
+ Trace("sygus-pbe-dt-debug") << "...fail" << std::endl;
+ }
+ else
+ {
+ total_inc[val_t] = tot;
+ inc_strs.push_back(val_t);
+ Trace("sygus-pbe-dt-debug") << "...success, total increment = " << tot
+ << std::endl;
+ }
+ }
+
+ if (!incr.empty())
+ {
+ ret_dt = constructBestStringToConcat(inc_strs, total_inc, incr, x);
+ Assert(!ret_dt.isNull());
+ indent("sygus-pbe-dt", ind);
+ Trace("sygus-pbe-dt") << "PBE: CONCAT strategy : choose "
+ << (isPrefix ? "pre" : "suf") << "fix value "
+ << d_tds->sygusToBuiltin(ret_dt) << std::endl;
+ // update the context
+ bool ret = x.updateStringPosition(this, incr[ret_dt]);
+ AlwaysAssert(ret == (total_inc[ret_dt] > 0));
+ x.d_has_string_pos = nrole;
+ }else{
+ indent("sygus-pbe-dt", ind);
+ Trace("sygus-pbe-dt") << "PBE: failed CONCAT strategy, no values are "
+ << (isPrefix ? "pre" : "suf")
+ << "fix of all examples." << std::endl;
+ }
+ }
+ else
+ {
+ indent("sygus-pbe-dt", ind);
+ Trace("sygus-pbe-dt")
+ << "PBE: failed CONCAT strategy, prefix/suffix mismatch."
+ << std::endl;
+ }
+ }
+ if (ret_dt.isNull() && !einfo.isTemplated())
+ {
+ // we will try a single strategy
+ EnumTypeInfoStrat* etis = nullptr;
+ std::map<NodeRole, StrategyNode>::iterator itsn =
+ tinfo.d_snodes.find(nrole);
+ if (itsn != tinfo.d_snodes.end())
+ {
+ // strategy info
+ StrategyNode& snode = itsn->second;
+ if (x.d_visit_role[e].find(nrole) == x.d_visit_role[e].end())
+ {
+ x.d_visit_role[e][nrole] = true;
+ // try a random strategy
+ if (snode.d_strats.size() > 1)
+ {
+ std::random_shuffle(snode.d_strats.begin(), snode.d_strats.end());
+ }
+ // get an eligible strategy index
+ unsigned sindex = 0;
+ while (sindex < snode.d_strats.size()
+ && !x.isValidStrategy(snode.d_strats[sindex]))
+ {
+ sindex++;
+ }
+ // if we found a eligible strategy
+ if (sindex < snode.d_strats.size())
+ {
+ etis = snode.d_strats[sindex];
+ }
+ }
+ }
+ if (etis != nullptr)
+ {
+ StrategyType strat = etis->d_this;
+ indent("sygus-pbe-dt", ind + 1);
+ Trace("sygus-pbe-dt") << "...try STRATEGY " << strat << "..."
+ << std::endl;
+
+ std::map<unsigned, Node> look_ahead_solved_children;
+ std::vector<Node> dt_children_cons;
+ bool success = true;
+
+ // for ITE
+ Node split_cond_enum;
+ int split_cond_res_index = -1;
+
+ for (unsigned sc = 0, size = etis->d_cenum.size(); sc < size; sc++)
+ {
+ indent("sygus-pbe-dt", ind + 1);
+ Trace("sygus-pbe-dt") << "construct PBE child #" << sc << "..."
+ << std::endl;
+ Node rec_c;
+ std::map<unsigned, Node>::iterator itla =
+ look_ahead_solved_children.find(sc);
+ if (itla != look_ahead_solved_children.end())
+ {
+ rec_c = itla->second;
+ indent("sygus-pbe-dt-debug", ind + 1);
+ Trace("sygus-pbe-dt-debug") << "ConstructPBE: look ahead solved : "
+ << d_tds->sygusToBuiltin(rec_c)
+ << std::endl;
+ }
+ else
+ {
+ std::pair<Node, NodeRole>& cenum = etis->d_cenum[sc];
+
+ // update the context
+ std::vector<Node> prev;
+ if (strat == strat_ITE && sc > 0)
+ {
+ std::map<Node, EnumInfo>::iterator itnc =
+ d_einfo.find(split_cond_enum);
+ Assert(itnc != d_einfo.end());
+ Assert(split_cond_res_index >= 0);
+ Assert(split_cond_res_index
+ < (int)itnc->second.d_enum_vals_res.size());
+ prev = x.d_vals;
+ bool ret = x.updateContext(
+ this,
+ itnc->second.d_enum_vals_res[split_cond_res_index],
+ sc == 1);
+ AlwaysAssert(ret);
+ }
+
+ // recurse
+ if (strat == strat_ITE && sc == 0)
+ {
+ Node ce = cenum.first;
+
+ // register the condition enumerator
+ std::map<Node, EnumInfo>::iterator itnc = d_einfo.find(ce);
+ Assert(itnc != d_einfo.end());
+ EnumInfo& einfo_child = itnc->second;
+
+ // only used if the return value is not modified
+ if (!x.isReturnValueModified())
+ {
+ if (x.d_uinfo.find(ce) == x.d_uinfo.end())
+ {
+ Trace("sygus-pbe-dt-debug2")
+ << " reg : PBE: Look for direct solutions for conditional "
+ "enumerator "
+ << ce << " ... " << std::endl;
+ Assert(einfo_child.d_enum_vals.size()
+ == einfo_child.d_enum_vals_res.size());
+ for (unsigned i = 1; i <= 2; i++)
+ {
+ std::pair<Node, NodeRole>& te_pair = etis->d_cenum[i];
+ Node te = te_pair.first;
+ std::map<Node, EnumInfo>::iterator itnt = d_einfo.find(te);
+ Assert(itnt != d_einfo.end());
+ bool branch_pol = (i == 1);
+ // for each condition, get terms that satisfy it in this
+ // branch
+ for (unsigned k = 0, size = einfo_child.d_enum_vals.size();
+ k < size;
+ k++)
+ {
+ Node cond = einfo_child.d_enum_vals[k];
+ std::vector<Node> solved;
+ itnt->second.d_term_trie.getSubsumedBy(
+ this,
+ einfo_child.d_enum_vals_res[k],
+ branch_pol,
+ solved);
+ Trace("sygus-pbe-dt-debug2")
+ << " reg : PBE: " << d_tds->sygusToBuiltin(cond)
+ << " has " << solved.size() << " solutions in branch "
+ << i << std::endl;
+ if (!solved.empty())
+ {
+ Node slv = constructBestSolvedTerm(solved, x);
+ Trace("sygus-pbe-dt-debug2")
+ << " reg : PBE: ..." << d_tds->sygusToBuiltin(slv)
+ << " is a solution under branch " << i;
+ Trace("sygus-pbe-dt-debug2")
+ << " of condition " << d_tds->sygusToBuiltin(cond)
+ << std::endl;
+ x.d_uinfo[ce].d_look_ahead_sols[cond][i] = slv;
+ }
+ }
+ }
+ }
+ }
+
+ // get the conditionals in the current context : they must be
+ // distinguishable
+ std::map<int, std::vector<Node> > possible_cond;
+ std::map<Node, int> solved_cond; // stores branch
+ einfo_child.d_term_trie.getLeaves(
+ this, x.d_vals, true, possible_cond);
+
+ std::map<int, std::vector<Node> >::iterator itpc =
+ possible_cond.find(0);
+ if (itpc != possible_cond.end())
+ {
+ if (Trace.isOn("sygus-pbe-dt-debug"))
+ {
+ indent("sygus-pbe-dt-debug", ind + 1);
+ Trace("sygus-pbe-dt-debug")
+ << "PBE : We have " << itpc->second.size()
+ << " distinguishable conditionals:" << std::endl;
+ for (Node& cond : itpc->second)
+ {
+ indent("sygus-pbe-dt-debug", ind + 2);
+ Trace("sygus-pbe-dt-debug") << d_tds->sygusToBuiltin(cond)
+ << std::endl;
+ }
+ }
+
+ // static look ahead conditional : choose conditionals that have
+ // solved terms in at least one branch
+ // only applicable if we have not modified the return value
+ std::map<int, std::vector<Node> > solved_cond;
+ if (!x.isReturnValueModified())
+ {
+ Assert(x.d_uinfo.find(ce) != x.d_uinfo.end());
+ int solve_max = 0;
+ for (Node& cond : itpc->second)
+ {
+ std::map<Node, std::map<unsigned, Node> >::iterator itla =
+ x.d_uinfo[ce].d_look_ahead_sols.find(cond);
+ if (itla != x.d_uinfo[ce].d_look_ahead_sols.end())
+ {
+ int nsolved = itla->second.size();
+ solve_max = nsolved > solve_max ? nsolved : solve_max;
+ solved_cond[nsolved].push_back(cond);
+ }
+ }
+ int n = solve_max;
+ while (n > 0)
+ {
+ if (!solved_cond[n].empty())
+ {
+ rec_c = constructBestSolvedConditional(solved_cond[n], x);
+ indent("sygus-pbe-dt", ind + 1);
+ Trace("sygus-pbe-dt")
+ << "PBE: ITE strategy : choose solved conditional "
+ << d_tds->sygusToBuiltin(rec_c) << " with " << n
+ << " solved children..." << std::endl;
+ std::map<Node, std::map<unsigned, Node> >::iterator itla =
+ x.d_uinfo[ce].d_look_ahead_sols.find(rec_c);
+ Assert(itla != x.d_uinfo[ce].d_look_ahead_sols.end());
+ for (std::pair<const unsigned, Node>& las : itla->second)
+ {
+ look_ahead_solved_children[las.first] = las.second;
+ }
+ break;
+ }
+ n--;
+ }
+ }
+
+ // otherwise, guess a conditional
+ if (rec_c.isNull())
+ {
+ rec_c = constructBestConditional(itpc->second, x);
+ Assert(!rec_c.isNull());
+ indent("sygus-pbe-dt", ind);
+ Trace("sygus-pbe-dt")
+ << "PBE: ITE strategy : choose random conditional "
+ << d_tds->sygusToBuiltin(rec_c) << std::endl;
+ }
+ }
+ else
+ {
+ // TODO (#1250) : degenerate case where children have different
+ // types?
+ indent("sygus-pbe-dt", ind);
+ Trace("sygus-pbe-dt") << "return PBE: failed ITE strategy, "
+ "cannot find a distinguishable condition"
+ << std::endl;
+ }
+ if( !rec_c.isNull() ){
+ Assert(einfo_child.d_enum_val_to_index.find(rec_c)
+ != einfo_child.d_enum_val_to_index.end());
+ split_cond_res_index = einfo_child.d_enum_val_to_index[rec_c];
+ split_cond_enum = ce;
+ Assert(split_cond_res_index >= 0);
+ Assert(split_cond_res_index
+ < (int)einfo_child.d_enum_vals_res.size());
+ }
+ }
+ else
+ {
+ rec_c = constructSolution(c, cenum.first, cenum.second, x, ind + 2);
+ }
+
+ // undo update the context
+ if (strat == strat_ITE && sc > 0)
+ {
+ x.d_vals = prev;
+ }
+ }
+ if (!rec_c.isNull())
+ {
+ dt_children_cons.push_back(rec_c);
+ }
+ else
+ {
+ success = false;
+ break;
+ }
+ }
+ if (success)
+ {
+ Assert(dt_children_cons.size() == etis->d_sol_templ_args.size());
+ // ret_dt = NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR,
+ // dt_children );
+ ret_dt = etis->d_sol_templ;
+ ret_dt = ret_dt.substitute(etis->d_sol_templ_args.begin(),
+ etis->d_sol_templ_args.end(),
+ dt_children_cons.begin(),
+ dt_children_cons.end());
+ indent("sygus-pbe-dt-debug", ind);
+ Trace("sygus-pbe-dt-debug")
+ << "PBE: success : constructed for strategy " << strat << std::endl;
+ }else{
+ indent("sygus-pbe-dt-debug", ind);
+ Trace("sygus-pbe-dt-debug") << "PBE: failed for strategy " << strat
+ << std::endl;
+ }
+ }
+ }
+
+ if( !ret_dt.isNull() ){
+ Assert( ret_dt.getType()==e.getType() );
+ }
+ indent("sygus-pbe-dt", ind);
+ Trace("sygus-pbe-dt") << "ConstructPBE: returned " << ret_dt << std::endl;
+ return ret_dt;
+}
+
+bool CegConjecturePbe::UnifContext::updateContext( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol ) {
+ Assert( d_vals.size()==vals.size() );
+ bool changed = false;
+ Node poln = pol ? pbe->d_true : pbe->d_false;
+ for( unsigned i=0; i<vals.size(); i++ ){
+ if( vals[i]!=poln ){
+ if( d_vals[i]==pbe->d_true ){
+ d_vals[i] = pbe->d_false;
+ changed = true;
+ }
+ }
+ }
+ if (changed)
+ {
+ d_visit_role.clear();
+ }
+ return changed;
+}
+
+bool CegConjecturePbe::UnifContext::updateStringPosition( CegConjecturePbe * pbe, std::vector< unsigned >& pos ) {
+ Assert( pos.size()==d_str_pos.size() );
+ bool changed = false;
+ for( unsigned i=0; i<pos.size(); i++ ){
+ if( pos[i]>0 ){
+ d_str_pos[i] += pos[i];
+ changed = true;
+ }
+ }
+ if (changed)
+ {
+ d_visit_role.clear();
+ }
+ return changed;
+}
+
+bool CegConjecturePbe::UnifContext::isReturnValueModified() {
+ if (d_has_string_pos != role_invalid)
+ {
+ return true;
+ }
+ return false;
+}
+
+bool CegConjecturePbe::UnifContext::isValidStrategy(EnumTypeInfoStrat* etis)
+{
+ StrategyType st = etis->d_this;
+ if (d_has_string_pos == role_string_prefix && st == strat_CONCAT_SUFFIX)
+ {
+ return false;
+ }
+ if (d_has_string_pos == role_string_suffix && st == strat_CONCAT_PREFIX)
+ {
+ return false;
+ }
+ return true;
+}
+
+void CegConjecturePbe::UnifContext::initialize( CegConjecturePbe * pbe, Node c ) {
+ Assert( d_vals.empty() );
+ Assert( d_str_pos.empty() );
+
+ // initialize with #examples
+ Assert( pbe->d_examples.find( c )!=pbe->d_examples.end() );
+ unsigned sz = pbe->d_examples[c].size();
+ for( unsigned i=0; i<sz; i++ ){
+ d_vals.push_back( pbe->d_true );
+ }
+
+ if( !pbe->d_examples_out[c].empty() ){
+ // output type of the examples
+ TypeNode exotn = pbe->d_examples_out[c][0].getType();
+
+ if( exotn.isString() ){
+ for( unsigned i=0; i<sz; i++ ){
+ d_str_pos.push_back( 0 );
+ }
+ }
+ }
+ d_visit_role.clear();
+}
+
+void CegConjecturePbe::UnifContext::getCurrentStrings(
+ CegConjecturePbe* pbe,
+ const std::vector<Node>& vals,
+ std::vector<String>& ex_vals)
+{
+ bool isPrefix = d_has_string_pos == role_string_prefix;
+ String dummy;
+ for( unsigned i=0; i<vals.size(); i++ ){
+ if( d_vals[i]==pbe->d_true ){
+ Assert( vals[i].isConst() );
+ unsigned pos_value = d_str_pos[i];
+ if( pos_value>0 ){
+ Assert(d_has_string_pos != role_invalid);
+ String s = vals[i].getConst<String>();
+ Assert( pos_value<=s.size() );
+ ex_vals.push_back( isPrefix ? s.suffix( s.size()-pos_value ) :
+ s.prefix( s.size()-pos_value ) );
+ }else{
+ ex_vals.push_back( vals[i].getConst<String>() );
+ }
+ }else{
+ // irrelevant, add dummy
+ ex_vals.push_back( dummy );
+ }
+ }
+}
+
+bool CegConjecturePbe::UnifContext::getStringIncrement(
+ CegConjecturePbe* pbe,
+ bool isPrefix,
+ const std::vector<String>& ex_vals,
+ const std::vector<Node>& vals,
+ std::vector<unsigned>& inc,
+ unsigned& tot)
+{
+ for( unsigned j=0; j<vals.size(); j++ ){
+ unsigned ival = 0;
+ if( d_vals[j]==pbe->d_true ){
+ // example is active in this context
+ Assert( vals[j].isConst() );
+ String mystr = vals[j].getConst<String>();
+ ival = mystr.size();
+ if( mystr.size()<=ex_vals[j].size() ){
+ if( !( isPrefix ? ex_vals[j].strncmp(mystr, ival) : ex_vals[j].rstrncmp(mystr, ival) ) ){
+ Trace("sygus-pbe-dt-debug") << "X";
+ return false;
+ }
+ }else{
+ Trace("sygus-pbe-dt-debug") << "X";
+ return false;
+ }
+ }
+ Trace("sygus-pbe-dt-debug") << ival;
+ tot += ival;
+ inc.push_back( ival );
+ }
+ return true;
+}
+bool CegConjecturePbe::UnifContext::isStringSolved(
+ CegConjecturePbe* pbe,
+ const std::vector<String>& ex_vals,
+ const std::vector<Node>& vals)
+{
+ for( unsigned j=0; j<vals.size(); j++ ){
+ if( d_vals[j]==pbe->d_true ){
+ // example is active in this context
+ Assert( vals[j].isConst() );
+ String mystr = vals[j].getConst<String>();
+ if( ex_vals[j]!=mystr ){
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+CegConjecturePbe::StrategyNode::~StrategyNode()
+{
+ for (unsigned j = 0, size = d_strats.size(); j < size; j++)
+ {
+ delete d_strats[j];
+ }
+ d_strats.clear();
+}
+}
+}
+}
--- /dev/null
+/********************* */
+/*! \file ce_guided_pbe.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2016 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief utility for processing programming by examples synthesis conjectures
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_PBE_H
+#define __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_PBE_H
+
+#include "context/cdhashmap.h"
+#include "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+/** roles for enumerators
+ *
+ * This indicates the role of an enumerator that is allocated by approaches
+ * for synthesis-by-unification (see details below).
+ * io : the enumerator should enumerate values that are overall solutions
+ * for the function-to-synthesize,
+ * ite_condition : the enumerator should enumerate values that are useful
+ * in ite conditions in the ITE strategy,
+ * concat_term : the enumerator should enumerate values that are used as
+ * components of string concatenation solutions.
+ */
+enum EnumRole
+{
+ enum_invalid,
+ enum_io,
+ enum_ite_condition,
+ enum_concat_term,
+};
+std::ostream& operator<<(std::ostream& os, EnumRole r);
+
+/** roles for strategy nodes
+ *
+ * This indicates the role of a strategy node, which is a subprocedure of
+ * CegConjecturePbe::constructSolution (see details below).
+ * equal : the node constructed must be equal to the overall solution for
+ * the function-to-synthesize,
+ * string_prefix/suffix : the node constructed must be a prefix/suffix
+ * of the function-to-synthesize,
+ * ite_condition : the node constructed must be a condition that makes some
+ * active input examples true and some input examples false.
+ */
+enum NodeRole
+{
+ role_invalid,
+ role_equal,
+ role_string_prefix,
+ role_string_suffix,
+ role_ite_condition,
+};
+std::ostream& operator<<(std::ostream& os, NodeRole r);
+
+/** enumerator role for node role */
+EnumRole getEnumeratorRoleForNodeRole(NodeRole r);
+
+/** strategy types
+ *
+ * This indicates a strategy for synthesis-by-unification (see details below).
+ * ITE : strategy for constructing if-then-else solutions via decision
+ * tree learning techniques,
+ * CONCAT_PREFIX/SUFFIX : strategy for constructing string concatenation
+ * solutions via a divide and conquer approach,
+ * ID : identity strategy used for calling strategies on child type through
+ * an identity function.
+ */
+enum StrategyType
+{
+ strat_INVALID,
+ strat_ITE,
+ strat_CONCAT_PREFIX,
+ strat_CONCAT_SUFFIX,
+ strat_ID,
+};
+std::ostream& operator<<(std::ostream& os, StrategyType st);
+
+class CegConjecture;
+
+/** CegConjecturePbe
+*
+* This class implements optimizations that target synthesis conjectures
+* that are in Programming-By-Examples (PBE) form.
+*
+* [EX#1] An example of a synthesis conjecture in PBE form is :
+* exists f. forall x.
+* ( x = 0 => f( x ) = 2 ) ^ ( x = 5 => f( x ) = 7 ) ^ ( x = 6 => f( x ) = 8 )
+*
+* We say that the above conjecture has I/O examples (0)->2, (5)->7, (6)->8.
+*
+* Internally, this class does the following for SyGuS inputs:
+*
+* (1) Infers whether the input conjecture is in PBE form or not.
+* (2) Based on this information and on the syntactic restrictions, it
+* devises a strategy for enumerating terms and construction solutions,
+* which is inspired by Alur et al. "Scaling Enumerative Program Synthesis
+* via Divide and Conquer" TACAS 2017. In particular, it may consider
+* strategies for constructing decision trees when the grammar permits ITEs
+* and a strategy for divide-and-conquer string synthesis when the grammar
+* permits string concatenation. This is stored in a set of data structures
+* within d_cinfo.
+* (3) It makes (possibly multiple) calls to
+* TermDatabaseSygus::registerMeasuredTerm(...) based
+* on the strategy, which inform the rest of the system to enumerate values
+* of particular types in the grammar through use of fresh variables which
+* we call "enumerators".
+*
+* Points (1)-(3) happen within a call to CegConjecturePbe::initialize(...).
+*
+* Notice that each enumerator is associated with a single
+* function-to-synthesize, but a function-to-sythesize may be mapped to multiple
+* enumerators. Some public functions of this class expect an enumerator as
+* input, which we map to a function-to-synthesize via
+* TermDatabaseSygus::getSynthFunFor(e).
+*
+* An enumerator is initially "active" but may become inactive if the enumeration
+* exhausts all possible values in the datatype corresponding to syntactic
+* restrictions for it. The search may continue unless all enumerators become
+* inactive.
+*
+* (4) During search, the extension of quantifier-free datatypes procedure for
+* SyGuS datatypes may ask this class whether current candidates can be
+* discarded based on
+* inferring when two candidate solutions are equivalent up to examples.
+* For example, the candidate solutions:
+* f = \x ite( x<0, x+1, x ) and f = \x x
+* are equivalent up to examples on the above conjecture, since they have the
+* same value on the points x = 0,5,6. Hence, we need only consider one of
+* them. The interface for querying this is
+* CegConjecturePbe::addSearchVal(...).
+* For details, see Reynolds et al. SYNT 2017.
+*
+* (5) When the extension of quantifier-free datatypes procedure for SyGuS
+* datatypes terminates with a model, the parent of this class calls
+* CegConjecturePbe::getCandidateList(...), where this class returns the list
+* of active enumerators.
+* (6) The parent class subsequently calls
+* CegConjecturePbe::constructValues(...), which
+* informs this class that new values have been enumerated for active
+* enumerators, as indicated by the current model. This call also requests
+* that based on these
+* newly enumerated values, whether this class is now able to construct a
+* solution based on the high-level strategy (stored in d_c_info).
+*
+* This class is not designed to work in incremental mode, since there is no way
+* to specify incremental problems in SyguS.
+*/
+class CegConjecturePbe {
+ public:
+ CegConjecturePbe(QuantifiersEngine* qe, CegConjecture* p);
+ ~CegConjecturePbe();
+
+ /** initialize this class
+ *
+ * n is the "base instantiation" of the deep-embedding version of
+ * the synthesis conjecture under "candidates".
+ * (see CegConjecture::d_base_inst)
+ *
+ * This function may add lemmas to the vector lemmas corresponding
+ * to initial lemmas regarding static analysis of enumerators it
+ * introduced. For example, we may say that the top-level symbol
+ * of an enumerator is not ITE if it is being used to construct
+ * return values for decision trees.
+ */
+ void initialize(Node n,
+ std::vector<Node>& candidates,
+ std::vector<Node>& lemmas);
+ /** get candidate list
+ * Adds all active enumerators associated with functions-to-synthesize in
+ * candidates to clist.
+ */
+ void getCandidateList(std::vector<Node>& candidates,
+ std::vector<Node>& clist);
+ /** construct candidates
+ * (1) Indicates that the list of enumerators in "enums" currently have model
+ * values "enum_values".
+ * (2) Asks whether based on these new enumerated values, we can construct a
+ * solution for
+ * the functions-to-synthesize in "candidates". If so, this function
+ * returns "true" and
+ * adds solutions for candidates into "candidate_values".
+ * During this class, this class may add auxiliary lemmas to "lems", which the
+ * caller should send on the output channel via lemma(...).
+ */
+ bool constructCandidates(std::vector<Node>& enums,
+ std::vector<Node>& enum_values,
+ std::vector<Node>& candidates,
+ std::vector<Node>& candidate_values,
+ std::vector<Node>& lems);
+ /** is PBE enabled for any enumerator? */
+ bool isPbe() { return d_is_pbe; }
+ /** is the enumerator e associated with I/O example pairs? */
+ bool hasExamples(Node e);
+ /** get number of I/O example pairs for enumerator e */
+ unsigned getNumExamples(Node e);
+ /** get the input arguments for i^th I/O example for e, which is added to the
+ * vector ex */
+ void getExample(Node e, unsigned i, std::vector<Node>& ex);
+ /** get the output value of the i^th I/O example for enumerator e */
+ Node getExampleOut(Node e, unsigned i);
+
+ /** add the search val
+ * This function is called by the extension of quantifier-free datatypes
+ * procedure for SyGuS datatypes when we are considering a value of
+ * enumerator e of sygus type tn whose analog in the signature of builtin
+ * theory is bvr.
+ *
+ * For example, bvr = x + 1 when e is the datatype value Plus( x(), One() ) and
+ * tn is a sygus datatype that encodes a subsignature of the integers.
+ *
+ * This returns either:
+ * - A SyGuS term whose analog is equivalent to bvr up to examples
+ * In the above example,
+ * it may return a term t of the form Plus( One(), x() ), such that this
+ * function was previously called with t as input.
+ * - e, indicating that no previous terms are equivalent to e up to examples.
+ */
+ Node addSearchVal(TypeNode tn, Node e, Node bvr);
+ /** evaluate builtin
+ * This returns the evaluation of bn on the i^th example for the
+ * function-to-synthesis
+ * associated with enumerator e. If there are not at least i examples, it
+ * returns the rewritten form of bn.
+ * For example, if bn = x+5, e is an enumerator for f in the above example
+ * [EX#1], then
+ * evaluateBuiltin( tn, bn, e, 0 ) = 7
+ * evaluateBuiltin( tn, bn, e, 1 ) = 9
+ * evaluateBuiltin( tn, bn, e, 2 ) = 10
+ */
+ Node evaluateBuiltin(TypeNode tn, Node bn, Node e, unsigned i);
+
+ private:
+ /** quantifiers engine associated with this class */
+ QuantifiersEngine* d_qe;
+ /** sygus term database of d_qe */
+ quantifiers::TermDbSygus * d_tds;
+ /** true and false nodes */
+ Node d_true;
+ Node d_false;
+ /** A reference to the conjecture that owns this class. */
+ CegConjecture* d_parent;
+ /** is this a PBE conjecture for any function? */
+ bool d_is_pbe;
+ /** for each candidate variable f (a function-to-synthesize), whether the
+ * conjecture is purely PBE for that variable
+ * In other words, all occurrences of f are guarded by equalities that
+ * constraint its arguments to constants.
+ */
+ std::map< Node, bool > d_examples_invalid;
+ /** for each candidate variable (function-to-synthesize), whether the
+ * conjecture is purely PBE for that variable.
+ * An example of a conjecture for which d_examples_invalid is false but
+ * d_examples_out_invalid is true is:
+ * exists f. forall x. ( x = 0 => f( x ) > 2 )
+ * another example is:
+ * exists f. forall x. ( ( x = 0 => f( x ) = 2 ) V ( x = 3 => f( x ) = 3 ) )
+ * since the formula is not a conjunction (the example values are not
+ * entailed).
+ * However, the domain of f in both cases is finite, which can be used for
+ * search space pruning.
+ */
+ std::map< Node, bool > d_examples_out_invalid;
+ /** for each candidate variable (function-to-synthesize), input of I/O
+ * examples */
+ std::map< Node, std::vector< std::vector< Node > > > d_examples;
+ /** for each candidate variable (function-to-synthesize), output of I/O
+ * examples */
+ std::map< Node, std::vector< Node > > d_examples_out;
+ /** the list of example terms
+ * For the example [EX#1] above, this is f( 0 ), f( 5 ), f( 6 )
+ */
+ std::map< Node, std::vector< Node > > d_examples_term;
+ /** collect the PBE examples in n
+ * This is called on the input conjecture, and will populate the above vectors.
+ * hasPol/pol denote the polarity of n in the conjecture.
+ */
+ void collectExamples( Node n, std::map< Node, bool >& visited, bool hasPol, bool pol );
+
+ //--------------------------------- PBE search values
+ /** this class is an index of candidate solutions for PBE synthesis */
+ class PbeTrie {
+ public:
+ PbeTrie() {}
+ ~PbeTrie() {}
+ Node d_lazy_child;
+ std::map<Node, PbeTrie> d_children;
+ void clear() { d_children.clear(); }
+ Node addPbeExample(TypeNode etn, Node e, Node b, CegConjecturePbe* cpbe,
+ unsigned index, unsigned ntotal);
+
+ private:
+ Node addPbeExampleEval(TypeNode etn, Node e, Node b, std::vector<Node>& ex,
+ CegConjecturePbe* cpbe, unsigned index,
+ unsigned ntotal);
+ };
+ /** trie of candidate solutions tried
+ * This stores information for each (enumerator, type),
+ * where type is a type in the grammar of the space of solutions for a subterm
+ * of e. This is used for symmetry breaking in quantifier-free reasoning
+ * about SyGuS datatypes.
+ */
+ std::map<Node, std::map<TypeNode, PbeTrie> > d_pbe_trie;
+ //--------------------------------- end PBE search values
+
+ // -------------------------------- decision tree learning
+ // index filter
+ class IndexFilter {
+ public:
+ IndexFilter(){}
+ void mk( std::vector< Node >& vals, bool pol = true );
+ std::map< unsigned, unsigned > d_next;
+ unsigned start();
+ unsigned next( unsigned i );
+ void clear() { d_next.clear(); }
+ bool isEq( std::vector< Node >& vs, Node v );
+ };
+ // subsumption trie
+ class SubsumeTrie {
+ public:
+ SubsumeTrie(){}
+ // adds term to the trie, removes based on subsumption
+ Node addTerm( CegConjecturePbe * pbe, Node t, std::vector< Node >& vals, bool pol, std::vector< Node >& subsumed, IndexFilter * f = NULL );
+ // adds condition to the trie (does not do subsumption)
+ Node addCond( CegConjecturePbe * pbe, Node c, std::vector< Node >& vals, bool pol, IndexFilter * f = NULL );
+ // returns the set of terms that are subsets of vals
+ void getSubsumed( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::vector< Node >& subsumed, IndexFilter * f = NULL );
+ // returns the set of terms that are supersets of vals
+ void getSubsumedBy( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::vector< Node >& subsumed_by, IndexFilter * f = NULL );
+ // v[-1,1,0] -> children always false, always true, both
+ void getLeaves( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::map< int, std::vector< Node > >& v, IndexFilter * f = NULL );
+ /** is this trie empty? */
+ bool isEmpty() { return d_term.isNull() && d_children.empty(); }
+ /** clear this trie */
+ void clear() {
+ d_term = Node::null();
+ d_children.clear();
+ }
+
+ private:
+ /** the term at this node */
+ Node d_term;
+ /** the children nodes of this trie */
+ std::map<Node, SubsumeTrie> d_children;
+ /** helper function for above functions */
+ Node addTermInternal(CegConjecturePbe* pbe,
+ Node t,
+ std::vector<Node>& vals,
+ bool pol,
+ std::vector<Node>& subsumed,
+ bool spol,
+ IndexFilter* f,
+ unsigned index,
+ int status,
+ bool checkExistsOnly,
+ bool checkSubsume);
+ /** helper function for above functions */
+ void getLeavesInternal(CegConjecturePbe* pbe,
+ std::vector<Node>& vals,
+ bool pol,
+ std::map<int, std::vector<Node> >& v,
+ IndexFilter* f,
+ unsigned index,
+ int status);
+ };
+ // -------------------------------- end decision tree learning
+
+ //------------------------------ representation of a enumeration strategy
+
+ /** information about an enumerator
+ *
+ * We say an enumerator is a master enumerator if it is the variable that
+ * we use to enumerate values for its sort. Master enumerators may have
+ * (possibly multiple) slave enumerators, stored in d_enum_slave,
+ */
+ class EnumInfo {
+ public:
+ EnumInfo() : d_role(enum_io), d_is_conditional(false) {}
+ /** initialize this class
+ * c is the parent function-to-synthesize
+ * role is the "role" the enumerator plays in the high-level strategy,
+ * which is one of enum_* above.
+ */
+ void initialize(Node c, EnumRole role);
+ /** is this enumerator associated with a template? */
+ bool isTemplated() { return !d_template.isNull(); }
+ /** set conditional
+ *
+ * This flag is set to true if this enumerator may not apply to all
+ * input/output examples. For example, if this enumerator is used
+ * as an output value beneath a conditional in an instance of strat_ITE,
+ * then this enumerator is conditional.
+ */
+ void setConditional() { d_is_conditional = true; }
+ /** is conditional */
+ bool isConditional() { return d_is_conditional; }
+ void addEnumValue(CegConjecturePbe* pbe,
+ Node v,
+ std::vector<Node>& results);
+ void setSolved(Node slv);
+ bool isSolved() { return !d_enum_solved.isNull(); }
+ Node getSolved() { return d_enum_solved; }
+ EnumRole getRole() { return d_role; }
+ Node d_parent_candidate;
+ // for template
+ Node d_template;
+ Node d_template_arg;
+
+ Node d_active_guard;
+ std::vector<Node> d_enum_slave;
+ /** values we have enumerated */
+ std::vector<Node> d_enum_vals;
+ /**
+ * This either stores the values of f( I ) for inputs
+ * or the value of f( I ) = O if d_role==enum_io
+ */
+ std::vector<std::vector<Node> > d_enum_vals_res;
+ std::vector<Node> d_enum_subsume;
+ std::map<Node, unsigned> d_enum_val_to_index;
+ SubsumeTrie d_term_trie;
+
+ private:
+ /**
+ * Whether an enumerated value for this conjecture has solved the entire
+ * conjecture.
+ */
+ Node d_enum_solved;
+ /** the role of this enumerator (one of enum_* above). */
+ EnumRole d_role;
+ /** is this enumerator conditional */
+ bool d_is_conditional;
+ };
+ /** maps enumerators to the information above */
+ std::map< Node, EnumInfo > d_einfo;
+
+ class CandidateInfo;
+
+ /** represents a strategy for a SyGuS datatype type
+ *
+ * This represents a possible strategy to apply when processing a strategy
+ * node in constructSolution. When applying the strategy represented by this
+ * class, we may make recursive calls to the children of the strategy,
+ * given in d_cenum. If all recursive calls to constructSolution are
+ * successful, say:
+ * constructSolution( c, d_cenum[1], ... ) = t1,
+ * ...,
+ * constructSolution( c, d_cenum[n], ... ) = tn,
+ * Then, the solution returned by this strategy is
+ * d_sol_templ * { d_sol_templ_args -> (t1,...,tn) }
+ */
+ class EnumTypeInfoStrat {
+ public:
+ /** the type of strategy this represents */
+ StrategyType d_this;
+ /** the sygus datatype constructor that induced this strategy
+ *
+ * For example, this may be a sygus datatype whose sygus operator is ITE,
+ * if the strategy type above is strat_ITE.
+ */
+ Node d_cons;
+ /** children of this strategy */
+ std::vector<std::pair<Node, NodeRole> > d_cenum;
+ /** the arguments for the (templated) solution */
+ std::vector<Node> d_sol_templ_args;
+ /** the template for the solution */
+ Node d_sol_templ;
+ };
+
+ /** represents a node in the strategy graph
+ *
+ * It contains a list of possible strategies which are tried during calls
+ * to constructSolution.
+ */
+ class StrategyNode
+ {
+ public:
+ StrategyNode() {}
+ ~StrategyNode();
+ /** the set of strategies to try at this node in the strategy graph */
+ std::vector<EnumTypeInfoStrat*> d_strats;
+ };
+
+ /** stores enumerators and strategies for a SyGuS datatype type */
+ class EnumTypeInfo {
+ public:
+ EnumTypeInfo() : d_parent( NULL ){}
+ /** the parent candidate info (see below) */
+ CandidateInfo * d_parent;
+ /** the type that this information is for */
+ TypeNode d_this_type;
+ /** map from enum roles to enumerators for this type */
+ std::map<EnumRole, Node> d_enum;
+ /** map from node roles to strategy nodes */
+ std::map<NodeRole, StrategyNode> d_snodes;
+ };
+
+ /** stores strategy and enumeration information for a function-to-synthesize
+ */
+ class CandidateInfo {
+ public:
+ CandidateInfo() : d_check_sol( false ), d_cond_count( 0 ){}
+ Node d_this_candidate;
+ /**
+ * The root sygus datatype for the function-to-synthesize,
+ * which encodes the overall syntactic restrictions on the space
+ * of solutions.
+ */
+ TypeNode d_root;
+ /** Info for sygus datatype type occurring in a field of d_root */
+ std::map< TypeNode, EnumTypeInfo > d_tinfo;
+ /** list of all enumerators for the function-to-synthesize */
+ std::vector< Node > d_esym_list;
+ /**
+ * Maps sygus datatypes to their search enumerator. This is the (single)
+ * enumerator of that type that we enumerate values for.
+ */
+ std::map< TypeNode, Node > d_search_enum;
+ bool d_check_sol;
+ unsigned d_cond_count;
+ Node d_solution;
+ void initialize( Node c );
+ void initializeType( TypeNode tn );
+ Node getRootEnumerator();
+ bool isNonTrivial();
+ };
+ /** maps a function-to-synthesize to the above information */
+ std::map< Node, CandidateInfo > d_cinfo;
+
+ //------------------------------ representation of an enumeration strategy
+ /** add enumerated value
+ *
+ * We have enumerated the value v for x. This function adds x->v to the
+ * relevant data structures that are used for strategy-specific construction
+ * of solutions when necessary, and returns a set of lemmas, which are added
+ * to the input argument lems. These lemmas are used to rule out models where
+ * x = v, to force that a new value is enumerated for x.
+ */
+ void addEnumeratedValue( Node x, Node v, std::vector< Node >& lems );
+ /** domain-specific enumerator exclusion techniques
+ *
+ * Returns true if the value v for x can be excluded based on a
+ * domain-specific exclusion technique like the ones below.
+ *
+ * c : the candidate variable that x is enumerating for,
+ * results : the values of v under the input examples of c,
+ * ei : the enumerator information for x,
+ * exp : if this function returns true, then exp contains a (possibly
+ * generalize) explanation for why v can be excluded.
+ */
+ bool getExplanationForEnumeratorExclude( Node c, Node x, Node v, std::vector< Node >& results, EnumInfo& ei, std::vector< Node >& exp );
+ /** returns true if we can exlude values of x based on negative str.contains
+ *
+ * Values v for x may be excluded if we realize that the value of v under the
+ * substitution for some input example will never be contained in some output
+ * example. For details on this technique, see NegContainsSygusInvarianceTest
+ * in sygus_invariance.h.
+ *
+ * This function depends on whether x is being used to enumerate values
+ * for any node that is conditional in the strategy graph. For example,
+ * nodes that are children of ITE strategy nodes are conditional. If any node
+ * is conditional, then this function returns false.
+ */
+ bool useStrContainsEnumeratorExclude(Node x, EnumInfo& ei);
+ /** cache for the above function */
+ std::map<Node, bool> d_use_str_contains_eexc;
+
+ //------------------------------ strategy registration
+ /** collect enumerator types
+ *
+ * This builds the strategy for enumerated values of type tn for the given
+ * role of nrole, for solutions to function-to-synthesize c.
+ */
+ void collectEnumeratorTypes(Node c, TypeNode tn, NodeRole nrole);
+ /** register enumerator
+ *
+ * This registers that et is an enumerator for function-to-synthesize c
+ * of type tn, having enumerator role enum_role.
+ *
+ * inSearch is whether we will enumerate values based on this enumerator.
+ * A strategy node is represented by a (enumerator, node role) pair. Hence,
+ * we may use enumerators for which this flag is false to represent strategy
+ * nodes that have child strategies.
+ */
+ void registerEnumerator(
+ Node et, Node c, TypeNode tn, EnumRole enum_role, bool inSearch);
+ /** infer template */
+ bool inferTemplate(unsigned k,
+ Node n,
+ std::map<Node, unsigned>& templ_var_index,
+ std::map<unsigned, unsigned>& templ_injection);
+ /** static learn redundant operators
+ *
+ * This learns static lemmas for pruning enumerative space based on the
+ * strategy for the function-to-synthesize c, and stores these into lemmas.
+ */
+ void staticLearnRedundantOps(Node c, std::vector<Node>& lemmas);
+ /** helper for static learn redundant operators
+ *
+ * (e, nrole) specify the strategy node in the graph we are currently
+ * analyzing, visited stores the nodes we have already visited.
+ *
+ * This method builds the mapping needs_cons, which maps (master) enumerators
+ * to a map from the constructors that it needs.
+ *
+ * ind is the depth in the strategy graph we are at (for debugging).
+ *
+ * isCond is whether the current enumerator is conditional (beneath a
+ * conditional of an strat_ITE strategy).
+ */
+ void staticLearnRedundantOps(
+ Node c,
+ Node e,
+ NodeRole nrole,
+ std::map<Node, std::map<NodeRole, bool> >& visited,
+ std::map<Node, std::map<unsigned, bool> >& needs_cons,
+ int ind,
+ bool isCond);
+ //------------------------------ end strategy registration
+
+ //------------------------------ constructing solutions
+ class UnifContext {
+ public:
+ UnifContext() : d_has_string_pos(role_invalid) {}
+ /** this intiializes this context for function-to-synthesize c */
+ void initialize(CegConjecturePbe* pbe, Node c);
+
+ //----------for ITE strategy
+ /** the value of the context conditional
+ *
+ * This stores a list of Boolean constants that is the same length of the
+ * number of input/output example pairs we are considering. For each i,
+ * if d_vals[i] = true, i/o pair #i is active according to this context
+ * if d_vals[i] = false, i/o pair #i is inactive according to this context
+ */
+ std::vector<Node> d_vals;
+ /** update the examples
+ *
+ * if pol=true, this method updates d_vals to d_vals & vals
+ * if pol=false, this method updates d_vals to d_vals & ( ~vals )
+ */
+ bool updateContext(CegConjecturePbe* pbe, std::vector<Node>& vals, bool pol);
+ //----------end for ITE strategy
+
+ //----------for CONCAT strategies
+ /** the position in the strings
+ *
+ * For each i/o example pair, this stores the length of the current solution
+ * for the input of the pair, where the solution for that input is a prefix
+ * or
+ * suffix of the output of the pair. For example, if our i/o pairs are:
+ * f( "abcd" ) = "abcdcd"
+ * f( "aa" ) = "aacd"
+ * If the solution we have currently constructed is str.++( x1, "c", ... ),
+ * then d_str_pos = ( 5, 3 ), where notice that
+ * str.++( "abc", "c" ) is a prefix of "abcdcd" and
+ * str.++( "aa", "c" ) is a prefix of "aacd".
+ */
+ std::vector<unsigned> d_str_pos;
+ /** has string position
+ *
+ * Whether the solution positions indicate a prefix or suffix of the output
+ * examples. If this is role_invalid, then we have not updated the string
+ * position.
+ */
+ NodeRole d_has_string_pos;
+ /** update the string examples
+ *
+ * This method updates d_str_pos to d_str_pos + pos.
+ */
+ bool updateStringPosition(CegConjecturePbe* pbe, std::vector<unsigned>& pos);
+ /** get current strings
+ *
+ * This returns the prefix/suffix of the string constants stored in vals
+ * of size d_str_pos, and stores the result in ex_vals. For example, if vals
+ * is (abcdcd", "aacde") and d_str_pos = ( 5, 3 ), then we add
+ * "d" and "de" to ex_vals.
+ */
+ void getCurrentStrings(CegConjecturePbe* pbe,
+ const std::vector<Node>& vals,
+ std::vector<String>& ex_vals);
+ /** get string increment
+ *
+ * If this method returns true, then inc and tot are updated such that
+ * for all active indices i,
+ * vals[i] is a prefix (or suffix if isPrefix=false) of ex_vals[i], and
+ * inc[i] = str.len(vals[i])
+ * for all inactive indices i, inc[i] = 0
+ * We set tot to the sum of inc[i] for i=1,...,n. This indicates the total
+ * number of characters incremented across all examples.
+ */
+ bool getStringIncrement(CegConjecturePbe* pbe,
+ bool isPrefix,
+ const std::vector<String>& ex_vals,
+ const std::vector<Node>& vals,
+ std::vector<unsigned>& inc,
+ unsigned& tot);
+ /** returns true if ex_vals[i] = vals[i] for all active indices i. */
+ bool isStringSolved(CegConjecturePbe* pbe,
+ const std::vector<String>& ex_vals,
+ const std::vector<Node>& vals);
+ //----------end for CONCAT strategies
+
+ /** is return value modified?
+ *
+ * This returns true if we are currently in a state where the return value
+ * of the solution has been modified, e.g. by a previous node that solved
+ * for a prefix.
+ */
+ bool isReturnValueModified();
+ /** returns true if argument is valid strategy in this context */
+ bool isValidStrategy(EnumTypeInfoStrat* etis);
+ /** visited role
+ *
+ * This is the current set of enumerator/node role pairs we are currently
+ * visiting. This set is cleared when the context is updated.
+ */
+ std::map<Node, std::map<NodeRole, bool> > d_visit_role;
+
+ /** unif context enumerator information */
+ class UEnumInfo
+ {
+ public:
+ UEnumInfo() {}
+ /** map from conditions and branch positions to a solved node
+ *
+ * For example, if we have:
+ * f( 1 ) = 2 ^ f( 3 ) = 4 ^ f( -1 ) = 1
+ * Then, valid entries in this map is:
+ * d_look_ahead_sols[x>0][1] = x+1
+ * d_look_ahead_sols[x>0][2] = 1
+ * For the first entry, notice that for all input examples such that x>0
+ * evaluates to true, which are (1) and (3), we have that their output
+ * values for x+1 under the substitution that maps x to the input value,
+ * resulting in 2 and 4, are equal to the output value for the respective
+ * pairs.
+ */
+ std::map<Node, std::map<unsigned, Node> > d_look_ahead_sols;
+ };
+ /** map from enumerators to the above info class */
+ std::map< Node, UEnumInfo > d_uinfo;
+ };
+
+ /** construct solution
+ *
+ * This method tries to construct a solution for function-to-synthesize c
+ * based on the strategy stored for c in d_cinfo, which may include
+ * synthesis-by-unification approaches for ite and string concatenation terms.
+ * These approaches include the work of Alur et al. TACAS 2017.
+ * If it cannot construct a solution, it returns the null node.
+ */
+ Node constructSolution( Node c );
+ /** helper function for construct solution.
+ *
+ * Construct a solution based on enumerator e for function-to-synthesize c
+ * with node role nrole in context x.
+ *
+ * ind is the term depth of the context (for debugging).
+ */
+ Node constructSolution(
+ Node c, Node e, NodeRole nrole, UnifContext& x, int ind);
+ /** Heuristically choose the best solved term from solved in context x,
+ * currently return the first. */
+ Node constructBestSolvedTerm( std::vector< Node >& solved, UnifContext& x );
+ /** Heuristically choose the best solved string term from solved in context
+ * x, currently return the first. */
+ Node constructBestStringSolvedTerm( std::vector< Node >& solved, UnifContext& x );
+ /** Heuristically choose the best solved conditional term from solved in
+ * context x, currently random */
+ Node constructBestSolvedConditional( std::vector< Node >& solved, UnifContext& x );
+ /** Heuristically choose the best conditional term from conds in context x,
+ * currently random */
+ Node constructBestConditional( std::vector< Node >& conds, UnifContext& x );
+ /** Heuristically choose the best string to concatenate from strs to the
+ * solution in context x, currently random
+ * incr stores the vector of indices that are incremented by this solution in
+ * example outputs.
+ * total_inc[x] is the sum of incr[x] for each x in strs.
+ */
+ Node constructBestStringToConcat( std::vector< Node > strs,
+ std::map< Node, unsigned > total_inc,
+ std::map< Node, std::vector< unsigned > > incr,
+ UnifContext& x );
+ //------------------------------ end constructing solutions
+};
+
+}/* namespace CVC4::theory::quantifiers */
+}/* namespace CVC4::theory */
+}/* namespace CVC4 */
+
+#endif
--- /dev/null
+/********************* */
+/*! \file sygus_process_conj.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief Implementation of techniqures for static preprocessing and analysis
+ ** of sygus conjectures.
+ **/
+#include "theory/quantifiers/sygus/sygus_process_conj.h"
+
+#include <stack>
+
+#include "expr/datatype.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+#include "theory/quantifiers/term_util.h"
+
+using namespace CVC4::kind;
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+void CegConjectureProcessFun::init(Node f)
+{
+ d_synth_fun = f;
+ Assert(f.getType().isFunction());
+
+ // initialize the arguments
+ std::unordered_map<TypeNode, unsigned, TypeNodeHashFunction>
+ type_to_init_deq_id;
+ std::vector<Type> argTypes =
+ static_cast<FunctionType>(f.getType().toType()).getArgTypes();
+ for (unsigned j = 0; j < argTypes.size(); j++)
+ {
+ TypeNode atn = TypeNode::fromType(argTypes[j]);
+ std::stringstream ss;
+ ss << "a" << j;
+ Node k = NodeManager::currentNM()->mkBoundVar(ss.str(), atn);
+ d_arg_vars.push_back(k);
+ d_arg_var_num[k] = j;
+ d_arg_props.push_back(CegConjectureProcessArg());
+ }
+}
+
+bool CegConjectureProcessFun::checkMatch(
+ Node cn, Node n, std::unordered_map<unsigned, Node>& n_arg_map)
+{
+ std::vector<Node> vars;
+ std::vector<Node> subs;
+ for (std::unordered_map<unsigned, Node>::iterator it = n_arg_map.begin();
+ it != n_arg_map.end();
+ ++it)
+ {
+ Assert(it->first < d_arg_vars.size());
+ Assert(
+ it->second.getType().isComparableTo(d_arg_vars[it->first].getType()));
+ vars.push_back(d_arg_vars[it->first]);
+ subs.push_back(it->second);
+ }
+ Node cn_subs =
+ cn.substitute(vars.begin(), vars.end(), subs.begin(), subs.end());
+ cn_subs = Rewriter::rewrite(cn_subs);
+ Assert(Rewriter::rewrite(n) == n);
+ return cn_subs == n;
+}
+
+bool CegConjectureProcessFun::isArgVar(Node n, unsigned& arg_index)
+{
+ if (n.isVar())
+ {
+ std::unordered_map<Node, unsigned, NodeHashFunction>::iterator ita =
+ d_arg_var_num.find(n);
+ if (ita != d_arg_var_num.end())
+ {
+ arg_index = ita->second;
+ return true;
+ }
+ }
+ return false;
+}
+
+Node CegConjectureProcessFun::inferDefinition(
+ Node n,
+ std::unordered_map<Node, unsigned, NodeHashFunction>& term_to_arg_carry,
+ std::unordered_map<Node,
+ std::unordered_set<Node, NodeHashFunction>,
+ NodeHashFunction>& free_vars)
+{
+ std::unordered_map<TNode, Node, TNodeHashFunction> visited;
+ std::unordered_map<TNode, Node, TNodeHashFunction>::iterator it;
+ std::stack<TNode> visit;
+ TNode cur;
+ visit.push(n);
+ do
+ {
+ cur = visit.top();
+ visit.pop();
+ it = visited.find(cur);
+ if (it == visited.end())
+ {
+ // if it is ground, we can use it
+ if (free_vars[cur].empty())
+ {
+ visited[cur] = cur;
+ }
+ else
+ {
+ // if it is term used by another argument, use it
+ std::unordered_map<Node, unsigned, NodeHashFunction>::iterator itt =
+ term_to_arg_carry.find(cur);
+ if (itt != term_to_arg_carry.end())
+ {
+ visited[cur] = d_arg_vars[itt->second];
+ }
+ else if (cur.getNumChildren() > 0)
+ {
+ // try constructing children
+ visited[cur] = Node::null();
+ visit.push(cur);
+ for (unsigned i = 0; i < cur.getNumChildren(); i++)
+ {
+ visit.push(cur[i]);
+ }
+ }
+ else
+ {
+ return Node::null();
+ }
+ }
+ }
+ else if (it->second.isNull())
+ {
+ Node ret = cur;
+ bool childChanged = false;
+ std::vector<Node> children;
+ if (cur.getMetaKind() == kind::metakind::PARAMETERIZED)
+ {
+ children.push_back(cur.getOperator());
+ }
+ for (unsigned i = 0; i < cur.getNumChildren(); i++)
+ {
+ it = visited.find(cur[i]);
+ Assert(it != visited.end());
+ Assert(!it->second.isNull());
+ childChanged = childChanged || cur[i] != it->second;
+ children.push_back(it->second);
+ }
+ if (childChanged)
+ {
+ ret = NodeManager::currentNM()->mkNode(cur.getKind(), children);
+ }
+ visited[cur] = ret;
+ }
+ } while (!visit.empty());
+ Assert(visited.find(n) != visited.end());
+ Assert(!visited.find(n)->second.isNull());
+ return visited[n];
+}
+
+unsigned CegConjectureProcessFun::assignRelevantDef(Node def,
+ std::vector<unsigned>& args)
+{
+ unsigned id = 0;
+ if (def.isNull())
+ {
+ // prefer one that already has a definition, if one exists
+ for (unsigned j = 0; j < args.size(); j++)
+ {
+ unsigned i = args[j];
+ if (!d_arg_props[i].d_template.isNull())
+ {
+ id = j;
+ break;
+ }
+ }
+ }
+ unsigned rid = args[id];
+ // for merging previously equivalent definitions
+ std::unordered_map<Node, unsigned, NodeHashFunction> prev_defs;
+ for (unsigned j = 0; j < args.size(); j++)
+ {
+ unsigned i = args[j];
+ Trace("sygus-process-arg-deps") << " ...processed arg #" << i;
+ if (!d_arg_props[i].d_template.isNull())
+ {
+ if (d_arg_props[i].d_template == def)
+ {
+ // definition was consistent
+ }
+ else
+ {
+ Node t = d_arg_props[i].d_template;
+ std::unordered_map<Node, unsigned, NodeHashFunction>::iterator itt =
+ prev_defs.find(t);
+ if (itt != prev_defs.end())
+ {
+ // merge previously equivalent definitions
+ d_arg_props[i].d_template = d_arg_vars[itt->second];
+ Trace("sygus-process-arg-deps")
+ << " (merged equivalent def from argument ";
+ Trace("sygus-process-arg-deps") << itt->second << ")." << std::endl;
+ }
+ else
+ {
+ // store this as previous
+ prev_defs[t] = i;
+ // now must be relevant
+ d_arg_props[i].d_relevant = true;
+ Trace("sygus-process-arg-deps")
+ << " (marked relevant, overwrite definition)." << std::endl;
+ }
+ }
+ }
+ else
+ {
+ if (def.isNull())
+ {
+ if (i != rid)
+ {
+ // marked as relevant, but template can be set equal to master
+ d_arg_props[i].d_template = d_arg_vars[rid];
+ Trace("sygus-process-arg-deps") << " (new definition, map to master "
+ << d_arg_vars[rid] << ")."
+ << std::endl;
+ }
+ else
+ {
+ d_arg_props[i].d_relevant = true;
+ Trace("sygus-process-arg-deps") << " (marked relevant)." << std::endl;
+ }
+ }
+ else
+ {
+ // has new definition
+ d_arg_props[i].d_template = def;
+ Trace("sygus-process-arg-deps") << " (new definition " << def << ")."
+ << std::endl;
+ }
+ }
+ }
+ return rid;
+}
+
+void CegConjectureProcessFun::processTerms(
+ std::vector<Node>& ns,
+ std::vector<Node>& ks,
+ Node nf,
+ std::unordered_set<Node, NodeHashFunction>& synth_fv,
+ std::unordered_map<Node,
+ std::unordered_set<Node, NodeHashFunction>,
+ NodeHashFunction>& free_vars)
+{
+ Assert(ns.size() == ks.size());
+ Trace("sygus-process-arg-deps") << "Process " << ns.size()
+ << " applications of " << d_synth_fun << "..."
+ << std::endl;
+
+ // get the relevant variables
+ // relevant variables are those that appear in the body of the conjunction
+ std::unordered_set<Node, NodeHashFunction> rlv_vars;
+ Assert(free_vars.find(nf) != free_vars.end());
+ rlv_vars = free_vars[nf];
+
+ // get the single occurrence variables
+ // single occurrence variables are those that appear in only one position,
+ // as an argument to the function-to-synthesize.
+ std::unordered_map<Node, bool, NodeHashFunction> single_occ_variables;
+ for (unsigned index = 0; index < ns.size(); index++)
+ {
+ Node n = ns[index];
+ for (unsigned i = 0; i < n.getNumChildren(); i++)
+ {
+ Node nn = n[i];
+ if (nn.isVar())
+ {
+ std::unordered_map<Node, bool, NodeHashFunction>::iterator its =
+ single_occ_variables.find(nn);
+ if (its == single_occ_variables.end())
+ {
+ // only irrelevant variables
+ single_occ_variables[nn] = rlv_vars.find(nn) == rlv_vars.end();
+ }
+ else
+ {
+ single_occ_variables[nn] = false;
+ }
+ }
+ else
+ {
+ std::unordered_map<Node,
+ std::unordered_set<Node, NodeHashFunction>,
+ NodeHashFunction>::iterator itf = free_vars.find(nn);
+ Assert(itf != free_vars.end());
+ for (std::unordered_set<Node, NodeHashFunction>::iterator itfv =
+ itf->second.begin();
+ itfv != itf->second.end();
+ ++itfv)
+ {
+ single_occ_variables[*itfv] = false;
+ }
+ }
+ }
+ }
+
+ // update constant argument information
+ for (unsigned index = 0; index < ns.size(); index++)
+ {
+ Node n = ns[index];
+ Trace("sygus-process-arg-deps")
+ << " Analyze argument information for application #" << index << ": "
+ << n << std::endl;
+
+ // in the following, we say an argument a "carries" a term t if
+ // the function to synthesize would use argument a to construct
+ // the term t in its definition.
+
+ // map that assumes all arguments carry their respective term
+ std::unordered_map<unsigned, Node> n_arg_map;
+ // terms to the argument that is carrying it.
+ // the arguments in the range of this map must be marked as relevant.
+ std::unordered_map<Node, unsigned, NodeHashFunction> term_to_arg_carry;
+ // map of terms to (unprocessed) arguments where it occurs
+ std::unordered_map<Node, std::vector<unsigned>, NodeHashFunction>
+ term_to_args;
+
+ // initialize
+ for (unsigned a = 0; a < n.getNumChildren(); a++)
+ {
+ n_arg_map[a] = n[a];
+ }
+
+ for (unsigned a = 0; a < n.getNumChildren(); a++)
+ {
+ bool processed = false;
+ if (d_arg_props[a].d_relevant)
+ {
+ // we can assume all relevant arguments carry their terms
+ processed = true;
+ Trace("sygus-process-arg-deps") << " ...processed arg #" << a
+ << " (already relevant)." << std::endl;
+ if (term_to_arg_carry.find(n[a]) == term_to_arg_carry.end())
+ {
+ Trace("sygus-process-arg-deps") << " carry " << n[a]
+ << " by argument #" << a << std::endl;
+ term_to_arg_carry[n[a]] = a;
+ }
+ }
+ else
+ {
+ // first, check if single occurrence variable
+ // check if an irrelevant variable
+ if (n[a].isVar() && synth_fv.find(n[a]) != synth_fv.end())
+ {
+ Assert(single_occ_variables.find(n[a]) != single_occ_variables.end());
+ // may be able to make this more precise?
+ // check if a single-occurrence variable
+ if (single_occ_variables[n[a]])
+ {
+ // if we do not already have a template definition, or the
+ // template is a single occurrence variable
+ if (d_arg_props[a].d_template.isNull()
+ || d_arg_props[a].d_var_single_occ)
+ {
+ processed = true;
+ Trace("sygus-process-arg-deps") << " ...processed arg #" << a;
+ Trace("sygus-process-arg-deps")
+ << " (single occurrence variable ";
+ Trace("sygus-process-arg-deps") << n[a] << ")." << std::endl;
+ d_arg_props[a].d_var_single_occ = true;
+ d_arg_props[a].d_template = n[a];
+ }
+ }
+ }
+ if (!processed && !d_arg_props[a].d_template.isNull()
+ && !d_arg_props[a].d_var_single_occ)
+ {
+ // argument already has a definition, see if it is maintained
+ if (checkMatch(d_arg_props[a].d_template, n[a], n_arg_map))
+ {
+ processed = true;
+ Trace("sygus-process-arg-deps") << " ...processed arg #" << a;
+ Trace("sygus-process-arg-deps") << " (consistent definition "
+ << n[a];
+ Trace("sygus-process-arg-deps")
+ << " with " << d_arg_props[a].d_template << ")." << std::endl;
+ }
+ }
+ }
+ if (!processed)
+ {
+ // otherwise, add it to the list of arguments for this term
+ term_to_args[n[a]].push_back(a);
+ }
+ }
+
+ Trace("sygus-process-arg-deps") << " Look at argument terms..."
+ << std::endl;
+
+ // list of all arguments
+ std::vector<Node> arg_list;
+ // now look at the terms for unprocessed arguments
+ for (std::unordered_map<Node, std::vector<unsigned>, NodeHashFunction>::
+ iterator it = term_to_args.begin();
+ it != term_to_args.end();
+ ++it)
+ {
+ Node nn = it->first;
+ arg_list.push_back(nn);
+ if (Trace.isOn("sygus-process-arg-deps"))
+ {
+ Trace("sygus-process-arg-deps") << " argument " << nn;
+ Trace("sygus-process-arg-deps") << " (" << it->second.size()
+ << " positions)";
+ // check the status of this term
+ if (nn.isVar() && synth_fv.find(nn) != synth_fv.end())
+ {
+ // is it relevant?
+ if (rlv_vars.find(nn) != rlv_vars.end())
+ {
+ Trace("sygus-process-arg-deps") << " is a relevant variable."
+ << std::endl;
+ }
+ else
+ {
+ Trace("sygus-process-arg-deps") << " is an irrelevant variable."
+ << std::endl;
+ }
+ }
+ else
+ {
+ // this can be more precise
+ Trace("sygus-process-arg-deps") << " is a relevant term."
+ << std::endl;
+ }
+ }
+ }
+
+ unsigned arg_list_counter = 0;
+ // sort arg_list by term size?
+
+ while (arg_list_counter < arg_list.size())
+ {
+ Node infer_def_t;
+ do
+ {
+ infer_def_t = Node::null();
+ // see if we can infer a definition
+ for (std::unordered_map<Node, std::vector<unsigned>, NodeHashFunction>::
+ iterator it = term_to_args.begin();
+ it != term_to_args.end();
+ ++it)
+ {
+ Node def = inferDefinition(it->first, term_to_arg_carry, free_vars);
+ if (!def.isNull())
+ {
+ Trace("sygus-process-arg-deps") << " *** Inferred definition "
+ << def << " for " << it->first
+ << std::endl;
+ // assign to each argument
+ assignRelevantDef(def, it->second);
+ // term_to_arg_carry[it->first] = rid;
+ infer_def_t = it->first;
+ break;
+ }
+ }
+ if (!infer_def_t.isNull())
+ {
+ term_to_args.erase(infer_def_t);
+ }
+ } while (!infer_def_t.isNull());
+
+ // decide to make an argument relevant
+ bool success = false;
+ while (arg_list_counter < arg_list.size() && !success)
+ {
+ Node curr = arg_list[arg_list_counter];
+ std::unordered_map<Node, std::vector<unsigned>, NodeHashFunction>::
+ iterator it = term_to_args.find(curr);
+ if (it != term_to_args.end())
+ {
+ Trace("sygus-process-arg-deps") << " *** Decide relevant " << curr
+ << std::endl;
+ // assign relevant to each
+ Node null_def;
+ unsigned rid = assignRelevantDef(null_def, it->second);
+ term_to_arg_carry[curr] = rid;
+ Trace("sygus-process-arg-deps")
+ << " carry " << curr << " by argument #" << rid << std::endl;
+ term_to_args.erase(curr);
+ success = true;
+ }
+ arg_list_counter++;
+ }
+ }
+ }
+}
+
+bool CegConjectureProcessFun::isArgRelevant(unsigned i)
+{
+ return d_arg_props[i].d_relevant;
+}
+
+void CegConjectureProcessFun::getIrrelevantArgs(
+ std::unordered_set<unsigned>& args)
+{
+ for (unsigned i = 0; i < d_arg_vars.size(); i++)
+ {
+ if (!d_arg_props[i].d_relevant)
+ {
+ args.insert(i);
+ }
+ }
+}
+
+CegConjectureProcess::CegConjectureProcess(QuantifiersEngine* qe) {}
+CegConjectureProcess::~CegConjectureProcess() {}
+Node CegConjectureProcess::preSimplify(Node q)
+{
+ Trace("sygus-process") << "Pre-simplify conjecture : " << q << std::endl;
+ return q;
+}
+
+Node CegConjectureProcess::postSimplify(Node q)
+{
+ Trace("sygus-process") << "Post-simplify conjecture : " << q << std::endl;
+ Assert(q.getKind() == FORALL);
+
+ // initialize the information about each function to synthesize
+ for (unsigned i = 0; i < q[0].getNumChildren(); i++)
+ {
+ Node f = q[0][i];
+ if (f.getType().isFunction())
+ {
+ d_sf_info[f].init(f);
+ }
+ }
+
+ // get the base on the conjecture
+ Node base = q[1];
+ std::unordered_set<Node, NodeHashFunction> synth_fv;
+ if (base.getKind() == NOT && base[0].getKind() == FORALL)
+ {
+ for (unsigned j = 0; j < base[0][0].getNumChildren(); j++)
+ {
+ synth_fv.insert(base[0][0][j]);
+ }
+ base = base[0][1];
+ }
+ std::vector<Node> conjuncts;
+ getComponentVector(AND, base, conjuncts);
+
+ // process the conjunctions
+ for (std::map<Node, CegConjectureProcessFun>::iterator it = d_sf_info.begin();
+ it != d_sf_info.end();
+ ++it)
+ {
+ Node f = it->first;
+ for (unsigned i = 0; i < conjuncts.size(); i++)
+ {
+ processConjunct(conjuncts[i], f, synth_fv);
+ }
+ }
+
+ return q;
+}
+
+void CegConjectureProcess::initialize(Node n, std::vector<Node>& candidates)
+{
+ if (Trace.isOn("sygus-process"))
+ {
+ Trace("sygus-process") << "Process conjecture : " << n
+ << " with candidates: " << std::endl;
+ for (unsigned i = 0; i < candidates.size(); i++)
+ {
+ Trace("sygus-process") << " " << candidates[i] << std::endl;
+ }
+ }
+}
+
+bool CegConjectureProcess::isArgRelevant(Node f, unsigned i)
+{
+ std::map<Node, CegConjectureProcessFun>::iterator its = d_sf_info.find(f);
+ if (its != d_sf_info.end())
+ {
+ return its->second.isArgRelevant(i);
+ }
+ Assert(false);
+ return true;
+}
+
+bool CegConjectureProcess::getIrrelevantArgs(Node f,
+ std::unordered_set<unsigned>& args)
+{
+ std::map<Node, CegConjectureProcessFun>::iterator its = d_sf_info.find(f);
+ if (its != d_sf_info.end())
+ {
+ its->second.getIrrelevantArgs(args);
+ return true;
+ }
+ return false;
+}
+
+void CegConjectureProcess::processConjunct(
+ Node n, Node f, std::unordered_set<Node, NodeHashFunction>& synth_fv)
+{
+ Trace("sygus-process-arg-deps") << "Process conjunct: " << std::endl;
+ Trace("sygus-process-arg-deps") << " " << n << " for synth fun " << f
+ << "..." << std::endl;
+
+ // first, flatten the conjunct
+ // make a copy of free variables since we may add new ones
+ std::unordered_set<Node, NodeHashFunction> synth_fv_n = synth_fv;
+ std::unordered_map<Node, Node, NodeHashFunction> defs;
+ Node nf = flatten(n, f, synth_fv_n, defs);
+
+ Trace("sygus-process-arg-deps") << "Flattened to: " << std::endl;
+ Trace("sygus-process-arg-deps") << " " << nf << std::endl;
+
+ // get free variables in nf
+ std::unordered_map<Node,
+ std::unordered_set<Node, NodeHashFunction>,
+ NodeHashFunction>
+ free_vars;
+ getFreeVariables(nf, synth_fv_n, free_vars);
+ // get free variables in each application
+ std::vector<Node> ns;
+ std::vector<Node> ks;
+ for (std::unordered_map<Node, Node, NodeHashFunction>::iterator it =
+ defs.begin();
+ it != defs.end();
+ ++it)
+ {
+ getFreeVariables(it->second, synth_fv_n, free_vars);
+ ns.push_back(it->second);
+ ks.push_back(it->first);
+ }
+
+ // process the applications of synthesis functions
+ if (!ns.empty())
+ {
+ std::map<Node, CegConjectureProcessFun>::iterator its = d_sf_info.find(f);
+ if (its != d_sf_info.end())
+ {
+ its->second.processTerms(ns, ks, nf, synth_fv_n, free_vars);
+ }
+ }
+}
+
+Node CegConjectureProcess::CegConjectureProcess::flatten(
+ Node n,
+ Node f,
+ std::unordered_set<Node, NodeHashFunction>& synth_fv,
+ std::unordered_map<Node, Node, NodeHashFunction>& defs)
+{
+ std::unordered_map<Node, Node, NodeHashFunction> visited;
+ std::unordered_map<Node, Node, NodeHashFunction>::iterator it;
+ std::stack<Node> visit;
+ Node cur;
+ visit.push(n);
+ do
+ {
+ cur = visit.top();
+ visit.pop();
+ it = visited.find(cur);
+
+ if (it == visited.end())
+ {
+ visited[cur] = Node::null();
+ visit.push(cur);
+ for (unsigned i = 0; i < cur.getNumChildren(); i++)
+ {
+ visit.push(cur[i]);
+ }
+ }
+ else if (it->second.isNull())
+ {
+ Node ret = cur;
+ bool childChanged = false;
+ std::vector<Node> children;
+ if (cur.getMetaKind() == kind::metakind::PARAMETERIZED)
+ {
+ children.push_back(cur.getOperator());
+ }
+ for (unsigned i = 0; i < cur.getNumChildren(); i++)
+ {
+ it = visited.find(cur[i]);
+ Assert(it != visited.end());
+ Assert(!it->second.isNull());
+ childChanged = childChanged || cur[i] != it->second;
+ children.push_back(it->second);
+ }
+ if (childChanged)
+ {
+ ret = NodeManager::currentNM()->mkNode(cur.getKind(), children);
+ }
+ // is it the function to synthesize?
+ if (cur.getKind() == APPLY_UF && cur.getOperator() == f)
+ {
+ // if so, flatten
+ Node k = NodeManager::currentNM()->mkBoundVar("vf", cur.getType());
+ defs[k] = ret;
+ ret = k;
+ synth_fv.insert(k);
+ }
+ // post-rewrite
+ visited[cur] = ret;
+ }
+ } while (!visit.empty());
+ Assert(visited.find(n) != visited.end());
+ Assert(!visited.find(n)->second.isNull());
+ return visited[n];
+}
+
+void CegConjectureProcess::getFreeVariables(
+ Node n,
+ std::unordered_set<Node, NodeHashFunction>& synth_fv,
+ std::unordered_map<Node,
+ std::unordered_set<Node, NodeHashFunction>,
+ NodeHashFunction>& free_vars)
+{
+ // first must compute free variables in each subterm of n,
+ // as well as contains_synth_fun
+ std::unordered_map<Node, bool, NodeHashFunction> visited;
+ std::unordered_map<Node, bool, NodeHashFunction>::iterator it;
+ std::stack<Node> visit;
+ Node cur;
+ visit.push(n);
+ do
+ {
+ cur = visit.top();
+ visit.pop();
+ it = visited.find(cur);
+
+ if (it == visited.end())
+ {
+ visited[cur] = false;
+ visit.push(cur);
+ for (unsigned i = 0; i < cur.getNumChildren(); i++)
+ {
+ visit.push(cur[i]);
+ }
+ }
+ else if (!it->second)
+ {
+ free_vars[cur].clear();
+ if (synth_fv.find(cur) != synth_fv.end())
+ {
+ // it is a free variable
+ free_vars[cur].insert(cur);
+ }
+ else
+ {
+ // otherwise, carry the free variables from the children
+ for (unsigned i = 0; i < cur.getNumChildren(); i++)
+ {
+ free_vars[cur].insert(free_vars[cur[i]].begin(),
+ free_vars[cur[i]].end());
+ }
+ }
+ visited[cur] = true;
+ }
+ } while (!visit.empty());
+}
+
+Node CegConjectureProcess::getSymmetryBreakingPredicate(
+ Node x, Node e, TypeNode tn, unsigned tindex, unsigned depth)
+{
+ return Node::null();
+}
+
+void CegConjectureProcess::debugPrint(const char* c) {}
+void CegConjectureProcess::getComponentVector(Kind k,
+ Node n,
+ std::vector<Node>& args)
+{
+ if (n.getKind() == k)
+ {
+ for (unsigned i = 0; i < n.getNumChildren(); i++)
+ {
+ args.push_back(n[i]);
+ }
+ }
+ else
+ {
+ args.push_back(n);
+ }
+}
+
+} /* namespace CVC4::theory::quantifiers */
+} /* namespace CVC4::theory */
+} /* namespace CVC4 */
--- /dev/null
+/********************* */
+/*! \file sygus_process_conj.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief Techniqures for static preprocessing and analysis of
+ ** sygus conjectures.
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__SYGUS_PROCESS_CONJ_H
+#define __CVC4__THEORY__QUANTIFIERS__SYGUS_PROCESS_CONJ_H
+
+#include <map>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "expr/node.h"
+#include "expr/type_node.h"
+#include "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+/** This file contains techniques that compute
+ * argument relevancy for synthesis functions
+ *
+ * Let F be a synthesis conjecture of the form:
+ * exists f. forall X. P( f, X )
+ *
+ * The classes below compute whether certain arguments of
+ * the function-to-synthesize f are irrelevant.
+ * Assume that f is a binary function, where possible solutions
+ * to the above conjecture are of the form:
+ * f -> (lambda (xy) t[x,y])
+ * We say e.g. that the 2nd argument of f is irrelevant if we
+ * can determine:
+ * F has a solution
+ * if and only if
+ * F has a solution of the form f -> (lambda (xy) t[x] )
+ * We conclude that arguments are irrelevant using the following
+ * techniques.
+ *
+ *
+ * (1) Argument invariance:
+ *
+ * Let s[z] be a term whose free variables are contained in { z }.
+ * If all occurrences of f-applications in F are of the form:
+ * f(t, s[t])
+ * then:
+ * f = (lambda (xy) r[x,y])
+ * is a solution to F only if:
+ * f = (lambda (xy) r[x,s[x]])
+ * is as well.
+ * Hence the second argument of f is not relevant.
+ *
+ *
+ * (2) Variable irrelevance:
+ *
+ * If F is equivalent to:
+ * exists f. forall w z u1...un. C1 ^...^Cm,
+ * where for i=1...m, Ci is of the form:
+ * ( w1 = f(tm1[z], u1) ^
+ * ... ^
+ * wn = f(tmn[z], un) ) => Pm(w1...wn, z)
+ * then the second argument of f is irrelevant.
+ * We call u1...un single occurrence variables
+ * (in Ci).
+ *
+ *
+ * TODO (#1210) others, generalize (2)?
+ *
+ */
+
+/** This structure stores information regarding
+ * an argument of a function to synthesize.
+ *
+ * It is used to store whether the argument
+ * position in the function to synthesize is
+ * relevant.
+ */
+class CegConjectureProcessArg
+{
+ public:
+ CegConjectureProcessArg() : d_var_single_occ(false), d_relevant(false) {}
+ /** template definition
+ * This is the term s[z] described
+ * under "Argument Invariance" above.
+ */
+ Node d_template;
+ /** single occurrence
+ * Whether we are trying to show this argument
+ * is irrelevant by "Variable irrelevance"
+ * described above.
+ */
+ bool d_var_single_occ;
+ /** whether this argument is relevant
+ * An argument is marked as relevant if:
+ * (A) it is explicitly marked as relevant
+ * due to a function application containing
+ * a relevant term at this argument position, or
+ * (B) if it is given conflicting template definitions.
+ */
+ bool d_relevant;
+};
+
+/** This structure stores information regarding conjecture-specific
+* analysis of a single function to synthesize within
+* a conjecture to synthesize.
+*
+* It maintains information about each of the function to
+* synthesize's arguments.
+*/
+struct CegConjectureProcessFun
+{
+ public:
+ CegConjectureProcessFun() {}
+ ~CegConjectureProcessFun() {}
+ /** initialize this class for function f */
+ void init(Node f);
+ /** process terms
+ *
+ * This is called once per conjunction in
+ * the synthesis conjecture.
+ *
+ * ns are the f-applications to process,
+ * ks are the variables we introduced to flatten them,
+ * nf is the flattened form of our conjecture to process,
+ * free_vars maps all subterms of n and nf to the set
+ * of variables (in set synth_fv) they contain.
+ *
+ * This updates information regarding which arguments
+ * of the function-to-synthesize are relevant.
+ */
+ void processTerms(
+ std::vector<Node>& ns,
+ std::vector<Node>& ks,
+ Node nf,
+ std::unordered_set<Node, NodeHashFunction>& synth_fv,
+ std::unordered_map<Node,
+ std::unordered_set<Node, NodeHashFunction>,
+ NodeHashFunction>& free_vars);
+ /** is the i^th argument of the function-to-synthesize of this class relevant?
+ */
+ bool isArgRelevant(unsigned i);
+ /** get irrelevant arguments for the function-to-synthesize of this class */
+ void getIrrelevantArgs(std::unordered_set<unsigned>& args);
+
+ private:
+ /** the synth fun associated with this */
+ Node d_synth_fun;
+ /** properties of each argument */
+ std::vector<CegConjectureProcessArg> d_arg_props;
+ /** variables for each argument type of f
+ *
+ * These are used to express templates for argument
+ * invariance, in the data member
+ * CegConjectureProcessArg::d_template.
+ */
+ std::vector<Node> d_arg_vars;
+ /** map from d_arg_vars to the argument #
+ * they represent.
+ */
+ std::unordered_map<Node, unsigned, NodeHashFunction> d_arg_var_num;
+ /** check match
+ * This function returns true iff we can infer:
+ * cn * { x -> n_arg_map[d_arg_var_num[x]] | x in d_arg_vars } = n
+ * In other words, cn and n are equivalent
+ * via the substitution mapping argument variables to terms
+ * specified by n_arg_map. The rewriter is used for inferring
+ * this equivalence.
+ *
+ * For example, if n_arg_map contains { 1 -> t, 2 -> s }, then
+ * checkMatch( x1+x2, t+s, n_arg_map ) returns true,
+ * checkMatch( x1+1, t+1, n_arg_map ) returns true,
+ * checkMatch( 0, 0, n_arg_map ) returns true,
+ * checkMatch( x1+1, s, n_arg_map ) returns false.
+ */
+ bool checkMatch(Node cn,
+ Node n,
+ std::unordered_map<unsigned, Node>& n_arg_map);
+ /** infer definition
+ *
+ * In the following, we say a term is a "template
+ * definition" if its free variables are a subset of d_arg_vars.
+ *
+ * If this function returns a non-null node ret, then
+ * checkMatch( ret, n, term_to_arg_carry^-1 ) returns true.
+ * and ret is a template definition.
+ *
+ * The free variables for all subterms of n are stored in
+ * free_vars. The map term_to_arg_carry is injective.
+ *
+ * For example, if term_to_arg_carry contains { t -> 1, s -> 2 } and
+ * free_vars is { t -> {y}, r -> {y}, s -> {}, q -> {}, ... -> {} }, then
+ * inferDefinition( 0, term_to_arg_carry, free_vars )
+ * returns 0
+ * inferDefinition( t, term_to_arg_carry, free_vars )
+ * returns x1
+ * inferDefinition( t+s+q, term_to_arg_carry, free_vars )
+ * returns x1+x2+q
+ * inferDefinition( t+r, term_to_arg_carry, free_vars )
+ * returns null
+ *
+ * Notice that multiple definitions are possible, e.g. above:
+ * inferDefinition( s, term_to_arg_carry, free_vars )
+ * may return either s or x2
+ * TODO (#1210) : try multiple definitions?
+ */
+ Node inferDefinition(
+ Node n,
+ std::unordered_map<Node, unsigned, NodeHashFunction>& term_to_arg_carry,
+ std::unordered_map<Node,
+ std::unordered_set<Node, NodeHashFunction>,
+ NodeHashFunction>& free_vars);
+ /** Assign relevant definition
+ *
+ * If def is non-null,
+ * this function assigns def as a template definition
+ * for the argument positions in args.
+ * This is called when there exists a term of the form
+ * f( t1....tn )
+ * in the synthesis conjecture that we are processing,
+ * where t_i = def * sigma for all i \in args,
+ * for some substitution sigma, where def is a template
+ * definition.
+ *
+ * If def is null, then there exists a term of the form
+ * f( t1....tn )
+ * where t_i = s for for all i \in args, and s is not
+ * a template definition. In this case, at least one
+ * argument in args must be marked as a relevant
+ * argument position.
+ *
+ * Returns a value rid such that:
+ * (1) rid occurs in args,
+ * (2) if def is null, then argument rid was marked
+ * relevant by this call.
+ */
+ unsigned assignRelevantDef(Node def, std::vector<unsigned>& args);
+ /** returns true if n is in d_arg_vars, updates arg_index
+ * to its position in d_arg_vars.
+ */
+ bool isArgVar(Node n, unsigned& arg_index);
+};
+
+/** Ceg Conjecture Process
+*
+* This class implements static techniques for preprocessing and analysis of
+* sygus conjectures.
+*
+* It is used as a back-end to CegConjecture, which calls it using the following
+* interface:
+* (1) When a sygus conjecture is asserted, we call
+* CegConjectureProcess::simplify( q ),
+* where q is the sygus conjecture in original form.
+*
+* (2) After a sygus conjecture is simplified and converted to deep
+* embedding form, we call CegConjectureProcess::initialize( n, candidates ).
+*
+* (3) During enumerative SyGuS search, calls may be made by
+* the extension of the quantifier-free datatypes decision procedure for
+* sygus to CegConjectureProcess::getSymmetryBreakingPredicate(...), which are
+* used for pruning search space based on conjecture-specific analysis.
+*/
+class CegConjectureProcess
+{
+ public:
+ CegConjectureProcess(QuantifiersEngine* qe);
+ ~CegConjectureProcess();
+ /** simplify the synthesis conjecture q
+ * Returns a formula that is equivalent to q.
+ * This simplification pass is called before all others
+ * in CegConjecture::assign.
+ *
+ * This function is intended for simplifications that
+ * impact whether or not the synthesis conjecture is
+ * single-invocation.
+ */
+ Node preSimplify(Node q);
+ /** simplify the synthesis conjecture q
+ * Returns a formula that is equivalent to q.
+ * This simplification pass is called after all others
+ * in CegConjecture::assign.
+ */
+ Node postSimplify(Node q);
+ /** initialize
+ *
+ * n is the "base instantiation" of the deep-embedding version of
+ * the synthesis conjecture under "candidates".
+ * (see CegConjecture::d_base_inst)
+ */
+ void initialize(Node n, std::vector<Node>& candidates);
+ /** is the i^th argument of the function-to-synthesize f relevant? */
+ bool isArgRelevant(Node f, unsigned i);
+ /** get irrelevant arguments for function-to-synthesize f
+ * returns true if f is a function-to-synthesize.
+ */
+ bool getIrrelevantArgs(Node f, std::unordered_set<unsigned>& args);
+ /** get symmetry breaking predicate
+ *
+ * Returns a formula that restricts the enumerative search space (for a given
+ * depth) for a term x of sygus type tn whose top symbol is the tindex^{th}
+ * constructor, where x is a subterm of enumerator e.
+ */
+ Node getSymmetryBreakingPredicate(
+ Node x, Node e, TypeNode tn, unsigned tindex, unsigned depth);
+ /** print out debug information about this conjecture */
+ void debugPrint(const char* c);
+ private:
+ /** process conjunct
+ *
+ * This sets up initial information about functions to synthesize
+ * where n is a conjunct of the synthesis conjecture, and synth_fv
+ * is the set of (inner) universal variables in the synthesis
+ * conjecture.
+ */
+ void processConjunct(Node n,
+ Node f,
+ std::unordered_set<Node, NodeHashFunction>& synth_fv);
+ /** flatten
+ *
+ * Flattens all applications of f in term n.
+ * This may add new variables to synth_fv, which
+ * are introduced at all positions of functions
+ * to synthesize in a bottom-up fashion. For each
+ * variable k introduced for a function application
+ * f(t), we add ( k -> f(t) ) to defs and ( f -> k )
+ * to fun_to_defs.
+ */
+ Node flatten(Node n,
+ Node f,
+ std::unordered_set<Node, NodeHashFunction>& synth_fv,
+ std::unordered_map<Node, Node, NodeHashFunction>& defs);
+ /** get free variables
+ * Constructs a map of all free variables that occur in n
+ * from synth_fv and stores them in the map free_vars.
+ */
+ void getFreeVariables(
+ Node n,
+ std::unordered_set<Node, NodeHashFunction>& synth_fv,
+ std::unordered_map<Node,
+ std::unordered_set<Node, NodeHashFunction>,
+ NodeHashFunction>& free_vars);
+ /** for each synth-fun, information that is specific to this conjecture */
+ std::map<Node, CegConjectureProcessFun> d_sf_info;
+
+ /** get component vector */
+ void getComponentVector(Kind k, Node n, std::vector<Node>& args);
+};
+
+} /* namespace CVC4::theory::quantifiers */
+} /* namespace CVC4::theory */
+} /* namespace CVC4 */
+
+#endif
--- /dev/null
+/********************* */
+/*! \file term_database_sygus.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief Implementation of term database sygus class
+ **/
+
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+
+#include "options/quantifiers_options.h"
+#include "theory/arith/arith_msum.h"
+#include "theory/quantifiers/quantifiers_attributes.h"
+#include "theory/quantifiers/term_database.h"
+#include "theory/quantifiers/term_util.h"
+#include "theory/quantifiers_engine.h"
+
+using namespace std;
+using namespace CVC4::kind;
+using namespace CVC4::context;
+using namespace CVC4::theory::inst;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+TermDbSygus::TermDbSygus(context::Context* c, QuantifiersEngine* qe)
+ : d_quantEngine(qe),
+ d_syexp(new SygusExplain(this)),
+ d_ext_rw(new ExtendedRewriter(true))
+{
+ d_true = NodeManager::currentNM()->mkConst( true );
+ d_false = NodeManager::currentNM()->mkConst( false );
+}
+
+bool TermDbSygus::reset( Theory::Effort e ) {
+ return true;
+}
+
+TNode TermDbSygus::getFreeVar( TypeNode tn, int i, bool useSygusType ) {
+ unsigned sindex = 0;
+ TypeNode vtn = tn;
+ if( useSygusType ){
+ if( tn.isDatatype() ){
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ if( !dt.getSygusType().isNull() ){
+ vtn = TypeNode::fromType( dt.getSygusType() );
+ sindex = 1;
+ }
+ }
+ }
+ while( i>=(int)d_fv[sindex][tn].size() ){
+ std::stringstream ss;
+ if( tn.isDatatype() ){
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ ss << "fv_" << dt.getName() << "_" << i;
+ }else{
+ ss << "fv_" << tn << "_" << i;
+ }
+ Assert( !vtn.isNull() );
+ Node v = NodeManager::currentNM()->mkSkolem( ss.str(), vtn, "for sygus normal form testing" );
+ d_fv_stype[v] = tn;
+ d_fv_num[v] = i;
+ d_fv[sindex][tn].push_back( v );
+ }
+ return d_fv[sindex][tn][i];
+}
+
+TNode TermDbSygus::getFreeVarInc( TypeNode tn, std::map< TypeNode, int >& var_count, bool useSygusType ) {
+ std::map< TypeNode, int >::iterator it = var_count.find( tn );
+ if( it==var_count.end() ){
+ var_count[tn] = 1;
+ return getFreeVar( tn, 0, useSygusType );
+ }else{
+ int index = it->second;
+ var_count[tn]++;
+ return getFreeVar( tn, index, useSygusType );
+ }
+}
+
+bool TermDbSygus::hasFreeVar( Node n, std::map< Node, bool >& visited ){
+ if( visited.find( n )==visited.end() ){
+ visited[n] = true;
+ if( isFreeVar( n ) ){
+ return true;
+ }
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ if( hasFreeVar( n[i], visited ) ){
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool TermDbSygus::hasFreeVar( Node n ) {
+ std::map< Node, bool > visited;
+ return hasFreeVar( n, visited );
+}
+
+TypeNode TermDbSygus::getSygusTypeForVar( Node v ) {
+ Assert( d_fv_stype.find( v )!=d_fv_stype.end() );
+ return d_fv_stype[v];
+}
+
+Node TermDbSygus::mkGeneric(const Datatype& dt,
+ unsigned c,
+ std::map<TypeNode, int>& var_count,
+ std::map<int, Node>& pre)
+{
+ Assert(c < dt.getNumConstructors());
+ Assert( dt.isSygus() );
+ Assert( !dt[c].getSygusOp().isNull() );
+ std::vector< Node > children;
+ Node op = Node::fromExpr( dt[c].getSygusOp() );
+ if( op.getKind()!=BUILTIN ){
+ children.push_back( op );
+ }
+ Trace("sygus-db-debug") << "mkGeneric " << dt.getName() << " " << op << " " << op.getKind() << "..." << std::endl;
+ for (unsigned i = 0, nargs = dt[c].getNumArgs(); i < nargs; i++)
+ {
+ TypeNode tna = getArgType( dt[c], i );
+ Node a;
+ std::map< int, Node >::iterator it = pre.find( i );
+ if( it!=pre.end() ){
+ a = it->second;
+ }else{
+ a = getFreeVarInc( tna, var_count, true );
+ }
+ Trace("sygus-db-debug")
+ << " child " << i << " : " << a << " : " << a.getType() << std::endl;
+ Assert( !a.isNull() );
+ children.push_back( a );
+ }
+ Node ret;
+ if( op.getKind()==BUILTIN ){
+ Trace("sygus-db-debug") << "Make builtin node..." << std::endl;
+ ret = NodeManager::currentNM()->mkNode( op, children );
+ }else{
+ Kind ok = getOperatorKind( op );
+ Trace("sygus-db-debug") << "Operator kind is " << ok << std::endl;
+ if( children.size()==1 && ok==kind::UNDEFINED_KIND ){
+ ret = children[0];
+ }else{
+ ret = NodeManager::currentNM()->mkNode( ok, children );
+ }
+ }
+ Trace("sygus-db-debug") << "...returning " << ret << std::endl;
+ return ret;
+}
+
+Node TermDbSygus::mkGeneric(const Datatype& dt, int c, std::map<int, Node>& pre)
+{
+ std::map<TypeNode, int> var_count;
+ return mkGeneric(dt, c, var_count, pre);
+}
+
+Node TermDbSygus::sygusToBuiltin( Node n, TypeNode tn ) {
+ Assert( n.getType()==tn );
+ Assert( tn.isDatatype() );
+ std::map< Node, Node >::iterator it = d_sygus_to_builtin[tn].find( n );
+ if( it==d_sygus_to_builtin[tn].end() ){
+ Trace("sygus-db-debug") << "SygusToBuiltin : compute for " << n << ", type = " << tn << std::endl;
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ if( n.getKind()==APPLY_CONSTRUCTOR ){
+ unsigned i = Datatype::indexOf( n.getOperator().toExpr() );
+ Assert( n.getNumChildren()==dt[i].getNumArgs() );
+ std::map< TypeNode, int > var_count;
+ std::map< int, Node > pre;
+ for (unsigned j = 0, size = n.getNumChildren(); j < size; j++)
+ {
+ pre[j] = sygusToBuiltin( n[j], getArgType( dt[i], j ) );
+ }
+ Node ret = mkGeneric(dt, i, var_count, pre);
+ Trace("sygus-db-debug") << "SygusToBuiltin : Generic is " << ret << std::endl;
+ d_sygus_to_builtin[tn][n] = ret;
+ return ret;
+ }
+ if (n.hasAttribute(SygusPrintProxyAttribute()))
+ {
+ // this variable was associated by an attribute to a builtin node
+ return n.getAttribute(SygusPrintProxyAttribute());
+ }
+ Assert(isFreeVar(n));
+ // map to builtin variable type
+ int fv_num = getVarNum(n);
+ Assert(!dt.getSygusType().isNull());
+ TypeNode vtn = TypeNode::fromType(dt.getSygusType());
+ Node ret = getFreeVar(vtn, fv_num);
+ return ret;
+ }else{
+ return it->second;
+ }
+}
+
+Node TermDbSygus::sygusSubstituted( TypeNode tn, Node n, std::vector< Node >& args ) {
+ Assert( d_var_list[tn].size()==args.size() );
+ return n.substitute( d_var_list[tn].begin(), d_var_list[tn].end(), args.begin(), args.end() );
+}
+
+unsigned TermDbSygus::getSygusTermSize( Node n ){
+ if( n.getNumChildren()==0 ){
+ return 0;
+ }else{
+ Assert(n.getKind() == APPLY_CONSTRUCTOR);
+ unsigned sum = 0;
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ sum += getSygusTermSize( n[i] );
+ }
+ const Datatype& dt = Datatype::datatypeOf(n.getOperator().toExpr());
+ int cindex = Datatype::indexOf(n.getOperator().toExpr());
+ Assert(cindex >= 0 && cindex < (int)dt.getNumConstructors());
+ unsigned weight = dt[cindex].getWeight();
+ return weight + sum;
+ }
+}
+
+class ReqTrie {
+public:
+ ReqTrie() : d_req_kind( UNDEFINED_KIND ){}
+ std::map< unsigned, ReqTrie > d_children;
+ Kind d_req_kind;
+ TypeNode d_req_type;
+ Node d_req_const;
+ void print( const char * c, int indent = 0 ){
+ if( d_req_kind!=UNDEFINED_KIND ){
+ Trace(c) << d_req_kind << " ";
+ }else if( !d_req_type.isNull() ){
+ Trace(c) << d_req_type;
+ }else if( !d_req_const.isNull() ){
+ Trace(c) << d_req_const;
+ }else{
+ Trace(c) << "_";
+ }
+ Trace(c) << std::endl;
+ for( std::map< unsigned, ReqTrie >::iterator it = d_children.begin(); it != d_children.end(); ++it ){
+ for( int i=0; i<=indent; i++ ) { Trace(c) << " "; }
+ Trace(c) << it->first << " : ";
+ it->second.print( c, indent+1 );
+ }
+ }
+ bool satisfiedBy( quantifiers::TermDbSygus * tdb, TypeNode tn ){
+ if( !d_req_const.isNull() ){
+ if( !tdb->hasConst( tn, d_req_const ) ){
+ return false;
+ }
+ }
+ if( !d_req_type.isNull() ){
+ if( tn!=d_req_type ){
+ return false;
+ }
+ }
+ if( d_req_kind!=UNDEFINED_KIND ){
+ int c = tdb->getKindConsNum( tn, d_req_kind );
+ if( c!=-1 ){
+ bool ret = true;
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ for( std::map< unsigned, ReqTrie >::iterator it = d_children.begin(); it != d_children.end(); ++it ){
+ if( it->first<dt[c].getNumArgs() ){
+ TypeNode tnc = tdb->getArgType( dt[c], it->first );
+ if( !it->second.satisfiedBy( tdb, tnc ) ){
+ ret = false;
+ break;
+ }
+ }else{
+ ret = false;
+ break;
+ }
+ }
+ if( !ret ){
+ return false;
+ }
+ // TODO : commutative operators try both?
+ }else{
+ return false;
+ }
+ }
+ return true;
+ }
+ bool empty() {
+ return d_req_kind==UNDEFINED_KIND && d_req_const.isNull() && d_req_type.isNull();
+ }
+};
+
+//this function gets all easy redundant cases, before consulting rewriters
+bool TermDbSygus::considerArgKind( TypeNode tn, TypeNode tnp, Kind k, Kind pk, int arg ) {
+ const Datatype& pdt = ((DatatypeType)(tnp).toType()).getDatatype();
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ Assert( hasKind( tn, k ) );
+ Assert( hasKind( tnp, pk ) );
+ Trace("sygus-sb-debug") << "Consider sygus arg kind " << k << ", pk = " << pk << ", arg = " << arg << "?" << std::endl;
+ int c = getKindConsNum( tn, k );
+ int pc = getKindConsNum( tnp, pk );
+ if( k==pk ){
+ //check for associativity
+ if( quantifiers::TermUtil::isAssoc( k ) ){
+ //if the operator is associative, then a repeated occurrence should only occur in the leftmost argument position
+ int firstArg = getFirstArgOccurrence( pdt[pc], tn );
+ Assert( firstArg!=-1 );
+ if( arg!=firstArg ){
+ Trace("sygus-sb-simple") << " sb-simple : do not consider " << k << " at child arg " << arg << " of " << k << " since it is associative, with first arg = " << firstArg << std::endl;
+ return false;
+ }else{
+ return true;
+ }
+ }
+ }
+ //describes the shape of an alternate term to construct
+ // we check whether this term is in the sygus grammar below
+ ReqTrie rt;
+ Assert( rt.empty() );
+
+ //construct rt by cases
+ if( pk==NOT || pk==BITVECTOR_NOT || pk==UMINUS || pk==BITVECTOR_NEG ){
+ //negation normal form
+ if( pk==k ){
+ rt.d_req_type = getArgType( dt[c], 0 );
+ }else{
+ Kind reqk = UNDEFINED_KIND; //required kind for all children
+ std::map< unsigned, Kind > reqkc; //required kind for some children
+ if( pk==NOT ){
+ if( k==AND ) {
+ rt.d_req_kind = OR;reqk = NOT;
+ }else if( k==OR ){
+ rt.d_req_kind = AND;reqk = NOT;
+ //AJR : eliminate this if we eliminate xor
+ }else if( k==EQUAL ) {
+ rt.d_req_kind = XOR;
+ }else if( k==XOR ) {
+ rt.d_req_kind = EQUAL;
+ }else if( k==ITE ){
+ rt.d_req_kind = ITE;reqkc[1] = NOT;reqkc[2] = NOT;
+ rt.d_children[0].d_req_type = getArgType( dt[c], 0 );
+ }else if( k==LEQ || k==GT ){
+ // (not (~ x y)) -----> (~ (+ y 1) x)
+ rt.d_req_kind = k;
+ rt.d_children[0].d_req_kind = PLUS;
+ rt.d_children[0].d_children[0].d_req_type = getArgType( dt[c], 1 );
+ rt.d_children[0].d_children[1].d_req_const = NodeManager::currentNM()->mkConst( Rational( 1 ) );
+ rt.d_children[1].d_req_type = getArgType( dt[c], 0 );
+ //TODO: other possibilities?
+ }else if( k==LT || k==GEQ ){
+ // (not (~ x y)) -----> (~ y (+ x 1))
+ rt.d_req_kind = k;
+ rt.d_children[0].d_req_type = getArgType( dt[c], 1 );
+ rt.d_children[1].d_req_kind = PLUS;
+ rt.d_children[1].d_children[0].d_req_type = getArgType( dt[c], 0 );
+ rt.d_children[1].d_children[1].d_req_const = NodeManager::currentNM()->mkConst( Rational( 1 ) );
+ }
+ }else if( pk==BITVECTOR_NOT ){
+ if( k==BITVECTOR_AND ) {
+ rt.d_req_kind = BITVECTOR_OR;reqk = BITVECTOR_NOT;
+ }else if( k==BITVECTOR_OR ){
+ rt.d_req_kind = BITVECTOR_AND;reqk = BITVECTOR_NOT;
+ }else if( k==BITVECTOR_XNOR ) {
+ rt.d_req_kind = BITVECTOR_XOR;
+ }else if( k==BITVECTOR_XOR ) {
+ rt.d_req_kind = BITVECTOR_XNOR;
+ }
+ }else if( pk==UMINUS ){
+ if( k==PLUS ){
+ rt.d_req_kind = PLUS;reqk = UMINUS;
+ }
+ }else if( pk==BITVECTOR_NEG ){
+ if( k==PLUS ){
+ rt.d_req_kind = PLUS;reqk = BITVECTOR_NEG;
+ }
+ }
+ if( !rt.empty() && ( reqk!=UNDEFINED_KIND || !reqkc.empty() ) ){
+ int pcr = getKindConsNum( tnp, rt.d_req_kind );
+ if( pcr!=-1 ){
+ Assert( pcr<(int)pdt.getNumConstructors() );
+ //must have same number of arguments
+ if( pdt[pcr].getNumArgs()==dt[c].getNumArgs() ){
+ for( unsigned i=0; i<pdt[pcr].getNumArgs(); i++ ){
+ Kind rk = reqk;
+ if( reqk==UNDEFINED_KIND ){
+ std::map< unsigned, Kind >::iterator itr = reqkc.find( i );
+ if( itr!=reqkc.end() ){
+ rk = itr->second;
+ }
+ }
+ if( rk!=UNDEFINED_KIND ){
+ rt.d_children[i].d_req_kind = rk;
+ rt.d_children[i].d_children[0].d_req_type = getArgType( dt[c], i );
+ }
+ }
+ }
+ }
+ }
+ }
+ }else if( k==MINUS || k==BITVECTOR_SUB ){
+ if( pk==EQUAL ||
+ pk==MINUS || pk==BITVECTOR_SUB ||
+ pk==LEQ || pk==LT || pk==GEQ || pk==GT ){
+ int oarg = arg==0 ? 1 : 0;
+ // (~ x (- y z)) ----> (~ (+ x z) y)
+ // (~ (- y z) x) ----> (~ y (+ x z))
+ rt.d_req_kind = pk;
+ rt.d_children[arg].d_req_type = getArgType( dt[c], 0 );
+ rt.d_children[oarg].d_req_kind = k==MINUS ? PLUS : BITVECTOR_PLUS;
+ rt.d_children[oarg].d_children[0].d_req_type = getArgType( pdt[pc], oarg );
+ rt.d_children[oarg].d_children[1].d_req_type = getArgType( dt[c], 1 );
+ }else if( pk==PLUS || pk==BITVECTOR_PLUS ){
+ // (+ x (- y z)) -----> (- (+ x y) z)
+ // (+ (- y z) x) -----> (- (+ x y) z)
+ rt.d_req_kind = pk==PLUS ? MINUS : BITVECTOR_SUB;
+ int oarg = arg==0 ? 1 : 0;
+ rt.d_children[0].d_req_kind = pk;
+ rt.d_children[0].d_children[0].d_req_type = getArgType( pdt[pc], oarg );
+ rt.d_children[0].d_children[1].d_req_type = getArgType( dt[c], 0 );
+ rt.d_children[1].d_req_type = getArgType( dt[c], 1 );
+ // TODO : this is subsumbed by solving for MINUS
+ }
+ }else if( k==ITE ){
+ if( pk!=ITE ){
+ // (o X (ite y z w) X') -----> (ite y (o X z X') (o X w X'))
+ rt.d_req_kind = ITE;
+ rt.d_children[0].d_req_type = getArgType( dt[c], 0 );
+ unsigned n_args = pdt[pc].getNumArgs();
+ for( unsigned r=1; r<=2; r++ ){
+ rt.d_children[r].d_req_kind = pk;
+ for( unsigned q=0; q<n_args; q++ ){
+ if( (int)q==arg ){
+ rt.d_children[r].d_children[q].d_req_type = getArgType( dt[c], r );
+ }else{
+ rt.d_children[r].d_children[q].d_req_type = getArgType( pdt[pc], q );
+ }
+ }
+ }
+ //TODO: this increases term size but is probably a good idea
+ }
+ }else if( k==NOT ){
+ if( pk==ITE ){
+ // (ite (not y) z w) -----> (ite y w z)
+ rt.d_req_kind = ITE;
+ rt.d_children[0].d_req_type = getArgType( dt[c], 0 );
+ rt.d_children[1].d_req_type = getArgType( pdt[pc], 2 );
+ rt.d_children[2].d_req_type = getArgType( pdt[pc], 1 );
+ }
+ }
+ Trace("sygus-sb-debug") << "Consider sygus arg kind " << k << ", pk = " << pk << ", arg = " << arg << "?" << std::endl;
+ if( !rt.empty() ){
+ rt.print("sygus-sb-debug");
+ //check if it meets the requirements
+ if( rt.satisfiedBy( this, tnp ) ){
+ Trace("sygus-sb-debug") << "...success!" << std::endl;
+ Trace("sygus-sb-simple") << " sb-simple : do not consider " << k << " as arg " << arg << " of " << pk << std::endl;
+ //do not need to consider the kind in the search since there are ways to construct equivalent terms
+ return false;
+ }else{
+ Trace("sygus-sb-debug") << "...failed." << std::endl;
+ }
+ Trace("sygus-sb-debug") << std::endl;
+ }
+ //must consider this kind in the search
+ return true;
+}
+
+bool TermDbSygus::considerConst( TypeNode tn, TypeNode tnp, Node c, Kind pk, int arg ) {
+ const Datatype& pdt = ((DatatypeType)(tnp).toType()).getDatatype();
+ // child grammar-independent
+ if( !considerConst( pdt, tnp, c, pk, arg ) ){
+ return false;
+ }
+ // TODO : this can probably be made child grammar independent
+ int pc = getKindConsNum( tnp, pk );
+ if( pdt[pc].getNumArgs()==2 ){
+ Kind ok;
+ int offset;
+ if (d_quantEngine->getTermUtil()->hasOffsetArg(pk, arg, offset, ok))
+ {
+ Trace("sygus-sb-simple-debug") << pk << " has offset arg " << ok << " " << offset << std::endl;
+ int ok_arg = getKindConsNum( tnp, ok );
+ if( ok_arg!=-1 ){
+ Trace("sygus-sb-simple-debug") << "...at argument " << ok_arg << std::endl;
+ //other operator be the same type
+ if( isTypeMatch( pdt[ok_arg], pdt[arg] ) ){
+ int status;
+ Node co = d_quantEngine->getTermUtil()->getTypeValueOffset(
+ c.getType(), c, offset, status);
+ Trace("sygus-sb-simple-debug") << c << " with offset " << offset << " is " << co << ", status=" << status << std::endl;
+ if( status==0 && !co.isNull() ){
+ if( hasConst( tn, co ) ){
+ Trace("sygus-sb-simple") << " sb-simple : by offset reasoning, do not consider const " << c;
+ Trace("sygus-sb-simple") << " as arg " << arg << " of " << pk << " since we can use " << co << " under " << ok << " " << std::endl;
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool TermDbSygus::considerConst( const Datatype& pdt, TypeNode tnp, Node c, Kind pk, int arg ) {
+ Assert( hasKind( tnp, pk ) );
+ int pc = getKindConsNum( tnp, pk );
+ bool ret = true;
+ Trace("sygus-sb-debug") << "Consider sygus const " << c << ", parent = " << pk << ", arg = " << arg << "?" << std::endl;
+ if (d_quantEngine->getTermUtil()->isIdempotentArg(c, pk, arg))
+ {
+ if( pdt[pc].getNumArgs()==2 ){
+ int oarg = arg==0 ? 1 : 0;
+ TypeNode otn = TypeNode::fromType( ((SelectorType)pdt[pc][oarg].getType()).getRangeType() );
+ if( otn==tnp ){
+ Trace("sygus-sb-simple") << " sb-simple : " << c << " is idempotent arg " << arg << " of " << pk << "..." << std::endl;
+ ret = false;
+ }
+ }
+ }else{
+ Node sc = d_quantEngine->getTermUtil()->isSingularArg(c, pk, arg);
+ if( !sc.isNull() ){
+ if( hasConst( tnp, sc ) ){
+ Trace("sygus-sb-simple") << " sb-simple : " << c << " is singular arg " << arg << " of " << pk << ", evaluating to " << sc << "..." << std::endl;
+ ret = false;
+ }
+ }
+ }
+ if( ret ){
+ ReqTrie rt;
+ Assert( rt.empty() );
+ Node max_c = d_quantEngine->getTermUtil()->getTypeMaxValue(c.getType());
+ Node zero_c = d_quantEngine->getTermUtil()->getTypeValue(c.getType(), 0);
+ Node one_c = d_quantEngine->getTermUtil()->getTypeValue(c.getType(), 1);
+ if( pk==XOR || pk==BITVECTOR_XOR ){
+ if( c==max_c ){
+ rt.d_req_kind = pk==XOR ? NOT : BITVECTOR_NOT;
+ }
+ }else if( pk==ITE ){
+ if( arg==0 ){
+ if( c==max_c ){
+ rt.d_children[2].d_req_type = tnp;
+ }else if( c==zero_c ){
+ rt.d_children[1].d_req_type = tnp;
+ }
+ }
+ }else if( pk==STRING_SUBSTR ){
+ if( c==one_c ){
+ rt.d_req_kind = STRING_CHARAT;
+ rt.d_children[0].d_req_type = getArgType( pdt[pc], 0 );
+ rt.d_children[1].d_req_type = getArgType( pdt[pc], 1 );
+ }
+ }
+ if( !rt.empty() ){
+ //check if satisfied
+ if( rt.satisfiedBy( this, tnp ) ){
+ Trace("sygus-sb-simple") << " sb-simple : do not consider const " << c << " as arg " << arg << " of " << pk;
+ Trace("sygus-sb-simple") << " in " << ((DatatypeType)tnp.toType()).getDatatype().getName() << std::endl;
+ //do not need to consider the constant in the search since there are ways to construct equivalent terms
+ ret = false;
+ }
+ }
+ }
+ // TODO : cache?
+ return ret;
+}
+
+int TermDbSygus::solveForArgument( TypeNode tn, unsigned cindex, unsigned arg ) {
+ // FIXME
+ return -1; // TODO : if using, modify considerArgKind above
+ Assert( isRegistered( tn ) );
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ Assert( cindex<dt.getNumConstructors() );
+ Assert( arg<dt[cindex].getNumArgs() );
+ Kind nk = getConsNumKind( tn, cindex );
+ TypeNode tnc = getArgType( dt[cindex], arg );
+ const Datatype& cdt = ((DatatypeType)(tnc).toType()).getDatatype();
+
+ ReqTrie rt;
+ Assert( rt.empty() );
+ int solve_ret = -1;
+ if( nk==MINUS || nk==BITVECTOR_SUB ){
+ if( dt[cindex].getNumArgs()==2 && arg==0 ){
+ TypeNode tnco = getArgType( dt[cindex], 1 );
+ Node builtin = d_quantEngine->getTermUtil()->getTypeValue(
+ sygusToBuiltinType(tnc), 0);
+ solve_ret = getConstConsNum( tn, builtin );
+ if( solve_ret!=-1 ){
+ // t - s -----> ( 0 - s ) + t
+ rt.d_req_kind = nk == MINUS ? PLUS : BITVECTOR_PLUS;
+ rt.d_children[0].d_req_type = tn; // avoid?
+ rt.d_children[0].d_req_kind = nk;
+ rt.d_children[0].d_children[0].d_req_const = builtin;
+ rt.d_children[0].d_children[0].d_req_type = tnco;
+ rt.d_children[1].d_req_type = tnc;
+ // TODO : this can be made more general for multiple type grammars to remove MINUS entirely
+ }
+ }
+ }
+
+ if( !rt.empty() ){
+ Assert( solve_ret>=0 );
+ Assert( solve_ret<=(int)cdt.getNumConstructors() );
+ //check if satisfied
+ if( rt.satisfiedBy( this, tn ) ){
+ Trace("sygus-sb-simple") << " sb-simple : ONLY consider " << cdt[solve_ret].getSygusOp() << " as arg " << arg << " of " << nk;
+ Trace("sygus-sb-simple") << " in " << ((DatatypeType)tn.toType()).getDatatype().getName() << std::endl;
+ return solve_ret;
+ }
+ }
+
+ return -1;
+}
+
+void TermDbSygus::registerSygusType( TypeNode tn ) {
+ std::map< TypeNode, TypeNode >::iterator itr = d_register.find( tn );
+ if( itr==d_register.end() ){
+ d_register[tn] = TypeNode::null();
+ if( tn.isDatatype() ){
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ Trace("sygus-db") << "Register type " << dt.getName() << "..." << std::endl;
+ TypeNode btn = TypeNode::fromType( dt.getSygusType() );
+ d_register[tn] = btn;
+ if( !d_register[tn].isNull() ){
+ // get the sygus variable list
+ Node var_list = Node::fromExpr( dt.getSygusVarList() );
+ if( !var_list.isNull() ){
+ for( unsigned j=0; j<var_list.getNumChildren(); j++ ){
+ Node sv = var_list[j];
+ SygusVarNumAttribute svna;
+ sv.setAttribute( svna, j );
+ d_var_list[tn].push_back( sv );
+ }
+ }else{
+ // no arguments to synthesis functions
+ }
+ //iterate over constructors
+ for( unsigned i=0; i<dt.getNumConstructors(); i++ ){
+ Expr sop = dt[i].getSygusOp();
+ Assert( !sop.isNull() );
+ Node n = Node::fromExpr( sop );
+ Trace("sygus-db") << " Operator #" << i << " : " << sop;
+ if( sop.getKind() == kind::BUILTIN ){
+ Kind sk = NodeManager::operatorToKind( n );
+ Trace("sygus-db") << ", kind = " << sk;
+ d_kinds[tn][sk] = i;
+ d_arg_kind[tn][i] = sk;
+ }else if( sop.isConst() ){
+ Trace("sygus-db") << ", constant";
+ d_consts[tn][n] = i;
+ d_arg_const[tn][i] = n;
+ }
+ d_ops[tn][n] = i;
+ d_arg_ops[tn][i] = n;
+ Trace("sygus-db") << std::endl;
+ }
+ //register connected types
+ for( unsigned i=0; i<dt.getNumConstructors(); i++ ){
+ for( unsigned j=0; j<dt[i].getNumArgs(); j++ ){
+ registerSygusType( getArgType( dt[i], j ) );
+ }
+ }
+ }
+ }
+ }
+}
+
+void TermDbSygus::registerEnumerator(Node e,
+ Node f,
+ CegConjecture* conj,
+ bool mkActiveGuard)
+{
+ Assert(d_enum_to_conjecture.find(e) == d_enum_to_conjecture.end());
+ Trace("sygus-db") << "Register measured term : " << e << std::endl;
+ d_enum_to_conjecture[e] = conj;
+ d_enum_to_synth_fun[e] = f;
+ if( mkActiveGuard ){
+ // make the guard
+ Node eg = Rewriter::rewrite( NodeManager::currentNM()->mkSkolem( "eG", NodeManager::currentNM()->booleanType() ) );
+ eg = d_quantEngine->getValuation().ensureLiteral( eg );
+ AlwaysAssert( !eg.isNull() );
+ d_quantEngine->getOutputChannel().requirePhase( eg, true );
+ //add immediate lemma
+ Node lem = NodeManager::currentNM()->mkNode( OR, eg, eg.negate() );
+ Trace("cegqi-lemma") << "Cegqi::Lemma : enumerator : " << lem << std::endl;
+ d_quantEngine->getOutputChannel().lemma( lem );
+ d_enum_to_active_guard[e] = eg;
+ }
+}
+
+bool TermDbSygus::isEnumerator(Node e) const
+{
+ return d_enum_to_conjecture.find(e) != d_enum_to_conjecture.end();
+}
+
+CegConjecture* TermDbSygus::getConjectureForEnumerator(Node e)
+{
+ std::map<Node, CegConjecture*>::iterator itm = d_enum_to_conjecture.find(e);
+ if (itm != d_enum_to_conjecture.end()) {
+ return itm->second;
+ }else{
+ return NULL;
+ }
+}
+
+Node TermDbSygus::getSynthFunForEnumerator(Node e)
+{
+ std::map<Node, Node>::iterator itsf = d_enum_to_synth_fun.find(e);
+ if (itsf != d_enum_to_synth_fun.end())
+ {
+ return itsf->second;
+ }
+ else
+ {
+ return Node::null();
+ }
+}
+
+Node TermDbSygus::getActiveGuardForEnumerator(Node e)
+{
+ std::map<Node, Node>::iterator itag = d_enum_to_active_guard.find(e);
+ if (itag != d_enum_to_active_guard.end()) {
+ return itag->second;
+ }else{
+ return Node::null();
+ }
+}
+
+void TermDbSygus::getEnumerators(std::vector<Node>& mts)
+{
+ for (std::map<Node, CegConjecture*>::iterator itm =
+ d_enum_to_conjecture.begin();
+ itm != d_enum_to_conjecture.end(); ++itm) {
+ mts.push_back( itm->first );
+ }
+}
+
+bool TermDbSygus::isRegistered( TypeNode tn ) {
+ return d_register.find( tn )!=d_register.end();
+}
+
+TypeNode TermDbSygus::sygusToBuiltinType( TypeNode tn ) {
+ Assert( isRegistered( tn ) );
+ return d_register[tn];
+}
+
+void TermDbSygus::computeMinTypeDepthInternal( TypeNode root_tn, TypeNode tn, unsigned type_depth ) {
+ std::map< TypeNode, unsigned >::iterator it = d_min_type_depth[root_tn].find( tn );
+ if( it==d_min_type_depth[root_tn].end() || type_depth<it->second ){
+ d_min_type_depth[root_tn][tn] = type_depth;
+ Assert( tn.isDatatype() );
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ //compute for connected types
+ for( unsigned i=0; i<dt.getNumConstructors(); i++ ){
+ for( unsigned j=0; j<dt[i].getNumArgs(); j++ ){
+ computeMinTypeDepthInternal( root_tn, getArgType( dt[i], j ), type_depth+1 );
+ }
+ }
+ }
+}
+
+unsigned TermDbSygus::getMinTypeDepth( TypeNode root_tn, TypeNode tn ){
+ std::map< TypeNode, unsigned >::iterator it = d_min_type_depth[root_tn].find( tn );
+ if( it==d_min_type_depth[root_tn].end() ){
+ computeMinTypeDepthInternal( root_tn, root_tn, 0 );
+ Assert( d_min_type_depth[root_tn].find( tn )!=d_min_type_depth[root_tn].end() );
+ return d_min_type_depth[root_tn][tn];
+ }else{
+ return it->second;
+ }
+}
+
+unsigned TermDbSygus::getMinTermSize( TypeNode tn ) {
+ Assert( isRegistered( tn ) );
+ std::map< TypeNode, unsigned >::iterator it = d_min_term_size.find( tn );
+ if( it==d_min_term_size.end() ){
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ for( unsigned i=0; i<dt.getNumConstructors(); i++ ){
+ if (dt[i].getNumArgs() == 0)
+ {
+ d_min_term_size[tn] = 0;
+ return 0;
+ }
+ }
+ // TODO : improve
+ d_min_term_size[tn] = 1;
+ return 1;
+ }else{
+ return it->second;
+ }
+}
+
+unsigned TermDbSygus::getMinConsTermSize( TypeNode tn, unsigned cindex ) {
+ Assert( isRegistered( tn ) );
+ std::map< unsigned, unsigned >::iterator it = d_min_cons_term_size[tn].find( cindex );
+ if( it==d_min_cons_term_size[tn].end() ){
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ Assert( cindex<dt.getNumConstructors() );
+ unsigned ret = 0;
+ if( dt[cindex].getNumArgs()>0 ){
+ ret = 1;
+ for( unsigned i=0; i<dt[cindex].getNumArgs(); i++ ){
+ ret += getMinTermSize( getArgType( dt[cindex], i ) );
+ }
+ }
+ d_min_cons_term_size[tn][cindex] = ret;
+ return ret;
+ }else{
+ return it->second;
+ }
+}
+
+unsigned TermDbSygus::getSelectorWeight(TypeNode tn, Node sel)
+{
+ std::map<TypeNode, std::map<Node, unsigned> >::iterator itsw =
+ d_sel_weight.find(tn);
+ if (itsw == d_sel_weight.end())
+ {
+ d_sel_weight[tn].clear();
+ itsw = d_sel_weight.find(tn);
+ Type t = tn.toType();
+ const Datatype& dt = static_cast<DatatypeType>(t).getDatatype();
+ Trace("sygus-db") << "Compute selector weights for " << dt.getName()
+ << std::endl;
+ for (unsigned i = 0, size = dt.getNumConstructors(); i < size; i++)
+ {
+ unsigned cw = dt[i].getWeight();
+ for (unsigned j = 0, size2 = dt[i].getNumArgs(); j < size2; j++)
+ {
+ Node csel = Node::fromExpr(dt[i].getSelectorInternal(t, j));
+ std::map<Node, unsigned>::iterator its = itsw->second.find(csel);
+ if (its == itsw->second.end() || cw < its->second)
+ {
+ d_sel_weight[tn][csel] = cw;
+ Trace("sygus-db") << " w(" << csel << ") <= " << cw << std::endl;
+ }
+ }
+ }
+ }
+ Assert(itsw->second.find(sel) != itsw->second.end());
+ return itsw->second[sel];
+}
+
+int TermDbSygus::getKindConsNum( TypeNode tn, Kind k ) {
+ Assert( isRegistered( tn ) );
+ std::map< TypeNode, std::map< Kind, int > >::iterator itt = d_kinds.find( tn );
+ if( itt!=d_kinds.end() ){
+ std::map< Kind, int >::iterator it = itt->second.find( k );
+ if( it!=itt->second.end() ){
+ return it->second;
+ }
+ }
+ return -1;
+}
+
+int TermDbSygus::getConstConsNum( TypeNode tn, Node n ){
+ Assert( isRegistered( tn ) );
+ std::map< TypeNode, std::map< Node, int > >::iterator itt = d_consts.find( tn );
+ if( itt!=d_consts.end() ){
+ std::map< Node, int >::iterator it = itt->second.find( n );
+ if( it!=itt->second.end() ){
+ return it->second;
+ }
+ }
+ return -1;
+}
+
+int TermDbSygus::getOpConsNum( TypeNode tn, Node n ) {
+ std::map< Node, int >::iterator it = d_ops[tn].find( n );
+ if( it!=d_ops[tn].end() ){
+ return it->second;
+ }else{
+ return -1;
+ }
+}
+
+bool TermDbSygus::hasKind( TypeNode tn, Kind k ) {
+ return getKindConsNum( tn, k )!=-1;
+}
+bool TermDbSygus::hasConst( TypeNode tn, Node n ) {
+ return getConstConsNum( tn, n )!=-1;
+}
+bool TermDbSygus::hasOp( TypeNode tn, Node n ) {
+ return getOpConsNum( tn, n )!=-1;
+}
+
+Node TermDbSygus::getConsNumOp( TypeNode tn, int i ) {
+ Assert( isRegistered( tn ) );
+ std::map< TypeNode, std::map< int, Node > >::iterator itt = d_arg_ops.find( tn );
+ if( itt!=d_arg_ops.end() ){
+ std::map< int, Node >::iterator itn = itt->second.find( i );
+ if( itn!=itt->second.end() ){
+ return itn->second;
+ }
+ }
+ return Node::null();
+}
+
+Node TermDbSygus::getConsNumConst( TypeNode tn, int i ) {
+ Assert( isRegistered( tn ) );
+ std::map< TypeNode, std::map< int, Node > >::iterator itt = d_arg_const.find( tn );
+ if( itt!=d_arg_const.end() ){
+ std::map< int, Node >::iterator itn = itt->second.find( i );
+ if( itn!=itt->second.end() ){
+ return itn->second;
+ }
+ }
+ return Node::null();
+}
+
+Kind TermDbSygus::getConsNumKind( TypeNode tn, int i ) {
+ Assert( isRegistered( tn ) );
+ std::map< TypeNode, std::map< int, Kind > >::iterator itt = d_arg_kind.find( tn );
+ if( itt!=d_arg_kind.end() ){
+ std::map< int, Kind >::iterator itk = itt->second.find( i );
+ if( itk!=itt->second.end() ){
+ return itk->second;
+ }
+ }
+ return UNDEFINED_KIND;
+}
+
+bool TermDbSygus::isKindArg( TypeNode tn, int i ) {
+ return getConsNumKind( tn, i )!=UNDEFINED_KIND;
+}
+
+bool TermDbSygus::isConstArg( TypeNode tn, int i ) {
+ Assert( isRegistered( tn ) );
+ std::map< TypeNode, std::map< int, Node > >::iterator itt = d_arg_const.find( tn );
+ if( itt!=d_arg_const.end() ){
+ return itt->second.find( i )!=itt->second.end();
+ }else{
+ return false;
+ }
+}
+
+TypeNode TermDbSygus::getArgType(const DatatypeConstructor& c, unsigned i)
+{
+ Assert(i < c.getNumArgs());
+ return TypeNode::fromType( ((SelectorType)c[i].getType()).getRangeType() );
+}
+
+/** get first occurrence */
+int TermDbSygus::getFirstArgOccurrence( const DatatypeConstructor& c, TypeNode tn ) {
+ for( unsigned i=0; i<c.getNumArgs(); i++ ){
+ TypeNode tni = getArgType( c, i );
+ if( tni==tn ){
+ return i;
+ }
+ }
+ return -1;
+}
+
+bool TermDbSygus::isTypeMatch( const DatatypeConstructor& c1, const DatatypeConstructor& c2 ) {
+ if( c1.getNumArgs()!=c2.getNumArgs() ){
+ return false;
+ }else{
+ for( unsigned i=0; i<c1.getNumArgs(); i++ ){
+ if( getArgType( c1, i )!=getArgType( c2, i ) ){
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+Node TermDbSygus::minimizeBuiltinTerm( Node n ) {
+ if( ( n.getKind()==EQUAL || n.getKind()==LEQ || n.getKind()==LT || n.getKind()==GEQ || n.getKind()==GT ) &&
+ ( n[0].getType().isInteger() || n[0].getType().isReal() ) ){
+ bool changed = false;
+ std::vector< Node > mon[2];
+ for( unsigned r=0; r<2; r++ ){
+ unsigned ro = r==0 ? 1 : 0;
+ Node c;
+ Node nc;
+ if( n[r].getKind()==PLUS ){
+ for( unsigned i=0; i<n[r].getNumChildren(); i++ ){
+ if (ArithMSum::getMonomial(n[r][i], c, nc)
+ && c.getConst<Rational>().isNegativeOne())
+ {
+ mon[ro].push_back( nc );
+ changed = true;
+ }else{
+ if( !n[r][i].isConst() || !n[r][i].getConst<Rational>().isZero() ){
+ mon[r].push_back( n[r][i] );
+ }
+ }
+ }
+ }else{
+ if (ArithMSum::getMonomial(n[r], c, nc)
+ && c.getConst<Rational>().isNegativeOne())
+ {
+ mon[ro].push_back( nc );
+ changed = true;
+ }else{
+ if( !n[r].isConst() || !n[r].getConst<Rational>().isZero() ){
+ mon[r].push_back( n[r] );
+ }
+ }
+ }
+ }
+ if( changed ){
+ Node nn[2];
+ for( unsigned r=0; r<2; r++ ){
+ nn[r] = mon[r].size()==0 ? NodeManager::currentNM()->mkConst( Rational(0) ) : ( mon[r].size()==1 ? mon[r][0] : NodeManager::currentNM()->mkNode( PLUS, mon[r] ) );
+ }
+ return NodeManager::currentNM()->mkNode( n.getKind(), nn[0], nn[1] );
+ }
+ }
+ return n;
+}
+
+Node TermDbSygus::expandBuiltinTerm( Node t ){
+ if( t.getKind()==EQUAL ){
+ if( t[0].getType().isReal() ){
+ return NodeManager::currentNM()->mkNode( AND, NodeManager::currentNM()->mkNode( LEQ, t[0], t[1] ),
+ NodeManager::currentNM()->mkNode( LEQ, t[1], t[0] ) );
+ }else if( t[0].getType().isBoolean() ){
+ return NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, t[0], t[1] ),
+ NodeManager::currentNM()->mkNode( AND, t[0].negate(), t[1].negate() ) );
+ }
+ }else if( t.getKind()==ITE && t.getType().isBoolean() ){
+ return NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, t[0], t[1] ),
+ NodeManager::currentNM()->mkNode( AND, t[0].negate(), t[2] ) );
+ }
+ return Node::null();
+}
+
+
+Kind TermDbSygus::getComparisonKind( TypeNode tn ) {
+ if( tn.isInteger() || tn.isReal() ){
+ return LT;
+ }else if( tn.isBitVector() ){
+ return BITVECTOR_ULT;
+ }else{
+ return UNDEFINED_KIND;
+ }
+}
+
+Kind TermDbSygus::getPlusKind( TypeNode tn, bool is_neg ) {
+ if( tn.isInteger() || tn.isReal() ){
+ return is_neg ? MINUS : PLUS;
+ }else if( tn.isBitVector() ){
+ return is_neg ? BITVECTOR_SUB : BITVECTOR_PLUS;
+ }else{
+ return UNDEFINED_KIND;
+ }
+}
+
+Node TermDbSygus::getSemanticSkolem( TypeNode tn, Node n, bool doMk ){
+ std::map< Node, Node >::iterator its = d_semantic_skolem[tn].find( n );
+ if( its!=d_semantic_skolem[tn].end() ){
+ return its->second;
+ }else if( doMk ){
+ Node ss = NodeManager::currentNM()->mkSkolem( "sem", tn, "semantic skolem for sygus" );
+ d_semantic_skolem[tn][n] = ss;
+ return ss;
+ }else{
+ return Node::null();
+ }
+}
+
+bool TermDbSygus::involvesDivByZero( Node n, std::map< Node, bool >& visited ){
+ if( visited.find( n )==visited.end() ){
+ visited[n] = true;
+ Kind k = n.getKind();
+ if( k==DIVISION || k==DIVISION_TOTAL || k==INTS_DIVISION || k==INTS_DIVISION_TOTAL ||
+ k==INTS_MODULUS || k==INTS_MODULUS_TOTAL ){
+ if( n[1].isConst() ){
+ if (n[1]
+ == d_quantEngine->getTermUtil()->getTypeValue(n[1].getType(), 0))
+ {
+ return true;
+ }
+ }else{
+ // if it has free variables it might be a non-zero constant
+ if( !hasFreeVar( n[1] ) ){
+ return true;
+ }
+ }
+ }
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ if( involvesDivByZero( n[i], visited ) ){
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool TermDbSygus::involvesDivByZero( Node n ) {
+ std::map< Node, bool > visited;
+ return involvesDivByZero( n, visited );
+}
+
+void doStrReplace(std::string& str, const std::string& oldStr, const std::string& newStr){
+ size_t pos = 0;
+ while((pos = str.find(oldStr, pos)) != std::string::npos){
+ str.replace(pos, oldStr.length(), newStr);
+ pos += newStr.length();
+ }
+}
+
+Kind TermDbSygus::getOperatorKind( Node op ) {
+ Assert( op.getKind()!=BUILTIN );
+ if (op.getKind() == LAMBDA)
+ {
+ // we use APPLY_UF instead of APPLY, since the rewriter for APPLY_UF
+ // does beta-reduction but does not for APPLY
+ return APPLY_UF;
+ }else{
+ TypeNode tn = op.getType();
+ if( tn.isConstructor() ){
+ return APPLY_CONSTRUCTOR;
+ }
+ else if (tn.isSelector())
+ {
+ return APPLY_SELECTOR;
+ }
+ else if (tn.isTester())
+ {
+ return APPLY_TESTER;
+ }
+ else if (tn.isFunction())
+ {
+ return APPLY_UF;
+ }
+ return NodeManager::operatorToKind(op);
+ }
+}
+
+Node TermDbSygus::getAnchor( Node n ) {
+ if( n.getKind()==APPLY_SELECTOR_TOTAL ){
+ return getAnchor( n[0] );
+ }else{
+ return n;
+ }
+}
+
+unsigned TermDbSygus::getAnchorDepth( Node n ) {
+ if( n.getKind()==APPLY_SELECTOR_TOTAL ){
+ return 1+getAnchorDepth( n[0] );
+ }else{
+ return 0;
+ }
+}
+
+
+void TermDbSygus::registerEvalTerm( Node n ) {
+ if( options::sygusDirectEval() ){
+ if( n.getKind()==APPLY_UF && !n.getType().isBoolean() ){
+ TypeNode tn = n[0].getType();
+ if( tn.isDatatype() ){
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ if( dt.isSygus() ){
+ Node f = n.getOperator();
+ if( n[0].getKind()!=APPLY_CONSTRUCTOR ){
+ if (d_eval_processed.find(n) == d_eval_processed.end())
+ {
+ Trace("sygus-eager")
+ << "TermDbSygus::eager: Register eval term : " << n
+ << std::endl;
+ d_eval_processed.insert(n);
+ d_evals[n[0]].push_back(n);
+ TypeNode tn = n[0].getType();
+ Assert(tn.isDatatype());
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ Node var_list = Node::fromExpr(dt.getSygusVarList());
+ Assert(dt.isSygus());
+ d_eval_args[n[0]].push_back(std::vector<Node>());
+ bool isConst = true;
+ for (unsigned j = 1; j < n.getNumChildren(); j++)
+ {
+ d_eval_args[n[0]].back().push_back(n[j]);
+ if (!n[j].isConst())
+ {
+ isConst = false;
+ }
+ }
+ d_eval_args_const[n[0]].push_back(isConst);
+ Node a = getAnchor(n[0]);
+ d_subterms[a][n[0]] = true;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void TermDbSygus::registerModelValue( Node a, Node v, std::vector< Node >& terms, std::vector< Node >& vals, std::vector< Node >& exps ) {
+ std::map< Node, std::map< Node, bool > >::iterator its = d_subterms.find( a );
+ if( its!=d_subterms.end() ){
+ Trace("sygus-eager") << "registerModelValue : " << a << ", has " << its->second.size() << " registered subterms." << std::endl;
+ for( std::map< Node, bool >::iterator itss = its->second.begin(); itss != its->second.end(); ++itss ){
+ Node n = itss->first;
+ Trace("sygus-eager-debug") << "...process : " << n << std::endl;
+ std::map< Node, std::vector< std::vector< Node > > >::iterator it = d_eval_args.find( n );
+ if( it!=d_eval_args.end() && !it->second.empty() ){
+ TNode at = a;
+ TNode vt = v;
+ Node vn = n.substitute( at, vt );
+ vn = Rewriter::rewrite( vn );
+ unsigned start = d_node_mv_args_proc[n][vn];
+ // get explanation in terms of testers
+ std::vector< Node > antec_exp;
+ d_syexp->getExplanationForConstantEquality(n, vn, antec_exp);
+ Node antec = antec_exp.size()==1 ? antec_exp[0] : NodeManager::currentNM()->mkNode( kind::AND, antec_exp );
+ //Node antec = n.eqNode( vn );
+ TypeNode tn = n.getType();
+ Assert( tn.isDatatype() );
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ Assert( dt.isSygus() );
+ Trace("sygus-eager") << "TermDbSygus::eager: Register model value : " << vn << " for " << n << std::endl;
+ Trace("sygus-eager") << "...it has " << it->second.size() << " evaluations, already processed " << start << "." << std::endl;
+ Node bTerm = sygusToBuiltin( vn, tn );
+ Trace("sygus-eager") << "Built-in term : " << bTerm << std::endl;
+ std::vector< Node > vars;
+ Node var_list = Node::fromExpr( dt.getSygusVarList() );
+ for( unsigned j=0; j<var_list.getNumChildren(); j++ ){
+ vars.push_back( var_list[j] );
+ }
+ //evaluation children
+ std::vector< Node > eval_children;
+ eval_children.push_back( Node::fromExpr( dt.getSygusEvaluationFunc() ) );
+ eval_children.push_back( n );
+ //for each evaluation
+ for( unsigned i=start; i<it->second.size(); i++ ){
+ Node res;
+ Node expn;
+ // unfold?
+ bool do_unfold = false;
+ if( options::sygusUnfoldBool() ){
+ if( bTerm.getKind()==ITE || bTerm.getType().isBoolean() ){
+ do_unfold = true;
+ }
+ }
+ if( do_unfold ){
+ // TODO : this is replicated for different values, possibly do better caching
+ std::map< Node, Node > vtm;
+ std::vector< Node > exp;
+ vtm[n] = vn;
+ eval_children.insert( eval_children.end(), it->second[i].begin(), it->second[i].end() );
+ Node eval_fun = NodeManager::currentNM()->mkNode( kind::APPLY_UF, eval_children );
+ eval_children.resize( 2 );
+ res = unfold( eval_fun, vtm, exp );
+ expn = exp.size()==1 ? exp[0] : NodeManager::currentNM()->mkNode( kind::AND, exp );
+ }else{
+
+ EvalSygusInvarianceTest esit;
+ eval_children.insert( eval_children.end(), it->second[i].begin(), it->second[i].end() );
+ Node conj =
+ NodeManager::currentNM()->mkNode(kind::APPLY_UF, eval_children);
+ eval_children[1] = vn;
+ Node eval_fun = NodeManager::currentNM()->mkNode( kind::APPLY_UF, eval_children );
+ res = evaluateWithUnfolding(eval_fun);
+ esit.init(conj, n, res);
+ eval_children.resize( 2 );
+ eval_children[1] = n;
+
+ //evaluate with minimal explanation
+ std::vector< Node > mexp;
+ d_syexp->getExplanationFor(n, vn, mexp, esit);
+ Assert( !mexp.empty() );
+ expn = mexp.size()==1 ? mexp[0] : NodeManager::currentNM()->mkNode( kind::AND, mexp );
+
+ //if all constant, we can use evaluation to minimize the explanation
+ //Assert( i<d_eval_args_const[n].size() );
+ //if( d_eval_args_const[n][i] ){
+ /*
+ std::map< Node, Node > vtm;
+ std::map< Node, Node > visited;
+ std::map< Node, std::vector< Node > > exp;
+ vtm[n] = vn;
+ res = crefEvaluate( eval_fun, vtm, visited, exp );
+ Assert( !exp[eval_fun].empty() );
+ expn = exp[eval_fun].size()==1 ? exp[eval_fun][0] : NodeManager::currentNM()->mkNode( kind::AND, exp[eval_fun] );
+ */
+ /*
+ //otherwise, just do a substitution
+ }else{
+ Assert( vars.size()==it->second[i].size() );
+ res = bTerm.substitute( vars.begin(), vars.end(), it->second[i].begin(), it->second[i].end() );
+ res = Rewriter::rewrite( res );
+ expn = antec;
+ }
+ */
+ }
+ Assert( !res.isNull() );
+ terms.push_back( d_evals[n][i] );
+ vals.push_back( res );
+ exps.push_back( expn );
+ Trace("sygus-eager") << "Conclude : " << d_evals[n][i] << " == " << res << ", cref eval = " << d_eval_args_const[n][i] << std::endl;
+ Trace("sygus-eager") << " from " << expn << std::endl;
+ }
+ d_node_mv_args_proc[n][vn] = it->second.size();
+ }
+ }
+ }
+}
+
+Node TermDbSygus::unfold( Node en, std::map< Node, Node >& vtm, std::vector< Node >& exp, bool track_exp ) {
+ if( en.getKind()==kind::APPLY_UF ){
+ Trace("sygus-db-debug") << "Unfold : " << en << std::endl;
+ Node ev = en[0];
+ if( track_exp ){
+ std::map< Node, Node >::iterator itv = vtm.find( en[0] );
+ if( itv!=vtm.end() ){
+ ev = itv->second;
+ }else{
+ Assert( false );
+ }
+ Assert( en[0].getType()==ev.getType() );
+ Assert( ev.isConst() );
+ }
+ Assert( ev.getKind()==kind::APPLY_CONSTRUCTOR );
+ std::vector< Node > args;
+ for( unsigned i=1; i<en.getNumChildren(); i++ ){
+ args.push_back( en[i] );
+ }
+ const Datatype& dt = ((DatatypeType)(ev.getType()).toType()).getDatatype();
+ unsigned i = Datatype::indexOf( ev.getOperator().toExpr() );
+ if( track_exp ){
+ //explanation
+ Node ee = NodeManager::currentNM()->mkNode( kind::APPLY_TESTER, Node::fromExpr( dt[i].getTester() ), en[0] );
+ if( std::find( exp.begin(), exp.end(), ee )==exp.end() ){
+ exp.push_back( ee );
+ }
+ }
+ Assert( !dt.isParametric() );
+ std::map< int, Node > pre;
+ for( unsigned j=0; j<dt[i].getNumArgs(); j++ ){
+ std::vector< Node > cc;
+ //get the evaluation argument for the selector
+ Type rt = dt[i][j].getRangeType();
+ const Datatype & ad = ((DatatypeType)dt[i][j].getRangeType()).getDatatype();
+ cc.push_back( Node::fromExpr( ad.getSygusEvaluationFunc() ) );
+ Node s;
+ if( en[0].getKind()==kind::APPLY_CONSTRUCTOR ){
+ s = en[0][j];
+ }else{
+ s = NodeManager::currentNM()->mkNode( kind::APPLY_SELECTOR_TOTAL, dt[i].getSelectorInternal( en[0].getType().toType(), j ), en[0] );
+ }
+ cc.push_back( s );
+ if( track_exp ){
+ //update vtm map
+ vtm[s] = ev[j];
+ }
+ cc.insert( cc.end(), args.begin(), args.end() );
+ pre[j] = NodeManager::currentNM()->mkNode( kind::APPLY_UF, cc );
+ }
+ std::map< TypeNode, int > var_count;
+ Node ret = mkGeneric( dt, i, var_count, pre );
+ // if it is a variable, apply the substitution
+ if( ret.getKind()==kind::BOUND_VARIABLE ){
+ Assert( ret.hasAttribute(SygusVarNumAttribute()) );
+ int i = ret.getAttribute(SygusVarNumAttribute());
+ Assert( Node::fromExpr( dt.getSygusVarList() )[i]==ret );
+ ret = args[i];
+ }
+ else
+ {
+ ret = Rewriter::rewrite(ret);
+ }
+ return ret;
+ }else{
+ Assert( en.isConst() );
+ }
+ return en;
+}
+
+
+Node TermDbSygus::getEagerUnfold( Node n, std::map< Node, Node >& visited ) {
+ std::map< Node, Node >::iterator itv = visited.find( n );
+ if( itv==visited.end() ){
+ Trace("cegqi-eager-debug") << "getEagerUnfold " << n << std::endl;
+ Node ret;
+ if( n.getKind()==APPLY_UF ){
+ TypeNode tn = n[0].getType();
+ Trace("cegqi-eager-debug") << "check " << n[0].getType() << std::endl;
+ if( tn.isDatatype() ){
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ if( dt.isSygus() ){
+ Trace("cegqi-eager") << "Unfold eager : " << n << std::endl;
+ Node bTerm = sygusToBuiltin( n[0], tn );
+ Trace("cegqi-eager") << "Built-in term : " << bTerm << std::endl;
+ std::vector< Node > vars;
+ std::vector< Node > subs;
+ Node var_list = Node::fromExpr( dt.getSygusVarList() );
+ Assert( var_list.getNumChildren()+1==n.getNumChildren() );
+ for( unsigned j=0; j<var_list.getNumChildren(); j++ ){
+ vars.push_back( var_list[j] );
+ }
+ for( unsigned j=1; j<n.getNumChildren(); j++ ){
+ Node nc = getEagerUnfold( n[j], visited );
+ subs.push_back( nc );
+ Assert(subs[j - 1].getType().isComparableTo(
+ var_list[j - 1].getType()));
+ }
+ Assert( vars.size()==subs.size() );
+ bTerm = bTerm.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() );
+ Trace("cegqi-eager") << "Built-in term after subs : " << bTerm << std::endl;
+ Trace("cegqi-eager-debug") << "Types : " << bTerm.getType() << " " << n.getType() << std::endl;
+ Assert(n.getType().isComparableTo(bTerm.getType()));
+ ret = bTerm;
+ }
+ }
+ }
+ if( ret.isNull() ){
+ if( n.getKind()!=FORALL ){
+ bool childChanged = false;
+ std::vector< Node > children;
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ Node nc = getEagerUnfold( n[i], visited );
+ childChanged = childChanged || n[i]!=nc;
+ children.push_back( nc );
+ }
+ if( childChanged ){
+ if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){
+ children.insert( children.begin(), n.getOperator() );
+ }
+ ret = NodeManager::currentNM()->mkNode( n.getKind(), children );
+ }
+ }
+ if( ret.isNull() ){
+ ret = n;
+ }
+ }
+ visited[n] = ret;
+ return ret;
+ }else{
+ return itv->second;
+ }
+}
+
+
+Node TermDbSygus::evaluateBuiltin( TypeNode tn, Node bn, std::vector< Node >& args ) {
+ if( !args.empty() ){
+ std::map< TypeNode, std::vector< Node > >::iterator it = d_var_list.find( tn );
+ Assert( it!=d_var_list.end() );
+ Assert( it->second.size()==args.size() );
+ return Rewriter::rewrite( bn.substitute( it->second.begin(), it->second.end(), args.begin(), args.end() ) );
+ }else{
+ return Rewriter::rewrite( bn );
+ }
+}
+
+Node TermDbSygus::evaluateWithUnfolding(
+ Node n, std::unordered_map<Node, Node, NodeHashFunction>& visited)
+{
+ std::unordered_map<Node, Node, NodeHashFunction>::iterator it =
+ visited.find(n);
+ if( it==visited.end() ){
+ Node ret = n;
+ while( ret.getKind()==APPLY_UF && ret[0].getKind()==APPLY_CONSTRUCTOR ){
+ ret = unfold( ret );
+ }
+ if( ret.getNumChildren()>0 ){
+ std::vector< Node > children;
+ if( ret.getMetaKind() == kind::metakind::PARAMETERIZED ){
+ children.push_back( ret.getOperator() );
+ }
+ bool childChanged = false;
+ for( unsigned i=0; i<ret.getNumChildren(); i++ ){
+ Node nc = evaluateWithUnfolding( ret[i], visited );
+ childChanged = childChanged || nc!=ret[i];
+ children.push_back( nc );
+ }
+ if( childChanged ){
+ ret = NodeManager::currentNM()->mkNode( ret.getKind(), children );
+ }
+ ret = getExtRewriter()->extendedRewrite(ret);
+ }
+ visited[n] = ret;
+ return ret;
+ }else{
+ return it->second;
+ }
+}
+
+Node TermDbSygus::evaluateWithUnfolding( Node n ) {
+ std::unordered_map<Node, Node, NodeHashFunction> visited;
+ return evaluateWithUnfolding( n, visited );
+}
+
+}/* CVC4::theory::quantifiers namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
--- /dev/null
+/********************* */
+/*! \file term_database_sygus.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief term database sygus class
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__TERM_DATABASE_SYGUS_H
+#define __CVC4__THEORY__QUANTIFIERS__TERM_DATABASE_SYGUS_H
+
+#include <unordered_set>
+
+#include "theory/quantifiers/extended_rewrite.h"
+#include "theory/quantifiers/sygus/sygus_explain.h"
+#include "theory/quantifiers/term_database.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+class CegConjecture;
+
+// TODO :issue #1235 split and document this class
+class TermDbSygus {
+ public:
+ TermDbSygus(context::Context* c, QuantifiersEngine* qe);
+ ~TermDbSygus() {}
+ /** Reset this utility */
+ bool reset(Theory::Effort e);
+ /** Identify this utility */
+ std::string identify() const { return "TermDbSygus"; }
+ /** register the sygus type */
+ void registerSygusType(TypeNode tn);
+ /** register a variable e that we will do enumerative search on
+ * conj is the conjecture that the enumeration of e is for.
+ * f is the synth-fun that the enumeration of e is for.
+ * mkActiveGuard is whether we want to make an active guard for e
+ * (see d_enum_to_active_guard).
+ *
+ * Notice that enumerator e may not be equivalent
+ * to f in synthesis-through-unification approaches
+ * (e.g. decision tree construction for PBE synthesis).
+ */
+ void registerEnumerator(Node e,
+ Node f,
+ CegConjecture* conj,
+ bool mkActiveGuard = false);
+ /** is e an enumerator? */
+ bool isEnumerator(Node e) const;
+ /** return the conjecture e is associated with */
+ CegConjecture* getConjectureForEnumerator(Node e);
+ /** return the function-to-synthesize e is associated with */
+ Node getSynthFunForEnumerator(Node e);
+ /** get active guard for e */
+ Node getActiveGuardForEnumerator(Node e);
+ /** get all registered enumerators */
+ void getEnumerators(std::vector<Node>& mts);
+ /** get the explanation utility */
+ SygusExplain* getExplain() { return d_syexp.get(); }
+ /** get the extended rewrite utility */
+ ExtendedRewriter* getExtRewriter() { return d_ext_rw.get(); }
+ //-----------------------------conversion from sygus to builtin
+ /** get free variable
+ *
+ * This class caches a list of free variables for each type, which are
+ * used, for instance, for constructing canonical forms of terms with free
+ * variables. This function returns the i^th free variable for type tn.
+ * If useSygusType is true, then this function returns a variable of the
+ * analog type for sygus type tn (see d_fv for details).
+ */
+ TNode getFreeVar(TypeNode tn, int i, bool useSygusType = false);
+ /** get free variable and increment
+ *
+ * This function returns the next free variable for type tn, and increments
+ * the counter in var_count for that type.
+ */
+ TNode getFreeVarInc(TypeNode tn,
+ std::map<TypeNode, int>& var_count,
+ bool useSygusType = false);
+ /** returns true if n is a cached free variable (in d_fv). */
+ bool isFreeVar(Node n) { return d_fv_stype.find(n) != d_fv_stype.end(); }
+ /** returns the index of n in the free variable cache (d_fv). */
+ int getVarNum(Node n) { return d_fv_num[n]; }
+ /** returns true if n has a cached free variable (in d_fv). */
+ bool hasFreeVar(Node n);
+ /** make generic
+ *
+ * This function returns a builtin term f( t1, ..., tn ) where f is the
+ * builtin op of the sygus datatype constructor specified by arguments
+ * dt and c. The mapping pre maps child indices to the term for that index
+ * in the term we are constructing. That is, for each i = 1,...,n:
+ * If i is in the domain of pre, then ti = pre[i].
+ * If i is not in the domain of pre, then ti = d_fv[1][ var_count[Ti ] ],
+ * and var_count[Ti] is incremented.
+ */
+ Node mkGeneric(const Datatype& dt,
+ unsigned c,
+ std::map<TypeNode, int>& var_count,
+ std::map<int, Node>& pre);
+ /** same as above, but with empty var_count */
+ Node mkGeneric(const Datatype& dt, int c, std::map<int, Node>& pre);
+ /** sygus to builtin
+ *
+ * Given a sygus datatype term n of type tn, this function returns its analog,
+ * that is, the term that n encodes.
+ */
+ Node sygusToBuiltin(Node n, TypeNode tn);
+ /** same as above, but without tn */
+ Node sygusToBuiltin(Node n) { return sygusToBuiltin(n, n.getType()); }
+ //-----------------------------end conversion from sygus to builtin
+
+ private:
+ /** reference to the quantifiers engine */
+ QuantifiersEngine* d_quantEngine;
+ /** sygus explanation */
+ std::unique_ptr<SygusExplain> d_syexp;
+ /** sygus explanation */
+ std::unique_ptr<ExtendedRewriter> d_ext_rw;
+ /** mapping from enumerator terms to the conjecture they are associated with
+ */
+ std::map<Node, CegConjecture*> d_enum_to_conjecture;
+ /** mapping from enumerator terms to the function-to-synthesize they are
+ * associated with
+ */
+ std::map<Node, Node> d_enum_to_synth_fun;
+ /** mapping from enumerator terms to the guard they are associated with
+ * The guard G for an enumerator e has the semantics
+ * if G is true, then there are more values of e to enumerate".
+ */
+ std::map<Node, Node> d_enum_to_active_guard;
+
+ //-----------------------------conversion from sygus to builtin
+ /** cache for sygusToBuiltin */
+ std::map<TypeNode, std::map<Node, Node> > d_sygus_to_builtin;
+ /** a cache of fresh variables for each type
+ *
+ * We store two versions of this list:
+ * index 0: mapping from builtin types to fresh variables of that type,
+ * index 1: mapping from sygus types to fresh varaibles of the type they
+ * encode.
+ */
+ std::map<TypeNode, std::vector<Node> > d_fv[2];
+ /** Maps free variables to the domain type they are associated with in d_fv */
+ std::map<Node, TypeNode> d_fv_stype;
+ /** Maps free variables to their index in d_fv. */
+ std::map<Node, int> d_fv_num;
+ /** recursive helper for hasFreeVar, visited stores nodes we have visited. */
+ bool hasFreeVar(Node n, std::map<Node, bool>& visited);
+ //-----------------------------end conversion from sygus to builtin
+
+ // TODO :issue #1235 : below here needs refactor
+
+ public:
+ Node d_true;
+ Node d_false;
+
+private:
+ void computeMinTypeDepthInternal( TypeNode root_tn, TypeNode tn, unsigned type_depth );
+ bool involvesDivByZero( Node n, std::map< Node, bool >& visited );
+
+ private:
+ // information for sygus types
+ std::map<TypeNode, TypeNode> d_register; // stores sygus -> builtin type
+ std::map<TypeNode, std::vector<Node> > d_var_list;
+ std::map<TypeNode, std::map<int, Kind> > d_arg_kind;
+ std::map<TypeNode, std::map<Kind, int> > d_kinds;
+ std::map<TypeNode, std::map<int, Node> > d_arg_const;
+ std::map<TypeNode, std::map<Node, int> > d_consts;
+ std::map<TypeNode, std::map<Node, int> > d_ops;
+ std::map<TypeNode, std::map<int, Node> > d_arg_ops;
+ std::map<TypeNode, std::map<Node, Node> > d_semantic_skolem;
+ // grammar information
+ // root -> type -> _
+ std::map<TypeNode, std::map<TypeNode, unsigned> > d_min_type_depth;
+ // std::map< TypeNode, std::map< Node, std::map< std::map< int, bool > > >
+ // d_consider_const;
+ // type -> cons -> _
+ std::map<TypeNode, unsigned> d_min_term_size;
+ std::map<TypeNode, std::map<unsigned, unsigned> > d_min_cons_term_size;
+ /** a cache for getSelectorWeight */
+ std::map<TypeNode, std::map<Node, unsigned> > d_sel_weight;
+
+ public: // general sygus utilities
+ bool isRegistered( TypeNode tn );
+ // get the minimum depth of type in its parent grammar
+ unsigned getMinTypeDepth( TypeNode root_tn, TypeNode tn );
+ // get the minimum size for a constructor term
+ unsigned getMinTermSize( TypeNode tn );
+ unsigned getMinConsTermSize( TypeNode tn, unsigned cindex );
+ /** get the weight of the selector, where tn is the domain of sel */
+ unsigned getSelectorWeight(TypeNode tn, Node sel);
+
+ public:
+ TypeNode sygusToBuiltinType( TypeNode tn );
+ int getKindConsNum( TypeNode tn, Kind k );
+ int getConstConsNum( TypeNode tn, Node n );
+ int getOpConsNum( TypeNode tn, Node n );
+ bool hasKind( TypeNode tn, Kind k );
+ bool hasConst( TypeNode tn, Node n );
+ bool hasOp( TypeNode tn, Node n );
+ Node getConsNumConst( TypeNode tn, int i );
+ Node getConsNumOp( TypeNode tn, int i );
+ Kind getConsNumKind( TypeNode tn, int i );
+ bool isKindArg( TypeNode tn, int i );
+ bool isConstArg( TypeNode tn, int i );
+ /** get arg type */
+ TypeNode getArgType(const DatatypeConstructor& c, unsigned i);
+ /** get first occurrence */
+ int getFirstArgOccurrence( const DatatypeConstructor& c, TypeNode tn );
+ /** is type match */
+ bool isTypeMatch( const DatatypeConstructor& c1, const DatatypeConstructor& c2 );
+
+ TypeNode getSygusTypeForVar( Node v );
+ Node sygusSubstituted( TypeNode tn, Node n, std::vector< Node >& args );
+ Node getSygusNormalized( Node n, std::map< TypeNode, int >& var_count, std::map< Node, Node >& subs );
+ Node getNormalized(TypeNode t, Node prog);
+ unsigned getSygusTermSize( Node n );
+ /** given a term, construct an equivalent smaller one that respects syntax */
+ Node minimizeBuiltinTerm( Node n );
+ /** given a term, expand it into more basic components */
+ Node expandBuiltinTerm( Node n );
+ /** get comparison kind */
+ Kind getComparisonKind( TypeNode tn );
+ Kind getPlusKind( TypeNode tn, bool is_neg = false );
+ // get semantic skolem for n (a sygus term whose builtin version is n)
+ Node getSemanticSkolem( TypeNode tn, Node n, bool doMk = true );
+ /** involves div-by-zero */
+ bool involvesDivByZero( Node n );
+
+ /** get operator kind */
+ static Kind getOperatorKind( Node op );
+
+ /** get anchor */
+ static Node getAnchor( Node n );
+ static unsigned getAnchorDepth( Node n );
+
+public: // for symmetry breaking
+ bool considerArgKind( TypeNode tn, TypeNode tnp, Kind k, Kind pk, int arg );
+ bool considerConst( TypeNode tn, TypeNode tnp, Node c, Kind pk, int arg );
+ bool considerConst( const Datatype& pdt, TypeNode tnp, Node c, Kind pk, int arg );
+ int solveForArgument( TypeNode tnp, unsigned cindex, unsigned arg );
+
+//for eager instantiation
+ // TODO (as part of #1235) move some of these functions to sygus_explain.h
+ private:
+ /** the set of evaluation terms we have already processed */
+ std::unordered_set<Node, NodeHashFunction> d_eval_processed;
+ std::map< Node, std::map< Node, bool > > d_subterms;
+ std::map< Node, std::vector< Node > > d_evals;
+ std::map< Node, std::vector< std::vector< Node > > > d_eval_args;
+ std::map< Node, std::vector< bool > > d_eval_args_const;
+ std::map< Node, std::map< Node, unsigned > > d_node_mv_args_proc;
+
+public:
+ void registerEvalTerm( Node n );
+ void registerModelValue( Node n, Node v, std::vector< Node >& exps, std::vector< Node >& terms, std::vector< Node >& vals );
+ Node unfold( Node en, std::map< Node, Node >& vtm, std::vector< Node >& exp, bool track_exp = true );
+ Node unfold( Node en ){
+ std::map< Node, Node > vtm;
+ std::vector< Node > exp;
+ return unfold( en, vtm, exp, false );
+ }
+ Node getEagerUnfold( Node n, std::map< Node, Node >& visited );
+
+ // builtin evaluation, returns rewrite( bn [ args / vars(tn) ] )
+ Node evaluateBuiltin( TypeNode tn, Node bn, std::vector< Node >& args );
+ // evaluate with unfolding
+ Node evaluateWithUnfolding(
+ Node n, std::unordered_map<Node, Node, NodeHashFunction>& visited);
+ Node evaluateWithUnfolding( Node n );
+};
+
+}/* CVC4::theory::quantifiers namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
+#endif /* __CVC4__THEORY__QUANTIFIERS__TERM_DATABASE_H */
+++ /dev/null
-/********************* */
-/*! \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<Node> currc;
- d_kind.push_back(n.getKind());
- if (n.getMetaKind() == kind::metakind::PARAMETERIZED)
- {
- currc.push_back(n.getOperator());
- d_has_op.push_back(true);
- }
- else
- {
- d_has_op.push_back(false);
- }
- for (unsigned i = 0; i < n.getNumChildren(); i++)
- {
- currc.push_back(n[i]);
- }
- d_children.push_back(currc);
-}
-
-void TermRecBuild::init(Node n)
-{
- Assert(d_term.empty());
- addTerm(n);
-}
-
-void TermRecBuild::push(unsigned p)
-{
- Assert(!d_term.empty());
- unsigned curr = d_term.size() - 1;
- Assert(d_pos.size() == curr);
- Assert(d_pos.size() + 1 == d_children.size());
- Assert(p < d_term[curr].getNumChildren());
- addTerm(d_term[curr][p]);
- d_pos.push_back(p);
-}
-
-void TermRecBuild::pop()
-{
- Assert(!d_pos.empty());
- d_pos.pop_back();
- d_kind.pop_back();
- d_has_op.pop_back();
- d_children.pop_back();
- d_term.pop_back();
-}
-
-void TermRecBuild::replaceChild(unsigned i, Node r)
-{
- Assert(!d_term.empty());
- unsigned curr = d_term.size() - 1;
- unsigned o = d_has_op[curr] ? 1 : 0;
- d_children[curr][i + o] = r;
-}
-
-Node TermRecBuild::getChild(unsigned i)
-{
- unsigned curr = d_term.size() - 1;
- unsigned o = d_has_op[curr] ? 1 : 0;
- return d_children[curr][i + o];
-}
-
-Node TermRecBuild::build(unsigned d)
-{
- Assert(d_pos.size() + 1 == d_term.size());
- Assert(d < d_term.size());
- int p = d < d_pos.size() ? d_pos[d] : -2;
- std::vector<Node> children;
- unsigned o = d_has_op[d] ? 1 : 0;
- for (unsigned i = 0; i < d_children[d].size(); i++)
- {
- Node nc;
- if (p + o == i)
- {
- nc = build(d + 1);
- }
- else
- {
- nc = d_children[d][i];
- }
- children.push_back(nc);
- }
- return NodeManager::currentNM()->mkNode(d_kind[d], children);
-}
-
-void SygusExplain::getExplanationForConstantEquality(Node n,
- Node vn,
- std::vector<Node>& exp)
-{
- std::map<unsigned, bool> cexc;
- getExplanationForConstantEquality(n, vn, exp, cexc);
-}
-
-void SygusExplain::getExplanationForConstantEquality(
- Node n, Node vn, std::vector<Node>& exp, std::map<unsigned, bool>& cexc)
-{
- Assert(vn.getKind() == kind::APPLY_CONSTRUCTOR);
- Assert(n.getType() == vn.getType());
- TypeNode tn = n.getType();
- Assert(tn.isDatatype());
- const Datatype& dt = ((DatatypeType)tn.toType()).getDatatype();
- int i = Datatype::indexOf(vn.getOperator().toExpr());
- Node tst = datatypes::DatatypesRewriter::mkTester(n, i, dt);
- exp.push_back(tst);
- for (unsigned j = 0; j < vn.getNumChildren(); j++)
- {
- if (cexc.find(j) == cexc.end())
- {
- Node sel = NodeManager::currentNM()->mkNode(
- kind::APPLY_SELECTOR_TOTAL,
- Node::fromExpr(dt[i].getSelectorInternal(tn.toType(), j)),
- n);
- getExplanationForConstantEquality(sel, vn[j], exp);
- }
- }
-}
-
-Node SygusExplain::getExplanationForConstantEquality(Node n, Node vn)
-{
- std::map<unsigned, bool> cexc;
- return getExplanationForConstantEquality(n, vn, cexc);
-}
-
-Node SygusExplain::getExplanationForConstantEquality(
- Node n, Node vn, std::map<unsigned, bool>& cexc)
-{
- std::vector<Node> exp;
- getExplanationForConstantEquality(n, vn, exp, cexc);
- Assert(!exp.empty());
- return exp.size() == 1 ? exp[0]
- : NodeManager::currentNM()->mkNode(kind::AND, exp);
-}
-
-// we have ( n = vn => eval( n ) = bvr ) ^ vn != vnr , returns exp such that exp
-// => ( eval( n ) = bvr ^ vn != vnr )
-void SygusExplain::getExplanationFor(TermRecBuild& trb,
- Node n,
- Node vn,
- std::vector<Node>& exp,
- std::map<TypeNode, int>& var_count,
- SygusInvarianceTest& et,
- Node vnr,
- Node& vnr_exp,
- int& sz)
-{
- Assert(vnr.isNull() || vn != vnr);
- Assert(vn.getKind() == APPLY_CONSTRUCTOR);
- Assert(vnr.isNull() || vnr.getKind() == APPLY_CONSTRUCTOR);
- Assert(n.getType() == vn.getType());
- TypeNode ntn = n.getType();
- std::map<unsigned, bool> cexc;
- // for each child,
- // check whether replacing that child by a fresh variable
- // also satisfies the invariance test.
- for (unsigned i = 0; i < vn.getNumChildren(); i++)
- {
- TypeNode xtn = vn[i].getType();
- Node x = d_tdb->getFreeVarInc(xtn, var_count);
- trb.replaceChild(i, x);
- Node nvn = trb.build();
- Assert(nvn.getKind() == kind::APPLY_CONSTRUCTOR);
- if (et.is_invariant(d_tdb, nvn, x))
- {
- cexc[i] = true;
- // we are tracking term size if positive
- if (sz >= 0)
- {
- int s = d_tdb->getSygusTermSize(vn[i]);
- sz = sz - s;
- }
- }
- else
- {
- trb.replaceChild(i, vn[i]);
- }
- }
- const Datatype& dt = ((DatatypeType)ntn.toType()).getDatatype();
- int cindex = Datatype::indexOf(vn.getOperator().toExpr());
- Assert(cindex >= 0 && cindex < (int)dt.getNumConstructors());
- Node tst = datatypes::DatatypesRewriter::mkTester(n, cindex, dt);
- exp.push_back(tst);
- // if the operator of vn is different than vnr, then disunification obligation
- // is met
- if (!vnr.isNull())
- {
- if (vnr.getOperator() != vn.getOperator())
- {
- vnr = Node::null();
- vnr_exp = NodeManager::currentNM()->mkConst(true);
- }
- }
- for (unsigned i = 0; i < vn.getNumChildren(); i++)
- {
- Node sel = NodeManager::currentNM()->mkNode(
- kind::APPLY_SELECTOR_TOTAL,
- Node::fromExpr(dt[cindex].getSelectorInternal(ntn.toType(), i)),
- n);
- Node vnr_c = vnr.isNull() ? vnr : (vn[i] == vnr[i] ? Node::null() : vnr[i]);
- if (cexc.find(i) == cexc.end())
- {
- trb.push(i);
- Node vnr_exp_c;
- getExplanationFor(
- trb, sel, vn[i], exp, var_count, et, vnr_c, vnr_exp_c, sz);
- trb.pop();
- if (!vnr_c.isNull())
- {
- Assert(!vnr_exp_c.isNull());
- if (vnr_exp_c.isConst() || vnr_exp.isNull())
- {
- // recursively satisfied the disunification obligation
- if (vnr_exp_c.isConst())
- {
- // was successful, don't consider further
- vnr = Node::null();
- }
- vnr_exp = vnr_exp_c;
- }
- }
- }
- else
- {
- // if excluded, we may need to add the explanation for this
- if (vnr_exp.isNull() && !vnr_c.isNull())
- {
- vnr_exp = getExplanationForConstantEquality(sel, vnr[i]);
- }
- }
- }
-}
-
-void SygusExplain::getExplanationFor(Node n,
- Node vn,
- std::vector<Node>& exp,
- SygusInvarianceTest& et,
- Node vnr,
- unsigned& sz)
-{
- // naive :
- // return getExplanationForConstantEquality( n, vn, exp );
-
- // set up the recursion object
- std::map<TypeNode, int> var_count;
- TermRecBuild trb;
- trb.init(vn);
- Node vnr_exp;
- int sz_use = sz;
- getExplanationFor(trb, n, vn, exp, var_count, et, vnr, vnr_exp, sz_use);
- Assert(sz_use >= 0);
- sz = sz_use;
- Assert(vnr.isNull() || !vnr_exp.isNull());
- if (!vnr_exp.isNull() && !vnr_exp.isConst())
- {
- exp.push_back(vnr_exp.negate());
- }
-}
-
-void SygusExplain::getExplanationFor(Node n,
- Node vn,
- std::vector<Node>& exp,
- SygusInvarianceTest& et)
-{
- int sz = -1;
- std::map<TypeNode, int> var_count;
- TermRecBuild trb;
- trb.init(vn);
- Node vnr;
- Node vnr_exp;
- getExplanationFor(trb, n, vn, exp, var_count, et, vnr, vnr_exp, sz);
-}
-
-} /* CVC4::theory::quantifiers namespace */
-} /* CVC4::theory namespace */
-} /* CVC4 namespace */
+++ /dev/null
-/********************* */
-/*! \file sygus_explain.h
- ** \verbatim
- ** Top contributors (to current version):
- ** Andrew Reynolds
- ** This file is part of the CVC4 project.
- ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
- ** in the top-level source directory) and their institutional affiliations.
- ** All rights reserved. See the file COPYING in the top-level source
- ** directory for licensing information.\endverbatim
- **
- ** \brief sygus explanations
- **/
-
-#include "cvc4_private.h"
-
-#ifndef __CVC4__THEORY__QUANTIFIERS__SYGUS_EXPLAIN_H
-#define __CVC4__THEORY__QUANTIFIERS__SYGUS_EXPLAIN_H
-
-#include <vector>
-
-#include "expr/node.h"
-#include "theory/quantifiers/sygus_invariance.h"
-
-namespace CVC4 {
-namespace theory {
-namespace quantifiers {
-
-/** Recursive term builder
- *
- * This is a utility used to generate variants
- * of a term n, where subterms of n can be replaced
- * by others via calls to replaceChild(...).
- *
- * This class maintains a "context", which indicates
- * a position in term n. Below, we call the subterm of
- * the initial term n at this position the "active term".
- *
- */
-class TermRecBuild
-{
- public:
- TermRecBuild() {}
- /** set the initial term to n
- *
- * The context initially empty, that is,
- * the active term is initially n.
- */
- void init(Node n);
-
- /** push the context
- *
- * This updates the context so that the
- * active term is updated to curr[p], where
- * curr is the previously active term.
- */
- void push(unsigned p);
-
- /** pop the context */
- void pop();
- /** indicates that the i^th child of the active
- * term should be replaced by r in calls to build().
- */
- void replaceChild(unsigned i, Node r);
- /** get the i^th child of the active term */
- Node getChild(unsigned i);
- /** build the (modified) version of the term
- * we intialized via the call to init().
- */
- Node build(unsigned p = 0);
-
- private:
- /** stack of active terms */
- std::vector<Node> d_term;
- /** stack of children of active terms
- * Notice that these may be modified with calls to replaceChild(...).
- */
- std::vector<std::vector<Node> > d_children;
- /** stack the kind of active terms */
- std::vector<Kind> d_kind;
- /** stack of whether the active terms had an operator */
- std::vector<bool> d_has_op;
- /** stack of positions that were pushed via calls to push(...) */
- std::vector<unsigned> d_pos;
- /** add term to the context stack */
- void addTerm(Node n);
-};
-
-/*The SygusExplain utility
- *
- * This class is used to produce explanations for refinement lemmas
- * in the counterexample-guided inductive synthesis (CEGIS) loop.
- *
- * When given an invariance test T traverses the AST of a given term,
- * uses TermRecBuild to replace various subterms by fresh variables and
- * recheck whether the invariant, as specified by T still holds.
- * If it does, then we may exclude the explanation for that subterm.
- *
- * For example, say we have that the current value of
- * (datatype) sygus term n is:
- * (if (gt x 0) 0 0)
- * where if, gt, x, 0 are datatype constructors.
- * The explanation returned by getExplanationForConstantEquality
- * below for n and the above term is:
- * { ((_ is if) n), ((_ is geq) n.0),
- * ((_ is x) n.0.0), ((_ is 0) n.0.1),
- * ((_ is 0) n.1), ((_ is 0) n.2) }
- *
- * This class can also return more precise
- * explanations based on a property that holds for
- * variants of n. For instance,
- * say we find that n's builtin analog rewrites to 0:
- * ite( x>0, 0, 0 ) ----> 0
- * and we would like to find the minimal explanation for
- * why the builtin analog of n rewrites to 0.
- * We use the invariance test EquivSygusInvarianceTest
- * (see sygus_invariance.h) for doing this.
- * Using the SygusExplain::getExplanationFor method below,
- * this will invoke the invariant test to check, e.g.
- * ite( x>0, 0, y1 ) ----> 0 ? fail
- * ite( x>0, y2, 0 ) ----> 0 ? fail
- * ite( y3, 0, 0 ) ----> 0 ? success
- * where y1, y2, y3 are fresh variables.
- * Hence the explanation for the condition x>0 is irrelevant.
- * This gives us the explanation:
- * { ((_ is if) n), ((_ is 0) n.1), ((_ is 0) n.2) }
- * indicating that all terms of the form:
- * (if _ 0 0) have a builtin equivalent that rewrites to 0.
- *
- * For details, see Reynolds et al SYNT 2017.
- *
- * Below, we let [[exp]]_n denote the term induced by
- * the explanation exp for n.
- * For example:
- * exp = { ((_ is plus) n), ((_ is y) n.1) }
- * is such that:
- * [[exp]]_n = (plus w y)
- * where w is a fresh variable.
- */
-class SygusExplain
-{
- public:
- SygusExplain(TermDbSygus* tdb) : d_tdb(tdb) {}
- ~SygusExplain() {}
- /** get explanation for constant equality
- *
- * This function constructs an explanation, stored in exp, such that:
- * - All formulas in exp are of the form ((_ is C) ns), where ns
- * is a chain of selectors applied to n, and
- * - exp => ( n = vn )
- */
- void getExplanationForConstantEquality(Node n,
- Node vn,
- std::vector<Node>& exp);
- /** returns the conjunction of exp computed in the above function */
- Node getExplanationForConstantEquality(Node n, Node vn);
-
- /** get explanation for constant equality
- * This is identical to the above function except that we
- * take an additional argument cexc, which says which
- * children of vn should be excluded from the explanation.
- *
- * For example, if vn = plus( plus( x, x ), y ) and cexc is { 0 -> true },
- * then the following is appended to exp :
- * { ((_ is plus) n), ((_ is y) n.1) }
- * where notice that the 0^th argument of vn is excluded.
- */
- void getExplanationForConstantEquality(Node n,
- Node vn,
- std::vector<Node>& exp,
- std::map<unsigned, bool>& cexc);
- /** returns the conjunction of exp computed in the above function */
- Node getExplanationForConstantEquality(Node n,
- Node vn,
- std::map<unsigned, bool>& cexc);
-
- /** get explanation for
- *
- * This function constructs an explanation, stored in exp, such that:
- * - All formulas in exp are of the form ((_ is C) ns), where ns
- * is a chain of selectors applied to n, and
- * - The test et holds for [[exp]]_n, and
- * - (if applicable) exp => ( n != vnr ).
- *
- * This function updates sz to be the term size of [[exp]]_n.
- */
- void getExplanationFor(Node n,
- Node vn,
- std::vector<Node>& exp,
- SygusInvarianceTest& et,
- Node vnr,
- unsigned& sz);
- void getExplanationFor(Node n,
- Node vn,
- std::vector<Node>& exp,
- SygusInvarianceTest& et);
-
- private:
- /** sygus term database associated with this utility */
- TermDbSygus* d_tdb;
- /** Helper function for getExplanationFor
- * var_count is the number of free variables we have introduced,
- * per type, for the purposes of generalizing subterms of n.
- * vnr_exp stores the explanation, if one exists, for
- * n != vnr. It is only non-null if vnr is non-null.
- */
- void getExplanationFor(TermRecBuild& trb,
- Node n,
- Node vn,
- std::vector<Node>& exp,
- std::map<TypeNode, int>& var_count,
- SygusInvarianceTest& et,
- Node vnr,
- Node& vnr_exp,
- int& sz);
-};
-
-} /* CVC4::theory::quantifiers namespace */
-} /* CVC4::theory namespace */
-} /* CVC4 namespace */
-
-#endif /* __CVC4__THEORY__QUANTIFIERS__SYGUS_EXPLAIN_H */
+++ /dev/null
-/********************* */
-/*! \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 <stack>
-
-#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<TNode, bool, TNodeHashFunction> visited;
- std::unordered_map<TNode, bool, TNodeHashFunction>::iterator it;
- std::stack<TNode> visit;
- TNode cur;
- visit.push(n);
- do {
- cur = visit.top();
- visit.pop();
- it = visited.find(cur);
- if (it == visited.end()) {
- visited[cur] = true;
- // is this a constant?
- if( cur.isConst() ){
- TypeNode tn = cur.getType();
- Node c = cur;
- if( tn.isReal() ){
- c = NodeManager::currentNM()->mkConst( c.getConst<Rational>().abs() );
- }
- if( std::find( consts[tn].begin(), consts[tn].end(), c )==consts[tn].end() ){
- Trace("cegqi-debug") << "...consider const : " << c << std::endl;
- consts[tn].push_back( c );
- }
- }
- // recurse
- for (unsigned i = 0; i < cur.getNumChildren(); i++) {
- visit.push(cur[i]);
- }
- }
- } while (!visit.empty());
-}
-
-
-
-Node CegGrammarConstructor::process( Node q, std::map< Node, Node >& templates, std::map< Node, Node >& templates_arg ) {
- // convert to deep embedding and finalize single invocation here
- // now, construct the grammar
- Trace("cegqi") << "CegConjecture : convert to deep embedding..." << std::endl;
- std::map< TypeNode, std::vector< Node > > extra_cons;
- if( options::sygusAddConstGrammar() ){
- Trace("cegqi") << "CegConjecture : collect constants..." << std::endl;
- collectTerms( q[1], extra_cons );
- }
-
- std::vector< Node > qchildren;
- std::map< Node, Node > synth_fun_vars;
- std::vector< Node > ebvl;
- Node qbody_subs = q[1];
- for( unsigned i=0; i<q[0].getNumChildren(); i++ ){
- Node sf = q[0][i];
- // v encodes the syntactic restrictions (via an inductive datatype) on sf
- // from the input
- Node v = sf.getAttribute(SygusSynthGrammarAttribute());
- Assert(!v.isNull());
- Node sfvl = sf.getAttribute(SygusSynthFunVarListAttribute());
- // sfvl may be null for constant synthesis functions
- Trace("cegqi-debug") << "...sygus var list associated with " << sf << " is " << sfvl << std::endl;
-
- TypeNode tn;
- std::stringstream ss;
- ss << sf;
- if( v.getType().isDatatype() && ((DatatypeType)v.getType().toType()).getDatatype().isSygus() ){
- tn = v.getType();
- }else{
- // check which arguments are irrelevant
- std::unordered_set<unsigned> arg_irrelevant;
- d_parent->getProcess()->getIrrelevantArgs(sf, arg_irrelevant);
- std::unordered_set<Node, NodeHashFunction> term_irrelevant;
- // convert to term
- for (std::unordered_set<unsigned>::iterator ita = arg_irrelevant.begin();
- ita != arg_irrelevant.end();
- ++ita)
- {
- unsigned arg = *ita;
- Assert(arg < sfvl.getNumChildren());
- term_irrelevant.insert(sfvl[arg]);
- }
-
- // make the default grammar
- tn = mkSygusDefaultType(
- v.getType(), sfvl, ss.str(), extra_cons, term_irrelevant);
- }
- // normalize type
- SygusGrammarNorm sygus_norm(d_qe);
- tn = sygus_norm.normalizeSygusType(tn, sfvl);
- // check if there is a template
- std::map< Node, Node >::iterator itt = templates.find( sf );
- if( itt!=templates.end() ){
- Node templ = itt->second;
- TNode templ_arg = templates_arg[sf];
- Assert( !templ_arg.isNull() );
- Trace("cegqi-debug") << "Template for " << sf << " is : " << templ << " with arg " << templ_arg << std::endl;
- // if there is a template for this argument, make a sygus type on top of it
- if( options::sygusTemplEmbedGrammar() ){
- Trace("cegqi-debug") << " embed this template as a grammar..." << std::endl;
- tn = mkSygusTemplateType( templ, templ_arg, tn, sfvl, ss.str() );
- }else{
- // otherwise, apply it as a preprocessing pass
- Trace("cegqi-debug") << " apply this template as a substituion during preprocess..." << std::endl;
- std::vector< Node > schildren;
- std::vector< Node > largs;
- for( unsigned j=0; j<sfvl.getNumChildren(); j++ ){
- schildren.push_back( sfvl[j] );
- largs.push_back( NodeManager::currentNM()->mkBoundVar( sfvl[j].getType() ) );
- }
- std::vector< Node > subsfn_children;
- subsfn_children.push_back( sf );
- subsfn_children.insert( subsfn_children.end(), schildren.begin(), schildren.end() );
- Node subsfn = NodeManager::currentNM()->mkNode( kind::APPLY_UF, subsfn_children );
- TNode subsf = subsfn;
- Trace("cegqi-debug") << " substitute arg : " << templ_arg << " -> " << subsf << std::endl;
- templ = templ.substitute( templ_arg, subsf );
- // substitute lambda arguments
- templ = templ.substitute( schildren.begin(), schildren.end(), largs.begin(), largs.end() );
- Node subsn = NodeManager::currentNM()->mkNode( kind::LAMBDA, NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, largs ), templ );
- TNode var = sf;
- TNode subs = subsn;
- Trace("cegqi-debug") << " substitute : " << var << " -> " << subs << std::endl;
- qbody_subs = qbody_subs.substitute( var, subs );
- Trace("cegqi-debug") << " body is now : " << qbody_subs << std::endl;
- }
- }
- d_qe->getTermDatabaseSygus()->registerSygusType( tn );
- // check grammar restrictions
- if( !d_qe->getTermDatabaseSygus()->sygusToBuiltinType( tn ).isBoolean() ){
- if( !d_qe->getTermDatabaseSygus()->hasKind( tn, ITE ) ){
- d_has_ite = false;
- }
- }
- Assert( tn.isDatatype() );
- const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
- Assert( dt.isSygus() );
- if( !dt.getSygusAllowAll() ){
- d_is_syntax_restricted = true;
- }
-
- // ev is the first-order variable corresponding to this synth fun
- std::stringstream ssf;
- ssf << "f" << sf;
- Node ev = NodeManager::currentNM()->mkBoundVar( ssf.str(), tn );
- ebvl.push_back( ev );
- synth_fun_vars[sf] = ev;
- Trace("cegqi") << "...embedding synth fun : " << sf << " -> " << ev << std::endl;
- }
- qchildren.push_back( NodeManager::currentNM()->mkNode( kind::BOUND_VAR_LIST, ebvl ) );
- if( qbody_subs!=q[1] ){
- Trace("cegqi") << "...rewriting : " << qbody_subs << std::endl;
- qbody_subs = Rewriter::rewrite( qbody_subs );
- Trace("cegqi") << "...got : " << qbody_subs << std::endl;
- }
- qchildren.push_back( convertToEmbedding( qbody_subs, synth_fun_vars ) );
- if( q.getNumChildren()==3 ){
- qchildren.push_back( q[2] );
- }
- return NodeManager::currentNM()->mkNode( kind::FORALL, qchildren );
-}
-
-Node CegGrammarConstructor::convertToEmbedding( Node n, std::map< Node, Node >& synth_fun_vars ){
- std::unordered_map<TNode, Node, TNodeHashFunction> visited;
- std::unordered_map<TNode, Node, TNodeHashFunction>::iterator it;
- std::stack<TNode> visit;
- TNode cur;
- visit.push(n);
- do {
- cur = visit.top();
- visit.pop();
- it = visited.find(cur);
- if (it == visited.end()) {
- visited[cur] = Node::null();
- visit.push(cur);
- for (unsigned i = 0; i < cur.getNumChildren(); i++) {
- visit.push(cur[i]);
- }
- } else if (it->second.isNull()) {
- Node ret = cur;
- Kind ret_k = cur.getKind();
- Node op;
- bool childChanged = false;
- std::vector<Node> children;
- // get the potential operator
- if( cur.getNumChildren()>0 ){
- if( cur.getKind()==kind::APPLY_UF ){
- op = cur.getOperator();
- }
- }else{
- op = cur;
- }
- // is the operator a synth function?
- if( !op.isNull() ){
- std::map< Node, Node >::iterator its = synth_fun_vars.find( op );
- if( its!=synth_fun_vars.end() ){
- Assert( its->second.getType().isDatatype() );
- // will make into an application of an evaluation function
- const Datatype& dt = ((DatatypeType)its->second.getType().toType()).getDatatype();
- Assert( dt.isSygus() );
- children.push_back( Node::fromExpr( dt.getSygusEvaluationFunc() ) );
- children.push_back( its->second );
- childChanged = true;
- ret_k = kind::APPLY_UF;
- }
- }
- if( !childChanged ){
- // otherwise, we apply the previous operator
- if( cur.getMetaKind() == kind::metakind::PARAMETERIZED ){
- children.push_back( cur.getOperator() );
- }
- }
- for (unsigned i = 0; i < cur.getNumChildren(); i++) {
- it = visited.find(cur[i]);
- Assert(it != visited.end());
- Assert(!it->second.isNull());
- childChanged = childChanged || cur[i] != it->second;
- children.push_back(it->second);
- }
- if (childChanged) {
- ret = NodeManager::currentNM()->mkNode(ret_k, children);
- }
- visited[cur] = ret;
- }
- } while (!visit.empty());
- Assert(visited.find(n) != visited.end());
- Assert(!visited.find(n)->second.isNull());
- return visited[n];
-}
-
-
-TypeNode CegGrammarConstructor::mkUnresolvedType(const std::string& name, std::set<Type>& unres) {
- TypeNode unresolved = NodeManager::currentNM()->mkSort(name, ExprManager::SORT_FLAG_PLACEHOLDER);
- unres.insert( unresolved.toType() );
- return unresolved;
-}
-
-void CegGrammarConstructor::mkSygusConstantsForType( TypeNode type, std::vector<CVC4::Node>& ops ) {
- if (type.isReal())
- {
- ops.push_back(NodeManager::currentNM()->mkConst(Rational(0)));
- ops.push_back(NodeManager::currentNM()->mkConst(Rational(1)));
- }else if( type.isBitVector() ){
- unsigned sz = ((BitVectorType)type.toType()).getSize();
- BitVector bval0(sz, (unsigned int)0);
- ops.push_back( NodeManager::currentNM()->mkConst(bval0) );
- BitVector bval1(sz, (unsigned int)1);
- ops.push_back( NodeManager::currentNM()->mkConst(bval1) );
- }else if( type.isBoolean() ){
- ops.push_back(NodeManager::currentNM()->mkConst(true));
- ops.push_back(NodeManager::currentNM()->mkConst(false));
- }
- //TODO : others?
-}
-
-void CegGrammarConstructor::collectSygusGrammarTypesFor( TypeNode range, std::vector< TypeNode >& types, std::map< TypeNode, std::vector< DatatypeConstructorArg > >& sels ){
- if( !range.isBoolean() ){
- if( std::find( types.begin(), types.end(), range )==types.end() ){
- Trace("sygus-grammar-def") << "...will make grammar for " << range << std::endl;
- types.push_back( range );
- if( range.isDatatype() ){
- const Datatype& dt = ((DatatypeType)range.toType()).getDatatype();
- for( unsigned i=0; i<dt.getNumConstructors(); i++ ){
- for( unsigned j=0; j<dt[i].getNumArgs(); j++ ){
- TypeNode crange = TypeNode::fromType( ((SelectorType)dt[i][j].getType()).getRangeType() );
- sels[crange].push_back( dt[i][j] );
- collectSygusGrammarTypesFor( crange, types, sels );
- }
- }
- }
- }
- }
-}
-
-void CegGrammarConstructor::mkSygusDefaultGrammar(
- TypeNode range,
- Node bvl,
- const std::string& fun,
- std::map<TypeNode, std::vector<Node> >& extra_cons,
- std::unordered_set<Node, NodeHashFunction>& term_irrelevant,
- std::vector<CVC4::Datatype>& datatypes,
- std::set<Type>& unres)
-{
- Trace("sygus-grammar-def") << "Construct default grammar for " << fun << " "
- << range << std::endl;
- // collect the variables
- std::vector<Node> sygus_vars;
- if( !bvl.isNull() ){
- for( unsigned i=0; i<bvl.getNumChildren(); i++ ){
- if (term_irrelevant.find(bvl[i]) == term_irrelevant.end())
- {
- sygus_vars.push_back(bvl[i]);
- }
- else
- {
- Trace("sygus-grammar-def") << "...synth var " << bvl[i]
- << " has been marked irrelevant."
- << std::endl;
- }
- }
- }
- //if( !range.isBoolean() && !range.isInteger() && !range.isBitVector() && !range.isDatatype() ){
- // parseError("No default grammar for type.");
- //}
- std::vector< std::vector< Expr > > ops;
- int startIndex = -1;
- std::map< Type, Type > sygus_to_builtin;
-
- std::vector< TypeNode > types;
- std::map< TypeNode, std::vector< DatatypeConstructorArg > > sels;
- //types for each of the variables of parametric sort
- for( unsigned i=0; i<sygus_vars.size(); i++ ){
- collectSygusGrammarTypesFor( sygus_vars[i].getType(), types, sels );
- }
- //types connected to range
- collectSygusGrammarTypesFor( range, types, sels );
-
- //name of boolean sort
- std::stringstream ssb;
- ssb << fun << "_Bool";
- std::string dbname = ssb.str();
- Type unres_bt = mkUnresolvedType(ssb.str(), unres).toType();
-
- std::vector< Type > unres_types;
- std::map< TypeNode, Type > type_to_unres;
- for( unsigned i=0; i<types.size(); i++ ){
- std::stringstream ss;
- ss << fun << "_" << types[i];
- std::string dname = ss.str();
- datatypes.push_back(Datatype(dname));
- ops.push_back(std::vector< Expr >());
- //make unresolved type
- Type unres_t = mkUnresolvedType(dname, unres).toType();
- unres_types.push_back(unres_t);
- type_to_unres[types[i]] = unres_t;
- sygus_to_builtin[unres_t] = types[i].toType();
- }
- for( unsigned i=0; i<types.size(); i++ ){
- Trace("sygus-grammar-def") << "Make grammar for " << types[i] << " " << unres_types[i] << std::endl;
- std::vector<std::string> cnames;
- std::vector<std::vector<CVC4::Type> > cargs;
- Type unres_t = unres_types[i];
- //add variables
- for( unsigned j=0; j<sygus_vars.size(); j++ ){
- if( sygus_vars[j].getType()==types[i] ){
- std::stringstream ss;
- ss << sygus_vars[j];
- Trace("sygus-grammar-def") << "...add for variable " << ss.str() << std::endl;
- ops[i].push_back( sygus_vars[j].toExpr() );
- cnames.push_back( ss.str() );
- cargs.push_back( std::vector< CVC4::Type >() );
- }
- }
- //add constants
- std::vector< Node > consts;
- mkSygusConstantsForType( types[i], consts );
- std::map< TypeNode, std::vector< Node > >::iterator itec = extra_cons.find( types[i] );
- if( itec!=extra_cons.end() ){
- //consts.insert( consts.end(), itec->second.begin(), itec->second.end() );
- for( unsigned j=0; j<itec->second.size(); j++ ){
- if( std::find( consts.begin(), consts.end(), itec->second[j] )==consts.end() ){
- consts.push_back( itec->second[j] );
- }
- }
- }
- for( unsigned j=0; j<consts.size(); j++ ){
- std::stringstream ss;
- ss << consts[j];
- Trace("sygus-grammar-def") << "...add for constant " << ss.str() << std::endl;
- ops[i].push_back( consts[j].toExpr() );
- cnames.push_back( ss.str() );
- cargs.push_back( std::vector< CVC4::Type >() );
- }
- //ITE
- CVC4::Kind k = kind::ITE;
- Trace("sygus-grammar-def") << "...add for " << k << std::endl;
- ops[i].push_back(NodeManager::currentNM()->operatorOf(k).toExpr());
- cnames.push_back( kind::kindToString(k) );
- cargs.push_back( std::vector< CVC4::Type >() );
- cargs.back().push_back(unres_bt);
- cargs.back().push_back(unres_t);
- cargs.back().push_back(unres_t);
-
- if (types[i].isReal())
- {
- for (unsigned j = 0; j < 2; j++)
- {
- Kind k = j == 0 ? PLUS : MINUS;
- Trace("sygus-grammar-def") << "...add for " << k << std::endl;
- ops[i].push_back(NodeManager::currentNM()->operatorOf(k).toExpr());
- cnames.push_back(kind::kindToString(k));
- cargs.push_back(std::vector<CVC4::Type>());
- cargs.back().push_back(unres_t);
- cargs.back().push_back(unres_t);
- }
- if (!types[i].isInteger())
- {
- Trace("sygus-grammar-def") << "...Dedicate to Real\n";
- /* Creating type for positive integers */
- std::stringstream ss;
- ss << fun << "_PosInt";
- std::string pos_int_name = ss.str();
- // make unresolved type
- Type unres_pos_int_t = mkUnresolvedType(pos_int_name, unres).toType();
- // make data type
- datatypes.push_back(Datatype(pos_int_name));
- /* add placeholders */
- std::vector<Expr> ops_pos_int;
- std::vector<std::string> cnames_pos_int;
- std::vector<std::vector<Type>> cargs_pos_int;
- /* Add operator 1 */
- Trace("sygus-grammar-def") << "\t...add for 1 to Pos_Int\n";
- ops_pos_int.push_back(
- NodeManager::currentNM()->mkConst(Rational(1)).toExpr());
- ss << "_1";
- cnames_pos_int.push_back(ss.str());
- cargs_pos_int.push_back(std::vector<Type>());
- /* Add operator PLUS */
- Kind k = PLUS;
- Trace("sygus-grammar-def") << "\t...add for PLUS to Pos_Int\n";
- ops_pos_int.push_back(NodeManager::currentNM()->operatorOf(k).toExpr());
- cnames_pos_int.push_back(kindToString(k));
- cargs_pos_int.push_back(std::vector<Type>());
- cargs_pos_int.back().push_back(unres_pos_int_t);
- cargs_pos_int.back().push_back(unres_pos_int_t);
- datatypes.back().setSygus(types[i].toType(), bvl.toExpr(), true, true);
- for (unsigned j = 0; j < ops_pos_int.size(); j++)
- {
- datatypes.back().addSygusConstructor(
- ops_pos_int[j], cnames_pos_int[j], cargs_pos_int[j]);
- }
- Trace("sygus-grammar-def")
- << "...built datatype " << datatypes.back() << " ";
- /* Adding division at root */
- k = DIVISION;
- Trace("sygus-grammar-def") << "\t...add for " << k << std::endl;
- ops[i].push_back(NodeManager::currentNM()->operatorOf(k).toExpr());
- cnames.push_back(kindToString(k));
- cargs.push_back(std::vector<Type>());
- cargs.back().push_back(unres_t);
- cargs.back().push_back(unres_pos_int_t);
- }
- }else if( types[i].isDatatype() ){
- Trace("sygus-grammar-def") << "...add for constructors" << std::endl;
- const Datatype& dt = ((DatatypeType)types[i].toType()).getDatatype();
- for( unsigned k=0; k<dt.getNumConstructors(); k++ ){
- Trace("sygus-grammar-def") << "...for " << dt[k].getName() << std::endl;
- ops[i].push_back( dt[k].getConstructor() );
- cnames.push_back( dt[k].getName() );
- cargs.push_back( std::vector< CVC4::Type >() );
- for( unsigned j=0; j<dt[k].getNumArgs(); j++ ){
- TypeNode crange = TypeNode::fromType( ((SelectorType)dt[k][j].getType()).getRangeType() );
- //Assert( type_to_unres.find(crange)!=type_to_unres.end() );
- cargs.back().push_back( type_to_unres[crange] );
- }
- }
- }else{
- std::stringstream sserr;
- sserr << "No implementation for default Sygus grammar of type " << types[i] << std::endl;
- //AlwaysAssert( false, sserr.str() );
- // FIXME
- AlwaysAssert( false );
- }
- //add for all selectors to this type
- if( !sels[types[i]].empty() ){
- Trace("sygus-grammar-def") << "...add for selectors" << std::endl;
- for( unsigned j=0; j<sels[types[i]].size(); j++ ){
- Trace("sygus-grammar-def") << "...for " << sels[types[i]][j].getName() << std::endl;
- TypeNode arg_type = TypeNode::fromType( ((SelectorType)sels[types[i]][j].getType()).getDomain() );
- ops[i].push_back( sels[types[i]][j].getSelector() );
- cnames.push_back( sels[types[i]][j].getName() );
- cargs.push_back( std::vector< CVC4::Type >() );
- //Assert( type_to_unres.find(arg_type)!=type_to_unres.end() );
- cargs.back().push_back( type_to_unres[arg_type] );
- }
- }
- Trace("sygus-grammar-def") << "...make datatype " << datatypes[i] << std::endl;
- datatypes[i].setSygus( types[i].toType(), bvl.toExpr(), true, true );
- for( unsigned j=0; j<ops[i].size(); j++ ){
- datatypes[i].addSygusConstructor( ops[i][j], cnames[j], cargs[j] );
- }
- Trace("sygus-grammar-def")
- << "...built datatype " << datatypes[i] << " ";
- //sorts.push_back( types[i] );
- //set start index if applicable
- if( types[i]==range ){
- startIndex = i;
- }
- }
-
- //make Boolean type
- TypeNode btype = NodeManager::currentNM()->booleanType();
- datatypes.push_back(Datatype(dbname));
- ops.push_back(std::vector<Expr>());
- std::vector<std::string> cnames;
- std::vector<std::vector< Type > > cargs;
- Trace("sygus-grammar-def") << "Make grammar for " << btype << " " << datatypes.back() << std::endl;
- //add variables
- for( unsigned i=0; i<sygus_vars.size(); i++ ){
- if( sygus_vars[i].getType().isBoolean() ){
- std::stringstream ss;
- ss << sygus_vars[i];
- Trace("sygus-grammar-def") << "...add for variable " << ss.str() << std::endl;
- ops.back().push_back( sygus_vars[i].toExpr() );
- cnames.push_back( ss.str() );
- cargs.push_back( std::vector< CVC4::Type >() );
- }
- }
- //add constants if no variables and no connected types
- if( ops.back().empty() && types.empty() ){
- std::vector< Node > consts;
- mkSygusConstantsForType( btype, consts );
- for( unsigned j=0; j<consts.size(); j++ ){
- std::stringstream ss;
- ss << consts[j];
- Trace("sygus-grammar-def") << "...add for constant " << ss.str() << std::endl;
- ops.back().push_back( consts[j].toExpr() );
- cnames.push_back( ss.str() );
- cargs.push_back( std::vector< CVC4::Type >() );
- }
- }
- //add operators
- for( unsigned i=0; i<3; i++ ){
- CVC4::Kind k = i==0 ? kind::NOT : ( i==1 ? kind::AND : kind::OR );
- Trace("sygus-grammar-def") << "...add for " << k << std::endl;
- ops.back().push_back(NodeManager::currentNM()->operatorOf(k).toExpr());
- cnames.push_back(kind::kindToString(k));
- cargs.push_back( std::vector< CVC4::Type >() );
- if( k==kind::NOT ){
- cargs.back().push_back(unres_bt);
- }else if( k==kind::AND || k==kind::OR ){
- cargs.back().push_back(unres_bt);
- cargs.back().push_back(unres_bt);
- }
- }
- //add predicates for types
- for( unsigned i=0; i<types.size(); i++ ){
- Trace("sygus-grammar-def") << "...add predicates for " << types[i] << std::endl;
- //add equality per type
- CVC4::Kind k = kind::EQUAL;
- Trace("sygus-grammar-def") << "...add for " << k << std::endl;
- ops.back().push_back(NodeManager::currentNM()->operatorOf(k).toExpr());
- std::stringstream ss;
- ss << kind::kindToString(k) << "_" << types[i];
- cnames.push_back(ss.str());
- cargs.push_back( std::vector< CVC4::Type >() );
- cargs.back().push_back(unres_types[i]);
- cargs.back().push_back(unres_types[i]);
- //type specific predicates
- if (types[i].isReal())
- {
- CVC4::Kind k = kind::LEQ;
- Trace("sygus-grammar-def") << "...add for " << k << std::endl;
- ops.back().push_back(NodeManager::currentNM()->operatorOf(k).toExpr());
- cnames.push_back(kind::kindToString(k));
- cargs.push_back( std::vector< CVC4::Type >() );
- cargs.back().push_back(unres_types[i]);
- cargs.back().push_back(unres_types[i]);
- }else if( types[i].isDatatype() ){
- //add for testers
- Trace("sygus-grammar-def") << "...add for testers" << std::endl;
- const Datatype& dt = ((DatatypeType)types[i].toType()).getDatatype();
- for( unsigned k=0; k<dt.getNumConstructors(); k++ ){
- Trace("sygus-grammar-def") << "...for " << dt[k].getTesterName() << std::endl;
- ops.back().push_back(dt[k].getTester());
- cnames.push_back(dt[k].getTesterName());
- cargs.push_back( std::vector< CVC4::Type >() );
- cargs.back().push_back(unres_types[i]);
- }
- }
- }
- if( range==btype ){
- startIndex = datatypes.size()-1;
- }
- Trace("sygus-grammar-def") << "...make datatype " << datatypes.back() << std::endl;
- datatypes.back().setSygus( btype.toType(), bvl.toExpr(), true, true );
- for( unsigned j=0; j<ops.back().size(); j++ ){
- datatypes.back().addSygusConstructor( ops.back()[j], cnames[j], cargs[j] );
- }
- //sorts.push_back( btype );
- Trace("sygus-grammar-def") << "...finished make default grammar for " << fun << " " << range << std::endl;
-
- if( startIndex>0 ){
- CVC4::Datatype tmp_dt = datatypes[0];
- datatypes[0] = datatypes[startIndex];
- datatypes[startIndex] = tmp_dt;
- }
-}
-
-TypeNode CegGrammarConstructor::mkSygusDefaultType(
- TypeNode range,
- Node bvl,
- const std::string& fun,
- std::map<TypeNode, std::vector<Node> >& extra_cons,
- std::unordered_set<Node, NodeHashFunction>& term_irrelevant)
-{
- Trace("sygus-grammar-def") << "*** Make sygus default type " << range << ", make datatypes..." << std::endl;
- for( std::map< TypeNode, std::vector< Node > >::iterator it = extra_cons.begin(); it != extra_cons.end(); ++it ){
- Trace("sygus-grammar-def") << " ...using " << it->second.size() << " extra constants for " << it->first << std::endl;
- }
- std::set<Type> unres;
- std::vector< CVC4::Datatype > datatypes;
- mkSygusDefaultGrammar(
- range, bvl, fun, extra_cons, term_irrelevant, datatypes, unres);
- Trace("sygus-grammar-def") << "...made " << datatypes.size() << " datatypes, now make mutual datatype types..." << std::endl;
- Assert( !datatypes.empty() );
- std::vector<DatatypeType> types = NodeManager::currentNM()->toExprManager()->mkMutualDatatypeTypes(datatypes, unres);
- Assert( types.size()==datatypes.size() );
- return TypeNode::fromType( types[0] );
-}
-
-TypeNode CegGrammarConstructor::mkSygusTemplateTypeRec( Node templ, Node templ_arg, TypeNode templ_arg_sygus_type, Node bvl,
- const std::string& fun, unsigned& tcount ) {
- if( templ==templ_arg ){
- //Assert( templ_arg.getType()==sygusToBuiltinType( templ_arg_sygus_type ) );
- return templ_arg_sygus_type;
- }else{
- tcount++;
- std::set<Type> unres;
- std::vector< CVC4::Datatype > datatypes;
- std::stringstream ssd;
- ssd << fun << "_templ_" << tcount;
- std::string dbname = ssd.str();
- datatypes.push_back(Datatype(dbname));
- Node op;
- std::vector< Type > argTypes;
- if( templ.getNumChildren()==0 ){
- // TODO : can short circuit to this case when !TermUtil::containsTerm( templ, templ_arg )
- op = templ;
- }else{
- Assert( templ.hasOperator() );
- op = templ.getOperator();
- // make constructor taking arguments types from children
- for( unsigned i=0; i<templ.getNumChildren(); i++ ){
- //recursion depth bound by the depth of SyGuS template expressions (low)
- TypeNode tnc = mkSygusTemplateTypeRec( templ[i], templ_arg, templ_arg_sygus_type, bvl, fun, tcount );
- argTypes.push_back( tnc.toType() );
- }
- }
- std::stringstream ssdc;
- ssdc << fun << "_templ_cons_" << tcount;
- std::string cname = ssdc.str();
- // we have a single sygus constructor that encodes the template
- datatypes.back().addSygusConstructor( op.toExpr(), cname, argTypes );
- datatypes.back().setSygus( templ.getType().toType(), bvl.toExpr(), true, true );
- std::vector<DatatypeType> types = NodeManager::currentNM()->toExprManager()->mkMutualDatatypeTypes(datatypes, unres);
- Assert( types.size()==1 );
- return TypeNode::fromType( types[0] );
- }
-}
-
-TypeNode CegGrammarConstructor::mkSygusTemplateType( Node templ, Node templ_arg, TypeNode templ_arg_sygus_type, Node bvl,
- const std::string& fun ) {
- unsigned tcount = 0;
- return mkSygusTemplateTypeRec( templ, templ_arg, templ_arg_sygus_type, bvl, fun, tcount );
-}
-
-}/* namespace CVC4::theory::quantifiers */
-}/* namespace CVC4::theory */
-}/* namespace CVC4 */
+++ /dev/null
-/********************* */
-/*! \file sygus_grammar_cons.h
- ** \verbatim
- ** Top contributors (to current version):
- ** Andrew Reynolds
- ** This file is part of the CVC4 project.
- ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
- ** in the top-level source directory) and their institutional affiliations.
- ** All rights reserved. See the file COPYING in the top-level source
- ** directory for licensing information.\endverbatim
- **
- ** \brief class for constructing inductive datatypes that correspond to
- ** grammars that encode syntactic restrictions for SyGuS.
- **/
-
-#include "cvc4_private.h"
-
-#ifndef __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_CONS_H
-#define __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_CONS_H
-
-#include "theory/quantifiers_engine.h"
-
-namespace CVC4 {
-namespace theory {
-namespace quantifiers {
-
-class CegConjecture;
-
-/** utility for constructing datatypes that correspond to syntactic restrictions,
-* and applying the deep embedding from Section 4 of Reynolds et al CAV 2015.
-*/
-class CegGrammarConstructor
-{
-public:
- CegGrammarConstructor(QuantifiersEngine* qe, CegConjecture* p);
- ~CegGrammarConstructor() {}
- /** process
- * This converts node q based on its deep embedding
- * (Section 4 of Reynolds et al CAV 2015).
- * The syntactic restrictions are associated with
- * the functions-to-synthesize using the attribute
- * SygusSynthGrammarAttribute.
- * The arguments templates and template_args
- * indicate templates for the function to synthesize,
- * in particular the solution for the i^th function
- * to synthesis must be of the form
- * templates[i]{ templates_arg[i] -> t }
- * for some t if !templates[i].isNull().
- */
- Node process(Node q,
- std::map<Node, Node>& templates,
- std::map<Node, Node>& templates_arg);
- /** is the syntax restricted? */
- bool isSyntaxRestricted() { return d_is_syntax_restricted; }
- /** does the syntax allow ITE expressions? */
- bool hasSyntaxITE() { return d_has_ite; }
- /** make the default sygus datatype type corresponding to builtin type range
- * bvl is the set of free variables to include in the grammar
- * fun is for naming
- * extra_cons is a set of extra constant symbols to include in the grammar
- * term_irrelevant is a set of terms that should not be included in the
- * grammar.
- */
- static TypeNode mkSygusDefaultType(
- TypeNode range,
- Node bvl,
- const std::string& fun,
- std::map<TypeNode, std::vector<Node> >& extra_cons,
- std::unordered_set<Node, NodeHashFunction>& term_irrelevant);
- /** make the default sygus datatype type corresponding to builtin type range */
- static TypeNode mkSygusDefaultType(TypeNode range,
- Node bvl,
- const std::string& fun)
- {
- std::map<TypeNode, std::vector<Node> > extra_cons;
- std::unordered_set<Node, NodeHashFunction> term_irrelevant;
- return mkSygusDefaultType(range, bvl, fun, extra_cons, term_irrelevant);
- }
- /** make the sygus datatype type that encodes the solution space (lambda
- * templ_arg. templ[templ_arg]) where templ_arg
- * has syntactic restrictions encoded by sygus type templ_arg_sygus_type
- * bvl is the set of free variables to include in the grammar
- * fun is for naming
- */
- static TypeNode mkSygusTemplateType( Node templ, Node templ_arg, TypeNode templ_arg_sygus_type, Node bvl, const std::string& fun );
-private:
- /** reference to quantifier engine */
- QuantifiersEngine * d_qe;
- /** parent conjecture
- * This contains global information about the synthesis conjecture.
- */
- CegConjecture* d_parent;
- /** is the syntax restricted? */
- bool d_is_syntax_restricted;
- /** does the syntax allow ITE expressions? */
- bool d_has_ite;
- /** collect terms */
- void collectTerms( Node n, std::map< TypeNode, std::vector< Node > >& consts );
- /** convert node n based on deep embedding (Section 4 of Reynolds et al CAV 2015) */
- Node convertToEmbedding( Node n, std::map< Node, Node >& synth_fun_vars );
- //---------------- grammar construction
- // helper for mkSygusDefaultGrammar (makes unresolved type for mutually recursive datatype construction)
- static TypeNode mkUnresolvedType(const std::string& name, std::set<Type>& unres);
- // make the builtin constants for type type that should be included in a sygus grammar
- static void mkSygusConstantsForType( TypeNode type, std::vector<CVC4::Node>& ops );
- // collect the list of types that depend on type range
- static void collectSygusGrammarTypesFor( TypeNode range, std::vector< TypeNode >& types, std::map< TypeNode, std::vector< DatatypeConstructorArg > >& sels );
- /** helper function for function mkSygusDefaultType
- * Collects a set of mutually recursive datatypes "datatypes" corresponding to
- * encoding type "range" to SyGuS.
- * unres is used for the resulting call to mkMutualDatatypeTypes
- */
- static void mkSygusDefaultGrammar(
- TypeNode range,
- Node bvl,
- const std::string& fun,
- std::map<TypeNode, std::vector<Node> >& extra_cons,
- std::unordered_set<Node, NodeHashFunction>& term_irrelevant,
- std::vector<CVC4::Datatype>& datatypes,
- std::set<Type>& unres);
- // helper function for mkSygusTemplateType
- static TypeNode mkSygusTemplateTypeRec( Node templ, Node templ_arg, TypeNode templ_arg_sygus_type, Node bvl,
- const std::string& fun, unsigned& tcount );
- //---------------- end grammar construction
-};
-
-} /* namespace CVC4::theory::quantifiers */
-} /* namespace CVC4::theory */
-} /* namespace CVC4 */
-
-#endif
+++ /dev/null
-/********************* */
-/*! \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 <numeric> // for std::iota
-
-using namespace CVC4::kind;
-
-namespace CVC4 {
-namespace theory {
-namespace quantifiers {
-
-bool OpPosTrie::getOrMakeType(TypeNode tn,
- TypeNode& unres_tn,
- const std::vector<unsigned>& op_pos,
- unsigned ind)
-{
- if (ind == op_pos.size())
- {
- /* Found type */
- if (!d_unres_tn.isNull())
- {
- Trace("sygus-grammar-normalize-trie")
- << "\tFound type " << d_unres_tn << "\n";
- unres_tn = d_unres_tn;
- return true;
- }
- /* Creating unresolved type */
- std::stringstream ss;
- ss << tn << "_";
- for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
- {
- ss << "_" << std::to_string(op_pos[i]);
- }
- d_unres_tn = NodeManager::currentNM()->mkSort(
- ss.str(), ExprManager::SORT_FLAG_PLACEHOLDER);
- Trace("sygus-grammar-normalize-trie")
- << "\tCreating type " << d_unres_tn << "\n";
- unres_tn = d_unres_tn;
- return false;
- }
- /* Go to next node */
- return d_children[op_pos[ind]].getOrMakeType(tn, unres_tn, op_pos, ind + 1);
-}
-
-void SygusGrammarNorm::TypeObject::addConsInfo(SygusGrammarNorm* sygus_norm,
- const DatatypeConstructor& cons)
-{
- Trace("sygus-grammar-normalize") << "...for " << cons.getName() << "\n";
- /* Recover the sygus operator to not lose reference to the original
- * operator (NOT, ITE, etc) */
- Node exp_sop_n = Node::fromExpr(
- smt::currentSmtEngine()->expandDefinitions(cons.getSygusOp()));
- d_ops.push_back(Rewriter::rewrite(exp_sop_n));
- Trace("sygus-grammar-normalize-defs")
- << "\tOriginal op: " << cons.getSygusOp()
- << "\n\tExpanded one: " << exp_sop_n
- << "\n\tRewritten one: " << d_ops.back() << "\n\n";
- d_cons_names.push_back(cons.getName());
- d_pc.push_back(cons.getSygusPrintCallback());
- d_weight.push_back(cons.getWeight());
- d_cons_args_t.push_back(std::vector<Type>());
- for (const DatatypeConstructorArg& arg : cons)
- {
- /* Collect unresolved type nodes corresponding to the typenode of the
- * arguments */
- d_cons_args_t.back().push_back(
- sygus_norm
- ->normalizeSygusRec(TypeNode::fromType(
- static_cast<SelectorType>(arg.getType()).getRangeType()))
- .toType());
- }
-}
-
-void SygusGrammarNorm::TypeObject::buildDatatype(SygusGrammarNorm* sygus_norm,
- const Datatype& dt)
-{
- /* Use the sygus type to not lose reference to the original types (Bool,
- * Int, etc) */
- d_dt.setSygus(dt.getSygusType(),
- sygus_norm->d_sygus_vars.toExpr(),
- dt.getSygusAllowConst(),
- dt.getSygusAllowAll());
- for (unsigned i = 0, size_d_ops = d_ops.size(); i < size_d_ops; ++i)
- {
- d_dt.addSygusConstructor(d_ops[i].toExpr(),
- d_cons_names[i],
- d_cons_args_t[i],
- d_pc[i],
- d_weight[i]);
- }
- Trace("sygus-grammar-normalize") << "...built datatype " << d_dt << " ";
- /* Add to global accumulators */
- sygus_norm->d_dt_all.push_back(d_dt);
- sygus_norm->d_unres_t_all.insert(d_unres_tn.toType());
- Trace("sygus-grammar-normalize") << "---------------------------------\n";
-}
-
-void SygusGrammarNorm::TransfDrop::buildType(SygusGrammarNorm* sygus_norm,
- TypeObject& to,
- const Datatype& dt,
- std::vector<unsigned>& op_pos)
-{
- std::vector<unsigned> difference;
- std::set_difference(op_pos.begin(),
- op_pos.end(),
- d_drop_indices.begin(),
- d_drop_indices.end(),
- std::back_inserter(difference));
- op_pos = difference;
-}
-
-/* TODO #1304: have more operators and types. Moreover, have more general ways
- of finding kind of operator, e.g. if op is (\lambda xy. x + y) this
- function should realize that it is chainable for integers */
-bool SygusGrammarNorm::TransfChain::isChainable(TypeNode tn, Node op)
-{
- /* Checks whether operator occurs chainable for its type */
- if (tn.isInteger() && NodeManager::currentNM()->operatorToKind(op) == PLUS)
- {
- return true;
- }
- return false;
-}
-
-/* TODO #1304: have more operators and types. Moreover, have more general ways
- of finding kind of operator, e.g. if op is (\lambda xy. x + y) this
- function should realize that it is chainable for integers */
-bool SygusGrammarNorm::TransfChain::isId(TypeNode tn, Node op, Node n)
-{
- if (tn.isInteger() && NodeManager::currentNM()->operatorToKind(op) == PLUS
- && n == TermUtil::mkTypeValue(tn, 0))
- {
- return true;
- }
- return false;
-}
-
-void SygusGrammarNorm::TransfChain::buildType(SygusGrammarNorm* sygus_norm,
- TypeObject& to,
- const Datatype& dt,
- std::vector<unsigned>& op_pos)
-{
- NodeManager* nm = NodeManager::currentNM();
- std::vector<unsigned> claimed(d_elem_pos);
- claimed.push_back(d_chain_op_pos);
- unsigned nb_op_pos = op_pos.size();
- /* TODO do this properly */
- /* Remove from op_pos the positions claimed by the transformation */
- std::sort(op_pos.begin(), op_pos.end());
- std::sort(claimed.begin(), claimed.end());
- std::vector<unsigned> difference;
- std::set_difference(op_pos.begin(),
- op_pos.end(),
- claimed.begin(),
- claimed.end(),
- std::back_inserter(difference));
- op_pos = difference;
- if (Trace.isOn("sygus-grammar-normalize-chain"))
- {
- Trace("sygus-grammar-normalize-chain")
- << "OP at " << d_chain_op_pos << "\n"
- << d_elem_pos.size() << " d_elem_pos: ";
- for (unsigned i = 0, size = d_elem_pos.size(); i < size; ++i)
- {
- Trace("sygus-grammar-normalize-chain") << d_elem_pos[i] << " ";
- }
- Trace("sygus-grammar-normalize-chain")
- << "\n"
- << op_pos.size() << " remaining op_pos: ";
- for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
- {
- Trace("sygus-grammar-normalize-chain") << op_pos[i] << " ";
- }
- Trace("sygus-grammar-normalize-chain") << "\n";
- }
- /* Build identity operator and empty callback */
- Node iden_op =
- SygusGrammarNorm::getIdOp(TypeNode::fromType(dt.getSygusType()));
- /* If all operators are claimed, create a monomial */
- if (nb_op_pos == d_elem_pos.size() + 1)
- {
- Trace("sygus-grammar-normalize-chain")
- << "\tCreating id type for " << d_elem_pos.back() << "\n";
- /* creates type for element */
- std::vector<unsigned> tmp;
- tmp.push_back(d_elem_pos.back());
- Type t = sygus_norm->normalizeSygusRec(to.d_tn, dt, tmp).toType();
- /* consumes element */
- d_elem_pos.pop_back();
- /* adds to Root: "type" */
- to.d_ops.push_back(iden_op);
- to.d_cons_names.push_back("id");
- to.d_pc.push_back(printer::SygusEmptyPrintCallback::getEmptyPC());
- /* Identity operators should not increase the size of terms */
- to.d_weight.push_back(0);
- to.d_cons_args_t.push_back(std::vector<Type>());
- to.d_cons_args_t.back().push_back(t);
- Trace("sygus-grammar-normalize-chain")
- << "\tAdding " << t << " to " << to.d_unres_tn << "\n";
- /* adds to Root: "type + Root" */
- to.d_ops.push_back(nm->operatorOf(PLUS));
- to.d_cons_names.push_back(kindToString(PLUS));
- to.d_pc.push_back(nullptr);
- to.d_weight.push_back(-1);
- to.d_cons_args_t.push_back(std::vector<Type>());
- to.d_cons_args_t.back().push_back(t);
- to.d_cons_args_t.back().push_back(to.d_unres_tn.toType());
- Trace("sygus-grammar-normalize-chain")
- << "\tAdding PLUS to " << to.d_unres_tn << " with arg types "
- << to.d_unres_tn << " and " << t << "\n";
- }
- /* In the initial case if not all operators claimed always creates a next */
- Assert(nb_op_pos != d_elem_pos.size() + 1 || d_elem_pos.size() > 1);
- /* TODO #1304: consider case in which CHAIN op has different types than
- to.d_tn */
- /* If no more elements to chain, finish */
- if (d_elem_pos.size() == 0)
- {
- return;
- }
- /* Creates a type do be added to root representing next step in the chain */
- /* Add + to elems */
- d_elem_pos.push_back(d_chain_op_pos);
- if (Trace.isOn("sygus-grammar-normalize-chain"))
- {
- Trace("sygus-grammar-normalize-chain")
- << "\tCreating type for next entry with sygus_ops ";
- for (unsigned i = 0, size = d_elem_pos.size(); i < size; ++i)
- {
- Trace("sygus-grammar-normalize-chain")
- << dt[d_elem_pos[i]].getSygusOp() << " ";
- }
- Trace("sygus-grammar-normalize-chain") << "\n";
- }
- /* adds to Root: (\lambda x. x ) Next */
- to.d_ops.push_back(iden_op);
- to.d_cons_names.push_back("id_next");
- to.d_pc.push_back(printer::SygusEmptyPrintCallback::getEmptyPC());
- to.d_weight.push_back(0);
- to.d_cons_args_t.push_back(std::vector<Type>());
- to.d_cons_args_t.back().push_back(
- sygus_norm->normalizeSygusRec(to.d_tn, dt, d_elem_pos).toType());
-}
-
-std::map<TypeNode, Node> SygusGrammarNorm::d_tn_to_id = {};
-
-/* Traverse the constructors of dt according to the positions in op_pos. Collect
- * those that fit the kinds established by to_collect. Remove collected operator
- * positions from op_pos. Accumulate collected positions in collected
- *
- * returns true if collected anything
- */
-std::unique_ptr<SygusGrammarNorm::Transf> SygusGrammarNorm::inferTransf(
- TypeNode tn, const Datatype& dt, const std::vector<unsigned>& op_pos)
-{
- NodeManager* nm = NodeManager::currentNM();
- TypeNode sygus_tn = TypeNode::fromType(dt.getSygusType());
- Trace("sygus-gnorm") << "Infer transf for " << dt.getName() << "..."
- << std::endl;
- Trace("sygus-gnorm") << " #cons = " << op_pos.size() << " / "
- << dt.getNumConstructors() << std::endl;
- // look for redundant constructors to drop
- if (options::sygusMinGrammar() && dt.getNumConstructors() == op_pos.size())
- {
- SygusRedundantCons src;
- src.initialize(d_qe, tn);
- std::vector<unsigned> rindices;
- src.getRedundant(rindices);
- if (!rindices.empty())
- {
- Trace("sygus-gnorm") << "...drop transf, " << rindices.size() << "/"
- << op_pos.size() << " constructors." << std::endl;
- Assert(rindices.size() < op_pos.size());
- return std::unique_ptr<Transf>(new TransfDrop(rindices));
- }
- }
-
- // if normalization option is not enabled, we do not infer the transformations
- // below
- if (!options::sygusGrammarNorm())
- {
- return nullptr;
- }
-
- /* TODO #1304: step 1: look for singleton */
- /* step 2: look for chain */
- unsigned chain_op_pos = dt.getNumConstructors();
- std::vector<unsigned> elem_pos;
- for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
- {
- Assert(op_pos[i] < dt.getNumConstructors());
- Expr sop = dt[op_pos[i]].getSygusOp();
- /* Collects a chainable operator such as PLUS */
- if (sop.getKind() == BUILTIN
- && TransfChain::isChainable(sygus_tn, Node::fromExpr(sop)))
- {
- Assert(nm->operatorToKind(Node::fromExpr(sop)) == PLUS);
- /* TODO #1304: be robust for this case */
- /* For now only transforms applications whose arguments have the same type
- * as the root */
- bool same_type_plus = true;
- for (const DatatypeConstructorArg& arg : dt[op_pos[i]])
- {
- if (TypeNode::fromType(
- static_cast<SelectorType>(arg.getType()).getRangeType())
- != tn)
- {
- same_type_plus = false;
- break;
- }
- }
- if (!same_type_plus)
- {
- Trace("sygus-grammar-normalize-infer")
- << "\tFor OP " << PLUS << " did not collecting sop " << sop
- << " in position " << op_pos[i] << "\n";
- continue;
- }
- Assert(chain_op_pos == dt.getNumConstructors());
- Trace("sygus-grammar-normalize-infer")
- << "\tCollecting chainable OP " << sop << " in position " << op_pos[i]
- << "\n";
- chain_op_pos = op_pos[i];
- continue;
- }
- /* TODO #1304: check this for each operator */
- /* Collects elements that are not the identity (e.g. 0 is the id of PLUS) */
- if (!TransfChain::isId(sygus_tn, nm->operatorOf(PLUS), Node::fromExpr(sop)))
- {
- Trace("sygus-grammar-normalize-infer")
- << "\tCollecting for NON_ID_ELEMS the sop " << sop
- << " in position " << op_pos[i] << "\n";
- elem_pos.push_back(op_pos[i]);
- }
- }
- /* Typenode admits a chain transformation for normalization */
- if (chain_op_pos != dt.getNumConstructors() && !elem_pos.empty())
- {
- Trace("sygus-gnorm") << "...chain transf." << std::endl;
- Trace("sygus-grammar-normalize-infer")
- << "\tInfering chain transformation\n";
- return std::unique_ptr<Transf>(new TransfChain(chain_op_pos, elem_pos));
- }
- return nullptr;
-}
-
-TypeNode SygusGrammarNorm::normalizeSygusRec(TypeNode tn,
- const Datatype& dt,
- std::vector<unsigned>& op_pos)
-{
- /* Corresponding type node to tn with the given operator positions. To be
- * retrieved (if cached) or defined (otherwise) */
- TypeNode unres_tn;
- if (Trace.isOn("sygus-grammar-normalize-trie"))
- {
- Trace("sygus-grammar-normalize-trie")
- << "\tRecursing on " << tn << " with op_positions ";
- for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
- {
- Trace("sygus-grammar-normalize-trie") << op_pos[i] << " ";
- }
- Trace("sygus-grammar-normalize-trie") << "\n";
- }
- /* Checks if unresolved type already created (and returns) or creates it
- * (and then proceeds to definition) */
- std::sort(op_pos.begin(), op_pos.end());
- if (d_tries[tn].getOrMakeType(tn, unres_tn, op_pos))
- {
- if (Trace.isOn("sygus-grammar-normalize-trie"))
- {
- Trace("sygus-grammar-normalize-trie")
- << "\tTypenode " << tn << " has already been normalized with op_pos ";
- for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
- {
- Trace("sygus-grammar-normalize-trie") << op_pos[i] << " ";
- }
- Trace("sygus-grammar-normalize-trie") << " with tn " << unres_tn << "\n";
- }
- return unres_tn;
- }
- if (Trace.isOn("sygus-grammar-normalize-trie"))
- {
- Trace("sygus-grammar-normalize-trie")
- << "\tTypenode " << tn << " not yet normalized with op_pos ";
- for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
- {
- Trace("sygus-grammar-normalize-trie") << op_pos[i] << " ";
- }
- Trace("sygus-grammar-normalize-trie") << "\n";
- }
- /* Creates type object for normalization */
- TypeObject to(tn, unres_tn);
-
- /* Determine normalization transformation based on sygus type and given
- * operators */
- std::unique_ptr<Transf> transformation = inferTransf(tn, dt, op_pos);
- /* If a transformation was selected, apply it */
- if (transformation != nullptr)
- {
- transformation->buildType(this, to, dt, op_pos);
- }
-
- /* Remaining operators are rebuilt as they are */
- for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
- {
- Assert(op_pos[i] < dt.getNumConstructors());
- to.addConsInfo(this, dt[op_pos[i]]);
- }
- /* Build normalize datatype */
- if (Trace.isOn("sygus-grammar-normalize"))
- {
- Trace("sygus-grammar-normalize") << "\nFor positions ";
- for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
- {
- Trace("sygus-grammar-normalize") << op_pos[i] << " ";
- }
- Trace("sygus-grammar-normalize") << " and datatype " << dt << " \n";
- }
- to.buildDatatype(this, dt);
- return to.d_unres_tn;
-}
-
-TypeNode SygusGrammarNorm::normalizeSygusRec(TypeNode tn)
-{
- /* Collect all operators for normalization */
- const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
- std::vector<unsigned> op_pos(dt.getNumConstructors());
- std::iota(op_pos.begin(), op_pos.end(), 0);
- return normalizeSygusRec(tn, dt, op_pos);
-}
-
-TypeNode SygusGrammarNorm::normalizeSygusType(TypeNode tn, Node sygus_vars)
-{
- /* Normalize all types in tn */
- d_sygus_vars = sygus_vars;
- normalizeSygusRec(tn);
- /* Resolve created types */
- Assert(!d_dt_all.empty() && !d_unres_t_all.empty());
- if (Trace.isOn("sygus-grammar-normalize-build"))
- {
- Trace("sygus-grammar-normalize-build")
- << "making mutual datatyes with datatypes \n";
- for (unsigned i = 0, size = d_dt_all.size(); i < size; ++i)
- {
- Trace("sygus-grammar-normalize-build") << d_dt_all[i];
- }
- Trace("sygus-grammar-normalize-build") << " and unresolved types\n";
- for (const Type& unres_t : d_unres_t_all)
- {
- Trace("sygus-grammar-normalize-build") << unres_t << " ";
- }
- Trace("sygus-grammar-normalize-build") << "\n";
- }
- Assert(d_dt_all.size() == d_unres_t_all.size());
- std::vector<DatatypeType> types =
- NodeManager::currentNM()->toExprManager()->mkMutualDatatypeTypes(
- d_dt_all, d_unres_t_all);
- Assert(types.size() == d_dt_all.size());
- /* Clear accumulators */
- d_dt_all.clear();
- d_unres_t_all.clear();
- /* By construction the normalized type node will be the last one considered */
- return TypeNode::fromType(types.back());
-}
-
-} // namespace quantifiers
-} // namespace theory
-} // namespace CVC4
+++ /dev/null
-/********************* */
-/*! \file sygus_grammar_norm.h
- ** \verbatim
- ** Top contributors (to current version):
- ** Haniel Barbosa
- ** This file is part of the CVC4 project.
- ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
- ** in the top-level source directory) and their institutional affiliations.
- ** All rights reserved. See the file COPYING in the top-level source
- ** directory for licensing information.\endverbatim
- **
- ** \brief class for simplifying SyGuS grammars after they are encoded into
- ** datatypes.
- **/
-#include "cvc4_private.h"
-
-#ifndef __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_NORM_H
-#define __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_NORM_H
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "expr/datatype.h"
-#include "expr/node.h"
-#include "expr/node_manager_attributes.h" // for VarNameAttr
-#include "expr/type.h"
-#include "expr/type_node.h"
-#include "theory/quantifiers/term_util.h"
-#include "theory/quantifiers_engine.h"
-
-namespace CVC4 {
-namespace theory {
-namespace quantifiers {
-
-class SygusGrammarNorm;
-
-/** Operator position trie class
- *
- * This data structure stores an unresolved type corresponding to the
- * normalization of a type. This unresolved type is indexed by the positions of
- * the construtors of the datatype associated with the original type. The list
- * of positions represent the operators, associated with the respective
- * considered constructors, that were used for building the unresolved type.
- *
- * Example:
- *
- * Let A be a type defined by the grammar "A -> x | 0 | 1 | A + A". In its
- * datatype representation the operator for "x" is in position 0, for "0" in
- * position "1" and so on. Consider entries (T, [op_1, ..., op_n]) -> T' to
- * represent that a type T is normalized with operators [op_1, ..., op_n] into
- * the type T'. For entries
- *
- * (A, [x, 0, 1, +]) -> A1
- * (A, [x, 1, +]) -> A2
- * (A, [1, +]) -> A3
- * (A, [0]) -> AZ
- * (A, [x]) -> AX
- * (A, [1]) -> AO
- *
- * the OpPosTrie T we build for this type is :
- *
- * T[A] :
- * T[A].d_children[0] : AX
- * T[A].d_children[0].d_children[1] :
- * T[A].d_children[0].d_children[1].d_children[2] :
- * T[A].d_children[0].d_children[1].d_children[2].d_children[3] : A1
- * T[A].d_children[0].d_children[2] :
- * T[A].d_children[0].d_children[2].d_children[3] : A2
- * T[A].d_children[1] : AZ
- * T[A].d_children[2] : AO
- * T[A].d_children[2].d_children[4] : A3
- *
- * Nodes store the types built for the path of positions up to that point, if
- * any.
- */
-class OpPosTrie
-{
- public:
- /** type retrieval/addition
- *
- * if type indexed by the given operator positions is already in the trie then
- * unres_t becomes the indexed type and true is returned. Otherwise a new type
- * is created, indexed by the given positions, and assigned to unres_t, with
- * false being returned.
- */
- bool getOrMakeType(TypeNode tn,
- TypeNode& unres_tn,
- const std::vector<unsigned>& op_pos,
- unsigned ind = 0);
- /** clear all data from this trie */
- void clear() { d_children.clear(); }
-
- private:
- /** the data (only set for the final node of an inserted path) */
- TypeNode d_unres_tn;
- /* the children of the trie node */
- std::map<unsigned, OpPosTrie> d_children;
-}; /* class OpPosTrie */
-
-/** Utility for normalizing SyGuS grammars to avoid spurious enumerations
- *
- * Uses the datatype representation of a SyGuS grammar to identify entries that
- * can normalized in order to have less possible enumerations. An example is
- * with integer types, e.g.:
- *
- * Int -> x | y | Int + Int | 0 | 1 | ite(Bool, Int, Int)
- *
- * becomes
- *
- * Int0 -> IntZ | Int1
- * IntZ -> 0
- * Int1 -> IntX | IntX + Int1 | Int2
- * IntX -> x
- * Int2 -> IntY | IntY + Int2 | Int3
- * IntY -> y
- * Int3 -> IntO | IntO + Int3 | Int4
- * IntO -> 1
- * Int4 -> IntITE | IntITE + Int4
- * IntITE -> ite(Bool, Int0, Int0)
- *
- * TODO: #1304 normalize more complex grammars
- *
- * This class also performs more straightforward normalizations, such as
- * expanding definitions of functions declared with a "define-fun" command.
- * These lighweight transformations are always applied, independently of the
- * normalization option being enabled.
- */
-class SygusGrammarNorm
-{
- public:
- SygusGrammarNorm(QuantifiersEngine* qe)
- : d_qe(qe), d_tds(d_qe->getTermDatabaseSygus())
- {
- }
- ~SygusGrammarNorm() {}
- /** creates a normalized typenode from a given one.
- *
- * In a normalized typenode all typenodes it contains are normalized.
- * Normalized typenodes can be structurally identicial to their original
- * counterparts.
- *
- * sygus_vars are the input variables for the function to be synthesized,
- * which are used as input for the built datatypes.
- *
- * This is the function that will resolve all types and datatypes built during
- * normalization. This operation can only be performed after all types
- * contained in "tn" have been normalized, since the resolution of datatypes
- * depends on all types involved being defined.
- */
- TypeNode normalizeSygusType(TypeNode tn, Node sygus_vars);
-
- /* Retrives, or, if none, creates, stores and returns, the node for the
- * identity operator (\lambda x. x) for the given type node */
- static inline Node getIdOp(TypeNode tn)
- {
- auto it = d_tn_to_id.find(tn);
- if (it == d_tn_to_id.end())
- {
- std::vector<Node> vars = {NodeManager::currentNM()->mkBoundVar(tn)};
- Node n = NodeManager::currentNM()->mkNode(
- kind::LAMBDA,
- NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, vars),
- vars.back());
- d_tn_to_id[tn] = n;
- return n;
- }
- return it->second;
- }
-
- private:
- /** Keeps the necessary information for bulding a normalized type:
- *
- * the original typenode, from which the datatype representation can be
- * extracted
- *
- * the operators, names, print callbacks and list of argument types for each
- * constructor
- *
- * the unresolved type node used as placeholder for references of the yet to
- * be built normalized type
- *
- * a datatype to represent the structure of the type node for the normalized
- * type
- */
- class TypeObject
- {
- public:
- /* Stores the original type node and the unresolved placeholder. The
- * datatype for the latter is created with the respective name. */
- TypeObject(TypeNode src_tn, TypeNode unres_tn)
- : d_tn(src_tn),
- d_unres_tn(unres_tn),
- d_dt(Datatype(unres_tn.getAttribute(expr::VarNameAttr())))
- {
- }
- ~TypeObject() {}
-
- /** adds information in "cons" (operator, name, print callback, argument
- * types) as it is into "to"
- *
- * A side effect of this procedure is to expand the definitions in the sygus
- * operator of "cons"
- *
- * The types of the arguments of "cons" are recursively normalized
- */
- void addConsInfo(SygusGrammarNorm* sygus_norm,
- const DatatypeConstructor& cons);
-
- /** builds a datatype with the information in the type object
- *
- * "dt" is the datatype of the original typenode. It is necessary for
- * retrieving ancillary information during the datatype building, such as
- * its sygus type (e.g. Int)
- *
- * The built datatype and its unresolved type are saved in the global
- * accumulators of "sygus_norm"
- */
- void buildDatatype(SygusGrammarNorm* sygus_norm, const Datatype& dt);
-
- //---------- information stored from original type node
-
- /* The original typenode this TypeObject is built from */
- TypeNode d_tn;
-
- //---------- information to build normalized type node
-
- /* Operators for each constructor. */
- std::vector<Node> d_ops;
- /* Names for each constructor. */
- std::vector<std::string> d_cons_names;
- /* Print callbacks for each constructor */
- std::vector<std::shared_ptr<SygusPrintCallback>> d_pc;
- /* Weights for each constructor */
- std::vector<int> d_weight;
- /* List of argument types for each constructor */
- std::vector<std::vector<Type>> d_cons_args_t;
- /* Unresolved type node placeholder */
- TypeNode d_unres_tn;
- /* Datatype to represent type's structure */
- Datatype d_dt;
- }; /* class TypeObject */
-
- /** Transformation abstract class
- *
- * Classes extending this one will define specif transformationst for building
- * normalized types based on applications of specific operators
- */
- class Transf
- {
- public:
- virtual ~Transf() {}
-
- /** abstract function for building normalized types
- *
- * Builds normalized types for the operators specifed by the positions in
- * op_pos of constructors from dt. The built types are associated with the
- * given type object and accumulated in the sygus_norm object, whose
- * utilities for any extra necessary normalization.
- */
- virtual void buildType(SygusGrammarNorm* sygus_norm,
- TypeObject& to,
- const Datatype& dt,
- std::vector<unsigned>& op_pos) = 0;
- }; /* class Transf */
-
- /** Drop transformation class
- *
- * This class builds a type by dropping a set of redundant constructors,
- * whose indices are given as input to the constructor of this class.
- */
- class TransfDrop : public Transf
- {
- public:
- TransfDrop(const std::vector<unsigned>& indices) : d_drop_indices(indices)
- {
- }
- /** build type */
- void buildType(SygusGrammarNorm* sygus_norm,
- TypeObject& to,
- const Datatype& dt,
- std::vector<unsigned>& op_pos) override;
-
- private:
- std::vector<unsigned> d_drop_indices;
- };
-
- /** Chain transformation class
- *
- * Determines how to build normalized types by chaining the application of one
- * of its operators. The resulting type should admit the same terms as the
- * previous one modulo commutativity, associativity and identity of the
- * neutral element.
- *
- * TODO: #1304:
- * - define this transformation for more than just PLUS for Int.
- * - improve the building such that elements that should not be entitled a
- * "link in the chain" (such as 5 in opposition to variables and 1) do not get
- * one
- * - consider the case when operator is applied to different types, e.g.:
- * A -> B + B | x; B -> 0 | 1
- * - consider the case in which in which the operator occurs nested in an
- * argument type of itself, e.g.:
- * A -> (B + B) + B | x; B -> 0 | 1
- */
- class TransfChain : public Transf
- {
- public:
- TransfChain(unsigned chain_op_pos, const std::vector<unsigned>& elem_pos)
- : d_chain_op_pos(chain_op_pos), d_elem_pos(elem_pos){};
-
- /** builds types encoding a chain in which each link contains a repetition
- * of the application of the chain operator over a non-identity element
- *
- * Example: when considering, over the integers, the operator "+" and the
- * elemenst "1", "x" and "y", the built chain is e.g.
- *
- * x + ... + x + y + ... + y + 1 + ...+ 1
- *
- * whose encoding in types would be e.g.
- *
- * A -> AX | AX + A | B
- * AX -> x
- * B -> BY | BY + B | C
- * BY -> y
- * C -> C1 | C1 + C
- * C1 -> 1
- *
- * ++++++++
- *
- * The types composing links in the chain are built recursively by invoking
- * sygus_norm, which caches results and handles the global normalization, on
- * the operators not used in a given link, which will lead to recalling this
- * transformation and so on until all operators originally given are
- * considered.
- */
- void buildType(SygusGrammarNorm* sygus_norm,
- TypeObject& to,
- const Datatype& dt,
- std::vector<unsigned>& op_pos) override;
-
- /** Whether operator is chainable for the type (e.g. PLUS for Int)
- *
- * Since the map this function depends on cannot be built statically, this
- * function first build maps the first time a type is checked. As a
- * consequence the list of chainabel operators is hardcoded in the map
- * building.
- *
- * TODO: #1304: Cover more types and operators, make this robust to more
- * complex grammars
- */
- static bool isChainable(TypeNode tn, Node op);
- /* Whether n is the identity for the chain operator of the type (e.g. 1 is
- * not the identity 0 for PLUS for Int)
- *
- * TODO: #1304: Cover more types, make this robust to more complex grammars
- */
- static bool isId(TypeNode tn, Node op, Node n);
-
- private:
- /* TODO #1304: this should admit more than one, as well as which elements
- * are associated with which operator */
- /* Position of chain operator */
- unsigned d_chain_op_pos;
- /* Positions (of constructors in the datatype whose type is being
- * normalized) of elements the chain operator is applied to */
- std::vector<unsigned> d_elem_pos;
- /** Specifies for each type node which are its chainable operators
- *
- * For example, for Int the map is {OP -> [+]}
- *
- * TODO #1304: consider more operators
- */
- static std::map<TypeNode, std::vector<Kind>> d_chain_ops;
- /** Specifies for each type node and chainable operator its identity
- *
- * For example, for Int and PLUS the map is {Int -> {+ -> 0}}
- *
- * TODO #1304: consider more operators
- */
- static std::map<TypeNode, std::map<Kind, Node>> d_chain_op_id;
-
- }; /* class TransfChain */
-
- /** reference to quantifier engine */
- QuantifiersEngine* d_qe;
- /** sygus term database associated with this utility */
- TermDbSygus* d_tds;
- /** List of variable inputs of function-to-synthesize.
- *
- * This information is needed in the construction of each datatype
- * representation of type nodes contained in the type node being normalized
- */
- TNode d_sygus_vars;
- /* Datatypes to be resolved */
- std::vector<Datatype> d_dt_all;
- /* Types to be resolved */
- std::set<Type> d_unres_t_all;
- /* Associates type nodes with OpPosTries */
- std::map<TypeNode, OpPosTrie> d_tries;
- /* Map of type nodes into their identity operators (\lambda x. x) */
- static std::map<TypeNode, Node> d_tn_to_id;
-
- /** recursively traverses a typenode normalizing all of its elements
- *
- * "tn" is the typenode to be normalized
- * "dt" is its datatype representation
- * "op_pos" is the list of positions of construtors of dt that are being
- * considered for the normalization
- *
- * The result of normalizing tn with the respective constructors is cached
- * with an OpPosTrie. New types and datatypes created during normalization are
- * accumulated grobally to be later resolved.
- *
- * The normalization occurs following some inferred transformation based on
- * the sygus type (e.g. Int) of tn, and the operators being considered.
- *
- * Example: Let A be the type node encoding the grammar
- *
- * Int -> x | y | Int + Int | 0 | 1 | ite(Bool, Int, Int)
- *
- * and assume all its datatype constructors are being used for
- * normalization. The inferred normalization transformation will consider the
- * non-zero elements {x, y, 1, ite(...)} and the operator {+} to build a chain
- * of monomials, as seen above. The operator for "0" is rebuilt as is (the
- * default behaviour of operators not selected for transformations).
- *
- * recursion depth is limited by the height of the types, which is small
- */
- TypeNode normalizeSygusRec(TypeNode tn,
- const Datatype& dt,
- std::vector<unsigned>& op_pos);
-
- /** wrapper for the above function
- *
- * invoked when all operators of "tn" are to be considered for normalization
- */
- TypeNode normalizeSygusRec(TypeNode tn);
-
- /** infers a transformation for normalizing dt when allowed to use the
- * operators in the positions op_pos.
- *
- * TODO: #1304: Infer more complex transformations
- */
- std::unique_ptr<Transf> inferTransf(TypeNode tn,
- const Datatype& dt,
- const std::vector<unsigned>& op_pos);
-}; /* class SygusGrammarNorm */
-
-} // namespace quantifiers
-} // namespace theory
-} // namespace CVC4
-
-#endif
+++ /dev/null
-/********************* */
-/*! \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<DatatypeType>(tn.toType()).getDatatype();
- Assert(dt.isSygus());
- TypeNode btn = TypeNode::fromType(dt.getSygusType());
- for (unsigned i = 0, ncons = dt.getNumConstructors(); i < ncons; i++)
- {
- Trace("sygus-red") << " Is " << dt[i].getName() << " a redundant operator?"
- << std::endl;
- std::map<int, Node> pre;
- Node g = tds->mkGeneric(dt, i, pre);
- Trace("sygus-red-debug") << " ...pre-rewrite : " << g << std::endl;
- Assert(g.getNumChildren() == dt[i].getNumArgs());
- d_gen_terms[i] = g;
- for (unsigned j = 0, nargs = dt[i].getNumArgs(); j < nargs; j++)
- {
- pre[j] = g[j];
- }
- std::vector<Node> glist;
- getGenericList(tds, dt, i, 0, pre, glist);
- // call the extended rewriter
- bool red = false;
- for (const Node& gr : glist)
- {
- Trace("sygus-red-debug") << " ...variant : " << gr << std::endl;
- std::map<Node, unsigned>::iterator itg = d_gen_cons.find(gr);
- if (itg != d_gen_cons.end() && itg->second != i)
- {
- red = true;
- Trace("sygus-red") << " ......redundant, since a variant of " << g
- << " and " << d_gen_terms[itg->second]
- << " both rewrite to " << gr << std::endl;
- break;
- }
- else
- {
- d_gen_cons[gr] = i;
- Trace("sygus-red") << " ......not redundant." << std::endl;
- }
- }
- d_sygus_red_status.push_back(red ? 1 : 0);
- }
-}
-
-void SygusRedundantCons::getRedundant(std::vector<unsigned>& indices)
-{
- const Datatype& dt = static_cast<DatatypeType>(d_type.toType()).getDatatype();
- for (unsigned i = 0, ncons = dt.getNumConstructors(); i < ncons; i++)
- {
- if (isRedundant(i))
- {
- indices.push_back(i);
- }
- }
-}
-
-bool SygusRedundantCons::isRedundant(unsigned i)
-{
- Assert(i < d_sygus_red_status.size());
- return d_sygus_red_status[i] == 1;
-}
-
-void SygusRedundantCons::getGenericList(TermDbSygus* tds,
- const Datatype& dt,
- unsigned c,
- unsigned index,
- std::map<int, Node>& pre,
- std::vector<Node>& terms)
-{
- if (index == dt[c].getNumArgs())
- {
- Node gt = tds->mkGeneric(dt, c, pre);
- gt = tds->getExtRewriter()->extendedRewrite(gt);
- terms.push_back(gt);
- return;
- }
- // with no swap
- getGenericList(tds, dt, c, index + 1, pre, terms);
- // swapping is exponential, only use for operators with small # args.
- if (dt[c].getNumArgs() <= 5)
- {
- TypeNode atype = tds->getArgType(dt[c], index);
- for (unsigned s = index + 1, nargs = dt[c].getNumArgs(); s < nargs; s++)
- {
- if (tds->getArgType(dt[c], s) == atype)
- {
- // swap s and index
- Node tmp = pre[s];
- pre[s] = pre[index];
- pre[index] = tmp;
- getGenericList(tds, dt, c, index + 1, pre, terms);
- // revert
- tmp = pre[s];
- pre[s] = pre[index];
- pre[index] = tmp;
- }
- }
- }
-}
-
-} /* CVC4::theory::quantifiers namespace */
-} /* CVC4::theory namespace */
-} /* CVC4 namespace */
+++ /dev/null
-/********************* */
-/*! \file sygus_grammar_red.h
- ** \verbatim
- ** Top contributors (to current version):
- ** Andrew Reynolds
- ** This file is part of the CVC4 project.
- ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
- ** in the top-level source directory) and their institutional affiliations.
- ** All rights reserved. See the file COPYING in the top-level source
- ** directory for licensing information.\endverbatim
- **
- ** \brief sygus_grammar_red
- **/
-
-#include "cvc4_private.h"
-
-#ifndef __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_RED_H
-#define __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_RED_H
-
-#include <map>
-#include "theory/quantifiers_engine.h"
-
-namespace CVC4 {
-namespace theory {
-namespace quantifiers {
-
-/** SygusRedundantCons
- *
- * This class computes the subset of indices of the constructors of a sygus type
- * that are redundant. To use this class, first call initialize( qe, tn ),
- * where tn is a sygus tn. Then, use getRedundant and/or isRedundant to get the
- * indicies of the constructors of tn that are redundant.
- */
-class SygusRedundantCons
-{
- public:
- SygusRedundantCons() {}
- ~SygusRedundantCons() {}
- /** register type tn
- *
- * qe : pointer to the quantifiers engine,
- * tn : the (sygus) type to compute redundant constructors for
- */
- void initialize(QuantifiersEngine* qe, TypeNode tn);
- /** Get the indices of the redundant constructors of the register type */
- void getRedundant(std::vector<unsigned>& indices);
- /**
- * This function returns true if the i^th constructor of the registered type
- * is redundant.
- */
- bool isRedundant(unsigned i);
-
- private:
- /** the registered type */
- TypeNode d_type;
- /** redundant status
- *
- * For each constructor, status indicating whether the constructor is
- * redundant, where:
- *
- * 0 : not redundant,
- * 1 : redundant since another constructor can be used to construct values for
- * this constructor.
- *
- * For example, for grammar:
- * A -> C > B | B < C | not D
- * B -> x | y
- * C -> 0 | 1 | C+C
- * D -> B >= C
- * If A is register with this class, then we store may store { 0, 1, 0 },
- * noting that the second constructor of A can be simulated with the first.
- * Notice that the third constructor is not considered redundant.
- */
- std::vector<int> d_sygus_red_status;
- /**
- * Map from constructor indices to the generic term for that constructor,
- * where the generic term for a constructor is the (canonical) term returned
- * by a call to TermDbSygus::mkGeneric.
- */
- std::map<unsigned, Node> d_gen_terms;
- /**
- * Map from the rewritten form of generic terms for constructors of the
- * registered type to their corresponding constructor index.
- */
- std::map<Node, unsigned> d_gen_cons;
- /** get generic list
- *
- * This function constructs all well-typed variants of a term of the form
- * op( x1, ..., xn )
- * where op is the builtin operator for dt[c], and xi = pre[i] for i=1,...,n.
- *
- * It constructs a list of terms of the form g * sigma, where sigma
- * is an automorphism on { x1...xn } such that for all xi -> xj in sigma,
- * the type for arguments i and j of dt[c] are the same. We store this
- * list of terms in terms.
- *
- * This function recurses on the arguments of g, index is the current argument
- * we are processing, and pre stores the current arguments of
- *
- * For example, for a sygus grammar
- * A -> and( A, A, B )
- * B -> false
- * passing arguments such that g=and( x1, x2, x3 ) to this function will add:
- * and( x1, x2, x3 ) and and( x2, x1, x3 )
- * to terms.
- */
- void getGenericList(TermDbSygus* tds,
- const Datatype& dt,
- unsigned c,
- unsigned index,
- std::map<int, Node>& pre,
- std::vector<Node>& terms);
-};
-
-} /* CVC4::theory::quantifiers namespace */
-} /* CVC4::theory namespace */
-} /* CVC4 namespace */
-
-#endif /* __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_RED_H */
+++ /dev/null
-/********************* */
-/*! \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<Node>& exo,
- std::vector<unsigned>& ncind)
-{
- if (conj->getPbe()->hasExamples(e))
- {
- Assert(conj->getPbe()->getNumExamples(e) == exo.size());
- d_enum = e;
- d_exo.insert(d_exo.end(), exo.begin(), exo.end());
- d_neg_con_indices.insert(
- d_neg_con_indices.end(), ncind.begin(), ncind.end());
- d_conj = conj;
- }
-}
-
-bool NegContainsSygusInvarianceTest::invariant(TermDbSygus* tds,
- Node nvn,
- Node x)
-{
- if (!d_enum.isNull())
- {
- TypeNode tn = nvn.getType();
- Node nbv = tds->sygusToBuiltin(nvn, tn);
- Node nbvr = tds->getExtRewriter()->extendedRewrite(nbv);
- // if for any of the examples, it is not contained, then we can exclude
- for (unsigned i = 0; i < d_neg_con_indices.size(); i++)
- {
- unsigned ii = d_neg_con_indices[i];
- Assert(ii < d_exo.size());
- Node nbvre = d_conj->getPbe()->evaluateBuiltin(tn, nbvr, d_enum, ii);
- Node out = d_exo[ii];
- Node cont =
- NodeManager::currentNM()->mkNode(kind::STRING_STRCTN, out, nbvre);
- Trace("sygus-pbe-cterm-debug") << "Check: " << cont << std::endl;
- Node contr = Rewriter::rewrite(cont);
- if (contr == tds->d_false)
- {
- if (Trace.isOn("sygus-pbe-cterm"))
- {
- Trace("sygus-pbe-cterm")
- << "PBE-cterm : enumerator : do not consider ";
- Trace("sygus-pbe-cterm") << nbv << " for any "
- << tds->sygusToBuiltin(x) << " since "
- << std::endl;
- Trace("sygus-pbe-cterm") << " PBE-cterm : for input example : ";
- std::vector<Node> ex;
- d_conj->getPbe()->getExample(d_enum, ii, ex);
- for (unsigned j = 0; j < ex.size(); j++)
- {
- Trace("sygus-pbe-cterm") << ex[j] << " ";
- }
- Trace("sygus-pbe-cterm") << std::endl;
- Trace("sygus-pbe-cterm")
- << " PBE-cterm : this rewrites to : " << nbvre << std::endl;
- Trace("sygus-pbe-cterm")
- << " PBE-cterm : and is not in output : " << out << std::endl;
- }
- return true;
- }
- Trace("sygus-pbe-cterm-debug2")
- << "...check failed, rewrites to : " << contr << std::endl;
- }
- }
- return false;
-}
-
-} /* CVC4::theory::quantifiers namespace */
-} /* CVC4::theory namespace */
-} /* CVC4 namespace */
+++ /dev/null
-/********************* */
-/*! \file sygus_invariance.h
- ** \verbatim
- ** Top contributors (to current version):
- ** Andrew Reynolds
- ** This file is part of the CVC4 project.
- ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
- ** in the top-level source directory) and their institutional affiliations.
- ** All rights reserved. See the file COPYING in the top-level source
- ** directory for licensing information.\endverbatim
- **
- ** \brief sygus invariance tests
- **/
-
-#include "cvc4_private.h"
-
-#ifndef __CVC4__THEORY__QUANTIFIERS__SYGUS_INVARIANCE_H
-#define __CVC4__THEORY__QUANTIFIERS__SYGUS_INVARIANCE_H
-
-#include <unordered_map>
-#include <vector>
-
-#include "expr/node.h"
-
-namespace CVC4 {
-namespace theory {
-namespace quantifiers {
-
-class TermDbSygus;
-class CegConjecture;
-
-/* SygusInvarianceTest
-*
-* This class is the standard interface for term generalization
-* in SyGuS. Its interface is a single function is_variant,
-* which is a virtual condition for SyGuS terms.
-*
-* The common use case of invariance tests is when constructing
-* minimal explanations for refinement lemmas in the
-* counterexample-guided inductive synthesis (CEGIS) loop.
-* See sygus_explain.h for more details.
-*/
-class SygusInvarianceTest
-{
- public:
- virtual ~SygusInvarianceTest() {}
-
- /** Is nvn invariant with respect to this test ?
- *
- * - nvn is the term to check whether it is invariant.
- * - x is a variable such that the previous call to
- * is_invariant (if any) was with term nvn_prev, and
- * nvn is equal to nvn_prev with some subterm
- * position replaced by x. This is typically used
- * for debugging only.
- */
- bool is_invariant(TermDbSygus* tds, Node nvn, Node x)
- {
- if (invariant(tds, nvn, x))
- {
- d_update_nvn = nvn;
- return true;
- }
- return false;
- }
- /** get updated term */
- Node getUpdatedTerm() { return d_update_nvn; }
- /** set updated term */
- void setUpdatedTerm(Node n) { d_update_nvn = n; }
- protected:
- /** result of the node that satisfies this invariant */
- Node d_update_nvn;
- /** check whether nvn[ x ] is invariant */
- virtual bool invariant(TermDbSygus* tds, Node nvn, Node x) = 0;
-};
-
-/** EquivSygusInvarianceTest
-*
-* This class tests whether a term evaluates via evaluation
-* operators in the deep embedding (Section 4 of Reynolds
-* et al. CAV 2015) to fixed term d_result.
-*
-* For example, consider a SyGuS evaluation function eval
-* for a synthesis conjecture with arguments x and y.
-* Notice that the term t = (mult x y) is such that:
-* eval( t, 0, 1 ) ----> 0
-* This test is invariant on the content of the second
-* argument of t, noting that:
-* eval( (mult x _), 0, 1 ) ----> 0
-* as well, via a call to EvalSygusInvarianceTest::invariant.
-*
-* Another example, t = ite( gt( x, y ), x, y ) is such that:
-* eval( t, 2, 3 ) ----> 3
-* This test is invariant on the second child of t, noting:
-* eval( ite( gt( x, y ), _, y ), 2, 3 ) ----> 3
-*/
-class EvalSygusInvarianceTest : public SygusInvarianceTest
-{
- public:
- EvalSygusInvarianceTest() {}
-
- /** initialize this invariance test
- * This sets d_conj/d_var/d_result, where
- * we are checking whether:
- * d_conj { d_var -> n } ----> d_result.
- * for terms n.
- */
- void init(Node conj, Node var, Node res);
-
- /** do evaluate with unfolding, using the cache of this class */
- Node doEvaluateWithUnfolding(TermDbSygus* tds, Node n);
-
- protected:
- /** does d_conj{ d_var -> nvn } still rewrite to d_result? */
- bool invariant(TermDbSygus* tds, Node nvn, Node x);
-
- private:
- /** the formula we are evaluating */
- Node d_conj;
- /** the variable */
- TNode d_var;
- /** the result of the evaluation */
- Node d_result;
- /** cache of n -> the simplified form of eval( n ) */
- std::unordered_map<Node, Node, NodeHashFunction> d_visited;
-};
-
-/** EquivSygusInvarianceTest
-*
-* This class tests whether a builtin version of a
-* sygus term is equivalent up to rewriting to a RHS value bvr.
-*
-* For example,
-*
-* ite( t>0, 0, 0 ) + s*0 ----> 0
-*
-* This test is invariant on the condition t>0 and s, since:
-*
-* ite( _, 0, 0 ) + _*0 ----> 0
-*
-* for any values of _.
-*
-* It also manages the case where the rewriting is invariant
-* wrt a finite set of examples occurring in the conjecture.
-* (EX1) : For example if our input examples are:
-* (x,y,z) = (3,2,4), (5,2,6), (3,2,1)
-* On these examples, we have:
-*
-* ite( x>y, z, 0) ---> 4,6,1
-*
-* which is invariant on the second argument:
-*
-* ite( x>y, z, _) ---> 4,6,1
-*
-* For details, see Reynolds et al SYNT 2017.
-*/
-class EquivSygusInvarianceTest : public SygusInvarianceTest
-{
- public:
- EquivSygusInvarianceTest() : d_conj(nullptr) {}
-
- /** initialize this invariance test
- * tn is the sygus type for e
- * aconj/e are used for conjecture-specific symmetry breaking
- * bvr is the builtin version of the right hand side of the rewrite that we
- * are checking for invariance
- */
- void init(
- TermDbSygus* tds, TypeNode tn, CegConjecture* aconj, Node e, Node bvr);
-
- protected:
- /** checks whether the analog of nvn still rewrites to d_bvr */
- bool invariant(TermDbSygus* tds, Node nvn, Node x);
-
- private:
- /** the conjecture associated with the enumerator d_enum */
- CegConjecture* d_conj;
- /** the enumerator associated with the term for which this test is for */
- Node d_enum;
- /** the RHS of the evaluation */
- Node d_bvr;
- /** the result of the examples
- * In (EX1), this is (4,6,1)
- */
- std::vector<Node> d_exo;
-};
-
-/** DivByZeroSygusInvarianceTest
- *
- * This class tests whether a sygus term involves
- * division by zero.
- *
- * For example the test for:
- * ( x + ( y/0 )*2 )
- * is invariant on the contents of _ below:
- * ( _ + ( _/0 )*_ )
- */
-class DivByZeroSygusInvarianceTest : public SygusInvarianceTest
-{
- public:
- DivByZeroSygusInvarianceTest() {}
-
- protected:
- /** checks whether nvn involves division by zero. */
- bool invariant(TermDbSygus* tds, Node nvn, Node x);
-};
-
-/** NegContainsSygusInvarianceTest
-*
-* This class is used to construct a minimal shape of a term that cannot
-* be contained in at least one output of an I/O pair.
-*
-* Say our PBE conjecture is:
-*
-* exists f.
-* f( "abc" ) = "abc abc" ^
-* f( "de" ) = "de de"
-*
-* Then, this class is used when there is a candidate solution t[x1]
-* such that either:
-* contains( "abc abc", t["abc"] ) ---> false or
-* contains( "de de", t["de"] ) ---> false
-*
-* It is used to determine whether certain generalizations of t[x1]
-* are still sufficient to falsify one of the above containments.
-*
-* For example:
-*
-* The test for str.++( x1, "d" ) is invariant on its first argument
-* ...since contains( "abc abc", str.++( _, "d" ) ) ---> false
-* The test for str.replace( "de", x1, "b" ) is invariant on its third argument
-* ...since contains( "abc abc", str.replace( "de", "abc", _ ) ) ---> false
-*/
-class NegContainsSygusInvarianceTest : public SygusInvarianceTest
-{
- public:
- NegContainsSygusInvarianceTest() : d_conj(nullptr) {}
-
- /** initialize this invariance test
- * cpbe is the conjecture utility.
- * e is the enumerator which we are reasoning about (associated with a synth
- * fun).
- * exo is the list of outputs of the PBE conjecture.
- * ncind is the set of possible indices of the PBE conjecture to check
- * invariance of non-containment.
- * For example, in the above example, when t[x1] = "ab", then this
- * has the index 1 since contains("de de", "ab") ---> false but not
- * the index 0 since contains("abc abc","ab") ---> true.
- */
- void init(CegConjecture* conj,
- Node e,
- std::vector<Node>& exo,
- std::vector<unsigned>& ncind);
-
- protected:
- /** checks if contains( out_i, nvn[in_i] ) --> false for some I/O pair i. */
- bool invariant(TermDbSygus* tds, Node nvn, Node x);
-
- private:
- /** The enumerator whose value we are considering in this invariance test */
- Node d_enum;
- /** The output examples for the enumerator */
- std::vector<Node> d_exo;
- /** The set of I/O pair indices i such that
- * contains( out_i, nvn[in_i] ) ---> false
- */
- std::vector<unsigned> d_neg_con_indices;
- /** reference to the conjecture associated with this test */
- CegConjecture* d_conj;
-};
-
-} /* CVC4::theory::quantifiers namespace */
-} /* CVC4::theory namespace */
-} /* CVC4 namespace */
-
-#endif /* __CVC4__THEORY__QUANTIFIERS__SYGUS_INVARIANCE_H */
+++ /dev/null
-/********************* */
-/*! \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 <stack>
-
-#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<TypeNode, unsigned, TypeNodeHashFunction>
- type_to_init_deq_id;
- std::vector<Type> argTypes =
- static_cast<FunctionType>(f.getType().toType()).getArgTypes();
- for (unsigned j = 0; j < argTypes.size(); j++)
- {
- TypeNode atn = TypeNode::fromType(argTypes[j]);
- std::stringstream ss;
- ss << "a" << j;
- Node k = NodeManager::currentNM()->mkBoundVar(ss.str(), atn);
- d_arg_vars.push_back(k);
- d_arg_var_num[k] = j;
- d_arg_props.push_back(CegConjectureProcessArg());
- }
-}
-
-bool CegConjectureProcessFun::checkMatch(
- Node cn, Node n, std::unordered_map<unsigned, Node>& n_arg_map)
-{
- std::vector<Node> vars;
- std::vector<Node> subs;
- for (std::unordered_map<unsigned, Node>::iterator it = n_arg_map.begin();
- it != n_arg_map.end();
- ++it)
- {
- Assert(it->first < d_arg_vars.size());
- Assert(
- it->second.getType().isComparableTo(d_arg_vars[it->first].getType()));
- vars.push_back(d_arg_vars[it->first]);
- subs.push_back(it->second);
- }
- Node cn_subs =
- cn.substitute(vars.begin(), vars.end(), subs.begin(), subs.end());
- cn_subs = Rewriter::rewrite(cn_subs);
- Assert(Rewriter::rewrite(n) == n);
- return cn_subs == n;
-}
-
-bool CegConjectureProcessFun::isArgVar(Node n, unsigned& arg_index)
-{
- if (n.isVar())
- {
- std::unordered_map<Node, unsigned, NodeHashFunction>::iterator ita =
- d_arg_var_num.find(n);
- if (ita != d_arg_var_num.end())
- {
- arg_index = ita->second;
- return true;
- }
- }
- return false;
-}
-
-Node CegConjectureProcessFun::inferDefinition(
- Node n,
- std::unordered_map<Node, unsigned, NodeHashFunction>& term_to_arg_carry,
- std::unordered_map<Node,
- std::unordered_set<Node, NodeHashFunction>,
- NodeHashFunction>& free_vars)
-{
- std::unordered_map<TNode, Node, TNodeHashFunction> visited;
- std::unordered_map<TNode, Node, TNodeHashFunction>::iterator it;
- std::stack<TNode> visit;
- TNode cur;
- visit.push(n);
- do
- {
- cur = visit.top();
- visit.pop();
- it = visited.find(cur);
- if (it == visited.end())
- {
- // if it is ground, we can use it
- if (free_vars[cur].empty())
- {
- visited[cur] = cur;
- }
- else
- {
- // if it is term used by another argument, use it
- std::unordered_map<Node, unsigned, NodeHashFunction>::iterator itt =
- term_to_arg_carry.find(cur);
- if (itt != term_to_arg_carry.end())
- {
- visited[cur] = d_arg_vars[itt->second];
- }
- else if (cur.getNumChildren() > 0)
- {
- // try constructing children
- visited[cur] = Node::null();
- visit.push(cur);
- for (unsigned i = 0; i < cur.getNumChildren(); i++)
- {
- visit.push(cur[i]);
- }
- }
- else
- {
- return Node::null();
- }
- }
- }
- else if (it->second.isNull())
- {
- Node ret = cur;
- bool childChanged = false;
- std::vector<Node> children;
- if (cur.getMetaKind() == kind::metakind::PARAMETERIZED)
- {
- children.push_back(cur.getOperator());
- }
- for (unsigned i = 0; i < cur.getNumChildren(); i++)
- {
- it = visited.find(cur[i]);
- Assert(it != visited.end());
- Assert(!it->second.isNull());
- childChanged = childChanged || cur[i] != it->second;
- children.push_back(it->second);
- }
- if (childChanged)
- {
- ret = NodeManager::currentNM()->mkNode(cur.getKind(), children);
- }
- visited[cur] = ret;
- }
- } while (!visit.empty());
- Assert(visited.find(n) != visited.end());
- Assert(!visited.find(n)->second.isNull());
- return visited[n];
-}
-
-unsigned CegConjectureProcessFun::assignRelevantDef(Node def,
- std::vector<unsigned>& args)
-{
- unsigned id = 0;
- if (def.isNull())
- {
- // prefer one that already has a definition, if one exists
- for (unsigned j = 0; j < args.size(); j++)
- {
- unsigned i = args[j];
- if (!d_arg_props[i].d_template.isNull())
- {
- id = j;
- break;
- }
- }
- }
- unsigned rid = args[id];
- // for merging previously equivalent definitions
- std::unordered_map<Node, unsigned, NodeHashFunction> prev_defs;
- for (unsigned j = 0; j < args.size(); j++)
- {
- unsigned i = args[j];
- Trace("sygus-process-arg-deps") << " ...processed arg #" << i;
- if (!d_arg_props[i].d_template.isNull())
- {
- if (d_arg_props[i].d_template == def)
- {
- // definition was consistent
- }
- else
- {
- Node t = d_arg_props[i].d_template;
- std::unordered_map<Node, unsigned, NodeHashFunction>::iterator itt =
- prev_defs.find(t);
- if (itt != prev_defs.end())
- {
- // merge previously equivalent definitions
- d_arg_props[i].d_template = d_arg_vars[itt->second];
- Trace("sygus-process-arg-deps")
- << " (merged equivalent def from argument ";
- Trace("sygus-process-arg-deps") << itt->second << ")." << std::endl;
- }
- else
- {
- // store this as previous
- prev_defs[t] = i;
- // now must be relevant
- d_arg_props[i].d_relevant = true;
- Trace("sygus-process-arg-deps")
- << " (marked relevant, overwrite definition)." << std::endl;
- }
- }
- }
- else
- {
- if (def.isNull())
- {
- if (i != rid)
- {
- // marked as relevant, but template can be set equal to master
- d_arg_props[i].d_template = d_arg_vars[rid];
- Trace("sygus-process-arg-deps") << " (new definition, map to master "
- << d_arg_vars[rid] << ")."
- << std::endl;
- }
- else
- {
- d_arg_props[i].d_relevant = true;
- Trace("sygus-process-arg-deps") << " (marked relevant)." << std::endl;
- }
- }
- else
- {
- // has new definition
- d_arg_props[i].d_template = def;
- Trace("sygus-process-arg-deps") << " (new definition " << def << ")."
- << std::endl;
- }
- }
- }
- return rid;
-}
-
-void CegConjectureProcessFun::processTerms(
- std::vector<Node>& ns,
- std::vector<Node>& ks,
- Node nf,
- std::unordered_set<Node, NodeHashFunction>& synth_fv,
- std::unordered_map<Node,
- std::unordered_set<Node, NodeHashFunction>,
- NodeHashFunction>& free_vars)
-{
- Assert(ns.size() == ks.size());
- Trace("sygus-process-arg-deps") << "Process " << ns.size()
- << " applications of " << d_synth_fun << "..."
- << std::endl;
-
- // get the relevant variables
- // relevant variables are those that appear in the body of the conjunction
- std::unordered_set<Node, NodeHashFunction> rlv_vars;
- Assert(free_vars.find(nf) != free_vars.end());
- rlv_vars = free_vars[nf];
-
- // get the single occurrence variables
- // single occurrence variables are those that appear in only one position,
- // as an argument to the function-to-synthesize.
- std::unordered_map<Node, bool, NodeHashFunction> single_occ_variables;
- for (unsigned index = 0; index < ns.size(); index++)
- {
- Node n = ns[index];
- for (unsigned i = 0; i < n.getNumChildren(); i++)
- {
- Node nn = n[i];
- if (nn.isVar())
- {
- std::unordered_map<Node, bool, NodeHashFunction>::iterator its =
- single_occ_variables.find(nn);
- if (its == single_occ_variables.end())
- {
- // only irrelevant variables
- single_occ_variables[nn] = rlv_vars.find(nn) == rlv_vars.end();
- }
- else
- {
- single_occ_variables[nn] = false;
- }
- }
- else
- {
- std::unordered_map<Node,
- std::unordered_set<Node, NodeHashFunction>,
- NodeHashFunction>::iterator itf = free_vars.find(nn);
- Assert(itf != free_vars.end());
- for (std::unordered_set<Node, NodeHashFunction>::iterator itfv =
- itf->second.begin();
- itfv != itf->second.end();
- ++itfv)
- {
- single_occ_variables[*itfv] = false;
- }
- }
- }
- }
-
- // update constant argument information
- for (unsigned index = 0; index < ns.size(); index++)
- {
- Node n = ns[index];
- Trace("sygus-process-arg-deps")
- << " Analyze argument information for application #" << index << ": "
- << n << std::endl;
-
- // in the following, we say an argument a "carries" a term t if
- // the function to synthesize would use argument a to construct
- // the term t in its definition.
-
- // map that assumes all arguments carry their respective term
- std::unordered_map<unsigned, Node> n_arg_map;
- // terms to the argument that is carrying it.
- // the arguments in the range of this map must be marked as relevant.
- std::unordered_map<Node, unsigned, NodeHashFunction> term_to_arg_carry;
- // map of terms to (unprocessed) arguments where it occurs
- std::unordered_map<Node, std::vector<unsigned>, NodeHashFunction>
- term_to_args;
-
- // initialize
- for (unsigned a = 0; a < n.getNumChildren(); a++)
- {
- n_arg_map[a] = n[a];
- }
-
- for (unsigned a = 0; a < n.getNumChildren(); a++)
- {
- bool processed = false;
- if (d_arg_props[a].d_relevant)
- {
- // we can assume all relevant arguments carry their terms
- processed = true;
- Trace("sygus-process-arg-deps") << " ...processed arg #" << a
- << " (already relevant)." << std::endl;
- if (term_to_arg_carry.find(n[a]) == term_to_arg_carry.end())
- {
- Trace("sygus-process-arg-deps") << " carry " << n[a]
- << " by argument #" << a << std::endl;
- term_to_arg_carry[n[a]] = a;
- }
- }
- else
- {
- // first, check if single occurrence variable
- // check if an irrelevant variable
- if (n[a].isVar() && synth_fv.find(n[a]) != synth_fv.end())
- {
- Assert(single_occ_variables.find(n[a]) != single_occ_variables.end());
- // may be able to make this more precise?
- // check if a single-occurrence variable
- if (single_occ_variables[n[a]])
- {
- // if we do not already have a template definition, or the
- // template is a single occurrence variable
- if (d_arg_props[a].d_template.isNull()
- || d_arg_props[a].d_var_single_occ)
- {
- processed = true;
- Trace("sygus-process-arg-deps") << " ...processed arg #" << a;
- Trace("sygus-process-arg-deps")
- << " (single occurrence variable ";
- Trace("sygus-process-arg-deps") << n[a] << ")." << std::endl;
- d_arg_props[a].d_var_single_occ = true;
- d_arg_props[a].d_template = n[a];
- }
- }
- }
- if (!processed && !d_arg_props[a].d_template.isNull()
- && !d_arg_props[a].d_var_single_occ)
- {
- // argument already has a definition, see if it is maintained
- if (checkMatch(d_arg_props[a].d_template, n[a], n_arg_map))
- {
- processed = true;
- Trace("sygus-process-arg-deps") << " ...processed arg #" << a;
- Trace("sygus-process-arg-deps") << " (consistent definition "
- << n[a];
- Trace("sygus-process-arg-deps")
- << " with " << d_arg_props[a].d_template << ")." << std::endl;
- }
- }
- }
- if (!processed)
- {
- // otherwise, add it to the list of arguments for this term
- term_to_args[n[a]].push_back(a);
- }
- }
-
- Trace("sygus-process-arg-deps") << " Look at argument terms..."
- << std::endl;
-
- // list of all arguments
- std::vector<Node> arg_list;
- // now look at the terms for unprocessed arguments
- for (std::unordered_map<Node, std::vector<unsigned>, NodeHashFunction>::
- iterator it = term_to_args.begin();
- it != term_to_args.end();
- ++it)
- {
- Node nn = it->first;
- arg_list.push_back(nn);
- if (Trace.isOn("sygus-process-arg-deps"))
- {
- Trace("sygus-process-arg-deps") << " argument " << nn;
- Trace("sygus-process-arg-deps") << " (" << it->second.size()
- << " positions)";
- // check the status of this term
- if (nn.isVar() && synth_fv.find(nn) != synth_fv.end())
- {
- // is it relevant?
- if (rlv_vars.find(nn) != rlv_vars.end())
- {
- Trace("sygus-process-arg-deps") << " is a relevant variable."
- << std::endl;
- }
- else
- {
- Trace("sygus-process-arg-deps") << " is an irrelevant variable."
- << std::endl;
- }
- }
- else
- {
- // this can be more precise
- Trace("sygus-process-arg-deps") << " is a relevant term."
- << std::endl;
- }
- }
- }
-
- unsigned arg_list_counter = 0;
- // sort arg_list by term size?
-
- while (arg_list_counter < arg_list.size())
- {
- Node infer_def_t;
- do
- {
- infer_def_t = Node::null();
- // see if we can infer a definition
- for (std::unordered_map<Node, std::vector<unsigned>, NodeHashFunction>::
- iterator it = term_to_args.begin();
- it != term_to_args.end();
- ++it)
- {
- Node def = inferDefinition(it->first, term_to_arg_carry, free_vars);
- if (!def.isNull())
- {
- Trace("sygus-process-arg-deps") << " *** Inferred definition "
- << def << " for " << it->first
- << std::endl;
- // assign to each argument
- assignRelevantDef(def, it->second);
- // term_to_arg_carry[it->first] = rid;
- infer_def_t = it->first;
- break;
- }
- }
- if (!infer_def_t.isNull())
- {
- term_to_args.erase(infer_def_t);
- }
- } while (!infer_def_t.isNull());
-
- // decide to make an argument relevant
- bool success = false;
- while (arg_list_counter < arg_list.size() && !success)
- {
- Node curr = arg_list[arg_list_counter];
- std::unordered_map<Node, std::vector<unsigned>, NodeHashFunction>::
- iterator it = term_to_args.find(curr);
- if (it != term_to_args.end())
- {
- Trace("sygus-process-arg-deps") << " *** Decide relevant " << curr
- << std::endl;
- // assign relevant to each
- Node null_def;
- unsigned rid = assignRelevantDef(null_def, it->second);
- term_to_arg_carry[curr] = rid;
- Trace("sygus-process-arg-deps")
- << " carry " << curr << " by argument #" << rid << std::endl;
- term_to_args.erase(curr);
- success = true;
- }
- arg_list_counter++;
- }
- }
- }
-}
-
-bool CegConjectureProcessFun::isArgRelevant(unsigned i)
-{
- return d_arg_props[i].d_relevant;
-}
-
-void CegConjectureProcessFun::getIrrelevantArgs(
- std::unordered_set<unsigned>& args)
-{
- for (unsigned i = 0; i < d_arg_vars.size(); i++)
- {
- if (!d_arg_props[i].d_relevant)
- {
- args.insert(i);
- }
- }
-}
-
-CegConjectureProcess::CegConjectureProcess(QuantifiersEngine* qe) {}
-CegConjectureProcess::~CegConjectureProcess() {}
-Node CegConjectureProcess::preSimplify(Node q)
-{
- Trace("sygus-process") << "Pre-simplify conjecture : " << q << std::endl;
- return q;
-}
-
-Node CegConjectureProcess::postSimplify(Node q)
-{
- Trace("sygus-process") << "Post-simplify conjecture : " << q << std::endl;
- Assert(q.getKind() == FORALL);
-
- // initialize the information about each function to synthesize
- for (unsigned i = 0; i < q[0].getNumChildren(); i++)
- {
- Node f = q[0][i];
- if (f.getType().isFunction())
- {
- d_sf_info[f].init(f);
- }
- }
-
- // get the base on the conjecture
- Node base = q[1];
- std::unordered_set<Node, NodeHashFunction> synth_fv;
- if (base.getKind() == NOT && base[0].getKind() == FORALL)
- {
- for (unsigned j = 0; j < base[0][0].getNumChildren(); j++)
- {
- synth_fv.insert(base[0][0][j]);
- }
- base = base[0][1];
- }
- std::vector<Node> conjuncts;
- getComponentVector(AND, base, conjuncts);
-
- // process the conjunctions
- for (std::map<Node, CegConjectureProcessFun>::iterator it = d_sf_info.begin();
- it != d_sf_info.end();
- ++it)
- {
- Node f = it->first;
- for (unsigned i = 0; i < conjuncts.size(); i++)
- {
- processConjunct(conjuncts[i], f, synth_fv);
- }
- }
-
- return q;
-}
-
-void CegConjectureProcess::initialize(Node n, std::vector<Node>& candidates)
-{
- if (Trace.isOn("sygus-process"))
- {
- Trace("sygus-process") << "Process conjecture : " << n
- << " with candidates: " << std::endl;
- for (unsigned i = 0; i < candidates.size(); i++)
- {
- Trace("sygus-process") << " " << candidates[i] << std::endl;
- }
- }
-}
-
-bool CegConjectureProcess::isArgRelevant(Node f, unsigned i)
-{
- std::map<Node, CegConjectureProcessFun>::iterator its = d_sf_info.find(f);
- if (its != d_sf_info.end())
- {
- return its->second.isArgRelevant(i);
- }
- Assert(false);
- return true;
-}
-
-bool CegConjectureProcess::getIrrelevantArgs(Node f,
- std::unordered_set<unsigned>& args)
-{
- std::map<Node, CegConjectureProcessFun>::iterator its = d_sf_info.find(f);
- if (its != d_sf_info.end())
- {
- its->second.getIrrelevantArgs(args);
- return true;
- }
- return false;
-}
-
-void CegConjectureProcess::processConjunct(
- Node n, Node f, std::unordered_set<Node, NodeHashFunction>& synth_fv)
-{
- Trace("sygus-process-arg-deps") << "Process conjunct: " << std::endl;
- Trace("sygus-process-arg-deps") << " " << n << " for synth fun " << f
- << "..." << std::endl;
-
- // first, flatten the conjunct
- // make a copy of free variables since we may add new ones
- std::unordered_set<Node, NodeHashFunction> synth_fv_n = synth_fv;
- std::unordered_map<Node, Node, NodeHashFunction> defs;
- Node nf = flatten(n, f, synth_fv_n, defs);
-
- Trace("sygus-process-arg-deps") << "Flattened to: " << std::endl;
- Trace("sygus-process-arg-deps") << " " << nf << std::endl;
-
- // get free variables in nf
- std::unordered_map<Node,
- std::unordered_set<Node, NodeHashFunction>,
- NodeHashFunction>
- free_vars;
- getFreeVariables(nf, synth_fv_n, free_vars);
- // get free variables in each application
- std::vector<Node> ns;
- std::vector<Node> ks;
- for (std::unordered_map<Node, Node, NodeHashFunction>::iterator it =
- defs.begin();
- it != defs.end();
- ++it)
- {
- getFreeVariables(it->second, synth_fv_n, free_vars);
- ns.push_back(it->second);
- ks.push_back(it->first);
- }
-
- // process the applications of synthesis functions
- if (!ns.empty())
- {
- std::map<Node, CegConjectureProcessFun>::iterator its = d_sf_info.find(f);
- if (its != d_sf_info.end())
- {
- its->second.processTerms(ns, ks, nf, synth_fv_n, free_vars);
- }
- }
-}
-
-Node CegConjectureProcess::CegConjectureProcess::flatten(
- Node n,
- Node f,
- std::unordered_set<Node, NodeHashFunction>& synth_fv,
- std::unordered_map<Node, Node, NodeHashFunction>& defs)
-{
- std::unordered_map<Node, Node, NodeHashFunction> visited;
- std::unordered_map<Node, Node, NodeHashFunction>::iterator it;
- std::stack<Node> visit;
- Node cur;
- visit.push(n);
- do
- {
- cur = visit.top();
- visit.pop();
- it = visited.find(cur);
-
- if (it == visited.end())
- {
- visited[cur] = Node::null();
- visit.push(cur);
- for (unsigned i = 0; i < cur.getNumChildren(); i++)
- {
- visit.push(cur[i]);
- }
- }
- else if (it->second.isNull())
- {
- Node ret = cur;
- bool childChanged = false;
- std::vector<Node> children;
- if (cur.getMetaKind() == kind::metakind::PARAMETERIZED)
- {
- children.push_back(cur.getOperator());
- }
- for (unsigned i = 0; i < cur.getNumChildren(); i++)
- {
- it = visited.find(cur[i]);
- Assert(it != visited.end());
- Assert(!it->second.isNull());
- childChanged = childChanged || cur[i] != it->second;
- children.push_back(it->second);
- }
- if (childChanged)
- {
- ret = NodeManager::currentNM()->mkNode(cur.getKind(), children);
- }
- // is it the function to synthesize?
- if (cur.getKind() == APPLY_UF && cur.getOperator() == f)
- {
- // if so, flatten
- Node k = NodeManager::currentNM()->mkBoundVar("vf", cur.getType());
- defs[k] = ret;
- ret = k;
- synth_fv.insert(k);
- }
- // post-rewrite
- visited[cur] = ret;
- }
- } while (!visit.empty());
- Assert(visited.find(n) != visited.end());
- Assert(!visited.find(n)->second.isNull());
- return visited[n];
-}
-
-void CegConjectureProcess::getFreeVariables(
- Node n,
- std::unordered_set<Node, NodeHashFunction>& synth_fv,
- std::unordered_map<Node,
- std::unordered_set<Node, NodeHashFunction>,
- NodeHashFunction>& free_vars)
-{
- // first must compute free variables in each subterm of n,
- // as well as contains_synth_fun
- std::unordered_map<Node, bool, NodeHashFunction> visited;
- std::unordered_map<Node, bool, NodeHashFunction>::iterator it;
- std::stack<Node> visit;
- Node cur;
- visit.push(n);
- do
- {
- cur = visit.top();
- visit.pop();
- it = visited.find(cur);
-
- if (it == visited.end())
- {
- visited[cur] = false;
- visit.push(cur);
- for (unsigned i = 0; i < cur.getNumChildren(); i++)
- {
- visit.push(cur[i]);
- }
- }
- else if (!it->second)
- {
- free_vars[cur].clear();
- if (synth_fv.find(cur) != synth_fv.end())
- {
- // it is a free variable
- free_vars[cur].insert(cur);
- }
- else
- {
- // otherwise, carry the free variables from the children
- for (unsigned i = 0; i < cur.getNumChildren(); i++)
- {
- free_vars[cur].insert(free_vars[cur[i]].begin(),
- free_vars[cur[i]].end());
- }
- }
- visited[cur] = true;
- }
- } while (!visit.empty());
-}
-
-Node CegConjectureProcess::getSymmetryBreakingPredicate(
- Node x, Node e, TypeNode tn, unsigned tindex, unsigned depth)
-{
- return Node::null();
-}
-
-void CegConjectureProcess::debugPrint(const char* c) {}
-void CegConjectureProcess::getComponentVector(Kind k,
- Node n,
- std::vector<Node>& args)
-{
- if (n.getKind() == k)
- {
- for (unsigned i = 0; i < n.getNumChildren(); i++)
- {
- args.push_back(n[i]);
- }
- }
- else
- {
- args.push_back(n);
- }
-}
-
-} /* namespace CVC4::theory::quantifiers */
-} /* namespace CVC4::theory */
-} /* namespace CVC4 */
+++ /dev/null
-/********************* */
-/*! \file sygus_process_conj.h
- ** \verbatim
- ** Top contributors (to current version):
- ** Andrew Reynolds
- ** This file is part of the CVC4 project.
- ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
- ** in the top-level source directory) and their institutional affiliations.
- ** All rights reserved. See the file COPYING in the top-level source
- ** directory for licensing information.\endverbatim
- **
- ** \brief Techniqures for static preprocessing and analysis of
- ** sygus conjectures.
- **/
-
-#include "cvc4_private.h"
-
-#ifndef __CVC4__THEORY__QUANTIFIERS__SYGUS_PROCESS_CONJ_H
-#define __CVC4__THEORY__QUANTIFIERS__SYGUS_PROCESS_CONJ_H
-
-#include <map>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-#include "expr/node.h"
-#include "expr/type_node.h"
-#include "theory/quantifiers_engine.h"
-
-namespace CVC4 {
-namespace theory {
-namespace quantifiers {
-
-/** This file contains techniques that compute
- * argument relevancy for synthesis functions
- *
- * Let F be a synthesis conjecture of the form:
- * exists f. forall X. P( f, X )
- *
- * The classes below compute whether certain arguments of
- * the function-to-synthesize f are irrelevant.
- * Assume that f is a binary function, where possible solutions
- * to the above conjecture are of the form:
- * f -> (lambda (xy) t[x,y])
- * We say e.g. that the 2nd argument of f is irrelevant if we
- * can determine:
- * F has a solution
- * if and only if
- * F has a solution of the form f -> (lambda (xy) t[x] )
- * We conclude that arguments are irrelevant using the following
- * techniques.
- *
- *
- * (1) Argument invariance:
- *
- * Let s[z] be a term whose free variables are contained in { z }.
- * If all occurrences of f-applications in F are of the form:
- * f(t, s[t])
- * then:
- * f = (lambda (xy) r[x,y])
- * is a solution to F only if:
- * f = (lambda (xy) r[x,s[x]])
- * is as well.
- * Hence the second argument of f is not relevant.
- *
- *
- * (2) Variable irrelevance:
- *
- * If F is equivalent to:
- * exists f. forall w z u1...un. C1 ^...^Cm,
- * where for i=1...m, Ci is of the form:
- * ( w1 = f(tm1[z], u1) ^
- * ... ^
- * wn = f(tmn[z], un) ) => Pm(w1...wn, z)
- * then the second argument of f is irrelevant.
- * We call u1...un single occurrence variables
- * (in Ci).
- *
- *
- * TODO (#1210) others, generalize (2)?
- *
- */
-
-/** This structure stores information regarding
- * an argument of a function to synthesize.
- *
- * It is used to store whether the argument
- * position in the function to synthesize is
- * relevant.
- */
-class CegConjectureProcessArg
-{
- public:
- CegConjectureProcessArg() : d_var_single_occ(false), d_relevant(false) {}
- /** template definition
- * This is the term s[z] described
- * under "Argument Invariance" above.
- */
- Node d_template;
- /** single occurrence
- * Whether we are trying to show this argument
- * is irrelevant by "Variable irrelevance"
- * described above.
- */
- bool d_var_single_occ;
- /** whether this argument is relevant
- * An argument is marked as relevant if:
- * (A) it is explicitly marked as relevant
- * due to a function application containing
- * a relevant term at this argument position, or
- * (B) if it is given conflicting template definitions.
- */
- bool d_relevant;
-};
-
-/** This structure stores information regarding conjecture-specific
-* analysis of a single function to synthesize within
-* a conjecture to synthesize.
-*
-* It maintains information about each of the function to
-* synthesize's arguments.
-*/
-struct CegConjectureProcessFun
-{
- public:
- CegConjectureProcessFun() {}
- ~CegConjectureProcessFun() {}
- /** initialize this class for function f */
- void init(Node f);
- /** process terms
- *
- * This is called once per conjunction in
- * the synthesis conjecture.
- *
- * ns are the f-applications to process,
- * ks are the variables we introduced to flatten them,
- * nf is the flattened form of our conjecture to process,
- * free_vars maps all subterms of n and nf to the set
- * of variables (in set synth_fv) they contain.
- *
- * This updates information regarding which arguments
- * of the function-to-synthesize are relevant.
- */
- void processTerms(
- std::vector<Node>& ns,
- std::vector<Node>& ks,
- Node nf,
- std::unordered_set<Node, NodeHashFunction>& synth_fv,
- std::unordered_map<Node,
- std::unordered_set<Node, NodeHashFunction>,
- NodeHashFunction>& free_vars);
- /** is the i^th argument of the function-to-synthesize of this class relevant?
- */
- bool isArgRelevant(unsigned i);
- /** get irrelevant arguments for the function-to-synthesize of this class */
- void getIrrelevantArgs(std::unordered_set<unsigned>& args);
-
- private:
- /** the synth fun associated with this */
- Node d_synth_fun;
- /** properties of each argument */
- std::vector<CegConjectureProcessArg> d_arg_props;
- /** variables for each argument type of f
- *
- * These are used to express templates for argument
- * invariance, in the data member
- * CegConjectureProcessArg::d_template.
- */
- std::vector<Node> d_arg_vars;
- /** map from d_arg_vars to the argument #
- * they represent.
- */
- std::unordered_map<Node, unsigned, NodeHashFunction> d_arg_var_num;
- /** check match
- * This function returns true iff we can infer:
- * cn * { x -> n_arg_map[d_arg_var_num[x]] | x in d_arg_vars } = n
- * In other words, cn and n are equivalent
- * via the substitution mapping argument variables to terms
- * specified by n_arg_map. The rewriter is used for inferring
- * this equivalence.
- *
- * For example, if n_arg_map contains { 1 -> t, 2 -> s }, then
- * checkMatch( x1+x2, t+s, n_arg_map ) returns true,
- * checkMatch( x1+1, t+1, n_arg_map ) returns true,
- * checkMatch( 0, 0, n_arg_map ) returns true,
- * checkMatch( x1+1, s, n_arg_map ) returns false.
- */
- bool checkMatch(Node cn,
- Node n,
- std::unordered_map<unsigned, Node>& n_arg_map);
- /** infer definition
- *
- * In the following, we say a term is a "template
- * definition" if its free variables are a subset of d_arg_vars.
- *
- * If this function returns a non-null node ret, then
- * checkMatch( ret, n, term_to_arg_carry^-1 ) returns true.
- * and ret is a template definition.
- *
- * The free variables for all subterms of n are stored in
- * free_vars. The map term_to_arg_carry is injective.
- *
- * For example, if term_to_arg_carry contains { t -> 1, s -> 2 } and
- * free_vars is { t -> {y}, r -> {y}, s -> {}, q -> {}, ... -> {} }, then
- * inferDefinition( 0, term_to_arg_carry, free_vars )
- * returns 0
- * inferDefinition( t, term_to_arg_carry, free_vars )
- * returns x1
- * inferDefinition( t+s+q, term_to_arg_carry, free_vars )
- * returns x1+x2+q
- * inferDefinition( t+r, term_to_arg_carry, free_vars )
- * returns null
- *
- * Notice that multiple definitions are possible, e.g. above:
- * inferDefinition( s, term_to_arg_carry, free_vars )
- * may return either s or x2
- * TODO (#1210) : try multiple definitions?
- */
- Node inferDefinition(
- Node n,
- std::unordered_map<Node, unsigned, NodeHashFunction>& term_to_arg_carry,
- std::unordered_map<Node,
- std::unordered_set<Node, NodeHashFunction>,
- NodeHashFunction>& free_vars);
- /** Assign relevant definition
- *
- * If def is non-null,
- * this function assigns def as a template definition
- * for the argument positions in args.
- * This is called when there exists a term of the form
- * f( t1....tn )
- * in the synthesis conjecture that we are processing,
- * where t_i = def * sigma for all i \in args,
- * for some substitution sigma, where def is a template
- * definition.
- *
- * If def is null, then there exists a term of the form
- * f( t1....tn )
- * where t_i = s for for all i \in args, and s is not
- * a template definition. In this case, at least one
- * argument in args must be marked as a relevant
- * argument position.
- *
- * Returns a value rid such that:
- * (1) rid occurs in args,
- * (2) if def is null, then argument rid was marked
- * relevant by this call.
- */
- unsigned assignRelevantDef(Node def, std::vector<unsigned>& args);
- /** returns true if n is in d_arg_vars, updates arg_index
- * to its position in d_arg_vars.
- */
- bool isArgVar(Node n, unsigned& arg_index);
-};
-
-/** Ceg Conjecture Process
-*
-* This class implements static techniques for preprocessing and analysis of
-* sygus conjectures.
-*
-* It is used as a back-end to CegConjecture, which calls it using the following
-* interface:
-* (1) When a sygus conjecture is asserted, we call
-* CegConjectureProcess::simplify( q ),
-* where q is the sygus conjecture in original form.
-*
-* (2) After a sygus conjecture is simplified and converted to deep
-* embedding form, we call CegConjectureProcess::initialize( n, candidates ).
-*
-* (3) During enumerative SyGuS search, calls may be made by
-* the extension of the quantifier-free datatypes decision procedure for
-* sygus to CegConjectureProcess::getSymmetryBreakingPredicate(...), which are
-* used for pruning search space based on conjecture-specific analysis.
-*/
-class CegConjectureProcess
-{
- public:
- CegConjectureProcess(QuantifiersEngine* qe);
- ~CegConjectureProcess();
- /** simplify the synthesis conjecture q
- * Returns a formula that is equivalent to q.
- * This simplification pass is called before all others
- * in CegConjecture::assign.
- *
- * This function is intended for simplifications that
- * impact whether or not the synthesis conjecture is
- * single-invocation.
- */
- Node preSimplify(Node q);
- /** simplify the synthesis conjecture q
- * Returns a formula that is equivalent to q.
- * This simplification pass is called after all others
- * in CegConjecture::assign.
- */
- Node postSimplify(Node q);
- /** initialize
- *
- * n is the "base instantiation" of the deep-embedding version of
- * the synthesis conjecture under "candidates".
- * (see CegConjecture::d_base_inst)
- */
- void initialize(Node n, std::vector<Node>& candidates);
- /** is the i^th argument of the function-to-synthesize f relevant? */
- bool isArgRelevant(Node f, unsigned i);
- /** get irrelevant arguments for function-to-synthesize f
- * returns true if f is a function-to-synthesize.
- */
- bool getIrrelevantArgs(Node f, std::unordered_set<unsigned>& args);
- /** get symmetry breaking predicate
- *
- * Returns a formula that restricts the enumerative search space (for a given
- * depth) for a term x of sygus type tn whose top symbol is the tindex^{th}
- * constructor, where x is a subterm of enumerator e.
- */
- Node getSymmetryBreakingPredicate(
- Node x, Node e, TypeNode tn, unsigned tindex, unsigned depth);
- /** print out debug information about this conjecture */
- void debugPrint(const char* c);
- private:
- /** process conjunct
- *
- * This sets up initial information about functions to synthesize
- * where n is a conjunct of the synthesis conjecture, and synth_fv
- * is the set of (inner) universal variables in the synthesis
- * conjecture.
- */
- void processConjunct(Node n,
- Node f,
- std::unordered_set<Node, NodeHashFunction>& synth_fv);
- /** flatten
- *
- * Flattens all applications of f in term n.
- * This may add new variables to synth_fv, which
- * are introduced at all positions of functions
- * to synthesize in a bottom-up fashion. For each
- * variable k introduced for a function application
- * f(t), we add ( k -> f(t) ) to defs and ( f -> k )
- * to fun_to_defs.
- */
- Node flatten(Node n,
- Node f,
- std::unordered_set<Node, NodeHashFunction>& synth_fv,
- std::unordered_map<Node, Node, NodeHashFunction>& defs);
- /** get free variables
- * Constructs a map of all free variables that occur in n
- * from synth_fv and stores them in the map free_vars.
- */
- void getFreeVariables(
- Node n,
- std::unordered_set<Node, NodeHashFunction>& synth_fv,
- std::unordered_map<Node,
- std::unordered_set<Node, NodeHashFunction>,
- NodeHashFunction>& free_vars);
- /** for each synth-fun, information that is specific to this conjecture */
- std::map<Node, CegConjectureProcessFun> d_sf_info;
-
- /** get component vector */
- void getComponentVector(Kind k, Node n, std::vector<Node>& args);
-};
-
-} /* namespace CVC4::theory::quantifiers */
-} /* namespace CVC4::theory */
-} /* namespace CVC4 */
-
-#endif
#include <map>
#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 {
#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"
+++ /dev/null
-/********************* */
-/*! \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<n.getNumChildren(); i++ ){
- if( hasFreeVar( n[i], visited ) ){
- return true;
- }
- }
- }
- return false;
-}
-
-bool TermDbSygus::hasFreeVar( Node n ) {
- std::map< Node, bool > visited;
- return hasFreeVar( n, visited );
-}
-
-TypeNode TermDbSygus::getSygusTypeForVar( Node v ) {
- Assert( d_fv_stype.find( v )!=d_fv_stype.end() );
- return d_fv_stype[v];
-}
-
-Node TermDbSygus::mkGeneric(const Datatype& dt,
- unsigned c,
- std::map<TypeNode, int>& var_count,
- std::map<int, Node>& pre)
-{
- Assert(c < dt.getNumConstructors());
- Assert( dt.isSygus() );
- Assert( !dt[c].getSygusOp().isNull() );
- std::vector< Node > children;
- Node op = Node::fromExpr( dt[c].getSygusOp() );
- if( op.getKind()!=BUILTIN ){
- children.push_back( op );
- }
- Trace("sygus-db-debug") << "mkGeneric " << dt.getName() << " " << op << " " << op.getKind() << "..." << std::endl;
- for (unsigned i = 0, nargs = dt[c].getNumArgs(); i < nargs; i++)
- {
- TypeNode tna = getArgType( dt[c], i );
- Node a;
- std::map< int, Node >::iterator it = pre.find( i );
- if( it!=pre.end() ){
- a = it->second;
- }else{
- a = getFreeVarInc( tna, var_count, true );
- }
- Trace("sygus-db-debug")
- << " child " << i << " : " << a << " : " << a.getType() << std::endl;
- Assert( !a.isNull() );
- children.push_back( a );
- }
- Node ret;
- if( op.getKind()==BUILTIN ){
- Trace("sygus-db-debug") << "Make builtin node..." << std::endl;
- ret = NodeManager::currentNM()->mkNode( op, children );
- }else{
- Kind ok = getOperatorKind( op );
- Trace("sygus-db-debug") << "Operator kind is " << ok << std::endl;
- if( children.size()==1 && ok==kind::UNDEFINED_KIND ){
- ret = children[0];
- }else{
- ret = NodeManager::currentNM()->mkNode( ok, children );
- }
- }
- Trace("sygus-db-debug") << "...returning " << ret << std::endl;
- return ret;
-}
-
-Node TermDbSygus::mkGeneric(const Datatype& dt, int c, std::map<int, Node>& pre)
-{
- std::map<TypeNode, int> var_count;
- return mkGeneric(dt, c, var_count, pre);
-}
-
-Node TermDbSygus::sygusToBuiltin( Node n, TypeNode tn ) {
- Assert( n.getType()==tn );
- Assert( tn.isDatatype() );
- std::map< Node, Node >::iterator it = d_sygus_to_builtin[tn].find( n );
- if( it==d_sygus_to_builtin[tn].end() ){
- Trace("sygus-db-debug") << "SygusToBuiltin : compute for " << n << ", type = " << tn << std::endl;
- const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
- if( n.getKind()==APPLY_CONSTRUCTOR ){
- unsigned i = Datatype::indexOf( n.getOperator().toExpr() );
- Assert( n.getNumChildren()==dt[i].getNumArgs() );
- std::map< TypeNode, int > var_count;
- std::map< int, Node > pre;
- for (unsigned j = 0, size = n.getNumChildren(); j < size; j++)
- {
- pre[j] = sygusToBuiltin( n[j], getArgType( dt[i], j ) );
- }
- Node ret = mkGeneric(dt, i, var_count, pre);
- Trace("sygus-db-debug") << "SygusToBuiltin : Generic is " << ret << std::endl;
- d_sygus_to_builtin[tn][n] = ret;
- return ret;
- }
- if (n.hasAttribute(SygusPrintProxyAttribute()))
- {
- // this variable was associated by an attribute to a builtin node
- return n.getAttribute(SygusPrintProxyAttribute());
- }
- Assert(isFreeVar(n));
- // map to builtin variable type
- int fv_num = getVarNum(n);
- Assert(!dt.getSygusType().isNull());
- TypeNode vtn = TypeNode::fromType(dt.getSygusType());
- Node ret = getFreeVar(vtn, fv_num);
- return ret;
- }else{
- return it->second;
- }
-}
-
-Node TermDbSygus::sygusSubstituted( TypeNode tn, Node n, std::vector< Node >& args ) {
- Assert( d_var_list[tn].size()==args.size() );
- return n.substitute( d_var_list[tn].begin(), d_var_list[tn].end(), args.begin(), args.end() );
-}
-
-unsigned TermDbSygus::getSygusTermSize( Node n ){
- if( n.getNumChildren()==0 ){
- return 0;
- }else{
- Assert(n.getKind() == APPLY_CONSTRUCTOR);
- unsigned sum = 0;
- for( unsigned i=0; i<n.getNumChildren(); i++ ){
- sum += getSygusTermSize( n[i] );
- }
- const Datatype& dt = Datatype::datatypeOf(n.getOperator().toExpr());
- int cindex = Datatype::indexOf(n.getOperator().toExpr());
- Assert(cindex >= 0 && cindex < (int)dt.getNumConstructors());
- unsigned weight = dt[cindex].getWeight();
- return weight + sum;
- }
-}
-
-class ReqTrie {
-public:
- ReqTrie() : d_req_kind( UNDEFINED_KIND ){}
- std::map< unsigned, ReqTrie > d_children;
- Kind d_req_kind;
- TypeNode d_req_type;
- Node d_req_const;
- void print( const char * c, int indent = 0 ){
- if( d_req_kind!=UNDEFINED_KIND ){
- Trace(c) << d_req_kind << " ";
- }else if( !d_req_type.isNull() ){
- Trace(c) << d_req_type;
- }else if( !d_req_const.isNull() ){
- Trace(c) << d_req_const;
- }else{
- Trace(c) << "_";
- }
- Trace(c) << std::endl;
- for( std::map< unsigned, ReqTrie >::iterator it = d_children.begin(); it != d_children.end(); ++it ){
- for( int i=0; i<=indent; i++ ) { Trace(c) << " "; }
- Trace(c) << it->first << " : ";
- it->second.print( c, indent+1 );
- }
- }
- bool satisfiedBy( quantifiers::TermDbSygus * tdb, TypeNode tn ){
- if( !d_req_const.isNull() ){
- if( !tdb->hasConst( tn, d_req_const ) ){
- return false;
- }
- }
- if( !d_req_type.isNull() ){
- if( tn!=d_req_type ){
- return false;
- }
- }
- if( d_req_kind!=UNDEFINED_KIND ){
- int c = tdb->getKindConsNum( tn, d_req_kind );
- if( c!=-1 ){
- bool ret = true;
- const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
- for( std::map< unsigned, ReqTrie >::iterator it = d_children.begin(); it != d_children.end(); ++it ){
- if( it->first<dt[c].getNumArgs() ){
- TypeNode tnc = tdb->getArgType( dt[c], it->first );
- if( !it->second.satisfiedBy( tdb, tnc ) ){
- ret = false;
- break;
- }
- }else{
- ret = false;
- break;
- }
- }
- if( !ret ){
- return false;
- }
- // TODO : commutative operators try both?
- }else{
- return false;
- }
- }
- return true;
- }
- bool empty() {
- return d_req_kind==UNDEFINED_KIND && d_req_const.isNull() && d_req_type.isNull();
- }
-};
-
-//this function gets all easy redundant cases, before consulting rewriters
-bool TermDbSygus::considerArgKind( TypeNode tn, TypeNode tnp, Kind k, Kind pk, int arg ) {
- const Datatype& pdt = ((DatatypeType)(tnp).toType()).getDatatype();
- const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
- Assert( hasKind( tn, k ) );
- Assert( hasKind( tnp, pk ) );
- Trace("sygus-sb-debug") << "Consider sygus arg kind " << k << ", pk = " << pk << ", arg = " << arg << "?" << std::endl;
- int c = getKindConsNum( tn, k );
- int pc = getKindConsNum( tnp, pk );
- if( k==pk ){
- //check for associativity
- if( quantifiers::TermUtil::isAssoc( k ) ){
- //if the operator is associative, then a repeated occurrence should only occur in the leftmost argument position
- int firstArg = getFirstArgOccurrence( pdt[pc], tn );
- Assert( firstArg!=-1 );
- if( arg!=firstArg ){
- Trace("sygus-sb-simple") << " sb-simple : do not consider " << k << " at child arg " << arg << " of " << k << " since it is associative, with first arg = " << firstArg << std::endl;
- return false;
- }else{
- return true;
- }
- }
- }
- //describes the shape of an alternate term to construct
- // we check whether this term is in the sygus grammar below
- ReqTrie rt;
- Assert( rt.empty() );
-
- //construct rt by cases
- if( pk==NOT || pk==BITVECTOR_NOT || pk==UMINUS || pk==BITVECTOR_NEG ){
- //negation normal form
- if( pk==k ){
- rt.d_req_type = getArgType( dt[c], 0 );
- }else{
- Kind reqk = UNDEFINED_KIND; //required kind for all children
- std::map< unsigned, Kind > reqkc; //required kind for some children
- if( pk==NOT ){
- if( k==AND ) {
- rt.d_req_kind = OR;reqk = NOT;
- }else if( k==OR ){
- rt.d_req_kind = AND;reqk = NOT;
- //AJR : eliminate this if we eliminate xor
- }else if( k==EQUAL ) {
- rt.d_req_kind = XOR;
- }else if( k==XOR ) {
- rt.d_req_kind = EQUAL;
- }else if( k==ITE ){
- rt.d_req_kind = ITE;reqkc[1] = NOT;reqkc[2] = NOT;
- rt.d_children[0].d_req_type = getArgType( dt[c], 0 );
- }else if( k==LEQ || k==GT ){
- // (not (~ x y)) -----> (~ (+ y 1) x)
- rt.d_req_kind = k;
- rt.d_children[0].d_req_kind = PLUS;
- rt.d_children[0].d_children[0].d_req_type = getArgType( dt[c], 1 );
- rt.d_children[0].d_children[1].d_req_const = NodeManager::currentNM()->mkConst( Rational( 1 ) );
- rt.d_children[1].d_req_type = getArgType( dt[c], 0 );
- //TODO: other possibilities?
- }else if( k==LT || k==GEQ ){
- // (not (~ x y)) -----> (~ y (+ x 1))
- rt.d_req_kind = k;
- rt.d_children[0].d_req_type = getArgType( dt[c], 1 );
- rt.d_children[1].d_req_kind = PLUS;
- rt.d_children[1].d_children[0].d_req_type = getArgType( dt[c], 0 );
- rt.d_children[1].d_children[1].d_req_const = NodeManager::currentNM()->mkConst( Rational( 1 ) );
- }
- }else if( pk==BITVECTOR_NOT ){
- if( k==BITVECTOR_AND ) {
- rt.d_req_kind = BITVECTOR_OR;reqk = BITVECTOR_NOT;
- }else if( k==BITVECTOR_OR ){
- rt.d_req_kind = BITVECTOR_AND;reqk = BITVECTOR_NOT;
- }else if( k==BITVECTOR_XNOR ) {
- rt.d_req_kind = BITVECTOR_XOR;
- }else if( k==BITVECTOR_XOR ) {
- rt.d_req_kind = BITVECTOR_XNOR;
- }
- }else if( pk==UMINUS ){
- if( k==PLUS ){
- rt.d_req_kind = PLUS;reqk = UMINUS;
- }
- }else if( pk==BITVECTOR_NEG ){
- if( k==PLUS ){
- rt.d_req_kind = PLUS;reqk = BITVECTOR_NEG;
- }
- }
- if( !rt.empty() && ( reqk!=UNDEFINED_KIND || !reqkc.empty() ) ){
- int pcr = getKindConsNum( tnp, rt.d_req_kind );
- if( pcr!=-1 ){
- Assert( pcr<(int)pdt.getNumConstructors() );
- //must have same number of arguments
- if( pdt[pcr].getNumArgs()==dt[c].getNumArgs() ){
- for( unsigned i=0; i<pdt[pcr].getNumArgs(); i++ ){
- Kind rk = reqk;
- if( reqk==UNDEFINED_KIND ){
- std::map< unsigned, Kind >::iterator itr = reqkc.find( i );
- if( itr!=reqkc.end() ){
- rk = itr->second;
- }
- }
- if( rk!=UNDEFINED_KIND ){
- rt.d_children[i].d_req_kind = rk;
- rt.d_children[i].d_children[0].d_req_type = getArgType( dt[c], i );
- }
- }
- }
- }
- }
- }
- }else if( k==MINUS || k==BITVECTOR_SUB ){
- if( pk==EQUAL ||
- pk==MINUS || pk==BITVECTOR_SUB ||
- pk==LEQ || pk==LT || pk==GEQ || pk==GT ){
- int oarg = arg==0 ? 1 : 0;
- // (~ x (- y z)) ----> (~ (+ x z) y)
- // (~ (- y z) x) ----> (~ y (+ x z))
- rt.d_req_kind = pk;
- rt.d_children[arg].d_req_type = getArgType( dt[c], 0 );
- rt.d_children[oarg].d_req_kind = k==MINUS ? PLUS : BITVECTOR_PLUS;
- rt.d_children[oarg].d_children[0].d_req_type = getArgType( pdt[pc], oarg );
- rt.d_children[oarg].d_children[1].d_req_type = getArgType( dt[c], 1 );
- }else if( pk==PLUS || pk==BITVECTOR_PLUS ){
- // (+ x (- y z)) -----> (- (+ x y) z)
- // (+ (- y z) x) -----> (- (+ x y) z)
- rt.d_req_kind = pk==PLUS ? MINUS : BITVECTOR_SUB;
- int oarg = arg==0 ? 1 : 0;
- rt.d_children[0].d_req_kind = pk;
- rt.d_children[0].d_children[0].d_req_type = getArgType( pdt[pc], oarg );
- rt.d_children[0].d_children[1].d_req_type = getArgType( dt[c], 0 );
- rt.d_children[1].d_req_type = getArgType( dt[c], 1 );
- // TODO : this is subsumbed by solving for MINUS
- }
- }else if( k==ITE ){
- if( pk!=ITE ){
- // (o X (ite y z w) X') -----> (ite y (o X z X') (o X w X'))
- rt.d_req_kind = ITE;
- rt.d_children[0].d_req_type = getArgType( dt[c], 0 );
- unsigned n_args = pdt[pc].getNumArgs();
- for( unsigned r=1; r<=2; r++ ){
- rt.d_children[r].d_req_kind = pk;
- for( unsigned q=0; q<n_args; q++ ){
- if( (int)q==arg ){
- rt.d_children[r].d_children[q].d_req_type = getArgType( dt[c], r );
- }else{
- rt.d_children[r].d_children[q].d_req_type = getArgType( pdt[pc], q );
- }
- }
- }
- //TODO: this increases term size but is probably a good idea
- }
- }else if( k==NOT ){
- if( pk==ITE ){
- // (ite (not y) z w) -----> (ite y w z)
- rt.d_req_kind = ITE;
- rt.d_children[0].d_req_type = getArgType( dt[c], 0 );
- rt.d_children[1].d_req_type = getArgType( pdt[pc], 2 );
- rt.d_children[2].d_req_type = getArgType( pdt[pc], 1 );
- }
- }
- Trace("sygus-sb-debug") << "Consider sygus arg kind " << k << ", pk = " << pk << ", arg = " << arg << "?" << std::endl;
- if( !rt.empty() ){
- rt.print("sygus-sb-debug");
- //check if it meets the requirements
- if( rt.satisfiedBy( this, tnp ) ){
- Trace("sygus-sb-debug") << "...success!" << std::endl;
- Trace("sygus-sb-simple") << " sb-simple : do not consider " << k << " as arg " << arg << " of " << pk << std::endl;
- //do not need to consider the kind in the search since there are ways to construct equivalent terms
- return false;
- }else{
- Trace("sygus-sb-debug") << "...failed." << std::endl;
- }
- Trace("sygus-sb-debug") << std::endl;
- }
- //must consider this kind in the search
- return true;
-}
-
-bool TermDbSygus::considerConst( TypeNode tn, TypeNode tnp, Node c, Kind pk, int arg ) {
- const Datatype& pdt = ((DatatypeType)(tnp).toType()).getDatatype();
- // child grammar-independent
- if( !considerConst( pdt, tnp, c, pk, arg ) ){
- return false;
- }
- // TODO : this can probably be made child grammar independent
- int pc = getKindConsNum( tnp, pk );
- if( pdt[pc].getNumArgs()==2 ){
- Kind ok;
- int offset;
- if (d_quantEngine->getTermUtil()->hasOffsetArg(pk, arg, offset, ok))
- {
- Trace("sygus-sb-simple-debug") << pk << " has offset arg " << ok << " " << offset << std::endl;
- int ok_arg = getKindConsNum( tnp, ok );
- if( ok_arg!=-1 ){
- Trace("sygus-sb-simple-debug") << "...at argument " << ok_arg << std::endl;
- //other operator be the same type
- if( isTypeMatch( pdt[ok_arg], pdt[arg] ) ){
- int status;
- Node co = d_quantEngine->getTermUtil()->getTypeValueOffset(
- c.getType(), c, offset, status);
- Trace("sygus-sb-simple-debug") << c << " with offset " << offset << " is " << co << ", status=" << status << std::endl;
- if( status==0 && !co.isNull() ){
- if( hasConst( tn, co ) ){
- Trace("sygus-sb-simple") << " sb-simple : by offset reasoning, do not consider const " << c;
- Trace("sygus-sb-simple") << " as arg " << arg << " of " << pk << " since we can use " << co << " under " << ok << " " << std::endl;
- return false;
- }
- }
- }
- }
- }
- }
- return true;
-}
-
-bool TermDbSygus::considerConst( const Datatype& pdt, TypeNode tnp, Node c, Kind pk, int arg ) {
- Assert( hasKind( tnp, pk ) );
- int pc = getKindConsNum( tnp, pk );
- bool ret = true;
- Trace("sygus-sb-debug") << "Consider sygus const " << c << ", parent = " << pk << ", arg = " << arg << "?" << std::endl;
- if (d_quantEngine->getTermUtil()->isIdempotentArg(c, pk, arg))
- {
- if( pdt[pc].getNumArgs()==2 ){
- int oarg = arg==0 ? 1 : 0;
- TypeNode otn = TypeNode::fromType( ((SelectorType)pdt[pc][oarg].getType()).getRangeType() );
- if( otn==tnp ){
- Trace("sygus-sb-simple") << " sb-simple : " << c << " is idempotent arg " << arg << " of " << pk << "..." << std::endl;
- ret = false;
- }
- }
- }else{
- Node sc = d_quantEngine->getTermUtil()->isSingularArg(c, pk, arg);
- if( !sc.isNull() ){
- if( hasConst( tnp, sc ) ){
- Trace("sygus-sb-simple") << " sb-simple : " << c << " is singular arg " << arg << " of " << pk << ", evaluating to " << sc << "..." << std::endl;
- ret = false;
- }
- }
- }
- if( ret ){
- ReqTrie rt;
- Assert( rt.empty() );
- Node max_c = d_quantEngine->getTermUtil()->getTypeMaxValue(c.getType());
- Node zero_c = d_quantEngine->getTermUtil()->getTypeValue(c.getType(), 0);
- Node one_c = d_quantEngine->getTermUtil()->getTypeValue(c.getType(), 1);
- if( pk==XOR || pk==BITVECTOR_XOR ){
- if( c==max_c ){
- rt.d_req_kind = pk==XOR ? NOT : BITVECTOR_NOT;
- }
- }else if( pk==ITE ){
- if( arg==0 ){
- if( c==max_c ){
- rt.d_children[2].d_req_type = tnp;
- }else if( c==zero_c ){
- rt.d_children[1].d_req_type = tnp;
- }
- }
- }else if( pk==STRING_SUBSTR ){
- if( c==one_c ){
- rt.d_req_kind = STRING_CHARAT;
- rt.d_children[0].d_req_type = getArgType( pdt[pc], 0 );
- rt.d_children[1].d_req_type = getArgType( pdt[pc], 1 );
- }
- }
- if( !rt.empty() ){
- //check if satisfied
- if( rt.satisfiedBy( this, tnp ) ){
- Trace("sygus-sb-simple") << " sb-simple : do not consider const " << c << " as arg " << arg << " of " << pk;
- Trace("sygus-sb-simple") << " in " << ((DatatypeType)tnp.toType()).getDatatype().getName() << std::endl;
- //do not need to consider the constant in the search since there are ways to construct equivalent terms
- ret = false;
- }
- }
- }
- // TODO : cache?
- return ret;
-}
-
-int TermDbSygus::solveForArgument( TypeNode tn, unsigned cindex, unsigned arg ) {
- // FIXME
- return -1; // TODO : if using, modify considerArgKind above
- Assert( isRegistered( tn ) );
- const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
- Assert( cindex<dt.getNumConstructors() );
- Assert( arg<dt[cindex].getNumArgs() );
- Kind nk = getConsNumKind( tn, cindex );
- TypeNode tnc = getArgType( dt[cindex], arg );
- const Datatype& cdt = ((DatatypeType)(tnc).toType()).getDatatype();
-
- ReqTrie rt;
- Assert( rt.empty() );
- int solve_ret = -1;
- if( nk==MINUS || nk==BITVECTOR_SUB ){
- if( dt[cindex].getNumArgs()==2 && arg==0 ){
- TypeNode tnco = getArgType( dt[cindex], 1 );
- Node builtin = d_quantEngine->getTermUtil()->getTypeValue(
- sygusToBuiltinType(tnc), 0);
- solve_ret = getConstConsNum( tn, builtin );
- if( solve_ret!=-1 ){
- // t - s -----> ( 0 - s ) + t
- rt.d_req_kind = nk == MINUS ? PLUS : BITVECTOR_PLUS;
- rt.d_children[0].d_req_type = tn; // avoid?
- rt.d_children[0].d_req_kind = nk;
- rt.d_children[0].d_children[0].d_req_const = builtin;
- rt.d_children[0].d_children[0].d_req_type = tnco;
- rt.d_children[1].d_req_type = tnc;
- // TODO : this can be made more general for multiple type grammars to remove MINUS entirely
- }
- }
- }
-
- if( !rt.empty() ){
- Assert( solve_ret>=0 );
- Assert( solve_ret<=(int)cdt.getNumConstructors() );
- //check if satisfied
- if( rt.satisfiedBy( this, tn ) ){
- Trace("sygus-sb-simple") << " sb-simple : ONLY consider " << cdt[solve_ret].getSygusOp() << " as arg " << arg << " of " << nk;
- Trace("sygus-sb-simple") << " in " << ((DatatypeType)tn.toType()).getDatatype().getName() << std::endl;
- return solve_ret;
- }
- }
-
- return -1;
-}
-
-void TermDbSygus::registerSygusType( TypeNode tn ) {
- std::map< TypeNode, TypeNode >::iterator itr = d_register.find( tn );
- if( itr==d_register.end() ){
- d_register[tn] = TypeNode::null();
- if( tn.isDatatype() ){
- const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
- Trace("sygus-db") << "Register type " << dt.getName() << "..." << std::endl;
- TypeNode btn = TypeNode::fromType( dt.getSygusType() );
- d_register[tn] = btn;
- if( !d_register[tn].isNull() ){
- // get the sygus variable list
- Node var_list = Node::fromExpr( dt.getSygusVarList() );
- if( !var_list.isNull() ){
- for( unsigned j=0; j<var_list.getNumChildren(); j++ ){
- Node sv = var_list[j];
- SygusVarNumAttribute svna;
- sv.setAttribute( svna, j );
- d_var_list[tn].push_back( sv );
- }
- }else{
- // no arguments to synthesis functions
- }
- //iterate over constructors
- for( unsigned i=0; i<dt.getNumConstructors(); i++ ){
- Expr sop = dt[i].getSygusOp();
- Assert( !sop.isNull() );
- Node n = Node::fromExpr( sop );
- Trace("sygus-db") << " Operator #" << i << " : " << sop;
- if( sop.getKind() == kind::BUILTIN ){
- Kind sk = NodeManager::operatorToKind( n );
- Trace("sygus-db") << ", kind = " << sk;
- d_kinds[tn][sk] = i;
- d_arg_kind[tn][i] = sk;
- }else if( sop.isConst() ){
- Trace("sygus-db") << ", constant";
- d_consts[tn][n] = i;
- d_arg_const[tn][i] = n;
- }
- d_ops[tn][n] = i;
- d_arg_ops[tn][i] = n;
- Trace("sygus-db") << std::endl;
- }
- //register connected types
- for( unsigned i=0; i<dt.getNumConstructors(); i++ ){
- for( unsigned j=0; j<dt[i].getNumArgs(); j++ ){
- registerSygusType( getArgType( dt[i], j ) );
- }
- }
- }
- }
- }
-}
-
-void TermDbSygus::registerEnumerator(Node e,
- Node f,
- CegConjecture* conj,
- bool mkActiveGuard)
-{
- Assert(d_enum_to_conjecture.find(e) == d_enum_to_conjecture.end());
- Trace("sygus-db") << "Register measured term : " << e << std::endl;
- d_enum_to_conjecture[e] = conj;
- d_enum_to_synth_fun[e] = f;
- if( mkActiveGuard ){
- // make the guard
- Node eg = Rewriter::rewrite( NodeManager::currentNM()->mkSkolem( "eG", NodeManager::currentNM()->booleanType() ) );
- eg = d_quantEngine->getValuation().ensureLiteral( eg );
- AlwaysAssert( !eg.isNull() );
- d_quantEngine->getOutputChannel().requirePhase( eg, true );
- //add immediate lemma
- Node lem = NodeManager::currentNM()->mkNode( OR, eg, eg.negate() );
- Trace("cegqi-lemma") << "Cegqi::Lemma : enumerator : " << lem << std::endl;
- d_quantEngine->getOutputChannel().lemma( lem );
- d_enum_to_active_guard[e] = eg;
- }
-}
-
-bool TermDbSygus::isEnumerator(Node e) const
-{
- return d_enum_to_conjecture.find(e) != d_enum_to_conjecture.end();
-}
-
-CegConjecture* TermDbSygus::getConjectureForEnumerator(Node e)
-{
- std::map<Node, CegConjecture*>::iterator itm = d_enum_to_conjecture.find(e);
- if (itm != d_enum_to_conjecture.end()) {
- return itm->second;
- }else{
- return NULL;
- }
-}
-
-Node TermDbSygus::getSynthFunForEnumerator(Node e)
-{
- std::map<Node, Node>::iterator itsf = d_enum_to_synth_fun.find(e);
- if (itsf != d_enum_to_synth_fun.end())
- {
- return itsf->second;
- }
- else
- {
- return Node::null();
- }
-}
-
-Node TermDbSygus::getActiveGuardForEnumerator(Node e)
-{
- std::map<Node, Node>::iterator itag = d_enum_to_active_guard.find(e);
- if (itag != d_enum_to_active_guard.end()) {
- return itag->second;
- }else{
- return Node::null();
- }
-}
-
-void TermDbSygus::getEnumerators(std::vector<Node>& mts)
-{
- for (std::map<Node, CegConjecture*>::iterator itm =
- d_enum_to_conjecture.begin();
- itm != d_enum_to_conjecture.end(); ++itm) {
- mts.push_back( itm->first );
- }
-}
-
-bool TermDbSygus::isRegistered( TypeNode tn ) {
- return d_register.find( tn )!=d_register.end();
-}
-
-TypeNode TermDbSygus::sygusToBuiltinType( TypeNode tn ) {
- Assert( isRegistered( tn ) );
- return d_register[tn];
-}
-
-void TermDbSygus::computeMinTypeDepthInternal( TypeNode root_tn, TypeNode tn, unsigned type_depth ) {
- std::map< TypeNode, unsigned >::iterator it = d_min_type_depth[root_tn].find( tn );
- if( it==d_min_type_depth[root_tn].end() || type_depth<it->second ){
- d_min_type_depth[root_tn][tn] = type_depth;
- Assert( tn.isDatatype() );
- const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
- //compute for connected types
- for( unsigned i=0; i<dt.getNumConstructors(); i++ ){
- for( unsigned j=0; j<dt[i].getNumArgs(); j++ ){
- computeMinTypeDepthInternal( root_tn, getArgType( dt[i], j ), type_depth+1 );
- }
- }
- }
-}
-
-unsigned TermDbSygus::getMinTypeDepth( TypeNode root_tn, TypeNode tn ){
- std::map< TypeNode, unsigned >::iterator it = d_min_type_depth[root_tn].find( tn );
- if( it==d_min_type_depth[root_tn].end() ){
- computeMinTypeDepthInternal( root_tn, root_tn, 0 );
- Assert( d_min_type_depth[root_tn].find( tn )!=d_min_type_depth[root_tn].end() );
- return d_min_type_depth[root_tn][tn];
- }else{
- return it->second;
- }
-}
-
-unsigned TermDbSygus::getMinTermSize( TypeNode tn ) {
- Assert( isRegistered( tn ) );
- std::map< TypeNode, unsigned >::iterator it = d_min_term_size.find( tn );
- if( it==d_min_term_size.end() ){
- const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
- for( unsigned i=0; i<dt.getNumConstructors(); i++ ){
- if (dt[i].getNumArgs() == 0)
- {
- d_min_term_size[tn] = 0;
- return 0;
- }
- }
- // TODO : improve
- d_min_term_size[tn] = 1;
- return 1;
- }else{
- return it->second;
- }
-}
-
-unsigned TermDbSygus::getMinConsTermSize( TypeNode tn, unsigned cindex ) {
- Assert( isRegistered( tn ) );
- std::map< unsigned, unsigned >::iterator it = d_min_cons_term_size[tn].find( cindex );
- if( it==d_min_cons_term_size[tn].end() ){
- const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
- Assert( cindex<dt.getNumConstructors() );
- unsigned ret = 0;
- if( dt[cindex].getNumArgs()>0 ){
- ret = 1;
- for( unsigned i=0; i<dt[cindex].getNumArgs(); i++ ){
- ret += getMinTermSize( getArgType( dt[cindex], i ) );
- }
- }
- d_min_cons_term_size[tn][cindex] = ret;
- return ret;
- }else{
- return it->second;
- }
-}
-
-unsigned TermDbSygus::getSelectorWeight(TypeNode tn, Node sel)
-{
- std::map<TypeNode, std::map<Node, unsigned> >::iterator itsw =
- d_sel_weight.find(tn);
- if (itsw == d_sel_weight.end())
- {
- d_sel_weight[tn].clear();
- itsw = d_sel_weight.find(tn);
- Type t = tn.toType();
- const Datatype& dt = static_cast<DatatypeType>(t).getDatatype();
- Trace("sygus-db") << "Compute selector weights for " << dt.getName()
- << std::endl;
- for (unsigned i = 0, size = dt.getNumConstructors(); i < size; i++)
- {
- unsigned cw = dt[i].getWeight();
- for (unsigned j = 0, size2 = dt[i].getNumArgs(); j < size2; j++)
- {
- Node csel = Node::fromExpr(dt[i].getSelectorInternal(t, j));
- std::map<Node, unsigned>::iterator its = itsw->second.find(csel);
- if (its == itsw->second.end() || cw < its->second)
- {
- d_sel_weight[tn][csel] = cw;
- Trace("sygus-db") << " w(" << csel << ") <= " << cw << std::endl;
- }
- }
- }
- }
- Assert(itsw->second.find(sel) != itsw->second.end());
- return itsw->second[sel];
-}
-
-int TermDbSygus::getKindConsNum( TypeNode tn, Kind k ) {
- Assert( isRegistered( tn ) );
- std::map< TypeNode, std::map< Kind, int > >::iterator itt = d_kinds.find( tn );
- if( itt!=d_kinds.end() ){
- std::map< Kind, int >::iterator it = itt->second.find( k );
- if( it!=itt->second.end() ){
- return it->second;
- }
- }
- return -1;
-}
-
-int TermDbSygus::getConstConsNum( TypeNode tn, Node n ){
- Assert( isRegistered( tn ) );
- std::map< TypeNode, std::map< Node, int > >::iterator itt = d_consts.find( tn );
- if( itt!=d_consts.end() ){
- std::map< Node, int >::iterator it = itt->second.find( n );
- if( it!=itt->second.end() ){
- return it->second;
- }
- }
- return -1;
-}
-
-int TermDbSygus::getOpConsNum( TypeNode tn, Node n ) {
- std::map< Node, int >::iterator it = d_ops[tn].find( n );
- if( it!=d_ops[tn].end() ){
- return it->second;
- }else{
- return -1;
- }
-}
-
-bool TermDbSygus::hasKind( TypeNode tn, Kind k ) {
- return getKindConsNum( tn, k )!=-1;
-}
-bool TermDbSygus::hasConst( TypeNode tn, Node n ) {
- return getConstConsNum( tn, n )!=-1;
-}
-bool TermDbSygus::hasOp( TypeNode tn, Node n ) {
- return getOpConsNum( tn, n )!=-1;
-}
-
-Node TermDbSygus::getConsNumOp( TypeNode tn, int i ) {
- Assert( isRegistered( tn ) );
- std::map< TypeNode, std::map< int, Node > >::iterator itt = d_arg_ops.find( tn );
- if( itt!=d_arg_ops.end() ){
- std::map< int, Node >::iterator itn = itt->second.find( i );
- if( itn!=itt->second.end() ){
- return itn->second;
- }
- }
- return Node::null();
-}
-
-Node TermDbSygus::getConsNumConst( TypeNode tn, int i ) {
- Assert( isRegistered( tn ) );
- std::map< TypeNode, std::map< int, Node > >::iterator itt = d_arg_const.find( tn );
- if( itt!=d_arg_const.end() ){
- std::map< int, Node >::iterator itn = itt->second.find( i );
- if( itn!=itt->second.end() ){
- return itn->second;
- }
- }
- return Node::null();
-}
-
-Kind TermDbSygus::getConsNumKind( TypeNode tn, int i ) {
- Assert( isRegistered( tn ) );
- std::map< TypeNode, std::map< int, Kind > >::iterator itt = d_arg_kind.find( tn );
- if( itt!=d_arg_kind.end() ){
- std::map< int, Kind >::iterator itk = itt->second.find( i );
- if( itk!=itt->second.end() ){
- return itk->second;
- }
- }
- return UNDEFINED_KIND;
-}
-
-bool TermDbSygus::isKindArg( TypeNode tn, int i ) {
- return getConsNumKind( tn, i )!=UNDEFINED_KIND;
-}
-
-bool TermDbSygus::isConstArg( TypeNode tn, int i ) {
- Assert( isRegistered( tn ) );
- std::map< TypeNode, std::map< int, Node > >::iterator itt = d_arg_const.find( tn );
- if( itt!=d_arg_const.end() ){
- return itt->second.find( i )!=itt->second.end();
- }else{
- return false;
- }
-}
-
-TypeNode TermDbSygus::getArgType(const DatatypeConstructor& c, unsigned i)
-{
- Assert(i < c.getNumArgs());
- return TypeNode::fromType( ((SelectorType)c[i].getType()).getRangeType() );
-}
-
-/** get first occurrence */
-int TermDbSygus::getFirstArgOccurrence( const DatatypeConstructor& c, TypeNode tn ) {
- for( unsigned i=0; i<c.getNumArgs(); i++ ){
- TypeNode tni = getArgType( c, i );
- if( tni==tn ){
- return i;
- }
- }
- return -1;
-}
-
-bool TermDbSygus::isTypeMatch( const DatatypeConstructor& c1, const DatatypeConstructor& c2 ) {
- if( c1.getNumArgs()!=c2.getNumArgs() ){
- return false;
- }else{
- for( unsigned i=0; i<c1.getNumArgs(); i++ ){
- if( getArgType( c1, i )!=getArgType( c2, i ) ){
- return false;
- }
- }
- return true;
- }
-}
-
-Node TermDbSygus::minimizeBuiltinTerm( Node n ) {
- if( ( n.getKind()==EQUAL || n.getKind()==LEQ || n.getKind()==LT || n.getKind()==GEQ || n.getKind()==GT ) &&
- ( n[0].getType().isInteger() || n[0].getType().isReal() ) ){
- bool changed = false;
- std::vector< Node > mon[2];
- for( unsigned r=0; r<2; r++ ){
- unsigned ro = r==0 ? 1 : 0;
- Node c;
- Node nc;
- if( n[r].getKind()==PLUS ){
- for( unsigned i=0; i<n[r].getNumChildren(); i++ ){
- if (ArithMSum::getMonomial(n[r][i], c, nc)
- && c.getConst<Rational>().isNegativeOne())
- {
- mon[ro].push_back( nc );
- changed = true;
- }else{
- if( !n[r][i].isConst() || !n[r][i].getConst<Rational>().isZero() ){
- mon[r].push_back( n[r][i] );
- }
- }
- }
- }else{
- if (ArithMSum::getMonomial(n[r], c, nc)
- && c.getConst<Rational>().isNegativeOne())
- {
- mon[ro].push_back( nc );
- changed = true;
- }else{
- if( !n[r].isConst() || !n[r].getConst<Rational>().isZero() ){
- mon[r].push_back( n[r] );
- }
- }
- }
- }
- if( changed ){
- Node nn[2];
- for( unsigned r=0; r<2; r++ ){
- nn[r] = mon[r].size()==0 ? NodeManager::currentNM()->mkConst( Rational(0) ) : ( mon[r].size()==1 ? mon[r][0] : NodeManager::currentNM()->mkNode( PLUS, mon[r] ) );
- }
- return NodeManager::currentNM()->mkNode( n.getKind(), nn[0], nn[1] );
- }
- }
- return n;
-}
-
-Node TermDbSygus::expandBuiltinTerm( Node t ){
- if( t.getKind()==EQUAL ){
- if( t[0].getType().isReal() ){
- return NodeManager::currentNM()->mkNode( AND, NodeManager::currentNM()->mkNode( LEQ, t[0], t[1] ),
- NodeManager::currentNM()->mkNode( LEQ, t[1], t[0] ) );
- }else if( t[0].getType().isBoolean() ){
- return NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, t[0], t[1] ),
- NodeManager::currentNM()->mkNode( AND, t[0].negate(), t[1].negate() ) );
- }
- }else if( t.getKind()==ITE && t.getType().isBoolean() ){
- return NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, t[0], t[1] ),
- NodeManager::currentNM()->mkNode( AND, t[0].negate(), t[2] ) );
- }
- return Node::null();
-}
-
-
-Kind TermDbSygus::getComparisonKind( TypeNode tn ) {
- if( tn.isInteger() || tn.isReal() ){
- return LT;
- }else if( tn.isBitVector() ){
- return BITVECTOR_ULT;
- }else{
- return UNDEFINED_KIND;
- }
-}
-
-Kind TermDbSygus::getPlusKind( TypeNode tn, bool is_neg ) {
- if( tn.isInteger() || tn.isReal() ){
- return is_neg ? MINUS : PLUS;
- }else if( tn.isBitVector() ){
- return is_neg ? BITVECTOR_SUB : BITVECTOR_PLUS;
- }else{
- return UNDEFINED_KIND;
- }
-}
-
-Node TermDbSygus::getSemanticSkolem( TypeNode tn, Node n, bool doMk ){
- std::map< Node, Node >::iterator its = d_semantic_skolem[tn].find( n );
- if( its!=d_semantic_skolem[tn].end() ){
- return its->second;
- }else if( doMk ){
- Node ss = NodeManager::currentNM()->mkSkolem( "sem", tn, "semantic skolem for sygus" );
- d_semantic_skolem[tn][n] = ss;
- return ss;
- }else{
- return Node::null();
- }
-}
-
-bool TermDbSygus::involvesDivByZero( Node n, std::map< Node, bool >& visited ){
- if( visited.find( n )==visited.end() ){
- visited[n] = true;
- Kind k = n.getKind();
- if( k==DIVISION || k==DIVISION_TOTAL || k==INTS_DIVISION || k==INTS_DIVISION_TOTAL ||
- k==INTS_MODULUS || k==INTS_MODULUS_TOTAL ){
- if( n[1].isConst() ){
- if (n[1]
- == d_quantEngine->getTermUtil()->getTypeValue(n[1].getType(), 0))
- {
- return true;
- }
- }else{
- // if it has free variables it might be a non-zero constant
- if( !hasFreeVar( n[1] ) ){
- return true;
- }
- }
- }
- for( unsigned i=0; i<n.getNumChildren(); i++ ){
- if( involvesDivByZero( n[i], visited ) ){
- return true;
- }
- }
- }
- return false;
-}
-
-bool TermDbSygus::involvesDivByZero( Node n ) {
- std::map< Node, bool > visited;
- return involvesDivByZero( n, visited );
-}
-
-void doStrReplace(std::string& str, const std::string& oldStr, const std::string& newStr){
- size_t pos = 0;
- while((pos = str.find(oldStr, pos)) != std::string::npos){
- str.replace(pos, oldStr.length(), newStr);
- pos += newStr.length();
- }
-}
-
-Kind TermDbSygus::getOperatorKind( Node op ) {
- Assert( op.getKind()!=BUILTIN );
- if (op.getKind() == LAMBDA)
- {
- // we use APPLY_UF instead of APPLY, since the rewriter for APPLY_UF
- // does beta-reduction but does not for APPLY
- return APPLY_UF;
- }else{
- TypeNode tn = op.getType();
- if( tn.isConstructor() ){
- return APPLY_CONSTRUCTOR;
- }
- else if (tn.isSelector())
- {
- return APPLY_SELECTOR;
- }
- else if (tn.isTester())
- {
- return APPLY_TESTER;
- }
- else if (tn.isFunction())
- {
- return APPLY_UF;
- }
- return NodeManager::operatorToKind(op);
- }
-}
-
-Node TermDbSygus::getAnchor( Node n ) {
- if( n.getKind()==APPLY_SELECTOR_TOTAL ){
- return getAnchor( n[0] );
- }else{
- return n;
- }
-}
-
-unsigned TermDbSygus::getAnchorDepth( Node n ) {
- if( n.getKind()==APPLY_SELECTOR_TOTAL ){
- return 1+getAnchorDepth( n[0] );
- }else{
- return 0;
- }
-}
-
-
-void TermDbSygus::registerEvalTerm( Node n ) {
- if( options::sygusDirectEval() ){
- if( n.getKind()==APPLY_UF && !n.getType().isBoolean() ){
- TypeNode tn = n[0].getType();
- if( tn.isDatatype() ){
- const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
- if( dt.isSygus() ){
- Node f = n.getOperator();
- if( n[0].getKind()!=APPLY_CONSTRUCTOR ){
- if (d_eval_processed.find(n) == d_eval_processed.end())
- {
- Trace("sygus-eager")
- << "TermDbSygus::eager: Register eval term : " << n
- << std::endl;
- d_eval_processed.insert(n);
- d_evals[n[0]].push_back(n);
- TypeNode tn = n[0].getType();
- Assert(tn.isDatatype());
- const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
- Node var_list = Node::fromExpr(dt.getSygusVarList());
- Assert(dt.isSygus());
- d_eval_args[n[0]].push_back(std::vector<Node>());
- bool isConst = true;
- for (unsigned j = 1; j < n.getNumChildren(); j++)
- {
- d_eval_args[n[0]].back().push_back(n[j]);
- if (!n[j].isConst())
- {
- isConst = false;
- }
- }
- d_eval_args_const[n[0]].push_back(isConst);
- Node a = getAnchor(n[0]);
- d_subterms[a][n[0]] = true;
- }
- }
- }
- }
- }
- }
-}
-
-void TermDbSygus::registerModelValue( Node a, Node v, std::vector< Node >& terms, std::vector< Node >& vals, std::vector< Node >& exps ) {
- std::map< Node, std::map< Node, bool > >::iterator its = d_subterms.find( a );
- if( its!=d_subterms.end() ){
- Trace("sygus-eager") << "registerModelValue : " << a << ", has " << its->second.size() << " registered subterms." << std::endl;
- for( std::map< Node, bool >::iterator itss = its->second.begin(); itss != its->second.end(); ++itss ){
- Node n = itss->first;
- Trace("sygus-eager-debug") << "...process : " << n << std::endl;
- std::map< Node, std::vector< std::vector< Node > > >::iterator it = d_eval_args.find( n );
- if( it!=d_eval_args.end() && !it->second.empty() ){
- TNode at = a;
- TNode vt = v;
- Node vn = n.substitute( at, vt );
- vn = Rewriter::rewrite( vn );
- unsigned start = d_node_mv_args_proc[n][vn];
- // get explanation in terms of testers
- std::vector< Node > antec_exp;
- d_syexp->getExplanationForConstantEquality(n, vn, antec_exp);
- Node antec = antec_exp.size()==1 ? antec_exp[0] : NodeManager::currentNM()->mkNode( kind::AND, antec_exp );
- //Node antec = n.eqNode( vn );
- TypeNode tn = n.getType();
- Assert( tn.isDatatype() );
- const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
- Assert( dt.isSygus() );
- Trace("sygus-eager") << "TermDbSygus::eager: Register model value : " << vn << " for " << n << std::endl;
- Trace("sygus-eager") << "...it has " << it->second.size() << " evaluations, already processed " << start << "." << std::endl;
- Node bTerm = sygusToBuiltin( vn, tn );
- Trace("sygus-eager") << "Built-in term : " << bTerm << std::endl;
- std::vector< Node > vars;
- Node var_list = Node::fromExpr( dt.getSygusVarList() );
- for( unsigned j=0; j<var_list.getNumChildren(); j++ ){
- vars.push_back( var_list[j] );
- }
- //evaluation children
- std::vector< Node > eval_children;
- eval_children.push_back( Node::fromExpr( dt.getSygusEvaluationFunc() ) );
- eval_children.push_back( n );
- //for each evaluation
- for( unsigned i=start; i<it->second.size(); i++ ){
- Node res;
- Node expn;
- // unfold?
- bool do_unfold = false;
- if( options::sygusUnfoldBool() ){
- if( bTerm.getKind()==ITE || bTerm.getType().isBoolean() ){
- do_unfold = true;
- }
- }
- if( do_unfold ){
- // TODO : this is replicated for different values, possibly do better caching
- std::map< Node, Node > vtm;
- std::vector< Node > exp;
- vtm[n] = vn;
- eval_children.insert( eval_children.end(), it->second[i].begin(), it->second[i].end() );
- Node eval_fun = NodeManager::currentNM()->mkNode( kind::APPLY_UF, eval_children );
- eval_children.resize( 2 );
- res = unfold( eval_fun, vtm, exp );
- expn = exp.size()==1 ? exp[0] : NodeManager::currentNM()->mkNode( kind::AND, exp );
- }else{
-
- EvalSygusInvarianceTest esit;
- eval_children.insert( eval_children.end(), it->second[i].begin(), it->second[i].end() );
- Node conj =
- NodeManager::currentNM()->mkNode(kind::APPLY_UF, eval_children);
- eval_children[1] = vn;
- Node eval_fun = NodeManager::currentNM()->mkNode( kind::APPLY_UF, eval_children );
- res = evaluateWithUnfolding(eval_fun);
- esit.init(conj, n, res);
- eval_children.resize( 2 );
- eval_children[1] = n;
-
- //evaluate with minimal explanation
- std::vector< Node > mexp;
- d_syexp->getExplanationFor(n, vn, mexp, esit);
- Assert( !mexp.empty() );
- expn = mexp.size()==1 ? mexp[0] : NodeManager::currentNM()->mkNode( kind::AND, mexp );
-
- //if all constant, we can use evaluation to minimize the explanation
- //Assert( i<d_eval_args_const[n].size() );
- //if( d_eval_args_const[n][i] ){
- /*
- std::map< Node, Node > vtm;
- std::map< Node, Node > visited;
- std::map< Node, std::vector< Node > > exp;
- vtm[n] = vn;
- res = crefEvaluate( eval_fun, vtm, visited, exp );
- Assert( !exp[eval_fun].empty() );
- expn = exp[eval_fun].size()==1 ? exp[eval_fun][0] : NodeManager::currentNM()->mkNode( kind::AND, exp[eval_fun] );
- */
- /*
- //otherwise, just do a substitution
- }else{
- Assert( vars.size()==it->second[i].size() );
- res = bTerm.substitute( vars.begin(), vars.end(), it->second[i].begin(), it->second[i].end() );
- res = Rewriter::rewrite( res );
- expn = antec;
- }
- */
- }
- Assert( !res.isNull() );
- terms.push_back( d_evals[n][i] );
- vals.push_back( res );
- exps.push_back( expn );
- Trace("sygus-eager") << "Conclude : " << d_evals[n][i] << " == " << res << ", cref eval = " << d_eval_args_const[n][i] << std::endl;
- Trace("sygus-eager") << " from " << expn << std::endl;
- }
- d_node_mv_args_proc[n][vn] = it->second.size();
- }
- }
- }
-}
-
-Node TermDbSygus::unfold( Node en, std::map< Node, Node >& vtm, std::vector< Node >& exp, bool track_exp ) {
- if( en.getKind()==kind::APPLY_UF ){
- Trace("sygus-db-debug") << "Unfold : " << en << std::endl;
- Node ev = en[0];
- if( track_exp ){
- std::map< Node, Node >::iterator itv = vtm.find( en[0] );
- if( itv!=vtm.end() ){
- ev = itv->second;
- }else{
- Assert( false );
- }
- Assert( en[0].getType()==ev.getType() );
- Assert( ev.isConst() );
- }
- Assert( ev.getKind()==kind::APPLY_CONSTRUCTOR );
- std::vector< Node > args;
- for( unsigned i=1; i<en.getNumChildren(); i++ ){
- args.push_back( en[i] );
- }
- const Datatype& dt = ((DatatypeType)(ev.getType()).toType()).getDatatype();
- unsigned i = Datatype::indexOf( ev.getOperator().toExpr() );
- if( track_exp ){
- //explanation
- Node ee = NodeManager::currentNM()->mkNode( kind::APPLY_TESTER, Node::fromExpr( dt[i].getTester() ), en[0] );
- if( std::find( exp.begin(), exp.end(), ee )==exp.end() ){
- exp.push_back( ee );
- }
- }
- Assert( !dt.isParametric() );
- std::map< int, Node > pre;
- for( unsigned j=0; j<dt[i].getNumArgs(); j++ ){
- std::vector< Node > cc;
- //get the evaluation argument for the selector
- Type rt = dt[i][j].getRangeType();
- const Datatype & ad = ((DatatypeType)dt[i][j].getRangeType()).getDatatype();
- cc.push_back( Node::fromExpr( ad.getSygusEvaluationFunc() ) );
- Node s;
- if( en[0].getKind()==kind::APPLY_CONSTRUCTOR ){
- s = en[0][j];
- }else{
- s = NodeManager::currentNM()->mkNode( kind::APPLY_SELECTOR_TOTAL, dt[i].getSelectorInternal( en[0].getType().toType(), j ), en[0] );
- }
- cc.push_back( s );
- if( track_exp ){
- //update vtm map
- vtm[s] = ev[j];
- }
- cc.insert( cc.end(), args.begin(), args.end() );
- pre[j] = NodeManager::currentNM()->mkNode( kind::APPLY_UF, cc );
- }
- std::map< TypeNode, int > var_count;
- Node ret = mkGeneric( dt, i, var_count, pre );
- // if it is a variable, apply the substitution
- if( ret.getKind()==kind::BOUND_VARIABLE ){
- Assert( ret.hasAttribute(SygusVarNumAttribute()) );
- int i = ret.getAttribute(SygusVarNumAttribute());
- Assert( Node::fromExpr( dt.getSygusVarList() )[i]==ret );
- ret = args[i];
- }
- else
- {
- ret = Rewriter::rewrite(ret);
- }
- return ret;
- }else{
- Assert( en.isConst() );
- }
- return en;
-}
-
-
-Node TermDbSygus::getEagerUnfold( Node n, std::map< Node, Node >& visited ) {
- std::map< Node, Node >::iterator itv = visited.find( n );
- if( itv==visited.end() ){
- Trace("cegqi-eager-debug") << "getEagerUnfold " << n << std::endl;
- Node ret;
- if( n.getKind()==APPLY_UF ){
- TypeNode tn = n[0].getType();
- Trace("cegqi-eager-debug") << "check " << n[0].getType() << std::endl;
- if( tn.isDatatype() ){
- const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
- if( dt.isSygus() ){
- Trace("cegqi-eager") << "Unfold eager : " << n << std::endl;
- Node bTerm = sygusToBuiltin( n[0], tn );
- Trace("cegqi-eager") << "Built-in term : " << bTerm << std::endl;
- std::vector< Node > vars;
- std::vector< Node > subs;
- Node var_list = Node::fromExpr( dt.getSygusVarList() );
- Assert( var_list.getNumChildren()+1==n.getNumChildren() );
- for( unsigned j=0; j<var_list.getNumChildren(); j++ ){
- vars.push_back( var_list[j] );
- }
- for( unsigned j=1; j<n.getNumChildren(); j++ ){
- Node nc = getEagerUnfold( n[j], visited );
- subs.push_back( nc );
- Assert(subs[j - 1].getType().isComparableTo(
- var_list[j - 1].getType()));
- }
- Assert( vars.size()==subs.size() );
- bTerm = bTerm.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() );
- Trace("cegqi-eager") << "Built-in term after subs : " << bTerm << std::endl;
- Trace("cegqi-eager-debug") << "Types : " << bTerm.getType() << " " << n.getType() << std::endl;
- Assert(n.getType().isComparableTo(bTerm.getType()));
- ret = bTerm;
- }
- }
- }
- if( ret.isNull() ){
- if( n.getKind()!=FORALL ){
- bool childChanged = false;
- std::vector< Node > children;
- for( unsigned i=0; i<n.getNumChildren(); i++ ){
- Node nc = getEagerUnfold( n[i], visited );
- childChanged = childChanged || n[i]!=nc;
- children.push_back( nc );
- }
- if( childChanged ){
- if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){
- children.insert( children.begin(), n.getOperator() );
- }
- ret = NodeManager::currentNM()->mkNode( n.getKind(), children );
- }
- }
- if( ret.isNull() ){
- ret = n;
- }
- }
- visited[n] = ret;
- return ret;
- }else{
- return itv->second;
- }
-}
-
-
-Node TermDbSygus::evaluateBuiltin( TypeNode tn, Node bn, std::vector< Node >& args ) {
- if( !args.empty() ){
- std::map< TypeNode, std::vector< Node > >::iterator it = d_var_list.find( tn );
- Assert( it!=d_var_list.end() );
- Assert( it->second.size()==args.size() );
- return Rewriter::rewrite( bn.substitute( it->second.begin(), it->second.end(), args.begin(), args.end() ) );
- }else{
- return Rewriter::rewrite( bn );
- }
-}
-
-Node TermDbSygus::evaluateWithUnfolding(
- Node n, std::unordered_map<Node, Node, NodeHashFunction>& visited)
-{
- std::unordered_map<Node, Node, NodeHashFunction>::iterator it =
- visited.find(n);
- if( it==visited.end() ){
- Node ret = n;
- while( ret.getKind()==APPLY_UF && ret[0].getKind()==APPLY_CONSTRUCTOR ){
- ret = unfold( ret );
- }
- if( ret.getNumChildren()>0 ){
- std::vector< Node > children;
- if( ret.getMetaKind() == kind::metakind::PARAMETERIZED ){
- children.push_back( ret.getOperator() );
- }
- bool childChanged = false;
- for( unsigned i=0; i<ret.getNumChildren(); i++ ){
- Node nc = evaluateWithUnfolding( ret[i], visited );
- childChanged = childChanged || nc!=ret[i];
- children.push_back( nc );
- }
- if( childChanged ){
- ret = NodeManager::currentNM()->mkNode( ret.getKind(), children );
- }
- ret = getExtRewriter()->extendedRewrite(ret);
- }
- visited[n] = ret;
- return ret;
- }else{
- return it->second;
- }
-}
-
-Node TermDbSygus::evaluateWithUnfolding( Node n ) {
- std::unordered_map<Node, Node, NodeHashFunction> visited;
- return evaluateWithUnfolding( n, visited );
-}
-
-}/* CVC4::theory::quantifiers namespace */
-}/* CVC4::theory namespace */
-}/* CVC4 namespace */
-
+++ /dev/null
-/********************* */
-/*! \file term_database_sygus.h
- ** \verbatim
- ** Top contributors (to current version):
- ** Andrew Reynolds
- ** This file is part of the CVC4 project.
- ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
- ** in the top-level source directory) and their institutional affiliations.
- ** All rights reserved. See the file COPYING in the top-level source
- ** directory for licensing information.\endverbatim
- **
- ** \brief term database sygus class
- **/
-
-#include "cvc4_private.h"
-
-#ifndef __CVC4__THEORY__QUANTIFIERS__TERM_DATABASE_SYGUS_H
-#define __CVC4__THEORY__QUANTIFIERS__TERM_DATABASE_SYGUS_H
-
-#include <unordered_set>
-
-#include "theory/quantifiers/extended_rewrite.h"
-#include "theory/quantifiers/sygus_explain.h"
-#include "theory/quantifiers/term_database.h"
-
-namespace CVC4 {
-namespace theory {
-namespace quantifiers {
-
-class CegConjecture;
-
-// TODO :issue #1235 split and document this class
-class TermDbSygus {
- public:
- TermDbSygus(context::Context* c, QuantifiersEngine* qe);
- ~TermDbSygus() {}
- /** Reset this utility */
- bool reset(Theory::Effort e);
- /** Identify this utility */
- std::string identify() const { return "TermDbSygus"; }
- /** register the sygus type */
- void registerSygusType(TypeNode tn);
- /** register a variable e that we will do enumerative search on
- * conj is the conjecture that the enumeration of e is for.
- * f is the synth-fun that the enumeration of e is for.
- * mkActiveGuard is whether we want to make an active guard for e
- * (see d_enum_to_active_guard).
- *
- * Notice that enumerator e may not be equivalent
- * to f in synthesis-through-unification approaches
- * (e.g. decision tree construction for PBE synthesis).
- */
- void registerEnumerator(Node e,
- Node f,
- CegConjecture* conj,
- bool mkActiveGuard = false);
- /** is e an enumerator? */
- bool isEnumerator(Node e) const;
- /** return the conjecture e is associated with */
- CegConjecture* getConjectureForEnumerator(Node e);
- /** return the function-to-synthesize e is associated with */
- Node getSynthFunForEnumerator(Node e);
- /** get active guard for e */
- Node getActiveGuardForEnumerator(Node e);
- /** get all registered enumerators */
- void getEnumerators(std::vector<Node>& mts);
- /** get the explanation utility */
- SygusExplain* getExplain() { return d_syexp.get(); }
- /** get the extended rewrite utility */
- ExtendedRewriter* getExtRewriter() { return d_ext_rw.get(); }
- //-----------------------------conversion from sygus to builtin
- /** get free variable
- *
- * This class caches a list of free variables for each type, which are
- * used, for instance, for constructing canonical forms of terms with free
- * variables. This function returns the i^th free variable for type tn.
- * If useSygusType is true, then this function returns a variable of the
- * analog type for sygus type tn (see d_fv for details).
- */
- TNode getFreeVar(TypeNode tn, int i, bool useSygusType = false);
- /** get free variable and increment
- *
- * This function returns the next free variable for type tn, and increments
- * the counter in var_count for that type.
- */
- TNode getFreeVarInc(TypeNode tn,
- std::map<TypeNode, int>& var_count,
- bool useSygusType = false);
- /** returns true if n is a cached free variable (in d_fv). */
- bool isFreeVar(Node n) { return d_fv_stype.find(n) != d_fv_stype.end(); }
- /** returns the index of n in the free variable cache (d_fv). */
- int getVarNum(Node n) { return d_fv_num[n]; }
- /** returns true if n has a cached free variable (in d_fv). */
- bool hasFreeVar(Node n);
- /** make generic
- *
- * This function returns a builtin term f( t1, ..., tn ) where f is the
- * builtin op of the sygus datatype constructor specified by arguments
- * dt and c. The mapping pre maps child indices to the term for that index
- * in the term we are constructing. That is, for each i = 1,...,n:
- * If i is in the domain of pre, then ti = pre[i].
- * If i is not in the domain of pre, then ti = d_fv[1][ var_count[Ti ] ],
- * and var_count[Ti] is incremented.
- */
- Node mkGeneric(const Datatype& dt,
- unsigned c,
- std::map<TypeNode, int>& var_count,
- std::map<int, Node>& pre);
- /** same as above, but with empty var_count */
- Node mkGeneric(const Datatype& dt, int c, std::map<int, Node>& pre);
- /** sygus to builtin
- *
- * Given a sygus datatype term n of type tn, this function returns its analog,
- * that is, the term that n encodes.
- */
- Node sygusToBuiltin(Node n, TypeNode tn);
- /** same as above, but without tn */
- Node sygusToBuiltin(Node n) { return sygusToBuiltin(n, n.getType()); }
- //-----------------------------end conversion from sygus to builtin
-
- private:
- /** reference to the quantifiers engine */
- QuantifiersEngine* d_quantEngine;
- /** sygus explanation */
- std::unique_ptr<SygusExplain> d_syexp;
- /** sygus explanation */
- std::unique_ptr<ExtendedRewriter> d_ext_rw;
- /** mapping from enumerator terms to the conjecture they are associated with
- */
- std::map<Node, CegConjecture*> d_enum_to_conjecture;
- /** mapping from enumerator terms to the function-to-synthesize they are
- * associated with
- */
- std::map<Node, Node> d_enum_to_synth_fun;
- /** mapping from enumerator terms to the guard they are associated with
- * The guard G for an enumerator e has the semantics
- * if G is true, then there are more values of e to enumerate".
- */
- std::map<Node, Node> d_enum_to_active_guard;
-
- //-----------------------------conversion from sygus to builtin
- /** cache for sygusToBuiltin */
- std::map<TypeNode, std::map<Node, Node> > d_sygus_to_builtin;
- /** a cache of fresh variables for each type
- *
- * We store two versions of this list:
- * index 0: mapping from builtin types to fresh variables of that type,
- * index 1: mapping from sygus types to fresh varaibles of the type they
- * encode.
- */
- std::map<TypeNode, std::vector<Node> > d_fv[2];
- /** Maps free variables to the domain type they are associated with in d_fv */
- std::map<Node, TypeNode> d_fv_stype;
- /** Maps free variables to their index in d_fv. */
- std::map<Node, int> d_fv_num;
- /** recursive helper for hasFreeVar, visited stores nodes we have visited. */
- bool hasFreeVar(Node n, std::map<Node, bool>& visited);
- //-----------------------------end conversion from sygus to builtin
-
- // TODO :issue #1235 : below here needs refactor
-
- public:
- Node d_true;
- Node d_false;
-
-private:
- void computeMinTypeDepthInternal( TypeNode root_tn, TypeNode tn, unsigned type_depth );
- bool involvesDivByZero( Node n, std::map< Node, bool >& visited );
-
- private:
- // information for sygus types
- std::map<TypeNode, TypeNode> d_register; // stores sygus -> builtin type
- std::map<TypeNode, std::vector<Node> > d_var_list;
- std::map<TypeNode, std::map<int, Kind> > d_arg_kind;
- std::map<TypeNode, std::map<Kind, int> > d_kinds;
- std::map<TypeNode, std::map<int, Node> > d_arg_const;
- std::map<TypeNode, std::map<Node, int> > d_consts;
- std::map<TypeNode, std::map<Node, int> > d_ops;
- std::map<TypeNode, std::map<int, Node> > d_arg_ops;
- std::map<TypeNode, std::map<Node, Node> > d_semantic_skolem;
- // grammar information
- // root -> type -> _
- std::map<TypeNode, std::map<TypeNode, unsigned> > d_min_type_depth;
- // std::map< TypeNode, std::map< Node, std::map< std::map< int, bool > > >
- // d_consider_const;
- // type -> cons -> _
- std::map<TypeNode, unsigned> d_min_term_size;
- std::map<TypeNode, std::map<unsigned, unsigned> > d_min_cons_term_size;
- /** a cache for getSelectorWeight */
- std::map<TypeNode, std::map<Node, unsigned> > d_sel_weight;
-
- public: // general sygus utilities
- bool isRegistered( TypeNode tn );
- // get the minimum depth of type in its parent grammar
- unsigned getMinTypeDepth( TypeNode root_tn, TypeNode tn );
- // get the minimum size for a constructor term
- unsigned getMinTermSize( TypeNode tn );
- unsigned getMinConsTermSize( TypeNode tn, unsigned cindex );
- /** get the weight of the selector, where tn is the domain of sel */
- unsigned getSelectorWeight(TypeNode tn, Node sel);
-
- public:
- TypeNode sygusToBuiltinType( TypeNode tn );
- int getKindConsNum( TypeNode tn, Kind k );
- int getConstConsNum( TypeNode tn, Node n );
- int getOpConsNum( TypeNode tn, Node n );
- bool hasKind( TypeNode tn, Kind k );
- bool hasConst( TypeNode tn, Node n );
- bool hasOp( TypeNode tn, Node n );
- Node getConsNumConst( TypeNode tn, int i );
- Node getConsNumOp( TypeNode tn, int i );
- Kind getConsNumKind( TypeNode tn, int i );
- bool isKindArg( TypeNode tn, int i );
- bool isConstArg( TypeNode tn, int i );
- /** get arg type */
- TypeNode getArgType(const DatatypeConstructor& c, unsigned i);
- /** get first occurrence */
- int getFirstArgOccurrence( const DatatypeConstructor& c, TypeNode tn );
- /** is type match */
- bool isTypeMatch( const DatatypeConstructor& c1, const DatatypeConstructor& c2 );
-
- TypeNode getSygusTypeForVar( Node v );
- Node sygusSubstituted( TypeNode tn, Node n, std::vector< Node >& args );
- Node getSygusNormalized( Node n, std::map< TypeNode, int >& var_count, std::map< Node, Node >& subs );
- Node getNormalized(TypeNode t, Node prog);
- unsigned getSygusTermSize( Node n );
- /** given a term, construct an equivalent smaller one that respects syntax */
- Node minimizeBuiltinTerm( Node n );
- /** given a term, expand it into more basic components */
- Node expandBuiltinTerm( Node n );
- /** get comparison kind */
- Kind getComparisonKind( TypeNode tn );
- Kind getPlusKind( TypeNode tn, bool is_neg = false );
- // get semantic skolem for n (a sygus term whose builtin version is n)
- Node getSemanticSkolem( TypeNode tn, Node n, bool doMk = true );
- /** involves div-by-zero */
- bool involvesDivByZero( Node n );
-
- /** get operator kind */
- static Kind getOperatorKind( Node op );
-
- /** get anchor */
- static Node getAnchor( Node n );
- static unsigned getAnchorDepth( Node n );
-
-public: // for symmetry breaking
- bool considerArgKind( TypeNode tn, TypeNode tnp, Kind k, Kind pk, int arg );
- bool considerConst( TypeNode tn, TypeNode tnp, Node c, Kind pk, int arg );
- bool considerConst( const Datatype& pdt, TypeNode tnp, Node c, Kind pk, int arg );
- int solveForArgument( TypeNode tnp, unsigned cindex, unsigned arg );
-
-//for eager instantiation
- // TODO (as part of #1235) move some of these functions to sygus_explain.h
- private:
- /** the set of evaluation terms we have already processed */
- std::unordered_set<Node, NodeHashFunction> d_eval_processed;
- std::map< Node, std::map< Node, bool > > d_subterms;
- std::map< Node, std::vector< Node > > d_evals;
- std::map< Node, std::vector< std::vector< Node > > > d_eval_args;
- std::map< Node, std::vector< bool > > d_eval_args_const;
- std::map< Node, std::map< Node, unsigned > > d_node_mv_args_proc;
-
-public:
- void registerEvalTerm( Node n );
- void registerModelValue( Node n, Node v, std::vector< Node >& exps, std::vector< Node >& terms, std::vector< Node >& vals );
- Node unfold( Node en, std::map< Node, Node >& vtm, std::vector< Node >& exp, bool track_exp = true );
- Node unfold( Node en ){
- std::map< Node, Node > vtm;
- std::vector< Node > exp;
- return unfold( en, vtm, exp, false );
- }
- Node getEagerUnfold( Node n, std::map< Node, Node >& visited );
-
- // builtin evaluation, returns rewrite( bn [ args / vars(tn) ] )
- Node evaluateBuiltin( TypeNode tn, Node bn, std::vector< Node >& args );
- // evaluate with unfolding
- Node evaluateWithUnfolding(
- Node n, std::unordered_map<Node, Node, NodeHashFunction>& visited);
- Node evaluateWithUnfolding( Node n );
-};
-
-}/* CVC4::theory::quantifiers namespace */
-}/* CVC4::theory namespace */
-}/* CVC4 namespace */
-
-#endif /* __CVC4__THEORY__QUANTIFIERS__TERM_DATABASE_H */
#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"
+++ /dev/null
-/********************* */
-/*! \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<Node>& 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; i<d_nodes.size(); i++ ){
- Trace("trigger") << " " << d_nodes[i] << std::endl;
- }
- if( d_nodes.size()==1 ){
- if( isSimpleTrigger( d_nodes[0] ) ){
- d_mg = new InstMatchGeneratorSimple(q, d_nodes[0], qe);
- }else{
- d_mg = InstMatchGenerator::mkInstMatchGenerator(q, d_nodes[0], qe);
- }
- }else{
- if( options::multiTriggerCache() ){
- d_mg = new InstMatchGeneratorMulti(q, d_nodes, qe);
- }else{
- d_mg = InstMatchGenerator::mkInstMatchGeneratorMulti(q, d_nodes, qe);
- }
- }
- if( d_nodes.size()==1 ){
- if( isSimpleTrigger( d_nodes[0] ) ){
- ++(qe->d_statistics.d_triggers);
- }else{
- ++(qe->d_statistics.d_simple_triggers);
- }
- }else{
- Trace("multi-trigger") << "Trigger for " << q << ": " << std::endl;
- for( unsigned i=0; i<d_nodes.size(); i++ ){
- Trace("multi-trigger") << " " << d_nodes[i] << std::endl;
- }
- ++(qe->d_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<temp.size(); i++ ){
- bool foundVar = false;
- for( unsigned j=0; j<varContains[ temp[i] ].size(); j++ ){
- Node v = varContains[ temp[i] ][j];
- Assert( quantifiers::TermUtil::getInstConstAttr(v)==q );
- if( vars.find( v )==vars.end() ){
- varCount++;
- vars[ v ] = true;
- foundVar = true;
- }
- }
- if( foundVar ){
- trNodes.push_back( temp[i] );
- for( unsigned j=0; j<varContains[ temp[i] ].size(); j++ ){
- Node v = varContains[ temp[i] ][j];
- patterns[ v ].push_back( temp[i] );
- }
- }
- if( varCount==n_vars ){
- break;
- }
- }
- if( varCount<n_vars ){
- Trace("trigger-debug") << "Don't consider trigger since it does not contain specified number of variables." << std::endl;
- for( unsigned i=0; i<nodes.size(); i++) {
- Trace("trigger-debug") << nodes[i] << " ";
- }
- Trace("trigger-debug") << std::endl;
-
- //do not generate multi-trigger if it does not contain all variables
- return false;
- }else{
- //now, minimize the trigger
- for( unsigned i=0; i<trNodes.size(); i++ ){
- bool keepPattern = false;
- Node n = trNodes[i];
- for( unsigned j=0; j<varContains[ n ].size(); j++ ){
- Node v = varContains[ n ][j];
- if( patterns[v].size()==1 ){
- keepPattern = true;
- break;
- }
- }
- if( !keepPattern ){
- //remove from pattern vector
- for( unsigned j=0; j<varContains[ n ].size(); j++ ){
- Node v = varContains[ n ][j];
- for( unsigned k=0; k<patterns[v].size(); k++ ){
- if( patterns[v][k]==n ){
- patterns[v].erase( patterns[v].begin() + k, patterns[v].begin() + k + 1 );
- break;
- }
- }
- }
- //remove from trigger nodes
- trNodes.erase( trNodes.begin() + i, trNodes.begin() + i + 1 );
- i--;
- }
- }
- }
- return true;
-}
-
-Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >& 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<Node, std::vector<Node> > 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<n.getNumChildren(); i++ ){
- if( !isUsable( n[i], q ) ){
- return false;
- }
- }
- return true;
- }else if( n.getKind()==INST_CONSTANT ){
- return true;
- }else{
- std::map< Node, Node > 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<t.getNumChildren(); i++ ){
- if( t[i].getKind()!=INST_CONSTANT && quantifiers::TermUtil::hasInstConstAttr(t[i]) ){
- return false;
- }
- }
- if( options::purifyDtTriggers() && t.getKind()==APPLY_SELECTOR_TOTAL ){
- return false;
- }
- if (t.getKind() == HO_APPLY && t[0].getKind() == INST_CONSTANT)
- {
- return false;
- }
- return true;
- }else{
- return false;
- }
-}
-
-//store triggers in reqPol, indicating their polarity (if any) they must appear to falsify the quantified formula
-void Trigger::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 ){
- 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<nrec.getNumChildren(); i++ ){
- bool newHasPol, newPol;
- bool newHasEPol, newEPol;
- QuantPhaseReq::getPolarity( nrec, i, hasPol, pol, newHasPol, newPol );
- QuantPhaseReq::getEntailPolarity( nrec, i, hasEPol, epol, newHasEPol, newEPol );
- collectPatTerms2( q, nrec[i], visited, tinfo, tstrt, exclude, added2, newPol, newHasPol, newEPol, newHasEPol );
- }
- // if this is not a usable trigger, don't worry about caching the results, since triggers do not contain non-usable subterms
- if( !nu.isNull() ){
- bool rm_nu = false;
- for( unsigned i=0; i<added2.size(); i++ ){
- Trace("auto-gen-trigger-debug2") << "..." << nu << " added child " << i << " : " << added2[i] << std::endl;
- Assert( added2[i]!=nu );
- // if child was not already removed
- if( tinfo.find( added2[i] )!=tinfo.end() ){
- if( tstrt==quantifiers::TRIGGER_SEL_MAX || ( tstrt==quantifiers::TRIGGER_SEL_MIN_SINGLE_MAX && !nu_single ) ){
- //discard all subterms
- Trace("auto-gen-trigger-debug2") << "......remove it." << std::endl;
- visited[ added2[i] ].clear();
- tinfo.erase( added2[i] );
- }else{
- if( tinfo[ nu ].d_fv.size()==tinfo[ added2[i] ].d_fv.size() ){
- if (tinfo[nu].d_weight >= 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; i<itv->second.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<BitVector>().toInteger()==1 &&
- n[2].getConst<BitVector>().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<n.getNumChildren(); i++ ){
- if( !isPureTheoryTrigger( n[i] ) ){
- return false;
- }
- }
- return true;
- }
-}
-
-int Trigger::getTriggerWeight( Node n ) {
- if (n.getKind() == APPLY_UF)
- {
- return 0;
- }
- else if (isAtomicTrigger(n))
- {
- return 1;
- }else{
- if( options::relationalTriggers() ){
- if( isRelationalTrigger( n ) ){
- for( unsigned i=0; i<2; i++ ){
- if( n[i].getKind()==INST_CONSTANT && !quantifiers::TermUtil::hasInstConstAttr( n[1-i] ) ){
- return 0;
- }
- }
- }
- }
- return 2;
- }
-}
-
-bool Trigger::isLocalTheoryExt( Node n, std::vector< Node >& 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<n.getNumChildren(); i++ ){
- if( n[i].getKind()==INST_CONSTANT ){
- hasVar = true;
- if( std::find( vars.begin(), vars.end(), n[i] )==vars.end() ){
- vars.push_back( n[i] );
- }else{
- //do not allow duplicate variables
- return false;
- }
- }else{
- //do not allow nested function applications
- return false;
- }
- }
- if( hasVar ){
- patTerms.push_back( n );
- }
- }
- }else{
- for( unsigned i=0; i<n.getNumChildren(); i++ ){
- if( !isLocalTheoryExt( n[i], vars, patTerms ) ){
- return false;
- }
- }
- }
- return true;
-}
-
-void Trigger::collectPatTerms( Node q, Node n, std::vector< Node >& 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<temp.size(); i++ ){
- //copy information
- tinfo[temp[i]].d_fv.insert( tinfo[temp[i]].d_fv.end(), tinfo2[temp[i]].d_fv.begin(), tinfo2[temp[i]].d_fv.end() );
- tinfo[temp[i]].d_reqPol = tinfo2[temp[i]].d_reqPol;
- tinfo[temp[i]].d_reqPolEq = tinfo2[temp[i]].d_reqPolEq;
- patTerms.push_back( temp[i] );
- }
- return;
- }else{
- //do not consider terms that have instances
- for( unsigned i=0; i<patTerms2.size(); i++ ){
- if( std::find( temp.begin(), temp.end(), patTerms2[i] )==temp.end() ){
- visited[ patTerms2[i] ].clear();
- }
- }
- }
- }
- std::vector< Node > 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<Node>& fv1,
- std::vector<Node>& fv2)
-{
- Assert(n1 != n2);
- int status = 0;
- std::unordered_set<TNode, TNodeHashFunction> subs_vars;
- std::unordered_set<std::pair<TNode, TNode>,
- PairHashFunction<TNode,
- TNode,
- TNodeHashFunction,
- TNodeHashFunction> >
- visited;
- std::vector<std::pair<TNode, TNode> > visit;
- std::pair<TNode, TNode> cur;
- TNode cur1;
- TNode cur2;
- visit.push_back(std::pair<TNode, TNode>(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<TNode, TNode>(cur1, cur2));
- for (unsigned i = 0, size = cur1.getNumChildren(); i < size; i++)
- {
- if (cur1[i] != cur2[i])
- {
- visit.push_back(std::pair<TNode, TNode>(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<Node>& 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<Node>& nodes)
-{
- std::map<unsigned, std::vector<Node> > fvs;
- for (unsigned i = 0, size = nodes.size(); i < size; i++)
- {
- quantifiers::TermUtil::computeVarContains(nodes[i], fvs[i]);
- }
- std::vector<bool> active;
- active.resize(nodes.size(), true);
- for (unsigned i = 0, size = nodes.size(); i < size; i++)
- {
- std::vector<Node>& 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<Node> 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<n.getNumChildren(); i++ ){
- if( quantifiers::TermUtil::hasInstConstAttr(n[i]) ){
- if( ret.isNull() ){
- ret = getInversionVariable( n[i] );
- if( ret.isNull() ){
- Trace("var-trigger-debug") << "No : multiple variables " << n << std::endl;
- return Node::null();
- }
- }else{
- return Node::null();
- }
- }else if( n.getKind()==MULT ){
- if( !n[i].isConst() ){
- Trace("var-trigger-debug") << "No : non-linear coefficient " << n << std::endl;
- return Node::null();
- }
- /*
- else if( n.getType().isInteger() ){
- Rational r = n[i].getConst<Rational>();
- 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; i<n.getNumChildren(); i++ ){
- if( !quantifiers::TermUtil::hasInstConstAttr(n[i]) ){
- if( n.getKind()==PLUS ){
- x = NodeManager::currentNM()->mkNode( 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<Rational>().abs() );
- if( !n[i].getConst<Rational>().abs().isOne() ){
- x = NodeManager::currentNM()->mkNode( INTS_DIVISION_TOTAL, x, coeff );
- }
- if( n[i].getConst<Rational>().sgn()<0 ){
- x = NodeManager::currentNM()->mkNode( UMINUS, x );
- }
- }else{
- Node coeff = NodeManager::currentNM()->mkConst( Rational(1) / n[i].getConst<Rational>() );
- 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<Node>& 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; i<patTerms.size(); i++ ){
- quantifiers::TermUtil::getVarContainsNode( q, patTerms[i], t_vars );
- }
-}
-
-int Trigger::getActiveScore() {
- return d_mg->getActiveScore( 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<d_tr.size(); i++ ){
- delete d_tr[i];
- }
-}
-
-inst::Trigger* TriggerTrie::getTrigger(std::vector<Node>& nodes)
-{
- std::vector<Node> 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<TNode, TriggerTrie*>::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<Node>& nodes, inst::Trigger* t)
-{
- std::vector<Node> 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<TNode, TriggerTrie*>::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 */
+++ /dev/null
-/********************* */
-/*! \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 <map>
-
-#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<Node> 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<Node>& 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<Node>& nodes,
- unsigned n_vars,
- std::vector<Node>& 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<Node>& t_vars);
-
- protected:
- /** trigger constructor, intentionally protected (use Trigger::mkTrigger). */
- Trigger(QuantifiersEngine* ie, Node q, std::vector<Node>& 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<Node>& 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<Node>& fv1,
- std::vector<Node>& 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<Node>& 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<Node>& nodes, Trigger* t);
-
- private:
- /** The trigger at this node in the trie. */
- std::vector<Trigger*> 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 */
#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"
#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"
#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"
#include "theory/rewriter.h"
#include "util/bitvector.h"
-#include "theory/quantifiers/ceg_t_instantiator.cpp"
+#include "theory/quantifiers/cegqi/ceg_t_instantiator.cpp"
#include <cxxtest/TestSuite.h>
#include <iostream>