diff options
Diffstat (limited to 'src/theory')
75 files changed, 3937 insertions, 3038 deletions
diff --git a/src/theory/Makefile.am b/src/theory/Makefile.am index 8f6ab76c2..8dcd14995 100644 --- a/src/theory/Makefile.am +++ b/src/theory/Makefile.am @@ -39,7 +39,9 @@ libtheory_la_SOURCES = \ quantifiers_engine.h \ quantifiers_engine.cpp \ model.h \ - model.cpp + model.cpp \ + rep_set.h \ + rep_set.cpp nodist_libtheory_la_SOURCES = \ rewriter_tables.h \ diff --git a/src/theory/arith/theory_arith.cpp b/src/theory/arith/theory_arith.cpp index ca2d74cf7..e159c0e42 100644 --- a/src/theory/arith/theory_arith.cpp +++ b/src/theory/arith/theory_arith.cpp @@ -1925,17 +1925,17 @@ DeltaRational TheoryArith::getDeltaValue(TNode n) { } } -void TheoryArith::collectModelInfo( TheoryModel* m ){ +void TheoryArith::collectModelInfo( TheoryModel* m, bool fullModel ){ Assert(d_qflraStatus == Result::SAT); - Debug("arith::collectModelInfo") << "collectModelInfo() begin " << endl; + Debug("arith::collectModelInfo") << "collectModelInfo() begin " << endl; // Delta lasts at least the duration of the function call const Rational& delta = d_partialModel.getDelta(); // TODO: // This is not very good for user push/pop.... - // Revisit when implementing push/pop + // Revisit when implementing push/pop for(ArithVar v = 0; v < d_variables.size(); ++v){ if(!isSlackVariable(v)){ Node term = d_arithvarNodeMap.asNode(v); diff --git a/src/theory/arith/theory_arith.h b/src/theory/arith/theory_arith.h index 35fcca406..a8c025452 100644 --- a/src/theory/arith/theory_arith.h +++ b/src/theory/arith/theory_arith.h @@ -323,7 +323,7 @@ public: void propagate(Effort e); Node explain(TNode n); - void collectModelInfo( TheoryModel* m ); + void collectModelInfo( TheoryModel* m, bool fullModel ); void shutdown(){ } diff --git a/src/theory/arith/theory_arith_instantiator.cpp b/src/theory/arith/theory_arith_instantiator.cpp index 51e3a6638..8d0815ee7 100644 --- a/src/theory/arith/theory_arith_instantiator.cpp +++ b/src/theory/arith/theory_arith_instantiator.cpp @@ -27,11 +27,7 @@ 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 ){ @@ -97,87 +93,6 @@ int InstStrategySimplex::process( Node f, Theory::Effort effort, int e ){ return STATUS_UNKNOWN; } -//void InstStrategySimplexUfMatch::resetInstantiationRound(){ -// -//} -// -//int InstStrategySimplexUfMatch::process( Node f, int effort ){ -// 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 ) ){ -// ++(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->getTermDatabase()->getNumInstantiationConstants( f ); i++ ){ -// Node ic = d_quantEngine->getTermDatabase()->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 ) ){ -// 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::cbqi() ){ @@ -392,59 +307,21 @@ Node InstantiatorTheoryArith::getTableauxValue( Node n, bool minus_delta ){ } Node InstantiatorTheoryArith::getTableauxValue( ArithVar v, bool minus_delta ){ + const Rational& delta = ((TheoryArith*)getTheory())->d_partialModel.getDelta(); 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()->mkSkolem( 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; + Rational qmodel = drv.substituteDelta( minus_delta ? -delta : delta ); + return mkRationalNode(qmodel); } 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) + d_instantiations_minus("InstantiatorTheoryArith::Instantiations_minus_delta", 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 index 406478a2a..a7602cf28 100644 --- a/src/theory/arith/theory_arith_instantiator.h +++ b/src/theory/arith/theory_arith_instantiator.h @@ -48,22 +48,6 @@ public: /** 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; @@ -105,16 +89,11 @@ private: int process( Node f, Theory::Effort effort, int e ); /** 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(); }; diff --git a/src/theory/arrays/theory_arrays.cpp b/src/theory/arrays/theory_arrays.cpp index 4beab2d61..0ec8e1384 100644 --- a/src/theory/arrays/theory_arrays.cpp +++ b/src/theory/arrays/theory_arrays.cpp @@ -24,6 +24,7 @@ #include "theory/rewriter.h" #include "expr/command.h" #include "theory/arrays/theory_arrays_instantiator.h" +#include "theory/arrays/theory_arrays_model.h" #include "theory/model.h" using namespace std; @@ -625,8 +626,50 @@ void TheoryArrays::computeCareGraph() // MODEL GENERATION ///////////////////////////////////////////////////////////////////////////// -void TheoryArrays::collectModelInfo( TheoryModel* m ){ +void TheoryArrays::collectModelInfo( TheoryModel* m, bool fullModel ){ m->assertEqualityEngine( &d_equalityEngine ); + //must determine proper representatives for all array equivalence classes + //first, we collect all select terms and array equivalence classes + std::map< Node, std::vector< Node > > selects; + std::vector< Node > arrays; + eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine ); + while( !eqcs_i.isFinished() ){ + Node eqc = (*eqcs_i); + if( eqc.getType().isArray() ){ + arrays.push_back( eqc ); + } + eq::EqClassIterator eqc_i = eq::EqClassIterator( eqc, &d_equalityEngine ); + while( !eqc_i.isFinished() ){ + Node n = *eqc_i; + if( n.getKind()==kind::SELECT ){ + selects[ n[0] ].push_back( n ); + } + ++eqc_i; + } + ++eqcs_i; + } + //for all array equivalence classes + for( size_t i=0; i<arrays.size(); i++ ){ + ArrayModel am( arrays[i], m ); + //set all values from existing select terms + eq::EqClassIterator eqc_i = eq::EqClassIterator( arrays[i], &d_equalityEngine ); + while( !eqc_i.isFinished() ){ + for( int i=0; i<(int)selects[ *eqc_i ].size(); i++ ){ + am.setValue( m, selects[ *eqc_i ][i][1], selects[ *eqc_i ][i] ); + } + ++eqc_i; + } + if( fullModel ){ + ////choose a representative as the default array + //am.setDefaultArray( ... ); + } + //construct the representative + Node rep = am.getArrayValue(); + Assert( !rep.isNull() ); + m->assertEquality( arrays[i], rep, true ); + //communicate to the model that it is the representative + m->assertRepresentative( rep ); + } } ///////////////////////////////////////////////////////////////////////////// diff --git a/src/theory/arrays/theory_arrays.h b/src/theory/arrays/theory_arrays.h index f7cbe8b73..aebee6817 100644 --- a/src/theory/arrays/theory_arrays.h +++ b/src/theory/arrays/theory_arrays.h @@ -219,7 +219,7 @@ class TheoryArrays : public Theory { private: public: - void collectModelInfo( TheoryModel* m ); + void collectModelInfo( TheoryModel* m, bool fullModel ); ///////////////////////////////////////////////////////////////////////////// // NOTIFICATIONS diff --git a/src/theory/arrays/theory_arrays_model.cpp b/src/theory/arrays/theory_arrays_model.cpp index 5c969060d..39fdd095f 100644 --- a/src/theory/arrays/theory_arrays_model.cpp +++ b/src/theory/arrays/theory_arrays_model.cpp @@ -16,8 +16,7 @@ #include "theory/theory_engine.h" #include "theory/arrays/theory_arrays_model.h" -#include "theory/quantifiers/first_order_model.h" -#include "theory/quantifiers/term_database.h" +#include "theory/model.h" using namespace std; using namespace CVC4; @@ -26,43 +25,41 @@ using namespace CVC4::context; using namespace CVC4::theory; using namespace CVC4::theory::arrays; -ArrayModel::ArrayModel( Node arr, quantifiers::FirstOrderModel* m ) : d_model( m ), d_arr( arr ){ - Assert( arr.getKind()!=STORE ); - //look at ground assertions - Node sel = NodeManager::currentNM()->mkNode( SELECT, arr, NodeManager::currentNM()->mkSkolem( arr.getType().getArrayIndexType() ) ); - Node sel_op = sel.getOperator(); //FIXME: easier way to do this? - for( size_t i=0; i<d_model->getTermDatabase()->d_op_map[ sel_op ].size(); i++ ){ - Node n = d_model->getTermDatabase()->d_op_map[ sel_op ][i]; - Assert( n.getKind()==SELECT ); - if( m->areEqual( n[0], arr ) ){ - //d_model->getTermDatabase()->computeModelBasisArgAttribute( n ); - //if( !n.getAttribute(NoMatchAttribute()) || n.getAttribute(ModelBasisArgAttribute())==1 ){ - Node r = d_model->getRepresentative( n ); - Node i = d_model->getRepresentative( n[1] ); - d_values[i] = r; - //} +ArrayModel::ArrayModel( Node arr, TheoryModel* m ) : d_arr( arr ){ + d_base_arr = arr; + while( d_base_arr.getKind()==STORE ){ + Node ri = m->getRepresentative( d_base_arr[1] ); + if( d_values.find( ri )==d_values.end() ){ + d_values[ ri ] = m->getRepresentative( d_base_arr[2] ); } + d_base_arr = d_base_arr[0]; } } -Node ArrayModel::getValue( Node n ){ - Assert( n.getKind()==SELECT ); - Assert( n[0]==d_arr ); - std::map< Node, Node >::iterator it = d_values.find( n[0] ); +Node ArrayModel::getValue( TheoryModel* m, Node i ){ + i = m->getRepresentative( i ); + std::map< Node, Node >::iterator it = d_values.find( i ); if( it!=d_values.end() ){ return it->second; }else{ - return n; - //return d_default_value; TODO: guarentee I can return this here + return NodeManager::currentNM()->mkNode( SELECT, getArrayValue(), i ); + //return d_default_value; //TODO: guarentee I can return this here } } -void ArrayModel::setDefaultValue( Node v ){ - d_default_value = v; +void ArrayModel::setValue( TheoryModel* m, Node i, Node e ){ + Node ri = m->getRepresentative( i ); + if( d_values.find( ri )==d_values.end() ){ + d_values[ ri ] = m->getRepresentative( e ); + } +} + +void ArrayModel::setDefaultArray( Node arr ){ + d_base_arr = arr; } Node ArrayModel::getArrayValue(){ - Node curr = d_arr; //TODO: make constant default + Node curr = d_base_arr; for( std::map< Node, Node >::iterator it = d_values.begin(); it != d_values.end(); ++it ){ curr = NodeManager::currentNM()->mkNode( STORE, curr, it->first, it->second ); } diff --git a/src/theory/arrays/theory_arrays_model.h b/src/theory/arrays/theory_arrays_model.h index 28852296d..4b93b38eb 100644 --- a/src/theory/arrays/theory_arrays_model.h +++ b/src/theory/arrays/theory_arrays_model.h @@ -1,62 +1,60 @@ -/********************* */ -/*! \file theory_arrays_model.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 MODEL for theory of arrays - **/ - - -#include "cvc4_private.h" - -#ifndef __CVC4__THEORY_ARRAYS_MODEL_H -#define __CVC4__THEORY_ARRAYS_MODEL_H - -#include "theory/quantifiers_engine.h" - -namespace CVC4 { -namespace theory { - -namespace quantifiers{ - class FirstOrderModel; -} - -namespace arrays { - -class ArrayModel{ -protected: - /** reference to model */ - quantifiers::FirstOrderModel* d_model; - /** the array this model is for */ - Node d_arr; -public: - ArrayModel(){} - ArrayModel( Node arr, quantifiers::FirstOrderModel* m ); - ~ArrayModel() {} -public: - /** pre-defined values */ - std::map< Node, Node > d_values; - /** default value */ - Node d_default_value; - /** get value, return arguments that the value depends on */ - Node getValue( Node n ); - /** set default */ - void setDefaultValue( Node v ); -public: - /** get array value */ - Node getArrayValue(); -};/* class ArrayModel */ - -} -} -} - +/********************* */
+/*! \file theory_arrays_model.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 MODEL for theory of arrays
+ **/
+
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY_ARRAYS_MODEL_H
+#define __CVC4__THEORY_ARRAYS_MODEL_H
+
+#include "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+
+class TheoryModel;
+
+namespace arrays {
+
+class ArrayModel{
+protected:
+ /** the array this model is for */
+ Node d_arr;
+public:
+ ArrayModel(){}
+ ArrayModel( Node arr, TheoryModel* m );
+ ~ArrayModel() {}
+public:
+ /** pre-defined values */
+ std::map< Node, Node > d_values;
+ /** base array */
+ Node d_base_arr;
+ /** get value, return arguments that the value depends on */
+ Node getValue( TheoryModel* m, Node i );
+ /** set value */
+ void setValue( TheoryModel* m, Node i, Node e );
+ /** set default */
+ void setDefaultArray( Node arr );
+public:
+ /** get array value */
+ Node getArrayValue();
+};/* class ArrayModel */
+
+}
+}
+}
+
#endif
\ No newline at end of file diff --git a/src/theory/booleans/theory_bool.cpp b/src/theory/booleans/theory_bool.cpp index f096987db..6e7e86e4f 100644 --- a/src/theory/booleans/theory_bool.cpp +++ b/src/theory/booleans/theory_bool.cpp @@ -32,10 +32,6 @@ namespace CVC4 { namespace theory { namespace booleans { -void TheoryBool::collectModelInfo( TheoryModel* m ){ - -} - Theory::PPAssertStatus TheoryBool::ppAssert(TNode in, SubstitutionMap& outSubstitutions) { if (in.getKind() == kind::CONST_BOOLEAN && !in.getConst<bool>()) { diff --git a/src/theory/booleans/theory_bool.h b/src/theory/booleans/theory_bool.h index 827b0ff57..45f0b4502 100644 --- a/src/theory/booleans/theory_bool.h +++ b/src/theory/booleans/theory_bool.h @@ -35,8 +35,6 @@ public: Theory(THEORY_BOOL, c, u, out, valuation, logicInfo, qe) { } - void collectModelInfo( TheoryModel* m ); - PPAssertStatus ppAssert(TNode in, SubstitutionMap& outSubstitutions); std::string identify() const { return std::string("TheoryBool"); } diff --git a/src/theory/builtin/theory_builtin.cpp b/src/theory/builtin/theory_builtin.cpp index fa176243c..90332f74a 100644 --- a/src/theory/builtin/theory_builtin.cpp +++ b/src/theory/builtin/theory_builtin.cpp @@ -27,10 +27,6 @@ namespace CVC4 { namespace theory { namespace builtin { -void TheoryBuiltin::collectModelInfo( TheoryModel* m ){ - -} - }/* CVC4::theory::builtin namespace */ }/* CVC4::theory */ }/* CVC4 namespace */ diff --git a/src/theory/builtin/theory_builtin.h b/src/theory/builtin/theory_builtin.h index 51bd7c756..3212caf01 100644 --- a/src/theory/builtin/theory_builtin.h +++ b/src/theory/builtin/theory_builtin.h @@ -31,7 +31,6 @@ class TheoryBuiltin : public Theory { public: 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) {} - void collectModelInfo( TheoryModel* m ); std::string identify() const { return std::string("TheoryBuiltin"); } };/* class TheoryBuiltin */ diff --git a/src/theory/bv/theory_bv.cpp b/src/theory/bv/theory_bv.cpp index 2bb4857a3..aa5281d2f 100644 --- a/src/theory/bv/theory_bv.cpp +++ b/src/theory/bv/theory_bv.cpp @@ -125,7 +125,7 @@ void TheoryBV::check(Effort e) } } -void TheoryBV::collectModelInfo( TheoryModel* m ){ +void TheoryBV::collectModelInfo( TheoryModel* m, bool fullModel ){ } diff --git a/src/theory/bv/theory_bv.h b/src/theory/bv/theory_bv.h index 611927b2b..30cf5ac52 100644 --- a/src/theory/bv/theory_bv.h +++ b/src/theory/bv/theory_bv.h @@ -60,7 +60,7 @@ public: Node explain(TNode n); - void collectModelInfo( TheoryModel* m ); + void collectModelInfo( TheoryModel* m, bool fullModel ); std::string identify() const { return std::string("TheoryBV"); } diff --git a/src/theory/datatypes/kinds b/src/theory/datatypes/kinds index 58c8fb5d2..eac3d6eac 100644 --- a/src/theory/datatypes/kinds +++ b/src/theory/datatypes/kinds @@ -8,7 +8,7 @@ theory THEORY_DATATYPES ::CVC4::theory::datatypes::TheoryDatatypes "theory/datat typechecker "theory/datatypes/theory_datatypes_type_rules.h" instantiator ::CVC4::theory::datatypes::InstantiatorTheoryDatatypes "theory/datatypes/theory_datatypes_instantiator.h" -properties check presolve parametric +properties check presolve parametric propagate rewriter ::CVC4::theory::datatypes::DatatypesRewriter "theory/datatypes/datatypes_rewriter.h" diff --git a/src/theory/datatypes/options b/src/theory/datatypes/options index 8a76e8134..ab627000e 100644 --- a/src/theory/datatypes/options +++ b/src/theory/datatypes/options @@ -5,4 +5,11 @@ module DATATYPES "theory/datatypes/options.h" Datatypes theory +# How to handle selectors applied to incorrect constructors. If this option is set, +# then we do not rewrite such a selector term to an arbitrary ground term. +# For example, by default cvc4 considers cdr( nil ) = nil. If this option is set, then +# cdr( nil ) has no set value. +option dtRewriteErrorSel /--disable-dt-rewrite-error-sel bool :default true + disable rewriting incorrectly applied selectors to arbitrary ground term + endmodule diff --git a/src/theory/datatypes/theory_datatypes.cpp b/src/theory/datatypes/theory_datatypes.cpp index b7f4f39d5..3305b88d8 100644 --- a/src/theory/datatypes/theory_datatypes.cpp +++ b/src/theory/datatypes/theory_datatypes.cpp @@ -23,7 +23,9 @@ #include "util/datatype.h" #include "util/Assert.h" #include "theory/datatypes/theory_datatypes_instantiator.h" +#include "theory/datatypes/datatypes_rewriter.h" #include "theory/model.h" +#include "smt/options.h" #include <map> @@ -34,51 +36,52 @@ using namespace CVC4::context; using namespace CVC4::theory; using namespace CVC4::theory::datatypes; -void TheoryDatatypes::printModelDebug(){ - /* - //std::cout << "Datatypes model : " << std::endl; +void TheoryDatatypes::printModelDebug( const char* c ){ + Trace( c ) << "Datatypes model : " << std::endl; eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine ); while( !eqcs_i.isFinished() ){ Node eqc = (*eqcs_i); - if( eqc.getType().isDatatype() || eqc.getType().isBoolean() ){ - //std::cout << eqc << " : " << eqc.getType() << " : " << std::endl; - //std::cout << " { "; - //add terms to model - eq::EqClassIterator eqc_i = eq::EqClassIterator( eqc, &d_equalityEngine ); - while( !eqc_i.isFinished() ){ - //std::cout << (*eqc_i) << " "; - ++eqc_i; - } - //std::cout << "}" << std::endl; - if( eqc.getType().isDatatype() ){ - EqcInfo* ei = getOrMakeEqcInfo( eqc ); - if( ei ){ - //std::cout << " Instantiated : " << ( ei->d_inst ? "yes" : "no" ) << std::endl; - if( ei->d_constructor.get().isNull() ){ - //std::cout << " Constructor : " << std::endl; - //std::cout << " Labels : "; - if( hasLabel( ei, eqc ) ){ - //std::cout << getLabel( eqc ); - }else{ - NodeListMap::iterator lbl_i = d_labels.find( eqc ); - if( lbl_i != d_labels.end() ){ - NodeList* lbl = (*lbl_i).second; - for( NodeList::const_iterator j = lbl->begin(); j != lbl->end(); j++ ){ - //std::cout << *j << " "; - } + if( eqc.getType().isDatatype()){ + Trace( c ) << "DATATYPE : "; + } + Trace( c ) << eqc << " : " << eqc.getType() << " : " << std::endl; + Trace( c ) << " { "; + //add terms to model + eq::EqClassIterator eqc_i = eq::EqClassIterator( eqc, &d_equalityEngine ); + while( !eqc_i.isFinished() ){ + Trace( c ) << (*eqc_i) << " "; + ++eqc_i; + } + Trace( c ) << "}" << std::endl; + if( eqc.getType().isDatatype() ){ + EqcInfo* ei = getOrMakeEqcInfo( eqc ); + if( ei ){ + Trace( c ) << " Instantiated : " << ei->d_inst.get() << std::endl; + if( ei->d_constructor.get().isNull() ){ + Trace("model-warn") << eqc << " has no constructor in equivalence class!" << std::endl; + Trace("model-warn") << " Type : " << eqc.getType() << std::endl; + Trace( c ) << " Constructor : " << std::endl; + Trace( c ) << " Labels : "; + if( hasLabel( ei, eqc ) ){ + Trace( c ) << getLabel( eqc ); + }else{ + NodeListMap::iterator lbl_i = d_labels.find( eqc ); + if( lbl_i != d_labels.end() ){ + NodeList* lbl = (*lbl_i).second; + for( NodeList::const_iterator j = lbl->begin(); j != lbl->end(); j++ ){ + Trace( c ) << *j << " "; } } - //std::cout << std::endl; - }else{ - //std::cout << " Constructor : " << ei->d_constructor.get() << std::endl; } - //std::cout << " Selectors : " << ( ei->d_selectors ? "yes" : "no" ) << std::endl; + Trace( c ) << std::endl; + }else{ + Trace( c ) << " Constructor : " << ei->d_constructor.get() << std::endl; } + Trace( c ) << " Selectors : " << ( ei->d_selectors ? "yes" : "no" ) << std::endl; } } ++eqcs_i; } - */ } @@ -129,6 +132,193 @@ TheoryDatatypes::EqcInfo* TheoryDatatypes::getOrMakeEqcInfo( Node n, bool doMake } } +void TheoryDatatypes::check(Effort e) { + + while(!done() && !d_conflict) { + // Get all the assertions + Assertion assertion = get(); + TNode fact = assertion.assertion; + Trace("datatypes-assert") << "Assert " << fact << std::endl; + //assert the fact + assertFact( fact, fact ); + flushPendingFacts(); + } + + if( e == EFFORT_FULL ) { + Debug("datatypes-split") << "Check for splits " << e << endl; + eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine ); + while( !eqcs_i.isFinished() ){ + Node n = (*eqcs_i); + if( n.getType().isDatatype() ){ + EqcInfo* eqc = getOrMakeEqcInfo( n, true ); + //if there are more than 1 possible constructors for eqc + if( eqc->d_constructor.get().isNull() && !hasLabel( eqc, n ) ) { + const Datatype& dt = ((DatatypeType)(n.getType()).toType()).getDatatype(); + //if only one constructor, then this term must be this constructor + if( dt.getNumConstructors()==1 ){ + Node t = NodeManager::currentNM()->mkNode( APPLY_TESTER, Node::fromExpr( dt[0].getTester() ), n ); + d_pending.push_back( t ); + d_pending_exp[ t ] = NodeManager::currentNM()->mkConst( true ); + Trace("datatypes-infer") << "DtInfer : " << t << ", trivial" << std::endl; + d_infer.push_back( t ); + }else{ + std::vector< bool > pcons; + getPossibleCons( eqc, n, pcons ); + //std::cout << "pcons " << n << " = "; + //for( int i=0; i<(int)pcons.size(); i++ ){ //std::cout << pcons[i] << " "; } + //std::cout << std::endl; + //check if we do not need to resolve the constructor type for this equivalence class. + // this is if there are no selectors for this equivalence class, its possible values are infinite, + // and we are not producing a model, then do not split. + int consIndex = -1; + bool needSplit = true; + for( unsigned int j=0; j<pcons.size(); j++ ) { + if( pcons[j] ) { + if( consIndex==-1 ){ + consIndex = j; + } + if( !dt[ j ].isFinite() && !eqc->d_selectors ) { + needSplit = false; + } + } + } + if( !needSplit && mustSpecifyModel() ){ + //for the sake of termination, we must choose the constructor of a ground term + //NEED GUARENTEE: groundTerm should not contain any subterms of the same type + Node groundTerm = n.getType().mkGroundTerm(); + int index = Datatype::indexOf( groundTerm.getOperator().toExpr() ); + if( pcons[index] ){ + consIndex = index; + } + needSplit = true; + } + if( needSplit && consIndex!=-1 ) { + Node test = NodeManager::currentNM()->mkNode( APPLY_TESTER, Node::fromExpr( dt[consIndex].getTester() ), n ); + Trace("dt-split") << "*************Split for possible constructor " << test << " for " << n << endl; + test = Rewriter::rewrite( test ); + NodeBuilder<> nb(kind::OR); + nb << test << test.notNode(); + Node lemma = nb; + d_out->lemma( lemma ); + d_out->requirePhase( test, true ); + return; + }else{ + Trace("dt-split") << "Do not split constructor for " << n << std::endl; + } + } + } + } + ++eqcs_i; + } + flushPendingFacts(); + //if( !d_conflict ){ + // printModelDebug(); + //} + } + + if( Debug.isOn("datatypes") || Debug.isOn("datatypes-split") ) { + Notice() << "TheoryDatatypes::check(): done" << endl; + } +} + +void TheoryDatatypes::flushPendingFacts(){ + doPendingMerges(); + if( !d_pending.empty() ){ + int i = 0; + while( !d_conflict && i<(int)d_pending.size() ){ + Node fact = d_pending[i]; + Node exp = d_pending_exp[ fact ]; + //check to see if we have to communicate it to the rest of the system + if( mustCommunicateFact( fact, exp ) ){ + Trace("dt-lemma-debug") << "Assert fact " << fact << " " << exp << std::endl; + Node lem = fact; + if( exp.isNull() || exp==NodeManager::currentNM()->mkConst( true ) ){ + lem = fact; + }else{ + Trace("dt-lemma-debug") << "Get explanation..." << std::endl; + Node ee_exp = explain( exp ); + Trace("dt-lemma-debug") << "Explanation : " << ee_exp << std::endl; + lem = NodeManager::currentNM()->mkNode( IMPLIES, ee_exp, fact ); + lem = Rewriter::rewrite( lem ); + } + Trace("dt-lemma") << "Datatypes lemma : " << lem << std::endl; + d_out->lemma( lem ); + }else{ + assertFact( fact, exp ); + } + i++; + } + d_pending.clear(); + d_pending_exp.clear(); + } +} + +void TheoryDatatypes::doPendingMerges(){ + //do all pending merges + int i=0; + while( i<(int)d_pending_merge.size() ){ + Assert( d_pending_merge[i].getKind()==EQUAL || d_pending_merge[i].getKind()==IFF ); + merge( d_pending_merge[i][0], d_pending_merge[i][1] ); + i++; + } + d_pending_merge.clear(); +} + +void TheoryDatatypes::assertFact( Node fact, Node exp ){ + Assert( d_pending_merge.empty() ); + bool polarity = fact.getKind() != kind::NOT; + TNode atom = polarity ? fact : fact[0]; + if (atom.getKind() == kind::EQUAL) { + d_equalityEngine.assertEquality( atom, polarity, exp ); + }else{ + d_equalityEngine.assertPredicate( atom, polarity, exp ); + } + doPendingMerges(); + //add to tester if applicable + if( atom.getKind()==kind::APPLY_TESTER ){ + Node rep = getRepresentative( atom[0] ); + EqcInfo* eqc = getOrMakeEqcInfo( rep, true ); + addTester( fact, eqc, rep ); + } + doPendingMerges(); +} + +void TheoryDatatypes::preRegisterTerm(TNode n) { + Debug("datatypes-prereg") << "TheoryDatatypes::preRegisterTerm() " << n << endl; + collectTerms( n ); + switch (n.getKind()) { + case kind::EQUAL: + // Add the trigger for equality + d_equalityEngine.addTriggerEquality(n); + break; + case kind::APPLY_TESTER: + // Get triggered for both equal and dis-equal + d_equalityEngine.addTriggerPredicate(n); + break; + default: + // Maybe it's a predicate + if (n.getType().isBoolean()) { + // Get triggered for both equal and dis-equal + d_equalityEngine.addTriggerPredicate(n); + } else { + // Function applications/predicates + d_equalityEngine.addTerm(n); + } + break; + } + flushPendingFacts(); +} + +void TheoryDatatypes::presolve() { + Debug("datatypes") << "TheoryDatatypes::presolve()" << endl; +} + +void TheoryDatatypes::addSharedTerm(TNode t) { + Debug("datatypes") << "TheoryDatatypes::addSharedTerm(): " + << t << endl; + d_equalityEngine.addTriggerTerm(t, THEORY_DATATYPES); +} + /** propagate */ void TheoryDatatypes::propagate(Effort effort){ @@ -142,6 +332,7 @@ bool TheoryDatatypes::propagate(TNode literal){ Debug("dt::propagate") << "TheoryDatatypes::propagate(" << literal << "): already in conflict" << std::endl; return false; } + Trace("dt-prop") << "dtPropagate " << literal << std::endl; // Propagate out bool ok = d_out->propagate(literal); if (!ok) { @@ -237,17 +428,19 @@ void TheoryDatatypes::merge( Node t1, Node t2 ){ //do unification Node unifEq = cons1.eqNode( cons2 ); for( int i=0; i<(int)cons1.getNumChildren(); i++ ) { - Node eq = cons1[i].eqNode( cons2[i] ); - d_pending.push_back( eq ); - d_pending_exp[ eq ] = unifEq; - Debug("datatypes-infer") << "DtInfer : " << eq << " by " << unifEq << std::endl; - d_infer.push_back( eq ); - d_infer_exp.push_back( unifEq ); + if( cons1[i]!=cons2[i] ){ + Node eq = cons1[i].eqNode( cons2[i] ); + d_pending.push_back( eq ); + d_pending_exp[ eq ] = unifEq; + Trace("datatypes-infer") << "DtInfer : " << eq << " by " << unifEq << std::endl; + d_infer.push_back( eq ); + d_infer_exp.push_back( unifEq ); + } } } } - if( eqc1->d_inst.get().isNull() && !eqc2->d_inst.get().isNull() ){ - eqc1->d_inst.set( eqc2->d_inst ); + if( !eqc1->d_inst && eqc2->d_inst ){ + eqc1->d_inst = true; } if( cons1.isNull() && !cons2.isNull() ){ checkInst = true; @@ -278,7 +471,7 @@ void TheoryDatatypes::merge( Node t1, Node t2 ){ checkInst = true; } if( checkInst ){ - checkInstantiate( eqc1, t1 ); + instantiate( eqc1, t1 ); if( d_conflict ){ return; } @@ -309,7 +502,7 @@ void TheoryDatatypes::eqNotifyDisequal(TNode t1, TNode t2, TNode reason){ } TheoryDatatypes::EqcInfo::EqcInfo( context::Context* c ) : -d_inst( c, Node::null() ), d_constructor( c, Node::null() ), d_selectors( c, false ){ +d_inst( c, false ), d_constructor( c, Node::null() ), d_selectors( c, false ){ } @@ -361,10 +554,12 @@ void TheoryDatatypes::addTester( Node t, EqcInfo* eqc, Node n ){ int ttindex = Datatype::indexOf( tt.getOperator().toExpr() ); Node j, jt; if( hasLabel( eqc, n ) ){ + //if we already know the constructor type, check whether it is in conflict or redundant int jtindex = getLabelIndex( eqc, n ); if( (jtindex==ttindex)!=tpolarity ){ d_conflict = true; if( !eqc->d_constructor.get().isNull() ){ + //conflict because equivalence class contains a constructor std::vector< TNode > assumptions; explain( t, assumptions ); explain( eqc->d_constructor.get().eqNode( tt[0] ), assumptions ); @@ -373,6 +568,7 @@ void TheoryDatatypes::addTester( Node t, EqcInfo* eqc, Node n ){ d_out->conflict( d_conflictNode ); return; }else{ + //conflict because the existing label is contradictory j = getLabel( n ); jt = j; } @@ -380,6 +576,7 @@ void TheoryDatatypes::addTester( Node t, EqcInfo* eqc, Node n ){ return; } }else{ + //otherwise, scan list of labels NodeListMap::iterator lbl_i = d_labels.find( n ); Assert( lbl_i != d_labels.end() ); NodeList* lbl = (*lbl_i).second; @@ -403,7 +600,7 @@ void TheoryDatatypes::addTester( Node t, EqcInfo* eqc, Node n ){ const Datatype& dt = ((DatatypeType)(tt[0].getType()).toType()).getDatatype(); Debug("datatypes-labels") << "Labels at " << lbl->size() << " / " << dt.getNumConstructors() << std::endl; if( tpolarity ){ - checkInstantiate( eqc, n ); + instantiate( eqc, n ); }else{ //check if we have reached the maximum number of testers // in this case, add the positive tester @@ -418,6 +615,7 @@ void TheoryDatatypes::addTester( Node t, EqcInfo* eqc, Node n ){ } } Assert( testerIndex!=-1 ); + //we must explain why each term in the set of testers for this equivalence class is equal std::vector< Node > eq_terms; NodeBuilder<> nb(kind::AND); for( NodeList::const_iterator i = lbl->begin(); i != lbl->end(); i++ ) { @@ -433,7 +631,7 @@ void TheoryDatatypes::addTester( Node t, EqcInfo* eqc, Node n ){ Node t_concl_exp = ( nb.getNumChildren() == 1 ) ? nb.getChild( 0 ) : nb; d_pending.push_back( t_concl ); d_pending_exp[ t_concl ] = t_concl_exp; - Debug("datatypes-infer") << "DtInfer : " << t_concl << " by " << t_concl_exp << std::endl; + Trace("datatypes-infer") << "DtInfer : " << t_concl << " by " << t_concl_exp << std::endl; d_infer.push_back( t_concl ); d_infer_exp.push_back( t_concl_exp ); return; @@ -453,162 +651,50 @@ void TheoryDatatypes::addTester( Node t, EqcInfo* eqc, Node n ){ } } - -void TheoryDatatypes::check(Effort e) { - - while(!done() && !d_conflict) { - // Get all the assertions - Assertion assertion = get(); - TNode fact = assertion.assertion; - Debug("datatypes-assert") << "Assert " << fact << std::endl; - - //reset the maps - d_pending.clear(); - d_pending_exp.clear(); - //assert the fact - assertFact( fact, fact ); - flushPendingFacts(); - } - - if( e == EFFORT_FULL ) { - Debug("datatypes-split") << "Check for splits " << e << endl; - eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine ); - while( !eqcs_i.isFinished() ){ - Node n = (*eqcs_i); - if( n.getType().isDatatype() ){ - EqcInfo* eqc = getOrMakeEqcInfo( n, true ); - //if there are more than 1 possible constructors for eqc - if( eqc->d_constructor.get().isNull() && !hasLabel( eqc, n ) ) { - const Datatype& dt = ((DatatypeType)(n.getType()).toType()).getDatatype(); - //if only one constructor, then this term must be this constructor - if( dt.getNumConstructors()==1 ){ - Node t = NodeManager::currentNM()->mkNode( APPLY_TESTER, Node::fromExpr( dt[0].getTester() ), n ); - d_pending.push_back( t ); - d_pending_exp[ t ] = NodeManager::currentNM()->mkConst( true ); - Debug("datatypes-infer") << "DtInfer : " << t << ", trivial" << std::endl; - d_infer.push_back( t ); - }else{ - std::vector< bool > pcons; - getPossibleCons( eqc, n, pcons ); - //std::cout << "pcons " << n << " = "; - //for( int i=0; i<(int)pcons.size(); i++ ){ //std::cout << pcons[i] << " "; } - //std::cout << std::endl; - //check if we do not need to resolve the constructor type for this equivalence class. - // this is if there are no selectors for this equivalence class, its type is infinite, - // and we are not producing a model, then do not split. - int consIndex = -1; - bool needSplit = true; - for( unsigned int j=0; j<pcons.size(); j++ ) { - if( pcons[j] ) { - if( consIndex==-1 ){ - consIndex = j; - } - if( !dt[ j ].isFinite() && !eqc->d_selectors ) {//&& !Options::current()->produceModels && !Options::current()->finiteModelFind ){ - needSplit = false; - } - } - } - if( needSplit && consIndex!=-1 ) { - Node test = NodeManager::currentNM()->mkNode( APPLY_TESTER, Node::fromExpr( dt[consIndex].getTester() ), n ); - Debug("datatypes-split") << "*************Split for possible constructor " << test << " for " << n << endl; - NodeBuilder<> nb(kind::OR); - nb << test << test.notNode(); - Node lemma = nb; - d_out->lemma( lemma ); - d_out->requirePhase( test, true ); - return; - }else{ - Debug("datatypes-split") << "Do not split constructor for " << n << std::endl; - } - } - } - } - ++eqcs_i; +EqualityStatus TheoryDatatypes::getEqualityStatus(TNode a, TNode b){ + if( d_equalityEngine.hasTerm(a) && d_equalityEngine.hasTerm(b) ){ + if (d_equalityEngine.areEqual(a, b)) { + // The terms are implied to be equal + return EQUALITY_TRUE; } - flushPendingFacts(); - if( !d_conflict ){ - printModelDebug(); + if (d_equalityEngine.areDisequal(a, b, false)) { + // The terms are implied to be dis-equal + return EQUALITY_FALSE; } } - if( Debug.isOn("datatypes") || Debug.isOn("datatypes-split") ) { - Notice() << "TheoryDatatypes::check(): done" << endl; - } + return EQUALITY_UNKNOWN; } -void TheoryDatatypes::assertFact( Node fact, Node exp ){ - Assert( d_pending_merge.empty() ); - bool polarity = fact.getKind() != kind::NOT; - TNode atom = polarity ? fact : fact[0]; - if (atom.getKind() == kind::EQUAL) { - d_equalityEngine.assertEquality( atom, polarity, exp ); - }else{ - d_equalityEngine.assertPredicate( atom, polarity, exp ); - } - //do all pending merges - int i=0; - while( i<(int)d_pending_merge.size() ){ - Assert( d_pending_merge[i].getKind()==EQUAL || d_pending_merge[i].getKind()==IFF ); - merge( d_pending_merge[i][0], d_pending_merge[i][1] ); - i++; - } - d_pending_merge.clear(); - //add to tester if applicable - if( atom.getKind()==kind::APPLY_TESTER ){ - Node rep = getRepresentative( atom[0] ); - EqcInfo* eqc = getOrMakeEqcInfo( rep, true ); - addTester( fact, eqc, rep ); - } +void TheoryDatatypes::computeCareGraph(){ + Theory::computeCareGraph(); } -void TheoryDatatypes::flushPendingFacts(){ - //also assert the pending facts - int i = 0; - while( !d_conflict && i<(int)d_pending.size() ){ - assertFact( d_pending[i], d_pending_exp[ d_pending[i] ] ); - i++; - } - d_pending.clear(); - d_pending_exp.clear(); -} - -void TheoryDatatypes::preRegisterTerm(TNode n) { - Debug("datatypes-prereg") << "TheoryDatatypes::preRegisterTerm() " << n << endl; - collectTerms( n ); - switch (n.getKind()) { - case kind::EQUAL: - // Add the trigger for equality - d_equalityEngine.addTriggerEquality(n); - break; - case kind::APPLY_TESTER: - // Get triggered for both equal and dis-equal - d_equalityEngine.addTriggerPredicate(n); - break; - default: - // Maybe it's a predicate - if (n.getType().isBoolean()) { - // Get triggered for both equal and dis-equal - d_equalityEngine.addTriggerPredicate(n); - } else { - // Function applications/predicates - d_equalityEngine.addTerm(n); +void TheoryDatatypes::collectModelInfo( TheoryModel* m, bool fullModel ){ + Trace("dt-model") << std::endl; + printModelDebug( "dt-model" ); + m->assertEqualityEngine( &d_equalityEngine ); + //must choose proper representatives + eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine ); + while( !eqcs_i.isFinished() ){ + Node eqc = (*eqcs_i); + //for all equivalence classes that are datatypes + if( eqc.getType().isDatatype() ){ + EqcInfo* ei = getOrMakeEqcInfo( eqc ); + if( ei ){ + if( !ei->d_constructor.get().isNull() ){ + //specify that we should use the constructor as the representative + m->assertRepresentative( ei->d_constructor.get() ); + }else{ + Trace("model-warn") << "WARNING: Datatypes: no constructor in equivalence class " << eqc << std::endl; + Trace("model-warn") << " Type : " << eqc.getType() << std::endl; + } + }else{ + Trace("model-warn") << "WARNING: Datatypes: no equivalence class info for " << eqc << std::endl; + Trace("model-warn") << " Type : " << eqc.getType() << std::endl; + } } - break; + ++eqcs_i; } - Assert( d_pending.empty() ); -} - -void TheoryDatatypes::presolve() { - Debug("datatypes") << "TheoryDatatypes::presolve()" << endl; -} - -void TheoryDatatypes::addSharedTerm(TNode t) { - Debug("datatypes") << "TheoryDatatypes::addSharedTerm(): " - << t << endl; -} - -void TheoryDatatypes::collectModelInfo( TheoryModel* m ){ - printModelDebug(); - m->assertEqualityEngine( &d_equalityEngine ); } @@ -617,12 +703,14 @@ void TheoryDatatypes::collectTerms( Node n ) { collectTerms( n[i] ); } if( n.getKind() == APPLY_CONSTRUCTOR ){ + //we must take into account subterm relation when checking for cycles for( int i=0; i<(int)n.getNumChildren(); i++ ) { Debug("datatypes-cycles") << "DtCyc: Subterm " << n << " -> " << n[i] << endl; bool result = d_cycle_check.addEdgeNode( n, n[i] ); d_hasSeenCycle.set( d_hasSeenCycle.get() || result ); } }else if( n.getKind() == APPLY_SELECTOR ){ + //we must also record which selectors exist Debug("datatypes") << " Found selector " << n << endl; if (n.getType().isBoolean()) { d_equalityEngine.addTriggerPredicate( n ); @@ -633,38 +721,58 @@ void TheoryDatatypes::collectTerms( Node n ) { EqcInfo* eqc = getOrMakeEqcInfo( rep, true ); if( !eqc->d_selectors ){ eqc->d_selectors = true; - checkInstantiate( eqc, rep ); + instantiate( eqc, rep ); } } } +void TheoryDatatypes::processNewTerm( Node n ){ + Trace("dt-terms") << "Created term : " << n << std::endl; + //see if it is rewritten to be something different + Node rn = Rewriter::rewrite( n ); + if( rn!=n ){ + Node eq = rn.eqNode( n ); + d_pending.push_back( eq ); + d_pending_exp[ eq ] = NodeManager::currentNM()->mkConst( true ); + Trace("datatypes-infer") << "DtInfer : " << eq << ", trivial" << std::endl; + d_infer.push_back( eq ); + } +} + Node TheoryDatatypes::getInstantiateCons( Node n, const Datatype& dt, int index ){ - //add constructor to equivalence class - std::vector< Node > children; - children.push_back( Node::fromExpr( dt[index].getConstructor() ) ); - for( int i=0; i<(int)dt[index].getNumArgs(); i++ ){ - children.push_back( NodeManager::currentNM()->mkNode( APPLY_SELECTOR, Node::fromExpr( dt[index][i].getSelector() ), n ) ); - } - Node n_ic = NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, children ); - collectTerms( n_ic ); - //add type ascription for ambiguous constructor types - if( n_ic.getType()!=n.getType() ){ - Assert( dt.isParametric() ); - Debug("datatypes-parametric") << "DtInstantiate: ambiguous type for " << n_ic << ", ascribe to " << n.getType() << std::endl; - Debug("datatypes-parametric") << "Constructor is " << dt[index] << std::endl; - Type tspec = dt[index].getSpecializedConstructorType(n.getType().toType()); - Debug("datatypes-parametric") << "Type specification is " << tspec << std::endl; - children[0] = NodeManager::currentNM()->mkNode(kind::APPLY_TYPE_ASCRIPTION, - NodeManager::currentNM()->mkConst(AscriptionType(tspec)), children[0] ); - n_ic = NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, children ); - Assert( n_ic.getType()==n.getType() ); - } - return n_ic; -} - -void TheoryDatatypes::checkInstantiate( EqcInfo* eqc, Node n ){ + //if( !d_inst_map[n][index].isNull() ){ + // return d_inst_map[n][index]; + //}else{ + //add constructor to equivalence class + std::vector< Node > children; + children.push_back( Node::fromExpr( dt[index].getConstructor() ) ); + for( int i=0; i<(int)dt[index].getNumArgs(); i++ ){ + Node nc = NodeManager::currentNM()->mkNode( APPLY_SELECTOR, Node::fromExpr( dt[index][i].getSelector() ), n ); + children.push_back( nc ); + processNewTerm( nc ); + } + Node n_ic = NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, children ); + collectTerms( n_ic ); + //add type ascription for ambiguous constructor types + if( n_ic.getType()!=n.getType() ){ + Assert( dt.isParametric() ); + Debug("datatypes-parametric") << "DtInstantiate: ambiguous type for " << n_ic << ", ascribe to " << n.getType() << std::endl; + Debug("datatypes-parametric") << "Constructor is " << dt[index] << std::endl; + Type tspec = dt[index].getSpecializedConstructorType(n.getType().toType()); + Debug("datatypes-parametric") << "Type specification is " << tspec << std::endl; + children[0] = NodeManager::currentNM()->mkNode(kind::APPLY_TYPE_ASCRIPTION, + NodeManager::currentNM()->mkConst(AscriptionType(tspec)), children[0] ); + n_ic = NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, children ); + Assert( n_ic.getType()==n.getType() ); + } + //d_inst_map[n][index] = n_ic; + return n_ic; + //} +} + +void TheoryDatatypes::instantiate( EqcInfo* eqc, Node n ){ //add constructor to equivalence class if not done so already - if( hasLabel( eqc, n ) && eqc->d_inst.get().isNull() ){ + if( hasLabel( eqc, n ) && !eqc->d_inst ){ Node exp; Node tt; if( !eqc->d_constructor.get().isNull() ){ @@ -677,8 +785,9 @@ void TheoryDatatypes::checkInstantiate( EqcInfo* eqc, Node n ){ int index = getLabelIndex( eqc, n ); const Datatype& dt = ((DatatypeType)(tt.getType()).toType()).getDatatype(); //must be finite or have a selector - if( eqc->d_selectors || dt[ index ].isFinite() ){ - eqc->d_inst.set( NodeManager::currentNM()->mkConst( true ) ); + if( eqc->d_selectors || dt[ index ].isFinite() || mustSpecifyModel() ){ + //instantiate this equivalence class + eqc->d_inst = true; Node tt_cons = getInstantiateCons( tt, dt, index ); Node eq; if( tt!=tt_cons ){ @@ -686,7 +795,7 @@ void TheoryDatatypes::checkInstantiate( EqcInfo* eqc, Node n ){ Debug("datatypes-inst") << "DtInstantiate : " << eqc << " " << eq << std::endl; d_pending.push_back( eq ); d_pending_exp[ eq ] = exp; - Debug("datatypes-infer") << "DtInfer : " << eq << " by " << exp << std::endl; + Trace("datatypes-infer") << "DtInfer : " << eq << " by " << exp << std::endl; //eqc->d_inst.set( eq ); d_infer.push_back( eq ); d_infer_exp.push_back( exp ); @@ -746,6 +855,46 @@ bool TheoryDatatypes::searchForCycle( Node n, Node on, return false; } +bool TheoryDatatypes::mustSpecifyModel(){ + return options::produceModels(); + //return options::finiteModelFind() || options::produceModels(); + //return false; +} + +bool TheoryDatatypes::mustCommunicateFact( Node n, Node exp ){ + //the datatypes decision procedure makes 3 "internal" inferences apart from the equality engine : + // (1) Unification : C( t1...tn ) = C( s1...sn ) => ti = si + // (2) Label : ~is_C1( t ) ... ~is_C{i-1}( t ) ~is_C{i+1}( t ) ... ~is_Cn( t ) => is_Ci( t ) + // (3) Instantiate : is_C( t ) => t = C( sel_1( t ) ... sel_n( t ) ) + //We may need to communicate (3) outwards if the conclusions involve other theories + Trace("dt-lemma-debug") << "Compute for " << exp << " => " << n << std::endl; + if( n.getKind()==EQUAL && n[1].getKind()==APPLY_CONSTRUCTOR && exp.getKind()!=EQUAL ){ + bool addLemma = false; +#if 1 + const Datatype& dt = ((DatatypeType)(n[1].getType()).toType()).getDatatype(); + addLemma = dt.involvesExternalType(); +#else + for( int j=0; j<(int)n[1].getNumChildren(); j++ ){ + if( !n[1][j].getType().isDatatype() ){ + addLemma = true; + break; + } + } +#endif + if( addLemma ){ + //check if we have already added this lemma + if( std::find( d_inst_lemmas[ n[0] ].begin(), d_inst_lemmas[ n[0] ].end(), n[1] )==d_inst_lemmas[ n[0] ].end() ){ + d_inst_lemmas[ n[0] ].push_back( n[1] ); + return true; + }else{ + return false; + } + } + Trace("dt-lemma-debug") << "Do not need to communicate " << n << std::endl; + } + return false; +} + bool TheoryDatatypes::hasTerm( Node a ){ return d_equalityEngine.hasTerm( a ); } diff --git a/src/theory/datatypes/theory_datatypes.h b/src/theory/datatypes/theory_datatypes.h index 725f9a5e6..17638b00a 100644 --- a/src/theory/datatypes/theory_datatypes.h +++ b/src/theory/datatypes/theory_datatypes.h @@ -118,7 +118,7 @@ private: EqcInfo( context::Context* c ); ~EqcInfo(){} //whether we have instantiatied this eqc - context::CDO< Node > d_inst; + context::CDO< bool > d_inst; //constructor equal to this eqc context::CDO< Node > d_constructor; //all selectors whose argument is this eqc @@ -139,6 +139,10 @@ private: eq::EqualityEngine d_equalityEngine; /** information necessary for equivalence classes */ std::map< Node, EqcInfo* > d_eqc_info; + /** map from nodes to their instantiated equivalent for each constructor type */ + std::map< Node, std::map< int, Node > > d_inst_map; + /** which instantiation lemmas we have sent */ + std::map< Node, std::vector< Node > > d_inst_lemmas; /** labels for each equivalence class * for each eqc n, d_labels[n] is testers that hold for this equivalence class, either: * a list of equations of the form @@ -162,10 +166,15 @@ private: void assertFact( Node fact, Node exp ); /** flush pending facts */ void flushPendingFacts(); + /** do pending merged */ + void doPendingMerges(); /** get or make eqc info */ EqcInfo* getOrMakeEqcInfo( Node n, bool doMake = false ); /** has eqc info */ bool hasEqcInfo( Node n ) { return d_labels.find( n )!=d_labels.end(); } +protected: + /** compute care graph */ + void computeCareGraph(); public: TheoryDatatypes(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe); @@ -192,9 +201,12 @@ public: void preRegisterTerm(TNode n); void presolve(); void addSharedTerm(TNode t); - void collectModelInfo( TheoryModel* m ); + EqualityStatus getEqualityStatus(TNode a, TNode b); + void collectModelInfo( TheoryModel* m, bool fullModel ); void shutdown() { } std::string identify() const { return std::string("TheoryDatatypes"); } + /** debug print */ + void printModelDebug( const char* c ); private: /** add tester to equivalence class info */ void addTester( Node t, EqcInfo* eqc, Node n ); @@ -209,11 +221,17 @@ private: void collectTerms( Node n ); /** get instantiate cons */ Node getInstantiateCons( Node n, const Datatype& dt, int index ); + /** process new term that was created internally */ + void processNewTerm( Node n ); /** check instantiate */ - void checkInstantiate( EqcInfo* eqc, Node n ); - /** debug print */ - void printModelDebug(); - + void instantiate( EqcInfo* eqc, Node n ); + /** must specify model + * This returns true when the datatypes theory is expected to specify the constructor + * type for all equivalence classes. + */ + bool mustSpecifyModel(); + /** must communicate fact */ + bool mustCommunicateFact( Node n, Node exp ); private: //equality queries bool hasTerm( Node a ); diff --git a/src/theory/datatypes/theory_datatypes_instantiator.cpp b/src/theory/datatypes/theory_datatypes_instantiator.cpp index 23f3e8950..0df293cf9 100644 --- a/src/theory/datatypes/theory_datatypes_instantiator.cpp +++ b/src/theory/datatypes/theory_datatypes_instantiator.cpp @@ -56,7 +56,6 @@ int InstantiatorTheoryDatatypes::process( Node f, Theory::Effort effort, int e ) if( e<2 ){ return InstStrategy::STATUS_UNFINISHED; }else if( e==2 ){ - /* InstMatch m; for( int j = 0; j<(int)d_quantEngine->getTermDatabase()->getNumInstantiationConstants( f ); j++ ){ Node i = d_quantEngine->getTermDatabase()->getInstantiationConstant( f, j ); @@ -66,18 +65,22 @@ int InstantiatorTheoryDatatypes::process( Node f, Theory::Effort effort, int e ) m.set(i,n); } } - d_quantEngine->addInstantiation( f, m ); - */ + //d_quantEngine->addInstantiation( f, m ); } } return InstStrategy::STATUS_UNKNOWN; } Node InstantiatorTheoryDatatypes::getValueFor( Node n ){ - return n; - /* FIXME //simply get the ground value for n in the current model, if it exists, // or return an arbitrary ground term otherwise + if( !n.hasAttribute(InstConstantAttribute()) ){ + return n; + }else{ + return n; + } + /* FIXME + Debug("quant-datatypes-debug") << "get value for " << n << std::endl; if( !n.hasAttribute(InstConstantAttribute()) ){ return n; diff --git a/src/theory/example/theory_uf_tim.h b/src/theory/example/theory_uf_tim.h index 5a6732bc4..09d8942fc 100644 --- a/src/theory/example/theory_uf_tim.h +++ b/src/theory/example/theory_uf_tim.h @@ -142,15 +142,6 @@ public: */ void explain(TNode n) {} - /** - * Get model - * - * See theory/theory.h for more information about this method. - */ - void collectModelInfo( TheoryModel* m ){ - Unimplemented("TheoryUFTim doesn't support model generation"); - } - std::string identify() const { return std::string("TheoryUFTim"); } private: diff --git a/src/theory/model.cpp b/src/theory/model.cpp index a194336fb..51d5a77b5 100644 --- a/src/theory/model.cpp +++ b/src/theory/model.cpp @@ -17,7 +17,9 @@ #include "theory/model.h" #include "theory/quantifiers_engine.h" #include "theory/theory_engine.h" -#include "util/datatype.h" +#include "theory/type_enumerator.h" +#include "smt/model_format_mode.h" +#include "smt/options.h" #include "theory/uf/theory_uf_model.h" using namespace std; @@ -26,52 +28,17 @@ using namespace CVC4::kind; using namespace CVC4::context; using namespace CVC4::theory; -void RepSet::clear(){ - d_type_reps.clear(); - d_tmap.clear(); -} - -void RepSet::add( Node n ){ - TypeNode t = n.getType(); - d_tmap[ n ] = (int)d_type_reps[t].size(); - d_type_reps[t].push_back( n ); -} - -void RepSet::set( TypeNode t, std::vector< Node >& reps ){ - for( size_t i=0; i<reps.size(); i++ ){ - d_tmap[ reps[i] ] = i; - } - d_type_reps[t].insert( d_type_reps[t].begin(), reps.begin(), reps.end() ); -} - -void RepSet::toStream(std::ostream& out){ -#if 0 - for( std::map< TypeNode, std::vector< Node > >::iterator it = d_type_reps.begin(); it != d_type_reps.end(); ++it ){ - out << it->first << " : " << std::endl; - for( int i=0; i<(int)it->second.size(); i++ ){ - out << " " << i << ": " << it->second[i] << std::endl; - } - } -#else - for( std::map< TypeNode, std::vector< Node > >::iterator it = d_type_reps.begin(); it != d_type_reps.end(); ++it ){ - if( !it->first.isFunction() && !it->first.isPredicate() ){ - out << "(" << it->first << " " << it->second.size(); - out << " ("; - for( int i=0; i<(int)it->second.size(); i++ ){ - if( i>0 ){ out << " "; } - out << it->second[i]; - } - out << ")"; - out << ")" << std::endl; - } - } -#endif -} - TheoryModel::TheoryModel( context::Context* c, std::string name ) : -d_equalityEngine( c, name ){ +d_substitutions( c ), d_equalityEngine( c, name ){ d_true = NodeManager::currentNM()->mkConst( true ); d_false = NodeManager::currentNM()->mkConst( false ); + // The kinds we are treating as function application in congruence + d_equalityEngine.addFunctionKind(kind::APPLY_UF); + d_equalityEngine.addFunctionKind(kind::SELECT); + d_equalityEngine.addFunctionKind(kind::STORE); + d_equalityEngine.addFunctionKind(kind::APPLY_CONSTRUCTOR); + d_equalityEngine.addFunctionKind(kind::APPLY_SELECTOR); + d_equalityEngine.addFunctionKind(kind::APPLY_TESTER); } void TheoryModel::reset(){ @@ -79,51 +46,6 @@ void TheoryModel::reset(){ d_rep_set.clear(); } -void TheoryModel::addDefineFunction( Node n ){ - d_define_funcs.push_back( n ); - d_defines.push_back( 0 ); -} - -void TheoryModel::addDefineType( TypeNode tn ){ - d_define_types.push_back( tn ); - d_defines.push_back( 1 ); -} - -void TheoryModel::toStreamFunction( Node n, std::ostream& out ){ - out << "(" << n; - out << " : " << n.getType(); - out << " "; - Node value = getValue( n ); - /* - if( n.getType().isSort() ){ - int index = d_rep_set.getIndexFor( value ); - if( index!=-1 ){ - out << value.getType() << "_" << index; - }else{ - out << value; - } - }else{ - */ - out << value; - out << ")" << std::endl; -} - -void TheoryModel::toStreamType( TypeNode tn, std::ostream& out ){ - out << "(" << tn; - if( tn.isSort() ){ - if( d_rep_set.d_type_reps.find( tn )!=d_rep_set.d_type_reps.end() ){ - out << " " << d_rep_set.d_type_reps[tn].size(); - //out << " ("; - //for( size_t i=0; i<d_rep_set.d_type_reps[tn].size(); i++ ){ - // if( i>0 ){ out << " "; } - // out << d_rep_set.d_type_reps[tn][i]; - //} - //out << ")"; - } - } - out << ")" << std::endl; -} - void TheoryModel::toStream( std::ostream& out ){ /*//for debugging eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine ); @@ -141,31 +63,21 @@ void TheoryModel::toStream( std::ostream& out ){ ++eqcs_i; } */ - int funcIndex = 0; - int typeIndex = 0; - for( size_t i=0; i<d_defines.size(); i++ ){ - if( d_defines[i]==0 ){ - toStreamFunction( d_define_funcs[funcIndex], out ); - funcIndex++; - }else if( d_defines[i]==1 ){ - toStreamType( d_define_types[typeIndex], out ); - typeIndex++; - } - } + //need this function? } -Node TheoryModel::getValue( TNode n ){ - Debug("model") << "TheoryModel::getValue " << n << std::endl; +Node TheoryModel::getModelValue( TNode n ){ + Trace("model") << "TheoryModel::getModelValue " << n << std::endl; //// special case: prop engine handles boolean vars - //if(n.isVar() && n.getType().isBoolean()) { - // Debug("model") << "-> Propositional variable." << std::endl; + //if(metakind == kind::metakind::VARIABLE && n.getType().isBoolean()) { + // Trace("model") << "-> Propositional variable." << std::endl; // return d_te->getPropEngine()->getValue( n ); //} // special case: value of a constant == itself - if(n.isConst()) { - Debug("model") << "-> Constant." << std::endl; + if( n.isConst() ) { + Trace("model") << "-> Constant." << std::endl; return n; } @@ -178,7 +90,7 @@ Node TheoryModel::getValue( TNode n ){ } //evaluate the children for( int i=0; i<(int)n.getNumChildren(); i++ ){ - Node val = getValue( n[i] ); + Node val = getModelValue( n[i] ); Debug("model-debug") << i << " : " << n[i] << " -> " << val << std::endl; Assert( !val.isNull() ); children.push_back( val ); @@ -192,16 +104,23 @@ Node TheoryModel::getValue( TNode n ){ nn = Rewriter::rewrite( nn ); // special case: value of a constant == itself - if(n.isConst()) { - Debug("model") << "-> Theory-interpreted term." << std::endl; + if( nn.isConst() ) { + Trace("model") << "-> Theory-interpreted term." << std::endl; return nn; }else{ - Debug("model") << "-> Model-interpreted term." << std::endl; + Trace("model") << "-> Model-interpreted term." << std::endl; //otherwise, get the interpreted value in the model return getInterpretedValue( nn ); } } +Node TheoryModel::getValue( TNode n ){ + //apply substitutions + Node nn = d_substitutions.apply( n ); + //get value in model + return getModelValue( nn ); +} + Node TheoryModel::getDomainValue( TypeNode tn, std::vector< Node >& exclude ){ if( d_rep_set.d_type_reps.find( tn )!=d_rep_set.d_type_reps.end() ){ //try to find a pre-existing arbitrary element @@ -214,8 +133,9 @@ Node TheoryModel::getDomainValue( TypeNode tn, std::vector< Node >& exclude ){ return Node::null(); } -//FIXME: use the theory enumerator to generate constants here +//FIXME: need to ensure that theory enumerators exist for each sort Node TheoryModel::getNewDomainValue( TypeNode tn ){ +#if 1 if( tn==NodeManager::currentNM()->booleanType() ){ if( d_rep_set.d_type_reps[tn].empty() ){ return d_false; @@ -239,6 +159,40 @@ Node TheoryModel::getNewDomainValue( TypeNode tn ){ //return NodeManager::currentNM()->mkSkolem( tn ); return Node::null(); } +#else + if( tn.isSort() ){ + return Node::null(); + }else{ + TypeEnumerator te(tn); + while( !te.isFinished() ){ + Node r = *te; + if(Debug.isOn("getNewDomainValue")) { + Debug("getNewDomainValue") << "getNewDomainValue( " << tn << ")" << endl; + Debug("getNewDomainValue") << "+ TypeEnumerator gave: " << r << endl; + Debug("getNewDomainValue") << "+ d_type_reps are:"; + for(vector<Node>::const_iterator i = d_rep_set.d_type_reps[tn].begin(); + i != d_rep_set.d_type_reps[tn].end(); + ++i) { + Debug("getNewDomainValue") << " " << *i; + } + Debug("getNewDomainValue") << endl; + } + if( std::find(d_rep_set.d_type_reps[tn].begin(), d_rep_set.d_type_reps[tn].end(), r) ==d_rep_set.d_type_reps[tn].end() ) { + Debug("getNewDomainValue") << "+ it's new, so returning " << r << endl; + return r; + } + ++te; + } + return Node::null(); + } +#endif +} + +/** add substitution */ +void TheoryModel::addSubstitution( TNode x, TNode t, bool invalidateCache ){ + if( !d_substitutions.hasSubstitution( x ) ){ + d_substitutions.addSubstitution( x, t, invalidateCache ); + } } /** assert equality */ @@ -262,7 +216,7 @@ void TheoryModel::assertEqualityEngine( const eq::EqualityEngine* ee ){ Node eqc = (*eqcs_i); bool predicate = false; bool predPolarity = false; - if( eqc.getType()==NodeManager::currentNM()->booleanType() ){ + if( eqc.getType().isBoolean() ){ predicate = true; predPolarity = ee->areEqual( eqc, d_true ); //FIXME: do we guarentee that all boolean equivalence classes contain either d_true or d_false? @@ -280,6 +234,11 @@ void TheoryModel::assertEqualityEngine( const eq::EqualityEngine* ee ){ } } +void TheoryModel::assertRepresentative( Node n ){ + Trace("model-builder-reps") << "Assert rep : " << n << std::endl; + d_reps[ n ] = n; +} + bool TheoryModel::hasTerm( Node a ){ return d_equalityEngine.hasTerm( a ); } @@ -287,7 +246,11 @@ bool TheoryModel::hasTerm( Node a ){ Node TheoryModel::getRepresentative( Node a ){ if( d_equalityEngine.hasTerm( a ) ){ Node r = d_equalityEngine.getRepresentative( a ); - return d_reps[ r ]; + if( d_reps.find( r )!=d_reps.end() ){ + return d_reps[ r ]; + }else{ + return r; + } }else{ return a; } @@ -315,7 +278,7 @@ bool TheoryModel::areDisequal( Node a, Node b ){ void TheoryModel::printRepresentativeDebug( const char* c, Node r ){ if( r.isNull() ){ Debug( c ) << "null"; - }else if( r.getType()==NodeManager::currentNM()->booleanType() ){ + }else if( r.getType().isBoolean() ){ if( areEqual( r, d_true ) ){ Debug( c ) << "true"; }else{ @@ -330,7 +293,7 @@ void TheoryModel::printRepresentative( std::ostream& out, Node r ){ Assert( !r.isNull() ); if( r.isNull() ){ out << "null"; - }else if( r.getType()==NodeManager::currentNM()->booleanType() ){ + }else if( r.getType().isBoolean() ){ if( areEqual( r, d_true ) ){ out << "true"; }else{ @@ -349,7 +312,10 @@ TheoryModel( c, name ), d_enableFuncModels( enableFuncModels ){ void DefaultModel::addTerm( Node n ){ //must collect UF terms if( d_enableFuncModels && n.getKind()==APPLY_UF ){ - d_uf_terms[ n.getOperator() ].push_back( n ); + Node op = n.getOperator(); + if( std::find( d_uf_terms[ op ].begin(), d_uf_terms[ op ].end(), n )==d_uf_terms[ op ].end() ){ + d_uf_terms[ op ].push_back( n ); + } } } @@ -374,38 +340,46 @@ Node DefaultModel::getInterpretedValue( TNode n ){ default_v = v; } if( default_v.isNull() ){ + //choose default value from model if none exists default_v = getInterpretedValue( NodeManager::currentNM()->mkSkolem( type.getRangeType() ) ); } ufmt.setDefaultValue( this, default_v ); ufmt.simplify(); - d_uf_models[n] = ufmt.getFunctionValue(); + d_uf_models[n] = ufmt.getFunctionValue( "$x" ); } return d_uf_models[n]; }else{ return n; } }else{ + Trace("model") << "Get interpreted value of " << n << std::endl; + //add term to equality engine, this will enforce a value if it exists + d_equalityEngine.addTerm( n ); //first, see if the representative is defined - if( d_equalityEngine.hasTerm( n ) ){ - n = d_equalityEngine.getRepresentative( n ); - //this check is required since d_equalityEngine.hasTerm( n ) - // does not ensure that n is in an equivalence class in d_equalityEngine - if( d_reps.find( n )!=d_reps.end() ){ - return d_reps[n]; - } + n = d_equalityEngine.getRepresentative( n ); + //this check is required since d_equalityEngine.hasTerm( n ) + // does not ensure that n is in an equivalence class in d_equalityEngine + if( d_reps.find( n )!=d_reps.end() ){ + return d_reps[n]; } //second, try to choose an existing term as value + Trace("model") << "Choose existing value..." << std::endl; std::vector< Node > v_emp; Node n2 = getDomainValue( type, v_emp ); if( !n2.isNull() ){ + //store the equality?? this is dangerous since it may cause representatives to change + //assertEquality( n, n2, true ); return n2; }else{ //otherwise, choose new value + Trace("model") << "Choose new value..." << std::endl; n2 = getNewDomainValue( type ); if( !n2.isNull() ){ + //store the equality?? + //assertEquality( n, n2, true ); return n2; }else{ - //otherwise, just return itself + //otherwise, just return itself (this usually should not happen) return n; } } @@ -416,191 +390,90 @@ TheoryEngineModelBuilder::TheoryEngineModelBuilder( TheoryEngine* te ) : d_te( t } -void TheoryEngineModelBuilder::buildModel( Model* m ){ +void TheoryEngineModelBuilder::buildModel( Model* m, bool fullModel ){ TheoryModel* tm = (TheoryModel*)m; //reset representative information tm->reset(); //collect model info from the theory engine - Debug( "model-builder" ) << "TheoryEngineModelBuilder: Collect model info..." << std::endl; - d_te->collectModelInfo( tm ); - //unresolved equivalence classes - std::map< Node, bool > unresolved_eqc; - std::map< TypeNode, bool > unresolved_types; - std::map< Node, std::vector< Node > > selects; - std::map< Node, Node > apply_constructors; - Debug( "model-builder" ) << "TheoryEngineModelBuilder: Build representatives..." << std::endl; - //populate term database, store constant representatives + Trace("model-builder") << "TheoryEngineModelBuilder: Collect model info..." << std::endl; + d_te->collectModelInfo( tm, fullModel ); + Trace("model-builder") << "Collect representatives..." << std::endl; + //store asserted representative map + std::map< Node, Node > assertedReps; + //process all terms in the equality engine, store representatives eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &tm->d_equalityEngine ); while( !eqcs_i.isFinished() ){ Node eqc = (*eqcs_i); - TypeNode eqct = eqc.getType(); - //initialize unresolved type information - initializeType( eqct, unresolved_types ); - //add terms to model, get constant rep if possible - Node const_rep; - eq::EqClassIterator eqc_i = eq::EqClassIterator( eqc, &tm->d_equalityEngine ); - while( !eqc_i.isFinished() ){ - Node n = *eqc_i; - //check if this is constant, if so, we will use it as representative - if( n.isConst() ){ - const_rep = n; - } - //theory-specific information needed - if( n.getKind()==SELECT ){ - selects[ n[0] ].push_back( n ); - }else if( n.getKind()==APPLY_CONSTRUCTOR ){ - apply_constructors[ eqc ] = n; - } - //model-specific processing of the term, this will include - tm->addTerm( n ); - ++eqc_i; - } - //store representative in representative set - if( !const_rep.isNull() ){ - //Message() << "Constant rep " << const_rep << " for " << eqc << std::endl; - tm->d_reps[ eqc ] = const_rep; - tm->d_rep_set.add( const_rep ); + if( assertedReps.find( eqc )!=assertedReps.end() ){ + Trace("model-warn") << "Duplicate equivalence class!!!! " << eqc << std::endl; }else{ - //Message() << "** unresolved eqc " << eqc << std::endl; - unresolved_eqc[ eqc ] = true; - unresolved_types[ eqct ] = true; - } - ++eqcs_i; - } - //choose representatives for unresolved equivalence classes - Debug( "model-builder" ) << "TheoryEngineModelBuilder: Complete model..." << std::endl; - bool fixedPoint; - do{ - fixedPoint = true; - //for calculating unresolved types - std::map< TypeNode, bool > unresolved_types_next; - for( std::map< TypeNode, bool >::iterator it = unresolved_types.begin(); it != unresolved_types.end(); ++it ){ - unresolved_types_next[ it->first ] = false; - } - //try to resolve each unresolved equivalence class - for( std::map< Node, bool >::iterator it = unresolved_eqc.begin(); it != unresolved_eqc.end(); ++it ){ - if( it->second ){ - Node n = it->first; - TypeNode tn = n.getType(); - Node rep; - bool mkRep = true; - if( tn.isArray() ){ - TypeNode index_t = tn.getArrayIndexType(); - TypeNode elem_t = tn.getArrayConstituentType(); - if( !unresolved_types[ index_t ] && !unresolved_types[ elem_t ] ){ - //collect all relevant set values of n - std::vector< Node > arr_selects; - std::vector< Node > arr_select_values; - Node nbase = n; - while( nbase.getKind()==STORE ){ - arr_selects.push_back( tm->getRepresentative( nbase[1] ) ); - arr_select_values.push_back( tm->getRepresentative( nbase[2] ) ); - nbase = nbase[0]; - } - eq::EqClassIterator eqc_i = eq::EqClassIterator( n, &tm->d_equalityEngine ); - while( !eqc_i.isFinished() ){ - for( int i=0; i<(int)selects[ *eqc_i ].size(); i++ ){ - Node r = tm->getRepresentative( selects[ *eqc_i ][i][1] ); - if( std::find( arr_selects.begin(), arr_selects.end(), r )==arr_selects.end() ){ - arr_selects.push_back( r ); - arr_select_values.push_back( tm->getRepresentative( selects[ *eqc_i ][i] ) ); - } - } - ++eqc_i; - } - //now, construct based on select/value pairs - //TODO: make this a constant - rep = chooseRepresentative( tm, nbase ); - for( int i=0; i<(int)arr_selects.size(); i++ ){ - rep = NodeManager::currentNM()->mkNode( STORE, rep, arr_selects[i], arr_select_values[i] ); - } - } - mkRep = false; - }else if( tn.isDatatype() ){ - if( apply_constructors.find( n )!=apply_constructors.end() ){ - Node ac = apply_constructors[n]; - std::vector< Node > children; - children.push_back( ac.getOperator() ); - for( size_t i = 0; i<ac.getNumChildren(); i++ ){ - Node acir = ac[i]; - if( tm->d_equalityEngine.hasTerm( acir ) ){ - acir = tm->d_equalityEngine.getRepresentative( acir ); - } - if( unresolved_eqc.find( acir )==unresolved_eqc.end() ){ - Message() << "TheoryEngineModelBuilder::buildModel : Datatype argument does not exist in the model " << acir << std::endl; - acir = Node::null(); - } - if( acir.isNull() || unresolved_eqc[ acir ] ){ - mkRep = false; - break; - }else{ - children.push_back( tm->getRepresentative( acir ) ); - } - } - if( mkRep ){ - rep = NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, children ); - } + TypeNode eqct = eqc.getType(); + Node const_rep; + eq::EqClassIterator eqc_i = eq::EqClassIterator( eqc, &tm->d_equalityEngine ); + while( !eqc_i.isFinished() ){ + Node n = *eqc_i; + //if this node was specified as a representative + if( tm->d_reps.find( n )!=tm->d_reps.end() ){ + Assert( !tm->d_reps[n].isNull() ); + //if not already specified + if( assertedReps.find( eqc )==assertedReps.end() ){ + Trace("model-builder") << "Rep( " << eqc << " ) = " << tm->d_reps[n] << std::endl; + assertedReps[ eqc ] = tm->d_reps[n]; }else{ - Message() << "TheoryEngineModelBuilder::buildModel : Do not know how to resolve datatype equivalence class " << n << std::endl; + if( n!=assertedReps[eqc] ){ //FIXME : this should be an assertion (EqClassIterator should not give duplicates) + //duplicate representative specified + Trace("model-warn") << "Duplicate representative specified for equivalence class " << eqc << ": " << std::endl; + Trace("model-warn") << " " << assertedReps[eqc] << ", " << n << std::endl; + Trace("model-warn") << " Type : " << n.getType() << std::endl; + } } - mkRep = false; - } - if( mkRep ){ - rep = chooseRepresentative( tm, n ); + }else if( n.isConst() ){ + //if this is constant, we will use it as representative (if none other specified) + const_rep = n; } - if( !rep.isNull() ){ - tm->assertEquality( n, rep, true ); - tm->d_reps[ n ] = rep; - tm->d_rep_set.add( rep ); - unresolved_eqc[ n ] = false; - fixedPoint = false; + //model-specific processing of the term + tm->addTerm( n ); + ++eqc_i; + } + //if a representative was not specified + if( assertedReps.find( eqc )==assertedReps.end() ){ + if( !const_rep.isNull() ){ + //use the constant representative + assertedReps[ eqc ] = const_rep; }else{ - unresolved_types_next[ tn ] = true; + if( fullModel ){ + //assertion failure? + Trace("model-warn") << "No representative specified for equivalence class " << eqc << std::endl; + Trace("model-warn") << " Type : " << eqc.getType() << std::endl; + } + //assertedReps[ eqc ] = chooseRepresentative( tm, eqc, fullModel ); + assertedReps[ eqc ] = eqc; } } } - //for calculating unresolved types - for( std::map< TypeNode, bool >::iterator it = unresolved_types.begin(); it != unresolved_types.end(); ++it ){ - unresolved_types[ it->first ] = unresolved_types_next[ it->first ]; - } - }while( !fixedPoint ); - - //for all unresolved equivalence classes, just get new domain value - // this should typically never happen (all equivalence classes should be resolved at this point) - for( std::map< Node, bool >::iterator it = unresolved_eqc.begin(); it != unresolved_eqc.end(); ++it ){ - if( it->second ){ - Node n = it->first; - Node rep = chooseRepresentative( tm, n ); - tm->assertEquality( n, rep, true ); - tm->d_reps[ n ] = rep; - tm->d_rep_set.add( rep ); - //FIXME: Assertion failure here? - Message() << "Warning : Unresolved eqc, assigning " << rep << " for eqc( " << n << " ), type = " << n.getType() << std::endl; - } + ++eqcs_i; } - - //model-specific initialization - processBuildModel( tm ); -} - -void TheoryEngineModelBuilder::initializeType( TypeNode tn, std::map< TypeNode, bool >& unresolved_types ){ - if( unresolved_types.find( tn )==unresolved_types.end() ){ - unresolved_types[tn] = false; - if( tn.isArray() ){ - initializeType( tn.getArrayIndexType(), unresolved_types ); - initializeType( tn.getArrayConstituentType(), unresolved_types ); - }else if( tn.isDatatype() ){ - const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); - for( size_t i = 0; i<dt.getNumConstructors(); i++ ){ - for( size_t j = 0; j<dt[i].getNumArgs(); j++ ){ - initializeType( TypeNode::fromType( dt[i][j].getType() ), unresolved_types ); - } - } - } + Trace("model-builder") << "Normalize representatives..." << std::endl; + //now, normalize all representatives + // this will make every leaf of asserted representatives into a representative + std::map< Node, bool > normalized; + for( std::map< Node, Node >::iterator it = assertedReps.begin(); it != assertedReps.end(); ++it ){ + std::map< Node, bool > normalizing; + normalizeRepresentative( tm, it->first, assertedReps, normalized, normalizing ); + } + Trace("model-builder") << "Copy representatives to model..." << std::endl; + //assertedReps has the actual representatives we will use, now copy to model + tm->d_reps.clear(); + for( std::map< Node, Node >::iterator it = assertedReps.begin(); it != assertedReps.end(); ++it ){ + tm->d_reps[ it->first ] = it->second; + tm->d_rep_set.add( it->second ); } + + //modelBuilder-specific initialization + processBuildModel( tm, fullModel ); } -Node TheoryEngineModelBuilder::chooseRepresentative( TheoryModel* m, Node eqc ){ +Node TheoryEngineModelBuilder::chooseRepresentative( TheoryModel* m, Node eqc, bool fullModel ){ //try to get a new domain value Node rep = m->getNewDomainValue( eqc.getType() ); if( !rep.isNull() ){ @@ -611,3 +484,71 @@ Node TheoryEngineModelBuilder::chooseRepresentative( TheoryModel* m, Node eqc ){ return eqc; } } + +Node TheoryEngineModelBuilder::normalizeRepresentative( TheoryModel* m, Node r, std::map< Node, Node >& reps, + std::map< Node, bool >& normalized, + std::map< Node, bool >& normalizing ){ + Trace("temb-normalize") << r << std::endl; + if( normalized.find( r )!=normalized.end() ){ + //Message() << " -> already normalized, return " << reps[r] << std::endl; + return reps[r]; + }else if( normalizing.find( r )!=normalizing.end() && normalizing[r] ){ + //this case is to handle things like when store( A, e, i ) is given + // as a representative for array A. + //Message() << " -> currently normalizing, give up : " << r << std::endl; + return r; + }else if( reps.find( r )!=reps.end() ){ + normalizing[ r ] = true; + Node retNode = normalizeNode( m, reps[r], reps, normalized, normalizing ); + normalizing[ r ] = false; + normalized[ r ] = true; + reps[ r ] = retNode; + //Message() << " --> returned " << retNode << " for " << r << std::endl; + return retNode; + }else if( m->d_equalityEngine.hasTerm( r ) ){ + normalizing[ r ] = true; + //return the normalized representative from the model + r = m->d_equalityEngine.getRepresentative( r ); + //Message() << " -> it is the representative " << r << std::endl; + Node retNode = normalizeRepresentative( m, r, reps, normalized, normalizing ); + normalizing[ r ] = false; + return retNode; + }else{ + if( !r.isConst() ){ + Trace("model-warn") << "Normalizing representative, unknown term: " << r << std::endl; + Trace("model-warn") << " Type : " << r.getType() << std::endl; + Trace("model-warn") << " Kind : " << r.getKind() << std::endl; + normalizing[ r ] = true; + r = normalizeNode( m, r, reps, normalized, normalizing ); + normalizing[ r ] = false; + } + //Message() << " -> unknown, return " << r << std::endl; + return r; + } +} + +Node TheoryEngineModelBuilder::normalizeNode( TheoryModel* m, Node r, std::map< Node, Node >& reps, + std::map< Node, bool >& normalized, + std::map< Node, bool >& normalizing ){ + if( r.getNumChildren()>0 ){ + //Message() << " ---> normalize " << r << " " << r.getNumChildren() << " " << r.getKind() << std::endl; + //non-leaf case: construct representative from children + std::vector< Node > children; + if( r.getMetaKind() == kind::metakind::PARAMETERIZED ){ + children.push_back( r.getOperator() ); + } + for( size_t i=0; i<r.getNumChildren(); i++ ){ + Node ri = normalizeRepresentative( m, r[i], reps, normalized, normalizing ); + children.push_back( ri ); + } + Node retNode = NodeManager::currentNM()->mkNode( r.getKind(), children ); + retNode = Rewriter::rewrite( retNode ); + if( retNode!=r ){ + //assure that it is made equal in the model + m->assertEquality( r, retNode, true ); + } + return retNode; + }else{ + return r; + } +}
\ No newline at end of file diff --git a/src/theory/model.h b/src/theory/model.h index 4a4bf48c9..086e39f3e 100644 --- a/src/theory/model.h +++ b/src/theory/model.h @@ -21,36 +21,13 @@ #include "util/model.h" #include "theory/uf/equality_engine.h" +#include "theory/rep_set.h" +#include "theory/substitutions.h" namespace CVC4 { namespace theory { class QuantifiersEngine; - -/** this class stores a representative set */ -class RepSet { -public: - RepSet(){} - ~RepSet(){} - std::map< TypeNode, std::vector< Node > > d_type_reps; - std::map< Node, int > d_tmap; - /** clear the set */ - void clear(); - /** has type */ - bool hasType( TypeNode tn ) { return d_type_reps.find( tn )!=d_type_reps.end(); } - /** add representative for type */ - void add( Node n ); - /** 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 toStream(std::ostream& out); -}; - -//representative domain -typedef std::vector< int > RepDomain; - class TheoryEngineModelBuilder; /** Theory Model class @@ -66,18 +43,8 @@ protected: * such as contraining the interpretation of uninterpretted functions. */ virtual void addTerm( Node n ) {} -private: - /** List of definitions that the user has given - * This is necessary for supporting the get-model command. - */ - std::vector< Node > d_define_funcs; - std::vector< TypeNode > d_define_types; - std::vector< int > d_defines; -protected: - /** print the value of the function n to stream */ - virtual void toStreamFunction( Node n, std::ostream& out ); - /** print the value of the type tn to stream */ - virtual void toStreamType( TypeNode tn, std::ostream& out ); + /** substitution map for this model */ + SubstitutionMap d_substitutions; public: TheoryModel( context::Context* c, std::string name ); virtual ~TheoryModel(){} @@ -90,7 +57,7 @@ public: /** true/false nodes */ Node d_true; Node d_false; -public: +protected: /** reset the model */ virtual void reset(); /** get interpreted value @@ -98,11 +65,11 @@ public: * This should function should return a representative in d_reps */ virtual Node getInterpretedValue( TNode n ) = 0; + /** + * Get model value function. This function is called by getValue + */ + Node getModelValue( TNode n ); public: - /** add defined function (for get-model) */ - void addDefineFunction( Node n ); - /** add defined type (for get-model) */ - void addDefineType( TypeNode tn ); /** * Get value function. This should be called only after a ModelBuilder has called buildModel(...) * on this model. @@ -117,13 +84,28 @@ public: * If it cannot find such a node, it returns null. */ Node getNewDomainValue( TypeNode tn ); + /** complete all values for type + * Calling this function ensures that all terms of type tn exist in d_rep_set.d_type_reps[tn] + */ + void completeDomainValues( TypeNode tn ){ + d_rep_set.complete( tn ); + } public: + /** Adds a substitution from x to t. */ + void addSubstitution(TNode x, TNode t, bool invalidateCache = true); /** assert equality holds in the model */ void assertEquality( Node a, Node b, bool polarity ); /** assert predicate holds in the model */ void assertPredicate( Node a, bool polarity ); /** assert all equalities/predicates in equality engine hold in the model */ void assertEqualityEngine( const eq::EqualityEngine* ee ); + /** assert representative + * This function tells the model that n should be the representative of its equivalence class. + * It should be called during model generation, before final representatives are chosen. In the + * case of TheoryEngineModelBuilder, it should be called during Theory's collectModelInfo( ... ) + * functions where fullModel = true. + */ + void assertRepresentative( Node n ); public: /** general queries */ bool hasTerm( Node a ); @@ -170,19 +152,24 @@ class TheoryEngineModelBuilder : public ModelBuilder protected: /** pointer to theory engine */ TheoryEngine* d_te; - /** choose representative for unresolved equivalence class */ - void initializeType( TypeNode tn, std::map< TypeNode, bool >& unresolved_types ); /** process build model */ - virtual void processBuildModel( TheoryModel* m ){} + virtual void processBuildModel( TheoryModel* m, bool fullModel ){} /** choose representative for unconstrained equivalence class */ - virtual Node chooseRepresentative( TheoryModel* m, Node eqc ); + virtual Node chooseRepresentative( TheoryModel* m, Node eqc, bool fullModel ); + /** normalize representative */ + Node normalizeRepresentative( TheoryModel* m, Node r, std::map< Node, Node >& reps, + std::map< Node, bool >& normalized, + std::map< Node, bool >& normalizing ); + Node normalizeNode( TheoryModel* m, Node r, std::map< Node, Node >& reps, + std::map< Node, bool >& normalized, + std::map< Node, bool >& normalizing ); public: TheoryEngineModelBuilder( TheoryEngine* te ); virtual ~TheoryEngineModelBuilder(){} /** Build model function. * Should be called only on TheoryModels m */ - void buildModel( Model* m ); + void buildModel( Model* m, bool fullModel ); }; } diff --git a/src/theory/output_channel.h b/src/theory/output_channel.h index a86984cca..8ddf809b6 100644 --- a/src/theory/output_channel.h +++ b/src/theory/output_channel.h @@ -27,6 +27,8 @@ namespace CVC4 { namespace theory { +class Theory; + /** * A LemmaStatus, returned from OutputChannel::lemma(), provides information * about the lemma added. In particular, it contains the T-rewritten lemma @@ -206,8 +208,14 @@ public: * long-running operations, they cannot rely on resource() to break * out of infinite or intractable computations. */ - virtual void spendResource() throw() { - } + virtual void spendResource() throw() {} + + /** Handle user attribute + * Associates theory t with the attribute attr. Theory t will be + * notifed whenever an attribute of name attr is set on a node. + * This can happen through, for example, the SMT LIB v2 language. + */ + virtual void handleUserAttribute( const char* attr, Theory* t ){} };/* class OutputChannel */ diff --git a/src/theory/quantifiers/Makefile.am b/src/theory/quantifiers/Makefile.am index 2b16c9af3..3a19ff9c6 100644 --- a/src/theory/quantifiers/Makefile.am +++ b/src/theory/quantifiers/Makefile.am @@ -25,20 +25,18 @@ libquantifiers_la_SOURCES = \ inst_match.cpp \ model_engine.h \ model_engine.cpp \ - inst_when_mode.cpp \ - inst_when_mode.h \ - literal_match_mode.cpp \ - literal_match_mode.h \ + modes.cpp \ + modes.h \ relevant_domain.h \ relevant_domain.cpp \ - rep_set_iterator.h \ - rep_set_iterator.cpp \ term_database.h \ term_database.cpp \ first_order_model.h \ first_order_model.cpp \ model_builder.h \ - model_builder.cpp + model_builder.cpp \ + quantifiers_attributes.h \ + quantifiers_attributes.cpp EXTRA_DIST = \ kinds \ diff --git a/src/theory/quantifiers/first_order_model.cpp b/src/theory/quantifiers/first_order_model.cpp index fd616948c..c3be1cdaf 100644 --- a/src/theory/quantifiers/first_order_model.cpp +++ b/src/theory/quantifiers/first_order_model.cpp @@ -15,9 +15,11 @@ **/ #include "theory/quantifiers/first_order_model.h" -#include "theory/quantifiers/rep_set_iterator.h" #include "theory/quantifiers/model_engine.h" #include "theory/quantifiers/term_database.h" +#include "theory/quantifiers/quantifiers_attributes.h" + +#define USE_INDEX_ORDERING using namespace std; using namespace CVC4; @@ -26,39 +28,38 @@ using namespace CVC4::context; using namespace CVC4::theory; using namespace CVC4::theory::quantifiers; -FirstOrderModel::FirstOrderModel( QuantifiersEngine* qe, context::Context* c, std::string name ) : DefaultModel( c, name, true ), -d_term_db( qe->getTermDatabase() ), d_forall_asserts( c ){ +FirstOrderModel::FirstOrderModel( context::Context* c, std::string name ) : DefaultModel( c, name, true ), +d_axiom_asserted( c, false ), d_forall_asserts( c ){ } +void FirstOrderModel::assertQuantifier( Node n ){ + d_forall_asserts.push_back( n ); + if( n.getAttribute(AxiomAttribute()) ){ + d_axiom_asserted = true; + } +} + void FirstOrderModel::reset(){ - //rebuild models - d_uf_model_tree.clear(); - d_uf_model_gen.clear(); - d_array_model.clear(); DefaultModel::reset(); } void FirstOrderModel::addTerm( Node n ){ - //std::vector< Node > added; - //d_term_db->addTerm( n, added, false ); DefaultModel::addTerm( n ); } -void FirstOrderModel::initialize(){ +void FirstOrderModel::initialize( bool considerAxioms ){ + //rebuild models + d_uf_model_tree.clear(); + d_uf_model_gen.clear(); + d_array_model.clear(); //this is called after representatives have been chosen and the equality engine has been built //for each quantifier, collect all operators we care about for( int i=0; i<getNumAssertedQuantifiers(); i++ ){ Node f = getAssertedQuantifier( i ); - //initialize relevant models within bodies of all quantifiers - initializeModelForTerm( f[1] ); - } - //for debugging - if( Trace.isOn("model-engine") ){ - for( std::map< TypeNode, std::vector< Node > >::iterator it = d_rep_set.d_type_reps.begin(); it != d_rep_set.d_type_reps.end(); ++it ){ - if( it->first.isSort() ){ - Trace("model-engine") << "Cardinality( " << it->first << " )" << " = " << it->second.size() << std::endl; - } + if( considerAxioms || !f.hasAttribute(AxiomAttribute()) ){ + //initialize relevant models within bodies of all quantifiers + initializeModelForTerm( f[1] ); } } } @@ -92,33 +93,16 @@ void FirstOrderModel::initializeModelForTerm( Node n ){ } } -void FirstOrderModel::toStreamFunction( Node n, std::ostream& out ){ - /* - if( d_uf_model.find( n )!=d_uf_model.end() ){ - //d_uf_model[n].toStream( out ); - Node value = d_uf_model[n].getFunctionValue(); - out << "(" << n << " " << value << ")"; - }else if( d_array_model.find( n )!=d_array_model.end() ){ - Node value = d_array_model[n].getArrayValue(); - out << "(" << n << " " << value << ")" << std::endl; - } - */ - DefaultModel::toStreamFunction( n, out ); -} - -void FirstOrderModel::toStreamType( TypeNode tn, std::ostream& out ){ - DefaultModel::toStreamType( tn, out ); -} - Node FirstOrderModel::getInterpretedValue( TNode n ){ Debug("fo-model") << "get interpreted value " << n << std::endl; TypeNode type = n.getType(); if( type.isFunction() || type.isPredicate() ){ - if( d_uf_models.find( n )==d_uf_models.end() ){ - //use the model tree to generate the model - Node fn = d_uf_model_tree[n].getFunctionValue(); - d_uf_models[n] = fn; - return fn; + if( d_uf_model_tree.find( n )!=d_uf_model_tree.end() ){ + if( d_uf_models.find( n )==d_uf_models.end() ){ + //use the model tree to generate the model + d_uf_models[n] = d_uf_model_tree[n].getFunctionValue( "$x" ); + } + return d_uf_models[n]; } /* }else if( type.isArray() ){ @@ -141,44 +125,387 @@ Node FirstOrderModel::getInterpretedValue( TNode n ){ return DefaultModel::getInterpretedValue( n ); } -TermDb* FirstOrderModel::getTermDatabase(){ - return d_term_db; +void FirstOrderModel::toStream(std::ostream& out){ + } -void FirstOrderModel::toStream(std::ostream& out){ - DefaultModel::toStream( out ); +//for evaluation of quantifier bodies + +void FirstOrderModel::resetEvaluate(){ + d_eval_uf_use_default.clear(); + d_eval_uf_model.clear(); + d_eval_term_index_order.clear(); + d_eval_failed.clear(); + d_eval_failed_lits.clear(); + d_eval_formulas = 0; + d_eval_uf_terms = 0; + d_eval_lits = 0; + d_eval_lits_unknown = 0; +} + +//if evaluate( n ) = eVal, +// let n' = ri * n be the formula n instantiated with the current values in r_iter +// if eVal = 1, then n' is true, if eVal = -1, then n' is false, +// if eVal = 0, then n' cannot be proven to be equal to phaseReq +// if eVal is not 0, then +// each n{ri->d_index[0]/x_0...ri->d_index[depIndex]/x_depIndex, */x_(depIndex+1) ... */x_n } is equivalent in the current model +int FirstOrderModel::evaluate( Node n, int& depIndex, RepSetIterator* ri ){ + ++d_eval_formulas; + //Debug("fmf-eval-debug") << "Evaluate " << n << " " << phaseReq << std::endl; + //Notice() << "Eval " << n << std::endl; + if( n.getKind()==NOT ){ + int val = evaluate( n[0], depIndex, ri ); + 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 = ri->getNumTerms(); + 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( nn, childDepIndex, ri ); + 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( n[0], depIndex1, ri ); + if( eVal!=0 ){ + int depIndex2; + int eVal2 = evaluate( n.getKind()==XOR ? n[1].notNode() : n[1], depIndex2, ri ); + if( eVal2!=0 ){ + depIndex = depIndex1>depIndex2 ? depIndex1 : depIndex2; + return eVal==eVal2 ? 1 : -1; + } + } + return 0; + }else if( n.getKind()==ITE ){ + int depIndex1, depIndex2; + int eVal = evaluate( n[0], depIndex1, ri ); + if( eVal==0 ){ + //evaluate children to see if they are the same value + int eval1 = evaluate( n[1], depIndex1, ri ); + if( eval1!=0 ){ + int eval2 = evaluate( n[1], depIndex2, ri ); + if( eval1==eval2 ){ + depIndex = depIndex1>depIndex2 ? depIndex1 : depIndex2; + return eval1; + } + } + }else{ + int eValT = evaluate( n[eVal==1 ? 1 : 2], depIndex2, ri ); + depIndex = depIndex1>depIndex2 ? depIndex1 : depIndex2; + return eValT; + } + return 0; + }else if( n.getKind()==FORALL ){ + return 0; + }else{ + ++d_eval_lits; + ////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-eval-debug") << "Evaluate literal " << n << std::endl; + int retVal = 0; + depIndex = ri->getNumTerms()-1; + Node val = evaluateTerm( n, depIndex, ri ); + if( !val.isNull() ){ + if( areEqual( val, d_true ) ){ + retVal = 1; + }else if( areEqual( val, d_false ) ){ + retVal = -1; + }else{ + if( val.getKind()==EQUAL ){ + if( areEqual( val[0], val[1] ) ){ + retVal = 1; + }else if( areDisequal( val[0], val[1] ) ){ + retVal = -1; + } + } + } + } + if( retVal!=0 ){ + Debug("fmf-eval-debug") << "Evaluate literal: return " << retVal << ", depIndex = " << depIndex << std::endl; + }else{ + ++d_eval_lits_unknown; + Trace("fmf-eval-amb") << "Neither true nor false : " << n << std::endl; + Trace("fmf-eval-amb") << " value : " << val << std::endl; + //std::cout << "Neither true nor false : " << n << std::endl; + //std::cout << " Value : " << val << std::endl; + //for( int i=0; i<(int)n.getNumChildren(); i++ ){ + // std::cout << " " << i << " : " << n[i].getType() << std::endl; + //} + } + return retVal; + } +} + +Node FirstOrderModel::evaluateTerm( Node n, int& depIndex, RepSetIterator* ri ){ + //Message() << "Eval term " << n << std::endl; + if( !n.hasAttribute(InstConstantAttribute()) ){ + //if evaluating a ground term, just consult the standard getValue functionality + depIndex = -1; + return getValue( n ); + }else{ + Node val; + depIndex = ri->getNumTerms()-1; + //check the type of n + if( n.getKind()==INST_CONSTANT ){ + int v = n.getAttribute(InstVarNumAttribute()); + depIndex = ri->d_var_order[ v ]; + val = ri->getTerm( v ); + }else if( n.getKind()==ITE ){ + int depIndex1, depIndex2; + int eval = evaluate( n[0], depIndex1, ri ); + if( eval==0 ){ + //evaluate children to see if they are the same + Node val1 = evaluateTerm( n[ 1 ], depIndex1, ri ); + Node val2 = evaluateTerm( n[ 2 ], depIndex2, ri ); + if( val1==val2 ){ + val = val1; + depIndex = depIndex1>depIndex2 ? depIndex1 : depIndex2; + }else{ + return Node::null(); + } + }else{ + val = evaluateTerm( n[ eval==1 ? 1 : 2 ], depIndex2, ri ); + depIndex = depIndex1>depIndex2 ? depIndex1 : depIndex2; + } + }else{ + std::vector< int > children_depIndex; + //for select, pre-process read over writes + if( n.getKind()==SELECT ){ #if 0 - out << "---Current Model---" << std::endl; - out << "Representatives: " << std::endl; - d_rep_set.toStream( out ); - out << "Functions: " << std::endl; - for( std::map< Node, uf::UfModel >::iterator it = d_uf_model.begin(); it != d_uf_model.end(); ++it ){ - it->second.toStream( out ); - out << std::endl; + //std::cout << "Evaluate " << n << std::endl; + Node sel = evaluateTerm( n[1], depIndex, ri ); + if( sel.isNull() ){ + depIndex = ri->getNumTerms()-1; + return Node::null(); + } + Node arr = getRepresentative( n[0] ); + //if( n[0]!=getRepresentative( n[0] ) ){ + // std::cout << n[0] << " is " << getRepresentative( n[0] ) << std::endl; + //} + int tempIndex; + int eval = 1; + while( arr.getKind()==STORE && eval!=0 ){ + eval = evaluate( sel.eqNode( arr[1] ), tempIndex, ri ); + depIndex = tempIndex > depIndex ? tempIndex : depIndex; + if( eval==1 ){ + val = evaluateTerm( arr[2], tempIndex, ri ); + depIndex = tempIndex > depIndex ? tempIndex : depIndex; + return val; + }else if( eval==-1 ){ + arr = arr[0]; + } + } + arr = evaluateTerm( arr, tempIndex, ri ); + depIndex = tempIndex > depIndex ? tempIndex : depIndex; + val = NodeManager::currentNM()->mkNode( SELECT, arr, sel ); +#else + val = evaluateTermDefault( n, depIndex, children_depIndex, ri ); +#endif + }else{ + //default term evaluate : evaluate all children, recreate the value + val = evaluateTermDefault( n, depIndex, children_depIndex, ri ); + } + if( !val.isNull() ){ + bool setVal = false; + //custom ways of evaluating terms + if( n.getKind()==APPLY_UF ){ + Node op = n.getOperator(); + //Debug("fmf-eval-debug") << "Evaluate term " << n << " (" << gn << ")" << std::endl; + //if it is a defined UF, then consult the interpretation + if( d_uf_model_tree.find( op )!=d_uf_model_tree.end() ){ + ++d_eval_uf_terms; + int argDepIndex = 0; + //make the term model specifically for n + makeEvalUfModel( n ); + //now, consult the model + if( d_eval_uf_use_default[n] ){ + val = d_uf_model_tree[ op ].getValue( this, val, argDepIndex ); + }else{ + val = d_eval_uf_model[ n ].getValue( this, val, argDepIndex ); + } + //Debug("fmf-eval-debug") << "Evaluate term " << n << " (" << gn << ")" << std::endl; + //d_eval_uf_model[ n ].debugPrint("fmf-eval-debug", d_qe ); + Assert( !val.isNull() ); + //recalculate the depIndex + depIndex = -1; + for( int i=0; i<argDepIndex; i++ ){ + int index = d_eval_uf_use_default[n] ? i : d_eval_term_index_order[n][i]; + Debug("fmf-eval-debug") << "Add variables from " << index << "..." << std::endl; + if( children_depIndex[index]>depIndex ){ + depIndex = children_depIndex[index]; + } + } + setVal = true; + } + }else if( n.getKind()==SELECT ){ + //we are free to interpret this term however we want + } + //if not set already, rewrite and consult model for interpretation + if( !setVal ){ + val = Rewriter::rewrite( val ); + if( val.getMetaKind()!=kind::metakind::CONSTANT ){ + //FIXME: we cannot do this until we trust all theories collectModelInfo! + //val = getInterpretedValue( val ); + //val = getRepresentative( val ); + } + } + Debug("fmf-eval-debug") << "Evaluate term " << n << " = "; + printRepresentativeDebug( "fmf-eval-debug", val ); + Debug("fmf-eval-debug") << ", depIndex = " << depIndex << std::endl; + } + } + return val; + } +} + +Node FirstOrderModel::evaluateTermDefault( Node n, int& depIndex, std::vector< int >& childDepIndex, RepSetIterator* ri ){ + depIndex = -1; + if( n.getNumChildren()==0 ){ + return n; + }else{ + //first we must evaluate the arguments + std::vector< Node > children; + if( n.getMetaKind()==kind::metakind::PARAMETERIZED ){ + children.push_back( n.getOperator() ); + } + //for each argument, calculate its value, and the variables its value depends upon + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + childDepIndex.push_back( -1 ); + Node nn = evaluateTerm( n[i], childDepIndex[i], ri ); + if( nn.isNull() ){ + depIndex = ri->getNumTerms()-1; + return nn; + }else{ + children.push_back( nn ); + if( childDepIndex[i]>depIndex ){ + depIndex = childDepIndex[i]; + } + } + } + //recreate the value + Node val = NodeManager::currentNM()->mkNode( n.getKind(), children ); + return val; + } +} + +void FirstOrderModel::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(); +} + +void FirstOrderModel::makeEvalUfModel( Node n ){ + if( d_eval_uf_model.find( n )==d_eval_uf_model.end() ){ + makeEvalUfIndexOrder( n ); + if( !d_eval_uf_use_default[n] ){ + Node op = n.getOperator(); + d_eval_uf_model[n] = uf::UfModelTree( op, d_eval_term_index_order[n] ); + d_uf_model_gen[op].makeModel( this, d_eval_uf_model[n] ); + //Debug("fmf-index-order") << "Make model for " << n << " : " << std::endl; + //d_eval_uf_model[n].debugPrint( "fmf-index-order", d_qe, 2 ); + } } -#elif 0 - d_rep_set.toStream( out ); - //print everything not related to UF in equality engine - eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine ); - while( !eqcs_i.isFinished() ){ - Node eqc = (*eqcs_i); - Node rep = getRepresentative( eqc ); - TypeNode type = rep.getType(); - eq::EqClassIterator eqc_i = eq::EqClassIterator( eqc, &d_equalityEngine ); - while( !eqc_i.isFinished() ){ - //do not print things that have interpretations in model - if( !(*eqc_i).isConst() && !hasInterpretedValue( *eqc_i ) ){ - out << "(" << (*eqc_i) << " " << rep << ")" << std::endl; +} + +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; + } } - ++eqc_i; + return maxVal; + }else{ + return -1; } - ++eqcs_i; } - //print functions - for( std::map< Node, uf::UfModel >::iterator it = d_uf_model.begin(); it != d_uf_model.end(); ++it ){ - it->second.toStream( out ); - out << std::endl; + 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 FirstOrderModel::makeEvalUfIndexOrder( Node n ){ + if( d_eval_term_index_order.find( n )==d_eval_term_index_order.end() ){ +#ifdef USE_INDEX_ORDERING + //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_uf_use_default[n] = useDefault; + Debug("fmf-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-index-order") << d_eval_term_index_order[n][i] << " "; + } + Debug("fmf-index-order") << std::endl; +#else + d_eval_uf_use_default[n] = true; #endif + } } diff --git a/src/theory/quantifiers/first_order_model.h b/src/theory/quantifiers/first_order_model.h index 8ad911452..e66bf8040 100644 --- a/src/theory/quantifiers/first_order_model.h +++ b/src/theory/quantifiers/first_order_model.h @@ -42,32 +42,41 @@ class TermDb; class FirstOrderModel : public DefaultModel { private: - //pointer to term database - TermDb* d_term_db; //add term function void addTerm( Node n ); //for initialize model void initializeModelForTerm( Node n ); - /** to stream functions */ - void toStreamFunction( Node n, std::ostream& out ); - void toStreamType( TypeNode tn, std::ostream& out ); + /** whether an axiom is asserted */ + context::CDO< bool > d_axiom_asserted; + /** list of quantifiers asserted in the current context */ + context::CDList<Node> d_forall_asserts; public: //for Theory UF: //models for each UF operator std::map< Node, uf::UfModelTree > d_uf_model_tree; //model generators std::map< Node, uf::UfModelTreeGenerator > d_uf_model_gen; +private: + //map from terms to the models used to calculate their value + std::map< Node, bool > d_eval_uf_use_default; + std::map< Node, uf::UfModelTree > d_eval_uf_model; + void makeEvalUfModel( Node n ); + //index ordering to use for each term + std::map< Node, std::vector< int > > d_eval_term_index_order; + void makeEvalUfIndexOrder( Node n ); public: //for Theory Arrays: //default value for each non-store array std::map< Node, arrays::ArrayModel > d_array_model; public: //for Theory Quantifiers: - /** list of quantifiers asserted in the current context */ - context::CDList<Node> d_forall_asserts; + /** assert quantifier */ + void assertQuantifier( Node n ); /** 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]; } + /** bool axiom asserted */ + bool isAxiomAsserted() { return d_axiom_asserted; } public: - FirstOrderModel( QuantifiersEngine* qe, context::Context* c, std::string name ); + FirstOrderModel( context::Context* c, std::string name ); virtual ~FirstOrderModel(){} // reset the model void reset(); @@ -75,11 +84,30 @@ public: Node getInterpretedValue( TNode n ); public: // initialize the model - void initialize(); - /** get term database */ - TermDb* getTermDatabase(); + void initialize( bool considerAxioms = true ); /** to stream function */ void toStream( std::ostream& out ); + +//the following functions are for evaluating quantifier bodies +public: + /** reset evaluation */ + void resetEvaluate(); + /** evaluate functions */ + int evaluate( Node n, int& depIndex, RepSetIterator* ri ); + Node evaluateTerm( Node n, int& depIndex, RepSetIterator* ri ); +public: + //statistics + int d_eval_formulas; + int d_eval_uf_terms; + int d_eval_lits; + int d_eval_lits_unknown; +private: + //default evaluate term function + Node evaluateTermDefault( Node n, int& depIndex, std::vector< int >& childDepIndex, RepSetIterator* ri ); + //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; };/* class FirstOrderModel */ }/* CVC4::theory::quantifiers namespace */ diff --git a/src/theory/quantifiers/inst_match.cpp b/src/theory/quantifiers/inst_match.cpp index ce35607d4..d2083ef3d 100644 --- a/src/theory/quantifiers/inst_match.cpp +++ b/src/theory/quantifiers/inst_match.cpp @@ -148,7 +148,9 @@ void InstMatch::computeTermVec( QuantifiersEngine* qe, const std::vector< Node > } 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] ] ); + if( d_map.find( vars[i] )!=d_map.end() && !d_map[ vars[i] ].isNull() ){ + match.push_back( d_map[ vars[i] ] ); + } } } @@ -177,7 +179,7 @@ bool InstMatchTrie::existsInstMatch( QuantifiersEngine* qe, Node f, InstMatch& m } if( modEq ){ //check modulo equality if any other instantiation match exists - if( qe->getEqualityQuery()->getEngine()->hasTerm( n ) ){ + if( !n.isNull() && qe->getEqualityQuery()->getEngine()->hasTerm( n ) ){ eq::EqClassIterator eqc( qe->getEqualityQuery()->getEngine()->getRepresentative( n ), qe->getEqualityQuery()->getEngine() ); while( !eqc.isFinished() ){ @@ -193,13 +195,6 @@ bool InstMatchTrie::existsInstMatch( QuantifiersEngine* qe, Node f, InstMatch& m ++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; } diff --git a/src/theory/quantifiers/instantiation_engine.cpp b/src/theory/quantifiers/instantiation_engine.cpp index 0fa4fad12..727f568c5 100644 --- a/src/theory/quantifiers/instantiation_engine.cpp +++ b/src/theory/quantifiers/instantiation_engine.cpp @@ -386,3 +386,16 @@ void InstantiationEngine::propagate( Theory::Effort level ){ } } } + +Node InstantiationEngine::getNextDecisionRequest(){ + //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_quantEngine->getValuation().hasSatValue( it->second, value ) ){ + //if not already set, propagate as decision + Debug("cbqi-prop-as-dec") << "CBQI: propagate as decision " << it->second << std::endl; + return it->second; + } + } + return Node::null(); +}
\ No newline at end of file diff --git a/src/theory/quantifiers/instantiation_engine.h b/src/theory/quantifiers/instantiation_engine.h index 37ee6d801..19dac736c 100644 --- a/src/theory/quantifiers/instantiation_engine.h +++ b/src/theory/quantifiers/instantiation_engine.h @@ -66,6 +66,7 @@ public: void assertNode( Node f ); Node explain(TNode n){ return Node::null(); } void propagate( Theory::Effort level ); + Node getNextDecisionRequest(); 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 ]; } diff --git a/src/theory/quantifiers/kinds b/src/theory/quantifiers/kinds index 106d95cef..c81816528 100644 --- a/src/theory/quantifiers/kinds +++ b/src/theory/quantifiers/kinds @@ -8,7 +8,7 @@ theory THEORY_QUANTIFIERS ::CVC4::theory::quantifiers::TheoryQuantifiers "theory typechecker "theory/quantifiers/theory_quantifiers_type_rules.h" instantiator ::CVC4::theory::quantifiers::InstantiatorTheoryQuantifiers "theory/quantifiers/theory_quantifiers_instantiator.h" -properties check propagate presolve +properties check propagate presolve getNextDecisionRequest rewriter ::CVC4::theory::quantifiers::QuantifiersRewriter "theory/quantifiers/quantifiers_rewriter.h" @@ -30,6 +30,9 @@ sort INST_PATTERN_TYPE \ not-well-founded \ "Instantiation pattern type" +# Instantiation pattern, also called trigger. +# This node is used for specifying hints for quantifier instantiation. +# An instantiation pattern may have more than 1 child, in which case it specifies a multi-trigger. operator INST_PATTERN 1: "instantiation pattern" sort INST_PATTERN_LIST_TYPE \ @@ -37,6 +40,7 @@ sort INST_PATTERN_LIST_TYPE \ not-well-founded \ "Instantiation pattern list type" +# a list of instantiation patterns operator INST_PATTERN_LIST 1: "instantiation pattern list" typerule FORALL ::CVC4::theory::quantifiers::QuantifierForallTypeRule diff --git a/src/theory/quantifiers/literal_match_mode.cpp b/src/theory/quantifiers/literal_match_mode.cpp deleted file mode 100644 index 87b4b94fe..000000000 --- a/src/theory/quantifiers/literal_match_mode.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/********************* */ -/*! \file literal_match_mode.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-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 [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** \todo document this file - **/ - -#include <iostream> -#include "theory/quantifiers/literal_match_mode.h" - -namespace CVC4 { - -std::ostream& operator<<(std::ostream& out, theory::quantifiers::LiteralMatchMode mode) { - switch(mode) { - case theory::quantifiers::LITERAL_MATCH_NONE: - out << "LITERAL_MATCH_NONE"; - break; - case theory::quantifiers::LITERAL_MATCH_PREDICATE: - out << "LITERAL_MATCH_PREDICATE"; - break; - case theory::quantifiers::LITERAL_MATCH_EQUALITY: - out << "LITERAL_MATCH_EQUALITY"; - break; - default: - out << "LiteralMatchMode!UNKNOWN"; - } - - return out; -} - -}/* CVC4 namespace */ diff --git a/src/theory/quantifiers/literal_match_mode.h b/src/theory/quantifiers/literal_match_mode.h deleted file mode 100644 index 189f0a235..000000000 --- a/src/theory/quantifiers/literal_match_mode.h +++ /dev/null @@ -1,47 +0,0 @@ -/********************* */ -/*! \file literal_match_mode.h - ** \verbatim - ** Original author: mdeters - ** Major contributors: none - ** Minor contributors (to current version): none - ** This file is part of the CVC4 prototype. - ** 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 - ** information.\endverbatim - ** - ** \brief [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** \todo document this file - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__THEORY__QUANTIFIERS__LITERAL_MATCH_MODE_H -#define __CVC4__THEORY__QUANTIFIERS__LITERAL_MATCH_MODE_H - -#include <iostream> - -namespace CVC4 { -namespace theory { -namespace quantifiers { - -typedef enum { - /** Do not consider polarity of patterns */ - LITERAL_MATCH_NONE, - /** Consider polarity of boolean predicates only */ - LITERAL_MATCH_PREDICATE, - /** Consider polarity of boolean predicates, as well as equalities */ - LITERAL_MATCH_EQUALITY, -} LiteralMatchMode; - -}/* CVC4::theory::quantifiers namespace */ -}/* CVC4::theory namespace */ - -std::ostream& operator<<(std::ostream& out, theory::quantifiers::LiteralMatchMode mode) CVC4_PUBLIC; - -}/* CVC4 namespace */ - -#endif /* __CVC4__THEORY__QUANTIFIERS__LITERAL_MATCH_MODE_H */ diff --git a/src/theory/quantifiers/model_builder.cpp b/src/theory/quantifiers/model_builder.cpp index 5d49c914f..c09346f35 100644 --- a/src/theory/quantifiers/model_builder.cpp +++ b/src/theory/quantifiers/model_builder.cpp @@ -20,15 +20,14 @@ #include "theory/uf/theory_uf.h" #include "theory/uf/theory_uf_model.h" #include "theory/uf/theory_uf_instantiator.h" +#include "theory/uf/theory_uf_strong_solver.h" #include "theory/arrays/theory_arrays_model.h" #include "theory/quantifiers/first_order_model.h" #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/model_builder.h" - -//#define ME_PRINT_WARNINGS +#include "theory/quantifiers/quantifiers_attributes.h" #define RECONSIDER_FUNC_CONSTANT -//#define ONE_QUANT_PER_ROUND_INST_GEN using namespace std; using namespace CVC4; @@ -37,78 +36,95 @@ using namespace CVC4::context; using namespace CVC4::theory; using namespace CVC4::theory::quantifiers; -ModelEngineBuilder::ModelEngineBuilder( QuantifiersEngine* qe ) : +ModelEngineBuilder::ModelEngineBuilder( context::Context* c, QuantifiersEngine* qe ) : TheoryEngineModelBuilder( qe->getTheoryEngine() ), -d_qe( qe ){ - +d_qe( qe ), d_curr_model( c, NULL ){ + d_considerAxioms = true; } -Node ModelEngineBuilder::chooseRepresentative( TheoryModel* m, Node eqc ){ - Assert( m->d_equalityEngine.hasTerm( eqc ) ); - Assert( m->d_equalityEngine.getRepresentative( eqc )==eqc ); - //avoid interpreted symbols - if( isBadRepresentative( eqc ) ){ - eq::EqClassIterator eqc_i = eq::EqClassIterator( eqc, &m->d_equalityEngine ); - while( !eqc_i.isFinished() ){ - if( !isBadRepresentative( *eqc_i ) ){ - return *eqc_i; +Node ModelEngineBuilder::chooseRepresentative( TheoryModel* m, Node eqc, bool fullModel ){ + if( fullModel ){ + return TheoryEngineModelBuilder::chooseRepresentative( m, eqc, fullModel ); + }else{ + Assert( m->d_equalityEngine.hasTerm( eqc ) ); + Assert( m->d_equalityEngine.getRepresentative( eqc )==eqc ); + //avoid bad representatives + if( isBadRepresentative( eqc ) ){ + eq::EqClassIterator eqc_i = eq::EqClassIterator( eqc, &m->d_equalityEngine ); + while( !eqc_i.isFinished() ){ + if( !isBadRepresentative( *eqc_i ) ){ + return *eqc_i; + } + ++eqc_i; } - ++eqc_i; + //otherwise, make new value? + //Message() << "Warning: Bad rep " << eqc << std::endl; } - //otherwise, make new value? - //Message() << "Warning: Bad rep " << eqc << std::endl; + return eqc; } - return eqc; } bool ModelEngineBuilder::isBadRepresentative( Node n ){ + //avoid interpreted symbols return n.getKind()==SELECT || n.getKind()==APPLY_SELECTOR; } -void ModelEngineBuilder::processBuildModel( TheoryModel* m ) { - d_addedLemmas = 0; - //only construct first order model if optUseModel() is true - if( optUseModel() ){ - FirstOrderModel* fm = (FirstOrderModel*)m; - //initialize model - fm->initialize(); - //analyze the functions - analyzeModel( fm ); - //analyze the quantifiers - Debug("fmf-model-debug") << "Analyzing quantifiers..." << std::endl; - analyzeQuantifiers( fm ); - //if applicable, find exceptions - if( optInstGen() ){ - //now, see if we know that any exceptions via InstGen exist - Debug("fmf-model-debug") << "Perform InstGen techniques for quantifiers..." << std::endl; - for( int i=0; i<fm->getNumAssertedQuantifiers(); i++ ){ - Node f = fm->getAssertedQuantifier( i ); - if( d_quant_sat.find( f )==d_quant_sat.end() ){ - d_addedLemmas += doInstGen( fm, f ); - if( optOneQuantPerRoundInstGen() && d_addedLemmas>0 ){ - break; +void ModelEngineBuilder::processBuildModel( TheoryModel* m, bool fullModel ) { + FirstOrderModel* fm = (FirstOrderModel*)m; + if( fullModel ){ + Assert( d_curr_model==fm ); + //update models + for( std::map< Node, uf::UfModelTree >::iterator it = fm->d_uf_model_tree.begin(); it != fm->d_uf_model_tree.end(); ++it ){ + it->second.update( fm ); + } + + }else{ + d_curr_model = fm; + //build model for relevant symbols contained in quantified formulas + d_addedLemmas = 0; + //only construct first order model if optUseModel() is true + if( optUseModel() ){ + //initialize model + fm->initialize( d_considerAxioms ); + //analyze the functions + Trace("model-engine-debug") << "Analyzing model..." << std::endl; + analyzeModel( fm ); + //analyze the quantifiers + Trace("model-engine-debug") << "Analyzing quantifiers..." << std::endl; + analyzeQuantifiers( fm ); + //if applicable, find exceptions + if( optInstGen() ){ + //now, see if we know that any exceptions via InstGen exist + Trace("model-engine-debug") << "Perform InstGen techniques for quantifiers..." << std::endl; + for( int i=0; i<fm->getNumAssertedQuantifiers(); i++ ){ + Node f = fm->getAssertedQuantifier( i ); + if( isQuantifierActive( f ) ){ + d_addedLemmas += doInstGen( fm, f ); + if( optOneQuantPerRoundInstGen() && d_addedLemmas>0 ){ + break; + } } } - } - if( Trace.isOn("model-engine") ){ - if( d_addedLemmas>0 ){ - Trace("model-engine") << "InstGen, added lemmas = " << d_addedLemmas << std::endl; - }else{ - Trace("model-engine") << "No InstGen lemmas..." << std::endl; + if( Trace.isOn("model-engine") ){ + if( d_addedLemmas>0 ){ + Trace("model-engine") << "InstGen, added lemmas = " << d_addedLemmas << std::endl; + }else{ + Trace("model-engine") << "No InstGen lemmas..." << std::endl; + } } } - Debug("fmf-model-debug") << "---> Added lemmas = " << d_addedLemmas << std::endl; - } - if( d_addedLemmas==0 ){ - //if no immediate exceptions, build the model - // this model will be an approximation that will need to be tested via exhaustive instantiation - Debug("fmf-model-debug") << "Building model..." << std::endl; - finishBuildModel( fm ); + if( d_addedLemmas==0 ){ + //if no immediate exceptions, build the model + // this model will be an approximation that will need to be tested via exhaustive instantiation + Trace("model-engine-debug") << "Building model..." << std::endl; + constructModel( fm ); + } } } } void ModelEngineBuilder::analyzeModel( FirstOrderModel* fm ){ + d_uf_model_constructed.clear(); //determine if any functions are constant for( std::map< Node, uf::UfModelTree >::iterator it = fm->d_uf_model_tree.begin(); it != fm->d_uf_model_tree.end(); ++it ){ Node op = it->first; @@ -138,108 +154,138 @@ void ModelEngineBuilder::analyzeModel( FirstOrderModel* fm ){ } void ModelEngineBuilder::analyzeQuantifiers( FirstOrderModel* fm ){ - d_quant_selection_lits.clear(); d_quant_sat.clear(); + d_quant_selection_lit.clear(); + d_quant_selection_lit_candidates.clear(); + d_quant_selection_lit_terms.clear(); + d_term_selection_lit.clear(); + d_op_selection_terms.clear(); d_uf_prefs.clear(); int quantSatInit = 0; int nquantSatInit = 0; //analyze the preferences of each quantifier for( int i=0; i<(int)fm->getNumAssertedQuantifiers(); i++ ){ Node f = fm->getAssertedQuantifier( i ); - Debug("fmf-model-prefs") << "Analyze quantifier " << f << std::endl; - std::vector< Node > pro_con[2]; - std::vector< Node > constantSatOps; - bool constantSatReconsider; - //for each asserted quantifier f, - // - determine which literals form model basis for each quantifier - // - check which function/predicates have good and bad definitions according to f - for( std::map< Node, bool >::iterator it = d_qe->d_phase_reqs[f].begin(); - it != d_qe->d_phase_reqs[f].end(); ++it ){ - Node n = it->first; - Node gn = d_qe->getTermDatabase()->getModelBasis( n ); - Debug("fmf-model-req") << " Req: " << n << " -> " << it->second << std::endl; - //calculate preference - int pref = 0; - bool value; - if( d_qe->getValuation().hasSatValue( gn, value ) ){ - if( value!=it->second ){ - //store this literal as a model basis literal - // this literal will force a default values in model that (modulo exceptions) shows - // that f is satisfied by the model - d_quant_selection_lits[f].push_back( value ? n : n.notNode() ); - pref = 1; - }else{ - pref = -1; - } - } - if( pref!=0 ){ - //Store preferences for UF - bool isConst = !n.hasAttribute(InstConstantAttribute()); - std::vector< Node > uf_terms; - if( gn.getKind()==APPLY_UF ){ - uf_terms.push_back( gn ); - isConst = !d_uf_prefs[gn.getOperator()].d_const_val.isNull(); - }else if( gn.getKind()==EQUAL ){ - isConst = true; - for( int j=0; j<2; j++ ){ - if( n[j].hasAttribute(InstConstantAttribute()) ){ - if( n[j].getKind()==APPLY_UF ){ - Node op = gn[j].getOperator(); - if( fm->d_uf_model_tree.find( op )!=fm->d_uf_model_tree.end() ){ - uf_terms.push_back( gn[j] ); - isConst = isConst && !d_uf_prefs[op].d_const_val.isNull(); - }else{ - isConst = false; + if( isQuantifierActive( f ) ){ + Debug("fmf-model-prefs") << "Analyze quantifier " << f << std::endl; + //the pro/con preferences for this quantifier + std::vector< Node > pro_con[2]; + //the terms in the selection literal we choose + std::vector< Node > selectionLitTerms; + //for each asserted quantifier f, + // - determine selection literals + // - check which function/predicates have good and bad definitions for satisfying f + for( std::map< Node, bool >::iterator it = d_qe->d_phase_reqs[f].begin(); + it != d_qe->d_phase_reqs[f].end(); ++it ){ + //the literal n is phase-required for quantifier f + Node n = it->first; + Node gn = d_qe->getTermDatabase()->getModelBasis( n ); + Debug("fmf-model-req") << " Req: " << n << " -> " << it->second << std::endl; + bool value; + //if the corresponding ground abstraction literal has a SAT value + if( d_qe->getValuation().hasSatValue( gn, value ) ){ + //collect the non-ground uf terms that this literal contains + // and compute if all of the symbols in this literal have + // constant definitions. + bool isConst = true; + std::vector< Node > uf_terms; + if( n.hasAttribute(InstConstantAttribute()) ){ + isConst = false; + if( gn.getKind()==APPLY_UF ){ + uf_terms.push_back( gn ); + isConst = !d_uf_prefs[gn.getOperator()].d_const_val.isNull(); + }else if( gn.getKind()==EQUAL ){ + isConst = true; + for( int j=0; j<2; j++ ){ + if( n[j].hasAttribute(InstConstantAttribute()) ){ + if( n[j].getKind()==APPLY_UF && + fm->d_uf_model_tree.find( gn[j].getOperator() )!=fm->d_uf_model_tree.end() ){ + uf_terms.push_back( gn[j] ); + isConst = isConst && !d_uf_prefs[ gn[j].getOperator() ].d_const_val.isNull(); + }else{ + isConst = false; + } } - }else{ - isConst = false; } } } - } - Debug("fmf-model-prefs") << " It is " << ( pref==1 ? "pro" : "con" ); - Debug("fmf-model-prefs") << " the definition of " << n << std::endl; - if( pref==1 && isConst ){ - d_quant_sat[f] = true; - //instead, just note to the model for each uf term that f is pro its definition - constantSatReconsider = false; - constantSatOps.clear(); - for( int j=0; j<(int)uf_terms.size(); j++ ){ - Node op = uf_terms[j].getOperator(); - constantSatOps.push_back( op ); - if( d_uf_prefs[op].d_reconsiderModel ){ - constantSatReconsider = true; + //check if the value in the SAT solver matches the preference according to the quantifier + int pref = 0; + if( value!=it->second ){ + //we have a possible selection literal + bool selectLit = d_quant_selection_lit[f].isNull(); + bool selectLitConstraints = true; + //it is a constantly defined selection literal : the quantifier is sat + if( isConst ){ + selectLit = selectLit || d_quant_sat.find( f )==d_quant_sat.end(); + d_quant_sat[f] = true; + //check if choosing this literal would add any additional constraints to default definitions + selectLitConstraints = false; + for( int j=0; j<(int)uf_terms.size(); j++ ){ + Node op = uf_terms[j].getOperator(); + if( d_uf_prefs[op].d_reconsiderModel ){ + selectLitConstraints = true; + } + } + if( !selectLitConstraints ){ + selectLit = true; + } } + //see if we wish to choose this as a selection literal + d_quant_selection_lit_candidates[f].push_back( value ? n : n.notNode() ); + if( selectLit ){ + Trace("inst-gen-debug") << "Choose selection literal " << gn << std::endl; + d_quant_selection_lit[f] = value ? n : n.notNode(); + selectionLitTerms.clear(); + selectionLitTerms.insert( selectionLitTerms.begin(), uf_terms.begin(), uf_terms.end() ); + if( !selectLitConstraints ){ + break; + } + } + pref = 1; + }else{ + pref = -1; } - if( !constantSatReconsider ){ - 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] ); + //if we are not yet SAT, so we will add to preferences + if( d_quant_sat.find( f )==d_quant_sat.end() ){ + Debug("fmf-model-prefs") << " It is " << ( pref==1 ? "pro" : "con" ); + Debug("fmf-model-prefs") << " the definition of " << n << std::endl; + for( int j=0; j<(int)uf_terms.size(); j++ ){ + pro_con[ pref==1 ? 0 : 1 ].push_back( uf_terms[j] ); + } } } } - } - if( d_quant_sat.find( f )!=d_quant_sat.end() ){ - Debug("fmf-model-prefs") << " * Constant SAT due to definition of ops: "; - for( int i=0; i<(int)constantSatOps.size(); i++ ){ - Debug("fmf-model-prefs") << constantSatOps[i] << " "; - d_uf_prefs[constantSatOps[i]].d_reconsiderModel = false; + //process information about selection literal for f + if( !d_quant_selection_lit[f].isNull() ){ + d_quant_selection_lit_terms[f].insert( d_quant_selection_lit_terms[f].begin(), selectionLitTerms.begin(), selectionLitTerms.end() ); + for( int i=0; i<(int)selectionLitTerms.size(); i++ ){ + d_term_selection_lit[ selectionLitTerms[i] ] = d_quant_selection_lit[f]; + d_op_selection_terms[ selectionLitTerms[i].getOperator() ].push_back( selectionLitTerms[i] ); + } + }else{ + Trace("inst-gen-warn") << "WARNING: " << f << " has no selection literals (is the body of f clausified?)" << std::endl; } - Debug("fmf-model-prefs") << std::endl; - quantSatInit++; - d_statistics.d_pre_sat_quant += quantSatInit; - }else{ - nquantSatInit++; - d_statistics.d_pre_nsat_quant += quantSatInit; - //note quantifier's value preferences to models - for( int k=0; k<2; k++ ){ - for( int j=0; j<(int)pro_con[k].size(); j++ ){ - Node op = pro_con[k][j].getOperator(); - Node r = fm->getRepresentative( pro_con[k][j] ); - d_uf_prefs[op].setValuePreference( f, pro_con[k][j], r, k==0 ); + //process information about requirements and preferences of quantifier f + if( d_quant_sat.find( f )!=d_quant_sat.end() ){ + Debug("fmf-model-prefs") << " * Constant SAT due to definition of ops: "; + for( int i=0; i<(int)selectionLitTerms.size(); i++ ){ + Debug("fmf-model-prefs") << selectionLitTerms[i] << " "; + d_uf_prefs[ selectionLitTerms[i].getOperator() ].d_reconsiderModel = false; + } + Debug("fmf-model-prefs") << std::endl; + quantSatInit++; + ++(d_statistics.d_pre_sat_quant); + }else{ + nquantSatInit++; + ++(d_statistics.d_pre_nsat_quant); + //note quantifier's value preferences to models + for( int k=0; k<2; k++ ){ + for( int j=0; j<(int)pro_con[k].size(); j++ ){ + Node op = pro_con[k][j].getOperator(); + Node r = fm->getRepresentative( pro_con[k][j] ); + d_uf_prefs[op].setValuePreference( f, pro_con[k][j], r, k==0 ); + } } } } @@ -248,58 +294,87 @@ void ModelEngineBuilder::analyzeQuantifiers( FirstOrderModel* fm ){ } int ModelEngineBuilder::doInstGen( FirstOrderModel* fm, Node f ){ - //we wish to add all known exceptions to our model basis literal(s) - // this will help to refine our current model. + int addedLemmas = 0; + //we wish to add all known exceptions to our selection literal for f. this will help to refine our current model. //This step is advantageous over exhaustive instantiation, since we are adding instantiations that involve model basis terms, // effectively acting as partial instantiations instead of pointwise instantiations. - int addedLemmas = 0; - for( int i=0; i<(int)d_quant_selection_lits[f].size(); i++ ){ - bool phase = d_quant_selection_lits[f][i].getKind()!=NOT; - Node lit = d_quant_selection_lits[f][i].getKind()==NOT ? d_quant_selection_lits[f][i][0] : d_quant_selection_lits[f][i]; + if( !d_quant_selection_lit[f].isNull() ){ +#if 0 + bool phase = d_quant_selection_lit[f].getKind()!=NOT; + Node lit = d_quant_selection_lit[f].getKind()==NOT ? d_quant_selection_lit[f][0] : d_quant_selection_lit[f]; Assert( lit.hasAttribute(InstConstantAttribute()) ); - std::vector< Node > tr_terms; - if( lit.getKind()==APPLY_UF ){ - //only match predicates that are contrary to this one, use literal matching - Node eq = NodeManager::currentNM()->mkNode( IFF, lit, !phase ? fm->d_true : fm->d_false ); - fm->getTermDatabase()->setInstantiationConstantAttr( eq, f ); - tr_terms.push_back( eq ); - }else if( lit.getKind()==EQUAL ){ - //collect trigger terms - for( int j=0; j<2; j++ ){ - if( lit[j].hasAttribute(InstConstantAttribute()) ){ - if( lit[j].getKind()==APPLY_UF ){ - tr_terms.push_back( lit[j] ); - }else{ - tr_terms.clear(); - break; - } + for( size_t i=0; i<d_quant_selection_lit_terms[f].size(); i++ ){ + Node n1 = d_quant_selection_lit_terms[f][i]; + Node op = d_quant_selection_lit_terms[f][i].getOperator(); + //check all other selection literals involving "op" + for( size_t i=0; i<d_op_selection_terms[op].size(); i++ ){ + Node n2 = d_op_selection_terms[op][i]; + Node n2_lit = d_term_selection_lit[ n2 ]; + if( n2_lit!=d_quant_selection_lit[f] ){ + //match n1 and n2 } } - if( tr_terms.size()==1 && !phase ){ - //equality between a function and a ground term, use literal matching - tr_terms.clear(); - tr_terms.push_back( lit ); + if( addedLemmas==0 ){ + //check all ground terms involving "op" + for( size_t i=0; i<fm->d_uf_terms[op].size(); i++ ){ + Node n2 = fm->d_uf_terms[op][i]; + if( n1!=n2 ){ + //match n1 and n2 + } + } } } - //if applicable, try to add exceptions here - if( !tr_terms.empty() ){ - //make a trigger for these terms, add instantiations - inst::Trigger* tr = inst::Trigger::mkTrigger( d_qe, f, tr_terms ); - //Notice() << "Trigger = " << (*tr) << std::endl; - tr->resetInstantiationRound(); - tr->reset( Node::null() ); - //d_qe->d_optInstMakeRepresentative = false; - //d_qe->d_optMatchIgnoreModelBasis = true; - addedLemmas += tr->addInstantiations( d_quant_basis_match[f] ); +#else + Trace("inst-gen") << "Do Inst-Gen for " << f << std::endl; + for( size_t i=0; i<d_quant_selection_lit_candidates[f].size(); i++ ){ + bool phase = d_quant_selection_lit_candidates[f][i].getKind()!=NOT; + Node lit = d_quant_selection_lit_candidates[f][i].getKind()==NOT ? d_quant_selection_lit_candidates[f][i][0] : d_quant_selection_lit_candidates[f][i]; + Assert( lit.hasAttribute(InstConstantAttribute()) ); + std::vector< Node > tr_terms; + if( lit.getKind()==APPLY_UF ){ + //only match predicates that are contrary to this one, use literal matching + Node eq = NodeManager::currentNM()->mkNode( IFF, lit, !phase ? fm->d_true : fm->d_false ); + d_qe->getTermDatabase()->setInstantiationConstantAttr( eq, f ); + tr_terms.push_back( eq ); + }else if( lit.getKind()==EQUAL ){ + //collect trigger terms + for( int j=0; j<2; j++ ){ + if( lit[j].hasAttribute(InstConstantAttribute()) ){ + if( lit[j].getKind()==APPLY_UF ){ + tr_terms.push_back( lit[j] ); + }else{ + tr_terms.clear(); + break; + } + } + } + if( tr_terms.size()==1 && !phase ){ + //equality between a function and a ground term, use literal matching + tr_terms.clear(); + tr_terms.push_back( lit ); + } + } + //if applicable, try to add exceptions here + if( !tr_terms.empty() ){ + //make a trigger for these terms, add instantiations + inst::Trigger* tr = inst::Trigger::mkTrigger( d_qe, f, tr_terms ); + //Notice() << "Trigger = " << (*tr) << std::endl; + tr->resetInstantiationRound(); + tr->reset( Node::null() ); + //d_qe->d_optInstMakeRepresentative = false; + //d_qe->d_optMatchIgnoreModelBasis = true; + addedLemmas += tr->addInstantiations( d_quant_basis_match[f] ); + } } +#endif } return addedLemmas; } -void ModelEngineBuilder::finishBuildModel( FirstOrderModel* fm ){ +void ModelEngineBuilder::constructModel( FirstOrderModel* fm ){ //build model for UF for( std::map< Node, uf::UfModelTree >::iterator it = fm->d_uf_model_tree.begin(); it != fm->d_uf_model_tree.end(); ++it ){ - finishBuildModelUf( fm, it->first ); + constructModelUf( fm, it->first ); } /* //build model for arrays @@ -311,10 +386,10 @@ void ModelEngineBuilder::finishBuildModel( FirstOrderModel* fm ){ it->second.setDefaultValue( fm->getRepresentative( selModelBasis ) ); } */ - Debug("fmf-model-debug") << "Done building models." << std::endl; + Trace("model-engine-debug") << "Done building models." << std::endl; } -void ModelEngineBuilder::finishBuildModelUf( FirstOrderModel* fm, Node op ){ +void ModelEngineBuilder::constructModelUf( FirstOrderModel* fm, Node op ){ #ifdef RECONSIDER_FUNC_CONSTANT if( d_uf_model_constructed[op] ){ if( d_uf_prefs[op].d_reconsiderModel ){ @@ -337,7 +412,7 @@ void ModelEngineBuilder::finishBuildModelUf( FirstOrderModel* fm, Node op ){ //set the values in the model for( size_t i=0; i<fm->d_uf_terms[op].size(); i++ ){ Node n = fm->d_uf_terms[op][i]; - fm->getTermDatabase()->computeModelBasisArgAttribute( n ); + d_qe->getTermDatabase()->computeModelBasisArgAttribute( n ); if( !n.getAttribute(NoMatchAttribute()) || n.getAttribute(ModelBasisArgAttribute())==1 ){ Node v = fm->getRepresentative( n ); //if this assertion did not help the model, just consider it ground @@ -381,12 +456,6 @@ void ModelEngineBuilder::finishBuildModelUf( FirstOrderModel* fm, Node op ){ } } -void ModelEngineBuilder::finishProcessBuildModel( TheoryModel* m ){ - for( std::map< Node, Node >::iterator it = m->d_reps.begin(); it != m->d_reps.end(); ++it ){ - //build proper representatives (TODO) - } -} - bool ModelEngineBuilder::optUseModel() { return options::fmfModelBasedInst(); } @@ -396,11 +465,11 @@ bool ModelEngineBuilder::optInstGen(){ } bool ModelEngineBuilder::optOneQuantPerRoundInstGen(){ -#ifdef ONE_QUANT_PER_ROUND_INST_GEN - return true; -#else - return false; -#endif + return options::fmfInstGenOneQuantPerRound(); +} + +void ModelEngineBuilder::setEffort( int effort ){ + d_considerAxioms = effort>=1; } ModelEngineBuilder::Statistics::Statistics(): @@ -415,3 +484,7 @@ ModelEngineBuilder::Statistics::~Statistics(){ StatisticsRegistry::unregisterStat(&d_pre_sat_quant); StatisticsRegistry::unregisterStat(&d_pre_nsat_quant); } + +bool ModelEngineBuilder::isQuantifierActive( Node f ){ + return ( d_considerAxioms || !f.getAttribute(AxiomAttribute()) ) && d_quant_sat.find( f )==d_quant_sat.end(); +} diff --git a/src/theory/quantifiers/model_builder.h b/src/theory/quantifiers/model_builder.h index 344b731e0..1366a7650 100644 --- a/src/theory/quantifiers/model_builder.h +++ b/src/theory/quantifiers/model_builder.h @@ -27,20 +27,29 @@ namespace CVC4 { namespace theory { namespace quantifiers { -//the model builder +/** model builder class + * This class is capable of building candidate models based on the current quantified formulas + * that are asserted. Use: + * (1) call ModelEngineBuilder::buildModel( m, false );, where m is a FirstOrderModel + * (2) if candidate model is determined to be a real model, + then call ModelEngineBuilder::buildModel( m, true ); + */ class ModelEngineBuilder : public TheoryEngineModelBuilder { protected: //quantifiers engine QuantifiersEngine* d_qe; + //the model we are working with + context::CDO< FirstOrderModel* > d_curr_model; //map from operators to model preference data std::map< Node, uf::UfModelPreferenceData > d_uf_prefs; //built model uf std::map< Node, bool > d_uf_model_constructed; +protected: /** process build model */ - void processBuildModel( TheoryModel* m ); + void processBuildModel( TheoryModel* m, bool fullModel ); /** choose representative for unconstrained equivalence class */ - Node chooseRepresentative( TheoryModel* m, Node eqc ); + Node chooseRepresentative( TheoryModel* m, Node eqc, bool fullModel ); /** bad representative */ bool isBadRepresentative( Node n ); protected: @@ -49,23 +58,30 @@ protected: //analyze quantifiers void analyzeQuantifiers( FirstOrderModel* fm ); //build model - void finishBuildModel( FirstOrderModel* fm ); + void constructModel( FirstOrderModel* fm ); //theory-specific build models - void finishBuildModelUf( FirstOrderModel* fm, Node op ); + void constructModelUf( FirstOrderModel* fm, Node op ); //do InstGen techniques for quantifier, return number of lemmas produced int doInstGen( FirstOrderModel* fm, Node f ); public: - ModelEngineBuilder( QuantifiersEngine* qe ); + ModelEngineBuilder( context::Context* c, QuantifiersEngine* qe ); virtual ~ModelEngineBuilder(){} - /** finish model */ - void finishProcessBuildModel( TheoryModel* m ); -public: /** number of lemmas generated while building model */ int d_addedLemmas; + //consider axioms + bool d_considerAxioms; +private: ///information for InstGen //map from quantifiers to if are constant SAT std::map< Node, bool > d_quant_sat; - //map from quantifiers to the instantiation literals that their model is dependent upon - std::map< Node, std::vector< Node > > d_quant_selection_lits; + //map from quantifiers to their selection literals + std::map< Node, Node > d_quant_selection_lit; + std::map< Node, std::vector< Node > > d_quant_selection_lit_candidates; + //map from quantifiers to their selection literal terms + std::map< Node, std::vector< Node > > d_quant_selection_lit_terms; + //map from terms to the selection literals they exist in + std::map< Node, Node > d_term_selection_lit; + //map from operators to terms that appear in selection literals + std::map< Node, std::vector< Node > > d_op_selection_terms; public: //map from quantifiers to model basis match std::map< Node, InstMatch > d_quant_basis_match; @@ -73,6 +89,8 @@ public: bool optUseModel(); bool optInstGen(); bool optOneQuantPerRoundInstGen(); + // set effort + void setEffort( int effort ); /** statistics class */ class Statistics { public: @@ -82,6 +100,8 @@ public: ~Statistics(); }; Statistics d_statistics; + // is quantifier active? + bool isQuantifierActive( Node f ); };/* class ModelEngineBuilder */ }/* CVC4::theory::quantifiers namespace */ diff --git a/src/theory/quantifiers/model_engine.cpp b/src/theory/quantifiers/model_engine.cpp index cbeeed8d0..b5a9ee74c 100644 --- a/src/theory/quantifiers/model_engine.cpp +++ b/src/theory/quantifiers/model_engine.cpp @@ -15,7 +15,6 @@ **/ #include "theory/quantifiers/model_engine.h" -#include "theory/quantifiers/rep_set_iterator.h" #include "theory/theory_engine.h" #include "theory/uf/equality_engine.h" #include "theory/uf/theory_uf.h" @@ -25,11 +24,11 @@ #include "theory/arrays/theory_arrays_model.h" #include "theory/quantifiers/first_order_model.h" #include "theory/quantifiers/term_database.h" +#include "theory/quantifiers/quantifiers_attributes.h" //#define ME_PRINT_WARNINGS #define EVAL_FAIL_SKIP_MULTIPLE -//#define ONE_QUANT_PER_ROUND using namespace std; using namespace CVC4; @@ -40,96 +39,97 @@ using namespace CVC4::theory::quantifiers; using namespace CVC4::theory::inst; //Model Engine constructor -ModelEngine::ModelEngine( QuantifiersEngine* qe ) : +ModelEngine::ModelEngine( context::Context* c, QuantifiersEngine* qe ) : QuantifiersModule( qe ), -d_builder( qe ), -d_rel_domain( qe->getModel() ){ +d_builder( c, qe ), +d_rel_domain( qe, qe->getModel() ){ } void ModelEngine::check( Theory::Effort e ){ if( e==Theory::EFFORT_LAST_CALL && !d_quantEngine->hasAddedLemma() ){ - //first, check if we can minimize the model further - if( !((uf::TheoryUF*)d_quantEngine->getTheoryEngine()->theoryOf( THEORY_UF ))->getStrongSolver()->minimize() ){ - return; - } + FirstOrderModel* fm = d_quantEngine->getModel(); //the following will attempt to build a model and test that it satisfies all asserted universal quantifiers int addedLemmas = 0; + Trace("model-engine") << "---Model Engine Round---" << std::endl; if( d_builder.optUseModel() ){ //check if any quantifiers are un-initialized - for( int i=0; i<d_quantEngine->getModel()->getNumAssertedQuantifiers(); i++ ){ - Node f = d_quantEngine->getModel()->getAssertedQuantifier( i ); + for( int i=0; i<fm->getNumAssertedQuantifiers(); i++ ){ + Node f = fm->getAssertedQuantifier( i ); addedLemmas += initializeQuantifier( f ); } } - if( addedLemmas==0 ){ - //quantifiers are initialized, we begin an instantiation round - double clSet = 0; - if( Trace.isOn("model-engine") ){ - clSet = double(clock())/double(CLOCKS_PER_SEC); - Trace("model-engine") << "---Model Engine Round---" << std::endl; - } - Debug("fmf-model-debug") << "---Begin Instantiation Round---" << std::endl; - ++(d_statistics.d_inst_rounds); - //reset the quantifiers engine - d_quantEngine->resetInstantiationRound( e ); - //initialize the model - Debug("fmf-model-debug") << "Build model..." << std::endl; - d_builder.buildModel( d_quantEngine->getModel() ); - d_quantEngine->d_model_set = true; - //if builder has lemmas, add and return - if( d_builder.d_addedLemmas>0 ){ - addedLemmas += (int)d_builder.d_addedLemmas; - }else{ - //print debug - Debug("fmf-model-complete") << std::endl; - debugPrint("fmf-model-complete"); - //verify we are SAT by trying exhaustive instantiation - if( optUseRelevantDomain() ){ - d_rel_domain.compute(); + if( addedLemmas>0 ){ + Trace("model-engine") << "Initialize, Added Lemmas = " << addedLemmas << std::endl; + } + //two effort levels: first try exhaustive instantiation without axioms, then with. + int startEffort = ( !fm->isAxiomAsserted() || options::axiomInstMode()==AXIOM_INST_MODE_DEFAULT ) ? 1 : 0; + for( int effort=startEffort; effort<2; effort++ ){ + // for effort = 0, we only instantiate non-axioms + // for effort = 1, we instantiate everything + if( addedLemmas==0 ){ + //quantifiers are initialized, we begin an instantiation round + double clSet = 0; + if( Trace.isOn("model-engine") ){ + clSet = double(clock())/double(CLOCKS_PER_SEC); } - d_triedLemmas = 0; - d_testLemmas = 0; - d_relevantLemmas = 0; - d_totalLemmas = 0; - Debug("fmf-model-debug") << "Do exhaustive instantiation..." << std::endl; - for( int i=0; i<d_quantEngine->getModel()->getNumAssertedQuantifiers(); i++ ){ - Node f = d_quantEngine->getModel()->getAssertedQuantifier( i ); - if( d_builder.d_quant_sat.find( f )==d_builder.d_quant_sat.end() ){ - addedLemmas += exhaustiveInstantiate( f, optUseRelevantDomain() ); - if( optOneQuantPerRound() && addedLemmas>0 ){ - break; - } - } -#ifdef ME_PRINT_WARNINGS - if( addedLemmas>10000 ){ - break; + ++(d_statistics.d_inst_rounds); + //reset the quantifiers engine + d_quantEngine->resetInstantiationRound( e ); + //initialize the model + Trace("model-engine-debug") << "Build model..." << std::endl; + d_builder.setEffort( effort ); + d_builder.buildModel( fm, false ); + //if builder has lemmas, add and return + if( d_builder.d_addedLemmas>0 ){ + addedLemmas += (int)d_builder.d_addedLemmas; + }else{ + Trace("model-engine-debug") << "Verify uf ss is minimal..." << std::endl; + //let the strong solver verify that the model is minimal + uf::StrongSolverTheoryUf* uf_ss = ((uf::TheoryUF*)d_quantEngine->getTheoryEngine()->theoryOf( THEORY_UF ))->getStrongSolver(); + //for debugging, this will if there are terms in the model that the strong solver was not notified of + uf_ss->debugModel( fm ); + Trace("model-engine-debug") << "Check model..." << std::endl; + //print debug + Debug("fmf-model-complete") << std::endl; + debugPrint("fmf-model-complete"); + //successfully built an acceptable model, now check it + checkModel( addedLemmas ); + //print debug information + if( Trace.isOn("model-engine") ){ + Trace("model-engine") << "Instantiate axioms : " << ( d_builder.d_considerAxioms ? "yes" : "no" ) << std::endl; + Trace("model-engine") << "Added Lemmas = " << addedLemmas << " / " << d_triedLemmas << " / "; + Trace("model-engine") << d_testLemmas << " / " << d_relevantLemmas << " / " << d_totalLemmas << std::endl; + double clSet2 = double(clock())/double(CLOCKS_PER_SEC); + Trace("model-engine") << "Finished model engine, time = " << (clSet2-clSet) << std::endl; } -#endif - } - Debug("fmf-model-debug") << "---> Added lemmas = " << addedLemmas << " / " << d_triedLemmas << " / "; - Debug("fmf-model-debug") << d_testLemmas << " / " << d_relevantLemmas << " / " << d_totalLemmas << std::endl; - if( Trace.isOn("model-engine") ){ - Trace("model-engine") << "Added Lemmas = " << addedLemmas << " / " << d_triedLemmas << " / "; - Trace("model-engine") << d_testLemmas << " / " << d_relevantLemmas << " / " << d_totalLemmas << std::endl; - double clSet2 = double(clock())/double(CLOCKS_PER_SEC); - Trace("model-engine") << "Finished model engine, time = " << (clSet2-clSet) << std::endl; } -#ifdef ME_PRINT_WARNINGS - if( addedLemmas>10000 ){ - Debug("fmf-exit") << std::endl; - debugPrint("fmf-exit"); - exit( 0 ); + } + if( addedLemmas==0 ){ + //if we have not added lemmas yet and axiomInstMode=trust, then we are done + if( options::axiomInstMode()==AXIOM_INST_MODE_TRUST ){ + //we must return unknown if an axiom is asserted + if( effort==0 ){ + d_incomplete_check = true; + } + break; } -#endif } } if( addedLemmas==0 ){ - //CVC4 will answer SAT - Debug("fmf-consistent") << std::endl; + Trace("model-engine-debug") << "No lemmas added, incomplete = " << d_incomplete_check << std::endl; + //CVC4 will answer SAT or unknown + Trace("fmf-consistent") << std::endl; debugPrint("fmf-consistent"); - // finish building the model in the standard way - d_builder.finishProcessBuildModel( d_quantEngine->getModel() ); + if( options::produceModels() ){ + // finish building the model in the standard way + d_builder.buildModel( fm, true ); + d_quantEngine->d_model_set = true; + } + //if the check was incomplete, we must set incomplete flag + if( d_incomplete_check ){ + d_quantEngine->getOutputChannel().setIncomplete(); + } }else{ //otherwise, the search will continue d_quantEngine->flushLemmas( &d_quantEngine->getOutputChannel() ); @@ -154,11 +154,7 @@ bool ModelEngine::optUseRelevantDomain(){ } bool ModelEngine::optOneQuantPerRound(){ -#ifdef ONE_QUANT_PER_ROUND - return true; -#else - return false; -#endif + return options::fmfOneQuantPerRound(); } int ModelEngine::initializeQuantifier( Node f ){ @@ -178,11 +174,13 @@ int ModelEngine::initializeQuantifier( Node f ){ // Notice() << "Unhandled phase req: " << n << std::endl; // } //} + std::vector< Node > vars; std::vector< Node > ics; std::vector< Node > terms; for( int j=0; j<(int)f[0].getNumChildren(); j++ ){ Node ic = d_quantEngine->getTermDatabase()->getInstantiationConstant( f, j ); Node t = d_quantEngine->getTermDatabase()->getModelBasisTerm( ic.getType() ); + vars.push_back( f[0][j] ); ics.push_back( ic ); terms.push_back( t ); //calculate the basis match for f @@ -193,67 +191,137 @@ int ModelEngine::initializeQuantifier( Node f ){ Node n = d_quantEngine->getTermDatabase()->getCounterexampleBody( f ); Node gn = n.substitute( ics.begin(), ics.end(), terms.begin(), terms.end() ); d_quantEngine->getTermDatabase()->registerModelBasis( n, gn ); - //add model basis instantiation - if( d_quantEngine->addInstantiation( f, terms ) ){ - return 1; - }else{ - //shouldn't happen usually, but will occur if x != y is a required literal for f. - //Notice() << "No model basis for " << f << std::endl; - ++(d_statistics.d_num_quants_init_fail); + if( d_builder.optInstGen() ){ + //add model basis instantiation + if( d_quantEngine->addInstantiation( f, vars, terms ) ){ + return 1; + }else{ + //shouldn't happen usually, but will occur if x != y is a required literal for f. + //Notice() << "No model basis for " << f << std::endl; + ++(d_statistics.d_num_quants_init_fail); + } } } return 0; } -int ModelEngine::exhaustiveInstantiate( Node f, bool useRelInstDomain ){ - int tests = 0; - int addedLemmas = 0; - int triedLemmas = 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->getTermDatabase()->getInstantiationConstant( f, i ) << " "; +void ModelEngine::checkModel( int& addedLemmas ){ + FirstOrderModel* fm = d_quantEngine->getModel(); + //for debugging + if( Trace.isOn("model-engine") ){ + for( std::map< TypeNode, std::vector< Node > >::iterator it = fm->d_rep_set.d_type_reps.begin(); it != fm->d_rep_set.d_type_reps.end(); ++it ){ + if( it->first.isSort() ){ + Trace("model-engine") << "Cardinality( " << it->first << " )" << " = " << it->second.size() << std::endl; + Trace("model-engine-debug") << " "; + for( size_t i=0; i<it->second.size(); i++ ){ + Trace("model-engine-debug") << it->second[i] << " "; + } + Trace("model-engine-debug") << std::endl; + } + } + } + //verify we are SAT by trying exhaustive instantiation + d_incomplete_check = false; + if( optUseRelevantDomain() ){ + d_rel_domain.compute(); } - Debug("inst-fmf-ei") << std::endl; - if( d_builder.d_quant_selection_lits[f].empty() ){ - Debug("inst-fmf-ei") << "WARNING: " << f << " has no model literal definitions (is f clausified?)" << std::endl; + d_triedLemmas = 0; + d_testLemmas = 0; + d_relevantLemmas = 0; + d_totalLemmas = 0; + Debug("fmf-model-debug") << "Do exhaustive instantiation..." << std::endl; + for( int i=0; i<fm->getNumAssertedQuantifiers(); i++ ){ + Node f = fm->getAssertedQuantifier( i ); + addedLemmas += exhaustiveInstantiate( f, optUseRelevantDomain() ); #ifdef ME_PRINT_WARNINGS - Message() << "WARNING: " << f << " has no model literal definitions (is f clausified?)" << std::endl; + if( addedLemmas>10000 ){ + Debug("fmf-exit") << std::endl; + debugPrint("fmf-exit"); + exit( 0 ); + } #endif - }else{ - Debug("inst-fmf-ei") << " Model literal definitions:" << std::endl; - for( size_t i=0; i<d_builder.d_quant_selection_lits[f].size(); i++ ){ - Debug("inst-fmf-ei") << " " << d_builder.d_quant_selection_lits[f][i] << std::endl; + if( optOneQuantPerRound() && addedLemmas>0 ){ + break; } } - RepSetIterator riter( f, d_quantEngine->getModel() ); - //set the domain for the iterator (the sufficient set of instantiations to try) - if( useRelInstDomain ){ - riter.setDomain( d_rel_domain.d_quant_inst_domain[f] ); + Debug("fmf-model-debug") << "---> Added lemmas = " << addedLemmas << " / " << d_triedLemmas << " / "; + Debug("fmf-model-debug") << d_testLemmas << " / " << d_relevantLemmas << " / " << d_totalLemmas << std::endl; +} + +int ModelEngine::exhaustiveInstantiate( Node f, bool useRelInstDomain ){ + int addedLemmas = 0; + //keep track of total instantiations for statistics + int totalInst = 1; + for( size_t i=0; i<f[0].getNumChildren(); i++ ){ + TypeNode tn = f[0][i].getType(); + if( d_quantEngine->getModel()->d_rep_set.hasType( tn ) ){ + totalInst = totalInst * (int)d_quantEngine->getModel()->d_rep_set.d_type_reps[ tn ].size(); + } } - RepSetEvaluator reval( d_quantEngine->getModel(), &riter ); - while( !riter.isFinished() && ( addedLemmas==0 || !optOneInstPerQuantRound() ) ){ - d_testLemmas++; - if( d_builder.optUseModel() ){ - //see if instantiation is already true in current model - Debug("fmf-model-eval") << "Evaluating "; - riter.debugPrintSmall("fmf-model-eval"); - Debug("fmf-model-eval") << "Done calculating terms." << std::endl; - tests++; - //if evaluate(...)==1, then the instantiation is already true in the model - // depIndex is the index of the least significant variable that this evaluation relies upon - int depIndex = riter.getNumTerms()-1; - int eval = reval.evaluate( d_quantEngine->getTermDatabase()->getCounterexampleBody( f ), depIndex ); + d_totalLemmas += totalInst; + //if we need to consider this quantifier on this iteration + if( d_builder.isQuantifierActive( f ) ){ + Trace("rel-dom") << "Exhaustive instantiate " << f << std::endl; + if( useRelInstDomain ){ + Trace("rel-dom") << "Relevant domain : " << std::endl; + for( size_t i=0; i<d_rel_domain.d_quant_inst_domain[f].size(); i++ ){ + Trace("rel-dom") << " " << i << " : "; + for( size_t j=0; j<d_rel_domain.d_quant_inst_domain[f][i].size(); j++ ){ + Trace("rel-dom") << d_rel_domain.d_quant_inst_domain[f][i][j] << " "; + } + Trace("rel-dom") << std::endl; + } + } + int tests = 0; + int triedLemmas = 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->getTermDatabase()->getInstantiationConstant( f, i ) << " "; + } + Debug("inst-fmf-ei") << std::endl; + RepSetIterator riter( &(d_quantEngine->getModel()->d_rep_set) ); + riter.setQuantifier( f ); + //if the iterator is incomplete, we will return unknown instead of sat if no instantiations are added this round + d_incomplete_check = d_incomplete_check || riter.d_incomplete; + //set the domain for the iterator (the sufficient set of instantiations to try) + if( useRelInstDomain ){ + riter.setDomain( d_rel_domain.d_quant_inst_domain[f] ); + } + d_quantEngine->getModel()->resetEvaluate(); + while( !riter.isFinished() && ( addedLemmas==0 || !optOneInstPerQuantRound() ) ){ + d_testLemmas++; + int eval = 0; + int depIndex; + if( d_builder.optUseModel() ){ + //see if instantiation is already true in current model + Debug("fmf-model-eval") << "Evaluating "; + riter.debugPrintSmall("fmf-model-eval"); + Debug("fmf-model-eval") << "Done calculating terms." << std::endl; + tests++; + //if evaluate(...)==1, then the instantiation is already true in the model + // depIndex is the index of the least significant variable that this evaluation relies upon + depIndex = riter.getNumTerms()-1; + eval = d_quantEngine->getModel()->evaluate( d_quantEngine->getTermDatabase()->getCounterexampleBody( f ), depIndex, &riter ); + if( eval==1 ){ + Debug("fmf-model-eval") << " Returned success with depIndex = " << depIndex << std::endl; + }else{ + Debug("fmf-model-eval") << " Returned " << (eval==-1 ? "failure" : "unknown") << ", depIndex = " << depIndex << std::endl; + } + } if( eval==1 ){ - Debug("fmf-model-eval") << " Returned success with depIndex = " << depIndex << std::endl; + //instantiation is already true -> skip riter.increment2( depIndex ); }else{ - Debug("fmf-model-eval") << " Returned " << (eval==-1 ? "failure" : "unknown") << ", depIndex = " << depIndex << std::endl; + //instantiation was not shown to be true, construct the match InstMatch m; - riter.getMatch( d_quantEngine, m ); + for( int i=0; i<riter.getNumTerms(); i++ ){ + m.set( d_quantEngine->getTermDatabase()->getInstantiationConstant( f, riter.d_index_order[i] ), riter.getTerm( i ) ); + } Debug("fmf-model-eval") << "* Add instantiation " << m << std::endl; triedLemmas++; d_triedLemmas++; + //add as instantiation if( d_quantEngine->addInstantiation( f, m ) ){ addedLemmas++; #ifdef EVAL_FAIL_SKIP_MULTIPLE @@ -270,70 +338,48 @@ int ModelEngine::exhaustiveInstantiate( Node f, bool useRelInstDomain ){ riter.increment(); } } - }else{ - InstMatch m; - riter.getMatch( d_quantEngine, m ); - Debug("fmf-model-eval") << "* Add instantiation " << std::endl; - triedLemmas++; - d_triedLemmas++; - if( d_quantEngine->addInstantiation( f, m ) ){ - addedLemmas++; - } - riter.increment(); } - } - d_statistics.d_eval_formulas += reval.d_eval_formulas; - d_statistics.d_eval_uf_terms += reval.d_eval_uf_terms; - d_statistics.d_eval_lits += reval.d_eval_lits; - d_statistics.d_eval_lits_unknown += reval.d_eval_lits_unknown; - int totalInst = 1; - int relevantInst = 1; - for( size_t i=0; i<f[0].getNumChildren(); i++ ){ - totalInst = totalInst * (int)d_quantEngine->getModel()->d_rep_set.d_type_reps[ f[0][i].getType() ].size(); - relevantInst = relevantInst * (int)riter.d_domain[i].size(); - } - d_totalLemmas += totalInst; - d_relevantLemmas += relevantInst; - Debug("inst-fmf-ei") << "Finished: " << std::endl; - Debug("inst-fmf-ei") << " Inst Total: " << totalInst << std::endl; - Debug("inst-fmf-ei") << " Inst Relevant: " << relevantInst << std::endl; - Debug("inst-fmf-ei") << " Inst Tried: " << triedLemmas << std::endl; - Debug("inst-fmf-ei") << " Inst Added: " << addedLemmas << std::endl; - Debug("inst-fmf-ei") << " # Tests: " << tests << std::endl; -///----------- + d_statistics.d_eval_formulas += d_quantEngine->getModel()->d_eval_formulas; + d_statistics.d_eval_uf_terms += d_quantEngine->getModel()->d_eval_uf_terms; + d_statistics.d_eval_lits += d_quantEngine->getModel()->d_eval_lits; + d_statistics.d_eval_lits_unknown += d_quantEngine->getModel()->d_eval_lits_unknown; + int relevantInst = 1; + for( size_t i=0; i<f[0].getNumChildren(); i++ ){ + relevantInst = relevantInst * (int)riter.d_domain[i].size(); + } + d_relevantLemmas += relevantInst; + Debug("inst-fmf-ei") << "Finished: " << std::endl; + Debug("inst-fmf-ei") << " Inst Total: " << totalInst << std::endl; + Debug("inst-fmf-ei") << " Inst Relevant: " << relevantInst << std::endl; + Debug("inst-fmf-ei") << " Inst Tried: " << triedLemmas << std::endl; + Debug("inst-fmf-ei") << " Inst Added: " << addedLemmas << std::endl; + Debug("inst-fmf-ei") << " # Tests: " << tests << std::endl; #ifdef ME_PRINT_WARNINGS - if( addedLemmas>1000 ){ - Notice() << "WARNING: many instantiations produced for " << f << ": " << std::endl; - Notice() << " Inst Total: " << totalInst << std::endl; - Notice() << " Inst Relevant: " << totalRelevant << std::endl; - Notice() << " Inst Tried: " << triedLemmas << std::endl; - Notice() << " Inst Added: " << addedLemmas << std::endl; - Notice() << " # Tests: " << tests << std::endl; - Notice() << std::endl; - if( !d_builder.d_quant_selection_lits[f].empty() ){ - Notice() << " Model literal definitions:" << std::endl; - for( size_t i=0; i<d_builder.d_quant_selection_lits[f].size(); i++ ){ - Notice() << " " << d_builder.d_quant_selection_lits[f][i] << std::endl; - } + if( addedLemmas>1000 ){ + Notice() << "WARNING: many instantiations produced for " << f << ": " << std::endl; + Notice() << " Inst Total: " << totalInst << std::endl; + Notice() << " Inst Relevant: " << totalRelevant << std::endl; + Notice() << " Inst Tried: " << triedLemmas << std::endl; + Notice() << " Inst Added: " << addedLemmas << std::endl; + Notice() << " # Tests: " << tests << std::endl; Notice() << std::endl; } - } #endif -///----------- + } return addedLemmas; } void ModelEngine::debugPrint( const char* c ){ - Debug( c ) << "Quantifiers: " << std::endl; + Trace( c ) << "Quantifiers: " << std::endl; for( int i=0; i<(int)d_quantEngine->getModel()->getNumAssertedQuantifiers(); i++ ){ Node f = d_quantEngine->getModel()->getAssertedQuantifier( i ); - Debug( c ) << " "; - if( d_builder.d_quant_sat.find( f )!=d_builder.d_quant_sat.end() ){ - Debug( c ) << "*SAT* "; + Trace( c ) << " "; + if( !d_builder.isQuantifierActive( f ) ){ + Trace( c ) << "*Inactive* "; }else{ - Debug( c ) << " "; + Trace( c ) << " "; } - Debug( c ) << f << std::endl; + Trace( c ) << f << std::endl; } //d_quantEngine->getModel()->debugPrint( c ); } diff --git a/src/theory/quantifiers/model_engine.h b/src/theory/quantifiers/model_engine.h index f5d1db736..a292eb5f8 100644 --- a/src/theory/quantifiers/model_engine.h +++ b/src/theory/quantifiers/model_engine.h @@ -40,6 +40,8 @@ private: //data maintained globally: private: //analysis of current model: //relevant domain RelevantDomain d_rel_domain; + //is the exhaustive instantiation incomplete? + bool d_incomplete_check; private: //options bool optOneInstPerQuantRound(); @@ -48,6 +50,8 @@ private: private: //initialize quantifiers, return number of lemmas produced int initializeQuantifier( Node f ); + //check model + void checkModel( int& addedLemmas ); //exhaustively instantiate quantifier (possibly using mbqi), return number of lemmas produced int exhaustiveInstantiate( Node f, bool useRelInstDomain = false ); private: @@ -57,7 +61,7 @@ private: int d_totalLemmas; int d_relevantLemmas; public: - ModelEngine( QuantifiersEngine* qe ); + ModelEngine( context::Context* c, QuantifiersEngine* qe ); ~ModelEngine(){} public: void check( Theory::Effort e ); diff --git a/src/theory/quantifiers/inst_when_mode.cpp b/src/theory/quantifiers/modes.cpp index b60148c21..b6786d9f0 100644 --- a/src/theory/quantifiers/inst_when_mode.cpp +++ b/src/theory/quantifiers/modes.cpp @@ -2,7 +2,7 @@ /*! \file inst_when_mode.cpp ** \verbatim ** Original author: mdeters - ** Major contributors: none + ** Major contributors: ajreynol ** Minor contributors (to current version): none ** This file is part of the CVC4 prototype. ** Copyright (c) 2009-2012 The Analysis of Computer Systems Group (ACSys) @@ -18,7 +18,7 @@ **/ #include <iostream> -#include "theory/quantifiers/inst_when_mode.h" +#include "theory/quantifiers/modes.h" namespace CVC4 { @@ -43,5 +43,41 @@ std::ostream& operator<<(std::ostream& out, theory::quantifiers::InstWhenMode mo return out; } +std::ostream& operator<<(std::ostream& out, theory::quantifiers::LiteralMatchMode mode) { + switch(mode) { + case theory::quantifiers::LITERAL_MATCH_NONE: + out << "LITERAL_MATCH_NONE"; + break; + case theory::quantifiers::LITERAL_MATCH_PREDICATE: + out << "LITERAL_MATCH_PREDICATE"; + break; + case theory::quantifiers::LITERAL_MATCH_EQUALITY: + out << "LITERAL_MATCH_EQUALITY"; + break; + default: + out << "LiteralMatchMode!UNKNOWN"; + } + + return out; +} + +std::ostream& operator<<(std::ostream& out, theory::quantifiers::AxiomInstMode mode) { + switch(mode) { + case theory::quantifiers::AXIOM_INST_MODE_DEFAULT: + out << "AXIOM_INST_MODE_DEFAULT"; + break; + case theory::quantifiers::AXIOM_INST_MODE_TRUST: + out << "AXIOM_INST_MODE_TRUST"; + break; + case theory::quantifiers::AXIOM_INST_MODE_PRIORITY: + out << "AXIOM_INST_MODE_PRIORITY"; + break; + default: + out << "AxiomInstMode!UNKNOWN"; + } + + return out; +} + }/* CVC4 namespace */ diff --git a/src/theory/quantifiers/inst_when_mode.h b/src/theory/quantifiers/modes.h index 2cfba4935..8c56b8f3f 100644 --- a/src/theory/quantifiers/inst_when_mode.h +++ b/src/theory/quantifiers/modes.h @@ -1,8 +1,8 @@ /********************* */ -/*! \file inst_when_mode.h +/*! \file modes.h ** \verbatim ** Original author: mdeters - ** Major contributors: none + ** Major contributors: ajreynol ** Minor contributors (to current version): none ** This file is part of the CVC4 prototype. ** Copyright (c) 2009-2012 The Analysis of Computer Systems Group (ACSys) @@ -19,8 +19,8 @@ #include "cvc4_private.h" -#ifndef __CVC4__THEORY__QUANTIFIERS__INST_WHEN_MODE_H -#define __CVC4__THEORY__QUANTIFIERS__INST_WHEN_MODE_H +#ifndef __CVC4__THEORY__QUANTIFIERS__MODEs_H +#define __CVC4__THEORY__QUANTIFIERS__MODEs_H #include <iostream> @@ -39,6 +39,25 @@ typedef enum { INST_WHEN_LAST_CALL, } InstWhenMode; +typedef enum { + /** Do not consider polarity of patterns */ + LITERAL_MATCH_NONE, + /** Consider polarity of boolean predicates only */ + LITERAL_MATCH_PREDICATE, + /** Consider polarity of boolean predicates, as well as equalities */ + LITERAL_MATCH_EQUALITY, +} LiteralMatchMode; + +typedef enum { + /** default, use all methods for axioms */ + AXIOM_INST_MODE_DEFAULT, + /** only use heuristic methods for axioms, return unknown in the case no instantiations are produced */ + AXIOM_INST_MODE_TRUST, + /** only use heuristic methods for axioms, resort to all methods when no instantiations are produced */ + AXIOM_INST_MODE_PRIORITY, +} AxiomInstMode; + + }/* CVC4::theory::quantifiers namespace */ }/* CVC4::theory namespace */ diff --git a/src/theory/quantifiers/options b/src/theory/quantifiers/options index 91e831092..a28ccb423 100644 --- a/src/theory/quantifiers/options +++ b/src/theory/quantifiers/options @@ -45,13 +45,13 @@ option smartTriggers /--disable-smart-triggers bool :default true option registerQuantBodyTerms --register-quant-body-terms bool :default false consider ground terms within bodies of quantified formulas for matching -option instWhenMode --inst-when=MODE CVC4::theory::quantifiers::InstWhenMode :default CVC4::theory::quantifiers::INST_WHEN_FULL_LAST_CALL :include "theory/quantifiers/inst_when_mode.h" :handler CVC4::theory::quantifiers::stringToInstWhenMode :handler-include "theory/quantifiers/options_handlers.h" :predicate CVC4::theory::quantifiers::checkInstWhenMode :predicate-include "theory/quantifiers/options_handlers.h" +option instWhenMode --inst-when=MODE CVC4::theory::quantifiers::InstWhenMode :default CVC4::theory::quantifiers::INST_WHEN_FULL_LAST_CALL :read-write :include "theory/quantifiers/modes.h" :handler CVC4::theory::quantifiers::stringToInstWhenMode :handler-include "theory/quantifiers/options_handlers.h" :predicate CVC4::theory::quantifiers::checkInstWhenMode :predicate-include "theory/quantifiers/options_handlers.h" when to apply instantiation - + option eagerInstQuant --eager-inst-quant bool :default false apply quantifier instantiation eagerly -option literalMatchMode --literal-matching=MODE CVC4::theory::quantifiers::LiteralMatchMode :default CVC4::theory::quantifiers::LITERAL_MATCH_NONE :include "theory/quantifiers/literal_match_mode.h" :handler CVC4::theory::quantifiers::stringToLiteralMatchMode :handler-include "theory/quantifiers/options_handlers.h" :predicate CVC4::theory::quantifiers::checkLiteralMatchMode :predicate-include "theory/quantifiers/options_handlers.h" +option literalMatchMode --literal-matching=MODE CVC4::theory::quantifiers::LiteralMatchMode :default CVC4::theory::quantifiers::LITERAL_MATCH_NONE :include "theory/quantifiers/modes.h" :handler CVC4::theory::quantifiers::stringToLiteralMatchMode :handler-include "theory/quantifiers/options_handlers.h" :predicate CVC4::theory::quantifiers::checkLiteralMatchMode :predicate-include "theory/quantifiers/options_handlers.h" choose literal matching mode option cbqi --enable-cbqi/--disable-cbqi bool :default false @@ -67,23 +67,23 @@ option flipDecision --enable-flip-decision/ bool :default false option finiteModelFind --finite-model-find bool :default false use finite model finding heuristic for quantifier instantiation -option ufssRegions /--disable-uf-ss-regions bool :default true - disable region-based method for discovering cliques and splits in uf strong solver -option ufssEagerSplits --uf-ss-eager-split bool :default false - add splits eagerly for uf strong solver -option ufssColoringSat --uf-ss-coloring-sat bool :default false - use coloring-based SAT heuristic for uf strong solver - option fmfModelBasedInst /--disable-fmf-mbqi bool :default true disable model-based quantifier instantiation for finite model finding -option fmfInstGen /--disable-fmf-inst-gen bool :default true - disable Inst-Gen instantiation techniques for finite model finding option fmfOneInstPerRound --fmf-one-inst-per-round bool :default false only add one instantiation per quantifier per round for fmf +option fmfOneQuantPerRound --fmf-one-quant-per-round bool :default false + only add instantiations for one quantifier per round for fmf option fmfInstEngine --fmf-inst-engine bool :default false use instantiation engine in conjunction with finite model finding option fmfRelevantDomain --fmf-relevant-domain bool :default false use relevant domain computation, similar to complete instantiation (Ge, deMoura 09) +option fmfInstGen /--disable-fmf-inst-gen bool :default true + disable Inst-Gen instantiation techniques for finite model finding +option fmfInstGenOneQuantPerRound --fmf-inst-gen-one-quant-per-round bool :default false + only perform Inst-Gen instantiation techniques on one quantifier per round + +option axiomInstMode --axiom-inst=MODE CVC4::theory::quantifiers::AxiomInstMode :default CVC4::theory::quantifiers::AXIOM_INST_MODE_DEFAULT :include "theory/quantifiers/modes.h" :handler CVC4::theory::quantifiers::stringToAxiomInstMode :handler-include "theory/quantifiers/options_handlers.h" + policy for instantiating axioms endmodule diff --git a/src/theory/quantifiers/options_handlers.h b/src/theory/quantifiers/options_handlers.h index 24734e8c8..00e91d115 100644 --- a/src/theory/quantifiers/options_handlers.h +++ b/src/theory/quantifiers/options_handlers.h @@ -58,6 +58,23 @@ predicate\n\ \n\ "; +static const std::string axiomInstModeHelp = "\ +Literal match modes currently supported by the --axiom-inst option:\n\ +\n\ +default \n\ ++ Treat axioms the same as usual quantifiers, i.e. use all available methods for\n\ + instantiating axioms.\n\ +\n\ +trust \n\ ++ Treat axioms only using heuristic instantiation. Return unknown if in the case\n\ + that no instantiations are produced.\n\ +\n\ +priority \n\ ++ Treat axioms only using heuristic instantiation. Resort to using all methods\n\ + in the case that no instantiations are produced.\n\ +\n\ +"; + inline InstWhenMode stringToInstWhenMode(std::string option, std::string optarg, SmtEngine* smt) throw(OptionException) { if(optarg == "pre-full") { return INST_WHEN_PRE_FULL; @@ -104,6 +121,22 @@ inline void checkLiteralMatchMode(std::string option, LiteralMatchMode mode, Smt } } +inline AxiomInstMode stringToAxiomInstMode(std::string option, std::string optarg, SmtEngine* smt) throw(OptionException) { + if(optarg == "default") { + return AXIOM_INST_MODE_DEFAULT; + } else if(optarg == "trust") { + return AXIOM_INST_MODE_TRUST; + } else if(optarg == "priority") { + return AXIOM_INST_MODE_PRIORITY; + } else if(optarg == "help") { + puts(axiomInstModeHelp.c_str()); + exit(1); + } else { + throw OptionException(std::string("unknown option for --axiom-inst: `") + + optarg + "'. Try --axiom-inst help."); + } +} + }/* CVC4::theory::quantifiers namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/quantifiers/quantifiers_attributes.cpp b/src/theory/quantifiers/quantifiers_attributes.cpp new file mode 100644 index 000000000..22ee66362 --- /dev/null +++ b/src/theory/quantifiers/quantifiers_attributes.cpp @@ -0,0 +1,43 @@ +/********************* */
+/*! \file quantifiers_attributes.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-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 Implementation of QuantifiersAttributes class
+ **/
+
+#include "theory/quantifiers/quantifiers_attributes.h"
+#include "theory/quantifiers/options.h"
+
+using namespace std;
+using namespace CVC4;
+using namespace CVC4::kind;
+using namespace CVC4::context;
+using namespace CVC4::theory;
+using namespace CVC4::theory::quantifiers;
+
+void QuantifiersAttributes::setUserAttribute( std::string& attr, Node n ){
+ if( n.getKind()==FORALL ){
+ if( attr=="axiom" ){
+ Trace("quant-attr") << "Set axiom " << n << std::endl;
+ AxiomAttribute aa;
+ n.setAttribute( aa, true );
+ }else if( attr=="conjecture" ){
+ Trace("quant-attr") << "Set conjecture " << n << std::endl;
+ ConjectureAttribute ca;
+ n.setAttribute( ca, true );
+ }
+ }else{
+ for( size_t i=0; i<n.getNumChildren(); i++ ){
+ setUserAttribute( attr, n[i] );
+ }
+ }
+}
diff --git a/src/theory/quantifiers/quantifiers_attributes.h b/src/theory/quantifiers/quantifiers_attributes.h new file mode 100644 index 000000000..49c374f3e --- /dev/null +++ b/src/theory/quantifiers/quantifiers_attributes.h @@ -0,0 +1,53 @@ +/********************* */
+/*! \file quantifiers_attributes.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 Attributes for the theory quantifiers
+ **
+ ** Attributes for the theory 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 true for quantifiers that are axioms */
+struct AxiomAttributeId {};
+typedef expr::Attribute< AxiomAttributeId, bool > AxiomAttribute;
+
+/** Attribute true for quantifiers that are conjecture */
+struct ConjectureAttributeId {};
+typedef expr::Attribute< ConjectureAttributeId, bool > ConjectureAttribute;
+
+struct QuantifiersAttributes
+{
+ /** set user attribute
+ * This function will apply a custom set of attributes to all top-level universal
+ * quantifiers contained in n
+ */
+ static void setUserAttribute( std::string& attr, Node n );
+};
+
+
+}
+}
+}
+
+#endif
diff --git a/src/theory/quantifiers/relevant_domain.cpp b/src/theory/quantifiers/relevant_domain.cpp index 1563a3d1d..495e0f7a4 100644 --- a/src/theory/quantifiers/relevant_domain.cpp +++ b/src/theory/quantifiers/relevant_domain.cpp @@ -26,16 +26,18 @@ using namespace CVC4::context; using namespace CVC4::theory; using namespace CVC4::theory::quantifiers; -RelevantDomain::RelevantDomain( FirstOrderModel* m ) : d_model( m ){ +RelevantDomain::RelevantDomain( QuantifiersEngine* qe, FirstOrderModel* m ) : d_qe( qe ), d_model( m ){ } void RelevantDomain::compute(){ + Trace("rel-dom") << "compute relevant domain" << std::endl; d_quant_inst_domain.clear(); for( int i=0; i<(int)d_model->getNumAssertedQuantifiers(); i++ ){ Node f = d_model->getAssertedQuantifier( i ); d_quant_inst_domain[f].resize( f[0].getNumChildren() ); } + Trace("rel-dom") << "account for ground terms" << std::endl; //add ground terms to domain (rule 1 of complete instantiation essentially uf fragment) for( std::map< Node, uf::UfModelTree >::iterator it = d_model->d_uf_model_tree.begin(); it != d_model->d_uf_model_tree.end(); ++it ){ @@ -47,6 +49,7 @@ void RelevantDomain::compute(){ if( d_model->d_rep_set.hasType( n[j].getType() ) ){ Node ra = d_model->getRepresentative( n[j] ); int raIndex = d_model->d_rep_set.getIndexFor( ra ); + if( raIndex==-1 ) Trace("rel-dom-warn") << "WARNING: Ground domain: rep set does not contain : " << ra << std::endl; Assert( raIndex!=-1 ); if( std::find( d_active_domain[op][j].begin(), d_active_domain[op][j].end(), raIndex )==d_active_domain[op][j].end() ){ d_active_domain[op][j].push_back( raIndex ); @@ -56,12 +59,14 @@ void RelevantDomain::compute(){ //add to range Node r = d_model->getRepresentative( n ); int raIndex = d_model->d_rep_set.getIndexFor( r ); + if( raIndex==-1 ) Trace("rel-dom-warn") << "WARNING: Ground range: rep set does not contain : " << r << std::endl; Assert( raIndex!=-1 ); if( std::find( d_active_range[op].begin(), d_active_range[op].end(), raIndex )==d_active_range[op].end() ){ d_active_range[op].push_back( raIndex ); } } } + Trace("rel-dom") << "do quantifiers" << std::endl; //find fixed point for relevant domain computation bool success; do{ @@ -69,19 +74,20 @@ void RelevantDomain::compute(){ for( int i=0; i<(int)d_model->getNumAssertedQuantifiers(); i++ ){ Node f = d_model->getAssertedQuantifier( i ); //compute the domain of relevant instantiations (rule 3 of complete instantiation, essentially uf fragment) - if( computeRelevantInstantiationDomain( d_model->getTermDatabase()->getCounterexampleBody( f ), Node::null(), -1, d_quant_inst_domain[f] ) ){ + if( computeRelevantInstantiationDomain( d_qe->getTermDatabase()->getCounterexampleBody( f ), Node::null(), -1, f ) ){ success = false; } //extend the possible domain for functions (rule 2 of complete instantiation, essentially uf fragment) RepDomain range; - if( extendFunctionDomains( d_model->getTermDatabase()->getCounterexampleBody( f ), range ) ){ + if( extendFunctionDomains( d_qe->getTermDatabase()->getCounterexampleBody( f ), range ) ){ success = false; } } }while( !success ); + Trace("rel-dom") << "done compute relevant domain" << std::endl; } -bool RelevantDomain::computeRelevantInstantiationDomain( Node n, Node parent, int arg, std::vector< RepDomain >& rd ){ +bool RelevantDomain::computeRelevantInstantiationDomain( Node n, Node parent, int arg, Node f ){ bool domainChanged = false; if( n.getKind()==INST_CONSTANT ){ bool domainSet = false; @@ -93,8 +99,9 @@ bool RelevantDomain::computeRelevantInstantiationDomain( Node n, Node parent, in if( d_active_domain.find( op )!=d_active_domain.end() ){ for( size_t i=0; i<d_active_domain[op][arg].size(); i++ ){ int d = d_active_domain[op][arg][i]; - if( std::find( rd[vi].begin(), rd[vi].end(), d )==rd[vi].end() ){ - rd[vi].push_back( d ); + if( std::find( d_quant_inst_domain[f][vi].begin(), d_quant_inst_domain[f][vi].end(), d )== + d_quant_inst_domain[f][vi].end() ){ + d_quant_inst_domain[f][vi].push_back( d ); domainChanged = true; } } @@ -104,21 +111,21 @@ bool RelevantDomain::computeRelevantInstantiationDomain( Node n, Node parent, in if( !domainSet ){ //otherwise, we must consider the entire domain TypeNode tn = n.getType(); - if( d_model->d_rep_set.hasType( tn ) ){ - if( rd[vi].size()!=d_model->d_rep_set.d_type_reps[tn].size() ){ - rd[vi].clear(); + if( d_quant_inst_domain_complete[f].find( vi )==d_quant_inst_domain_complete[f].end() ){ + if( d_model->d_rep_set.hasType( tn ) ){ + //it is the complete domain + d_quant_inst_domain[f][vi].clear(); for( size_t i=0; i<d_model->d_rep_set.d_type_reps[tn].size(); i++ ){ - rd[vi].push_back( i ); - domainChanged = true; + d_quant_inst_domain[f][vi].push_back( i ); } + domainChanged = true; } - }else{ - //infinite domain? + d_quant_inst_domain_complete[f][vi] = true; } } }else{ for( int i=0; i<(int)n.getNumChildren(); i++ ){ - if( computeRelevantInstantiationDomain( n[i], n, i, rd ) ){ + if( computeRelevantInstantiationDomain( n[i], n, i, f ) ){ domainChanged = true; } } @@ -166,7 +173,13 @@ bool RelevantDomain::extendFunctionDomains( Node n, RepDomain& range ){ } }else{ Node r = d_model->getRepresentative( n ); - range.push_back( d_model->d_rep_set.getIndexFor( r ) ); + int index = d_model->d_rep_set.getIndexFor( r ); + if( index==-1 ){ + //we consider all ground terms in bodies of quantifiers to be the first ground representative + range.push_back( 0 ); + }else{ + range.push_back( index ); + } } return domainChanged; } diff --git a/src/theory/quantifiers/relevant_domain.h b/src/theory/quantifiers/relevant_domain.h index 6ce47d114..62522812a 100644 --- a/src/theory/quantifiers/relevant_domain.h +++ b/src/theory/quantifiers/relevant_domain.h @@ -28,6 +28,7 @@ namespace quantifiers { class RelevantDomain { private: + QuantifiersEngine* d_qe; FirstOrderModel* d_model; //the domain of the arguments for each operator @@ -35,16 +36,17 @@ private: //the range for each operator std::map< Node, RepDomain > d_active_range; //for computing relevant instantiation domain, return true if changed - bool computeRelevantInstantiationDomain( Node n, Node parent, int arg, std::vector< RepDomain >& rd ); + bool computeRelevantInstantiationDomain( Node n, Node parent, int arg, Node f ); //for computing extended bool extendFunctionDomains( Node n, RepDomain& range ); public: - RelevantDomain( FirstOrderModel* m ); + RelevantDomain( QuantifiersEngine* qe, FirstOrderModel* m ); virtual ~RelevantDomain(){} //compute the relevant domain void compute(); //relevant instantiation domain for each quantifier std::map< Node, std::vector< RepDomain > > d_quant_inst_domain; + std::map< Node, std::map< int, bool > > d_quant_inst_domain_complete; };/* class RelevantDomain */ }/* CVC4::theory::quantifiers namespace */ diff --git a/src/theory/quantifiers/rep_set_iterator.cpp b/src/theory/quantifiers/rep_set_iterator.cpp deleted file mode 100644 index 7461f3477..000000000 --- a/src/theory/quantifiers/rep_set_iterator.cpp +++ /dev/null @@ -1,517 +0,0 @@ -/********************* */ -/*! \file rep_set_iterator.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 relevant domain class - **/ - -#include "theory/quantifiers/rep_set_iterator.h" -#include "theory/quantifiers/model_engine.h" -#include "theory/quantifiers/term_database.h" - -#define USE_INDEX_ORDERING - -using namespace std; -using namespace CVC4; -using namespace CVC4::kind; -using namespace CVC4::context; -using namespace CVC4::theory; -using namespace CVC4::theory::quantifiers; - -RepSetIterator::RepSetIterator( Node f, FirstOrderModel* model ) : d_f( f ), d_model( model ){ - //store instantiation constants - for( size_t i=0; i<f[0].getNumChildren(); i++ ){ - d_index.push_back( 0 ); - } - for( size_t i=0; i<f[0].getNumChildren(); i++ ){ - //store default index order - d_index_order.push_back( i ); - d_var_order[i] = i; - //store default domain - d_domain.push_back( RepDomain() ); - TypeNode tn = d_f[0][i].getType(); - if( tn.isSort() ){ - if( d_model->d_rep_set.hasType( tn ) ){ - for( int j=0; j<(int)d_model->d_rep_set.d_type_reps[d_f[0][i].getType()].size(); j++ ){ - d_domain[i].push_back( j ); - } - }else{ - Unimplemented("Cannot create instantiation iterator for unknown uninterpretted sort"); - } - }else if( tn==NodeManager::currentNM()->integerType() || tn==NodeManager::currentNM()->realType() ){ - Unimplemented("Cannot create instantiation iterator for arithmetic quantifier"); - }else if( tn.isDatatype() ){ - const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); - //if finite, then use type enumerator - if( dt.isFinite() ){ - //DO_THIS: use type enumerator - Unimplemented("Not yet implemented: instantiation iterator for finite datatype quantifier"); - }else{ - Unimplemented("Cannot create instantiation iterator for infinite datatype quantifier"); - } - }else{ - Unimplemented("Cannot create instantiation iterator for quantifier"); - } - } -} - -void RepSetIterator::setIndexOrder( std::vector< int >& indexOrder ){ - d_index_order.clear(); - d_index_order.insert( d_index_order.begin(), indexOrder.begin(), indexOrder.end() ); - //make the d_var_order mapping - for( int i=0; i<(int)d_index_order.size(); i++ ){ - d_var_order[d_index_order[i]] = i; - } -} - -void RepSetIterator::setDomain( std::vector< RepDomain >& domain ){ - d_domain.clear(); - d_domain.insert( d_domain.begin(), domain.begin(), domain.end() ); - //we are done if a domain is empty - for( int i=0; i<(int)d_domain.size(); i++ ){ - if( d_domain[i].empty() ){ - d_index.clear(); - } - } -} - -void RepSetIterator::increment2( int counter ){ - Assert( !isFinished() ); -#ifdef DISABLE_EVAL_SKIP_MULTIPLE - counter = (int)d_index.size()-1; -#endif - //increment d_index - while( counter>=0 && d_index[counter]==(int)(d_domain[counter].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 RepSetIterator::increment(){ - if( !isFinished() ){ - increment2( (int)d_index.size()-1 ); - } -} - -bool RepSetIterator::isFinished(){ - return d_index.empty(); -} - -void RepSetIterator::getMatch( QuantifiersEngine* qe, InstMatch& m ){ - for( int i=0; i<(int)d_index.size(); i++ ){ - m.set( qe->getTermDatabase()->getInstantiationConstant( d_f, d_index_order[i] ), getTerm( i )); - } -} - -Node RepSetIterator::getTerm( int i ){ - TypeNode tn = d_f[0][d_index_order[i]].getType(); - Assert( d_model->d_rep_set.d_type_reps.find( tn )!=d_model->d_rep_set.d_type_reps.end() ); - int index = d_index_order[i]; - return d_model->d_rep_set.d_type_reps[tn][d_domain[index][d_index[index]]]; -} - -void RepSetIterator::debugPrint( const char* c ){ - for( int i=0; i<(int)d_index.size(); i++ ){ - Debug( c ) << i << " : " << d_index[i] << " : " << getTerm( i ) << std::endl; - } -} - -void RepSetIterator::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; -} - -RepSetEvaluator::RepSetEvaluator( FirstOrderModel* m, RepSetIterator* ri ) : d_model( m ), d_riter( ri ){ - d_eval_formulas = 0; - d_eval_uf_terms = 0; - d_eval_lits = 0; - d_eval_lits_unknown = 0; -} - -//if evaluate( n ) = eVal, -// let n' = d_riter * n be the formula n instantiated with the current values in r_iter -// if eVal = 1, then n' is true, if eVal = -1, then n' is false, -// if eVal = 0, then n' cannot be proven to be equal to phaseReq -// if eVal is not 0, then -// each n{d_riter->d_index[0]/x_0...d_riter->d_index[depIndex]/x_depIndex, */x_(depIndex+1) ... */x_n } is equivalent in the current model -int RepSetEvaluator::evaluate( Node n, int& depIndex ){ - ++d_eval_formulas; - //Debug("fmf-eval-debug") << "Evaluate " << n << " " << phaseReq << std::endl; - //Notice() << "Eval " << n << std::endl; - if( n.getKind()==NOT ){ - int val = evaluate( 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 = d_riter->getNumTerms(); - 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( 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( n[0], depIndex1 ); - if( eVal!=0 ){ - int depIndex2; - int eVal2 = evaluate( 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, depIndex2; - int eVal = evaluate( n[0], depIndex1 ); - if( eVal==0 ){ - //evaluate children to see if they are the same value - int eval1 = evaluate( n[1], depIndex1 ); - if( eval1!=0 ){ - int eval2 = evaluate( n[1], depIndex2 ); - if( eval1==eval2 ){ - depIndex = depIndex1>depIndex2 ? depIndex1 : depIndex2; - return eval1; - } - } - }else{ - int eValT = evaluate( n[eVal==1 ? 1 : 2], depIndex2 ); - depIndex = depIndex1>depIndex2 ? depIndex1 : depIndex2; - return eValT; - } - return 0; - }else if( n.getKind()==FORALL ){ - return 0; - }else{ - ++d_eval_lits; - ////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-eval-debug") << "Evaluate literal " << n << std::endl; - int retVal = 0; - depIndex = d_riter->getNumTerms()-1; - Node val = evaluateTerm( n, depIndex ); - if( !val.isNull() ){ - if( d_model->areEqual( val, d_model->d_true ) ){ - retVal = 1; - }else if( d_model->areEqual( val, d_model->d_false ) ){ - retVal = -1; - }else{ - if( val.getKind()==EQUAL ){ - if( d_model->areEqual( val[0], val[1] ) ){ - retVal = 1; - }else if( d_model->areDisequal( val[0], val[1] ) ){ - retVal = -1; - } - } - } - } - if( retVal!=0 ){ - Debug("fmf-eval-debug") << "Evaluate literal: return " << retVal << ", depIndex = " << depIndex << std::endl; - }else{ - ++d_eval_lits_unknown; - Debug("fmf-eval-amb") << "Neither true nor false : " << n << std::endl; - //std::cout << "Neither true nor false : " << n << std::endl; - //std::cout << " Value : " << val << std::endl; - //for( int i=0; i<(int)n.getNumChildren(); i++ ){ - // std::cout << " " << i << " : " << n[i].getType() << std::endl; - //} - } - return retVal; - } -} - -Node RepSetEvaluator::evaluateTerm( Node n, int& depIndex ){ - //Message() << "Eval term " << n << std::endl; - if( !n.hasAttribute(InstConstantAttribute()) ){ - //if evaluating a ground term, just consult the standard getValue functionality - depIndex = -1; - return d_model->getValue( n ); - }else{ - Node val; - depIndex = d_riter->getNumTerms()-1; - //check the type of n - if( n.getKind()==INST_CONSTANT ){ - int v = n.getAttribute(InstVarNumAttribute()); - depIndex = d_riter->d_var_order[ v ]; - val = d_riter->getTerm( v ); - }else if( n.getKind()==ITE ){ - int depIndex1, depIndex2; - int eval = evaluate( n[0], depIndex1 ); - if( eval==0 ){ - //evaluate children to see if they are the same - Node val1 = evaluateTerm( n[ 1 ], depIndex1 ); - Node val2 = evaluateTerm( n[ 2 ], depIndex2 ); - if( val1==val2 ){ - val = val1; - depIndex = depIndex1>depIndex2 ? depIndex1 : depIndex2; - }else{ - return Node::null(); - } - }else{ - val = evaluateTerm( n[ eval==1 ? 1 : 2 ], depIndex2 ); - depIndex = depIndex1>depIndex2 ? depIndex1 : depIndex2; - } - }else{ - std::vector< int > children_depIndex; - //for select, pre-process read over writes - if( n.getKind()==SELECT ){ -#if 1 - //std::cout << "Evaluate " << n << std::endl; - Node sel = evaluateTerm( n[1], depIndex ); - if( sel.isNull() ){ - depIndex = d_riter->getNumTerms()-1; - return Node::null(); - } - Node arr = d_model->getRepresentative( n[0] ); - //if( n[0]!=d_model->getRepresentative( n[0] ) ){ - // std::cout << n[0] << " is " << d_model->getRepresentative( n[0] ) << std::endl; - //} - int tempIndex; - int eval = 1; - while( arr.getKind()==STORE && eval!=0 ){ - eval = evaluate( sel.eqNode( arr[1] ), tempIndex ); - depIndex = tempIndex > depIndex ? tempIndex : depIndex; - if( eval==1 ){ - val = evaluateTerm( arr[2], tempIndex ); - depIndex = tempIndex > depIndex ? tempIndex : depIndex; - return val; - }else if( eval==-1 ){ - arr = arr[0]; - } - } - arr = evaluateTerm( arr, tempIndex ); - depIndex = tempIndex > depIndex ? tempIndex : depIndex; - val = NodeManager::currentNM()->mkNode( SELECT, arr, sel ); -#else - val = evaluateTermDefault( n, depIndex, children_depIndex ); -#endif - }else{ - //default term evaluate : evaluate all children, recreate the value - val = evaluateTermDefault( n, depIndex, children_depIndex ); - } - if( !val.isNull() ){ - bool setVal = false; - //custom ways of evaluating terms - if( n.getKind()==APPLY_UF ){ - Node op = n.getOperator(); - //Debug("fmf-eval-debug") << "Evaluate term " << n << " (" << gn << ")" << std::endl; - //if it is a defined UF, then consult the interpretation - if( d_model->d_uf_model_tree.find( op )!=d_model->d_uf_model_tree.end() ){ - ++d_eval_uf_terms; - int argDepIndex = 0; - //make the term model specifically for n - makeEvalUfModel( n ); - //now, consult the model - if( d_eval_uf_use_default[n] ){ - val = d_model->d_uf_model_tree[op].getValue( d_model, val, argDepIndex ); - }else{ - val = d_eval_uf_model[ n ].getValue( d_model, val, argDepIndex ); - } - //Debug("fmf-eval-debug") << "Evaluate term " << n << " (" << gn << ")" << std::endl; - //d_eval_uf_model[ n ].debugPrint("fmf-eval-debug", d_qe ); - Assert( !val.isNull() ); - //recalculate the depIndex - depIndex = -1; - for( int i=0; i<argDepIndex; i++ ){ - int index = d_eval_uf_use_default[n] ? i : d_eval_term_index_order[n][i]; - Debug("fmf-eval-debug") << "Add variables from " << index << "..." << std::endl; - if( children_depIndex[index]>depIndex ){ - depIndex = children_depIndex[index]; - } - } - setVal = true; - } - }else if( n.getKind()==SELECT ){ - //we are free to interpret this term however we want - } - //if not set already, rewrite and consult model for interpretation - if( !setVal ){ - val = Rewriter::rewrite( val ); - if( !val.isConst() ){ - //FIXME: we cannot do this until we trust all theories collectModelInfo! - //val = d_model->getInterpretedValue( val ); - //val = d_model->getRepresentative( val ); - } - } - Debug("fmf-eval-debug") << "Evaluate term " << n << " = "; - d_model->printRepresentativeDebug( "fmf-eval-debug", val ); - Debug("fmf-eval-debug") << ", depIndex = " << depIndex << std::endl; - } - } - return val; - } -} - -Node RepSetEvaluator::evaluateTermDefault( Node n, int& depIndex, std::vector< int >& childDepIndex ){ - depIndex = -1; - if( n.getNumChildren()==0 ){ - return n; - }else{ - //first we must evaluate the arguments - std::vector< Node > children; - if( n.getMetaKind()==kind::metakind::PARAMETERIZED ){ - children.push_back( n.getOperator() ); - } - //for each argument, calculate its value, and the variables its value depends upon - for( int i=0; i<(int)n.getNumChildren(); i++ ){ - childDepIndex.push_back( -1 ); - Node nn = evaluateTerm( n[i], childDepIndex[i] ); - if( nn.isNull() ){ - depIndex = d_riter->getNumTerms()-1; - return nn; - }else{ - children.push_back( nn ); - if( childDepIndex[i]>depIndex ){ - depIndex = childDepIndex[i]; - } - } - } - //recreate the value - Node val = NodeManager::currentNM()->mkNode( n.getKind(), children ); - return val; - } -} - -void RepSetEvaluator::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(); -} - -void RepSetEvaluator::makeEvalUfModel( Node n ){ - if( d_eval_uf_model.find( n )==d_eval_uf_model.end() ){ - makeEvalUfIndexOrder( n ); - if( !d_eval_uf_use_default[n] ){ - Node op = n.getOperator(); - d_eval_uf_model[n] = uf::UfModelTree( op, d_eval_term_index_order[n] ); - d_model->d_uf_model_gen[op].makeModel( d_model, d_eval_uf_model[n] ); - //Debug("fmf-index-order") << "Make model for " << n << " : " << std::endl; - //d_eval_uf_model[n].debugPrint( "fmf-index-order", d_qe, 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 RepSetEvaluator::makeEvalUfIndexOrder( Node n ){ - if( d_eval_term_index_order.find( n )==d_eval_term_index_order.end() ){ -#ifdef USE_INDEX_ORDERING - //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_uf_use_default[n] = useDefault; - Debug("fmf-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-index-order") << d_eval_term_index_order[n][i] << " "; - } - Debug("fmf-index-order") << std::endl; -#else - d_eval_uf_use_default[n] = true; -#endif - } -} - - diff --git a/src/theory/quantifiers/rep_set_iterator.h b/src/theory/quantifiers/rep_set_iterator.h deleted file mode 100644 index 85a2f3fd2..000000000 --- a/src/theory/quantifiers/rep_set_iterator.h +++ /dev/null @@ -1,119 +0,0 @@ -/********************* */ -/*! \file rep_set_iterator.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 rep_set_iterator class - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__THEORY__QUANTIFIERS__REP_SET_ITERATOR_H -#define __CVC4__THEORY__QUANTIFIERS__REP_SET_ITERATOR_H - -#include "theory/quantifiers_engine.h" -#include "theory/quantifiers/first_order_model.h" - -namespace CVC4 { -namespace theory { -namespace quantifiers { - -/** this class iterates over a RepSet */ -class RepSetIterator { -public: - RepSetIterator( Node f, FirstOrderModel* model ); - ~RepSetIterator(){} - //pointer to quantifier - Node d_f; - //pointer to model - FirstOrderModel* d_model; - //index we are considering - std::vector< int > d_index; - //domain we are considering - std::vector< RepDomain > d_domain; - //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: - /** set index order */ - void setIndexOrder( std::vector< int >& indexOrder ); - /** set domain */ - void setDomain( std::vector< RepDomain >& domain ); - /** increment the iterator */ - void increment2( int counter ); - void increment(); - /** 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(); } - /** debug print */ - void debugPrint( const char* c ); - void debugPrintSmall( const char* c ); -}; - -class RepSetEvaluator -{ -private: - FirstOrderModel* d_model; - RepSetIterator* d_riter; -private: //for Theory UF: - //map from terms to the models used to calculate their value - std::map< Node, bool > d_eval_uf_use_default; - std::map< Node, uf::UfModelTree > d_eval_uf_model; - void makeEvalUfModel( 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 makeEvalUfIndexOrder( Node n ); -private: - //default evaluate term function - Node evaluateTermDefault( Node n, int& depIndex, std::vector< int >& childDepIndex ); - //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; -public: - RepSetEvaluator( FirstOrderModel* m, RepSetIterator* ri ); - virtual ~RepSetEvaluator(){} - /** evaluate functions */ - int evaluate( Node n, int& depIndex ); - Node evaluateTerm( Node n, int& depIndex ); -public: - //statistics - int d_eval_formulas; - int d_eval_uf_terms; - int d_eval_lits; - int d_eval_lits_unknown; -};/* class RepSetEvaluator */ - -}/* CVC4::theory::quantifiers namespace */ -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ - -#endif diff --git a/src/theory/quantifiers/term_database.cpp b/src/theory/quantifiers/term_database.cpp index a73d42a31..bd6b03a78 100644 --- a/src/theory/quantifiers/term_database.cpp +++ b/src/theory/quantifiers/term_database.cpp @@ -131,7 +131,7 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant ){ //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() ){ + if( it->second[0].getType().isBoolean() ){ d_pred_map_trie[ 0 ][ it->first ].d_data.clear(); d_pred_map_trie[ 1 ][ it->first ].d_data.clear(); }else{ @@ -199,6 +199,7 @@ Node TermDb::getModelBasisTerm( TypeNode tn, int i ){ ss << Expr::setlanguage(options::outputLanguage()); ss << "e_" << tn; mbt = NodeManager::currentNM()->mkSkolem( ss.str(), tn ); + Trace("mkVar") << "ModelBasis:: Make variable " << mbt << " : " << tn << std::endl; }else{ mbt = d_type_map[ tn ][ 0 ]; } @@ -337,12 +338,13 @@ Node TermDb::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() ){ + if( tn.isInteger() || tn.isReal() ){ Rational z(0); d_free_vars[tn] = NodeManager::currentNM()->mkConst( z ); }else{ if( d_type_map[ tn ].empty() ){ d_free_vars[tn] = NodeManager::currentNM()->mkSkolem( tn ); + Trace("mkVar") << "FreeVar:: Make variable " << d_free_vars[tn] << " : " << tn << std::endl; }else{ d_free_vars[tn] = d_type_map[ tn ][ 0 ]; } diff --git a/src/theory/quantifiers/theory_quantifiers.cpp b/src/theory/quantifiers/theory_quantifiers.cpp index 3d41d28b7..c45626dd9 100644 --- a/src/theory/quantifiers/theory_quantifiers.cpp +++ b/src/theory/quantifiers/theory_quantifiers.cpp @@ -24,11 +24,10 @@ #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" #include "theory/quantifiers/options.h" #include "theory/quantifiers/term_database.h" +#include "theory/quantifiers/quantifiers_attributes.h" using namespace std; using namespace CVC4; @@ -42,6 +41,8 @@ TheoryQuantifiers::TheoryQuantifiers(Context* c, context::UserContext* u, Output d_numRestarts(0){ d_numInstantiations = 0; d_baseDecLevel = -1; + out.handleUserAttribute( "axiom", this ); + out.handleUserAttribute( "conjecture", this ); } @@ -89,7 +90,7 @@ Node TheoryQuantifiers::getValue(TNode n) { } } -void TheoryQuantifiers::collectModelInfo( TheoryModel* m ){ +void TheoryQuantifiers::collectModelInfo( TheoryModel* m, bool fullModel ){ } @@ -126,9 +127,12 @@ void TheoryQuantifiers::check(Effort e) { } void TheoryQuantifiers::propagate(Effort level){ - CodeTimer codeTimer(d_theoryTime); + //CodeTimer codeTimer(d_theoryTime); + //getQuantifiersEngine()->propagate( level ); +} - getQuantifiersEngine()->propagate( level ); +Node TheoryQuantifiers::getNextDecisionRequest(){ + return getQuantifiersEngine()->getNextDecisionRequest(); } void TheoryQuantifiers::assertUniversal( Node n ){ @@ -186,6 +190,6 @@ bool TheoryQuantifiers::restart(){ } } -void TheoryQuantifiers::performCheck(Effort e){ - getQuantifiersEngine()->check( e ); +void TheoryQuantifiers::setUserAttribute( std::string& attr, Node n ){ + QuantifiersAttributes::setUserAttribute( attr, n ); } diff --git a/src/theory/quantifiers/theory_quantifiers.h b/src/theory/quantifiers/theory_quantifiers.h index 1e42abd22..bc712955e 100644 --- a/src/theory/quantifiers/theory_quantifiers.h +++ b/src/theory/quantifiers/theory_quantifiers.h @@ -61,17 +61,17 @@ public: void presolve(); void check(Effort e); void propagate(Effort level); + Node getNextDecisionRequest(); Node getValue(TNode n); - void collectModelInfo( TheoryModel* m ); + void collectModelInfo( TheoryModel* m, bool fullModel ); void shutdown() { } std::string identify() const { return std::string("TheoryQuantifiers"); } bool flipDecision(); + void setUserAttribute( std::string& attr, Node n ); private: void assertUniversal( Node n ); void assertExistential( Node n ); bool restart(); -public: - void performCheck(Effort e); };/* class TheoryQuantifiers */ }/* CVC4::theory::quantifiers namespace */ diff --git a/src/theory/quantifiers_engine.cpp b/src/theory/quantifiers_engine.cpp index 3dcd20d78..ca5cc568e 100644 --- a/src/theory/quantifiers_engine.cpp +++ b/src/theory/quantifiers_engine.cpp @@ -70,7 +70,7 @@ d_active( c ){ d_hasAddedLemma = false; //the model object - d_model = new quantifiers::FirstOrderModel( this, c, "FirstOrderModel" ); + d_model = new quantifiers::FirstOrderModel( c, "FirstOrderModel" ); //add quantifiers modules if( !options::finiteModelFind() || options::fmfInstEngine() ){ @@ -81,7 +81,7 @@ d_active( c ){ d_inst_engine = NULL; } if( options::finiteModelFind() ){ - d_model_engine = new quantifiers::ModelEngine( this ); + d_model_engine = new quantifiers::ModelEngine( c, this ); d_modules.push_back( d_model_engine ); }else{ d_model_engine = NULL; @@ -121,18 +121,26 @@ void QuantifiersEngine::check( Theory::Effort e ){ d_hasAddedLemma = false; d_model_set = false; + d_resetInstRound = false; if( e==Theory::EFFORT_LAST_CALL ){ ++(d_statistics.d_instantiation_rounds_lc); }else if( e==Theory::EFFORT_FULL ){ ++(d_statistics.d_instantiation_rounds); } + //if effort is last call, try to minimize model first + if( e==Theory::EFFORT_LAST_CALL && options::finiteModelFind() ){ + //first, check if we can minimize the model further + if( !((uf::TheoryUF*)getTheoryEngine()->theoryOf( THEORY_UF ))->getStrongSolver()->minimize() ){ + return; + } + } for( int i=0; i<(int)d_modules.size(); i++ ){ d_modules[i]->check( e ); } //build the model if not done so already // this happens if no quantifiers are currently asserted and no model-building module is enabled if( options::produceModels() && e==Theory::EFFORT_LAST_CALL && !d_hasAddedLemma && !d_model_set ){ - d_te->getModelBuilder()->buildModel( d_model ); + d_te->getModelBuilder()->buildModel( d_model, true ); } } @@ -227,7 +235,7 @@ void QuantifiersEngine::registerPattern( std::vector<Node> & pattern) { void QuantifiersEngine::assertNode( Node f ){ Assert( f.getKind()==FORALL ); for( int j=0; j<(int)d_quant_rewritten[f].size(); j++ ){ - d_model->d_forall_asserts.push_back( d_quant_rewritten[f][j] ); + d_model->assertQuantifier( d_quant_rewritten[f][j] ); for( int i=0; i<(int)d_modules.size(); i++ ){ d_modules[i]->assertNode( d_quant_rewritten[f][j] ); } @@ -242,13 +250,26 @@ void QuantifiersEngine::propagate( Theory::Effort level ){ } } +Node QuantifiersEngine::getNextDecisionRequest(){ + for( int i=0; i<(int)d_modules.size(); i++ ){ + Node n = d_modules[i]->getNextDecisionRequest(); + if( !n.isNull() ){ + return n; + } + } + return Node::null(); +} + void QuantifiersEngine::resetInstantiationRound( Theory::Effort level ){ + //if( !d_resetInstRound ){ + d_resetInstRound = true; for( theory::TheoryId i=theory::THEORY_FIRST; i<theory::THEORY_LAST; ++i ){ if( getInstantiator( i ) ){ getInstantiator( i )->resetInstantiationRound( level ); } } getTermDatabase()->reset( level ); + //} } void QuantifiersEngine::addTermToDatabase( Node n, bool withinQuant ){ @@ -286,30 +307,24 @@ bool QuantifiersEngine::addLemma( Node lem ){ } } -bool QuantifiersEngine::addInstantiation( Node f, std::vector< Node >& terms ) +bool QuantifiersEngine::addInstantiation( Node f, std::vector< Node >& vars, 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_term_db->d_vars[f].size()==terms.size() && d_term_db->d_vars[f].size()==f[0].getNumChildren() ); - Node body = f[ 1 ].substitute( d_term_db->d_vars[f].begin(), d_term_db->d_vars[f].end(), - terms.begin(), terms.end() ); - NodeBuilder<> nb(kind::OR); - nb << d_rewritten_quant[f].notNode() << body; - Node lem = nb; + Assert( vars.size()==terms.size() ); + Node body = f[ 1 ].substitute( vars.begin(), vars.end(), terms.begin(), terms.end() ); + Node lem; + if( d_term_db->d_vars[f].size()==vars.size() ){ + NodeBuilder<> nb(kind::OR); + nb << d_rewritten_quant[f].notNode() << body; + lem = nb; + }else{ + //doing a partial instantiation, must add quantifier for all uninstantiated variables + Notice() << "Partial instantiation not implemented yet." << std::endl; + Unimplemented(); + } 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; + Trace("inst") << "*** Instantiate " << f << " with " << std::endl; uint64_t maxInstLevel = 0; for( int i=0; i<(int)terms.size(); i++ ){ if( terms[i].hasAttribute(InstConstantAttribute()) ){ @@ -319,10 +334,9 @@ bool QuantifiersEngine::addInstantiation( Node f, std::vector< Node >& terms ) } Unreachable("Bad instantiation"); }else{ - Debug("inst") << " " << terms[i]; - //Notice() << " " << terms[i] << std::endl; + Trace("inst") << " " << terms[i]; //Debug("inst-engine") << " " << terms[i].getAttribute(InstLevelAttribute()); - Debug("inst") << std::endl; + Trace("inst") << std::endl; if( terms[i].hasAttribute(InstLevelAttribute()) ){ if( terms[i].getAttribute(InstLevelAttribute())>maxInstLevel ){ maxInstLevel = terms[i].getAttribute(InstLevelAttribute()); @@ -332,6 +346,7 @@ bool QuantifiersEngine::addInstantiation( Node f, std::vector< Node >& terms ) } } } + Trace("inst-debug") << "*** Lemma is " << lem << std::endl; d_term_db->setInstantiationLevelAttr( body, maxInstLevel+1 ); ++(d_statistics.d_instantiations); d_statistics.d_total_inst_var += (int)terms.size(); @@ -343,47 +358,46 @@ bool QuantifiersEngine::addInstantiation( Node f, std::vector< Node >& terms ) } } -bool QuantifiersEngine::addInstantiation( Node f, InstMatch& m ){ - m.makeComplete( f, this ); +bool QuantifiersEngine::addInstantiation( Node f, InstMatch& m, bool makeComplete ){ + //make sure there are values for each variable we are instantiating + if( makeComplete ){ + m.makeComplete( f, this ); + } + //make it representative, this is helpful for recognizing duplication m.makeRepresentative( this ); - Debug("quant-duplicate") << "After make rep: " << m << std::endl; + Trace("inst-add") << "Add instantiation: " << m << std::endl; + //check for duplication modulo equality if( !d_inst_match_trie[f].addInstMatch( this, f, m, true ) ){ - Debug("quant-duplicate") << " -> Already exists." << std::endl; + Trace("inst-add") << " -> Already exists." << std::endl; ++(d_statistics.d_inst_duplicate); return false; } - Debug("quant-duplicate") << " -> Does not exist." << std::endl; + //compute the vector of terms for the instantiation std::vector< Node > match; m.computeTermVec( d_term_db->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 ); - // } - //} + //add the instantiation + bool addedInst = false; + if( match.size()==d_term_db->d_vars[f].size() ){ + addedInst = addInstantiation( f, d_term_db->d_vars[f], match ); + }else{ + //must compute the subset of variables we are instantiating + std::vector< Node > vars; + for( size_t i=0; i<d_term_db->d_vars[f].size(); i++ ){ + Node val = m.get( getTermDatabase()->getInstantiationConstant( f, i ) ); + if( !val.isNull() ){ + vars.push_back( d_term_db->d_vars[f][i] ); + } + } + addedInst = addInstantiation( f, vars, match ); + } + //report the result + if( addedInst ){ + Trace("inst-add") << " -> Success." << std::endl; return true; + }else{ + Trace("inst-add") << " -> Lemma already exists." << std::endl; + return false; } - return false; } bool QuantifiersEngine::addSplit( Node n, bool reqPhase, bool reqPhasePol ){ diff --git a/src/theory/quantifiers_engine.h b/src/theory/quantifiers_engine.h index 5afc34bf6..34d9d69a2 100644 --- a/src/theory/quantifiers_engine.h +++ b/src/theory/quantifiers_engine.h @@ -102,7 +102,8 @@ public: /* Called for new quantifiers */ virtual void registerQuantifier( Node n ) = 0; virtual void assertNode( Node n ) = 0; - virtual void propagate( Theory::Effort level ) = 0; + virtual void propagate( Theory::Effort level ){} + virtual Node getNextDecisionRequest() { return TNode::null(); } virtual Node explain(TNode n) = 0; };/* class QuantifiersModule */ @@ -153,6 +154,8 @@ public: quantifiers::FirstOrderModel* d_model; /** has the model been set? */ bool d_model_set; + /** has resetInstantiationRound() been called on this check(...) */ + bool d_resetInstRound; /** 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 */ @@ -214,6 +217,8 @@ public: void assertNode( Node f ); /** propagate */ void propagate( Theory::Effort level ); + /** get next decision request */ + Node getNextDecisionRequest(); /** reset instantiation round */ void resetInstantiationRound( Theory::Effort level ); @@ -223,9 +228,9 @@ public: /** add lemma lem */ bool addLemma( Node lem ); /** instantiate f with arguments terms */ - bool addInstantiation( Node f, std::vector< Node >& terms ); + bool addInstantiation( Node f, std::vector< Node >& vars, std::vector< Node >& terms ); /** do instantiation specified by m */ - bool addInstantiation( Node f, InstMatch& m ); + bool addInstantiation( Node f, InstMatch& m, bool makeComplete = true ); /** split on node n */ bool addSplit( Node n, bool reqPhase = false, bool reqPhasePol = true ); /** add split equality */ diff --git a/src/theory/rep_set.cpp b/src/theory/rep_set.cpp new file mode 100644 index 000000000..aaca53464 --- /dev/null +++ b/src/theory/rep_set.cpp @@ -0,0 +1,216 @@ +/********************* */
+/*! \file rep_set.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 representative set
+ **/
+
+#include "theory/rep_set.h"
+#include "theory/type_enumerator.h"
+
+using namespace std;
+using namespace CVC4;
+using namespace CVC4::kind;
+using namespace CVC4::context;
+using namespace CVC4::theory;
+
+void RepSet::clear(){
+ d_type_reps.clear();
+ d_type_complete.clear();
+ d_tmap.clear();
+}
+
+void RepSet::add( Node n ){
+ TypeNode t = n.getType();
+ d_tmap[ n ] = (int)d_type_reps[t].size();
+ d_type_reps[t].push_back( n );
+}
+
+void RepSet::complete( TypeNode t ){
+ if( d_type_complete.find( t )==d_type_complete.end() ){
+ d_type_complete[t] = true;
+ TypeEnumerator te(t);
+ while( !te.isFinished() ){
+ Node n = *te;
+ if( std::find( d_type_reps[t].begin(), d_type_reps[t].end(), n )==d_type_reps[t].end() ){
+ add( n );
+ }
+ ++te;
+ }
+ for( size_t i=0; i<d_type_reps[t].size(); i++ ){
+ Trace("reps-complete") << d_type_reps[t][i] << " ";
+ }
+ Trace("reps-complete") << std::endl;
+ }
+}
+
+void RepSet::toStream(std::ostream& out){
+#if 0
+ for( std::map< TypeNode, std::vector< Node > >::iterator it = d_type_reps.begin(); it != d_type_reps.end(); ++it ){
+ out << it->first << " : " << std::endl;
+ for( int i=0; i<(int)it->second.size(); i++ ){
+ out << " " << i << ": " << it->second[i] << std::endl;
+ }
+ }
+#else
+ for( std::map< TypeNode, std::vector< Node > >::iterator it = d_type_reps.begin(); it != d_type_reps.end(); ++it ){
+ if( !it->first.isFunction() && !it->first.isPredicate() ){
+ out << "(" << it->first << " " << it->second.size();
+ out << " (";
+ for( int i=0; i<(int)it->second.size(); i++ ){
+ if( i>0 ){ out << " "; }
+ out << it->second[i];
+ }
+ out << ")";
+ out << ")" << std::endl;
+ }
+ }
+#endif
+}
+
+
+RepSetIterator::RepSetIterator( RepSet* rs ) : d_rep_set( rs ){
+ d_incomplete = false;
+
+}
+
+void RepSetIterator::setQuantifier( Node f ){
+ Assert( d_types.empty() );
+ //store indicies
+ for( size_t i=0; i<f[0].getNumChildren(); i++ ){
+ d_types.push_back( f[0][i].getType() );
+ }
+ initialize();
+}
+
+void RepSetIterator::setFunctionDomain( Node op ){
+ Assert( d_types.empty() );
+ TypeNode tn = op.getType();
+ for( size_t i=0; i<tn.getNumChildren()-1; i++ ){
+ d_types.push_back( tn[i] );
+ }
+ initialize();
+}
+
+void RepSetIterator::initialize(){
+ for( size_t i=0; i<d_types.size(); i++ ){
+ d_index.push_back( 0 );
+ //store default index order
+ d_index_order.push_back( i );
+ d_var_order[i] = i;
+ //store default domain
+ d_domain.push_back( RepDomain() );
+ TypeNode tn = d_types[i];
+ if( tn.isSort() ){
+ if( !d_rep_set->hasType( tn ) ){
+ Node var = NodeManager::currentNM()->mkSkolem( tn );
+ Trace("mkVar") << "RepSetIterator:: Make variable " << var << " : " << tn << std::endl;
+ d_rep_set->add( var );
+ }
+ }else if( tn.isInteger() || tn.isReal() ){
+ Trace("fmf-incomplete") << "Incomplete because of infinite type " << tn << std::endl;
+ d_incomplete = true;
+ }else if( tn.isDatatype() ){
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ //if finite, then complete all values of the domain
+ if( dt.isFinite() ){
+ d_rep_set->complete( tn );
+ //d_incomplete = true;
+ }else{
+ Trace("fmf-incomplete") << "Incomplete because of infinite datatype " << tn << std::endl;
+ d_incomplete = true;
+ }
+ }else{
+ Trace("fmf-incomplete") << "Incomplete because of type " << tn << std::endl;
+ d_incomplete = true;
+ }
+ if( d_rep_set->hasType( tn ) ){
+ for( size_t j=0; j<d_rep_set->d_type_reps[tn].size(); j++ ){
+ d_domain[i].push_back( j );
+ }
+ }else{
+ Trace("fmf-incomplete") << "Incomplete, unknown type " << tn << std::endl;
+ d_incomplete = true;
+ Unimplemented("Cannot create representative set iterator for unknown type quantifier");
+ }
+ }
+}
+
+void RepSetIterator::setIndexOrder( std::vector< int >& indexOrder ){
+ d_index_order.clear();
+ d_index_order.insert( d_index_order.begin(), indexOrder.begin(), indexOrder.end() );
+ //make the d_var_order mapping
+ for( int i=0; i<(int)d_index_order.size(); i++ ){
+ d_var_order[d_index_order[i]] = i;
+ }
+}
+
+void RepSetIterator::setDomain( std::vector< RepDomain >& domain ){
+ d_domain.clear();
+ d_domain.insert( d_domain.begin(), domain.begin(), domain.end() );
+ //we are done if a domain is empty
+ for( int i=0; i<(int)d_domain.size(); i++ ){
+ if( d_domain[i].empty() ){
+ d_index.clear();
+ }
+ }
+}
+
+void RepSetIterator::increment2( int counter ){
+ Assert( !isFinished() );
+#ifdef DISABLE_EVAL_SKIP_MULTIPLE
+ counter = (int)d_index.size()-1;
+#endif
+ //increment d_index
+ while( counter>=0 && d_index[counter]==(int)(d_domain[counter].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_index[counter]++;
+ }
+}
+
+void RepSetIterator::increment(){
+ if( !isFinished() ){
+ increment2( (int)d_index.size()-1 );
+ }
+}
+
+bool RepSetIterator::isFinished(){
+ return d_index.empty();
+}
+
+Node RepSetIterator::getTerm( int i ){
+ TypeNode tn = d_types[d_index_order[i]];
+ Assert( d_rep_set->d_type_reps.find( tn )!=d_rep_set->d_type_reps.end() );
+ int index = d_index_order[i];
+ return d_rep_set->d_type_reps[tn][d_domain[index][d_index[index]]];
+}
+
+void RepSetIterator::debugPrint( const char* c ){
+ for( int i=0; i<(int)d_index.size(); i++ ){
+ Debug( c ) << i << " : " << d_index[i] << " : " << getTerm( i ) << std::endl;
+ }
+}
+
+void RepSetIterator::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;
+}
diff --git a/src/theory/rep_set.h b/src/theory/rep_set.h new file mode 100644 index 000000000..3427502b1 --- /dev/null +++ b/src/theory/rep_set.h @@ -0,0 +1,112 @@ +/********************* */
+/*! \file rep_set.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 Representative set class and utilities
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__REP_SET_H
+#define __CVC4__REP_SET_H
+
+#include "expr/node.h"
+#include <map>
+
+namespace CVC4 {
+namespace theory {
+
+/** this class stores a representative set */
+class RepSet {
+public:
+ RepSet(){}
+ ~RepSet(){}
+ std::map< TypeNode, std::vector< Node > > d_type_reps;
+ std::map< TypeNode, bool > d_type_complete;
+ std::map< Node, int > d_tmap;
+ /** clear the set */
+ void clear();
+ /** has type */
+ bool hasType( TypeNode tn ) { return d_type_reps.find( tn )!=d_type_reps.end(); }
+ /** add representative for type */
+ void add( Node n );
+ /** 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; }
+ /** complete all values */
+ void complete( TypeNode t );
+ /** debug print */
+ void toStream(std::ostream& out);
+};
+
+//representative domain
+typedef std::vector< int > RepDomain;
+
+/** this class iterates over a RepSet */
+class RepSetIterator {
+private:
+ //initialize function
+ void initialize();
+public:
+ RepSetIterator( RepSet* rs );
+ ~RepSetIterator(){}
+ //set that this iterator will be iterating over instantiations for a quantifier
+ void setQuantifier( Node f );
+ //set that this iterator will be iterating over the domain of a function
+ void setFunctionDomain( Node op );
+public:
+ //pointer to model
+ RepSet* d_rep_set;
+ //index we are considering
+ std::vector< int > d_index;
+ //types we are considering
+ std::vector< TypeNode > d_types;
+ //domain we are considering
+ std::vector< RepDomain > d_domain;
+ //are we only considering a strict subset of the domain of the quantifier?
+ bool d_incomplete;
+ //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;
+public:
+ /** set index order */
+ void setIndexOrder( std::vector< int >& indexOrder );
+ /** set domain */
+ void setDomain( std::vector< RepDomain >& domain );
+ /** increment the iterator at index=counter */
+ void increment2( int counter );
+ /** increment the iterator */
+ void increment();
+ /** is the iterator finished? */
+ bool isFinished();
+ /** get the i_th term we are considering */
+ Node getTerm( int i );
+ /** get the number of terms we are considering */
+ int getNumTerms() { return (int)d_index_order.size(); }
+ /** debug print */
+ void debugPrint( const char* c );
+ void debugPrintSmall( const char* c );
+};
+
+}
+}
+
+#endif
\ No newline at end of file diff --git a/src/theory/rewriterules/theory_rewriterules.cpp b/src/theory/rewriterules/theory_rewriterules.cpp index b08b770d2..5ffd4ac4a 100644 --- a/src/theory/rewriterules/theory_rewriterules.cpp +++ b/src/theory/rewriterules/theory_rewriterules.cpp @@ -587,7 +587,7 @@ Node TheoryRewriteRules::explain(TNode n){ return normalizeConjunction(explanation); } -void TheoryRewriteRules::collectModelInfo( TheoryModel* m ){ +void TheoryRewriteRules::collectModelInfo( TheoryModel* m, bool fullModel ){ } diff --git a/src/theory/rewriterules/theory_rewriterules.h b/src/theory/rewriterules/theory_rewriterules.h index bb5537474..5937c541f 100644 --- a/src/theory/rewriterules/theory_rewriterules.h +++ b/src/theory/rewriterules/theory_rewriterules.h @@ -207,7 +207,7 @@ private: /** Usual function for theories */ void check(Theory::Effort e); Node explain(TNode n); - void collectModelInfo( TheoryModel* m ); + void collectModelInfo( TheoryModel* m, bool fullModel ); void notifyEq(TNode lhs, TNode rhs); std::string identify() const { return "THEORY_REWRITERULES"; diff --git a/src/theory/shared_terms_database.cpp b/src/theory/shared_terms_database.cpp index 7abc7f1e5..426458202 100644 --- a/src/theory/shared_terms_database.cpp +++ b/src/theory/shared_terms_database.cpp @@ -130,7 +130,7 @@ bool SharedTermsDatabase::propagateSharedEquality(TheoryId theory, TNode a, TNod return false; } - // Propagate away + // Propagate away Node equality = a.eqNode(b); if (value) { d_theoryEngine->assertToTheory(equality, theory, THEORY_BUILTIN); @@ -156,20 +156,20 @@ void SharedTermsDatabase::markNotified(TNode term, Theory::Set theories) { if (newlyNotified == 0) { return; } - + Debug("shared-terms-database") << "SharedTermsDatabase::markNotified(" << term << ")" << endl; // First update the set of notified theories for this term d_alreadyNotifiedMap[term] = Theory::setUnion(newlyNotified, alreadyNotified); // Mark the shared terms in the equality engine - theory::TheoryId currentTheory; + theory::TheoryId currentTheory; while ((currentTheory = Theory::setPop(newlyNotified)) != THEORY_LAST) { - d_equalityEngine.addTriggerTerm(term, currentTheory); + d_equalityEngine.addTriggerTerm(term, currentTheory); } - + // Check for any conflits - checkForConflict(); + checkForConflict(); } bool SharedTermsDatabase::areEqual(TNode a, TNode b) const { @@ -181,7 +181,7 @@ bool SharedTermsDatabase::areEqual(TNode a, TNode b) const { // since one (or both) of them is a constant, and the other is in the equality engine, they are not same return false; } -} +} bool SharedTermsDatabase::areDisequal(TNode a, TNode b) const { if (d_equalityEngine.hasTerm(a) && d_equalityEngine.hasTerm(b)) { @@ -240,7 +240,7 @@ void SharedTermsDatabase::checkForConflict() { std::vector<TNode> assumptions; d_equalityEngine.explainEquality(d_conflictLHS, d_conflictRHS, d_conflictPolarity, assumptions); Node conflict = mkAnd(assumptions); - d_theoryEngine->conflict(conflict, THEORY_BUILTIN); + d_theoryEngine->conflict(conflict, THEORY_BUILTIN); d_conflictLHS = d_conflictRHS = Node::null(); } } @@ -261,6 +261,9 @@ Node SharedTermsDatabase::explain(TNode literal) const { Assert(atom.getKind() == kind::EQUAL); std::vector<TNode> assumptions; d_equalityEngine.explainEquality(atom[0], atom[1], polarity, assumptions); - return mkAnd(assumptions); + return mkAnd(assumptions); } +void SharedTermsDatabase::collectModelInfo( theory::TheoryModel* m, bool fullModel ){ + m->assertEqualityEngine( &d_equalityEngine ); +} diff --git a/src/theory/shared_terms_database.h b/src/theory/shared_terms_database.h index 7b6527517..c685257ba 100644 --- a/src/theory/shared_terms_database.h +++ b/src/theory/shared_terms_database.h @@ -242,6 +242,11 @@ public: * get equality engine */ theory::eq::EqualityEngine* getEqualityEngine() { return &d_equalityEngine; } + + /** + * collect model info + */ + void collectModelInfo( theory::TheoryModel* m, bool fullModel ); protected: /** diff --git a/src/theory/term_registration_visitor.cpp b/src/theory/term_registration_visitor.cpp index ab6b27dff..104292e18 100644 --- a/src/theory/term_registration_visitor.cpp +++ b/src/theory/term_registration_visitor.cpp @@ -17,6 +17,7 @@ #include "theory/term_registration_visitor.h" #include "theory/theory_engine.h" +#include "theory/quantifiers/options.h" using namespace std; using namespace CVC4; @@ -37,7 +38,8 @@ bool PreRegisterVisitor::alreadyVisited(TNode current, TNode parent) { if( ( parent.getKind() == kind::FORALL || parent.getKind() == kind::EXISTS || - parent.getKind() == kind::REWRITE_RULE ) && + parent.getKind() == kind::REWRITE_RULE /*|| + parent.getKind() == kind::CARDINALITY_CONSTRAINT*/ ) && current != parent ) { Debug("register::internal") << "quantifier:true" << std::endl; return true; @@ -160,7 +162,8 @@ bool SharedTermsVisitor::alreadyVisited(TNode current, TNode parent) const { if( ( parent.getKind() == kind::FORALL || parent.getKind() == kind::EXISTS || - parent.getKind() == kind::REWRITE_RULE) && + parent.getKind() == kind::REWRITE_RULE /*|| + parent.getKind() == kind::CARDINALITY_CONSTRAINT*/ ) && current != parent ) { Debug("register::internal") << "quantifier:true" << std::endl; return true; @@ -179,12 +182,40 @@ bool SharedTermsVisitor::alreadyVisited(TNode current, TNode parent) const { TheoryId parentTheoryId = Theory::theoryOf(parent); // Should we use the theory of the type +#if 0 bool useType = current != parent && currentTheoryId != parentTheoryId; +#else + bool useType = false; + TheoryId typeTheoryId = THEORY_LAST; + + if (current != parent) { + if (currentTheoryId != parentTheoryId) { + // If enclosed by different theories it's shared -- in read(a, f(a)) f(a) should be shared with integers + TypeNode type = current.getType(); + useType = true; + typeTheoryId = Theory::theoryOf(type); + } else { + TypeNode type = current.getType(); + typeTheoryId = Theory::theoryOf(type); + if (typeTheoryId != currentTheoryId) { + if (options::finiteModelFind() && type.isSort()) { + // We're looking for finite models + useType = true; + } else { + Cardinality card = type.getCardinality(); + if (card.isFinite()) { + useType = true; + } + } + } + } + } +#endif if (Theory::setContains(currentTheoryId, theories)) { if (Theory::setContains(parentTheoryId, theories)) { if (useType) { - TheoryId typeTheoryId = Theory::theoryOf(current.getType()); + ////TheoryId typeTheoryId = Theory::theoryOf(current.getType()); return Theory::setContains(typeTheoryId, theories); } else { return true; @@ -208,7 +239,36 @@ void SharedTermsVisitor::visit(TNode current, TNode parent) { TheoryId currentTheoryId = Theory::theoryOf(current); TheoryId parentTheoryId = Theory::theoryOf(parent); +#if 0 bool useType = current != parent && currentTheoryId != parentTheoryId; +#else + // Should we use the theory of the type + bool useType = false; + TheoryId typeTheoryId = THEORY_LAST; + + if (current != parent) { + if (currentTheoryId != parentTheoryId) { + // If enclosed by different theories it's shared -- in read(a, f(a)) f(a) should be shared with integers + TypeNode type = current.getType(); + useType = true; + typeTheoryId = Theory::theoryOf(type); + } else { + TypeNode type = current.getType(); + typeTheoryId = Theory::theoryOf(type); + if (typeTheoryId != currentTheoryId) { + if (options::finiteModelFind() && type.isSort()) { + // We're looking for finite models + useType = true; + } else { + Cardinality card = type.getCardinality(); + if (card.isFinite()) { + useType = true; + } + } + } + } + } +#endif Theory::Set visitedTheories = d_visited[current]; Debug("register::internal") << "SharedTermsVisitor::visit(" << current << "," << parent << "): previously registered with " << Theory::setToString(visitedTheories) << std::endl; @@ -221,7 +281,7 @@ void SharedTermsVisitor::visit(TNode current, TNode parent) { Debug("register::internal") << "SharedTermsVisitor::visit(" << current << "," << parent << "): adding " << parentTheoryId << std::endl; } if (useType) { - TheoryId typeTheoryId = Theory::theoryOf(current.getType()); + //////TheoryId typeTheoryId = Theory::theoryOf(current.getType()); if (!Theory::setContains(typeTheoryId, visitedTheories)) { visitedTheories = Theory::setInsert(typeTheoryId, visitedTheories); Debug("register::internal") << "SharedTermsVisitor::visit(" << current << "," << parent << "): adding " << typeTheoryId << std::endl; diff --git a/src/theory/theory.h b/src/theory/theory.h index 46244aec6..2f980fe2f 100644 --- a/src/theory/theory.h +++ b/src/theory/theory.h @@ -553,11 +553,11 @@ public: * Get all relevant information in this theory regarding the current * model. This should be called after a call to check( FULL_EFFORT ) * for all theories with no conflicts and no lemmas added. + * If fullModel is true, then we must specify sufficient information for + * the model class to construct constant representatives for each equivalence + * class. */ - virtual void collectModelInfo( TheoryModel* m ){ - Unimplemented("Theory %s doesn't support Theory::getModel interface", - identify().c_str()); - } + virtual void collectModelInfo( TheoryModel* m, bool fullModel ){ } /** * Return a decision request, if the theory has one, or the NULL node @@ -657,6 +657,15 @@ public: */ virtual std::string identify() const = 0; + /** Set user attribute + * This function is called when an attribute is set by a user. In SMT-LIBv2 this is done + * via the syntax (! n :attr) + */ + virtual void setUserAttribute( std::string& attr, Node n ) { + Unimplemented("Theory %s doesn't support Theory::setUserAttribute interface", + identify().c_str()); + } + /** A set of theories */ typedef uint32_t Set; diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp index 7a67012a2..6dbabfe4d 100644 --- a/src/theory/theory_engine.cpp +++ b/src/theory/theory_engine.cpp @@ -42,6 +42,11 @@ #include "theory/quantifiers/model_engine.h" #include "theory/quantifiers/first_order_model.h" +//hack +#include "theory/arith/options.h" +#include "theory/uf/options.h" + + using namespace std; using namespace CVC4; @@ -85,7 +90,7 @@ TheoryEngine::TheoryEngine(context::Context* context, d_quantEngine = new QuantifiersEngine(context, this); //build model information if applicable - d_curr_model = new theory::DefaultModel( context, "DefaultModel", false ); + d_curr_model = new theory::DefaultModel( context, "DefaultModel", true ); d_curr_model_builder = new theory::TheoryEngineModelBuilder( this ); Rewriter::init(); @@ -144,33 +149,57 @@ void TheoryEngine::preRegister(TNode preprocessed) { } } +void collectGroundTerms( Node n, std::vector< Node >& defineFuns, + std::vector< Node >& groundTerms ){ + if( std::find( groundTerms.begin(), groundTerms.end(), n )==groundTerms.end() ){ + groundTerms.push_back( n ); + if( n.getKind()==kind::APPLY_UF ){ + if( std::find( defineFuns.begin(), defineFuns.end(), n.getOperator() )==defineFuns.end() ){ + defineFuns.push_back( n.getOperator() ); + } + }else if( n.getNumChildren()==0 ){ + if( std::find( defineFuns.begin(), defineFuns.end(), n )==defineFuns.end() ){ + defineFuns.push_back( n ); + } + } + if( n.getKind()==kind::FORALL ){ + std::cout << "Bad ground assertion : " << n << std::endl; + std::cout << "...possible nested quantifiers?" << std::endl; + exit( -1 ); + } + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + collectGroundTerms( n[i], defineFuns, groundTerms ); + } + } +} + void TheoryEngine::printAssertions(const char* tag) { - if (Debug.isOn(tag)) { + if (Trace.isOn(tag)) { + for (TheoryId theoryId = THEORY_FIRST; theoryId < THEORY_LAST; ++theoryId) { Theory* theory = d_theoryTable[theoryId]; if (theory && d_logicInfo.isTheoryEnabled(theoryId)) { - Debug(tag) << "--------------------------------------------" << std::endl; - Debug(tag) << "Assertions of " << theory->getId() << ": " << std::endl; + Trace(tag) << "--------------------------------------------" << std::endl; + Trace(tag) << "Assertions of " << theory->getId() << ": " << std::endl; context::CDList<Assertion>::const_iterator it = theory->facts_begin(), it_end = theory->facts_end(); for (unsigned i = 0; it != it_end; ++ it, ++i) { if ((*it).isPreregistered) { - Debug(tag) << "[" << i << "]: "; + Trace(tag) << "[" << i << "]: "; } else { - Debug(tag) << "(" << i << "): "; + Trace(tag) << "(" << i << "): "; } - Debug(tag) << (*it).assertion << endl; + Trace(tag) << (*it).assertion << endl; } if (d_logicInfo.isSharingEnabled()) { - Debug(tag) << "Shared terms of " << theory->getId() << ": " << std::endl; + Trace(tag) << "Shared terms of " << theory->getId() << ": " << std::endl; context::CDList<TNode>::const_iterator it = theory->shared_terms_begin(), it_end = theory->shared_terms_end(); for (unsigned i = 0; it != it_end; ++ it, ++i) { - Debug(tag) << "[" << i << "]: " << (*it) << endl; + Trace(tag) << "[" << i << "]: " << (*it) << endl; } } } } - } } @@ -312,7 +341,8 @@ void TheoryEngine::check(Theory::Effort effort) { Debug("theory") << "TheoryEngine::check(" << effort << "): running check" << std::endl; - if (Debug.isOn("theory::assertions")) { + Trace("theory::assertions") << std::endl; + if (Trace.isOn("theory::assertions")) { printAssertions("theory::assertions"); } @@ -346,7 +376,8 @@ void TheoryEngine::check(Theory::Effort effort) { ! d_inConflict && ! d_lemmasAdded ) { if( d_logicInfo.isQuantified() ){ - ((theory::quantifiers::TheoryQuantifiers*) d_theoryTable[THEORY_QUANTIFIERS])->performCheck(Theory::EFFORT_LAST_CALL); + //quantifiers engine must pass effort last call check + d_quantEngine->check(Theory::EFFORT_LAST_CALL); // if we have given up, then possibly flip decision if(options::flipDecision()) { if(d_incomplete && !d_inConflict && !d_lemmasAdded) { @@ -358,7 +389,7 @@ void TheoryEngine::check(Theory::Effort effort) { //if returning incomplete or SAT, we have ensured that the model in the quantifiers engine has been built }else if( options::produceModels() ){ //must build model at this point - d_curr_model_builder->buildModel( d_curr_model ); + d_curr_model_builder->buildModel( d_curr_model, true ); } } @@ -436,6 +467,7 @@ void TheoryEngine::combineTheories() { d_factsAsserted = true; continue; } else { + Message() << "mark propagation fail: " << literal << " " << normalizedLiteral << " " << carePair.theory << std::endl; Unreachable(); } } @@ -560,12 +592,14 @@ bool TheoryEngine::properExplanation(TNode node, TNode expl) const { return true; } -void TheoryEngine::collectModelInfo( theory::TheoryModel* m ){ +void TheoryEngine::collectModelInfo( theory::TheoryModel* m, bool fullModel ){ + //have shared term engine collectModelInfo + d_sharedTerms.collectModelInfo( m, fullModel ); // Consult each active theory to get all relevant information // concerning the model. for(TheoryId theoryId = theory::THEORY_FIRST; theoryId < theory::THEORY_LAST; ++theoryId) { if(d_logicInfo.isTheoryEnabled(theoryId)) { - d_theoryTable[theoryId]->collectModelInfo(m); + d_theoryTable[theoryId]->collectModelInfo( m, fullModel ); } } } @@ -688,6 +722,16 @@ theory::Theory::PPAssertStatus TheoryEngine::solve(TNode literal, SubstitutionMa Theory::PPAssertStatus solveStatus = theoryOf(atom)->ppAssert(literal, substitutionOut); Trace("theory::solve") << "TheoryEngine::solve(" << literal << ") => " << solveStatus << endl; + //must add substitutions to model + theory::TheoryModel* m = getModel(); + if( m ){ + for( SubstitutionMap::iterator pos = substitutionOut.begin(); pos != substitutionOut.end(); ++pos) { + Node n = (*pos).first; + Node v = (*pos).second; + Trace("model") << "Add substitution : " << n << " " << v << std::endl; + m->addSubstitution( n, v ); + } + } return solveStatus; } @@ -1307,3 +1351,21 @@ void TheoryEngine::ppUnconstrainedSimp(vector<Node>& assertions) { d_unconstrainedSimp.processAssertions(assertions); } + + +void TheoryEngine::setUserAttribute( std::string& attr, Node n ){ + Trace("te-attr") << "set user attribute " << attr << " " << n << std::endl; + if( d_attr_handle.find( attr )!=d_attr_handle.end() ){ + for( size_t i=0; i<d_attr_handle[attr].size(); i++ ){ + d_attr_handle[attr][i]->setUserAttribute( attr, n ); + } + }else{ + //unhandled exception? + } +} + +void TheoryEngine::handleUserAttribute( const char* attr, Theory* t ){ + Trace("te-attr") << "Handle user attribute " << attr << " " << t << std::endl; + std::string str( attr ); + d_attr_handle[ str ].push_back( t ); +} diff --git a/src/theory/theory_engine.h b/src/theory/theory_engine.h index 75f4d6a37..d1d6bd1f3 100644 --- a/src/theory/theory_engine.h +++ b/src/theory/theory_engine.h @@ -274,7 +274,9 @@ class TheoryEngine { void spendResource() throw() { d_engine->spendResource(); } - + void handleUserAttribute( const char* attr, theory::Theory* t ){ + d_engine->handleUserAttribute( attr, t ); + } };/* class TheoryEngine::EngineOutputChannel */ /** @@ -616,7 +618,7 @@ public: /** * collect model info */ - void collectModelInfo( theory::TheoryModel* m ); + void collectModelInfo( theory::TheoryModel* m, bool fullModel ); /** * Get the current model @@ -680,6 +682,22 @@ public: SharedTermsDatabase* getSharedTermsDatabase() { return &d_sharedTerms; } +private: + std::map< std::string, std::vector< theory::Theory* > > d_attr_handle; +public: + + /** Set user attribute + * This function is called when an attribute is set by a user. In SMT-LIBv2 this is done + * via the syntax (! n :attr) + */ + void setUserAttribute( std::string& attr, Node n ); + + /** Handle user attribute + * Associates theory t with the attribute attr. Theory t will be + * notifed whenever an attribute of name attr is set. + */ + void handleUserAttribute( const char* attr, theory::Theory* t ); + };/* class TheoryEngine */ }/* CVC4 namespace */ diff --git a/src/theory/theory_test_utils.h b/src/theory/theory_test_utils.h index f827b9ee7..ee7b4cf2d 100644 --- a/src/theory/theory_test_utils.h +++ b/src/theory/theory_test_utils.h @@ -103,6 +103,8 @@ public: void setIncomplete() throw(AssertionException) {} + void handleUserAttribute( const char* attr, theory::Theory* t ){} + void clear() { d_callHistory.clear(); } diff --git a/src/theory/uf/equality_engine.h b/src/theory/uf/equality_engine.h index 1e3b276a4..45d1b4acf 100644 --- a/src/theory/uf/equality_engine.h +++ b/src/theory/uf/equality_engine.h @@ -82,7 +82,7 @@ public: virtual bool eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value) = 0; /** - * Notifies about the merge of two constant terms. After this, all work is suspended and all you + * Notifies about the merge of two constant terms. After this, all work is suspended and all you * can do is ask for explanations. * * @param t1 a constant term @@ -384,7 +384,7 @@ private: std::vector<TriggerId> d_nodeTriggers; /** - * Map from ids to whether they are constants (constants are always + * Map from ids to whether they are constants (constants are always * representatives of their class. */ std::vector<bool> d_isConstant; @@ -427,7 +427,7 @@ private: /** * Get an explanation of the equality t1 = t2. Returns the asserted equalities that * imply t1 = t2. Returns TNodes as the assertion equalities should be hashed somewhere - * else. + * else. */ void getExplanation(EqualityEdgeId t1Id, EqualityNodeId t2Id, std::vector<TNode>& equalities) const; @@ -440,7 +440,7 @@ private: Node d_true; /** True node id */ EqualityNodeId d_trueId; - + /** The false node */ Node d_false; /** False node id */ @@ -484,12 +484,12 @@ private: /** Internal tags for creating a new set */ Theory::Set d_newSetTags; - + /** Internal triggers for creating a new set */ EqualityNodeId d_newSetTriggers[THEORY_LAST]; - + /** Size of the internal triggers array */ - unsigned d_newSetTriggersSize; + unsigned d_newSetTriggersSize; /** The information about trigger terms is stored in this easily maintained memory. */ char* d_triggerDatabase; @@ -524,7 +524,7 @@ private: struct TriggerSetUpdate { EqualityNodeId classId; TriggerTermSetRef oldValue; - TriggerSetUpdate(EqualityNodeId classId = null_id, TriggerTermSetRef oldValue = null_set_id) + TriggerSetUpdate(EqualityNodeId classId = null_id, TriggerTermSetRef oldValue = null_set_id) : classId(classId), oldValue(oldValue) {} };/* struct EqualityEngine::TriggerSetUpdate */ @@ -591,7 +591,7 @@ private: * reasons should be pushed on the reasons vector. */ void storePropagatedDisequality(TheoryId tag, EqualityNodeId lhsId, EqualityNodeId rhsId); - + /** * An equality tagged with a set of tags. */ @@ -599,10 +599,10 @@ private: /** Id of the equality */ EqualityNodeId equalityId; /** TriggerSet reference for the class of one of the sides */ - TriggerTermSetRef triggerSetRef; + TriggerTermSetRef triggerSetRef; /** Is trigger equivalent to the lhs (rhs otherwise) */ bool lhs; - + TaggedEquality(EqualityNodeId equalityId = null_id, TriggerTermSetRef triggerSetRef = null_set_id, bool lhs = true) : equalityId(equalityId), triggerSetRef(triggerSetRef), lhs(lhs) {} }; @@ -625,9 +625,9 @@ private: /** * Propagates the remembered disequalities with given tags the original triggers for those tags, - * and the set of disequalities produced by above. + * and the set of disequalities produced by above. */ - bool propagateTriggerTermDisequalities(Theory::Set tags, + bool propagateTriggerTermDisequalities(Theory::Set tags, TriggerTermSetRef triggerSetRef, const TaggedEqualitiesSet& disequalitiesToNotify); /** Name of the equality engine */ @@ -636,12 +636,12 @@ private: public: /** - * Initialize the equality engine, given the notification class. + * Initialize the equality engine, given the notification class. */ EqualityEngine(EqualityEngineNotify& notify, context::Context* context, std::string name); /** - * Initialize the equality engine with no notification class. + * Initialize the equality engine with no notification class. */ EqualityEngine(context::Context* context, std::string name); @@ -791,7 +791,7 @@ class EqClassesIterator { const eq::EqualityEngine* d_ee; size_t d_it; - + std::vector< Node > d_visited; public: EqClassesIterator(): d_ee(NULL), d_it(0){ } @@ -812,11 +812,11 @@ public: return !(*this == i); } EqClassesIterator& operator++() { - Node orig = d_ee->d_nodes[d_it]; + d_visited.push_back( 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_ee->getRepresentative(d_ee->d_nodes[d_it]) != d_ee->d_nodes[d_it] || + std::find( d_visited.begin(), d_visited.end(), d_ee->d_nodes[d_it] )!=d_visited.end() ) ) { // this line is necessary for ignoring duplicates ++d_it; } return *this; diff --git a/src/theory/uf/kinds b/src/theory/uf/kinds index efad8beb9..1d179248c 100644 --- a/src/theory/uf/kinds +++ b/src/theory/uf/kinds @@ -9,7 +9,7 @@ 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 ppStaticLearn presolve +properties check propagate ppStaticLearn presolve getNextDecisionRequest rewriter ::CVC4::theory::uf::TheoryUfRewriter "theory/uf/theory_uf_rewriter.h" parameterized APPLY_UF VARIABLE 1: "uninterpreted function application" @@ -19,47 +19,4 @@ typerule APPLY_UF ::CVC4::theory::uf::UfTypeRule operator CARDINALITY_CONSTRAINT 2 "cardinality constraint" typerule CARDINALITY_CONSTRAINT ::CVC4::theory::uf::CardinalityConstraintTypeRule -# -# For compact function models -# There are three cases for FUNCTION_MODEL nodes: -# (1) The node has two children, the first being of kind FUNCTION_CASE_SPLIT. The second child specifies a default value. -# (2) The node has one child of kind FUNCTION_CASE_SPLIT. -# (3) The node has one child, it's default value. -# -# Semantics of FUNCTION_MODEL kind-ed nodes. The value of n applied to arguments args is -# -# getValueFM( n, args, 0 ), where: -# -# Node getValueFM( n, args, argIndex ) -# if n.getKind()!=FUNCTION_MODEL -# return n; -# else if (1) -# val = getValueFCS( n[0], args, argIndex ); -# if !val.isNull() -# return val; -# else -# return getValueFM( n[1], args, argIndex+1 ); -# else if (2) -# return getValueFCS( n[0], args, argIndex ); -# else if (3) -# return getValueFM( n[0], args, argIndex+1 ); -# -# Node getValueFCS( n, args, argIndex ) : -# //n.getKind()==FUNCTION_CASE_SPLIT -# //n[j].getKind()==FUNCTION_CASE for all 0<=j<n.getNumChildren() -# if( args[argIndex]=n[i][0] for some i) -# return getValueFM( n[i][1], args, argIndex+1 ); -# else -# return null; -# - -operator FUNCTION_MODEL 1:2 "function model" -typerule FUNCTION_MODEL ::CVC4::theory::uf::FunctionModelTypeRule - -operator FUNCTION_CASE_SPLIT 1: "function case split" -typerule FUNCTION_CASE_SPLIT ::CVC4::theory::uf::FunctionCaseSplitTypeRule - -operator FUNCTION_CASE 2 "function case" -typerule FUNCTION_CASE ::CVC4::theory::uf::FunctionCaseTypeRule - endtheory diff --git a/src/theory/uf/options b/src/theory/uf/options index 6f6900da0..8185f0b3d 100644 --- a/src/theory/uf/options +++ b/src/theory/uf/options @@ -9,4 +9,22 @@ option ufSymmetryBreaker uf-symmetry-breaker --enable-symmetry-breaker/--disable use UF symmetry breaker (Deharbe et al., CADE 2011) /turns off UF symmetry breaker (Deharbe et al., CADE 2011) +option ufssRegions /--disable-uf-ss-regions bool :default true + disable region-based method for discovering cliques and splits in uf strong solver +option ufssEagerSplits --uf-ss-eager-split bool :default false + add splits eagerly for uf strong solver +option ufssColoringSat --uf-ss-coloring-sat bool :default false + use coloring-based SAT heuristic for uf strong solver +option ufssTotality --uf-ss-totality bool :default false + use totality axioms for enforcing cardinality constraints +option ufssTotalityLazy --uf-ss-totality-lazy bool :default false + apply totality axioms lazily +option ufssAbortCardinality --uf-ss-abort-card=N int :default -1 + tells the uf strong solver a cardinality to abort at (-1 == no limit, default) +option ufssSmartSplits --uf-ss-smart-split bool :default false + use smart splitting heuristic for uf strong solver +option ufssModelInference --uf-ss-model-infer bool :default false + use model inference method for uf strong solver + + endmodule diff --git a/src/theory/uf/theory_uf.cpp b/src/theory/uf/theory_uf.cpp index 5b8470567..a1500e084 100644 --- a/src/theory/uf/theory_uf.cpp +++ b/src/theory/uf/theory_uf.cpp @@ -23,6 +23,7 @@ #include "theory/uf/theory_uf_instantiator.h" #include "theory/uf/theory_uf_strong_solver.h" #include "theory/model.h" +#include "theory/type_enumerator.h" using namespace std; using namespace CVC4; @@ -80,6 +81,10 @@ void TheoryUF::check(Effort level) { if (d_thss != NULL) { bool isDecision = d_valuation.isSatLiteral(fact) && d_valuation.isDecision(fact); d_thss->assertNode(fact, isDecision); + if( d_thss->isConflict() ){ + d_conflict = true; + return; + } } // Do the work @@ -98,6 +103,9 @@ void TheoryUF::check(Effort level) { if (d_thss != NULL) { if (! d_conflict) { d_thss->check(level); + if( d_thss->isConflict() ){ + d_conflict = true; + } } } @@ -127,6 +135,9 @@ void TheoryUF::preRegisterTerm(TNode node) { // Remember the function and predicate terms d_functionsTerms.push_back(node); break; + case kind::CARDINALITY_CONSTRAINT: + //do nothing + break; default: // Variables etc d_equalityEngine.addTerm(node); @@ -150,8 +161,16 @@ bool TheoryUF::propagate(TNode literal) { }/* TheoryUF::propagate(TNode) */ void TheoryUF::propagate(Effort effort) { - if (d_thss != NULL) { - return d_thss->propagate(effort); + //if (d_thss != NULL) { + // return d_thss->propagate(effort); + //} +} + +Node TheoryUF::getNextDecisionRequest(){ + if (d_thss != NULL && !d_conflict) { + return d_thss->getNextDecisionRequest(); + }else{ + return Node::null(); } } @@ -173,8 +192,55 @@ Node TheoryUF::explain(TNode literal) { return mkAnd(assumptions); } -void TheoryUF::collectModelInfo( TheoryModel* m ){ +void TheoryUF::collectModelInfo( TheoryModel* m, bool fullModel ){ m->assertEqualityEngine( &d_equalityEngine ); + if( fullModel ){ +#if 1 + std::map< TypeNode, int > type_count; + //must choose proper representatives + // for each equivalence class, specify the constructor as a representative + eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine ); + while( !eqcs_i.isFinished() ){ + Node eqc = (*eqcs_i); + TypeNode tn = eqc.getType(); + if( tn.isSort() ){ + if( type_count.find( tn )==type_count.end() ){ + type_count[tn] = 0; + } + std::stringstream ss; + ss << Expr::setlanguage(options::outputLanguage()); + ss << "$t_" << tn << (type_count[tn]+1); + type_count[tn]++; + Node rep = NodeManager::currentNM()->mkSkolem( ss.str(), tn ); + Trace("mkVar") << "TheoryUF::collectModelInfo: make variable " << rep << " : " << tn << std::endl; + //specify the constant as the representative + m->assertEquality( eqc, rep, true ); + m->assertRepresentative( rep ); + } + ++eqcs_i; + } +#else + std::map< TypeNode, TypeEnumerator* > type_enums; + //must choose proper representatives + // for each equivalence class, specify the constructor as a representative + eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine ); + while( !eqcs_i.isFinished() ){ + Node eqc = (*eqcs_i); + TypeNode tn = eqc.getType(); + if( tn.isSort() ){ + if( type_enums.find( tn )==type_enums.end() ){ + type_enums[tn] = new TypeEnumerator( tn ); + } + Node rep = *(*type_enums[tn]); + ++(*type_enums[tn]); + //specify the constant as the representative + m->assertEquality( eqc, rep, true ); + m->assertRepresentative( rep ); + } + ++eqcs_i; + } + #endif + } } void TheoryUF::presolve() { @@ -481,3 +547,4 @@ Node TheoryUF::ppRewrite(TNode node) { } } } + diff --git a/src/theory/uf/theory_uf.h b/src/theory/uf/theory_uf.h index 604b1f44c..62ca640aa 100644 --- a/src/theory/uf/theory_uf.h +++ b/src/theory/uf/theory_uf.h @@ -193,7 +193,7 @@ public: void preRegisterTerm(TNode term); Node explain(TNode n); - void collectModelInfo( TheoryModel* m ); + void collectModelInfo( TheoryModel* m, bool fullModel ); void ppStaticLearn(TNode in, NodeBuilder<>& learned); void presolve(); @@ -202,6 +202,7 @@ public: void computeCareGraph(); void propagate(Effort effort); + Node getNextDecisionRequest(); EqualityStatus getEqualityStatus(TNode a, TNode b); @@ -226,7 +227,6 @@ public: 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_model.cpp b/src/theory/uf/theory_uf_model.cpp index 0082f4840..b8110a2aa 100644 --- a/src/theory/uf/theory_uf_model.cpp +++ b/src/theory/uf/theory_uf_model.cpp @@ -136,35 +136,46 @@ Node UfModelTreeNode::getValue( TheoryModel* m, Node n, std::vector< int >& inde } } -Node UfModelTreeNode::getFunctionValue(){ +Node UfModelTreeNode::getFunctionValue( std::vector< Node >& args, int index, Node argDefaultValue ){ if( !d_data.empty() ){ - Node defaultValue; + Node defaultValue = argDefaultValue; + if( d_data.find( Node::null() )!=d_data.end() ){ + defaultValue = d_data[Node::null()].getFunctionValue( args, index+1, argDefaultValue ); + } std::vector< Node > caseValues; + std::map< Node, Node > caseArg; for( std::map< Node, UfModelTreeNode >::iterator it = d_data.begin(); it != d_data.end(); ++it ){ - if( it->first.isNull() ){ - defaultValue = it->second.getFunctionValue(); - }else{ - caseValues.push_back( NodeManager::currentNM()->mkNode( FUNCTION_CASE, it->first, it->second.getFunctionValue() ) ); + if( !it->first.isNull() ){ + Node val = it->second.getFunctionValue( args, index+1, defaultValue ); + caseValues.push_back( val ); + caseArg[ val ] = it->first; } } - if( caseValues.empty() && defaultValue.getKind()!=FUNCTION_CASE_SPLIT && defaultValue.getKind()!=FUNCTION_MODEL ){ - return defaultValue; - }else{ - std::vector< Node > children; - if( !caseValues.empty() ){ - children.push_back( NodeManager::currentNM()->mkNode( FUNCTION_CASE_SPLIT, caseValues ) ); - } - if( !defaultValue.isNull() ){ - children.push_back( defaultValue ); - } - return NodeManager::currentNM()->mkNode( FUNCTION_MODEL, children ); + Node retNode = defaultValue; + for( int i=((int)caseValues.size()-1); i>=0; i-- ){ + retNode = NodeManager::currentNM()->mkNode( ITE, args[index].eqNode( caseArg[ caseValues[i] ] ), caseValues[i], retNode ); } + return retNode; }else{ Assert( !d_value.isNull() ); return d_value; } } +//update function +void UfModelTreeNode::update( TheoryModel* m ){ + if( !d_value.isNull() ){ + d_value = m->getRepresentative( d_value ); + } + std::map< Node, UfModelTreeNode > old = d_data; + d_data.clear(); + for( std::map< Node, UfModelTreeNode >::iterator it = old.begin(); it != old.end(); ++it ){ + Node rep = m->getRepresentative( it->first ); + d_data[ rep ] = it->second; + d_data[ rep ].update( m ); + } +} + //simplify function void UfModelTreeNode::simplify( Node op, Node defaultVal, int argIndex ){ if( argIndex<(int)op.getType().getNumChildren()-1 ){ @@ -251,31 +262,17 @@ void UfModelTreeNode::debugPrint( std::ostream& out, TheoryModel* m, std::vector } } - -Node UfModelTree::toIte2( Node fm_node, std::vector< Node >& args, int index, Node defaultNode ){ - if( fm_node.getKind()==FUNCTION_MODEL ){ - if( fm_node[0].getKind()==FUNCTION_CASE_SPLIT ){ - Node retNode; - Node childDefaultNode = defaultNode; - //get new default - if( fm_node.getNumChildren()==2 ){ - childDefaultNode = toIte2( fm_node[1], args, index+1, defaultNode ); - } - retNode = childDefaultNode; - for( int i=(int)fm_node[0].getNumChildren()-1; i>=0; i-- ){ - Node childNode = toIte2( fm_node[0][1], args, index+1, childDefaultNode ); - retNode = NodeManager::currentNM()->mkNode( ITE, args[index].eqNode( fm_node[0][0] ), childNode, retNode ); - } - return retNode; - }else{ - return toIte2( fm_node[0], args, index+1, defaultNode ); - } - }else{ - return fm_node; +Node UfModelTree::getFunctionValue( const char* argPrefix ){ + TypeNode type = d_op.getType(); + std::vector< Node > vars; + for( size_t i=0; i<type.getNumChildren()-1; i++ ){ + std::stringstream ss; + ss << argPrefix << (i+1); + vars.push_back( NodeManager::currentNM()->mkSkolem( ss.str(), type[i] ) ); } + return getFunctionValue( vars ); } - Node UfModelTreeGenerator::getIntersection( TheoryModel* m, Node n1, Node n2, bool& isGround ){ //Notice() << "Get intersection " << n1 << " " << n2 << std::endl; isGround = true; diff --git a/src/theory/uf/theory_uf_model.h b/src/theory/uf/theory_uf_model.h index 9dba16608..61c0714a3 100644 --- a/src/theory/uf/theory_uf_model.h +++ b/src/theory/uf/theory_uf_model.h @@ -48,7 +48,9 @@ public: /** getConstant Value function */ Node getConstantValue( TheoryModel* m, Node n, std::vector< int >& indexOrder, int argIndex ); /** getFunctionValue */ - Node getFunctionValue(); + Node getFunctionValue( std::vector< Node >& args, int index, Node argDefaultValue ); + /** update function */ + void update( TheoryModel* m ); /** simplify function */ void simplify( Node op, Node defaultVal, int argIndex ); /** is total ? */ @@ -123,12 +125,15 @@ public: return d_tree.getConstantValue( m, n, d_index_order, 0 ); } /** getFunctionValue - * Returns a compact representation of this function, of kind FUNCTION_MODEL. - * See documentation in theory/uf/kinds + * Returns a representation of this function. */ - Node getFunctionValue(){ - return d_tree.getFunctionValue(); - } + Node getFunctionValue( std::vector< Node >& args ){ return d_tree.getFunctionValue( args, 0, Node::null() ); } + /** getFunctionValue for args with set prefix */ + Node getFunctionValue( const char* argPrefix ); + /** update + * This will update all values in the tree to be representatives in m. + */ + void update( TheoryModel* m ){ d_tree.update( m ); } /** simplify the tree */ void simplify() { d_tree.simplify( d_op, Node::null(), 0 ); } /** is this tree total? */ @@ -147,6 +152,7 @@ private: public: /** to ITE function for function model nodes */ static Node toIte( Node fm_node, std::vector< Node >& args ) { return toIte2( fm_node, args, 0, Node::null() ); } + static Node toIte( TypeNode type, Node fm_node, const char* argPrefix ); }; class UfModelTreeGenerator diff --git a/src/theory/uf/theory_uf_strong_solver.cpp b/src/theory/uf/theory_uf_strong_solver.cpp index f0b386cae..47c51d8b9 100644 --- a/src/theory/uf/theory_uf_strong_solver.cpp +++ b/src/theory/uf/theory_uf_strong_solver.cpp @@ -19,10 +19,9 @@ #include "theory/uf/equality_engine.h" #include "theory/uf/theory_uf_instantiator.h" #include "theory/theory_engine.h" -#include "theory/quantifiers/options.h" #include "theory/quantifiers/term_database.h" +#include "theory/uf/options.h" -//#define USE_SMART_SPLITS //#define ONE_SPLIT_REGION //#define DISABLE_QUICK_CLIQUE_CHECKS //#define COMBINE_REGIONS_SMALL_INTO_LARGE @@ -34,16 +33,11 @@ using namespace CVC4::context; using namespace CVC4::theory; using namespace CVC4::theory::uf; -void StrongSolverTheoryUf::ConflictFind::Region::addRep( Node n ) { +void StrongSolverTheoryUf::SortRepModel::Region::addRep( Node n ) { setRep( n, true ); } -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"); +void StrongSolverTheoryUf::SortRepModel::Region::takeNode( StrongSolverTheoryUf::SortRepModel::Region* r, Node n ){ Assert( !hasRep( n ) ); Assert( r->hasRep( n ) ); //add representative @@ -75,7 +69,7 @@ void StrongSolverTheoryUf::ConflictFind::Region::takeNode( StrongSolverTheoryUf: r->setRep( n, false ); } -void StrongSolverTheoryUf::ConflictFind::Region::combine( StrongSolverTheoryUf::ConflictFind::Region* r ){ +void StrongSolverTheoryUf::SortRepModel::Region::combine( StrongSolverTheoryUf::SortRepModel::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 ){ @@ -107,7 +101,7 @@ void StrongSolverTheoryUf::ConflictFind::Region::combine( StrongSolverTheoryUf:: } /** setEqual */ -void StrongSolverTheoryUf::ConflictFind::Region::setEqual( Node a, Node b ){ +void StrongSolverTheoryUf::SortRepModel::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++ ){ @@ -129,7 +123,7 @@ void StrongSolverTheoryUf::ConflictFind::Region::setEqual( Node a, Node b ){ setRep( b, false ); } -void StrongSolverTheoryUf::ConflictFind::Region::setDisequal( Node n1, Node n2, int type, bool valid ){ +void StrongSolverTheoryUf::SortRepModel::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 ); @@ -155,7 +149,7 @@ void StrongSolverTheoryUf::ConflictFind::Region::setDisequal( Node n1, Node n2, } } -void StrongSolverTheoryUf::ConflictFind::Region::setRep( Node n, bool valid ){ +void StrongSolverTheoryUf::SortRepModel::Region::setRep( Node n, bool valid ){ Assert( hasRep( n )!=valid ); if( valid && d_nodes.find( n )==d_nodes.end() ){ d_nodes[n] = new RegionNodeInfo( d_cf->d_th->getSatContext() ); @@ -179,22 +173,24 @@ void StrongSolverTheoryUf::ConflictFind::Region::setRep( Node n, bool valid ){ } } -bool StrongSolverTheoryUf::ConflictFind::Region::isDisequal( Node n1, Node n2, int type ){ +bool StrongSolverTheoryUf::SortRepModel::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]; } struct sortInternalDegree { - StrongSolverTheoryUf::ConflictFind::Region* r; + StrongSolverTheoryUf::SortRepModel::Region* r; bool operator() (Node i,Node j) { return (r->d_nodes[i]->getNumInternalDisequalities()>r->d_nodes[j]->getNumInternalDisequalities());} }; struct sortExternalDegree { - StrongSolverTheoryUf::ConflictFind::Region* r; + StrongSolverTheoryUf::SortRepModel::Region* r; bool operator() (Node i,Node j) { return (r->d_nodes[i]->getNumExternalDisequalities()>r->d_nodes[j]->getNumExternalDisequalities());} }; -bool StrongSolverTheoryUf::ConflictFind::Region::getMustCombine( int cardinality ){ +int gmcCount = 0; + +bool StrongSolverTheoryUf::SortRepModel::Region::getMustCombine( int cardinality ){ if( options::ufssRegions() && 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 @@ -218,11 +214,10 @@ bool StrongSolverTheoryUf::ConflictFind::Region::getMustCombine( int cardinality } } } - //static int gmcCount = 0; - //gmcCount++; - //if( gmcCount%100==0 ){ - // std::cout << gmcCount << " " << cardinality << std::endl; - //} + gmcCount++; + if( gmcCount%100==0 ){ + Trace("gmc-count") << gmcCount << " " << cardinality << " sample : " << degrees.size() << std::endl; + } //this should happen relatively infrequently.... std::sort( degrees.begin(), degrees.end() ); for( int i=0; i<(int)degrees.size(); i++ ){ @@ -234,16 +229,21 @@ bool StrongSolverTheoryUf::ConflictFind::Region::getMustCombine( int cardinality return false; } -bool StrongSolverTheoryUf::ConflictFind::Region::check( Theory::Effort level, int cardinality, std::vector< Node >& clique ){ +bool StrongSolverTheoryUf::SortRepModel::Region::check( Theory::Effort level, int cardinality, std::vector< Node >& clique ){ if( d_reps_size>long(cardinality) ){ if( 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 ); + if( 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 ); + } } + Trace("quick-clique") << "Found quick clique" << std::endl; + return true; + }else{ + return false; } - return true; }else if( options::ufssRegions() || options::ufssEagerSplits() || level==Theory::EFFORT_FULL ){ //build test clique, up to size cardinality+1 if( d_testCliqueSize<=long(cardinality) ){ @@ -318,7 +318,7 @@ bool StrongSolverTheoryUf::ConflictFind::Region::check( Theory::Effort level, in return false; } -void StrongSolverTheoryUf::ConflictFind::Region::getRepresentatives( std::vector< Node >& reps ){ +void StrongSolverTheoryUf::SortRepModel::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 ){ @@ -327,7 +327,7 @@ void StrongSolverTheoryUf::ConflictFind::Region::getRepresentatives( std::vector } } -void StrongSolverTheoryUf::ConflictFind::Region::getNumExternalDisequalities( std::map< Node, int >& num_ext_disequalities ){ +void StrongSolverTheoryUf::SortRepModel::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 ){ @@ -341,98 +341,7 @@ void StrongSolverTheoryUf::ConflictFind::Region::getNumExternalDisequalities( st } } -Node StrongSolverTheoryUf::ConflictFind::Region::getBestSplit(){ -#ifndef USE_SMART_SPLITS - //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(); -#else - std::vector< Node > splits; - for( NodeBoolMap::iterator it = d_splits.begin(); it != d_splits.end(); ++it ){ - if( (*it).second ){ - splits.push_back( (*it).first ); - } - } - if( splits.size()>1 ){ - std::map< Node, std::map< Node, bool > > ops; - Debug("uf-ss-split") << "Choice for splits: " << std::endl; - double maxScore = -1; - int maxIndex; - for( int i=0; i<(int)splits.size(); i++ ){ - Debug("uf-ss-split") << " " << splits[i] << std::endl; - for( int j=0; j<2; j++ ){ - if( ops.find( splits[i][j] )==ops.end() ){ - EqClassIterator eqc( splits[i][j], ((uf::TheoryUF*)d_cf->d_th)->getEqualityEngine() ); - while( !eqc.isFinished() ){ - Node n = (*eqc); - if( n.getKind()==APPLY_UF ){ - ops[ splits[i][j] ][ n.getOperator() ] = true; - } - ++eqc; - } - } - } - //now, compute score - int common[2] = { 0, 0 }; - for( int j=0; j<2; j++ ){ - int j2 = j==0 ? 1 : 0; - for( std::map< Node, bool >::iterator it = ops[ splits[i][j] ].begin(); it != ops[ splits[i][j] ].end(); ++it ){ - if( ops[ splits[i][j2] ].find( it->first )!=ops[ splits[i][j2] ].end() ){ - common[0]++; - }else{ - common[1]++; - } - } - } - double score = ( 1.0 + (double)common[0] )/( 1.0 + (double)common[1] ); - if( score>maxScore ){ - maxScore = score; - maxIndex = i; - } - } - //if( maxIndex!=0 ){ - // std::cout << "Chose maxIndex = " << maxIndex << std::endl; - //} - return splits[maxIndex]; - }else if( !splits.empty() ){ - return splits[0]; - }else{ - return Node::null(); - } -#endif -} - -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 ); -} - -bool StrongSolverTheoryUf::ConflictFind::Region::minimize( OutputChannel* out ){ - if( hasSplits() ){ - addSplit( out ); - return false; - }else{ - return true; - } -} - -void StrongSolverTheoryUf::ConflictFind::Region::debugPrint( const char* c, bool incClique ){ +void StrongSolverTheoryUf::SortRepModel::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; @@ -474,238 +383,431 @@ void StrongSolverTheoryUf::ConflictFind::Region::debugPrint( const char* c, bool } } -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++; + + + + + + + +StrongSolverTheoryUf::SortRepModel::SortRepModel( Node n, context::Context* c, TheoryUF* th ) : RepModel( n.getType() ), + d_th( th ), d_regions_index( c, 0 ), d_regions_map( c ), d_split_score( c ), d_disequalities_index( c, 0 ), + d_reps( c, 0 ), d_conflict( c, false ), d_cardinality( c, 1 ), d_aloc_cardinality( 0 ), + d_cardinality_assertions( c ), d_hasCard( c, false ){ + d_cardinality_term = n; +} + +/** initialize */ +void StrongSolverTheoryUf::SortRepModel::initialize( OutputChannel* out ){ + allocateCardinality( out ); +} + +/** new node */ +void StrongSolverTheoryUf::SortRepModel::newEqClass( Node n ){ + if( !d_conflict ){ + if( d_regions_map.find( n )==d_regions_map.end() ){ + if( !options::ufssTotalityLazy() ){ + //must generate totality axioms for every cardinality we have allocated thus far + for( std::map< int, Node >::iterator it = d_cardinality_literal.begin(); it != d_cardinality_literal.end(); ++it ){ + if( applyTotality( it->first ) ){ + addTotalityAxiom( n, it->first, &d_th->getOutputChannel() ); + } + } } + if( options::ufssTotality() ){ + //regions map will store whether we need to equate this term with a constant equivalence class + if( std::find( d_totality_terms[0].begin(), d_totality_terms[0].end(), n )==d_totality_terms[0].end() ){ + d_regions_map[n] = 0; + }else{ + d_regions_map[n] = -1; + } + }else{ + if( !options::ufssRegions() ){ + //if not using regions, always add new equivalence classes to region index = 0 + d_regions_index = 0; + } + d_regions_map[n] = d_regions_index; + if( options::ufssSmartSplits() ){ + setSplitScore( n, 0 ); + } + 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( !options::ufssRegions() || d_regions[ d_regions_index ]->getNumReps()==0 ); + }else{ + d_regions.push_back( new Region( this, d_th->getSatContext() ) ); + } + d_regions[ d_regions_index ]->addRep( n ); + d_regions_index = d_regions_index + 1; + } + d_reps = d_reps + 1; } } - 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 ){ - 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 ] ]++; +/** merge */ +void StrongSolverTheoryUf::SortRepModel::merge( Node a, Node b ){ + if( !d_conflict ){ + if( options::ufssTotality() ){ + if( d_regions_map[b]==-1 ){ + d_regions_map[a] = -1; + } + d_regions_map[b] = -1; + }else{ + //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 ){ + int ri = combineRegions( bi, ai ); + d_regions[ri]->setEqual( a, b ); + checkRegion( ri ); + }else if( d_regions[bi]->getNumReps()==1 ){ + int ri = combineRegions( ai, bi ); + d_regions[ri]->setEqual( a, b ); + checkRegion( ri ); + }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_regions_map[b] = -1; } + d_reps = d_reps - 1; + Debug("uf-ss") << "Done merge." << std::endl; } } } -void StrongSolverTheoryUf::ConflictFind::explainClique( std::vector< Node >& clique, OutputChannel* out ){ - Assert( d_cardinality>0 ); - while( clique.size()>size_t(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()==(clique.size()*( clique.size()-1 )/2) ){ - break; +/** assert terms are disequal */ +void StrongSolverTheoryUf::SortRepModel::assertDisequal( Node a, Node b, Node reason ){ + if( !d_conflict ){ + if( options::ufssTotality() ){ + //do nothing + }else{ + //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; } } } - //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] ); +} + + +/** check */ +void StrongSolverTheoryUf::SortRepModel::check( Theory::Effort level, OutputChannel* out ){ + if( level>=Theory::EFFORT_STANDARD && d_hasCard && !d_conflict ){ + 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{ + if( applyTotality( d_cardinality ) ){ + //if we are applying totality to this cardinality + if( options::ufssTotalityLazy() ){ + //add totality axioms for all nodes that have not yet been equated to cardinality terms + if( level==Theory::EFFORT_FULL ){ + for( NodeIntMap::iterator it = d_regions_map.begin(); it != d_regions_map.end(); ++it ){ + if( !options::ufssTotality() || d_regions_map[ (*it).first ]!=-1 ){ + addTotalityAxiom( (*it).first, d_cardinality, &d_th->getOutputChannel() ); + } + } + } + } + }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 ) ){ + //add clique lemma + addCliqueLemma( clique, out ); + return; + }else{ + Trace("uf-ss-debug") << "No clique in Region #" << i << std::endl; + } + } + } + bool addedLemma = false; + //do splitting on demand + if( level==Theory::EFFORT_FULL || options::ufssEagerSplits() ){ + Trace("uf-ss-debug") << "Add splits?" << std::endl; + //see if we have any recommended splits from large regions + for( int i=0; i<(int)d_regions_index; i++ ){ + if( d_regions[i]->d_valid && d_regions[i]->getNumReps()>d_cardinality ){ + if( addSplit( d_regions[i], out ) ){ + addedLemma = true; +#ifdef ONE_SPLIT_REGION + break; +#endif + } + } + } + } + //if no added lemmas, force continuation via combination of regions + if( level==Theory::EFFORT_FULL ){ + if( !addedLemma ){ + Trace("uf-ss-debug") << "No splits added. " << d_cardinality << std::endl; + if( !options::ufssColoringSat() ){ + bool recheck = false; + //naive strategy, force region combination involving the first valid region + for( int i=0; i<(int)d_regions_index; i++ ){ + if( d_regions[i]->d_valid ){ + forceCombineRegion( i, false ); + recheck = true; + break; + } + } + if( recheck ){ + check( level, out ); + } } } } - 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 ); +} + +void StrongSolverTheoryUf::SortRepModel::propagate( Theory::Effort level, OutputChannel* out ){ - //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() ){ - if( !options::ufssRegions() ){ - //if not using regions, always add new equivalence classes to region index = 0 - d_regions_index = 0; - } - 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( !options::ufssRegions() || d_regions[ d_regions_index ]->getNumReps()==0 ); - }else{ - d_regions.push_back( new Region( this, d_th->getSatContext() ) ); +Node StrongSolverTheoryUf::SortRepModel::getNextDecisionRequest(){ + //request the current cardinality as a decision literal, if not already asserted + for( int i=1; i<=d_aloc_cardinality; i++ ){ + if( !d_hasCard || i<d_cardinality ){ + Node cn = d_cardinality_literal[ i ]; + Assert( !cn.isNull() ); + if( d_cardinality_assertions.find( cn )==d_cardinality_assertions.end() ){ + Trace("uf-ss-dec") << "Propagate as decision " << d_type << " " << i << std::endl; + return cn; + } } - d_regions[ d_regions_index ]->addRep( n ); - d_regions_index = d_regions_index + 1; - d_reps = d_reps + 1; } + return Node::null(); } -/** 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 ){ - int ri = combineRegions( bi, ai ); - d_regions[ri]->setEqual( a, b ); - checkRegion( ri ); - }else if( d_regions[bi]->getNumReps()==1 ){ - int ri = combineRegions( ai, bi ); - d_regions[ri]->setEqual( a, b ); - checkRegion( ri ); - }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 ); +bool StrongSolverTheoryUf::SortRepModel::minimize( OutputChannel* out, TheoryModel* m ){ + if( options::ufssTotality() ){ + //do nothing + }else{ + if( m ){ +#if 0 + // ensure that the constructed model is minimal + // if the model has terms that the strong solver does not know about + if( (int)m->d_rep_set.d_type_reps[ d_type ].size()>d_cardinality ){ + eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &m->d_equalityEngine ); + while( !eqcs_i.isFinished() ){ + Node eqc = (*eqcs_i); + if( eqc.getType()==d_type ){ + //we must ensure that this equivalence class has been accounted for + if( d_regions_map.find( eqc )==d_regions_map.end() ){ + //split on unaccounted for term and cardinality lemma term (as default) + Node splitEq = eqc.eqNode( d_cardinality_term ); + splitEq = Rewriter::rewrite( splitEq ); + Trace("uf-ss-minimize") << "Last chance minimize : " << splitEq << std::endl; + out->split( splitEq ); + //tell the sat solver to explore the equals branch first + out->requirePhase( splitEq, true ); + ++( d_th->getStrongSolver()->d_statistics.d_split_lemmas ); + return false; + } + } + ++eqcs_i; } - checkRegion( ai ); - checkRegion( bi ); + Assert( false ); } +#endif }else{ - d_regions[ai]->setEqual( a, b ); - checkRegion( ai ); + //internal minimize, ensure that model forms a clique: + // if two equivalence classes are neither equal nor disequal, add a split + 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( addSplit( d_regions[validRegionIndex], out ) ){ + return false; + } + }else{ + validRegionIndex = i; + } + } + } + if( addSplit( d_regions[validRegionIndex], out ) ){ + return false; + } } - d_reps = d_reps - 1; - d_regions_map[b] = -1; } - Debug("uf-ss") << "Done merge." << std::endl; + return true; } -/** 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 ); + +int StrongSolverTheoryUf::SortRepModel::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++; + } } - 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 ); + } + return counter; +} + +void StrongSolverTheoryUf::SortRepModel::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 ){ + 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 ] ]++; + } + } } - //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; +void StrongSolverTheoryUf::SortRepModel::setSplitScore( Node n, int s ){ + if( d_split_score.find( n )!=d_split_score.end() ){ + int ss = d_split_score[ n ]; + d_split_score[ n ] = s>ss ? s : ss; + }else{ + d_split_score[ n ] = s; + } + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + setSplitScore( n[i], s+1 ); + } +} + +void StrongSolverTheoryUf::SortRepModel::assertCardinality( OutputChannel* out, int c, bool val ){ + if( !d_conflict ){ + Trace("uf-ss-assert") << "Assert cardinality " << d_type << " " << c << " " << val << " level = " << d_th->d_valuation.getAssertionLevel() << std::endl; + Assert( d_cardinality_literal.find( c )!=d_cardinality_literal.end() ); + d_cardinality_assertions[ d_cardinality_literal[c] ] = val; + if( val ){ + bool doCheckRegions = !d_hasCard; + if( !d_hasCard || c<d_cardinality ){ + d_cardinality = c; + } + d_hasCard = true; + //should check all regions now + if( doCheckRegions ){ + for( int i=0; i<(int)d_regions_index; i++ ){ + if( d_regions[i]->d_valid ){ + checkRegion( i ); + if( d_conflict ){ + return; + } + } + } + } + }else{ + if( options::ufssModelInference() ){ + //check if we are at decision level 0 + if( d_th->d_valuation.getAssertionLevel()==0 ){ + Trace("uf-ss-mi") << "We have proved that no models of size " << c << " for type " << d_type << " exist." << std::endl; + Trace("uf-ss-mi") << " # Clique lemmas : " << d_cliques[c].size() << std::endl; + if( d_cliques[c].size()==1 ){ + if( d_totality_terms[c+1].empty() ){ + Trace("uf-ss-mi") << "*** Establish model" << std::endl; + //d_totality_terms[c+1].insert( d_totality_terms[c].begin(), d_cliques[c][0].begin(), d_cliques[c][0].end() ); + } + } + } + } + //see if we need to request a new cardinality + if( !d_hasCard ){ + bool needsCard = true; + for( std::map< int, Node >::iterator it = d_cardinality_literal.begin(); it!=d_cardinality_literal.end(); ++it ){ + if( d_cardinality_assertions.find( it->second )==d_cardinality_assertions.end() ){ + needsCard = false; + break; + } + } + if( needsCard ){ + allocateCardinality( out ); + } + } + } } } -void StrongSolverTheoryUf::ConflictFind::checkRegion( int ri, bool rec ){ - if( isValid(ri) ){ +void StrongSolverTheoryUf::SortRepModel::checkRegion( int ri, bool rec ){ + if( isValid(ri) && d_hasCard ){ 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() ); + addCliqueLemma( clique, &d_th->getOutputChannel() ); }else if( d_regions[ri]->getMustCombine( d_cardinality ) ){ ////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 ){ @@ -724,7 +826,7 @@ void StrongSolverTheoryUf::ConflictFind::checkRegion( int ri, bool rec ){ } } -int StrongSolverTheoryUf::ConflictFind::forceCombineRegion( int ri, bool useDensity ){ +int StrongSolverTheoryUf::SortRepModel::forceCombineRegion( int ri, bool useDensity ){ if( !useDensity ){ for( int i=0; i<(int)d_regions_index; i++ ){ if( ri!=i && d_regions[i]->d_valid ){ @@ -764,7 +866,7 @@ int StrongSolverTheoryUf::ConflictFind::forceCombineRegion( int ri, bool useDens } -int StrongSolverTheoryUf::ConflictFind::combineRegions( int ai, int bi ){ +int StrongSolverTheoryUf::SortRepModel::combineRegions( int ai, int bi ){ #ifdef COMBINE_REGIONS_SMALL_INTO_LARGE if( d_regions[ai]->getNumReps()<d_regions[bi]->getNumReps() ){ return combineRegions( bi, ai ); @@ -784,7 +886,7 @@ int StrongSolverTheoryUf::ConflictFind::combineRegions( int ai, int bi ){ return ai; } -void StrongSolverTheoryUf::ConflictFind::moveNode( Node n, int ri ){ +void StrongSolverTheoryUf::SortRepModel::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 ) ); @@ -793,161 +895,248 @@ void StrongSolverTheoryUf::ConflictFind::moveNode( Node n, int ri ){ d_regions_map[n] = ri; } -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 - quantifiers::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" ) << "*** Disambiguate lemma : " << lem << std::endl; - //Notice() << "*** Disambiguate lemma : " << lem << std::endl; - out->lemma( lem ); - d_term_amb[ eq ] = false; - lemmaAdded = true; - ++( d_th->getStrongSolver()->d_statistics.d_disamb_term_lemmas ); - } - } - } +void StrongSolverTheoryUf::SortRepModel::allocateCardinality( OutputChannel* out ){ + if( d_aloc_cardinality>0 ){ + Trace("uf-ss-fmf") << "No model of size " << d_aloc_cardinality << " exists for type " << d_type << " in this branch" << std::endl; + if( Trace.isOn("uf-ss-cliques") ){ + Trace("uf-ss-cliques") << "Cliques of size " << (d_aloc_cardinality+1) << " : " << std::endl; + for( size_t i=0; i<d_cliques[ d_aloc_cardinality ].size(); i++ ){ + Trace("uf-ss-cliques") << " "; + for( size_t j=0; j<d_cliques[ d_aloc_cardinality ][i].size(); j++ ){ + Trace("uf-ss-cliques") << d_cliques[ d_aloc_cardinality ][i][j] << " "; } + Trace("uf-ss-cliques") << std::endl; + } + } + } + d_aloc_cardinality++; + + //check for abort case + if( options::ufssAbortCardinality()==d_aloc_cardinality ){ + //abort here DO_THIS + Message() << "Maximum cardinality reached." << std::endl; + exit( 0 ); + }else{ + if( options::ufssTotality() ){ + //must generate new cardinality lemma term + std::stringstream ss; + ss << "_c_" << d_aloc_cardinality; + Node var = NodeManager::currentNM()->mkSkolem( ss.str(), d_type ); + d_totality_terms[0].push_back( var ); + Trace("mkVar") << "allocateCardinality, mkVar : " << var << " : " << d_type << std::endl; + //must be distinct from all other cardinality terms + for( int i=0; i<(int)(d_totality_terms[0].size()-1); i++ ){ + Node lem = NodeManager::currentNM()->mkNode( NOT, var.eqNode( d_totality_terms[0][i] ) ); + d_th->getOutputChannel().lemma( lem ); + } + } + + //add splitting lemma for cardinality constraint + Assert( !d_cardinality_term.isNull() ); + Node lem = NodeManager::currentNM()->mkNode( CARDINALITY_CONSTRAINT, d_cardinality_term, + NodeManager::currentNM()->mkConst( Rational( d_aloc_cardinality ) ) ); + lem = Rewriter::rewrite(lem); + d_cardinality_literal[ d_aloc_cardinality ] = lem; + lem = NodeManager::currentNM()->mkNode( OR, lem, lem.notNode() ); + d_cardinality_lemma[ d_aloc_cardinality ] = lem; + //add as lemma to output channel + out->lemma( lem ); + //require phase + out->requirePhase( d_cardinality_literal[ d_aloc_cardinality ], true ); + //add the appropriate lemma, propagate as decision + //Trace("uf-ss-prop-as-dec") << "Propagate as decision " << lem[0] << " " << d_type << std::endl; + //out->propagateAsDecision( lem[0] ); + d_th->getStrongSolver()->d_statistics.d_max_model_size.maxAssign( d_aloc_cardinality ); + + if( applyTotality( d_aloc_cardinality ) && !options::ufssTotalityLazy() ){ + //must send totality axioms for each existing term + for( NodeIntMap::iterator it = d_regions_map.begin(); it != d_regions_map.end(); ++it ){ + addTotalityAxiom( (*it).first, d_aloc_cardinality, &d_th->getOutputChannel() ); } } } - 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 << " "; +bool StrongSolverTheoryUf::SortRepModel::addSplit( Region* r, OutputChannel* out ){ + if( r->hasSplits() ){ + Node s; + if( !options::ufssSmartSplits() ){ + //take the first split you find + for( NodeBoolMap::iterator it = r->d_splits.begin(); it != r->d_splits.end(); ++it ){ + if( (*it).second ){ + s = (*it).first; + break; + } } - 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; + int maxScore = -1; + std::vector< Node > splits; + for( NodeBoolMap::iterator it = r->d_splits.begin(); it != r->d_splits.end(); ++it ){ + if( (*it).second ){ + int score1 = d_split_score[ (*it).first[0] ]; + int score2 = d_split_score[ (*it).first[1] ]; + int score = score1<score2 ? score1 : score2; + if( score>maxScore ){ + maxScore = -1; + s = (*it).first; } } } - bool addedLemma = false; - //do splitting on demand - if( level==Theory::EFFORT_FULL || options::ufssEagerSplits() ){ - Debug("uf-ss-debug") << "Add splits?" << std::endl; - //see if we have any recommended splits from large regions - for( int i=0; i<(int)d_regions_index; i++ ){ - if( d_regions[i]->d_valid && d_regions[i]->getNumReps()>d_cardinality ){ - if( d_regions[i]->hasSplits() ){ - d_regions[i]->addSplit( out ); - addedLemma = true; - ++( d_th->getStrongSolver()->d_statistics.d_split_lemmas ); -#ifdef ONE_SPLIT_REGION - break; -#endif - } - } - } + } + //add lemma to output channel + Assert( s!=Node::null() && s.getKind()==EQUAL ); + s = Rewriter::rewrite( s ); + Trace("uf-ss-lemma") << "*** Split on " << s << std::endl; + //Trace("uf-ss-lemma") << d_th->getEqualityEngine()->areEqual( s[0], s[1] ) << " "; + //Trace("uf-ss-lemma") << d_th->getEqualityEngine()->areDisequal( s[0], s[1] ) << std::endl; + //Trace("uf-ss-lemma") << s[0].getType() << " " << s[1].getType() << std::endl; + //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 ); + ++( d_th->getStrongSolver()->d_statistics.d_split_lemmas ); + return true; + }else{ + return false; + } +} + + +void StrongSolverTheoryUf::SortRepModel::addCliqueLemma( std::vector< Node >& clique, OutputChannel* out ){ + Assert( d_hasCard ); + Assert( d_cardinality>0 ); + while( clique.size()>size_t(d_cardinality+1) ){ + clique.pop_back(); + } + if( options::ufssModelInference() || Trace.isOn("uf-ss-cliques") ){ + std::vector< Node > clique_vec; + clique_vec.insert( clique_vec.begin(), clique.begin(), clique.end() ); + d_cliques[ d_cardinality ].push_back( clique_vec ); + } + + //found a clique + Debug("uf-ss-cliques") << "Found a clique (cardinality=" << d_cardinality << ") :" << std::endl; + Debug("uf-ss-cliques") << " "; + for( int i=0; i<(int)clique.size(); i++ ){ + Debug("uf-ss-cliques") << clique[i] << " "; + } + Debug("uf-ss-cliques") << std::endl; + Debug("uf-ss-cliques") << "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] ); + Debug("uf-ss-cliques") << " -> disequality : " << d_disequalities[i] << std::endl; + nodesWithinRep[r1][ d_disequalities[i][0][0] ] = true; + nodesWithinRep[r2][ d_disequalities[i][0][1] ] = true; + if( conflict.size()==(clique.size()*( clique.size()-1 )/2) ){ + break; } - //force continuation via term disambiguation or combination of regions - if( level==Theory::EFFORT_FULL ){ - if( !addedLemma ){ - Debug("uf-ss") << "No splits added." << std::endl; - if( options::ufssColoringSat() ){ - //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{ - bool recheck = false; - //naive strategy, force region combination involving the first valid region - for( int i=0; i<(int)d_regions_index; i++ ){ - if( d_regions[i]->d_valid ){ - forceCombineRegion( i, false ); - recheck = true; - break; - } - } - if( recheck ){ - check( level, out ); + } + } + //Debug("uf-ss-cliques") << 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-cliques") << "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 + Debug("uf-ss-cliques") << "Explain "; + for( std::map< Node, bool >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){ + if( prev!=Node::null() ){ + Debug("uf-ss-cliques") << " = "; + //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-cliques") << prev; } + Debug("uf-ss-cliques") << std::endl; } } + Debug("uf-ss-cliques") << "Explanation of clique (size=" << conflict.size() << ") = " << std::endl; + for( int i=0; i<(int)conflict.size(); i++ ){ + Debug("uf-ss-cliques") << conflict[i] << " "; + //bool value; + //bool hasValue = d_th->getValuation().hasSatValue( conflict[i], value ); + //Assert( hasValue ); + //Assert( value ); + } + Debug("uf-ss-cliques") << std::endl; + //now, make the conflict +#if 1 + conflict.push_back( d_cardinality_literal[ d_cardinality ] ); + Node conflictNode = NodeManager::currentNM()->mkNode( AND, conflict ); + Trace("uf-ss-lemma") << "*** Add clique conflict " << conflictNode << std::endl; + //Notice() << "*** Add clique conflict " << conflictNode << std::endl; + out->conflict( conflictNode ); + d_conflict = true; +#else + Node conflictNode = conflict.size()==1 ? conflict[0] : NodeManager::currentNM()->mkNode( AND, conflict ); + //add cardinality constraint + Node cardNode = d_cardinality_literal[ d_cardinality ]; + //bool value; + //bool hasValue = d_th->getValuation().hasSatValue( cardNode, value ); + //Assert( hasValue ); + //Assert( value ); + conflictNode = NodeManager::currentNM()->mkNode( IMPLIES, conflictNode, cardNode.notNode() ); + Trace("uf-ss-lemma") << "*** Add clique conflict " << conflictNode << std::endl; + //Notice() << "*** Add clique conflict " << conflictNode << std::endl; + out->lemma( conflictNode ); +#endif + ++( 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. } -void StrongSolverTheoryUf::ConflictFind::propagate( Theory::Effort level, OutputChannel* out ){ - Assert( d_cardinality>0 ); - //propagate the current cardinality as a decision literal, if not already asserted - 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::SortRepModel::addTotalityAxiom( Node n, int cardinality, OutputChannel* out ){ + Node cardLit = d_cardinality_literal[ cardinality ]; + std::vector< Node > eqs; + for( int i=0; i<cardinality; i++ ){ + eqs.push_back( n.eqNode( getTotalityLemmaTerm( cardinality, i ) ) ); } + Node ax = NodeManager::currentNM()->mkNode( OR, eqs ); + Node lem = NodeManager::currentNM()->mkNode( IMPLIES, cardLit, ax ); + Trace("uf-ss-lemma") << "*** Add totality axiom " << lem << std::endl; + //send as lemma to the output channel + d_th->getOutputChannel().lemma( lem ); + ++( d_th->getStrongSolver()->d_statistics.d_totality_lemmas ); } -void StrongSolverTheoryUf::ConflictFind::debugPrint( const char* c ){ +/** apply totality */ +bool StrongSolverTheoryUf::SortRepModel::applyTotality( int cardinality ){ + return options::ufssTotality() || ( options::ufssModelInference() && !d_totality_terms[cardinality].empty() ); +} + +/** get totality lemma terms */ +Node StrongSolverTheoryUf::SortRepModel::getTotalityLemmaTerm( int cardinality, int i ){ + if( options::ufssTotality() ){ + return d_totality_terms[0][i]; + }else{ + return d_totality_terms[cardinality][i]; + } +} + +void StrongSolverTheoryUf::SortRepModel::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; @@ -972,7 +1161,32 @@ void StrongSolverTheoryUf::ConflictFind::debugPrint( const char* c ){ } } -int StrongSolverTheoryUf::ConflictFind::getNumRegions(){ +void StrongSolverTheoryUf::SortRepModel::debugModel( TheoryModel* m ){ + std::vector< Node > eqcs; + eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &m->d_equalityEngine ); + while( !eqcs_i.isFinished() ){ + Node eqc = (*eqcs_i); + if( eqc.getType()==d_type ){ + if( std::find( eqcs.begin(), eqcs.end(), eqc )==eqcs.end() ){ + eqcs.push_back( eqc ); + //we must ensure that this equivalence class has been accounted for + if( d_regions_map.find( eqc )==d_regions_map.end() ){ + Trace("uf-ss-warn") << "WARNING : equivalence class " << eqc << " unaccounted for." << std::endl; + Trace("uf-ss-warn") << " type : " << d_type << std::endl; + Trace("uf-ss-warn") << " kind : " << eqc.getKind() << std::endl; + } + } + } + ++eqcs_i; + } + if( (int)eqcs.size()!=d_cardinality ){ + Trace("uf-ss-warn") << "WARNING : Model does not have same # representatives as cardinality for " << d_type << "." << std::endl; + Trace("uf-ss-warn") << " cardinality : " << d_cardinality << std::endl; + Trace("uf-ss-warn") << " # reps : " << (int)eqcs.size() << std::endl; + } +} + +int StrongSolverTheoryUf::SortRepModel::getNumRegions(){ int count = 0; for( int i=0; i<(int)d_regions_index; i++ ){ if( d_regions[i]->d_valid ){ @@ -982,25 +1196,7 @@ int StrongSolverTheoryUf::ConflictFind::getNumRegions(){ 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 ){ +void StrongSolverTheoryUf::SortRepModel::getRepresentatives( std::vector< Node >& reps ){ if( !options::ufssColoringSat() ){ bool foundRegion = false; for( int i=0; i<(int)d_regions_index; i++ ){ @@ -1019,87 +1215,158 @@ void StrongSolverTheoryUf::ConflictFind::getRepresentatives( std::vector< Node > } } -bool StrongSolverTheoryUf::ConflictFind::minimize( OutputChannel* out ){ - //ensure that model forms a clique: - // if two equivalence classes are neither equal nor disequal, add a split - 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; + +/** initialize */ +void StrongSolverTheoryUf::InfRepModel::initialize( OutputChannel* out ){ + +} + +/** new node */ +void StrongSolverTheoryUf::InfRepModel::newEqClass( Node n ){ + d_rep[n] = n; + //d_const_rep[n] = n.getMetaKind()==metakind::CONSTANT; +} + +/** merge */ +void StrongSolverTheoryUf::InfRepModel::merge( Node a, Node b ){ + //d_rep[b] = false; + //d_const_rep[a] = d_const_rep[a] || d_const_rep[b]; + Node repb = d_rep[b]; + Assert( !repb.isNull() ); + if( repb.getMetaKind()==metakind::CONSTANT || isBadRepresentative( d_rep[a] ) ){ + d_rep[a] = repb; + } + d_rep[b] = Node::null(); +} + +/** check */ +void StrongSolverTheoryUf::InfRepModel::check( Theory::Effort level, OutputChannel* out ){ + +} + +/** minimize */ +bool StrongSolverTheoryUf::InfRepModel::minimize( OutputChannel* out ){ +#if 0 + bool retVal = true; +#else + bool retVal = !addSplit( out ); +#endif + if( retVal ){ + std::vector< Node > reps; + getRepresentatives( reps ); + Trace("uf-ss-fmf") << "Num representatives of type " << d_type << " : " << reps.size() << std::endl; + /* + for( int i=0; i<(int)reps.size(); i++ ){ + std::cout << reps[i] << " "; + } + std::cout << std::endl; + for( int i=0; i<(int)reps.size(); i++ ){ + std::cout << reps[i].getMetaKind() << " "; + } + std::cout << std::endl; + for( NodeNodeMap::iterator it = d_rep.begin(); it != d_rep.end(); ++it ){ + Node rep = (*it).second; + if( !rep.isNull() && !isBadRepresentative( rep ) ){ + for( NodeNodeMap::iterator it2 = d_rep.begin(); it2 != d_rep.end(); ++it2 ){ + Node rep2 = (*it2).second; + if( !rep2.isNull() && !isBadRepresentative( rep2 ) ){ + if( d_th->getQuantifiersEngine()->getEqualityQuery()->areDisequal( rep, rep2 ) ){ + std::cout << "1 "; + }else{ + std::cout << "0 "; + } + } } - }else{ - validRegionIndex = i; + //std::cout << " : " << rep; + std::cout << std::endl; } } + */ } - if( !d_regions[validRegionIndex]->minimize( out ) ){ - return false; + return retVal; +} + +/** get representatives */ +void StrongSolverTheoryUf::InfRepModel::getRepresentatives( std::vector< Node >& reps ){ + for( NodeNodeMap::iterator it = d_rep.begin(); it != d_rep.end(); ++it ){ + if( !(*it).second.isNull() ){ + reps.push_back( (*it).first ); + } } - 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 << Expr::setlanguage(options::outputLanguage()); - ss << "t_" << d_type; - d_cardinality_lemma_term = NodeManager::currentNM()->mkSkolem( ss.str(), d_type ); +/** add split function */ +bool StrongSolverTheoryUf::InfRepModel::addSplit( OutputChannel* out ){ + std::vector< Node > visited; + for( NodeNodeMap::iterator it = d_rep.begin(); it != d_rep.end(); ++it ){ + Node rep = (*it).second; + if( !rep.isNull() && !isBadRepresentative( rep ) ){ + bool constRep = rep.getMetaKind()==metakind::CONSTANT; + for( size_t i=0; i<visited.size(); i++ ){ + if( !constRep || !visited[i].getMetaKind()==metakind::CONSTANT ){ + if( !d_th->getQuantifiersEngine()->getEqualityQuery()->areDisequal( rep, visited[i] ) ){ + //split on these nodes + Node eq = rep.eqNode( visited[i] ); + Trace("uf-ss-lemma") << "*** Split on " << eq << std::endl; + eq = Rewriter::rewrite( eq ); + Debug("uf-ss-lemma-debug") << "Rewritten " << eq << std::endl; + out->split( eq ); + //explore the equals branch first + out->requirePhase( eq, true ); + ++( d_th->getStrongSolver()->d_statistics.d_split_lemmas ); + return true; + } + } + } + visited.push_back( rep ); } - 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 ]; + return false; +} + +bool StrongSolverTheoryUf::InfRepModel::isBadRepresentative( Node n ){ + return n.getKind()==kind::PLUS; } StrongSolverTheoryUf::StrongSolverTheoryUf(context::Context* c, context::UserContext* u, OutputChannel& out, TheoryUF* th) : d_out( &out ), d_th( th ), -d_conf_find(), +d_conflict( c, false ), +d_rep_model(), d_conf_types(), -d_conf_find_init( c ) +d_rep_model_init( c ) { - + if( options::ufssColoringSat() ){ + d_term_amb = new TermDisambiguator( th->getQuantifiersEngine(), c ); + }else{ + d_term_amb = NULL; + } } /** new node */ void StrongSolverTheoryUf::newEqClass( Node n ){ - TypeNode tn = n.getType(); - ConflictFind* c = getConflictFind( tn ); + RepModel* c = getRepModel( n ); if( c ){ - Debug("uf-ss-solver") << "StrongSolverTheoryUf: New eq class " << n << " " << tn << std::endl; + Trace("uf-ss-solver") << "StrongSolverTheoryUf: New eq class " << n << " : " << n.getType() << std::endl; c->newEqClass( n ); } - //else if( tn.isSort() ){ - // //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 ); + RepModel* c = getRepModel( a ); if( c ){ - Debug("uf-ss-solver") << "StrongSolverTheoryUf: Merge " << a << " " << b << " " << tn << std::endl; + Trace("uf-ss-solver") << "StrongSolverTheoryUf: Merge " << a << " " << b << " : " << a.getType() << std::endl; c->merge( a, b ); } } /** assert terms are disequal */ void StrongSolverTheoryUf::assertDisequal( Node a, Node b, Node reason ){ - TypeNode tn = a.getType(); - ConflictFind* c = getConflictFind( tn ); + RepModel* c = getRepModel( a ); if( c ){ - Debug("uf-ss-solver") << "StrongSolverTheoryUf: Assert disequal " << a << " " << b << " " << tn << std::endl; + Trace("uf-ss-solver") << "StrongSolverTheoryUf: Assert disequal " << a << " " << b << " : " << a.getType() << std::endl; //Assert( d_th->d_equalityEngine.getRepresentative( a )==a ); //Assert( d_th->d_equalityEngine.getRepresentative( b )==b ); c->assertDisequal( a, b, reason ); @@ -1108,88 +1375,89 @@ void StrongSolverTheoryUf::assertDisequal( Node a, Node b, Node reason ){ /** assert a node */ void StrongSolverTheoryUf::assertNode( Node n, bool isDecision ){ - Debug("uf-ss-assert") << "Assert " << n << " " << isDecision << std::endl; + Trace("uf-ss") << "Assert " << n << " " << isDecision << std::endl; if( n.getKind()==CARDINALITY_CONSTRAINT ){ TypeNode tn = n[0].getType(); - Assert( d_conf_find[tn]->getCardinality()>0 ); Assert( tn.isSort() ); - Assert( d_conf_find[tn] ); + Assert( d_rep_model[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; - } + d_rep_model[tn]->assertCardinality( d_out, nCard, true ); }else if( n.getKind()==NOT && n[0].getKind()==CARDINALITY_CONSTRAINT ){ - //must add new lemma Node nn = n[0]; TypeNode tn = nn[0].getType(); Assert( tn.isSort() ); - Assert( d_conf_find[tn] ); + Assert( d_rep_model[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 ); - } + d_rep_model[tn]->assertCardinality( d_out, nCard, false ); }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(); - // } - // } - //} + if( isDecision ){ + for( std::map< TypeNode, RepModel* >::iterator it = d_rep_model.begin(); it != d_rep_model.end(); ++it ){ + if( !it->second->hasCardinalityAsserted() ){ + Trace("uf-ss-warn") << "WARNING: Assert " << n << " as a decision before cardinality for " << it->first << "." << std::endl; + //Message() << "Error: constraint asserted before cardinality for " << it->first << std::endl; + //Unimplemented(); + } + } + } } + Trace("uf-ss") << "Assert: done " << n << " " << isDecision << std::endl; } /** 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 ); + if( !d_conflict ){ + Trace("uf-ss-solver") << "StrongSolverTheoryUf: check " << level << std::endl; + if( level==Theory::EFFORT_FULL ){ + debugPrint( "uf-ss-debug" ); + } + for( std::map< TypeNode, RepModel* >::iterator it = d_rep_model.begin(); it != d_rep_model.end(); ++it ){ + it->second->check( level, d_out ); + if( it->second->isConflict() ){ + d_conflict = true; + break; + } + } + //disambiguate terms if necessary + if( !d_conflict && level==Theory::EFFORT_FULL && options::ufssColoringSat() ){ + Assert( d_term_amb!=NULL ); + d_statistics.d_disamb_term_lemmas += d_term_amb->disambiguateTerms( d_out ); + } + Trace("uf-ss-solver") << "Done StrongSolverTheoryUf: check " << level << std::endl; } - 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 ); - } + //for( std::map< TypeNode, RepModel* >::iterator it = d_rep_model.begin(); it != d_rep_model.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( tn.isSort() ){ - preRegisterType( tn ); +/** get next decision request */ +Node StrongSolverTheoryUf::getNextDecisionRequest(){ + for( std::map< TypeNode, RepModel* >::iterator it = d_rep_model.begin(); it != d_rep_model.end(); ++it ){ + Node n = it->second->getNextDecisionRequest(); + if( !n.isNull() ){ + return n; + } } + return Node::null(); } -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(); +void StrongSolverTheoryUf::preRegisterTerm( TNode n ){ + //shouldn't have to preregister this type (it may be that there are no quantifiers over tn) + TypeNode tn = n.getType(); + if( d_rep_model.find( tn )==d_rep_model.end() ){ + RepModel* rm = NULL; if( tn.isSort() ){ - preRegisterType( tn ); + Debug("uf-ss-register") << "Preregister sort " << tn << "." << std::endl; + rm = new SortRepModel( n, d_th->getSatContext(), d_th ); + }else if( tn.isInteger() ){ + //rm = new InfRepModel( tn, d_th->getSatContext(), d_th ); + //rm = new SortRepModel( tn, d_th->getSatContext(), d_th ); }else{ /* if( tn==NodeManager::currentNM()->integerType() || tn==NodeManager::currentNM()->realType() ){ @@ -1205,63 +1473,51 @@ void StrongSolverTheoryUf::registerQuantifier( Node f ){ } */ } + if( rm ){ + rm->initialize( d_out ); + d_rep_model[tn] = rm; + d_rep_model_init[tn] = true; + } } } -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 ); - } +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(); + // preRegisterType( tn, true ); + //} } -StrongSolverTheoryUf::ConflictFind* StrongSolverTheoryUf::getConflictFind( TypeNode tn ){ - std::map< TypeNode, ConflictFind* >::iterator it = d_conf_find.find( tn ); + +StrongSolverTheoryUf::RepModel* StrongSolverTheoryUf::getRepModel( Node n ){ + TypeNode tn = n.getType(); + std::map< TypeNode, RepModel* >::iterator it = d_rep_model.find( tn ); //pre-register the type if not done already - if( it==d_conf_find.end() ){ - if( tn.isSort() ){ - preRegisterType( tn ); - it = d_conf_find.find( tn ); - } + if( it==d_rep_model.end() ){ + preRegisterTerm( n ); + it = d_rep_model.find( tn ); } - if( it!=d_conf_find.end() ){ + if( it!=d_rep_model.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; - } + //if( d_rep_model_init.find( tn )==d_rep_model_init.end() ){ + ////initialize the model + //it->second->initialize( d_out ); + //d_rep_model_init[tn] = true; + //} return it->second; - }else{ - return NULL; } + 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 ); +int StrongSolverTheoryUf::getCardinality( Node n ) { + RepModel* c = getRepModel( n ); if( c ){ return c->getCardinality(); }else{ @@ -1269,28 +1525,22 @@ int StrongSolverTheoryUf::getCardinality( TypeNode t ) { } } -void StrongSolverTheoryUf::getRepresentatives( TypeNode t, std::vector< Node >& reps ){ - ConflictFind* c = getConflictFind( t ); +void StrongSolverTheoryUf::getRepresentatives( Node n, std::vector< Node >& reps ){ + RepModel* c = getRepModel( n ); 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 ) ){ +bool StrongSolverTheoryUf::minimize( TheoryModel* m ){ + for( std::map< TypeNode, RepModel* >::iterator it = d_rep_model.begin(); it != d_rep_model.end(); ++it ){ + if( !it->second->minimize( d_out, m ) ){ return false; } } + for( std::map< TypeNode, RepModel* >::iterator it = d_rep_model.begin(); it != d_rep_model.end(); ++it ){ + Trace("uf-ss-minimize") << "Cardinality( " << it->first << " ) : " << it->second->getCardinality() << std::endl; + } return true; } @@ -1309,22 +1559,32 @@ void StrongSolverTheoryUf::debugPrint( const char* c ){ // eqc_iter++; //} - for( std::map< TypeNode, ConflictFind* >::iterator it = d_conf_find.begin(); it != d_conf_find.end(); ++it ){ + for( std::map< TypeNode, RepModel* >::iterator it = d_rep_model.begin(); it != d_rep_model.end(); ++it ){ Debug( c ) << "Conflict find structure for " << it->first << ": " << std::endl; it->second->debugPrint( c ); Debug( c ) << std::endl; } } +void StrongSolverTheoryUf::debugModel( TheoryModel* m ){ + if( Trace.isOn("uf-ss-warn") ){ + for( std::map< TypeNode, RepModel* >::iterator it = d_rep_model.begin(); it != d_rep_model.end(); ++it ){ + it->second->debugModel( m ); + } + } +} + 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) + d_totality_lemmas("StrongSolverTheoryUf::Totality_Lemmas", 0), + d_max_model_size("StrongSolverTheoryUf::Max_Model_Size", 1) { StatisticsRegistry::registerStat(&d_clique_lemmas); StatisticsRegistry::registerStat(&d_split_lemmas); StatisticsRegistry::registerStat(&d_disamb_term_lemmas); + StatisticsRegistry::registerStat(&d_totality_lemmas); StatisticsRegistry::registerStat(&d_max_model_size); } @@ -1332,10 +1592,70 @@ StrongSolverTheoryUf::Statistics::~Statistics(){ StatisticsRegistry::unregisterStat(&d_clique_lemmas); StatisticsRegistry::unregisterStat(&d_split_lemmas); StatisticsRegistry::unregisterStat(&d_disamb_term_lemmas); + StatisticsRegistry::unregisterStat(&d_totality_lemmas); StatisticsRegistry::unregisterStat(&d_max_model_size); } -bool StrongSolverTheoryUf::involvesRelevantType( Node n ){ + +int TermDisambiguator::disambiguateTerms( OutputChannel* out ){ + Debug("uf-ss-disamb") << "Disambiguate terms." << std::endl; + int lemmaAdded = 0; + //otherwise, determine ambiguous pairs of ground terms for relevant sorts + quantifiers::TermDb* db = d_qe->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(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_qe->getEqualityQuery()->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_qe->getEqualityQuery()->areDisequal( it->second[i][k], it->second[j][k] ) ){ + 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" ) << "*** Disambiguate lemma : " << lem << std::endl; + //Notice() << "*** Disambiguate lemma : " << lem << std::endl; + out->lemma( lem ); + d_term_amb[ eq ] = false; + lemmaAdded++; + } + } + } + } + } + } + } + Debug("uf-ss-disamb") << "Done disambiguate terms. " << lemmaAdded << std::endl; + return lemmaAdded; +} + +bool TermDisambiguator::involvesRelevantType( Node n ){ if( n.getKind()==APPLY_UF ){ for( int i=0; i<(int)n.getNumChildren(); i++ ){ if( n[i].getType().isSort() ){ diff --git a/src/theory/uf/theory_uf_strong_solver.h b/src/theory/uf/theory_uf_strong_solver.h index 479fea05f..8c63b4308 100644 --- a/src/theory/uf/theory_uf_strong_solver.h +++ b/src/theory/uf/theory_uf_strong_solver.h @@ -32,24 +32,63 @@ namespace theory { namespace uf { class TheoryUF; +class TermDisambiguator; class StrongSolverTheoryUf{ protected: typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap; typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap; + typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap; typedef context::CDChunkList<Node> NodeList; typedef context::CDList<bool> BoolList; typedef context::CDList<bool> IntList; typedef context::CDHashMap<TypeNode, bool, TypeNodeHashFunction> TypeNodeBoolMap; public: + class RepModel { + protected: + /** type */ + TypeNode d_type; + public: + RepModel( TypeNode tn ) : d_type( tn ){} + virtual ~RepModel(){} + /** initialize */ + virtual void initialize( OutputChannel* out ) = 0; + /** new node */ + virtual void newEqClass( Node n ) = 0; + /** merge */ + virtual void merge( Node a, Node b ) = 0; + /** assert terms are disequal */ + virtual void assertDisequal( Node a, Node b, Node reason ) = 0; + /** check */ + virtual void check( Theory::Effort level, OutputChannel* out ){} + /** get next decision request */ + virtual Node getNextDecisionRequest() { return Node::null(); } + /** minimize */ + virtual bool minimize( OutputChannel* out, TheoryModel* m ){ return true; } + /** assert cardinality */ + virtual void assertCardinality( OutputChannel* out, int c, bool val ){} + /** is in conflict */ + virtual bool isConflict() { return false; } + /** get cardinality */ + virtual int getCardinality() { return -1; } + /** has cardinality */ + virtual bool hasCardinalityAsserted() { return true; } + /** get representatives */ + virtual void getRepresentatives( std::vector< Node >& reps ){} + /** print debug */ + virtual void debugPrint( const char* c ){} + /** debug a model */ + virtual void debugModel( TheoryModel* m ){} + }; +public: /** information for incremental conflict/clique finding for a particular sort */ - class ConflictFind { + class SortRepModel : public RepModel { public: /** a partition of the current equality graph for which cliques can occur internally */ class Region { public: /** conflict find pointer */ - ConflictFind* d_cf; + SortRepModel* d_cf; /** information stored about each node in region */ class RegionNodeInfo { public: @@ -86,14 +125,13 @@ public: }; ///** end class RegionNodeInfo */ private: + context::CDO< unsigned > d_testCliqueSize; + context::CDO< unsigned > d_splitsSize; + public: //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; @@ -106,11 +144,11 @@ public: void setRep( Node n, bool valid ); 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( SortRepModel* cf, context::Context* c ) : d_cf( cf ), d_testCliqueSize( c, 0 ), + d_splitsSize( c, 0 ), d_testClique( c ), d_splits( c ), d_reps_size( c, 0 ), + d_total_diseq_external( c, 0 ), d_total_diseq_internal( c, 0 ), d_valid( c, true ) { } - ~Region(){} + virtual ~Region(){} //region node infomation std::map< Node, RegionNodeInfo* > d_nodes; //whether region is valid @@ -146,10 +184,6 @@ public: public: /** check for cliques */ bool check( Theory::Effort level, int cardinality, std::vector< Node >& clique ); - /** add split */ - void addSplit( OutputChannel* out ); - /** minimize */ - bool minimize( OutputChannel* out ); //print debug void debugPrint( const char* c, bool incClique = false ); }; @@ -162,25 +196,23 @@ public: std::vector< Region* > d_regions; /** map from Nodes to index of d_regions they exist in, -1 means invalid */ NodeIntMap d_regions_map; + /** the score for each node for splitting */ + NodeIntMap d_split_score; /** 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: /** 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 ); - /** 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 ); + /** set split score */ + void setSplitScore( Node n, int s ); private: /** check if we need to combine region ri */ void checkRegion( int ri, bool rec = true ); @@ -191,78 +223,131 @@ public: /** move node n to region ri */ void moveNode( Node n, int ri ); private: - /** cardinality operating with */ + /** allocate cardinality */ + void allocateCardinality( OutputChannel* out ); + /** add split */ + bool addSplit( Region* r, OutputChannel* out ); + /** add clique lemma */ + void addCliqueLemma( std::vector< Node >& clique, OutputChannel* out ); + /** add totality axiom */ + void addTotalityAxiom( Node n, int cardinality, OutputChannel* out ); + private: + /** Are we in conflict */ + context::CDO<bool> d_conflict; + /** cardinality */ context::CDO< int > d_cardinality; - /** type */ - TypeNode d_type; + /** maximum allocated cardinality */ + int d_aloc_cardinality; /** cardinality lemma term */ - Node d_cardinality_lemma_term; + Node d_cardinality_term; + /** cardinality totality terms */ + std::map< int, std::vector< Node > > d_totality_terms; /** 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; + /** whether a positive cardinality constraint has been asserted */ + context::CDO< bool > d_hasCard; + /** clique lemmas that have been asserted */ + std::map< int, std::vector< std::vector< Node > > > d_cliques; + private: + /** apply totality */ + bool applyTotality( int cardinality ); + /** get totality lemma terms */ + Node getTotalityLemmaTerm( int cardinality, int i ); 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(){} + SortRepModel( Node n, context::Context* c, TheoryUF* th ); + virtual ~SortRepModel(){} + /** initialize */ + void initialize( OutputChannel* out ); /** 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 next decision request */ + Node getNextDecisionRequest(); + /** minimize */ + bool minimize( OutputChannel* out, TheoryModel* m ); + /** assert cardinality */ + void assertCardinality( OutputChannel* out, int c, bool val ); + /** is in conflict */ + bool isConflict() { return d_conflict; } /** 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(); + /** has cardinality */ + bool hasCardinalityAsserted() { return d_hasCard; } + //print debug + void debugPrint( const char* c ); + /** debug a model */ + void debugModel( TheoryModel* m ); 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 */ + }; /** class SortRepModel */ +private: + /** infinite rep model */ + class InfRepModel : public RepModel + { + protected: + /** theory uf pointer */ + TheoryUF* d_th; + /** list of representatives */ + NodeNodeMap d_rep; + /** whether representatives are constant */ + NodeBoolMap d_const_rep; + /** add split */ + bool addSplit( OutputChannel* out ); + /** is bad representative */ + bool isBadRepresentative( Node n ); + public: + InfRepModel( TypeNode tn, context::Context* c, TheoryUF* th ) : RepModel( tn ), + d_th( th ), d_rep( c ), d_const_rep( c ){} + virtual ~InfRepModel(){} + /** initialize */ + void initialize( OutputChannel* out ); + /** 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 ){} + /** check */ + void check( Theory::Effort level, OutputChannel* out ); + /** minimize */ + bool minimize( OutputChannel* out ); + /** get representatives */ + void getRepresentatives( std::vector< Node >& reps ); + /** print debug */ + void debugPrint( const char* c ){} + }; 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; + /** Are we in conflict */ + context::CDO<bool> d_conflict; + /** rep model structure, one for each type */ + std::map< TypeNode, RepModel* > d_rep_model; /** 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 ); + TypeNodeBoolMap d_rep_model_init; /** get conflict find */ - ConflictFind* getConflictFind( TypeNode tn ); + RepModel* getRepModel( Node n ); +private: + /** term disambiguator */ + TermDisambiguator* d_term_amb; public: StrongSolverTheoryUf(context::Context* c, context::UserContext* u, OutputChannel& out, TheoryUF* th); ~StrongSolverTheoryUf() {} @@ -279,6 +364,8 @@ public: void check( Theory::Effort level ); /** propagate */ void propagate( Theory::Effort level ); + /** get next decision request */ + Node getNextDecisionRequest(); /** preregister a term */ void preRegisterTerm( TNode n ); /** preregister a quantifier */ @@ -290,35 +377,52 @@ public: std::string identify() const { return std::string("StrongSolverTheoryUf"); } //print debug void debugPrint( const char* c ); + /** debug a model */ + void debugModel( TheoryModel* m ); 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 is in conflict */ + bool isConflict() { return d_conflict; } /** get cardinality for sort */ - int getCardinality( TypeNode t ); + int getCardinality( Node n ); /** get representatives */ - void getRepresentatives( TypeNode t, std::vector< Node >& reps ); - /** get cardinality term */ - //Node getCardinalityTerm( TypeNode t ); + void getRepresentatives( Node n, std::vector< Node >& reps ); /** minimize */ - bool minimize(); + bool minimize( TheoryModel* m = NULL ); class Statistics { public: IntStat d_clique_lemmas; IntStat d_split_lemmas; IntStat d_disamb_term_lemmas; + IntStat d_totality_lemmas; IntStat d_max_model_size; Statistics(); ~Statistics(); }; /** statistics class */ Statistics d_statistics; +};/* class StrongSolverTheoryUf */ + +class TermDisambiguator +{ +private: + /** quantifiers engine */ + QuantifiersEngine* d_qe; + /** whether two terms are ambiguous (indexed by equalities) */ + context::CDHashMap<Node, bool, NodeHashFunction> d_term_amb; /** involves relevant type */ static bool involvesRelevantType( Node n ); -};/* class StrongSolverTheoryUf */ +public: + TermDisambiguator( QuantifiersEngine* qe, context::Context* c ) : d_qe( qe ), d_term_amb( c ){} + ~TermDisambiguator(){} + /** check ambiguous terms */ + int disambiguateTerms( OutputChannel* out ); +}; } }/* CVC4::theory namespace */ diff --git a/src/theory/uf/theory_uf_type_rules.h b/src/theory/uf/theory_uf_type_rules.h index d00b69398..09f287884 100644 --- a/src/theory/uf/theory_uf_type_rules.h +++ b/src/theory/uf/theory_uf_type_rules.h @@ -72,68 +72,6 @@ public: } };/* class CardinalityConstraintTypeRule */ -class FunctionModelTypeRule { -public: - inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) - throw(TypeCheckingExceptionPrivate) { - TypeNode tn = n[0].getType(check); - if( check ){ - if( n.getNumChildren()==2 ){ - if( n[0].getKind()!=kind::FUNCTION_CASE_SPLIT ){ - throw TypeCheckingExceptionPrivate(n, "improper function model representation : first child must be case split"); - } - TypeNode tn2 = n[1].getType(check); - if( tn!=tn2 ){ - std::stringstream ss; - ss << "function model has inconsistent return types : " << tn << " " << tn2; - throw TypeCheckingExceptionPrivate(n, ss.str()); - } - } - } - return tn; - } -};/* class FunctionModelTypeRule */ - -class FunctionCaseSplitTypeRule { -public: - inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) - throw(TypeCheckingExceptionPrivate) { - TypeNode retType = n[0][1].getType(check); - if( check ){ - TypeNode argType = n[0][0].getType(check); - for( size_t i=0; i<n.getNumChildren(); i++ ){ - TypeNode argType2 = n[i][0].getType(check); - if( argType!=argType2 ){ - std::stringstream ss; - ss << "function case split has inconsistent argument types : " << argType << " " << argType2; - throw TypeCheckingExceptionPrivate(n, ss.str()); - } - TypeNode retType2 = n[i][1].getType(check); - if( retType!=retType2 ){ - std::stringstream ss; - ss << "function case split has inconsistent return types : " << retType << " " << retType2; - throw TypeCheckingExceptionPrivate(n, ss.str()); - } - } - } - return retType; - } -};/* class FunctionCaseSplitTypeRule */ - - -class FunctionCaseTypeRule { -public: - inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) - throw(TypeCheckingExceptionPrivate) { - TypeNode retType = n[1].getType(check); - if( check ){ - TypeNode argType = n[0].getType(check); - } - return retType; - } -};/* class FunctionCaseTypeRule */ - - }/* CVC4::theory::uf namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/valuation.cpp b/src/theory/valuation.cpp index 948a7a130..d4c7a299e 100644 --- a/src/theory/valuation.cpp +++ b/src/theory/valuation.cpp @@ -99,5 +99,9 @@ bool Valuation::isDecision(Node lit) const { return d_engine->getPropEngine()->isDecision(lit); } +unsigned Valuation::getAssertionLevel() const{ + return d_engine->getPropEngine()->getAssertionLevel(); +} + }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/valuation.h b/src/theory/valuation.h index 7f3a00ec1..083163a5c 100644 --- a/src/theory/valuation.h +++ b/src/theory/valuation.h @@ -117,6 +117,11 @@ public: */ bool isDecision(Node lit) const; + /** + * Get the assertion level of the SAT solver. + */ + unsigned getAssertionLevel() const; + };/* class Valuation */ }/* CVC4::theory namespace */ |