From: Andrew Reynolds Date: Fri, 31 Aug 2012 16:48:20 +0000 (+0000) Subject: merge from fmf-devel branch. more updates to models: now with collectModelInfo with... X-Git-Tag: cvc5-1.0.0~7831 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=3c4935c7c0c6774588af94c82307a960e58a1154;p=cvc5.git merge from fmf-devel branch. more updates to models: now with collectModelInfo with fullModel argument, most theory-specific implementation out of the model class, model printer relegated to printer classes. Also updates to finite mode finding, modifications to datatypes making them compatible with theory combination, support for theory-specific handling of user attributes, refactoring of uf models --- diff --git a/src/expr/command.cpp b/src/expr/command.cpp index f93df3722..648c64388 100644 --- a/src/expr/command.cpp +++ b/src/expr/command.cpp @@ -462,13 +462,17 @@ DeclareFunctionCommand::DeclareFunctionCommand(const std::string& id, Expr func, d_type(t) { } +Expr DeclareFunctionCommand::getFunction() const throw() { + return d_func; +} + Type DeclareFunctionCommand::getType() const throw() { return d_type; } void DeclareFunctionCommand::invoke(SmtEngine* smtEngine) throw() { Dump("declarations") << *this; - smtEngine->addToModelFunction( d_func ); + smtEngine->addToModelCommand( clone(), Model::COMMAND_DECLARE_FUN ); d_commandStatus = CommandSuccess::instance(); } @@ -500,7 +504,7 @@ Type DeclareTypeCommand::getType() const throw() { void DeclareTypeCommand::invoke(SmtEngine* smtEngine) throw() { Dump("declarations") << *this; - smtEngine->addToModelType( d_type ); + smtEngine->addToModelCommand( clone(), Model::COMMAND_DECLARE_SORT ); d_commandStatus = CommandSuccess::instance(); } @@ -644,6 +648,43 @@ Command* DefineNamedFunctionCommand::clone() const { return new DefineNamedFunctionCommand(d_symbol, d_func, d_formals, d_formula); } +/* class SetUserAttribute */ + +SetUserAttributeCommand::SetUserAttributeCommand( const std::string& attr, Expr expr ) throw() : + d_attr( attr ), d_expr( expr ){ +} +/* +SetUserAttributeCommand::SetUserAttributeCommand( const std::string& id, Expr expr, + std::vector& values ) throw() : + d_id( id ), d_expr( expr ){ + d_expr_values.insert( d_expr_values.begin(), values.begin(), values.end() ); +} + +SetUserAttributeCommand::SetUserAttributeCommand( const std::string& id, Expr expr, + std::string& value ) throw() : + d_id( id ), d_expr( expr ), d_str_value( value ){ +} +*/ +void SetUserAttributeCommand::invoke(SmtEngine* smtEngine) throw(){ + try { + if(!d_expr.isNull()) { + smtEngine->setUserAttribute( d_attr, d_expr ); + } + d_commandStatus = CommandSuccess::instance(); + } catch(exception& e) { + d_commandStatus = new CommandFailure(e.what()); + } +} + +Command* SetUserAttributeCommand::exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap){ + Expr expr = d_expr.exportTo(exprManager, variableMap); + return new SetUserAttributeCommand( d_attr, expr ); +} + +Command* SetUserAttributeCommand::clone() const{ + return new SetUserAttributeCommand( d_attr, d_expr ); +} + /* class Simplify */ SimplifyCommand::SimplifyCommand(Expr term) throw() : @@ -1095,7 +1136,7 @@ void GetOptionCommand::invoke(SmtEngine* smtEngine) throw() { v.push_back(SExpr(SExpr::Keyword(string(":") + d_flag))); v.push_back(smtEngine->getOption(d_flag)); stringstream ss; - + ss << SExpr(v); d_result = ss.str(); d_commandStatus = CommandSuccess::instance(); @@ -1148,6 +1189,7 @@ DatatypeDeclarationCommand::getDatatypes() const throw() { void DatatypeDeclarationCommand::invoke(SmtEngine* smtEngine) throw() { Dump("declarations") << *this; + smtEngine->addToModelCommand( clone(), Model::COMMAND_DECLARE_DATATYPES ); d_commandStatus = CommandSuccess::instance(); } diff --git a/src/expr/command.h b/src/expr/command.h index 2c56e60d9..4657755e7 100644 --- a/src/expr/command.h +++ b/src/expr/command.h @@ -322,6 +322,7 @@ protected: public: DeclareFunctionCommand(const std::string& id, Expr func, Type type) throw(); ~DeclareFunctionCommand() throw() {} + Expr getFunction() const throw(); Type getType() const throw(); void invoke(SmtEngine* smtEngine) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); @@ -389,6 +390,27 @@ public: Command* clone() const; };/* class DefineNamedFunctionCommand */ +/** + * The command when an attribute is set by a user. In SMT-LIBv2 this is done + * via the syntax (! expr :atrr) + */ +class CVC4_PUBLIC SetUserAttributeCommand : public Command { +protected: + std::string d_attr; + Expr d_expr; + //std::vector d_expr_values; + //std::string d_str_value; +public: + SetUserAttributeCommand( const std::string& attr, Expr expr ) throw(); + //SetUserAttributeCommand( const std::string& id, Expr expr, std::vector& values ) throw(); + //SetUserAttributeCommand( const std::string& id, Expr expr, std::string& value ) throw(); + ~SetUserAttributeCommand() throw() {} + void invoke(SmtEngine* smtEngine) throw(); + Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); + Command* clone() const; +};/* class SetUserAttributeCommand */ + + class CVC4_PUBLIC CheckSatCommand : public Command { protected: BoolExpr d_expr; diff --git a/src/parser/cvc/Cvc.g b/src/parser/cvc/Cvc.g index 3c4a51ad4..82e27401e 100644 --- a/src/parser/cvc/Cvc.g +++ b/src/parser/cvc/Cvc.g @@ -747,9 +747,9 @@ mainCommand[CVC4::Command*& cmd] { cmd = new GetAssertionsCommand(); } | COUNTEREXAMPLE_TOK - { UNSUPPORTED("COUNTEREXAMPLE command"); } + { cmd = new GetModelCommand; } | COUNTERMODEL_TOK - { UNSUPPORTED("COUNTERMODEL command"); } + { cmd = new GetModelCommand; } | ARITH_VAR_ORDER_TOK LPAREN formula[f] ( COMMA formula[f] )* RPAREN { UNSUPPORTED("ARITH_VAR_ORDER command"); } diff --git a/src/parser/smt2/Smt2.g b/src/parser/smt2/Smt2.g index d8157acbc..867250c0f 100644 --- a/src/parser/smt2/Smt2.g +++ b/src/parser/smt2/Smt2.g @@ -705,8 +705,6 @@ term[CVC4::Expr& expr, CVC4::Expr& expr2] ( attribute[expr, attexpr,attr] { if( attr == ":pattern" && ! attexpr.isNull()) { patexprs.push_back( attexpr ); - }else if( attr==":axiom" ){ - //do this? } } )+ RPAREN_TOK @@ -788,18 +786,15 @@ attribute[CVC4::Expr& expr,CVC4::Expr& retExpr, std::string& attr] Expr e2; } : KEYWORD - { attr = AntlrInput::tokenText($KEYWORD); } - symbolicExpr[sexpr] - { if(attr == ":named") { - std::string name = sexpr.getValue(); - // FIXME ensure expr is a closed subterm - // check that sexpr is a fresh function symbol - PARSER_STATE->checkDeclaration(name, CHECK_UNDECLARED, SYM_VARIABLE); - // define it - Expr func = PARSER_STATE->mkFunction(name, expr.getType()); - // bind name to expr with define-fun - Command* c = - new DefineNamedFunctionCommand(name, func, std::vector(), expr); + { + attr = AntlrInput::tokenText($KEYWORD); + //EXPR_MANAGER->setNamedAttribute( expr, attr ); + if( attr==":rewrite-rule" ){ + //do nothing + } else if( attr==":axiom" || attr==":conjecture" ){ + std::string attr_name = attr; + attr_name.erase( attr_name.begin() ); + Command* c = new SetUserAttributeCommand( attr_name, expr ); PARSER_STATE->preemptCommand(c); } else { std::stringstream ss; @@ -812,9 +807,20 @@ attribute[CVC4::Expr& expr,CVC4::Expr& retExpr, std::string& attr] attr = std::string(":pattern"); retExpr = MK_EXPR(kind::INST_PATTERN, patexprs); } - | ATTRIBUTE_REWRITE_RULE { - attr = std::string(":rewrite-rule"); - } + | ATTRIBUTE_NAMED_TOK symbolicExpr[sexpr] + { + attr = std::string(":named"); + std::string name = sexpr.getValue(); + // FIXME ensure expr is a closed subterm + // check that sexpr is a fresh function symbol + PARSER_STATE->checkDeclaration(name, CHECK_UNDECLARED, SYM_VARIABLE); + // define it + Expr func = PARSER_STATE->mkFunction(name, expr.getType()); + // bind name to expr with define-fun + Command* c = + new DefineNamedFunctionCommand(name, func, std::vector(), expr); + PARSER_STATE->preemptCommand(c); + } ; /** @@ -1198,7 +1204,7 @@ PROPAGATION_RULE_TOK : 'assert-propagation'; // attributes ATTRIBUTE_PATTERN_TOK : ':pattern'; -ATTRIBUTE_REWRITE_RULE : ':rewrite-rule'; +ATTRIBUTE_NAMED_TOK : ':named'; // operators (NOTE: theory symbols go here) AMPERSAND_TOK : '&'; diff --git a/src/printer/ast/ast_printer.cpp b/src/printer/ast/ast_printer.cpp index 50bd5016d..39d76728a 100644 --- a/src/printer/ast/ast_printer.cpp +++ b/src/printer/ast/ast_printer.cpp @@ -186,6 +186,10 @@ void AstPrinter::toStream(std::ostream& out, const CommandStatus* s) const throw }/* AstPrinter::toStream(CommandStatus*) */ +void AstPrinter::toStream(std::ostream& out, Model* m, Command* c, int c_type ) const throw(){ + +} + static void toStream(std::ostream& out, const EmptyCommand* c) throw() { out << "EmptyCommand(" << c->getName() << ")"; } diff --git a/src/printer/ast/ast_printer.h b/src/printer/ast/ast_printer.h index 4dfb2c0d5..1cac966df 100644 --- a/src/printer/ast/ast_printer.h +++ b/src/printer/ast/ast_printer.h @@ -35,6 +35,8 @@ public: void toStream(std::ostream& out, TNode n, int toDepth, bool types, size_t dag) const throw(); void toStream(std::ostream& out, const Command* c, int toDepth, bool types, size_t dag) const throw(); void toStream(std::ostream& out, const CommandStatus* s) const throw(); + //for models + void toStream(std::ostream& out, Model* m, Command* c, int c_type ) const throw(); };/* class AstPrinter */ }/* CVC4::printer::ast namespace */ diff --git a/src/printer/cvc/cvc_printer.cpp b/src/printer/cvc/cvc_printer.cpp index 6a709b833..5803ad23f 100644 --- a/src/printer/cvc/cvc_printer.cpp +++ b/src/printer/cvc/cvc_printer.cpp @@ -23,6 +23,8 @@ #include "expr/command.h" #include "theory/substitutions.h" +#include "theory/model.h" + #include #include #include @@ -500,7 +502,7 @@ void CvcPrinter::toStream(std::ostream& out, TNode n, int depth, bool types, boo out << BitVectorType(n.getType().toType()).getSize(); out << ','; toStream(out, n[child], depth, types, false); - out << ','; + out << ','; toStream(out, n[child+1], depth, types, false); while (child > 0) { out << ')'; @@ -537,7 +539,7 @@ void CvcPrinter::toStream(std::ostream& out, TNode n, int depth, bool types, boo out << BitVectorType(n.getType().toType()).getSize(); out << ','; toStream(out, n[child], depth, types, false); - out << ','; + out << ','; toStream(out, n[child+1], depth, types, false); while (child > 0) { out << ')'; @@ -729,6 +731,83 @@ void CvcPrinter::toStream(std::ostream& out, const CommandStatus* s) const throw }/* CvcPrinter::toStream(CommandStatus*) */ +void CvcPrinter::toStream(std::ostream& out, Model* m, Command* c, int c_type ) const throw(){ + theory::TheoryModel* tm = (theory::TheoryModel*)m; + if( c_type==Model::COMMAND_DECLARE_SORT ){ + TypeNode tn = TypeNode::fromType( ((DeclareTypeCommand*)c)->getType() ); + if( tn.isSort() ){ + //print the cardinality + if( tm->d_rep_set.d_type_reps.find( tn )!=tm->d_rep_set.d_type_reps.end() ){ + out << "; cardinality of " << tn << " is " << tm->d_rep_set.d_type_reps[tn].size() << std::endl; + } + } + out << c << std::endl; + if( tn.isSort() ){ + //print the representatives + if( tm->d_rep_set.d_type_reps.find( tn )!=tm->d_rep_set.d_type_reps.end() ){ + for( size_t i=0; id_rep_set.d_type_reps[tn].size(); i++ ){ + if( tm->d_rep_set.d_type_reps[tn][i].isVar() ){ + out << tm->d_rep_set.d_type_reps[tn][i] << " : " << tn << ";" << std::endl; + }else{ + out << "% rep: " << tm->d_rep_set.d_type_reps[tn][i] << std::endl; + } + } + } + } + }else if( c_type==Model::COMMAND_DECLARE_FUN ){ + Node n = Node::fromExpr( ((DeclareFunctionCommand*)c)->getFunction() ); + TypeNode tn = n.getType(); + out << n << " : "; + if( tn.isFunction() || tn.isPredicate() ){ + out << "("; + for( size_t i=0; i0 ) out << ", "; + out << tn[i]; + } + out << ") -> " << tn.getRangeType(); + }else{ + out << tn; + } + out << " = "; + if( tn.isFunction() || tn.isPredicate() ){ + out << "LAMBDA ("; + for( size_t i=0; i0 ) out << ", "; + out << "$x" << (i+1) << " : " << tn[i]; + } + out << "): "; + } + out << tm->getValue( n ); + out << ";" << std::endl; + +/* + //for table format (work in progress) + bool printedModel = false; + if( tn.isFunction() ){ + if( options::modelFormatMode()==MODEL_FORMAT_MODE_TABLE ){ + //specialized table format for functions + RepSetIterator riter( &d_rep_set ); + riter.setFunctionDomain( n ); + while( !riter.isFinished() ){ + std::vector< Node > children; + children.push_back( n ); + for( int i=0; imkNode( APPLY_UF, children ); + Node val = getValue( nn ); + out << val << " "; + riter.increment(); + } + printedModel = true; + } + } +*/ + }else{ + out << c << std::endl; + } +} + static void toStream(std::ostream& out, const AssertCommand* c) throw() { out << "ASSERT " << c->getExpr() << ";"; } diff --git a/src/printer/cvc/cvc_printer.h b/src/printer/cvc/cvc_printer.h index 7fb611a79..c868025ef 100644 --- a/src/printer/cvc/cvc_printer.h +++ b/src/printer/cvc/cvc_printer.h @@ -38,6 +38,8 @@ public: void toStream(std::ostream& out, TNode n, int toDepth, bool types, size_t dag) const throw(); void toStream(std::ostream& out, const Command* c, int toDepth, bool types, size_t dag) const throw(); void toStream(std::ostream& out, const CommandStatus* s) const throw(); + //for models + void toStream(std::ostream& out, Model* m, Command* c, int c_type ) const throw(); };/* class CvcPrinter */ }/* CVC4::printer::cvc namespace */ diff --git a/src/printer/dagification_visitor.cpp b/src/printer/dagification_visitor.cpp index cb56c3430..40b532612 100644 --- a/src/printer/dagification_visitor.cpp +++ b/src/printer/dagification_visitor.cpp @@ -55,6 +55,7 @@ bool DagificationVisitor::alreadyVisited(TNode current, TNode parent) { // increment again (they'll be dagified anyway). return current.isVar() || current.getMetaKind() == kind::metakind::CONSTANT || + current.getNumChildren()==0 || ( ( current.getKind() == kind::NOT || current.getKind() == kind::UMINUS ) && ( current[0].isVar() || diff --git a/src/printer/printer.cpp b/src/printer/printer.cpp index 0881b814b..24baafa14 100644 --- a/src/printer/printer.cpp +++ b/src/printer/printer.cpp @@ -127,4 +127,10 @@ void Printer::toStream(std::ostream& out, const SExpr& sexpr) const throw() { } }/* Printer::toStream() */ +void Printer::toStream(std::ostream& out, Model* m ) const throw(){ + for( int i=0; igetNumCommands(); i++ ){ + toStream( out, m, m->getCommand( i ), m->getCommandType( i ) ); + } +} + }/* CVC4 namespace */ diff --git a/src/printer/printer.h b/src/printer/printer.h index e3b1d6f40..6fedc854c 100644 --- a/src/printer/printer.h +++ b/src/printer/printer.h @@ -23,6 +23,7 @@ #include "util/language.h" #include "util/sexpr.h" +#include "util/model.h" #include "expr/node.h" #include "expr/command.h" @@ -76,6 +77,13 @@ public: */ virtual void toStream(std::ostream& out, const Result& r) const throw(); + /** Write a Model out to a stream with this Printer. */ + virtual void toStream(std::ostream& out, Model* m ) const throw(); + + //for models + + /** write model response to command */ + virtual void toStream(std::ostream& out, Model* m, Command* c, int c_type ) const throw() = 0; };/* class Printer */ }/* CVC4 namespace */ diff --git a/src/printer/smt/smt_printer.cpp b/src/printer/smt/smt_printer.cpp index fa46523a4..14a680a1e 100644 --- a/src/printer/smt/smt_printer.cpp +++ b/src/printer/smt/smt_printer.cpp @@ -51,6 +51,10 @@ void SmtPrinter::toStream(std::ostream& out, const SExpr& sexpr) const throw() { Printer::getPrinter(language::output::LANG_SMTLIB_V2)->toStream(out, sexpr); }/* SmtPrinter::toStream() */ +void SmtPrinter::toStream(std::ostream& out, Model* m, Command* c, int c_type ) const throw(){ + Printer::getPrinter(language::output::LANG_SMTLIB_V2)->toStream(out, m, c, c_type); +} + }/* CVC4::printer::smt namespace */ }/* CVC4::printer namespace */ }/* CVC4 namespace */ diff --git a/src/printer/smt/smt_printer.h b/src/printer/smt/smt_printer.h index 6e1c607bf..1cf7fcf50 100644 --- a/src/printer/smt/smt_printer.h +++ b/src/printer/smt/smt_printer.h @@ -35,6 +35,8 @@ public: void toStream(std::ostream& out, const Command* c, int toDepth, bool types, size_t dag) const throw(); void toStream(std::ostream& out, const CommandStatus* s) const throw(); void toStream(std::ostream& out, const SExpr& sexpr) const throw(); + //for models + void toStream(std::ostream& out, Model* m, Command* c, int c_type ) const throw(); };/* class SmtPrinter */ }/* CVC4::printer::smt namespace */ diff --git a/src/printer/smt2/smt2_printer.cpp b/src/printer/smt2/smt2_printer.cpp index ed8648c47..9400b7732 100644 --- a/src/printer/smt2/smt2_printer.cpp +++ b/src/printer/smt2/smt2_printer.cpp @@ -29,6 +29,8 @@ #include "theory/substitutions.h" #include "util/language.h" +#include "theory/model.h" + using namespace std; namespace CVC4 { @@ -306,14 +308,6 @@ void Smt2Printer::toStream(std::ostream& out, TNode n, // TODO user patterns break; - //function models - case kind::FUNCTION_MODEL: - break; - case kind::FUNCTION_CASE_SPLIT: - break; - case kind::FUNCTION_CASE: - out << "if "; - break; default: // fall back on however the kind prints itself; this probably // won't be SMT-LIB v2 compliant, but it will be clear from the @@ -526,6 +520,76 @@ void Smt2Printer::toStream(std::ostream& out, const CommandStatus* s) const thro }/* Smt2Printer::toStream(CommandStatus*) */ + +void Smt2Printer::toStream(std::ostream& out, Model* m, Command* c, int c_type ) const throw(){ + theory::TheoryModel* tm = (theory::TheoryModel*)m; + if( c_type==Model::COMMAND_DECLARE_SORT ){ + TypeNode tn = TypeNode::fromType( ((DeclareTypeCommand*)c)->getType() ); + if( tn.isSort() ){ + //print the cardinality + if( tm->d_rep_set.d_type_reps.find( tn )!=tm->d_rep_set.d_type_reps.end() ){ + out << "; cardinality of " << tn << " is " << tm->d_rep_set.d_type_reps[tn].size() << std::endl; + } + } + out << c << std::endl; + if( tn.isSort() ){ + //print the representatives + if( tm->d_rep_set.d_type_reps.find( tn )!=tm->d_rep_set.d_type_reps.end() ){ + for( size_t i=0; id_rep_set.d_type_reps[tn].size(); i++ ){ + if( tm->d_rep_set.d_type_reps[tn][i].isVar() ){ + out << "(declare-fun " << tm->d_rep_set.d_type_reps[tn][i] << " () " << tn << ")" << std::endl; + }else{ + out << "; rep: " << tm->d_rep_set.d_type_reps[tn][i] << std::endl; + } + } + } + } + }else if( c_type==Model::COMMAND_DECLARE_FUN ){ + Node n = Node::fromExpr( ((DeclareFunctionCommand*)c)->getFunction() ); + TypeNode tn = n.getType(); + out << "(define-fun " << n << " ("; + if( tn.isFunction() || tn.isPredicate() ){ + for( size_t i=0; i0 ) out << " "; + out << "($x" << (i+1) << " " << tn[i] << ")"; + } + out << ") " << tn.getRangeType(); + }else{ + out << ") " << tn; + } + out << " "; + out << tm->getValue( n ); + out << ")" << std::endl; + +/* + //for table format (work in progress) + bool printedModel = false; + if( tn.isFunction() ){ + if( options::modelFormatMode()==MODEL_FORMAT_MODE_TABLE ){ + //specialized table format for functions + RepSetIterator riter( &d_rep_set ); + riter.setFunctionDomain( n ); + while( !riter.isFinished() ){ + std::vector< Node > children; + children.push_back( n ); + for( int i=0; imkNode( APPLY_UF, children ); + Node val = getValue( nn ); + out << val << " "; + riter.increment(); + } + printedModel = true; + } + } +*/ + }else{ + out << c << std::endl; + } +} + + static void toStream(std::ostream& out, const AssertCommand* c) throw() { out << "(assert " << c->getExpr() << ")"; } @@ -687,7 +751,7 @@ static void toStream(std::ostream& out, const GetOptionCommand* c) throw() { static void toStream(std::ostream& out, const DatatypeDeclarationCommand* c) throw() { const vector& datatypes = c->getDatatypes(); - out << "(declare-datatypes ("; + out << "(declare-datatypes () ("; for(vector::const_iterator i = datatypes.begin(), i_end = datatypes.end(); i != i_end; @@ -698,14 +762,15 @@ static void toStream(std::ostream& out, const DatatypeDeclarationCommand* c) thr out << "(" << d.getName() << " "; for(Datatype::const_iterator ctor = d.begin(), ctor_end = d.end(); ctor != ctor_end; ++ctor){ - out << "(" << ctor->getName() << " "; + if( ctor!=d.begin() ) out << " "; + out << "(" << ctor->getName(); for(DatatypeConstructor::const_iterator arg = ctor->begin(), arg_end = ctor->end(); arg != arg_end; ++arg){ - out << "(" << arg->getSelector() << " " + out << " (" << arg->getSelector() << " " << static_cast(arg->getType()).getRangeType() << ")"; } - out << ") "; + out << ")"; } out << ")" << endl; } diff --git a/src/printer/smt2/smt2_printer.h b/src/printer/smt2/smt2_printer.h index fd65a1efa..30c0ce647 100644 --- a/src/printer/smt2/smt2_printer.h +++ b/src/printer/smt2/smt2_printer.h @@ -35,6 +35,8 @@ public: void toStream(std::ostream& out, TNode n, int toDepth, bool types, size_t dag) const throw(); void toStream(std::ostream& out, const Command* c, int toDepth, bool types, size_t dag) const throw(); void toStream(std::ostream& out, const CommandStatus* s) const throw(); + //for models + void toStream(std::ostream& out, Model* m, Command* c, int c_type ) const throw(); };/* class Smt2Printer */ }/* CVC4::printer::smt2 namespace */ diff --git a/src/prop/minisat/core/Solver.cc b/src/prop/minisat/core/Solver.cc index 675bc8f4e..5e19eb776 100644 --- a/src/prop/minisat/core/Solver.cc +++ b/src/prop/minisat/core/Solver.cc @@ -407,6 +407,7 @@ Lit Solver::pickBranchLit() if(stopSearch) { return lit_Undef; } + Debug("propagateAsDecision") << "propagateAsDecision(): decide on another literal" << std::endl; Var next = var_Undef; diff --git a/src/prop/options b/src/prop/options index c3c2674c4..cda99538c 100644 --- a/src/prop/options +++ b/src/prop/options @@ -25,7 +25,7 @@ option satRestartInc --restart-int-inc=F double :default 3.0 :predicate greater_ option sat_refine_conflicts --refine-conflicts bool refine theory conflict clauses -option minisatUseElim --minisat-elimination bool :default true +option minisatUseElim --minisat-elimination bool :default true :read-write use Minisat elimination endmodule diff --git a/src/smt/Makefile.am b/src/smt/Makefile.am index 9bfc9680a..333c887ee 100644 --- a/src/smt/Makefile.am +++ b/src/smt/Makefile.am @@ -12,7 +12,9 @@ libsmt_la_SOURCES = \ smt_engine_scope.h \ modal_exception.h \ simplification_mode.h \ - simplification_mode.cpp + simplification_mode.cpp \ + model_format_mode.h \ + model_format_mode.cpp nodist_libsmt_la_SOURCES = \ smt_options.cpp diff --git a/src/smt/model_format_mode.cpp b/src/smt/model_format_mode.cpp new file mode 100644 index 000000000..ffaa3df95 --- /dev/null +++ b/src/smt/model_format_mode.cpp @@ -0,0 +1,39 @@ +/********************* */ +/*! \file model_format_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 "smt/model_format_mode.h" + +namespace CVC4 { + +std::ostream& operator<<(std::ostream& out, ModelFormatMode mode) { + switch(mode) { + case MODEL_FORMAT_MODE_DEFAULT: + out << "MODEL_FORMAT_MODE_DEFAULT"; + break; + case MODEL_FORMAT_MODE_TABLE: + out << "MODEL_FORMAT_MODE_TABLE"; + break; + default: + out << "ModelFormatMode:UNKNOWN![" << unsigned(mode) << "]"; + } + + return out; +} + +}/* CVC4 namespace */ diff --git a/src/smt/model_format_mode.h b/src/smt/model_format_mode.h new file mode 100644 index 000000000..3c0a3569e --- /dev/null +++ b/src/smt/model_format_mode.h @@ -0,0 +1,41 @@ +/********************* */ +/*! \file model_format_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_public.h" + +#ifndef __CVC4__SMT__MODEL_FORMAT_MODE_H +#define __CVC4__SMT__MODEL_FORMAT_MODE_H + +#include + +namespace CVC4 { + +/** Enumeration of model_format modes (how to print models from get-model command). */ +typedef enum { + /** default mode (print expressions in the output language format) */ + MODEL_FORMAT_MODE_DEFAULT, + /** print functional values in a table format */ + MODEL_FORMAT_MODE_TABLE, +} ModelFormatMode; + +std::ostream& operator<<(std::ostream& out, ModelFormatMode mode) CVC4_PUBLIC; + +}/* CVC4 namespace */ + +#endif /* __CVC4__SMT__MODEL_FORMAT_H */ diff --git a/src/smt/options b/src/smt/options index fea609bb5..bb0cf1a00 100644 --- a/src/smt/options +++ b/src/smt/options @@ -34,6 +34,8 @@ common-option produceModels produce-models -m --produce-models bool :predicate C support the get-value and get-model commands common-option produceAssignments produce-assignments --produce-assignments bool support the get-assignment command +option modelFormatMode --model-format=MODE ModelFormatMode :handler CVC4::smt::stringToModelFormatMode :default MODEL_FORMAT_MODE_DEFAULT :read-write :include "smt/model_format_mode.h" :handler-include "smt/options_handlers.h" + print format mode for models, see --model-format=help # This could go in src/main/options, but by SMT-LIBv2 spec, "interactive" # is a mode in which the assertion list must be kept. So it belongs here. diff --git a/src/smt/options_handlers.h b/src/smt/options_handlers.h index 925f86d48..fb6cd84d8 100644 --- a/src/smt/options_handlers.h +++ b/src/smt/options_handlers.h @@ -150,6 +150,16 @@ none\n\ + do not perform nonclausal simplification\n\ "; +static const std::string modelFormatHelp = "\ +Model format modes currently supported by the --model-format option:\n\ +\n\ +default \n\ ++ Print model as expressions in the output language format.\n\ +\n\ +table\n\ ++ Print functional expressions over finite domains in a table format.\n\ +"; + inline void dumpMode(std::string option, std::string optarg, SmtEngine* smt) { #ifdef CVC4_DUMPING char* optargPtr = strdup(optarg.c_str()); @@ -231,6 +241,20 @@ inline SimplificationMode stringToSimplificationMode(std::string option, std::st } } +inline ModelFormatMode stringToModelFormatMode(std::string option, std::string optarg, SmtEngine* smt) throw(OptionException) { + if(optarg == "default") { + return MODEL_FORMAT_MODE_DEFAULT; + } else if(optarg == "table") { + return MODEL_FORMAT_MODE_TABLE; + } else if(optarg == "help") { + puts(modelFormatHelp.c_str()); + exit(1); + } else { + throw OptionException(std::string("unknown option for --model-format: `") + + optarg + "'. Try --model-format help."); + } +} + // This macro is used for setting :regular-output-channel and :diagnostic-output-channel // to redirect a stream. It maintains all attributes set on the stream. #define __CVC4__SMT__OUTPUTCHANNELS__SETSTREAM__(__channel_get, __channel_set) \ diff --git a/src/smt/smt_engine.cpp b/src/smt/smt_engine.cpp index d450319b1..832779944 100644 --- a/src/smt/smt_engine.cpp +++ b/src/smt/smt_engine.cpp @@ -60,6 +60,8 @@ #include "theory/booleans/circuit_propagator.h" #include "util/ite_removal.h" #include "theory/model.h" +#include "printer/printer.h" +#include "prop/options.h" using namespace std; using namespace CVC4; @@ -488,7 +490,9 @@ void SmtEngine::setLogicInternal() throw(AssertionException) { bool qf_sat = d_logic.isPure(THEORY_BOOL) && !d_logic.isQuantified(); bool quantifiers = d_logic.isQuantified(); Trace("smt") << "setting simplification mode to <" << d_logic.getLogicString() << "> " << (!qf_sat && !quantifiers) << std::endl; - options::simplificationMode.set(qf_sat || quantifiers ? SIMPLIFICATION_MODE_NONE : SIMPLIFICATION_MODE_BATCH); + //simplifaction=none works better for SMT LIB benchmarks with quantifiers, not others + //options::simplificationMode.set(qf_sat || quantifiers ? SIMPLIFICATION_MODE_NONE : SIMPLIFICATION_MODE_BATCH); + options::simplificationMode.set(qf_sat ? SIMPLIFICATION_MODE_NONE : SIMPLIFICATION_MODE_BATCH); } // If in arrays, set the UF handler to arrays @@ -617,6 +621,21 @@ void SmtEngine::setLogicInternal() throw(AssertionException) { options::decisionMode.set(decMode); options::decisionStopOnly.set(stoponly); } + + //for finite model finding + if( ! options::instWhenMode.wasSetByUser()){ + if( options::fmfInstEngine() ){ + Trace("smt") << "setting inst when mode to LAST_CALL" << std::endl; + options::instWhenMode.set( INST_WHEN_LAST_CALL ); + } + } + + //until bug 371 is fixed + if( ! options::minisatUseElim.wasSetByUser()){ + if( d_logic.isQuantified() ){ + options::minisatUseElim.set( false ); + } + } } void SmtEngine::setInfo(const std::string& key, const CVC4::SExpr& value) @@ -1727,11 +1746,10 @@ Expr SmtEngine::getValue(const Expr& e) throw ModalException(msg); } - // Apply what was learned from preprocessing - Node n = d_private->applySubstitutions(e.getNode()); + // do not need to apply preprocessing substitutions (should be recorded in model already) // Normalize for the theories - n = Rewriter::rewrite(n); + Node n = Rewriter::rewrite( e.getNode() ); Trace("smt") << "--- getting value of " << n << endl; theory::TheoryModel* m = d_theoryEngine->getModel(); @@ -1835,34 +1853,19 @@ CVC4::SExpr SmtEngine::getAssignment() throw(ModalException, AssertionException) } -void SmtEngine::addToModelType( Type& t ){ - Trace("smt") << "SMT addToModelType(" << t << ")" << endl; - SmtScope smts(this); - finalOptionsAreSet(); - if( options::produceModels() ) { - d_theoryEngine->getModel()->addDefineType( TypeNode::fromType( t ) ); - } -} - -void SmtEngine::addToModelFunction( Expr& e ){ - Trace("smt") << "SMT addToModelFunction(" << e << ")" << endl; +void SmtEngine::addToModelCommand( Command* c, int c_type ){ + Trace("smt") << "SMT addToModelCommand(" << c << ", " << c_type << ")" << endl; SmtScope smts(this); finalOptionsAreSet(); if( options::produceModels() ) { - d_theoryEngine->getModel()->addDefineFunction( e.getNode() ); + d_theoryEngine->getModel()->addCommand( c, c_type ); } } - Model* SmtEngine::getModel() throw(ModalException, AssertionException){ Trace("smt") << "SMT getModel()" << endl; SmtScope smts(this); - if(!options::produceModels()) { - const char* msg = - "Cannot get model when produce-models options is off."; - throw ModalException(msg); - } if(d_status.isNull() || d_status.asSatisfiabilityResult() == Result::UNSAT || d_problemExtended) { @@ -1871,7 +1874,11 @@ Model* SmtEngine::getModel() throw(ModalException, AssertionException){ "preceded by SAT/INVALID or UNKNOWN response."; throw ModalException(msg); } - + if(!options::produceModels()) { + const char* msg = + "Cannot get model when produce-models options is off."; + throw ModalException(msg); + } return d_theoryEngine->getModel(); } @@ -2066,7 +2073,13 @@ StatisticsRegistry* SmtEngine::getStatisticsRegistry() const { void SmtEngine::printModel( std::ostream& out, Model* m ){ SmtScope smts(this); - m->toStream(out); + Printer::getPrinter(options::outputLanguage())->toStream( out, m ); + //m->toStream(out); +} + +void SmtEngine::setUserAttribute( std::string& attr, Expr expr ){ + SmtScope smts(this); + d_theoryEngine->setUserAttribute( attr, expr.getNode() ); } }/* CVC4 namespace */ diff --git a/src/smt/smt_engine.h b/src/smt/smt_engine.h index 25800f5b3..234814245 100644 --- a/src/smt/smt_engine.h +++ b/src/smt/smt_engine.h @@ -389,16 +389,10 @@ public: CVC4::SExpr getAssignment() throw(ModalException, AssertionException); /** - * Add to Model Type. This is used for recording which types should be reported + * Add to Model command. This is used for recording a command that should be reported * during a get-model call. */ - void addToModelType( Type& t ); - - /** - * Add to Model Function. This is used for recording which functions should be reported - * during a get-model call. - */ - void addToModelFunction( Expr& e ); + void addToModelCommand( Command* c, int c_type ); /** * Get the model (only if immediately preceded by a SAT @@ -565,6 +559,12 @@ public: */ void printModel( std::ostream& out, Model* m ); + /** Set user attribute + * This function is called when an attribute is set by a user. In SMT-LIBv2 this is done + * via the syntax (! expr :attr) + */ + void setUserAttribute( std::string& attr, Expr expr ); + };/* class SmtEngine */ }/* CVC4 namespace */ 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; igetTermDatabase()->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; iassertEquality( 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; igetTermDatabase()->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()) { 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 @@ -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; jd_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; jd_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 >::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; i0 ){ 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 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::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; id_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 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; imkNode( 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 >::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( childDepIndexdepIndex2 ? 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; idepIndex ){ + 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) > 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 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 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/inst_when_mode.cpp b/src/theory/quantifiers/inst_when_mode.cpp deleted file mode 100644 index b60148c21..000000000 --- a/src/theory/quantifiers/inst_when_mode.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/********************* */ -/*! \file inst_when_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 -#include "theory/quantifiers/inst_when_mode.h" - -namespace CVC4 { - -std::ostream& operator<<(std::ostream& out, theory::quantifiers::InstWhenMode mode) { - switch(mode) { - case theory::quantifiers::INST_WHEN_PRE_FULL: - out << "INST_WHEN_PRE_FULL"; - break; - case theory::quantifiers::INST_WHEN_FULL: - out << "INST_WHEN_FULL"; - break; - case theory::quantifiers::INST_WHEN_FULL_LAST_CALL: - out << "INST_WHEN_FULL_LAST_CALL"; - break; - case theory::quantifiers::INST_WHEN_LAST_CALL: - out << "INST_WHEN_LAST_CALL"; - break; - default: - out << "InstWhenMode!UNKNOWN"; - } - - return out; -} - -}/* CVC4 namespace */ - diff --git a/src/theory/quantifiers/inst_when_mode.h b/src/theory/quantifiers/inst_when_mode.h deleted file mode 100644 index 2cfba4935..000000000 --- a/src/theory/quantifiers/inst_when_mode.h +++ /dev/null @@ -1,49 +0,0 @@ -/********************* */ -/*! \file inst_when_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__INST_WHEN_MODE_H -#define __CVC4__THEORY__QUANTIFIERS__INST_WHEN_MODE_H - -#include - -namespace CVC4 { -namespace theory { -namespace quantifiers { - -typedef enum { - /** Apply instantiation round before full effort (possibly at standard effort) */ - INST_WHEN_PRE_FULL, - /** Apply instantiation round at full effort or above */ - INST_WHEN_FULL, - /** Apply instantiation round at full effort half the time, and last call always */ - INST_WHEN_FULL_LAST_CALL, - /** Apply instantiation round at last call only */ - INST_WHEN_LAST_CALL, -} InstWhenMode; - -}/* CVC4::theory::quantifiers namespace */ -}/* CVC4::theory namespace */ - -std::ostream& operator<<(std::ostream& out, theory::quantifiers::InstWhenMode mode) CVC4_PUBLIC; - -}/* CVC4 namespace */ - -#endif /* __CVC4__THEORY__QUANTIFIERS__INST_WHEN_MODE_H */ 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 -#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 - -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; igetNumAssertedQuantifiers(); 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; igetNumAssertedQuantifiers(); 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; id_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 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; id_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; igetModel()->getNumAssertedQuantifiers(); i++ ){ - Node f = d_quantEngine->getModel()->getAssertedQuantifier( i ); + for( int i=0; igetNumAssertedQuantifiers(); 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; igetModel()->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; igetTermDatabase()->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; isecond.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; igetNumAssertedQuantifiers(); 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; i0 ){ + 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; igetModel()->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; igetTermDatabase()->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; igetTermDatabase()->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; igetModel()->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; i1000 ){ - 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; i1000 ){ + 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/modes.cpp b/src/theory/quantifiers/modes.cpp new file mode 100644 index 000000000..b6786d9f0 --- /dev/null +++ b/src/theory/quantifiers/modes.cpp @@ -0,0 +1,83 @@ +/********************* */ +/*! \file inst_when_mode.cpp + ** \verbatim + ** Original author: mdeters + ** 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) + ** 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 +#include "theory/quantifiers/modes.h" + +namespace CVC4 { + +std::ostream& operator<<(std::ostream& out, theory::quantifiers::InstWhenMode mode) { + switch(mode) { + case theory::quantifiers::INST_WHEN_PRE_FULL: + out << "INST_WHEN_PRE_FULL"; + break; + case theory::quantifiers::INST_WHEN_FULL: + out << "INST_WHEN_FULL"; + break; + case theory::quantifiers::INST_WHEN_FULL_LAST_CALL: + out << "INST_WHEN_FULL_LAST_CALL"; + break; + case theory::quantifiers::INST_WHEN_LAST_CALL: + out << "INST_WHEN_LAST_CALL"; + break; + default: + out << "InstWhenMode!UNKNOWN"; + } + + 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/modes.h b/src/theory/quantifiers/modes.h new file mode 100644 index 000000000..8c56b8f3f --- /dev/null +++ b/src/theory/quantifiers/modes.h @@ -0,0 +1,68 @@ +/********************* */ +/*! \file modes.h + ** \verbatim + ** Original author: mdeters + ** 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) + ** 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__MODEs_H +#define __CVC4__THEORY__QUANTIFIERS__MODEs_H + +#include + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +typedef enum { + /** Apply instantiation round before full effort (possibly at standard effort) */ + INST_WHEN_PRE_FULL, + /** Apply instantiation round at full effort or above */ + INST_WHEN_FULL, + /** Apply instantiation round at full effort half the time, and last call always */ + INST_WHEN_FULL_LAST_CALL, + /** Apply instantiation round at last call only */ + 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 */ + +std::ostream& operator<<(std::ostream& out, theory::quantifiers::InstWhenMode mode) CVC4_PUBLIC; + +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__QUANTIFIERS__INST_WHEN_MODE_H */ 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 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; id_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; id_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; id_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( childDepIndexdepIndex2 ? 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; idepIndex ){ - 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) > 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 -#include #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 & 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; iresetInstantiationRound( 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; id_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 >::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; ihasType( 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; jd_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 + +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 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 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::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::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& 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; isetUserAttribute( 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 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 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& 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_itd_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<=jassertNode(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; imkSkolem( 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_indexdebugPrint("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( aexsetEqual( 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_indexsetDisequal( 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_indexdebugPrint("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 || iaddRep( 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( aexsetEqual( 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_indexd_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 || cd_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()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; imkSkolem( 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 = score1maxScore ){ + 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; imkNode( 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; igetQuantifiersEngine()->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().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().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 NodeBoolMap; typedef context::CDHashMap NodeIntMap; + typedef context::CDHashMap NodeNodeMap; typedef context::CDChunkList NodeList; typedef context::CDList BoolList; typedef context::CDList IntList; typedef context::CDHashMap 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 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 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 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; igetPropEngine()->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 */ diff --git a/src/util/datatype.cpp b/src/util/datatype.cpp index bdefe6755..bdb3f6cf6 100644 --- a/src/util/datatype.cpp +++ b/src/util/datatype.cpp @@ -379,6 +379,15 @@ Expr Datatype::getConstructor(std::string name) const { return (*this)[name].getConstructor(); } +bool Datatype::involvesExternalType() const{ + for(const_iterator i = begin(); i != end(); ++i) { + if( (*i).involvesExternalType() ){ + return true; + } + } + return false; +} + void DatatypeConstructor::resolve(ExprManager* em, DatatypeType self, const std::map& resolutions, const std::vector& placeholders, @@ -689,7 +698,7 @@ Expr DatatypeConstructor::mkGroundTerm( Type t ) const throw(AssertionException) } groundTerms.push_back(selType.mkGroundTerm()); } - + groundTerm = getConstructor().getExprManager()->mkExpr(kind::APPLY_CONSTRUCTOR, groundTerms); if( groundTerm.getType()!=t ){ Assert( Datatype::datatypeOf( d_constructor ).isParametric() ); @@ -722,6 +731,15 @@ Expr DatatypeConstructor::getSelector(std::string name) const { return (*this)[name].getSelector(); } +bool DatatypeConstructor::involvesExternalType() const{ + for(const_iterator i = begin(); i != end(); ++i) { + if(! SelectorType((*i).getSelector().getType()).getRangeType().isDatatype()) { + return true; + } + } + return false; +} + DatatypeConstructorArg::DatatypeConstructorArg(std::string name, Expr selector) : d_name(name), d_selector(selector), diff --git a/src/util/datatype.h b/src/util/datatype.h index 60d2c7acd..9853ba417 100644 --- a/src/util/datatype.h +++ b/src/util/datatype.h @@ -316,6 +316,12 @@ public: */ Expr getSelector(std::string name) const; + /** + * Get whether this datatype involves an external type. If so, + * then we will pose additional requirements for sharing. + */ + bool involvesExternalType() const; + };/* class DatatypeConstructor */ /** @@ -370,7 +376,7 @@ public: * list[T] = cons(car : T, cdr : list[T]) | null, * tree = node(children : list[tree]) | leaf * END; - * + * * Here, the definition of the parametric datatype list, where T is a type variable. * In other words, this defines a family of types list[C] where C is any concrete * type. Datatypes can be parameterized over multiple type variables using the @@ -562,6 +568,12 @@ public: */ Expr getConstructor(std::string name) const; + /** + * Get whether this datatype involves an external type. If so, + * then we will pose additional requirements for sharing. + */ + bool involvesExternalType() const; + };/* class Datatype */ /** diff --git a/src/util/model.h b/src/util/model.h index 60f1ab23f..747247ae1 100644 --- a/src/util/model.h +++ b/src/util/model.h @@ -20,11 +20,37 @@ #define __CVC4__MODEL_H #include +#include namespace CVC4 { +class Command; + class Model { +public: + //types of commands that are recorded for get-model + enum { + COMMAND_DECLARE_SORT, //DeclareTypeCommand + COMMAND_DECLARE_FUN, //DeclareFunctionCommand + COMMAND_DECLARE_DATATYPES, //DatatypeDeclarationCommand + }; +private: + //list of commands that the model must report when calling get model + std::vector< Command* > d_commands; + std::vector< int > d_command_types; +public: + /** add command */ + virtual void addCommand( Command* c, int c_type ){ + d_commands.push_back( c ); + d_command_types.push_back( c_type ); + } + /** get number of commands to report */ + int getNumCommands() { return (int)d_commands.size(); } + /** get command */ + Command* getCommand( int i ) { return d_commands[i]; } + /** get type of command */ + int getCommandType( int i ) { return d_command_types[i]; } public: virtual void toStream(std::ostream& out) = 0; };/* class Model */ @@ -34,7 +60,7 @@ class ModelBuilder public: ModelBuilder(){} virtual ~ModelBuilder(){} - virtual void buildModel( Model* m ) = 0; + virtual void buildModel( Model* m, bool fullModel ) = 0; };/* class ModelBuilder */ }/* CVC4 namespace */ diff --git a/test/regress/regress0/quantifiers/refcount24.cvc b/test/regress/regress0/quantifiers/refcount24.cvc new file mode 100644 index 000000000..21c9a3cfe --- /dev/null +++ b/test/regress/regress0/quantifiers/refcount24.cvc @@ -0,0 +1,112 @@ +% Preamble -------------- +DATATYPE UNIT = Unit END; +DATATYPE BOOL = Truth | Falsity END; + +% Decls -------------- +resource$type: TYPE; +process$type: TYPE; +null: resource$type; +S$elem$type: TYPE = process$type; +S$t$type: TYPE; +S$empty: S$t$type; +S$mem:(S$elem$type, S$t$type) -> BOOL; +S$add:(S$elem$type, S$t$type) -> S$t$type; +S$remove:(S$elem$type, S$t$type) -> S$t$type; +S$cardinality:(S$t$type) -> INT; +S$mem_empty: BOOLEAN = (FORALL (e: S$elem$type): (NOT ((S$mem((e), (S$empty))) = + (Truth)))); +ASSERT S$mem_empty; +S$mem_add: BOOLEAN = (FORALL (x: S$elem$type, y: S$elem$type, s: S$t$type): + ((S$mem((x), (S$add((y), (s))))) = (IF (((x) = (y)) OR + ((S$mem((x), (s))) = + (Truth))) THEN + (Truth) ELSE + (Falsity) ENDIF))); +ASSERT S$mem_add; +S$mem_remove: BOOLEAN = (FORALL (x: S$elem$type, y: S$elem$type, s: S$t$type): + ((S$mem((x), (S$remove((y), (s))))) = (IF ((NOT + ((x) = + (y))) AND + ((S$mem( + (x), (s))) = + (Truth))) THEN + (Truth) ELSE + (Falsity) ENDIF))); +ASSERT S$mem_remove; +S$card_empty: BOOLEAN = ((S$cardinality((S$empty))) = (0)); +ASSERT S$card_empty; +S$card_zero: BOOLEAN = (FORALL (s: S$t$type): (((S$cardinality((s))) = (0)) => + ((s) = (S$empty)))); +ASSERT S$card_zero; +S$card_non_negative: BOOLEAN = (FORALL (s: S$t$type): ((S$cardinality((s))) >= + (0))); +ASSERT S$card_non_negative; +S$card_add: BOOLEAN = (FORALL (x: S$elem$type, s: S$t$type): ((S$cardinality( + (S$add( + (x), (s))))) = + (IF ((S$mem( + (x), (s))) = + (Truth)) THEN + (S$cardinality( + (s))) ELSE ( + (S$cardinality( + (s))) + + (1)) ENDIF))); +ASSERT S$card_add; +S$card_remove: BOOLEAN = (FORALL (x: S$elem$type, s: S$t$type): ((S$cardinality( + (S$remove( + (x), (s))))) = + (IF ( + (S$mem( + (x), (s))) = + (Truth)) THEN ( + (S$cardinality( + (s))) - + (1)) ELSE + (S$cardinality( + (s))) ENDIF))); +ASSERT S$card_remove; + +% Var Decls -------------- +count: ARRAY resource$type OF INT; +ref: ARRAY process$type OF resource$type; +valid: ARRAY resource$type OF BOOL; +destroy$r: resource$type; +valid$1: ARRAY resource$type OF BOOL; + +% Asserts -------------- +ASSERT (NOT ((FORALL (p: process$type): ((NOT + ( + ( + (ref)[ + (p)]) = + (null))) => + (( + (valid)[ + ( + (ref)[ + (p)])]) = + (Truth)))) => + ((NOT ((destroy$r) = (null))) => + ((((valid)[(destroy$r)]) = (Truth)) => + ((((count)[(destroy$r)]) = (0)) => + (((valid$1) = ((valid) WITH [(destroy$r)] := + (Falsity))) => (FORALL + ( + p: process$type): + ( + (NOT + ( + ( + (ref)[ + (p)]) = + (null))) => + ( + ( + (valid$1)[ + ( + (ref)[ + (p)])]) = + (Truth)))))))))); + +CHECKSAT; diff --git a/test/unit/theory/theory_engine_white.h b/test/unit/theory/theory_engine_white.h index eb6e5eefb..2a176e3d9 100644 --- a/test/unit/theory/theory_engine_white.h +++ b/test/unit/theory/theory_engine_white.h @@ -75,6 +75,9 @@ class FakeOutputChannel : public OutputChannel { void setIncomplete() throw(AssertionException) { Unimplemented(); } + void handleUserAttribute( const char* attr, Theory* t ){ + Unimplemented(); + } };/* class FakeOutputChannel */ template