diff options
author | Morgan Deters <mdeters@gmail.com> | 2012-06-11 16:28:23 +0000 |
---|---|---|
committer | Morgan Deters <mdeters@gmail.com> | 2012-06-11 16:28:23 +0000 |
commit | 3378e253fcdb34c753407bb16d08929da06b3aaa (patch) | |
tree | db7c7118dd0d1594175b56866f845b42426ae0a7 /src/theory | |
parent | 42794501e81c44dce5c2f7687af288af030ef63e (diff) |
Merge from quantifiers2-trunkmerge branch.
Adds TheoryQuantifiers and TheoryRewriteRules, QuantifiersEngine, and other infrastructure.
Adds theory instantiators to many theories.
Adds the UF strong solver.
Diffstat (limited to 'src/theory')
93 files changed, 14211 insertions, 150 deletions
diff --git a/src/theory/Makefile.am b/src/theory/Makefile.am index 85d6fbdf8..19e7d588a 100644 --- a/src/theory/Makefile.am +++ b/src/theory/Makefile.am @@ -3,7 +3,7 @@ AM_CPPFLAGS = \ -I@srcdir@/../include -I@srcdir@/.. -I@builddir@/.. AM_CXXFLAGS = -Wall -Wno-unknown-pragmas $(FLAG_VISIBILITY_HIDDEN) -SUBDIRS = builtin booleans uf arith arrays bv datatypes +SUBDIRS = builtin booleans uf arith arrays bv datatypes quantifiers rewriterules DIST_SUBDIRS = $(SUBDIRS) example noinst_LTLIBRARIES = libtheory.la @@ -33,10 +33,20 @@ libtheory_la_SOURCES = \ ite_simplifier.h \ ite_simplifier.cpp \ unconstrained_simplifier.h \ - unconstrained_simplifier.cpp + unconstrained_simplifier.cpp \ + quantifiers_engine.h \ + quantifiers_engine.cpp \ + instantiator_default.h \ + instantiator_default.cpp \ + inst_match.h \ + inst_match_impl.h \ + inst_match.cpp \ + trigger.h \ + trigger.cpp nodist_libtheory_la_SOURCES = \ rewriter_tables.h \ + instantiator_tables.cpp \ theory_traits.h libtheory_la_LIBADD = \ @@ -46,21 +56,27 @@ libtheory_la_LIBADD = \ @builddir@/arith/libarith.la \ @builddir@/arrays/libarrays.la \ @builddir@/bv/libbv.la \ - @builddir@/datatypes/libdatatypes.la + @builddir@/datatypes/libdatatypes.la \ + @builddir@/quantifiers/libquantifiers.la \ + @builddir@/rewriterules/librewriterules.la EXTRA_DIST = \ rewriter_tables_template.h \ + instantiator_tables_template.cpp \ theory_traits_template.h \ mktheorytraits \ mkrewriter \ + mkinstantiator \ Makefile.subdirs BUILT_SOURCES = \ rewriter_tables.h \ + instantiator_tables.cpp \ theory_traits.h CLEANFILES = \ rewriter_tables.h \ + instantiator_tables.cpp \ theory_traits.h include @top_srcdir@/src/theory/Makefile.subdirs @@ -73,6 +89,14 @@ rewriter_tables.h: rewriter_tables_template.h mkrewriter @top_builddir@/src/theo `cat @top_builddir@/src/theory/.subdirs` \ > $@) || (rm -f $@ && exit 1) +instantiator_tables.cpp: instantiator_tables_template.cpp mkinstantiator @top_builddir@/src/theory/.subdirs @top_srcdir@/src/theory/*/kinds + $(AM_V_at)chmod +x @srcdir@/mkinstantiator + $(AM_V_at)$(am__mv) $@ $@~ 2>/dev/null || true + $(AM_V_GEN)(@srcdir@/mkinstantiator \ + $< \ + `cat @top_builddir@/src/theory/.subdirs` \ + > $@) || (rm -f $@ && exit 1) + theory_traits.h: theory_traits_template.h mktheorytraits @top_builddir@/src/theory/.subdirs @top_srcdir@/src/theory/*/kinds $(AM_V_at)chmod +x @srcdir@/mktheorytraits $(AM_V_at)$(am__mv) $@ $@~ 2>/dev/null || true diff --git a/src/theory/arith/Makefile.am b/src/theory/arith/Makefile.am index a029bc97b..b1e8855c7 100644 --- a/src/theory/arith/Makefile.am +++ b/src/theory/arith/Makefile.am @@ -36,7 +36,9 @@ libarith_la_SOURCES = \ theory_arith.h \ theory_arith.cpp \ dio_solver.h \ - dio_solver.cpp + dio_solver.cpp \ + theory_arith_instantiator.h \ + theory_arith_instantiator.cpp EXTRA_DIST = \ kinds diff --git a/src/theory/arith/congruence_manager.h b/src/theory/arith/congruence_manager.h index 5f49ab3ab..63a370f9a 100644 --- a/src/theory/arith/congruence_manager.h +++ b/src/theory/arith/congruence_manager.h @@ -73,7 +73,12 @@ private: d_acm.propagate(t1.eqNode(t2)); } } - }; + + void eqNotifyNewClass(TNode t) { } + void eqNotifyPreMerge(TNode t1, TNode t2) { } + void eqNotifyPostMerge(TNode t1, TNode t2) { } + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { } + }; ArithCongruenceNotify d_notify; context::CDList<Node> d_keepAlive; diff --git a/src/theory/arith/kinds b/src/theory/arith/kinds index 8ffe68376..c06cbc140 100644 --- a/src/theory/arith/kinds +++ b/src/theory/arith/kinds @@ -6,6 +6,7 @@ theory THEORY_ARITH ::CVC4::theory::arith::TheoryArith "theory/arith/theory_arith.h" typechecker "theory/arith/theory_arith_type_rules.h" +instantiator ::CVC4::theory::arith::InstantiatorTheoryArith "theory/arith/theory_arith_instantiator.h" properties stable-infinite properties check propagate staticLearning presolve notifyRestart diff --git a/src/theory/arith/theory_arith.cpp b/src/theory/arith/theory_arith.cpp index ac9796986..9ff9ceeb9 100644 --- a/src/theory/arith/theory_arith.cpp +++ b/src/theory/arith/theory_arith.cpp @@ -53,8 +53,8 @@ namespace arith { const uint32_t RESET_START = 2; -TheoryArith::TheoryArith(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) : - Theory(THEORY_ARITH, c, u, out, valuation, logicInfo), +TheoryArith::TheoryArith(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) : + Theory(THEORY_ARITH, c, u, out, valuation, logicInfo, qe), d_hasDoneWorkSinceCut(false), d_learner(d_pbSubstitutions), d_setupLiteralCallback(this), diff --git a/src/theory/arith/theory_arith.h b/src/theory/arith/theory_arith.h index ebc131b60..1f0120387 100644 --- a/src/theory/arith/theory_arith.h +++ b/src/theory/arith/theory_arith.h @@ -53,12 +53,15 @@ namespace CVC4 { namespace theory { namespace arith { +class InstantiatorTheoryArith; + /** * Implementation of QF_LRA. * Based upon: * http://research.microsoft.com/en-us/um/people/leonardo/cav06.pdf */ class TheoryArith : public Theory { + friend class InstantiatorTheoryArith; private: bool rowImplication(ArithVar v, bool upperBound, const DeltaRational& r); @@ -269,7 +272,7 @@ private: DeltaRational getDeltaValue(TNode n); public: - TheoryArith(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo); + TheoryArith(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe); virtual ~TheoryArith(); /** diff --git a/src/theory/arith/theory_arith_instantiator.cpp b/src/theory/arith/theory_arith_instantiator.cpp new file mode 100644 index 000000000..48c8a30ee --- /dev/null +++ b/src/theory/arith/theory_arith_instantiator.cpp @@ -0,0 +1,448 @@ +/********************* */ +/*! \file instantiator_arith_instantiator.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of instantiator_arith_instantiator class + **/ + +#include "theory/arith/theory_arith_instantiator.h" +#include "theory/arith/theory_arith.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::arith; + +#define ARITH_INSTANTIATOR_USE_DELTA +#define ARITH_INSTANTIATOR_USE_MINUS_DELTA +#define ARITH_INSTANTIATOR_STRONG_DELTA_LEMMA + +#define USE_ARITH_INSTANTIATION + +InstStrategySimplex::InstStrategySimplex( InstantiatorTheoryArith* th, QuantifiersEngine* ie ) : + InstStrategy( ie ), d_th( th ), d_counter( 0 ){ + d_negOne = NodeManager::currentNM()->mkConst( Rational(-1) ); +} + +void InstStrategySimplex::processResetInstantiationRound( Theory::Effort effort ){ + d_counter++; +} + +int InstStrategySimplex::process( Node f, Theory::Effort effort, int e, int instLimit ){ + if( e<2 ){ + return STATUS_UNFINISHED; + }else if( e==2 ){ + //Notice() << f << std::endl; + //Notice() << "Num inst rows = " << d_th->d_instRows[f].size() << std::endl; + //Notice() << "Num inst constants = " << d_quantEngine->getNumInstantiationConstants( f ) << std::endl; + Debug("quant-arith-simplex") << "InstStrategySimplex check " << f << ", rows = " << d_th->d_instRows[f].size() << std::endl; + for( int j=0; j<(int)d_th->d_instRows[f].size(); j++ ){ + ArithVar x = d_th->d_instRows[f][j]; + if( !d_th->d_ceTableaux[x].empty() ){ + Debug("quant-arith-simplex") << "Check row " << x << std::endl; + //instantiation row will be A*e + B*t = beta, + // where e is a vector of terms , and t is vector of ground terms. + // Say one term in A*e is coeff*e_i, where e_i is an instantiation constant + // We will construct the term ( beta - B*t)/coeff to use for e_i. + InstMatch m; + //By default, choose the first instantiation constant to be e_i. + Node var = d_th->d_ceTableaux[x].begin()->first; + if( var.getType().isInteger() ){ + std::map< Node, Node >::iterator it = d_th->d_ceTableaux[x].begin(); + //try to find coefficent that is +/- 1 + while( !var.isNull() && !d_th->d_ceTableaux[x][var].isNull() && d_th->d_ceTableaux[x][var]!=d_negOne ){ + ++it; + if( it==d_th->d_ceTableaux[x].end() ){ + var = Node::null(); + }else{ + var = it->first; + } + } + //otherwise, try one that divides all ground term coefficients? DO_THIS + } + if( !var.isNull() ){ + Debug("quant-arith-simplex") << "Instantiate with var " << var << std::endl; + d_th->doInstantiation( f, d_th->d_tableaux_term[x], x, m, var ); + }else{ + Debug("quant-arith-simplex") << "Could not find var." << std::endl; + } + ////choose a new variable based on alternation strategy + //int index = d_counter%(int)d_th->d_ceTableaux[x].size(); + //Node var; + //for( std::map< Node, Node >::iterator it = d_th->d_ceTableaux[x].begin(); it != d_th->d_ceTableaux[x].end(); ++it ){ + // if( index==0 ){ + // var = it->first; + // break; + // } + // index--; + //} + //d_th->doInstantiation( f, d_th->d_tableaux_term[x], x, &m, var ); + } + } + } + return STATUS_UNKNOWN; +} + +//void InstStrategySimplexUfMatch::resetInstantiationRound(){ +// +//} +// +//int InstStrategySimplexUfMatch::process( Node f, int effort, int instLimit ){ +// if( effort<2 ){ +// return STATUS_UNFINISHED; +// }else if( effort==2 ){ +// for( int j=0; j<(int)d_th->d_instRows[f].size(); j++ ){ +// ArithVar x = d_th->d_instRows[f][j]; +// if( !d_th->d_ceTableaux[x].empty() && !d_th->d_tableaux_ce_term[x].empty() ){ +// if( d_tableaux_ce_term_trigger.find( x )==d_tableaux_ce_term_trigger.end() ){ +// std::vector< Node > terms; +// for( std::map< Node, Node >::iterator it = d_th->d_tableaux_ce_term[x].begin(); it != d_th->d_tableaux_ce_term[x].end(); ++it ){ +// terms.push_back( it->first ); +// } +// d_tableaux_ce_term_trigger[x] = new Trigger( d_quantEngine, f, terms ); +// }else{ +// d_tableaux_ce_term_trigger[x]->resetInstantiationRound(); +// } +// Node term; +// bool addedLemma = false; +// while( d_tableaux_ce_term_trigger[x]->getNextMatch() && !addedLemma ){ +// InstMatch* m = d_tableaux_ce_term_trigger[x]->getCurrent(); +// if( m->isComplete( f ) ){ +// if( d_quantEngine->addInstantiation( f, m, true ) ){ +// ++(d_th->d_statistics.d_instantiations_match_pure); +// ++(d_th->d_statistics.d_instantiations); +// addedLemma = true; +// } +// }else{ +// NodeBuilder<> plus_term(kind::PLUS); +// plus_term << d_th->d_tableaux_term[x]; +// //Debug("quant-arith") << "Produced this match for ce_term_tableaux: " << std::endl; +// //m->debugPrint("quant-arith"); +// //Debug("quant-arith") << std::endl; +// std::vector< Node > vars; +// std::vector< Node > matches; +// for( int i=0; i<d_quantEngine->getNumInstantiationConstants( f ); i++ ){ +// Node ic = d_quantEngine->getInstantiationConstant( f, i ); +// if( m->d_map[ ic ]!=Node::null() ){ +// vars.push_back( ic ); +// matches.push_back( m->d_map[ ic ] ); +// } +// } +// Node var; +// //otherwise try to find a variable that is not specified in m +// for( std::map< Node, Node >::iterator it = d_th->d_ceTableaux[x].begin(); it != d_th->d_ceTableaux[x].end(); ++it ){ +// if( m->d_map[ it->first ]!=Node::null() ){ +// plus_term << NodeManager::currentNM()->mkNode( MULT, it->second, d_th->getTableauxValue( m->d_map[ it->first ] ) ); +// }else if( var==Node::null() ){ +// var = it->first; +// } +// } +// for( std::map< Node, Node >::iterator it = d_th->d_tableaux_ce_term[x].begin(); it != d_th->d_tableaux_ce_term[x].end(); ++it ){ +// Node n = it->first; +// //substitute in matches +// n = n.substitute( vars.begin(), vars.end(), matches.begin(), matches.end() ); +// plus_term << NodeManager::currentNM()->mkNode( MULT, it->second, d_th->getTableauxValue( n ) ); +// } +// term = plus_term.getNumChildren()==1 ? plus_term.getChild( 0 ) : plus_term; +// if( var!=Node::null() ){ +// if( d_th->doInstantiation( f, term, x, m, var ) ){ +// addedLemma = true; +// ++(d_th->d_statistics.d_instantiations_match_var); +// } +// }else{ +// if( d_quantEngine->addInstantiation( f, m, true ) ){ +// addedLemma = true; +// ++(d_th->d_statistics.d_instantiations_match_no_var); +// ++(d_th->d_statistics.d_instantiations); +// } +// } +// } +// } +// } +// } +// } +// return STATUS_UNKNOWN; +//} + +InstantiatorTheoryArith::InstantiatorTheoryArith(context::Context* c, QuantifiersEngine* ie, Theory* th) : +Instantiator( c, ie, th ){ + if( Options::current()->cbqi ){ + addInstStrategy( new InstStrategySimplex( this, d_quantEngine ) ); + } +} + +void InstantiatorTheoryArith::preRegisterTerm( Node t ){ + +} + +void InstantiatorTheoryArith::assertNode( Node assertion ){ + Debug("quant-arith-assert") << "InstantiatorTheoryArith::check: " << assertion << std::endl; + d_quantEngine->addTermToDatabase( assertion ); + if( Options::current()->cbqi ){ + if( assertion.hasAttribute(InstConstantAttribute()) ){ + setHasConstraintsFrom( assertion.getAttribute(InstConstantAttribute()) ); + }else if( assertion.getKind()==NOT && assertion[0].hasAttribute(InstConstantAttribute()) ){ + setHasConstraintsFrom( assertion[0].getAttribute(InstConstantAttribute()) ); + } + } +} + +void InstantiatorTheoryArith::processResetInstantiationRound( Theory::Effort effort ){ + if( Options::current()->cbqi ){ + Debug("quant-arith") << "Setting up simplex for instantiator... " << std::endl; + d_instRows.clear(); + d_tableaux_term.clear(); + d_tableaux.clear(); + d_ceTableaux.clear(); + //search for instantiation rows in simplex tableaux + ArithVarToNodeMap avtnm = ((TheoryArith*)getTheory())->d_arithvarNodeMap.getArithVarToNodeMap(); + for( ArithVarToNodeMap::iterator it = avtnm.begin(); it != avtnm.end(); ++it ){ + ArithVar x = (*it).first; + if( ((TheoryArith*)getTheory())->d_partialModel.hasEitherBound( x ) ){ + Node n = (*it).second; + Node f; + NodeBuilder<> t(kind::PLUS); + if( n.getKind()==PLUS ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + addTermToRow( x, n[i], f, t ); + } + }else{ + addTermToRow( x, n, f, t ); + } + if( f!=Node::null() ){ + d_instRows[f].push_back( x ); + //this theory has constraints from f + Debug("quant-arith") << "Has constraints from " << f << std::endl; + setHasConstraintsFrom( f ); + //set tableaux term + if( t.getNumChildren()==0 ){ + d_tableaux_term[x] = NodeManager::currentNM()->mkConst( Rational(0) ); + }else if( t.getNumChildren()==1 ){ + d_tableaux_term[x] = t.getChild( 0 ); + }else{ + d_tableaux_term[x] = t; + } + } + } + } + //print debug + debugPrint( "quant-arith-debug" ); + } +} + +int InstantiatorTheoryArith::process( Node f, Theory::Effort effort, int e, int instLimit ){ + Debug("quant-arith") << "Arith: Try to solve (" << effort << ") for " << f << "... " << std::endl; + return InstStrategy::STATUS_UNKNOWN; +} + +void InstantiatorTheoryArith::addTermToRow( ArithVar x, Node n, Node& f, NodeBuilder<>& t ){ + if( n.getKind()==MULT ){ + if( n[1].hasAttribute(InstConstantAttribute()) ){ + f = n[1].getAttribute(InstConstantAttribute()); + if( n[1].getKind()==INST_CONSTANT ){ + d_ceTableaux[x][ n[1] ] = n[0]; + }else{ + d_tableaux_ce_term[x][ n[1] ] = n[0]; + } + }else{ + d_tableaux[x][ n[1] ] = n[0]; + t << n; + } + }else{ + if( n.hasAttribute(InstConstantAttribute()) ){ + f = n.getAttribute(InstConstantAttribute()); + if( n.getKind()==INST_CONSTANT ){ + d_ceTableaux[x][ n ] = Node::null(); + }else{ + d_tableaux_ce_term[x][ n ] = NodeManager::currentNM()->mkConst( Rational(1) ); + } + }else{ + d_tableaux[x][ n ] = NodeManager::currentNM()->mkConst( Rational(1) ); + t << n; + } + } +} + +void InstantiatorTheoryArith::debugPrint( const char* c ){ + ArithVarToNodeMap avtnm = ((TheoryArith*)getTheory())->d_arithvarNodeMap.getArithVarToNodeMap(); + for( ArithVarToNodeMap::iterator it = avtnm.begin(); it != avtnm.end(); ++it ){ + ArithVar x = (*it).first; + Node n = (*it).second; + //if( ((TheoryArith*)getTheory())->d_partialModel.hasEitherBound( x ) ){ + Debug(c) << x << " : " << n << ", bounds = "; + if( ((TheoryArith*)getTheory())->d_partialModel.hasLowerBound( x ) ){ + Debug(c) << ((TheoryArith*)getTheory())->d_partialModel.getLowerBound( x ); + }else{ + Debug(c) << "-infty"; + } + Debug(c) << " <= "; + Debug(c) << ((TheoryArith*)getTheory())->d_partialModel.getAssignment( x ); + Debug(c) << " <= "; + if( ((TheoryArith*)getTheory())->d_partialModel.hasUpperBound( x ) ){ + Debug(c) << ((TheoryArith*)getTheory())->d_partialModel.getUpperBound( x ); + }else{ + Debug(c) << "+infty"; + } + Debug(c) << std::endl; + //Debug(c) << " Term = " << d_tableaux_term[x] << std::endl; + //Debug(c) << " "; + //for( std::map< Node, Node >::iterator it2 = d_tableaux[x].begin(); it2 != d_tableaux[x].end(); ++it2 ){ + // Debug(c) << "( " << it2->first << ", " << it2->second << " ) "; + //} + //for( std::map< Node, Node >::iterator it2 = d_ceTableaux[x].begin(); it2 != d_ceTableaux[x].end(); ++it2 ){ + // Debug(c) << "(CE)( " << it2->first << ", " << it2->second << " ) "; + //} + //for( std::map< Node, Node >::iterator it2 = d_tableaux_ce_term[x].begin(); it2 != d_tableaux_ce_term[x].end(); ++it2 ){ + // Debug(c) << "(CE-term)( " << it2->first << ", " << it2->second << " ) "; + //} + //Debug(c) << std::endl; + //} + } + Debug(c) << std::endl; + + for( int q=0; q<d_quantEngine->getNumQuantifiers(); q++ ){ + Node f = d_quantEngine->getQuantifier( q ); + Debug(c) << f << std::endl; + Debug(c) << " Inst constants: "; + for( int i=0; i<(int)d_quantEngine->getNumInstantiationConstants( f ); i++ ){ + if( i>0 ){ + Debug( c ) << ", "; + } + Debug( c ) << d_quantEngine->getInstantiationConstant( f, i ); + } + Debug(c) << std::endl; + Debug(c) << " Instantiation rows: "; + for( int i=0; i<(int)d_instRows[f].size(); i++ ){ + if( i>0 ){ + Debug(c) << ", "; + } + Debug(c) << d_instRows[f][i]; + } + Debug(c) << std::endl; + } +} + +//say instantiation row x for quantifier f is coeff*var + A*t[e] + term = beta, +// where var is an instantiation constant from f, +// t[e] is a vector of terms containing instantiation constants from f, +// and term is a ground term (c1*t1 + ... + cn*tn). +// We construct the term ( beta - term )/coeff to use as an instantiation for var. +bool InstantiatorTheoryArith::doInstantiation( Node f, Node term, ArithVar x, InstMatch& m, Node var ){ + //first try +delta + if( doInstantiation2( f, term, x, m, var ) ){ + ++(d_statistics.d_instantiations); + return true; + }else{ +#ifdef ARITH_INSTANTIATOR_USE_MINUS_DELTA + //otherwise try -delta + if( doInstantiation2( f, term, x, m, var, true ) ){ + ++(d_statistics.d_instantiations_minus); + ++(d_statistics.d_instantiations); + return true; + }else{ + return false; + } +#else + return false; +#endif + } +} + +bool InstantiatorTheoryArith::doInstantiation2( Node f, Node term, ArithVar x, InstMatch& m, Node var, bool minus_delta ){ + // make term ( beta - term )/coeff + Node beta = getTableauxValue( x, minus_delta ); + Node instVal = NodeManager::currentNM()->mkNode( MINUS, beta, term ); + if( !d_ceTableaux[x][var].isNull() ){ + if( var.getType().isInteger() ){ + Assert( d_ceTableaux[x][var]==NodeManager::currentNM()->mkConst( Rational(-1) ) ); + instVal = NodeManager::currentNM()->mkNode( MULT, d_ceTableaux[x][var], instVal ); + }else{ + Node coeff = NodeManager::currentNM()->mkConst( Rational(1) / d_ceTableaux[x][var].getConst<Rational>() ); + instVal = NodeManager::currentNM()->mkNode( MULT, coeff, instVal ); + } + } + instVal = Rewriter::rewrite( instVal ); + //use as instantiation value for var + m.d_map[ var ] = instVal; + Debug("quant-arith") << "Add instantiation " << m << std::endl; + return d_quantEngine->addInstantiation( f, m, true ); +} + +Node InstantiatorTheoryArith::getTableauxValue( Node n, bool minus_delta ){ + if( ((TheoryArith*)getTheory())->d_arithvarNodeMap.hasArithVar(n) ){ + ArithVar v = ((TheoryArith*)getTheory())->d_arithvarNodeMap.asArithVar( n ); + return getTableauxValue( v, minus_delta ); + }else{ + return NodeManager::currentNM()->mkConst( Rational(0) ); + } +} + +Node InstantiatorTheoryArith::getTableauxValue( ArithVar v, bool minus_delta ){ + DeltaRational drv = ((TheoryArith*)getTheory())->d_partialModel.getAssignment( v ); + Node val = NodeManager::currentNM()->mkConst( drv.getNoninfinitesimalPart() ); +#ifdef ARITH_INSTANTIATOR_USE_DELTA + //the tableaux value for v may contain an infinitesemal part: getDelta( val ) will return a fresh variable "delta" + // (one for each sort) for which the lemma ( delta > 0 ) is asserted. + if( drv.getInfinitesimalPart()!=0 ){ + Node delta = NodeManager::currentNM()->mkNode( MULT, getDelta( val ), + NodeManager::currentNM()->mkConst( drv.getInfinitesimalPart() ) ); + // add (or subtract) this delta component from the value of v + val = NodeManager::currentNM()->mkNode( minus_delta ? MINUS : PLUS, val, delta ); + } +#endif + return val; +} + +Node InstantiatorTheoryArith::getDelta( Node n ){ + std::map< TypeNode, Node >::iterator it = d_deltas.find( n.getType() ); + if( it==d_deltas.end() ){ + std::ostringstream os; + os << "delta_" << d_deltas.size(); + Node delta = NodeManager::currentNM()->mkVar( os.str(), n.getType() ); + d_deltas[ n.getType() ] = delta; + Node gt = NodeManager::currentNM()->mkNode( GT, delta, NodeManager::currentNM()->mkConst( Rational(0) ) ); + //add split +#ifdef ARITH_INSTANTIATOR_STRONG_DELTA_LEMMA + d_quantEngine->addLemma( gt ); +#else + gt = Rewriter::rewrite( gt ); + d_quantEngine->addSplit( gt, true, true ); +#endif + return delta; + } + return it->second; +} + +InstantiatorTheoryArith::Statistics::Statistics(): + d_instantiations("InstantiatorTheoryArith::Instantiations_Total", 0), + d_instantiations_minus("InstantiatorTheoryArith::Instantiations_minus_delta", 0), + d_instantiations_match_pure("InstantiatorTheoryArith::Instantiations_via_pure_matching", 0), + d_instantiations_match_var("InstantiatorTheoryArith::Instantiations_via_matching_var", 0), + d_instantiations_match_no_var("InstantiatorTheoryArith::Instantiations_via_matching_no_var", 0) +{ + StatisticsRegistry::registerStat(&d_instantiations); + StatisticsRegistry::registerStat(&d_instantiations_minus); + StatisticsRegistry::registerStat(&d_instantiations_match_pure); + StatisticsRegistry::registerStat(&d_instantiations_match_var); + StatisticsRegistry::registerStat(&d_instantiations_match_no_var); +} + +InstantiatorTheoryArith::Statistics::~Statistics(){ + StatisticsRegistry::unregisterStat(&d_instantiations); + StatisticsRegistry::unregisterStat(&d_instantiations_minus); + StatisticsRegistry::unregisterStat(&d_instantiations_match_pure); + StatisticsRegistry::unregisterStat(&d_instantiations_match_var); + StatisticsRegistry::unregisterStat(&d_instantiations_match_no_var); +} diff --git a/src/theory/arith/theory_arith_instantiator.h b/src/theory/arith/theory_arith_instantiator.h new file mode 100644 index 000000000..524d16859 --- /dev/null +++ b/src/theory/arith/theory_arith_instantiator.h @@ -0,0 +1,128 @@ +/********************* */ +/*! \file instantiator_arith_instantiator.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief instantiator_arith_instantiator + **/ + + +#include "cvc4_private.h" + +#ifndef __CVC4__INSTANTIATOR_ARITH_H +#define __CVC4__INSTANTIATOR_ARITH_H + +#include "theory/quantifiers_engine.h" +#include "theory/arith/arithvar_node_map.h" + +#include "util/stats.h" + +namespace CVC4 { +namespace theory { +namespace arith { + +class InstantiatorTheoryArith; + +class InstStrategySimplex : public InstStrategy{ +private: + /** InstantiatorTheoryUf class */ + InstantiatorTheoryArith* d_th; + /** */ + int d_counter; + /** negative one */ + Node d_negOne; + /** process functions */ + void processResetInstantiationRound( Theory::Effort effort ); + int process( Node f, Theory::Effort effort, int e, int instLimit ); +public: + InstStrategySimplex( InstantiatorTheoryArith* th, QuantifiersEngine* ie ); + ~InstStrategySimplex(){} + /** identify */ + std::string identify() const { return std::string("Simplex"); } +}; +// +//class InstStrategySimplexUfMatch : public InstStrategy{ +//private: +// /** InstantiatorTheoryUf class */ +// InstantiatorTheoryArith* d_th; +// /** trigger for instantiation rows */ +// std::map< ArithVar, Trigger* > d_tableaux_ce_term_trigger; +//public: +// InstStrategySimplexUfMatch( InstantiatorTheoryArith* th, QuantifiersEngine* ie ) : +// InstStrategy( ie ), d_th( th ){} +// ~InstStrategySimplexUfMatch(){} +// void resetInstantiationRound(); +// int process( Node f, Theory::Effort effort, int e, int instLimit ); +// /** identify */ +// std::string identify() const { return std::string("SimplexUfMatch"); } +//}; + +class InstantiatorTheoryArith : public Instantiator{ + friend class QuantifiersEngine; + friend class InstStrategySimplex; + friend class InstStrategySimplexUfMatch; +private: + /** delta */ + std::map< TypeNode, Node > d_deltas; + /** for each quantifier, simplex rows */ + std::map< Node, std::vector< ArithVar > > d_instRows; + /** tableaux */ + std::map< ArithVar, Node > d_tableaux_term; + std::map< ArithVar, std::map< Node, Node > > d_tableaux_ce_term; + std::map< ArithVar, std::map< Node, Node > > d_tableaux; + /** ce tableaux */ + std::map< ArithVar, std::map< Node, Node > > d_ceTableaux; + /** get value */ + Node getTableauxValue( Node n, bool minus_delta = false ); + Node getTableauxValue( ArithVar v, bool minus_delta = false ); + /** do instantiation */ + bool doInstantiation( Node f, Node term, ArithVar x, InstMatch& m, Node var ); + bool doInstantiation2( Node f, Node term, ArithVar x, InstMatch& m, Node var, bool minus_delta = false ); +public: + InstantiatorTheoryArith(context::Context* c, QuantifiersEngine* ie, Theory* th); + ~InstantiatorTheoryArith() {} + + /** assertNode function, assertion is asserted to theory */ + void assertNode( Node assertion ); + /** Pre-register a term. Done one time for a Node, ever. */ + void preRegisterTerm( Node t ); + /** identify */ + std::string identify() const { return std::string("InstantiatorTheoryArith"); } + /** print debug */ + void debugPrint( const char* c ); +private: + /** reset instantiation */ + void processResetInstantiationRound( Theory::Effort effort ); + /** process at effort */ + int process( Node f, Theory::Effort effort, int e, int instLimit ); + /** add term to row */ + void addTermToRow( ArithVar x, Node n, Node& f, NodeBuilder<>& t ); + /** get delta for node */ + Node getDelta( Node n ); + + class Statistics { + public: + IntStat d_instantiations; + IntStat d_instantiations_minus; + IntStat d_instantiations_match_pure; + IntStat d_instantiations_match_var; + IntStat d_instantiations_match_no_var; + Statistics(); + ~Statistics(); + }; + Statistics d_statistics; +};/* class InstantiatiorTheoryArith */ + +} +} +} + +#endif
\ No newline at end of file diff --git a/src/theory/arrays/Makefile.am b/src/theory/arrays/Makefile.am index 3dde70145..57c55d765 100644 --- a/src/theory/arrays/Makefile.am +++ b/src/theory/arrays/Makefile.am @@ -15,6 +15,8 @@ libarrays_la_SOURCES = \ array_info.h \ array_info.cpp \ static_fact_manager.h \ - static_fact_manager.cpp + static_fact_manager.cpp \ + theory_arrays_instantiator.h \ + theory_arrays_instantiator.cpp EXTRA_DIST = kinds diff --git a/src/theory/arrays/kinds b/src/theory/arrays/kinds index 06240a315..195a60035 100644 --- a/src/theory/arrays/kinds +++ b/src/theory/arrays/kinds @@ -6,6 +6,7 @@ theory THEORY_ARRAY ::CVC4::theory::arrays::TheoryArrays "theory/arrays/theory_arrays.h" typechecker "theory/arrays/theory_arrays_type_rules.h" +instantiator ::CVC4::theory::arrays::InstantiatorTheoryArrays "theory/arrays/theory_arrays_instantiator.h" properties polite stable-infinite parametric properties check propagate presolve diff --git a/src/theory/arrays/theory_arrays.cpp b/src/theory/arrays/theory_arrays.cpp index 81661acd1..376a7e90f 100644 --- a/src/theory/arrays/theory_arrays.cpp +++ b/src/theory/arrays/theory_arrays.cpp @@ -23,6 +23,7 @@ #include <map> #include "theory/rewriter.h" #include "expr/command.h" +#include "theory/arrays/theory_arrays_instantiator.h" using namespace std; @@ -45,8 +46,8 @@ const bool d_solveWrite2 = false; const bool d_useNonLinearOpt = true; const bool d_eagerIndexSplitting = true; -TheoryArrays::TheoryArrays(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) : - Theory(THEORY_ARRAY, c, u, out, valuation, logicInfo), +TheoryArrays::TheoryArrays(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) : + Theory(THEORY_ARRAY, c, u, out, valuation, logicInfo, qe), d_numRow("theory::arrays::number of Row lemmas", 0), d_numExt("theory::arrays::number of Ext lemmas", 0), d_numProp("theory::arrays::number of propagations", 0), @@ -218,7 +219,7 @@ Node TheoryArrays::ppRewrite(TNode term) { } Node r1 = nm->mkNode(kind::SELECT, e1, index_i); - conc = (r1.getType() == nm->booleanType())? + conc = (r1.getType() == nm->booleanType())? r1.iffNode(write_i[2]) : r1.eqNode(write_i[2]); if (hyp.getNumChildren() != 0) { if (hyp.getNumChildren() == 1) { @@ -582,7 +583,6 @@ void TheoryArrays::computeCareGraph() // Get representative trigger terms TNode x_shared = d_equalityEngine.getTriggerTermRepresentative(x, THEORY_ARRAY); TNode y_shared = d_equalityEngine.getTriggerTermRepresentative(y, THEORY_ARRAY); - EqualityStatus eqStatusDomain = d_valuation.getEqualityStatus(x_shared, y_shared); switch (eqStatusDomain) { case EQUALITY_TRUE_AND_PROPAGATED: @@ -605,6 +605,7 @@ void TheoryArrays::computeCareGraph() break; } + // Otherwise, add this pair Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): adding to care-graph" << std::endl; addCarePair(x_shared, y_shared); diff --git a/src/theory/arrays/theory_arrays.h b/src/theory/arrays/theory_arrays.h index 80fe692c0..1bf42a105 100644 --- a/src/theory/arrays/theory_arrays.h +++ b/src/theory/arrays/theory_arrays.h @@ -39,32 +39,32 @@ namespace arrays { * Overview of decision procedure: * * Preliminary notation: - * Stores(a) = {t | a ~ t and t = store( _ _ _ )} + * Stores(a) = {t | a ~ t and t = store( _ _ _ )} * InStores(a) = {t | t = store (b _ _) and a ~ b } * Indices(a) = {i | there exists a term b[i] such that a ~ b or store(b i v)} * ~ represents the equivalence relation based on the asserted equalities in the * current context. - * + * * The rules implemented are the following: * store(b i v) * Row1 ------------------- * store(b i v)[i] = v - * + * * store(b i v) a'[j] * Row ---------------------- [ a' ~ store(b i v) or a' ~ b ] * i = j OR a[j] = b[j] - * + * * a b same kind arrays * Ext ------------------------ [ a!= b in current context, k new var] * a = b OR a[k] != b[k]p - * - * + * + * * The Row1 one rule is implemented implicitly as follows: * - for each store(b i v) term add the following equality to the congruence * closure store(b i v)[i] = v * - if one of the literals in a conflict is of the form store(b i v)[i] = v * remove it from the conflict - * + * * Because new store terms are not created, we need to check if we need to * instantiate a new Row axiom in the following cases: * 1. the congruence relation changes (i.e. two terms get merged) @@ -77,7 +77,7 @@ namespace arrays { * - this is implemented in the checkRowForIndex method which is called * when preregistering a term of the form a[i]. * - as a consequence lemmas are instantiated even before full effort check - * + * * The Ext axiom is instantiated when a disequality is asserted during full effort * check. Ext lemmas are stored in a cache to prevent instantiating essentially * the same lemma multiple times. @@ -122,7 +122,7 @@ class TheoryArrays : public Theory { public: - TheoryArrays(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo); + TheoryArrays(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe); ~TheoryArrays(); std::string identify() const { return std::string("TheoryArrays"); } @@ -133,6 +133,16 @@ class TheoryArrays : public Theory { private: + // PPNotifyClass: dummy template class for d_ppEqualityEngine - notifications not used + class PPNotifyClass { + public: + bool notify(TNode propagation) { return true; } + void notify(TNode t1, TNode t2) { } + }; + + /** The notify class for d_ppEqualityEngine */ + PPNotifyClass d_ppNotify; + /** Equaltity engine */ eq::EqualityEngine d_ppEqualityEngine; @@ -181,6 +191,15 @@ class TheoryArrays : public Theory { private: + class MayEqualNotifyClass { + public: + bool notify(TNode propagation) { return true; } + void notify(TNode t1, TNode t2) { } + }; + + /** The notify class for d_mayEqualEqualityEngine */ + MayEqualNotifyClass d_mayEqualNotify; + /** Equaltity engine for determining if two arrays might be equal */ eq::EqualityEngine d_mayEqualEqualityEngine; @@ -270,6 +289,11 @@ class TheoryArrays : public Theory { Debug("arrays::propagate") << spaces(d_arrays.getSatContext()->getLevel()) << "NotifyClass::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << ")" << std::endl; d_arrays.conflict(t1, t2); } + + void eqNotifyNewClass(TNode t) { } + void eqNotifyPreMerge(TNode t1, TNode t2) { } + void eqNotifyPostMerge(TNode t1, TNode t2) { } + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { } }; /** The notify class for d_equalityEngine */ diff --git a/src/theory/arrays/theory_arrays_instantiator.cpp b/src/theory/arrays/theory_arrays_instantiator.cpp new file mode 100644 index 000000000..334d70eea --- /dev/null +++ b/src/theory/arrays/theory_arrays_instantiator.cpp @@ -0,0 +1,56 @@ +/********************* */ +/*! \file theory_arrays_instantiator.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of theory_arrays_instantiator class + **/ + +#include "theory/theory_engine.h" +#include "theory/arrays/theory_arrays_instantiator.h" +#include "theory/arrays/theory_arrays.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::arrays; + +InstantiatorTheoryArrays::InstantiatorTheoryArrays(context::Context* c, QuantifiersEngine* ie, Theory* th) : +Instantiator( c, ie, th ){ + +} + +void InstantiatorTheoryArrays::preRegisterTerm( Node t ){ + +} + +void InstantiatorTheoryArrays::assertNode( Node assertion ){ + Debug("quant-arrays-assert") << "InstantiatorTheoryArrays::assertNode: " << assertion << std::endl; + d_quantEngine->addTermToDatabase( assertion ); + if( Options::current()->cbqi ){ + if( assertion.hasAttribute(InstConstantAttribute()) ){ + setHasConstraintsFrom( assertion.getAttribute(InstConstantAttribute()) ); + }else if( assertion.getKind()==NOT && assertion[0].hasAttribute(InstConstantAttribute()) ){ + setHasConstraintsFrom( assertion[0].getAttribute(InstConstantAttribute()) ); + } + } +} + + +void InstantiatorTheoryArrays::processResetInstantiationRound( Theory::Effort effort ){ + +} + +int InstantiatorTheoryArrays::process( Node f, Theory::Effort effort, int e, int limitInst ){ + return InstStrategy::STATUS_SAT; +} diff --git a/src/theory/arrays/theory_arrays_instantiator.h b/src/theory/arrays/theory_arrays_instantiator.h new file mode 100644 index 000000000..6a7c9c3ed --- /dev/null +++ b/src/theory/arrays/theory_arrays_instantiator.h @@ -0,0 +1,51 @@ +/********************* */ +/*! \file theory_arrays_instantiator.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Instantiator for theory of arrays + **/ + + +#include "cvc4_private.h" + +#ifndef __CVC4__INSTANTIATOR_THEORY_ARRAYS_H +#define __CVC4__INSTANTIATOR_THEORY_ARRAYS_H + +#include "theory/quantifiers_engine.h" + +namespace CVC4 { +namespace theory { +namespace arrays { + +class InstantiatorTheoryArrays : public Instantiator{ + friend class QuantifiersEngine; +protected: + /** reset instantiation round */ + void processResetInstantiationRound( Theory::Effort effort ); + /** process quantifier */ + int process( Node f, Theory::Effort effort, int e, int limitInst = 0 ); +public: + InstantiatorTheoryArrays(context::Context* c, QuantifiersEngine* ie, Theory* th); + ~InstantiatorTheoryArrays() {} + /** Pre-register a term. */ + void preRegisterTerm( Node t ); + /** assertNode function, assertion is asserted to theory */ + void assertNode( Node assertion ); + /** identify */ + std::string identify() const { return std::string("InstantiatorTheoryArrays"); } +};/* class Instantiatior */ + +} +} +} + +#endif
\ No newline at end of file diff --git a/src/theory/booleans/theory_bool.h b/src/theory/booleans/theory_bool.h index 99b5b6007..40783a6ce 100644 --- a/src/theory/booleans/theory_bool.h +++ b/src/theory/booleans/theory_bool.h @@ -30,8 +30,8 @@ namespace booleans { class TheoryBool : public Theory { public: - TheoryBool(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) : - Theory(THEORY_BOOL, c, u, out, valuation, logicInfo) { + TheoryBool(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) : + Theory(THEORY_BOOL, c, u, out, valuation, logicInfo, qe) { } Node getValue(TNode n); diff --git a/src/theory/builtin/theory_builtin.h b/src/theory/builtin/theory_builtin.h index 30d2aaca7..a13c69d9d 100644 --- a/src/theory/builtin/theory_builtin.h +++ b/src/theory/builtin/theory_builtin.h @@ -29,8 +29,8 @@ namespace builtin { class TheoryBuiltin : public Theory { public: - TheoryBuiltin(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) : - Theory(THEORY_BUILTIN, c, u, out, valuation, logicInfo) {} + TheoryBuiltin(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) : + Theory(THEORY_BUILTIN, c, u, out, valuation, logicInfo, qe) {} Node getValue(TNode n); std::string identify() const { return std::string("TheoryBuiltin"); } };/* class TheoryBuiltin */ diff --git a/src/theory/bv/bv_subtheory_eq.h b/src/theory/bv/bv_subtheory_eq.h index d4239ff13..01178b453 100644 --- a/src/theory/bv/bv_subtheory_eq.h +++ b/src/theory/bv/bv_subtheory_eq.h @@ -40,6 +40,10 @@ class EqualitySolver : public SubtheorySolver { bool eqNotifyTriggerPredicate(TNode predicate, bool value); bool eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value); void eqNotifyConstantTermMerge(TNode t1, TNode t2); + void eqNotifyNewClass(TNode t) { } + void eqNotifyPreMerge(TNode t1, TNode t2) { } + void eqNotifyPostMerge(TNode t1, TNode t2) { } + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { } }; diff --git a/src/theory/bv/theory_bv.cpp b/src/theory/bv/theory_bv.cpp index 30493737a..66f443d50 100644 --- a/src/theory/bv/theory_bv.cpp +++ b/src/theory/bv/theory_bv.cpp @@ -33,8 +33,8 @@ using namespace CVC4::theory::bv::utils; -TheoryBV::TheoryBV(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) - : Theory(THEORY_BV, c, u, out, valuation, logicInfo), +TheoryBV::TheoryBV(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) + : Theory(THEORY_BV, c, u, out, valuation, logicInfo, qe), d_context(c), d_alreadyPropagatedSet(c), d_sharedTermsSet(c), diff --git a/src/theory/bv/theory_bv.h b/src/theory/bv/theory_bv.h index 9c27f62c5..f79b7ab71 100644 --- a/src/theory/bv/theory_bv.h +++ b/src/theory/bv/theory_bv.h @@ -49,8 +49,8 @@ class TheoryBV : public Theory { EqualitySolver d_equalitySolver; public: - TheoryBV(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo); - ~TheoryBV(); + TheoryBV(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe); + ~TheoryBV(); void preRegisterTerm(TNode n); @@ -64,21 +64,21 @@ public: std::string identify() const { return std::string("TheoryBV"); } - PPAssertStatus ppAssert(TNode in, SubstitutionMap& outSubstitutions); + PPAssertStatus ppAssert(TNode in, SubstitutionMap& outSubstitutions); private: - + class Statistics { public: AverageStat d_avgConflictSize; IntStat d_solveSubstitutions; - TimerStat d_solveTimer; + TimerStat d_solveTimer; Statistics(); - ~Statistics(); - }; - + ~Statistics(); + }; + Statistics d_statistics; - + // Are we in conflict? context::CDO<bool> d_conflict; @@ -133,7 +133,7 @@ private: } bool inConflict() { return d_conflict == true; } - + friend class Bitblaster; friend class BitblastSolver; friend class EqualitySolver; diff --git a/src/theory/datatypes/Makefile.am b/src/theory/datatypes/Makefile.am index f8bfa3dc5..d6622b19a 100644 --- a/src/theory/datatypes/Makefile.am +++ b/src/theory/datatypes/Makefile.am @@ -13,6 +13,8 @@ libdatatypes_la_SOURCES = \ union_find.h \ union_find.cpp \ explanation_manager.h \ - explanation_manager.cpp + explanation_manager.cpp \ + theory_datatypes_instantiator.h \ + theory_datatypes_instantiator.cpp EXTRA_DIST = kinds diff --git a/src/theory/datatypes/kinds b/src/theory/datatypes/kinds index 7acb6d17d..4b6bfd8f6 100644 --- a/src/theory/datatypes/kinds +++ b/src/theory/datatypes/kinds @@ -6,6 +6,7 @@ theory THEORY_DATATYPES ::CVC4::theory::datatypes::TheoryDatatypes "theory/datatypes/theory_datatypes.h" typechecker "theory/datatypes/theory_datatypes_type_rules.h" +instantiator ::CVC4::theory::datatypes::InstantiatorTheoryDatatypes "theory/datatypes/theory_datatypes_instantiator.h" properties check presolve parametric diff --git a/src/theory/datatypes/theory_datatypes.cpp b/src/theory/datatypes/theory_datatypes.cpp index 7b1562ada..3b8efabb7 100644 --- a/src/theory/datatypes/theory_datatypes.cpp +++ b/src/theory/datatypes/theory_datatypes.cpp @@ -22,6 +22,7 @@ #include "expr/kind.h" #include "util/datatype.h" #include "util/Assert.h" +#include "theory/datatypes/theory_datatypes_instantiator.h" #include <map> @@ -53,8 +54,8 @@ Node TheoryDatatypes::getConstructorForSelector( Node sel ) } -TheoryDatatypes::TheoryDatatypes(Context* c, UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) : - Theory(THEORY_DATATYPES, c, u, out, valuation, logicInfo), +TheoryDatatypes::TheoryDatatypes(Context* c, UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) : + Theory(THEORY_DATATYPES, c, u, out, valuation, logicInfo, qe), d_currAsserts(c), d_currEqualities(c), d_selectors(c), @@ -71,7 +72,6 @@ TheoryDatatypes::TheoryDatatypes(Context* c, UserContext* u, OutputChannel& out, d_disequalities(c), d_em(c), d_cce(&d_cc){ - } @@ -114,7 +114,7 @@ void TheoryDatatypes::check(Effort e) { Node assertion = get(); if( Debug.isOn("datatypes") || Debug.isOn("datatypes-split") || Debug.isOn("datatypes-cycles") || Debug.isOn("datatypes-debug-pf") || Debug.isOn("datatypes-conflict") ) { - cout << "*** TheoryDatatypes::check(): " << assertion << endl; + Notice() << "*** TheoryDatatypes::check(): " << assertion << endl; d_currAsserts.push_back( assertion ); } @@ -211,7 +211,7 @@ void TheoryDatatypes::check(Effort e) { Node conflict = d_em.getConflict(); if( Debug.isOn("datatypes") || Debug.isOn("datatypes-split") || Debug.isOn("datatypes-cycles") || Debug.isOn("datatypes-conflict") ){ - cout << "Conflict constructed : " << conflict << endl; + Notice() << "Conflict constructed : " << conflict << endl; } if( conflict.getKind()!=kind::AND ){ conflict = NodeManager::currentNM()->mkNode(kind::AND, conflict, conflict); @@ -283,7 +283,7 @@ void TheoryDatatypes::check(Effort e) { } } if( Debug.isOn("datatypes") || Debug.isOn("datatypes-split") ) { - cout << "TheoryDatatypes::check(): done" << endl; + Notice() << "TheoryDatatypes::check(): done" << endl; } } diff --git a/src/theory/datatypes/theory_datatypes.h b/src/theory/datatypes/theory_datatypes.h index 967000c3e..5a4135a3b 100644 --- a/src/theory/datatypes/theory_datatypes.h +++ b/src/theory/datatypes/theory_datatypes.h @@ -37,7 +37,10 @@ namespace CVC4 { namespace theory { namespace datatypes { +class InstantiatorTheoryDatatypes; + class TheoryDatatypes : public Theory { + friend class InstantiatorTheoryDatatypes; private: typedef context::CDChunkList<TNode> EqList; typedef context::CDHashMap<Node, EqList*, NodeHashFunction> EqLists; @@ -53,7 +56,7 @@ private: BoolMap d_selectors; /** keeps track of which nodes are representatives */ BoolMap d_reps; - /** map from (representative) nodes to a list of selectors whose arguments are + /** map from (representative) nodes to a list of selectors whose arguments are in the equivalence class of that node */ EqListsN d_selector_eq; /** map from (representative) nodes to list of nodes in their eq class */ @@ -140,7 +143,7 @@ private: CongruenceClosureExplainer<CongruenceChannel, CONGRUENCE_OPERATORS_2 (kind::APPLY_CONSTRUCTOR, kind::APPLY_SELECTOR)> d_cce; public: - TheoryDatatypes(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo); + TheoryDatatypes(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe); ~TheoryDatatypes(); void preRegisterTerm(TNode n); void presolve(); @@ -167,7 +170,7 @@ private: /* from uf_morgan */ void merge(TNode a, TNode b); - inline TNode find(TNode a); + inline TNode find(TNode a); inline TNode debugFind(TNode a) const; void appendToDiseqList(TNode of, TNode eq); void addDisequality(TNode eq); @@ -179,8 +182,8 @@ private: NodeBuilder<>& explanation ); };/* class TheoryDatatypes */ -inline bool TheoryDatatypes::hasConflict() { - return d_em.hasConflict(); +inline bool TheoryDatatypes::hasConflict() { + return d_em.hasConflict(); } inline TNode TheoryDatatypes::find(TNode a) { diff --git a/src/theory/datatypes/theory_datatypes_instantiator.cpp b/src/theory/datatypes/theory_datatypes_instantiator.cpp new file mode 100644 index 000000000..6a32466e4 --- /dev/null +++ b/src/theory/datatypes/theory_datatypes_instantiator.cpp @@ -0,0 +1,158 @@ +/********************* */ +/*! \file theory_datatypes_instantiator.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of theory_datatypes_instantiator class + **/ + +#include "theory/datatypes/theory_datatypes_instantiator.h" +#include "theory/datatypes/theory_datatypes.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::datatypes; + +InstantiatorTheoryDatatypes::InstantiatorTheoryDatatypes(context::Context* c, QuantifiersEngine* ie, Theory* th) : +Instantiator( c, ie, th ){ + +} + +void InstantiatorTheoryDatatypes::assertNode( Node assertion ){ + Debug("quant-datatypes-assert") << "InstantiatorTheoryDatatypes::check: " << assertion << std::endl; + d_quantEngine->addTermToDatabase( assertion ); + if( Options::current()->cbqi ){ + if( assertion.hasAttribute(InstConstantAttribute()) ){ + setHasConstraintsFrom( assertion.getAttribute(InstConstantAttribute()) ); + }else if( assertion.getKind()==NOT && assertion[0].hasAttribute(InstConstantAttribute()) ){ + setHasConstraintsFrom( assertion[0].getAttribute(InstConstantAttribute()) ); + } + } +} + +void InstantiatorTheoryDatatypes::processResetInstantiationRound( Theory::Effort effort ){ + +} + + +int InstantiatorTheoryDatatypes::process( Node f, Theory::Effort effort, int e, int limitInst ){ + Debug("quant-datatypes") << "Datatypes: Try to solve (" << e << ") for " << f << "... " << std::endl; + if( Options::current()->cbqi ){ + if( e<2 ){ + return InstStrategy::STATUS_UNFINISHED; + }else if( e==2 ){ + InstMatch m; + for( int j = 0; j<(int)d_quantEngine->getNumInstantiationConstants( f ); j++ ){ + Node i = d_quantEngine->getInstantiationConstant( f, j ); + if( i.getType().isDatatype() ){ + Node n = getValueFor( i ); + Debug("quant-datatypes-debug") << "Value for " << i << " is " << n << std::endl; + m.d_map[ i ] = n; + } + } + d_quantEngine->addInstantiation( f, m ); + } + } + return InstStrategy::STATUS_UNKNOWN; +} + +Node InstantiatorTheoryDatatypes::getValueFor( Node n ){ + //simply get the ground value for n in the current model, if it exists, + // or return an arbitrary ground term otherwise + Debug("quant-datatypes-debug") << "get value for " << n << std::endl; + if( !n.hasAttribute(InstConstantAttribute()) ){ + return n; + }else{ + Assert( n.getType().isDatatype() ); + //check if in equivalence class with ground term + Node rep = getRepresentative( n ); + Debug("quant-datatypes-debug") << "Rep is " << rep << std::endl; + if( !rep.hasAttribute(InstConstantAttribute()) ){ + return rep; + }else{ + if( !n.getType().isDatatype() ){ + return n.getType().mkGroundTerm(); + }else{ + if( n.getKind()==APPLY_CONSTRUCTOR ){ + std::vector< Node > children; + children.push_back( n.getOperator() ); + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + children.push_back( getValueFor( n[i] ) ); + } + return NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, children ); + }else{ + const Datatype& dt = ((DatatypeType)(n.getType()).toType()).getDatatype(); + TheoryDatatypes::EqLists* labels = &((TheoryDatatypes*)d_th)->d_labels; + //otherwise, use which constructor the inst constant is current chosen to be + if( labels->find( n )!=labels->end() ){ + TheoryDatatypes::EqList* lbl = (*labels->find( n )).second; + int tIndex = -1; + if( !lbl->empty() && (*lbl)[ lbl->size()-1 ].getKind()==APPLY_TESTER ){ + Debug("quant-datatypes-debug") << n << " tester is " << (*lbl)[ lbl->size()-1 ] << std::endl; + tIndex = Datatype::indexOf((*lbl)[ lbl->size()-1 ].getOperator().toExpr()); + }else{ + Debug("quant-datatypes-debug") << "find possible tester choice" << std::endl; + //must find a possible choice + vector< bool > possibleCons; + possibleCons.resize( dt.getNumConstructors(), true ); + for( TheoryDatatypes::EqList::const_iterator j = lbl->begin(); j != lbl->end(); j++ ) { + Node leqn = (*j); + possibleCons[ Datatype::indexOf( leqn[0].getOperator().toExpr() ) ] = false; + } + for( unsigned int j=0; j<possibleCons.size(); j++ ) { + if( possibleCons[j] ){ + tIndex = j; + break; + } + } + } + Assert( tIndex!=-1 ); + Node cons = Node::fromExpr( dt[ tIndex ].getConstructor() ); + Debug("quant-datatypes-debug") << n << " cons is " << cons << std::endl; + std::vector< Node > children; + children.push_back( cons ); + for( int i=0; i<(int)dt[ tIndex ].getNumArgs(); i++ ) { + Node sn = NodeManager::currentNM()->mkNode( APPLY_SELECTOR, Node::fromExpr( dt[tIndex][i].getSelector() ), n ); + if( n.hasAttribute(InstConstantAttribute()) ){ + InstConstantAttribute ica; + sn.setAttribute(ica,n.getAttribute(InstConstantAttribute()) ); + } + Node snn = getValueFor( sn ); + children.push_back( snn ); + } + return NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, children ); + }else{ + return n.getType().mkGroundTerm(); + } + } + } + } + } +} + +Node InstantiatorTheoryDatatypes::getRepresentative( Node n ){ + return ((TheoryDatatypes*)d_th)->find( n ); +} + +InstantiatorTheoryDatatypes::Statistics::Statistics(): + d_instantiations("InstantiatorTheoryDatatypes::Instantiations_Total", 0) +{ + StatisticsRegistry::registerStat(&d_instantiations); +} + +InstantiatorTheoryDatatypes::Statistics::~Statistics(){ + StatisticsRegistry::unregisterStat(&d_instantiations); +} + diff --git a/src/theory/datatypes/theory_datatypes_instantiator.h b/src/theory/datatypes/theory_datatypes_instantiator.h new file mode 100644 index 000000000..5c52f7f48 --- /dev/null +++ b/src/theory/datatypes/theory_datatypes_instantiator.h @@ -0,0 +1,64 @@ +/********************* */ +/*! \file instantiator_datatypes_instantiator.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief instantiator_datatypes_instantiator + **/ + + +#include "cvc4_private.h" + +#ifndef __CVC4__INSTANTIATOR_DATATYPES_H +#define __CVC4__INSTANTIATOR_DATATYPES_H + +#include "theory/quantifiers_engine.h" + +#include "util/stats.h" + +namespace CVC4 { +namespace theory { +namespace datatypes { + +class InstantiatorTheoryDatatypes : public Instantiator{ + friend class QuantifiersEngine; +public: + InstantiatorTheoryDatatypes(context::Context* c, QuantifiersEngine* ie, Theory* th); + ~InstantiatorTheoryDatatypes() {} + + /** assertNode function, assertion is asserted to theory */ + void assertNode( Node assertion ); + /** identify */ + std::string identify() const { return std::string("InstantiatorTheoryDatatypes"); } +private: + /** reset instantiation */ + void processResetInstantiationRound( Theory::Effort effort ); + /** process at effort */ + int process( Node f, Theory::Effort effort, int e, int limitInst ); + /** get value for */ + Node getValueFor( Node n ); + /** get representative */ + Node getRepresentative( Node n ); + + class Statistics { + public: + IntStat d_instantiations; + Statistics(); + ~Statistics(); + }; + Statistics d_statistics; +};/* class InstantiatiorTheoryDatatypes */ + +} +} +} + +#endif
\ No newline at end of file diff --git a/src/theory/example/theory_uf_tim.cpp b/src/theory/example/theory_uf_tim.cpp index 03787703a..638a03478 100644 --- a/src/theory/example/theory_uf_tim.cpp +++ b/src/theory/example/theory_uf_tim.cpp @@ -27,8 +27,8 @@ using namespace CVC4::theory; using namespace CVC4::theory::uf; using namespace CVC4::theory::uf::tim; -TheoryUFTim::TheoryUFTim(Context* c, UserContext* u, OutputChannel& out, Valuation valuation) : - Theory(THEORY_UF, c, u, out, valuation), +TheoryUFTim::TheoryUFTim(Context* c, UserContext* u, OutputChannel& out, Valuation valuation, QuantifiersEngine* qe) : + Theory(THEORY_UF, c, u, out, valuation, qe), d_assertions(c), d_pending(c), d_currentPendingIdx(c,0), diff --git a/src/theory/example/theory_uf_tim.h b/src/theory/example/theory_uf_tim.h index b47536f07..41e58349a 100644 --- a/src/theory/example/theory_uf_tim.h +++ b/src/theory/example/theory_uf_tim.h @@ -85,7 +85,7 @@ private: public: /** Constructs a new instance of TheoryUF w.r.t. the provided context.*/ - TheoryUFTim(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation); + TheoryUFTim(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, QuantifiersEngine* qe); /** Destructor for the TheoryUF object. */ ~TheoryUFTim(); diff --git a/src/theory/inst_match.cpp b/src/theory/inst_match.cpp new file mode 100644 index 000000000..e340da75d --- /dev/null +++ b/src/theory/inst_match.cpp @@ -0,0 +1,903 @@ +/********************* */ +/*! \file inst_match.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of inst match class + **/ + +#include "theory/inst_match.h" +#include "theory/theory_engine.h" +#include "theory/quantifiers_engine.h" +#include "theory/uf/theory_uf_instantiator.h" +#include "theory/uf/theory_uf_candidate_generator.h" +#include "theory/uf/equality_engine.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; + + +bool CandidateGenerator::isLegalCandidate( Node n ){ + return !n.getAttribute(NoMatchAttribute()) && ( !Options::current()->cbqi || !n.hasAttribute(InstConstantAttribute()) ); +} + +void CandidateGeneratorQueue::addCandidate( Node n ) { + if( isLegalCandidate( n ) ){ + d_candidates.push_back( n ); + } +} + +void CandidateGeneratorQueue::reset( Node eqc ){ + if( d_candidate_index>0 ){ + d_candidates.erase( d_candidates.begin(), d_candidates.begin() + d_candidate_index ); + d_candidate_index = 0; + } + if( !eqc.isNull() ){ + d_candidates.push_back( eqc ); + } +} +Node CandidateGeneratorQueue::getNextCandidate(){ + if( d_candidate_index<(int)d_candidates.size() ){ + Node n = d_candidates[d_candidate_index]; + d_candidate_index++; + return n; + }else{ + d_candidate_index = 0; + d_candidates.clear(); + return Node::null(); + } +} + +InstMatch::InstMatch() { +} + +InstMatch::InstMatch( InstMatch* m ) { + d_map = m->d_map; +} + +bool InstMatch::setMatch( EqualityQuery* q, Node v, Node m ){ + if( d_map.find( v )==d_map.end() ){ + d_map[v] = m; + Debug("matching-debug") << "Add partial " << v << "->" << m << std::endl; + return true; + }else{ + return q->areEqual( d_map[v], m ); + } +} + +bool InstMatch::add( InstMatch& m ){ + for( std::map< Node, Node >::iterator it = m.d_map.begin(); it != m.d_map.end(); ++it ){ + if( d_map.find( it->first )==d_map.end() ){ + d_map[it->first] = it->second; + } + } + return true; +} + +bool InstMatch::merge( EqualityQuery* q, InstMatch& m ){ + for( std::map< Node, Node >::iterator it = m.d_map.begin(); it != m.d_map.end(); ++it ){ + if( d_map.find( it->first )==d_map.end() ){ + d_map[ it->first ] = it->second; + }else{ + if( it->second!=d_map[it->first] ){ + if( !q->areEqual( it->second, d_map[it->first] ) ){ + d_map.clear(); + return false; + } + } + } + } + return true; +} + +void InstMatch::debugPrint( const char* c ){ + for( std::map< Node, Node >::iterator it = d_map.begin(); it != d_map.end(); ++it ){ + Debug( c ) << " " << it->first << " -> " << it->second << std::endl; + } + //if( !d_splits.empty() ){ + // Debug( c ) << " Conditions: "; + // for( std::map< Node, Node >::iterator it = d_splits.begin(); it !=d_splits.end(); ++it ){ + // Debug( c ) << it->first << " = " << it->second << " "; + // } + // Debug( c ) << std::endl; + //} +} + +void InstMatch::makeComplete( Node f, QuantifiersEngine* qe ){ + for( int i=0; i<(int)qe->d_inst_constants[f].size(); i++ ){ + if( d_map.find( qe->d_inst_constants[f][i] )==d_map.end() ){ + d_map[ qe->d_inst_constants[f][i] ] = qe->getFreeVariableForInstConstant( qe->d_inst_constants[f][i] ); + } + } +} + +void InstMatch::makeInternal( QuantifiersEngine* qe ){ + for( std::map< Node, Node >::iterator it = d_map.begin(); it != d_map.end(); ++it ){ + if( Options::current()->cbqi && it->second.hasAttribute(InstConstantAttribute()) ){ + d_map[ it->first ] = qe->getEqualityQuery()->getInternalRepresentative( it->second ); + if( Options::current()->cbqi && it->second.hasAttribute(InstConstantAttribute()) ){ + d_map[ it->first ] = qe->getFreeVariableForInstConstant( it->first ); + } + } + } +} + +void InstMatch::makeRepresentative( QuantifiersEngine* qe ){ + for( std::map< Node, Node >::iterator it = d_map.begin(); it != d_map.end(); ++it ){ + d_map[ it->first ] = qe->getEqualityQuery()->getInternalRepresentative( it->second ); + if( Options::current()->cbqi && it->second.hasAttribute(InstConstantAttribute()) ){ + d_map[ it->first ] = qe->getFreeVariableForInstConstant( it->first ); + } + } +} + +void InstMatch::applyRewrite(){ + for( std::map< Node, Node >::iterator it = d_map.begin(); it != d_map.end(); ++it ){ + it->second = Rewriter::rewrite(it->second); + } +} + +void InstMatch::computeTermVec( QuantifiersEngine* qe, const std::vector< Node >& vars, std::vector< Node >& match ){ + for( int i=0; i<(int)vars.size(); i++ ){ + std::map< Node, Node >::iterator it = d_map.find( vars[i] ); + if( it!=d_map.end() && !it->second.isNull() ){ + match.push_back( it->second ); + }else{ + match.push_back( qe->getFreeVariableForInstConstant( vars[i] ) ); + } + } +} +void InstMatch::computeTermVec( const std::vector< Node >& vars, std::vector< Node >& match ){ + for( int i=0; i<(int)vars.size(); i++ ){ + match.push_back( d_map[ vars[i] ] ); + } +} + + +/** add match m for quantifier f starting at index, take into account equalities q, return true if successful */ +void InstMatchTrie::addInstMatch2( QuantifiersEngine* qe, Node f, InstMatch& m, int index, ImtIndexOrder* imtio ){ + if( long(index)<f[0].getNumChildren() && ( !imtio || long(index)<imtio->d_order.size() ) ){ + int i_index = imtio ? imtio->d_order[index] : index; + Node n = m.d_map[ qe->getInstantiationConstant( f, i_index ) ]; + d_data[n].addInstMatch2( qe, f, m, index+1, imtio ); + } +} + +/** exists match */ +bool InstMatchTrie::existsInstMatch( QuantifiersEngine* qe, Node f, InstMatch& m, bool modEq, int index, ImtIndexOrder* imtio ){ + if( long(index)==f[0].getNumChildren() || ( imtio && long(index)==imtio->d_order.size() ) ){ + return true; + }else{ + int i_index = imtio ? imtio->d_order[index] : index; + Node n = m.d_map[ qe->getInstantiationConstant( f, i_index ) ]; + std::map< Node, InstMatchTrie >::iterator it = d_data.find( n ); + if( it!=d_data.end() ){ + if( it->second.existsInstMatch( qe, f, m, modEq, index+1, imtio ) ){ + return true; + } + } + if( modEq ){ + //check modulo equality if any other instantiation match exists + if( ((uf::TheoryUF*)qe->getTheoryEngine()->getTheory( THEORY_UF ))->getEqualityEngine()->hasTerm( n ) ){ + eq::EqClassIterator eqc( qe->getEqualityQuery()->getRepresentative( n ), + ((uf::TheoryUF*)qe->getTheoryEngine()->getTheory( THEORY_UF ))->getEqualityEngine() ); + while( !eqc.isFinished() ){ + Node en = (*eqc); + if( en!=n ){ + std::map< Node, InstMatchTrie >::iterator itc = d_data.find( en ); + if( itc!=d_data.end() ){ + if( itc->second.existsInstMatch( qe, f, m, modEq, index+1, imtio ) ){ + return true; + } + } + } + ++eqc; + } + } + //for( std::map< Node, InstMatchTrie >::iterator itc = d_data.begin(); itc != d_data.end(); ++itc ){ + // if( itc->first!=n && qe->getEqualityQuery()->areEqual( n, itc->first ) ){ + // if( itc->second.existsInstMatch( qe, f, m, modEq, index+1 ) ){ + // return true; + // } + // } + //} + } + return false; + } +} + +bool InstMatchTrie::addInstMatch( QuantifiersEngine* qe, Node f, InstMatch& m, bool modEq, ImtIndexOrder* imtio ){ + if( !existsInstMatch( qe, f, m, modEq, 0, imtio ) ){ + addInstMatch2( qe, f, m, 0, imtio ); + return true; + }else{ + return false; + } +} + +InstMatchGenerator::InstMatchGenerator( Node pat, QuantifiersEngine* qe, int matchPolicy ) : d_matchPolicy( matchPolicy ){ + initializePattern( pat, qe ); +} + +InstMatchGenerator::InstMatchGenerator( std::vector< Node >& pats, QuantifiersEngine* qe, int matchPolicy ) : d_matchPolicy( matchPolicy ){ + if( pats.size()==1 ){ + initializePattern( pats[0], qe ); + }else{ + initializePatterns( pats, qe ); + } +} + +void InstMatchGenerator::initializePatterns( std::vector< Node >& pats, QuantifiersEngine* qe ){ + int childMatchPolicy = d_matchPolicy==MATCH_GEN_EFFICIENT_E_MATCH ? 0 : d_matchPolicy; + for( int i=0; i<(int)pats.size(); i++ ){ + d_children.push_back( new InstMatchGenerator( pats[i], qe, childMatchPolicy ) ); + } + d_pattern = Node::null(); + d_match_pattern = Node::null(); + d_cg = NULL; +} + +void InstMatchGenerator::initializePattern( Node pat, QuantifiersEngine* qe ){ + Debug("inst-match-gen") << "Pattern term is " << pat << std::endl; + Assert( pat.hasAttribute(InstConstantAttribute()) ); + d_pattern = pat; + d_match_pattern = pat; + 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()==IFF || d_match_pattern.getKind()==EQUAL ){ + if( !d_match_pattern[0].hasAttribute(InstConstantAttribute()) ){ + Assert( d_match_pattern[1].hasAttribute(InstConstantAttribute()) ); + //swap sides + d_pattern = NodeManager::currentNM()->mkNode( d_match_pattern.getKind(), d_match_pattern[1], d_match_pattern[0] ); + d_pattern = pat.getKind()==NOT ? d_pattern.notNode() : d_pattern; + if( pat.getKind()!=NOT ){ //TEMPORARY until we do better implementation of disequality matching + d_match_pattern = d_match_pattern[1]; + }else{ + d_match_pattern = d_pattern[0][0]; + } + }else if( !d_match_pattern[1].hasAttribute(InstConstantAttribute()) ){ + Assert( d_match_pattern[0].hasAttribute(InstConstantAttribute()) ); + if( pat.getKind()!=NOT ){ //TEMPORARY until we do better implementation of disequality matching + d_match_pattern = d_match_pattern[0]; + } + } + } + int childMatchPolicy = MATCH_GEN_DEFAULT; + for( int i=0; i<(int)d_match_pattern.getNumChildren(); i++ ){ + if( d_match_pattern[i].hasAttribute(InstConstantAttribute()) ){ + if( d_match_pattern[i].getKind()!=INST_CONSTANT ){ + d_children.push_back( new InstMatchGenerator( d_match_pattern[i], qe, childMatchPolicy ) ); + d_children_index.push_back( i ); + } + } + } + + Debug("inst-match-gen") << "Pattern is " << d_pattern << ", match pattern is " << d_match_pattern << std::endl; + + //get the equality engine + Theory* th_uf = qe->getTheoryEngine()->getTheory( theory::THEORY_UF ); + uf::InstantiatorTheoryUf* ith = (uf::InstantiatorTheoryUf*)th_uf->getInstantiator(); + //create candidate generator + if( d_match_pattern.getKind()==EQUAL || d_match_pattern.getKind()==IFF ){ + Assert( d_matchPolicy==MATCH_GEN_DEFAULT ); + //we will be producing candidates via literal matching heuristics + if( d_pattern.getKind()!=NOT ){ + //candidates will be all equalities + d_cg = new uf::CandidateGeneratorTheoryUfLitEq( ith, d_match_pattern ); + }else{ + //candidates will be all disequalities + d_cg = new uf::CandidateGeneratorTheoryUfLitDeq( ith, d_match_pattern ); + } + }else if( d_pattern.getKind()==EQUAL || d_pattern.getKind()==IFF || d_pattern.getKind()==NOT ){ + Assert( d_matchPolicy==MATCH_GEN_DEFAULT ); + if( d_pattern.getKind()==NOT ){ + Unimplemented("Disequal generator unimplemented"); + }else{ + Assert( Trigger::isAtomicTrigger( d_match_pattern ) ); + //we are matching only in a particular equivalence class + d_cg = new uf::CandidateGeneratorTheoryUf( ith, d_match_pattern.getOperator() ); + //store the equivalence class that we will call d_cg->reset( ... ) on + d_eq_class = d_pattern[1]; + } + }else if( Trigger::isAtomicTrigger( d_match_pattern ) ){ + if( d_matchPolicy==MATCH_GEN_EFFICIENT_E_MATCH ){ + //we will manually add candidates to queue + d_cg = new CandidateGeneratorQueue; + //register this candidate generator + ith->registerCandidateGenerator( d_cg, d_match_pattern ); + }else{ + //we will be scanning lists trying to find d_match_pattern.getOperator() + d_cg = new uf::CandidateGeneratorTheoryUf( ith, d_match_pattern.getOperator() ); + } + }else{ + d_cg = new CandidateGeneratorQueue; + if( !Trigger::getPatternArithmetic( d_match_pattern.getAttribute(InstConstantAttribute()), d_match_pattern, d_arith_coeffs ) ){ + Debug("inst-match-gen") << "(?) Unknown matching pattern is " << d_match_pattern << std::endl; + Warning() << "(?) Unknown matching pattern is " << d_match_pattern << std::endl; + d_matchPolicy = MATCH_GEN_INTERNAL_ERROR; + }else{ + Debug("matching-arith") << "Generated arithmetic pattern for " << d_match_pattern << ": " << std::endl; + for( std::map< Node, Node >::iterator it = d_arith_coeffs.begin(); it != d_arith_coeffs.end(); ++it ){ + Debug("matching-arith") << " " << it->first << " -> " << it->second << std::endl; + } + //we will treat this as match gen internal arithmetic + d_matchPolicy = MATCH_GEN_INTERNAL_ARITHMETIC; + } + } +} + +/** get match (not modulo equality) */ +bool InstMatchGenerator::getMatch( Node t, InstMatch& m, QuantifiersEngine* qe ){ + Debug("matching") << "Matching " << t << " against pattern " << d_match_pattern << " (" + << m.d_map.size() << ")" << ", " << d_children.size() << std::endl; + Assert( !d_match_pattern.isNull() ); + if( d_matchPolicy==MATCH_GEN_INTERNAL_ARITHMETIC ){ + return getMatchArithmetic( t, m, qe ); + }else if( d_matchPolicy==MATCH_GEN_INTERNAL_ERROR ){ + return false; + }else{ + EqualityQuery* q = qe->getEqualityQuery(); + //add m to partial match vector + std::vector< InstMatch > partial; + partial.push_back( InstMatch( &m ) ); + //if t is null + Assert( !t.isNull() ); + Assert( !t.hasAttribute(InstConstantAttribute()) ); + Assert( 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 + for( int i=0; i<(int)d_match_pattern.getNumChildren(); i++ ){ + if( d_match_pattern[i].hasAttribute(InstConstantAttribute()) ){ + if( d_match_pattern[i].getKind()==INST_CONSTANT ){ + if( !partial[0].setMatch( q, d_match_pattern[i], t[i] ) ){ + //match is in conflict + Debug("matching-debug") << "Match in conflict " << t[i] << " and " + << d_match_pattern[i] << " because " + << partial[0].d_map[d_match_pattern[i]] + << std::endl; + Debug("matching-fail") << "Match fail: " << partial[0].d_map[d_match_pattern[i]] << " and " << t[i] << std::endl; + return false; + } + } + }else{ + if( !q->areEqual( d_match_pattern[i], t[i] ) ){ + Debug("matching-fail") << "Match fail arg: " << d_match_pattern[i] << " and " << t[i] << std::endl; + //ground arguments are not equal + return false; + } + } + } + //now, fit children into match + //we will be requesting candidates for matching terms for each child + std::vector< Node > reps; + for( int i=0; i<(int)d_children.size(); i++ ){ + Node rep = q->getRepresentative( t[ d_children_index[i] ] ); + reps.push_back( rep ); + d_children[i]->d_cg->reset( rep ); + } + + //combine child matches + int index = 0; + while( index>=0 && index<(int)d_children.size() ){ + partial.push_back( InstMatch( &partial[index] ) ); + if( d_children[index]->getNextMatch2( partial[index+1], qe ) ){ + index++; + }else{ + d_children[index]->d_cg->reset( reps[index] ); + partial.pop_back(); + if( !partial.empty() ){ + partial.pop_back(); + } + index--; + } + } + if( index>=0 ){ + m = partial.back(); + return true; + }else{ + return false; + } + } +} + +bool InstMatchGenerator::getNextMatch2( InstMatch& m, QuantifiersEngine* qe, bool saveMatched ){ + bool success = false; + Node t; + do{ + //get the next candidate term t + t = d_cg->getNextCandidate(); + //if t not null, try to fit it into match m + if( !t.isNull() && t.getType()==d_match_pattern.getType() ){ + //Assert( t.getType()==d_match_pattern.getType() ); + success = getMatch( t, m, qe ); + } + }while( !success && !t.isNull() ); + if (saveMatched) m.d_matched = t; + return success; +} + +bool InstMatchGenerator::getMatchArithmetic( Node t, InstMatch& m, QuantifiersEngine* qe ){ + Debug("matching-arith") << "Matching " << t << " " << d_match_pattern << std::endl; + if( !d_arith_coeffs.empty() ){ + NodeBuilder<> tb(kind::PLUS); + Node ic = Node::null(); + for( std::map< Node, Node >::iterator it = d_arith_coeffs.begin(); it != d_arith_coeffs.end(); ++it ){ + Debug("matching-arith") << it->first << " -> " << it->second << std::endl; + if( !it->first.isNull() ){ + if( m.d_map.find( it->first )==m.d_map.end() ){ + //see if we can choose this to set + if( ic.isNull() && ( it->second.isNull() || !it->first.getType().isInteger() ) ){ + ic = it->first; + } + }else{ + Debug("matching-arith") << "already set " << m.d_map[ it->first ] << std::endl; + Node tm = m.d_map[ it->first ]; + if( !it->second.isNull() ){ + tm = NodeManager::currentNM()->mkNode( MULT, it->second, tm ); + } + tb << tm; + } + }else{ + tb << it->second; + } + } + if( !ic.isNull() ){ + Node tm; + if( tb.getNumChildren()==0 ){ + tm = t; + }else{ + tm = tb.getNumChildren()==1 ? tb.getChild( 0 ) : tb; + tm = NodeManager::currentNM()->mkNode( MINUS, t, tm ); + } + if( !d_arith_coeffs[ ic ].isNull() ){ + Assert( !ic.getType().isInteger() ); + Node coeff = NodeManager::currentNM()->mkConst( Rational(1) / d_arith_coeffs[ ic ].getConst<Rational>() ); + tm = NodeManager::currentNM()->mkNode( MULT, coeff, tm ); + } + m.d_map[ ic ] = Rewriter::rewrite( tm ); + //set the rest to zeros + for( std::map< Node, Node >::iterator it = d_arith_coeffs.begin(); it != d_arith_coeffs.end(); ++it ){ + if( !it->first.isNull() ){ + if( m.d_map.find( it->first )==m.d_map.end() ){ + m.d_map[ it->first ] = NodeManager::currentNM()->mkConst( Rational(0) ); + } + } + } + Debug("matching-arith") << "Setting " << ic << " to " << tm << std::endl; + return true; + }else{ + return false; + } + }else{ + return false; + } +} + + +/** reset instantiation round */ +void InstMatchGenerator::resetInstantiationRound( QuantifiersEngine* qe ){ + if( d_match_pattern.isNull() ){ + for( int i=0; i<(int)d_children.size(); i++ ){ + d_children[i]->resetInstantiationRound( qe ); + } + }else{ + if( d_cg ){ + d_cg->resetInstantiationRound(); + } + } +} + +void InstMatchGenerator::reset( Node eqc, QuantifiersEngine* qe ){ + if( d_match_pattern.isNull() ){ + for( int i=0; i<(int)d_children.size(); i++ ){ + d_children[i]->reset( eqc, qe ); + } + d_partial.clear(); + }else{ + if( !d_eq_class.isNull() ){ + //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 ); + }else{ + d_cg->reset( eqc ); + } + } +} + +bool InstMatchGenerator::getNextMatch( InstMatch& m, QuantifiersEngine* qe ){ + m.d_matched = Node::null(); + if( d_match_pattern.isNull() ){ + int index = (int)d_partial.size(); + while( index>=0 && index<(int)d_children.size() ){ + if( index>0 ){ + d_partial.push_back( InstMatch( &d_partial[index-1] ) ); + }else{ + d_partial.push_back( InstMatch() ); + } + if( d_children[index]->getNextMatch( d_partial[index], qe ) ){ + index++; + }else{ + d_children[index]->reset( Node::null(), qe ); + d_partial.pop_back(); + if( !d_partial.empty() ){ + d_partial.pop_back(); + } + index--; + } + } + if( index>=0 ){ + m = d_partial.back(); + d_partial.pop_back(); + return true; + }else{ + return false; + } + }else{ + bool res = getNextMatch2( m, qe, true ); + Assert(!res || !m.d_matched.isNull()); + return res; + } +} + + + +// Currently the implementation doesn't take into account that +// variable should have the same value given. +// TODO use the d_children way perhaps +// TODO replace by a real dictionnary +// We should create a real substitution? slower more precise +// We don't do that often +bool InstMatchGenerator::nonunifiable( TNode t0, const std::vector<Node> & vars){ + if(d_match_pattern.isNull()) return true; + + typedef std::vector<std::pair<TNode,TNode> > tstack; + tstack stack(1,std::make_pair(t0,d_match_pattern)); // t * pat + + while(!stack.empty()){ + const std::pair<TNode,TNode> p = stack.back(); stack.pop_back(); + const TNode & t = p.first; + const TNode & pat = p.second; + + // t or pat is a variable currently we consider that can match anything + if( find(vars.begin(),vars.end(),t) != vars.end() ) continue; + if( pat.getKind() == INST_CONSTANT ) continue; + + // t and pat are nonunifiable + if( !Trigger::isAtomicTrigger( t ) || !Trigger::isAtomicTrigger( pat ) ) { + if(t == pat) continue; + else return true; + }; + if( t.getOperator() != pat.getOperator() ) return true; + + //put the children on the stack + for( size_t i=0; i < pat.getNumChildren(); i++ ){ + stack.push_back(std::make_pair(t[i],pat[i])); + }; + } + // The heuristic can't find non-unifiability + return false; +} + +int InstMatchGenerator::addInstantiations( Node f, InstMatch& baseMatch, QuantifiersEngine* qe, int instLimit, bool addSplits ){ + //now, try to add instantiation for each match produced + int addedLemmas = 0; + InstMatch m; + while( getNextMatch( m, qe ) ){ + //m.makeInternal( d_quantEngine->getEqualityQuery() ); + m.add( baseMatch ); + if( qe->addInstantiation( f, m, addSplits ) ){ + addedLemmas++; + if( instLimit>0 && addedLemmas==instLimit ){ + return addedLemmas; + } + } + m.clear(); + } + //return number of lemmas added + return addedLemmas; +} + +int InstMatchGenerator::addTerm( Node f, Node t, QuantifiersEngine* qe ){ + Assert( Options::current()->eagerInstQuant ); + if( !d_match_pattern.isNull() ){ + InstMatch m; + if( getMatch( t, m, qe ) ){ + if( qe->addInstantiation( f, m ) ){ + return 1; + } + } + }else{ + for( int i=0; i<(int)d_children.size(); i++ ){ + d_children[i]->addTerm( f, t, qe ); + } + } + return 0; +} + +/** constructors */ +InstMatchGeneratorMulti::InstMatchGeneratorMulti( Node f, std::vector< Node >& pats, QuantifiersEngine* qe, int matchOption ) : +d_f( f ){ + Debug("smart-multi-trigger") << "Making smart multi-trigger for " << f << std::endl; + std::map< Node, std::vector< Node > > var_contains; + Trigger::getVarContains( f, pats, var_contains ); + //convert to indicies + for( std::map< Node, std::vector< Node > >::iterator it = var_contains.begin(); it != var_contains.end(); ++it ){ + Debug("smart-multi-trigger") << "Pattern " << it->first << " contains: "; + for( int i=0; i<(int)it->second.size(); i++ ){ + Debug("smart-multi-trigger") << 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 ); + } + Debug("smart-multi-trigger") << std::endl; + } + for( int i=0; i<(int)pats.size(); i++ ){ + Node n = pats[i]; + //make the match generator + d_children.push_back( new InstMatchGenerator( n, qe, matchOption ) ); + //compute unique/shared variables + std::vector< int > unique_vars; + std::map< int, bool > shared_vars; + int numSharedVars = 0; + for( int j=0; j<(int)d_var_contains[n].size(); j++ ){ + if( d_var_to_node[ d_var_contains[n][j] ].size()==1 ){ + Debug("smart-multi-trigger") << "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; + int index = i==0 ? (int)(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() ); + Debug("smart-multi-trigger") << " Index[" << i << "]: "; + for( int i=0; i<(int)vars.size(); i++ ){ + Debug("smart-multi-trigger") << vars[i] << " "; + } + Debug("smart-multi-trigger") << std::endl; + //make ordered inst match trie + InstMatchTrie::ImtIndexOrder* imtio = new InstMatchTrie::ImtIndexOrder; + imtio->d_order.insert( imtio->d_order.begin(), vars.begin(), vars.end() ); + d_children_trie.push_back( InstMatchTrieOrdered( imtio ) ); + } + +} + +/** reset instantiation round (call this whenever equivalence classes have changed) */ +void InstMatchGeneratorMulti::resetInstantiationRound( QuantifiersEngine* qe ){ + for( int i=0; i<(int)d_children.size(); i++ ){ + d_children[i]->resetInstantiationRound( qe ); + } +} + +/** reset, eqc is the equivalence class to search in (any if eqc=null) */ +void InstMatchGeneratorMulti::reset( Node eqc, QuantifiersEngine* qe ){ + for( int i=0; i<(int)d_children.size(); i++ ){ + d_children[i]->reset( eqc, qe ); + } +} + +void InstMatchGeneratorMulti::collectInstantiations( QuantifiersEngine* qe, InstMatch& m, int& addedLemmas, InstMatchTrie* tr, + std::vector< IndexedTrie >& unique_var_tries, + int trieIndex, int childIndex, int endChildIndex, bool modEq ){ + if( childIndex==endChildIndex ){ + //now, process unique variables + collectInstantiations2( qe, m, addedLemmas, unique_var_tries, 0 ); + }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->getInstantiationConstant( d_f, curr_index ); + if( m.d_map.find( curr_ic )==m.d_map.end() ){ + //if( d_var_to_node[ curr_index ].size()==1 ){ //FIXME + // //unique variable(s), defer calculation + // unique_var_tries.push_back( IndexedTrie( std::pair< int, int >( childIndex, trieIndex ), tr ) ); + // int newChildIndex = (childIndex+1)%(int)d_children.size(); + // collectInstantiations( qe, m, d_children_trie[newChildIndex].getTrie(), unique_var_tries, + // 0, newChildIndex, endChildIndex, modEq ); + //}else{ + //shared and non-set variable, add to InstMatch + for( std::map< Node, InstMatchTrie >::iterator it = tr->d_data.begin(); it != tr->d_data.end(); ++it ){ + InstMatch mn( &m ); + mn.d_map[ curr_ic ] = it->first; + collectInstantiations( qe, mn, addedLemmas, &(it->second), unique_var_tries, + trieIndex+1, childIndex, endChildIndex, modEq ); + } + //} + }else{ + //shared and set variable, try to merge + Node n = m.d_map[ curr_ic ]; + std::map< Node, InstMatchTrie >::iterator it = tr->d_data.find( n ); + if( it!=tr->d_data.end() ){ + collectInstantiations( qe, m, addedLemmas, &(it->second), unique_var_tries, + trieIndex+1, childIndex, endChildIndex, modEq ); + } + if( modEq ){ + //check modulo equality for other possible instantiations + if( ((uf::TheoryUF*)qe->getTheoryEngine()->getTheory( THEORY_UF ))->getEqualityEngine()->hasTerm( n ) ){ + eq::EqClassIterator eqc( qe->getEqualityQuery()->getRepresentative( n ), + ((uf::TheoryUF*)qe->getTheoryEngine()->getTheory( THEORY_UF ))->getEqualityEngine() ); + 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() ){ + collectInstantiations( qe, m, addedLemmas, &(itc->second), unique_var_tries, + trieIndex+1, childIndex, endChildIndex, modEq ); + } + } + ++eqc; + } + } + } + } + }else{ + int newChildIndex = (childIndex+1)%(int)d_children.size(); + collectInstantiations( qe, m, addedLemmas, d_children_trie[newChildIndex].getTrie(), unique_var_tries, + 0, newChildIndex, endChildIndex, modEq ); + } +} + +void InstMatchGeneratorMulti::collectInstantiations2( QuantifiersEngine* qe, InstMatch& m, int& addedLemmas, + std::vector< IndexedTrie >& unique_var_tries, + int uvtIndex, InstMatchTrie* tr, int trieIndex ){ + if( uvtIndex<(int)unique_var_tries.size() ){ + int childIndex = unique_var_tries[uvtIndex].first.first; + if( !tr ){ + tr = unique_var_tries[uvtIndex].second; + trieIndex = unique_var_tries[uvtIndex].first.second; + } + 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->getInstantiationConstant( d_f, curr_index ); + //unique non-set variable, add to InstMatch + for( std::map< Node, InstMatchTrie >::iterator it = tr->d_data.begin(); it != tr->d_data.end(); ++it ){ + InstMatch mn( &m ); + mn.d_map[ curr_ic ] = it->first; + collectInstantiations2( qe, mn, addedLemmas, unique_var_tries, uvtIndex, &(it->second), trieIndex+1 ); + } + }else{ + collectInstantiations2( qe, m, addedLemmas, unique_var_tries, uvtIndex+1 ); + } + }else{ + //m is an instantiation + if( qe->addInstantiation( d_f, m, true ) ){ + addedLemmas++; + Debug("smart-multi-trigger") << "-> Produced instantiation " << m << std::endl; + } + } +} + +int InstMatchGeneratorMulti::addInstantiations( Node f, InstMatch& baseMatch, QuantifiersEngine* qe, int instLimit, bool addSplits ){ + int addedLemmas = 0; + Debug("smart-multi-trigger") << "Process smart multi trigger" << std::endl; + for( int i=0; i<(int)d_children.size(); i++ ){ + Debug("smart-multi-trigger") << "Calculate matches " << i << std::endl; + std::vector< InstMatch > newMatches; + InstMatch m; + while( d_children[i]->getNextMatch( m, qe ) ){ + m.makeRepresentative( qe ); + newMatches.push_back( InstMatch( &m ) ); + m.clear(); + } + for( int j=0; j<(int)newMatches.size(); j++ ){ + processNewMatch( qe, newMatches[j], i, addedLemmas ); + } + } + return addedLemmas; +} + +void InstMatchGeneratorMulti::processNewMatch( QuantifiersEngine* qe, InstMatch& m, int fromChildIndex, int& addedLemmas ){ + //see if these produce new matches + d_children_trie[fromChildIndex].addInstMatch( qe, d_f, m, true ); + //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. + Debug("smart-multi-trigger") << "Child " << fromChildIndex << " produced match " << m << std::endl; + //collect new instantiations + int childIndex = (fromChildIndex+1)%(int)d_children.size(); + std::vector< IndexedTrie > unique_var_tries; + collectInstantiations( qe, m, addedLemmas, + d_children_trie[childIndex].getTrie(), unique_var_tries, 0, childIndex, fromChildIndex, true ); +} + +int InstMatchGeneratorMulti::addTerm( Node f, Node t, QuantifiersEngine* qe ){ + Assert( Options::current()->eagerInstQuant ); + int addedLemmas = 0; + for( int i=0; i<(int)d_children.size(); i++ ){ + if( ((InstMatchGenerator*)d_children[i])->d_match_pattern.getOperator()==t.getOperator() ){ + InstMatch m; + //if it produces a match, then process it with the rest + if( ((InstMatchGenerator*)d_children[i])->getMatch( t, m, qe ) ){ + processNewMatch( qe, m, i, addedLemmas ); + } + } + } + return addedLemmas; +} + +int InstMatchGeneratorSimple::addInstantiations( Node f, InstMatch& baseMatch, QuantifiersEngine* qe, int instLimit, bool addSplits ){ + InstMatch m; + m.add( baseMatch ); + int addedLemmas = 0; + if( d_match_pattern.getType()==NodeManager::currentNM()->booleanType() ){ + for( int i=0; i<2; i++ ){ + addInstantiations( m, qe, addedLemmas, 0, &(qe->getTermDatabase()->d_pred_map_trie[i][ d_match_pattern.getOperator() ]), + instLimit, addSplits ); + } + }else{ + addInstantiations( m, qe, addedLemmas, 0, &(qe->getTermDatabase()->d_func_map_trie[ d_match_pattern.getOperator() ]), + instLimit, addSplits ); + } + return addedLemmas; +} + +void InstMatchGeneratorSimple::addInstantiations( InstMatch& m, QuantifiersEngine* qe, int& addedLemmas, int argIndex, + TermArgTrie* tat, int instLimit, bool addSplits ){ + if( argIndex==(int)d_match_pattern.getNumChildren() ){ + //m is an instantiation + if( qe->addInstantiation( d_f, m, addSplits ) ){ + addedLemmas++; + Debug("simple-multi-trigger") << "-> Produced instantiation " << m << std::endl; + } + }else{ + if( d_match_pattern[argIndex].getKind()==INST_CONSTANT ){ + Node ic = d_match_pattern[argIndex]; + for( std::map< Node, TermArgTrie >::iterator it = tat->d_data.begin(); it != tat->d_data.end(); ++it ){ + Node t = it->first; + if( m.d_map[ ic ].isNull() || m.d_map[ ic ]==t ){ + Node prev = m.d_map[ ic ]; + m.d_map[ ic ] = t; + addInstantiations( m, qe, addedLemmas, argIndex+1, &(it->second), instLimit, addSplits ); + m.d_map[ ic ] = prev; + } + } + }else{ + Node r = qe->getEqualityQuery()->getRepresentative( d_match_pattern[argIndex] ); + std::map< Node, TermArgTrie >::iterator it = tat->d_data.find( r ); + if( it!=tat->d_data.end() ){ + addInstantiations( m, qe, addedLemmas, argIndex+1, &(it->second), instLimit, addSplits ); + } + } + } +} + +int InstMatchGeneratorSimple::addTerm( Node f, Node t, QuantifiersEngine* qe ){ + Assert( Options::current()->eagerInstQuant ); + InstMatch m; + for( int i=0; i<(int)t.getNumChildren(); i++ ){ + if( d_match_pattern[i].getKind()==INST_CONSTANT ){ + m.d_map[d_match_pattern[i]] = t[i]; + }else if( !qe->getEqualityQuery()->areEqual( d_match_pattern[i], t[i] ) ){ + return 0; + } + } + return qe->addInstantiation( f, m ) ? 1 : 0; +} diff --git a/src/theory/inst_match.h b/src/theory/inst_match.h new file mode 100644 index 000000000..73a99bcc5 --- /dev/null +++ b/src/theory/inst_match.h @@ -0,0 +1,443 @@ +/********************* */ +/*! \file inst_match.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief inst match class + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__INST_MATCH_H +#define __CVC4__INST_MATCH_H + +#include "theory/theory.h" +#include "util/hash.h" + +#include <ext/hash_set> +#include <iostream> +#include <map> + +#include "theory/uf/equality_engine.h" +#include "theory/uf/theory_uf.h" +#include "context/cdlist.h" + +//#define USE_EFFICIENT_E_MATCHING + +namespace CVC4 { +namespace theory { + +/** Attribute true for nodes that should not be used for matching */ +struct NoMatchAttributeId {}; +/** use the special for boolean flag */ +typedef expr::Attribute< NoMatchAttributeId, + bool, + expr::attr::NullCleanupStrategy, + true // context dependent + > NoMatchAttribute; + +class QuantifiersEngine; + +namespace uf { + class InstantiatorTheoryUf; + class TheoryUF; +}/* CVC4::theory::uf namespace */ + +class CandidateGenerator { +public: + CandidateGenerator(){} + ~CandidateGenerator(){} + + /** Get candidates functions. These set up a context to get all match candidates. + cg->reset( eqc ); + do{ + Node cand = cg->getNextCandidate(); + //....... + }while( !cand.isNull() ); + + eqc is the equivalence class you are searching in + */ + virtual void reset( Node eqc ) = 0; + virtual Node getNextCandidate() = 0; + /** add candidate to list of nodes returned by this generator */ + virtual void addCandidate( Node n ) {} + /** call this at the beginning of each instantiation round */ + virtual void resetInstantiationRound() = 0; +public: + /** legal candidate */ + static bool isLegalCandidate( Node n ); +};/* class CandidateGenerator */ + +/** candidate generator queue (for manual candidate generation) */ +class CandidateGeneratorQueue : public CandidateGenerator { +private: + std::vector< Node > d_candidates; + int d_candidate_index; +public: + CandidateGeneratorQueue() : d_candidate_index( 0 ){} + ~CandidateGeneratorQueue(){} + + void addCandidate( Node n ); + + void resetInstantiationRound(){} + void reset( Node eqc ); + Node getNextCandidate(); +};/* class CandidateGeneratorQueue */ + +class EqualityQuery { +public: + EqualityQuery(){} + ~EqualityQuery(){} + /** contains term */ + virtual bool hasTerm( Node a ) = 0; + /** get the representative of the equivalence class of a */ + virtual Node getRepresentative( Node a ) = 0; + /** returns true if a and b are equal in the current context */ + virtual bool areEqual( Node a, Node b ) = 0; + /** returns true is a and b are disequal in the current context */ + virtual bool areDisequal( Node a, Node b ) = 0; + /** getInternalRepresentative gets the current best representative in the equivalence class of a, based on some criteria. + If cbqi is active, this will return a term in the equivalence class of "a" that does + not contain instantiation constants, if such a term exists. + */ + virtual Node getInternalRepresentative( Node a ) = 0; +};/* class EqualityQuery */ + +/** basic class defining an instantiation */ +class InstMatch { +public: + InstMatch(); + InstMatch( InstMatch* m ); + + /** set the match of v to m */ + bool setMatch( EqualityQuery* q, Node v, Node m ); + /** fill all unfilled values with m */ + bool add( InstMatch& m ); + /** if compatible, fill all unfilled values with m and return true + return false otherwise */ + bool merge( EqualityQuery* q, InstMatch& m ); + /** debug print method */ + void debugPrint( const char* c ); + /** make complete */ + void makeComplete( Node f, QuantifiersEngine* qe ); + /** make internal: ensure that no term in d_map contains instantiation constants */ + void makeInternal( QuantifiersEngine* qe ); + /** make representative */ + void makeRepresentative( QuantifiersEngine* qe ); + /** apply rewrite */ + void applyRewrite(); + /** compute d_match */ + void computeTermVec( QuantifiersEngine* qe, const std::vector< Node >& vars, std::vector< Node >& match ); + /** compute d_match */ + void computeTermVec( const std::vector< Node >& vars, std::vector< Node >& match ); + /** clear */ + void clear(){ d_map.clear(); } + /** is_empty */ + bool empty(){ return d_map.empty(); } + /* map from variable to ground terms */ + std::map< Node, Node > d_map; + /* Node used for matching the trigger only for mono-trigger (just for + efficiency because I need only that) */ + Node d_matched; + /** to stream */ + inline void toStream(std::ostream& out) const { + out << "INST_MATCH( "; + for( std::map< Node, Node >::const_iterator it = d_map.begin(); it != d_map.end(); ++it ){ + if( it != d_map.begin() ){ out << ", "; } + out << it->first << " -> " << it->second; + } + out << " )"; + } +};/* class InstMatch */ + +inline std::ostream& operator<<(std::ostream& out, const InstMatch& m) { + m.toStream(out); + return out; +} + +/** trie for InstMatch objects */ +class InstMatchTrie { +public: + class ImtIndexOrder { + public: + std::vector< int > d_order; + };/* class InstMatchTrie ImtIndexOrder */ +private: + /** add match m for quantifier f starting at index, take into account equalities q, return true if successful */ + void addInstMatch2( QuantifiersEngine* qe, Node f, InstMatch& m, int index, ImtIndexOrder* imtio ); + /** exists match */ + bool existsInstMatch( QuantifiersEngine* qe, Node f, InstMatch& m, bool modEq, int index, ImtIndexOrder* imtio ); +public: + /** the data */ + std::map< Node, InstMatchTrie > d_data; +public: + InstMatchTrie(){} + ~InstMatchTrie(){} +public: + /** add match m for quantifier f, take into account equalities if modEq = true, + if imtio is non-null, this is the order to add to trie + return true if successful + */ + bool addInstMatch( QuantifiersEngine* qe, Node f, InstMatch& m, bool modEq = false, ImtIndexOrder* imtio = NULL ); +};/* class InstMatchTrie */ + +class InstMatchTrieOrdered { +private: + InstMatchTrie::ImtIndexOrder* d_imtio; + InstMatchTrie d_imt; +public: + InstMatchTrieOrdered( InstMatchTrie::ImtIndexOrder* imtio ) : d_imtio( imtio ){} + ~InstMatchTrieOrdered(){} + /** get ordering */ + InstMatchTrie::ImtIndexOrder* getOrdering() { return d_imtio; } + /** get trie */ + InstMatchTrie* getTrie() { return &d_imt; } +public: + /** add match m, return true if successful */ + bool addInstMatch( QuantifiersEngine* qe, Node f, InstMatch& m, bool modEq = false ){ + return d_imt.addInstMatch( qe, f, m, modEq, d_imtio ); + } +};/* class InstMatchTrieOrdered */ + +template<bool modEq = false> +class InstMatchTrie2 { +private: + + class Tree { + public: + typedef std::hash_map< Node, Tree *, NodeHashFunction > MLevel; + MLevel e; + const size_t level; //context level of creation + Tree() CVC4_UNDEFINED; + const Tree & operator =(const Tree & t) CVC4_UNDEFINED; + Tree(size_t l): level(l) {}; + ~Tree(){ + for(typename MLevel::iterator i = e.begin(); i!=e.end(); ++i) + delete(i->second); + }; + };/* class InstMatchTrie2::Tree */ + + + typedef std::pair<Tree *, TNode> Mod; + + class CleanUp { + public: + inline void operator()(Mod * m){ + typename Tree::MLevel::iterator i = m->first->e.find(m->second); + Assert (i != m->first->e.end()); //should not have been already removed + m->first->e.erase(i); + } + };/* class InstMatchTrie2::CleanUp */ + + EqualityQuery* d_eQ; + eq::EqualityEngine* d_eE; + + /* before for the order of destruction */ + Tree d_data; + + context::Context* d_context; + context::CDList<Mod, CleanUp, std::allocator<Mod> > d_mods; + + typedef std::map<Node, Node>::const_iterator mapIter; + + /** add the substitution given by the iterator*/ + void addSubTree( Tree* root, mapIter current, mapIter end, size_t currLevel); + /** test if it exists match, modulo uf-equations if modEq is true if + * return false the deepest point of divergence is put in [e] and + * [diverge]. + */ + bool existsInstMatch( Tree* root, + mapIter & current, mapIter& end, + Tree*& e, mapIter& diverge) const; + +public: + InstMatchTrie2(context::Context* c, QuantifiersEngine* q); + InstMatchTrie2(const InstMatchTrie2&) CVC4_UNDEFINED; + const InstMatchTrie2& operator=(const InstMatchTrie2 & e) CVC4_UNDEFINED; + /** add match m in the trie, + modEq specify to take into account equalities, + return true if it was never seen */ + bool addInstMatch( InstMatch& m); +};/* class InstMatchTrie2 */ + +/** base class for producing InstMatch objects */ +class IMGenerator { +public: + /** reset instantiation round (call this at beginning of instantiation round) */ + virtual void resetInstantiationRound( QuantifiersEngine* qe ) = 0; + /** reset, eqc is the equivalence class to search in (any if eqc=null) */ + virtual void reset( Node eqc, QuantifiersEngine* qe ) = 0; + /** get the next match. must call reset( eqc ) before this function. */ + virtual bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ) = 0; + /** return true if whatever Node is subsituted for the variables the + given Node can't match the pattern */ + virtual bool nonunifiable( TNode t, const std::vector<Node> & vars) = 0; + /** add instantiations directly */ + virtual int addInstantiations( Node f, InstMatch& baseMatch, QuantifiersEngine* qe, int instLimit = 0, bool addSplits = false ) = 0; + /** add ground term t, called when t is added to term db */ + virtual int addTerm( Node f, Node t, QuantifiersEngine* qe ) = 0; +};/* class IMGenerator */ + + +class InstMatchGenerator : public IMGenerator { +private: + /** candidate generator */ + CandidateGenerator* d_cg; + /** policy to use for matching */ + int d_matchPolicy; + /** children generators */ + std::vector< InstMatchGenerator* > d_children; + std::vector< int > d_children_index; + /** partial vector */ + std::vector< InstMatch > d_partial; + /** eq class */ + Node d_eq_class; + /** for arithmetic matching */ + std::map< Node, Node > d_arith_coeffs; + /** initialize pattern */ + void initializePatterns( std::vector< Node >& pats, QuantifiersEngine* qe ); + void initializePattern( Node pat, QuantifiersEngine* qe ); +public: + enum { + //options for producing matches + MATCH_GEN_DEFAULT = 0, + MATCH_GEN_EFFICIENT_E_MATCH, //generate matches via Efficient E-matching for SMT solvers + //others (internally used) + MATCH_GEN_INTERNAL_ARITHMETIC, + MATCH_GEN_INTERNAL_ERROR, + }; +private: + /** get the next match. must call d_cg->reset( ... ) before using. + only valid for use where !d_match_pattern.isNull(). + */ + bool getNextMatch2( InstMatch& m, QuantifiersEngine* qe, bool saveMatched = false ); + /** for arithmetic */ + bool getMatchArithmetic( Node t, InstMatch& m, QuantifiersEngine* qe ); +public: + /** get the match against ground term or formula t. + d_match_mattern and t should have the same shape. + only valid for use where !d_match_pattern.isNull(). + */ + bool getMatch( Node t, InstMatch& m, QuantifiersEngine* qe ); + + /** constructors */ + InstMatchGenerator( Node pat, QuantifiersEngine* qe, int matchOption = 0 ); + InstMatchGenerator( std::vector< Node >& pats, QuantifiersEngine* qe, int matchOption = 0 ); + /** destructor */ + ~InstMatchGenerator(){} + /** The pattern we are producing matches for. + If null, this is a multi trigger that is merging matches from d_children. + */ + Node d_pattern; + /** match pattern */ + Node d_match_pattern; +public: + /** reset instantiation round (call this whenever equivalence classes have changed) */ + void resetInstantiationRound( QuantifiersEngine* qe ); + /** reset, eqc is the equivalence class to search in (any if eqc=null) */ + void reset( Node eqc, QuantifiersEngine* qe ); + /** get the next match. must call reset( eqc ) before this function. */ + bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ); + /** return true if whatever Node is subsituted for the variables the + given Node can't match the pattern */ + bool nonunifiable( TNode t, const std::vector<Node> & vars); + /** add instantiations */ + int addInstantiations( Node f, InstMatch& baseMatch, QuantifiersEngine* qe, int instLimit = 0, bool addSplits = false ); + /** add ground term t */ + int addTerm( Node f, Node t, QuantifiersEngine* qe ); +};/* class InstMatchGenerator */ + +/** smart multi-trigger implementation */ +class InstMatchGeneratorMulti : public IMGenerator { +private: + void processNewMatch( QuantifiersEngine* qe, InstMatch& m, int fromChildIndex, int& addedLemmas ); +private: + /** indexed trie */ + typedef std::pair< std::pair< int, int >, InstMatchTrie* > IndexedTrie; + /** collect instantiations */ + void collectInstantiations( QuantifiersEngine* qe, InstMatch& m, int& addedLemmas, InstMatchTrie* tr, + std::vector< IndexedTrie >& unique_var_tries, + int trieIndex, int childIndex, int endChildIndex, bool modEq ); + /** collect instantiations 2 */ + void collectInstantiations2( QuantifiersEngine* qe, InstMatch& m, int& addedLemmas, + std::vector< IndexedTrie >& unique_var_tries, + int uvtIndex, InstMatchTrie* tr = NULL, int trieIndex = 0 ); +private: + /** var contains (variable indicies) for each pattern node */ + std::map< Node, std::vector< int > > d_var_contains; + /** variable indicies contained to pattern nodes */ + std::map< int, std::vector< Node > > d_var_to_node; + /** quantifier to use */ + Node d_f; + /** policy to use for matching */ + int d_matchPolicy; + /** children generators */ + std::vector< InstMatchGenerator* > d_children; + /** inst match tries for each child */ + std::vector< InstMatchTrieOrdered > d_children_trie; + /** calculate matches */ + void calculateMatches( QuantifiersEngine* qe ); +public: + /** constructors */ + InstMatchGeneratorMulti( Node f, std::vector< Node >& pats, QuantifiersEngine* qe, int matchOption = 0 ); + /** destructor */ + ~InstMatchGeneratorMulti(){} + /** reset instantiation round (call this whenever equivalence classes have changed) */ + void resetInstantiationRound( QuantifiersEngine* qe ); + /** reset, eqc is the equivalence class to search in (any if eqc=null) */ + void reset( Node eqc, QuantifiersEngine* qe ); + /** get the next match. must call reset( eqc ) before this function. (not implemented) */ + bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ) { return false; } + /** return true if whatever Node is subsituted for the variables the + given Node can't match the pattern */ + bool nonunifiable( TNode t, const std::vector<Node> & vars) { return true; } + /** add instantiations */ + int addInstantiations( Node f, InstMatch& baseMatch, QuantifiersEngine* qe, int instLimit = 0, bool addSplits = false ); + /** add ground term t */ + int addTerm( Node f, Node t, QuantifiersEngine* qe ); +};/* class InstMatchGeneratorMulti */ + +class TermArgTrie; + +/** smart (single)-trigger implementation */ +class InstMatchGeneratorSimple : public IMGenerator { +private: + /** quantifier for match term */ + Node d_f; + /** match term */ + Node d_match_pattern; + /** add instantiations */ + void addInstantiations( InstMatch& m, QuantifiersEngine* qe, int& addedLemmas, + int argIndex, TermArgTrie* tat, int instLimit, bool addSplits ); +public: + /** constructors */ + InstMatchGeneratorSimple( Node f, Node pat ) : d_f( f ), d_match_pattern( pat ){} + /** destructor */ + ~InstMatchGeneratorSimple(){} + /** reset instantiation round (call this whenever equivalence classes have changed) */ + void resetInstantiationRound( QuantifiersEngine* qe ) {} + /** reset, eqc is the equivalence class to search in (any if eqc=null) */ + void reset( Node eqc, QuantifiersEngine* qe ) {} + /** get the next match. must call reset( eqc ) before this function. (not implemented) */ + bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ) { return false; } + /** return true if whatever Node is subsituted for the variables the + given Node can't match the pattern */ + bool nonunifiable( TNode t, const std::vector<Node> & vars) { return true; } + /** add instantiations */ + int addInstantiations( Node f, InstMatch& baseMatch, QuantifiersEngine* qe, int instLimit = 0, bool addSplits = false ); + /** add ground term t, possibly add instantiations */ + int addTerm( Node f, Node t, QuantifiersEngine* qe ); +};/* class InstMatchGeneratorSimple */ + +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__INST_MATCH_H */ diff --git a/src/theory/inst_match_impl.h b/src/theory/inst_match_impl.h new file mode 100644 index 000000000..18c4998b8 --- /dev/null +++ b/src/theory/inst_match_impl.h @@ -0,0 +1,125 @@ +/********************* */ +/*! \file inst_match.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief inst match class + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__INST_MATCH_IMPL_H +#define __CVC4__INST_MATCH_IMPL_H + +#include "theory/inst_match.h" +#include "theory/theory_engine.h" +#include "theory/quantifiers_engine.h" +#include "theory/uf/theory_uf_instantiator.h" +#include "theory/uf/theory_uf_candidate_generator.h" +#include "theory/uf/equality_engine.h" + +namespace CVC4 { +namespace theory { + +template<bool modEq> +InstMatchTrie2<modEq>::InstMatchTrie2(context::Context* c, QuantifiersEngine* qe): + d_data(c->getLevel()), d_context(c), d_mods(c) { + d_eQ = qe->getEqualityQuery(); + d_eE = ((uf::TheoryUF*)qe->getTheoryEngine()->getTheory( THEORY_UF ))->getEqualityEngine(); +}; + +/** add match m for quantifier f starting at index, take into account equalities q, return true if successful */ +template<bool modEq> +void InstMatchTrie2<modEq>::addSubTree( Tree * root, mapIter current, mapIter end, size_t currLevel ) { + if( current == end ) return; + + Assert(root->e.find(current->second) == root->e.end()); + Tree * root2 = new Tree(currLevel); + root->e.insert(make_pair(current->second, root2)); + addSubTree(root2, ++current, end, currLevel ); +} + +/** exists match */ +template<bool modEq> +bool InstMatchTrie2<modEq>::existsInstMatch(InstMatchTrie2<modEq>::Tree * root, + mapIter & current, mapIter & end, + Tree * & e, mapIter & diverge) const{ + if( current == end ) { + Debug("Trie2") << "Trie2 Bottom " << std::endl; + --current; + return true; + }; //Already their + + if (current->first > diverge->first){ + // this point is the deepest point currently seen map are ordered + e = root; + diverge = current; + }; + + TNode n = current->second; + typename InstMatchTrie2<modEq>::Tree::MLevel::iterator it = + root->e.find( n ); + if( it!=root->e.end() && + existsInstMatch( (*it).second, ++current, end, e, diverge) ){ + Debug("Trie2") << "Trie2 Directly here " << n << std::endl; + --current; + return true; + } + Assert( it==root->e.end() || e != root ); + + // Even if n is in the trie others of the equivalence class + // can also be in it since the equality can have appeared + // after they have been added + if( modEq && d_eE->hasTerm( n ) ){ + //check modulo equality if any other instantiation match exists + eq::EqClassIterator eqc( d_eQ->getRepresentative( n ), d_eE ); + for( ;!eqc.isFinished();++eqc ){ + TNode en = (*eqc); + if( en == n ) continue; // already tested + typename InstMatchTrie2<modEq>::Tree::MLevel::iterator itc = + root->e.find( en ); + if( itc!=root->e.end() && + existsInstMatch( (*itc).second, ++current, end, e, diverge) ){ + Debug("Trie2") << "Trie2 Indirectly here by equality " << n << " = " << en << std::endl; + --current; + return true; + } + Assert( itc==root->e.end() || e != root ); + } + } + --current; + return false; +} + +template<bool modEq> +bool InstMatchTrie2<modEq>::addInstMatch( InstMatch& m ) { + mapIter begin = m.d_map.begin(); + mapIter end = m.d_map.end(); + InstMatchTrie2<modEq>::Tree * e = &d_data; + mapIter diverge = begin; + if( !existsInstMatch(e, begin, end, e, diverge ) ){ + Assert(!diverge->second.isNull()); + size_t currLevel = d_context->getLevel(); + addSubTree( e, diverge, end, currLevel ); + if(e->level != currLevel) + //If same level that e, will be removed at the same time than e + d_mods.push_back(make_pair(e,diverge->second)); + return true; + }else{ + return false; + } +} + +}/* CVC4::theory namespace */ + +}/* CVC4 namespace */ + +#endif /* __CVC4__INST_MATCH_IMPL_H */ diff --git a/src/theory/instantiator_default.cpp b/src/theory/instantiator_default.cpp new file mode 100644 index 000000000..4d6ea2fdb --- /dev/null +++ b/src/theory/instantiator_default.cpp @@ -0,0 +1,54 @@ +/********************* */ +/*! \file instantiator_default.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of instantiator_default class + **/ + +#include "theory/instantiator_default.h" +#include "theory/theory_engine.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; + +InstantiatorDefault::InstantiatorDefault(context::Context* c, QuantifiersEngine* ie, Theory* th) : + Instantiator( c, ie, th ) { +} + +void InstantiatorDefault::assertNode( Node assertion ){ +} + +void InstantiatorDefault::processResetInstantiationRound( Theory::Effort effort ){ +} + +int InstantiatorDefault::process( Node f, Theory::Effort effort, int e, int limitInst ){ + if( e < 4 ){ + return InstStrategy::STATUS_UNFINISHED; + }else if( e == 4 ){ + Debug("quant-default") << "Process " << f << " : " << std::endl; + InstMatch m; + for( int j=0; j<(int)d_quantEngine->getNumInstantiationConstants( f ); j++ ){ + Node i = d_quantEngine->getInstantiationConstant( f, j ); + Debug("quant-default") << "Getting value for " << i << std::endl; + if( d_quantEngine->getTheoryEngine()->theoryOf( i )==getTheory() ){ //if it belongs to this theory + Node val = d_th->getValue( i ); + Debug("quant-default") << "Default Instantiate for " << d_th->getId() << ", setting " << i << " = " << val << std::endl; + m.d_map[ i ] = val; + } + } + d_quantEngine->addInstantiation( f, m ); + } + return InstStrategy::STATUS_UNKNOWN; +} diff --git a/src/theory/instantiator_default.h b/src/theory/instantiator_default.h new file mode 100644 index 000000000..351d0c4a3 --- /dev/null +++ b/src/theory/instantiator_default.h @@ -0,0 +1,48 @@ +/********************* */ +/*! \file instantiator_default.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief instantiator_default + **/ + + +#include "cvc4_private.h" + +#ifndef __CVC4__INSTANTIATOR_DEFAULT_H +#define __CVC4__INSTANTIATOR_DEFAULT_H + +#include <string> +#include "theory/quantifiers_engine.h" + +namespace CVC4 { +namespace theory { + +class InstantiatorDefault : public Instantiator { + friend class QuantifiersEngine; +protected: + /** reset instantiation round */ + void processResetInstantiationRound(Theory::Effort effort); + /** process quantifier */ + int process(Node f, Theory::Effort effort, int e, int limitInst = 0); +public: + InstantiatorDefault(context::Context* c, QuantifiersEngine* ie, Theory* th); + ~InstantiatorDefault() { } + /** check function, assertion is asserted to theory */ + void assertNode( Node assertion ); + /** identify */ + std::string identify() const { return std::string("InstantiatorDefault"); } +};/* class Instantiatior */ + +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__INSTANTIATOR_DEFAULT_H */ diff --git a/src/theory/instantiator_tables_template.cpp b/src/theory/instantiator_tables_template.cpp new file mode 100644 index 000000000..7a78c3aae --- /dev/null +++ b/src/theory/instantiator_tables_template.cpp @@ -0,0 +1,40 @@ +/********************* */ +/*! \file instantiator_tables_template.cpp + ** \verbatim + ** Original author: mdeters + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011, 2012 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Instantiator tables for quantifier-friendly theories + ** + ** This file contains template code for the instantiator tables that are + ** generated from the Theory kinds files. + **/ + +#include "context/context.h" +#include "theory/quantifiers_engine.h" + +${instantiator_includes} + +#line 26 "${template}" + +namespace CVC4 { +namespace theory { + +Instantiator* Theory::makeInstantiator(context::Context* c, theory::QuantifiersEngine* qe) { + switch(d_id) { +${make_instantiator_cases} +#line 34 "${template}" + default: + Unhandled(d_id); + } +} + +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/mkinstantiator b/src/theory/mkinstantiator new file mode 100755 index 000000000..1908d2e96 --- /dev/null +++ b/src/theory/mkinstantiator @@ -0,0 +1,242 @@ +#!/bin/bash +# +# mkinstantiator +# Morgan Deters <mdeters@cs.nyu.edu> for CVC4 +# Copyright (c) 2010-2012 The CVC4 Project +# +# The purpose of this script is to create rewriter_tables.h from a template +# and a list of theory kinds. +# +# Invocation: +# +# mkinstantiator template-file theory-kind-files... +# +# Output is to standard out. +# + +copyright=2010-2012 + +cat <<EOF +/********************* */ +/** instantiator_tables.h + ** + ** Copyright $copyright The AcSys Group, New York University, and as below. + ** + ** This header file automatically generated by: + ** + ** $0 $@ + ** + ** for the CVC4 project. + **/ + +EOF + +me=$(basename "$0") + +template=$1; shift + +instantiator_includes= +instantiator_class= +theory_id= +theory_class= +theory_header= + +make_instantiator_cases= +instantiator= + +seen_theory=false +seen_theory_builtin=false + +function theory { + # theory id T header + + lineno=${BASH_LINENO[0]} + + if $seen_theory; then + echo "$kf:$lineno: theory declaration can only appear once" >&2 + exit 1; + fi + + seen_theory=true + if [ "$1" = THEORY_BUILTIN ]; then + if $seen_theory_builtin; then + echo "$kf:$lineno: error: \"builtin\" theory redefined" >&2 + exit 1 + fi + seen_theory_builtin=true + elif [ -z "$1" -o -z "$2" -o -z "$3" ]; then + echo "$kf:$lineno: error: \"theory\" directive missing class or header argument" >&2 + exit 1 + elif ! expr "$2" : '\(::*\)' >/dev/null; then + echo "$kf:$lineno: warning: theory class \`$2' isn't fully-qualified (e.g., ::CVC4::theory::foo)" >&2 + elif ! expr "$2" : '\(::CVC4::theory::*\)' >/dev/null; then + echo "$kf:$lineno: warning: theory class not under ::CVC4::theory namespace" >&2 + fi + + theory_id="$1" + theory_class="$2" + theory_header="$3" + + instantiator_class= + instantiator=NULL +} + +function instantiator { + # instantiator class header + lineno=${BASH_LINENO[0]} + check_theory_seen + + if [ -n "$instantiator_class" ]; then + echo "$kf:$lineno: error: cannot have two \"instantiator\" directives" >&2 + exit 1 + fi + + instantiator_class="$1" + instantiator_header="$2" + + if [ -z "$instantiator_class" -o -z "$instantiator_header" ]; then + echo "$kf:$lineno: error: \"instantiator\" directive missing class or header argument" >&2 + exit 1 + fi + + instantiator_includes="${instantiator_includes}#include \"$theory_header\" +#line $lineno \"$kf\" +#include \"$instantiator_header\" +" + instantiator="new $instantiator_class(c, qe, static_cast< $theory_class* >(this))"; +} + +function properties { + # properties prop* + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function endtheory { + # endtheory + lineno=${BASH_LINENO[0]} + check_theory_seen + seen_endtheory=true + + make_instantiator_cases="${make_instantiator_cases} +#line $lineno \"$kf\" + case $theory_id: + return $instantiator; +" +} + +function typechecker { + # typechecker header + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function typerule { + # typerule OPERATOR typechecking-class + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function rewriter { + # rewriter class header + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function sort { + # sort TYPE cardinality [well-founded ground-term header | not-well-founded] ["comment"] + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function cardinality { + # cardinality TYPE cardinality-computer [header] + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function well-founded { + # well-founded TYPE wellfoundedness-computer [header] + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function variable { + # variable K ["comment"] + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function operator { + # operator K #children ["comment"] + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function parameterized { + # parameterized K1 K2 #children ["comment"] + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function constant { + # constant K T Hasher header ["comment"] + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function check_theory_seen { + if $seen_endtheory; then + echo "$kf:$lineno: error: command after \"endtheory\" declaration (endtheory has to be last)" >&2 + exit 1 + fi + if ! $seen_theory; then + echo "$kf:$lineno: error: no \"theory\" declaration found (it has to be first)" >&2 + exit 1 + fi +} + +function check_builtin_theory_seen { + if ! $seen_theory_builtin; then + echo "$me: warning: no declaration for the builtin theory found" >&2 + fi +} + +while [ $# -gt 0 ]; do + kf=$1 + seen_theory=false + seen_endtheory=false + b=$(basename $(dirname "$kf")) + source "$kf" + if ! $seen_theory; then + echo "$kf: error: no theory content found in file!" >&2 + exit 1 + fi + if ! $seen_endtheory; then + echo "$kf:$lineno: error: no \"endtheory\" declaration found (it is required at the end)" >&2 + exit 1 + fi + shift +done +check_builtin_theory_seen + +## output + +# generate warnings about incorrect #line annotations in templates +nl -ba -s' ' "$template" | grep '^ *[0-9][0-9]* # *line' | + awk '{OFS="";if($1+1!=$3) print "'"$template"':",$1,": warning: incorrect annotation \"#line ",$3,"\" (it should be \"#line ",($1+1),"\")"}' >&2 + +text=$(cat "$template") +for var in \ + instantiator_includes \ + make_instantiator_cases \ + template \ + ; do + eval text="\${text//\\\$\\{$var\\}/\${$var}}" +done +error=`expr "$text" : '.*\${\([^}]*\)}.*'` +if [ -n "$error" ]; then + echo "$template:0: error: undefined replacement \${$error}" >&2 + exit 1 +fi +echo "$text" diff --git a/src/theory/mkrewriter b/src/theory/mkrewriter index b8fa51d77..780409d52 100755 --- a/src/theory/mkrewriter +++ b/src/theory/mkrewriter @@ -2,7 +2,7 @@ # # mkrewriter # Morgan Deters <mdeters@cs.nyu.edu> for CVC4 -# Copyright (c) 2010-2011 The CVC4 Project +# Copyright (c) 2010-2012 The CVC4 Project # # The purpose of this script is to create rewriter_tables.h from a template # and a list of theory kinds. @@ -14,7 +14,7 @@ # Output is to standard out. # -copyright=2010-2011 +copyright=2010-2012 cat <<EOF /********************* */ @@ -83,6 +83,12 @@ function theory { theory_id="$1" } +function instantiator { + # instantiator class header + lineno=${BASH_LINENO[0]} + check_theory_seen +} + function properties { # properties prop* lineno=${BASH_LINENO[0]} diff --git a/src/theory/mktheorytraits b/src/theory/mktheorytraits index 2d3b4a43a..297df1f36 100755 --- a/src/theory/mktheorytraits +++ b/src/theory/mktheorytraits @@ -2,7 +2,7 @@ # # mktheorytraits # Morgan Deters <mdeters@cs.nyu.edu> for CVC4 -# Copyright (c) 2010-2011 The CVC4 Project +# Copyright (c) 2010-2012 The CVC4 Project # # The purpose of this script is to create theory_traits.h from a template # and a list of theory kinds. @@ -14,7 +14,7 @@ # Output is to standard out. # -copyright=2010-2011 +copyright=2010-2012 cat <<EOF /********************* */ @@ -55,6 +55,9 @@ theory_parametric="false" rewriter_class= rewriter_header= +instantiator_class=void +instantiator_header= + theory_id= theory_class= @@ -123,6 +126,7 @@ template<> struct TheoryTraits<${theory_id}> { typedef ${theory_class} theory_class; typedef ${rewriter_class} rewriter_class; + typedef ${instantiator_class} instantiator_class; static const bool isStableInfinite = ${theory_stable_infinite}; static const bool isFinite = ${theory_finite}; @@ -188,6 +192,18 @@ function typerule { check_theory_seen } +function instantiator { + # instantiator class header + lineno=${BASH_LINENO[0]} + check_theory_seen + + instantiator_class="$1" + instantiator_header="$2" + + theory_includes="${theory_includes}#include \"$2\" +" +} + function properties { # properties property* lineno=${BASH_LINENO[0]} diff --git a/src/theory/output_channel.h b/src/theory/output_channel.h index 5c2cedf5b..b1a5fc60c 100644 --- a/src/theory/output_channel.h +++ b/src/theory/output_channel.h @@ -149,6 +149,64 @@ public: } /** + * If a decision is made on n, it must be in the phase specified. + * Note that this is enforced *globally*, i.e., it is completely + * context-INdependent. If you ever requirePhase() on a literal, + * it is phase-locked forever and ever. If it is to ever have the + * other phase as its assignment, it will be because it has been + * propagated that way (or it's a unit, at decision level 0). + * + * @param n - a theory atom with a SAT literal assigned; must have + * been pre-registered + * @param phase - the phase to decide on n + */ + virtual void requirePhase(TNode n, bool phase) + throw(Interrupted, TypeCheckingExceptionPrivate, AssertionException) = 0; + + /** + * Flips the most recent unflipped decision to the other phase and + * returns true. If all decisions have been flipped, the root + * decision is re-flipped and flipDecision() returns false. If no + * decisions (flipped nor unflipped) are on the decision stack, the + * state is not affected and flipDecision() returns false. + * + * For example, if l1, l2, and l3 are all decision literals, and + * have been decided in positive phase, a series of flipDecision() + * calls has the following effects: + * + * l1 l2 l3 <br/> + * l1 l2 ~l3 <br/> + * l1 ~l2 <br/> + * ~l1 <br/> + * l1 (and flipDecision() returns false) + * + * Naturally, flipDecision() might be interleaved with search. For example: + * + * l1 l2 l3 <br/> + * flipDecision() <br/> + * l1 l2 ~l3 <br/> + * flipDecision() <br/> + * l1 ~l2 <br/> + * SAT decides l3 <br/> + * l1 ~l2 l3 <br/> + * flipDecision() <br/> + * l1 ~l2 ~l3 <br/> + * flipDecision() <br/> + * ~l1 <br/> + * SAT decides l2 <br/> + * ~l1 l2 <br/> + * flipDecision() <br/> + * ~l1 ~l2 <br/> + * flipDecision() returns FALSE<br/> + * l1 + * + * @return true if a decision was flipped; false if no decision + * could be flipped, or if the root decision was re-flipped + */ + virtual bool flipDecision() + throw(Interrupted, TypeCheckingExceptionPrivate, AssertionException) = 0; + + /** * Notification from a theory that it realizes it is incomplete at * this context level. If SAT is later determined by the * TheoryEngine, it should actually return an UNKNOWN result. diff --git a/src/theory/quantifiers/Makefile b/src/theory/quantifiers/Makefile new file mode 100644 index 000000000..8ffdfb575 --- /dev/null +++ b/src/theory/quantifiers/Makefile @@ -0,0 +1,4 @@ +topdir = ../../.. +srcdir = src/theory/quantifiers + +include $(topdir)/Makefile.subdir diff --git a/src/theory/quantifiers/Makefile.am b/src/theory/quantifiers/Makefile.am new file mode 100644 index 000000000..de74e44f8 --- /dev/null +++ b/src/theory/quantifiers/Makefile.am @@ -0,0 +1,21 @@ +AM_CPPFLAGS = \ + -D__BUILDING_CVC4LIB \ + -I@srcdir@/../../include -I@srcdir@/../.. -I@builddir@/../.. +AM_CXXFLAGS = -Wall $(FLAG_VISIBILITY_HIDDEN) + +noinst_LTLIBRARIES = libquantifiers.la + +libquantifiers_la_SOURCES = \ + theory_quantifiers_type_rules.h \ + theory_quantifiers.h \ + quantifiers_rewriter.h \ + quantifiers_rewriter.cpp \ + theory_quantifiers.cpp \ + theory_quantifiers_instantiator.h \ + theory_quantifiers_instantiator.cpp \ + instantiation_engine.h \ + instantiation_engine.cpp \ + model_engine.h \ + model_engine.cpp + +EXTRA_DIST = kinds
\ No newline at end of file diff --git a/src/theory/quantifiers/instantiation_engine.cpp b/src/theory/quantifiers/instantiation_engine.cpp new file mode 100644 index 000000000..8478dff1e --- /dev/null +++ b/src/theory/quantifiers/instantiation_engine.cpp @@ -0,0 +1,393 @@ +/********************* */ +/*! \file instantiation_engine.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** 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 "theory/theory_engine.h" +#include "theory/uf/theory_uf_instantiator.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; + +//#define IE_PRINT_PROCESS_TIMES + +InstantiationEngine::InstantiationEngine( TheoryQuantifiers* th ) : +d_th( th ){ + +} + +QuantifiersEngine* InstantiationEngine::getQuantifiersEngine(){ + return d_th->getQuantifiersEngine(); +} + +bool InstantiationEngine::hasAddedCbqiLemma( Node f ) { + return d_ce_lit.find( f ) != d_ce_lit.end(); +} + +void InstantiationEngine::addCbqiLemma( Node f ){ + Assert( doCbqi( f ) && !hasAddedCbqiLemma( f ) ); + //code for counterexample-based quantifier instantiation + Debug("cbqi") << "Do cbqi for " << f << std::endl; + //make the counterexample body + //Node ceBody = f[1].substitute( getQuantifiersEngine()->d_vars[f].begin(), getQuantifiersEngine()->d_vars[f].end(), + // getQuantifiersEngine()->d_inst_constants[f].begin(), + // getQuantifiersEngine()->d_inst_constants[f].end() ); + //get the counterexample literal + Node ceBody = getQuantifiersEngine()->getCounterexampleBody( f ); + Node ceLit = d_th->getValuation().ensureLiteral( ceBody.notNode() ); + d_ce_lit[ f ] = ceLit; + getQuantifiersEngine()->setInstantiationConstantAttr( ceLit, f ); + // set attributes, mark all literals in the body of n as dependent on cel + //registerLiterals( ceLit, f ); + //require any decision on cel to be phase=true + d_th->getOutputChannel().requirePhase( ceLit, true ); + Debug("cbqi-debug") << "Require phase " << ceLit << " = true." << std::endl; + //add counterexample lemma + NodeBuilder<> nb(kind::OR); + nb << f << ceLit; + Node lem = nb; + Debug("cbqi-debug") << "Counterexample lemma : " << lem << std::endl; + d_th->getOutputChannel().lemma( lem ); +} + +bool InstantiationEngine::doInstantiationRound( Theory::Effort effort ){ + //if counterexample-based quantifier instantiation is active + if( Options::current()->cbqi ){ + //check if any cbqi lemma has not been added yet + bool addedLemma = false; + for( int i=0; i<(int)getQuantifiersEngine()->getNumAssertedQuantifiers(); i++ ){ + Node f = getQuantifiersEngine()->getAssertedQuantifier( i ); + if( doCbqi( f ) && !hasAddedCbqiLemma( f ) ){ + //add cbqi lemma + addCbqiLemma( f ); + addedLemma = true; + } + } + if( addedLemma ){ + return true; + } + } + //if not, proceed to instantiation round + Debug("inst-engine") << "IE: Instantiation Round." << std::endl; + Debug("inst-engine-ctrl") << "IE: Instantiation Round." << std::endl; + //reset instantiators + Debug("inst-engine-ctrl") << "Reset IE" << std::endl; + for( int i=0; i<theory::THEORY_LAST; i++ ){ + if( getQuantifiersEngine()->getInstantiator( i ) ){ + getQuantifiersEngine()->getInstantiator( i )->resetInstantiationRound( effort ); + } + } + getQuantifiersEngine()->getTermDatabase()->reset( effort ); + //iterate over an internal effort level e + int e = 0; + int eLimit = effort==Theory::EFFORT_LAST_CALL ? 10 : 2; + d_inst_round_status = InstStrategy::STATUS_UNFINISHED; + //while unfinished, try effort level=0,1,2.... + while( d_inst_round_status==InstStrategy::STATUS_UNFINISHED && e<=eLimit ){ + Debug("inst-engine") << "IE: Prepare instantiation (" << e << ")." << std::endl; + d_inst_round_status = InstStrategy::STATUS_SAT; + //instantiate each quantifier + for( int q=0; q<getQuantifiersEngine()->getNumAssertedQuantifiers(); q++ ){ + Node f = getQuantifiersEngine()->getAssertedQuantifier( q ); + Debug("inst-engine-debug") << "IE: Instantiate " << f << "..." << std::endl; + //if this quantifier is active + if( getQuantifiersEngine()->getActive( f ) ){ + //int e_use = getQuantifiersEngine()->getRelevance( f )==-1 ? e - 1 : e; + int e_use = e; + if( e_use>=0 ){ + //use each theory instantiator to instantiate f + for( int i=0; i<theory::THEORY_LAST; i++ ){ + if( getQuantifiersEngine()->getInstantiator( i ) ){ + Debug("inst-engine-debug") << "Do " << getQuantifiersEngine()->getInstantiator( i )->identify() << " " << e_use << std::endl; + int limitInst = 0; + int quantStatus = getQuantifiersEngine()->getInstantiator( i )->doInstantiation( f, effort, e_use, limitInst ); + Debug("inst-engine-debug") << " -> status is " << quantStatus << std::endl; + InstStrategy::updateStatus( d_inst_round_status, quantStatus ); + } + } + } + } + } + //do not consider another level if already added lemma at this level + if( getQuantifiersEngine()->hasAddedLemma() ){ + d_inst_round_status = InstStrategy::STATUS_UNKNOWN; + } + e++; + } + Debug("inst-engine") << "All instantiators finished, # added lemmas = "; + Debug("inst-engine") << (int)getQuantifiersEngine()->d_lemmas_waiting.size() << std::endl; + //Notice() << "All instantiators finished, # added lemmas = " << (int)d_lemmas_waiting.size() << std::endl; + if( !getQuantifiersEngine()->hasAddedLemma() ){ + Debug("inst-engine-stuck") << "No instantiations produced at this state: " << std::endl; + for( int i=0; i<theory::THEORY_LAST; i++ ){ + if( getQuantifiersEngine()->getInstantiator( i ) ){ + getQuantifiersEngine()->getInstantiator( i )->debugPrint("inst-engine-stuck"); + Debug("inst-engine-stuck") << std::endl; + } + } + Debug("inst-engine-ctrl") << "---Fail." << std::endl; + return false; + }else{ + Debug("inst-engine-ctrl") << "---Done. " << (int)getQuantifiersEngine()->d_lemmas_waiting.size() << std::endl; +#ifdef IE_PRINT_PROCESS_TIMES + Notice() << "lemmas = " << (int)getQuantifiersEngine()->d_lemmas_waiting.size() << std::endl; +#endif + //flush lemmas to output channel + getQuantifiersEngine()->flushLemmas( &d_th->getOutputChannel() ); + return true; + } +} + +int ierCounter = 0; + +void InstantiationEngine::check( Theory::Effort e ){ + if( e==Theory::EFFORT_FULL ){ + ierCounter++; + } + //determine if we should perform check, based on instWhenMode + bool performCheck = false; + if( Options::current()->instWhenMode==Options::INST_WHEN_FULL ){ + performCheck = ( e >= Theory::EFFORT_FULL ); + }else if( Options::current()->instWhenMode==Options::INST_WHEN_FULL_LAST_CALL ){ + performCheck = ( ( e==Theory::EFFORT_FULL && ierCounter%2==0 ) || e==Theory::EFFORT_LAST_CALL ); + }else if( Options::current()->instWhenMode==Options::INST_WHEN_LAST_CALL ){ + performCheck = ( e >= Theory::EFFORT_LAST_CALL ); + }else{ + performCheck = true; + } + if( performCheck ){ + Debug("inst-engine") << "IE: Check " << e << " " << ierCounter << std::endl; +#ifdef IE_PRINT_PROCESS_TIMES + double clSet = double(clock())/double(CLOCKS_PER_SEC); + Notice() << "Run instantiation round " << e << " " << ierCounter << std::endl; +#endif + bool quantActive = false; + //for each quantifier currently asserted, + // such that the counterexample literal is not in positive in d_counterexample_asserts + // for( BoolMap::iterator i = d_forall_asserts.begin(); i != d_forall_asserts.end(); i++ ) { + // if( (*i).second ) { + for( int i=0; i<(int)getQuantifiersEngine()->getNumAssertedQuantifiers(); i++ ){ + Node n = getQuantifiersEngine()->getAssertedQuantifier( i ); + if( Options::current()->cbqi && hasAddedCbqiLemma( n ) ){ + Node cel = d_ce_lit[ n ]; + bool active, value; + bool ceValue = false; + if( d_th->getValuation().hasSatValue( cel, value ) ){ + active = value; + ceValue = true; + }else{ + active = true; + } + getQuantifiersEngine()->setActive( n, active ); + if( active ){ + Debug("quantifiers") << " Active : " << n; + quantActive = true; + }else{ + Debug("quantifiers") << " NOT active : " << n; + if( d_th->getValuation().isDecision( cel ) ){ + Debug("quant-req-phase") << "Bad decision : " << cel << std::endl; + } + //note that the counterexample literal must not be a decision + Assert( !d_th->getValuation().isDecision( cel ) ); + } + if( d_th->getValuation().hasSatValue( n, value ) ){ + Debug("quantifiers") << ", value = " << value; + } + if( ceValue ){ + Debug("quantifiers") << ", ce is asserted"; + } + Debug("quantifiers") << std::endl; + }else{ + getQuantifiersEngine()->setActive( n, true ); + quantActive = true; + Debug("quantifiers") << " Active : " << n << ", no ce assigned." << std::endl; + } + Debug("quantifiers-relevance") << "Quantifier : " << n << std::endl; + Debug("quantifiers-relevance") << " Relevance : " << getQuantifiersEngine()->getRelevance( n ) << std::endl; + Debug("quantifiers") << " Relevance : " << getQuantifiersEngine()->getRelevance( n ) << std::endl; + } + //} + if( quantActive ){ + bool addedLemmas = doInstantiationRound( e ); + //Debug("quantifiers-dec") << "Do instantiation, level = " << d_th->getValuation().getDecisionLevel() << std::endl; + //for( int i=1; i<=(int)d_valuation.getDecisionLevel(); i++ ){ + // Debug("quantifiers-dec") << " " << d_valuation.getDecision( i ) << std::endl; + //} + if( e==Theory::EFFORT_LAST_CALL ){ + if( !addedLemmas ){ + if( d_inst_round_status==InstStrategy::STATUS_SAT ){ + Debug("inst-engine") << "No instantiation given, returning SAT..." << std::endl; + debugSat( SAT_INST_STRATEGY ); + }else{ + Debug("inst-engine") << "No instantiation given, returning unknown..." << std::endl; + d_th->getOutputChannel().setIncomplete(); + } + } + } + }else{ + if( e==Theory::EFFORT_LAST_CALL ){ + if( Options::current()->cbqi ){ + debugSat( SAT_CBQI ); + } + } + } +#ifdef IE_PRINT_PROCESS_TIMES + double clSet2 = double(clock())/double(CLOCKS_PER_SEC); + Notice() << "Done Run instantiation round " << (clSet2-clSet) << std::endl; +#endif + } +} + +void InstantiationEngine::registerQuantifier( Node f ){ + //Notice() << "do cbqi " << f << " ? " << std::endl; + Node ceBody = getQuantifiersEngine()->getCounterexampleBody( f ); + if( !doCbqi( f ) ){ + getQuantifiersEngine()->addTermToDatabase( ceBody, true ); + //need to tell which instantiators will be responsible + //by default, just chose the UF instantiator + getQuantifiersEngine()->getInstantiator( theory::THEORY_UF )->setHasConstraintsFrom( f ); + } + + //take into account user patterns + if( f.getNumChildren()==3 ){ + Node subsPat = getQuantifiersEngine()->getSubstitutedNode( f[2], f ); + //add patterns + for( int i=0; i<(int)subsPat.getNumChildren(); i++ ){ + //Notice() << "Add pattern " << subsPat[i] << " for " << f << std::endl; + ((uf::InstantiatorTheoryUf*)getQuantifiersEngine()->getInstantiator( theory::THEORY_UF ))->addUserPattern( f, subsPat[i] ); + } + } +} + +void InstantiationEngine::assertNode( Node f ){ + ////if we are doing cbqi and have not added the lemma yet, do so + //if( doCbqi( f ) && !hasAddedCbqiLemma( f ) ){ + // addCbqiLemma( f ); + //} +} + +bool InstantiationEngine::hasApplyUf( Node f ){ + if( f.getKind()==APPLY_UF ){ + return true; + }else{ + for( int i=0; i<(int)f.getNumChildren(); i++ ){ + if( hasApplyUf( f[i] ) ){ + return true; + } + } + return false; + } +} +bool InstantiationEngine::hasNonArithmeticVariable( Node f ){ + for( int i=0; i<(int)f[0].getNumChildren(); i++ ){ + TypeNode tn = f[0][i].getType(); + if( !tn.isInteger() && !tn.isReal() ){ + return true; + } + } + return false; +} + +bool InstantiationEngine::doCbqi( Node f ){ + if( Options::current()->cbqiSetByUser ){ + return Options::current()->cbqi; + }else if( Options::current()->cbqi ){ + //if quantifier has a non-arithmetic variable, then do not use cbqi + //if quantifier has an APPLY_UF term, then do not use cbqi + return !hasNonArithmeticVariable( f ) && !hasApplyUf( f[1] ); + }else{ + return false; + } +} + + + + + + + + + + + + + +//void InstantiationEngine::registerLiterals( Node n, Node f ){ +// if( n.getAttribute(InstConstantAttribute())==f ){ +// for( int i=0; i<(int)n.getNumChildren(); i++ ){ +// registerLiterals( n[i], f ); +// } +// if( !d_ce_lit[ f ].isNull() ){ +// if( getQuantifiersEngine()->d_te->getPropEngine()->isSatLiteral( n ) && n.getKind()!=NOT ){ +// if( n!=d_ce_lit[ f ] && n.notNode()!=d_ce_lit[ f ] ){ +// Debug("quant-dep-dec") << "Make " << n << " dependent on "; +// Debug("quant-dep-dec") << d_ce_lit[ f ] << std::endl; +// d_th->getOutputChannel().dependentDecision( d_ce_lit[ f ], n ); +// } +// } +// } +// } +//} + +void InstantiationEngine::debugSat( int reason ){ + if( reason==SAT_CBQI ){ + //Debug("quantifiers-sat") << "Decisions:" << std::endl; + //for( int i=1; i<=(int)d_th->getValuation().getDecisionLevel(); i++ ){ + // Debug("quantifiers-sat") << " " << i << ": " << d_th->getValuation().getDecision( i ) << std::endl; + //} + //for( BoolMap::iterator i = d_forall_asserts.begin(); i != d_forall_asserts.end(); i++ ) { + // if( (*i).second ) { + for( int i=0; i<(int)getQuantifiersEngine()->getNumAssertedQuantifiers(); i++ ){ + Node f = getQuantifiersEngine()->getAssertedQuantifier( i ); + Node cel = d_ce_lit[ f ]; + Assert( !cel.isNull() ); + bool value; + if( d_th->getValuation().hasSatValue( cel, value ) ){ + if( !value ){ + AlwaysAssert(! d_th->getValuation().isDecision( cel ), + "bad decision on counterexample literal"); + } + } + } + //} + Debug("quantifiers-sat") << "return SAT: Cbqi, no quantifier is active. " << std::endl; + //static bool setTrust = false; + //if( !setTrust ){ + // setTrust = true; + // Notice() << "trust-"; + //} + }else if( reason==SAT_INST_STRATEGY ){ + Debug("quantifiers-sat") << "return SAT: No strategy chose to add an instantiation." << std::endl; + //Notice() << "sat "; + //Unimplemented(); + } +} + +void InstantiationEngine::propagate( Theory::Effort level ){ + //propagate as decision all counterexample literals that are not asserted + for( std::map< Node, Node >::iterator it = d_ce_lit.begin(); it != d_ce_lit.end(); ++it ){ + bool value; + if( !d_th->getValuation().hasSatValue( it->second, value ) ){ + //if not already set, propagate as decision + d_th->getOutputChannel().propagateAsDecision( it->second ); + Debug("cbqi-prop-as-dec") << "CBQI: propagate as decision " << it->second << std::endl; + } + } +} diff --git a/src/theory/quantifiers/instantiation_engine.h b/src/theory/quantifiers/instantiation_engine.h new file mode 100644 index 000000000..c6aaed18a --- /dev/null +++ b/src/theory/quantifiers/instantiation_engine.h @@ -0,0 +1,79 @@ +/********************* */ +/*! \file instantiation_engine.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Instantiation Engine classes + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__INSTANTIATION_ENGINE_H +#define __CVC4__INSTANTIATION_ENGINE_H + +#include "theory/quantifiers_engine.h" +#include "theory/quantifiers/theory_quantifiers.h" + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +class InstantiationEngine : public QuantifiersModule +{ +private: + TheoryQuantifiers* d_th; + QuantifiersEngine* getQuantifiersEngine(); +private: + typedef context::CDHashMap< Node, bool, NodeHashFunction > BoolMap; + /** status of instantiation round (one of InstStrategy::STATUS_*) */ + int d_inst_round_status; + /** map from universal quantifiers to their counterexample literals */ + std::map< Node, Node > d_ce_lit; +private: + bool hasAddedCbqiLemma( Node f ); + void addCbqiLemma( Node f ); +private: + /** helper functions */ + bool hasNonArithmeticVariable( Node f ); + bool hasApplyUf( Node f ); + /** whether to do CBQI for quantifier f */ + bool doCbqi( Node f ); +private: + /** do instantiation round */ + bool doInstantiationRound( Theory::Effort effort ); + /** register literals of n, f is the quantifier it belongs to */ + //void registerLiterals( Node n, Node f ); +private: + enum{ + SAT_CBQI, + SAT_INST_STRATEGY, + }; + /** debug sat */ + void debugSat( int reason ); +public: + InstantiationEngine( TheoryQuantifiers* th ); + ~InstantiationEngine(){} + + void check( Theory::Effort e ); + void registerQuantifier( Node f ); + void assertNode( Node f ); + Node explain(TNode n){ return Node::null(); } + void propagate( Theory::Effort level ); +public: + /** get the corresponding counterexample literal for quantified formula node n */ + Node getCounterexampleLiteralFor( Node f ) { return d_ce_lit.find( f )==d_ce_lit.end() ? Node::null() : d_ce_lit[ f ]; } +}; + +} +} +} + +#endif diff --git a/src/theory/quantifiers/kinds b/src/theory/quantifiers/kinds new file mode 100644 index 000000000..106d95cef --- /dev/null +++ b/src/theory/quantifiers/kinds @@ -0,0 +1,48 @@ +# kinds -*- sh -*- +# +# For documentation on this file format, please refer to +# src/theory/builtin/kinds. +# + +theory THEORY_QUANTIFIERS ::CVC4::theory::quantifiers::TheoryQuantifiers "theory/quantifiers/theory_quantifiers.h" +typechecker "theory/quantifiers/theory_quantifiers_type_rules.h" +instantiator ::CVC4::theory::quantifiers::InstantiatorTheoryQuantifiers "theory/quantifiers/theory_quantifiers_instantiator.h" + +properties check propagate presolve + +rewriter ::CVC4::theory::quantifiers::QuantifiersRewriter "theory/quantifiers/quantifiers_rewriter.h" + +operator FORALL 2:3 "universally quantified formula" + +operator EXISTS 2:3 "existentially quantified formula" + +variable INST_CONSTANT "instantiation constant" + +sort BOUND_VAR_LIST_TYPE \ + Cardinality::INTEGERS \ + not-well-founded \ + "Bound Var type" + +operator BOUND_VAR_LIST 1: "bound variables" + +sort INST_PATTERN_TYPE \ + Cardinality::INTEGERS \ + not-well-founded \ + "Instantiation pattern type" + +operator INST_PATTERN 1: "instantiation pattern" + +sort INST_PATTERN_LIST_TYPE \ + Cardinality::INTEGERS \ + not-well-founded \ + "Instantiation pattern list type" + +operator INST_PATTERN_LIST 1: "instantiation pattern list" + +typerule FORALL ::CVC4::theory::quantifiers::QuantifierForallTypeRule +typerule EXISTS ::CVC4::theory::quantifiers::QuantifierExistsTypeRule +typerule BOUND_VAR_LIST ::CVC4::theory::quantifiers::QuantifierBoundVarListTypeRule +typerule INST_PATTERN ::CVC4::theory::quantifiers::QuantifierInstPatternTypeRule +typerule INST_PATTERN_LIST ::CVC4::theory::quantifiers::QuantifierInstPatternListTypeRule + +endtheory diff --git a/src/theory/quantifiers/model_engine.cpp b/src/theory/quantifiers/model_engine.cpp new file mode 100644 index 000000000..a72b103d1 --- /dev/null +++ b/src/theory/quantifiers/model_engine.cpp @@ -0,0 +1,1401 @@ +/********************* */ +/*! \file model_engine.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** 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 "theory/theory_engine.h" +#include "theory/uf/equality_engine.h" +#include "theory/uf/theory_uf.h" +#include "theory/uf/theory_uf_strong_solver.h" +#include "theory/uf/theory_uf_instantiator.h" + +//#define ME_PRINT_PROCESS_TIMES + +//#define DISABLE_EVAL_SKIP_MULTIPLE +#define RECONSIDER_FUNC_DEFAULT_VALUE +#define RECONSIDER_FUNC_CONSTANT +#define USE_INDEX_ORDERING +//#define ONE_INST_PER_QUANT_PER_ROUND + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; + +void printRepresentative( const char* c, QuantifiersEngine* qe, Node r ){ + if( r.getType()==NodeManager::currentNM()->booleanType() ){ + if( qe->getEqualityQuery()->areEqual( r, NodeManager::currentNM()->mkConst( true ) ) ){ + Debug( c ) << "true"; + }else{ + Debug( c ) << "false"; + } + }else{ + Debug( c ) << qe->getEqualityQuery()->getRepresentative( r ); + } +} + +RepAlphabet::RepAlphabet( RepAlphabet& ra, QuantifiersEngine* qe ){ + //translate to current representatives + for( std::map< TypeNode, std::vector< Node > >::iterator it = ra.d_type_reps.begin(); it != ra.d_type_reps.end(); ++it ){ + std::vector< Node > reps; + for( int i=0; i<(int)it->second.size(); i++ ){ + //reps.push_back( ie->getEqualityQuery()->getRepresentative( it->second[i] ) ); + reps.push_back( it->second[i] ); + } + set( it->first, reps ); + } +} + +void RepAlphabet::set( TypeNode t, std::vector< Node >& reps ){ + d_type_reps[t].insert( d_type_reps[t].begin(), reps.begin(), reps.end() ); + for( int i=0; i<(int)reps.size(); i++ ){ + d_tmap[ reps[i] ] = i; + } +} + +void RepAlphabet::debugPrint( const char* c, QuantifiersEngine* qe ){ + for( std::map< TypeNode, std::vector< Node > >::iterator it = d_type_reps.begin(); it != d_type_reps.end(); ++it ){ + Debug( c ) << it->first << " : " << std::endl; + for( int i=0; i<(int)it->second.size(); i++ ){ + Debug( c ) << " " << i << ": " << it->second[i] << std::endl; + Debug( c ) << " eq_class( " << it->second[i] << " ) : "; + ((uf::InstantiatorTheoryUf*)qe->getInstantiator( THEORY_UF ))->outputEqClass( c, it->second[i] ); + Debug( c ) << std::endl; + } + } +} + +RepAlphabetIterator::RepAlphabetIterator( QuantifiersEngine* qe, Node f, ModelEngine* model ){ + for( size_t i=0; i<f[0].getNumChildren(); i++ ){ + d_index_order.push_back( i ); + } + initialize( qe, f, model ); +} + +RepAlphabetIterator::RepAlphabetIterator( QuantifiersEngine* qe, Node f, ModelEngine* model, std::vector< int >& indexOrder ){ + d_index_order.insert( d_index_order.begin(), indexOrder.begin(), indexOrder.end() ); + initialize( qe, f, model ); +} + +void RepAlphabetIterator::initialize( QuantifiersEngine* qe, Node f, ModelEngine* model ){ + d_f = f; + d_model = model; + //store instantiation constants + for( size_t i=0; i<f[0].getNumChildren(); i++ ){ + d_ic.push_back( qe->getInstantiationConstant( d_f, i ) ); + d_index.push_back( 0 ); + } + //make the d_var_order mapping + for( size_t i=0; i<d_index_order.size(); i++ ){ + d_var_order[d_index_order[i]] = i; + } + //for testing + d_inst_tried = 0; + d_inst_tests = 0; +} + +void RepAlphabetIterator::increment2( QuantifiersEngine* qe, int counter ){ + Assert( !isFinished() ); + //increment d_index + while( counter>=0 && d_index[counter]==(int)(d_model->getReps()->d_type_reps[d_f[0][d_index_order[counter]].getType()].size()-1) ){ + counter--; + } + if( counter==-1 ){ + d_index.clear(); + }else{ + for( int i=(int)d_index.size()-1; i>counter; i-- ){ + d_index[i] = 0; + d_model->clearEvalFailed( i ); + } + d_index[counter]++; + d_model->clearEvalFailed( counter ); + } +} + +void RepAlphabetIterator::increment( QuantifiersEngine* qe ){ + if( !isFinished() ){ + increment2( qe, (int)d_index.size()-1 ); + } +} + +bool RepAlphabetIterator::isFinished(){ + return d_index.empty(); +} + +void RepAlphabetIterator::getMatch( QuantifiersEngine* ie, InstMatch& m ){ + for( int i=0; i<(int)d_index.size(); i++ ){ + m.d_map[ ie->getInstantiationConstant( d_f, i ) ] = getTerm( i ); + } +} + +Node RepAlphabetIterator::getTerm( int i ){ + TypeNode tn = d_f[0][d_index_order[i]].getType(); + Assert( d_model->getReps()->d_type_reps.find( tn )!=d_model->getReps()->d_type_reps.end() ); + return d_model->getReps()->d_type_reps[tn][d_index[d_index_order[i]]]; +} + +void RepAlphabetIterator::calculateTerms( QuantifiersEngine* qe ){ + d_terms.clear(); + for( int i=0; i<qe->getNumInstantiationConstants( d_f ); i++ ){ + d_terms.push_back( getTerm( i ) ); + } +} + +void RepAlphabetIterator::debugPrint( const char* c ){ + for( int i=0; i<(int)d_index.size(); i++ ){ + Debug( c ) << i << ": " << d_index[i] << ", (" << getTerm( i ) << " / " << d_ic[ i ] << std::endl; + } +} + +void RepAlphabetIterator::debugPrintSmall( const char* c ){ + Debug( c ) << "RI: "; + for( int i=0; i<(int)d_index.size(); i++ ){ + Debug( c ) << d_index[i] << ": " << getTerm( i ) << " "; + } + Debug( c ) << std::endl; +} + +//set value function +void UfModelTree::setValue( QuantifiersEngine* qe, Node n, Node v, std::vector< int >& indexOrder, bool ground, int argIndex ){ + if( d_data.empty() ){ + d_value = v; + }else if( !d_value.isNull() && d_value!=v ){ + d_value = Node::null(); + } + if( argIndex<(int)n.getNumChildren() ){ + //take r = null when argument is the model basis + Node r; + if( ground || !n[ indexOrder[argIndex] ].getAttribute(ModelBasisAttribute()) ){ + r = qe->getEqualityQuery()->getRepresentative( n[ indexOrder[argIndex] ] ); + } + d_data[ r ].setValue( qe, n, v, indexOrder, ground, argIndex+1 ); + } +} + +//get value function +Node UfModelTree::getValue( QuantifiersEngine* qe, Node n, std::vector< int >& indexOrder, int& depIndex, int argIndex ){ + if( !d_value.isNull() && isTotal( n.getOperator(), argIndex ) ){ + //Notice() << "Constant, return " << d_value << ", depIndex = " << argIndex << std::endl; + depIndex = argIndex; + return d_value; + }else{ + Node val; + int childDepIndex[2] = { argIndex, argIndex }; + for( int i=0; i<2; i++ ){ + //first check the argument, then check default + Node r; + if( i==0 ){ + r = qe->getEqualityQuery()->getRepresentative( n[ indexOrder[argIndex] ] ); + } + std::map< Node, UfModelTree >::iterator it = d_data.find( r ); + if( it!=d_data.end() ){ + val = it->second.getValue( qe, n, indexOrder, childDepIndex[i], argIndex+1 ); + if( !val.isNull() ){ + break; + } + }else{ + //argument is not a defined argument: thus, it depends on this argument + childDepIndex[i] = argIndex+1; + } + } + //update depIndex + depIndex = childDepIndex[0]>childDepIndex[1] ? childDepIndex[0] : childDepIndex[1]; + //Notice() << "Return " << val << ", depIndex = " << depIndex; + //Notice() << " ( " << childDepIndex[0] << ", " << childDepIndex[1] << " )" << std::endl; + return val; + } +} + +//simplify function +void UfModelTree::simplify( Node op, Node defaultVal, int argIndex ){ + if( argIndex<(int)op.getType().getNumChildren()-1 ){ + std::vector< Node > eraseData; + //first process the default argument + Node r; + std::map< Node, UfModelTree >::iterator it = d_data.find( r ); + if( it!=d_data.end() ){ + if( !defaultVal.isNull() && it->second.d_value==defaultVal ){ + eraseData.push_back( r ); + }else{ + it->second.simplify( op, defaultVal, argIndex+1 ); + if( !it->second.d_value.isNull() && it->second.isTotal( op, argIndex+1 ) ){ + defaultVal = it->second.d_value; + }else{ + defaultVal = Node::null(); + } + } + } + //now see if any children can be removed, and simplify the ones that cannot + for( std::map< Node, UfModelTree >::iterator it = d_data.begin(); it != d_data.end(); ++it ){ + if( !it->first.isNull() ){ + if( !defaultVal.isNull() && it->second.d_value==defaultVal ){ + eraseData.push_back( it->first ); + }else{ + it->second.simplify( op, defaultVal, argIndex+1 ); + } + } + } + for( int i=0; i<(int)eraseData.size(); i++ ){ + d_data.erase( eraseData[i] ); + } + } +} + +//is total function +bool UfModelTree::isTotal( Node op, int argIndex ){ + if( argIndex==(int)(op.getType().getNumChildren()-1) ){ + return !d_value.isNull(); + }else{ + Node r; + std::map< Node, UfModelTree >::iterator it = d_data.find( r ); + if( it!=d_data.end() ){ + return it->second.isTotal( op, argIndex+1 ); + }else{ + return false; + } + } +} + +Node UfModelTree::getConstantValue( QuantifiersEngine* qe, Node n, std::vector< int >& indexOrder, int argIndex ){ + return d_value; +} + +void indent( const char* c, int ind ){ + for( int i=0; i<ind; i++ ){ + Debug( c ) << " "; + } +} + +void UfModelTree::debugPrint( const char* c, QuantifiersEngine* qe, std::vector< int >& indexOrder, int ind, int arg ){ + if( !d_data.empty() ){ + for( std::map< Node, UfModelTree >::iterator it = d_data.begin(); it != d_data.end(); ++it ){ + if( !it->first.isNull() ){ + indent( c, ind ); + Debug( c ) << "if x_" << indexOrder[arg] << " == " << it->first << std::endl; + it->second.debugPrint( c, qe, indexOrder, ind+2, arg+1 ); + } + } + if( d_data.find( Node::null() )!=d_data.end() ){ + d_data[ Node::null() ].debugPrint( c, qe, indexOrder, ind, arg+1 ); + } + }else{ + indent( c, ind ); + Debug( c ) << "return "; + printRepresentative( c, qe, d_value ); + //Debug( c ) << " { "; + //for( int i=0; i<(int)d_explicit.size(); i++ ){ + // Debug( c ) << d_explicit[i] << " "; + //} + //Debug( c ) << "}"; + Debug( c ) << std::endl; + } +} + +UfModel::UfModel( Node op, ModelEngine* me ) : d_op( op ), d_me( me ), + d_model_constructed( false ), d_reconsider_model( false ){ + + d_tree = UfModelTreeOrdered( op ); TypeNode tn = d_op.getType(); tn = tn[(int)tn.getNumChildren()-1]; Assert( tn==NodeManager::currentNM()->booleanType() || uf::StrongSolverTheoryUf::isRelevantType( tn ) ); //look at ground assertions + for( int i=0; i<(int)d_me->getQuantifiersEngine()->getTermDatabase()->d_op_map[ d_op ].size(); i++ ){ + Node n = d_me->getQuantifiersEngine()->getTermDatabase()->d_op_map[ d_op ][i]; + bool add = true; + if( n.getAttribute(NoMatchAttribute()) ){ + add = false; + //determine if it has model basis attribute + for( int j=0; j<(int)n.getNumChildren(); j++ ){ + if( n[j].getAttribute(ModelBasisAttribute()) ){ + add = true; + break; + } + } + } + if( add ){ + d_ground_asserts.push_back( n ); + Node r = d_me->getQuantifiersEngine()->getEqualityQuery()->getRepresentative( n ); + d_ground_asserts_reps.push_back( r ); + } + } + //determine if it is constant + if( !d_ground_asserts.empty() ){ + bool isConstant = true; + for( int i=1; i<(int)d_ground_asserts.size(); i++ ){ + if( d_ground_asserts_reps[0]!=d_ground_asserts_reps[i] ){ + isConstant = false; + break; + } + } + if( isConstant ){ + //set constant value + Node t = d_me->getModelBasisApplyUfTerm( d_op ); + Node r = d_ground_asserts_reps[0]; + setValue( t, r, false ); + setModel(); + d_reconsider_model = true; + Debug("fmf-model-cons") << "Function " << d_op << " is the constant function "; + printRepresentative( "fmf-model-cons", d_me->getQuantifiersEngine(), r ); + Debug("fmf-model-cons") << std::endl; + } + } +} + +void UfModel::setValue( Node n, Node v, bool ground ){ + d_set_values[ ground ? 1 : 0 ][n] = v; +} + +void UfModel::setModel(){ + makeModel( d_me->getQuantifiersEngine(), d_tree ); + d_model_constructed = true; +} + +void UfModel::clearModel(){ + for( int k=0; k<2; k++ ){ + d_set_values[k].clear(); + } + d_tree.clear(); + d_model_constructed = false; +} + +Node UfModel::getConstantValue( QuantifiersEngine* qe, Node n ){ + if( d_model_constructed ){ + return d_tree.getConstantValue( qe, n ); + }else{ + return Node::null(); + } +} + +bool UfModel::isConstant(){ + Node gn = d_me->getModelBasisApplyUfTerm( d_op ); + Node n = getConstantValue( d_me->getQuantifiersEngine(), gn ); + return !n.isNull(); +} + +void UfModel::buildModel(){ +#ifdef RECONSIDER_FUNC_CONSTANT + if( d_model_constructed ){ + if( d_reconsider_model ){ + //if we are allowed to reconsider default value, then see if the default value can be improved + Node t = d_me->getModelBasisApplyUfTerm( d_op ); + Node v = d_set_values[0][t]; + if( d_value_pro_con[1][v].size()>d_value_pro_con[0][v].size() ){ + Debug("fmf-model-cons-debug") << "Consider changing the default value for " << d_op << std::endl; + clearModel(); + } + } + } +#endif + //now, construct models for each uninterpretted function/predicate + if( !d_model_constructed ){ + Debug("fmf-model-cons") << "Construct model for " << d_op << "..." << std::endl; + //now, set the values in the model + for( int i=0; i<(int)d_ground_asserts.size(); i++ ){ + Node n = d_ground_asserts[i]; + Node v = d_ground_asserts_reps[i]; + //if this assertion did not help the model, just consider it ground + //set n = v in the model tree + Debug("fmf-model-cons") << " Set " << n << " = "; + printRepresentative( "fmf-model-cons", d_me->getQuantifiersEngine(), v ); + Debug("fmf-model-cons") << std::endl; + //set it as ground value + setValue( n, v ); + } + //set the default value + //chose defaultVal based on heuristic (the best proportion of "pro" responses) + Node defaultVal; + double maxScore = -1; + for( int i=0; i<(int)d_values.size(); i++ ){ + Node v = d_values[i]; + double score = ( 1.0 + (double)d_value_pro_con[0][v].size() )/( 1.0 + (double)d_value_pro_con[1][v].size() ); + Debug("fmf-model-cons") << " - score( "; + printRepresentative( "fmf-model-cons", d_me->getQuantifiersEngine(), v ); + Debug("fmf-model-cons") << " ) = " << score << std::endl; + if( score>maxScore ){ + defaultVal = v; + maxScore = score; + } + } +#ifdef RECONSIDER_FUNC_DEFAULT_VALUE + if( maxScore<1.0 ){ + //consider finding another value, if possible + Debug("fmf-model-cons-debug") << "Poor choice for default value, score = " << maxScore << std::endl; + TypeNode tn = d_op.getType(); + Node newDefaultVal = d_me->getArbitraryElement( tn[(int)tn.getNumChildren()-1], d_values ); + if( !newDefaultVal.isNull() ){ + defaultVal = newDefaultVal; + Debug("fmf-model-cons-debug") << "-> Change default value to "; + printRepresentative( "fmf-model-cons-debug", d_me->getQuantifiersEngine(), defaultVal ); + Debug("fmf-model-cons-debug") << std::endl; + }else{ + Debug("fmf-model-cons-debug") << "-> Could not find arbitrary element of type " << tn[(int)tn.getNumChildren()-1] << std::endl; + Debug("fmf-model-cons-debug") << " Excluding: "; + for( int i=0; i<(int)d_values.size(); i++ ){ + Debug("fmf-model-cons-debug") << d_values[i] << " "; + } + Debug("fmf-model-cons-debug") << std::endl; + } + } +#endif + Assert( !defaultVal.isNull() ); + //get the default term (this term must be defined non-ground in model) + Node defaultTerm = d_me->getModelBasisApplyUfTerm( d_op ); + Debug("fmf-model-cons") << " Choose "; + printRepresentative("fmf-model-cons", d_me->getQuantifiersEngine(), defaultVal ); + Debug("fmf-model-cons") << " as default value (" << defaultTerm << ")" << std::endl; + Debug("fmf-model-cons") << " # quantifiers pro = " << d_value_pro_con[0][defaultVal].size() << std::endl; + Debug("fmf-model-cons") << " # quantifiers con = " << d_value_pro_con[1][defaultVal].size() << std::endl; + setValue( defaultTerm, defaultVal, false ); + Debug("fmf-model-cons") << " Making model..."; + setModel(); + Debug("fmf-model-cons") << " Finished constructing model for " << d_op << "." << std::endl; + } +} + +void UfModel::setValuePreference( Node f, Node n, bool isPro ){ + Node v = d_me->getQuantifiersEngine()->getEqualityQuery()->getRepresentative( n ); + //Notice() << "Set value preference " << n << " = " << v << " " << isPro << std::endl; + if( std::find( d_values.begin(), d_values.end(), v )==d_values.end() ){ + d_values.push_back( v ); + } + int index = isPro ? 0 : 1; + if( std::find( d_value_pro_con[index][v].begin(), d_value_pro_con[index][v].end(), f )==d_value_pro_con[index][v].end() ){ + d_value_pro_con[index][v].push_back( f ); + } +} + +void UfModel::makeModel( QuantifiersEngine* qe, UfModelTreeOrdered& tree ){ + for( int k=0; k<2; k++ ){ + for( std::map< Node, Node >::iterator it = d_set_values[k].begin(); it != d_set_values[k].end(); ++it ){ + tree.setValue( qe, it->first, it->second, k==1 ); + } + } + tree.simplify(); +} + +void UfModel::debugPrint( const char* c ){ + //Debug( c ) << "Function " << d_op << std::endl; + //Debug( c ) << " Type: " << d_op.getType() << std::endl; + //Debug( c ) << " Ground asserts:" << std::endl; + //for( int i=0; i<(int)d_ground_asserts.size(); i++ ){ + // Debug( c ) << " " << d_ground_asserts[i] << " = "; + // printRepresentative( c, d_me->getQuantifiersEngine(), d_ground_asserts[i] ); + // Debug( c ) << std::endl; + //} + //Debug( c ) << " Model:" << std::endl; + + TypeNode t = d_op.getType(); + Debug( c ) << d_op << "( "; + for( int i=0; i<(int)(t.getNumChildren()-1); i++ ){ + Debug( c ) << "x_" << i << " : " << t[i]; + if( i<(int)(t.getNumChildren()-2) ){ + Debug( c ) << ", "; + } + } + Debug( c ) << " ) : " << t[(int)t.getNumChildren()-1] << std::endl; + if( d_tree.isEmpty() ){ + Debug( c ) << " [undefined]" << std::endl; + }else{ + d_tree.debugPrint( c, d_me->getQuantifiersEngine(), 3 ); + Debug( c ) << std::endl; + } + //Debug( c ) << " Phase reqs:" << std::endl; //for( int i=0; i<2; i++ ){ + // for( std::map< Node, std::vector< Node > >::iterator it = d_reqs[i].begin(); it != d_reqs[i].end(); ++it ){ + // Debug( c ) << " " << it->first << std::endl; + // for( int j=0; j<(int)it->second.size(); j++ ){ + // Debug( c ) << " " << it->second[j] << " -> " << (i==1) << std::endl; + // } + // } + //} + //Debug( c ) << std::endl; + //for( int i=0; i<2; i++ ){ + // for( std::map< Node, std::map< Node, std::vector< Node > > >::iterator it = d_eq_reqs[i].begin(); it != d_eq_reqs[i].end(); ++it ){ + // Debug( c ) << " " << "For " << it->first << ":" << std::endl; + // for( std::map< Node, std::vector< Node > >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){ + // for( int j=0; j<(int)it2->second.size(); j++ ){ + // Debug( c ) << " " << it2->first << ( i==1 ? "==" : "!=" ) << it2->second[j] << std::endl; + // } + // } + // } + //} +} + +//Model Engine constructor +ModelEngine::ModelEngine( TheoryQuantifiers* th ){ + d_th = th; + d_quantEngine = th->getQuantifiersEngine(); + d_ss = ((uf::TheoryUF*)d_quantEngine->getTheoryEngine()->getTheory( THEORY_UF ))->getStrongSolver(); +} + +void ModelEngine::check( Theory::Effort e ){ + if( e==Theory::EFFORT_LAST_CALL ){ + bool quantsInit = true; + //first, check if we can minimize the model further + if( !d_ss->minimize() ){ + return; + } + if( useModel() ){ + //now, check if any quantifiers are un-initialized + for( int i=0; i<d_quantEngine->getNumAssertedQuantifiers(); i++ ){ + Node f = d_quantEngine->getAssertedQuantifier( i ); + if( !initializeQuantifier( f ) ){ + quantsInit = false; + } + } + } + if( quantsInit ){ +#ifdef ME_PRINT_PROCESS_TIMES + Notice() << "---Instantiation Round---" << std::endl; +#endif + Debug("fmf-model-debug") << "---Begin Instantiation Round---" << std::endl; + ++(d_statistics.d_inst_rounds); + d_quantEngine->getTermDatabase()->reset( e ); + //build the representatives + Debug("fmf-model-debug") << "Building representatives..." << std::endl; + buildRepresentatives(); + if( useModel() ){ + //initialize the model + Debug("fmf-model-debug") << "Initializing model..." << std::endl; + initializeModel(); + //analyze the quantifiers + Debug("fmf-model-debug") << "Analyzing quantifiers..." << std::endl; + analyzeQuantifiers(); + //build the model + Debug("fmf-model-debug") << "Building model..." << std::endl; + for( std::map< Node, UfModel >::iterator it = d_uf_model.begin(); it != d_uf_model.end(); ++it ){ + it->second.buildModel(); + } + } + //print debug + debugPrint("fmf-model-complete"); + //try exhaustive instantiation + Debug("fmf-model-debug") << "Do exhaustive instantiation..." << std::endl; + int addedLemmas = 0; + for( int i=0; i<d_quantEngine->getNumAssertedQuantifiers(); i++ ){ + Node f = d_quantEngine->getAssertedQuantifier( i ); + if( d_quant_sat.find( f )==d_quant_sat.end() ){ + addedLemmas += instantiateQuantifier( f ); + } + } +#ifdef ME_PRINT_PROCESS_TIMES + Notice() << "Added Lemmas = " << addedLemmas << std::endl; +#endif + if( addedLemmas==0 ){ + //debugPrint("fmf-consistent"); + //for( std::map< Node, UfModel >::iterator it = d_uf_model.begin(); it != d_uf_model.end(); ++it ){ + // it->second.simplify(); + //} + Debug("fmf-consistent") << std::endl; + debugPrint("fmf-consistent"); + } + } + d_quantEngine->flushLemmas( &d_th->getOutputChannel() ); + } +} + +void ModelEngine::registerQuantifier( Node f ){ + +} + +void ModelEngine::assertNode( Node f ){ + +} + +bool ModelEngine::useModel() { + return Options::current()->fmfModelBasedInst; +} + +bool ModelEngine::initializeQuantifier( Node f ){ + if( d_quant_init.find( f )==d_quant_init.end() ){ + d_quant_init[f] = true; + Debug("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; + // } + //} + + std::vector< Node > terms; + for( int j=0; j<(int)f[0].getNumChildren(); j++ ){ + terms.push_back( getModelBasisTerm( f[0][j].getType() ) ); + } + ++(d_statistics.d_num_quants_init); + if( d_quantEngine->addInstantiation( f, terms ) ){ + return false; + }else{ + //usually shouldn't happen + //Notice() << "No model basis for " << f << std::endl; + ++(d_statistics.d_num_quants_init_fail); + } + } + return true; +} + +void ModelEngine::buildRepresentatives(){ + d_ra.clear(); + //collect all representatives for all types and store as representative alphabet + for( int i=0; i<d_ss->getNumCardinalityTypes(); i++ ){ + TypeNode tn = d_ss->getCardinalityType( i ); + std::vector< Node > reps; + d_ss->getRepresentatives( tn, reps ); + Assert( !reps.empty() ); + //if( (int)reps.size()!=d_ss->getCardinality( tn ) ){ + // Notice() << "InstStrategyFinteModelFind::processResetInstantiationRound: Bad representatives for type." << std::endl; + // Notice() << " " << tn << " has cardinality " << d_ss->getCardinality( tn ); + // Notice() << " but only " << (int)reps.size() << " were given." << std::endl; + // Unimplemented( 27 ); + //} + Debug("fmf-model-debug") << " " << tn << " -> " << reps.size() << std::endl; + Debug("fmf-model-debug") << " "; + for( int i=0; i<(int)reps.size(); i++ ){ + Debug("fmf-model-debug") << reps[i] << " "; + } + Debug("fmf-model-debug") << std::endl; + //set them in the alphabet + d_ra.set( tn, reps ); +#ifdef ME_PRINT_PROCESS_TIMES + Notice() << tn << " has " << reps.size() << " representatives. " << std::endl; +#endif + } +} + +void ModelEngine::initializeModel(){ + d_uf_model.clear(); + d_quant_sat.clear(); + for( int k=0; k<2; k++ ){ + d_pro_con_quant[k].clear(); + } + + ////now analyze quantifiers (temporary) + //for( int i=0; i<(int)d_quantEngine->getNumAssertedQuantifiers(); i++ ){ + // Node f = d_quantEngine->getAssertedQuantifier( i ); + // Debug("fmf-model-req") << "Phase requirements for " << f << ": " << std::endl; + // 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; + // Debug("fmf-model-req") << " " << n << " -> " << it->second << std::endl; + // if( n.getKind()==APPLY_UF ){ + // processPredicate( f, n, it->second ); + // }else if( n.getKind()==EQUAL ){ + // processEquality( f, n, it->second ); + // } + // } + //} + for( int i=0; i<(int)d_quantEngine->getNumAssertedQuantifiers(); i++ ){ + Node f = d_quantEngine->getAssertedQuantifier( i ); + initializeUf( f[1] ); + //register model basis terms + std::vector< Node > vars; + std::vector< Node > subs; + for( int j=0; j<(int)f[0].getNumChildren(); j++ ){ + vars.push_back( d_quantEngine->getInstantiationConstant( f, j ) ); + subs.push_back( getModelBasisTerm( f[0][j].getType() ) ); + } + Node n = d_quantEngine->getCounterexampleBody( f ); + Node gn = n.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + registerModelBasis( n, gn ); + } +} + +void ModelEngine::analyzeQuantifiers(){ + int quantSatInit = 0; + int nquantSatInit = 0; + //analyze the preferences of each quantifier + for( int i=0; i<(int)d_quantEngine->getNumAssertedQuantifiers(); i++ ){ + Node f = d_quantEngine->getAssertedQuantifier( i ); + Debug("fmf-model-prefs") << "Analyze quantifier " << f << std::endl; + std::vector< Node > pro_con[2]; + std::vector< Node > pro_con_patterns[2]; + //check which model basis terms have good and bad definitions according to f + 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; + Node gn = d_model_basis[n]; + Debug("fmf-model-req") << " Req: " << n << " -> " << it->second << std::endl; + int pref = 0; + bool isConst = true; + std::vector< Node > uf_terms; + std::vector< Node > uf_term_patterns; + if( gn.getKind()==APPLY_UF ){ + if( d_quantEngine->getEqualityQuery()->hasTerm( gn ) ){ + uf_terms.push_back( gn ); + uf_term_patterns.push_back( n ); + bool phase = areEqual( gn, NodeManager::currentNM()->mkConst( true ) ); + pref = phase!=it->second ? 1 : -1; + } + }else if( gn.getKind()==EQUAL ){ + bool success = true; + for( int j=0; j<2; j++ ){ + if( n[j].getKind()==APPLY_UF ){ + Node op = gn[j].getOperator(); + if( d_uf_model.find( op )!=d_uf_model.end() ){ + Assert( gn[j].getKind()==APPLY_UF ); + uf_terms.push_back( gn[j] ); + uf_term_patterns.push_back( n[j] ); + }else{ + //found undefined uf operator + success = false; + } + }else if( n[j].hasAttribute(InstConstantAttribute()) ){ + isConst = false; + } + } + if( success && !uf_terms.empty() ){ + if( (!it->second && areEqual( gn[0], gn[1] )) || (it->second && areDisequal( gn[0], gn[1] )) ){ + pref = 1; + }else if( (it->second && areEqual( gn[0], gn[1] )) || (!it->second && areDisequal( gn[0], gn[1] )) ){ + pref = -1; + } + } + } + if( pref!=0 ){ + Debug("fmf-model-prefs") << " It is " << ( pref==1 ? "pro" : "con" ); + Debug("fmf-model-prefs") << " the definition of " << n << std::endl; + if( pref==1 ){ + if( isConst ){ + for( int j=0; j<(int)uf_terms.size(); j++ ){ + //the only uf that is initialized are those that are constant + Node op = uf_terms[j].getOperator(); + Assert( d_uf_model.find( op )!=d_uf_model.end() ); + if( !d_uf_model[op].isConstant() ){ + isConst = false; + break; + } + } + if( isConst ){ + d_quant_sat[f] = true; + //we only need to consider current term definition(s) for this quantifier to be satisified, ignore the others + for( int k=0; k<2; k++ ){ + pro_con[k].clear(); + pro_con_patterns[k].clear(); + } + //instead, just note to the model for each uf term that f is pro its definition + for( int j=0; j<(int)uf_terms.size(); j++ ){ + Node op = uf_terms[j].getOperator(); + d_uf_model[op].d_reconsider_model = false; + } + } + } + } + if( d_quant_sat.find( f )!=d_quant_sat.end() ){ + Debug("fmf-model-prefs") << " * Constant SAT due to definition of " << n << std::endl; + break; + }else{ + int pcIndex = pref==1 ? 0 : 1; + for( int j=0; j<(int)uf_terms.size(); j++ ){ + pro_con[pcIndex].push_back( uf_terms[j] ); + pro_con_patterns[pcIndex].push_back( uf_term_patterns[j] ); + } + } + } + } + if( d_quant_sat.find( f )!=d_quant_sat.end() ){ + quantSatInit++; + d_statistics.d_pre_sat_quant += quantSatInit; + }else{ + nquantSatInit++; + d_statistics.d_pre_nsat_quant += quantSatInit; + } + //add quantifier's preferences to vector + 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(); + d_uf_model[op].setValuePreference( f, pro_con[k][j], k==0 ); + d_pro_con_quant[k][ f ].push_back( pro_con_patterns[k][j] ); + } + } + } + Debug("fmf-model-prefs") << "Pre-Model Completion: Quantifiers SAT: " << quantSatInit << " / " << (quantSatInit+nquantSatInit) << std::endl; +} + +int ModelEngine::instantiateQuantifier( Node f ){ + int addedLemmas = 0; + Debug("inst-fmf-ei") << "Add matches for " << f << "..." << std::endl; + Debug("inst-fmf-ei") << " Instantiation Constants: "; + for( size_t i=0; i<f[0].getNumChildren(); i++ ){ + Debug("inst-fmf-ei") << d_quantEngine->getInstantiationConstant( f, i ) << " "; + } + Debug("inst-fmf-ei") << std::endl; + for( int k=0; k<2; k++ ){ + Debug("inst-fmf-ei") << " " << ( k==0 ? "Pro" : "Con" ) << " definitions:" << std::endl; + for( int i=0; i<(int)d_pro_con_quant[k][f].size(); i++ ){ + Debug("inst-fmf-ei") << " " << d_pro_con_quant[k][f][i] << std::endl; + } + } + if( d_pro_con_quant[0][f].empty() ){ + Debug("inst-fmf-ei") << "WARNING: " << f << " has no pros for default literal definitions" << std::endl; + } + d_eval_failed_lits.clear(); + d_eval_failed.clear(); + d_eval_term_model.clear(); + //d_eval_term_vals.clear(); + //d_eval_term_fv_deps.clear(); + RepAlphabetIterator riter( d_quantEngine, f, this ); + increment( &riter ); +#ifdef ONE_INST_PER_QUANT_PER_ROUND + while( !riter.isFinished() && addedLemmas==0 ){ +#else + while( !riter.isFinished() ){ +#endif + InstMatch m; + riter.getMatch( d_quantEngine, m ); + Debug("fmf-model-eval") << "* Add instantiation " << std::endl; + riter.d_inst_tried++; + if( d_quantEngine->addInstantiation( f, m ) ){ + addedLemmas++; + } + riter.increment( d_quantEngine ); + increment( &riter ); + } + if( Debug.isOn("inst-fmf-ei") ){ + int totalInst = 1; + for( int i=0; i<(int)f[0].getNumChildren(); i++ ){ + totalInst = totalInst * (int)getReps()->d_type_reps[ f[0][i].getType() ].size(); + } + Debug("inst-fmf-ei") << "Finished: " << std::endl; + Debug("inst-fmf-ei") << " Inst Skipped: " << (totalInst-riter.d_inst_tried) << std::endl; + Debug("inst-fmf-ei") << " Inst Tried: " << riter.d_inst_tried << std::endl; + Debug("inst-fmf-ei") << " Inst Added: " << addedLemmas << std::endl; + Debug("inst-fmf-ei") << " # Tests: " << riter.d_inst_tests << std::endl; + } + return addedLemmas; +} + +void ModelEngine::registerModelBasis( Node n, Node gn ){ + if( d_model_basis.find( n )==d_model_basis.end() ){ + d_model_basis[n] = gn; + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + registerModelBasis( n[i], gn[i] ); + } + } +} + +Node ModelEngine::getArbitraryElement( TypeNode tn, std::vector< Node >& exclude ){ + Node retVal; + if( tn==NodeManager::currentNM()->booleanType() ){ + if( exclude.empty() ){ + retVal = NodeManager::currentNM()->mkConst( false ); + }else if( exclude.size()==1 ){ + retVal = NodeManager::currentNM()->mkConst( areEqual( exclude[0], NodeManager::currentNM()->mkConst( false ) ) ); + } + }else if( d_ra.d_type_reps.find( tn )!=d_ra.d_type_reps.end() ){ + for( int i=0; i<(int)d_ra.d_type_reps[tn].size(); i++ ){ + if( std::find( exclude.begin(), exclude.end(), d_ra.d_type_reps[tn][i] )==exclude.end() ){ + retVal = d_ra.d_type_reps[tn][i]; + break; + } + } + } + if( !retVal.isNull() ){ + return d_quantEngine->getEqualityQuery()->getRepresentative( retVal ); + }else{ + return Node::null(); + } +} + +Node ModelEngine::getModelBasisTerm( TypeNode tn, int i ){ + return d_ss->getCardinalityTerm( tn ); +} + +Node ModelEngine::getModelBasisApplyUfTerm( Node op ){ + if( d_model_basis_term.find( op )==d_model_basis_term.end() ){ + TypeNode t = op.getType(); + std::vector< Node > children; + children.push_back( op ); + for( int i=0; i<(int)t.getNumChildren()-1; i++ ){ + children.push_back( getModelBasisTerm( t[i] ) ); + } + d_model_basis_term[op] = NodeManager::currentNM()->mkNode( APPLY_UF, children ); + } + return d_model_basis_term[op]; +} + +bool ModelEngine::isModelBasisTerm( Node op, Node n ){ + if( n.getOperator()==op ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( !n[i].getAttribute(ModelBasisAttribute()) ){ + return false; + } + } + return true; + }else{ + return false; + } +} + +void ModelEngine::initializeUf( Node n ){ + std::vector< Node > terms; + collectUfTerms( n, terms ); + for( int i=0; i<(int)terms.size(); i++ ){ + initializeUfModel( terms[i].getOperator() ); + } +} + +void ModelEngine::collectUfTerms( Node n, std::vector< Node >& terms ){ + if( n.getKind()==APPLY_UF ){ + terms.push_back( n ); + } + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + collectUfTerms( n[i], terms ); + } +} + +void ModelEngine::initializeUfModel( Node op ){ + if( d_uf_model.find( op )==d_uf_model.end() ){ + TypeNode tn = op.getType(); + tn = tn[ (int)tn.getNumChildren()-1 ]; + if( tn==NodeManager::currentNM()->booleanType() || uf::StrongSolverTheoryUf::isRelevantType( tn ) ){ + d_uf_model[ op ] = UfModel( op, this ); + } + } +} + +void ModelEngine::makeEvalTermModel( Node n ){ + if( d_eval_term_model.find( n )==d_eval_term_model.end() ){ + makeEvalTermIndexOrder( n ); + if( !d_eval_term_use_default_model[n] ){ + Node op = n.getOperator(); + d_eval_term_model[n] = UfModelTreeOrdered( op, d_eval_term_index_order[n] ); + d_uf_model[op].makeModel( d_quantEngine, d_eval_term_model[n] ); + Debug("fmf-model-index-order") << "Make model for " << n << " : " << std::endl; + d_eval_term_model[n].debugPrint( "fmf-model-index-order", d_quantEngine, 2 ); + } + } +} + +struct sortGetMaxVariableNum { + std::map< Node, int > d_max_var_num; + int computeMaxVariableNum( Node n ){ + if( n.getKind()==INST_CONSTANT ){ + return n.getAttribute(InstVarNumAttribute()); + }else if( n.hasAttribute(InstConstantAttribute()) ){ + int maxVal = -1; + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + int val = getMaxVariableNum( n[i] ); + if( val>maxVal ){ + maxVal = val; + } + } + return maxVal; + }else{ + return -1; + } + } + int getMaxVariableNum( Node n ){ + std::map< Node, int >::iterator it = d_max_var_num.find( n ); + if( it==d_max_var_num.end() ){ + int num = computeMaxVariableNum( n ); + d_max_var_num[n] = num; + return num; + }else{ + return it->second; + } + } + bool operator() (Node i,Node j) { return (getMaxVariableNum(i)<getMaxVariableNum(j));} +}; + +void ModelEngine::makeEvalTermIndexOrder( Node n ){ + if( d_eval_term_index_order.find( n )==d_eval_term_index_order.end() ){ + //sort arguments in order of least significant vs. most significant variable in default ordering + std::map< Node, std::vector< int > > argIndex; + std::vector< Node > args; + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( argIndex.find( n[i] )==argIndex.end() ){ + args.push_back( n[i] ); + } + argIndex[n[i]].push_back( i ); + } + sortGetMaxVariableNum sgmvn; + std::sort( args.begin(), args.end(), sgmvn ); + for( int i=0; i<(int)args.size(); i++ ){ + for( int j=0; j<(int)argIndex[ args[i] ].size(); j++ ){ + d_eval_term_index_order[n].push_back( argIndex[ args[i] ][j] ); + } + } + bool useDefault = true; + for( int i=0; i<(int)d_eval_term_index_order[n].size(); i++ ){ + if( i!=d_eval_term_index_order[n][i] ){ + useDefault = false; + break; + } + } + d_eval_term_use_default_model[n] = useDefault; + Debug("fmf-model-index-order") << "Will consider the following index ordering for " << n << " : "; + for( int i=0; i<(int)d_eval_term_index_order[n].size(); i++ ){ + Debug("fmf-model-index-order") << d_eval_term_index_order[n][i] << " "; + } + Debug("fmf-model-index-order") << std::endl; + } +} + +//void ModelEngine::processPredicate( Node f, Node p, bool phase ){ +// Node op = p.getOperator(); +// initializeUfModel( op ); +// d_uf_model[ op ].addRequirement( f, p, phase ); +//} +// +//void ModelEngine::processEquality( Node f, Node eq, bool phase ){ +// for( int i=0; i<2; i++ ){ +// int j = i==0 ? 1 : 0; +// if( eq[i].getKind()==APPLY_UF && eq[i].hasAttribute(InstConstantAttribute()) ){ +// Node op = eq[i].getOperator(); +// initializeUfModel( op ); +// d_uf_model[ op ].addEqRequirement( f, eq[i], eq[j], phase ); +// } +// } +//} + +void ModelEngine::increment( RepAlphabetIterator* rai ){ + if( useModel() ){ + bool success; + do{ + success = true; + if( !rai->isFinished() ){ + //see if instantiation is already true in current model + Debug("fmf-model-eval") << "Evaulating "; + rai->debugPrintSmall("fmf-model-eval"); + //calculate represenative terms we are currently considering + rai->calculateTerms( d_quantEngine ); + rai->d_inst_tests++; + //if eVal is not (int)rai->d_index.size(), then the instantiation is already true in the model, + // and eVal is the highest index in rai which we can safely iterate + int depIndex; + if( evaluate( rai, d_quantEngine->getCounterexampleBody( rai->d_f ), depIndex )==1 ){ + Debug("fmf-model-eval") << " Returned success with depIndex = " << depIndex << std::endl; + //Notice() << "Returned eVal = " << eVal << "/" << rai->d_index.size() << std::endl; + if( depIndex<(int)rai->d_index.size() ){ +#ifdef DISABLE_EVAL_SKIP_MULTIPLE + depIndex = (int)rai->d_index.size()-1; +#endif + rai->increment2( d_quantEngine, depIndex ); + success = false; + } + }else{ + Debug("fmf-model-eval") << " Returned failure." << std::endl; + } + } + }while( !success ); + } +} + +//if evaluate( rai, n, phaseReq ) = eVal, +// if eVal = rai->d_index.size() +// then the formula n instantiated with rai cannot be proven to be equal to phaseReq +// otherwise, +// each n{rai->d_index[0]/x_0...rai->d_index[eVal]/x_eVal, */x_(eVal+1) ... */x_n } is equal to phaseReq in the current model +int ModelEngine::evaluate( RepAlphabetIterator* rai, Node n, int& depIndex ){ + ++(d_statistics.d_eval_formulas); + //Debug("fmf-model-eval-debug") << "Evaluate " << n << " " << phaseReq << std::endl; + if( n.getKind()==NOT ){ + int val = evaluate( rai, n[0], depIndex ); + return val==1 ? -1 : ( val==-1 ? 1 : 0 ); + }else if( n.getKind()==OR || n.getKind()==AND || n.getKind()==IMPLIES ){ + int baseVal = n.getKind()==AND ? 1 : -1; + int eVal = baseVal; + int posDepIndex = (int)rai->d_index.size(); + int negDepIndex = -1; + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + //evaluate subterm + int childDepIndex; + Node nn = ( i==0 && n.getKind()==IMPLIES ) ? n[i].notNode() : n[i]; + int eValT = evaluate( rai, nn, childDepIndex ); + if( eValT==baseVal ){ + if( eVal==baseVal ){ + if( childDepIndex>negDepIndex ){ + negDepIndex = childDepIndex; + } + } + }else if( eValT==-baseVal ){ + eVal = -baseVal; + if( childDepIndex<posDepIndex ){ + posDepIndex = childDepIndex; + if( posDepIndex==-1 ){ + break; + } + } + }else if( eValT==0 ){ + if( eVal==baseVal ){ + eVal = 0; + } + } + } + if( eVal!=0 ){ + depIndex = eVal==-baseVal ? posDepIndex : negDepIndex; + return eVal; + }else{ + return 0; + } + }else if( n.getKind()==IFF || n.getKind()==XOR ){ + int depIndex1; + int eVal = evaluate( rai, n[0], depIndex1 ); + if( eVal!=0 ){ + int depIndex2; + int eVal2 = evaluate( rai, n.getKind()==XOR ? n[1].notNode() : n[1], depIndex2 ); + if( eVal2!=0 ){ + depIndex = depIndex1>depIndex2 ? depIndex1 : depIndex2; + return eVal==eVal2 ? 1 : -1; + } + } + return 0; + }else if( n.getKind()==ITE ){ + int depIndex1; + int eVal = evaluate( rai, n[0], depIndex1 ); + if( eVal==0 ){ + //DO_THIS: evaluate children to see if they are the same value? + return 0; + }else{ + int depIndex2; + int eValT = evaluate( rai, n[eVal==1 ? 1 : 2], depIndex2 ); + depIndex = depIndex1>depIndex2 ? depIndex1 : depIndex2; + return eValT; + } + }else if( n.getKind()==FORALL ){ + return 0; + }else{ + ////if we know we will fail again, immediately return + //if( d_eval_failed.find( n )!=d_eval_failed.end() ){ + // if( d_eval_failed[n] ){ + // return -1; + // } + //} + //Debug("fmf-model-eval-debug") << "Evaluate literal " << n << std::endl; + //this should be a literal + Node gn = n.substitute( rai->d_ic.begin(), rai->d_ic.end(), rai->d_terms.begin(), rai->d_terms.end() ); + //Debug("fmf-model-eval-debug") << " Ground version = " << gn << std::endl; + int retVal = 0; + std::vector< Node > fv_deps; + if( n.getKind()==APPLY_UF ){ + //case for boolean predicates + Node val = evaluateTerm( n, gn, fv_deps ); + if( d_quantEngine->getEqualityQuery()->hasTerm( val ) ){ + if( areEqual( val, NodeManager::currentNM()->mkConst( true ) ) ){ + retVal = 1; + }else{ + retVal = -1; + } + } + }else if( n.getKind()==EQUAL ){ + //case for equality + retVal = evaluateEquality( n[0], n[1], gn[0], gn[1], fv_deps ); + } + if( retVal!=0 ){ + int maxIndex = -1; + for( int i=0; i<(int)fv_deps.size(); i++ ){ + int index = rai->d_var_order[ fv_deps[i].getAttribute(InstVarNumAttribute()) ]; + if( index>maxIndex ){ + maxIndex = index; + if( index==(int)rai->d_index.size()-1 ){ + break; + } + } + } + Debug("fmf-model-eval-debug") << "Evaluate literal: return " << retVal << ", depends = " << maxIndex << std::endl; + depIndex = maxIndex; + } + return retVal; + } +} + +int ModelEngine::evaluateEquality( Node n1, Node n2, Node gn1, Node gn2, std::vector< Node >& fv_deps ){ + ++(d_statistics.d_eval_eqs); + Debug("fmf-model-eval-debug") << "Evaluate equality: " << std::endl; + Debug("fmf-model-eval-debug") << " " << n1 << " = " << n2 << std::endl; + Debug("fmf-model-eval-debug") << " " << gn1 << " = " << gn2 << std::endl; + Node val1 = evaluateTerm( n1, gn1, fv_deps ); + Node val2 = evaluateTerm( n2, gn2, fv_deps ); + Debug("fmf-model-eval-debug") << " Values : "; + printRepresentative( "fmf-model-eval-debug", d_quantEngine, val1 ); + Debug("fmf-model-eval-debug") << " = "; + printRepresentative( "fmf-model-eval-debug", d_quantEngine, val2 ); + Debug("fmf-model-eval-debug") << std::endl; + int retVal = 0; + if( areEqual( val1, val2 ) ){ + retVal = 1; + }else if( areDisequal( val1, val2 ) ){ + retVal = -1; + } + if( retVal!=0 ){ + Debug("fmf-model-eval-debug") << " ---> Success, value = " << (retVal==1) << std::endl; + }else{ + Debug("fmf-model-eval-debug") << " ---> Failed" << std::endl; + } + Debug("fmf-model-eval-debug") << " Value depends on variables: "; + for( int i=0; i<(int)fv_deps.size(); i++ ){ + Debug("fmf-model-eval-debug") << fv_deps[i] << " "; + } + Debug("fmf-model-eval-debug") << std::endl; + return retVal; +} + +Node ModelEngine::evaluateTerm( Node n, Node gn, std::vector< Node >& fv_deps ){ + if( n.hasAttribute(InstConstantAttribute()) ){ + if( n.getKind()==INST_CONSTANT ){ + fv_deps.push_back( n ); + return gn; + //}else if( d_eval_term_fv_deps.find( n )!=d_eval_term_fv_deps.end() && + // d_eval_term_fv_deps[n].find( gn )!=d_eval_term_fv_deps[n].end() ){ + // fv_deps.insert( fv_deps.end(), d_eval_term_fv_deps[n][gn].begin(), d_eval_term_fv_deps[n][gn].end() ); + // return d_eval_term_vals[gn]; + }else{ + //Debug("fmf-model-eval-debug") << "Evaluate term " << n << " (" << gn << ")" << std::endl; + //first we must evaluate the arguments + Node val = gn; + if( n.getKind()==APPLY_UF ){ + Node op = gn.getOperator(); + //if it is a defined UF, then consult the interpretation + Node gnn = gn; + ++(d_statistics.d_eval_uf_terms); + int depIndex = 0; + //first we must evaluate the arguments + bool childrenChanged = false; + std::vector< Node > children; + children.push_back( op ); + std::map< int, std::vector< Node > > children_var_deps; + //for each argument, calculate its value, and the variables its value depends upon + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + Node nn = evaluateTerm( n[i], gn[i], children_var_deps[i] ); + children.push_back( nn ); + childrenChanged = childrenChanged || nn!=gn[i]; + } + //remake gn if changed + if( childrenChanged ){ + gnn = NodeManager::currentNM()->mkNode( APPLY_UF, children ); + } + if( d_uf_model.find( op )!=d_uf_model.end() ){ +#ifdef USE_INDEX_ORDERING + //make the term model specifically for n + makeEvalTermModel( n ); + //now, consult the model + if( d_eval_term_use_default_model[n] ){ + val = d_uf_model[op].d_tree.getValue( d_quantEngine, gnn, depIndex ); + }else{ + val = d_eval_term_model[ n ].getValue( d_quantEngine, gnn, depIndex ); + } + //Debug("fmf-model-eval-debug") << "Evaluate term " << n << " (" << gn << ", " << gnn << ")" << std::endl; + //d_eval_term_model[ n ].debugPrint("fmf-model-eval-debug", d_quantEngine ); + Assert( !val.isNull() ); +#else + //now, consult the model + val = d_uf_model[op].d_tree.getValue( d_quantEngine, gnn, depIndex ); +#endif + }else{ + d_eval_term_use_default_model[n] = true; + val = gnn; + depIndex = (int)n.getNumChildren(); + } + Debug("fmf-model-eval-debug") << "Evaluate term " << n << " = " << gn << " = " << gnn << " = "; + printRepresentative( "fmf-model-eval-debug", d_quantEngine, val ); + Debug("fmf-model-eval-debug") << ", depIndex = " << depIndex << std::endl; + if( !val.isNull() ){ +#ifdef USE_INDEX_ORDERING + for( int i=0; i<depIndex; i++ ){ + int index = d_eval_term_use_default_model[n] ? i : d_eval_term_index_order[n][i]; + Debug("fmf-model-eval-debug") << "Add variables from " << index << "..." << std::endl; + fv_deps.insert( fv_deps.end(), children_var_deps[index].begin(), + children_var_deps[index].end() ); + } +#else + //calculate the free variables that the value depends on : take those in children_var_deps[0...depIndex-1] + for( std::map< int, std::vector< Node > >::iterator it = children_var_deps.begin(); it != children_var_deps.end(); ++it ){ + if( it->first<depIndex ){ + fv_deps.insert( fv_deps.end(), it->second.begin(), it->second.end() ); + } + } +#endif + } + ////cache the result + //d_eval_term_vals[gn] = val; + //d_eval_term_fv_deps[n][gn].insert( d_eval_term_fv_deps[n][gn].end(), fv_deps.begin(), fv_deps.end() ); + } + return val; + } + }else{ + return n; + } +} + +void ModelEngine::clearEvalFailed( int index ){ + for( int i=0; i<(int)d_eval_failed_lits[index].size(); i++ ){ + d_eval_failed[ d_eval_failed_lits[index][i] ] = false; + } + d_eval_failed_lits[index].clear(); +} + +bool ModelEngine::areEqual( Node a, Node b ){ + return d_quantEngine->getEqualityQuery()->areEqual( a, b ); +} + +bool ModelEngine::areDisequal( Node a, Node b ){ + return d_quantEngine->getEqualityQuery()->areDisequal( a, b ); +} + +void ModelEngine::debugPrint( const char* c ){ + Debug( c ) << "---Current Model---" << std::endl; + Debug( c ) << "Representatives: " << std::endl; + d_ra.debugPrint( c, d_quantEngine ); + Debug( c ) << "Quantifiers: " << std::endl; + for( int i=0; i<(int)d_quantEngine->getNumAssertedQuantifiers(); i++ ){ + Node f = d_quantEngine->getAssertedQuantifier( i ); + Debug( c ) << " "; + if( d_quant_sat.find( f )!=d_quant_sat.end() ){ + Debug( c ) << "*SAT* "; + }else{ + Debug( c ) << " "; + } + Debug( c ) << f << std::endl; + } + Debug( c ) << "Functions: " << std::endl; + for( std::map< Node, UfModel >::iterator it = d_uf_model.begin(); it != d_uf_model.end(); ++it ){ + it->second.debugPrint( c ); + Debug( c ) << std::endl; + } +} + +ModelEngine::Statistics::Statistics(): + d_inst_rounds("ModelEngine::Inst_Rounds", 0), + d_pre_sat_quant("ModelEngine::Status_quant_pre_sat", 0), + d_pre_nsat_quant("ModelEngine::Status_quant_pre_non_sat", 0), + d_eval_formulas("ModelEngine::Eval_Formulas", 0 ), + d_eval_eqs("ModelEngine::Eval_Equalities", 0 ), + d_eval_uf_terms("ModelEngine::Eval_Uf_Terms", 0 ), + d_num_quants_init("ModelEngine::Num_Quants", 0 ), + d_num_quants_init_fail("ModelEngine::Num_Quants_No_Basis", 0 ) +{ + StatisticsRegistry::registerStat(&d_inst_rounds); + StatisticsRegistry::registerStat(&d_pre_sat_quant); + StatisticsRegistry::registerStat(&d_pre_nsat_quant); + StatisticsRegistry::registerStat(&d_eval_formulas); + StatisticsRegistry::registerStat(&d_eval_eqs); + StatisticsRegistry::registerStat(&d_eval_uf_terms); + StatisticsRegistry::registerStat(&d_num_quants_init); + StatisticsRegistry::registerStat(&d_num_quants_init_fail); +} + +ModelEngine::Statistics::~Statistics(){ + StatisticsRegistry::unregisterStat(&d_inst_rounds); + StatisticsRegistry::unregisterStat(&d_pre_sat_quant); + StatisticsRegistry::unregisterStat(&d_pre_nsat_quant); + StatisticsRegistry::unregisterStat(&d_eval_formulas); + StatisticsRegistry::unregisterStat(&d_eval_eqs); + StatisticsRegistry::unregisterStat(&d_eval_uf_terms); + StatisticsRegistry::unregisterStat(&d_num_quants_init); + StatisticsRegistry::unregisterStat(&d_num_quants_init_fail); +} diff --git a/src/theory/quantifiers/model_engine.h b/src/theory/quantifiers/model_engine.h new file mode 100644 index 000000000..4031efdf9 --- /dev/null +++ b/src/theory/quantifiers/model_engine.h @@ -0,0 +1,369 @@ +/********************* */ +/*! \file instantiation_engine.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Instantiation Engine classes + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__MODEL_ENGINE_H +#define __CVC4__MODEL_ENGINE_H + +#include "theory/quantifiers_engine.h" +#include "theory/quantifiers/theory_quantifiers.h" + +namespace CVC4 { +namespace theory { + +namespace uf{ + class StrongSolverTheoryUf; +} + +namespace quantifiers { + +/** this class stores a representative alphabet */ +class RepAlphabet { +public: + RepAlphabet(){} + RepAlphabet( RepAlphabet& ra, QuantifiersEngine* qe ); + ~RepAlphabet(){} + std::map< TypeNode, std::vector< Node > > d_type_reps; + std::map< Node, int > d_tmap; + /** clear the alphabet */ + void clear(){ + d_type_reps.clear(); + d_tmap.clear(); + } + /** set the representatives for type */ + void set( TypeNode t, std::vector< Node >& reps ); + /** returns index in d_type_reps for node n */ + int getIndexFor( Node n ) { return d_tmap.find( n )!=d_tmap.end() ? d_tmap[n] : -1; } + /** debug print */ + void debugPrint( const char* c, QuantifiersEngine* qe ); +}; + +class ModelEngine; + +/** this class iterates over a RepAlphabet */ +class RepAlphabetIterator { +private: + void initialize( QuantifiersEngine* qe, Node f, ModelEngine* model ); +public: + RepAlphabetIterator( QuantifiersEngine* qe, Node f, ModelEngine* model ); + RepAlphabetIterator( QuantifiersEngine* qe, Node f, ModelEngine* model, std::vector< int >& indexOrder ); + ~RepAlphabetIterator(){} + //pointer to quantifier + Node d_f; + //pointer to model + ModelEngine* d_model; + //index we are considering + std::vector< int > d_index; + //ordering for variables we are indexing over + // for example, given reps = { a, b } and quantifier forall( x, y, z ) P( x, y, z ) with d_index_order = { 2, 0, 1 }, + // then we consider instantiations in this order: + // a/x a/y a/z + // a/x b/y a/z + // b/x a/y a/z + // b/x b/y a/z + // ... + std::vector< int > d_index_order; + //variables to index they are considered at + // for example, if d_index_order = { 2, 0, 1 } + // then d_var_order = { 0 -> 1, 1 -> 2, 2 -> 0 } + std::map< int, int > d_var_order; + //the instantiation constants of d_f + std::vector< Node > d_ic; + //the current terms we are considering + std::vector< Node > d_terms; +public: + /** increment the iterator */ + void increment2( QuantifiersEngine* qe, int counter ); + void increment( QuantifiersEngine* qe ); + /** is the iterator finished? */ + bool isFinished(); + /** produce the match that this iterator represents */ + void getMatch( QuantifiersEngine* qe, InstMatch& m ); + /** get the i_th term we are considering */ + Node getTerm( int i ); + /** get the number of terms we are considering */ + int getNumTerms() { return d_f[0].getNumChildren(); } + /** refresh d_term to be current with d_index */ + void calculateTerms( QuantifiersEngine* qe ); + /** debug print */ + void debugPrint( const char* c ); + void debugPrintSmall( const char* c ); + //for debugging + int d_inst_tried; + int d_inst_tests; +}; + + +class UfModelTree +{ +public: + UfModelTree(){} + /** the data */ + std::map< Node, UfModelTree > d_data; + /** the value of this tree node (if all paths lead to same value) */ + Node d_value; +public: + //is this model tree empty? + bool isEmpty() { return d_data.empty(); } + //clear + void clear(){ + d_data.clear(); + d_value = Node::null(); + } + /** setValue function + * + * For each argument of n with ModelBasisAttribute() set to true will be considered default arguments if ground=false + * + */ + void setValue( QuantifiersEngine* qe, Node n, Node v, std::vector< int >& indexOrder, bool ground, int argIndex ); + /** getValue function + * + * returns $val, the value of ground term n + * Say n is f( t_0...t_n ) + * depIndex is the index for which every term of the form f( t_0 ... t_depIndex, *,... * ) is equal to $val + * for example, if g( x_0, x_1, x_2 ) := lambda x_0 x_1 x_2. if( x_1==a ) b else c, + * then g( a, a, a ) would return b with depIndex = 1 + * If ground = true, we are asking whether the term n is constant (assumes that all non-model basis arguments are ground) + * + */ + Node getValue( QuantifiersEngine* qe, Node n, std::vector< int >& indexOrder, int& depIndex, int argIndex ); + ///** getConstant Value function + // * + // * given term n, where n may contain model basis arguments + // * if n is constant for its entire domain, then this function returns the value of its domain + // * otherwise, it returns null + // * for example, if f( x_0, x_1 ) := if( x_0 = a ) b else if( x_1 = a ) a else b, + // * then f( a, e ) would return b, while f( e, a ) would return null + // * + // */ + Node getConstantValue( QuantifiersEngine* qe, Node n, std::vector< int >& indexOrder, int argIndex ); + /** simplify function */ + void simplify( Node op, Node defaultVal, int argIndex ); + // is total ? + bool isTotal( Node op, int argIndex ); +public: + void debugPrint( const char* c, QuantifiersEngine* qe, std::vector< int >& indexOrder, int ind = 0, int arg = 0 ); +}; + +class UfModelTreeOrdered +{ +private: + Node d_op; + std::vector< int > d_index_order; + UfModelTree d_tree; +public: + UfModelTreeOrdered(){} + UfModelTreeOrdered( Node op ) : d_op( op ){ + TypeNode tn = d_op.getType(); + for( int i=0; i<(int)(tn.getNumChildren()-1); i++ ){ + d_index_order.push_back( i ); + } + } + UfModelTreeOrdered( Node op, std::vector< int >& indexOrder ) : d_op( op ){ + d_index_order.insert( d_index_order.end(), indexOrder.begin(), indexOrder.end() ); + } + bool isEmpty() { return d_tree.isEmpty(); } + void clear() { d_tree.clear(); } + void setValue( QuantifiersEngine* qe, Node n, Node v, bool ground = true ){ + d_tree.setValue( qe, n, v, d_index_order, ground, 0 ); + } + Node getValue( QuantifiersEngine* qe, Node n, int& depIndex ){ + return d_tree.getValue( qe, n, d_index_order, depIndex, 0 ); + } + Node getConstantValue( QuantifiersEngine* qe, Node n ) { + return d_tree.getConstantValue( qe, n, d_index_order, 0 ); + } + void simplify() { d_tree.simplify( d_op, Node::null(), 0 ); } + bool isTotal() { return d_tree.isTotal( d_op, 0 ); } +public: + void debugPrint( const char* c, QuantifiersEngine* qe, int ind = 0 ){ + d_tree.debugPrint( c, qe, d_index_order, ind ); + } +}; + +class UfModel +{ +//public: + //std::map< Node, std::vector< Node > > d_reqs[2]; + //std::map< Node, std::map< Node, std::vector< Node > > > d_eq_reqs[2]; + ///** add requirement */ + //void addRequirement( Node f, Node p, bool phase ) { d_reqs[ phase ? 1 : 0 ][ f ].push_back( p ); } + ///** add equality requirement */ + //void addEqRequirement( Node f, Node t, Node te, bool phase ) { d_eq_reqs[ phase ? 1 : 0 ][ f ][ t ].push_back( te ); } +private: + Node d_op; + ModelEngine* d_me; + std::vector< Node > d_ground_asserts; + std::vector< Node > d_ground_asserts_reps; + bool d_model_constructed; + //store for set values + std::map< Node, Node > d_set_values[2]; + // preferences for default values + std::vector< Node > d_values; + std::map< Node, std::vector< Node > > d_value_pro_con[2]; + /** set value */ + void setValue( Node n, Node v, bool ground = true ); + /** set model */ + void setModel(); + /** clear model */ + void clearModel(); +public: + UfModel(){} + UfModel( Node op, ModelEngine* qe ); + ~UfModel(){} + //data structure that stores the model + UfModelTreeOrdered d_tree; + //quantifiers that are satisfied because of the constant definition of d_op + bool d_reconsider_model; +public: + /** debug print */ + void debugPrint( const char* c ); + /** get constant value */ + Node getConstantValue( QuantifiersEngine* qe, Node n ); + /** is empty */ + bool isEmpty() { return d_ground_asserts.empty(); } + /** is constant */ + bool isConstant(); +public: + /** build model */ + void buildModel(); + /** make model */ + void makeModel( QuantifiersEngine* qe, UfModelTreeOrdered& tree ); +public: + /** set value preference */ + void setValuePreference( Node f, Node n, bool isPro ); +}; + + + + +class ModelEngine : public QuantifiersModule +{ + friend class UfModel; + friend class RepAlphabetIterator; +private: + TheoryQuantifiers* d_th; + QuantifiersEngine* d_quantEngine; + uf::StrongSolverTheoryUf* d_ss; + //which quantifiers have been initialized + std::map< Node, bool > d_quant_init; + //map from ops to model basis terms + std::map< Node, Node > d_model_basis_term; + //map from instantiation terms to their model basis equivalent + std::map< Node, Node > d_model_basis; + //the model we are working with + RepAlphabet d_ra; + std::map< Node, UfModel > d_uf_model; + ////map from model basis terms to quantifiers that are pro/con their definition + //std::map< Node, std::vector< Node > > d_quant_pro_con[2]; + //map from quantifiers to model basis terms that are pro the definition of + std::map< Node, std::vector< Node > > d_pro_con_quant[2]; + //map from quantifiers to if are constant SAT + std::map< Node, bool > d_quant_sat; +private: + int evaluate( RepAlphabetIterator* rai, Node n, int& depIndex ); + int evaluateEquality( Node n1, Node n2, Node gn1, Node gn2, std::vector< Node >& fv_deps ); + Node evaluateTerm( Node n, Node gn, std::vector< Node >& fv_deps ); + //temporary storing which literals have failed + void clearEvalFailed( int index ); + std::map< Node, bool > d_eval_failed; + std::map< int, std::vector< Node > > d_eval_failed_lits; + ////temporary storing for values/free variable dependencies + //std::map< Node, Node > d_eval_term_vals; + //std::map< Node, std::map< Node, std::vector< Node > > > d_eval_term_fv_deps; +private: + //map from terms to the models used to calculate their value + std::map< Node, UfModelTreeOrdered > d_eval_term_model; + std::map< Node, bool > d_eval_term_use_default_model; + void makeEvalTermModel( Node n ); + //index ordering to use for each term + std::map< Node, std::vector< int > > d_eval_term_index_order; + int getMaxVariableNum( int n ); + void makeEvalTermIndexOrder( Node n ); +public: + void increment( RepAlphabetIterator* rai ); +private: + //queries about equality + bool areEqual( Node a, Node b ); + bool areDisequal( Node a, Node b ); +private: + bool useModel(); +private: + //initialize quantifiers, return false if lemma needed to be added + bool initializeQuantifier( Node f ); + //build representatives + void buildRepresentatives(); + //initialize model + void initializeModel(); + //analyze quantifiers + void analyzeQuantifiers(); + //instantiate quantifier, return number of lemmas produced + int instantiateQuantifier( Node f ); +private: + //register instantiation terms with their corresponding model basis terms + void registerModelBasis( Node n, Node gn ); + //for building UF model + void initializeUf( Node n ); + void collectUfTerms( Node n, std::vector< Node >& terms ); + void initializeUfModel( Node op ); + //void processPredicate( Node f, Node p, bool phase ); + //void processEquality( Node f, Node eq, bool phase ); +public: + ModelEngine( TheoryQuantifiers* th ); + ~ModelEngine(){} + //get quantifiers engine + QuantifiersEngine* getQuantifiersEngine() { return d_quantEngine; } + //get representatives + RepAlphabet* getReps() { return &d_ra; } + //get arbitrary element + Node getArbitraryElement( TypeNode tn, std::vector< Node >& exclude ); + //get model basis term + Node getModelBasisTerm( TypeNode tn, int i = 0 ); + //get model basis term for op + Node getModelBasisApplyUfTerm( Node op ); + //is model basis term for op + bool isModelBasisTerm( Node op, Node n ); +public: + void check( Theory::Effort e ); + void registerQuantifier( Node f ); + void assertNode( Node f ); + Node explain(TNode n){ return Node::null(); } + void propagate( Theory::Effort level ){} + void debugPrint( const char* c ); +public: + /** statistics class */ + class Statistics { + public: + IntStat d_inst_rounds; + IntStat d_pre_sat_quant; + IntStat d_pre_nsat_quant; + IntStat d_eval_formulas; + IntStat d_eval_eqs; + IntStat d_eval_uf_terms; + IntStat d_num_quants_init; + IntStat d_num_quants_init_fail; + Statistics(); + ~Statistics(); + }; + Statistics d_statistics; +}; + +} +} +} + +#endif diff --git a/src/theory/quantifiers/quantifiers_rewriter.cpp b/src/theory/quantifiers/quantifiers_rewriter.cpp new file mode 100644 index 000000000..0fba9d59e --- /dev/null +++ b/src/theory/quantifiers/quantifiers_rewriter.cpp @@ -0,0 +1,722 @@ +/********************* */ +/*! \file theory_quantifiers_rewriter.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of QuantifiersRewriter class + **/ + +#include "theory/quantifiers/quantifiers_rewriter.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; + +bool QuantifiersRewriter::isClause( Node n ){ + if( isLiteral( n ) ){ + return true; + }else if( n.getKind()==NOT ){ + return isCube( n[0] ); + }else if( n.getKind()==OR ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( !isClause( n[i] ) ){ + return false; + } + } + return true; + }else if( n.getKind()==IMPLIES ){ + return isCube( n[0] ) && isClause( n[1] ); + }else{ + return false; + } +} + +bool QuantifiersRewriter::isLiteral( Node n ){ + switch( n.getKind() ){ + case NOT: + return isLiteral( n[0] ); + break; + case OR: + case AND: + case IMPLIES: + case XOR: + case ITE: + case IFF: + return false; + break; + case EQUAL: + return n[0].getType()!=NodeManager::currentNM()->booleanType(); + break; + default: + break; + } + return true; +} + +bool QuantifiersRewriter::isCube( Node n ){ + if( isLiteral( n ) ){ + return true; + }else if( n.getKind()==NOT ){ + return isClause( n[0] ); + }else if( n.getKind()==AND ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( !isCube( n[i] ) ){ + return false; + } + } + return true; + }else{ + return false; + } +} + +void QuantifiersRewriter::addNodeToOrBuilder( Node n, NodeBuilder<>& t ){ + if( n.getKind()==OR ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + t << n[i]; + } + }else{ + t << n; + } +} + +void QuantifiersRewriter::computeArgs( std::map< Node, bool >& active, Node n ){ + if( active.find( n )!=active.end() ){ + active[n] = true; + }else{ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + computeArgs( active, n[i] ); + } + } +} + +void QuantifiersRewriter::computeArgs( std::vector< Node >& args, std::vector< Node >& activeArgs, Node n ){ + std::map< Node, bool > active; + for( int i=0; i<(int)args.size(); i++ ){ + active[ args[i] ] = false; + } + //Notice() << "For " << n << " : " << std::endl; + computeArgs( active, n ); + activeArgs.clear(); + for( std::map< Node, bool >::iterator it = active.begin(); it != active.end(); ++it ){ + Node n = it->first; + //Notice() << " " << it->first << " is " << it->second << std::endl; + if( it->second ){ //only add bound variables that occur in body + activeArgs.push_back( it->first ); + } + } +} + +bool QuantifiersRewriter::hasArg( std::vector< Node >& args, Node n ){ + if( std::find( args.begin(), args.end(), n )!=args.end() ){ + return true; + }else{ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( hasArg( args, n[i] ) ){ + return true; + } + } + return false; + } +} + +void QuantifiersRewriter::setNestedQuantifiers( Node n, Node q ){ + if( n.getKind()==FORALL || n.getKind()==EXISTS ){ + Debug("quantifiers-rewrite-debug") << "Set nested quant attribute " << n << std::endl; + NestedQuantAttribute nqai; + n.setAttribute(nqai,q); + } + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + setNestedQuantifiers( n[i], q ); + } +} + +RewriteResponse QuantifiersRewriter::preRewrite(TNode in) { + Debug("quantifiers-rewrite-debug") << "pre-rewriting " << in << " " << in.hasAttribute(NestedQuantAttribute()) << std::endl; + if( in.getKind()==kind::EXISTS || in.getKind()==kind::FORALL ){ + if( !in.hasAttribute(NestedQuantAttribute()) ){ + setNestedQuantifiers( in[ 1 ], in ); + } + std::vector< Node > args; + for( int i=0; i<(int)in[0].getNumChildren(); i++ ){ + args.push_back( in[0][i] ); + } + Node body = in[1]; + bool doRewrite = false; + while( body.getNumChildren()>=2 && body.getKind()==in.getKind() ){ + for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ + args.push_back( body[0][i] ); + } + body = body[1]; + doRewrite = true; + } + if( doRewrite ){ + std::vector< Node > children; + children.push_back( NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST,args) ); + children.push_back( body ); + if( in.getNumChildren()==3 ){ + children.push_back( in[2] ); + } + Node n = NodeManager::currentNM()->mkNode( in.getKind(), children ); + if( in!=n ){ + if( in.hasAttribute(NestedQuantAttribute()) ){ + setNestedQuantifiers( n, in.getAttribute(NestedQuantAttribute()) ); + } + Debug("quantifiers-pre-rewrite") << "*** pre-rewrite " << in << std::endl; + Debug("quantifiers-pre-rewrite") << " to " << std::endl; + Debug("quantifiers-pre-rewrite") << n << std::endl; + } + return RewriteResponse(REWRITE_DONE, n); + } + } + return RewriteResponse(REWRITE_DONE, in); +} + +RewriteResponse QuantifiersRewriter::postRewrite(TNode in) { + Debug("quantifiers-rewrite-debug") << "post-rewriting " << in << " " << in.hasAttribute(NestedQuantAttribute()) << std::endl; + if( in.getKind()==kind::EXISTS || in.getKind()==kind::FORALL ){ + //get the arguments + std::vector< Node > args; + for( int i=0; i<(int)in[0].getNumChildren(); i++ ){ + args.push_back( in[0][i] ); + } + //get the body + Node body = in[1]; + if( in.getKind()==EXISTS ){ + body = body.getKind()==NOT ? body[0] : body.notNode(); + } + //get the instantiation pattern list + Node ipl; + if( in.getNumChildren()==3 ){ + ipl = in[2]; + } + bool isNested = in.hasAttribute(NestedQuantAttribute()); + //compute miniscoping first + Node n = body;//computeNNF( body ); TODO: compute NNF here (current bad idea since arithmetic rewrites equalities) + if( body!=n ){ + Notice() << "NNF " << body << " -> " << n << std::endl; + } + n = computeMiniscoping( args, n, ipl, isNested ); + if( in.getKind()==kind::EXISTS ){ + n = n.getKind()==NOT ? n[0] : n.notNode(); + } + //compute other rewrite options for each produced quantifier + n = rewriteQuants( n, isNested, true ); + //print if changed + if( in!=n ){ + if( in.hasAttribute(NestedQuantAttribute()) ){ + setNestedQuantifiers( n, in.getAttribute(NestedQuantAttribute()) ); + } + Debug("quantifiers-rewrite") << "*** rewrite " << in << std::endl; + Debug("quantifiers-rewrite") << " to " << std::endl; + Debug("quantifiers-rewrite") << n << std::endl; + if( in.hasAttribute(InstConstantAttribute()) ){ + InstConstantAttribute ica; + n.setAttribute(ica,in.getAttribute(InstConstantAttribute()) ); + } + } + return RewriteResponse(REWRITE_DONE, n ); + } + return RewriteResponse(REWRITE_DONE, in); +} + +Node QuantifiersRewriter::computeNNF( Node body ){ + if( body.getKind()==NOT ){ + if( body[0].getKind()==NOT ){ + return computeNNF( body[0][0] ); + }else if( isLiteral( body[0] ) ){ + return body; + }else{ + std::vector< Node > children; + Kind k = body[0].getKind(); + if( body[0].getKind()==OR || body[0].getKind()==IMPLIES || body[0].getKind()==AND ){ + for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ + Node nn = body[0].getKind()==IMPLIES && i==0 ? body[0][i] : body[0][i].notNode(); + children.push_back( computeNNF( nn ) ); + } + k = body[0].getKind()==AND ? OR : AND; + }else if( body[0].getKind()==XOR || body[0].getKind()==IFF ){ + for( int i=0; i<2; i++ ){ + Node nn = i==0 ? body[0][i] : body[0][i].notNode(); + children.push_back( computeNNF( nn ) ); + } + }else if( body[0].getKind()==ITE ){ + for( int i=0; i<3; i++ ){ + Node nn = i==0 ? body[0][i] : body[0][i].notNode(); + children.push_back( computeNNF( nn ) ); + } + }else{ + Notice() << "Unhandled Quantifiers NNF: " << body << std::endl; + return body; + } + return NodeManager::currentNM()->mkNode( k, children ); + } + }else if( isLiteral( body ) ){ + return body; + }else{ + std::vector< Node > children; + bool childrenChanged = false; + for( int i=0; i<(int)body.getNumChildren(); i++ ){ + Node nc = computeNNF( body[i] ); + children.push_back( nc ); + childrenChanged = childrenChanged || nc!=body[i]; + } + if( childrenChanged ){ + return NodeManager::currentNM()->mkNode( body.getKind(), children ); + }else{ + return body; + } + } +} + +Node QuantifiersRewriter::computeVarElimination( Node body, std::vector< Node >& args, Node& ipl ){ + //Notice() << "Compute var elimination for " << f << std::endl; + std::map< Node, bool > litPhaseReq; + QuantifiersEngine::computePhaseReqs( body, false, litPhaseReq ); + std::vector< Node > vars; + std::vector< Node > subs; + for( std::map< Node, bool >::iterator it = litPhaseReq.begin(); it != litPhaseReq.end(); ++it ){ + //Notice() << " " << it->first << " -> " << ( it->second ? "true" : "false" ) << std::endl; + if( it->first.getKind()==EQUAL ){ + if( it->second ){ + for( int i=0; i<2; i++ ){ + int j = i==0 ? 1 : 0; + std::vector< Node >::iterator ita = std::find( args.begin(), args.end(), it->first[i] ); + if( ita!=args.end() ){ + std::vector< Node > temp; + temp.push_back( it->first[i] ); + if( !hasArg( temp, it->first[j] ) ){ + vars.push_back( it->first[i] ); + subs.push_back( it->first[j] ); + args.erase( ita ); + break; + } + } + } + if( !vars.empty() ){ + break; + } + } + } + } + if( !vars.empty() ){ + //Notice() << "VE " << vars.size() << "/" << n[0].getNumChildren() << std::endl; + //remake with eliminated nodes + body = body.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + body = Rewriter::rewrite( body ); + if( !ipl.isNull() ){ + ipl = ipl.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + } + } + return body; +} + +Node QuantifiersRewriter::computeClause( Node n ){ + Assert( isClause( n ) ); + if( isLiteral( n ) ){ + return n; + }else{ + NodeBuilder<> t(OR); + if( n.getKind()==NOT ){ + if( n[0].getKind()==NOT ){ + return computeClause( n[0][0] ); + }else{ + //De-Morgan's law + Assert( n[0].getKind()==AND ); + for( int i=0; i<(int)n[0].getNumChildren(); i++ ){ + Node nn = computeClause( n[0][i].notNode() ); + addNodeToOrBuilder( nn, t ); + } + } + }else{ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + Node nc = ( ( i==0 && n.getKind()==IMPLIES ) ? n[i].notNode() : n[i] ); + Node nn = computeClause( nc ); + addNodeToOrBuilder( nn, t ); + } + } + return t.constructNode(); + } +} + +Node QuantifiersRewriter::computeCNF( Node n, std::vector< Node >& args, NodeBuilder<>& defs, bool forcePred ){ + if( isLiteral( n ) ){ + return n; + }else if( !forcePred && isClause( n ) ){ + return computeClause( n ); + }else{ + Kind k = ( n.getKind()==IMPLIES ? OR : ( n.getKind()==XOR ? IFF : n.getKind() ) ); + NodeBuilder<> t(k); + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + Node nc = ( i==0 && ( n.getKind()==IMPLIES || n.getKind()==XOR ) ) ? n[i].notNode() : n[i]; + Node ncnf = computeCNF( nc, args, defs, k!=OR ); + if( k==OR ){ + addNodeToOrBuilder( ncnf, t ); + }else{ + t << ncnf; + } + } + if( !forcePred && k==OR ){ + return t.constructNode(); + }else{ + //compute the free variables + Node nt = t; + std::vector< Node > activeArgs; + computeArgs( args, activeArgs, nt ); + std::vector< TypeNode > argTypes; + for( int i=0; i<(int)activeArgs.size(); i++ ){ + argTypes.push_back( activeArgs[i].getType() ); + } + //create the predicate + Assert( argTypes.size()>0 ); + TypeNode typ = NodeManager::currentNM()->mkFunctionType( argTypes, NodeManager::currentNM()->booleanType() ); + std::stringstream ss; + ss << "cnf_" << n.getKind() << "_" << n.getId(); + Node op = NodeManager::currentNM()->mkVar( ss.str(), typ ); + std::vector< Node > predArgs; + predArgs.push_back( op ); + predArgs.insert( predArgs.end(), activeArgs.begin(), activeArgs.end() ); + Node pred = NodeManager::currentNM()->mkNode( APPLY_UF, predArgs ); + Debug("quantifiers-rewrite-cnf-debug") << "Made predicate " << pred << " for " << nt << std::endl; + //create the bound var list + Node bvl = NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, activeArgs ); + //now, look at the structure of nt + if( nt.getKind()==NOT ){ + //case for NOT + for( int i=0; i<2; i++ ){ + NodeBuilder<> tt(OR); + tt << ( i==0 ? nt[0].notNode() : nt[0] ); + tt << ( i==0 ? pred.notNode() : pred ); + defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); + } + }else if( nt.getKind()==OR ){ + //case for OR + for( int i=0; i<(int)nt.getNumChildren(); i++ ){ + NodeBuilder<> tt(OR); + tt << nt[i].notNode() << pred; + defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); + } + NodeBuilder<> tt(OR); + for( int i=0; i<(int)nt.getNumChildren(); i++ ){ + tt << nt[i]; + } + tt << pred.notNode(); + defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); + }else if( nt.getKind()==AND ){ + //case for AND + for( int i=0; i<(int)nt.getNumChildren(); i++ ){ + NodeBuilder<> tt(OR); + tt << nt[i] << pred.notNode(); + defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); + } + NodeBuilder<> tt(OR); + for( int i=0; i<(int)nt.getNumChildren(); i++ ){ + tt << nt[i].notNode(); + } + tt << pred; + defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); + }else if( nt.getKind()==IFF ){ + //case for IFF + for( int i=0; i<4; i++ ){ + NodeBuilder<> tt(OR); + tt << ( ( i==0 || i==3 ) ? nt[0].notNode() : nt[0] ); + tt << ( ( i==1 || i==3 ) ? nt[1].notNode() : nt[1] ); + tt << ( ( i==0 || i==1 ) ? pred.notNode() : pred ); + defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); + } + }else if( nt.getKind()==ITE ){ + //case for ITE + for( int j=1; j<=2; j++ ){ + for( int i=0; i<2; i++ ){ + NodeBuilder<> tt(OR); + tt << ( ( j==1 ) ? nt[0].notNode() : nt[0] ); + tt << ( ( i==1 ) ? nt[j].notNode() : nt[j] ); + tt << ( ( i==0 ) ? pred.notNode() : pred ); + defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); + } + } + for( int i=0; i<2; i++ ){ + NodeBuilder<> tt(OR); + tt << ( i==0 ? nt[1].notNode() : nt[1] ); + tt << ( i==0 ? nt[2].notNode() : nt[2] ); + tt << ( i==1 ? pred.notNode() : pred ); + defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); + } + }else{ + Notice() << "Unhandled Quantifiers CNF: " << nt << std::endl; + } + return pred; + } + } +} + +Node QuantifiersRewriter::computePrenex( Node body, std::vector< Node >& args, bool pol, bool polReq ){ + if( body.getKind()==FORALL ){ + if( pol==polReq ){ + std::vector< Node > terms; + std::vector< Node > subs; + if( polReq ){ + //for doing prenexing of same-signed quantifiers + //must rename each variable that already exists + for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ + //if( std::find( args.begin(), args.end(), body[0][i] )!=args.end() ){ + terms.push_back( body[0][i] ); + subs.push_back( NodeManager::currentNM()->mkVar( body[0][i].getType() ) ); + } + args.insert( args.end(), subs.begin(), subs.end() ); + }else{ + std::vector< TypeNode > argTypes; + for( int i=0; i<(int)args.size(); i++ ){ + argTypes.push_back( args[i].getType() ); + } + //for doing pre-skolemization of opposite-signed quantifiers + for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ + terms.push_back( body[0][i] ); + //make the new function symbol + TypeNode typ = NodeManager::currentNM()->mkFunctionType( argTypes, body[0][i].getType() ); + Node op = NodeManager::currentNM()->mkVar( typ ); + std::vector< Node > funcArgs; + funcArgs.push_back( op ); + funcArgs.insert( funcArgs.end(), args.begin(), args.end() ); + subs.push_back( NodeManager::currentNM()->mkNode( APPLY_UF, funcArgs ) ); + } + } + Node newBody = body[1]; + newBody = newBody.substitute( terms.begin(), terms.end(), subs.begin(), subs.end() ); + return newBody; + }else{ + return body; + } + }else if( body.getKind()==ITE || body.getKind()==XOR || body.getKind()==IFF ){ + return body; + }else{ + Assert( body.getKind()!=EXISTS ); + bool childrenChanged = false; + std::vector< Node > newChildren; + for( int i=0; i<(int)body.getNumChildren(); i++ ){ + bool newPol = ( body.getKind()==NOT || ( body.getKind()==IMPLIES && i==0 ) ) ? !pol : pol; + Node n = computePrenex( body[i], args, newPol, polReq ); + newChildren.push_back( n ); + if( n!=body[i] ){ + childrenChanged = true; + } + } + if( childrenChanged ){ + if( body.getKind()==NOT && newChildren[0].getKind()==NOT ){ + return newChildren[0][0]; + }else{ + return NodeManager::currentNM()->mkNode( body.getKind(), newChildren ); + } + }else{ + return body; + } + } +} + +//general method for computing various rewrites +Node QuantifiersRewriter::computeOperation( Node f, int computeOption ){ + std::vector< Node > args; + for( int i=0; i<(int)f[0].getNumChildren(); i++ ){ + args.push_back( f[0][i] ); + } + NodeBuilder<> defs(kind::AND); + Node n = f[1]; + Node ipl; + if( f.getNumChildren()==3 ){ + ipl = f[2]; + } + if( computeOption==COMPUTE_NNF ){ + n = computeNNF( n ); + }else if( computeOption==COMPUTE_PRENEX || computeOption==COMPUTE_PRE_SKOLEM ){ + n = computePrenex( n, args, true, computeOption==COMPUTE_PRENEX ); + }else if( computeOption==COMPUTE_VAR_ELIMINATION ){ + Node prev; + do{ + prev = n; + n = computeVarElimination( n, args, ipl ); + }while( prev!=n && !args.empty() ); + }else if( computeOption==COMPUTE_CNF ){ + //n = computeNNF( n ); + n = computeCNF( n, args, defs, false ); + ipl = Node::null(); + } + if( f[1]==n && args.size()==long(f[0].getNumChildren()) ){ + return f; + }else{ + if( args.empty() ){ + defs << n; + }else{ + std::vector< Node > children; + children.push_back( NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, args ) ); + children.push_back( n ); + if( !ipl.isNull() ){ + children.push_back( ipl ); + } + defs << NodeManager::currentNM()->mkNode(kind::FORALL, children ); + } + return defs.getNumChildren()==1 ? defs.getChild( 0 ) : defs.constructNode(); + } +} + +Node QuantifiersRewriter::mkForAll( std::vector< Node >& args, Node body, Node ipl ){ + std::vector< Node > activeArgs; + computeArgs( args, activeArgs, body ); + if( activeArgs.empty() ){ + return body; + }else{ + std::vector< Node > children; + children.push_back( NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, activeArgs ) ); + children.push_back( body ); + if( !ipl.isNull() ){ + children.push_back( ipl ); + } + return NodeManager::currentNM()->mkNode( kind::FORALL, children ); + } +} + +Node QuantifiersRewriter::computeMiniscoping( std::vector< Node >& args, Node body, Node ipl, bool isNested ){ + //Notice() << "rewrite quant " << body << std::endl; + if( body.getKind()==FORALL ){ + //combine arguments + std::vector< Node > newArgs; + for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ + newArgs.push_back( body[0][i] ); + } + newArgs.insert( newArgs.end(), args.begin(), args.end() ); + return mkForAll( newArgs, body[ 1 ], ipl ); + }else if( !isNested ){ + if( body.getKind()==NOT ){ + //push not downwards + if( body[0].getKind()==NOT ){ + return computeMiniscoping( args, body[0][0], ipl ); + }else if( body[0].getKind()==AND ){ + if( doMiniscopingNoFreeVar() ){ + NodeBuilder<> t(kind::OR); + for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ + t << ( body[0][i].getKind()==NOT ? body[0][i][0] : body[0][i].notNode() ); + } + return computeMiniscoping( args, t.constructNode(), ipl ); + } + }else if( body[0].getKind()==OR || body[0].getKind()==IMPLIES ){ + if( doMiniscopingAnd() ){ + NodeBuilder<> t(kind::AND); + for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ + Node trm = ( body[0].getKind()==IMPLIES && i==0 ) ? body[0][i] : ( body[0][i].getKind()==NOT ? body[0][i][0] : body[0][i].notNode() ); + t << computeMiniscoping( args, trm, ipl ); + } + return t.constructNode(); + } + } + }else if( body.getKind()==AND ){ + if( doMiniscopingAnd() ){ + //break apart + NodeBuilder<> t(kind::AND); + for( int i=0; i<(int)body.getNumChildren(); i++ ){ + t << computeMiniscoping( args, body[i], ipl ); + } + Node retVal = t; + return retVal; + } + }else if( body.getKind()==OR || body.getKind()==IMPLIES ){ + if( doMiniscopingNoFreeVar() ){ + Node newBody = body; + NodeBuilder<> body_split(kind::OR); + NodeBuilder<> tb(kind::OR); + for( int i=0; i<(int)body.getNumChildren(); i++ ){ + Node trm = ( body.getKind()==IMPLIES && i==0 ) ? ( body[i].getKind()==NOT ? body[i][0] : body[i].notNode() ) : body[i]; + if( hasArg( args, body[i] ) ){ + tb << trm; + }else{ + body_split << trm; + } + } + if( tb.getNumChildren()==0 ){ + return body_split; + }else if( body_split.getNumChildren()>0 ){ + newBody = tb.getNumChildren()==1 ? tb.getChild( 0 ) : tb; + body_split << mkForAll( args, newBody, ipl ); + return body_split.getNumChildren()==1 ? body_split.getChild( 0 ) : body_split; + } + } + } + } + return mkForAll( args, body, ipl ); +} + +Node QuantifiersRewriter::rewriteQuants( Node n, bool isNested, bool duringRewrite ){ + if( n.getKind()==FORALL ){ + return rewriteQuant( n, isNested, duringRewrite ); + }else if( isLiteral( n ) ){ + return n; + }else{ + NodeBuilder<> tt(n.getKind()); + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + tt << rewriteQuants( n[i], isNested, duringRewrite ); + } + return tt.constructNode(); + } +} + +Node QuantifiersRewriter::rewriteQuant( Node n, bool isNested, bool duringRewrite ){ + Node prev = n; + for( int op=0; op<COMPUTE_LAST; op++ ){ + if( doOperation( n, isNested, op, duringRewrite ) ){ + Node prev2 = n; + n = computeOperation( n, op ); + if( prev2!=n ){ + Debug("quantifiers-rewrite-op") << "Rewrite op " << op << ": rewrite " << prev2 << std::endl; + Debug("quantifiers-rewrite-op") << " to " << std::endl; + Debug("quantifiers-rewrite-op") << n << std::endl; + } + } + } + if( prev==n ){ + return n; + }else{ + //rewrite again until fix point is reached + return rewriteQuant( n, isNested, duringRewrite ); + } +} + +bool QuantifiersRewriter::doMiniscopingNoFreeVar(){ + return Options::current()->miniscopeQuantFreeVar; +} + +bool QuantifiersRewriter::doMiniscopingAnd(){ + if( Options::current()->miniscopeQuant ){ + return true; + }else{ + if( Options::current()->cbqi ){ + return true; + }else{ + return false; + } + } +} + +bool QuantifiersRewriter::doOperation( Node f, bool isNested, int computeOption, bool duringRewrite ){ + if( computeOption==COMPUTE_NNF ){ + return false;//TODO: compute NNF (current bad idea since arithmetic rewrites equalities) + }else if( computeOption==COMPUTE_PRE_SKOLEM ){ + return Options::current()->preSkolemQuant && !duringRewrite; + }else if( computeOption==COMPUTE_PRENEX ){ + return Options::current()->prenexQuant; + }else if( computeOption==COMPUTE_VAR_ELIMINATION ){ + return Options::current()->varElimQuant; + }else if( computeOption==COMPUTE_CNF ){ + return Options::current()->cnfQuant && !duringRewrite;// || Options::current()->finiteModelFind; + }else{ + return false; + } +} diff --git a/src/theory/quantifiers/quantifiers_rewriter.h b/src/theory/quantifiers/quantifiers_rewriter.h new file mode 100644 index 000000000..8c037d30b --- /dev/null +++ b/src/theory/quantifiers/quantifiers_rewriter.h @@ -0,0 +1,88 @@ +/********************* */ +/*! \file quantifiers_rewriter.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: mdeters + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Rewriter for the theory of inductive quantifiers + ** + ** Rewriter for the theory of inductive quantifiers. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__QUANTIFIERS__QUANTIFIERS_REWRITER_H +#define __CVC4__THEORY__QUANTIFIERS__QUANTIFIERS_REWRITER_H + +#include "theory/rewriter.h" +#include "theory/quantifiers_engine.h" + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +// attribute for "contains instantiation constants from" +struct NestedQuantAttributeId {}; +typedef expr::Attribute<NestedQuantAttributeId, Node> NestedQuantAttribute; + +class QuantifiersRewriter { +public: + static bool isClause( Node n ); + static bool isLiteral( Node n ); + static bool isCube( Node n ); +private: + static void addNodeToOrBuilder( Node n, NodeBuilder<>& t ); + static Node mkForAll( std::vector< Node >& args, Node body, Node ipl ); + static void computeArgs( std::vector< Node >& args, std::vector< Node >& activeArgs, Node n ); + static bool hasArg( std::vector< Node >& args, Node n ); + static void setNestedQuantifiers( Node n, Node q ); + static void computeArgs( std::map< Node, bool >& active, Node n ); + static Node computeClause( Node n ); +private: + static Node computeMiniscoping( std::vector< Node >& args, Node body, Node ipl, bool isNested = false ); + static Node computeNNF( Node body ); + static Node computeVarElimination( Node body, std::vector< Node >& args, Node& ipl ); + static Node computeCNF( Node body, std::vector< Node >& args, NodeBuilder<>& defs, bool forcePred ); + static Node computePrenex( Node body, std::vector< Node >& args, bool pol, bool polReq ); +private: + enum{ + COMPUTE_NNF = 0, + COMPUTE_PRE_SKOLEM, + COMPUTE_PRENEX, + COMPUTE_VAR_ELIMINATION, + //COMPUTE_FLATTEN_ARGS_UF, + COMPUTE_CNF, + COMPUTE_LAST + }; + static Node computeOperation( Node f, int computeOption ); +public: + static RewriteResponse preRewrite(TNode in); + static RewriteResponse postRewrite(TNode in); + static Node rewriteEquality(TNode equality) { + return postRewrite(equality).node; + } + static inline void init() {} + static inline void shutdown() {} +private: + /** options */ + static bool doMiniscopingNoFreeVar(); + static bool doMiniscopingAnd(); + static bool doOperation( Node f, bool isNested, int computeOption, bool duringRewrite = true ); +public: + static Node rewriteQuants( Node n, bool isNested = false, bool duringRewrite = true ); + static Node rewriteQuant( Node n, bool isNested = false, bool duringRewrite = true ); +};/* class QuantifiersRewriter */ + +}/* CVC4::theory::quantifiers namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__QUANTIFIERS__QUANTIFIERS_REWRITER_H */ + diff --git a/src/theory/quantifiers/theory_quantifiers.cpp b/src/theory/quantifiers/theory_quantifiers.cpp new file mode 100644 index 000000000..ead47e4b0 --- /dev/null +++ b/src/theory/quantifiers/theory_quantifiers.cpp @@ -0,0 +1,199 @@ +/********************* */ +/*! \file theory_quantifiers.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of the theory of quantifiers + ** + ** Implementation of the theory of quantifiers. + **/ + + +#include "theory/quantifiers/theory_quantifiers.h" +#include "theory/valuation.h" +#include "theory/quantifiers_engine.h" +#include "theory/quantifiers/instantiation_engine.h" +#include "theory/quantifiers/model_engine.h" +#include "expr/kind.h" +#include "util/Assert.h" +#include <map> +#include <time.h> +#include "theory/quantifiers/theory_quantifiers_instantiator.h" + +#define USE_FLIP_DECISION + +//static bool clockSet = false; +//double initClock; + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; + +TheoryQuantifiers::TheoryQuantifiers(Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) : + Theory(THEORY_QUANTIFIERS, c, u, out, valuation, logicInfo, qe), + d_numRestarts(0){ + d_numInstantiations = 0; + d_baseDecLevel = -1; + if( Options::current()->finiteModelFind ){ + qe->addModule( new ModelEngine( this ) ); + }else{ + qe->addModule( new InstantiationEngine( this ) ); + } +} + + +TheoryQuantifiers::~TheoryQuantifiers() { +} + +void TheoryQuantifiers::addSharedTerm(TNode t) { + Debug("quantifiers-other") << "TheoryQuantifiers::addSharedTerm(): " + << t << endl; +} + + +void TheoryQuantifiers::notifyEq(TNode lhs, TNode rhs) { + Debug("quantifiers-other") << "TheoryQuantifiers::notifyEq(): " + << lhs << " = " << rhs << endl; + +} + +void TheoryQuantifiers::preRegisterTerm(TNode n) { + Debug("quantifiers-prereg") << "TheoryQuantifiers::preRegisterTerm() " << n << endl; + if( n.getKind()==FORALL && !n.hasAttribute(InstConstantAttribute()) ){ + getQuantifiersEngine()->registerQuantifier( n ); + } +} + + +void TheoryQuantifiers::presolve() { + Debug("quantifiers-presolve") << "TheoryQuantifiers::presolve()" << endl; +} + +Node TheoryQuantifiers::getValue(TNode n) { + //NodeManager* nodeManager = NodeManager::currentNM(); + switch(n.getKind()) { + case FORALL: + case EXISTS: + bool value; + if( d_valuation.hasSatValue( n, value ) ){ + return NodeManager::currentNM()->mkConst(value); + }else{ + return NodeManager::currentNM()->mkConst(false); //FIX_THIS? + } + break; + default: + Unhandled(n.getKind()); + } +} + +void TheoryQuantifiers::check(Effort e) { + CodeTimer codeTimer(d_theoryTime); + + Debug("quantifiers-check") << "quantifiers::check(" << e << ")" << std::endl; + while(!done()) { + Node assertion = get(); + Debug("quantifiers-assert") << "quantifiers::assert(): " << assertion << std::endl; + switch(assertion.getKind()) { + case kind::FORALL: + assertUniversal( assertion ); + break; + case kind::NOT: + { + switch( assertion[0].getKind()) { + case kind::FORALL: + assertExistential( assertion ); + break; + default: + Unhandled(assertion[0].getKind()); + break; + } + } + break; + default: + Unhandled(assertion.getKind()); + break; + } + } + // call the quantifiers engine to check + getQuantifiersEngine()->check( e ); +} + +void TheoryQuantifiers::propagate(Effort level){ + CodeTimer codeTimer(d_theoryTime); + + getQuantifiersEngine()->propagate( level ); +} + +void TheoryQuantifiers::assertUniversal( Node n ){ + Assert( n.getKind()==FORALL ); + if( !n.hasAttribute(InstConstantAttribute()) ){ + getQuantifiersEngine()->registerQuantifier( n ); + getQuantifiersEngine()->assertNode( n ); + } +} + +void TheoryQuantifiers::assertExistential( Node n ){ + Assert( n.getKind()== NOT && n[0].getKind()==FORALL ); + if( !n[0].hasAttribute(InstConstantAttribute()) ){ + if( d_skolemized.find( n )==d_skolemized.end() ){ + Node body = getQuantifiersEngine()->getSkolemizedBody( n[0] ); + NodeBuilder<> nb(kind::OR); + nb << n[0] << body.notNode(); + Node lem = nb; + Debug("quantifiers-sk") << "Skolemize lemma : " << lem << std::endl; + d_out->lemma( lem ); + d_skolemized[n] = true; + } + } +} + +bool TheoryQuantifiers::flipDecision(){ +#ifndef USE_FLIP_DECISION + return false; +#else + //Debug("quantifiers-flip") << "No instantiation given, flip decision, level = " << d_valuation.getDecisionLevel() << std::endl; + //for( int i=1; i<=(int)d_valuation.getDecisionLevel(); i++ ){ + // Debug("quantifiers-flip") << " " << d_valuation.getDecision( i ) << std::endl; + //} + //if( d_valuation.getDecisionLevel()>0 ){ + // double r = double(rand())/double(RAND_MAX); + // unsigned decisionLevel = (unsigned)(r*d_valuation.getDecisionLevel()); + // d_out->flipDecision( decisionLevel ); + // return true; + //}else{ + // return false; + //} + + if( !d_out->flipDecision() ){ + return restart(); + } + return true; +#endif +} + +bool TheoryQuantifiers::restart(){ + static const int restartLimit = 0; + if( d_numRestarts==restartLimit ){ + Debug("quantifiers-flip") << "No more restarts." << std::endl; + return false; + }else{ + d_numRestarts++; + Debug("quantifiers-flip") << "Do restart." << std::endl; + return true; + } +} + +void TheoryQuantifiers::performCheck(Effort e){ + getQuantifiersEngine()->check( e ); +} diff --git a/src/theory/quantifiers/theory_quantifiers.h b/src/theory/quantifiers/theory_quantifiers.h new file mode 100644 index 000000000..05c3b9695 --- /dev/null +++ b/src/theory/quantifiers/theory_quantifiers.h @@ -0,0 +1,77 @@ +/********************* */ +/*! \file theory_quantifiers.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Theory of quantifiers. + ** + ** Theory of quantifiers. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__QUANTIFIERS__THEORY_QUANTIFIERS_H +#define __CVC4__THEORY__QUANTIFIERS__THEORY_QUANTIFIERS_H + +#include "theory/theory.h" +#include "util/hash.h" +#include "util/stats.h" + +#include <ext/hash_set> +#include <iostream> +#include <map> + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +class TheoryEngine; + +class TheoryQuantifiers : public Theory { +private: + typedef context::CDHashMap< Node, bool, NodeHashFunction > BoolMap; + /** quantifiers that have been skolemized */ + std::map< Node, bool > d_skolemized; + /** number of instantiations */ + int d_numInstantiations; + int d_baseDecLevel; + /** number of restarts */ + int d_numRestarts; + + KEEP_STATISTIC(TimerStat, d_theoryTime, "theory::quantifiers::theoryTime"); + +public: + TheoryQuantifiers(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe); + ~TheoryQuantifiers(); + + void addSharedTerm(TNode t); + void notifyEq(TNode lhs, TNode rhs); + void preRegisterTerm(TNode n); + void presolve(); + void check(Effort e); + void propagate(Effort level); + Node getValue(TNode n); + void shutdown() { } + std::string identify() const { return std::string("TheoryQuantifiers"); } + bool flipDecision(); +private: + void assertUniversal( Node n ); + void assertExistential( Node n ); + bool restart(); +public: + void performCheck(Effort e); +};/* class TheoryQuantifiers */ + +}/* CVC4::theory::quantifiers namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__QUANTIFIERS__THEORY_QUANTIFIERS_H */ diff --git a/src/theory/quantifiers/theory_quantifiers_instantiator.cpp b/src/theory/quantifiers/theory_quantifiers_instantiator.cpp new file mode 100644 index 000000000..a5b6cc3bc --- /dev/null +++ b/src/theory/quantifiers/theory_quantifiers_instantiator.cpp @@ -0,0 +1,76 @@ +/********************* */ +/*! \file theory_quantifiers_instantiator.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of theory_quantifiers_instantiator class + **/ + +#include "theory/quantifiers/theory_quantifiers_instantiator.h" +#include "theory/quantifiers/theory_quantifiers.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; + +InstantiatorTheoryQuantifiers::InstantiatorTheoryQuantifiers(context::Context* c, QuantifiersEngine* ie, Theory* th) : +Instantiator( c, ie, th ){ + +} + +void InstantiatorTheoryQuantifiers::assertNode( Node assertion ){ + Debug("quant-quant-assert") << "InstantiatorTheoryQuantifiers::check: " << assertion << std::endl; + if( Options::current()->cbqi ){ + if( assertion.hasAttribute(InstConstantAttribute()) ){ + Debug("quant-quant-assert") << " -> has constraints from " << assertion.getAttribute(InstConstantAttribute()) << std::endl; + setHasConstraintsFrom( assertion.getAttribute(InstConstantAttribute()) ); + }else if( assertion.getKind()==NOT && assertion[0].hasAttribute(InstConstantAttribute()) ){ + Debug("quant-quant-assert") << " -> has constraints from " << assertion[0].getAttribute(InstConstantAttribute()) << std::endl; + setHasConstraintsFrom( assertion[0].getAttribute(InstConstantAttribute()) ); + } + } +} + +void InstantiatorTheoryQuantifiers::processResetInstantiationRound( Theory::Effort effort ){ + +} + + +int InstantiatorTheoryQuantifiers::process( Node f, Theory::Effort effort, int e, int limitInst ){ + Debug("quant-quant") << "Quant: Try to solve (" << e << ") for " << f << "... " << std::endl; + if( e<5 ){ + return InstStrategy::STATUS_UNFINISHED; + }else if( e==5 ){ + //add random addition + if( isOwnerOf( f ) ){ + InstMatch m; + if( d_quantEngine->addInstantiation( f, m ) ){ + ++(d_statistics.d_instantiations); + } + } + } + return InstStrategy::STATUS_UNKNOWN; +} + +InstantiatorTheoryQuantifiers::Statistics::Statistics(): + d_instantiations("InstantiatorTheoryQuantifiers::Instantiations_Total", 0) +{ + StatisticsRegistry::registerStat(&d_instantiations); +} + +InstantiatorTheoryQuantifiers::Statistics::~Statistics(){ + StatisticsRegistry::unregisterStat(&d_instantiations); +} + diff --git a/src/theory/quantifiers/theory_quantifiers_instantiator.h b/src/theory/quantifiers/theory_quantifiers_instantiator.h new file mode 100644 index 000000000..dda3bfeaa --- /dev/null +++ b/src/theory/quantifiers/theory_quantifiers_instantiator.h @@ -0,0 +1,60 @@ +/********************* */ +/*! \file instantiator_quantifiers_instantiator.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief instantiator_quantifiers_instantiator + **/ + + +#include "cvc4_private.h" + +#ifndef __CVC4__INSTANTIATOR_QUANTIFIERS_H +#define __CVC4__INSTANTIATOR_QUANTIFIERS_H + +#include "theory/quantifiers_engine.h" + +#include "util/stats.h" + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +class InstantiatorTheoryQuantifiers : public Instantiator{ + friend class QuantifiersEngine; +public: + InstantiatorTheoryQuantifiers(context::Context* c, QuantifiersEngine* ie, Theory* th); + ~InstantiatorTheoryQuantifiers() {} + + /** check function, assertion is asserted to theory */ + void assertNode( Node assertion ); + /** identify */ + std::string identify() const { return std::string("InstantiatorTheoryQuantifiers"); } +private: + /** reset instantiation */ + void processResetInstantiationRound( Theory::Effort effort ); + /** process at effort */ + int process( Node f, Theory::Effort effort, int e, int limitInst ); + + class Statistics { + public: + IntStat d_instantiations; + Statistics(); + ~Statistics(); + }; + Statistics d_statistics; +};/* class InstantiatiorTheoryArith */ + +} +} +} + +#endif
\ No newline at end of file diff --git a/src/theory/quantifiers/theory_quantifiers_type_rules.h b/src/theory/quantifiers/theory_quantifiers_type_rules.h new file mode 100644 index 000000000..ceec36d7b --- /dev/null +++ b/src/theory/quantifiers/theory_quantifiers_type_rules.h @@ -0,0 +1,113 @@ +/********************* */ +/*! \file theory_quantifiers_type_rules.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Theory of quantifiers + ** + ** Theory of quantifiers. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__QUANTIFIERS__THEORY_QUANTIFIERS_TYPE_RULES_H +#define __CVC4__THEORY__QUANTIFIERS__THEORY_QUANTIFIERS_TYPE_RULES_H + +#include "util/matcher.h" + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +struct QuantifierForallTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw(TypeCheckingExceptionPrivate) { + Debug("typecheck-q") << "type check for fa " << n << std::endl; + Assert(n.getKind() == kind::FORALL && n.getNumChildren()>0 ); + if( check ){ + if( n[ 0 ].getType(check)!=nodeManager->boundVarListType() ){ + throw TypeCheckingExceptionPrivate(n, "first argument of universal quantifier is not bound var list"); + } + if( n[ 1 ].getType(check)!=nodeManager->booleanType() ){ + throw TypeCheckingExceptionPrivate(n, "body of universal quantifier is not boolean"); + } + if( n.getNumChildren()==3 && n[2].getType(check)!=nodeManager->instPatternListType() ){ + throw TypeCheckingExceptionPrivate(n, "third argument of universal quantifier is not instantiation pattern list"); + } + } + return nodeManager->booleanType(); + } +};/* struct QuantifierForallTypeRule */ + +struct QuantifierExistsTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw(TypeCheckingExceptionPrivate) { + Debug("typecheck-q") << "type check for ex " << n << std::endl; + Assert(n.getKind() == kind::EXISTS && n.getNumChildren()>0 ); + if( check ){ + if( n[ 0 ].getType(check)!=nodeManager->boundVarListType() ){ + throw TypeCheckingExceptionPrivate(n, "first argument of existential quantifier is not bound var list"); + } + if( n[ 1 ].getType(check)!=nodeManager->booleanType() ){ + throw TypeCheckingExceptionPrivate(n, "body of existential quantifier is not boolean"); + } + if( n.getNumChildren()==3 && n[2].getType(check)!=nodeManager->instPatternListType() ){ + throw TypeCheckingExceptionPrivate(n, "third argument of existential quantifier is not instantiation pattern list"); + } + } + return nodeManager->booleanType(); + } +};/* struct QuantifierExistsTypeRule */ + +struct QuantifierBoundVarListTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw(TypeCheckingExceptionPrivate) { + Assert(n.getKind() == kind::BOUND_VAR_LIST ); + if( check ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( n[i].getKind()!=kind::VARIABLE ){ + throw TypeCheckingExceptionPrivate(n, "argument of bound var list is not variable"); + } + } + } + return nodeManager->boundVarListType(); + } +};/* struct QuantifierBoundVarListTypeRule */ + +struct QuantifierInstPatternTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw(TypeCheckingExceptionPrivate) { + Assert(n.getKind() == kind::INST_PATTERN ); + return nodeManager->instPatternType(); + } +};/* struct QuantifierInstPatternTypeRule */ + + +struct QuantifierInstPatternListTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw(TypeCheckingExceptionPrivate) { + Assert(n.getKind() == kind::INST_PATTERN_LIST ); + if( check ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( n[i].getKind()!=kind::INST_PATTERN ){ + throw TypeCheckingExceptionPrivate(n, "argument of inst pattern list is not inst pattern"); + } + } + } + return nodeManager->instPatternListType(); + } +};/* struct QuantifierInstPatternListTypeRule */ + +}/* CVC4::theory::quantifiers namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__QUANTIFIERS__THEORY_QUANTIFIERS_TYPE_RULES_H */ diff --git a/src/theory/quantifiers_engine.cpp b/src/theory/quantifiers_engine.cpp new file mode 100644 index 000000000..ddb085632 --- /dev/null +++ b/src/theory/quantifiers_engine.cpp @@ -0,0 +1,788 @@ +/********************* */ +/*! \file quantifiers_engine.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of quantifiers engine class + **/ + +#include "theory/quantifiers_engine.h" +#include "theory/theory_engine.h" +#include "theory/uf/theory_uf_instantiator.h" +#include "theory/uf/theory_uf_strong_solver.h" +#include "theory/uf/equality_engine.h" +#include "theory/quantifiers/quantifiers_rewriter.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; + +//#define COMPUTE_RELEVANCE +//#define REWRITE_ASSERTED_QUANTIFIERS + + /** reset instantiation */ +void InstStrategy::resetInstantiationRound( Theory::Effort effort ){ + d_no_instantiate_temp.clear(); + d_no_instantiate_temp.insert( d_no_instantiate_temp.begin(), d_no_instantiate.begin(), d_no_instantiate.end() ); + processResetInstantiationRound( effort ); +} +/** do instantiation round method */ +int InstStrategy::doInstantiation( Node f, Theory::Effort effort, int e, int limitInst ){ + if( shouldInstantiate( f ) ){ + int origLemmas = d_quantEngine->getNumLemmasWaiting(); + int retVal = process( f, effort, e, limitInst ); + if( d_quantEngine->getNumLemmasWaiting()!=origLemmas ){ + for( int i=0; i<(int)d_priority_over.size(); i++ ){ + d_priority_over[i]->d_no_instantiate_temp.push_back( f ); + } + } + return retVal; + }else{ + return STATUS_UNKNOWN; + } +} + +bool TermArgTrie::addTerm2( QuantifiersEngine* qe, Node n, int argIndex ){ + if( argIndex<(int)n.getNumChildren() ){ + Node r = qe->getEqualityQuery()->getRepresentative( n[ argIndex ] ); + std::map< Node, TermArgTrie >::iterator it = d_data.find( r ); + if( it==d_data.end() ){ + d_data[r].addTerm2( qe, n, argIndex+1 ); + return true; + }else{ + return it->second.addTerm2( qe, n, argIndex+1 ); + } + }else{ + //store n in d_data (this should be interpretted as the "data" and not as a reference to a child) + d_data[n].d_data.clear(); + return false; + } +} + +void TermDb::addTerm( Node n, std::vector< Node >& added, bool withinQuant ){ + //don't add terms in quantifier bodies + if( !withinQuant || Options::current()->registerQuantBodyTerms ){ + if( d_processed.find( n )==d_processed.end() ){ + d_processed[n] = true; + //if this is an atomic trigger, consider adding it + if( Trigger::isAtomicTrigger( n ) ){ + if( !n.hasAttribute(InstConstantAttribute()) ){ + Debug("term-db") << "register trigger term " << n << std::endl; + //Notice() << "register trigger term " << n << std::endl; + Node op = n.getOperator(); + d_op_map[op].push_back( n ); + d_type_map[ n.getType() ].push_back( n ); + added.push_back( n ); + + uf::InstantiatorTheoryUf* d_ith = (uf::InstantiatorTheoryUf*)d_quantEngine->getInstantiator( THEORY_UF ); + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + addTerm( n[i], added, withinQuant ); + if( Options::current()->efficientEMatching ){ + if( d_parents[n[i]][op].empty() ){ + //must add parent to equivalence class info + Node nir = d_ith->getRepresentative( n[i] ); + uf::EqClassInfo* eci_nir = d_ith->getEquivalenceClassInfo( nir ); + if( eci_nir ){ + eci_nir->d_pfuns[ op ] = true; + } + } + //add to parent structure + if( std::find( d_parents[n[i]][op][i].begin(), d_parents[n[i]][op][i].end(), n )==d_parents[n[i]][op][i].end() ){ + d_parents[n[i]][op][i].push_back( n ); + } + } + } + if( Options::current()->efficientEMatching ){ + //new term, add n to candidate generators + for( int i=0; i<(int)d_ith->d_cand_gens[op].size(); i++ ){ + d_ith->d_cand_gens[op][i]->addCandidate( n ); + } + } + if( Options::current()->eagerInstQuant ){ + if( !n.hasAttribute(InstLevelAttribute()) && n.getAttribute(InstLevelAttribute())==0 ){ + int addedLemmas = 0; + for( int i=0; i<(int)d_ith->d_op_triggers[op].size(); i++ ){ + addedLemmas += d_ith->d_op_triggers[op][i]->addTerm( n ); + } + //std::cout << "Terms, added lemmas: " << addedLemmas << std::endl; + d_quantEngine->flushLemmas( &d_quantEngine->getTheoryEngine()->getTheory( THEORY_QUANTIFIERS )->getOutputChannel() ); + } + } + } + } + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + addTerm( n[i], added, withinQuant ); + } + } + } +} + +void TermDb::reset( Theory::Effort effort ){ + int nonCongruentCount = 0; + int congruentCount = 0; + int alreadyCongruentCount = 0; + //rebuild d_func/pred_map_trie for each operation, this will calculate all congruent terms + for( std::map< Node, std::vector< Node > >::iterator it = d_op_map.begin(); it != d_op_map.end(); ++it ){ + if( !it->second.empty() ){ + if( it->second[0].getType()==NodeManager::currentNM()->booleanType() ){ + d_pred_map_trie[ 0 ][ it->first ].d_data.clear(); + d_pred_map_trie[ 1 ][ it->first ].d_data.clear(); + }else{ + d_func_map_trie[ it->first ].d_data.clear(); + for( int i=0; i<(int)it->second.size(); i++ ){ + Node n = it->second[i]; + if( !n.getAttribute(NoMatchAttribute()) ){ + if( !d_func_map_trie[ it->first ].addTerm( d_quantEngine, n ) ){ + NoMatchAttribute nma; + n.setAttribute(nma,true); + congruentCount++; + }else{ + nonCongruentCount++; + } + }else{ + congruentCount++; + alreadyCongruentCount++; + } + } + } + } + } + for( int i=0; i<2; i++ ){ + Node n = NodeManager::currentNM()->mkConst( i==1 ); + eq::EqClassIterator eqc( d_quantEngine->getEqualityQuery()->getRepresentative( n ), + ((uf::TheoryUF*)d_quantEngine->getTheoryEngine()->getTheory( THEORY_UF ))->getEqualityEngine() ); + while( !eqc.isFinished() ){ + Node en = (*eqc); + if( en.getKind()==APPLY_UF && !en.hasAttribute(InstConstantAttribute()) ){ + if( !en.getAttribute(NoMatchAttribute()) ){ + Node op = en.getOperator(); + if( !d_pred_map_trie[i][op].addTerm( d_quantEngine, en ) ){ + NoMatchAttribute nma; + en.setAttribute(nma,true); + congruentCount++; + }else{ + nonCongruentCount++; + } + }else{ + alreadyCongruentCount++; + } + } + ++eqc; + } + } + Debug("term-db-cong") << "TermDb: Reset" << std::endl; + Debug("term-db-cong") << "Congruent/Non-Congruent = "; + Debug("term-db-cong") << congruentCount << "(" << alreadyCongruentCount << ") / " << nonCongruentCount << std::endl; +} + + + +QuantifiersEngine::QuantifiersEngine(context::Context* c, TheoryEngine* te): +d_te( te ), +d_forall_asserts( c ), +d_active( c ){ + d_eq_query = NULL; + d_term_db = new TermDb( this ); +} + +Instantiator* QuantifiersEngine::getInstantiator( int id ){ + return d_te->getTheory( id )->getInstantiator(); +} + +void QuantifiersEngine::check( Theory::Effort e ){ + CodeTimer codeTimer(d_time); + + if( e==Theory::EFFORT_LAST_CALL ){ + ++(d_statistics.d_instantiation_rounds_lc); + }else if( e==Theory::EFFORT_FULL ){ + ++(d_statistics.d_instantiation_rounds); + } + for( int i=0; i<(int)d_modules.size(); i++ ){ + d_modules[i]->check( e ); + } + //if( e==Theory::EFFORT_FULL ){ + // Notice() << "Done instantiation Round" << std::endl; + //} +} + +std::vector<Node> QuantifiersEngine::createInstVariable( std::vector<Node> & vars ){ + std::vector<Node> inst_constant; + inst_constant.reserve(vars.size()); + for( std::vector<Node>::const_iterator v = vars.begin(); + v != vars.end(); ++v ){ + //make instantiation constants + Node ic = NodeManager::currentNM()->mkInstConstant( (*v).getType() ); + inst_constant.push_back( ic ); + }; + return inst_constant; +} + +void QuantifiersEngine::makeInstantiationConstantsFor( Node f ){ + if( d_inst_constants.find( f )==d_inst_constants.end() ){ + Debug("quantifiers-engine") << "Instantiation constants for " << f << " : " << std::endl; + for( int i=0; i<(int)f[0].getNumChildren(); i++ ){ + d_vars[f].push_back( f[0][i] ); + //make instantiation constants + Node ic = NodeManager::currentNM()->mkInstConstant( f[0][i].getType() ); + d_inst_constants_map[ic] = f; + d_inst_constants[ f ].push_back( ic ); + Debug("quantifiers-engine") << " " << ic << std::endl; + //set the var number attribute + InstVarNumAttribute ivna; + ic.setAttribute(ivna,i); + } + } +} + +void QuantifiersEngine::registerQuantifier( Node f ){ + if( std::find( d_quants.begin(), d_quants.end(), f )==d_quants.end() ){ + std::vector< Node > quants; +#ifdef REWRITE_ASSERTED_QUANTIFIERS + //do assertion-time rewriting of quantifier + Node nf = quantifiers::QuantifiersRewriter::rewriteQuant( f, false, false ); + if( nf!=f ){ + Debug("quantifiers-rewrite") << "*** assert-rewrite " << f << std::endl; + Debug("quantifiers-rewrite") << " to " << std::endl; + Debug("quantifiers-rewrite") << nf << std::endl; + //we will instead register all the rewritten quantifiers + if( nf.getKind()==FORALL ){ + quants.push_back( nf ); + }else if( nf.getKind()==AND ){ + for( int i=0; i<(int)nf.getNumChildren(); i++ ){ + quants.push_back( nf[i] ); + } + }else{ + //unhandled: rewrite must go to a quantifier, or conjunction of quantifiers + Assert( false ); + } + }else{ + quants.push_back( f ); + } +#else + quants.push_back( f ); +#endif + for( int q=0; q<(int)quants.size(); q++ ){ + d_quant_rewritten[f].push_back( quants[q] ); + d_rewritten_quant[ quants[q] ] = f; + ++(d_statistics.d_num_quant); + Assert( quants[q].getKind()==FORALL ); + //register quantifier + d_quants.push_back( quants[q] ); + //make instantiation constants for quants[q] + makeInstantiationConstantsFor( quants[q] ); + //compute symbols in quants[q] + std::vector< Node > syms; + computeSymbols( quants[q][1], syms ); + d_syms[quants[q]].insert( d_syms[quants[q]].begin(), syms.begin(), syms.end() ); + //set initial relevance + int minRelevance = -1; + for( int i=0; i<(int)syms.size(); i++ ){ + d_syms_quants[ syms[i] ].push_back( quants[q] ); + int r = getRelevance( syms[i] ); + if( r!=-1 && ( minRelevance==-1 || r<minRelevance ) ){ + minRelevance = r; + } + } +#ifdef COMPUTE_RELEVANCE + if( minRelevance!=-1 ){ + setRelevance( quants[q], minRelevance+1 ); + } +#endif + //register with each module + for( int i=0; i<(int)d_modules.size(); i++ ){ + d_modules[i]->registerQuantifier( quants[q] ); + } + Node ceBody = getCounterexampleBody( quants[q] ); + generatePhaseReqs( quants[q], ceBody ); + //also register it with the strong solver + if( Options::current()->finiteModelFind ){ + ((uf::TheoryUF*)d_te->getTheory( THEORY_UF ))->getStrongSolver()->registerQuantifier( quants[q] ); + } + } + } +} + +void QuantifiersEngine::registerPattern( std::vector<Node> & pattern) { + for(std::vector<Node>::iterator p = pattern.begin(); p != pattern.end(); ++p){ + std::vector< Node > added; + d_term_db->addTerm(*p,added); + } +} + +void QuantifiersEngine::assertNode( Node f ){ + Assert( f.getKind()==FORALL ); + for( int j=0; j<(int)d_quant_rewritten[f].size(); j++ ){ + d_forall_asserts.push_back( d_quant_rewritten[f][j] ); + for( int i=0; i<(int)d_modules.size(); i++ ){ + d_modules[i]->assertNode( d_quant_rewritten[f][j] ); + } + } +} + +void QuantifiersEngine::propagate( Theory::Effort level ){ + CodeTimer codeTimer(d_time); + + for( int i=0; i<(int)d_modules.size(); i++ ){ + d_modules[i]->propagate( level ); + } +} + +void QuantifiersEngine::addTermToDatabase( Node n, bool withinQuant ){ + if( d_term_db ){ + std::vector< Node > added; + d_term_db->addTerm( n, added, withinQuant ); +#ifdef COMPUTE_RELEVANCE + for( int i=0; i<(int)added.size(); i++ ){ + if( !withinQuant ){ + setRelevance( added[i].getOperator(), 0 ); + } + } +#endif + }else{ + Notice() << "Warning: no term database for quantifier engine." << std::endl; + } +} + +bool QuantifiersEngine::addLemma( Node lem ){ + //AJR: the following check is necessary until FULL_CHECK is guarenteed after d_out->lemma(...) + Debug("inst-engine-debug") << "Adding lemma : " << lem << std::endl; + lem = Rewriter::rewrite(lem); + if( d_lemmas_produced.find( lem )==d_lemmas_produced.end() ){ + //d_curr_out->lemma( lem ); + d_lemmas_produced[ lem ] = true; + d_lemmas_waiting.push_back( lem ); + Debug("inst-engine-debug") << "Added lemma : " << lem << std::endl; + return true; + }else{ + Debug("inst-engine-debug") << "Duplicate." << std::endl; + return false; + } +} + +bool QuantifiersEngine::addInstantiation( Node f, std::vector< Node >& terms ) +{ + //Notice() << "***& Instantiate " << f << " with " << std::endl; + //for( int i=0; i<(int)terms.size(); i++ ){ + // Notice() << " " << terms[i] << std::endl; + //} + Assert( f.getKind()==FORALL ); + Assert( !f.hasAttribute(InstConstantAttribute()) ); + Assert( d_vars[f].size()==terms.size() && d_vars[f].size()==f[0].getNumChildren() ); + Node body = f[ 1 ].substitute( d_vars[f].begin(), d_vars[f].end(), + terms.begin(), terms.end() ); + NodeBuilder<> nb(kind::OR); + nb << d_rewritten_quant[f].notNode() << body; + Node lem = nb; + if( addLemma( lem ) ){ + //Notice() << " Added lemma : " << body << std::endl; + //Notice() << "***& Instantiate " << f << " with " << std::endl; + //for( int i=0; i<(int)terms.size(); i++ ){ + // Notice() << " " << terms[i] << std::endl; + //} + + //Notice() << "**INST" << std::endl; + Debug("inst") << "*** Instantiate " << f << " with " << std::endl; + //Notice() << "*** Instantiate " << f << " with " << std::endl; + uint64_t maxInstLevel = 0; + for( int i=0; i<(int)terms.size(); i++ ){ + if( terms[i].hasAttribute(InstConstantAttribute()) ){ + Debug("inst")<< "***& Bad Instantiate " << f << " with " << std::endl; + for( int i=0; i<(int)terms.size(); i++ ){ + Debug("inst") << " " << terms[i] << std::endl; + } + Unreachable("Bad instantiation"); + }else{ + Debug("inst") << " " << terms[i]; + //Notice() << " " << terms[i] << std::endl; + //Debug("inst-engine") << " " << terms[i].getAttribute(InstLevelAttribute()); + Debug("inst") << std::endl; + if( terms[i].hasAttribute(InstLevelAttribute()) ){ + if( terms[i].getAttribute(InstLevelAttribute())>maxInstLevel ){ + maxInstLevel = terms[i].getAttribute(InstLevelAttribute()); + } + }else{ + setInstantiationLevelAttr( terms[i], 0 ); + } + } + } + setInstantiationLevelAttr( body, maxInstLevel+1 ); + ++(d_statistics.d_instantiations); + d_statistics.d_total_inst_var += (int)terms.size(); + d_statistics.d_max_instantiation_level.maxAssign( maxInstLevel+1 ); + return true; + }else{ + ++(d_statistics.d_inst_duplicate); + return false; + } +} + +bool QuantifiersEngine::addInstantiation( Node f, InstMatch& m, bool addSplits ){ + m.makeComplete( f, this ); + m.makeRepresentative( this ); + Debug("quant-duplicate") << "After make rep: " << m << std::endl; + if( !d_inst_match_trie[f].addInstMatch( this, f, m, true ) ){ + Debug("quant-duplicate") << " -> Already exists." << std::endl; + ++(d_statistics.d_inst_duplicate); + return false; + } + Debug("quant-duplicate") << " -> Does not exist." << std::endl; + std::vector< Node > match; + m.computeTermVec( d_inst_constants[f], match ); + + //old.... + //m.makeRepresentative( d_eq_query ); + //std::vector< Node > match; + //m.computeTermVec( this, d_inst_constants[f], match ); + + //Notice() << "*** Instantiate " << m->getQuantifier() << " with " << std::endl; + //for( int i=0; i<(int)m->d_match.size(); i++ ){ + // Notice() << " " << m->d_match[i] << std::endl; + //} + + if( addInstantiation( f, match ) ){ + //d_statistics.d_total_inst_var_unspec.setData( d_statistics.d_total_inst_var_unspec.getData() + (int)d_inst_constants[f].size() - m.d_map.size()/2 ); + //if( d_inst_constants[f].size()!=m.d_map.size() ){ + // //Notice() << "Unspec. " << std::endl; + // //Notice() << "*** Instantiate " << m->getQuantifier() << " with " << std::endl; + // //for( int i=0; i<(int)m->d_match.size(); i++ ){ + // // Notice() << " " << m->d_match[i] << std::endl; + // //} + // ++(d_statistics.d_inst_unspec); + //} + //if( addSplits ){ + // for( std::map< Node, Node >::iterator it = m->d_splits.begin(); it != m->d_splits.end(); ++it ){ + // addSplitEquality( it->first, it->second, true, true ); + // } + //} + return true; + } + return false; +} + +bool QuantifiersEngine::addSplit( Node n, bool reqPhase, bool reqPhasePol ){ + n = Rewriter::rewrite( n ); + Node lem = NodeManager::currentNM()->mkNode( OR, n, n.notNode() ); + if( addLemma( lem ) ){ + ++(d_statistics.d_splits); + Debug("inst") << "*** Add split " << n<< std::endl; + //if( reqPhase ){ + // d_curr_out->requirePhase( n, reqPhasePol ); + //} + return true; + } + return false; +} + +bool QuantifiersEngine::addSplitEquality( Node n1, Node n2, bool reqPhase, bool reqPhasePol ){ + //Assert( !n1.hasAttribute(InstConstantAttribute()) ); + //Assert( !n2.hasAttribute(InstConstantAttribute()) ); + //Assert( !areEqual( n1, n2 ) ); + //Assert( !areDisequal( n1, n2 ) ); + Kind knd = n1.getType()==NodeManager::currentNM()->booleanType() ? IFF : EQUAL; + Node fm = NodeManager::currentNM()->mkNode( knd, n1, n2 ); + return addSplit( fm ); +} + +void QuantifiersEngine::flushLemmas( OutputChannel* out ){ + for( int i=0; i<(int)d_lemmas_waiting.size(); i++ ){ + out->lemma( d_lemmas_waiting[i] ); + } + d_lemmas_waiting.clear(); +} + +Node QuantifiersEngine::getCounterexampleBody( Node f ){ + std::map< Node, Node >::iterator it = d_counterexample_body.find( f ); + if( it==d_counterexample_body.end() ){ + makeInstantiationConstantsFor( f ); + Node n = getSubstitutedNode( f[1], f ); + d_counterexample_body[ f ] = n; + return n; + }else{ + return it->second; + } +} + +Node QuantifiersEngine::getSkolemizedBody( Node f ){ + Assert( f.getKind()==FORALL ); + if( d_skolem_body.find( f )==d_skolem_body.end() ){ + std::vector< Node > vars; + for( int i=0; i<(int)f[0].getNumChildren(); i++ ){ + Node skv = NodeManager::currentNM()->mkSkolem( f[0][i].getType() ); + d_skolem_constants[ f ].push_back( skv ); + vars.push_back( f[0][i] ); + } + d_skolem_body[ f ] = f[ 1 ].substitute( vars.begin(), vars.end(), + d_skolem_constants[ f ].begin(), d_skolem_constants[ f ].end() ); + if( f.hasAttribute(InstLevelAttribute()) ){ + setInstantiationLevelAttr( d_skolem_body[ f ], f.getAttribute(InstLevelAttribute()) ); + } + } + return d_skolem_body[ f ]; +} + +void QuantifiersEngine::getPhaseReqTerms( Node f, std::vector< Node >& nodes ){ + if( Options::current()->literalMatchMode!=Options::LITERAL_MATCH_NONE ){ + bool printed = false; + // doing literal-based matching (consider polarity of literals) + for( int i=0; i<(int)nodes.size(); i++ ){ + Node prev = nodes[i]; + bool nodeChanged = false; + if( isPhaseReq( f, nodes[i] ) ){ + bool preq = getPhaseReq( f, nodes[i] ); + nodes[i] = NodeManager::currentNM()->mkNode( IFF, nodes[i], NodeManager::currentNM()->mkConst<bool>(preq) ); + nodeChanged = true; + } + //else if( qe->isPhaseReqEquality( f, trNodes[i] ) ){ + // Node req = qe->getPhaseReqEquality( f, trNodes[i] ); + // trNodes[i] = NodeManager::currentNM()->mkNode( EQUAL, trNodes[i], req ); + //} + if( nodeChanged ){ + if( !printed ){ + Debug("literal-matching") << "LitMatch for " << f << ":" << std::endl; + printed = true; + } + Debug("literal-matching") << " Make " << prev << " -> " << nodes[i] << std::endl; + Assert( prev.hasAttribute(InstConstantAttribute()) ); + setInstantiationConstantAttr( nodes[i], prev.getAttribute(InstConstantAttribute()) ); + ++(d_statistics.d_lit_phase_req); + }else{ + ++(d_statistics.d_lit_phase_nreq); + } + } + }else{ + d_statistics.d_lit_phase_nreq += (int)nodes.size(); + } +} + +void QuantifiersEngine::computePhaseReqs2( Node n, bool polarity, std::map< Node, int >& phaseReqs ){ + bool newReqPol = false; + bool newPolarity; + if( n.getKind()==NOT ){ + newReqPol = true; + newPolarity = !polarity; + }else if( n.getKind()==OR || n.getKind()==IMPLIES ){ + if( !polarity ){ + newReqPol = true; + newPolarity = false; + } + }else if( n.getKind()==AND ){ + if( polarity ){ + newReqPol = true; + newPolarity = true; + } + }else{ + int val = polarity ? 1 : -1; + if( phaseReqs.find( n )==phaseReqs.end() ){ + phaseReqs[n] = val; + }else if( val!=phaseReqs[n] ){ + phaseReqs[n] = 0; + } + } + if( newReqPol ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( n.getKind()==IMPLIES && i==0 ){ + computePhaseReqs2( n[i], !newPolarity, phaseReqs ); + }else{ + computePhaseReqs2( n[i], newPolarity, phaseReqs ); + } + } + } +} + +void QuantifiersEngine::computePhaseReqs( Node n, bool polarity, std::map< Node, bool >& phaseReqs ){ + std::map< Node, int > phaseReqs2; + computePhaseReqs2( n, polarity, phaseReqs2 ); + for( std::map< Node, int >::iterator it = phaseReqs2.begin(); it != phaseReqs2.end(); ++it ){ + if( it->second==1 ){ + phaseReqs[ it->first ] = true; + }else if( it->second==-1 ){ + phaseReqs[ it->first ] = false; + } + } +} + +void QuantifiersEngine::generatePhaseReqs( Node f, Node n ){ + computePhaseReqs( n, false, d_phase_reqs[f] ); + Debug("inst-engine-phase-req") << "Phase requirements for " << f << ":" << std::endl; + //now, compute if any patterns are equality required + for( std::map< Node, bool >::iterator it = d_phase_reqs[f].begin(); it != d_phase_reqs[f].end(); ++it ){ + Debug("inst-engine-phase-req") << " " << it->first << " -> " << it->second << std::endl; + if( it->first.getKind()==EQUAL ){ + if( it->first[0].hasAttribute(InstConstantAttribute()) ){ + if( !it->first[1].hasAttribute(InstConstantAttribute()) ){ + d_phase_reqs_equality_term[f][ it->first[0] ] = it->first[1]; + d_phase_reqs_equality[f][ it->first[0] ] = it->second; + Debug("inst-engine-phase-req") << " " << it->first[0] << ( it->second ? " == " : " != " ) << it->first[1] << std::endl; + } + }else if( it->first[1].hasAttribute(InstConstantAttribute()) ){ + d_phase_reqs_equality_term[f][ it->first[1] ] = it->first[0]; + d_phase_reqs_equality[f][ it->first[1] ] = it->second; + Debug("inst-engine-phase-req") << " " << it->first[1] << ( it->second ? " == " : " != " ) << it->first[0] << std::endl; + } + } + } + +} + +Node QuantifiersEngine::getSubstitutedNode( Node n, Node f ){ + return convertNodeToPattern(n,f,d_vars[f],d_inst_constants[ f ]); +} + +Node QuantifiersEngine::convertNodeToPattern( Node n, Node f, const std::vector<Node> & vars, + const std::vector<Node> & inst_constants){ + Node n2 = n.substitute( vars.begin(), vars.end(), + inst_constants.begin(), + inst_constants.end() ); + setInstantiationConstantAttr( n2, f ); + return n2; +} + + +void QuantifiersEngine::setInstantiationLevelAttr( Node n, uint64_t level ){ + if( !n.hasAttribute(InstLevelAttribute()) ){ + InstLevelAttribute ila; + n.setAttribute(ila,level); + } + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + setInstantiationLevelAttr( n[i], level ); + } +} + + +void QuantifiersEngine::setInstantiationConstantAttr( Node n, Node f ){ + if( !n.hasAttribute(InstConstantAttribute()) ){ + bool setAttr = false; + if( n.getKind()==INST_CONSTANT ){ + setAttr = true; + }else{ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + setInstantiationConstantAttr( n[i], f ); + if( n[i].hasAttribute(InstConstantAttribute()) ){ + setAttr = true; + } + } + } + if( setAttr ){ + InstConstantAttribute ica; + n.setAttribute(ica,f); + //also set the no-match attribute + NoMatchAttribute nma; + n.setAttribute(nma,true); + } + } +} + +QuantifiersEngine::Statistics::Statistics(): + d_num_quant("QuantifiersEngine::Num_Quantifiers", 0), + d_instantiation_rounds("QuantifiersEngine::Rounds_Instantiation_Full", 0), + d_instantiation_rounds_lc("QuantifiersEngine::Rounds_Instantiation_Last_Call", 0), + d_instantiations("QuantifiersEngine::Instantiations_Total", 0), + d_max_instantiation_level("QuantifiersEngine::Max_Instantiation_Level", 0), + d_splits("QuantifiersEngine::Total_Splits", 0), + d_total_inst_var("QuantifiersEngine::Vars_Inst", 0), + d_total_inst_var_unspec("QuantifiersEngine::Vars_Inst_Unspecified", 0), + d_inst_unspec("QuantifiersEngine::Unspecified_Inst", 0), + d_inst_duplicate("QuantifiersEngine::Duplicate_Inst", 0), + d_lit_phase_req("QuantifiersEngine::lit_phase_req", 0), + d_lit_phase_nreq("QuantifiersEngine::lit_phase_nreq", 0), + d_triggers("QuantifiersEngine::Triggers", 0), + d_simple_triggers("QuantifiersEngine::Triggers_Simple", 0), + d_multi_triggers("QuantifiersEngine::Triggers_Multi", 0), + d_multi_trigger_instantiations("QuantifiersEngine::Multi_Trigger_Instantiations", 0) +{ + StatisticsRegistry::registerStat(&d_num_quant); + StatisticsRegistry::registerStat(&d_instantiation_rounds); + StatisticsRegistry::registerStat(&d_instantiation_rounds_lc); + StatisticsRegistry::registerStat(&d_instantiations); + StatisticsRegistry::registerStat(&d_max_instantiation_level); + StatisticsRegistry::registerStat(&d_splits); + StatisticsRegistry::registerStat(&d_total_inst_var); + StatisticsRegistry::registerStat(&d_total_inst_var_unspec); + StatisticsRegistry::registerStat(&d_inst_unspec); + StatisticsRegistry::registerStat(&d_inst_duplicate); + StatisticsRegistry::registerStat(&d_lit_phase_req); + StatisticsRegistry::registerStat(&d_lit_phase_nreq); + StatisticsRegistry::registerStat(&d_triggers); + StatisticsRegistry::registerStat(&d_simple_triggers); + StatisticsRegistry::registerStat(&d_multi_triggers); + StatisticsRegistry::registerStat(&d_multi_trigger_instantiations); +} + +QuantifiersEngine::Statistics::~Statistics(){ + StatisticsRegistry::unregisterStat(&d_num_quant); + StatisticsRegistry::unregisterStat(&d_instantiation_rounds); + StatisticsRegistry::unregisterStat(&d_instantiation_rounds_lc); + StatisticsRegistry::unregisterStat(&d_instantiations); + StatisticsRegistry::unregisterStat(&d_max_instantiation_level); + StatisticsRegistry::unregisterStat(&d_splits); + StatisticsRegistry::unregisterStat(&d_total_inst_var); + StatisticsRegistry::unregisterStat(&d_total_inst_var_unspec); + StatisticsRegistry::unregisterStat(&d_inst_unspec); + StatisticsRegistry::unregisterStat(&d_inst_duplicate); + StatisticsRegistry::unregisterStat(&d_lit_phase_req); + StatisticsRegistry::unregisterStat(&d_lit_phase_nreq); + StatisticsRegistry::unregisterStat(&d_triggers); + StatisticsRegistry::unregisterStat(&d_simple_triggers); + StatisticsRegistry::unregisterStat(&d_multi_triggers); + StatisticsRegistry::unregisterStat(&d_multi_trigger_instantiations); +} + +Node QuantifiersEngine::getFreeVariableForInstConstant( Node n ){ + TypeNode tn = n.getType(); + if( d_free_vars.find( tn )==d_free_vars.end() ){ + //if integer or real, make zero + if( tn==NodeManager::currentNM()->integerType() || tn==NodeManager::currentNM()->realType() ){ + Rational z(0); + d_free_vars[tn] = NodeManager::currentNM()->mkConst( z ); + }else{ + if( d_term_db->d_type_map[ tn ].empty() ){ + d_free_vars[tn] = NodeManager::currentNM()->mkVar( tn ); + }else{ + d_free_vars[tn] =d_term_db->d_type_map[ tn ][ 0 ]; + } + } + } + return d_free_vars[tn]; +} + +/** compute symbols */ +void QuantifiersEngine::computeSymbols( Node n, std::vector< Node >& syms ){ + if( n.getKind()==APPLY_UF ){ + Node op = n.getOperator(); + if( std::find( syms.begin(), syms.end(), op )==syms.end() ){ + syms.push_back( op ); + } + } + if( n.getKind()!=FORALL ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + computeSymbols( n[i], syms ); + } + } +} + +/** set relevance */ +void QuantifiersEngine::setRelevance( Node s, int r ){ + int rOld = getRelevance( s ); + if( rOld==-1 || r<rOld ){ + d_relevance[s] = r; + if( s.getKind()==FORALL ){ + for( int i=0; i<(int)d_syms[s].size(); i++ ){ + setRelevance( d_syms[s][i], r ); + } + }else{ + for( int i=0; i<(int)d_syms_quants[s].size(); i++ ){ + setRelevance( d_syms_quants[s][i], r+1 ); + } + } + } +} diff --git a/src/theory/quantifiers_engine.h b/src/theory/quantifiers_engine.h new file mode 100644 index 000000000..d0c5fb00b --- /dev/null +++ b/src/theory/quantifiers_engine.h @@ -0,0 +1,388 @@ +/********************* */ +/*! \file quantifiers_engine.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Theory instantiator, Instantiation Engine classes + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__QUANTIFIERS_ENGINE_H +#define __CVC4__THEORY__QUANTIFIERS_ENGINE_H + +#include "theory/theory.h" +#include "util/hash.h" +#include "theory/trigger.h" + +#include "util/stats.h" + +#include <ext/hash_set> +#include <iostream> +#include <map> + +namespace CVC4 { + +class TheoryEngine; + +// attribute for "contains instantiation constants from" +struct InstConstantAttributeId {}; +typedef expr::Attribute<InstConstantAttributeId, Node> InstConstantAttribute; + +struct InstLevelAttributeId {}; +typedef expr::Attribute<InstLevelAttributeId, uint64_t> InstLevelAttribute; + +struct InstVarNumAttributeId {}; +typedef expr::Attribute<InstVarNumAttributeId, uint64_t> InstVarNumAttribute; + +namespace theory { + +class QuantifiersEngine; + +class InstStrategy { +public: + enum Status { + STATUS_UNFINISHED, + STATUS_UNKNOWN, + STATUS_SAT, + };/* enum Status */ +protected: + /** reference to the instantiation engine */ + QuantifiersEngine* d_quantEngine; +protected: + /** giving priorities */ + std::vector< InstStrategy* > d_priority_over; + /** do not instantiate list */ + std::vector< Node > d_no_instantiate; + std::vector< Node > d_no_instantiate_temp; + /** reset instantiation */ + virtual void processResetInstantiationRound( Theory::Effort effort ) = 0; + /** process method */ + virtual int process( Node f, Theory::Effort effort, int e, int limitInst = 0 ) = 0; +public: + InstStrategy( QuantifiersEngine* ie ) : d_quantEngine( ie ){} + virtual ~InstStrategy(){} + + /** reset instantiation */ + void resetInstantiationRound( Theory::Effort effort ); + /** do instantiation round method */ + int doInstantiation( Node f, Theory::Effort effort, int e, int limitInst = 0 ); + /** update status */ + static void updateStatus( int& currStatus, int addStatus ){ + if( addStatus==STATUS_UNFINISHED ){ + currStatus = STATUS_UNFINISHED; + }else if( addStatus==STATUS_UNKNOWN ){ + if( currStatus==STATUS_SAT ){ + currStatus = STATUS_UNKNOWN; + } + } + } + /** identify */ + virtual std::string identify() const { return std::string("Unknown"); } +public: + /** set priority */ + void setPriorityOver( InstStrategy* is ) { d_priority_over.push_back( is ); } + /** set no instantiate */ + void setNoInstantiate( Node n ) { d_no_instantiate.push_back( n ); } + /** should instantiate */ + bool shouldInstantiate( Node n ) { + return std::find( d_no_instantiate_temp.begin(), d_no_instantiate_temp.end(), n )==d_no_instantiate_temp.end(); + } +};/* class InstStrategy */ + +class QuantifiersModule { +public: + QuantifiersModule(){} + ~QuantifiersModule(){} + /* Call during check registerQuantifier has already been called */ + virtual void check( Theory::Effort e ) = 0; + /* Called for new quantifiers */ + virtual void registerQuantifier( Node n ) = 0; + virtual void assertNode( Node n ) = 0; + virtual void propagate( Theory::Effort level ) = 0; + virtual Node explain(TNode n) = 0; +};/* class QuantifiersModule */ + +class TermArgTrie { +private: + bool addTerm2( QuantifiersEngine* qe, Node n, int argIndex ); +public: + /** the data */ + std::map< Node, TermArgTrie > d_data; +public: + bool addTerm( QuantifiersEngine* qe, Node n ) { return addTerm2( qe, n, 0 ); } +};/* class TermArgTrie */ + +class TermDb { +private: + /** reference to the quantifiers engine */ + QuantifiersEngine* d_quantEngine; + /** calculated no match terms */ + bool d_matching_active; + /** terms processed */ + std::map< Node, bool > d_processed; +public: + TermDb( QuantifiersEngine* qe ) : d_quantEngine( qe ), d_matching_active( true ){} + ~TermDb(){} + /** map from APPLY_UF operators to ground terms for that operator */ + std::map< Node, std::vector< Node > > d_op_map; + /** map from APPLY_UF functions to trie */ + std::map< Node, TermArgTrie > d_func_map_trie; + /** map from APPLY_UF predicates to trie */ + std::map< Node, TermArgTrie > d_pred_map_trie[2]; + /** map from type nodes to terms of that type */ + std::map< TypeNode, std::vector< Node > > d_type_map; + /** add a term to the database */ + void addTerm( Node n, std::vector< Node >& added, bool withinQuant = false ); + /** reset (calculate which terms are active) */ + void reset( Theory::Effort effort ); + /** set active */ + void setMatchingActive( bool a ) { d_matching_active = a; } + /** get active */ + bool getMatchingActive() { return d_matching_active; } +public: + /** parent structure (for efficient E-matching): + n -> op -> index -> L + map from node "n" to a list of nodes "L", where each node n' in L + has operator "op", and n'["index"] = n. + for example, d_parents[n][f][1] = { f( t1, n ), f( t2, n ), ... } + */ + std::map< Node, std::map< Node, std::map< int, std::vector< Node > > > > d_parents; +};/* class TermDb */ + +namespace quantifiers { + class InstantiationEngine; +}/* CVC4::theory::quantifiers */ + +class QuantifiersEngine { + friend class quantifiers::InstantiationEngine; + friend class InstMatch; +private: + typedef context::CDHashMap< Node, bool, NodeHashFunction > BoolMap; + /** reference to theory engine object */ + TheoryEngine* d_te; + /** vector of modules for quantifiers */ + std::vector< QuantifiersModule* > d_modules; + /** equality query class */ + EqualityQuery* d_eq_query; + + /** list of all quantifiers */ + std::vector< Node > d_quants; + /** list of quantifiers asserted in the current context */ + context::CDList<Node> d_forall_asserts; + /** map from universal quantifiers to the list of variables */ + std::map< Node, std::vector< Node > > d_vars; + /** map from universal quantifiers to the list of skolem constants */ + std::map< Node, std::vector< Node > > d_skolem_constants; + /** map from universal quantifiers to their skolemized body */ + std::map< Node, Node > d_skolem_body; + /** map from universal quantifiers to their bound body */ + std::map< Node, Node > d_bound_body; + /** instantiation constants to universal quantifiers */ + std::map< Node, Node > d_inst_constants_map; + /** map from universal quantifiers to their counterexample body */ + std::map< Node, Node > d_counterexample_body; + /** map from universal quantifiers to the list of instantiation constants */ + std::map< Node, std::vector< Node > > d_inst_constants; + /** map from quantifiers to whether they are active */ + BoolMap d_active; + /** lemmas produced */ + std::map< Node, bool > d_lemmas_produced; + /** lemmas waiting */ + std::vector< Node > d_lemmas_waiting; + /** inst matches produced for each quantifier */ + std::map< Node, InstMatchTrie > d_inst_match_trie; + /** free variable for instantiation constant type */ + std::map< TypeNode, Node > d_free_vars; + /** owner of quantifiers */ + std::map< Node, Theory* > d_owner; + /** term database */ + TermDb* d_term_db; + /** universal quantifiers that have been rewritten */ + std::map< Node, std::vector< Node > > d_quant_rewritten; + /** map from rewritten universal quantifiers to the quantifier they are the consequence of */ + std::map< Node, Node > d_rewritten_quant; +private: + /** for computing relavance */ + /** map from quantifiers to symbols they contain */ + std::map< Node, std::vector< Node > > d_syms; + /** map from symbols to quantifiers */ + std::map< Node, std::vector< Node > > d_syms_quants; + /** relevance for quantifiers and symbols */ + std::map< Node, int > d_relevance; + /** compute symbols */ + void computeSymbols( Node n, std::vector< Node >& syms ); +private: + /** helper functions compute phase requirements */ + static void computePhaseReqs2( Node n, bool polarity, std::map< Node, int >& phaseReqs ); + /** set instantiation level attr */ + void setInstantiationLevelAttr( Node n, uint64_t level ); + /** set instantiation constant attr */ + void setInstantiationConstantAttr( Node n, Node f ); + /** make instantiation constants for */ + void makeInstantiationConstantsFor( Node f ); + + KEEP_STATISTIC(TimerStat, d_time, "theory::QuantifiersEngine::time"); + +public: + QuantifiersEngine(context::Context* c, TheoryEngine* te); + ~QuantifiersEngine(){} + /** get instantiator for id */ + Instantiator* getInstantiator( int id ); + /** get theory engine */ + TheoryEngine* getTheoryEngine() { return d_te; } + /** get equality query object */ + EqualityQuery* getEqualityQuery() { return d_eq_query; } + /** set equality query object */ + void setEqualityQuery( EqualityQuery* eq ) { d_eq_query = eq; } +public: + /** add module */ + void addModule( QuantifiersModule* qm ) { d_modules.push_back( qm ); } + /** check at level */ + void check( Theory::Effort e ); + /** register (non-rewritten) quantifier */ + void registerQuantifier( Node f ); + /** register (non-rewritten) quantifier */ + void registerPattern( std::vector<Node> & pattern); + /** assert (universal) quantifier */ + void assertNode( Node f ); + /** propagate */ + void propagate( Theory::Effort level ); +public: + /** add lemma lem */ + bool addLemma( Node lem ); + /** instantiate f with arguments terms */ + bool addInstantiation( Node f, std::vector< Node >& terms ); + /** do instantiation specified by m */ + bool addInstantiation( Node f, InstMatch& m, bool addSplits = false ); + /** split on node n */ + bool addSplit( Node n, bool reqPhase = false, bool reqPhasePol = true ); + /** add split equality */ + bool addSplitEquality( Node n1, Node n2, bool reqPhase = false, bool reqPhasePol = true ); + /** has added lemma */ + bool hasAddedLemma() { return !d_lemmas_waiting.empty(); } + /** flush lemmas */ + void flushLemmas( OutputChannel* out ); + /** get number of waiting lemmas */ + int getNumLemmasWaiting() { return (int)d_lemmas_waiting.size(); } +public: + /** get number of quantifiers */ + int getNumQuantifiers() { return (int)d_quants.size(); } + /** get quantifier */ + Node getQuantifier( int i ) { return d_quants[i]; } + /** get number of asserted quantifiers */ + int getNumAssertedQuantifiers() { return (int)d_forall_asserts.size(); } + /** get asserted quantifier */ + Node getAssertedQuantifier( int i ) { return d_forall_asserts[i]; } + /** get instantiation constants */ + void getInstantiationConstantsFor( Node f, std::vector< Node >& ics ) { + ics.insert( ics.begin(), d_inst_constants[f].begin(), d_inst_constants[f].end() ); + } + /** get the i^th instantiation constant of f */ + Node getInstantiationConstant( Node f, int i ) { return d_inst_constants[f][i]; } + /** get number of instantiation constants for f */ + int getNumInstantiationConstants( Node f ) { return (int)d_inst_constants[f].size(); } + std::vector<Node> createInstVariable( std::vector<Node> & vars ); +public: + /** get the ce body f[e/x] */ + Node getCounterexampleBody( Node f ); + /** get the skolemized body f[e/x] */ + Node getSkolemizedBody( Node f ); + /** set active */ + void setActive( Node n, bool val ) { d_active[n] = val; } + /** get active */ + bool getActive( Node n ) { return d_active.find( n )!=d_active.end() && d_active[n]; } +public: + /** phase requirements for each quantifier for each instantiation literal */ + std::map< Node, std::map< Node, bool > > d_phase_reqs; + std::map< Node, std::map< Node, bool > > d_phase_reqs_equality; + std::map< Node, std::map< Node, Node > > d_phase_reqs_equality_term; +public: + /** is phase required */ + bool isPhaseReq( Node f, Node lit ) { return d_phase_reqs[f].find( lit )!=d_phase_reqs[f].end(); } + /** get phase requirement */ + bool getPhaseReq( Node f, Node lit ) { return d_phase_reqs[f].find( lit )==d_phase_reqs[f].end() ? false : d_phase_reqs[f][ lit ]; } + /** get term req terms */ + void getPhaseReqTerms( Node f, std::vector< Node >& nodes ); + /** helper functions compute phase requirements */ + static void computePhaseReqs( Node n, bool polarity, std::map< Node, bool >& phaseReqs ); + /** compute phase requirements */ + void generatePhaseReqs( Node f, Node n ); +public: + /** returns node n with bound vars of f replaced by instantiation constants of f + node n : is the futur pattern + node f : is the quantifier containing which bind the variable + return a pattern where the variable are replaced by variable for + instantiation. + */ + Node getSubstitutedNode( Node n, Node f ); + /** same as before but node f is just linked to the new pattern by the + applied attribute + vars the bind variable + nvars the same variable but with an attribute + */ + Node convertNodeToPattern( Node n, Node f, + const std::vector<Node> & vars, + const std::vector<Node> & nvars); + /** get free variable for instantiation constant */ + Node getFreeVariableForInstConstant( Node n ); + /** get bound variable for variable */ + Node getBoundVariableForVariable( Node n ); +public: + /** has owner */ + bool hasOwner( Node f ) { return d_owner.find( f )!=d_owner.end(); } + /** get owner */ + Theory* getOwner( Node f ) { return d_owner[f]; } + /** set owner */ + void setOwner( Node f, Theory* t ) { d_owner[f] = t; } +public: + /** get term database */ + TermDb* getTermDatabase() { return d_term_db; } + /** add term to database */ + void addTermToDatabase( Node n, bool withinQuant = false ); +private: + /** set relevance */ + void setRelevance( Node s, int r ); +public: + /** get relevance */ + int getRelevance( Node s ) { return d_relevance.find( s )==d_relevance.end() ? -1 : d_relevance[s]; } + /** get number of quantifiers for symbol s */ + int getNumQuantifiersForSymbol( Node s ) { return (int)d_syms_quants[s].size(); } +public: + /** statistics class */ + class Statistics { + public: + IntStat d_num_quant; + IntStat d_instantiation_rounds; + IntStat d_instantiation_rounds_lc; + IntStat d_instantiations; + IntStat d_max_instantiation_level; + IntStat d_splits; + IntStat d_total_inst_var; + IntStat d_total_inst_var_unspec; + IntStat d_inst_unspec; + IntStat d_inst_duplicate; + IntStat d_lit_phase_req; + IntStat d_lit_phase_nreq; + IntStat d_triggers; + IntStat d_simple_triggers; + IntStat d_multi_triggers; + IntStat d_multi_trigger_instantiations; + Statistics(); + ~Statistics(); + };/* class QuantifiersEngine::Statistics */ + Statistics d_statistics; +};/* class QuantifiersEngine */ + +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__QUANTIFIERS_ENGINE_H */ diff --git a/src/theory/rewriterules/Makefile b/src/theory/rewriterules/Makefile new file mode 100644 index 000000000..4b1d4fc55 --- /dev/null +++ b/src/theory/rewriterules/Makefile @@ -0,0 +1,4 @@ +topdir = ../../.. +srcdir = src/theory/rewriterules + +include $(topdir)/Makefile.subdir diff --git a/src/theory/rewriterules/Makefile.am b/src/theory/rewriterules/Makefile.am new file mode 100644 index 000000000..46cffda11 --- /dev/null +++ b/src/theory/rewriterules/Makefile.am @@ -0,0 +1,19 @@ +AM_CPPFLAGS = \ + -D__BUILDING_CVC4LIB \ + -I@srcdir@/../../include -I@srcdir@/../.. -I@builddir@/../.. +AM_CXXFLAGS = -Wall -Wno-unknown-pragmas $(FLAG_VISIBILITY_HIDDEN) + +noinst_LTLIBRARIES = librewriterules.la + +librewriterules_la_SOURCES = \ + theory_rewriterules_rules.h \ + theory_rewriterules_rules.cpp \ + theory_rewriterules.h \ + theory_rewriterules.cpp \ + theory_rewriterules_rewriter.h \ + theory_rewriterules_type_rules.h \ + theory_rewriterules_preprocess.h \ + theory_rewriterules_params.h + +EXTRA_DIST = \ + kinds diff --git a/src/theory/rewriterules/README.WHATS-NEXT b/src/theory/rewriterules/README.WHATS-NEXT new file mode 100644 index 000000000..eda7dcbe6 --- /dev/null +++ b/src/theory/rewriterules/README.WHATS-NEXT @@ -0,0 +1,29 @@ +Congratulations, you now have a new theory of rewriterules ! + +Your next steps will likely be: + +* to specify theory constants, types, and operators in your \`kinds' file +* to add typing rules to theory_${dir}_type_rules.h for your operators + and constants +* to write code in theory_${dir}_rewriter.h to implement a normal form + for your theory's terms +* to write parser rules in src/parser/cvc/Cvc.g to support the CVC input + language, src/parser/smt/Smt.g to support the (deprecated) SMT-LIBv1 + language, and src/parser/smt2/Smt2.g to support SMT-LIBv2 +* to write printer code in src/printer/*/*_printer* to support printing + your theory terms and types in various output languages + +and finally: + +* to implement a decision procedure for your theory by implementing + TheoryRewriterules::check() in theory_rewriterules.cpp. Before writing the actual + code, you will need : + + * to determine which datastructures are context dependent and use for them + context dependent datastructures (context/cd*.h) + * to choose which work will be done at QUICK_CHECK, STANDARD or at + FULL_EFFORT. + + +Good luck, and please contact cvc4-devel@cs.nyu.edu for assistance +should you need it! diff --git a/src/theory/rewriterules/kinds b/src/theory/rewriterules/kinds new file mode 100644 index 000000000..01fbda51e --- /dev/null +++ b/src/theory/rewriterules/kinds @@ -0,0 +1,37 @@ +# kinds -*- sh -*- +# +# For documentation on this file format, please refer to +# src/theory/builtin/kinds. +# + +theory THEORY_REWRITERULES ::CVC4::theory::rewriterules::TheoryRewriteRules "theory/rewriterules/theory_rewriterules.h" +typechecker "theory/rewriterules/theory_rewriterules_type_rules.h" +rewriter ::CVC4::theory::rewriterules::TheoryRewriterulesRewriter "theory/rewriterules/theory_rewriterules_rewriter.h" + +properties check + +# Theory content goes here. + +# constants... + +# types... +sort RRHB_TYPE \ + Cardinality::INTEGERS \ + not-well-founded \ + "head and body of the rule type" + +# operators... + +# variables, guards, RR_REWRITE/REDUCTION_RULE/DEDUCTION_RULE +operator REWRITE_RULE 3 "generale rewrite rule" +#HEAD/BODY/TRIGGER +operator RR_REWRITE 2:3 "actual rewrite rule" +operator RR_REDUCTION 2:3 "actual reduction rule" +operator RR_DEDUCTION 2:3 "actual deduction rule" + +typerule REWRITE_RULE ::CVC4::theory::rewriterules::RewriteRuleTypeRule +typerule RR_REWRITE ::CVC4::theory::rewriterules::RRRewriteTypeRule +typerule RR_REDUCTION ::CVC4::theory::rewriterules::RRRedDedTypeRule +typerule RR_DEDUCTION ::CVC4::theory::rewriterules::RRRedDedTypeRule + +endtheory diff --git a/src/theory/rewriterules/theory_rewriterules.cpp b/src/theory/rewriterules/theory_rewriterules.cpp new file mode 100644 index 000000000..0072a36e9 --- /dev/null +++ b/src/theory/rewriterules/theory_rewriterules.cpp @@ -0,0 +1,519 @@ +/********************* */ +/*! \file rewrite_engine.cpp + ** \verbatim + ** Original author: ajreynolds + ** Major contributors: bobot + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 + ** The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief [[ Deals with rewrite rules ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#include "theory/rewriterules/theory_rewriterules.h" +#include "theory/rewriterules/theory_rewriterules_rules.h" +#include "theory/rewriterules/theory_rewriterules_params.h" + +#include "theory/rewriterules/theory_rewriterules_preprocess.h" +#include "theory/rewriter.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::rewriterules; + + +namespace CVC4 { +namespace theory { +namespace rewriterules { + + +inline std::ostream& operator <<(std::ostream& stream, const RuleInst& ri) { + ri.toStream(stream); + return stream; +} + +static const RuleInst* RULEINST_TRUE = (RuleInst*) 1; +static const RuleInst* RULEINST_FALSE = (RuleInst*) 2; + + /** Rule an instantiation with the given match */ +RuleInst::RuleInst(TheoryRewriteRules & re, const RewriteRule * r, + std::vector<Node> & inst_subst, + Node matched): + rule(r), d_matched(matched) +{ + Assert(r != NULL); + Assert(!r->directrr || !d_matched.isNull()); + subst.swap(inst_subst); +}; + +Node RuleInst::substNode(const TheoryRewriteRules & re, TNode r, + TCache cache ) const { + Assert(this != RULEINST_TRUE && this != RULEINST_FALSE); + return r.substitute (rule->free_vars.begin(),rule->free_vars.end(), + subst.begin(),subst.end(),cache); +}; +size_t RuleInst::findGuard(TheoryRewriteRules & re, size_t start)const{ + TCache cache; + Assert(this != RULEINST_TRUE && this != RULEINST_FALSE); + while (start < (rule->guards).size()){ + Node g = substNode(re,rule->guards[start],cache); + switch(re.addWatchIfDontKnow(g,this,start)){ + case ATRUE: + Debug("rewriterules") << g << "is true" << std::endl; + ++start; + continue; + case AFALSE: + Debug("rewriterules") << g << "is false" << std::endl; + return -1; + case ADONTKNOW: + Debug("rewriterules") << g << "is unknown" << std::endl; + return start; + } + } + /** All the guards are verified */ + re.propagateRule(this,cache); + return start; +}; + +bool RuleInst::alreadyRewritten(TheoryRewriteRules & re) const{ + Assert(this != RULEINST_TRUE && this != RULEINST_FALSE); + static NoMatchAttribute rewrittenNodeAttribute; + TCache cache; + for(std::vector<Node>::const_iterator + iter = rule->to_remove.begin(); + iter != rule->to_remove.end(); ++iter){ + if (substNode(re,*iter,cache).getAttribute(rewrittenNodeAttribute)) + return true; + }; + return false; +} + +void RuleInst::toStream(std::ostream& out) const{ + if(this == RULEINST_TRUE){ out << "TRUE"; return;}; + if(this == RULEINST_FALSE){ out << "FALSE"; return;}; + out << "(" << *rule << ") "; + for(std::vector<Node>::const_iterator + iter = subst.begin(); iter != subst.end(); ++iter){ + out << *iter << " "; + }; +} + + +void Guarded::nextGuard(TheoryRewriteRules & re)const{ + Assert(inst != RULEINST_TRUE && inst != RULEINST_FALSE); + if(simulateRewritting && inst->alreadyRewritten(re)) return; + inst->findGuard(re,d_guard+1); +}; + +/** start indicate the first guard which is not true */ +Guarded::Guarded(const RuleInst* ri, const size_t start) : + d_guard(start),inst(ri) {}; +Guarded::Guarded(const Guarded & g) : + d_guard(g.d_guard),inst(g.inst) {}; +Guarded::Guarded() : + //dumb value + d_guard(-1),inst(RULEINST_TRUE) {}; + +TheoryRewriteRules::TheoryRewriteRules(context::Context* c, + context::UserContext* u, + OutputChannel& out, + Valuation valuation, + const LogicInfo& logicInfo, + QuantifiersEngine* qe) : + Theory(THEORY_REWRITERULES, c, u, out, valuation, logicInfo, qe), + d_rules(c), d_ruleinsts(c), d_guardeds(c), d_checkLevel(c,0), + d_explanations(c), d_ruleinsts_to_add() + { + d_true = NodeManager::currentNM()->mkConst<bool>(true); + d_false = NodeManager::currentNM()->mkConst<bool>(false); + Debug("rewriterules") << Node::setdepth(-1); + Debug("rewriterules-rewrite") << Node::setdepth(-1); +} + +void TheoryRewriteRules::addMatchRuleTrigger(const RewriteRule * r, + InstMatch & im, + bool delay){ + ++r->nb_matched; + if(rewrite_instantiation) im.applyRewrite(); + if(representative_instantiation) + im.makeRepresentative( getQuantifiersEngine() ); + + if(!cache_match || !r->inCache(*this,im)){ + ++r->nb_applied; + std::vector<Node> subst; + im.computeTermVec(getQuantifiersEngine(), r->inst_vars , subst); + RuleInst * ri = new RuleInst(*this,r,subst, + r->directrr ? im.d_matched : Node::null()); + Debug("rewriterules") << "One matching found" + << (delay? "(delayed)":"") + << ":" << *ri << std::endl; + // Find the first non verified guard, don't save the rule if the + // rule can already be fired In fact I save it otherwise strange + // things append. + Assert(ri->rule != NULL); + if(delay) d_ruleinsts_to_add.push_back(ri); + else{ + if(simulateRewritting && ri->alreadyRewritten(*this)) return; + if(ri->findGuard(*this, 0) != (r->guards).size()) + d_ruleinsts.push_back(ri); + else delete(ri); + }; + }; +} + +void TheoryRewriteRules::check(Effort level) { + CodeTimer codeTimer(d_theoryTime); + + Assert(d_ruleinsts_to_add.empty()); + + while(!done()) { + // Get all the assertions + // TODO: Test that it have already been ppAsserted + get(); + // Assertion assertion = get(); + // TNode fact = assertion.assertion; + + // Debug("rewriterules") << "TheoryRewriteRules::check(): processing " << fact << std::endl; + // if (getValuation().getDecisionLevel()>0) + // Unhandled(getValuation().getDecisionLevel()); + // addRewriteRule(fact); + + }; + + Debug("rewriterules") << "Check:" << d_checkLevel << (level==EFFORT_FULL? " EFFORT_FULL":"") << std::endl; + + /** Test each rewrite rule */ + for(size_t rid = 0, end = d_rules.size(); rid < end; ++rid) { + RewriteRule * r = d_rules[rid]; + if (level!=EFFORT_FULL && r->d_split) continue; + Debug("rewriterules") << " rule: " << r << std::endl; + Trigger & tr = r->trigger; + //reset instantiation round for trigger (set up match production) + tr.resetInstantiationRound(); + //begin iterating from the first match produced by the trigger + tr.reset( Node::null() ); + + /** Test the possible matching one by one */ + InstMatch im; + while(tr.getNextMatch( im )){ + addMatchRuleTrigger(r, im, true); + im.clear(); + } + } + + const bool do_notification = d_checkLevel == 0 || level==EFFORT_FULL; + bool polldone = false; + + GuardedMap::const_iterator p = d_guardeds.begin(); + do{ + + + //dequeue instantiated rules + for(; !d_ruleinsts_to_add.empty();){ + RuleInst * ri = d_ruleinsts_to_add.back(); d_ruleinsts_to_add.pop_back(); + if(simulateRewritting && ri->alreadyRewritten(*this)) break; + if(ri->findGuard(*this, 0) != ri->rule->guards.size()) + d_ruleinsts.push_back(ri); + else delete(ri); + }; + + if(do_notification) + /** Temporary way. Poll value */ + for (; p != d_guardeds.end(); ++p){ + TNode g = (*p).first; + const GList * const l = (*p).second; + const Guarded & glast = l->back(); + // Notice() << "Polled?:" << g << std::endl; + if(glast.inst == RULEINST_TRUE||glast.inst == RULEINST_FALSE) continue; + // Notice() << "Polled!:" << g << "->" << (glast.inst == RULEINST_TRUE||glast.inst == RULEINST_FALSE) << std::endl; + bool value; + if(getValuation().hasSatValue(g,value)){ + if(value) polldone = true; //One guard is true so pass n check + Debug("rewriterules") << "Poll value:" << g + << " is " << (value ? "true" : "false") << std::endl; + notification(g,value); + //const Guarded & glast2 = (*l)[l->size()-1]; + // Notice() << "Polled!!:" << g << "->" << (glast2.inst == RULEINST_TRUE||glast2.inst == RULEINST_FALSE) << std::endl; + }; + }; + + }while(!d_ruleinsts_to_add.empty() || + (p != d_guardeds.end() && do_notification)); + + if(polldone) d_checkLevel = checkSlowdown; + else d_checkLevel = d_checkLevel > 0 ? (d_checkLevel - 1) : 0; + + /** Narrowing by splitting on the guards */ + /** Perhaps only when no notification? */ + if(narrowing_full_effort && level==EFFORT_FULL){ + for (GuardedMap::const_iterator p = d_guardeds.begin(); + p != d_guardeds.end(); ++p){ + TNode g = (*p).first; + const GList * const l = (*p).second; + const Guarded & glast = (*l)[l->size()-1]; + if(glast.inst == RULEINST_TRUE||glast.inst == RULEINST_FALSE) + continue; + // If it has a value it should already has been notified + bool value; value = value; // avoiding the warning in non debug mode + Assert(!getValuation().hasSatValue(g,value)); + Debug("rewriterules") << "Narrowing on:" << g << std::endl; + /** Can split on already rewritten instrule... but... */ + getOutputChannel().split(g); + } + } + + Assert(d_ruleinsts_to_add.empty()); + Debug("rewriterules") << "Check done:" << d_checkLevel << std::endl; + +}; + +void TheoryRewriteRules::registerQuantifier( Node n ){}; + +Trigger TheoryRewriteRules::createTrigger( TNode n, std::vector<Node> & pattern ) { + // Debug("rewriterules") << "createTrigger:"; + getQuantifiersEngine()->registerPattern(pattern); + return *Trigger::mkTrigger(getQuantifiersEngine(),n,pattern, + match_gen_kind, true); +}; + +bool TheoryRewriteRules::notifyIfKnown(const GList * const ltested, + GList * const lpropa) { + Assert(ltested->size() > 0); + const Guarded & glast = (*ltested)[ltested->size()-1]; + if(glast.inst == RULEINST_TRUE || glast.inst == RULEINST_FALSE){ + notification(lpropa,glast.inst == RULEINST_TRUE); + return true; + }; + return false; +}; + +void TheoryRewriteRules::notification(GList * const lpropa, bool b){ + if (b){ + for(GList::const_iterator g = lpropa->begin(); + g != lpropa->end(); ++g) { + (*g).nextGuard(*this); + }; + lpropa->push_back(Guarded(RULEINST_TRUE,0)); + }else{ + lpropa->push_back(Guarded(RULEINST_FALSE,0)); + }; + Assert(lpropa->back().inst == RULEINST_TRUE || + lpropa->back().inst == RULEINST_FALSE); +}; + + + +Answer TheoryRewriteRules::addWatchIfDontKnow(Node g0, const RuleInst* ri, + const size_t gid){ + /** TODO: Should use the representative of g, but should I keep the + mapping for myself? */ + /* If it false in one model (current valuation) it's false for all */ + if (useCurrentModel){ + Node val = getValuation().getValue(g0); + Debug("rewriterules") << "getValue:" << g0 << " = " + << val << " is " << (val == d_false) << std::endl; + if (val == d_false) return AFALSE; + }; + /** Currently create a node with a literal */ + Node g = getValuation().ensureLiteral(g0); + GuardedMap::iterator l_i = d_guardeds.find(g); + GList* l; + if( l_i == d_guardeds.end() ) { + /** Normally Not watched so IDONTNOW but since we poll, we can poll now */ + bool value; + if(getValuation().hasSatValue(g,value)){ + if(value) return ATRUE; + else return AFALSE; + }; + //Not watched so IDONTNOW + l = new(getSatContext()->getCMM()) + GList(true, getSatContext());//, + //ContextMemoryAllocator<Guarded>(getContext()->getCMM())); + d_guardeds.insert(g ,l);//.insertDataFromContextMemory(g, l); + /* TODO Add register propagation */ + } else { + l = (*l_i).second; + Assert(l->size() > 0); + const Guarded & glast = (*l)[l->size()-1]; + if(glast.inst == RULEINST_TRUE) return ATRUE; + if(glast.inst == RULEINST_FALSE) return AFALSE; + + }; + /** I DONT KNOW because not watched or because not true nor false */ + l->push_back(Guarded(ri,gid)); + return ADONTKNOW; +}; + +void TheoryRewriteRules::notification(Node g, bool b){ + GuardedMap::const_iterator l = d_guardeds.find(g); + /** Should be a propagated node already known */ + Assert(l != d_guardeds.end()); + notification((*l).second,b); +} + + +void TheoryRewriteRules::notifyEq(TNode lhs, TNode rhs) { + GuardedMap::const_iterator ilhs = d_guardeds.find(lhs); + GuardedMap::const_iterator irhs = d_guardeds.find(rhs); + /** Should be a propagated node already known */ + Assert(ilhs != d_guardeds.end()); + if( irhs == d_guardeds.end() ) { + /** Not watched so points to the list directly */ + d_guardeds.insertDataFromContextMemory(rhs, (*ilhs).second); + } else { + GList * const llhs = (*ilhs).second; + GList * const lrhs = (*irhs).second; + if(!(notifyIfKnown(llhs,lrhs) || notifyIfKnown(lrhs,llhs))){ + /** If none of the two is known */ + for(GList::const_iterator g = llhs->begin(); g != llhs->end(); ++g){ + lrhs->push_back(*g); + }; + }; + }; +}; + + +void TheoryRewriteRules::propagateRule(const RuleInst * inst, TCache cache){ + // Debug("rewriterules") << "A rewrite rules is verified. Add lemma:"; + Debug("rewriterules") << "propagateRule" << *inst << std::endl; + const RewriteRule * rule = inst->rule; + ++rule->nb_applied; + // Can be more something else than an equality in fact (eg. propagation rule) + Node equality = inst->substNode(*this,rule->body,cache); + if(propagate_as_lemma){ + Node lemma = equality; + if(rule->guards.size() > 0){ + // TODO: problem with reduction rule => instead of <=> + lemma = substGuards(inst, cache).impNode(equality); + }; + if(rule->directrr){ + TypeNode booleanType = NodeManager::currentNM()->booleanType(); + // if the rule is directly applied by the rewriter, + // we should take care to use the representative that can't be directly rewritable: + // If "car(a)" is somewhere and we know that "a = cons(x,l)" we shouldn't + // add the constraint car(cons(x,l) = x because it is rewritten to x = x. + // But we should say cons(a) = x + Assert(lemma.getKind() == kind::EQUAL || + lemma.getKind() == kind::IMPLIES); + Assert(!inst->d_matched.isNull()); + Assert( inst->d_matched.getOperator() == lemma[0].getOperator() ); + // replace the left hand side by the term really matched + Debug("rewriterules-directrr") << "lemma:" << lemma << " :: " << inst->d_matched; + Node hyp; + NodeBuilder<2> nb; + if(inst->d_matched.getNumChildren() == 1){ + nb.clear( inst->d_matched[0].getType(false) == booleanType ? + kind::IFF : kind::EQUAL ); + nb << inst->d_matched[0] << lemma[0][0]; + hyp = nb; + }else{ + NodeBuilder<> andb(kind::AND); + for(size_t i = 0, + iend = inst->d_matched.getNumChildren(); i < iend; ++i){ + nb.clear( inst->d_matched[i].getType(false) == booleanType ? + kind::IFF : kind::EQUAL ); + nb << inst->d_matched[i] << lemma[0][i]; + andb << static_cast<Node>(nb); + } + hyp = andb; + }; + nb.clear(lemma.getKind()); + nb << inst->d_matched << lemma[1]; + lemma = hyp.impNode(static_cast<Node>(nb)); + Debug("rewriterules-directrr") << " -> " << lemma << std::endl; + }; + // Debug("rewriterules") << "lemma:" << lemma << std::endl; + getOutputChannel().lemma(lemma); + }else{ + Assert(!direct_rewrite); + Node lemma_lit = getValuation().ensureLiteral(equality); + ExplanationMap::const_iterator p = d_explanations.find(lemma_lit); + if(p!=d_explanations.end()) return; //Already propagated + bool value; + if(getValuation().hasSatValue(lemma_lit,value)){ + /* Already assigned */ + if (!value){ + Node conflict = substGuards(inst,cache,lemma_lit); + getOutputChannel().conflict(conflict); + }; + }else{ + getOutputChannel().propagate(lemma_lit); + d_explanations.insert(lemma_lit, *inst); + }; + }; + + if(simulateRewritting){ + static NoMatchAttribute rewrittenNodeAttribute; + // Tag the rewritted terms + for(std::vector<Node>::iterator i = rule->to_remove.begin(); + i == rule->to_remove.end(); ++i){ + (*i).setAttribute(rewrittenNodeAttribute,true); + }; + }; + + //Verify that this instantiation can't immediately fire another rule + for(RewriteRule::BodyMatch::const_iterator p = rule->body_match.begin(); + p != rule->body_match.end(); ++p){ + RewriteRule * r = (*p).second; + // Debug("rewriterules") << " rule: " << r << std::endl; + // Use trigger2 since we can be in check + Trigger & tr = r->trigger_for_body_match; + InstMatch im; + TNode m = inst->substNode(*this,(*p).first, cache); + tr.getMatch(m,im); + if(!im.empty()){ + im.d_matched = m; + addMatchRuleTrigger(r, im); + } + } +}; + + +Node TheoryRewriteRules::substGuards(const RuleInst *inst, + TCache cache, + /* Already substituted */ + Node last){ + const RewriteRule * r = inst->rule; + /** No guards */ + const size_t size = r->guards.size(); + if(size == 0) return (last.isNull()?d_true:last); + /** One guard */ + if(size == 1 && last.isNull()) return inst->substNode(*this,r->guards[0],cache); + /** Guards */ /* TODO remove the duplicate with a set like in uf? */ + NodeBuilder<> conjunction(kind::AND); + for(std::vector<Node>::const_iterator p = r->guards.begin(); + p != r->guards.end(); ++p) { + Assert(!p->isNull()); + conjunction << inst->substNode(*this,*p,cache); + }; + if (!last.isNull()) conjunction << last; + return conjunction; +} + +Node TheoryRewriteRules::explain(TNode n){ + ExplanationMap::const_iterator p = d_explanations.find(n); + Assert(p!=d_explanations.end(),"I forget the explanation..."); + RuleInst i = (*p).second; + //Notice() << n << "<-" << *(i.rule) << std::endl; + return substGuards(&i, TCache ()); +} + +Theory::PPAssertStatus TheoryRewriteRules::ppAssert(TNode in, SubstitutionMap& outSubstitutions) { + addRewriteRule(in); + return PP_ASSERT_STATUS_UNSOLVED; +} + +}/* CVC4::theory::rewriterules namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/rewriterules/theory_rewriterules.h b/src/theory/rewriterules/theory_rewriterules.h new file mode 100644 index 000000000..499535687 --- /dev/null +++ b/src/theory/rewriterules/theory_rewriterules.h @@ -0,0 +1,264 @@ +/********************* */ +/*! \file rewrite_engine.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: bobot + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 + ** The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Rewrite Engine classes + **/ + + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_H +#define __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_H + +#include "context/cdlist.h" +#include "context/cdqueue.h" +#include "theory/valuation.h" +#include "theory/theory.h" +#include "theory/theory_engine.h" +#include "theory/quantifiers_engine.h" +#include "context/context_mm.h" +#include "theory/inst_match_impl.h" +#include "util/stats.h" +#include "theory/rewriterules/theory_rewriterules_preprocess.h" + +namespace CVC4 { +namespace theory { +namespace rewriterules { + +typedef std::hash_map<TNode, TNode, TNodeHashFunction> TCache; + + enum Answer {ATRUE, AFALSE, ADONTKNOW}; + + class TheoryRewriteRules; /** forward */ + + class RewriteRule{ + public: + // constant + const bool d_split; + mutable Trigger trigger; + std::vector<Node> guards; + mutable std::vector<Node> to_remove; /** terms to remove */ + const Node body; + const TNode new_terms; /** new terms included in the body */ + std::vector<Node> free_vars; /* free variable in the rule */ + std::vector<Node> inst_vars; /* corresponding vars in the triggers */ + /* After instantiating the body new match can appear (TNode + because is a subterm of a body on the assicaited rewrite + rule) */ + typedef context::CDList< std::pair<TNode,RewriteRule* > > BodyMatch; + mutable BodyMatch body_match; + mutable Trigger trigger_for_body_match; // used because we can be matching + // trigger when we need new match. + // So currently we use another + // trigger for that. + + //context dependent + typedef InstMatchTrie2<true> CacheNode; + mutable CacheNode d_cache; + + const bool directrr; + + RewriteRule(TheoryRewriteRules & re, + Trigger & tr, Trigger & tr2, + std::vector<Node> & g, Node b, TNode nt, + std::vector<Node> & fv,std::vector<Node> & iv, + std::vector<Node> & to_r, bool drr); + RewriteRule(const RewriteRule & r) CVC4_UNUSED; + RewriteRule& operator=(const RewriteRule &) CVC4_UNUSED; + ~RewriteRule(); + + bool noGuard()const throw () { return guards.size() == 0; }; + bool inCache(TheoryRewriteRules & re, InstMatch & im)const; + + void toStream(std::ostream& out) const; + + /* statistics */ + mutable size_t nb_matched; + mutable size_t nb_applied; + mutable size_t nb_propagated; + + }; + + class RuleInst{ + public: + /** The rule has at least one guard */ + const RewriteRule* rule; + + /** the substitution */ + std::vector<Node> subst; + + /** the term matched (not null if mono-pattern and direct-rr) */ + Node d_matched; + + /** Rule an instantiation with the given match */ + RuleInst(TheoryRewriteRules & re, const RewriteRule* r, + std::vector<Node> & inst_subst, + Node matched); + RuleInst():rule(NULL){} // Dumb + + Node substNode(const TheoryRewriteRules & re, TNode r, TCache cache) const; + size_t findGuard(TheoryRewriteRules & re, size_t start)const; + + void toStream(std::ostream& out) const; + + bool alreadyRewritten(TheoryRewriteRules & re) const; + }; + +/** A pair? */ + class Guarded { + public: + /** The backtracking is done somewhere else */ + const size_t d_guard; /* the id of the guard */ + + /** The shared instantiation data */ + const RuleInst* inst; + + void nextGuard(TheoryRewriteRules & re)const; + + /** start indicate the first guard which is not true */ + Guarded(const RuleInst* ri, const size_t start); + Guarded(const Guarded & g); + /** Should be ssigned by a good garded after */ + Guarded(); + + ~Guarded(){}; + void destroy(){}; + }; + +template<class T> +class CleanUpPointer{ +public: + inline void operator()(T** e){ + delete(*e); + }; +}; + +class TheoryRewriteRules : public Theory { +private: + + KEEP_STATISTIC(TimerStat, d_theoryTime, "theory::rewriterules::theoryTime"); + + /** list of all rewrite rules */ + /* std::vector< Node > d_rules; */ + // typedef std::vector< std::pair<Node, Trigger > > Rules; + typedef context::CDList< RewriteRule *, + CleanUpPointer<RewriteRule >, + std::allocator< RewriteRule * > > Rules; + Rules d_rules; + typedef context::CDList< RuleInst *, + CleanUpPointer<RuleInst>, + std::allocator< RuleInst * > > RuleInsts; + RuleInsts d_ruleinsts; + + /** The GList* will lead too memory leaks since that doesn't use + CDChunckList */ + typedef context::CDList< Guarded > GList; + typedef context::CDHashMap<Node, GList *, NodeHashFunction> GuardedMap; + GuardedMap d_guardeds; + + /* In order to not monopolize, the system slow down himself: If a + guard stored in d_guardeds become true or false, it waits + checkSlowdown(=10) checks before checking again if some guard take a + value. At FULL_EFFORT regardless of d_checkLevel it check the + guards + */ + context::CDO<size_t> d_checkLevel; + + /** explanation */ + typedef context::CDHashMap<Node, RuleInst , NodeHashFunction> ExplanationMap; + ExplanationMap d_explanations; + + /** new instantiation must be cleared at each conflict used only + inside check */ + typedef std::vector< RuleInst* > QRuleInsts; + QRuleInsts d_ruleinsts_to_add; + public: + /** true and false for predicate */ + Node d_true; + Node d_false; + + /** Constructs a new instance of TheoryRewriteRules + w.r.t. the provided context.*/ + TheoryRewriteRules(context::Context* c, + context::UserContext* u, + OutputChannel& out, + Valuation valuation, + const LogicInfo& logicInfo, + QuantifiersEngine* qe); + + /** Usual function for theories */ + void check(Theory::Effort e); + Node explain(TNode n); + void notifyEq(TNode lhs, TNode rhs); + std::string identify() const { + return "THEORY_REWRITERULES"; + } + + Theory::PPAssertStatus ppAssert(TNode in, SubstitutionMap& outSubstitutions); + + bool ppDontRewriteSubterm(TNode atom){ return true; } + + + private: + void registerQuantifier( Node n ); + + public: + /* TODO modify when notification will be available */ + void notification( Node n, bool b); + + Trigger createTrigger( TNode n, std::vector<Node> & pattern ); + + /** return if the guard (already substituted) is known true or false + or unknown. In the last case it add the Guarded(rid,gid) to the watch + list of this guard */ + Answer addWatchIfDontKnow(Node g, const RuleInst* r, const size_t gid); + + /** An instantiation of a rule is fired (all guards true) we + propagate the body. That can be done by theory propagation if + possible or by lemmas. + */ + void propagateRule(const RuleInst * r, TCache cache); + + /** Auxillary functions */ +private: + /** A guard is verify, notify the Guarded */ + void notification(GList * const lpropa, bool b); + /* If two guards becomes equals we should notify if one of them is + already true */ + bool notifyIfKnown(const GList * const ltested, GList * const lpropa); + + Node substGuards(const RuleInst * inst, + TCache cache, + Node last = Node::null()); + + void addRewriteRule(const Node r); + void computeMatchBody ( const RewriteRule * r, size_t start = 0); + void addMatchRuleTrigger(const RewriteRule* r, + InstMatch & im, bool delay = true); + + /* rewrite pattern */ + typedef std::hash_map< Node, rewriter::RRPpRewrite*, NodeHashFunction > RegisterRRPpRewrite; + RegisterRRPpRewrite d_registeredRRPpRewrite; + + bool addRewritePattern(TNode pattern, TNode body, + rewriter::Subst & pvars, + rewriter::Subst & vars); + +};/* class TheoryRewriteRules */ + +}/* CVC4::theory::rewriterules namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_H */ diff --git a/src/theory/rewriterules/theory_rewriterules_params.h b/src/theory/rewriterules/theory_rewriterules_params.h new file mode 100644 index 000000000..ecb5385f9 --- /dev/null +++ b/src/theory/rewriterules/theory_rewriterules_params.h @@ -0,0 +1,95 @@ +/********************* */ +/*! \file rewrite_engine.cpp + ** \verbatim + ** Original author: bobot + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 + ** The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Parameters for the rewrite rules theory + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + + +#ifndef __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_PARAMS_H +#define __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_PARAMS_H + +namespace CVC4 { +namespace theory { +namespace rewriterules { + +/** + Specify if the propagation is done by lemma or by real theory propagation + */ +static const bool propagate_as_lemma = true; + +/** + Cache the instantiation of rules in order to remove duplicate + */ +static const bool cache_match = true; + +/** + Compute when the rules are created which terms in the body can lead + to new instantiation (try only single trigger). During propagation + we check if the instantiation of these terms are known terms. + */ +static const bool compute_opt = true; + +/** + rewrite the matching found + */ +static const bool rewrite_instantiation = true; + +/** + use the representative for the matching found + */ +static const bool representative_instantiation = false; + +/** + Wait the specified number of check after a new propagation (a + previous unknown guards becomes true) is found before verifying again the guards. + + Allow to break loop with other theories. + */ +static const size_t checkSlowdown = 10; + +/** + Use the current model to eliminate guard before asking for notification + */ +static const bool useCurrentModel = false; + +/** + Simulate rewritting by tagging rewritten terms. + */ +static const bool simulateRewritting = false; + +/** + Choose the kind of matching to use: + - InstMatchGenerator::MATCH_GEN_DEFAULT 0 + - InstMatchGenerator::MATCH_GEN_EFFICIENT_E_MATCH 1 +*/ +static const int match_gen_kind = 0; + +/** + Do narrowing at full effort +*/ +static const bool narrowing_full_effort = false; + +/** + Direct rewrite: Add rewrite rules directly in the rewriter. + */ +static const bool direct_rewrite = true; + +}/* CVC4::theory::rewriterules namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_PARAMS_H */ diff --git a/src/theory/rewriterules/theory_rewriterules_preprocess.h b/src/theory/rewriterules/theory_rewriterules_preprocess.h new file mode 100644 index 000000000..f17e30758 --- /dev/null +++ b/src/theory/rewriterules/theory_rewriterules_preprocess.h @@ -0,0 +1,176 @@ +/********************* */ +/*! \file rewriterules.h + ** \verbatim + ** Original author: bobot + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief One utilitise for rewriterules definition + ** + **/ + + +#ifndef __CVC4__REWRITERULES_H +#define __CVC4__REWRITERULES_H + +#include <vector> +#include <hash_set> +#include <hash_map> +#include "expr/expr.h" +#include "expr/node.h" +#include "theory/rewriterules/theory_rewriterules_params.h" +#include "theory/uf/theory_uf.h" + +namespace CVC4 { +namespace theory { +namespace rewriterules { + +namespace rewriter{ + + typedef Node TMPNode; + typedef std::vector<Node> Subst; + typedef std::vector<Expr> ESubst; + typedef std::vector<TMPNode> TSubst; + + struct Step{ + + /** match the node and add in Vars the found variables */ + virtual Node run(TMPNode node) = 0; + virtual bool add(TMPNode pattern, TMPNode body, Subst & pvars, Subst & vars) = 0; + }; + + struct FinalStep : Step { + Node body; + TSubst vars; + + Node subst(Subst & subst){ + return body.substitute(vars.begin(), vars.end(), + subst.begin(), subst.end()); + } + + }; + + typedef std::hash_map< Node, int, NodeHashFunction > PVars; + + struct Pattern : FinalStep{ + Node pattern; + PVars pattern_vars; + + Node run(TMPNode node){ + Subst vars = Subst(pattern_vars.size(),Node::null()); + + typedef std::vector<std::pair<TMPNode,TMPNode> > tstack; + tstack stack(1,std::make_pair(node,pattern)); // t * pat + + while(!stack.empty()){ + const std::pair<TMPNode,TMPNode> p = stack.back(); stack.pop_back(); + const TMPNode & t = p.first; + const TMPNode & pat = p.second; + + // pat is a variable + if( pat.getKind() == kind::INST_CONSTANT || + pat.getKind() == kind::VARIABLE){ + PVars::iterator i = pattern_vars.find(pat); + Assert(i != pattern_vars.end()); + if(vars[i->second].isNull()) vars[i->second] = t; + if(vars[i->second] == t) continue; + return Node::null(); + }; + + // t is not an UF application + if( t.getKind() != kind::APPLY_UF ){ + if (t == pat) continue; + else return Node::null(); + }; + + //different UF_application + if( t.getOperator() != pat.getOperator() ) return Node::null(); + + //put the children on the stack + for( size_t i=0; i < pat.getNumChildren(); i++ ){ + stack.push_back(std::make_pair(t[i],pat[i])); + }; + } + + // Matching is done + return subst(vars); + } + + bool add(TMPNode pattern, TMPNode body, Subst & pvars, Subst & vars){ + return false; + } + + }; + + + struct Args : Step { + typedef std::vector<Pattern> Patterns; + Patterns d_matches; + + Node run(TMPNode node){ + Node n; + for (Patterns::iterator i = d_matches.begin(); + i != d_matches.end() && n.isNull(); ++i){ + Debug("rewriterules-rewrite") << "test?" << i->pattern << std::endl; + n = i->run(node); + } + return n; + } + + bool add(TMPNode pattern, TMPNode body, Subst & pvars, Subst & vars){ + Debug("rewriterules-rewrite") << "theoryrewrite::Args::add" << "(" + << d_matches.size() << ")" + << pattern << "->" << body << std::endl; + d_matches.push_back(Pattern()); + Pattern & p = d_matches.back(); + p.body = body; + p.vars.reserve(vars.size()); + for( size_t i=0; i < vars.size(); i++ ){ + p.vars.push_back(vars[i]); + }; + p.pattern = pattern; + for( size_t i=0; i < pvars.size(); i++ ){ + p.pattern_vars[pvars[i]] = i; + }; + return true; + }; + + void clear(){ + d_matches.clear(); + } + }; + +class RRPpRewrite : public uf::TheoryUF::PpRewrite { + Args d_pattern; +public: + Node ppRewrite(TNode node){ + Debug("rewriterules-rewrite") << "rewrite?" << node << std::endl; + Node t = d_pattern.run(node); + Debug("rewriterules-rewrite") << "rewrite:" << node + << (t.isNull()? " to": " to ") + << t << std::endl; + if (t.isNull()) return node; + else return t; + } + + bool addRewritePattern(TMPNode pattern, TMPNode body, + Subst & pvars, Subst & vars){ + return d_pattern.add(pattern,body,pvars,vars); + } + +}; + + + +} + +} +} +} +#endif /* __CVC4__REWRITERULES_H */ diff --git a/src/theory/rewriterules/theory_rewriterules_rewriter.h b/src/theory/rewriterules/theory_rewriterules_rewriter.h new file mode 100644 index 000000000..d9da095f7 --- /dev/null +++ b/src/theory/rewriterules/theory_rewriterules_rewriter.h @@ -0,0 +1,104 @@ +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_REWRITER_H +#define __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_REWRITER_H + +#include "theory/rewriter.h" +#include "theory/rewriter_attributes.h" + +namespace CVC4 { +namespace theory { +namespace rewriterules { + +class TheoryRewriterulesRewriter { +public: + + /** + * Rewrite a node into the normal form for the theory of rewriterules. + * Called in post-order (really reverse-topological order) when + * traversing the expression DAG during rewriting. This is the + * main function of the rewriter, and because of the ordering, + * it can assume its children are all rewritten already. + * + * This function can return one of three rewrite response codes + * along with the rewritten node: + * + * REWRITE_DONE indicates that no more rewriting is needed. + * REWRITE_AGAIN means that the top-level expression should be + * rewritten again, but that its children are in final form. + * REWRITE_AGAIN_FULL means that the entire returned expression + * should be rewritten again (top-down with preRewrite(), then + * bottom-up with postRewrite()). + * + * Even if this function returns REWRITE_DONE, if the returned + * expression belongs to a different theory, it will be fully + * rewritten by that theory's rewriter. + */ + static RewriteResponse postRewrite(TNode node) { + + // Implement me! + + // This default implementation + return RewriteResponse(REWRITE_DONE, node); + } + + /** + * Rewrite a node into the normal form for the theory of rewriterules + * in pre-order (really topological order)---meaning that the + * children may not be in the normal form. This is an optimization + * for theories with cancelling terms (e.g., 0 * (big-nasty-expression) + * in arithmetic rewrites to 0 without the need to look at the big + * nasty expression). Since it's only an optimization, the + * implementation here can do nothing. + */ + static RewriteResponse preRewrite(TNode node) { + // do nothing + return RewriteResponse(REWRITE_DONE, node); + } + + /** + * Rewrite an equality, in case special handling is required. + */ + static Node rewriteEquality(TNode equality) { + // often this will suffice + return postRewrite(equality).node; + } + + /** + * Initialize the rewriter. + */ + static inline void init() { + // nothing to do + } + + /** + * Shut down the rewriter. + */ + static inline void shutdown() { + // nothing to do + } + +};/* class TheoryRewriterulesRewriter */ + +}/* CVC4::theory::rewriterules namespace */ + +template<> +struct RewriteAttibute<THEORY_REWRITERULES> { + static Node getPreRewriteCache(TNode node) throw() { + return node; + } + + static void setPreRewriteCache(TNode node, TNode cache) throw() { } + + static Node getPostRewriteCache(TNode node) throw() { + return node; + } + + static void setPostRewriteCache(TNode node, TNode cache) throw() { } + +}; + +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_REWRITER_H */ diff --git a/src/theory/rewriterules/theory_rewriterules_rules.cpp b/src/theory/rewriterules/theory_rewriterules_rules.cpp new file mode 100644 index 000000000..9847f1727 --- /dev/null +++ b/src/theory/rewriterules/theory_rewriterules_rules.cpp @@ -0,0 +1,303 @@ +/********************* */ +/*! \file rewrite_engine.cpp + ** \verbatim + ** Original author: ajreynolds + ** Major contributors: bobot + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 + ** The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief [[ Deals with rewrite rules ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#include "theory/rewriterules/theory_rewriterules_rules.h" +#include "theory/rewriterules/theory_rewriterules_params.h" +#include "theory/rewriterules/theory_rewriterules_preprocess.h" +#include "theory/rewriterules/theory_rewriterules.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::rewriterules; + + +namespace CVC4 { +namespace theory { +namespace rewriterules { + +void TheoryRewriteRules::computeMatchBody ( const RewriteRule * rule, + size_t start){ + std::vector<TNode> stack(1,rule->new_terms); + + while(!stack.empty()){ + Node t = stack.back(); stack.pop_back(); + + // We don't want to consider variable in t + if( std::find(rule->free_vars.begin(), rule->free_vars.end(), t) + != rule->free_vars.end()) continue; + // t we want to consider only UF function + if( t.getKind() == APPLY_UF ){ + for(size_t rid = start, end = d_rules.size(); rid < end; ++rid) { + RewriteRule * r = d_rules[rid]; + if(r->d_split) continue; + Trigger & tr = const_cast<Trigger &> (r->trigger_for_body_match); + if(!tr.nonunifiable(t, rule->free_vars)){ + rule->body_match.push_back(std::make_pair(t,r)); + } + } + } + + //put the children on the stack + for( size_t i=0; i < t.getNumChildren(); i++ ){ + stack.push_back(t[i]); + }; + + } +} + +inline void addPattern(TheoryRewriteRules & re, + TNode tri, + std::vector<Node> & pattern, + std::vector<Node> & vars, + std::vector<Node> & inst_constants, + TNode r){ + if (tri.getKind() == kind::NOT && tri[0].getKind() == kind::APPLY_UF) + tri = tri[0]; + pattern.push_back(re.getQuantifiersEngine()-> + convertNodeToPattern(tri,r,vars,inst_constants)); +} + +/*** Check that triggers contains all the variables */ +void checkPatternVarsAux(TNode pat,const std::vector<Node> & vars, + std::vector<bool> & seen){ + for(size_t id=0;id < vars.size(); ++id){ + if(pat == vars[id]){ + seen[id]=true; + break; + }; + }; + for(Node::iterator i = pat.begin(); i != pat.end(); ++i) { + checkPatternVarsAux(*i,vars,seen); + }; +} + +bool checkPatternVars(const std::vector<Node> & pattern, + const std::vector<Node> & vars){ + std::vector<bool> seen(vars.size(),false); + for(std::vector<Node>::const_iterator i = pattern.begin(); + i != pattern.end(); ++i) { + checkPatternVarsAux(*i,vars,seen); + }; + return (find(seen.begin(),seen.end(),false) == seen.end()); +} + +/** Main function for construction of RewriteRule */ +void TheoryRewriteRules::addRewriteRule(const Node r) +{ + Assert(r.getKind() == kind::REWRITE_RULE); + /* Replace variables by Inst_* variable and tag the terms that + contain them */ + std::vector<Node> vars; + vars.reserve(r[0].getNumChildren()); + for( Node::const_iterator v = r[0].begin(); v != r[0].end(); ++v ){ + vars.push_back(*v); + }; + /* Instantiation version */ + std::vector<Node> inst_constants = + getQuantifiersEngine()->createInstVariable(vars); + /* Body/Remove_term/Guards/Triggers */ + Node body = r[2][1]; + TNode new_terms = r[2][1]; + std::vector<Node> guards; + std::vector<Node> pattern; + std::vector<Node> to_remove; /* "remove" the terms from the database + when fired */ + /* shortcut */ + TNode head = r[2][0]; + switch(r[2].getKind()){ + case kind::RR_REWRITE: + /* Equality */ + to_remove.push_back(head); + addPattern(*this,head,pattern,vars,inst_constants,r); + body = head.eqNode(body); + break; + case kind::RR_REDUCTION: + /** Add head to remove */ + to_remove.push_back(head); + case kind::RR_DEDUCTION: + /** Add head to guards and pattern */ + switch(head.getKind()){ + case kind::AND: + guards.reserve(head.getNumChildren()); + for(Node::iterator i = head.begin(); i != head.end(); ++i) { + guards.push_back(*i); + addPattern(*this,*i,pattern,vars,inst_constants,r); + }; + break; + default: + if (head != d_true){ + guards.push_back(head); + addPattern(*this,head,pattern,vars,inst_constants,r); + }; + /** otherwise guards is empty */ + }; + break; + default: + Unreachable("RewriteRules can be of only threee kinds"); + }; + /* Add the other guards */ + TNode g = r[1]; + switch(g.getKind()){ + case kind::AND: + guards.reserve(g.getNumChildren()); + for(Node::iterator i = g.begin(); i != g.end(); ++i) { + guards.push_back(*i); + }; + break; + default: + if (g != d_true) guards.push_back(g); + /** otherwise guards is empty */ + }; + /* Add the other triggers */ + if( r[2].getNumChildren() >= 3 ) + for(Node::iterator i = r[2][2].begin(); i != r[2][2].end(); ++i) { + // todo test during typing that its a good term (no not, atom, or term...) + addPattern(*this,(*i)[0],pattern,vars,inst_constants,r); + }; + // Assert(pattern.size() == 1, "currently only single pattern are supported"); + //Every variable must be seen in the pattern + if (!checkPatternVars(pattern,inst_constants)){ + Warning() << "The rule" << r << + " has been removed since it doesn't contain every variables." + << std::endl; + return; + } + + //Add to direct rewrite rule + bool directrr = false; + if(direct_rewrite && + ((guards.size() == 0 && r[2].getKind() == kind::RR_REWRITE) || + (guards.size() == 1 && r[2].getKind() == kind::RR_REDUCTION)) + && pattern.size() == 1){ + directrr = addRewritePattern(pattern[0],new_terms, inst_constants, vars); + } + + // final construction + Trigger trigger = createTrigger(r,pattern); + Trigger trigger2 = createTrigger(r,pattern); //Hack + RewriteRule * rr = new RewriteRule(*this, trigger, trigger2, + guards, body, new_terms, + vars, inst_constants, to_remove, + directrr); + /** other -> rr */ + if(compute_opt && !rr->d_split) computeMatchBody(rr); + d_rules.push_back(rr); + /** rr -> all (including himself) */ + if(compute_opt && !rr->d_split) + for(size_t rid = 0, end = d_rules.size(); rid < end; ++rid) + computeMatchBody(d_rules[rid], + d_rules.size() - 1); + + Debug("rewriterules") << "created rewriterule"<< (rr->d_split?"(split)":"") << ":(" << d_rules.size() - 1 << ")" + << *rr << std::endl; + +} + + +bool willDecide(TNode node, bool positive = true){ + /* TODO something better */ + switch(node.getKind()) { + case AND: + return !positive; + case OR: + return positive; + case IFF: + return true; + case XOR: + return true; + case IMPLIES: + return false; + case ITE: + return true; + case NOT: + return willDecide(node[0],!positive); + default: + return false; + } +} + + +RewriteRule::RewriteRule(TheoryRewriteRules & re, + Trigger & tr, Trigger & tr2, + std::vector<Node> & g, Node b, TNode nt, + std::vector<Node> & fv,std::vector<Node> & iv, + std::vector<Node> & to_r, bool drr) : + d_split(willDecide(b)), + trigger(tr), body(b), new_terms(nt), free_vars(), inst_vars(), + body_match(re.getSatContext()),trigger_for_body_match(tr2), + d_cache(re.getSatContext(),re.getQuantifiersEngine()), directrr(drr){ + free_vars.swap(fv); inst_vars.swap(iv); guards.swap(g); to_remove.swap(to_r); +}; + + +bool RewriteRule::inCache(TheoryRewriteRules & re, InstMatch & im)const{ + return !d_cache.addInstMatch(im); +}; + +/** A rewrite rule */ +void RewriteRule::toStream(std::ostream& out) const{ + for(std::vector<Node>::const_iterator + iter = guards.begin(); iter != guards.end(); ++iter){ + out << *iter; + }; + out << "=>" << body << std::endl; + out << "["; + for(BodyMatch::const_iterator + iter = body_match.begin(); iter != body_match.end(); ++iter){ + out << (*iter).first << "(" << (*iter).second << ")" << ","; + }; + out << "]" << (directrr?"*":"") << std::endl; +} + +RewriteRule::~RewriteRule(){ + Debug("rewriterule-stats") << *this + << " (" << nb_matched + << "," << nb_applied + << "," << nb_propagated + << ")" << std::endl; +} + +bool TheoryRewriteRules::addRewritePattern(TNode pattern, TNode body, + rewriter::Subst & pvars, + rewriter::Subst & vars){ + Assert(pattern.getKind() == kind::APPLY_UF); + TNode op = pattern.getOperator(); + TheoryRewriteRules::RegisterRRPpRewrite::iterator i = + d_registeredRRPpRewrite.find(op); + + rewriter::RRPpRewrite * p; + if (i == d_registeredRRPpRewrite.end()){ + p = new rewriter::RRPpRewrite(); + d_registeredRRPpRewrite.insert(std::make_pair(op,p)); + ((uf::TheoryUF*)getQuantifiersEngine()->getTheoryEngine()->getTheory( THEORY_UF ))-> + registerPpRewrite(op,p); + } else p = i->second; + + return p->addRewritePattern(pattern,body,pvars,vars); + +} + + +}/* CVC4::theory::rewriterules namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/rewriterules/theory_rewriterules_rules.h b/src/theory/rewriterules/theory_rewriterules_rules.h new file mode 100644 index 000000000..a8e70f3e6 --- /dev/null +++ b/src/theory/rewriterules/theory_rewriterules_rules.h @@ -0,0 +1,40 @@ +/********************* */ +/*! \file rewrite_engine.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: bobot + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 + ** The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Rewrite Engine classes + **/ + + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_RULES_H +#define __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_RULES_H + +#include "theory/rewriterules/theory_rewriterules.h" +#include "theory/rewriterules/theory_rewriterules_params.h" + +namespace CVC4 { +namespace theory { +namespace rewriterules { + +inline std::ostream& operator <<(std::ostream& stream, const RewriteRule& r) { + r.toStream(stream); + return stream; +} + +}/* CVC4::theory::rewriterules namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_RULES_H */ diff --git a/src/theory/rewriterules/theory_rewriterules_type_rules.h b/src/theory/rewriterules/theory_rewriterules_type_rules.h new file mode 100644 index 000000000..5a0e8c5e0 --- /dev/null +++ b/src/theory/rewriterules/theory_rewriterules_type_rules.h @@ -0,0 +1,98 @@ +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_TYPE_RULES_H +#define __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_TYPE_RULES_H + +#include "node_manager.h" + +namespace CVC4 { +namespace theory { +namespace rewriterules { + +class RewriteRuleTypeRule { +public: + + /** + * Compute the type for (and optionally typecheck) a term belonging + * to the theory of rewriterules. + * + * @param check if true, the node's type should be checked as well + * as computed. + */ + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, + bool check) + throw(TypeCheckingExceptionPrivate) { + Debug("typecheck-r") << "type check for rr " << n << std::endl; + Assert(n.getKind() == kind::REWRITE_RULE && n.getNumChildren()==3 ); + if( check ){ + if( n[ 0 ].getType(check)!=nodeManager->boundVarListType() ){ + throw TypeCheckingExceptionPrivate(n, + "first argument of rewrite rule is not bound var list"); + } + if( n[ 1 ].getType(check)!=nodeManager->booleanType() ){ + throw TypeCheckingExceptionPrivate(n, + "guard of rewrite rule is not an actual guard"); + } + if( n[2].getType(check) != + TypeNode(nodeManager->mkTypeConst<TypeConstant>(RRHB_TYPE))){ + throw TypeCheckingExceptionPrivate(n, + "not a correct rewrite rule"); + } + } + return nodeManager->booleanType(); + } +};/* class RewriterulesTypeRule */ + + +class RRRewriteTypeRule { +public: + + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw(TypeCheckingExceptionPrivate) { + Assert(n.getKind() == kind::RR_REWRITE ); + if( check ){ + if( n[0].getType(check)!=n[1].getType(check) ){ + throw TypeCheckingExceptionPrivate(n, + "terms of rewrite rule are not equal"); + } + if( n.getNumChildren() == 3 && n[2].getType(check)!=nodeManager->instPatternListType() ){ + throw TypeCheckingExceptionPrivate(n, "third argument of rewrite rule is not instantiation pattern list"); + } + if( n[0].getKind()!=kind::APPLY_UF ){ + throw TypeCheckingExceptionPrivate(n[0], "head of rewrite rules must start with an uninterpreted symbols. If you want to write a propagation rule, add the guard [true] for disambiguation"); + } + } + return TypeNode(nodeManager->mkTypeConst<TypeConstant>(RRHB_TYPE)); + } +};/* struct QuantifierReductionRuleRule */ + +class RRRedDedTypeRule { +public: + + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw(TypeCheckingExceptionPrivate) { + Assert(n.getKind() == kind::RR_REDUCTION || + n.getKind() == kind::RR_DEDUCTION ); + if( check ){ + if( n[ 0 ].getType(check)!=nodeManager->booleanType() ){ + throw TypeCheckingExceptionPrivate(n, "head of reduction rule is not boolean"); + } + if( n[ 1 ].getType(check)!=nodeManager->booleanType() ){ + throw TypeCheckingExceptionPrivate(n, "body of reduction rule is not boolean"); + } + if( n.getNumChildren() == 3 && n[2].getType(check)!=nodeManager->instPatternListType() ){ + throw TypeCheckingExceptionPrivate(n, "third argument of rewrite rule is not instantiation pattern list"); + } + if( n.getNumChildren() < 3 && n[ 0 ] == nodeManager->mkConst<bool>(true) ){ + throw TypeCheckingExceptionPrivate(n, "A rewrite rule must have one head or one trigger at least"); + } + } + return TypeNode(nodeManager->mkTypeConst<TypeConstant>(RRHB_TYPE)); + } +};/* struct QuantifierReductionRuleRule */ + +}/* CVC4::theory::rewriterules namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_TYPE_RULES_H */ diff --git a/src/theory/shared_terms_database.cpp b/src/theory/shared_terms_database.cpp index b081e27ef..98ab3f10d 100644 --- a/src/theory/shared_terms_database.cpp +++ b/src/theory/shared_terms_database.cpp @@ -52,7 +52,7 @@ void SharedTermsDatabase::addEqualityToPropagate(TNode equality) { void SharedTermsDatabase::addSharedTerm(TNode atom, TNode term, Theory::Set theories) { - Debug("register") << "SharedTermsDatabase::addSharedTerm(" << atom << ", " << term << ", " << Theory::setToString(theories) << ")" << std::endl; + Debug("register") << "SharedTermsDatabase::addSharedTerm(" << atom << ", " << term << ", " << Theory::setToString(theories) << ")" << std::endl; std::pair<TNode, TNode> search_pair(atom, term); SharedTermsTheoriesMap::iterator find = d_termsToTheories.find(search_pair); @@ -64,18 +64,18 @@ void SharedTermsDatabase::addSharedTerm(TNode atom, TNode term, Theory::Set theo d_termsToTheories[search_pair] = theories; } else { Assert(theories != (*find).second); - d_termsToTheories[search_pair] = Theory::setUnion(theories, (*find).second); + d_termsToTheories[search_pair] = Theory::setUnion(theories, (*find).second); } } SharedTermsDatabase::shared_terms_iterator SharedTermsDatabase::begin(TNode atom) const { Assert(hasSharedTerms(atom)); - return d_atomsToTerms.find(atom)->second.begin(); + return d_atomsToTerms.find(atom)->second.begin(); } SharedTermsDatabase::shared_terms_iterator SharedTermsDatabase::end(TNode atom) const { Assert(hasSharedTerms(atom)); - return d_atomsToTerms.find(atom)->second.end(); + return d_atomsToTerms.find(atom)->second.end(); } bool SharedTermsDatabase::hasSharedTerms(TNode atom) const { @@ -89,24 +89,24 @@ void SharedTermsDatabase::backtrack() { list.pop_back(); if (list.empty()) { d_atomsToTerms.erase(atom); - } + } } d_addedSharedTerms.resize(d_addedSharedTermsSize); } Theory::Set SharedTermsDatabase::getTheoriesToNotify(TNode atom, TNode term) const { - // Get the theories that share this term from this atom + // Get the theories that share this term from this atom std::pair<TNode, TNode> search_pair(atom, term); SharedTermsTheoriesMap::iterator find = d_termsToTheories.find(search_pair); - Assert(find != d_termsToTheories.end()); - + Assert(find != d_termsToTheories.end()); + // Get the theories that were already notified Theory::Set alreadyNotified = 0; AlreadyNotifiedMap::iterator theoriesFind = d_alreadyNotifiedMap.find(term); if (theoriesFind != d_alreadyNotifiedMap.end()) { alreadyNotified = (*theoriesFind).second; } - + // Return the ones that haven't been notified yet return Theory::setDifference((*find).second, alreadyNotified); } diff --git a/src/theory/shared_terms_database.h b/src/theory/shared_terms_database.h index fccd2e6bc..c7da8a463 100644 --- a/src/theory/shared_terms_database.h +++ b/src/theory/shared_terms_database.h @@ -2,7 +2,7 @@ /*! \file node_visitor.h ** \verbatim ** Original author: dejan - ** Major contributors: + ** Major contributors: ** Minor contributors (to current version): ** This file is part of the CVC4 prototype. ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) @@ -40,7 +40,7 @@ private: /** The context */ context::Context* d_context; - + /** Some statistics */ IntStat d_statSharedTerms; @@ -49,13 +49,13 @@ private: /** A map from atoms to a list of shared terms */ SharedTermsMap d_atomsToTerms; - + /** Each time we add a shared term, we add it's parent to this list */ std::vector<TNode> d_addedSharedTerms; - + /** Context-dependent size of the d_addedSharedTerms list */ context::CDO<unsigned> d_addedSharedTermsSize; - + /** A map from atoms and subterms to the theories that use it */ typedef context::CDHashMap<std::pair<Node, TNode>, theory::Theory::Set, TNodePairHashFunction> SharedTermsTheoriesMap; SharedTermsTheoriesMap d_termsToTheories; @@ -95,6 +95,11 @@ private: void eqNotifyConstantTermMerge(TNode t1, TNode t2) { d_sharedTerms.conflict(t1, t2, true); } + + void eqNotifyNewClass(TNode t) { } + void eqNotifyPreMerge(TNode t1, TNode t2) { } + void eqNotifyPostMerge(TNode t1, TNode t2) { } + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { } }; /** The notify class for d_equalityEngine */ @@ -147,7 +152,7 @@ public: SharedTermsDatabase(TheoryEngine* theoryEngine, context::Context* context); ~SharedTermsDatabase() throw(AssertionException); - + /** * Asserts the equality to the shared terms database, */ @@ -185,12 +190,12 @@ public: bool hasSharedTerms(TNode atom) const; /** - * Iterator pointing to the first shared term belonging to the given atom. + * Iterator pointing to the first shared term belonging to the given atom. */ shared_terms_iterator begin(TNode atom) const; /** - * Iterator pointing to the end of the list of shared terms belonging to the given atom. + * Iterator pointing to the end of the list of shared terms belonging to the given atom. */ shared_terms_iterator end(TNode atom) const; diff --git a/src/theory/term_registration_visitor.cpp b/src/theory/term_registration_visitor.cpp index 22b87c32f..b0b712356 100644 --- a/src/theory/term_registration_visitor.cpp +++ b/src/theory/term_registration_visitor.cpp @@ -2,10 +2,10 @@ /*! \file term_registration_visitor.h ** \verbatim ** Original author: dejan - ** Major contributors: - ** Minor contributors (to current version): + ** Major contributors: none + ** Minor contributors (to current version): none ** This file is part of the CVC4 prototype. - ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Copyright (c) 2009-2012 The Analysis of Computer Systems Group (ACSys) ** Courant Institute of Mathematical Sciences ** New York University ** See the file COPYING in the top-level source directory for licensing @@ -32,6 +32,14 @@ bool PreRegisterVisitor::alreadyVisited(TNode current, TNode parent) { Debug("register::internal") << "PreRegisterVisitor::alreadyVisited(" << current << "," << parent << ")" << std::endl; + if( ( parent.getKind() == kind::FORALL || + parent.getKind() == kind::EXISTS || + parent.getKind() == kind::REWRITE_RULE ) && + current != parent ) { + Debug("register::internal") << "quantifier:true" << std::endl; + return true; + } + TheoryId currentTheoryId = Theory::theoryOf(current); TheoryId parentTheoryId = Theory::theoryOf(parent); @@ -89,13 +97,21 @@ void PreRegisterVisitor::visit(TNode current, TNode parent) { if (!Theory::setContains(currentTheoryId, visitedTheories)) { visitedTheories = Theory::setInsert(currentTheoryId, visitedTheories); d_visited[current] = visitedTheories; - d_engine->theoryOf(currentTheoryId)->preRegisterTerm(current); + Theory* th = d_engine->theoryOf(currentTheoryId); + th->preRegisterTerm(current); + if(th->getInstantiator() != NULL) { + th->getInstantiator()->preRegisterTerm(current); + } Debug("register::internal") << "PreRegisterVisitor::visit(" << current << "," << parent << "): adding " << currentTheoryId << std::endl; } if (!Theory::setContains(parentTheoryId, visitedTheories)) { visitedTheories = Theory::setInsert(parentTheoryId, visitedTheories); d_visited[current] = visitedTheories; - d_engine->theoryOf(parentTheoryId)->preRegisterTerm(current); + Theory* th = d_engine->theoryOf(parentTheoryId); + th->preRegisterTerm(current); + if(th->getInstantiator() != NULL) { + th->getInstantiator()->preRegisterTerm(current); + } Debug("register::internal") << "PreRegisterVisitor::visit(" << current << "," << parent << "): adding " << parentTheoryId << std::endl; } if (useType) { @@ -103,7 +119,11 @@ void PreRegisterVisitor::visit(TNode current, TNode parent) { if (!Theory::setContains(typeTheoryId, visitedTheories)) { visitedTheories = Theory::setInsert(typeTheoryId, visitedTheories); d_visited[current] = visitedTheories; - d_engine->theoryOf(typeTheoryId)->preRegisterTerm(current); + Theory* th = d_engine->theoryOf(typeTheoryId); + th->preRegisterTerm(current); + if(th->getInstantiator() != NULL) { + th->getInstantiator()->preRegisterTerm(current); + } Debug("register::internal") << "PreRegisterVisitor::visit(" << current << "," << parent << "): adding " << parentTheoryId << std::endl; } } @@ -135,6 +155,13 @@ bool SharedTermsVisitor::alreadyVisited(TNode current, TNode parent) const { Debug("register::internal") << "SharedTermsVisitor::alreadyVisited(" << current << "," << parent << ")" << std::endl; + if( ( parent.getKind() == kind::FORALL || + parent.getKind() == kind::EXISTS || + parent.getKind() == kind::REWRITE_RULE) && + current != parent ) { + Debug("register::internal") << "quantifier:true" << std::endl; + return true; + } TNodeVisitedMap::const_iterator find = d_visited.find(current); // If node is not visited at all, just return false diff --git a/src/theory/theory.cpp b/src/theory/theory.cpp index 1ed1f99ff..7555282d8 100644 --- a/src/theory/theory.cpp +++ b/src/theory/theory.cpp @@ -18,6 +18,8 @@ #include "theory/theory.h" #include "util/Assert.h" +#include "theory/quantifiers_engine.h" +#include "theory/instantiator_default.h" #include <vector> @@ -37,21 +39,32 @@ std::ostream& operator<<(std::ostream& os, Theory::Effort level){ os << "EFFORT_FULL"; break; case Theory::EFFORT_COMBINATION: os << "EFFORT_COMBINATION"; break; + case Theory::EFFORT_LAST_CALL: + os << "EFFORT_LAST_CALL"; break; default: Unreachable(); } return os; }/* ostream& operator<<(ostream&, Theory::Effort) */ +Theory::~Theory() { + if(d_inst != NULL) { + delete d_inst; + d_inst = NULL; + } + + StatisticsRegistry::unregisterStat(&d_computeCareGraphTime); +} + void Theory::addSharedTermInternal(TNode n) { - Debug("sharing") << "Theory::addSharedTerm<" << getId() << ">(" << n << ")" << std::endl; - Debug("theory::assertions") << "Theory::addSharedTerm<" << getId() << ">(" << n << ")" << std::endl; + Debug("sharing") << "Theory::addSharedTerm<" << getId() << ">(" << n << ")" << endl; + Debug("theory::assertions") << "Theory::addSharedTerm<" << getId() << ">(" << n << ")" << endl; d_sharedTerms.push_back(n); addSharedTerm(n); } void Theory::computeCareGraph() { - Debug("sharing") << "Theory::computeCareGraph<" << getId() << ">()" << std::endl; + Debug("sharing") << "Theory::computeCareGraph<" << getId() << ">()" << endl; for (unsigned i = 0; i < d_sharedTerms.size(); ++ i) { TNode a = d_sharedTerms[i]; TypeNode aType = a.getType(); @@ -71,7 +84,7 @@ void Theory::computeCareGraph() { addCarePair(a, b); break; } - } + } } } @@ -85,8 +98,97 @@ void Theory::printFacts(std::ostream& os) const { } void Theory::debugPrintFacts() const{ - cout << "Theory::debugPrintFacts()" << endl; - printFacts(cout); + DebugChannel.getStream() << "Theory::debugPrintFacts()" << endl; + printFacts(DebugChannel.getStream()); +} + +Instantiator::Instantiator(context::Context* c, QuantifiersEngine* qe, Theory* th) : + d_quantEngine(qe), + d_th(th) { +} + +Instantiator::~Instantiator() { +} + +void Instantiator::resetInstantiationRound(Theory::Effort effort) { + for(int i = 0; i < (int) d_instStrategies.size(); ++i) { + if(isActiveStrategy(d_instStrategies[i])) { + d_instStrategies[i]->resetInstantiationRound(effort); + } + } + processResetInstantiationRound(effort); +} + +int Instantiator::doInstantiation(Node f, Theory::Effort effort, int e, int limitInst) { + if(hasConstraintsFrom(f)) { + int origLemmas = d_quantEngine->getNumLemmasWaiting(); + int status = process(f, effort, e, limitInst); + if(limitInst <= 0 || (d_quantEngine->getNumLemmasWaiting()-origLemmas) < limitInst) { + if(d_instStrategies.empty()) { + Debug("inst-engine-inst") << "There are no instantiation strategies allocated." << endl; + } else { + for(int i = 0; i < (int) d_instStrategies.size(); ++i) { + if(isActiveStrategy(d_instStrategies[i])) { + Debug("inst-engine-inst") << d_instStrategies[i]->identify() << " process " << effort << endl; + //call the instantiation strategy's process method + int s_limitInst = limitInst > 0 ? limitInst - (d_quantEngine->getNumLemmasWaiting() - origLemmas) : 0; + int s_status = d_instStrategies[i]->doInstantiation(f, effort, e, s_limitInst); + Debug("inst-engine-inst") << " -> status is " << s_status << endl; + if(limitInst > 0 && (d_quantEngine->getNumLemmasWaiting() - origLemmas) >= limitInst) { + Assert( (d_quantEngine->getNumLemmasWaiting() - origLemmas) == limitInst ); + i = (int) d_instStrategies.size(); + status = InstStrategy::STATUS_UNKNOWN; + } else { + InstStrategy::updateStatus(status, s_status); + } + } else { + Debug("inst-engine-inst") << d_instStrategies[i]->identify() << " is not active." << endl; + } + } + } + } + return status; + } else { + Debug("inst-engine-inst") << "We have no constraints from this quantifier." << endl; + return InstStrategy::STATUS_SAT; + } +} + +//void Instantiator::doInstantiation(int effort) { +// d_status = InstStrategy::STATUS_SAT; +// for( int q = 0; q < d_quantEngine->getNumQuantifiers(); ++q ) { +// Node f = d_quantEngine->getQuantifier(q); +// if( d_quantEngine->getActive(f) && hasConstraintsFrom(f) ) { +// int d_quantStatus = process( f, effort ); +// InstStrategy::updateStatus( d_status, d_quantStatus ); +// for( int i = 0; i < (int)d_instStrategies.size(); ++i ) { +// if( isActiveStrategy( d_instStrategies[i] ) ) { +// Debug("inst-engine-inst") << d_instStrategies[i]->identify() << " process " << effort << endl; +// //call the instantiation strategy's process method +// d_quantStatus = d_instStrategies[i]->process( f, effort ); +// Debug("inst-engine-inst") << " -> status is " << d_quantStatus << endl; +// InstStrategy::updateStatus( d_status, d_quantStatus ); +// } +// } +// } +// } +//} + +void Instantiator::setHasConstraintsFrom(Node f) { + d_hasConstraints[f] = true; + if(! d_quantEngine->hasOwner(f)) { + d_quantEngine->setOwner(f, getTheory()); + } else if(d_quantEngine->getOwner(f) != getTheory()) { + d_quantEngine->setOwner(f, NULL); + } +} + +bool Instantiator::hasConstraintsFrom(Node f) { + return d_hasConstraints.find(f) != d_hasConstraints.end() && d_hasConstraints[f]; +} + +bool Instantiator::isOwnerOf(Node f) { + return d_quantEngine->hasOwner(f) && d_quantEngine->getOwner(f) == getTheory(); } }/* CVC4::theory namespace */ diff --git a/src/theory/theory.h b/src/theory/theory.h index 020a2b194..8c830f8a2 100644 --- a/src/theory/theory.h +++ b/src/theory/theory.h @@ -46,6 +46,10 @@ class TheoryEngine; namespace theory { +class Instantiator; +class InstStrategy; +class QuantifiersEngine; + /** * Information about an assertion for the theories. */ @@ -72,7 +76,8 @@ struct Assertion { operator Node () const { return assertion; } -}; + +};/* struct Assertion */ /** * A (oredered) pair of terms a theory cares about. @@ -84,7 +89,7 @@ struct CarePair { public: - CarePair(TNode a, TNode b, TheoryId theory) + CarePair(TNode a, TNode b, TheoryId theory) : a(a < b ? a : b), b(a < b ? b : a), theory(theory) {} bool operator == (const CarePair& other) const { @@ -99,7 +104,7 @@ public: return b < other.b; } -}; +};/* struct CarePair */ /** * A set of care pairs. @@ -172,6 +177,18 @@ private: */ CareGraph* d_careGraph; + /** + * Reference to the quantifiers engine (or NULL, if quantifiers are + * not supported or not enabled). + */ + QuantifiersEngine* d_quantEngine; + + /** + * The instantiator for this theory, or NULL if quantifiers are not + * supported or not enabled. + */ + Instantiator* d_inst; + // === STATISTICS === /** time spent in theory combination */ TimerStat d_computeCareGraphTime; @@ -182,6 +199,12 @@ private: return ss.str(); } + /** + * Construct and return the instantiator for the given theory. + * If there is no instantiator class, NULL is returned. + */ + theory::Instantiator* makeInstantiator(context::Context* c, theory::QuantifiersEngine* qe); + protected: /** @@ -208,7 +231,7 @@ protected: * Construct a Theory. */ Theory(TheoryId id, context::Context* satContext, context::UserContext* userContext, - OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) throw() + OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) throw() : d_id(id) , d_satContext(satContext) , d_userContext(userContext) @@ -217,6 +240,8 @@ protected: , d_factsHead(satContext, 0) , d_sharedTermsIndex(satContext, 0) , d_careGraph(0) + , d_quantEngine(qe) + , d_inst(makeInstantiator(satContext, qe)) , d_computeCareGraphTime(statName(id, "computeCareGraphTime")) , d_sharedTerms(satContext) , d_out(&out) @@ -252,19 +277,7 @@ protected: * * @return the next assertion in the assertFact() queue */ - Assertion get() { - Assert( !done(), "Theory`() called with assertion queue empty!" ); - - // Get the assertion - Assertion fact = d_facts[d_factsHead]; - d_factsHead = d_factsHead + 1; - Trace("theory") << "Theory::get() => " << fact << " (" << d_facts.size() - d_factsHead << " left)" << std::endl; - if(Dump.isOn("state")) { - Dump("state") << AssertCommand(fact.assertion.toExpr()); - } - - return fact; - } + inline Assertion get(); const LogicInfo& getLogicInfo() const { return d_logicInfo; @@ -348,13 +361,9 @@ public: } /** - * Destructs a Theory. This implementation does nothing, but we - * need a virtual destructor for safety in case subclasses have a - * destructor. + * Destructs a Theory. */ - virtual ~Theory() { - StatisticsRegistry::unregisterStat(&d_computeCareGraphTime); - } + virtual ~Theory(); /** * Subclasses of Theory may add additional efforts. DO NOT CHECK @@ -376,7 +385,11 @@ public: * Combination effort means that the individual theories are already satisfied, and * it is time to put some effort into propagation of shared term equalities */ - EFFORT_COMBINATION = 150 + EFFORT_COMBINATION = 150, + /** + * Last call effort, reserved for quantifiers. + */ + EFFORT_LAST_CALL = 200 };/* enum Effort */ static inline bool standardEffortOrMore(Effort e) CVC4_CONST_FUNCTION @@ -385,7 +398,7 @@ public: { return e >= EFFORT_STANDARD && e < EFFORT_FULL; } static inline bool fullEffort(Effort e) CVC4_CONST_FUNCTION { return e == EFFORT_FULL; } - static inline bool combination(Effort e) CVC4_CONST_FUNCTION + static inline bool combination(Effort e) CVC4_CONST_FUNCTION { return e == EFFORT_COMBINATION; } /** @@ -424,6 +437,41 @@ public: } /** + * Get the valuation associated to this theory. + */ + Valuation& getValuation() { + return d_valuation; + } + + /** + * Get the quantifiers engine associated to this theory. + */ + QuantifiersEngine* getQuantifiersEngine() { + return d_quantEngine; + } + + /** + * Get the quantifiers engine associated to this theory (const version). + */ + const QuantifiersEngine* getQuantifiersEngine() const { + return d_quantEngine; + } + + /** + * Get the theory instantiator. + */ + Instantiator* getInstantiator() { + return d_inst; + } + + /** + * Get the theory instantiator (const version). + */ + const Instantiator* getInstantiator() const { + return d_inst; + } + + /** * Pre-register a term. Done one time for a Node, ever. */ virtual void preRegisterTerm(TNode) { } @@ -455,7 +503,7 @@ public: } /** - * Return the status of two terms in the current context. Should be implemented in + * Return the status of two terms in the current context. Should be implemented in * sub-theories to enable more efficient theory-combination. */ virtual EqualityStatus getEqualityStatus(TNode a, TNode b) { return EQUALITY_UNKNOWN; } @@ -573,6 +621,11 @@ public: virtual Node ppRewrite(TNode atom) { return atom; } /** + * Don't preprocess subterm of this term + */ + virtual bool ppDontRewriteSubterm(TNode atom) { return false; } + + /** * A Theory is called with presolve exactly one time per user * check-sat. presolve() is called after preregistration, * rewriting, and Boolean propagation, (other theories' @@ -664,7 +717,7 @@ public: static inline Set setIntersection(Set a, Set b) { return a & b; - } + } static inline Set setUnion(Set a, Set b) { return a | b; @@ -735,6 +788,84 @@ public: std::ostream& operator<<(std::ostream& os, Theory::Effort level); +class Instantiator { + friend class QuantifiersEngine; +protected: + /** reference to the quantifiers engine */ + QuantifiersEngine* d_quantEngine; + /** reference to the theory that it looks at */ + Theory* d_th; + /** instantiation strategies */ + std::vector< InstStrategy* > d_instStrategies; + /** instantiation strategies active */ + std::map< InstStrategy*, bool > d_instStrategyActive; + /** has constraints from quantifier */ + std::map< Node, bool > d_hasConstraints; + /** is instantiation strategy active */ + bool isActiveStrategy( InstStrategy* is ) { + return d_instStrategyActive.find( is )!=d_instStrategyActive.end() && d_instStrategyActive[is]; + } + /** add inst strategy */ + void addInstStrategy( InstStrategy* is ){ + d_instStrategies.push_back( is ); + d_instStrategyActive[is] = true; + } + /** reset instantiation round */ + virtual void processResetInstantiationRound( Theory::Effort effort ) = 0; + /** process quantifier */ + virtual int process( Node f, Theory::Effort effort, int e, int limitInst = 0 ) = 0; +public: + /** set has constraints from quantifier f */ + void setHasConstraintsFrom( Node f ); + /** has constraints from */ + bool hasConstraintsFrom( Node f ); + /** is full owner of quantifier f? */ + bool isOwnerOf( Node f ); +public: + Instantiator(context::Context* c, QuantifiersEngine* qe, Theory* th); + virtual ~Instantiator(); + + /** get quantifiers engine */ + QuantifiersEngine* getQuantifiersEngine() { return d_quantEngine; } + /** get corresponding theory for this instantiator */ + Theory* getTheory() { return d_th; } + /** Pre-register a term. */ + virtual void preRegisterTerm( Node t ) { } + /** assertNode function, assertion was asserted to theory */ + virtual void assertNode( Node assertion ){} + /** reset instantiation round */ + void resetInstantiationRound( Theory::Effort effort ); + /** do instantiation method*/ + int doInstantiation( Node f, Theory::Effort effort, int e, int limitInst = 0 ); + /** identify */ + virtual std::string identify() const { return std::string("Unknown"); } + /** print debug information */ + virtual void debugPrint( const char* c ) {} + /** get status */ + //int getStatus() { return d_status; } +};/* class Instantiator */ + +inline Assertion Theory::get() { + Assert( !done(), "Theory::get() called with assertion queue empty!" ); + + // Get the assertion + Assertion fact = d_facts[d_factsHead]; + d_factsHead = d_factsHead + 1; + + Trace("theory") << "Theory::get() => " << fact << " (" << d_facts.size() - d_factsHead << " left)" << std::endl; + + if(Dump.isOn("state")) { + Dump("state") << AssertCommand(fact.assertion.toExpr()); + } + + // if quantifiers are turned on and we have an instantiator, notify it + if(getLogicInfo().isQuantified() && getInstantiator() != NULL) { + getInstantiator()->assertNode(fact); + } + + return fact; +} + }/* CVC4::theory namespace */ inline std::ostream& operator<<(std::ostream& out, diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp index 97c17222c..7ea9e063e 100644 --- a/src/theory/theory_engine.cpp +++ b/src/theory/theory_engine.cpp @@ -32,6 +32,9 @@ #include "util/node_visitor.h" #include "util/ite_removal.h" +#include "theory/quantifiers_engine.h" +#include "theory/quantifiers/theory_quantifiers.h" + using namespace std; using namespace CVC4; @@ -46,6 +49,7 @@ TheoryEngine::TheoryEngine(context::Context* context, d_userContext(userContext), d_logicInfo(logicInfo), d_sharedTerms(this, context), + d_quantEngine(NULL), d_ppCache(), d_possiblePropagations(context), d_hasPropagated(context), @@ -69,6 +73,10 @@ TheoryEngine::TheoryEngine(context::Context* context, d_theoryTable[theoryId] = NULL; d_theoryOut[theoryId] = NULL; } + + // initialize the quantifiers engine + d_quantEngine = new QuantifiersEngine(context, this); + Rewriter::init(); StatisticsRegistry::registerStat(&d_combineTheoriesTime); d_true = NodeManager::currentNM()->mkConst<bool>(true); @@ -85,6 +93,8 @@ TheoryEngine::~TheoryEngine() { } } + delete d_quantEngine; + StatisticsRegistry::unregisterStat(&d_combineTheoriesTime); } @@ -320,6 +330,22 @@ void TheoryEngine::check(Theory::Effort effort) { } } + // Must consult quantifiers theory for last call to ensure sat, or otherwise add a lemma + if( effort == Theory::EFFORT_FULL && + d_logicInfo.isQuantified() && + ! d_inConflict && + ! d_lemmasAdded ) { + ((theory::quantifiers::TheoryQuantifiers*) d_theoryTable[THEORY_QUANTIFIERS])->performCheck(Theory::EFFORT_LAST_CALL); + // if we have given up, then possibly flip decision + if(Options::current()->flipDecision) { + if(d_incomplete && !d_inConflict && !d_lemmasAdded) { + if( ((theory::quantifiers::TheoryQuantifiers*) d_theoryTable[THEORY_QUANTIFIERS])->flipDecision() ) { + d_incomplete = false; + } + } + } + } + Debug("theory") << "TheoryEngine::check(" << effort << "): done, we are " << (d_inConflict ? "unsat" : "sat") << (d_lemmasAdded ? " with new lemmas" : " with no new lemmas") << std::endl; } catch(const theory::Interrupted&) { @@ -596,7 +622,7 @@ void TheoryEngine::shutdown() { // Shutdown all the theories for(TheoryId theoryId = theory::THEORY_FIRST; theoryId < theory::THEORY_LAST; ++theoryId) { if(d_theoryTable[theoryId]) { - theoryOf(static_cast<TheoryId>(theoryId))->shutdown(); + theoryOf(theoryId)->shutdown(); } } @@ -624,15 +650,21 @@ Node TheoryEngine::ppTheoryRewrite(TNode term) return theoryOf(term)->ppRewrite(term); } Trace("theory-pp") << "ppTheoryRewrite { " << term << endl; - NodeBuilder<> newNode(term.getKind()); - if (term.getMetaKind() == kind::metakind::PARAMETERIZED) { - newNode << term.getOperator(); - } - unsigned i; - for (i = 0; i < nc; ++i) { - newNode << ppTheoryRewrite(term[i]); + + Node newTerm; + if (theoryOf(term)->ppDontRewriteSubterm(term)) { + newTerm = term; + } else { + NodeBuilder<> newNode(term.getKind()); + if (term.getMetaKind() == kind::metakind::PARAMETERIZED) { + newNode << term.getOperator(); + } + unsigned i; + for (i = 0; i < nc; ++i) { + newNode << ppTheoryRewrite(term[i]); + } + newTerm = Rewriter::rewrite(Node(newNode)); } - Node newTerm = Rewriter::rewrite(Node(newNode)); Node newTerm2 = theoryOf(newTerm)->ppRewrite(newTerm); if (newTerm != newTerm2) { newTerm = ppTheoryRewrite(Rewriter::rewrite(newTerm2)); diff --git a/src/theory/theory_engine.h b/src/theory/theory_engine.h index ed95149b3..ff7648843 100644 --- a/src/theory/theory_engine.h +++ b/src/theory/theory_engine.h @@ -72,6 +72,10 @@ struct NodeTheoryPairHashFunction { } };/* struct NodeTheoryPairHashFunction */ +namespace theory { + class Instantiator; +}/* CVC4::theory namespace */ + /** * This is essentially an abstraction for a collection of theories. A * TheoryEngine provides services to a PropEngine, making various @@ -115,6 +119,11 @@ class TheoryEngine { */ SharedTermsDatabase d_sharedTerms; + /** + * The quantifiers engine + */ + theory::QuantifiersEngine* d_quantEngine; + typedef std::hash_map<Node, Node, NodeHashFunction> NodeMap; typedef std::hash_map<TNode, Node, TNodeHashFunction> TNodeMap; @@ -151,18 +160,22 @@ class TheoryEngine { public: - IntStat conflicts, propagations, lemmas, propagationsAsDecisions; + IntStat conflicts, propagations, lemmas, propagationsAsDecisions, requirePhase, flipDecision; Statistics(theory::TheoryId theory): conflicts(mkName("theory<", theory, ">::conflicts"), 0), propagations(mkName("theory<", theory, ">::propagations"), 0), lemmas(mkName("theory<", theory, ">::lemmas"), 0), - propagationsAsDecisions(mkName("theory<", theory, ">::propagationsAsDecisions"), 0) + propagationsAsDecisions(mkName("theory<", theory, ">::propagationsAsDecisions"), 0), + requirePhase(mkName("theory<", theory, ">::requirePhase"), 0), + flipDecision(mkName("theory<", theory, ">::flipDecision"), 0) { StatisticsRegistry::registerStat(&conflicts); StatisticsRegistry::registerStat(&propagations); StatisticsRegistry::registerStat(&lemmas); StatisticsRegistry::registerStat(&propagationsAsDecisions); + StatisticsRegistry::registerStat(&requirePhase); + StatisticsRegistry::registerStat(&flipDecision); } ~Statistics() { @@ -170,6 +183,8 @@ class TheoryEngine { StatisticsRegistry::unregisterStat(&propagations); StatisticsRegistry::unregisterStat(&lemmas); StatisticsRegistry::unregisterStat(&propagationsAsDecisions); + StatisticsRegistry::unregisterStat(&requirePhase); + StatisticsRegistry::unregisterStat(&flipDecision); } };/* class TheoryEngine::Statistics */ @@ -234,6 +249,21 @@ class TheoryEngine { return d_engine->lemma(lemma, false, removable); } + void requirePhase(TNode n, bool phase) + throw(theory::Interrupted, AssertionException) { + Debug("theory") << "EngineOutputChannel::requirePhase(" + << n << ", " << phase << ")" << std::endl; + ++ d_statistics.requirePhase; + d_engine->d_propEngine->requirePhase(n, phase); + } + + bool flipDecision() + throw(theory::Interrupted, AssertionException) { + Debug("theory") << "EngineOutputChannel::flipDecision()" << std::endl; + ++ d_statistics.flipDecision; + return d_engine->d_propEngine->flipDecision(); + } + void setIncomplete() throw(AssertionException) { d_engine->setIncomplete(d_theory); } @@ -380,7 +410,7 @@ public: inline void addTheory(theory::TheoryId theoryId) { Assert(d_theoryTable[theoryId] == NULL && d_theoryOut[theoryId] == NULL); d_theoryOut[theoryId] = new EngineOutputChannel(this, theoryId); - d_theoryTable[theoryId] = new TheoryClass(d_context, d_userContext, *d_theoryOut[theoryId], theory::Valuation(this), d_logicInfo); + d_theoryTable[theoryId] = new TheoryClass(d_context, d_userContext, *d_theoryOut[theoryId], theory::Valuation(this), d_logicInfo, getQuantifiersEngine()); } inline void setPropEngine(prop::PropEngine* propEngine) { @@ -400,6 +430,13 @@ public: return d_propEngine; } + /** + * Get a pointer to the underlying quantifiers engine. + */ + theory::QuantifiersEngine* getQuantifiersEngine() const { + return d_quantEngine; + } + private: /** @@ -610,6 +647,11 @@ public: return d_theoryTable[theoryId]; } + /** Get the theory for id */ + theory::Theory* getTheory(int id) { + return d_theoryTable[id]; + } + /** * Returns the equality status of the two terms, from the theory * that owns the domain type. The types of a and b must be the same. diff --git a/src/theory/theory_test_utils.h b/src/theory/theory_test_utils.h index bf1370090..44f009bc0 100644 --- a/src/theory/theory_test_utils.h +++ b/src/theory/theory_test_utils.h @@ -94,6 +94,13 @@ public: return LemmaStatus(Node::null(), 0); } + void requirePhase(TNode, bool) throw(Interrupted, AssertionException) { + } + + bool flipDecision() throw(Interrupted, AssertionException) { + return true; + } + void setIncomplete() throw(AssertionException) {} void clear() { diff --git a/src/theory/trigger.cpp b/src/theory/trigger.cpp new file mode 100644 index 000000000..d665fef91 --- /dev/null +++ b/src/theory/trigger.cpp @@ -0,0 +1,555 @@ +/********************* */ +/*! \file trigger.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of trigger class + **/ + +#include "theory/trigger.h" +#include "theory/theory_engine.h" +#include "theory/quantifiers_engine.h" +#include "theory/uf/theory_uf_instantiator.h" +#include "theory/uf/theory_uf_candidate_generator.h" +#include "theory/uf/equality_engine.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; + +//#define NESTED_PATTERN_SELECTION + +Trigger* Trigger::TrTrie::getTrigger2( std::vector< Node >& nodes ){ + if( nodes.empty() ){ + return d_tr; + }else{ + Node n = nodes.back(); + nodes.pop_back(); + if( d_children.find( n )!=d_children.end() ){ + return d_children[n]->getTrigger2( nodes ); + }else{ + return NULL; + } + } +} +void Trigger::TrTrie::addTrigger2( std::vector< Node >& nodes, Trigger* t ){ + if( nodes.empty() ){ + d_tr = t; + }else{ + Node n = nodes.back(); + nodes.pop_back(); + if( d_children.find( n )==d_children.end() ){ + d_children[n] = new TrTrie; + } + d_children[n]->addTrigger2( nodes, t ); + } +} + +/** trigger static members */ +std::map< TNode, std::vector< TNode > > Trigger::d_var_contains; +Trigger::TrTrie Trigger::d_tr_trie; + +/** trigger class constructor */ +Trigger::Trigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, int matchOption, bool smartTriggers ) : +d_quantEngine( qe ), d_f( f ){ + d_nodes.insert( d_nodes.begin(), nodes.begin(), nodes.end() ); + if( smartTriggers ){ + if( d_nodes.size()==1 ){ + if( isSimpleTrigger( d_nodes[0] ) ){ + d_mg = new InstMatchGeneratorSimple( f, d_nodes[0] ); + }else{ + d_mg = new InstMatchGenerator( d_nodes[0], qe, matchOption ); + } + }else{ + d_mg = new InstMatchGeneratorMulti( f, d_nodes, qe, matchOption ); + } + }else{ + d_mg = new InstMatchGenerator( d_nodes, qe, matchOption ); + } + Debug("trigger") << "Trigger for " << f << ": " << std::endl; + for( int i=0; i<(int)d_nodes.size(); i++ ){ + Debug("trigger") << " " << d_nodes[i] << std::endl; + } + Debug("trigger") << std::endl; + if( d_nodes.size()==1 ){ + if( isSimpleTrigger( d_nodes[0] ) ){ + ++(qe->d_statistics.d_triggers); + }else{ + ++(qe->d_statistics.d_simple_triggers); + } + }else{ + Debug("multi-trigger") << "Multi-trigger " << (*this) << std::endl; + //Notice() << "Multi-trigger for " << f << " : " << std::endl; + //Notice() << " " << (*this) << std::endl; + ++(qe->d_statistics.d_multi_triggers); + } + //Notice() << "Trigger : " << (*this) << " for " << f << std::endl; + if( Options::current()->eagerInstQuant ){ + Theory* th_uf = qe->getTheoryEngine()->getTheory( theory::THEORY_UF ); + uf::InstantiatorTheoryUf* ith = (uf::InstantiatorTheoryUf*)th_uf->getInstantiator(); + for( int i=0; i<(int)d_nodes.size(); i++ ){ + ith->registerTrigger( this, d_nodes[i].getOperator() ); + } + } +} +void Trigger::computeVarContains( Node n ) { + if( d_var_contains.find( n )==d_var_contains.end() ){ + d_var_contains[n].clear(); + computeVarContains2( n, n ); + } +} + +void Trigger::computeVarContains2( Node n, Node parent ){ + if( n.getKind()==INST_CONSTANT ){ + if( std::find( d_var_contains[parent].begin(), d_var_contains[parent].end(), n )==d_var_contains[parent].end() ){ + d_var_contains[parent].push_back( n ); + } + }else{ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + computeVarContains2( n[i], parent ); + } + } +} + +void Trigger::resetInstantiationRound(){ + d_mg->resetInstantiationRound( d_quantEngine ); +} + +void Trigger::reset( Node eqc ){ + d_mg->reset( eqc, d_quantEngine ); +} + +bool Trigger::getNextMatch( InstMatch& m ){ + bool retVal = d_mg->getNextMatch( m, d_quantEngine ); + //m.makeInternal( d_quantEngine->getEqualityQuery() ); + return retVal; +} + +bool Trigger::getMatch( Node t, InstMatch& m ){ + //FIXME: this assumes d_mg is an inst match generator + return ((InstMatchGenerator*)d_mg)->getMatch( t, m, d_quantEngine ); +} + +int Trigger::addTerm( Node t ){ + return d_mg->addTerm( d_f, t, d_quantEngine ); +} + +int Trigger::addInstantiations( InstMatch& baseMatch, int instLimit, bool addSplits ){ + int addedLemmas = d_mg->addInstantiations( d_f, baseMatch, d_quantEngine, instLimit, addSplits ); + if( addedLemmas>0 ){ + Debug("inst-trigger") << "Added " << addedLemmas << " lemmas, trigger was "; + for( int i=0; i<(int)d_nodes.size(); i++ ){ + Debug("inst-trigger") << d_nodes[i] << " "; + } + Debug("inst-trigger") << std::endl; + } + return addedLemmas; +} + +Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, int matchOption, bool keepAll, int trOption, + bool smartTriggers ){ + std::vector< Node > trNodes; + if( !keepAll ){ + //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; + for( int i=0; i<(int)temp.size(); i++ ){ + bool foundVar = false; + computeVarContains( temp[i] ); + for( int j=0; j<(int)d_var_contains[ temp[i] ].size(); j++ ){ + Node v = d_var_contains[ temp[i] ][j]; + if( v.getAttribute(InstConstantAttribute())==f ){ + if( vars.find( v )==vars.end() ){ + vars[ v ] = true; + foundVar = true; + } + } + } + if( foundVar ){ + trNodes.push_back( temp[i] ); + for( int j=0; j<(int)d_var_contains[ temp[i] ].size(); j++ ){ + Node v = d_var_contains[ temp[i] ][j]; + patterns[ v ].push_back( temp[i] ); + } + } + } + //now, minimalize the trigger + for( int i=0; i<(int)trNodes.size(); i++ ){ + bool keepPattern = false; + Node n = trNodes[i]; + for( int j=0; j<(int)d_var_contains[ n ].size(); j++ ){ + Node v = d_var_contains[ n ][j]; + if( patterns[v].size()==1 ){ + keepPattern = true; + break; + } + } + if( !keepPattern ){ + //remove from pattern vector + for( int j=0; j<(int)d_var_contains[ n ].size(); j++ ){ + Node v = d_var_contains[ n ][j]; + for( int k=0; k<(int)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--; + } + } + }else{ + trNodes.insert( trNodes.begin(), nodes.begin(), nodes.end() ); + } + + //check for duplicate? + if( trOption==TR_MAKE_NEW ){ + //static int trNew = 0; + //static int trOld = 0; + //Trigger* t = d_tr_trie.getTrigger( trNodes ); + //if( t ){ + // trOld++; + //}else{ + // trNew++; + //} + //if( (trNew+trOld)%100==0 ){ + // Notice() << "Trigger new old = " << trNew << " " << trOld << std::endl; + //} + }else{ + Trigger* t = d_tr_trie.getTrigger( trNodes ); + if( t ){ + if( trOption==TR_GET_OLD ){ + //just return old trigger + return t; + }else{ + return NULL; + } + } + } + Trigger* t = new Trigger( qe, f, trNodes, matchOption, smartTriggers ); + d_tr_trie.addTrigger( trNodes, t ); + return t; +} +Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, Node n, int matchOption, bool keepAll, int trOption, bool smartTriggers ){ + std::vector< Node > nodes; + nodes.push_back( n ); + return mkTrigger( qe, f, nodes, matchOption, keepAll, trOption, smartTriggers ); +} + +bool Trigger::isUsableTrigger( std::vector< Node >& nodes, Node f ){ + for( int i=0; i<(int)nodes.size(); i++ ){ + if( !isUsableTrigger( nodes[i], f ) ){ + return false; + } + } + return true; +} + +bool Trigger::isUsable( Node n, Node f ){ + if( n.getAttribute(InstConstantAttribute())==f ){ + if( !isAtomicTrigger( n ) && n.getKind()!=INST_CONSTANT ){ + std::map< Node, Node > coeffs; + return getPatternArithmetic( f, n, coeffs ); + }else{ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( !isUsable( n[i], f ) ){ + return false; + } + } + return true; + } + }else{ + return true; + } +} + +bool Trigger::isUsableTrigger( Node n, Node f ){ + //return n.getAttribute(InstConstantAttribute())==f && n.getKind()==APPLY_UF; + return n.getAttribute(InstConstantAttribute())==f && isAtomicTrigger( n ) && isUsable( n, f ); +} + +bool Trigger::isAtomicTrigger( Node n ){ + return n.getKind()==APPLY_UF || n.getKind()==SELECT || n.getKind()==STORE; +} +bool Trigger::isSimpleTrigger( Node n ){ + if( isAtomicTrigger( n ) ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( n[i].getKind()!=INST_CONSTANT && n[i].hasAttribute(InstConstantAttribute()) ){ + return false; + } + } + return true; + }else{ + return false; + } +} + +/** filter all nodes that have instances */ +void Trigger::filterInstances( std::vector< Node >& nodes ){ + std::vector< bool > active; + active.resize( nodes.size(), true ); + for( int i=0; i<(int)nodes.size(); i++ ){ + for( int j=i+1; j<(int)nodes.size(); j++ ){ + if( active[i] && active[j] ){ + int result = isInstanceOf( nodes[i], nodes[j] ); + if( result==1 ){ + active[j] = false; + }else if( result==-1 ){ + active[i] = false; + } + } + } + } + std::vector< Node > temp; + for( int i=0; i<(int)nodes.size(); i++ ){ + if( active[i] ){ + temp.push_back( nodes[i] ); + } + } + nodes.clear(); + nodes.insert( nodes.begin(), temp.begin(), temp.end() ); +} + + +bool Trigger::collectPatTerms2( QuantifiersEngine* qe, Node f, Node n, std::map< Node, bool >& patMap, int tstrt ){ + if( patMap.find( n )==patMap.end() ){ + patMap[ n ] = false; + if( tstrt==TS_MIN_TRIGGER ){ + if( n.getKind()==FORALL ){ +#ifdef NESTED_PATTERN_SELECTION + //return collectPatTerms2( qe, f, qe->getOrCreateCounterexampleBody( n ), patMap, tstrt ); + return collectPatTerms2( qe, f, qe->getBoundBody( n ), patMap, tstrt ); +#else + return false; +#endif + }else{ + bool retVal = false; + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( collectPatTerms2( qe, f, n[i], patMap, tstrt ) ){ + retVal = true; + } + } + if( retVal ){ + return true; + }else if( isUsableTrigger( n, f ) ){ + patMap[ n ] = true; + return true; + }else{ + return false; + } + } + }else{ + bool retVal = false; + if( isUsableTrigger( n, f ) ){ + patMap[ n ] = true; + if( tstrt==TS_MAX_TRIGGER ){ + return true; + }else{ + retVal = true; + } + } + if( n.getKind()==FORALL ){ +#ifdef NESTED_PATTERN_SELECTION + //if( collectPatTerms2( qe, f, qe->getOrCreateCounterexampleBody( n ), patMap, tstrt ) ){ + // retVal = true; + //} + if( collectPatTerms2( qe, f, qe->getBoundBody( n ), patMap, tstrt ) ){ + retVal = true; + } +#endif + }else{ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( collectPatTerms2( qe, f, n[i], patMap, tstrt ) ){ + retVal = true; + } + } + } + return retVal; + } + }else{ + return patMap[ n ]; + } +} + +void Trigger::collectPatTerms( QuantifiersEngine* qe, Node f, Node n, std::vector< Node >& patTerms, int tstrt, bool filterInst ){ + std::map< Node, bool > patMap; + if( filterInst ){ + //immediately do not consider any term t for which another term is an instance of t + std::vector< Node > patTerms2; + collectPatTerms( qe, f, n, patTerms2, TS_ALL, false ); + std::vector< Node > temp; + temp.insert( temp.begin(), patTerms2.begin(), patTerms2.end() ); + filterInstances( temp ); + if( temp.size()!=patTerms2.size() ){ + Debug("trigger-filter-instance") << "Filtered an instance: " << std::endl; + Debug("trigger-filter-instance") << "Old: "; + for( int i=0; i<(int)patTerms2.size(); i++ ){ + Debug("trigger-filter-instance") << patTerms2[i] << " "; + } + Debug("trigger-filter-instance") << std::endl << "New: "; + for( int i=0; i<(int)temp.size(); i++ ){ + Debug("trigger-filter-instance") << temp[i] << " "; + } + Debug("trigger-filter-instance") << std::endl; + } + if( tstrt==TS_ALL ){ + patTerms.insert( patTerms.begin(), temp.begin(), temp.end() ); + return; + }else{ + //do not consider terms that have instances + for( int i=0; i<(int)patTerms2.size(); i++ ){ + if( std::find( temp.begin(), temp.end(), patTerms2[i] )==temp.end() ){ + patMap[ patTerms2[i] ] = false; + } + } + } + } + collectPatTerms2( qe, f, n, patMap, tstrt ); + for( std::map< Node, bool >::iterator it = patMap.begin(); it != patMap.end(); ++it ){ + if( it->second ){ + patTerms.push_back( it->first ); + } + } +} + +/** is n1 an instance of n2 or vice versa? */ +int Trigger::isInstanceOf( Node n1, Node n2 ){ + if( n1==n2 ){ + return 1; + }else if( n1.getKind()==n2.getKind() ){ + if( n1.getKind()==APPLY_UF ){ + if( n1.getOperator()==n2.getOperator() ){ + int result = 0; + for( int i=0; i<(int)n1.getNumChildren(); i++ ){ + if( n1[i]!=n2[i] ){ + int cResult = isInstanceOf( n1[i], n2[i] ); + if( cResult==0 ){ + return 0; + }else if( cResult!=result ){ + if( result!=0 ){ + return 0; + }else{ + result = cResult; + } + } + } + } + return result; + } + } + return 0; + }else if( n2.getKind()==INST_CONSTANT ){ + computeVarContains( n1 ); + //if( std::find( d_var_contains[ n1 ].begin(), d_var_contains[ n1 ].end(), n2 )!=d_var_contains[ n1 ].end() ){ + // return 1; + //} + if( d_var_contains[ n1 ].size()==1 && d_var_contains[ n1 ][ 0 ]==n2 ){ + return 1; + } + }else if( n1.getKind()==INST_CONSTANT ){ + computeVarContains( n2 ); + //if( std::find( d_var_contains[ n2 ].begin(), d_var_contains[ n2 ].end(), n1 )!=d_var_contains[ n2 ].end() ){ + // return -1; + //} + if( d_var_contains[ n2 ].size()==1 && d_var_contains[ n2 ][ 0 ]==n1 ){ + return 1; + } + } + return 0; +} + +bool Trigger::isVariableSubsume( Node n1, Node n2 ){ + if( n1==n2 ){ + return true; + }else{ + //Notice() << "is variable subsume ? " << n1 << " " << n2 << std::endl; + computeVarContains( n1 ); + computeVarContains( n2 ); + for( int i=0; i<(int)d_var_contains[n2].size(); i++ ){ + if( std::find( d_var_contains[n1].begin(), d_var_contains[n1].end(), d_var_contains[n2][i] )==d_var_contains[n1].end() ){ + //Notice() << "no" << std::endl; + return false; + } + } + //Notice() << "yes" << std::endl; + return true; + } +} + +void Trigger::getVarContains( Node f, std::vector< Node >& pats, std::map< Node, std::vector< Node > >& varContains ){ + for( int i=0; i<(int)pats.size(); i++ ){ + computeVarContains( pats[i] ); + varContains[ pats[i] ].clear(); + for( int j=0; j<(int)d_var_contains[pats[i]].size(); j++ ){ + if( d_var_contains[pats[i]][j].getAttribute(InstConstantAttribute())==f ){ + varContains[ pats[i] ].push_back( d_var_contains[pats[i]][j] ); + } + } + } +} + +void Trigger::getVarContainsNode( Node f, Node n, std::vector< Node >& varContains ){ + computeVarContains( n ); + for( int j=0; j<(int)d_var_contains[n].size(); j++ ){ + if( d_var_contains[n][j].getAttribute(InstConstantAttribute())==f ){ + varContains.push_back( d_var_contains[n][j] ); + } + } +} + +bool Trigger::getPatternArithmetic( Node f, Node n, std::map< Node, Node >& coeffs ){ + if( n.getKind()==PLUS ){ + Assert( coeffs.empty() ); + NodeBuilder<> t(kind::PLUS); + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( n[i].hasAttribute(InstConstantAttribute()) ){ + if( n[i].getKind()==INST_CONSTANT ){ + if( n[i].getAttribute(InstConstantAttribute())==f ){ + coeffs[ n[i] ] = Node::null(); + }else{ + coeffs.clear(); + return false; + } + }else if( !getPatternArithmetic( f, n[i], coeffs ) ){ + coeffs.clear(); + return false; + } + }else{ + t << n[i]; + } + } + if( t.getNumChildren()==0 ){ + coeffs[ Node::null() ] = NodeManager::currentNM()->mkConst( Rational(0) ); + }else if( t.getNumChildren()==1 ){ + coeffs[ Node::null() ] = t.getChild( 0 ); + }else{ + coeffs[ Node::null() ] = t; + } + return true; + }else if( n.getKind()==MULT ){ + if( n[0].getKind()==INST_CONSTANT && n[0].getAttribute(InstConstantAttribute())==f ){ + Assert( !n[1].hasAttribute(InstConstantAttribute()) ); + coeffs[ n[0] ] = n[1]; + return true; + }else if( n[1].getKind()==INST_CONSTANT && n[1].getAttribute(InstConstantAttribute())==f ){ + Assert( !n[0].hasAttribute(InstConstantAttribute()) ); + coeffs[ n[1] ] = n[0]; + return true; + } + } + return false; +} diff --git a/src/theory/trigger.h b/src/theory/trigger.h new file mode 100644 index 000000000..457df0ab4 --- /dev/null +++ b/src/theory/trigger.h @@ -0,0 +1,170 @@ +/********************* */ +/*! \file trigger.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief trigger class + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__TRIGGER_H +#define __CVC4__TRIGGER_H + +#include "theory/inst_match.h" + +namespace CVC4 { +namespace theory { + +//a collect of nodes representing a trigger +class Trigger { +private: + /** computation of variable contains */ + static std::map< TNode, std::vector< TNode > > d_var_contains; + static void computeVarContains( Node n ); + static void computeVarContains2( Node n, Node parent ); +private: + /** the quantifiers engine */ + QuantifiersEngine* d_quantEngine; + /** the quantifier this trigger is for */ + Node d_f; + /** match generators */ + IMGenerator* d_mg; +private: + /** a trie of triggers */ + class TrTrie { + private: + Trigger* getTrigger2( std::vector< Node >& nodes ); + void addTrigger2( std::vector< Node >& nodes, Trigger* t ); + public: + TrTrie() : d_tr( NULL ){} + Trigger* d_tr; + std::map< TNode, TrTrie* > d_children; + Trigger* getTrigger( std::vector< Node >& nodes ){ + std::vector< Node > temp; + temp.insert( temp.begin(), nodes.begin(), nodes.end() ); + std::sort( temp.begin(), temp.end() ); + return getTrigger2( temp ); + } + void addTrigger( std::vector< Node >& nodes, Trigger* t ){ + std::vector< Node > temp; + temp.insert( temp.begin(), nodes.begin(), nodes.end() ); + std::sort( temp.begin(), temp.end() ); + return addTrigger2( temp, t ); + } + };/* class Trigger::TrTrie */ + /** all triggers will be stored in this trie */ + static TrTrie d_tr_trie; +private: + /** trigger constructor */ + Trigger( QuantifiersEngine* ie, Node f, std::vector< Node >& nodes, int matchOption = 0, bool smartTriggers = false ); +public: + ~Trigger(){} +public: + std::vector< Node > d_nodes; +public: + void debugPrint( const char* c ); + IMGenerator* getGenerator() { return d_mg; } +public: + /** reset instantiation round (call this whenever equivalence classes have changed) */ + void resetInstantiationRound(); + /** reset, eqc is the equivalence class to search in (search in any if eqc=null) */ + void reset( Node eqc ); + /** get next match. must call reset( eqc ) once before this function. */ + bool getNextMatch( InstMatch& m ); + /** get the match against ground term or formula t. + the trigger and t should have the same shape. + Currently the trigger should not be a multi-trigger. + */ + bool getMatch( Node t, InstMatch& m); + /** add ground term t, called when t is added to the TermDb */ + int addTerm( Node t ); + /** return true if whatever Node is subsituted for the variables the + given Node can't match the pattern */ + bool nonunifiable( TNode t, const std::vector<Node> & vars){ + return d_mg->nonunifiable(t,vars); + } + /** return whether this is a multi-trigger */ + bool isMultiTrigger() { return d_nodes.size()>1; } +public: + /** add all available instantiations exhaustively, in any equivalence class + if limitInst>0, limitInst is the max # of instantiations to try */ + int addInstantiations( InstMatch& baseMatch, int instLimit = 0, bool addSplits = false ); + /** mkTrigger method + ie : quantifier engine; + f : forall something .... + nodes : (multi-)trigger + matchOption : which policy to use for creating matches (one of InstMatchGenerator::MATCH_GEN_* ) + keepAll: don't remove unneeded patterns; + trOption : policy for dealing with triggers that already existed (see below) + */ + 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 f, std::vector< Node >& nodes, + int matchOption = 0, bool keepAll = true, int trOption = TR_MAKE_NEW, + bool smartTriggers = false ); + static Trigger* mkTrigger( QuantifiersEngine* qe, Node f, Node n, + int matchOption = 0, bool keepAll = true, int trOption = TR_MAKE_NEW, + bool smartTriggers = false ); +private: + /** is subterm of trigger usable */ + static bool isUsable( Node n, Node f ); + /** collect all APPLY_UF pattern terms for f in n */ + static bool collectPatTerms2( QuantifiersEngine* qe, Node f, Node n, std::map< Node, bool >& patMap, int tstrt ); +public: + //different strategies for choosing trigger terms + enum { + TS_MAX_TRIGGER = 0, + TS_MIN_TRIGGER, + TS_ALL, + }; + static void collectPatTerms( QuantifiersEngine* qe, Node f, Node n, std::vector< Node >& patTerms, int tstrt, bool filterInst = false ); +public: + /** is usable trigger */ + static bool isUsableTrigger( std::vector< Node >& nodes, Node f ); + static bool isUsableTrigger( Node n, Node f ); + static bool isAtomicTrigger( Node n ); + static bool isSimpleTrigger( Node n ); + /** filter all nodes that have instances */ + static void filterInstances( std::vector< Node >& nodes ); + /** -1: n1 is an instance of n2, 1: n1 is an instance of n2 */ + static int isInstanceOf( Node n1, Node n2 ); + /** variables subsume, return true if n1 contains all free variables in n2 */ + static bool isVariableSubsume( Node n1, Node n2 ); + /** get var contains */ + static void getVarContains( Node f, std::vector< Node >& pats, std::map< Node, std::vector< Node > >& varContains ); + static void getVarContainsNode( Node f, Node n, std::vector< Node >& varContains ); + /** get pattern arithmetic */ + static bool getPatternArithmetic( Node f, Node n, std::map< Node, Node >& coeffs ); + + inline void toStream(std::ostream& out) const { + out << "TRIGGER( "; + for( int i=0; i<(int)d_nodes.size(); i++ ){ + if( i>0 ){ out << ", "; } + out << d_nodes[i]; + } + out << " )"; + } +}; + +inline std::ostream& operator<<(std::ostream& out, const Trigger & tr) { + tr.toStream(out); + return out; +} + +}/* CVC4::theory namespace */ + +}/* CVC4 namespace */ + +#endif /* __CVC4__TRIGGER_H */ diff --git a/src/theory/uf/Makefile.am b/src/theory/uf/Makefile.am index 50147b997..2c9cd3b80 100644 --- a/src/theory/uf/Makefile.am +++ b/src/theory/uf/Makefile.am @@ -14,6 +14,14 @@ libuf_la_SOURCES = \ equality_engine_types.h \ equality_engine.cpp \ symmetry_breaker.h \ - symmetry_breaker.cpp + symmetry_breaker.cpp \ + theory_uf_instantiator.h \ + theory_uf_instantiator.cpp \ + theory_uf_strong_solver.h \ + theory_uf_strong_solver.cpp \ + theory_uf_candidate_generator.h \ + theory_uf_candidate_generator.cpp \ + inst_strategy.h \ + inst_strategy.cpp EXTRA_DIST = kinds diff --git a/src/theory/uf/equality_engine.cpp b/src/theory/uf/equality_engine.cpp index 25645c472..96c8e8b59 100644 --- a/src/theory/uf/equality_engine.cpp +++ b/src/theory/uf/equality_engine.cpp @@ -211,6 +211,11 @@ EqualityNodeId EqualityEngine::newNode(TNode node) { Debug("equality") << d_name << "::eq::newNode(" << node << ") => " << newId << std::endl; + // notify e.g. the UF theory strong solver + if (d_performNotify) { + d_notify.eqNotifyNewClass(node); + } + return newId; } @@ -346,7 +351,12 @@ void EqualityEngine::assertEquality(TNode eq, bool polarity, TNode reason) { if (hasTerm(eq[0]) && hasTerm(eq[1]) && areDisequal(eq[0], eq[1], false)) { return; } - + + // notify the theory + if (d_performNotify) { + d_notify.eqNotifyDisequal(eq[0], eq[1], reason); + } + Debug("equality::trigger") << d_name << "::eq::addEquality(" << eq << "," << (polarity ? "true" : "false") << ")" << std::endl; assertEqualityInternal(eq, d_false, reason); @@ -437,6 +447,21 @@ bool EqualityEngine::merge(EqualityNode& class1, EqualityNode& class2, std::vect EqualityNodeId class1Id = class1.getFind(); EqualityNodeId class2Id = class2.getFind(); + Node n1 = d_nodes[class1Id]; + Node n2 = d_nodes[class2Id]; + EqualityNode cc1 = getEqualityNode(n1); + EqualityNode cc2 = getEqualityNode(n2); + bool doNotify = false; + // notify the theory + // the second part of this check is needed due to the internal implementation of this class. + // It ensures that we are merging terms and not operators. + if (d_performNotify && class1Id==cc1.getFind() && class2Id==cc2.getFind()) { + doNotify = true; + } + if (doNotify) { + d_notify.eqNotifyPreMerge(n1, n2); + } + // Check for constant merges bool class1isConstant = d_isConstant[class1Id]; bool class2isConstant = d_isConstant[class2Id]; @@ -559,7 +584,12 @@ bool EqualityEngine::merge(EqualityNode& class1, EqualityNode& class2, std::vect // Now merge the lists class1.merge<true>(class2); - + + // notify the theory + if (doNotify) { + d_notify.eqNotifyPostMerge(n1, n2); + } + // Go through the trigger term disequalities and propagate if (!propagateTriggerTermDisequalities(class1OnlyTags, class1triggerRef, class2disequalitiesToNotify)) { return false; diff --git a/src/theory/uf/equality_engine.h b/src/theory/uf/equality_engine.h index 8cf159cd7..cb0c81872 100644 --- a/src/theory/uf/equality_engine.h +++ b/src/theory/uf/equality_engine.h @@ -39,6 +39,9 @@ namespace CVC4 { namespace theory { namespace eq { +class EqClassesIterator; +class EqClassIterator; + /** * Interface for equality engine notifications. All the notifications * are safe as TNodes, but not necessarily for negations. @@ -62,7 +65,7 @@ public: /** * Notifies about a trigger predicate that became true or false. * - * @param predicate the trigger predicate that bacame true or false + * @param predicate the trigger predicate that became true or false * @param value the value of the predicate */ virtual bool eqNotifyTriggerPredicate(TNode predicate, bool value) = 0; @@ -82,10 +85,43 @@ public: * can do is ask for explanations. * * @param t1 a constant term - * @param t2 a constnat term + * @param t2 a constant term */ virtual void eqNotifyConstantTermMerge(TNode t1, TNode t2) = 0; -}; + + /** + * Notifies about the creation of a new equality class. + * + * @param t the term forming the new class + */ + virtual void eqNotifyNewClass(TNode t) = 0; + + /** + * Notifies about the merge of two classes (just before the merge). + * + * @param t1 a term + * @param t2 a term + */ + virtual void eqNotifyPreMerge(TNode t1, TNode t2) = 0; + + /** + * Notifies about the merge of two classes (just after the merge). + * + * @param t1 a term + * @param t2 a term + */ + virtual void eqNotifyPostMerge(TNode t1, TNode t2) = 0; + + /** + * Notifies about the disequality of two terms. + * + * @param t1 a term + * @param t2 a term + * @param reason the reason + */ + virtual void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) = 0; + +};/* class EqualityEngineNotify */ /** * Implementation of the notification interface that ignores all the @@ -97,7 +133,11 @@ public: bool eqNotifyTriggerPredicate(TNode predicate, bool value) { return true; } bool eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value) { return true; } void eqNotifyConstantTermMerge(TNode t1, TNode t2) { } -}; + void eqNotifyNewClass(TNode t) { } + void eqNotifyPreMerge(TNode t1, TNode t2) { } + void eqNotifyPostMerge(TNode t1, TNode t2) { } + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { } +};/* class EqualityEngineNotifyNone */ /** @@ -106,6 +146,9 @@ public: */ class EqualityEngine : public context::ContextNotifyObj { + friend class EqClassesIterator; + friend class EqClassIterator; + /** Default implementation of the notification object */ static EqualityEngineNotifyNone s_notifyNone; @@ -140,14 +183,14 @@ public: StatisticsRegistry::unregisterStat(&functionTermsCount); StatisticsRegistry::unregisterStat(&constantTermsCount); } - }; + };/* struct EqualityEngine::statistics */ /** * Store the application lookup, with enough information to backtrack */ void storeApplicationLookup(FunctionApplication& funNormalized, EqualityNodeId funId); -private: +//private: /** The context we are using */ context::Context* d_context; @@ -212,7 +255,7 @@ private: /** Equality constructor */ Equality(EqualityNodeId lhs = null_id, EqualityNodeId rhs = null_id) : lhs(lhs), rhs(rhs) {} - }; + };/* struct EqualityEngine::Equality */ /** The ids of the classes we have merged */ std::vector<Equality> d_assertedEqualities; @@ -253,7 +296,7 @@ private: /** The reason of this edge */ TNode getReason() const { return d_reason; } -}; + };/* class EqualityEngine::EqualityEdge */ /** * All the equality edges (twice as many as the number of asserted equalities. If an equality @@ -268,7 +311,7 @@ private: std::string edgesToString(EqualityEdgeId edgeId) const; /** - * Map from a node to it's first edge in the equality graph. Edges are added to the front of the + * Map from a node to its first edge in the equality graph. Edges are added to the front of the * list which makes the insertion/backtracking easy. */ std::vector<EqualityEdgeId> d_equalityGraph; @@ -297,7 +340,7 @@ private: */ bool merge(EqualityNode& class1, EqualityNode& class2, std::vector<TriggerId>& triggers); - /** Undo the mereg of class2 into class1 */ + /** Undo the merge of class2 into class1 */ void undoMerge(EqualityNode& class1, EqualityNode& class2, EqualityNodeId class2Id); /** Backtrack the information if necessary */ @@ -314,7 +357,7 @@ private: Trigger(EqualityNodeId classId = null_id, TriggerId nextTrigger = null_trigger) : classId(classId), nextTrigger(nextTrigger) {} - }; + };/* struct EqualityEngine::Trigger */ /** * Vector of triggers. Triggers come in pairs for an @@ -436,7 +479,7 @@ private: EqualityNodeId getTrigger(TheoryId tag) const { return triggers[Theory::setIndex(tag, tags)]; } - }; + };/* struct EqualityEngine::TriggerTermSet */ /** Internal tags for creating a new set */ Theory::Set d_newSetTags; @@ -451,7 +494,7 @@ private: char* d_triggerDatabase; /** Allocated size of the trigger term database */ - DefaultSizeType d_triggerDatabaseAllocatedSize ; + DefaultSizeType d_triggerDatabaseAllocatedSize; /** Reference for the trigger terms set */ typedef DefaultSizeType TriggerTermSetRef; @@ -482,7 +525,7 @@ private: TriggerTermSetRef oldValue; TriggerSetUpdate(EqualityNodeId classId = null_id, TriggerTermSetRef oldValue = null_set_id) : classId(classId), oldValue(oldValue) {} - }; + };/* struct EqualityEngine::TriggerSetUpdate */ /** * List of trigger updates for backtracking. @@ -685,7 +728,7 @@ public: * Add term to the set of trigger terms with a corresponding tag. The notify class will get * notified when two trigger terms with the same tag become equal or dis-equal. The notification * will not happen on all the terms, but only on the ones that are represent the class. Note that - * a term can be added more than once with different tags, and each tag apperance will merit + * a term can be added more than once with different tags, and each tag appearance will merit * it's own notification. * * @param t the trigger term @@ -743,7 +786,97 @@ public: }; -} // Namespace uf +class EqClassesIterator { + + eq::EqualityEngine* d_ee; + size_t d_it; + +public: + + EqClassesIterator() { } + EqClassesIterator(eq::EqualityEngine* ee) : d_ee(ee) { + d_it = 0; + if ( d_it < d_ee->d_nodesCount && + d_ee->getRepresentative(d_ee->d_nodes[d_it]) != d_ee->d_nodes[d_it] ) { + ++*this; + } + } + Node operator*() { + return d_ee->d_nodes[d_it]; + } + bool operator==(const EqClassesIterator& i) { + return d_ee == i.d_ee && d_it == i.d_it; + } + bool operator!=(const EqClassesIterator& i) { + return !(*this == i); + } + EqClassesIterator& operator++() { + Node orig = d_ee->d_nodes[d_it]; + ++d_it; + while ( d_it<d_ee->d_nodesCount && + ( d_ee->getRepresentative(d_ee->d_nodes[d_it]) != d_ee->d_nodes[d_it] + || d_ee->d_nodes[d_it] == orig ) ) { // this line is necessary for ignoring duplicates + ++d_it; + } + return *this; + } + EqClassesIterator operator++(int) { + EqClassesIterator i = *this; + ++*this; + return i; + } + bool isFinished() { + return d_it>=d_ee->d_nodesCount; + } +};/* class EqClassesIterator */ + +class EqClassIterator { + + Node d_rep; + eq::EqualityNode d_curr; + Node d_curr_node; + eq::EqualityEngine* d_ee; + +public: + + EqClassIterator() { } + EqClassIterator(Node eqc, eq::EqualityEngine* ee) : d_ee(ee) { + Assert( d_ee->getRepresentative(eqc) == eqc ); + d_rep = eqc; + d_curr_node = eqc; + d_curr = d_ee->getEqualityNode(eqc); + } + Node operator*() { + return d_curr_node; + } + bool operator==(const EqClassIterator& i) { + return d_ee == i.d_ee && d_curr_node == i.d_curr_node; + } + bool operator!=(const EqClassIterator& i) { + return !(*this == i); + } + EqClassIterator& operator++() { + Node next = d_ee->d_nodes[ d_curr.getNext() ]; + Assert( d_rep==d_ee->getRepresentative(next) ); + if (d_rep != next) { // we end when we have cycled back to the original representative + d_curr_node = next; + d_curr = d_ee->getEqualityNode(d_curr.getNext()); + } else { + d_curr_node = Node::null(); + } + return *this; + } + EqClassIterator operator++(int) { + EqClassIterator i = *this; + ++*this; + return i; + } + bool isFinished() { + return d_curr_node == Node::null(); + } +};/* class EqClassIterator */ + +} // Namespace eq } // Namespace theory } // Namespace CVC4 diff --git a/src/theory/uf/inst_strategy.cpp b/src/theory/uf/inst_strategy.cpp new file mode 100644 index 000000000..2ca2dcb5a --- /dev/null +++ b/src/theory/uf/inst_strategy.cpp @@ -0,0 +1,412 @@ +/********************* */ +/*! \file inst_strategy.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of theory uf instantiation strategies + **/ + +#include "theory/uf/inst_strategy.h" + +#include "theory/uf/theory_uf_instantiator.h" +#include "theory/theory_engine.h" +#include "theory/uf/theory_uf.h" +#include "theory/uf/equality_engine.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::uf; + +#define USE_SINGLE_TRIGGER_BEFORE_MULTI_TRIGGER +//#define MULTI_TRIGGER_FULL_EFFORT_HALF +#define MULTI_MULTI_TRIGGERS + +struct sortQuantifiersForSymbol { + QuantifiersEngine* d_qe; + bool operator() (Node i, Node j) { + int nqfsi = d_qe->getNumQuantifiersForSymbol( i.getOperator() ); + int nqfsj = d_qe->getNumQuantifiersForSymbol( j.getOperator() ); + if( nqfsi<nqfsj ){ + return true; + }else if( nqfsi>nqfsj ){ + return false; + }else{ + return false; + } + } +}; + + +void InstStrategyCheckCESolved::processResetInstantiationRound( Theory::Effort effort ){ + for( std::map< Node, bool >::iterator it = d_solved.begin(); it != d_solved.end(); ++it ){ + calcSolved( it->first ); + } +} + +int InstStrategyCheckCESolved::process( Node f, Theory::Effort effort, int e, int instLimit ){ + if( e==0 ){ + //calc solved if not done so already + if( d_solved.find( f )==d_solved.end() ){ + calcSolved( f ); + } + //check if f is counterexample-solved + Debug("quant-uf-strategy") << "Try CE-solved.." << std::endl; + if( d_solved[ f ] ){ + if( d_quantEngine->addInstantiation( f, d_th->d_baseMatch[f] ) ){ + ++(d_th->d_statistics.d_instantiations_ce_solved); + //d_quantEngine->d_hasInstantiated[f] = true; + } + d_solved[f] = false; + } + Debug("quant-uf-strategy") << "done." << std::endl; + } + return STATUS_UNKNOWN; +} + +void InstStrategyCheckCESolved::calcSolved( Node f ){ + d_th->d_baseMatch[f].clear(); + d_solved[ f ]= true; + //check if instantiation constants are solved for + for( int j = 0; j<(int)d_quantEngine->getNumInstantiationConstants( f ); j++ ){ + Node i = d_quantEngine->getInstantiationConstant( f, j ); + Node rep = d_th->getInternalRepresentative( i ); + if( !rep.hasAttribute(InstConstantAttribute()) ){ + d_th->d_baseMatch[f].d_map[ i ] = rep; + }else{ + d_solved[ f ] = false; + } + } +} + +void InstStrategyUserPatterns::processResetInstantiationRound( Theory::Effort effort ){ + //reset triggers + for( std::map< Node, std::vector< Trigger* > >::iterator it = d_user_gen.begin(); it != d_user_gen.end(); ++it ){ + for( int i=0; i<(int)it->second.size(); i++ ){ + it->second[i]->resetInstantiationRound(); + it->second[i]->reset( Node::null() ); + } + } +} + +int InstStrategyUserPatterns::process( Node f, Theory::Effort effort, int e, int instLimit ){ + if( e==0 ){ + return STATUS_UNFINISHED; + }else if( e==1 ){ + d_counter[f]++; + Debug("quant-uf-strategy") << "Try user-provided patterns..." << std::endl; + //Notice() << "Try user-provided patterns..." << std::endl; + for( int i=0; i<(int)d_user_gen[f].size(); i++ ){ + bool processTrigger = true; + if( effort!=Theory::EFFORT_LAST_CALL && d_user_gen[f][i]->isMultiTrigger() ){ +//#ifdef MULTI_TRIGGER_FULL_EFFORT_HALF +// processTrigger = d_counter[f]%2==0; +//#endif + } + if( processTrigger ){ + //if( d_user_gen[f][i]->isMultiTrigger() ) + //Notice() << " Process (user) " << (*d_user_gen[f][i]) << " for " << f << "..." << std::endl; + int numInst = d_user_gen[f][i]->addInstantiations( d_th->d_baseMatch[f], instLimit ); + //if( d_user_gen[f][i]->isMultiTrigger() ) + //Notice() << " Done, numInst = " << numInst << "." << std::endl; + d_th->d_statistics.d_instantiations_user_pattern += numInst; + if( d_user_gen[f][i]->isMultiTrigger() ){ + d_quantEngine->d_statistics.d_multi_trigger_instantiations += numInst; + } + //d_quantEngine->d_hasInstantiated[f] = true; + } + } + Debug("quant-uf-strategy") << "done." << std::endl; + //Notice() << "done" << std::endl; + } + return STATUS_UNKNOWN; +} + +void InstStrategyUserPatterns::addUserPattern( Node f, Node pat ){ + //add to generators + std::vector< Node > nodes; + for( int i=0; i<(int)pat.getNumChildren(); i++ ){ + nodes.push_back( pat[i] ); + } + if( Trigger::isUsableTrigger( nodes, f ) ){ + //extend to literal matching + d_quantEngine->getPhaseReqTerms( f, nodes ); + //check match option + int matchOption = Options::current()->efficientEMatching ? InstMatchGenerator::MATCH_GEN_EFFICIENT_E_MATCH : 0; + d_user_gen[f].push_back( Trigger::mkTrigger( d_quantEngine, f, nodes, matchOption, true, Trigger::TR_MAKE_NEW, + Options::current()->smartTriggers ) ); + } +} + +void InstStrategyAutoGenTriggers::processResetInstantiationRound( Theory::Effort effort ){ + //reset triggers + for( std::map< Node, std::map< Trigger*, bool > >::iterator it = d_auto_gen_trigger.begin(); it != d_auto_gen_trigger.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() ); + } + } +} + +int InstStrategyAutoGenTriggers::process( Node f, Theory::Effort effort, int e, int instLimit ){ + int peffort = f.getNumChildren()==3 ? 2 : 1; + //int peffort = f.getNumChildren()==3 ? 2 : 1; + //int peffort = 1; + if( e<peffort ){ + return STATUS_UNFINISHED; + }else{ + 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 ); + } + Debug("quant-uf-strategy") << "Try auto-generated triggers... " << d_tr_strategy << " " << e << std::endl; + //Notice() << "Try auto-generated triggers..." << std::endl; + for( std::map< Trigger*, bool >::iterator itt = d_auto_gen_trigger[f].begin(); itt != d_auto_gen_trigger[f].end(); ++itt ){ + Trigger* tr = itt->first; + if( tr ){ + bool processTrigger = itt->second; + if( effort!=Theory::EFFORT_LAST_CALL && tr->isMultiTrigger() ){ +#ifdef MULTI_TRIGGER_FULL_EFFORT_HALF + processTrigger = d_counter[f]%2==0; +#endif + } + if( processTrigger ){ + //if( tr->isMultiTrigger() ) + Debug("quant-uf-strategy-auto-gen-triggers") << " Process " << (*tr) << "..." << std::endl; + int numInst = tr->addInstantiations( d_th->d_baseMatch[f], instLimit ); + //if( tr->isMultiTrigger() ) + Debug("quant-uf-strategy-auto-gen-triggers") << " Done, numInst = " << numInst << "." << std::endl; + if( d_tr_strategy==Trigger::TS_MIN_TRIGGER ){ + d_th->d_statistics.d_instantiations_auto_gen_min += numInst; + }else{ + d_th->d_statistics.d_instantiations_auto_gen += numInst; + } + if( tr->isMultiTrigger() ){ + d_quantEngine->d_statistics.d_multi_trigger_instantiations += numInst; + } + //d_quantEngine->d_hasInstantiated[f] = true; + } + } + } + Debug("quant-uf-strategy") << "done." << std::endl; + //Notice() << "done" << std::endl; + } + return STATUS_UNKNOWN; +} + +void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ + Debug("auto-gen-trigger") << "Generate trigger for " << f << 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(); + std::vector< Node > patTermsF; + Trigger::collectPatTerms( d_quantEngine, f, d_quantEngine->getCounterexampleBody( f ), patTermsF, d_tr_strategy, true ); + Debug("auto-gen-trigger") << "Collected pat terms for " << d_quantEngine->getCounterexampleBody( f ) << std::endl; + Debug("auto-gen-trigger") << " "; + for( int i=0; i<(int)patTermsF.size(); i++ ){ + Debug("auto-gen-trigger") << patTermsF[i] << " "; + } + Debug("auto-gen-trigger") << std::endl; + //extend to literal matching + d_quantEngine->getPhaseReqTerms( f, patTermsF ); + //sort into single/multi triggers + std::map< Node, std::vector< Node > > varContains; + Trigger::getVarContains( f, patTermsF, varContains ); + for( std::map< Node, std::vector< Node > >::iterator it = varContains.begin(); it != varContains.end(); ++it ){ + if( it->second.size()==f[0].getNumChildren() ){ + d_patTerms[0][f].push_back( it->first ); + d_is_single_trigger[ it->first ] = true; + }else{ + d_patTerms[1][f].push_back( it->first ); + d_is_single_trigger[ it->first ] = false; + } + } + d_made_multi_trigger[f] = false; + Debug("auto-gen-trigger") << "Single triggers for " << f << " : " << std::endl; + Debug("auto-gen-trigger") << " "; + for( int i=0; i<(int)d_patTerms[0][f].size(); i++ ){ + Debug("auto-gen-trigger") << d_patTerms[0][f][i] << " "; + } + Debug("auto-gen-trigger") << std::endl; + Debug("auto-gen-trigger") << "Multi-trigger term pool for " << f << " : " << std::endl; + Debug("auto-gen-trigger") << " "; + for( int i=0; i<(int)d_patTerms[1][f].size(); i++ ){ + Debug("auto-gen-trigger") << d_patTerms[1][f][i] << " "; + } + Debug("auto-gen-trigger") << std::endl; + } + + //populate candidate pattern term vector for the current trigger + std::vector< Node > patTerms; +#ifdef USE_SINGLE_TRIGGER_BEFORE_MULTI_TRIGGER + //try to add single triggers first + for( int i=0; i<(int)d_patTerms[0][f].size(); i++ ){ + if( !d_single_trigger_gen[d_patTerms[0][f][i]] ){ + patTerms.push_back( d_patTerms[0][f][i] ); + } + } + //if no single triggers exist, add multi trigger terms + if( patTerms.empty() ){ + patTerms.insert( patTerms.begin(), d_patTerms[1][f].begin(), d_patTerms[1][f].end() ); + } +#else + patTerms.insert( patTerms.begin(), d_patTerms[0][f].begin(), d_patTerms[0][f].end() ); + patTerms.insert( patTerms.begin(), d_patTerms[1][f].begin(), d_patTerms[1][f].end() ); +#endif + + if( !patTerms.empty() ){ + Debug("auto-gen-trigger") << "Generate trigger for " << f << std::endl; + //sort terms based on relevance + if( d_rlv_strategy==RELEVANCE_DEFAULT ){ + sortQuantifiersForSymbol sqfs; + sqfs.d_qe = d_quantEngine; + //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( int i=0; i<(int)patTerms.size(); i++ ){ + Debug("relevant-trigger") << " " << patTerms[i] << " ("; + Debug("relevant-trigger") << d_quantEngine->getNumQuantifiersForSymbol( patTerms[i].getOperator() ) << ")" << std::endl; + } + //Notice() << "Terms based on relevance: " << std::endl; + //for( int i=0; i<(int)patTerms.size(); i++ ){ + // Notice() << " " << patTerms[i] << " ("; + // Notice() << d_quantEngine->getNumQuantifiersForSymbol( patTerms[i].getOperator() ) << ")" << std::endl; + //} + } + //now, generate the trigger... + int matchOption = Options::current()->efficientEMatching ? InstMatchGenerator::MATCH_GEN_EFFICIENT_E_MATCH : 0; + Trigger* tr = NULL; + if( d_is_single_trigger[ patTerms[0] ] ){ + tr = Trigger::mkTrigger( d_quantEngine, f, patTerms[0], matchOption, false, Trigger::TR_RETURN_NULL, + Options::current()->smartTriggers ); + d_single_trigger_gen[ patTerms[0] ] = true; + }else{ + //if we are re-generating triggers, shuffle based on some method + if( d_made_multi_trigger[f] ){ +#ifndef MULTI_MULTI_TRIGGERS + return; +#endif + 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, matchOption, false, Trigger::TR_GET_OLD, + Options::current()->smartTriggers ); + } + if( tr ){ + if( tr->isMultiTrigger() ){ + //disable all other multi triggers + for( std::map< Trigger*, bool >::iterator it = d_auto_gen_trigger[f].begin(); it != d_auto_gen_trigger[f].end(); ++it ){ + if( it->first->isMultiTrigger() ){ + d_auto_gen_trigger[f][ it->first ] = false; + } + } + } + //making it during an instantiation round, so must reset + if( d_auto_gen_trigger[f].find( tr )==d_auto_gen_trigger[f].end() ){ + tr->resetInstantiationRound(); + tr->reset( Node::null() ); + } + d_auto_gen_trigger[f][tr] = true; + //if we are generating additional triggers... + if( d_generate_additional && d_is_single_trigger[ patTerms[0] ] ){ + int index = 0; + if( index<(int)patTerms.size() ){ + //Notice() << "check add additional" << std::endl; + //check if similar patterns exist, and if so, add them additionally + int nqfs_curr = d_quantEngine->getNumQuantifiersForSymbol( patTerms[0].getOperator() ); + index++; + bool success = true; + while( success && index<(int)patTerms.size() && d_is_single_trigger[ patTerms[index] ] ){ + success = false; + if( d_quantEngine->getNumQuantifiersForSymbol( patTerms[index].getOperator() )<=nqfs_curr ){ + d_single_trigger_gen[ patTerms[index] ] = true; + Trigger* tr2 = Trigger::mkTrigger( d_quantEngine, f, patTerms[index], matchOption, false, Trigger::TR_RETURN_NULL, + Options::current()->smartTriggers ); + if( tr2 ){ + //Notice() << "Add additional trigger " << patTerms[index] << std::endl; + tr2->resetInstantiationRound(); + tr2->reset( Node::null() ); + d_auto_gen_trigger[f][tr2] = true; + } + success = true; + } + index++; + } + //Notice() << "done check add additional" << std::endl; + } + } + } + } +} + +#if 0 + +void InstStrategyAddFailSplits::processResetInstantiationRound( Theory::Effort effort ){ +} + +int InstStrategyAddFailSplits::process( Node f, Theory::Effort effort, int e, int instLimit ){ + if( e<4 ){ + return STATUS_UNFINISHED; + }else{ + for( std::map< Node, std::map< Node, std::vector< InstMatchGenerator* > > >::iterator it = InstMatchGenerator::d_match_fails.begin(); + it != InstMatchGenerator::d_match_fails.end(); ++it ){ + for( std::map< Node, std::vector< InstMatchGenerator* > >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){ + if( !it2->second.empty() ){ + Node n1 = it->first; + Node n2 = it2->first; + if( !d_quantEngine->getEqualityQuery()->areEqual( n1, n2 ) && !d_quantEngine->getEqualityQuery()->areDisequal( n1, n2 ) ){ + d_quantEngine->addSplitEquality( n1, n2, true ); + } + it2->second.clear(); + } + } + } + return STATUS_UNKNOWN; + } +} + +#endif /* 0 */ + +void InstStrategyFreeVariable::processResetInstantiationRound( Theory::Effort effort ){ +} + +int InstStrategyFreeVariable::process( Node f, Theory::Effort effort, int e, int instLimit ){ + if( e<5 ){ + return STATUS_UNFINISHED; + }else{ + if( d_guessed.find( f )==d_guessed.end() ){ + d_guessed[f] = true; + Debug("quant-uf-alg") << "Add guessed instantiation" << std::endl; + InstMatch m; + if( d_quantEngine->addInstantiation( f, m ) ){ + ++(d_th->d_statistics.d_instantiations_guess); + //d_quantEngine->d_hasInstantiated[f] = true; + } + } + return STATUS_UNKNOWN; + } +} diff --git a/src/theory/uf/inst_strategy.h b/src/theory/uf/inst_strategy.h new file mode 100644 index 000000000..906169811 --- /dev/null +++ b/src/theory/uf/inst_strategy.h @@ -0,0 +1,179 @@ +/********************* */ +/*! \file inst_strategy.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Theory uf instantiation strategies + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__INST_STRATEGY_H +#define __CVC4__INST_STRATEGY_H + +#include "theory/quantifiers_engine.h" + +#include "context/context.h" +#include "context/context_mm.h" + +#include "util/stats.h" +#include "theory/uf/theory_uf.h" + +namespace CVC4 { +namespace theory { +namespace uf { + +class InstantiatorTheoryUf; + +//instantiation strategies + +class InstStrategyCheckCESolved : public InstStrategy{ +private: + /** InstantiatorTheoryUf class */ + InstantiatorTheoryUf* d_th; + /** is solved? */ + std::map< Node, bool > d_solved; + /** calc if f is solved */ + void calcSolved( Node f ); + /** process functions */ + void processResetInstantiationRound( Theory::Effort effort ); + int process( Node f, Theory::Effort effort, int e, int instLimit ); +public: + InstStrategyCheckCESolved( InstantiatorTheoryUf* th, QuantifiersEngine* ie ) : + InstStrategy( ie ), d_th( th ){} + ~InstStrategyCheckCESolved(){} + /** identify */ + std::string identify() const { return std::string("CheckCESolved"); } +};/* class InstStrategyCheckCESolved */ + +class InstStrategyUserPatterns : public InstStrategy{ +private: + /** InstantiatorTheoryUf class */ + InstantiatorTheoryUf* d_th; + /** explicitly provided patterns */ + std::map< Node, std::vector< Trigger* > > d_user_gen; + /** 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, int instLimit ); +public: + InstStrategyUserPatterns( InstantiatorTheoryUf* th, QuantifiersEngine* ie ) : + InstStrategy( ie ), d_th( th ){} + ~InstStrategyUserPatterns(){} +public: + /** add pattern */ + void addUserPattern( Node f, Node pat ); + /** get num patterns */ + int getNumUserGenerators( Node f ) { return (int)d_user_gen[f].size(); } + /** get user pattern */ + Trigger* getUserGenerator( Node f, int i ) { return d_user_gen[f][ i ]; } + /** identify */ + std::string identify() const { return std::string("UserPatterns"); } +};/* class InstStrategyUserPatterns */ + +class InstStrategyAutoGenTriggers : public InstStrategy{ +public: + enum { + RELEVANCE_NONE, + RELEVANCE_DEFAULT, + }; +private: + /** InstantiatorTheoryUf class */ + InstantiatorTheoryUf* d_th; + /** trigger generation strategy */ + int d_tr_strategy; + /** relevance strategy */ + int d_rlv_strategy; + /** regeneration */ + bool d_regenerate; + int d_regenerate_frequency; + /** generate additional triggers */ + bool d_generate_additional; + /** triggers for each quantifier */ + std::map< Node, std::map< Trigger*, bool > > d_auto_gen_trigger; + std::map< Node, int > d_counter; + /** single, multi triggers for each quantifier */ + std::map< Node, std::vector< Node > > d_patTerms[2]; + std::map< Node, bool > d_is_single_trigger; + std::map< Node, bool > d_single_trigger_gen; + std::map< Node, bool > d_made_multi_trigger; +private: + /** process functions */ + void processResetInstantiationRound( Theory::Effort effort ); + int process( Node f, Theory::Effort effort, int e, int instLimit ); + /** generate triggers */ + void generateTriggers( Node f ); +public: + InstStrategyAutoGenTriggers( InstantiatorTheoryUf* th, QuantifiersEngine* ie, int tstrt, int rstrt, int rgfr = -1 ) : + InstStrategy( ie ), d_th( th ), d_tr_strategy( tstrt ), d_rlv_strategy( rstrt ), d_generate_additional( false ){ + setRegenerateFrequency( rgfr ); + } + ~InstStrategyAutoGenTriggers(){} +public: + /** get auto-generated trigger */ + Trigger* getAutoGenTrigger( Node f ); + /** identify */ + std::string identify() const { return std::string("AutoGenTriggers"); } + /** set regenerate frequency, if fr<0, turn off regenerate */ + void setRegenerateFrequency( int fr ){ + if( fr<0 ){ + d_regenerate = false; + }else{ + d_regenerate_frequency = fr; + d_regenerate = true; + } + } + /** set generate additional */ + void setGenerateAdditional( bool val ) { d_generate_additional = val; } +};/* class InstStrategyAutoGenTriggers */ + +#if 0 + +class InstStrategyAddFailSplits : public InstStrategy{ +private: + /** InstantiatorTheoryUf class */ + InstantiatorTheoryUf* d_th; + /** process functions */ + void processResetInstantiationRound( Theory::Effort effort ); + int process( Node f, Theory::Effort effort, int e, int instLimit ); +public: + InstStrategyAddFailSplits( InstantiatorTheoryUf* th, QuantifiersEngine* ie ) : + InstStrategy( ie ), d_th( th ){} + ~InstStrategyAddFailSplits(){} + /** identify */ + std::string identify() const { return std::string("AddFailSplits"); } +};/* class InstStrategyAddFailSplits */ + +#endif /* 0 */ + +class InstStrategyFreeVariable : public InstStrategy{ +private: + /** InstantiatorTheoryUf class */ + InstantiatorTheoryUf* d_th; + /** guessed instantiations */ + std::map< Node, bool > d_guessed; + /** process functions */ + void processResetInstantiationRound( Theory::Effort effort ); + int process( Node f, Theory::Effort effort, int e, int instLimit ); +public: + InstStrategyFreeVariable( InstantiatorTheoryUf* th, QuantifiersEngine* ie ) : + InstStrategy( ie ), d_th( th ){} + ~InstStrategyFreeVariable(){} + /** identify */ + std::string identify() const { return std::string("FreeVariable"); } +};/* class InstStrategyFreeVariable */ + +}/* CVC4::theory::uf namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif diff --git a/src/theory/uf/kinds b/src/theory/uf/kinds index 2de3715e1..ec353dc59 100644 --- a/src/theory/uf/kinds +++ b/src/theory/uf/kinds @@ -6,6 +6,7 @@ theory THEORY_UF ::CVC4::theory::uf::TheoryUF "theory/uf/theory_uf.h" typechecker "theory/uf/theory_uf_type_rules.h" +instantiator ::CVC4::theory::uf::InstantiatorTheoryUf "theory/uf/theory_uf_instantiator.h" properties stable-infinite parametric properties check propagate staticLearning presolve @@ -15,4 +16,8 @@ parameterized APPLY_UF VARIABLE 1: "uninterpreted function application" typerule APPLY_UF ::CVC4::theory::uf::UfTypeRule +operator CARDINALITY_CONSTRAINT 2 "cardinality constraint" + +typerule CARDINALITY_CONSTRAINT ::CVC4::theory::uf::CardinalityConstraintTypeRule + endtheory diff --git a/src/theory/uf/symmetry_breaker.cpp b/src/theory/uf/symmetry_breaker.cpp index 5761ee4f5..26678f21d 100644 --- a/src/theory/uf/symmetry_breaker.cpp +++ b/src/theory/uf/symmetry_breaker.cpp @@ -443,6 +443,16 @@ bool SymmetryBreaker::invariantByPermutations(const Permutation& p) { Assert(p.size() > 1); + // check that the types match + Permutation::iterator permIt = p.begin(); + TypeNode type = (*permIt++).getType(); + do { + if(type != (*permIt++).getType()) { + Debug("ufsymm") << "UFSYMM types don't match, aborting.." << endl; + return false; + } + } while(permIt != p.end()); + // check P_swap vector<Node> subs; vector<Node> repls; diff --git a/src/theory/uf/theory_uf.cpp b/src/theory/uf/theory_uf.cpp index 7583f8ee7..dc7bb7c92 100644 --- a/src/theory/uf/theory_uf.cpp +++ b/src/theory/uf/theory_uf.cpp @@ -18,6 +18,8 @@ **/ #include "theory/uf/theory_uf.h" +#include "theory/uf/theory_uf_instantiator.h" +#include "theory/uf/theory_uf_strong_solver.h" using namespace std; using namespace CVC4; @@ -25,8 +27,8 @@ using namespace CVC4::theory; using namespace CVC4::theory::uf; /** Constructs a new instance of TheoryUF w.r.t. the provided context.*/ -TheoryUF::TheoryUF(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) : - Theory(THEORY_UF, c, u, out, valuation, logicInfo), +TheoryUF::TheoryUF(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) : + Theory(THEORY_UF, c, u, out, valuation, logicInfo, qe), d_notify(*this), d_equalityEngine(d_notify, c, "theory::uf::TheoryUF"), d_conflict(c, false), @@ -36,6 +38,12 @@ TheoryUF::TheoryUF(context::Context* c, context::UserContext* u, OutputChannel& { // The kinds we are treating as function application in congruence d_equalityEngine.addFunctionKind(kind::APPLY_UF); + + if (Options::current()->finiteModelFind) { + d_thss = new StrongSolverTheoryUf(c, u, out, this); + } else { + d_thss = NULL; + } }/* TheoryUF::TheoryUF() */ static Node mkAnd(const std::vector<TNode>& conjunctions) { @@ -62,29 +70,46 @@ static Node mkAnd(const std::vector<TNode>& conjunctions) { void TheoryUF::check(Effort level) { - while (!done() && !d_conflict) + while (!done() && !d_conflict) { // Get all the assertions Assertion assertion = get(); TNode fact = assertion.assertion; Debug("uf") << "TheoryUF::check(): processing " << fact << std::endl; + if (d_thss != NULL) { + bool isDecision = d_valuation.isSatLiteral(fact) && d_valuation.isDecision(fact); + d_thss->assertNode(fact, isDecision); + } // Do the work bool polarity = fact.getKind() != kind::NOT; TNode atom = polarity ? fact : fact[0]; if (atom.getKind() == kind::EQUAL) { d_equalityEngine.assertEquality(atom, polarity, fact); + } else if (atom.getKind() == kind::CARDINALITY_CONSTRAINT) { + // do nothing } else { d_equalityEngine.assertPredicate(atom, polarity, fact); } } + + if (d_thss != NULL) { + if (! d_conflict) { + d_thss->check(level); + } + } + }/* TheoryUF::check() */ void TheoryUF::preRegisterTerm(TNode node) { Debug("uf") << "TheoryUF::preRegisterTerm(" << node << ")" << std::endl; + if (d_thss != NULL) { + d_thss->preRegisterTerm(node); + } + switch (node.getKind()) { case kind::EQUAL: // Add the trigger for equality @@ -124,6 +149,12 @@ bool TheoryUF::propagate(TNode literal) { return ok; }/* TheoryUF::propagate(TNode) */ +void TheoryUF::propagate(Effort effort) { + if (d_thss != NULL) { + return d_thss->propagate(effort); + } +} + void TheoryUF::explain(TNode literal, std::vector<TNode>& assumptions) { // Do the work bool polarity = literal.getKind() != kind::NOT; @@ -395,3 +426,54 @@ void TheoryUF::conflict(TNode a, TNode b) { d_out->conflict(d_conflictNode); d_conflict = true; } + +void TheoryUF::eqNotifyNewClass(TNode t) { + if (d_thss != NULL) { + d_thss->newEqClass(t); + } + // this can be called very early, during initialization + if (!getLogicInfo().isLocked() || getLogicInfo().isQuantified()) { + ((InstantiatorTheoryUf*) getInstantiator())->newEqClass(t); + } +} + +void TheoryUF::eqNotifyPreMerge(TNode t1, TNode t2) { + if (getLogicInfo().isQuantified()) { + ((InstantiatorTheoryUf*) getInstantiator())->merge(t1, t2); + } +} + +void TheoryUF::eqNotifyPostMerge(TNode t1, TNode t2) { + if (d_thss != NULL) { + d_thss->merge(t1, t2); + } +} + +void TheoryUF::eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { + if (d_thss != NULL) { + d_thss->assertDisequal(t1, t2, reason); + } + if (getLogicInfo().isQuantified()) { + ((InstantiatorTheoryUf*) getInstantiator())->assertDisequal(t1, t2, reason); + } +} + +Node TheoryUF::ppRewrite(TNode node) { + + if (node.getKind() != kind::APPLY_UF) { + return node; + } + + // perform the callbacks requested by TheoryUF::registerPpRewrite() + RegisterPpRewrites::iterator c = d_registeredPpRewrites.find(node.getOperator()); + if (c == d_registeredPpRewrites.end()) { + return node; + } else { + Node res = c->second->ppRewrite(node); + if (res != node) { + return ppRewrite(res); + } else { + return res; + } + } +} diff --git a/src/theory/uf/theory_uf.h b/src/theory/uf/theory_uf.h index eceead38a..a55ef92b5 100644 --- a/src/theory/uf/theory_uf.h +++ b/src/theory/uf/theory_uf.h @@ -36,7 +36,15 @@ namespace CVC4 { namespace theory { namespace uf { +class UfTermDb; +class InstantiatorTheoryUf; +class StrongSolverTheoryUf; + class TheoryUF : public Theory { + + friend class InstantiatorTheoryUf; + friend class StrongSolverTheoryUf; + public: class NotifyClass : public eq::EqualityEngineNotify { @@ -76,13 +84,43 @@ public: Debug("uf") << "NotifyClass::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << ")" << std::endl; d_uf.conflict(t1, t2); } - }; + + void eqNotifyNewClass(TNode t) { + Debug("uf") << "NotifyClass::eqNotifyNewClass(" << t << std::endl; + d_uf.eqNotifyNewClass(t); + } + + void eqNotifyPreMerge(TNode t1, TNode t2) { + Debug("uf") << "NotifyClass::eqNotifyPreMerge(" << t1 << ", " << t2 << std::endl; + d_uf.eqNotifyPreMerge(t1, t2); + } + + void eqNotifyPostMerge(TNode t1, TNode t2) { + Debug("uf") << "NotifyClass::eqNotifyPostMerge(" << t1 << ", " << t2 << std::endl; + d_uf.eqNotifyPostMerge(t1, t2); + } + + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { + Debug("uf") << "NotifyClass::eqNotifyDisequal(" << t1 << ", " << t2 << ", " << reason << std::endl; + d_uf.eqNotifyDisequal(t1, t2, reason); + } + + };/* class TheoryUF::NotifyClass */ + + /** A callback class for ppRewrite(). See registerPpRewrite(), below. */ + class PpRewrite { + public: + virtual Node ppRewrite(TNode node) = 0; + };/* class TheoryUF::PpRewrite */ private: /** The notify class */ NotifyClass d_notify; + /** The associated theory strong solver (or NULL if none) */ + StrongSolverTheoryUf* d_thss; + /** Equaltity engine */ eq::EqualityEngine d_equalityEngine; @@ -118,10 +156,37 @@ private: /** Conflict when merging two constants */ void conflict(TNode a, TNode b); + /** called when a new equivalance class is created */ + void eqNotifyNewClass(TNode t); + + /** called when two equivalance classes will merge */ + void eqNotifyPreMerge(TNode t1, TNode t2); + + /** called when two equivalance classes have merged */ + void eqNotifyPostMerge(TNode t1, TNode t2); + + /** called when two equivalence classes are made disequal */ + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason); + + /** a registry type for keeping Node-specific callbacks for ppRewrite() */ + typedef std::hash_map<Node, PpRewrite*, NodeHashFunction> RegisterPpRewrites; + + /** a collection of callbacks to issue while doing a ppRewrite() */ + RegisterPpRewrites d_registeredPpRewrites; + public: /** Constructs a new instance of TheoryUF w.r.t. the provided context.*/ - TheoryUF(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo); + TheoryUF(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe); + + ~TheoryUF() { + // destruct all ppRewrite() callbacks + for(RegisterPpRewrites::iterator i = d_registeredPpRewrites.begin(); + i != d_registeredPpRewrites.end(); + ++i) { + delete i->second; + } + } void check(Effort); void preRegisterTerm(TNode term); @@ -133,7 +198,7 @@ public: void addSharedTerm(TNode n); void computeCareGraph(); - void propagate(Effort effort) {} + void propagate(Effort effort); EqualityStatus getEqualityStatus(TNode a, TNode b); @@ -141,6 +206,24 @@ public: return "THEORY_UF"; } + eq::EqualityEngine* getEqualityEngine() { + return &d_equalityEngine; + } + + StrongSolverTheoryUf* getStrongSolver() { + return d_thss; + } + + Node ppRewrite(TNode node); + + /** + * Register a ppRewrite() callback on "op." TheoryUF owns + * the callback, and will delete it when it is destructed. + */ + void registerPpRewrite(TNode op, PpRewrite* callback) { + d_registeredPpRewrites.insert(std::make_pair(op, callback)); + } + };/* class TheoryUF */ }/* CVC4::theory::uf namespace */ diff --git a/src/theory/uf/theory_uf_candidate_generator.cpp b/src/theory/uf/theory_uf_candidate_generator.cpp new file mode 100644 index 000000000..e8aa98aa7 --- /dev/null +++ b/src/theory/uf/theory_uf_candidate_generator.cpp @@ -0,0 +1,170 @@ +/********************* */ +/*! \file theory_uf_candidate_generator.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of theory uf candidate generator class + **/ + +#include "theory/uf/theory_uf_candidate_generator.h" +#include "theory/theory_engine.h" +#include "theory/uf/theory_uf.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::uf; + +CandidateGeneratorTheoryUf::CandidateGeneratorTheoryUf( InstantiatorTheoryUf* ith, Node op ) : + d_op( op ), d_ith( ith ), d_term_iter( -2 ){ + Assert( !d_op.isNull() ); +} +void CandidateGeneratorTheoryUf::resetInstantiationRound(){ + d_term_iter_limit = d_ith->getQuantifiersEngine()->getTermDatabase()->d_op_map[d_op].size(); +} + +void CandidateGeneratorTheoryUf::reset( Node eqc ){ + if( eqc.isNull() ){ + d_term_iter = 0; + }else{ + //create an equivalence class iterator in eq class eqc + if( ((TheoryUF*)d_ith->getTheory())->getEqualityEngine()->hasTerm( eqc ) ){ + eqc = ((TheoryUF*)d_ith->getTheory())->getEqualityEngine()->getRepresentative( eqc ); + d_eqc = eq::EqClassIterator( eqc, ((TheoryUF*)d_ith->getTheory())->getEqualityEngine() ); + d_retNode = Node::null(); + }else{ + d_retNode = eqc; + } + d_term_iter = -1; + } +} + +Node CandidateGeneratorTheoryUf::getNextCandidate(){ + if( d_term_iter>=0 ){ + //get next candidate term in the uf term database + while( d_term_iter<d_term_iter_limit ){ + Node n = d_ith->getQuantifiersEngine()->getTermDatabase()->d_op_map[d_op][d_term_iter]; + d_term_iter++; + if( isLegalCandidate( n ) ){ + return n; + } + } + }else if( d_term_iter==-1 ){ + if( d_retNode.isNull() ){ + //get next candidate term in equivalence class + while( !d_eqc.isFinished() ){ + Node n = (*d_eqc); + ++d_eqc; + if( n.getKind()==APPLY_UF && n.getOperator()==d_op ){ + if( isLegalCandidate( n ) ){ + return n; + } + } + } + }else{ + Node ret; + if( d_retNode.hasOperator() && d_retNode.getOperator()==d_op ){ + ret = d_retNode; + } + d_term_iter = -2; //done returning matches + return ret; + } + } + return Node::null(); +} + + +//CandidateGeneratorTheoryUfDisequal::CandidateGeneratorTheoryUfDisequal( InstantiatorTheoryUf* ith, Node eqc ) : +// d_ith( ith ), d_eq_class( eqc ){ +// d_eci = NULL; +//} +//void CandidateGeneratorTheoryUfDisequal::resetInstantiationRound(){ +// +//} +////we will iterate over all terms that are disequal from eqc +//void CandidateGeneratorTheoryUfDisequal::reset( Node eqc ){ +// //Assert( !eqc.isNull() ); +// ////begin iterating over equivalence classes that are disequal from eqc +// //d_eci = d_ith->getEquivalenceClassInfo( eqc ); +// //if( d_eci ){ +// // d_eqci_iter = d_eci->d_disequal.begin(); +// //} +//} +//Node CandidateGeneratorTheoryUfDisequal::getNextCandidate(){ +// //if( d_eci ){ +// // while( d_eqci_iter != d_eci->d_disequal.end() ){ +// // if( (*d_eqci_iter).second ){ +// // //we have an equivalence class that is disequal from eqc +// // d_cg->reset( (*d_eqci_iter).first ); +// // Node n = d_cg->getNextCandidate(); +// // //if there is a candidate in this equivalence class, return it +// // if( !n.isNull() ){ +// // return n; +// // } +// // } +// // ++d_eqci_iter; +// // } +// //} +// return Node::null(); +//} + + +CandidateGeneratorTheoryUfLitEq::CandidateGeneratorTheoryUfLitEq( InstantiatorTheoryUf* ith, Node mpat ) : + d_match_pattern( mpat ), d_ith( ith ){ + +} +void CandidateGeneratorTheoryUfLitEq::resetInstantiationRound(){ + +} +void CandidateGeneratorTheoryUfLitEq::reset( Node eqc ){ + d_eq = eq::EqClassesIterator( ((TheoryUF*)d_ith->getTheory())->getEqualityEngine() ); +} +Node CandidateGeneratorTheoryUfLitEq::getNextCandidate(){ + while( d_eq.isFinished() ){ + Node n = (*d_eq); + ++d_eq; + if( n.getType()==d_match_pattern[0].getType() ){ + //an equivalence class with the same type as the pattern, return reflexive equality + return NodeManager::currentNM()->mkNode( d_match_pattern.getKind(), n, n ); + } + } + return Node::null(); +} + + +CandidateGeneratorTheoryUfLitDeq::CandidateGeneratorTheoryUfLitDeq( InstantiatorTheoryUf* ith, Node mpat ) : + d_match_pattern( mpat ), d_ith( ith ){ + +} +void CandidateGeneratorTheoryUfLitDeq::resetInstantiationRound(){ + +} +void CandidateGeneratorTheoryUfLitDeq::reset( Node eqc ){ + Node false_term = ((TheoryUF*)d_ith->getTheory())->getEqualityEngine()->getRepresentative( + NodeManager::currentNM()->mkConst<bool>(false) ); + d_eqc_false = eq::EqClassIterator( false_term, ((TheoryUF*)d_ith->getTheory())->getEqualityEngine() ); +} +Node CandidateGeneratorTheoryUfLitDeq::getNextCandidate(){ + //get next candidate term in equivalence class + while( !d_eqc_false.isFinished() ){ + Node n = (*d_eqc_false); + ++d_eqc_false; + if( n.getKind()==d_match_pattern.getKind() ){ + //found an iff or equality, try to match it + //DO_THIS: cache to avoid redundancies? + //DO_THIS: do we need to try the symmetric equality for n? or will it also exist in the eq class of false? + return n; + } + } + return Node::null(); +} diff --git a/src/theory/uf/theory_uf_candidate_generator.h b/src/theory/uf/theory_uf_candidate_generator.h new file mode 100644 index 000000000..948573439 --- /dev/null +++ b/src/theory/uf/theory_uf_candidate_generator.h @@ -0,0 +1,115 @@ +/********************* */ +/*! \file theory_uf_candidate generator.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Theory uf candidate generator + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY_UF_CANDIDATE_GENERATOR_H +#define __CVC4__THEORY_UF_CANDIDATE_GENERATOR_H + +#include "theory/quantifiers_engine.h" +#include "theory/uf/theory_uf_instantiator.h" + +namespace CVC4 { +namespace theory { +namespace uf { + +class CandidateGeneratorTheoryUfDisequal; + +class CandidateGeneratorTheoryUf : public CandidateGenerator +{ + friend class CandidateGeneratorTheoryUfDisequal; +private: + //operator you are looking for + Node d_op; + //instantiator pointer + InstantiatorTheoryUf* d_ith; + //the equality class iterator + eq::EqClassIterator d_eqc; + int d_term_iter; + int d_term_iter_limit; +private: + Node d_retNode; +public: + CandidateGeneratorTheoryUf( InstantiatorTheoryUf* ith, Node op ); + ~CandidateGeneratorTheoryUf(){} + + void resetInstantiationRound(); + void reset( Node eqc ); + Node getNextCandidate(); +}; + +//class CandidateGeneratorTheoryUfDisequal : public CandidateGenerator +//{ +//private: +// //equivalence class +// Node d_eq_class; +// //equivalence class info +// EqClassInfo* d_eci; +// //equivalence class iterator +// EqClassInfo::BoolMap::const_iterator d_eqci_iter; +// //instantiator pointer +// InstantiatorTheoryUf* d_ith; +//public: +// CandidateGeneratorTheoryUfDisequal( InstantiatorTheoryUf* ith, Node eqc ); +// ~CandidateGeneratorTheoryUfDisequal(){} +// +// void resetInstantiationRound(); +// void reset( Node eqc ); //should be what you want to be disequal from +// Node getNextCandidate(); +//}; + +class CandidateGeneratorTheoryUfLitEq : public CandidateGenerator +{ +private: + //the equality classes iterator + eq::EqClassesIterator d_eq; + //equality you are trying to match equalities for + Node d_match_pattern; + //einstantiator pointer + InstantiatorTheoryUf* d_ith; +public: + CandidateGeneratorTheoryUfLitEq( InstantiatorTheoryUf* ith, Node mpat ); + ~CandidateGeneratorTheoryUfLitEq(){} + + void resetInstantiationRound(); + void reset( Node eqc ); + Node getNextCandidate(); +}; + +class CandidateGeneratorTheoryUfLitDeq : public CandidateGenerator +{ +private: + //the equality class iterator for false + eq::EqClassIterator d_eqc_false; + //equality you are trying to match disequalities for + Node d_match_pattern; + //einstantiator pointer + InstantiatorTheoryUf* d_ith; +public: + CandidateGeneratorTheoryUfLitDeq( InstantiatorTheoryUf* ith, Node mpat ); + ~CandidateGeneratorTheoryUfLitDeq(){} + + void resetInstantiationRound(); + void reset( Node eqc ); + Node getNextCandidate(); +}; + + +} +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY_UF_INSTANTIATOR_H */ diff --git a/src/theory/uf/theory_uf_instantiator.cpp b/src/theory/uf/theory_uf_instantiator.cpp new file mode 100644 index 000000000..9fdcb5952 --- /dev/null +++ b/src/theory/uf/theory_uf_instantiator.cpp @@ -0,0 +1,520 @@ +/********************* */ +/*! \file theory_uf_instantiator.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of theory uf instantiator class + **/ + +#include "theory/uf/theory_uf_instantiator.h" +#include "theory/theory_engine.h" +#include "theory/uf/theory_uf.h" +#include "theory/uf/equality_engine.h" +//#include "theory/uf/inst_strategy_model_find.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::uf; + +EqClassInfo::EqClassInfo( context::Context* c ) : d_funs( c ), d_pfuns( c ), d_disequal( c ){ + +} + +//set member +void EqClassInfo::setMember( Node n, TermDb* db ){ + if( n.getKind()==APPLY_UF ){ + d_funs[n.getOperator()] = true; + } + //add parent functions + for( std::map< Node, std::map< int, std::vector< Node > > >::iterator it = db->d_parents[n].begin(); + it != db->d_parents[n].end(); ++it ){ + d_pfuns[ it->first ] = true; + } +} + +//get has function +bool EqClassInfo::hasFunction( Node op ){ + return d_funs.find( op )!=d_funs.end(); +} + +bool EqClassInfo::hasParent( Node op ){ + return d_pfuns.find( op )!=d_pfuns.end(); +} + +//merge with another eq class info +void EqClassInfo::merge( EqClassInfo* eci ){ + for( BoolMap::iterator it = eci->d_funs.begin(); it != eci->d_funs.end(); it++ ) { + d_funs[ (*it).first ] = true; + } + for( BoolMap::iterator it = eci->d_pfuns.begin(); it != eci->d_pfuns.end(); it++ ) { + d_pfuns[ (*it).first ] = true; + } +} + +InstantiatorTheoryUf::InstantiatorTheoryUf(context::Context* c, CVC4::theory::QuantifiersEngine* qe, Theory* th) : +Instantiator( c, qe, th ) +{ + qe->setEqualityQuery( new EqualityQueryInstantiatorTheoryUf( this ) ); + + if( Options::current()->finiteModelFind ){ + //if( Options::current()->cbqi ){ + // addInstStrategy( new InstStrategyCheckCESolved( this, qe ) ); + //} + //addInstStrategy( new InstStrategyFiniteModelFind( c, this, ((TheoryUF*)th)->getStrongSolver(), qe ) ); + qe->getTermDatabase()->setMatchingActive( false ); + }else{ + if( Options::current()->cbqi ){ + addInstStrategy( new InstStrategyCheckCESolved( this, qe ) ); + } + if( Options::current()->userPatternsQuant ){ + d_isup = new InstStrategyUserPatterns( this, qe ); + addInstStrategy( d_isup ); + }else{ + d_isup = NULL; + } + InstStrategyAutoGenTriggers* i_ag = new InstStrategyAutoGenTriggers( this, qe, Trigger::TS_ALL, + InstStrategyAutoGenTriggers::RELEVANCE_DEFAULT, 3 ); + i_ag->setGenerateAdditional( true ); + addInstStrategy( i_ag ); + //addInstStrategy( new InstStrategyAddFailSplits( this, ie ) ); + addInstStrategy( new InstStrategyFreeVariable( this, qe ) ); + //d_isup->setPriorityOver( i_ag ); + //d_isup->setPriorityOver( i_agm ); + //i_ag->setPriorityOver( i_agm ); + } +} + +void InstantiatorTheoryUf::preRegisterTerm( Node t ){ + //d_quantEngine->addTermToDatabase( t ); +} + +void InstantiatorTheoryUf::assertNode( Node assertion ) +{ + Debug("quant-uf-assert") << "InstantiatorTheoryUf::check: " << assertion << std::endl; + //preRegisterTerm( assertion ); + d_quantEngine->addTermToDatabase( assertion ); + if( Options::current()->cbqi ){ + if( assertion.hasAttribute(InstConstantAttribute()) ){ + setHasConstraintsFrom( assertion.getAttribute(InstConstantAttribute()) ); + }else if( assertion.getKind()==NOT && assertion[0].hasAttribute(InstConstantAttribute()) ){ + setHasConstraintsFrom( assertion[0].getAttribute(InstConstantAttribute()) ); + } + } +} + +void InstantiatorTheoryUf::addUserPattern( Node f, Node pat ){ + if( d_isup ){ + d_isup->addUserPattern( f, pat ); + } + setHasConstraintsFrom( f ); +} + + +void InstantiatorTheoryUf::processResetInstantiationRound( Theory::Effort effort ){ + d_ground_reps.clear(); +} + +int InstantiatorTheoryUf::process( Node f, Theory::Effort effort, int e, int instLimit ){ + Debug("quant-uf") << "UF: Try to solve (" << e << ") for " << f << "... " << std::endl; + return InstStrategy::STATUS_SAT; +} + +void InstantiatorTheoryUf::debugPrint( const char* c ) +{ + +} + +bool InstantiatorTheoryUf::hasTerm( Node a ){ + return ((TheoryUF*)d_th)->d_equalityEngine.hasTerm( a ); +} + +bool InstantiatorTheoryUf::areEqual( Node a, Node b ){ + if( hasTerm( a ) && hasTerm( b ) ){ + return ((TheoryUF*)d_th)->d_equalityEngine.areEqual( a, b ); + }else{ + return a==b; + } +} + +bool InstantiatorTheoryUf::areDisequal( Node a, Node b ){ + if( hasTerm( a ) && hasTerm( b ) ){ + return ((TheoryUF*)d_th)->d_equalityEngine.areDisequal( a, b, false ); + }else{ + return false; + } +} + +Node InstantiatorTheoryUf::getRepresentative( Node a ){ + if( hasTerm( a ) ){ + return ((TheoryUF*)d_th)->d_equalityEngine.getRepresentative( a ); + }else{ + return a; + } +} + +Node InstantiatorTheoryUf::getInternalRepresentative( Node a ){ + if( d_ground_reps.find( a )==d_ground_reps.end() ){ + if( !hasTerm( a ) ){ + return a; + }else{ + Node rep = getRepresentative( a ); + if( !rep.hasAttribute(InstConstantAttribute()) ){ + //return the representative of a + d_ground_reps[a] = rep; + return rep; + }else{ + //otherwise, must search eq class + eq::EqClassIterator eqc_iter( rep, &((TheoryUF*)d_th)->d_equalityEngine ); + rep = Node::null(); + while( !eqc_iter.isFinished() ){ + if( !(*eqc_iter).hasAttribute(InstConstantAttribute()) ){ + d_ground_reps[ a ] = *eqc_iter; + return *eqc_iter; + } + eqc_iter++; + } + d_ground_reps[ a ] = a; + } + } + } + return d_ground_reps[a]; +} + +InstantiatorTheoryUf::Statistics::Statistics(): + //d_instantiations("InstantiatorTheoryUf::Total_Instantiations", 0), + d_instantiations_ce_solved("InstantiatorTheoryUf::Instantiations_CE-Solved", 0), + d_instantiations_e_induced("InstantiatorTheoryUf::Instantiations_E-Induced", 0), + d_instantiations_user_pattern("InstantiatorTheoryUf::Instantiations_User_Pattern", 0), + d_instantiations_guess("InstantiatorTheoryUf::Instantiations_Free_Var", 0), + d_instantiations_auto_gen("InstantiatorTheoryUf::Instantiations_Auto_Gen", 0), + d_instantiations_auto_gen_min("InstantiatorTheoryUf::Instantiations_Auto_Gen_Min", 0), + d_splits("InstantiatorTheoryUf::Total_Splits", 0) +{ + //StatisticsRegistry::registerStat(&d_instantiations); + StatisticsRegistry::registerStat(&d_instantiations_ce_solved); + StatisticsRegistry::registerStat(&d_instantiations_e_induced); + StatisticsRegistry::registerStat(&d_instantiations_user_pattern ); + StatisticsRegistry::registerStat(&d_instantiations_guess ); + StatisticsRegistry::registerStat(&d_instantiations_auto_gen ); + StatisticsRegistry::registerStat(&d_instantiations_auto_gen_min ); + StatisticsRegistry::registerStat(&d_splits); +} + +InstantiatorTheoryUf::Statistics::~Statistics(){ + //StatisticsRegistry::unregisterStat(&d_instantiations); + StatisticsRegistry::unregisterStat(&d_instantiations_ce_solved); + StatisticsRegistry::unregisterStat(&d_instantiations_e_induced); + StatisticsRegistry::unregisterStat(&d_instantiations_user_pattern ); + StatisticsRegistry::unregisterStat(&d_instantiations_guess ); + StatisticsRegistry::unregisterStat(&d_instantiations_auto_gen ); + StatisticsRegistry::unregisterStat(&d_instantiations_auto_gen_min ); + StatisticsRegistry::unregisterStat(&d_splits); +} + +/** new node */ +void InstantiatorTheoryUf::newEqClass( TNode n ){ + d_quantEngine->addTermToDatabase( n ); +} + +/** merge */ +void InstantiatorTheoryUf::merge( TNode a, TNode b ){ + if( Options::current()->efficientEMatching ){ + if( a.getKind()!=IFF && a.getKind()!=EQUAL && b.getKind()!=IFF && b.getKind()!=EQUAL ){ + Debug("efficient-e-match") << "Merging " << a << " with " << b << std::endl; + + //determine new candidates for instantiation + computeCandidatesPcPairs( a, b ); + computeCandidatesPcPairs( b, a ); + computeCandidatesPpPairs( a, b ); + computeCandidatesPpPairs( b, a ); + } + //merge eqc_ops of b into a + EqClassInfo* eci_a = getOrCreateEquivalenceClassInfo( a ); + EqClassInfo* eci_b = getOrCreateEquivalenceClassInfo( b ); + eci_a->merge( eci_b ); + } +} + +/** assert terms are disequal */ +void InstantiatorTheoryUf::assertDisequal( TNode a, TNode b, TNode reason ){ + +} + +EqClassInfo* InstantiatorTheoryUf::getEquivalenceClassInfo( Node n ) { + return d_eqc_ops.find( n )==d_eqc_ops.end() ? NULL : d_eqc_ops[n]; +} +EqClassInfo* InstantiatorTheoryUf::getOrCreateEquivalenceClassInfo( Node n ){ + Assert( n==getRepresentative( n ) ); + if( d_eqc_ops.find( n )==d_eqc_ops.end() ){ + EqClassInfo* eci = new EqClassInfo( d_th->getSatContext() ); + eci->setMember( n, d_quantEngine->getTermDatabase() ); + d_eqc_ops[n] = eci; + } + return d_eqc_ops[n]; +} + +void InstantiatorTheoryUf::computeCandidatesPcPairs( Node a, Node b ){ + Debug("efficient-e-match") << "Compute candidates for pc pairs..." << std::endl; + Debug("efficient-e-match") << " Eq class = ["; + outputEqClass( "efficient-e-match", a); + Debug("efficient-e-match") << "]" << std::endl; + EqClassInfo* eci_a = getOrCreateEquivalenceClassInfo( a ); + EqClassInfo* eci_b = getOrCreateEquivalenceClassInfo( a ); + for( BoolMap::iterator it = eci_a->d_funs.begin(); it != eci_a->d_funs.end(); it++ ) { + //the child function: a member of eq_class( a ) has top symbol g, in other words g is in funs( a ) + Node g = (*it).first; + Debug("efficient-e-match") << " Checking application " << g << std::endl; + //look at all parent/child pairs + for( std::map< Node, std::map< Node, std::vector< InvertedPathString > > >::iterator itf = d_pc_pairs[g].begin(); + itf != d_pc_pairs[g].end(); ++itf ){ + //f/g is a parent/child pair + Node f = itf->first; + if( eci_b->hasParent( f ) || true ){ + //DO_THIS: determine if f in pfuns( b ), only do the follow if so + Debug("efficient-e-match") << " Checking parent application " << f << std::endl; + //scan through the list of inverted path strings/candidate generators + for( std::map< Node, std::vector< InvertedPathString > >::iterator cit = itf->second.begin(); + cit != itf->second.end(); ++cit ){ + Node pat = cit->first; + Debug("efficient-e-match") << " Checking pattern " << pat << std::endl; + for( int c=0; c<(int)cit->second.size(); c++ ){ + Debug("efficient-e-match") << " Check inverted path string for pattern "; + outputInvertedPathString( "efficient-e-match", cit->second[c] ); + Debug("efficient-e-match") << std::endl; + + //collect all new relevant terms + std::vector< Node > terms; + terms.push_back( b ); + collectTermsIps( cit->second[c], terms ); + if( !terms.empty() ){ + Debug("efficient-e-match") << " -> Added terms (" << (int)terms.size() << "): "; + //add them as candidates to the candidate generator + for( int t=0; t<(int)terms.size(); t++ ){ + Debug("efficient-e-match") << terms[t] << " "; + //Notice() << "Add candidate (PC) " << terms[t] << std::endl; + for( int cg=0; cg<(int)d_pat_cand_gens[pat].size(); cg++ ){ + d_pat_cand_gens[pat][cg]->addCandidate( terms[t] ); + } + } + Debug("efficient-e-match") << std::endl; + } + } + } + } + } + } +} + +void InstantiatorTheoryUf::computeCandidatesPpPairs( Node a, Node b ){ + Debug("efficient-e-match") << "Compute candidates for pp pairs..." << std::endl; + EqClassInfo* eci_a = getOrCreateEquivalenceClassInfo( a ); + EqClassInfo* eci_b = getOrCreateEquivalenceClassInfo( a ); + for( std::map< Node, std::map< Node, std::map< Node, std::vector< IpsPair > > > >::iterator it = d_pp_pairs.begin(); + it != d_pp_pairs.end(); ++it ){ + Node f = it->first; + if( eci_a->hasParent( f ) ){ + Debug("efficient-e-match") << " Checking parent application " << f << std::endl; + for( std::map< Node, std::map< Node, std::vector< IpsPair > > >::iterator it2 = it->second.begin(); + it2 != it->second.end(); ++it2 ){ + Node g = it2->first; + if( eci_b->hasParent( g ) ){ + Debug("efficient-e-match") << " Checking parent application " << g << std::endl; + //if f in pfuns( a ) and g is in pfuns( b ), only do the follow if so + for( std::map< Node, std::vector< IpsPair > >::iterator cit = it2->second.begin(); + cit != it2->second.end(); ++cit ){ + Node pat = cit->first; + for( int c=0; c<(int)cit->second.size(); c++ ){ + std::vector< Node > a_terms; + a_terms.push_back( a ); + if( !a_terms.empty() ){ + collectTermsIps( cit->second[c].first, a_terms ); + std::vector< Node > b_terms; + b_terms.push_back( b ); + collectTermsIps( cit->second[c].first, b_terms ); + //take intersection + for( int t=0; t<(int)a_terms.size(); t++ ){ + if( std::find( b_terms.begin(), b_terms.end(), a_terms[t] )!=b_terms.end() ){ + //Notice() << "Add candidate (PP) " << a_terms[t] << std::endl; + Debug("efficient-e-match") << " -> Add term " << a_terms[t] << std::endl; + //add to all candidate generators having this term + for( int cg=0; cg<(int)d_pat_cand_gens[pat].size(); cg++ ){ + d_pat_cand_gens[pat][cg]->addCandidate( a_terms[t] ); + } + } + } + } + } + } + } + } + } + } +} + +void InstantiatorTheoryUf::collectTermsIps( InvertedPathString& ips, std::vector< Node >& terms, int index ){ + if( index<(int)ips.size() && !terms.empty() ){ + Debug("efficient-e-match-debug") << "> Process " << index << std::endl; + Node f = ips[index].first; + int arg = ips[index].second; + + //for each term in terms, determine if any term (modulo equality) has parent "f" from position "arg" + bool addRep = ( index!=(int)ips.size()-1 ); + std::vector< Node > newTerms; + for( int t=0; t<(int)terms.size(); t++ ){ + collectParentsTermsIps( terms[t], f, arg, newTerms, addRep ); + } + terms.clear(); + terms.insert( terms.begin(), newTerms.begin(), newTerms.end() ); + + Debug("efficient-e-match-debug") << "> Terms are now: "; + for( int t=0; t<(int)terms.size(); t++ ){ + Debug("efficient-e-match-debug") << terms[t] << " "; + } + Debug("efficient-e-match-debug") << std::endl; + + collectTermsIps( ips, terms, index+1 ); + } +} + +bool InstantiatorTheoryUf::collectParentsTermsIps( Node n, Node f, int arg, std::vector< Node >& terms, bool addRep, bool modEq ){ + bool addedTerm = false; + if( ((TheoryUF*)d_th)->d_equalityEngine.hasTerm( n ) && modEq ){ + Assert( getRepresentative( n )==n ); + //collect modulo equality + //DO_THIS: this should (if necessary) compute a current set of (f, arg) parents for n and cache it + eq::EqClassIterator eqc_iter( getRepresentative( n ), &((TheoryUF*)d_th)->d_equalityEngine ); + while( !eqc_iter.isFinished() ){ + if( collectParentsTermsIps( (*eqc_iter), f, arg, terms, addRep, false ) ){ + //if only one argument, we know we can stop (since all others added will be congruent) + if( f.getType().getNumChildren()==2 ){ + return true; + } + addedTerm = true; + } + eqc_iter++; + } + }else{ + TermDb* db = d_quantEngine->getTermDatabase(); + //see if parent f exists from argument arg + if( db->d_parents.find( n )!=db->d_parents.end() ){ + if( db->d_parents[n].find( f )!=db->d_parents[n].end() ){ + if( db->d_parents[n][f].find( arg )!=db->d_parents[n][f].end() ){ + for( int i=0; i<(int)db->d_parents[n][f][arg].size(); i++ ){ + Node t = db->d_parents[n][f][arg][i]; + if( addRep ){ + t = getRepresentative( t ); + } + if( std::find( terms.begin(), terms.end(), t )==terms.end() ){ + terms.push_back( t ); + } + addedTerm = true; + } + } + } + } + } + return addedTerm; +} + +void InstantiatorTheoryUf::registerPatternElementPairs2( Node opat, Node pat, InvertedPathString& ips, + std::map< Node, std::vector< std::pair< Node, InvertedPathString > > >& ips_map ){ + Assert( pat.getKind()==APPLY_UF ); + //add information for possible pp-pair + for( int i=0; i<(int)pat.getNumChildren(); i++ ){ + if( pat[i].getKind()==INST_CONSTANT ){ + ips_map[ pat[i] ].push_back( std::pair< Node, InvertedPathString >( pat.getOperator(), InvertedPathString( ips ) ) ); + } + } + ips.push_back( std::pair< Node, int >( pat.getOperator(), 0 ) ); + for( int i=0; i<(int)pat.getNumChildren(); i++ ){ + if( pat[i].getKind()==APPLY_UF ){ + ips.back().second = i; + registerPatternElementPairs2( opat, pat[i], ips, ips_map ); + Debug("pattern-element-opt") << "Found pc-pair ( " << pat.getOperator() << ", " << pat[i].getOperator() << " )" << std::endl; + Debug("pattern-element-opt") << " Path = "; + outputInvertedPathString( "pattern-element-opt", ips ); + Debug("pattern-element-opt") << std::endl; + //pat.getOperator() and pat[i].getOperator() are a pc-pair + d_pc_pairs[ pat[i].getOperator() ][ pat.getOperator() ][opat].push_back( InvertedPathString( ips ) ); + } + } + ips.pop_back(); +} + +void InstantiatorTheoryUf::registerPatternElementPairs( Node pat ){ + InvertedPathString ips; + std::map< Node, std::vector< std::pair< Node, InvertedPathString > > > ips_map; + registerPatternElementPairs2( pat, pat, ips, ips_map ); + for( std::map< Node, std::vector< std::pair< Node, InvertedPathString > > >::iterator it = ips_map.begin(); it != ips_map.end(); ++it ){ + for( int j=0; j<(int)it->second.size(); j++ ){ + for( int k=j+1; k<(int)it->second.size(); k++ ){ + //found a pp-pair + Debug("pattern-element-opt") << "Found pp-pair ( " << it->second[j].first << ", " << it->second[k].first << " )" << std::endl; + Debug("pattern-element-opt") << " Paths = "; + outputInvertedPathString( "pattern-element-opt", it->second[j].second ); + Debug("pattern-element-opt") << " and "; + outputInvertedPathString( "pattern-element-opt", it->second[k].second ); + Debug("pattern-element-opt") << std::endl; + d_pp_pairs[ it->second[j].first ][ it->second[k].first ][pat].push_back( IpsPair( it->second[j].second, it->second[k].second ) ); + } + } + } +} + +void InstantiatorTheoryUf::registerCandidateGenerator( CandidateGenerator* cg, Node pat ){ + Debug("efficient-e-match") << "Register candidate generator..." << pat << std::endl; + if( d_pat_cand_gens.find( pat )==d_pat_cand_gens.end() ){ + registerPatternElementPairs( pat ); + } + d_pat_cand_gens[pat].push_back( cg ); + + //take all terms from the uf term db and add to candidate generator + Node op = pat.getOperator(); + TermDb* db = d_quantEngine->getTermDatabase(); + for( int i=0; i<(int)db->d_op_map[op].size(); i++ ){ + cg->addCandidate( db->d_op_map[op][i] ); + } + d_cand_gens[op].push_back( cg ); + + Debug("efficient-e-match") << "Done." << std::endl; +} + +void InstantiatorTheoryUf::registerTrigger( Trigger* tr, Node op ){ + if( std::find( d_op_triggers[op].begin(), d_op_triggers[op].end(), tr )==d_op_triggers[op].end() ){ + d_op_triggers[op].push_back( tr ); + } +} + +void InstantiatorTheoryUf::outputEqClass( const char* c, Node n ){ + if( ((TheoryUF*)d_th)->d_equalityEngine.hasTerm( n ) ){ + eq::EqClassIterator eqc_iter( getRepresentative( n ), + &((TheoryUF*)d_th)->d_equalityEngine ); + bool firstTime = true; + while( !eqc_iter.isFinished() ){ + if( !firstTime ){ Debug(c) << ", "; } + Debug(c) << (*eqc_iter); + firstTime = false; + eqc_iter++; + } + }else{ + Debug(c) << n; + } +} + +void InstantiatorTheoryUf::outputInvertedPathString( const char* c, InvertedPathString& ips ){ + for( int i=0; i<(int)ips.size(); i++ ){ + if( i>0 ){ Debug( c ) << "."; } + Debug( c ) << ips[i].first << "." << ips[i].second; + } +} diff --git a/src/theory/uf/theory_uf_instantiator.h b/src/theory/uf/theory_uf_instantiator.h new file mode 100644 index 000000000..e50e3823c --- /dev/null +++ b/src/theory/uf/theory_uf_instantiator.h @@ -0,0 +1,201 @@ +/********************* */ +/*! \file theory_uf_instantiator.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Theory uf instantiator + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY_UF_INSTANTIATOR_H +#define __CVC4__THEORY_UF_INSTANTIATOR_H + +#include "theory/uf/inst_strategy.h" + +#include "context/context.h" +#include "context/context_mm.h" +#include "context/cdchunk_list.h" + +#include "util/stats.h" +#include "theory/uf/theory_uf.h" + +namespace CVC4 { +namespace theory { + +class TermDb; + +namespace uf { + +class InstantiatorTheoryUf; + +//equivalence class info +class EqClassInfo +{ +public: + typedef context::CDHashMap<Node, bool, NodeHashFunction> BoolMap; + typedef context::CDChunkList<Node> NodeList; +public: + //a list of operators that occur as top symbols in this equivalence class + // Efficient E-Matching for SMT Solvers: "funs" + BoolMap d_funs; + //a list of operators f for which a term of the form f( ... t ... ) exists + // Efficient E-Matching for SMT Solvers: "pfuns" + BoolMap d_pfuns; + //a list of equivalence classes that are disequal + BoolMap d_disequal; +public: + EqClassInfo( context::Context* c ); + ~EqClassInfo(){} + //set member + void setMember( Node n, TermDb* db ); + //has function "funs" + bool hasFunction( Node op ); + //has parent "pfuns" + bool hasParent( Node op ); + //merge with another eq class info + void merge( EqClassInfo* eci ); +}; + +class InstantiatorTheoryUf : public Instantiator{ + friend class ::CVC4::theory::InstMatchGenerator; + friend class ::CVC4::theory::TermDb; +protected: + typedef context::CDHashMap<Node, bool, NodeHashFunction> BoolMap; + typedef context::CDHashMap<Node, int, NodeHashFunction> IntMap; + typedef context::CDChunkList<Node> NodeList; + typedef context::CDHashMap<Node, NodeList*, NodeHashFunction> NodeLists; + /** map to representatives used */ + std::map< Node, Node > d_ground_reps; +protected: + /** instantiation strategies */ + InstStrategyUserPatterns* d_isup; +public: + InstantiatorTheoryUf(context::Context* c, CVC4::theory::QuantifiersEngine* qe, Theory* th); + ~InstantiatorTheoryUf() {} + /** assertNode method */ + void assertNode( Node assertion ); + /** Pre-register a term. Done one time for a Node, ever. */ + void preRegisterTerm( Node t ); + /** identify */ + std::string identify() const { return std::string("InstantiatorTheoryUf"); } + /** debug print */ + void debugPrint( const char* c ); + /** add user pattern */ + void addUserPattern( Node f, Node pat ); +private: + /** reset instantiation */ + void processResetInstantiationRound( Theory::Effort effort ); + /** calculate matches for quantifier f at effort */ + int process( Node f, Theory::Effort effort, int e, int instLimit ); +public: + /** statistics class */ + class Statistics { + public: + //IntStat d_instantiations; + IntStat d_instantiations_ce_solved; + IntStat d_instantiations_e_induced; + IntStat d_instantiations_user_pattern; + IntStat d_instantiations_guess; + IntStat d_instantiations_auto_gen; + IntStat d_instantiations_auto_gen_min; + IntStat d_splits; + Statistics(); + ~Statistics(); + }; + Statistics d_statistics; +public: + /** the base match */ + std::map< Node, InstMatch > d_baseMatch; +private: + //for each equivalence class + std::map< Node, EqClassInfo* > d_eqc_ops; +public: + /** general queries about equality */ + bool hasTerm( Node a ); + bool areEqual( Node a, Node b ); + bool areDisequal( Node a, Node b ); + Node getRepresentative( Node a ); + Node getInternalRepresentative( Node a ); + /** new node */ + void newEqClass( TNode n ); + /** merge */ + void merge( TNode a, TNode b ); + /** assert terms are disequal */ + void assertDisequal( TNode a, TNode b, TNode reason ); + /** get equivalence class info */ + EqClassInfo* getEquivalenceClassInfo( Node n ); + EqClassInfo* getOrCreateEquivalenceClassInfo( Node n ); +private: + typedef std::vector< std::pair< Node, int > > InvertedPathString; + typedef std::pair< InvertedPathString, InvertedPathString > IpsPair; + /** Parent/Child Pairs (for efficient E-matching) + So, for example, if we have the pattern f( g( x ) ), then d_pc_pairs[g][f][f( g( x ) )] = { f.0 }. + */ + std::map< Node, std::map< Node, std::map< Node, std::vector< InvertedPathString > > > > d_pc_pairs; + /** Parent/Parent Pairs (for efficient E-matching) */ + std::map< Node, std::map< Node, std::map< Node, std::vector< IpsPair > > > > d_pp_pairs; + /** list of all candidate generators for each operator */ + std::map< Node, std::vector< CandidateGenerator* > > d_cand_gens; + /** map from patterns to candidate generators */ + std::map< Node, std::vector< CandidateGenerator* > > d_pat_cand_gens; + /** helper functions */ + void registerPatternElementPairs2( Node opat, Node pat, InvertedPathString& ips, + std::map< Node, std::vector< std::pair< Node, InvertedPathString > > >& ips_map ); + void registerPatternElementPairs( Node pat ); + /** compute candidates for pc pairs */ + void computeCandidatesPcPairs( Node a, Node b ); + /** compute candidates for pp pairs */ + void computeCandidatesPpPairs( Node a, Node b ); + /** collect terms based on inverted path string */ + void collectTermsIps( InvertedPathString& ips, std::vector< Node >& terms, int index = 0 ); + bool collectParentsTermsIps( Node n, Node f, int arg, std::vector< Node >& terms, bool addRep, bool modEq = true ); +public: + /** Register candidate generator cg for pattern pat. (for use with efficient e-matching) + This request will ensure that calls will be made to cg->addCandidate( n ) for all + ground terms n that are relevant for matching with pat. + */ + void registerCandidateGenerator( CandidateGenerator* cg, Node pat ); +private: + /** triggers */ + std::map< Node, std::vector< Trigger* > > d_op_triggers; +public: + /** register trigger (for eager quantifier instantiation) */ + void registerTrigger( Trigger* tr, Node op ); +public: + /** output eq class */ + void outputEqClass( const char* c, Node n ); + /** output inverted path string */ + void outputInvertedPathString( const char* c, InvertedPathString& ips ); +};/* class InstantiatorTheoryUf */ + +/** equality query object using instantiator theory uf */ +class EqualityQueryInstantiatorTheoryUf : public EqualityQuery +{ +private: + /** pointer to instantiator uf class */ + InstantiatorTheoryUf* d_ith; +public: + EqualityQueryInstantiatorTheoryUf( InstantiatorTheoryUf* ith ) : d_ith( ith ){} + ~EqualityQueryInstantiatorTheoryUf(){} + /** general queries about equality */ + bool hasTerm( Node a ) { return d_ith->hasTerm( a ); } + Node getRepresentative( Node a ) { return d_ith->getRepresentative( a ); } + bool areEqual( Node a, Node b ) { return d_ith->areEqual( a, b ); } + bool areDisequal( Node a, Node b ) { return d_ith->areDisequal( a, b ); } + Node getInternalRepresentative( Node a ) { return d_ith->getInternalRepresentative( a ); } +}; /* EqualityQueryInstantiatorTheoryUf */ + +} +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY_UF_INSTANTIATOR_H */ diff --git a/src/theory/uf/theory_uf_strong_solver.cpp b/src/theory/uf/theory_uf_strong_solver.cpp new file mode 100644 index 000000000..a793b6a57 --- /dev/null +++ b/src/theory/uf/theory_uf_strong_solver.cpp @@ -0,0 +1,1267 @@ +/********************* */ +/*! \file theory_uf_strong_solver.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of theory uf strong solver class + **/ + +#include "theory/uf/theory_uf_strong_solver.h" +#include "theory/uf/theory_uf.h" +#include "theory/uf/equality_engine.h" +#include "theory/uf/theory_uf_instantiator.h" +#include "theory/theory_engine.h" + +//#define USE_REGION_SAT + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::uf; + +void StrongSolverTheoryUf::ConflictFind::Region::takeNode( StrongSolverTheoryUf::ConflictFind::Region* r, Node n ){ + //Debug("uf-ss") << "takeNode " << r << " " << n << std::endl; + //Debug("uf-ss") << "r : " << std::endl; + //r->debugPrint("uf-ss"); + //Debug("uf-ss") << "this : " << std::endl; + //debugPrint("uf-ss"); + Assert( !hasRep( n ) ); + Assert( r->hasRep( n ) ); + //add representative + setRep( n, true ); + //take disequalities from r + RegionNodeInfo* rni = r->d_nodes[n]; + for( int t=0; t<2; t++ ){ + RegionNodeInfo::DiseqList* del = rni->d_disequalities[t]; + for( NodeBoolMap::iterator it = del->d_disequalities.begin(); it != del->d_disequalities.end(); ++it ){ + if( (*it).second ){ + r->setDisequal( n, (*it).first, t, false ); + if( t==0 ){ + if( hasRep( (*it).first ) ){ + setDisequal( (*it).first, n, 0, false ); + setDisequal( (*it).first, n, 1, true ); + setDisequal( n, (*it).first, 1, true ); + }else{ + setDisequal( n, (*it).first, 0, true ); + } + }else{ + r->setDisequal( (*it).first, n, 1, false ); + r->setDisequal( (*it).first, n, 0, true ); + setDisequal( n, (*it).first, 0, true ); + } + } + } + } + //remove representative + r->setRep( n, false ); + //Debug("uf-ss") << "done takeNode " << r << " " << n << std::endl; +} + +void StrongSolverTheoryUf::ConflictFind::Region::combine( StrongSolverTheoryUf::ConflictFind::Region* r ){ + //take all nodes from r + for( std::map< Node, RegionNodeInfo* >::iterator it = r->d_nodes.begin(); it != r->d_nodes.end(); ++it ){ + if( it->second->d_valid ){ + setRep( it->first, true ); + } + } + for( std::map< Node, RegionNodeInfo* >::iterator it = r->d_nodes.begin(); it != r->d_nodes.end(); ++it ){ + if( it->second->d_valid ){ + //take disequalities from r + Node n = it->first; + RegionNodeInfo* rni = it->second; + for( int t=0; t<2; t++ ){ + RegionNodeInfo::DiseqList* del = rni->d_disequalities[t]; + for( NodeBoolMap::iterator it2 = del->d_disequalities.begin(); it2 != del->d_disequalities.end(); ++it2 ){ + if( (*it2).second ){ + if( t==0 && hasRep( (*it2).first ) ){ + setDisequal( (*it2).first, n, 0, false ); + setDisequal( (*it2).first, n, 1, true ); + setDisequal( n, (*it2).first, 1, true ); + }else{ + setDisequal( n, (*it2).first, t, true ); + } + } + } + } + } + } + r->d_valid = false; +} + +void StrongSolverTheoryUf::ConflictFind::Region::setRep( Node n, bool valid ){ + Assert( hasRep( n )!=valid ); + if( d_nodes.find( n )==d_nodes.end() && valid ){ + d_nodes[n] = new RegionNodeInfo( d_cf->d_th->getSatContext() ); + } + d_nodes[n]->d_valid = valid; + d_reps_size = d_reps_size + ( valid ? 1 : -1 ); + if( d_testClique.find( n )!=d_testClique.end() && d_testClique[n] ){ + Assert( !valid ); + d_testClique[n] = false; + d_testCliqueSize = d_testCliqueSize - 1; + //remove all splits involving n + for( NodeBoolMap::iterator it = d_splits.begin(); it != d_splits.end(); ++it ){ + if( (*it).second ){ + if( (*it).first[0]==n || (*it).first[1]==n ){ + d_splits[ (*it).first ] = false; + d_splitsSize = d_splitsSize - 1; + } + } + } + } +} + +/** setEqual */ +void StrongSolverTheoryUf::ConflictFind::Region::setEqual( Node a, Node b ){ + Assert( hasRep( a ) && hasRep( b ) ); + //move disequalities of b over to a + for( int t=0; t<2; t++ ){ + RegionNodeInfo::DiseqList* del = d_nodes[b]->d_disequalities[t]; + for( NodeBoolMap::iterator it = del->d_disequalities.begin(); it != del->d_disequalities.end(); ++it ){ + if( (*it).second ){ + Node n = (*it).first; + Region* nr = d_cf->d_regions[ d_cf->d_regions_map[ n ] ]; + if( !isDisequal( a, n, t ) ){ + setDisequal( a, n, t, true ); + nr->setDisequal( n, a, t, true ); + } + setDisequal( b, n, t, false ); + nr->setDisequal( n, b, t, false ); + } + } + } + //remove b from representatives + setRep( b, false ); +} + +void StrongSolverTheoryUf::ConflictFind::Region::setDisequal( Node n1, Node n2, int type, bool valid ){ + //Debug("uf-ss-region-debug") << "set disequal " << n1 << " " << n2 << " " << type << " " << valid << std::endl; + //debugPrint("uf-ss-region-debug"); + //Assert( isDisequal( n1, n2, type )!=valid ); + if( isDisequal( n1, n2, type )!=valid ){ //DO_THIS: make assertion + d_nodes[ n1 ]->d_disequalities[type]->setDisequal( n2, valid ); + if( type==0 ){ + d_total_diseq_external = d_total_diseq_external + ( valid ? 1 : -1 ); + }else{ + d_total_diseq_internal = d_total_diseq_internal + ( valid ? 1 : -1 ); + if( valid ){ + //if they are both a part of testClique, then remove split + if( d_testClique.find( n1 )!=d_testClique.end() && d_testClique[n1] && + d_testClique.find( n2 )!=d_testClique.end() && d_testClique[n2] ){ + Node eq = NodeManager::currentNM()->mkNode( EQUAL, n1, n2 ); + if( d_splits.find( eq )!=d_splits.end() && d_splits[ eq ] ){ + Debug("uf-ss-debug") << "removing split for " << n1 << " " << n2 << std::endl; + d_splits[ eq ] = false; + d_splitsSize = d_splitsSize - 1; + } + } + } + } + } +} + +bool StrongSolverTheoryUf::ConflictFind::Region::isDisequal( Node n1, Node n2, int type ){ + RegionNodeInfo::DiseqList* del = d_nodes[ n1 ]->d_disequalities[type]; + return del->d_disequalities.find( n2 )!=del->d_disequalities.end() && del->d_disequalities[n2]; +} + +bool StrongSolverTheoryUf::ConflictFind::Region::getMustCombine( int cardinality ){ + if( d_total_diseq_external>=long(cardinality) ){ + //The number of external disequalities is greater than or equal to cardinality. + //Thus, a clique of size cardinality+1 may exist between nodes in d_regions[i] and other regions + //Check if this is actually the case: must have n nodes with outgoing degree (cardinality+1-n) for some n>0 + std::vector< int > degrees; + for( std::map< Node, RegionNodeInfo* >::iterator it = d_nodes.begin(); it != d_nodes.end(); ++it ){ + RegionNodeInfo* rni = it->second; + if( rni->d_valid ){ + if( rni->getNumDisequalities()>=cardinality ){ + int outDeg = rni->getNumExternalDisequalities(); + if( outDeg>=cardinality ){ + //we have 1 node of degree greater than (cardinality) + return true; + }else if( outDeg>0 ){ + degrees.push_back( outDeg ); + if( (int)degrees.size()>=cardinality ){ + //we have (cardinality) nodes of degree 1 + return true; + } + } + } + } + } + std::sort( degrees.begin(), degrees.end() ); + for( int i=0; i<(int)degrees.size(); i++ ){ + if( degrees[i]>=cardinality+1-((int)degrees.size()-i) ){ + return true; + } + } + } + return false; +} + +struct sortInternalDegree { + StrongSolverTheoryUf::ConflictFind::Region* r; + bool operator() (Node i,Node j) { return (r->d_nodes[i]->getNumInternalDisequalities()>r->d_nodes[j]->getNumInternalDisequalities());} +}; + + +bool StrongSolverTheoryUf::ConflictFind::Region::check( Theory::Effort level, int cardinality, std::vector< Node >& clique ){ + if( d_reps_size>long(cardinality) ){ + if( d_reps_size>long(cardinality) && d_total_diseq_internal==d_reps_size*( d_reps_size - 1 ) ){ + //quick clique check, all reps form a clique + for( std::map< Node, RegionNodeInfo* >::iterator it = d_nodes.begin(); it != d_nodes.end(); ++it ){ + if( it->second->d_valid ){ + clique.push_back( it->first ); + } + } + return true; + }else{ + //build test clique, up to size cardinality+1 + if( d_testCliqueSize<=long(cardinality) ){ + std::vector< Node > newClique; + if( d_testCliqueSize<long(cardinality) ){ + for( std::map< Node, RegionNodeInfo* >::iterator it = d_nodes.begin(); it != d_nodes.end(); ++it ){ + //if not in the test clique, add it to the set of new members + if( it->second->d_valid && ( d_testClique.find( it->first )==d_testClique.end() || !d_testClique[ it->first ] ) ){ + newClique.push_back( it->first ); + } + } + //choose remaining nodes with the highest degrees + sortInternalDegree sidObj; + sidObj.r = this; + std::sort( newClique.begin(), newClique.end(), sidObj ); + newClique.erase( newClique.begin() + ( cardinality - d_testCliqueSize ) + 1, newClique.end() ); + }else{ + //scan for the highest degree + int maxDeg = -1; + Node maxNode; + for( std::map< Node, RegionNodeInfo* >::iterator it = d_nodes.begin(); it != d_nodes.end(); ++it ){ + //if not in the test clique, add it to the set of new members + if( it->second->d_valid && ( d_testClique.find( it->first )==d_testClique.end() || !d_testClique[ it->first ] ) ){ + if( it->second->getNumInternalDisequalities()>maxDeg ){ + maxDeg = it->second->getNumInternalDisequalities(); + maxNode = it->first; + } + } + } + Assert( maxNode!=Node::null() ); + newClique.push_back( maxNode ); + } + //check splits internal to new members + for( int j=0; j<(int)newClique.size(); j++ ){ + Debug("uf-ss-debug") << "Choose to add clique member " << newClique[j] << std::endl; + for( int k=(j+1); k<(int)newClique.size(); k++ ){ + if( !isDisequal( newClique[j], newClique[k], 1 ) ){ + d_splits[ NodeManager::currentNM()->mkNode( EQUAL, newClique[j], newClique[k] ) ] = true; + d_splitsSize = d_splitsSize + 1; + } + } + //check disequalities with old members + for( NodeBoolMap::iterator it = d_testClique.begin(); it != d_testClique.end(); ++it ){ + if( (*it).second ){ + if( !isDisequal( (*it).first, newClique[j], 1 ) ){ + d_splits[ NodeManager::currentNM()->mkNode( EQUAL, (*it).first, newClique[j] ) ] = true; + d_splitsSize = d_splitsSize + 1; + } + } + } + } + //add new clique members to test clique + for( int j=0; j<(int)newClique.size(); j++ ){ + d_testClique[ newClique[j] ] = true; + d_testCliqueSize = d_testCliqueSize + 1; + } + } + Assert( d_testCliqueSize==long(cardinality+1) ); + if( d_splitsSize==0 ){ + //test clique is a clique + for( NodeBoolMap::iterator it = d_testClique.begin(); it != d_testClique.end(); ++it ){ + if( (*it).second ){ + clique.push_back( (*it).first ); + } + } + return true; + } + } + } + return false; +} + +Node StrongSolverTheoryUf::ConflictFind::Region::getBestSplit(){ + //take the first split you find + for( NodeBoolMap::iterator it = d_splits.begin(); it != d_splits.end(); ++it ){ + if( (*it).second ){ + return (*it).first; + } + } + return Node::null(); +} + +void StrongSolverTheoryUf::ConflictFind::Region::addSplit( OutputChannel* out ){ + Node s = getBestSplit(); + //add lemma to output channel + Assert( s!=Node::null() && s.getKind()==EQUAL ); + s = Rewriter::rewrite( s ); + Debug("uf-ss-lemma") << "*** Split on " << s << std::endl; + //Debug("uf-ss-lemma") << d_th->getEqualityEngine()->areEqual( s[0], s[1] ) << " "; + //Debug("uf-ss-lemma") << d_th->getEqualityEngine()->areDisequal( s[0], s[1] ) << std::endl; + //Debug("uf-ss-lemma") << s[0].getType() << " " << s[1].getType() << std::endl; + debugPrint("uf-ss-temp"); + //Notice() << "*** Split on " << s << std::endl; + //split on the equality s + out->split( s ); + //tell the sat solver to explore the equals branch first + out->requirePhase( s, true ); +} + +void StrongSolverTheoryUf::ConflictFind::Region::getRepresentatives( std::vector< Node >& reps ){ + for( std::map< Node, RegionNodeInfo* >::iterator it = d_nodes.begin(); it != d_nodes.end(); ++it ){ + RegionNodeInfo* rni = it->second; + if( rni->d_valid ){ + reps.push_back( it->first ); + } + } +} + +bool StrongSolverTheoryUf::ConflictFind::Region::minimize( OutputChannel* out ){ + if( hasSplits() ){ + addSplit( out ); + return false; + }else{ + return true; + } +} + +void StrongSolverTheoryUf::ConflictFind::Region::getNumExternalDisequalities( std::map< Node, int >& num_ext_disequalities ){ + for( std::map< Node, RegionNodeInfo* >::iterator it = d_nodes.begin(); it != d_nodes.end(); ++it ){ + RegionNodeInfo* rni = it->second; + if( rni->d_valid ){ + RegionNodeInfo::DiseqList* del = rni->d_disequalities[0]; + for( NodeBoolMap::iterator it2 = del->d_disequalities.begin(); it2 != del->d_disequalities.end(); ++it2 ){ + if( (*it2).second ){ + num_ext_disequalities[ (*it2).first ]++; + } + } + } + } +} + +void StrongSolverTheoryUf::ConflictFind::Region::debugPrint( const char* c, bool incClique ){ + Debug( c ) << "Num reps: " << d_reps_size << std::endl; + for( std::map< Node, RegionNodeInfo* >::iterator it = d_nodes.begin(); it != d_nodes.end(); ++it ){ + RegionNodeInfo* rni = it->second; + if( rni->d_valid ){ + Node n = it->first; + Debug( c ) << " " << n << std::endl; + for( int i=0; i<2; i++ ){ + Debug( c ) << " " << ( i==0 ? "Ext" : "Int" ) << " disequal:"; + RegionNodeInfo::DiseqList* del = rni->d_disequalities[i]; + for( NodeBoolMap::iterator it2 = del->d_disequalities.begin(); it2 != del->d_disequalities.end(); ++it2 ){ + if( (*it2).second ){ + Debug( c ) << " " << (*it2).first; + } + } + Debug( c ) << ", total = " << del->d_size << std::endl; + } + } + } + Debug( c ) << "Total disequal: " << d_total_diseq_external << " external," << std::endl; + Debug( c ) << " " << d_total_diseq_internal<< " internal." << std::endl; + + if( incClique ){ + Debug( c ) << "Candidate clique members: " << std::endl; + Debug( c ) << " "; + for( NodeBoolMap::iterator it = d_testClique.begin(); it != d_testClique.end(); ++ it ){ + if( (*it).second ){ + Debug( c ) << (*it).first << " "; + } + } + Debug( c ) << ", size = " << d_testCliqueSize << std::endl; + Debug( c ) << "Required splits: " << std::endl; + Debug( c ) << " "; + for( NodeBoolMap::iterator it = d_splits.begin(); it != d_splits.end(); ++ it ){ + if( (*it).second ){ + Debug( c ) << (*it).first << " "; + } + } + Debug( c ) << ", size = " << d_splitsSize << std::endl; + } +} + +void StrongSolverTheoryUf::ConflictFind::combineRegions( int ai, int bi ){ + Debug("uf-ss-region") << "uf-ss: Combine Region #" << bi << " with Region #" << ai << std::endl; + Assert( isValid( ai ) && isValid( bi ) ); + for( std::map< Node, Region::RegionNodeInfo* >::iterator it = d_regions[bi]->d_nodes.begin(); it != d_regions[bi]->d_nodes.end(); ++it ){ + Region::RegionNodeInfo* rni = it->second; + if( rni->d_valid ){ + d_regions_map[ it->first ] = ai; + } + } + //update regions disequal DO_THIS? + d_regions[ai]->combine( d_regions[bi] ); + d_regions[bi]->d_valid = false; +} + +void StrongSolverTheoryUf::ConflictFind::moveNode( Node n, int ri ){ + Debug("uf-ss-region") << "uf-ss: Move node " << n << " to Region #" << ri << std::endl; + Assert( isValid( d_regions_map[ n ] ) ); + Assert( isValid( ri ) ); + ////update regions disequal DO_THIS? + //Region::RegionNodeInfo::DiseqList* del = d_regions[ d_regions_map[n] ]->d_nodes[n]->d_disequalities[0]; + //for( NodeBoolMap::iterator it = del->d_disequalities.begin(); it != del->d_disequalities.end(); ++it ){ + // if( (*it).second ){ + // } + //} + //move node to region ri + d_regions[ri]->takeNode( d_regions[ d_regions_map[n] ], n ); + d_regions_map[n] = ri; +} + +int StrongSolverTheoryUf::ConflictFind::getNumDisequalitiesToRegion( Node n, int ri ){ + int ni = d_regions_map[n]; + int counter = 0; + Region::RegionNodeInfo::DiseqList* del = d_regions[ni]->d_nodes[n]->d_disequalities[0]; + for( NodeBoolMap::iterator it = del->d_disequalities.begin(); it != del->d_disequalities.end(); ++it ){ + if( (*it).second ){ + if( d_regions_map[ (*it).first ]==ri ){ + counter++; + } + } + } + return counter; +} + +void StrongSolverTheoryUf::ConflictFind::getDisequalitiesToRegions( int ri, std::map< int, int >& regions_diseq ){ + for( std::map< Node, Region::RegionNodeInfo* >::iterator it = d_regions[ri]->d_nodes.begin(); + it != d_regions[ri]->d_nodes.end(); ++it ){ + if( it->second->d_valid ){ + Region::RegionNodeInfo::DiseqList* del = it->second->d_disequalities[0]; + for( NodeBoolMap::iterator it2 = del->d_disequalities.begin(); it2 != del->d_disequalities.end(); ++it2 ){ + if( (*it2).second ){ + //if( !isValid( d_regions_map[ (*it2).first ] ) ){ + // Debug( "uf-ss-temp" ) << "^^^" << ri << " " << d_regions_map[ (*it2).first ].get() << std::endl; + // debugPrint( "uf-ss-temp" ); + //} + Assert( isValid( d_regions_map[ (*it2).first ] ) ); + //Notice() << "Found disequality with " << (*it2).first << ", region = " << d_regions_map[ (*it2).first ] << std::endl; + regions_diseq[ d_regions_map[ (*it2).first ] ]++; + } + } + } + } +} + +void StrongSolverTheoryUf::ConflictFind::explainClique( std::vector< Node >& clique, OutputChannel* out ){ + Assert( d_cardinality>0 ); + while( clique.size()>long(d_cardinality+1) ){ + clique.pop_back(); + } + //found a clique + Debug("uf-ss") << "Found a clique (cardinality=" << d_cardinality << ") :" << std::endl; + Debug("uf-ss") << " "; + for( int i=0; i<(int)clique.size(); i++ ){ + Debug("uf-ss") << clique[i] << " "; + } + Debug("uf-ss") << std::endl; + Debug("uf-ss") << "Finding clique disequalities..." << std::endl; + std::vector< Node > conflict; + //collect disequalities, and nodes that must be equal within representatives + std::map< Node, std::map< Node, bool > > explained; + std::map< Node, std::map< Node, bool > > nodesWithinRep; + for( int i=0; i<(int)d_disequalities_index; i++ ){ + //if both sides of disequality exist in clique + Node r1 = d_th->d_equalityEngine.getRepresentative( d_disequalities[i][0][0] ); + Node r2 = d_th->d_equalityEngine.getRepresentative( d_disequalities[i][0][1] ); + if( r1!=r2 && ( explained.find( r1 )==explained.end() || explained[r1].find( r2 )==explained[r1].end() ) && + std::find( clique.begin(), clique.end(), r1 )!=clique.end() && + std::find( clique.begin(), clique.end(), r2 )!=clique.end() ){ + explained[r1][r2] = true; + explained[r2][r1] = true; + conflict.push_back( d_disequalities[i] ); + nodesWithinRep[r1][ d_disequalities[i][0][0] ] = true; + nodesWithinRep[r2][ d_disequalities[i][0][1] ] = true; + if( conflict.size()==((int)clique.size()*( (int)clique.size()-1 )/2) ){ + break; + } + } + } + //Debug("uf-ss") << conflict.size() << " " << clique.size() << std::endl; + Assert( (int)conflict.size()==((int)clique.size()*( (int)clique.size()-1 )/2) ); + //Assert( (int)conflict.size()==(int)clique.size()*( (int)clique.size()-1 )/2 ); + Debug("uf-ss") << "Finding clique equalities internal to eq classes..." << std::endl; + //now, we must explain equalities within each equivalence class + for( std::map< Node, std::map< Node, bool > >::iterator it = nodesWithinRep.begin(); it != nodesWithinRep.end(); ++it ){ + if( it->second.size()>1 ){ + Node prev; + //add explanation of t1 = t2 = ... = tn + for( std::map< Node, bool >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){ + if( prev!=Node::null() ){ + //explain it2->first and prev + std::vector< TNode > expl; + d_th->d_equalityEngine.explainEquality( it2->first, prev, true, expl ); + for( int i=0; i<(int)expl.size(); i++ ){ + if( std::find( conflict.begin(), conflict.end(), expl[i] )==conflict.end() ){ + conflict.push_back( expl[i] ); + } + } + } + prev = it2->first; + } + } + } + Debug("uf-ss") << "Explanation of clique (size=" << conflict.size() << ") = " << std::endl; + for( int i=0; i<(int)conflict.size(); i++ ){ + Debug("uf-ss") << conflict[i] << " "; + } + Debug("uf-ss") << std::endl; + //now, make the conflict + Node conflictNode = conflict.size()==1 ? conflict[0] : NodeManager::currentNM()->mkNode( AND, conflict ); + //add cardinality constraint + //Node cardNode = NodeManager::currentNM()->mkNode( CARDINALITY_CONSTRAINT, d_cardinality_lemma_term, + // NodeManager::currentNM()->mkConst( Rational(d_cardinality) ) ); + Node cardNode = d_cardinality_literal[ d_cardinality ]; + conflictNode = NodeManager::currentNM()->mkNode( IMPLIES, conflictNode, cardNode.notNode() ); + Debug("uf-ss-lemma") << "*** Add clique conflict " << conflictNode << std::endl; + //Notice() << "*** Add clique conflict " << conflictNode << std::endl; + out->lemma( conflictNode ); + ++( d_th->getStrongSolver()->d_statistics.d_clique_lemmas ); + + //DO_THIS: ensure that the same clique is not reported??? Check standard effort after assertDisequal can produce same clique. +} + +/** new node */ +void StrongSolverTheoryUf::ConflictFind::newEqClass( Node n ){ + if( d_regions_map.find( n )==d_regions_map.end() ){ + d_regions_map[n] = d_regions_index; + Debug("uf-ss") << "StrongSolverTheoryUf: New Eq Class " << n << std::endl; + Debug("uf-ss-debug") << d_regions_index << " " << (int)d_regions.size() << std::endl; + if( d_regions_index<d_regions.size() ){ + d_regions[ d_regions_index ]->debugPrint("uf-ss-debug",true); + d_regions[ d_regions_index ]->d_valid = true; + //Assert( d_regions[ d_regions_index ]->d_valid ); + Assert( d_regions[ d_regions_index ]->getNumReps()==0 ); + }else{ + d_regions.push_back( new Region( this, d_th->getSatContext() ) ); + } + d_regions[ d_regions_index ]->setRep( n, true ); + d_regions_index = d_regions_index + 1; + d_reps = d_reps + 1; + } +} + +/** merge */ +void StrongSolverTheoryUf::ConflictFind::merge( Node a, Node b ){ + //Assert( a==d_th->d_equalityEngine.getRepresentative( a ) ); + //Assert( b==d_th->d_equalityEngine.getRepresentative( b ) ); + Debug("uf-ss") << "StrongSolverTheoryUf: Merging " << a << " = " << b << "..." << std::endl; + if( a!=b ){ + Assert( d_regions_map.find( a )!=d_regions_map.end() ); + Assert( d_regions_map.find( b )!=d_regions_map.end() ); + int ai = d_regions_map[a]; + int bi = d_regions_map[b]; + Debug("uf-ss") << " regions: " << ai << " " << bi << std::endl; + if( ai!=bi ){ + if( d_regions[ai]->getNumReps()==1 ){ + combineRegions( bi, ai ); + d_regions[bi]->setEqual( a, b ); + checkRegion( bi ); + }else if( d_regions[bi]->getNumReps()==1 ){ + combineRegions( ai, bi ); + d_regions[ai]->setEqual( a, b ); + checkRegion( ai ); + }else{ + // either move a to d_regions[bi], or b to d_regions[ai] + int aex = d_regions[ai]->d_nodes[a]->getNumInternalDisequalities() - getNumDisequalitiesToRegion( a, bi ); + int bex = d_regions[bi]->d_nodes[b]->getNumInternalDisequalities() - getNumDisequalitiesToRegion( b, ai ); + //based on which would produce the fewest number of external disequalities + if( aex<bex ){ + moveNode( a, bi ); + d_regions[bi]->setEqual( a, b ); + }else{ + moveNode( b, ai ); + d_regions[ai]->setEqual( a, b ); + } + checkRegion( ai ); + checkRegion( bi ); + } + }else{ + d_regions[ai]->setEqual( a, b ); + checkRegion( ai ); + } + d_reps = d_reps - 1; + d_regions_map[b] = -1; + } + Debug("uf-ss") << "Done merge." << std::endl; +} + +/** assert terms are disequal */ +void StrongSolverTheoryUf::ConflictFind::assertDisequal( Node a, Node b, Node reason ){ + //if they are not already disequal + a = d_th->d_equalityEngine.getRepresentative( a ); + b = d_th->d_equalityEngine.getRepresentative( b ); + if( !d_th->d_equalityEngine.areDisequal( a, b, true ) ){ + Debug("uf-ss") << "Assert disequal " << a << " != " << b << "..." << std::endl; + //if( reason.getKind()!=NOT || ( reason[0].getKind()!=EQUAL && reason[0].getKind()!=IFF ) || + // a!=reason[0][0] || b!=reason[0][1] ){ + // Notice() << "Assert disequal " << a << " != " << b << ", reason = " << reason << "..." << std::endl; + //} + Debug("uf-ss-disequal") << "Assert disequal " << a << " != " << b << "..." << std::endl; + //add to list of disequalities + if( d_disequalities_index<d_disequalities.size() ){ + d_disequalities[d_disequalities_index] = reason; + }else{ + d_disequalities.push_back( reason ); + } + d_disequalities_index = d_disequalities_index + 1; + //now, add disequalities to regions + Assert( d_regions_map.find( a )!=d_regions_map.end() ); + Assert( d_regions_map.find( b )!=d_regions_map.end() ); + int ai = d_regions_map[a]; + int bi = d_regions_map[b]; + Debug("uf-ss") << " regions: " << ai << " " << bi << std::endl; + if( ai==bi ){ + //internal disequality + d_regions[ai]->setDisequal( a, b, 1, true ); + d_regions[ai]->setDisequal( b, a, 1, true ); + }else{ + //external disequality + d_regions[ai]->setDisequal( a, b, 0, true ); + d_regions[bi]->setDisequal( b, a, 0, true ); + checkRegion( ai ); + checkRegion( bi ); + } + //Notice() << "done" << std::endl; + } +} + +void StrongSolverTheoryUf::ConflictFind::assertCardinality( int c, bool val ){ + Assert( d_cardinality_literal.find( c )!=d_cardinality_literal.end() ); + d_cardinality_assertions[ d_cardinality_literal[c] ] = val; + if( val ){ + d_hasCard = true; + } +} + +bool StrongSolverTheoryUf::ConflictFind::checkRegion( int ri, bool rec ){ + if( isValid(ri) ){ + Assert( d_cardinality>0 ); + //first check if region is in conflict + std::vector< Node > clique; + if( d_regions[ri]->check( Theory::EFFORT_STANDARD, d_cardinality, clique ) ){ + //explain clique + explainClique( clique, &d_th->getOutputChannel() ); + return false; + }else if( d_regions[ri]->getMustCombine( d_cardinality ) ){ + //this region must merge with another + Debug("uf-ss-check-region") << "We must combine Region #" << ri << ". " << std::endl; + d_regions[ri]->debugPrint("uf-ss-check-region"); + ////alternatively, check if we can reduce the number of external disequalities by moving single nodes + //for( std::map< Node, bool >::iterator it = d_regions[i]->d_reps.begin(); it != d_regions[i]->d_reps.end(); ++it ){ + // if( it->second ){ + // int inDeg = d_regions[i]->d_disequalities_size[1][ it-> first ]; + // int outDeg = d_regions[i]->d_disequalities_size[1][ it-> first ]; + // if( inDeg<outDeg ){ + // } + // } + //} + //take region with maximum disequality density + double maxScore = 0; + int maxRegion = -1; + std::map< int, int > regions_diseq; + getDisequalitiesToRegions( ri, regions_diseq ); + for( std::map< int, int >::iterator it = regions_diseq.begin(); it != regions_diseq.end(); ++it ){ + Debug("uf-ss-check-region") << it->first << " : " << it->second << std::endl; + } + for( std::map< int, int >::iterator it = regions_diseq.begin(); it != regions_diseq.end(); ++it ){ + Assert( it->first!=ri ); + Assert( isValid( it->first ) ); + Assert( d_regions[ it->first ]->getNumReps()>0 ); + double tempScore = double(it->second)/double(d_regions[it->first]->getNumReps() ); + if( tempScore>maxScore ){ + maxRegion = it->first; + maxScore = tempScore; + } + } + Assert( maxRegion!=-1 ); + Debug("uf-ss-check-region") << "Combine with region #" << maxRegion << ":" << std::endl; + d_regions[maxRegion]->debugPrint("uf-ss-check-region"); + combineRegions( ri, maxRegion ); + if( rec ){ + checkRegion( ri, rec ); + } + //std::vector< Node > clique; + //if( d_regions[ri]->check( Theory::EFFORT_STANDARD, cardinality, clique ) ){ + // //explain clique + // Notice() << "found clique " << std::endl; + //} + return true; + } + } + return false; +} + +bool StrongSolverTheoryUf::ConflictFind::disambiguateTerms( OutputChannel* out ){ + Debug("uf-ss-disamb") << "Disambiguate terms." << std::endl; + bool lemmaAdded = false; + //otherwise, determine ambiguous pairs of ground terms for relevant sorts + TermDb* db = d_th->getQuantifiersEngine()->getTermDatabase(); + for( std::map< Node, std::vector< Node > >::iterator it = db->d_op_map.begin(); it != db->d_op_map.end(); ++it ){ + Debug("uf-ss-disamb") << "Check " << it->first << std::endl; + if( it->second.size()>1 ){ + if( StrongSolverTheoryUf::involvesRelevantType( it->second[0] ) ){ + for( int i=0; i<(int)it->second.size(); i++ ){ + for( int j=(i+1); j<(int)it->second.size(); j++ ){ + Kind knd = it->second[i].getType()==NodeManager::currentNM()->booleanType() ? IFF : EQUAL; + Node eq = NodeManager::currentNM()->mkNode( knd, it->second[i], it->second[j] ); + eq = Rewriter::rewrite(eq); + //determine if they are ambiguous + if( d_term_amb.find( eq )==d_term_amb.end() ){ + Debug("uf-ss-disamb") << "Check disambiguate " << it->second[i] << " " << it->second[j] << std::endl; + d_term_amb[ eq ] = true; + //if they are equal + if( d_th->d_equalityEngine.areEqual( it->second[i], it->second[j] ) ){ + d_term_amb[ eq ] = false; + }else{ + //if an argument is disequal, then they are not ambiguous + for( int k=0; k<(int)it->second[i].getNumChildren(); k++ ){ + if( d_th->d_equalityEngine.areDisequal( it->second[i][k], it->second[j][k], true ) ){ + d_term_amb[ eq ] = false; + break; + } + } + } + if( d_term_amb[ eq ] ){ + Debug("uf-ss-disamb") << "Disambiguate " << it->second[i] << " " << it->second[j] << std::endl; + //must add lemma + std::vector< Node > children; + children.push_back( eq ); + for( int k=0; k<(int)it->second[i].getNumChildren(); k++ ){ + Kind knd2 = it->second[i][k].getType()==NodeManager::currentNM()->booleanType() ? IFF : EQUAL; + Node eqc = NodeManager::currentNM()->mkNode( knd2, it->second[i][k], it->second[j][k] ); + children.push_back( eqc.notNode() ); + } + Assert( children.size()>1 ); + Node lem = NodeManager::currentNM()->mkNode( OR, children ); + Debug( "uf-ss-lemma" ) << "*** Diambiguate lemma : " << lem << std::endl; + //Notice() << "*** Diambiguate lemma : " << lem << std::endl; + out->lemma( lem ); + d_term_amb[ eq ] = false; + lemmaAdded = true; + ++( d_th->getStrongSolver()->d_statistics.d_disamb_term_lemmas ); + } + } + } + } + } + } + } + Debug("uf-ss-disamb") << "Done disambiguate terms. " << lemmaAdded << std::endl; + return lemmaAdded; +} + +/** check */ +void StrongSolverTheoryUf::ConflictFind::check( Theory::Effort level, OutputChannel* out ){ + if( level>=Theory::EFFORT_STANDARD ){ + Assert( d_cardinality>0 ); + Debug("uf-ss") << "StrongSolverTheoryUf: Check " << level << " " << d_type << std::endl; + //Notice() << "StrongSolverTheoryUf: Check " << level << std::endl; + if( d_reps<=(unsigned)d_cardinality ){ + Debug("uf-ss-debug") << "We have " << d_reps << " representatives for type " << d_type << ", <= " << d_cardinality << std::endl; + if( level==Theory::EFFORT_FULL ){ + Debug("uf-ss-sat") << "We have " << d_reps << " representatives for type " << d_type << ", <= " << d_cardinality << std::endl; + //Notice() << "We have " << d_reps << " representatives for type " << d_type << ", <= " << cardinality << std::endl; + //Notice() << "Model size for " << d_type << " is " << cardinality << std::endl; + //Notice() << cardinality << " "; + } + return; + }else{ + //do a check within each region + for( int i=0; i<(int)d_regions_index; i++ ){ + if( d_regions[i]->d_valid ){ + std::vector< Node > clique; + if( d_regions[i]->check( level, d_cardinality, clique ) ){ + //explain clique + explainClique( clique, out ); + return; + }else{ + Debug("uf-ss-debug") << "No clique in Region #" << i << std::endl; + } + } + } + if( level==Theory::EFFORT_FULL ){ + Debug("uf-ss-debug") << "Add splits?" << std::endl; + //see if we have any recommended splits + bool addedLemma = false; + for( int i=0; i<(int)d_regions_index; i++ ){ + if( d_regions[i]->d_valid ){ + if( d_regions[i]->hasSplits() ){ + d_regions[i]->addSplit( out ); + addedLemma = true; + ++( d_th->getStrongSolver()->d_statistics.d_split_lemmas ); + } + } + } + if( !addedLemma ){ + Debug("uf-ss") << "No splits added." << std::endl; + if( Options::current()->fmfRegionSat ){ + //otherwise, try to disambiguate individual terms + if( !disambiguateTerms( out ) ){ + //no disequalities can be propagated + //we are in a situation where it suffices to apply a coloring to equivalence classes + //due to our invariants, we know no coloring conflicts will occur between regions, and thus + // we are SAT in this case. + Debug("uf-ss-sat") << "SAT: regions = " << getNumRegions() << std::endl; + //Notice() << "Model size for " << d_type << " is " << cardinality << ", regions = " << getNumRegions() << std::endl; + debugPrint("uf-ss-sat"); + } + }else{ + //naive strategy. combine the first two valid regions + int regIndex = -1; + for( int i=0; i<(int)d_regions_index; i++ ){ + if( d_regions[i]->d_valid ){ + if( regIndex==-1 ){ + regIndex = i; + }else{ + combineRegions( regIndex, i ); + check( level, out ); + } + } + } + } + } + } + } + } +} + +void StrongSolverTheoryUf::ConflictFind::propagate( Theory::Effort level, OutputChannel* out ){ + Assert( d_cardinality>0 ); + + //propagate the current cardinality as a decision literal + Node cn = d_cardinality_literal[ d_cardinality ]; + Debug("uf-ss-prop-as-dec") << "Propagate as decision " << d_type << ", cardinality = " << d_cardinality << std::endl; + Assert( !cn.isNull() ); + if( d_cardinality_assertions.find( cn )==d_cardinality_assertions.end() ){ + out->propagateAsDecision( d_cardinality_literal[ d_cardinality ] ); + Debug("uf-ss-prop-as-dec") << "Propagate as decision " << d_cardinality_literal[ d_cardinality ]; + Debug("uf-ss-prop-as-dec") << " " << d_cardinality_literal[ d_cardinality ][0].getType() << std::endl; + } + +} + +void StrongSolverTheoryUf::ConflictFind::debugPrint( const char* c ){ + Debug( c ) << "-- Conflict Find:" << std::endl; + Debug( c ) << "Number of reps = " << d_reps << std::endl; + Debug( c ) << "Cardinality req = " << d_cardinality << std::endl; + unsigned debugReps = 0; + for( int i=0; i<(int)d_regions_index; i++ ){ + if( d_regions[i]->d_valid ){ + Debug( c ) << "Region #" << i << ": " << std::endl; + d_regions[i]->debugPrint( c, true ); + Debug( c ) << std::endl; + for( std::map< Node, Region::RegionNodeInfo* >::iterator it = d_regions[i]->d_nodes.begin(); it != d_regions[i]->d_nodes.end(); ++it ){ + if( it->second->d_valid ){ + if( d_regions_map[ it->first ]!=i ){ + Debug( c ) << "***Bad regions map : " << it->first << " " << d_regions_map[ it->first ].get() << std::endl; + } + } + } + debugReps += d_regions[i]->getNumReps(); + } + } + if( debugReps!=d_reps ){ + Debug( c ) << "***Bad reps: " << d_reps << ", actual = " << debugReps << std::endl; + } +} + +int StrongSolverTheoryUf::ConflictFind::getNumRegions(){ + int count = 0; + for( int i=0; i<(int)d_regions_index; i++ ){ + if( d_regions[i]->d_valid ){ + count++; + } + } + return count; +} + +void StrongSolverTheoryUf::ConflictFind::setCardinality( int c, OutputChannel* out ){ + d_cardinality = c; + //add appropriate lemma + Node lem = getCardinalityLemma(); + out->lemma( lem ); + //add the appropriate lemma + Debug("uf-ss-fmf") << "Set cardinality " << d_type << " = " << c << std::endl; + Debug("uf-ss-prop-as-dec") << "Propagate as decision " << lem[0] << std::endl; + out->propagateAsDecision( lem[0] ); + d_is_cardinality_requested = true; + d_is_cardinality_requested_c = true; + //now, require old literal to be decided false + //if( d_cardinality_literal.find( c-1 )!=d_cardinality_literal.end() ){ + // Debug("uf-ss-req-phase") << "Require phase " << d_cardinality_literal[c-1] << " = false " << std::endl; + // out->requirePhase( d_cardinality_literal[c-1], false ); + //} +} + +void StrongSolverTheoryUf::ConflictFind::getRepresentatives( std::vector< Node >& reps ){ + if( !Options::current()->fmfRegionSat ){ + bool foundRegion = false; + for( int i=0; i<(int)d_regions_index; i++ ){ + //should not have multiple regions at this point + if( foundRegion ){ + Assert( !d_regions[i]->d_valid ); + } + if( d_regions[i]->d_valid ){ + //this is the only valid region + d_regions[i]->getRepresentatives( reps ); + foundRegion = true; + } + } + }else{ + Unimplemented("Build representatives for fmf region sat is not implemented"); + } +} + +bool StrongSolverTheoryUf::ConflictFind::minimize( OutputChannel* out ){ + int validRegionIndex = -1; + for( int i=0; i<(int)d_regions_index; i++ ){ + if( d_regions[i]->d_valid ){ + if( validRegionIndex!=-1 ){ + combineRegions( validRegionIndex, i ); + if( !d_regions[validRegionIndex]->minimize( out ) ){ + return false; + } + }else{ + validRegionIndex = i; + } + } + } + if( !d_regions[validRegionIndex]->minimize( out ) ){ + return false; + } + return true; +} + + +Node StrongSolverTheoryUf::ConflictFind::getCardinalityLemma(){ + if( d_cardinality_lemma.find( d_cardinality )==d_cardinality_lemma.end() ){ + if( d_cardinality_lemma_term.isNull() ){ + std::stringstream ss; + ss << "fmf_term_" << d_type; + d_cardinality_lemma_term = NodeManager::currentNM()->mkVar( ss.str(), d_type ); + ModelBasisAttribute mba; + d_cardinality_lemma_term.setAttribute(mba,true); + } + Node lem = NodeManager::currentNM()->mkNode( CARDINALITY_CONSTRAINT, d_cardinality_lemma_term, + NodeManager::currentNM()->mkConst( Rational( d_cardinality ) ) ); + lem = Rewriter::rewrite(lem); + d_cardinality_literal[ d_cardinality ] = lem; + lem = NodeManager::currentNM()->mkNode( OR, lem, lem.notNode() ); + d_cardinality_lemma[ d_cardinality ] = lem; + } + return d_cardinality_lemma[ d_cardinality ]; +} + +StrongSolverTheoryUf::StrongSolverTheoryUf(context::Context* c, context::UserContext* u, OutputChannel& out, TheoryUF* th) : +d_out( &out ), +d_th( th ), +d_conf_find_init( c ) +{ + +} + +/** new node */ +void StrongSolverTheoryUf::newEqClass( Node n ){ + TypeNode tn = n.getType(); + ConflictFind* c = getConflictFind( tn ); + if( c ){ + Debug("uf-ss-solver") << "StrongSolverTheoryUf: New eq class " << n << " " << tn << std::endl; + c->newEqClass( n ); + } + //else if( isRelevantType( tn ) ){ + // //Debug("uf-ss-solver") << "WAIT: StrongSolverTheoryUf: New eq class " << n << " " << tn << std::endl; + // //d_new_eq_class_waiting[tn].push_back( n ); + //} +} + +/** merge */ +void StrongSolverTheoryUf::merge( Node a, Node b ){ + TypeNode tn = a.getType(); + ConflictFind* c = getConflictFind( tn ); + if( c ){ + Debug("uf-ss-solver") << "StrongSolverTheoryUf: Merge " << a << " " << b << " " << tn << std::endl; + c->merge( a, b ); + } + //else if( isRelevantType( tn ) ){ + //} +} + +/** assert terms are disequal */ +void StrongSolverTheoryUf::assertDisequal( Node a, Node b, Node reason ){ + TypeNode tn = a.getType(); + ConflictFind* c = getConflictFind( tn ); + if( c ){ + Debug("uf-ss-solver") << "StrongSolverTheoryUf: Assert disequal " << a << " " << b << " " << tn << std::endl; + //Assert( d_th->d_equalityEngine.getRepresentative( a )==a ); + //Assert( d_th->d_equalityEngine.getRepresentative( b )==b ); + c->assertDisequal( a, b, reason ); + } + //else if( isRelevantType( tn ) ){ + //} +} + +/** assert a node */ +void StrongSolverTheoryUf::assertNode( Node n, bool isDecision ){ + Debug("uf-ss-assert") << "Assert " << n << " " << isDecision << std::endl; + if( n.getKind()==CARDINALITY_CONSTRAINT ){ + TypeNode tn = n[0].getType(); + Assert( d_conf_find[tn]->getCardinality()>0 ); + Assert( isRelevantType( tn ) ); + Assert( d_conf_find[tn] ); + long nCard = n[1].getConst<Rational>().getNumerator().getLong(); + d_conf_find[tn]->assertCardinality( nCard, true ); + if( nCard==d_conf_find[tn]->getCardinality() ){ + d_conf_find[tn]->d_is_cardinality_set = true; + d_conf_find[tn]->d_is_cardinality_requested = false; + d_conf_find[tn]->d_is_cardinality_requested_c = false; + } + }else if( n.getKind()==NOT && n[0].getKind()==CARDINALITY_CONSTRAINT ){ + //must add new lemma + Node nn = n[0]; + TypeNode tn = nn[0].getType(); + Assert( isRelevantType( tn ) ); + Assert( d_conf_find[tn] ); + long nCard = nn[1].getConst<Rational>().getNumerator().getLong(); + d_conf_find[tn]->assertCardinality( nCard, false ); + if( nCard==d_conf_find[tn]->getCardinality() ){ + AlwaysAssert(!isDecision, "Error: Negative cardinality node decided upon"); + Debug("uf-ss-fmf") << "No model of size " << d_conf_find[tn]->getCardinality() << " exists for type " << tn << std::endl; + //Notice() << "No model of size " << d_conf_find[tn]->getCardinality() << " exists for type " << tn << std::endl; + //increment to next cardinality + d_statistics.d_max_model_size.maxAssign( d_conf_find[tn]->getCardinality() + 1 ); + d_conf_find[tn]->setCardinality( d_conf_find[tn]->getCardinality() + 1, d_out ); + //Notice() << d_conf_find[tn]->getCardinality() << " "; + ////give up permanently on this cardinality + //d_out->lemma( n ); + } + }else{ + ////FIXME: this is too strict: theory propagations are showing up as isDecision=true, but + //// a theory propagation is not a decision. + //if( isDecision ){ + // for( std::map< TypeNode, ConflictFind* >::iterator it = d_conf_find.begin(); it != d_conf_find.end(); ++it ){ + // if( !it->second->hasCardinalityAsserted() ){ + // Notice() << "Assert " << n << " " << isDecision << std::endl; + // Notice() << "Error: constraint asserted before cardinality for " << it->first << std::endl; + // Unimplemented(); + // } + // } + //} + } +} + + +/** check */ +void StrongSolverTheoryUf::check( Theory::Effort level ){ + Debug("uf-ss-solver") << "StrongSolverTheoryUf: check " << level << std::endl; + if( level==Theory::EFFORT_FULL ){ + debugPrint( "uf-ss-debug" ); + } + for( std::map< TypeNode, ConflictFind* >::iterator it = d_conf_find.begin(); it != d_conf_find.end(); ++it ){ + it->second->check( level, d_out ); + } + Debug("uf-ss-solver") << "Done StrongSolverTheoryUf: check " << level << std::endl; +} + +/** propagate */ +void StrongSolverTheoryUf::propagate( Theory::Effort level ){ + for( std::map< TypeNode, ConflictFind* >::iterator it = d_conf_find.begin(); it != d_conf_find.end(); ++it ){ + it->second->propagate( level, d_out ); + } +} + +void StrongSolverTheoryUf::preRegisterTerm( TNode n ){ + //shouldn't have to preregister this type (it may be that there are no quantifiers over tn) FIXME + TypeNode tn = n.getType(); + if( isRelevantType( tn ) ){ + preRegisterType( tn ); + } +} + +void StrongSolverTheoryUf::registerQuantifier( Node f ){ + Debug("uf-ss-register") << "Register quantifier " << f << std::endl; + //must ensure the quantifier does not quantify over arithmetic + for( int i=0; i<(int)f[0].getNumChildren(); i++ ){ + TypeNode tn = f[0][i].getType(); + if( isRelevantType( tn ) ){ + preRegisterType( tn ); + }else{ + if( tn==NodeManager::currentNM()->integerType() || tn==NodeManager::currentNM()->realType() ){ + Debug("uf-ss-na") << "Error: Cannot perform finite model finding on arithmetic quantifier"; + Debug("uf-ss-na") << " (" << f << ")"; + Debug("uf-ss-na") << std::endl; + Unimplemented("Cannot perform finite model finding on arithmetic quantifier"); + }else if( tn.isDatatype() ){ + Debug("uf-ss-na") << "Error: Cannot perform finite model finding on datatype quantifier"; + Debug("uf-ss-na") << " (" << f << ")"; + Debug("uf-ss-na") << std::endl; + Unimplemented("Cannot perform finite model finding on datatype quantifier"); + } + } + } +} + +void StrongSolverTheoryUf::preRegisterType( TypeNode tn ){ + if( d_conf_find.find( tn )==d_conf_find.end() ){ + Debug("uf-ss-register") << "Preregister " << tn << "." << std::endl; + //enter into incremental finite model finding mode: try cardinality = 1 first + //if( !d_conf_types.empty() ){ + // Debug("uf-ss-na") << "Strong solver unimplemented for multiple sorts." << std::endl; + // Unimplemented(); + //} + d_conf_find[tn] = new ConflictFind( tn, d_th->getSatContext(), d_th ); + //assign cardinality restriction + d_statistics.d_max_model_size.maxAssign( 1 ); + d_conf_find[tn]->setCardinality( 1, d_out ); + ////add waiting equivalence classes now + //if( !d_new_eq_class_waiting[tn].empty() ){ + // Debug("uf-ss-register") << "Add " << (int)d_new_eq_class_waiting[tn].size() << " new eq classes." << std::endl; + // for( int i=0; i<(int)d_new_eq_class_waiting[tn].size(); i++ ){ + // newEqClass( d_new_eq_class_waiting[tn][i] ); + // } + // d_new_eq_class_waiting[tn].clear(); + //} + d_conf_types.push_back( tn ); + } +} + +StrongSolverTheoryUf::ConflictFind* StrongSolverTheoryUf::getConflictFind( TypeNode tn ){ + std::map< TypeNode, ConflictFind* >::iterator it = d_conf_find.find( tn ); + //pre-register the type if not done already + if( it==d_conf_find.end() ){ + if( isRelevantType( tn ) ){ + preRegisterType( tn ); + it = d_conf_find.find( tn ); + } + } + if( it!=d_conf_find.end() ){ + //initialize the type if necessary + if( d_conf_find_init.find( tn )==d_conf_find_init.end() ){ + //assign cardinality restriction + d_statistics.d_max_model_size.maxAssign( 1 ); + it->second->setCardinality( 1, d_out ); + d_conf_find_init[tn] = true; + } + return it->second; + }else{ + return NULL; + } +} + +void StrongSolverTheoryUf::notifyRestart(){ + Debug("uf-ss-prop-as-dec") << "Restart?" << std::endl; +} + +/** get cardinality for sort */ +int StrongSolverTheoryUf::getCardinality( TypeNode t ) { + ConflictFind* c = getConflictFind( t ); + if( c ){ + return c->getCardinality(); + }else{ + return -1; + } +} + +void StrongSolverTheoryUf::getRepresentatives( TypeNode t, std::vector< Node >& reps ){ + ConflictFind* c = getConflictFind( t ); + if( c ){ + c->getRepresentatives( reps ); + } +} + +Node StrongSolverTheoryUf::getCardinalityTerm( TypeNode t ){ + ConflictFind* c = getConflictFind( t ); + if( c ){ + return c->getCardinalityTerm(); + }else{ + return Node::null(); + } +} + +bool StrongSolverTheoryUf::minimize(){ + for( std::map< TypeNode, ConflictFind* >::iterator it = d_conf_find.begin(); it != d_conf_find.end(); ++it ){ + if( !it->second->minimize( d_out ) ){ + return false; + } + } + return true; +} + +//print debug +void StrongSolverTheoryUf::debugPrint( const char* c ){ + //EqClassesIterator< TheoryUF::NotifyClass > eqc_iter( &((TheoryUF*)d_th)->d_equalityEngine ); + //while( !eqc_iter.isFinished() ){ + // Debug( c ) << "Eq class [[" << (*eqc_iter) << "]]" << std::endl; + // EqClassIterator< TheoryUF::NotifyClass > eqc_iter2( *eqc_iter, &((TheoryUF*)d_th)->d_equalityEngine ); + // Debug( c ) << " "; + // while( !eqc_iter2.isFinished() ){ + // Debug( c ) << "[" << (*eqc_iter2) << "] "; + // eqc_iter2++; + // } + // Debug( c ) << std::endl; + // eqc_iter++; + //} + + for( std::map< TypeNode, ConflictFind* >::iterator it = d_conf_find.begin(); it != d_conf_find.end(); ++it ){ + Debug( c ) << "Conflict find structure for " << it->first << ": " << std::endl; + it->second->debugPrint( c ); + Debug( c ) << std::endl; + } +} + +StrongSolverTheoryUf::Statistics::Statistics(): + d_clique_lemmas("StrongSolverTheoryUf::Clique_Lemmas", 0), + d_split_lemmas("StrongSolverTheoryUf::Split_Lemmas", 0), + d_disamb_term_lemmas("StrongSolverTheoryUf::Disambiguate_Term_Lemmas", 0), + d_max_model_size("StrongSolverTheoryUf::Max_Model_Size", 0) +{ + StatisticsRegistry::registerStat(&d_clique_lemmas); + StatisticsRegistry::registerStat(&d_split_lemmas); + StatisticsRegistry::registerStat(&d_disamb_term_lemmas); + StatisticsRegistry::registerStat(&d_max_model_size); +} + +StrongSolverTheoryUf::Statistics::~Statistics(){ + StatisticsRegistry::unregisterStat(&d_clique_lemmas); + StatisticsRegistry::unregisterStat(&d_split_lemmas); + StatisticsRegistry::unregisterStat(&d_disamb_term_lemmas); + StatisticsRegistry::unregisterStat(&d_max_model_size); +} + +bool StrongSolverTheoryUf::isRelevantType( TypeNode t ){ + return t!=NodeManager::currentNM()->booleanType() && + t!=NodeManager::currentNM()->integerType() && + t!=NodeManager::currentNM()->realType() && + t!=NodeManager::currentNM()->builtinOperatorType() && + !t.isFunction() && + !t.isDatatype(); +} + +bool StrongSolverTheoryUf::involvesRelevantType( Node n ){ + if( n.getKind()==APPLY_UF ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( isRelevantType( n[i].getType() ) ){ + return true; + } + } + } + return false; +} diff --git a/src/theory/uf/theory_uf_strong_solver.h b/src/theory/uf/theory_uf_strong_solver.h new file mode 100644 index 000000000..e36441f6d --- /dev/null +++ b/src/theory/uf/theory_uf_strong_solver.h @@ -0,0 +1,322 @@ +/********************* */ +/*! \file theory_uf_strong_solver.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Theory uf strong solver + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY_UF_STRONG_SOLVER_H +#define __CVC4__THEORY_UF_STRONG_SOLVER_H + +#include "theory/theory.h" + +#include "context/context.h" +#include "context/context_mm.h" +#include "context/cdchunk_list.h" + +#include "util/stats.h" + +namespace CVC4 { +namespace theory { + +struct ModelBasisAttributeId {}; +typedef expr::Attribute<ModelBasisAttributeId, bool> ModelBasisAttribute; + +namespace uf { + +class TheoryUF; + +class StrongSolverTheoryUf{ +protected: + typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap; + typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap; + typedef context::CDChunkList<Node> NodeList; + typedef context::CDList<bool> BoolList; + typedef context::CDHashMap<TypeNode, bool, TypeNodeHashFunction> TypeNodeBoolMap; +public: + /** information for incremental conflict/clique finding for a particular sort */ + class ConflictFind { + public: + /** a partition of the current equality graph for which cliques can occur internally */ + class Region { + public: + /** conflict find pointer */ + ConflictFind* d_cf; + /** information stored about each node in region */ + class RegionNodeInfo { + public: + /** disequality list for node */ + class DiseqList { + public: + DiseqList( context::Context* c ) : d_size( c, 0 ), d_disequalities( c ){} + ~DiseqList(){} + context::CDO< unsigned > d_size; + NodeBoolMap d_disequalities; + void setDisequal( Node n, bool valid ){ + Assert( d_disequalities.find( n )==d_disequalities.end() || d_disequalities[n]!=valid ); + d_disequalities[ n ] = valid; + d_size = d_size + ( valid ? 1 : -1 ); + } + }; + private: + DiseqList d_internal; + DiseqList d_external; + public: + /** constructor */ + RegionNodeInfo( context::Context* c ) : d_internal( c ), d_external( c ), d_valid( c, true ){ + d_disequalities[0] = &d_internal; + d_disequalities[1] = &d_external; + } + ~RegionNodeInfo(){} + context::CDO< bool > d_valid; + DiseqList* d_disequalities[2]; + + int getNumDisequalities() { return d_disequalities[0]->d_size + d_disequalities[1]->d_size; } + int getNumExternalDisequalities() { return d_disequalities[0]->d_size; } + int getNumInternalDisequalities() { return d_disequalities[1]->d_size; } + }; + ///** end class RegionNodeInfo */ + private: + //a postulated clique + NodeBoolMap d_testClique; + context::CDO< unsigned > d_testCliqueSize; + //disequalities needed for this clique to happen + NodeBoolMap d_splits; + context::CDO< unsigned > d_splitsSize; + /** get split */ + Node getBestSplit(); + private: + //number of valid representatives in this region + context::CDO< unsigned > d_reps_size; + //total disequality size (external) + context::CDO< unsigned > d_total_diseq_external; + //total disequality size (internal) + context::CDO< unsigned > d_total_diseq_internal; + public: + //constructor + Region( ConflictFind* cf, context::Context* c ) : d_cf( cf ), d_testClique( c ), d_testCliqueSize( c, 0 ), + d_splits( c ), d_splitsSize( c, 0 ), d_reps_size( c, 0 ), d_total_diseq_external( c, 0 ), + d_total_diseq_internal( c, 0 ), d_valid( c, true ) { + } + ~Region(){} + //region node infomation + std::map< Node, RegionNodeInfo* > d_nodes; + //whether region is valid + context::CDO< bool > d_valid; + public: + //get num reps + int getNumReps() { return d_reps_size; } + // has representative + bool hasRep( Node n ) { return d_nodes.find( n )!=d_nodes.end() && d_nodes[n]->d_valid; } + //take node from region + void takeNode( Region* r, Node n ); + //merge with other region + void combine( Region* r ); + /** set rep */ + void setRep( Node n, bool valid ); + /** merge */ + void setEqual( Node a, Node b ); + //set n1 != n2 to value 'valid', type is whether it is internal/external + void setDisequal( Node n1, Node n2, int type, bool valid ); + // is disequal + bool isDisequal( Node n1, Node n2, int type ); + public: + /** get must merge */ + bool getMustCombine( int cardinality ); + /** check for cliques */ + bool check( Theory::Effort level, int cardinality, std::vector< Node >& clique ); + /** has splits */ + bool hasSplits() { return d_splitsSize>0; } + /** add split */ + void addSplit( OutputChannel* out ); + /** get representatives */ + void getRepresentatives( std::vector< Node >& reps ); + /** minimize */ + bool minimize( OutputChannel* out ); + /** get external disequalities */ + void getNumExternalDisequalities( std::map< Node, int >& num_ext_disequalities ); + //print debug + void debugPrint( const char* c, bool incClique = false ); + }; + private: + /** theory uf pointer */ + TheoryUF* d_th; + /** regions used to d_region_index */ + context::CDO< unsigned > d_regions_index; + /** vector of regions */ + std::vector< Region* > d_regions; + /** map from Nodes to index of d_regions they exist in, -1 means invalid */ + NodeIntMap d_regions_map; + /** regions used to d_region_index */ + context::CDO< unsigned > d_disequalities_index; + /** list of all disequalities */ + std::vector< Node > d_disequalities; + /** number of representatives in all regions */ + context::CDO< unsigned > d_reps; + /** whether two terms are ambiguous (indexed by equalities) */ + NodeBoolMap d_term_amb; + private: + /** merge regions */ + void combineRegions( int ai, int bi ); + /** move node n to region ri */ + void moveNode( Node n, int ri ); + /** get number of disequalities from node n to region ri */ + int getNumDisequalitiesToRegion( Node n, int ri ); + /** get number of disequalities from Region r to other regions */ + void getDisequalitiesToRegions( int ri, std::map< int, int >& regions_diseq ); + /** check if we need to combine region ri */ + bool checkRegion( int ri, bool rec = true ); + /** explain clique */ + void explainClique( std::vector< Node >& clique, OutputChannel* out ); + /** is valid */ + bool isValid( int ri ) { return ri>=0 && ri<(int)d_regions_index && d_regions[ ri ]->d_valid; } + /** check ambiguous terms */ + bool disambiguateTerms( OutputChannel* out ); + private: + /** cardinality operating with */ + context::CDO< int > d_cardinality; + /** type */ + TypeNode d_type; + /** cardinality lemma term */ + Node d_cardinality_lemma_term; + /** cardinality literals */ + std::map< int, Node > d_cardinality_literal; + /** cardinality lemmas */ + std::map< int, Node > d_cardinality_lemma; + /** cardinality assertions (indexed by cardinality literals ) */ + NodeBoolMap d_cardinality_assertions; + public: + ConflictFind( TypeNode tn, context::Context* c, TheoryUF* th ) : + d_th( th ), d_regions_index( c, 0 ), d_regions_map( c ), d_disequalities_index( c, 0 ), + d_reps( c, 0 ), d_term_amb( c ), d_cardinality( c, 1 ), d_type( tn ), + d_cardinality_assertions( c ), d_is_cardinality_set( c, false ), + d_is_cardinality_requested_c( c, false ), d_is_cardinality_requested( false ), d_hasCard( c, false ){} + ~ConflictFind(){} + /** new node */ + void newEqClass( Node n ); + /** merge */ + void merge( Node a, Node b ); + /** assert terms are disequal */ + void assertDisequal( Node a, Node b, Node reason ); + /** assert cardinality */ + void assertCardinality( int c, bool val ); + /** whether cardinality has been asserted */ + bool hasCardinalityAsserted() { return d_hasCard; } + /** check */ + void check( Theory::Effort level, OutputChannel* out ); + /** propagate */ + void propagate( Theory::Effort level, OutputChannel* out ); + //print debug + void debugPrint( const char* c ); + /** set cardinality */ + void setCardinality( int c, OutputChannel* out ); + /** get cardinality */ + int getCardinality() { return d_cardinality; } + /** get representatives */ + void getRepresentatives( std::vector< Node >& reps ); + /** get model basis term */ + Node getCardinalityTerm() { return d_cardinality_lemma_term; } + /** minimize */ + bool minimize( OutputChannel* out ); + /** get cardinality lemma */ + Node getCardinalityLemma(); + public: + /** get number of regions (for debugging) */ + int getNumRegions(); + /** is cardinality set */ + context::CDO< bool > d_is_cardinality_set; + context::CDO< bool > d_is_cardinality_requested_c; + bool d_is_cardinality_requested; + /** whether a positive cardinality constraint has been asserted */ + context::CDO< bool > d_hasCard; + }; /** class ConflictFind */ +private: + /** The output channel for the strong solver. */ + OutputChannel* d_out; + /** theory uf pointer */ + TheoryUF* d_th; + /** conflict find structure, one for each type */ + std::map< TypeNode, ConflictFind* > d_conf_find; + /** all types */ + std::vector< TypeNode > d_conf_types; + /** whether conflict find data structures have been initialized */ + TypeNodeBoolMap d_conf_find_init; + /** pre register type */ + void preRegisterType( TypeNode tn ); + /** get conflict find */ + ConflictFind* getConflictFind( TypeNode tn ); +public: + StrongSolverTheoryUf(context::Context* c, context::UserContext* u, OutputChannel& out, TheoryUF* th); + ~StrongSolverTheoryUf() {} + /** new node */ + void newEqClass( Node n ); + /** merge */ + void merge( Node a, Node b ); + /** assert terms are disequal */ + void assertDisequal( Node a, Node b, Node reason ); + /** assert node */ + void assertNode( Node n, bool isDecision ); +public: + /** check */ + void check( Theory::Effort level ); + /** propagate */ + void propagate( Theory::Effort level ); + /** preregister a term */ + void preRegisterTerm( TNode n ); + /** preregister a quantifier */ + void registerQuantifier( Node f ); + /** notify restart */ + void notifyRestart(); +public: + /** identify */ + std::string identify() const { return std::string("StrongSolverTheoryUf"); } + //print debug + void debugPrint( const char* c ); +public: + /** get number of types */ + int getNumCardinalityTypes() { return (int)d_conf_types.size(); } + /** get type */ + TypeNode getCardinalityType( int i ) { return d_conf_types[i]; } + /** get cardinality for sort */ + int getCardinality( TypeNode t ); + /** get representatives */ + void getRepresentatives( TypeNode t, std::vector< Node >& reps ); + /** get cardinality term */ + Node getCardinalityTerm( TypeNode t ); + /** minimize */ + bool minimize(); + + class Statistics { + public: + IntStat d_clique_lemmas; + IntStat d_split_lemmas; + IntStat d_disamb_term_lemmas; + IntStat d_max_model_size; + Statistics(); + ~Statistics(); + }; + /** statistics class */ + Statistics d_statistics; + + /** is relavant type */ + static bool isRelevantType( TypeNode t ); + /** involves relavant type */ + static bool involvesRelevantType( Node n ); +};/* class StrongSolverTheoryUf */ + +} +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY_UF_STRONG_SOLVER_H */ diff --git a/src/theory/uf/theory_uf_type_rules.h b/src/theory/uf/theory_uf_type_rules.h index 3d7e51746..b68a11abd 100644 --- a/src/theory/uf/theory_uf_type_rules.h +++ b/src/theory/uf/theory_uf_type_rules.h @@ -59,6 +59,20 @@ public: } };/* class UfTypeRule */ +class CardinalityConstraintTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw(TypeCheckingExceptionPrivate) { + if( check ) { + TypeNode valType = n[1].getType(check); + if( valType != nodeManager->integerType() ) { + throw TypeCheckingExceptionPrivate(n, "cardinality constraint must be integer"); + } + } + return nodeManager->booleanType(); + } +};/* class UfTypeRule */ + }/* CVC4::theory::uf namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/valuation.cpp b/src/theory/valuation.cpp index cae62570c..ef02f6278 100644 --- a/src/theory/valuation.cpp +++ b/src/theory/valuation.cpp @@ -100,5 +100,9 @@ Node Valuation::ensureLiteral(TNode n) { return preprocessed; } +bool Valuation::isDecision(Node lit) const { + return d_engine->getPropEngine()->isDecision(lit); +} + }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/valuation.h b/src/theory/valuation.h index ab47dcbdd..dd3848e7f 100644 --- a/src/theory/valuation.h +++ b/src/theory/valuation.h @@ -112,6 +112,14 @@ public: */ Node ensureLiteral(TNode n) CVC4_WARN_UNUSED_RESULT; + /** + * Returns whether the given lit (which must be a SAT literal) is a decision + * literal or not. Throws an exception if lit is not a SAT literal. "lit" may + * be in either phase; that is, if "lit" is a SAT literal, this function returns + * true both for lit and the negation of lit. + */ + bool isDecision(Node lit) const; + };/* class Valuation */ }/* CVC4::theory namespace */ |