diff options
author | Morgan Deters <mdeters@gmail.com> | 2012-06-11 16:28:23 +0000 |
---|---|---|
committer | Morgan Deters <mdeters@gmail.com> | 2012-06-11 16:28:23 +0000 |
commit | 3378e253fcdb34c753407bb16d08929da06b3aaa (patch) | |
tree | db7c7118dd0d1594175b56866f845b42426ae0a7 /src | |
parent | 42794501e81c44dce5c2f7687af288af030ef63e (diff) |
Merge from quantifiers2-trunkmerge branch.
Adds TheoryQuantifiers and TheoryRewriteRules, QuantifiersEngine, and other infrastructure.
Adds theory instantiators to many theories.
Adds the UF strong solver.
Diffstat (limited to 'src')
123 files changed, 15107 insertions, 300 deletions
diff --git a/src/bindings/compat/java/src/cvc3/Embedded.java b/src/bindings/compat/java/src/cvc3/Embedded.java index fdeeef058..c645f2655 100644 --- a/src/bindings/compat/java/src/cvc3/Embedded.java +++ b/src/bindings/compat/java/src/cvc3/Embedded.java @@ -12,8 +12,6 @@ public abstract class Embedded { // load jni c++ library static { - System.loadLibrary("cvc4"); - System.loadLibrary("cvc4parser"); System.loadLibrary("cvc4bindings_java_compat"); /* diff --git a/src/bindings/compat/java/src/cvc3/Expr_impl.cpp b/src/bindings/compat/java/src/cvc3/Expr_impl.cpp index f002109c5..41e96f53e 100644 --- a/src/bindings/compat/java/src/cvc3/Expr_impl.cpp +++ b/src/bindings/compat/java/src/cvc3/Expr_impl.cpp @@ -282,7 +282,7 @@ return expr->arity(); DEFINITION: Java_cvc3_Expr_jniGetKid jobject c Expr expr n int i -return embed_copy<Expr>(env, (*expr)[ji]); +return embed_copy<Expr>(env, &((*expr)[ji])); DEFINITION: Java_cvc3_Expr_jniGetKids jobjectArray c Expr expr diff --git a/src/context/cdhashmap.h b/src/context/cdhashmap.h index de21515c7..321a378bd 100644 --- a/src/context/cdhashmap.h +++ b/src/context/cdhashmap.h @@ -1,11 +1,11 @@ /********************* */ -/*! \file cdmap.h +/*! \file cdhashmap.h ** \verbatim ** Original author: mdeters ** Major contributors: none ** Minor contributors (to current version): taking, dejan ** This file is part of the CVC4 prototype. - ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Copyright (c) 2009-2012 The Analysis of Computer Systems Group (ACSys) ** Courant Institute of Mathematical Sciences ** New York University ** See the file COPYING in the top-level source directory for licensing diff --git a/src/expr/kind_template.h b/src/expr/kind_template.h index 74641513b..6313aa30b 100644 --- a/src/expr/kind_template.h +++ b/src/expr/kind_template.h @@ -124,8 +124,6 @@ namespace theory { enum TheoryId { ${theory_enum} - THEORY_QUANTIFIERS, - THEORY_REWRITERULES, THEORY_LAST }; diff --git a/src/expr/mkexpr b/src/expr/mkexpr index 0cae68ed0..e113a1e8f 100755 --- a/src/expr/mkexpr +++ b/src/expr/mkexpr @@ -2,7 +2,7 @@ # # mkexpr # Morgan Deters <mdeters@cs.nyu.edu> for CVC4 -# Copyright (c) 2010-2011 The CVC4 Project +# Copyright (c) 2010-2012 The CVC4 Project # # The purpose of this script is to create {expr,expr_manager}.{h,cpp} # from template files and a list of theory kinds. Basically it just @@ -15,7 +15,7 @@ # Output is to standard out. # -copyright=2010-2011 +copyright=2010-2012 filename=`basename "$1" | sed 's,_template,,'` @@ -95,6 +95,12 @@ function rewriter { check_theory_seen } +function instantiator { + # instantiator class header + lineno=${BASH_LINENO[0]} + check_theory_seen +} + function properties { # properties prop* lineno=${BASH_LINENO[0]} diff --git a/src/expr/mkkind b/src/expr/mkkind index abb238f1a..2f361cb63 100755 --- a/src/expr/mkkind +++ b/src/expr/mkkind @@ -2,7 +2,7 @@ # # mkkind # Morgan Deters <mdeters@cs.nyu.edu> for CVC4 -# Copyright (c) 2010-2011 The CVC4 Project +# Copyright (c) 2010-2012 The CVC4 Project # # The purpose of this script is to create kind.h (and also # type_properties.h) from a template and a list of theory kinds. @@ -14,7 +14,7 @@ # Output is to standard out. # -copyright=2010-2011 +copyright=2010-2012 filename=`basename "$1" | sed 's,_template,,'` @@ -105,6 +105,12 @@ function theory { " } +function instantiator { + # instantiator class header + lineno=${BASH_LINENO[0]} + check_theory_seen +} + function properties { # rewriter class header lineno=${BASH_LINENO[0]} diff --git a/src/expr/mkmetakind b/src/expr/mkmetakind index d84691e14..2e94e41be 100755 --- a/src/expr/mkmetakind +++ b/src/expr/mkmetakind @@ -2,7 +2,7 @@ # # mkmetakind # Morgan Deters <mdeters@cs.nyu.edu> for CVC4 -# Copyright (c) 2010-2011 The CVC4 Project +# Copyright (c) 2010-2012 The CVC4 Project # # The purpose of this script is to create metakind.h from a template # and a list of theory kinds. @@ -17,7 +17,7 @@ # Output is to standard out. # -copyright=2010-2011 +copyright=2010-2012 cat <<EOF /********************* */ @@ -80,6 +80,12 @@ function theory { // #include \"theory/$b/$2\"" } +function instantiator { + # instantiator class header + lineno=${BASH_LINENO[0]} + check_theory_seen +} + function properties { # properties prop* lineno=${BASH_LINENO[0]} diff --git a/src/expr/node.h b/src/expr/node.h index a61944433..e56774d7e 100644 --- a/src/expr/node.h +++ b/src/expr/node.h @@ -159,6 +159,14 @@ namespace kind { }/* CVC4::kind::metakind namespace */ }/* CVC4::kind namespace */ +// for hash_maps, hash_sets.. +struct NodeHashFunction { + inline size_t operator()(Node node) const; +};/* struct NodeHashFunction */ +struct TNodeHashFunction { + inline size_t operator()(TNode node) const; +};/* struct TNodeHashFunction */ + /** * Encapsulation of an NodeValue pointer. The reference count is * maintained in the NodeValue if ref_count is true. @@ -166,17 +174,6 @@ namespace kind { */ template <bool ref_count> class NodeTemplate { - - // for hash_maps, hash_sets.. - template <bool ref_count1> - struct HashFunction { - size_t operator()(CVC4::NodeTemplate<ref_count1> node) const { - return (size_t) node.getId(); - } - };/* struct HashFunction */ - - typedef HashFunction<false> TNodeHashFunction; - /** * The NodeValue has access to the private constructors, so that the * iterators can can create new nodes. @@ -233,7 +230,7 @@ class NodeTemplate { Assert( d_nv->d_rc > 0, "TNode pointing to an expired NodeValue" ); } } - +public: /** * Cache-aware, recursive version of substitute() used by the public * member function with a similar signature. @@ -904,19 +901,12 @@ inline std::ostream& operator<<(std::ostream& out, TNode n) { namespace CVC4 { -// for hash_maps, hash_sets.. -struct NodeHashFunction { - size_t operator()(CVC4::Node node) const { - return (size_t) node.getId(); - } -};/* struct NodeHashFunction */ - -// for hash_maps, hash_sets.. -struct TNodeHashFunction { - size_t operator()(CVC4::TNode node) const { - return (size_t) node.getId(); - } -};/* struct TNodeHashFunction */ +inline size_t NodeHashFunction::operator()(Node node) const { + return node.getId(); +} +inline size_t TNodeHashFunction::operator()(TNode node) const { + return node.getId(); +} struct TNodePairHashFunction { size_t operator()(const std::pair<CVC4::TNode, CVC4::TNode>& pair ) const { diff --git a/src/expr/node_manager.h b/src/expr/node_manager.h index 00fe6baa8..e763a1f10 100644 --- a/src/expr/node_manager.h +++ b/src/expr/node_manager.h @@ -362,6 +362,9 @@ public: /** Create a skolem constant with the given type. */ Node mkSkolem(const TypeNode& type); + /** Create a instantiation constant with the given type. */ + Node mkInstConstant(const TypeNode& type); + /** * Create a constant of type T. It will have the appropriate * CONST_* kind defined for T. @@ -579,6 +582,15 @@ public: /** Get the (singleton) type for sorts. */ inline TypeNode kindType(); + /** Get the bound var list type. */ + inline TypeNode boundVarListType(); + + /** Get the instantiation pattern type. */ + inline TypeNode instPatternType(); + + /** Get the instantiation pattern type. */ + inline TypeNode instPatternListType(); + /** * Get the (singleton) type for builtin operators (that is, the type * of the Node returned from Node::getOperator() when the operator @@ -897,6 +909,21 @@ inline TypeNode NodeManager::kindType() { return TypeNode(mkTypeConst<TypeConstant>(KIND_TYPE)); } +/** Get the bound var list type. */ +inline TypeNode NodeManager::boundVarListType(){ + return TypeNode(mkTypeConst<TypeConstant>(BOUND_VAR_LIST_TYPE)); +} + +/** Get the instantiation pattern type. */ +inline TypeNode NodeManager::instPatternType(){ + return TypeNode(mkTypeConst<TypeConstant>(INST_PATTERN_TYPE)); +} + +/** Get the instantiation pattern type. */ +inline TypeNode NodeManager::instPatternListType(){ + return TypeNode(mkTypeConst<TypeConstant>(INST_PATTERN_LIST_TYPE)); +} + /** Get the (singleton) type for builtin operators. */ inline TypeNode NodeManager::builtinOperatorType() { return TypeNode(mkTypeConst<TypeConstant>(BUILTIN_OPERATOR_TYPE)); @@ -1366,6 +1393,13 @@ inline Node NodeManager::mkSkolem(const TypeNode& type) { return n; } +inline Node NodeManager::mkInstConstant(const TypeNode& type) { + Node n = NodeBuilder<0>(this, kind::INST_CONSTANT); + n.setAttribute(TypeAttr(), type); + n.setAttribute(TypeCheckedAttr(), true); + return n; +} + template <class T> Node NodeManager::mkConst(const T& val) { return mkConstInternal<Node, T>(val); diff --git a/src/parser/cvc/Cvc.g b/src/parser/cvc/Cvc.g index 1f0e6b890..2988ae4ef 100644 --- a/src/parser/cvc/Cvc.g +++ b/src/parser/cvc/Cvc.g @@ -1066,13 +1066,13 @@ restrictedTypePossiblyFunctionLHS[CVC4::Type& t, PARSER_STATE->isDeclared(id, SYM_SORT)) { Debug("parser-param") << "param: getSort " << id << " " << types.size() << " " << PARSER_STATE->getArity( id ) << " " << PARSER_STATE->isDeclared(id, SYM_SORT) << std::endl; - if( types.size()>0 ){ + if(types.size() > 0) { t = PARSER_STATE->getSort(id, types); }else{ t = PARSER_STATE->getSort(id); } } else { - if( types.empty() ){ + if(types.empty()) { t = PARSER_STATE->mkUnresolvedType(id); Debug("parser-param") << "param: make unres type " << id << std::endl; }else{ @@ -1236,22 +1236,36 @@ prefixFormula[CVC4::Expr& f] std::vector<std::string> ids; std::vector<Expr> terms; std::vector<Type> types; + std::vector<Expr> bvs; Type t; + Kind k; + Expr ipl; } /* quantifiers */ - : FORALL_TOK { PARSER_STATE->pushScope(); } LPAREN - boundVarDecl[ids,t] (COMMA boundVarDecl[ids,t])* RPAREN - COLON instantiationPatterns? formula[f] - { PARSER_STATE->popScope(); - UNSUPPORTED("quantifiers not supported yet"); - f = EXPR_MANAGER->mkVar(EXPR_MANAGER->booleanType()); + : ( FORALL_TOK { k = kind::FORALL; } | EXISTS_TOK { k = kind::EXISTS; } ) + { PARSER_STATE->pushScope(); } LPAREN + boundVarDecl[ids,t] + { for(std::vector<std::string>::const_iterator i = ids.begin(); i != ids.end(); ++i) { + bvs.push_back(PARSER_STATE->mkVar(*i, t)); + } + ids.clear(); } - | EXISTS_TOK { PARSER_STATE->pushScope(); } LPAREN - boundVarDecl[ids,t] (COMMA boundVarDecl[ids,t])* RPAREN - COLON instantiationPatterns? formula[f] + ( COMMA boundVarDecl[ids,t] + { + for(std::vector<std::string>::const_iterator i = ids.begin(); i != ids.end(); ++i) { + bvs.push_back(PARSER_STATE->mkVar(*i, t)); + } + ids.clear(); + } + )* RPAREN { + terms.push_back( EXPR_MANAGER->mkExpr( kind::BOUND_VAR_LIST, bvs ) ); } + COLON instantiationPatterns[ipl]? formula[f] { PARSER_STATE->popScope(); - UNSUPPORTED("quantifiers not supported yet"); - f = EXPR_MANAGER->mkVar(EXPR_MANAGER->booleanType()); + terms.push_back(f); + if(! ipl.isNull()) { + terms.push_back(ipl); + } + f = MK_EXPR(k, terms); } /* lets: letDecl defines the variables and functionss, we just @@ -1283,11 +1297,20 @@ prefixFormula[CVC4::Expr& f] } ; -instantiationPatterns +instantiationPatterns[ CVC4::Expr& expr ] @init { + std::vector<Expr> args; Expr f; + std::vector<Expr> patterns; } - : ( PATTERN_TOK LPAREN formula[f] (COMMA formula[f])* RPAREN COLON )+ + : ( PATTERN_TOK LPAREN formula[f] { args.push_back( f ); } (COMMA formula[f] { args.push_back( f ); } )* RPAREN COLON + { patterns.push_back( EXPR_MANAGER->mkExpr( kind::INST_PATTERN, args ) ); + args.clear(); + } )+ + { if(! patterns.empty()) { + expr = EXPR_MANAGER->mkExpr( kind::INST_PATTERN_LIST, patterns ); + } + } ; /** @@ -1417,7 +1440,7 @@ tupleStore[CVC4::Expr& f] Expr f2; } : k=numeral ASSIGN_TOK uminusTerm[f2] - { + { Type t = f.getType(); if(! t.isDatatype()) { PARSER_STATE->parseError("tuple-update applied to non-tuple"); @@ -1456,7 +1479,7 @@ recordStore[CVC4::Expr& f] Expr f2; } : identifier[id,CHECK_NONE,SYM_VARIABLE] ASSIGN_TOK uminusTerm[f2] - { + { Type t = f.getType(); if(! t.isDatatype()) { PARSER_STATE->parseError("record-update applied to non-record"); diff --git a/src/parser/smt/Smt.g b/src/parser/smt/Smt.g index 6dd4e78f3..568f3bb92 100644 --- a/src/parser/smt/Smt.g +++ b/src/parser/smt/Smt.g @@ -244,6 +244,19 @@ annotatedFormula[CVC4::Expr& expr] } } + | /* A quantifier */ + LPAREN_TOK + ( FORALL_TOK { kind = kind::FORALL; } | EXISTS_TOK { kind = kind::EXISTS; } ) + { PARSER_STATE->pushScope(); } + ( LPAREN_TOK let_identifier[name,CHECK_NONE] t=sortSymbol RPAREN_TOK + { args.push_back(PARSER_STATE->mkVar(name, t)); } + )+ + annotatedFormula[expr] RPAREN_TOK + { args.push_back(expr); + expr = MK_EXPR(kind, args); + PARSER_STATE->popScope(); + } + | /* A non-built-in function application */ // Semantic predicate not necessary if parenthesized subexpressions diff --git a/src/parser/smt/smt.cpp b/src/parser/smt/smt.cpp index c3b81655c..4d3c1d086 100644 --- a/src/parser/smt/smt.cpp +++ b/src/parser/smt/smt.cpp @@ -29,6 +29,10 @@ namespace parser { std::hash_map<const std::string, Smt::Logic, CVC4::StringHashFunction> Smt::newLogicMap() { std::hash_map<const std::string, Smt::Logic, CVC4::StringHashFunction> logicMap; + logicMap["AUFLIA"] = AUFLIA; + logicMap["AUFLIRA"] = AUFLIRA; + logicMap["AUFNIRA"] = AUFNIRA; + logicMap["LRA"] = LRA; logicMap["QF_AX"] = QF_AX; logicMap["QF_BV"] = QF_BV; logicMap["QF_IDL"] = QF_IDL; @@ -54,6 +58,9 @@ std::hash_map<const std::string, Smt::Logic, CVC4::StringHashFunction> Smt::newL logicMap["QF_UFNIRA"] = QF_UFNIRA; logicMap["QF_AUFLIA"] = QF_AUFLIA; logicMap["QF_AUFLIRA"] = QF_AUFLIRA; + logicMap["UFNIA"] = UFNIA; + logicMap["UFNIRA"] = UFNIRA; + logicMap["UFLRA"] = UFLRA; logicMap["QF_ALL_SUPPORTED"] = QF_ALL_SUPPORTED; logicMap["ALL_SUPPORTED"] = ALL_SUPPORTED; return logicMap; @@ -109,6 +116,22 @@ void Smt::addTheory(Theory theory) { break; } + case THEORY_BITVECTOR_ARRAYS_EX: { + Unimplemented("Cannot yet handle SMT-LIBv1 bitvector arrays (i.e., the BitVector_ArraysEx theory)"); + //addOperator(kind::SELECT); + //addOperator(kind::STORE); + break; + } + + case THEORY_INT_INT_REAL_ARRAY_ARRAYS_EX: { + defineType("Array1", getExprManager()->mkArrayType(getSort("Int"), getSort("Real"))); + defineType("Array2", getExprManager()->mkArrayType(getSort("Int"), getSort("Array1"))); + addOperator(kind::SELECT); + addOperator(kind::STORE); + break; + } + + case THEORY_INT_ARRAYS: case THEORY_INT_ARRAYS_EX: { defineType("Array", getExprManager()->mkArrayType(getExprManager()->integerType(), getExprManager()->integerType())); @@ -140,6 +163,9 @@ void Smt::addTheory(Theory theory) { case THEORY_BITVECTORS: break; + case THEORY_QUANTIFIERS: + break; + default: Unhandled(theory); } @@ -244,13 +270,14 @@ void Smt::setLogic(const std::string& name) { break; case QF_AUFLIRA: - addTheory(THEORY_ARRAYS_EX); + addTheory(THEORY_INT_INT_REAL_ARRAY_ARRAYS_EX); addUf(); addTheory(THEORY_INTS); addTheory(THEORY_REALS); break; case ALL_SUPPORTED: + addTheory(THEORY_QUANTIFIERS); /* fall through */ case QF_ALL_SUPPORTED: addTheory(THEORY_ARRAYS_EX); @@ -261,14 +288,41 @@ void Smt::setLogic(const std::string& name) { break; case AUFLIA: + addUf(); + addTheory(THEORY_INTS); + addTheory(THEORY_INT_ARRAYS_EX); + addTheory(THEORY_QUANTIFIERS); + break; + case AUFLIRA: case AUFNIRA: + addUf(); + addTheory(THEORY_INTS); + addTheory(THEORY_REALS); + addTheory(THEORY_INT_INT_REAL_ARRAY_ARRAYS_EX); + addTheory(THEORY_QUANTIFIERS); + break; + case LRA: - case UFLRA: case UFNIA: - Unhandled(name); + addUf(); + addTheory(THEORY_INTS); + addTheory(THEORY_QUANTIFIERS); + break; + case UFNIRA: + addUf(); + addTheory(THEORY_INTS); + addTheory(THEORY_REALS); + addTheory(THEORY_QUANTIFIERS); + break; + + case UFLRA: + addUf(); + addTheory(THEORY_REALS); + addTheory(THEORY_QUANTIFIERS); + break; } -} +}/* Smt::setLogic() */ }/* CVC4::parser namespace */ }/* CVC4 namespace */ diff --git a/src/parser/smt/smt.h b/src/parser/smt/smt.h index d77808930..7b1dfc345 100644 --- a/src/parser/smt/smt.h +++ b/src/parser/smt/smt.h @@ -65,6 +65,7 @@ public: QF_UFNIRA, // nonstandard QF_UFNRA, UFLRA, + UFNIRA, // nonstandard UFNIA, QF_ALL_SUPPORTED, // nonstandard ALL_SUPPORTED // nonstandard @@ -75,7 +76,7 @@ public: THEORY_ARRAYS_EX, THEORY_BITVECTORS, THEORY_BITVECTORS_32, - THEORY_BITVECTORS_ARRAYS_EX, + THEORY_BITVECTOR_ARRAYS_EX, THEORY_EMPTY, THEORY_INTS, THEORY_INT_ARRAYS, @@ -83,6 +84,7 @@ public: THEORY_INT_INT_REAL_ARRAY_ARRAYS_EX, THEORY_REALS, THEORY_REALS_INTS, + THEORY_QUANTIFIERS }; private: diff --git a/src/parser/smt2/Smt2.g b/src/parser/smt2/Smt2.g index 7a7c7df62..59b6715b9 100644 --- a/src/parser/smt2/Smt2.g +++ b/src/parser/smt2/Smt2.g @@ -136,7 +136,10 @@ using namespace CVC4::parser; * @return the parsed expression, or the Null Expr if we've reached the end of the input */ parseExpr returns [CVC4::parser::smt2::myExpr expr] - : term[expr] +@declarations { + Expr expr2; +} + : term[expr, expr2] | EOF ; @@ -156,7 +159,7 @@ command returns [CVC4::Command* cmd = NULL] @declarations { std::string name; std::vector<std::string> names; - Expr expr; + Expr expr, expr2; Type t; std::vector<Expr> terms; std::vector<Type> sorts; @@ -258,7 +261,7 @@ command returns [CVC4::Command* cmd = NULL] terms.push_back(PARSER_STATE->mkVar((*i).first, (*i).second)); } } - term[expr] + term[expr, expr2] { PARSER_STATE->popScope(); // declare the name down here (while parsing term, signature // must not be extended with the name itself; no recursion @@ -287,7 +290,7 @@ command returns [CVC4::Command* cmd = NULL] { cmd = new GetAssignmentCommand; } | /* assertion */ ASSERT_TOK { PARSER_STATE->checkThatLogicIsSet(); } - term[expr] + term[expr, expr2] { cmd = new AssertCommand(expr); } | /* checksat */ CHECKSAT_TOK { PARSER_STATE->checkThatLogicIsSet(); } @@ -359,7 +362,7 @@ extendedCommand[CVC4::Command*& cmd] @declarations { std::vector<CVC4::Datatype> dts; Type t; - Expr e; + Expr e, e2; SExpr sexpr; std::string name; std::vector<std::string> names; @@ -420,7 +423,7 @@ symbolicExpr[CVC4::SExpr& sexpr] * Matches a term. * @return the expression representing the formula */ -term[CVC4::Expr& expr] +term[CVC4::Expr& expr, CVC4::Expr& expr2] @init { Debug("parser") << "term: " << AntlrInput::tokenText(LT(1)) << std::endl; Kind kind; @@ -428,6 +431,11 @@ term[CVC4::Expr& expr] std::string name; std::vector<Expr> args; SExpr sexpr; + std::vector< std::pair<std::string, Type> > sortedVarNames; + Expr f, f2; + std::string attr; + Expr attexpr; + std::vector<Expr> attexprs; } : /* a built-in operator application */ LPAREN_TOK builtinOp[kind] termList[args,expr] RPAREN_TOK @@ -456,10 +464,45 @@ term[CVC4::Expr& expr] expr = MK_EXPR(kind, args); } } - + | LPAREN_TOK quantOp[kind] + LPAREN_TOK sortedVarList[sortedVarNames] RPAREN_TOK + { + PARSER_STATE->pushScope(); + for(std::vector<std::pair<std::string, CVC4::Type> >::const_iterator i = + sortedVarNames.begin(), iend = sortedVarNames.end(); + i != iend; + ++i) { + args.push_back(PARSER_STATE->mkVar((*i).first, (*i).second)); + } + Expr bvl = MK_EXPR(kind::BOUND_VAR_LIST, args); + args.clear(); + args.push_back(bvl); + } + term[f, f2] RPAREN_TOK + { + PARSER_STATE->popScope(); + switch(f.getKind()) { + case CVC4::kind::RR_REWRITE: + case CVC4::kind::RR_REDUCTION: + case CVC4::kind::RR_DEDUCTION: + if(kind == CVC4::kind::EXISTS) { + PARSER_STATE->parseError("Use Exists instead of Forall for a rewrite rule."); + } + args.push_back(f2); // guards + args.push_back(f); // rule + expr = MK_EXPR(CVC4::kind::REWRITE_RULE, args); + break; + default: + args.push_back(f); + if(! f2.isNull()){ + args.push_back(f2); + } + expr = MK_EXPR(kind, args); + } + } | /* A non-built-in function application */ LPAREN_TOK - functionName[name,CHECK_DECLARED] + functionName[name, CHECK_DECLARED] { PARSER_STATE->checkFunctionLike(name); const bool isDefinedFunction = PARSER_STATE->isDefinedFunction(name); @@ -469,13 +512,13 @@ term[CVC4::Expr& expr] } else { expr = PARSER_STATE->getVariable(name); Type t = expr.getType(); - if( t.isConstructor() ){ + if(t.isConstructor()) { kind = CVC4::kind::APPLY_CONSTRUCTOR; - }else if( t.isSelector() ){ + } else if(t.isSelector()) { kind = CVC4::kind::APPLY_SELECTOR; - }else if( t.isTester() ){ + } else if(t.isTester()) { kind = CVC4::kind::APPLY_TESTER; - }else{ + } else { kind = CVC4::kind::APPLY_UF; } } @@ -494,10 +537,10 @@ term[CVC4::Expr& expr] | /* a let binding */ LPAREN_TOK LET_TOK LPAREN_TOK { PARSER_STATE->pushScope(); } - ( LPAREN_TOK symbol[name,CHECK_UNDECLARED,SYM_VARIABLE] term[expr] RPAREN_TOK + ( LPAREN_TOK symbol[name,CHECK_UNDECLARED,SYM_VARIABLE] term[expr, f2] RPAREN_TOK { PARSER_STATE->defineVar(name,expr); } )+ RPAREN_TOK - term[expr] + term[expr, f2] RPAREN_TOK { PARSER_STATE->popScope(); } @@ -519,26 +562,45 @@ term[CVC4::Expr& expr] } /* attributed expressions */ - | LPAREN_TOK ATTRIBUTE_TOK term[expr] KEYWORD symbolicExpr[sexpr] RPAREN_TOK - { std::string attr = AntlrInput::tokenText($KEYWORD); - if(attr == ":named") { - 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>(), expr); - PARSER_STATE->preemptCommand(c); - } else { - std::stringstream ss; - ss << "Attribute `" << attr << "' not supported"; - PARSER_STATE->parseError(ss.str()); + | LPAREN_TOK ATTRIBUTE_TOK term[expr, f2] + ( attribute[expr, attexpr,attr] + { if(! attexpr.isNull()) { + attexprs.push_back(attexpr); + } } - } + )+ RPAREN_TOK + { + if(attr == ":rewrite-rule") { + Expr guard; + Expr body; + if(expr[1].getKind() == kind::IMPLIES || + expr[1].getKind() == kind::IFF || + expr[1].getKind() == kind::EQUAL) { + guard = expr[0]; + body = expr[1]; + } else { + guard = MK_CONST(bool(true)); + body = expr; + } + expr2 = guard; + args.push_back(body[0]); + args.push_back(body[1]); + if(!f2.isNull()) { + args.push_back(f2); + } + + if ( body.getKind()==kind::IMPLIES ) kind = kind::RR_DEDUCTION; + else if( body.getKind()==kind::IFF ) kind = kind::RR_REDUCTION; + else if( body.getKind()==kind::EQUAL ) kind = kind::RR_REWRITE; + else PARSER_STATE->parseError("Error parsing rewrite rule."); + expr = MK_EXPR( kind, args ); + } else if(! attexprs.empty()) { + if(attexprs[0].getKind() == kind::INST_PATTERN) { + expr2 = MK_EXPR(kind::INST_PATTERN_LIST, attexprs); + } + } + } /* constants */ | INTEGER_LITERAL { expr = MK_CONST( AntlrInput::tokenToInteger($INTEGER_LITERAL) ); } @@ -570,6 +632,46 @@ term[CVC4::Expr& expr] ; /** + * Read attribute + */ +attribute[CVC4::Expr& expr,CVC4::Expr& retExpr, std::string& attr] +@init { + SExpr sexpr; + Expr patexpr; + std::vector<Expr> patexprs; + 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>(), expr); + PARSER_STATE->preemptCommand(c); + } else { + std::stringstream ss; + ss << "Attribute `" << attr << "' not supported"; + PARSER_STATE->parseError(ss.str()); + } + } + | ATTRIBUTE_PATTERN_TOK LPAREN_TOK ( term[patexpr, e2] { patexprs.push_back( patexpr ); } )+ RPAREN_TOK + { + attr = std::string(":pattern"); + retExpr = MK_EXPR(kind::INST_PATTERN, patexprs); + } + | ATTRIBUTE_REWRITE_RULE { + attr = std::string(":rewrite-rule"); + } + ; + +/** * Matches a bit-vector operator (the ones parametrized by numbers) */ indexedFunctionName[CVC4::Expr& op] @@ -613,7 +715,10 @@ badIndexedFunctionName /* NOTE: We pass an Expr in here just to avoid allocating a fresh Expr every * time through this rule. */ termList[std::vector<CVC4::Expr>& formulas, CVC4::Expr& expr] - : ( term[expr] { formulas.push_back(expr); } )+ +@declarations { + Expr expr2; +} + : ( term[expr, expr2] { formulas.push_back(expr); } )+ ; /** @@ -693,6 +798,14 @@ builtinOp[CVC4::Kind& kind] // NOTE: Theory operators go here ; +quantOp[CVC4::Kind& kind] +@init { + Debug("parser") << "quant: " << AntlrInput::tokenText(LT(1)) << std::endl; +} + : EXISTS_TOK { $kind = CVC4::kind::EXISTS; } + | FORALL_TOK { $kind = CVC4::kind::FORALL; } + ; + /** * Matches a (possibly undeclared) function symbol (returning the string) * @param check what kind of check to do with the symbol @@ -760,12 +873,12 @@ sortSymbol[CVC4::Type& t, CVC4::parser::DeclarationCheck check] std::vector<uint64_t> numerals; } : sortName[name,CHECK_NONE] - { - if( check == CHECK_DECLARED || PARSER_STATE->isDeclared(name, SYM_SORT) ){ - t = PARSER_STATE->getSort(name); - }else{ - t = PARSER_STATE->mkUnresolvedType(name); - } + { + if( check == CHECK_DECLARED || PARSER_STATE->isDeclared(name, SYM_SORT) ){ + t = PARSER_STATE->getSort(name); + }else{ + t = PARSER_STATE->mkUnresolvedType(name); + } } | LPAREN_TOK INDEX_TOK symbol[name,CHECK_NONE,SYM_SORT] nonemptyNumeralList[numerals] RPAREN_TOK { @@ -929,6 +1042,10 @@ POP_TOK : 'pop'; DECLARE_DATATYPES_TOK : 'declare-datatypes'; ECHO_TOK : 'echo'; +// attributes +ATTRIBUTE_PATTERN_TOK : ':pattern'; +ATTRIBUTE_REWRITE_RULE : ':rewrite-rule'; + // operators (NOTE: theory symbols go here) AMPERSAND_TOK : '&'; AND_TOK : 'and'; diff --git a/src/parser/smt2/smt2.cpp b/src/parser/smt2/smt2.cpp index 709ba087f..549878e46 100644 --- a/src/parser/smt2/smt2.cpp +++ b/src/parser/smt2/smt2.cpp @@ -86,6 +86,9 @@ void Smt2::addTheory(Theory theory) { case THEORY_BITVECTORS: break; + case THEORY_QUANTIFIERS: + break; + default: Unhandled(theory); } @@ -195,6 +198,7 @@ void Smt2::setLogic(const std::string& name) { break; case Smt::ALL_SUPPORTED: + addTheory(THEORY_QUANTIFIERS); /* fall through */ case Smt::QF_ALL_SUPPORTED: addTheory(THEORY_ARRAYS); @@ -208,11 +212,25 @@ void Smt2::setLogic(const std::string& name) { case Smt::AUFLIRA: case Smt::AUFNIRA: case Smt::LRA: - case Smt::UFLRA: case Smt::UFNIA: - Unhandled(name); + case Smt::UFNIRA: + case Smt::UFLRA: + if(d_logic != Smt::AUFLIA && d_logic != Smt::UFNIA) { + addTheory(THEORY_REALS); + } + if(d_logic != Smt::LRA) { + addOperator(kind::APPLY_UF); + if(d_logic != Smt::UFLRA) { + addTheory(THEORY_INTS); + if(d_logic != Smt::UFNIA && d_logic != Smt::UFNIRA) { + addTheory(THEORY_ARRAYS); + } + } + } + addTheory(THEORY_QUANTIFIERS); + break; } -} +}/* Smt2::setLogic() */ void Smt2::setInfo(const std::string& flag, const SExpr& sexpr) { // TODO: ??? diff --git a/src/parser/smt2/smt2.h b/src/parser/smt2/smt2.h index b71f63558..9d33e3e62 100644 --- a/src/parser/smt2/smt2.h +++ b/src/parser/smt2/smt2.h @@ -41,6 +41,7 @@ public: THEORY_INTS, THEORY_REALS, THEORY_REALS_INTS, + THEORY_QUANTIFIERS, }; private: diff --git a/src/printer/cvc/cvc_printer.cpp b/src/printer/cvc/cvc_printer.cpp index d20ba0354..76c4f0abc 100644 --- a/src/printer/cvc/cvc_printer.cpp +++ b/src/printer/cvc/cvc_printer.cpp @@ -5,7 +5,7 @@ ** Major contributors: none ** Minor contributors (to current version): none ** This file is part of the CVC4 prototype. - ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Copyright (c) 2009-2012 The Analysis of Computer Systems Group (ACSys) ** Courant Institute of Mathematical Sciences ** New York University ** See the file COPYING in the top-level source directory for licensing @@ -253,6 +253,9 @@ void CvcPrinter::toStream(std::ostream& out, TNode n, int depth, bool types, boo case kind::APPLY_UF: toStream(op, n.getOperator(), depth, types, false); break; + case kind::CARDINALITY_CONSTRAINT: + out << "CARDINALITY_CONSTRAINT"; + break; case kind::FUNCTION_TYPE: if (n.getNumChildren() > 1) { @@ -548,6 +551,44 @@ void CvcPrinter::toStream(std::ostream& out, TNode n, int depth, bool types, boo out << ", " << n.getOperator().getConst<BitVectorRotateRight>() << ')'; return; break; + + // Quantifiers + case kind::FORALL: + out << "(FORALL"; + toStream(out, n[0], depth, types, false); + out << " : "; + toStream(out, n[1], depth, types, false); + out << ')'; + // TODO: user patterns? + return; + case kind::EXISTS: + out << "(EXISTS"; + toStream(out, n[0], depth, types, false); + out << " : "; + toStream(out, n[1], depth, types, false); + out << ')'; + // TODO: user patterns? + break; + case kind::INST_CONSTANT: + out << "INST_CONSTANT"; + break; + case kind::BOUND_VAR_LIST: + out << '('; + for(size_t i = 0; i < n.getNumChildren(); ++i) { + if(i > 0) { + out << ", "; + } + toStream(out, n[i], -1, true, false); // ascribe types + } + out << ')'; + return; + case kind::INST_PATTERN: + out << "INST_PATTERN"; + break; + case kind::INST_PATTERN_LIST: + out << "INST_PATTERN_LIST"; + break; + default: Warning() << "Kind printing not implemented for the case of " << n.getKind() << endl; break; @@ -662,12 +703,12 @@ static void toStream(std::ostream& out, const SExpr& sexpr) throw() { } else if(sexpr.isString()) { string s = sexpr.getValue(); // escape backslash and quote - for(string::iterator i = s.begin(); i != s.end(); ++i) { - if(*i == '"') { - s.replace(i, i + 1, "\\\""); + for(size_t i = 0; i < s.size(); ++i) { + if(s[i] == '"') { + s.replace(i, 1, "\\\""); ++i; - } else if(*i == '\\') { - s.replace(i, i + 1, "\\\\"); + } else if(s[i] == '\\') { + s.replace(i, 1, "\\\\"); ++i; } } diff --git a/src/printer/smt2/smt2_printer.cpp b/src/printer/smt2/smt2_printer.cpp index 72ce3804d..ebec37031 100644 --- a/src/printer/smt2/smt2_printer.cpp +++ b/src/printer/smt2/smt2_printer.cpp @@ -281,6 +281,32 @@ void Smt2Printer::toStream(std::ostream& out, TNode n, case kind::APPLY_SELECTOR: break; + // quantifiers + case kind::FORALL: out << "forall "; break; + case kind::EXISTS: out << "exists "; break; + case kind::BOUND_VAR_LIST: + out << '('; + for(TNode::iterator i = n.begin(), + iend = n.end(); + i != iend; ) { + out << '('; + (*i).toStream(out, toDepth < 0 ? toDepth : toDepth - 1, + types, language::output::LANG_SMTLIB_V2); + out << ' '; + (*i).getType().toStream(out, toDepth < 0 ? toDepth : toDepth - 1, + false, language::output::LANG_SMTLIB_V2); + out << ')'; + if(++i != iend) { + out << ' '; + } + } + out << ')'; + return; + case kind::INST_PATTERN: + case kind::INST_PATTERN_LIST: + // TODO user patterns + 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 diff --git a/src/prop/minisat/core/Solver.cc b/src/prop/minisat/core/Solver.cc index a260a8ca1..697ab4853 100644 --- a/src/prop/minisat/core/Solver.cc +++ b/src/prop/minisat/core/Solver.cc @@ -351,7 +351,7 @@ void Solver::cancelUntil(int level) { if(intro_level(x) != -1) {// might be unregistered assigns [x] = l_Undef; vardata[x].trail_index = -1; - if (phase_saving > 1 || (phase_saving == 1) && c > trail_lim.last()) + if ((phase_saving > 1 || (phase_saving == 1) && c > trail_lim.last()) && (polarity[x] & 0x2) == 0) polarity[x] = sign(trail[c]); insertVarOrder(x); } @@ -359,6 +359,7 @@ void Solver::cancelUntil(int level) { qhead = trail_lim[level]; trail.shrink(trail.size() - trail_lim[level]); trail_lim.shrink(trail_lim.size() - level); + flipped.shrink(flipped.size() - level); // Register variables that have not been registered yet int currentLevel = decisionLevel(); @@ -442,7 +443,7 @@ Lit Solver::pickBranchLit() return mkLit(next, (dec_pol == l_True) ); } // If it can't use internal heuristic to do that - return mkLit(next, rnd_pol ? drand(random_seed) < 0.5 : polarity[next]); + return mkLit(next, rnd_pol ? drand(random_seed) < 0.5 : (polarity[next] & 0x1)); } } @@ -1461,7 +1462,7 @@ void Solver::pop() Debug("minisat") << "== unassigning " << l << std::endl; Var x = var(l); assigns [x] = l_Undef; - if (phase_saving >= 1) + if (phase_saving >= 1 && (polarity[x] & 0x2) == 0) polarity[x] = sign(l); insertVarOrder(x); trail_user.pop(); @@ -1489,8 +1490,46 @@ void Solver::renewVar(Lit lit, int level) { Var v = var(lit); vardata[v].intro_level = (level == -1 ? getAssertionLevel() : level); setDecisionVar(v, true); + // explicitly not resetting polarity phase-locking here } +bool Solver::flipDecision() { + Debug("flipdec") << "FLIP: decision level is " << decisionLevel() << std::endl; + if(decisionLevel() == 0) { + Debug("flipdec") << "FLIP: no decisions, returning false" << std::endl; + return false; + } + + // find the level to cancel until + int level = trail_lim.size() - 1; + Debug("flipdec") << "FLIP: looking at level " << level << " dec is " << trail[trail_lim[level]] << " flippable?" << ((polarity[var(trail[trail_lim[level]])] & 0x2) == 0 ? 1 : 0) << " flipped?" << flipped[level] << std::endl; + while(level > 0 && (flipped[level] || /* phase-locked */ (polarity[var(trail[trail_lim[level]])] & 0x2) != 0)) { + --level; + Debug("flipdec") << "FLIP: looking at level " << level << " dec is " << trail[trail_lim[level]] << " flippable?" << ((polarity[var(trail[trail_lim[level]])] & 0x2) == 0 ? 2 : 0) << " flipped?" << flipped[level] << std::endl; + } + if(level < 0) { + Lit l = trail[trail_lim[0]]; + Debug("flipdec") << "FLIP: canceling everything, flipping root decision " << l << std::endl; + cancelUntil(0); + newDecisionLevel(); + Debug("flipdec") << "FLIP: enqueuing " << ~l << std::endl; + uncheckedEnqueue(~l); + flipped[0] = true; + Debug("flipdec") << "FLIP: returning false" << std::endl; + return false; + } + Lit l = trail[trail_lim[level]]; + Debug("flipdec") << "FLIP: canceling to level " << level << ", flipping decision " << l << std::endl; + cancelUntil(level); + newDecisionLevel(); + Debug("flipdec") << "FLIP: enqueuing " << ~l << std::endl; + uncheckedEnqueue(~l); + flipped[level] = true; + Debug("flipdec") << "FLIP: returning true" << std::endl; + return true; +} + + CRef Solver::updateLemmas() { Debug("minisat::lemmas") << "Solver::updateLemmas()" << std::endl; diff --git a/src/prop/minisat/core/Solver.h b/src/prop/minisat/core/Solver.h index e677d7220..fdc9a98b7 100644 --- a/src/prop/minisat/core/Solver.h +++ b/src/prop/minisat/core/Solver.h @@ -54,7 +54,7 @@ class Solver { /** The only two CVC4 entry points to the private solver data */ friend class CVC4::prop::TheoryProxy; - friend class CVC4::SatProof; + friend class CVC4::SatProof; protected: /** The pointer to the proxy that provides interfaces to the SMT engine */ @@ -64,7 +64,7 @@ protected: CVC4::context::Context* context; /** The current assertion level (user) */ - int assertionLevel; + int assertionLevel; /** Variable representing true */ Var varTrue; @@ -77,7 +77,7 @@ public: int getAssertionLevel() const { return assertionLevel; } protected: /** Do we allow incremental solving */ - bool enable_incremental; + bool enable_incremental; /** Literals propagated by lemmas */ vec< vec<Lit> > lemmas; @@ -89,7 +89,7 @@ protected: bool recheck; /** Shrink 'cs' to contain only clauses below given level */ - void removeClausesAboveLevel(vec<CRef>& cs, int level); + void removeClausesAboveLevel(vec<CRef>& cs, int level); /** True if we are currently solving. */ bool minisat_busy; @@ -182,11 +182,13 @@ public: void toDimacs (const char* file, Lit p); void toDimacs (const char* file, Lit p, Lit q); void toDimacs (const char* file, Lit p, Lit q, Lit r); - + // Variable mode: - // + // void setPolarity (Var v, bool b); // Declare which polarity the decision heuristic should use for a variable. Requires mode 'polarity_user'. + void freezePolarity (Var v, bool b); // Declare which polarity the decision heuristic MUST ALWAYS use for a variable. Requires mode 'polarity_user'. void setDecisionVar (Var v, bool b); // Declare if a variable should be eligible for selection in the decision heuristic. + bool flipDecision (); // Backtrack and flip most recent decision // Read state: // @@ -199,6 +201,7 @@ public: int nLearnts () const; // The current number of learnt clauses. int nVars () const; // The current number of variables. int nFreeVars () const; + bool isDecision (Var x) const; // is the given var a decision? // Debugging SMT explanations // @@ -290,8 +293,9 @@ protected: OccLists<Lit, vec<Watcher>, WatcherDeleted> watches; // 'watches[lit]' is a list of constraints watching 'lit' (will go there if literal becomes true). vec<lbool> assigns; // The current assignments. - vec<char> polarity; // The preferred polarity of each variable. + vec<char> polarity; // The preferred polarity of each variable (bit 0) and whether it's locked (bit 1). vec<char> decision; // Declares if a variable is eligible for selection in the decision heuristic. + vec<int> flipped; // Which trail_lim decisions have been flipped in this context. vec<Lit> trail; // Assignment stack; stores all assigments made in the order they were made. vec<int> trail_lim; // Separator indices for different decision levels in 'trail'. vec<Lit> trail_user; // Stack of assignments to UNdo on user pop. @@ -423,6 +427,8 @@ inline bool Solver::isPropagated(Var x) const { return vardata[x].reason != CRef inline bool Solver::isPropagatedBy(Var x, const Clause& c) const { return vardata[x].reason != CRef_Undef && vardata[x].reason != CRef_Lazy && ca.lea(vardata[var(c[0])].reason) == &c; } +inline bool Solver::isDecision(Var x) const { Debug("minisat") << "var " << x << " is a decision iff " << (vardata[x].reason == CRef_Undef) << " && " << level(x) << " > 0" << std::endl; return vardata[x].reason == CRef_Undef && level(x) > 0; } + inline int Solver::level (Var x) const { return vardata[x].level; } inline int Solver::intro_level(Var x) const { return vardata[x].intro_level; } @@ -466,7 +472,7 @@ inline bool Solver::addClause (Lit p, bool removable) inline bool Solver::addClause (Lit p, Lit q, bool removable) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); return addClause_(add_tmp, removable); } inline bool Solver::addClause (Lit p, Lit q, Lit r, bool removable) { add_tmp.clear(); add_tmp.push(p); add_tmp.push(q); add_tmp.push(r); return addClause_(add_tmp, removable); } inline bool Solver::locked (const Clause& c) const { return value(c[0]) == l_True && isPropagatedBy(var(c[0]), c); } -inline void Solver::newDecisionLevel() { trail_lim.push(trail.size()); context->push(); if(Dump.isOn("state")) { Dump("state") << CVC4::PushCommand(); } } +inline void Solver::newDecisionLevel() { trail_lim.push(trail.size()); flipped.push(false); context->push(); if(Dump.isOn("state")) { Dump("state") << CVC4::PushCommand(); } } inline int Solver::decisionLevel () const { return trail_lim.size(); } inline uint32_t Solver::abstractLevel (Var x) const { return 1 << (level(x) & 31); } @@ -481,14 +487,16 @@ inline int Solver::nVars () const { return vardata.size(); } inline int Solver::nFreeVars () const { return (int)dec_vars - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]); } inline bool Solver::properExplanation(Lit l, Lit expl) const { return value(l) == l_True && value(expl) == l_True && trail_index(var(expl)) < trail_index(var(l)); } inline void Solver::setPolarity (Var v, bool b) { polarity[v] = b; } -inline void Solver::setDecisionVar(Var v, bool b) -{ - if ( b && !decision[v]) dec_vars++; - else if (!b && decision[v]) dec_vars--; +inline void Solver::freezePolarity(Var v, bool b) { polarity[v] = int(b) | 0x2; } +inline void Solver::setDecisionVar(Var v, bool b) +{ + if ( b && !decision[v] ) dec_vars++; + else if (!b && decision[v] ) dec_vars--; decision[v] = b; insertVarOrder(v); } + inline void Solver::setConfBudget(int64_t x){ conflict_budget = conflicts + x; } inline void Solver::setPropBudget(int64_t x){ propagation_budget = propagations + x; } inline void Solver::interrupt(){ asynch_interrupt = true; } diff --git a/src/prop/minisat/minisat.cpp b/src/prop/minisat/minisat.cpp index 6b02153c7..6fa698bd0 100644 --- a/src/prop/minisat/minisat.cpp +++ b/src/prop/minisat/minisat.cpp @@ -178,6 +178,22 @@ bool MinisatSatSolver::properExplanation(SatLiteral lit, SatLiteral expl) const return true; } +void MinisatSatSolver::requirePhase(SatLiteral lit) { + Assert(!d_minisat->rnd_pol); + Debug("minisat") << "requirePhase(" << lit << ")" << " " << lit.getSatVariable() << " " << lit.isNegated() << std::endl; + SatVariable v = lit.getSatVariable(); + d_minisat->freezePolarity(v, lit.isNegated()); +} + +bool MinisatSatSolver::flipDecision() { + Debug("minisat") << "flipDecision()" << std::endl; + return d_minisat->flipDecision(); +} + +bool MinisatSatSolver::isDecision(SatVariable decn) const { + return d_minisat->isDecision( decn ); +} + /** Incremental interface */ unsigned MinisatSatSolver::getAssertionLevel() const { diff --git a/src/prop/minisat/minisat.h b/src/prop/minisat/minisat.h index 9171bf232..f87e4ae53 100644 --- a/src/prop/minisat/minisat.h +++ b/src/prop/minisat/minisat.h @@ -84,6 +84,12 @@ public: void renewVar(SatLiteral lit, int level = -1); + void requirePhase(SatLiteral lit); + + bool flipDecision(); + + bool isDecision(SatVariable decn) const; + class Statistics { private: ReferenceStat<uint64_t> d_statStarts, d_statDecisions; diff --git a/src/prop/prop_engine.cpp b/src/prop/prop_engine.cpp index 702a530cf..f3904549f 100644 --- a/src/prop/prop_engine.cpp +++ b/src/prop/prop_engine.cpp @@ -126,6 +126,24 @@ void PropEngine::assertLemma(TNode node, bool negated, bool removable) { d_cnfStream->convertAndAssert(node, removable, negated); } +void PropEngine::requirePhase(TNode n, bool phase) { + Debug("prop") << "requirePhase(" << n << ", " << phase << ")" << endl; + + Assert(n.getType().isBoolean()); + SatLiteral lit = d_cnfStream->getLiteral(n); + d_satSolver->requirePhase(phase ? lit : ~lit); +} + +bool PropEngine::flipDecision() { + Debug("prop") << "flipDecision()" << endl; + return d_satSolver->flipDecision(); +} + +bool PropEngine::isDecision(Node lit) const { + Assert(isTranslatedSatLiteral(lit)); + return d_satSolver->isDecision(d_cnfStream->getLiteral(lit).getSatVariable()); +} + void PropEngine::printSatisfyingAssignment(){ const CnfStream::TranslationCache& transCache = d_cnfStream->getTranslationCache(); diff --git a/src/prop/prop_engine.h b/src/prop/prop_engine.h index 603cdb0e6..dcfc5a9df 100644 --- a/src/prop/prop_engine.h +++ b/src/prop/prop_engine.h @@ -194,6 +194,37 @@ public: void assertLemma(TNode node, bool negated, bool removable); /** + * If ever n is decided upon, it must be in the given phase. This + * occurs *globally*, i.e., even if the literal is untranslated by + * user pop and retranslated, it keeps this phase. The associated + * variable will _always_ be phase-locked. + * + * @param n the node in question; must have an associated SAT literal + * @param phase the phase to use + */ + void requirePhase(TNode n, bool phase); + + /** + * Backtracks to and flips the most recent unflipped decision, and + * returns TRUE. If the decision stack is nonempty but all + * decisions have been flipped already, the state is backtracked to + * the root decision, which is re-flipped to the original phase (and + * FALSE is returned). If the decision stack is empty, the state is + * unchanged and FALSE is returned. + * + * @return true if a decision was flipped as requested; false if the + * root decision was reflipped, or if no decisions are on the stack. + */ + bool flipDecision(); + + /** + * Return whether the given literal is a SAT decision. Either phase + * is permitted; that is, if "lit" is a SAT decision, this function + * returns true for both lit and the negation of lit. + */ + bool isDecision(Node lit) const; + + /** * Checks the current context for satisfiability. * * @param millis the time limit for this call in milliseconds diff --git a/src/prop/sat_solver.h b/src/prop/sat_solver.h index 2865f2cb5..79e54cac2 100644 --- a/src/prop/sat_solver.h +++ b/src/prop/sat_solver.h @@ -123,6 +123,12 @@ public: virtual bool properExplanation(SatLiteral lit, SatLiteral expl) const = 0; + virtual void requirePhase(SatLiteral lit) = 0; + + virtual bool flipDecision() = 0; + + virtual bool isDecision(SatVariable decn) const = 0; + }; }/* prop namespace */ diff --git a/src/theory/Makefile.am b/src/theory/Makefile.am index 85d6fbdf8..19e7d588a 100644 --- a/src/theory/Makefile.am +++ b/src/theory/Makefile.am @@ -3,7 +3,7 @@ AM_CPPFLAGS = \ -I@srcdir@/../include -I@srcdir@/.. -I@builddir@/.. AM_CXXFLAGS = -Wall -Wno-unknown-pragmas $(FLAG_VISIBILITY_HIDDEN) -SUBDIRS = builtin booleans uf arith arrays bv datatypes +SUBDIRS = builtin booleans uf arith arrays bv datatypes quantifiers rewriterules DIST_SUBDIRS = $(SUBDIRS) example noinst_LTLIBRARIES = libtheory.la @@ -33,10 +33,20 @@ libtheory_la_SOURCES = \ ite_simplifier.h \ ite_simplifier.cpp \ unconstrained_simplifier.h \ - unconstrained_simplifier.cpp + unconstrained_simplifier.cpp \ + quantifiers_engine.h \ + quantifiers_engine.cpp \ + instantiator_default.h \ + instantiator_default.cpp \ + inst_match.h \ + inst_match_impl.h \ + inst_match.cpp \ + trigger.h \ + trigger.cpp nodist_libtheory_la_SOURCES = \ rewriter_tables.h \ + instantiator_tables.cpp \ theory_traits.h libtheory_la_LIBADD = \ @@ -46,21 +56,27 @@ libtheory_la_LIBADD = \ @builddir@/arith/libarith.la \ @builddir@/arrays/libarrays.la \ @builddir@/bv/libbv.la \ - @builddir@/datatypes/libdatatypes.la + @builddir@/datatypes/libdatatypes.la \ + @builddir@/quantifiers/libquantifiers.la \ + @builddir@/rewriterules/librewriterules.la EXTRA_DIST = \ rewriter_tables_template.h \ + instantiator_tables_template.cpp \ theory_traits_template.h \ mktheorytraits \ mkrewriter \ + mkinstantiator \ Makefile.subdirs BUILT_SOURCES = \ rewriter_tables.h \ + instantiator_tables.cpp \ theory_traits.h CLEANFILES = \ rewriter_tables.h \ + instantiator_tables.cpp \ theory_traits.h include @top_srcdir@/src/theory/Makefile.subdirs @@ -73,6 +89,14 @@ rewriter_tables.h: rewriter_tables_template.h mkrewriter @top_builddir@/src/theo `cat @top_builddir@/src/theory/.subdirs` \ > $@) || (rm -f $@ && exit 1) +instantiator_tables.cpp: instantiator_tables_template.cpp mkinstantiator @top_builddir@/src/theory/.subdirs @top_srcdir@/src/theory/*/kinds + $(AM_V_at)chmod +x @srcdir@/mkinstantiator + $(AM_V_at)$(am__mv) $@ $@~ 2>/dev/null || true + $(AM_V_GEN)(@srcdir@/mkinstantiator \ + $< \ + `cat @top_builddir@/src/theory/.subdirs` \ + > $@) || (rm -f $@ && exit 1) + theory_traits.h: theory_traits_template.h mktheorytraits @top_builddir@/src/theory/.subdirs @top_srcdir@/src/theory/*/kinds $(AM_V_at)chmod +x @srcdir@/mktheorytraits $(AM_V_at)$(am__mv) $@ $@~ 2>/dev/null || true diff --git a/src/theory/arith/Makefile.am b/src/theory/arith/Makefile.am index a029bc97b..b1e8855c7 100644 --- a/src/theory/arith/Makefile.am +++ b/src/theory/arith/Makefile.am @@ -36,7 +36,9 @@ libarith_la_SOURCES = \ theory_arith.h \ theory_arith.cpp \ dio_solver.h \ - dio_solver.cpp + dio_solver.cpp \ + theory_arith_instantiator.h \ + theory_arith_instantiator.cpp EXTRA_DIST = \ kinds diff --git a/src/theory/arith/congruence_manager.h b/src/theory/arith/congruence_manager.h index 5f49ab3ab..63a370f9a 100644 --- a/src/theory/arith/congruence_manager.h +++ b/src/theory/arith/congruence_manager.h @@ -73,7 +73,12 @@ private: d_acm.propagate(t1.eqNode(t2)); } } - }; + + void eqNotifyNewClass(TNode t) { } + void eqNotifyPreMerge(TNode t1, TNode t2) { } + void eqNotifyPostMerge(TNode t1, TNode t2) { } + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { } + }; ArithCongruenceNotify d_notify; context::CDList<Node> d_keepAlive; diff --git a/src/theory/arith/kinds b/src/theory/arith/kinds index 8ffe68376..c06cbc140 100644 --- a/src/theory/arith/kinds +++ b/src/theory/arith/kinds @@ -6,6 +6,7 @@ theory THEORY_ARITH ::CVC4::theory::arith::TheoryArith "theory/arith/theory_arith.h" typechecker "theory/arith/theory_arith_type_rules.h" +instantiator ::CVC4::theory::arith::InstantiatorTheoryArith "theory/arith/theory_arith_instantiator.h" properties stable-infinite properties check propagate staticLearning presolve notifyRestart diff --git a/src/theory/arith/theory_arith.cpp b/src/theory/arith/theory_arith.cpp index ac9796986..9ff9ceeb9 100644 --- a/src/theory/arith/theory_arith.cpp +++ b/src/theory/arith/theory_arith.cpp @@ -53,8 +53,8 @@ namespace arith { const uint32_t RESET_START = 2; -TheoryArith::TheoryArith(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) : - Theory(THEORY_ARITH, c, u, out, valuation, logicInfo), +TheoryArith::TheoryArith(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) : + Theory(THEORY_ARITH, c, u, out, valuation, logicInfo, qe), d_hasDoneWorkSinceCut(false), d_learner(d_pbSubstitutions), d_setupLiteralCallback(this), diff --git a/src/theory/arith/theory_arith.h b/src/theory/arith/theory_arith.h index ebc131b60..1f0120387 100644 --- a/src/theory/arith/theory_arith.h +++ b/src/theory/arith/theory_arith.h @@ -53,12 +53,15 @@ namespace CVC4 { namespace theory { namespace arith { +class InstantiatorTheoryArith; + /** * Implementation of QF_LRA. * Based upon: * http://research.microsoft.com/en-us/um/people/leonardo/cav06.pdf */ class TheoryArith : public Theory { + friend class InstantiatorTheoryArith; private: bool rowImplication(ArithVar v, bool upperBound, const DeltaRational& r); @@ -269,7 +272,7 @@ private: DeltaRational getDeltaValue(TNode n); public: - TheoryArith(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo); + TheoryArith(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe); virtual ~TheoryArith(); /** diff --git a/src/theory/arith/theory_arith_instantiator.cpp b/src/theory/arith/theory_arith_instantiator.cpp new file mode 100644 index 000000000..48c8a30ee --- /dev/null +++ b/src/theory/arith/theory_arith_instantiator.cpp @@ -0,0 +1,448 @@ +/********************* */ +/*! \file instantiator_arith_instantiator.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of instantiator_arith_instantiator class + **/ + +#include "theory/arith/theory_arith_instantiator.h" +#include "theory/arith/theory_arith.h" +#include "theory/theory_engine.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::arith; + +#define ARITH_INSTANTIATOR_USE_DELTA +#define ARITH_INSTANTIATOR_USE_MINUS_DELTA +#define ARITH_INSTANTIATOR_STRONG_DELTA_LEMMA + +#define USE_ARITH_INSTANTIATION + +InstStrategySimplex::InstStrategySimplex( InstantiatorTheoryArith* th, QuantifiersEngine* ie ) : + InstStrategy( ie ), d_th( th ), d_counter( 0 ){ + d_negOne = NodeManager::currentNM()->mkConst( Rational(-1) ); +} + +void InstStrategySimplex::processResetInstantiationRound( Theory::Effort effort ){ + d_counter++; +} + +int InstStrategySimplex::process( Node f, Theory::Effort effort, int e, int instLimit ){ + if( e<2 ){ + return STATUS_UNFINISHED; + }else if( e==2 ){ + //Notice() << f << std::endl; + //Notice() << "Num inst rows = " << d_th->d_instRows[f].size() << std::endl; + //Notice() << "Num inst constants = " << d_quantEngine->getNumInstantiationConstants( f ) << std::endl; + Debug("quant-arith-simplex") << "InstStrategySimplex check " << f << ", rows = " << d_th->d_instRows[f].size() << std::endl; + for( int j=0; j<(int)d_th->d_instRows[f].size(); j++ ){ + ArithVar x = d_th->d_instRows[f][j]; + if( !d_th->d_ceTableaux[x].empty() ){ + Debug("quant-arith-simplex") << "Check row " << x << std::endl; + //instantiation row will be A*e + B*t = beta, + // where e is a vector of terms , and t is vector of ground terms. + // Say one term in A*e is coeff*e_i, where e_i is an instantiation constant + // We will construct the term ( beta - B*t)/coeff to use for e_i. + InstMatch m; + //By default, choose the first instantiation constant to be e_i. + Node var = d_th->d_ceTableaux[x].begin()->first; + if( var.getType().isInteger() ){ + std::map< Node, Node >::iterator it = d_th->d_ceTableaux[x].begin(); + //try to find coefficent that is +/- 1 + while( !var.isNull() && !d_th->d_ceTableaux[x][var].isNull() && d_th->d_ceTableaux[x][var]!=d_negOne ){ + ++it; + if( it==d_th->d_ceTableaux[x].end() ){ + var = Node::null(); + }else{ + var = it->first; + } + } + //otherwise, try one that divides all ground term coefficients? DO_THIS + } + if( !var.isNull() ){ + Debug("quant-arith-simplex") << "Instantiate with var " << var << std::endl; + d_th->doInstantiation( f, d_th->d_tableaux_term[x], x, m, var ); + }else{ + Debug("quant-arith-simplex") << "Could not find var." << std::endl; + } + ////choose a new variable based on alternation strategy + //int index = d_counter%(int)d_th->d_ceTableaux[x].size(); + //Node var; + //for( std::map< Node, Node >::iterator it = d_th->d_ceTableaux[x].begin(); it != d_th->d_ceTableaux[x].end(); ++it ){ + // if( index==0 ){ + // var = it->first; + // break; + // } + // index--; + //} + //d_th->doInstantiation( f, d_th->d_tableaux_term[x], x, &m, var ); + } + } + } + return STATUS_UNKNOWN; +} + +//void InstStrategySimplexUfMatch::resetInstantiationRound(){ +// +//} +// +//int InstStrategySimplexUfMatch::process( Node f, int effort, int instLimit ){ +// if( effort<2 ){ +// return STATUS_UNFINISHED; +// }else if( effort==2 ){ +// for( int j=0; j<(int)d_th->d_instRows[f].size(); j++ ){ +// ArithVar x = d_th->d_instRows[f][j]; +// if( !d_th->d_ceTableaux[x].empty() && !d_th->d_tableaux_ce_term[x].empty() ){ +// if( d_tableaux_ce_term_trigger.find( x )==d_tableaux_ce_term_trigger.end() ){ +// std::vector< Node > terms; +// for( std::map< Node, Node >::iterator it = d_th->d_tableaux_ce_term[x].begin(); it != d_th->d_tableaux_ce_term[x].end(); ++it ){ +// terms.push_back( it->first ); +// } +// d_tableaux_ce_term_trigger[x] = new Trigger( d_quantEngine, f, terms ); +// }else{ +// d_tableaux_ce_term_trigger[x]->resetInstantiationRound(); +// } +// Node term; +// bool addedLemma = false; +// while( d_tableaux_ce_term_trigger[x]->getNextMatch() && !addedLemma ){ +// InstMatch* m = d_tableaux_ce_term_trigger[x]->getCurrent(); +// if( m->isComplete( f ) ){ +// if( d_quantEngine->addInstantiation( f, m, true ) ){ +// ++(d_th->d_statistics.d_instantiations_match_pure); +// ++(d_th->d_statistics.d_instantiations); +// addedLemma = true; +// } +// }else{ +// NodeBuilder<> plus_term(kind::PLUS); +// plus_term << d_th->d_tableaux_term[x]; +// //Debug("quant-arith") << "Produced this match for ce_term_tableaux: " << std::endl; +// //m->debugPrint("quant-arith"); +// //Debug("quant-arith") << std::endl; +// std::vector< Node > vars; +// std::vector< Node > matches; +// for( int i=0; i<d_quantEngine->getNumInstantiationConstants( f ); i++ ){ +// Node ic = d_quantEngine->getInstantiationConstant( f, i ); +// if( m->d_map[ ic ]!=Node::null() ){ +// vars.push_back( ic ); +// matches.push_back( m->d_map[ ic ] ); +// } +// } +// Node var; +// //otherwise try to find a variable that is not specified in m +// for( std::map< Node, Node >::iterator it = d_th->d_ceTableaux[x].begin(); it != d_th->d_ceTableaux[x].end(); ++it ){ +// if( m->d_map[ it->first ]!=Node::null() ){ +// plus_term << NodeManager::currentNM()->mkNode( MULT, it->second, d_th->getTableauxValue( m->d_map[ it->first ] ) ); +// }else if( var==Node::null() ){ +// var = it->first; +// } +// } +// for( std::map< Node, Node >::iterator it = d_th->d_tableaux_ce_term[x].begin(); it != d_th->d_tableaux_ce_term[x].end(); ++it ){ +// Node n = it->first; +// //substitute in matches +// n = n.substitute( vars.begin(), vars.end(), matches.begin(), matches.end() ); +// plus_term << NodeManager::currentNM()->mkNode( MULT, it->second, d_th->getTableauxValue( n ) ); +// } +// term = plus_term.getNumChildren()==1 ? plus_term.getChild( 0 ) : plus_term; +// if( var!=Node::null() ){ +// if( d_th->doInstantiation( f, term, x, m, var ) ){ +// addedLemma = true; +// ++(d_th->d_statistics.d_instantiations_match_var); +// } +// }else{ +// if( d_quantEngine->addInstantiation( f, m, true ) ){ +// addedLemma = true; +// ++(d_th->d_statistics.d_instantiations_match_no_var); +// ++(d_th->d_statistics.d_instantiations); +// } +// } +// } +// } +// } +// } +// } +// return STATUS_UNKNOWN; +//} + +InstantiatorTheoryArith::InstantiatorTheoryArith(context::Context* c, QuantifiersEngine* ie, Theory* th) : +Instantiator( c, ie, th ){ + if( Options::current()->cbqi ){ + addInstStrategy( new InstStrategySimplex( this, d_quantEngine ) ); + } +} + +void InstantiatorTheoryArith::preRegisterTerm( Node t ){ + +} + +void InstantiatorTheoryArith::assertNode( Node assertion ){ + Debug("quant-arith-assert") << "InstantiatorTheoryArith::check: " << assertion << std::endl; + d_quantEngine->addTermToDatabase( assertion ); + if( Options::current()->cbqi ){ + if( assertion.hasAttribute(InstConstantAttribute()) ){ + setHasConstraintsFrom( assertion.getAttribute(InstConstantAttribute()) ); + }else if( assertion.getKind()==NOT && assertion[0].hasAttribute(InstConstantAttribute()) ){ + setHasConstraintsFrom( assertion[0].getAttribute(InstConstantAttribute()) ); + } + } +} + +void InstantiatorTheoryArith::processResetInstantiationRound( Theory::Effort effort ){ + if( Options::current()->cbqi ){ + Debug("quant-arith") << "Setting up simplex for instantiator... " << std::endl; + d_instRows.clear(); + d_tableaux_term.clear(); + d_tableaux.clear(); + d_ceTableaux.clear(); + //search for instantiation rows in simplex tableaux + ArithVarToNodeMap avtnm = ((TheoryArith*)getTheory())->d_arithvarNodeMap.getArithVarToNodeMap(); + for( ArithVarToNodeMap::iterator it = avtnm.begin(); it != avtnm.end(); ++it ){ + ArithVar x = (*it).first; + if( ((TheoryArith*)getTheory())->d_partialModel.hasEitherBound( x ) ){ + Node n = (*it).second; + Node f; + NodeBuilder<> t(kind::PLUS); + if( n.getKind()==PLUS ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + addTermToRow( x, n[i], f, t ); + } + }else{ + addTermToRow( x, n, f, t ); + } + if( f!=Node::null() ){ + d_instRows[f].push_back( x ); + //this theory has constraints from f + Debug("quant-arith") << "Has constraints from " << f << std::endl; + setHasConstraintsFrom( f ); + //set tableaux term + if( t.getNumChildren()==0 ){ + d_tableaux_term[x] = NodeManager::currentNM()->mkConst( Rational(0) ); + }else if( t.getNumChildren()==1 ){ + d_tableaux_term[x] = t.getChild( 0 ); + }else{ + d_tableaux_term[x] = t; + } + } + } + } + //print debug + debugPrint( "quant-arith-debug" ); + } +} + +int InstantiatorTheoryArith::process( Node f, Theory::Effort effort, int e, int instLimit ){ + Debug("quant-arith") << "Arith: Try to solve (" << effort << ") for " << f << "... " << std::endl; + return InstStrategy::STATUS_UNKNOWN; +} + +void InstantiatorTheoryArith::addTermToRow( ArithVar x, Node n, Node& f, NodeBuilder<>& t ){ + if( n.getKind()==MULT ){ + if( n[1].hasAttribute(InstConstantAttribute()) ){ + f = n[1].getAttribute(InstConstantAttribute()); + if( n[1].getKind()==INST_CONSTANT ){ + d_ceTableaux[x][ n[1] ] = n[0]; + }else{ + d_tableaux_ce_term[x][ n[1] ] = n[0]; + } + }else{ + d_tableaux[x][ n[1] ] = n[0]; + t << n; + } + }else{ + if( n.hasAttribute(InstConstantAttribute()) ){ + f = n.getAttribute(InstConstantAttribute()); + if( n.getKind()==INST_CONSTANT ){ + d_ceTableaux[x][ n ] = Node::null(); + }else{ + d_tableaux_ce_term[x][ n ] = NodeManager::currentNM()->mkConst( Rational(1) ); + } + }else{ + d_tableaux[x][ n ] = NodeManager::currentNM()->mkConst( Rational(1) ); + t << n; + } + } +} + +void InstantiatorTheoryArith::debugPrint( const char* c ){ + ArithVarToNodeMap avtnm = ((TheoryArith*)getTheory())->d_arithvarNodeMap.getArithVarToNodeMap(); + for( ArithVarToNodeMap::iterator it = avtnm.begin(); it != avtnm.end(); ++it ){ + ArithVar x = (*it).first; + Node n = (*it).second; + //if( ((TheoryArith*)getTheory())->d_partialModel.hasEitherBound( x ) ){ + Debug(c) << x << " : " << n << ", bounds = "; + if( ((TheoryArith*)getTheory())->d_partialModel.hasLowerBound( x ) ){ + Debug(c) << ((TheoryArith*)getTheory())->d_partialModel.getLowerBound( x ); + }else{ + Debug(c) << "-infty"; + } + Debug(c) << " <= "; + Debug(c) << ((TheoryArith*)getTheory())->d_partialModel.getAssignment( x ); + Debug(c) << " <= "; + if( ((TheoryArith*)getTheory())->d_partialModel.hasUpperBound( x ) ){ + Debug(c) << ((TheoryArith*)getTheory())->d_partialModel.getUpperBound( x ); + }else{ + Debug(c) << "+infty"; + } + Debug(c) << std::endl; + //Debug(c) << " Term = " << d_tableaux_term[x] << std::endl; + //Debug(c) << " "; + //for( std::map< Node, Node >::iterator it2 = d_tableaux[x].begin(); it2 != d_tableaux[x].end(); ++it2 ){ + // Debug(c) << "( " << it2->first << ", " << it2->second << " ) "; + //} + //for( std::map< Node, Node >::iterator it2 = d_ceTableaux[x].begin(); it2 != d_ceTableaux[x].end(); ++it2 ){ + // Debug(c) << "(CE)( " << it2->first << ", " << it2->second << " ) "; + //} + //for( std::map< Node, Node >::iterator it2 = d_tableaux_ce_term[x].begin(); it2 != d_tableaux_ce_term[x].end(); ++it2 ){ + // Debug(c) << "(CE-term)( " << it2->first << ", " << it2->second << " ) "; + //} + //Debug(c) << std::endl; + //} + } + Debug(c) << std::endl; + + for( int q=0; q<d_quantEngine->getNumQuantifiers(); q++ ){ + Node f = d_quantEngine->getQuantifier( q ); + Debug(c) << f << std::endl; + Debug(c) << " Inst constants: "; + for( int i=0; i<(int)d_quantEngine->getNumInstantiationConstants( f ); i++ ){ + if( i>0 ){ + Debug( c ) << ", "; + } + Debug( c ) << d_quantEngine->getInstantiationConstant( f, i ); + } + Debug(c) << std::endl; + Debug(c) << " Instantiation rows: "; + for( int i=0; i<(int)d_instRows[f].size(); i++ ){ + if( i>0 ){ + Debug(c) << ", "; + } + Debug(c) << d_instRows[f][i]; + } + Debug(c) << std::endl; + } +} + +//say instantiation row x for quantifier f is coeff*var + A*t[e] + term = beta, +// where var is an instantiation constant from f, +// t[e] is a vector of terms containing instantiation constants from f, +// and term is a ground term (c1*t1 + ... + cn*tn). +// We construct the term ( beta - term )/coeff to use as an instantiation for var. +bool InstantiatorTheoryArith::doInstantiation( Node f, Node term, ArithVar x, InstMatch& m, Node var ){ + //first try +delta + if( doInstantiation2( f, term, x, m, var ) ){ + ++(d_statistics.d_instantiations); + return true; + }else{ +#ifdef ARITH_INSTANTIATOR_USE_MINUS_DELTA + //otherwise try -delta + if( doInstantiation2( f, term, x, m, var, true ) ){ + ++(d_statistics.d_instantiations_minus); + ++(d_statistics.d_instantiations); + return true; + }else{ + return false; + } +#else + return false; +#endif + } +} + +bool InstantiatorTheoryArith::doInstantiation2( Node f, Node term, ArithVar x, InstMatch& m, Node var, bool minus_delta ){ + // make term ( beta - term )/coeff + Node beta = getTableauxValue( x, minus_delta ); + Node instVal = NodeManager::currentNM()->mkNode( MINUS, beta, term ); + if( !d_ceTableaux[x][var].isNull() ){ + if( var.getType().isInteger() ){ + Assert( d_ceTableaux[x][var]==NodeManager::currentNM()->mkConst( Rational(-1) ) ); + instVal = NodeManager::currentNM()->mkNode( MULT, d_ceTableaux[x][var], instVal ); + }else{ + Node coeff = NodeManager::currentNM()->mkConst( Rational(1) / d_ceTableaux[x][var].getConst<Rational>() ); + instVal = NodeManager::currentNM()->mkNode( MULT, coeff, instVal ); + } + } + instVal = Rewriter::rewrite( instVal ); + //use as instantiation value for var + m.d_map[ var ] = instVal; + Debug("quant-arith") << "Add instantiation " << m << std::endl; + return d_quantEngine->addInstantiation( f, m, true ); +} + +Node InstantiatorTheoryArith::getTableauxValue( Node n, bool minus_delta ){ + if( ((TheoryArith*)getTheory())->d_arithvarNodeMap.hasArithVar(n) ){ + ArithVar v = ((TheoryArith*)getTheory())->d_arithvarNodeMap.asArithVar( n ); + return getTableauxValue( v, minus_delta ); + }else{ + return NodeManager::currentNM()->mkConst( Rational(0) ); + } +} + +Node InstantiatorTheoryArith::getTableauxValue( ArithVar v, bool minus_delta ){ + DeltaRational drv = ((TheoryArith*)getTheory())->d_partialModel.getAssignment( v ); + Node val = NodeManager::currentNM()->mkConst( drv.getNoninfinitesimalPart() ); +#ifdef ARITH_INSTANTIATOR_USE_DELTA + //the tableaux value for v may contain an infinitesemal part: getDelta( val ) will return a fresh variable "delta" + // (one for each sort) for which the lemma ( delta > 0 ) is asserted. + if( drv.getInfinitesimalPart()!=0 ){ + Node delta = NodeManager::currentNM()->mkNode( MULT, getDelta( val ), + NodeManager::currentNM()->mkConst( drv.getInfinitesimalPart() ) ); + // add (or subtract) this delta component from the value of v + val = NodeManager::currentNM()->mkNode( minus_delta ? MINUS : PLUS, val, delta ); + } +#endif + return val; +} + +Node InstantiatorTheoryArith::getDelta( Node n ){ + std::map< TypeNode, Node >::iterator it = d_deltas.find( n.getType() ); + if( it==d_deltas.end() ){ + std::ostringstream os; + os << "delta_" << d_deltas.size(); + Node delta = NodeManager::currentNM()->mkVar( os.str(), n.getType() ); + d_deltas[ n.getType() ] = delta; + Node gt = NodeManager::currentNM()->mkNode( GT, delta, NodeManager::currentNM()->mkConst( Rational(0) ) ); + //add split +#ifdef ARITH_INSTANTIATOR_STRONG_DELTA_LEMMA + d_quantEngine->addLemma( gt ); +#else + gt = Rewriter::rewrite( gt ); + d_quantEngine->addSplit( gt, true, true ); +#endif + return delta; + } + return it->second; +} + +InstantiatorTheoryArith::Statistics::Statistics(): + d_instantiations("InstantiatorTheoryArith::Instantiations_Total", 0), + d_instantiations_minus("InstantiatorTheoryArith::Instantiations_minus_delta", 0), + d_instantiations_match_pure("InstantiatorTheoryArith::Instantiations_via_pure_matching", 0), + d_instantiations_match_var("InstantiatorTheoryArith::Instantiations_via_matching_var", 0), + d_instantiations_match_no_var("InstantiatorTheoryArith::Instantiations_via_matching_no_var", 0) +{ + StatisticsRegistry::registerStat(&d_instantiations); + StatisticsRegistry::registerStat(&d_instantiations_minus); + StatisticsRegistry::registerStat(&d_instantiations_match_pure); + StatisticsRegistry::registerStat(&d_instantiations_match_var); + StatisticsRegistry::registerStat(&d_instantiations_match_no_var); +} + +InstantiatorTheoryArith::Statistics::~Statistics(){ + StatisticsRegistry::unregisterStat(&d_instantiations); + StatisticsRegistry::unregisterStat(&d_instantiations_minus); + StatisticsRegistry::unregisterStat(&d_instantiations_match_pure); + StatisticsRegistry::unregisterStat(&d_instantiations_match_var); + StatisticsRegistry::unregisterStat(&d_instantiations_match_no_var); +} diff --git a/src/theory/arith/theory_arith_instantiator.h b/src/theory/arith/theory_arith_instantiator.h new file mode 100644 index 000000000..524d16859 --- /dev/null +++ b/src/theory/arith/theory_arith_instantiator.h @@ -0,0 +1,128 @@ +/********************* */ +/*! \file instantiator_arith_instantiator.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief instantiator_arith_instantiator + **/ + + +#include "cvc4_private.h" + +#ifndef __CVC4__INSTANTIATOR_ARITH_H +#define __CVC4__INSTANTIATOR_ARITH_H + +#include "theory/quantifiers_engine.h" +#include "theory/arith/arithvar_node_map.h" + +#include "util/stats.h" + +namespace CVC4 { +namespace theory { +namespace arith { + +class InstantiatorTheoryArith; + +class InstStrategySimplex : public InstStrategy{ +private: + /** InstantiatorTheoryUf class */ + InstantiatorTheoryArith* d_th; + /** */ + int d_counter; + /** negative one */ + Node d_negOne; + /** process functions */ + void processResetInstantiationRound( Theory::Effort effort ); + int process( Node f, Theory::Effort effort, int e, int instLimit ); +public: + InstStrategySimplex( InstantiatorTheoryArith* th, QuantifiersEngine* ie ); + ~InstStrategySimplex(){} + /** identify */ + std::string identify() const { return std::string("Simplex"); } +}; +// +//class InstStrategySimplexUfMatch : public InstStrategy{ +//private: +// /** InstantiatorTheoryUf class */ +// InstantiatorTheoryArith* d_th; +// /** trigger for instantiation rows */ +// std::map< ArithVar, Trigger* > d_tableaux_ce_term_trigger; +//public: +// InstStrategySimplexUfMatch( InstantiatorTheoryArith* th, QuantifiersEngine* ie ) : +// InstStrategy( ie ), d_th( th ){} +// ~InstStrategySimplexUfMatch(){} +// void resetInstantiationRound(); +// int process( Node f, Theory::Effort effort, int e, int instLimit ); +// /** identify */ +// std::string identify() const { return std::string("SimplexUfMatch"); } +//}; + +class InstantiatorTheoryArith : public Instantiator{ + friend class QuantifiersEngine; + friend class InstStrategySimplex; + friend class InstStrategySimplexUfMatch; +private: + /** delta */ + std::map< TypeNode, Node > d_deltas; + /** for each quantifier, simplex rows */ + std::map< Node, std::vector< ArithVar > > d_instRows; + /** tableaux */ + std::map< ArithVar, Node > d_tableaux_term; + std::map< ArithVar, std::map< Node, Node > > d_tableaux_ce_term; + std::map< ArithVar, std::map< Node, Node > > d_tableaux; + /** ce tableaux */ + std::map< ArithVar, std::map< Node, Node > > d_ceTableaux; + /** get value */ + Node getTableauxValue( Node n, bool minus_delta = false ); + Node getTableauxValue( ArithVar v, bool minus_delta = false ); + /** do instantiation */ + bool doInstantiation( Node f, Node term, ArithVar x, InstMatch& m, Node var ); + bool doInstantiation2( Node f, Node term, ArithVar x, InstMatch& m, Node var, bool minus_delta = false ); +public: + InstantiatorTheoryArith(context::Context* c, QuantifiersEngine* ie, Theory* th); + ~InstantiatorTheoryArith() {} + + /** assertNode function, assertion is asserted to theory */ + void assertNode( Node assertion ); + /** Pre-register a term. Done one time for a Node, ever. */ + void preRegisterTerm( Node t ); + /** identify */ + std::string identify() const { return std::string("InstantiatorTheoryArith"); } + /** print debug */ + void debugPrint( const char* c ); +private: + /** reset instantiation */ + void processResetInstantiationRound( Theory::Effort effort ); + /** process at effort */ + int process( Node f, Theory::Effort effort, int e, int instLimit ); + /** add term to row */ + void addTermToRow( ArithVar x, Node n, Node& f, NodeBuilder<>& t ); + /** get delta for node */ + Node getDelta( Node n ); + + class Statistics { + public: + IntStat d_instantiations; + IntStat d_instantiations_minus; + IntStat d_instantiations_match_pure; + IntStat d_instantiations_match_var; + IntStat d_instantiations_match_no_var; + Statistics(); + ~Statistics(); + }; + Statistics d_statistics; +};/* class InstantiatiorTheoryArith */ + +} +} +} + +#endif
\ No newline at end of file diff --git a/src/theory/arrays/Makefile.am b/src/theory/arrays/Makefile.am index 3dde70145..57c55d765 100644 --- a/src/theory/arrays/Makefile.am +++ b/src/theory/arrays/Makefile.am @@ -15,6 +15,8 @@ libarrays_la_SOURCES = \ array_info.h \ array_info.cpp \ static_fact_manager.h \ - static_fact_manager.cpp + static_fact_manager.cpp \ + theory_arrays_instantiator.h \ + theory_arrays_instantiator.cpp EXTRA_DIST = kinds diff --git a/src/theory/arrays/kinds b/src/theory/arrays/kinds index 06240a315..195a60035 100644 --- a/src/theory/arrays/kinds +++ b/src/theory/arrays/kinds @@ -6,6 +6,7 @@ theory THEORY_ARRAY ::CVC4::theory::arrays::TheoryArrays "theory/arrays/theory_arrays.h" typechecker "theory/arrays/theory_arrays_type_rules.h" +instantiator ::CVC4::theory::arrays::InstantiatorTheoryArrays "theory/arrays/theory_arrays_instantiator.h" properties polite stable-infinite parametric properties check propagate presolve diff --git a/src/theory/arrays/theory_arrays.cpp b/src/theory/arrays/theory_arrays.cpp index 81661acd1..376a7e90f 100644 --- a/src/theory/arrays/theory_arrays.cpp +++ b/src/theory/arrays/theory_arrays.cpp @@ -23,6 +23,7 @@ #include <map> #include "theory/rewriter.h" #include "expr/command.h" +#include "theory/arrays/theory_arrays_instantiator.h" using namespace std; @@ -45,8 +46,8 @@ const bool d_solveWrite2 = false; const bool d_useNonLinearOpt = true; const bool d_eagerIndexSplitting = true; -TheoryArrays::TheoryArrays(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) : - Theory(THEORY_ARRAY, c, u, out, valuation, logicInfo), +TheoryArrays::TheoryArrays(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) : + Theory(THEORY_ARRAY, c, u, out, valuation, logicInfo, qe), d_numRow("theory::arrays::number of Row lemmas", 0), d_numExt("theory::arrays::number of Ext lemmas", 0), d_numProp("theory::arrays::number of propagations", 0), @@ -218,7 +219,7 @@ Node TheoryArrays::ppRewrite(TNode term) { } Node r1 = nm->mkNode(kind::SELECT, e1, index_i); - conc = (r1.getType() == nm->booleanType())? + conc = (r1.getType() == nm->booleanType())? r1.iffNode(write_i[2]) : r1.eqNode(write_i[2]); if (hyp.getNumChildren() != 0) { if (hyp.getNumChildren() == 1) { @@ -582,7 +583,6 @@ void TheoryArrays::computeCareGraph() // Get representative trigger terms TNode x_shared = d_equalityEngine.getTriggerTermRepresentative(x, THEORY_ARRAY); TNode y_shared = d_equalityEngine.getTriggerTermRepresentative(y, THEORY_ARRAY); - EqualityStatus eqStatusDomain = d_valuation.getEqualityStatus(x_shared, y_shared); switch (eqStatusDomain) { case EQUALITY_TRUE_AND_PROPAGATED: @@ -605,6 +605,7 @@ void TheoryArrays::computeCareGraph() break; } + // Otherwise, add this pair Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): adding to care-graph" << std::endl; addCarePair(x_shared, y_shared); diff --git a/src/theory/arrays/theory_arrays.h b/src/theory/arrays/theory_arrays.h index 80fe692c0..1bf42a105 100644 --- a/src/theory/arrays/theory_arrays.h +++ b/src/theory/arrays/theory_arrays.h @@ -39,32 +39,32 @@ namespace arrays { * Overview of decision procedure: * * Preliminary notation: - * Stores(a) = {t | a ~ t and t = store( _ _ _ )} + * Stores(a) = {t | a ~ t and t = store( _ _ _ )} * InStores(a) = {t | t = store (b _ _) and a ~ b } * Indices(a) = {i | there exists a term b[i] such that a ~ b or store(b i v)} * ~ represents the equivalence relation based on the asserted equalities in the * current context. - * + * * The rules implemented are the following: * store(b i v) * Row1 ------------------- * store(b i v)[i] = v - * + * * store(b i v) a'[j] * Row ---------------------- [ a' ~ store(b i v) or a' ~ b ] * i = j OR a[j] = b[j] - * + * * a b same kind arrays * Ext ------------------------ [ a!= b in current context, k new var] * a = b OR a[k] != b[k]p - * - * + * + * * The Row1 one rule is implemented implicitly as follows: * - for each store(b i v) term add the following equality to the congruence * closure store(b i v)[i] = v * - if one of the literals in a conflict is of the form store(b i v)[i] = v * remove it from the conflict - * + * * Because new store terms are not created, we need to check if we need to * instantiate a new Row axiom in the following cases: * 1. the congruence relation changes (i.e. two terms get merged) @@ -77,7 +77,7 @@ namespace arrays { * - this is implemented in the checkRowForIndex method which is called * when preregistering a term of the form a[i]. * - as a consequence lemmas are instantiated even before full effort check - * + * * The Ext axiom is instantiated when a disequality is asserted during full effort * check. Ext lemmas are stored in a cache to prevent instantiating essentially * the same lemma multiple times. @@ -122,7 +122,7 @@ class TheoryArrays : public Theory { public: - TheoryArrays(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo); + TheoryArrays(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe); ~TheoryArrays(); std::string identify() const { return std::string("TheoryArrays"); } @@ -133,6 +133,16 @@ class TheoryArrays : public Theory { private: + // PPNotifyClass: dummy template class for d_ppEqualityEngine - notifications not used + class PPNotifyClass { + public: + bool notify(TNode propagation) { return true; } + void notify(TNode t1, TNode t2) { } + }; + + /** The notify class for d_ppEqualityEngine */ + PPNotifyClass d_ppNotify; + /** Equaltity engine */ eq::EqualityEngine d_ppEqualityEngine; @@ -181,6 +191,15 @@ class TheoryArrays : public Theory { private: + class MayEqualNotifyClass { + public: + bool notify(TNode propagation) { return true; } + void notify(TNode t1, TNode t2) { } + }; + + /** The notify class for d_mayEqualEqualityEngine */ + MayEqualNotifyClass d_mayEqualNotify; + /** Equaltity engine for determining if two arrays might be equal */ eq::EqualityEngine d_mayEqualEqualityEngine; @@ -270,6 +289,11 @@ class TheoryArrays : public Theory { Debug("arrays::propagate") << spaces(d_arrays.getSatContext()->getLevel()) << "NotifyClass::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << ")" << std::endl; d_arrays.conflict(t1, t2); } + + void eqNotifyNewClass(TNode t) { } + void eqNotifyPreMerge(TNode t1, TNode t2) { } + void eqNotifyPostMerge(TNode t1, TNode t2) { } + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { } }; /** The notify class for d_equalityEngine */ diff --git a/src/theory/arrays/theory_arrays_instantiator.cpp b/src/theory/arrays/theory_arrays_instantiator.cpp new file mode 100644 index 000000000..334d70eea --- /dev/null +++ b/src/theory/arrays/theory_arrays_instantiator.cpp @@ -0,0 +1,56 @@ +/********************* */ +/*! \file theory_arrays_instantiator.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of theory_arrays_instantiator class + **/ + +#include "theory/theory_engine.h" +#include "theory/arrays/theory_arrays_instantiator.h" +#include "theory/arrays/theory_arrays.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::arrays; + +InstantiatorTheoryArrays::InstantiatorTheoryArrays(context::Context* c, QuantifiersEngine* ie, Theory* th) : +Instantiator( c, ie, th ){ + +} + +void InstantiatorTheoryArrays::preRegisterTerm( Node t ){ + +} + +void InstantiatorTheoryArrays::assertNode( Node assertion ){ + Debug("quant-arrays-assert") << "InstantiatorTheoryArrays::assertNode: " << assertion << std::endl; + d_quantEngine->addTermToDatabase( assertion ); + if( Options::current()->cbqi ){ + if( assertion.hasAttribute(InstConstantAttribute()) ){ + setHasConstraintsFrom( assertion.getAttribute(InstConstantAttribute()) ); + }else if( assertion.getKind()==NOT && assertion[0].hasAttribute(InstConstantAttribute()) ){ + setHasConstraintsFrom( assertion[0].getAttribute(InstConstantAttribute()) ); + } + } +} + + +void InstantiatorTheoryArrays::processResetInstantiationRound( Theory::Effort effort ){ + +} + +int InstantiatorTheoryArrays::process( Node f, Theory::Effort effort, int e, int limitInst ){ + return InstStrategy::STATUS_SAT; +} diff --git a/src/theory/arrays/theory_arrays_instantiator.h b/src/theory/arrays/theory_arrays_instantiator.h new file mode 100644 index 000000000..6a7c9c3ed --- /dev/null +++ b/src/theory/arrays/theory_arrays_instantiator.h @@ -0,0 +1,51 @@ +/********************* */ +/*! \file theory_arrays_instantiator.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Instantiator for theory of arrays + **/ + + +#include "cvc4_private.h" + +#ifndef __CVC4__INSTANTIATOR_THEORY_ARRAYS_H +#define __CVC4__INSTANTIATOR_THEORY_ARRAYS_H + +#include "theory/quantifiers_engine.h" + +namespace CVC4 { +namespace theory { +namespace arrays { + +class InstantiatorTheoryArrays : public Instantiator{ + friend class QuantifiersEngine; +protected: + /** reset instantiation round */ + void processResetInstantiationRound( Theory::Effort effort ); + /** process quantifier */ + int process( Node f, Theory::Effort effort, int e, int limitInst = 0 ); +public: + InstantiatorTheoryArrays(context::Context* c, QuantifiersEngine* ie, Theory* th); + ~InstantiatorTheoryArrays() {} + /** Pre-register a term. */ + void preRegisterTerm( Node t ); + /** assertNode function, assertion is asserted to theory */ + void assertNode( Node assertion ); + /** identify */ + std::string identify() const { return std::string("InstantiatorTheoryArrays"); } +};/* class Instantiatior */ + +} +} +} + +#endif
\ No newline at end of file diff --git a/src/theory/booleans/theory_bool.h b/src/theory/booleans/theory_bool.h index 99b5b6007..40783a6ce 100644 --- a/src/theory/booleans/theory_bool.h +++ b/src/theory/booleans/theory_bool.h @@ -30,8 +30,8 @@ namespace booleans { class TheoryBool : public Theory { public: - TheoryBool(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) : - Theory(THEORY_BOOL, c, u, out, valuation, logicInfo) { + TheoryBool(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) : + Theory(THEORY_BOOL, c, u, out, valuation, logicInfo, qe) { } Node getValue(TNode n); diff --git a/src/theory/builtin/theory_builtin.h b/src/theory/builtin/theory_builtin.h index 30d2aaca7..a13c69d9d 100644 --- a/src/theory/builtin/theory_builtin.h +++ b/src/theory/builtin/theory_builtin.h @@ -29,8 +29,8 @@ namespace builtin { class TheoryBuiltin : public Theory { public: - TheoryBuiltin(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) : - Theory(THEORY_BUILTIN, c, u, out, valuation, logicInfo) {} + TheoryBuiltin(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) : + Theory(THEORY_BUILTIN, c, u, out, valuation, logicInfo, qe) {} Node getValue(TNode n); std::string identify() const { return std::string("TheoryBuiltin"); } };/* class TheoryBuiltin */ diff --git a/src/theory/bv/bv_subtheory_eq.h b/src/theory/bv/bv_subtheory_eq.h index d4239ff13..01178b453 100644 --- a/src/theory/bv/bv_subtheory_eq.h +++ b/src/theory/bv/bv_subtheory_eq.h @@ -40,6 +40,10 @@ class EqualitySolver : public SubtheorySolver { bool eqNotifyTriggerPredicate(TNode predicate, bool value); bool eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value); void eqNotifyConstantTermMerge(TNode t1, TNode t2); + void eqNotifyNewClass(TNode t) { } + void eqNotifyPreMerge(TNode t1, TNode t2) { } + void eqNotifyPostMerge(TNode t1, TNode t2) { } + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { } }; diff --git a/src/theory/bv/theory_bv.cpp b/src/theory/bv/theory_bv.cpp index 30493737a..66f443d50 100644 --- a/src/theory/bv/theory_bv.cpp +++ b/src/theory/bv/theory_bv.cpp @@ -33,8 +33,8 @@ using namespace CVC4::theory::bv::utils; -TheoryBV::TheoryBV(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) - : Theory(THEORY_BV, c, u, out, valuation, logicInfo), +TheoryBV::TheoryBV(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) + : Theory(THEORY_BV, c, u, out, valuation, logicInfo, qe), d_context(c), d_alreadyPropagatedSet(c), d_sharedTermsSet(c), diff --git a/src/theory/bv/theory_bv.h b/src/theory/bv/theory_bv.h index 9c27f62c5..f79b7ab71 100644 --- a/src/theory/bv/theory_bv.h +++ b/src/theory/bv/theory_bv.h @@ -49,8 +49,8 @@ class TheoryBV : public Theory { EqualitySolver d_equalitySolver; public: - TheoryBV(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo); - ~TheoryBV(); + TheoryBV(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe); + ~TheoryBV(); void preRegisterTerm(TNode n); @@ -64,21 +64,21 @@ public: std::string identify() const { return std::string("TheoryBV"); } - PPAssertStatus ppAssert(TNode in, SubstitutionMap& outSubstitutions); + PPAssertStatus ppAssert(TNode in, SubstitutionMap& outSubstitutions); private: - + class Statistics { public: AverageStat d_avgConflictSize; IntStat d_solveSubstitutions; - TimerStat d_solveTimer; + TimerStat d_solveTimer; Statistics(); - ~Statistics(); - }; - + ~Statistics(); + }; + Statistics d_statistics; - + // Are we in conflict? context::CDO<bool> d_conflict; @@ -133,7 +133,7 @@ private: } bool inConflict() { return d_conflict == true; } - + friend class Bitblaster; friend class BitblastSolver; friend class EqualitySolver; diff --git a/src/theory/datatypes/Makefile.am b/src/theory/datatypes/Makefile.am index f8bfa3dc5..d6622b19a 100644 --- a/src/theory/datatypes/Makefile.am +++ b/src/theory/datatypes/Makefile.am @@ -13,6 +13,8 @@ libdatatypes_la_SOURCES = \ union_find.h \ union_find.cpp \ explanation_manager.h \ - explanation_manager.cpp + explanation_manager.cpp \ + theory_datatypes_instantiator.h \ + theory_datatypes_instantiator.cpp EXTRA_DIST = kinds diff --git a/src/theory/datatypes/kinds b/src/theory/datatypes/kinds index 7acb6d17d..4b6bfd8f6 100644 --- a/src/theory/datatypes/kinds +++ b/src/theory/datatypes/kinds @@ -6,6 +6,7 @@ theory THEORY_DATATYPES ::CVC4::theory::datatypes::TheoryDatatypes "theory/datatypes/theory_datatypes.h" typechecker "theory/datatypes/theory_datatypes_type_rules.h" +instantiator ::CVC4::theory::datatypes::InstantiatorTheoryDatatypes "theory/datatypes/theory_datatypes_instantiator.h" properties check presolve parametric diff --git a/src/theory/datatypes/theory_datatypes.cpp b/src/theory/datatypes/theory_datatypes.cpp index 7b1562ada..3b8efabb7 100644 --- a/src/theory/datatypes/theory_datatypes.cpp +++ b/src/theory/datatypes/theory_datatypes.cpp @@ -22,6 +22,7 @@ #include "expr/kind.h" #include "util/datatype.h" #include "util/Assert.h" +#include "theory/datatypes/theory_datatypes_instantiator.h" #include <map> @@ -53,8 +54,8 @@ Node TheoryDatatypes::getConstructorForSelector( Node sel ) } -TheoryDatatypes::TheoryDatatypes(Context* c, UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) : - Theory(THEORY_DATATYPES, c, u, out, valuation, logicInfo), +TheoryDatatypes::TheoryDatatypes(Context* c, UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) : + Theory(THEORY_DATATYPES, c, u, out, valuation, logicInfo, qe), d_currAsserts(c), d_currEqualities(c), d_selectors(c), @@ -71,7 +72,6 @@ TheoryDatatypes::TheoryDatatypes(Context* c, UserContext* u, OutputChannel& out, d_disequalities(c), d_em(c), d_cce(&d_cc){ - } @@ -114,7 +114,7 @@ void TheoryDatatypes::check(Effort e) { Node assertion = get(); if( Debug.isOn("datatypes") || Debug.isOn("datatypes-split") || Debug.isOn("datatypes-cycles") || Debug.isOn("datatypes-debug-pf") || Debug.isOn("datatypes-conflict") ) { - cout << "*** TheoryDatatypes::check(): " << assertion << endl; + Notice() << "*** TheoryDatatypes::check(): " << assertion << endl; d_currAsserts.push_back( assertion ); } @@ -211,7 +211,7 @@ void TheoryDatatypes::check(Effort e) { Node conflict = d_em.getConflict(); if( Debug.isOn("datatypes") || Debug.isOn("datatypes-split") || Debug.isOn("datatypes-cycles") || Debug.isOn("datatypes-conflict") ){ - cout << "Conflict constructed : " << conflict << endl; + Notice() << "Conflict constructed : " << conflict << endl; } if( conflict.getKind()!=kind::AND ){ conflict = NodeManager::currentNM()->mkNode(kind::AND, conflict, conflict); @@ -283,7 +283,7 @@ void TheoryDatatypes::check(Effort e) { } } if( Debug.isOn("datatypes") || Debug.isOn("datatypes-split") ) { - cout << "TheoryDatatypes::check(): done" << endl; + Notice() << "TheoryDatatypes::check(): done" << endl; } } diff --git a/src/theory/datatypes/theory_datatypes.h b/src/theory/datatypes/theory_datatypes.h index 967000c3e..5a4135a3b 100644 --- a/src/theory/datatypes/theory_datatypes.h +++ b/src/theory/datatypes/theory_datatypes.h @@ -37,7 +37,10 @@ namespace CVC4 { namespace theory { namespace datatypes { +class InstantiatorTheoryDatatypes; + class TheoryDatatypes : public Theory { + friend class InstantiatorTheoryDatatypes; private: typedef context::CDChunkList<TNode> EqList; typedef context::CDHashMap<Node, EqList*, NodeHashFunction> EqLists; @@ -53,7 +56,7 @@ private: BoolMap d_selectors; /** keeps track of which nodes are representatives */ BoolMap d_reps; - /** map from (representative) nodes to a list of selectors whose arguments are + /** map from (representative) nodes to a list of selectors whose arguments are in the equivalence class of that node */ EqListsN d_selector_eq; /** map from (representative) nodes to list of nodes in their eq class */ @@ -140,7 +143,7 @@ private: CongruenceClosureExplainer<CongruenceChannel, CONGRUENCE_OPERATORS_2 (kind::APPLY_CONSTRUCTOR, kind::APPLY_SELECTOR)> d_cce; public: - TheoryDatatypes(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo); + TheoryDatatypes(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe); ~TheoryDatatypes(); void preRegisterTerm(TNode n); void presolve(); @@ -167,7 +170,7 @@ private: /* from uf_morgan */ void merge(TNode a, TNode b); - inline TNode find(TNode a); + inline TNode find(TNode a); inline TNode debugFind(TNode a) const; void appendToDiseqList(TNode of, TNode eq); void addDisequality(TNode eq); @@ -179,8 +182,8 @@ private: NodeBuilder<>& explanation ); };/* class TheoryDatatypes */ -inline bool TheoryDatatypes::hasConflict() { - return d_em.hasConflict(); +inline bool TheoryDatatypes::hasConflict() { + return d_em.hasConflict(); } inline TNode TheoryDatatypes::find(TNode a) { diff --git a/src/theory/datatypes/theory_datatypes_instantiator.cpp b/src/theory/datatypes/theory_datatypes_instantiator.cpp new file mode 100644 index 000000000..6a32466e4 --- /dev/null +++ b/src/theory/datatypes/theory_datatypes_instantiator.cpp @@ -0,0 +1,158 @@ +/********************* */ +/*! \file theory_datatypes_instantiator.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of theory_datatypes_instantiator class + **/ + +#include "theory/datatypes/theory_datatypes_instantiator.h" +#include "theory/datatypes/theory_datatypes.h" +#include "theory/theory_engine.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::datatypes; + +InstantiatorTheoryDatatypes::InstantiatorTheoryDatatypes(context::Context* c, QuantifiersEngine* ie, Theory* th) : +Instantiator( c, ie, th ){ + +} + +void InstantiatorTheoryDatatypes::assertNode( Node assertion ){ + Debug("quant-datatypes-assert") << "InstantiatorTheoryDatatypes::check: " << assertion << std::endl; + d_quantEngine->addTermToDatabase( assertion ); + if( Options::current()->cbqi ){ + if( assertion.hasAttribute(InstConstantAttribute()) ){ + setHasConstraintsFrom( assertion.getAttribute(InstConstantAttribute()) ); + }else if( assertion.getKind()==NOT && assertion[0].hasAttribute(InstConstantAttribute()) ){ + setHasConstraintsFrom( assertion[0].getAttribute(InstConstantAttribute()) ); + } + } +} + +void InstantiatorTheoryDatatypes::processResetInstantiationRound( Theory::Effort effort ){ + +} + + +int InstantiatorTheoryDatatypes::process( Node f, Theory::Effort effort, int e, int limitInst ){ + Debug("quant-datatypes") << "Datatypes: Try to solve (" << e << ") for " << f << "... " << std::endl; + if( Options::current()->cbqi ){ + if( e<2 ){ + return InstStrategy::STATUS_UNFINISHED; + }else if( e==2 ){ + InstMatch m; + for( int j = 0; j<(int)d_quantEngine->getNumInstantiationConstants( f ); j++ ){ + Node i = d_quantEngine->getInstantiationConstant( f, j ); + if( i.getType().isDatatype() ){ + Node n = getValueFor( i ); + Debug("quant-datatypes-debug") << "Value for " << i << " is " << n << std::endl; + m.d_map[ i ] = n; + } + } + d_quantEngine->addInstantiation( f, m ); + } + } + return InstStrategy::STATUS_UNKNOWN; +} + +Node InstantiatorTheoryDatatypes::getValueFor( Node n ){ + //simply get the ground value for n in the current model, if it exists, + // or return an arbitrary ground term otherwise + Debug("quant-datatypes-debug") << "get value for " << n << std::endl; + if( !n.hasAttribute(InstConstantAttribute()) ){ + return n; + }else{ + Assert( n.getType().isDatatype() ); + //check if in equivalence class with ground term + Node rep = getRepresentative( n ); + Debug("quant-datatypes-debug") << "Rep is " << rep << std::endl; + if( !rep.hasAttribute(InstConstantAttribute()) ){ + return rep; + }else{ + if( !n.getType().isDatatype() ){ + return n.getType().mkGroundTerm(); + }else{ + if( n.getKind()==APPLY_CONSTRUCTOR ){ + std::vector< Node > children; + children.push_back( n.getOperator() ); + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + children.push_back( getValueFor( n[i] ) ); + } + return NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, children ); + }else{ + const Datatype& dt = ((DatatypeType)(n.getType()).toType()).getDatatype(); + TheoryDatatypes::EqLists* labels = &((TheoryDatatypes*)d_th)->d_labels; + //otherwise, use which constructor the inst constant is current chosen to be + if( labels->find( n )!=labels->end() ){ + TheoryDatatypes::EqList* lbl = (*labels->find( n )).second; + int tIndex = -1; + if( !lbl->empty() && (*lbl)[ lbl->size()-1 ].getKind()==APPLY_TESTER ){ + Debug("quant-datatypes-debug") << n << " tester is " << (*lbl)[ lbl->size()-1 ] << std::endl; + tIndex = Datatype::indexOf((*lbl)[ lbl->size()-1 ].getOperator().toExpr()); + }else{ + Debug("quant-datatypes-debug") << "find possible tester choice" << std::endl; + //must find a possible choice + vector< bool > possibleCons; + possibleCons.resize( dt.getNumConstructors(), true ); + for( TheoryDatatypes::EqList::const_iterator j = lbl->begin(); j != lbl->end(); j++ ) { + Node leqn = (*j); + possibleCons[ Datatype::indexOf( leqn[0].getOperator().toExpr() ) ] = false; + } + for( unsigned int j=0; j<possibleCons.size(); j++ ) { + if( possibleCons[j] ){ + tIndex = j; + break; + } + } + } + Assert( tIndex!=-1 ); + Node cons = Node::fromExpr( dt[ tIndex ].getConstructor() ); + Debug("quant-datatypes-debug") << n << " cons is " << cons << std::endl; + std::vector< Node > children; + children.push_back( cons ); + for( int i=0; i<(int)dt[ tIndex ].getNumArgs(); i++ ) { + Node sn = NodeManager::currentNM()->mkNode( APPLY_SELECTOR, Node::fromExpr( dt[tIndex][i].getSelector() ), n ); + if( n.hasAttribute(InstConstantAttribute()) ){ + InstConstantAttribute ica; + sn.setAttribute(ica,n.getAttribute(InstConstantAttribute()) ); + } + Node snn = getValueFor( sn ); + children.push_back( snn ); + } + return NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, children ); + }else{ + return n.getType().mkGroundTerm(); + } + } + } + } + } +} + +Node InstantiatorTheoryDatatypes::getRepresentative( Node n ){ + return ((TheoryDatatypes*)d_th)->find( n ); +} + +InstantiatorTheoryDatatypes::Statistics::Statistics(): + d_instantiations("InstantiatorTheoryDatatypes::Instantiations_Total", 0) +{ + StatisticsRegistry::registerStat(&d_instantiations); +} + +InstantiatorTheoryDatatypes::Statistics::~Statistics(){ + StatisticsRegistry::unregisterStat(&d_instantiations); +} + diff --git a/src/theory/datatypes/theory_datatypes_instantiator.h b/src/theory/datatypes/theory_datatypes_instantiator.h new file mode 100644 index 000000000..5c52f7f48 --- /dev/null +++ b/src/theory/datatypes/theory_datatypes_instantiator.h @@ -0,0 +1,64 @@ +/********************* */ +/*! \file instantiator_datatypes_instantiator.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief instantiator_datatypes_instantiator + **/ + + +#include "cvc4_private.h" + +#ifndef __CVC4__INSTANTIATOR_DATATYPES_H +#define __CVC4__INSTANTIATOR_DATATYPES_H + +#include "theory/quantifiers_engine.h" + +#include "util/stats.h" + +namespace CVC4 { +namespace theory { +namespace datatypes { + +class InstantiatorTheoryDatatypes : public Instantiator{ + friend class QuantifiersEngine; +public: + InstantiatorTheoryDatatypes(context::Context* c, QuantifiersEngine* ie, Theory* th); + ~InstantiatorTheoryDatatypes() {} + + /** assertNode function, assertion is asserted to theory */ + void assertNode( Node assertion ); + /** identify */ + std::string identify() const { return std::string("InstantiatorTheoryDatatypes"); } +private: + /** reset instantiation */ + void processResetInstantiationRound( Theory::Effort effort ); + /** process at effort */ + int process( Node f, Theory::Effort effort, int e, int limitInst ); + /** get value for */ + Node getValueFor( Node n ); + /** get representative */ + Node getRepresentative( Node n ); + + class Statistics { + public: + IntStat d_instantiations; + Statistics(); + ~Statistics(); + }; + Statistics d_statistics; +};/* class InstantiatiorTheoryDatatypes */ + +} +} +} + +#endif
\ No newline at end of file diff --git a/src/theory/example/theory_uf_tim.cpp b/src/theory/example/theory_uf_tim.cpp index 03787703a..638a03478 100644 --- a/src/theory/example/theory_uf_tim.cpp +++ b/src/theory/example/theory_uf_tim.cpp @@ -27,8 +27,8 @@ using namespace CVC4::theory; using namespace CVC4::theory::uf; using namespace CVC4::theory::uf::tim; -TheoryUFTim::TheoryUFTim(Context* c, UserContext* u, OutputChannel& out, Valuation valuation) : - Theory(THEORY_UF, c, u, out, valuation), +TheoryUFTim::TheoryUFTim(Context* c, UserContext* u, OutputChannel& out, Valuation valuation, QuantifiersEngine* qe) : + Theory(THEORY_UF, c, u, out, valuation, qe), d_assertions(c), d_pending(c), d_currentPendingIdx(c,0), diff --git a/src/theory/example/theory_uf_tim.h b/src/theory/example/theory_uf_tim.h index b47536f07..41e58349a 100644 --- a/src/theory/example/theory_uf_tim.h +++ b/src/theory/example/theory_uf_tim.h @@ -85,7 +85,7 @@ private: public: /** Constructs a new instance of TheoryUF w.r.t. the provided context.*/ - TheoryUFTim(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation); + TheoryUFTim(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, QuantifiersEngine* qe); /** Destructor for the TheoryUF object. */ ~TheoryUFTim(); diff --git a/src/theory/inst_match.cpp b/src/theory/inst_match.cpp new file mode 100644 index 000000000..e340da75d --- /dev/null +++ b/src/theory/inst_match.cpp @@ -0,0 +1,903 @@ +/********************* */ +/*! \file inst_match.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of inst match class + **/ + +#include "theory/inst_match.h" +#include "theory/theory_engine.h" +#include "theory/quantifiers_engine.h" +#include "theory/uf/theory_uf_instantiator.h" +#include "theory/uf/theory_uf_candidate_generator.h" +#include "theory/uf/equality_engine.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; + + +bool CandidateGenerator::isLegalCandidate( Node n ){ + return !n.getAttribute(NoMatchAttribute()) && ( !Options::current()->cbqi || !n.hasAttribute(InstConstantAttribute()) ); +} + +void CandidateGeneratorQueue::addCandidate( Node n ) { + if( isLegalCandidate( n ) ){ + d_candidates.push_back( n ); + } +} + +void CandidateGeneratorQueue::reset( Node eqc ){ + if( d_candidate_index>0 ){ + d_candidates.erase( d_candidates.begin(), d_candidates.begin() + d_candidate_index ); + d_candidate_index = 0; + } + if( !eqc.isNull() ){ + d_candidates.push_back( eqc ); + } +} +Node CandidateGeneratorQueue::getNextCandidate(){ + if( d_candidate_index<(int)d_candidates.size() ){ + Node n = d_candidates[d_candidate_index]; + d_candidate_index++; + return n; + }else{ + d_candidate_index = 0; + d_candidates.clear(); + return Node::null(); + } +} + +InstMatch::InstMatch() { +} + +InstMatch::InstMatch( InstMatch* m ) { + d_map = m->d_map; +} + +bool InstMatch::setMatch( EqualityQuery* q, Node v, Node m ){ + if( d_map.find( v )==d_map.end() ){ + d_map[v] = m; + Debug("matching-debug") << "Add partial " << v << "->" << m << std::endl; + return true; + }else{ + return q->areEqual( d_map[v], m ); + } +} + +bool InstMatch::add( InstMatch& m ){ + for( std::map< Node, Node >::iterator it = m.d_map.begin(); it != m.d_map.end(); ++it ){ + if( d_map.find( it->first )==d_map.end() ){ + d_map[it->first] = it->second; + } + } + return true; +} + +bool InstMatch::merge( EqualityQuery* q, InstMatch& m ){ + for( std::map< Node, Node >::iterator it = m.d_map.begin(); it != m.d_map.end(); ++it ){ + if( d_map.find( it->first )==d_map.end() ){ + d_map[ it->first ] = it->second; + }else{ + if( it->second!=d_map[it->first] ){ + if( !q->areEqual( it->second, d_map[it->first] ) ){ + d_map.clear(); + return false; + } + } + } + } + return true; +} + +void InstMatch::debugPrint( const char* c ){ + for( std::map< Node, Node >::iterator it = d_map.begin(); it != d_map.end(); ++it ){ + Debug( c ) << " " << it->first << " -> " << it->second << std::endl; + } + //if( !d_splits.empty() ){ + // Debug( c ) << " Conditions: "; + // for( std::map< Node, Node >::iterator it = d_splits.begin(); it !=d_splits.end(); ++it ){ + // Debug( c ) << it->first << " = " << it->second << " "; + // } + // Debug( c ) << std::endl; + //} +} + +void InstMatch::makeComplete( Node f, QuantifiersEngine* qe ){ + for( int i=0; i<(int)qe->d_inst_constants[f].size(); i++ ){ + if( d_map.find( qe->d_inst_constants[f][i] )==d_map.end() ){ + d_map[ qe->d_inst_constants[f][i] ] = qe->getFreeVariableForInstConstant( qe->d_inst_constants[f][i] ); + } + } +} + +void InstMatch::makeInternal( QuantifiersEngine* qe ){ + for( std::map< Node, Node >::iterator it = d_map.begin(); it != d_map.end(); ++it ){ + if( Options::current()->cbqi && it->second.hasAttribute(InstConstantAttribute()) ){ + d_map[ it->first ] = qe->getEqualityQuery()->getInternalRepresentative( it->second ); + if( Options::current()->cbqi && it->second.hasAttribute(InstConstantAttribute()) ){ + d_map[ it->first ] = qe->getFreeVariableForInstConstant( it->first ); + } + } + } +} + +void InstMatch::makeRepresentative( QuantifiersEngine* qe ){ + for( std::map< Node, Node >::iterator it = d_map.begin(); it != d_map.end(); ++it ){ + d_map[ it->first ] = qe->getEqualityQuery()->getInternalRepresentative( it->second ); + if( Options::current()->cbqi && it->second.hasAttribute(InstConstantAttribute()) ){ + d_map[ it->first ] = qe->getFreeVariableForInstConstant( it->first ); + } + } +} + +void InstMatch::applyRewrite(){ + for( std::map< Node, Node >::iterator it = d_map.begin(); it != d_map.end(); ++it ){ + it->second = Rewriter::rewrite(it->second); + } +} + +void InstMatch::computeTermVec( QuantifiersEngine* qe, const std::vector< Node >& vars, std::vector< Node >& match ){ + for( int i=0; i<(int)vars.size(); i++ ){ + std::map< Node, Node >::iterator it = d_map.find( vars[i] ); + if( it!=d_map.end() && !it->second.isNull() ){ + match.push_back( it->second ); + }else{ + match.push_back( qe->getFreeVariableForInstConstant( vars[i] ) ); + } + } +} +void InstMatch::computeTermVec( const std::vector< Node >& vars, std::vector< Node >& match ){ + for( int i=0; i<(int)vars.size(); i++ ){ + match.push_back( d_map[ vars[i] ] ); + } +} + + +/** add match m for quantifier f starting at index, take into account equalities q, return true if successful */ +void InstMatchTrie::addInstMatch2( QuantifiersEngine* qe, Node f, InstMatch& m, int index, ImtIndexOrder* imtio ){ + if( long(index)<f[0].getNumChildren() && ( !imtio || long(index)<imtio->d_order.size() ) ){ + int i_index = imtio ? imtio->d_order[index] : index; + Node n = m.d_map[ qe->getInstantiationConstant( f, i_index ) ]; + d_data[n].addInstMatch2( qe, f, m, index+1, imtio ); + } +} + +/** exists match */ +bool InstMatchTrie::existsInstMatch( QuantifiersEngine* qe, Node f, InstMatch& m, bool modEq, int index, ImtIndexOrder* imtio ){ + if( long(index)==f[0].getNumChildren() || ( imtio && long(index)==imtio->d_order.size() ) ){ + return true; + }else{ + int i_index = imtio ? imtio->d_order[index] : index; + Node n = m.d_map[ qe->getInstantiationConstant( f, i_index ) ]; + std::map< Node, InstMatchTrie >::iterator it = d_data.find( n ); + if( it!=d_data.end() ){ + if( it->second.existsInstMatch( qe, f, m, modEq, index+1, imtio ) ){ + return true; + } + } + if( modEq ){ + //check modulo equality if any other instantiation match exists + if( ((uf::TheoryUF*)qe->getTheoryEngine()->getTheory( THEORY_UF ))->getEqualityEngine()->hasTerm( n ) ){ + eq::EqClassIterator eqc( qe->getEqualityQuery()->getRepresentative( n ), + ((uf::TheoryUF*)qe->getTheoryEngine()->getTheory( THEORY_UF ))->getEqualityEngine() ); + while( !eqc.isFinished() ){ + Node en = (*eqc); + if( en!=n ){ + std::map< Node, InstMatchTrie >::iterator itc = d_data.find( en ); + if( itc!=d_data.end() ){ + if( itc->second.existsInstMatch( qe, f, m, modEq, index+1, imtio ) ){ + return true; + } + } + } + ++eqc; + } + } + //for( std::map< Node, InstMatchTrie >::iterator itc = d_data.begin(); itc != d_data.end(); ++itc ){ + // if( itc->first!=n && qe->getEqualityQuery()->areEqual( n, itc->first ) ){ + // if( itc->second.existsInstMatch( qe, f, m, modEq, index+1 ) ){ + // return true; + // } + // } + //} + } + return false; + } +} + +bool InstMatchTrie::addInstMatch( QuantifiersEngine* qe, Node f, InstMatch& m, bool modEq, ImtIndexOrder* imtio ){ + if( !existsInstMatch( qe, f, m, modEq, 0, imtio ) ){ + addInstMatch2( qe, f, m, 0, imtio ); + return true; + }else{ + return false; + } +} + +InstMatchGenerator::InstMatchGenerator( Node pat, QuantifiersEngine* qe, int matchPolicy ) : d_matchPolicy( matchPolicy ){ + initializePattern( pat, qe ); +} + +InstMatchGenerator::InstMatchGenerator( std::vector< Node >& pats, QuantifiersEngine* qe, int matchPolicy ) : d_matchPolicy( matchPolicy ){ + if( pats.size()==1 ){ + initializePattern( pats[0], qe ); + }else{ + initializePatterns( pats, qe ); + } +} + +void InstMatchGenerator::initializePatterns( std::vector< Node >& pats, QuantifiersEngine* qe ){ + int childMatchPolicy = d_matchPolicy==MATCH_GEN_EFFICIENT_E_MATCH ? 0 : d_matchPolicy; + for( int i=0; i<(int)pats.size(); i++ ){ + d_children.push_back( new InstMatchGenerator( pats[i], qe, childMatchPolicy ) ); + } + d_pattern = Node::null(); + d_match_pattern = Node::null(); + d_cg = NULL; +} + +void InstMatchGenerator::initializePattern( Node pat, QuantifiersEngine* qe ){ + Debug("inst-match-gen") << "Pattern term is " << pat << std::endl; + Assert( pat.hasAttribute(InstConstantAttribute()) ); + d_pattern = pat; + d_match_pattern = pat; + if( d_match_pattern.getKind()==NOT ){ + //we want to add the children of the NOT + d_match_pattern = d_pattern[0]; + } + if( d_match_pattern.getKind()==IFF || d_match_pattern.getKind()==EQUAL ){ + if( !d_match_pattern[0].hasAttribute(InstConstantAttribute()) ){ + Assert( d_match_pattern[1].hasAttribute(InstConstantAttribute()) ); + //swap sides + d_pattern = NodeManager::currentNM()->mkNode( d_match_pattern.getKind(), d_match_pattern[1], d_match_pattern[0] ); + d_pattern = pat.getKind()==NOT ? d_pattern.notNode() : d_pattern; + if( pat.getKind()!=NOT ){ //TEMPORARY until we do better implementation of disequality matching + d_match_pattern = d_match_pattern[1]; + }else{ + d_match_pattern = d_pattern[0][0]; + } + }else if( !d_match_pattern[1].hasAttribute(InstConstantAttribute()) ){ + Assert( d_match_pattern[0].hasAttribute(InstConstantAttribute()) ); + if( pat.getKind()!=NOT ){ //TEMPORARY until we do better implementation of disequality matching + d_match_pattern = d_match_pattern[0]; + } + } + } + int childMatchPolicy = MATCH_GEN_DEFAULT; + for( int i=0; i<(int)d_match_pattern.getNumChildren(); i++ ){ + if( d_match_pattern[i].hasAttribute(InstConstantAttribute()) ){ + if( d_match_pattern[i].getKind()!=INST_CONSTANT ){ + d_children.push_back( new InstMatchGenerator( d_match_pattern[i], qe, childMatchPolicy ) ); + d_children_index.push_back( i ); + } + } + } + + Debug("inst-match-gen") << "Pattern is " << d_pattern << ", match pattern is " << d_match_pattern << std::endl; + + //get the equality engine + Theory* th_uf = qe->getTheoryEngine()->getTheory( theory::THEORY_UF ); + uf::InstantiatorTheoryUf* ith = (uf::InstantiatorTheoryUf*)th_uf->getInstantiator(); + //create candidate generator + if( d_match_pattern.getKind()==EQUAL || d_match_pattern.getKind()==IFF ){ + Assert( d_matchPolicy==MATCH_GEN_DEFAULT ); + //we will be producing candidates via literal matching heuristics + if( d_pattern.getKind()!=NOT ){ + //candidates will be all equalities + d_cg = new uf::CandidateGeneratorTheoryUfLitEq( ith, d_match_pattern ); + }else{ + //candidates will be all disequalities + d_cg = new uf::CandidateGeneratorTheoryUfLitDeq( ith, d_match_pattern ); + } + }else if( d_pattern.getKind()==EQUAL || d_pattern.getKind()==IFF || d_pattern.getKind()==NOT ){ + Assert( d_matchPolicy==MATCH_GEN_DEFAULT ); + if( d_pattern.getKind()==NOT ){ + Unimplemented("Disequal generator unimplemented"); + }else{ + Assert( Trigger::isAtomicTrigger( d_match_pattern ) ); + //we are matching only in a particular equivalence class + d_cg = new uf::CandidateGeneratorTheoryUf( ith, d_match_pattern.getOperator() ); + //store the equivalence class that we will call d_cg->reset( ... ) on + d_eq_class = d_pattern[1]; + } + }else if( Trigger::isAtomicTrigger( d_match_pattern ) ){ + if( d_matchPolicy==MATCH_GEN_EFFICIENT_E_MATCH ){ + //we will manually add candidates to queue + d_cg = new CandidateGeneratorQueue; + //register this candidate generator + ith->registerCandidateGenerator( d_cg, d_match_pattern ); + }else{ + //we will be scanning lists trying to find d_match_pattern.getOperator() + d_cg = new uf::CandidateGeneratorTheoryUf( ith, d_match_pattern.getOperator() ); + } + }else{ + d_cg = new CandidateGeneratorQueue; + if( !Trigger::getPatternArithmetic( d_match_pattern.getAttribute(InstConstantAttribute()), d_match_pattern, d_arith_coeffs ) ){ + Debug("inst-match-gen") << "(?) Unknown matching pattern is " << d_match_pattern << std::endl; + Warning() << "(?) Unknown matching pattern is " << d_match_pattern << std::endl; + d_matchPolicy = MATCH_GEN_INTERNAL_ERROR; + }else{ + Debug("matching-arith") << "Generated arithmetic pattern for " << d_match_pattern << ": " << std::endl; + for( std::map< Node, Node >::iterator it = d_arith_coeffs.begin(); it != d_arith_coeffs.end(); ++it ){ + Debug("matching-arith") << " " << it->first << " -> " << it->second << std::endl; + } + //we will treat this as match gen internal arithmetic + d_matchPolicy = MATCH_GEN_INTERNAL_ARITHMETIC; + } + } +} + +/** get match (not modulo equality) */ +bool InstMatchGenerator::getMatch( Node t, InstMatch& m, QuantifiersEngine* qe ){ + Debug("matching") << "Matching " << t << " against pattern " << d_match_pattern << " (" + << m.d_map.size() << ")" << ", " << d_children.size() << std::endl; + Assert( !d_match_pattern.isNull() ); + if( d_matchPolicy==MATCH_GEN_INTERNAL_ARITHMETIC ){ + return getMatchArithmetic( t, m, qe ); + }else if( d_matchPolicy==MATCH_GEN_INTERNAL_ERROR ){ + return false; + }else{ + EqualityQuery* q = qe->getEqualityQuery(); + //add m to partial match vector + std::vector< InstMatch > partial; + partial.push_back( InstMatch( &m ) ); + //if t is null + Assert( !t.isNull() ); + Assert( !t.hasAttribute(InstConstantAttribute()) ); + Assert( t.getKind()==d_match_pattern.getKind() ); + Assert( !Trigger::isAtomicTrigger( d_match_pattern ) || t.getOperator()==d_match_pattern.getOperator() ); + //first, check if ground arguments are not equal, or a match is in conflict + for( int i=0; i<(int)d_match_pattern.getNumChildren(); i++ ){ + if( d_match_pattern[i].hasAttribute(InstConstantAttribute()) ){ + if( d_match_pattern[i].getKind()==INST_CONSTANT ){ + if( !partial[0].setMatch( q, d_match_pattern[i], t[i] ) ){ + //match is in conflict + Debug("matching-debug") << "Match in conflict " << t[i] << " and " + << d_match_pattern[i] << " because " + << partial[0].d_map[d_match_pattern[i]] + << std::endl; + Debug("matching-fail") << "Match fail: " << partial[0].d_map[d_match_pattern[i]] << " and " << t[i] << std::endl; + return false; + } + } + }else{ + if( !q->areEqual( d_match_pattern[i], t[i] ) ){ + Debug("matching-fail") << "Match fail arg: " << d_match_pattern[i] << " and " << t[i] << std::endl; + //ground arguments are not equal + return false; + } + } + } + //now, fit children into match + //we will be requesting candidates for matching terms for each child + std::vector< Node > reps; + for( int i=0; i<(int)d_children.size(); i++ ){ + Node rep = q->getRepresentative( t[ d_children_index[i] ] ); + reps.push_back( rep ); + d_children[i]->d_cg->reset( rep ); + } + + //combine child matches + int index = 0; + while( index>=0 && index<(int)d_children.size() ){ + partial.push_back( InstMatch( &partial[index] ) ); + if( d_children[index]->getNextMatch2( partial[index+1], qe ) ){ + index++; + }else{ + d_children[index]->d_cg->reset( reps[index] ); + partial.pop_back(); + if( !partial.empty() ){ + partial.pop_back(); + } + index--; + } + } + if( index>=0 ){ + m = partial.back(); + return true; + }else{ + return false; + } + } +} + +bool InstMatchGenerator::getNextMatch2( InstMatch& m, QuantifiersEngine* qe, bool saveMatched ){ + bool success = false; + Node t; + do{ + //get the next candidate term t + t = d_cg->getNextCandidate(); + //if t not null, try to fit it into match m + if( !t.isNull() && t.getType()==d_match_pattern.getType() ){ + //Assert( t.getType()==d_match_pattern.getType() ); + success = getMatch( t, m, qe ); + } + }while( !success && !t.isNull() ); + if (saveMatched) m.d_matched = t; + return success; +} + +bool InstMatchGenerator::getMatchArithmetic( Node t, InstMatch& m, QuantifiersEngine* qe ){ + Debug("matching-arith") << "Matching " << t << " " << d_match_pattern << std::endl; + if( !d_arith_coeffs.empty() ){ + NodeBuilder<> tb(kind::PLUS); + Node ic = Node::null(); + for( std::map< Node, Node >::iterator it = d_arith_coeffs.begin(); it != d_arith_coeffs.end(); ++it ){ + Debug("matching-arith") << it->first << " -> " << it->second << std::endl; + if( !it->first.isNull() ){ + if( m.d_map.find( it->first )==m.d_map.end() ){ + //see if we can choose this to set + if( ic.isNull() && ( it->second.isNull() || !it->first.getType().isInteger() ) ){ + ic = it->first; + } + }else{ + Debug("matching-arith") << "already set " << m.d_map[ it->first ] << std::endl; + Node tm = m.d_map[ it->first ]; + if( !it->second.isNull() ){ + tm = NodeManager::currentNM()->mkNode( MULT, it->second, tm ); + } + tb << tm; + } + }else{ + tb << it->second; + } + } + if( !ic.isNull() ){ + Node tm; + if( tb.getNumChildren()==0 ){ + tm = t; + }else{ + tm = tb.getNumChildren()==1 ? tb.getChild( 0 ) : tb; + tm = NodeManager::currentNM()->mkNode( MINUS, t, tm ); + } + if( !d_arith_coeffs[ ic ].isNull() ){ + Assert( !ic.getType().isInteger() ); + Node coeff = NodeManager::currentNM()->mkConst( Rational(1) / d_arith_coeffs[ ic ].getConst<Rational>() ); + tm = NodeManager::currentNM()->mkNode( MULT, coeff, tm ); + } + m.d_map[ ic ] = Rewriter::rewrite( tm ); + //set the rest to zeros + for( std::map< Node, Node >::iterator it = d_arith_coeffs.begin(); it != d_arith_coeffs.end(); ++it ){ + if( !it->first.isNull() ){ + if( m.d_map.find( it->first )==m.d_map.end() ){ + m.d_map[ it->first ] = NodeManager::currentNM()->mkConst( Rational(0) ); + } + } + } + Debug("matching-arith") << "Setting " << ic << " to " << tm << std::endl; + return true; + }else{ + return false; + } + }else{ + return false; + } +} + + +/** reset instantiation round */ +void InstMatchGenerator::resetInstantiationRound( QuantifiersEngine* qe ){ + if( d_match_pattern.isNull() ){ + for( int i=0; i<(int)d_children.size(); i++ ){ + d_children[i]->resetInstantiationRound( qe ); + } + }else{ + if( d_cg ){ + d_cg->resetInstantiationRound(); + } + } +} + +void InstMatchGenerator::reset( Node eqc, QuantifiersEngine* qe ){ + if( d_match_pattern.isNull() ){ + for( int i=0; i<(int)d_children.size(); i++ ){ + d_children[i]->reset( eqc, qe ); + } + d_partial.clear(); + }else{ + if( !d_eq_class.isNull() ){ + //we have a specific equivalence class in mind + //we are producing matches for f(E) ~ t, where E is a non-ground vector of terms, and t is a ground term + //just look in equivalence class of the RHS + d_cg->reset( d_eq_class ); + }else{ + d_cg->reset( eqc ); + } + } +} + +bool InstMatchGenerator::getNextMatch( InstMatch& m, QuantifiersEngine* qe ){ + m.d_matched = Node::null(); + if( d_match_pattern.isNull() ){ + int index = (int)d_partial.size(); + while( index>=0 && index<(int)d_children.size() ){ + if( index>0 ){ + d_partial.push_back( InstMatch( &d_partial[index-1] ) ); + }else{ + d_partial.push_back( InstMatch() ); + } + if( d_children[index]->getNextMatch( d_partial[index], qe ) ){ + index++; + }else{ + d_children[index]->reset( Node::null(), qe ); + d_partial.pop_back(); + if( !d_partial.empty() ){ + d_partial.pop_back(); + } + index--; + } + } + if( index>=0 ){ + m = d_partial.back(); + d_partial.pop_back(); + return true; + }else{ + return false; + } + }else{ + bool res = getNextMatch2( m, qe, true ); + Assert(!res || !m.d_matched.isNull()); + return res; + } +} + + + +// Currently the implementation doesn't take into account that +// variable should have the same value given. +// TODO use the d_children way perhaps +// TODO replace by a real dictionnary +// We should create a real substitution? slower more precise +// We don't do that often +bool InstMatchGenerator::nonunifiable( TNode t0, const std::vector<Node> & vars){ + if(d_match_pattern.isNull()) return true; + + typedef std::vector<std::pair<TNode,TNode> > tstack; + tstack stack(1,std::make_pair(t0,d_match_pattern)); // t * pat + + while(!stack.empty()){ + const std::pair<TNode,TNode> p = stack.back(); stack.pop_back(); + const TNode & t = p.first; + const TNode & pat = p.second; + + // t or pat is a variable currently we consider that can match anything + if( find(vars.begin(),vars.end(),t) != vars.end() ) continue; + if( pat.getKind() == INST_CONSTANT ) continue; + + // t and pat are nonunifiable + if( !Trigger::isAtomicTrigger( t ) || !Trigger::isAtomicTrigger( pat ) ) { + if(t == pat) continue; + else return true; + }; + if( t.getOperator() != pat.getOperator() ) return true; + + //put the children on the stack + for( size_t i=0; i < pat.getNumChildren(); i++ ){ + stack.push_back(std::make_pair(t[i],pat[i])); + }; + } + // The heuristic can't find non-unifiability + return false; +} + +int InstMatchGenerator::addInstantiations( Node f, InstMatch& baseMatch, QuantifiersEngine* qe, int instLimit, bool addSplits ){ + //now, try to add instantiation for each match produced + int addedLemmas = 0; + InstMatch m; + while( getNextMatch( m, qe ) ){ + //m.makeInternal( d_quantEngine->getEqualityQuery() ); + m.add( baseMatch ); + if( qe->addInstantiation( f, m, addSplits ) ){ + addedLemmas++; + if( instLimit>0 && addedLemmas==instLimit ){ + return addedLemmas; + } + } + m.clear(); + } + //return number of lemmas added + return addedLemmas; +} + +int InstMatchGenerator::addTerm( Node f, Node t, QuantifiersEngine* qe ){ + Assert( Options::current()->eagerInstQuant ); + if( !d_match_pattern.isNull() ){ + InstMatch m; + if( getMatch( t, m, qe ) ){ + if( qe->addInstantiation( f, m ) ){ + return 1; + } + } + }else{ + for( int i=0; i<(int)d_children.size(); i++ ){ + d_children[i]->addTerm( f, t, qe ); + } + } + return 0; +} + +/** constructors */ +InstMatchGeneratorMulti::InstMatchGeneratorMulti( Node f, std::vector< Node >& pats, QuantifiersEngine* qe, int matchOption ) : +d_f( f ){ + Debug("smart-multi-trigger") << "Making smart multi-trigger for " << f << std::endl; + std::map< Node, std::vector< Node > > var_contains; + Trigger::getVarContains( f, pats, var_contains ); + //convert to indicies + for( std::map< Node, std::vector< Node > >::iterator it = var_contains.begin(); it != var_contains.end(); ++it ){ + Debug("smart-multi-trigger") << "Pattern " << it->first << " contains: "; + for( int i=0; i<(int)it->second.size(); i++ ){ + Debug("smart-multi-trigger") << it->second[i] << " "; + int index = it->second[i].getAttribute(InstVarNumAttribute()); + d_var_contains[ it->first ].push_back( index ); + d_var_to_node[ index ].push_back( it->first ); + } + Debug("smart-multi-trigger") << std::endl; + } + for( int i=0; i<(int)pats.size(); i++ ){ + Node n = pats[i]; + //make the match generator + d_children.push_back( new InstMatchGenerator( n, qe, matchOption ) ); + //compute unique/shared variables + std::vector< int > unique_vars; + std::map< int, bool > shared_vars; + int numSharedVars = 0; + for( int j=0; j<(int)d_var_contains[n].size(); j++ ){ + if( d_var_to_node[ d_var_contains[n][j] ].size()==1 ){ + Debug("smart-multi-trigger") << "Var " << d_var_contains[n][j] << " is unique to " << pats[i] << std::endl; + unique_vars.push_back( d_var_contains[n][j] ); + }else{ + shared_vars[ d_var_contains[n][j] ] = true; + numSharedVars++; + } + } + //we use the latest shared variables, then unique variables + std::vector< int > vars; + int index = i==0 ? (int)(pats.size()-1) : (i-1); + while( numSharedVars>0 && index!=i ){ + for( std::map< int, bool >::iterator it = shared_vars.begin(); it != shared_vars.end(); ++it ){ + if( it->second ){ + if( std::find( d_var_contains[ pats[index] ].begin(), d_var_contains[ pats[index] ].end(), it->first )!= + d_var_contains[ pats[index] ].end() ){ + vars.push_back( it->first ); + shared_vars[ it->first ] = false; + numSharedVars--; + } + } + } + index = index==0 ? (int)(pats.size()-1) : (index-1); + } + vars.insert( vars.end(), unique_vars.begin(), unique_vars.end() ); + Debug("smart-multi-trigger") << " Index[" << i << "]: "; + for( int i=0; i<(int)vars.size(); i++ ){ + Debug("smart-multi-trigger") << vars[i] << " "; + } + Debug("smart-multi-trigger") << std::endl; + //make ordered inst match trie + InstMatchTrie::ImtIndexOrder* imtio = new InstMatchTrie::ImtIndexOrder; + imtio->d_order.insert( imtio->d_order.begin(), vars.begin(), vars.end() ); + d_children_trie.push_back( InstMatchTrieOrdered( imtio ) ); + } + +} + +/** reset instantiation round (call this whenever equivalence classes have changed) */ +void InstMatchGeneratorMulti::resetInstantiationRound( QuantifiersEngine* qe ){ + for( int i=0; i<(int)d_children.size(); i++ ){ + d_children[i]->resetInstantiationRound( qe ); + } +} + +/** reset, eqc is the equivalence class to search in (any if eqc=null) */ +void InstMatchGeneratorMulti::reset( Node eqc, QuantifiersEngine* qe ){ + for( int i=0; i<(int)d_children.size(); i++ ){ + d_children[i]->reset( eqc, qe ); + } +} + +void InstMatchGeneratorMulti::collectInstantiations( QuantifiersEngine* qe, InstMatch& m, int& addedLemmas, InstMatchTrie* tr, + std::vector< IndexedTrie >& unique_var_tries, + int trieIndex, int childIndex, int endChildIndex, bool modEq ){ + if( childIndex==endChildIndex ){ + //now, process unique variables + collectInstantiations2( qe, m, addedLemmas, unique_var_tries, 0 ); + }else if( trieIndex<(int)d_children_trie[childIndex].getOrdering()->d_order.size() ){ + int curr_index = d_children_trie[childIndex].getOrdering()->d_order[trieIndex]; + Node curr_ic = qe->getInstantiationConstant( d_f, curr_index ); + if( m.d_map.find( curr_ic )==m.d_map.end() ){ + //if( d_var_to_node[ curr_index ].size()==1 ){ //FIXME + // //unique variable(s), defer calculation + // unique_var_tries.push_back( IndexedTrie( std::pair< int, int >( childIndex, trieIndex ), tr ) ); + // int newChildIndex = (childIndex+1)%(int)d_children.size(); + // collectInstantiations( qe, m, d_children_trie[newChildIndex].getTrie(), unique_var_tries, + // 0, newChildIndex, endChildIndex, modEq ); + //}else{ + //shared and non-set variable, add to InstMatch + for( std::map< Node, InstMatchTrie >::iterator it = tr->d_data.begin(); it != tr->d_data.end(); ++it ){ + InstMatch mn( &m ); + mn.d_map[ curr_ic ] = it->first; + collectInstantiations( qe, mn, addedLemmas, &(it->second), unique_var_tries, + trieIndex+1, childIndex, endChildIndex, modEq ); + } + //} + }else{ + //shared and set variable, try to merge + Node n = m.d_map[ curr_ic ]; + std::map< Node, InstMatchTrie >::iterator it = tr->d_data.find( n ); + if( it!=tr->d_data.end() ){ + collectInstantiations( qe, m, addedLemmas, &(it->second), unique_var_tries, + trieIndex+1, childIndex, endChildIndex, modEq ); + } + if( modEq ){ + //check modulo equality for other possible instantiations + if( ((uf::TheoryUF*)qe->getTheoryEngine()->getTheory( THEORY_UF ))->getEqualityEngine()->hasTerm( n ) ){ + eq::EqClassIterator eqc( qe->getEqualityQuery()->getRepresentative( n ), + ((uf::TheoryUF*)qe->getTheoryEngine()->getTheory( THEORY_UF ))->getEqualityEngine() ); + while( !eqc.isFinished() ){ + Node en = (*eqc); + if( en!=n ){ + std::map< Node, InstMatchTrie >::iterator itc = tr->d_data.find( en ); + if( itc!=tr->d_data.end() ){ + collectInstantiations( qe, m, addedLemmas, &(itc->second), unique_var_tries, + trieIndex+1, childIndex, endChildIndex, modEq ); + } + } + ++eqc; + } + } + } + } + }else{ + int newChildIndex = (childIndex+1)%(int)d_children.size(); + collectInstantiations( qe, m, addedLemmas, d_children_trie[newChildIndex].getTrie(), unique_var_tries, + 0, newChildIndex, endChildIndex, modEq ); + } +} + +void InstMatchGeneratorMulti::collectInstantiations2( QuantifiersEngine* qe, InstMatch& m, int& addedLemmas, + std::vector< IndexedTrie >& unique_var_tries, + int uvtIndex, InstMatchTrie* tr, int trieIndex ){ + if( uvtIndex<(int)unique_var_tries.size() ){ + int childIndex = unique_var_tries[uvtIndex].first.first; + if( !tr ){ + tr = unique_var_tries[uvtIndex].second; + trieIndex = unique_var_tries[uvtIndex].first.second; + } + if( trieIndex<(int)d_children_trie[childIndex].getOrdering()->d_order.size() ){ + int curr_index = d_children_trie[childIndex].getOrdering()->d_order[trieIndex]; + Node curr_ic = qe->getInstantiationConstant( d_f, curr_index ); + //unique non-set variable, add to InstMatch + for( std::map< Node, InstMatchTrie >::iterator it = tr->d_data.begin(); it != tr->d_data.end(); ++it ){ + InstMatch mn( &m ); + mn.d_map[ curr_ic ] = it->first; + collectInstantiations2( qe, mn, addedLemmas, unique_var_tries, uvtIndex, &(it->second), trieIndex+1 ); + } + }else{ + collectInstantiations2( qe, m, addedLemmas, unique_var_tries, uvtIndex+1 ); + } + }else{ + //m is an instantiation + if( qe->addInstantiation( d_f, m, true ) ){ + addedLemmas++; + Debug("smart-multi-trigger") << "-> Produced instantiation " << m << std::endl; + } + } +} + +int InstMatchGeneratorMulti::addInstantiations( Node f, InstMatch& baseMatch, QuantifiersEngine* qe, int instLimit, bool addSplits ){ + int addedLemmas = 0; + Debug("smart-multi-trigger") << "Process smart multi trigger" << std::endl; + for( int i=0; i<(int)d_children.size(); i++ ){ + Debug("smart-multi-trigger") << "Calculate matches " << i << std::endl; + std::vector< InstMatch > newMatches; + InstMatch m; + while( d_children[i]->getNextMatch( m, qe ) ){ + m.makeRepresentative( qe ); + newMatches.push_back( InstMatch( &m ) ); + m.clear(); + } + for( int j=0; j<(int)newMatches.size(); j++ ){ + processNewMatch( qe, newMatches[j], i, addedLemmas ); + } + } + return addedLemmas; +} + +void InstMatchGeneratorMulti::processNewMatch( QuantifiersEngine* qe, InstMatch& m, int fromChildIndex, int& addedLemmas ){ + //see if these produce new matches + d_children_trie[fromChildIndex].addInstMatch( qe, d_f, m, true ); + //possibly only do the following if we know that new matches will be produced? + //the issue is that instantiations are filtered in quantifiers engine, and so there is no guarentee that + // we can safely skip the following lines, even when we have already produced this match. + Debug("smart-multi-trigger") << "Child " << fromChildIndex << " produced match " << m << std::endl; + //collect new instantiations + int childIndex = (fromChildIndex+1)%(int)d_children.size(); + std::vector< IndexedTrie > unique_var_tries; + collectInstantiations( qe, m, addedLemmas, + d_children_trie[childIndex].getTrie(), unique_var_tries, 0, childIndex, fromChildIndex, true ); +} + +int InstMatchGeneratorMulti::addTerm( Node f, Node t, QuantifiersEngine* qe ){ + Assert( Options::current()->eagerInstQuant ); + int addedLemmas = 0; + for( int i=0; i<(int)d_children.size(); i++ ){ + if( ((InstMatchGenerator*)d_children[i])->d_match_pattern.getOperator()==t.getOperator() ){ + InstMatch m; + //if it produces a match, then process it with the rest + if( ((InstMatchGenerator*)d_children[i])->getMatch( t, m, qe ) ){ + processNewMatch( qe, m, i, addedLemmas ); + } + } + } + return addedLemmas; +} + +int InstMatchGeneratorSimple::addInstantiations( Node f, InstMatch& baseMatch, QuantifiersEngine* qe, int instLimit, bool addSplits ){ + InstMatch m; + m.add( baseMatch ); + int addedLemmas = 0; + if( d_match_pattern.getType()==NodeManager::currentNM()->booleanType() ){ + for( int i=0; i<2; i++ ){ + addInstantiations( m, qe, addedLemmas, 0, &(qe->getTermDatabase()->d_pred_map_trie[i][ d_match_pattern.getOperator() ]), + instLimit, addSplits ); + } + }else{ + addInstantiations( m, qe, addedLemmas, 0, &(qe->getTermDatabase()->d_func_map_trie[ d_match_pattern.getOperator() ]), + instLimit, addSplits ); + } + return addedLemmas; +} + +void InstMatchGeneratorSimple::addInstantiations( InstMatch& m, QuantifiersEngine* qe, int& addedLemmas, int argIndex, + TermArgTrie* tat, int instLimit, bool addSplits ){ + if( argIndex==(int)d_match_pattern.getNumChildren() ){ + //m is an instantiation + if( qe->addInstantiation( d_f, m, addSplits ) ){ + addedLemmas++; + Debug("simple-multi-trigger") << "-> Produced instantiation " << m << std::endl; + } + }else{ + if( d_match_pattern[argIndex].getKind()==INST_CONSTANT ){ + Node ic = d_match_pattern[argIndex]; + for( std::map< Node, TermArgTrie >::iterator it = tat->d_data.begin(); it != tat->d_data.end(); ++it ){ + Node t = it->first; + if( m.d_map[ ic ].isNull() || m.d_map[ ic ]==t ){ + Node prev = m.d_map[ ic ]; + m.d_map[ ic ] = t; + addInstantiations( m, qe, addedLemmas, argIndex+1, &(it->second), instLimit, addSplits ); + m.d_map[ ic ] = prev; + } + } + }else{ + Node r = qe->getEqualityQuery()->getRepresentative( d_match_pattern[argIndex] ); + std::map< Node, TermArgTrie >::iterator it = tat->d_data.find( r ); + if( it!=tat->d_data.end() ){ + addInstantiations( m, qe, addedLemmas, argIndex+1, &(it->second), instLimit, addSplits ); + } + } + } +} + +int InstMatchGeneratorSimple::addTerm( Node f, Node t, QuantifiersEngine* qe ){ + Assert( Options::current()->eagerInstQuant ); + InstMatch m; + for( int i=0; i<(int)t.getNumChildren(); i++ ){ + if( d_match_pattern[i].getKind()==INST_CONSTANT ){ + m.d_map[d_match_pattern[i]] = t[i]; + }else if( !qe->getEqualityQuery()->areEqual( d_match_pattern[i], t[i] ) ){ + return 0; + } + } + return qe->addInstantiation( f, m ) ? 1 : 0; +} diff --git a/src/theory/inst_match.h b/src/theory/inst_match.h new file mode 100644 index 000000000..73a99bcc5 --- /dev/null +++ b/src/theory/inst_match.h @@ -0,0 +1,443 @@ +/********************* */ +/*! \file inst_match.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief inst match class + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__INST_MATCH_H +#define __CVC4__INST_MATCH_H + +#include "theory/theory.h" +#include "util/hash.h" + +#include <ext/hash_set> +#include <iostream> +#include <map> + +#include "theory/uf/equality_engine.h" +#include "theory/uf/theory_uf.h" +#include "context/cdlist.h" + +//#define USE_EFFICIENT_E_MATCHING + +namespace CVC4 { +namespace theory { + +/** Attribute true for nodes that should not be used for matching */ +struct NoMatchAttributeId {}; +/** use the special for boolean flag */ +typedef expr::Attribute< NoMatchAttributeId, + bool, + expr::attr::NullCleanupStrategy, + true // context dependent + > NoMatchAttribute; + +class QuantifiersEngine; + +namespace uf { + class InstantiatorTheoryUf; + class TheoryUF; +}/* CVC4::theory::uf namespace */ + +class CandidateGenerator { +public: + CandidateGenerator(){} + ~CandidateGenerator(){} + + /** Get candidates functions. These set up a context to get all match candidates. + cg->reset( eqc ); + do{ + Node cand = cg->getNextCandidate(); + //....... + }while( !cand.isNull() ); + + eqc is the equivalence class you are searching in + */ + virtual void reset( Node eqc ) = 0; + virtual Node getNextCandidate() = 0; + /** add candidate to list of nodes returned by this generator */ + virtual void addCandidate( Node n ) {} + /** call this at the beginning of each instantiation round */ + virtual void resetInstantiationRound() = 0; +public: + /** legal candidate */ + static bool isLegalCandidate( Node n ); +};/* class CandidateGenerator */ + +/** candidate generator queue (for manual candidate generation) */ +class CandidateGeneratorQueue : public CandidateGenerator { +private: + std::vector< Node > d_candidates; + int d_candidate_index; +public: + CandidateGeneratorQueue() : d_candidate_index( 0 ){} + ~CandidateGeneratorQueue(){} + + void addCandidate( Node n ); + + void resetInstantiationRound(){} + void reset( Node eqc ); + Node getNextCandidate(); +};/* class CandidateGeneratorQueue */ + +class EqualityQuery { +public: + EqualityQuery(){} + ~EqualityQuery(){} + /** contains term */ + virtual bool hasTerm( Node a ) = 0; + /** get the representative of the equivalence class of a */ + virtual Node getRepresentative( Node a ) = 0; + /** returns true if a and b are equal in the current context */ + virtual bool areEqual( Node a, Node b ) = 0; + /** returns true is a and b are disequal in the current context */ + virtual bool areDisequal( Node a, Node b ) = 0; + /** getInternalRepresentative gets the current best representative in the equivalence class of a, based on some criteria. + If cbqi is active, this will return a term in the equivalence class of "a" that does + not contain instantiation constants, if such a term exists. + */ + virtual Node getInternalRepresentative( Node a ) = 0; +};/* class EqualityQuery */ + +/** basic class defining an instantiation */ +class InstMatch { +public: + InstMatch(); + InstMatch( InstMatch* m ); + + /** set the match of v to m */ + bool setMatch( EqualityQuery* q, Node v, Node m ); + /** fill all unfilled values with m */ + bool add( InstMatch& m ); + /** if compatible, fill all unfilled values with m and return true + return false otherwise */ + bool merge( EqualityQuery* q, InstMatch& m ); + /** debug print method */ + void debugPrint( const char* c ); + /** make complete */ + void makeComplete( Node f, QuantifiersEngine* qe ); + /** make internal: ensure that no term in d_map contains instantiation constants */ + void makeInternal( QuantifiersEngine* qe ); + /** make representative */ + void makeRepresentative( QuantifiersEngine* qe ); + /** apply rewrite */ + void applyRewrite(); + /** compute d_match */ + void computeTermVec( QuantifiersEngine* qe, const std::vector< Node >& vars, std::vector< Node >& match ); + /** compute d_match */ + void computeTermVec( const std::vector< Node >& vars, std::vector< Node >& match ); + /** clear */ + void clear(){ d_map.clear(); } + /** is_empty */ + bool empty(){ return d_map.empty(); } + /* map from variable to ground terms */ + std::map< Node, Node > d_map; + /* Node used for matching the trigger only for mono-trigger (just for + efficiency because I need only that) */ + Node d_matched; + /** to stream */ + inline void toStream(std::ostream& out) const { + out << "INST_MATCH( "; + for( std::map< Node, Node >::const_iterator it = d_map.begin(); it != d_map.end(); ++it ){ + if( it != d_map.begin() ){ out << ", "; } + out << it->first << " -> " << it->second; + } + out << " )"; + } +};/* class InstMatch */ + +inline std::ostream& operator<<(std::ostream& out, const InstMatch& m) { + m.toStream(out); + return out; +} + +/** trie for InstMatch objects */ +class InstMatchTrie { +public: + class ImtIndexOrder { + public: + std::vector< int > d_order; + };/* class InstMatchTrie ImtIndexOrder */ +private: + /** add match m for quantifier f starting at index, take into account equalities q, return true if successful */ + void addInstMatch2( QuantifiersEngine* qe, Node f, InstMatch& m, int index, ImtIndexOrder* imtio ); + /** exists match */ + bool existsInstMatch( QuantifiersEngine* qe, Node f, InstMatch& m, bool modEq, int index, ImtIndexOrder* imtio ); +public: + /** the data */ + std::map< Node, InstMatchTrie > d_data; +public: + InstMatchTrie(){} + ~InstMatchTrie(){} +public: + /** add match m for quantifier f, take into account equalities if modEq = true, + if imtio is non-null, this is the order to add to trie + return true if successful + */ + bool addInstMatch( QuantifiersEngine* qe, Node f, InstMatch& m, bool modEq = false, ImtIndexOrder* imtio = NULL ); +};/* class InstMatchTrie */ + +class InstMatchTrieOrdered { +private: + InstMatchTrie::ImtIndexOrder* d_imtio; + InstMatchTrie d_imt; +public: + InstMatchTrieOrdered( InstMatchTrie::ImtIndexOrder* imtio ) : d_imtio( imtio ){} + ~InstMatchTrieOrdered(){} + /** get ordering */ + InstMatchTrie::ImtIndexOrder* getOrdering() { return d_imtio; } + /** get trie */ + InstMatchTrie* getTrie() { return &d_imt; } +public: + /** add match m, return true if successful */ + bool addInstMatch( QuantifiersEngine* qe, Node f, InstMatch& m, bool modEq = false ){ + return d_imt.addInstMatch( qe, f, m, modEq, d_imtio ); + } +};/* class InstMatchTrieOrdered */ + +template<bool modEq = false> +class InstMatchTrie2 { +private: + + class Tree { + public: + typedef std::hash_map< Node, Tree *, NodeHashFunction > MLevel; + MLevel e; + const size_t level; //context level of creation + Tree() CVC4_UNDEFINED; + const Tree & operator =(const Tree & t) CVC4_UNDEFINED; + Tree(size_t l): level(l) {}; + ~Tree(){ + for(typename MLevel::iterator i = e.begin(); i!=e.end(); ++i) + delete(i->second); + }; + };/* class InstMatchTrie2::Tree */ + + + typedef std::pair<Tree *, TNode> Mod; + + class CleanUp { + public: + inline void operator()(Mod * m){ + typename Tree::MLevel::iterator i = m->first->e.find(m->second); + Assert (i != m->first->e.end()); //should not have been already removed + m->first->e.erase(i); + } + };/* class InstMatchTrie2::CleanUp */ + + EqualityQuery* d_eQ; + eq::EqualityEngine* d_eE; + + /* before for the order of destruction */ + Tree d_data; + + context::Context* d_context; + context::CDList<Mod, CleanUp, std::allocator<Mod> > d_mods; + + typedef std::map<Node, Node>::const_iterator mapIter; + + /** add the substitution given by the iterator*/ + void addSubTree( Tree* root, mapIter current, mapIter end, size_t currLevel); + /** test if it exists match, modulo uf-equations if modEq is true if + * return false the deepest point of divergence is put in [e] and + * [diverge]. + */ + bool existsInstMatch( Tree* root, + mapIter & current, mapIter& end, + Tree*& e, mapIter& diverge) const; + +public: + InstMatchTrie2(context::Context* c, QuantifiersEngine* q); + InstMatchTrie2(const InstMatchTrie2&) CVC4_UNDEFINED; + const InstMatchTrie2& operator=(const InstMatchTrie2 & e) CVC4_UNDEFINED; + /** add match m in the trie, + modEq specify to take into account equalities, + return true if it was never seen */ + bool addInstMatch( InstMatch& m); +};/* class InstMatchTrie2 */ + +/** base class for producing InstMatch objects */ +class IMGenerator { +public: + /** reset instantiation round (call this at beginning of instantiation round) */ + virtual void resetInstantiationRound( QuantifiersEngine* qe ) = 0; + /** reset, eqc is the equivalence class to search in (any if eqc=null) */ + virtual void reset( Node eqc, QuantifiersEngine* qe ) = 0; + /** get the next match. must call reset( eqc ) before this function. */ + virtual bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ) = 0; + /** return true if whatever Node is subsituted for the variables the + given Node can't match the pattern */ + virtual bool nonunifiable( TNode t, const std::vector<Node> & vars) = 0; + /** add instantiations directly */ + virtual int addInstantiations( Node f, InstMatch& baseMatch, QuantifiersEngine* qe, int instLimit = 0, bool addSplits = false ) = 0; + /** add ground term t, called when t is added to term db */ + virtual int addTerm( Node f, Node t, QuantifiersEngine* qe ) = 0; +};/* class IMGenerator */ + + +class InstMatchGenerator : public IMGenerator { +private: + /** candidate generator */ + CandidateGenerator* d_cg; + /** policy to use for matching */ + int d_matchPolicy; + /** children generators */ + std::vector< InstMatchGenerator* > d_children; + std::vector< int > d_children_index; + /** partial vector */ + std::vector< InstMatch > d_partial; + /** eq class */ + Node d_eq_class; + /** for arithmetic matching */ + std::map< Node, Node > d_arith_coeffs; + /** initialize pattern */ + void initializePatterns( std::vector< Node >& pats, QuantifiersEngine* qe ); + void initializePattern( Node pat, QuantifiersEngine* qe ); +public: + enum { + //options for producing matches + MATCH_GEN_DEFAULT = 0, + MATCH_GEN_EFFICIENT_E_MATCH, //generate matches via Efficient E-matching for SMT solvers + //others (internally used) + MATCH_GEN_INTERNAL_ARITHMETIC, + MATCH_GEN_INTERNAL_ERROR, + }; +private: + /** get the next match. must call d_cg->reset( ... ) before using. + only valid for use where !d_match_pattern.isNull(). + */ + bool getNextMatch2( InstMatch& m, QuantifiersEngine* qe, bool saveMatched = false ); + /** for arithmetic */ + bool getMatchArithmetic( Node t, InstMatch& m, QuantifiersEngine* qe ); +public: + /** get the match against ground term or formula t. + d_match_mattern and t should have the same shape. + only valid for use where !d_match_pattern.isNull(). + */ + bool getMatch( Node t, InstMatch& m, QuantifiersEngine* qe ); + + /** constructors */ + InstMatchGenerator( Node pat, QuantifiersEngine* qe, int matchOption = 0 ); + InstMatchGenerator( std::vector< Node >& pats, QuantifiersEngine* qe, int matchOption = 0 ); + /** destructor */ + ~InstMatchGenerator(){} + /** The pattern we are producing matches for. + If null, this is a multi trigger that is merging matches from d_children. + */ + Node d_pattern; + /** match pattern */ + Node d_match_pattern; +public: + /** reset instantiation round (call this whenever equivalence classes have changed) */ + void resetInstantiationRound( QuantifiersEngine* qe ); + /** reset, eqc is the equivalence class to search in (any if eqc=null) */ + void reset( Node eqc, QuantifiersEngine* qe ); + /** get the next match. must call reset( eqc ) before this function. */ + bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ); + /** return true if whatever Node is subsituted for the variables the + given Node can't match the pattern */ + bool nonunifiable( TNode t, const std::vector<Node> & vars); + /** add instantiations */ + int addInstantiations( Node f, InstMatch& baseMatch, QuantifiersEngine* qe, int instLimit = 0, bool addSplits = false ); + /** add ground term t */ + int addTerm( Node f, Node t, QuantifiersEngine* qe ); +};/* class InstMatchGenerator */ + +/** smart multi-trigger implementation */ +class InstMatchGeneratorMulti : public IMGenerator { +private: + void processNewMatch( QuantifiersEngine* qe, InstMatch& m, int fromChildIndex, int& addedLemmas ); +private: + /** indexed trie */ + typedef std::pair< std::pair< int, int >, InstMatchTrie* > IndexedTrie; + /** collect instantiations */ + void collectInstantiations( QuantifiersEngine* qe, InstMatch& m, int& addedLemmas, InstMatchTrie* tr, + std::vector< IndexedTrie >& unique_var_tries, + int trieIndex, int childIndex, int endChildIndex, bool modEq ); + /** collect instantiations 2 */ + void collectInstantiations2( QuantifiersEngine* qe, InstMatch& m, int& addedLemmas, + std::vector< IndexedTrie >& unique_var_tries, + int uvtIndex, InstMatchTrie* tr = NULL, int trieIndex = 0 ); +private: + /** var contains (variable indicies) for each pattern node */ + std::map< Node, std::vector< int > > d_var_contains; + /** variable indicies contained to pattern nodes */ + std::map< int, std::vector< Node > > d_var_to_node; + /** quantifier to use */ + Node d_f; + /** policy to use for matching */ + int d_matchPolicy; + /** children generators */ + std::vector< InstMatchGenerator* > d_children; + /** inst match tries for each child */ + std::vector< InstMatchTrieOrdered > d_children_trie; + /** calculate matches */ + void calculateMatches( QuantifiersEngine* qe ); +public: + /** constructors */ + InstMatchGeneratorMulti( Node f, std::vector< Node >& pats, QuantifiersEngine* qe, int matchOption = 0 ); + /** destructor */ + ~InstMatchGeneratorMulti(){} + /** reset instantiation round (call this whenever equivalence classes have changed) */ + void resetInstantiationRound( QuantifiersEngine* qe ); + /** reset, eqc is the equivalence class to search in (any if eqc=null) */ + void reset( Node eqc, QuantifiersEngine* qe ); + /** get the next match. must call reset( eqc ) before this function. (not implemented) */ + bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ) { return false; } + /** return true if whatever Node is subsituted for the variables the + given Node can't match the pattern */ + bool nonunifiable( TNode t, const std::vector<Node> & vars) { return true; } + /** add instantiations */ + int addInstantiations( Node f, InstMatch& baseMatch, QuantifiersEngine* qe, int instLimit = 0, bool addSplits = false ); + /** add ground term t */ + int addTerm( Node f, Node t, QuantifiersEngine* qe ); +};/* class InstMatchGeneratorMulti */ + +class TermArgTrie; + +/** smart (single)-trigger implementation */ +class InstMatchGeneratorSimple : public IMGenerator { +private: + /** quantifier for match term */ + Node d_f; + /** match term */ + Node d_match_pattern; + /** add instantiations */ + void addInstantiations( InstMatch& m, QuantifiersEngine* qe, int& addedLemmas, + int argIndex, TermArgTrie* tat, int instLimit, bool addSplits ); +public: + /** constructors */ + InstMatchGeneratorSimple( Node f, Node pat ) : d_f( f ), d_match_pattern( pat ){} + /** destructor */ + ~InstMatchGeneratorSimple(){} + /** reset instantiation round (call this whenever equivalence classes have changed) */ + void resetInstantiationRound( QuantifiersEngine* qe ) {} + /** reset, eqc is the equivalence class to search in (any if eqc=null) */ + void reset( Node eqc, QuantifiersEngine* qe ) {} + /** get the next match. must call reset( eqc ) before this function. (not implemented) */ + bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ) { return false; } + /** return true if whatever Node is subsituted for the variables the + given Node can't match the pattern */ + bool nonunifiable( TNode t, const std::vector<Node> & vars) { return true; } + /** add instantiations */ + int addInstantiations( Node f, InstMatch& baseMatch, QuantifiersEngine* qe, int instLimit = 0, bool addSplits = false ); + /** add ground term t, possibly add instantiations */ + int addTerm( Node f, Node t, QuantifiersEngine* qe ); +};/* class InstMatchGeneratorSimple */ + +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__INST_MATCH_H */ diff --git a/src/theory/inst_match_impl.h b/src/theory/inst_match_impl.h new file mode 100644 index 000000000..18c4998b8 --- /dev/null +++ b/src/theory/inst_match_impl.h @@ -0,0 +1,125 @@ +/********************* */ +/*! \file inst_match.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief inst match class + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__INST_MATCH_IMPL_H +#define __CVC4__INST_MATCH_IMPL_H + +#include "theory/inst_match.h" +#include "theory/theory_engine.h" +#include "theory/quantifiers_engine.h" +#include "theory/uf/theory_uf_instantiator.h" +#include "theory/uf/theory_uf_candidate_generator.h" +#include "theory/uf/equality_engine.h" + +namespace CVC4 { +namespace theory { + +template<bool modEq> +InstMatchTrie2<modEq>::InstMatchTrie2(context::Context* c, QuantifiersEngine* qe): + d_data(c->getLevel()), d_context(c), d_mods(c) { + d_eQ = qe->getEqualityQuery(); + d_eE = ((uf::TheoryUF*)qe->getTheoryEngine()->getTheory( THEORY_UF ))->getEqualityEngine(); +}; + +/** add match m for quantifier f starting at index, take into account equalities q, return true if successful */ +template<bool modEq> +void InstMatchTrie2<modEq>::addSubTree( Tree * root, mapIter current, mapIter end, size_t currLevel ) { + if( current == end ) return; + + Assert(root->e.find(current->second) == root->e.end()); + Tree * root2 = new Tree(currLevel); + root->e.insert(make_pair(current->second, root2)); + addSubTree(root2, ++current, end, currLevel ); +} + +/** exists match */ +template<bool modEq> +bool InstMatchTrie2<modEq>::existsInstMatch(InstMatchTrie2<modEq>::Tree * root, + mapIter & current, mapIter & end, + Tree * & e, mapIter & diverge) const{ + if( current == end ) { + Debug("Trie2") << "Trie2 Bottom " << std::endl; + --current; + return true; + }; //Already their + + if (current->first > diverge->first){ + // this point is the deepest point currently seen map are ordered + e = root; + diverge = current; + }; + + TNode n = current->second; + typename InstMatchTrie2<modEq>::Tree::MLevel::iterator it = + root->e.find( n ); + if( it!=root->e.end() && + existsInstMatch( (*it).second, ++current, end, e, diverge) ){ + Debug("Trie2") << "Trie2 Directly here " << n << std::endl; + --current; + return true; + } + Assert( it==root->e.end() || e != root ); + + // Even if n is in the trie others of the equivalence class + // can also be in it since the equality can have appeared + // after they have been added + if( modEq && d_eE->hasTerm( n ) ){ + //check modulo equality if any other instantiation match exists + eq::EqClassIterator eqc( d_eQ->getRepresentative( n ), d_eE ); + for( ;!eqc.isFinished();++eqc ){ + TNode en = (*eqc); + if( en == n ) continue; // already tested + typename InstMatchTrie2<modEq>::Tree::MLevel::iterator itc = + root->e.find( en ); + if( itc!=root->e.end() && + existsInstMatch( (*itc).second, ++current, end, e, diverge) ){ + Debug("Trie2") << "Trie2 Indirectly here by equality " << n << " = " << en << std::endl; + --current; + return true; + } + Assert( itc==root->e.end() || e != root ); + } + } + --current; + return false; +} + +template<bool modEq> +bool InstMatchTrie2<modEq>::addInstMatch( InstMatch& m ) { + mapIter begin = m.d_map.begin(); + mapIter end = m.d_map.end(); + InstMatchTrie2<modEq>::Tree * e = &d_data; + mapIter diverge = begin; + if( !existsInstMatch(e, begin, end, e, diverge ) ){ + Assert(!diverge->second.isNull()); + size_t currLevel = d_context->getLevel(); + addSubTree( e, diverge, end, currLevel ); + if(e->level != currLevel) + //If same level that e, will be removed at the same time than e + d_mods.push_back(make_pair(e,diverge->second)); + return true; + }else{ + return false; + } +} + +}/* CVC4::theory namespace */ + +}/* CVC4 namespace */ + +#endif /* __CVC4__INST_MATCH_IMPL_H */ diff --git a/src/theory/instantiator_default.cpp b/src/theory/instantiator_default.cpp new file mode 100644 index 000000000..4d6ea2fdb --- /dev/null +++ b/src/theory/instantiator_default.cpp @@ -0,0 +1,54 @@ +/********************* */ +/*! \file instantiator_default.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of instantiator_default class + **/ + +#include "theory/instantiator_default.h" +#include "theory/theory_engine.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; + +InstantiatorDefault::InstantiatorDefault(context::Context* c, QuantifiersEngine* ie, Theory* th) : + Instantiator( c, ie, th ) { +} + +void InstantiatorDefault::assertNode( Node assertion ){ +} + +void InstantiatorDefault::processResetInstantiationRound( Theory::Effort effort ){ +} + +int InstantiatorDefault::process( Node f, Theory::Effort effort, int e, int limitInst ){ + if( e < 4 ){ + return InstStrategy::STATUS_UNFINISHED; + }else if( e == 4 ){ + Debug("quant-default") << "Process " << f << " : " << std::endl; + InstMatch m; + for( int j=0; j<(int)d_quantEngine->getNumInstantiationConstants( f ); j++ ){ + Node i = d_quantEngine->getInstantiationConstant( f, j ); + Debug("quant-default") << "Getting value for " << i << std::endl; + if( d_quantEngine->getTheoryEngine()->theoryOf( i )==getTheory() ){ //if it belongs to this theory + Node val = d_th->getValue( i ); + Debug("quant-default") << "Default Instantiate for " << d_th->getId() << ", setting " << i << " = " << val << std::endl; + m.d_map[ i ] = val; + } + } + d_quantEngine->addInstantiation( f, m ); + } + return InstStrategy::STATUS_UNKNOWN; +} diff --git a/src/theory/instantiator_default.h b/src/theory/instantiator_default.h new file mode 100644 index 000000000..351d0c4a3 --- /dev/null +++ b/src/theory/instantiator_default.h @@ -0,0 +1,48 @@ +/********************* */ +/*! \file instantiator_default.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief instantiator_default + **/ + + +#include "cvc4_private.h" + +#ifndef __CVC4__INSTANTIATOR_DEFAULT_H +#define __CVC4__INSTANTIATOR_DEFAULT_H + +#include <string> +#include "theory/quantifiers_engine.h" + +namespace CVC4 { +namespace theory { + +class InstantiatorDefault : public Instantiator { + friend class QuantifiersEngine; +protected: + /** reset instantiation round */ + void processResetInstantiationRound(Theory::Effort effort); + /** process quantifier */ + int process(Node f, Theory::Effort effort, int e, int limitInst = 0); +public: + InstantiatorDefault(context::Context* c, QuantifiersEngine* ie, Theory* th); + ~InstantiatorDefault() { } + /** check function, assertion is asserted to theory */ + void assertNode( Node assertion ); + /** identify */ + std::string identify() const { return std::string("InstantiatorDefault"); } +};/* class Instantiatior */ + +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__INSTANTIATOR_DEFAULT_H */ diff --git a/src/theory/instantiator_tables_template.cpp b/src/theory/instantiator_tables_template.cpp new file mode 100644 index 000000000..7a78c3aae --- /dev/null +++ b/src/theory/instantiator_tables_template.cpp @@ -0,0 +1,40 @@ +/********************* */ +/*! \file instantiator_tables_template.cpp + ** \verbatim + ** Original author: mdeters + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011, 2012 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Instantiator tables for quantifier-friendly theories + ** + ** This file contains template code for the instantiator tables that are + ** generated from the Theory kinds files. + **/ + +#include "context/context.h" +#include "theory/quantifiers_engine.h" + +${instantiator_includes} + +#line 26 "${template}" + +namespace CVC4 { +namespace theory { + +Instantiator* Theory::makeInstantiator(context::Context* c, theory::QuantifiersEngine* qe) { + switch(d_id) { +${make_instantiator_cases} +#line 34 "${template}" + default: + Unhandled(d_id); + } +} + +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/mkinstantiator b/src/theory/mkinstantiator new file mode 100755 index 000000000..1908d2e96 --- /dev/null +++ b/src/theory/mkinstantiator @@ -0,0 +1,242 @@ +#!/bin/bash +# +# mkinstantiator +# Morgan Deters <mdeters@cs.nyu.edu> for CVC4 +# Copyright (c) 2010-2012 The CVC4 Project +# +# The purpose of this script is to create rewriter_tables.h from a template +# and a list of theory kinds. +# +# Invocation: +# +# mkinstantiator template-file theory-kind-files... +# +# Output is to standard out. +# + +copyright=2010-2012 + +cat <<EOF +/********************* */ +/** instantiator_tables.h + ** + ** Copyright $copyright The AcSys Group, New York University, and as below. + ** + ** This header file automatically generated by: + ** + ** $0 $@ + ** + ** for the CVC4 project. + **/ + +EOF + +me=$(basename "$0") + +template=$1; shift + +instantiator_includes= +instantiator_class= +theory_id= +theory_class= +theory_header= + +make_instantiator_cases= +instantiator= + +seen_theory=false +seen_theory_builtin=false + +function theory { + # theory id T header + + lineno=${BASH_LINENO[0]} + + if $seen_theory; then + echo "$kf:$lineno: theory declaration can only appear once" >&2 + exit 1; + fi + + seen_theory=true + if [ "$1" = THEORY_BUILTIN ]; then + if $seen_theory_builtin; then + echo "$kf:$lineno: error: \"builtin\" theory redefined" >&2 + exit 1 + fi + seen_theory_builtin=true + elif [ -z "$1" -o -z "$2" -o -z "$3" ]; then + echo "$kf:$lineno: error: \"theory\" directive missing class or header argument" >&2 + exit 1 + elif ! expr "$2" : '\(::*\)' >/dev/null; then + echo "$kf:$lineno: warning: theory class \`$2' isn't fully-qualified (e.g., ::CVC4::theory::foo)" >&2 + elif ! expr "$2" : '\(::CVC4::theory::*\)' >/dev/null; then + echo "$kf:$lineno: warning: theory class not under ::CVC4::theory namespace" >&2 + fi + + theory_id="$1" + theory_class="$2" + theory_header="$3" + + instantiator_class= + instantiator=NULL +} + +function instantiator { + # instantiator class header + lineno=${BASH_LINENO[0]} + check_theory_seen + + if [ -n "$instantiator_class" ]; then + echo "$kf:$lineno: error: cannot have two \"instantiator\" directives" >&2 + exit 1 + fi + + instantiator_class="$1" + instantiator_header="$2" + + if [ -z "$instantiator_class" -o -z "$instantiator_header" ]; then + echo "$kf:$lineno: error: \"instantiator\" directive missing class or header argument" >&2 + exit 1 + fi + + instantiator_includes="${instantiator_includes}#include \"$theory_header\" +#line $lineno \"$kf\" +#include \"$instantiator_header\" +" + instantiator="new $instantiator_class(c, qe, static_cast< $theory_class* >(this))"; +} + +function properties { + # properties prop* + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function endtheory { + # endtheory + lineno=${BASH_LINENO[0]} + check_theory_seen + seen_endtheory=true + + make_instantiator_cases="${make_instantiator_cases} +#line $lineno \"$kf\" + case $theory_id: + return $instantiator; +" +} + +function typechecker { + # typechecker header + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function typerule { + # typerule OPERATOR typechecking-class + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function rewriter { + # rewriter class header + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function sort { + # sort TYPE cardinality [well-founded ground-term header | not-well-founded] ["comment"] + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function cardinality { + # cardinality TYPE cardinality-computer [header] + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function well-founded { + # well-founded TYPE wellfoundedness-computer [header] + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function variable { + # variable K ["comment"] + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function operator { + # operator K #children ["comment"] + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function parameterized { + # parameterized K1 K2 #children ["comment"] + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function constant { + # constant K T Hasher header ["comment"] + lineno=${BASH_LINENO[0]} + check_theory_seen +} + +function check_theory_seen { + if $seen_endtheory; then + echo "$kf:$lineno: error: command after \"endtheory\" declaration (endtheory has to be last)" >&2 + exit 1 + fi + if ! $seen_theory; then + echo "$kf:$lineno: error: no \"theory\" declaration found (it has to be first)" >&2 + exit 1 + fi +} + +function check_builtin_theory_seen { + if ! $seen_theory_builtin; then + echo "$me: warning: no declaration for the builtin theory found" >&2 + fi +} + +while [ $# -gt 0 ]; do + kf=$1 + seen_theory=false + seen_endtheory=false + b=$(basename $(dirname "$kf")) + source "$kf" + if ! $seen_theory; then + echo "$kf: error: no theory content found in file!" >&2 + exit 1 + fi + if ! $seen_endtheory; then + echo "$kf:$lineno: error: no \"endtheory\" declaration found (it is required at the end)" >&2 + exit 1 + fi + shift +done +check_builtin_theory_seen + +## output + +# generate warnings about incorrect #line annotations in templates +nl -ba -s' ' "$template" | grep '^ *[0-9][0-9]* # *line' | + awk '{OFS="";if($1+1!=$3) print "'"$template"':",$1,": warning: incorrect annotation \"#line ",$3,"\" (it should be \"#line ",($1+1),"\")"}' >&2 + +text=$(cat "$template") +for var in \ + instantiator_includes \ + make_instantiator_cases \ + template \ + ; do + eval text="\${text//\\\$\\{$var\\}/\${$var}}" +done +error=`expr "$text" : '.*\${\([^}]*\)}.*'` +if [ -n "$error" ]; then + echo "$template:0: error: undefined replacement \${$error}" >&2 + exit 1 +fi +echo "$text" diff --git a/src/theory/mkrewriter b/src/theory/mkrewriter index b8fa51d77..780409d52 100755 --- a/src/theory/mkrewriter +++ b/src/theory/mkrewriter @@ -2,7 +2,7 @@ # # mkrewriter # Morgan Deters <mdeters@cs.nyu.edu> for CVC4 -# Copyright (c) 2010-2011 The CVC4 Project +# Copyright (c) 2010-2012 The CVC4 Project # # The purpose of this script is to create rewriter_tables.h from a template # and a list of theory kinds. @@ -14,7 +14,7 @@ # Output is to standard out. # -copyright=2010-2011 +copyright=2010-2012 cat <<EOF /********************* */ @@ -83,6 +83,12 @@ function theory { theory_id="$1" } +function instantiator { + # instantiator class header + lineno=${BASH_LINENO[0]} + check_theory_seen +} + function properties { # properties prop* lineno=${BASH_LINENO[0]} diff --git a/src/theory/mktheorytraits b/src/theory/mktheorytraits index 2d3b4a43a..297df1f36 100755 --- a/src/theory/mktheorytraits +++ b/src/theory/mktheorytraits @@ -2,7 +2,7 @@ # # mktheorytraits # Morgan Deters <mdeters@cs.nyu.edu> for CVC4 -# Copyright (c) 2010-2011 The CVC4 Project +# Copyright (c) 2010-2012 The CVC4 Project # # The purpose of this script is to create theory_traits.h from a template # and a list of theory kinds. @@ -14,7 +14,7 @@ # Output is to standard out. # -copyright=2010-2011 +copyright=2010-2012 cat <<EOF /********************* */ @@ -55,6 +55,9 @@ theory_parametric="false" rewriter_class= rewriter_header= +instantiator_class=void +instantiator_header= + theory_id= theory_class= @@ -123,6 +126,7 @@ template<> struct TheoryTraits<${theory_id}> { typedef ${theory_class} theory_class; typedef ${rewriter_class} rewriter_class; + typedef ${instantiator_class} instantiator_class; static const bool isStableInfinite = ${theory_stable_infinite}; static const bool isFinite = ${theory_finite}; @@ -188,6 +192,18 @@ function typerule { check_theory_seen } +function instantiator { + # instantiator class header + lineno=${BASH_LINENO[0]} + check_theory_seen + + instantiator_class="$1" + instantiator_header="$2" + + theory_includes="${theory_includes}#include \"$2\" +" +} + function properties { # properties property* lineno=${BASH_LINENO[0]} diff --git a/src/theory/output_channel.h b/src/theory/output_channel.h index 5c2cedf5b..b1a5fc60c 100644 --- a/src/theory/output_channel.h +++ b/src/theory/output_channel.h @@ -149,6 +149,64 @@ public: } /** + * If a decision is made on n, it must be in the phase specified. + * Note that this is enforced *globally*, i.e., it is completely + * context-INdependent. If you ever requirePhase() on a literal, + * it is phase-locked forever and ever. If it is to ever have the + * other phase as its assignment, it will be because it has been + * propagated that way (or it's a unit, at decision level 0). + * + * @param n - a theory atom with a SAT literal assigned; must have + * been pre-registered + * @param phase - the phase to decide on n + */ + virtual void requirePhase(TNode n, bool phase) + throw(Interrupted, TypeCheckingExceptionPrivate, AssertionException) = 0; + + /** + * Flips the most recent unflipped decision to the other phase and + * returns true. If all decisions have been flipped, the root + * decision is re-flipped and flipDecision() returns false. If no + * decisions (flipped nor unflipped) are on the decision stack, the + * state is not affected and flipDecision() returns false. + * + * For example, if l1, l2, and l3 are all decision literals, and + * have been decided in positive phase, a series of flipDecision() + * calls has the following effects: + * + * l1 l2 l3 <br/> + * l1 l2 ~l3 <br/> + * l1 ~l2 <br/> + * ~l1 <br/> + * l1 (and flipDecision() returns false) + * + * Naturally, flipDecision() might be interleaved with search. For example: + * + * l1 l2 l3 <br/> + * flipDecision() <br/> + * l1 l2 ~l3 <br/> + * flipDecision() <br/> + * l1 ~l2 <br/> + * SAT decides l3 <br/> + * l1 ~l2 l3 <br/> + * flipDecision() <br/> + * l1 ~l2 ~l3 <br/> + * flipDecision() <br/> + * ~l1 <br/> + * SAT decides l2 <br/> + * ~l1 l2 <br/> + * flipDecision() <br/> + * ~l1 ~l2 <br/> + * flipDecision() returns FALSE<br/> + * l1 + * + * @return true if a decision was flipped; false if no decision + * could be flipped, or if the root decision was re-flipped + */ + virtual bool flipDecision() + throw(Interrupted, TypeCheckingExceptionPrivate, AssertionException) = 0; + + /** * Notification from a theory that it realizes it is incomplete at * this context level. If SAT is later determined by the * TheoryEngine, it should actually return an UNKNOWN result. diff --git a/src/theory/quantifiers/Makefile b/src/theory/quantifiers/Makefile new file mode 100644 index 000000000..8ffdfb575 --- /dev/null +++ b/src/theory/quantifiers/Makefile @@ -0,0 +1,4 @@ +topdir = ../../.. +srcdir = src/theory/quantifiers + +include $(topdir)/Makefile.subdir diff --git a/src/theory/quantifiers/Makefile.am b/src/theory/quantifiers/Makefile.am new file mode 100644 index 000000000..de74e44f8 --- /dev/null +++ b/src/theory/quantifiers/Makefile.am @@ -0,0 +1,21 @@ +AM_CPPFLAGS = \ + -D__BUILDING_CVC4LIB \ + -I@srcdir@/../../include -I@srcdir@/../.. -I@builddir@/../.. +AM_CXXFLAGS = -Wall $(FLAG_VISIBILITY_HIDDEN) + +noinst_LTLIBRARIES = libquantifiers.la + +libquantifiers_la_SOURCES = \ + theory_quantifiers_type_rules.h \ + theory_quantifiers.h \ + quantifiers_rewriter.h \ + quantifiers_rewriter.cpp \ + theory_quantifiers.cpp \ + theory_quantifiers_instantiator.h \ + theory_quantifiers_instantiator.cpp \ + instantiation_engine.h \ + instantiation_engine.cpp \ + model_engine.h \ + model_engine.cpp + +EXTRA_DIST = kinds
\ No newline at end of file diff --git a/src/theory/quantifiers/instantiation_engine.cpp b/src/theory/quantifiers/instantiation_engine.cpp new file mode 100644 index 000000000..8478dff1e --- /dev/null +++ b/src/theory/quantifiers/instantiation_engine.cpp @@ -0,0 +1,393 @@ +/********************* */ +/*! \file instantiation_engine.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of instantiation engine class + **/ + +#include "theory/quantifiers/instantiation_engine.h" + +#include "theory/theory_engine.h" +#include "theory/uf/theory_uf_instantiator.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; + +//#define IE_PRINT_PROCESS_TIMES + +InstantiationEngine::InstantiationEngine( TheoryQuantifiers* th ) : +d_th( th ){ + +} + +QuantifiersEngine* InstantiationEngine::getQuantifiersEngine(){ + return d_th->getQuantifiersEngine(); +} + +bool InstantiationEngine::hasAddedCbqiLemma( Node f ) { + return d_ce_lit.find( f ) != d_ce_lit.end(); +} + +void InstantiationEngine::addCbqiLemma( Node f ){ + Assert( doCbqi( f ) && !hasAddedCbqiLemma( f ) ); + //code for counterexample-based quantifier instantiation + Debug("cbqi") << "Do cbqi for " << f << std::endl; + //make the counterexample body + //Node ceBody = f[1].substitute( getQuantifiersEngine()->d_vars[f].begin(), getQuantifiersEngine()->d_vars[f].end(), + // getQuantifiersEngine()->d_inst_constants[f].begin(), + // getQuantifiersEngine()->d_inst_constants[f].end() ); + //get the counterexample literal + Node ceBody = getQuantifiersEngine()->getCounterexampleBody( f ); + Node ceLit = d_th->getValuation().ensureLiteral( ceBody.notNode() ); + d_ce_lit[ f ] = ceLit; + getQuantifiersEngine()->setInstantiationConstantAttr( ceLit, f ); + // set attributes, mark all literals in the body of n as dependent on cel + //registerLiterals( ceLit, f ); + //require any decision on cel to be phase=true + d_th->getOutputChannel().requirePhase( ceLit, true ); + Debug("cbqi-debug") << "Require phase " << ceLit << " = true." << std::endl; + //add counterexample lemma + NodeBuilder<> nb(kind::OR); + nb << f << ceLit; + Node lem = nb; + Debug("cbqi-debug") << "Counterexample lemma : " << lem << std::endl; + d_th->getOutputChannel().lemma( lem ); +} + +bool InstantiationEngine::doInstantiationRound( Theory::Effort effort ){ + //if counterexample-based quantifier instantiation is active + if( Options::current()->cbqi ){ + //check if any cbqi lemma has not been added yet + bool addedLemma = false; + for( int i=0; i<(int)getQuantifiersEngine()->getNumAssertedQuantifiers(); i++ ){ + Node f = getQuantifiersEngine()->getAssertedQuantifier( i ); + if( doCbqi( f ) && !hasAddedCbqiLemma( f ) ){ + //add cbqi lemma + addCbqiLemma( f ); + addedLemma = true; + } + } + if( addedLemma ){ + return true; + } + } + //if not, proceed to instantiation round + Debug("inst-engine") << "IE: Instantiation Round." << std::endl; + Debug("inst-engine-ctrl") << "IE: Instantiation Round." << std::endl; + //reset instantiators + Debug("inst-engine-ctrl") << "Reset IE" << std::endl; + for( int i=0; i<theory::THEORY_LAST; i++ ){ + if( getQuantifiersEngine()->getInstantiator( i ) ){ + getQuantifiersEngine()->getInstantiator( i )->resetInstantiationRound( effort ); + } + } + getQuantifiersEngine()->getTermDatabase()->reset( effort ); + //iterate over an internal effort level e + int e = 0; + int eLimit = effort==Theory::EFFORT_LAST_CALL ? 10 : 2; + d_inst_round_status = InstStrategy::STATUS_UNFINISHED; + //while unfinished, try effort level=0,1,2.... + while( d_inst_round_status==InstStrategy::STATUS_UNFINISHED && e<=eLimit ){ + Debug("inst-engine") << "IE: Prepare instantiation (" << e << ")." << std::endl; + d_inst_round_status = InstStrategy::STATUS_SAT; + //instantiate each quantifier + for( int q=0; q<getQuantifiersEngine()->getNumAssertedQuantifiers(); q++ ){ + Node f = getQuantifiersEngine()->getAssertedQuantifier( q ); + Debug("inst-engine-debug") << "IE: Instantiate " << f << "..." << std::endl; + //if this quantifier is active + if( getQuantifiersEngine()->getActive( f ) ){ + //int e_use = getQuantifiersEngine()->getRelevance( f )==-1 ? e - 1 : e; + int e_use = e; + if( e_use>=0 ){ + //use each theory instantiator to instantiate f + for( int i=0; i<theory::THEORY_LAST; i++ ){ + if( getQuantifiersEngine()->getInstantiator( i ) ){ + Debug("inst-engine-debug") << "Do " << getQuantifiersEngine()->getInstantiator( i )->identify() << " " << e_use << std::endl; + int limitInst = 0; + int quantStatus = getQuantifiersEngine()->getInstantiator( i )->doInstantiation( f, effort, e_use, limitInst ); + Debug("inst-engine-debug") << " -> status is " << quantStatus << std::endl; + InstStrategy::updateStatus( d_inst_round_status, quantStatus ); + } + } + } + } + } + //do not consider another level if already added lemma at this level + if( getQuantifiersEngine()->hasAddedLemma() ){ + d_inst_round_status = InstStrategy::STATUS_UNKNOWN; + } + e++; + } + Debug("inst-engine") << "All instantiators finished, # added lemmas = "; + Debug("inst-engine") << (int)getQuantifiersEngine()->d_lemmas_waiting.size() << std::endl; + //Notice() << "All instantiators finished, # added lemmas = " << (int)d_lemmas_waiting.size() << std::endl; + if( !getQuantifiersEngine()->hasAddedLemma() ){ + Debug("inst-engine-stuck") << "No instantiations produced at this state: " << std::endl; + for( int i=0; i<theory::THEORY_LAST; i++ ){ + if( getQuantifiersEngine()->getInstantiator( i ) ){ + getQuantifiersEngine()->getInstantiator( i )->debugPrint("inst-engine-stuck"); + Debug("inst-engine-stuck") << std::endl; + } + } + Debug("inst-engine-ctrl") << "---Fail." << std::endl; + return false; + }else{ + Debug("inst-engine-ctrl") << "---Done. " << (int)getQuantifiersEngine()->d_lemmas_waiting.size() << std::endl; +#ifdef IE_PRINT_PROCESS_TIMES + Notice() << "lemmas = " << (int)getQuantifiersEngine()->d_lemmas_waiting.size() << std::endl; +#endif + //flush lemmas to output channel + getQuantifiersEngine()->flushLemmas( &d_th->getOutputChannel() ); + return true; + } +} + +int ierCounter = 0; + +void InstantiationEngine::check( Theory::Effort e ){ + if( e==Theory::EFFORT_FULL ){ + ierCounter++; + } + //determine if we should perform check, based on instWhenMode + bool performCheck = false; + if( Options::current()->instWhenMode==Options::INST_WHEN_FULL ){ + performCheck = ( e >= Theory::EFFORT_FULL ); + }else if( Options::current()->instWhenMode==Options::INST_WHEN_FULL_LAST_CALL ){ + performCheck = ( ( e==Theory::EFFORT_FULL && ierCounter%2==0 ) || e==Theory::EFFORT_LAST_CALL ); + }else if( Options::current()->instWhenMode==Options::INST_WHEN_LAST_CALL ){ + performCheck = ( e >= Theory::EFFORT_LAST_CALL ); + }else{ + performCheck = true; + } + if( performCheck ){ + Debug("inst-engine") << "IE: Check " << e << " " << ierCounter << std::endl; +#ifdef IE_PRINT_PROCESS_TIMES + double clSet = double(clock())/double(CLOCKS_PER_SEC); + Notice() << "Run instantiation round " << e << " " << ierCounter << std::endl; +#endif + bool quantActive = false; + //for each quantifier currently asserted, + // such that the counterexample literal is not in positive in d_counterexample_asserts + // for( BoolMap::iterator i = d_forall_asserts.begin(); i != d_forall_asserts.end(); i++ ) { + // if( (*i).second ) { + for( int i=0; i<(int)getQuantifiersEngine()->getNumAssertedQuantifiers(); i++ ){ + Node n = getQuantifiersEngine()->getAssertedQuantifier( i ); + if( Options::current()->cbqi && hasAddedCbqiLemma( n ) ){ + Node cel = d_ce_lit[ n ]; + bool active, value; + bool ceValue = false; + if( d_th->getValuation().hasSatValue( cel, value ) ){ + active = value; + ceValue = true; + }else{ + active = true; + } + getQuantifiersEngine()->setActive( n, active ); + if( active ){ + Debug("quantifiers") << " Active : " << n; + quantActive = true; + }else{ + Debug("quantifiers") << " NOT active : " << n; + if( d_th->getValuation().isDecision( cel ) ){ + Debug("quant-req-phase") << "Bad decision : " << cel << std::endl; + } + //note that the counterexample literal must not be a decision + Assert( !d_th->getValuation().isDecision( cel ) ); + } + if( d_th->getValuation().hasSatValue( n, value ) ){ + Debug("quantifiers") << ", value = " << value; + } + if( ceValue ){ + Debug("quantifiers") << ", ce is asserted"; + } + Debug("quantifiers") << std::endl; + }else{ + getQuantifiersEngine()->setActive( n, true ); + quantActive = true; + Debug("quantifiers") << " Active : " << n << ", no ce assigned." << std::endl; + } + Debug("quantifiers-relevance") << "Quantifier : " << n << std::endl; + Debug("quantifiers-relevance") << " Relevance : " << getQuantifiersEngine()->getRelevance( n ) << std::endl; + Debug("quantifiers") << " Relevance : " << getQuantifiersEngine()->getRelevance( n ) << std::endl; + } + //} + if( quantActive ){ + bool addedLemmas = doInstantiationRound( e ); + //Debug("quantifiers-dec") << "Do instantiation, level = " << d_th->getValuation().getDecisionLevel() << std::endl; + //for( int i=1; i<=(int)d_valuation.getDecisionLevel(); i++ ){ + // Debug("quantifiers-dec") << " " << d_valuation.getDecision( i ) << std::endl; + //} + if( e==Theory::EFFORT_LAST_CALL ){ + if( !addedLemmas ){ + if( d_inst_round_status==InstStrategy::STATUS_SAT ){ + Debug("inst-engine") << "No instantiation given, returning SAT..." << std::endl; + debugSat( SAT_INST_STRATEGY ); + }else{ + Debug("inst-engine") << "No instantiation given, returning unknown..." << std::endl; + d_th->getOutputChannel().setIncomplete(); + } + } + } + }else{ + if( e==Theory::EFFORT_LAST_CALL ){ + if( Options::current()->cbqi ){ + debugSat( SAT_CBQI ); + } + } + } +#ifdef IE_PRINT_PROCESS_TIMES + double clSet2 = double(clock())/double(CLOCKS_PER_SEC); + Notice() << "Done Run instantiation round " << (clSet2-clSet) << std::endl; +#endif + } +} + +void InstantiationEngine::registerQuantifier( Node f ){ + //Notice() << "do cbqi " << f << " ? " << std::endl; + Node ceBody = getQuantifiersEngine()->getCounterexampleBody( f ); + if( !doCbqi( f ) ){ + getQuantifiersEngine()->addTermToDatabase( ceBody, true ); + //need to tell which instantiators will be responsible + //by default, just chose the UF instantiator + getQuantifiersEngine()->getInstantiator( theory::THEORY_UF )->setHasConstraintsFrom( f ); + } + + //take into account user patterns + if( f.getNumChildren()==3 ){ + Node subsPat = getQuantifiersEngine()->getSubstitutedNode( f[2], f ); + //add patterns + for( int i=0; i<(int)subsPat.getNumChildren(); i++ ){ + //Notice() << "Add pattern " << subsPat[i] << " for " << f << std::endl; + ((uf::InstantiatorTheoryUf*)getQuantifiersEngine()->getInstantiator( theory::THEORY_UF ))->addUserPattern( f, subsPat[i] ); + } + } +} + +void InstantiationEngine::assertNode( Node f ){ + ////if we are doing cbqi and have not added the lemma yet, do so + //if( doCbqi( f ) && !hasAddedCbqiLemma( f ) ){ + // addCbqiLemma( f ); + //} +} + +bool InstantiationEngine::hasApplyUf( Node f ){ + if( f.getKind()==APPLY_UF ){ + return true; + }else{ + for( int i=0; i<(int)f.getNumChildren(); i++ ){ + if( hasApplyUf( f[i] ) ){ + return true; + } + } + return false; + } +} +bool InstantiationEngine::hasNonArithmeticVariable( Node f ){ + for( int i=0; i<(int)f[0].getNumChildren(); i++ ){ + TypeNode tn = f[0][i].getType(); + if( !tn.isInteger() && !tn.isReal() ){ + return true; + } + } + return false; +} + +bool InstantiationEngine::doCbqi( Node f ){ + if( Options::current()->cbqiSetByUser ){ + return Options::current()->cbqi; + }else if( Options::current()->cbqi ){ + //if quantifier has a non-arithmetic variable, then do not use cbqi + //if quantifier has an APPLY_UF term, then do not use cbqi + return !hasNonArithmeticVariable( f ) && !hasApplyUf( f[1] ); + }else{ + return false; + } +} + + + + + + + + + + + + + +//void InstantiationEngine::registerLiterals( Node n, Node f ){ +// if( n.getAttribute(InstConstantAttribute())==f ){ +// for( int i=0; i<(int)n.getNumChildren(); i++ ){ +// registerLiterals( n[i], f ); +// } +// if( !d_ce_lit[ f ].isNull() ){ +// if( getQuantifiersEngine()->d_te->getPropEngine()->isSatLiteral( n ) && n.getKind()!=NOT ){ +// if( n!=d_ce_lit[ f ] && n.notNode()!=d_ce_lit[ f ] ){ +// Debug("quant-dep-dec") << "Make " << n << " dependent on "; +// Debug("quant-dep-dec") << d_ce_lit[ f ] << std::endl; +// d_th->getOutputChannel().dependentDecision( d_ce_lit[ f ], n ); +// } +// } +// } +// } +//} + +void InstantiationEngine::debugSat( int reason ){ + if( reason==SAT_CBQI ){ + //Debug("quantifiers-sat") << "Decisions:" << std::endl; + //for( int i=1; i<=(int)d_th->getValuation().getDecisionLevel(); i++ ){ + // Debug("quantifiers-sat") << " " << i << ": " << d_th->getValuation().getDecision( i ) << std::endl; + //} + //for( BoolMap::iterator i = d_forall_asserts.begin(); i != d_forall_asserts.end(); i++ ) { + // if( (*i).second ) { + for( int i=0; i<(int)getQuantifiersEngine()->getNumAssertedQuantifiers(); i++ ){ + Node f = getQuantifiersEngine()->getAssertedQuantifier( i ); + Node cel = d_ce_lit[ f ]; + Assert( !cel.isNull() ); + bool value; + if( d_th->getValuation().hasSatValue( cel, value ) ){ + if( !value ){ + AlwaysAssert(! d_th->getValuation().isDecision( cel ), + "bad decision on counterexample literal"); + } + } + } + //} + Debug("quantifiers-sat") << "return SAT: Cbqi, no quantifier is active. " << std::endl; + //static bool setTrust = false; + //if( !setTrust ){ + // setTrust = true; + // Notice() << "trust-"; + //} + }else if( reason==SAT_INST_STRATEGY ){ + Debug("quantifiers-sat") << "return SAT: No strategy chose to add an instantiation." << std::endl; + //Notice() << "sat "; + //Unimplemented(); + } +} + +void InstantiationEngine::propagate( Theory::Effort level ){ + //propagate as decision all counterexample literals that are not asserted + for( std::map< Node, Node >::iterator it = d_ce_lit.begin(); it != d_ce_lit.end(); ++it ){ + bool value; + if( !d_th->getValuation().hasSatValue( it->second, value ) ){ + //if not already set, propagate as decision + d_th->getOutputChannel().propagateAsDecision( it->second ); + Debug("cbqi-prop-as-dec") << "CBQI: propagate as decision " << it->second << std::endl; + } + } +} diff --git a/src/theory/quantifiers/instantiation_engine.h b/src/theory/quantifiers/instantiation_engine.h new file mode 100644 index 000000000..c6aaed18a --- /dev/null +++ b/src/theory/quantifiers/instantiation_engine.h @@ -0,0 +1,79 @@ +/********************* */ +/*! \file instantiation_engine.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Instantiation Engine classes + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__INSTANTIATION_ENGINE_H +#define __CVC4__INSTANTIATION_ENGINE_H + +#include "theory/quantifiers_engine.h" +#include "theory/quantifiers/theory_quantifiers.h" + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +class InstantiationEngine : public QuantifiersModule +{ +private: + TheoryQuantifiers* d_th; + QuantifiersEngine* getQuantifiersEngine(); +private: + typedef context::CDHashMap< Node, bool, NodeHashFunction > BoolMap; + /** status of instantiation round (one of InstStrategy::STATUS_*) */ + int d_inst_round_status; + /** map from universal quantifiers to their counterexample literals */ + std::map< Node, Node > d_ce_lit; +private: + bool hasAddedCbqiLemma( Node f ); + void addCbqiLemma( Node f ); +private: + /** helper functions */ + bool hasNonArithmeticVariable( Node f ); + bool hasApplyUf( Node f ); + /** whether to do CBQI for quantifier f */ + bool doCbqi( Node f ); +private: + /** do instantiation round */ + bool doInstantiationRound( Theory::Effort effort ); + /** register literals of n, f is the quantifier it belongs to */ + //void registerLiterals( Node n, Node f ); +private: + enum{ + SAT_CBQI, + SAT_INST_STRATEGY, + }; + /** debug sat */ + void debugSat( int reason ); +public: + InstantiationEngine( TheoryQuantifiers* th ); + ~InstantiationEngine(){} + + void check( Theory::Effort e ); + void registerQuantifier( Node f ); + void assertNode( Node f ); + Node explain(TNode n){ return Node::null(); } + void propagate( Theory::Effort level ); +public: + /** get the corresponding counterexample literal for quantified formula node n */ + Node getCounterexampleLiteralFor( Node f ) { return d_ce_lit.find( f )==d_ce_lit.end() ? Node::null() : d_ce_lit[ f ]; } +}; + +} +} +} + +#endif diff --git a/src/theory/quantifiers/kinds b/src/theory/quantifiers/kinds new file mode 100644 index 000000000..106d95cef --- /dev/null +++ b/src/theory/quantifiers/kinds @@ -0,0 +1,48 @@ +# kinds -*- sh -*- +# +# For documentation on this file format, please refer to +# src/theory/builtin/kinds. +# + +theory THEORY_QUANTIFIERS ::CVC4::theory::quantifiers::TheoryQuantifiers "theory/quantifiers/theory_quantifiers.h" +typechecker "theory/quantifiers/theory_quantifiers_type_rules.h" +instantiator ::CVC4::theory::quantifiers::InstantiatorTheoryQuantifiers "theory/quantifiers/theory_quantifiers_instantiator.h" + +properties check propagate presolve + +rewriter ::CVC4::theory::quantifiers::QuantifiersRewriter "theory/quantifiers/quantifiers_rewriter.h" + +operator FORALL 2:3 "universally quantified formula" + +operator EXISTS 2:3 "existentially quantified formula" + +variable INST_CONSTANT "instantiation constant" + +sort BOUND_VAR_LIST_TYPE \ + Cardinality::INTEGERS \ + not-well-founded \ + "Bound Var type" + +operator BOUND_VAR_LIST 1: "bound variables" + +sort INST_PATTERN_TYPE \ + Cardinality::INTEGERS \ + not-well-founded \ + "Instantiation pattern type" + +operator INST_PATTERN 1: "instantiation pattern" + +sort INST_PATTERN_LIST_TYPE \ + Cardinality::INTEGERS \ + not-well-founded \ + "Instantiation pattern list type" + +operator INST_PATTERN_LIST 1: "instantiation pattern list" + +typerule FORALL ::CVC4::theory::quantifiers::QuantifierForallTypeRule +typerule EXISTS ::CVC4::theory::quantifiers::QuantifierExistsTypeRule +typerule BOUND_VAR_LIST ::CVC4::theory::quantifiers::QuantifierBoundVarListTypeRule +typerule INST_PATTERN ::CVC4::theory::quantifiers::QuantifierInstPatternTypeRule +typerule INST_PATTERN_LIST ::CVC4::theory::quantifiers::QuantifierInstPatternListTypeRule + +endtheory diff --git a/src/theory/quantifiers/model_engine.cpp b/src/theory/quantifiers/model_engine.cpp new file mode 100644 index 000000000..a72b103d1 --- /dev/null +++ b/src/theory/quantifiers/model_engine.cpp @@ -0,0 +1,1401 @@ +/********************* */ +/*! \file model_engine.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of model engine class + **/ + +#include "theory/quantifiers/model_engine.h" +#include "theory/theory_engine.h" +#include "theory/uf/equality_engine.h" +#include "theory/uf/theory_uf.h" +#include "theory/uf/theory_uf_strong_solver.h" +#include "theory/uf/theory_uf_instantiator.h" + +//#define ME_PRINT_PROCESS_TIMES + +//#define DISABLE_EVAL_SKIP_MULTIPLE +#define RECONSIDER_FUNC_DEFAULT_VALUE +#define RECONSIDER_FUNC_CONSTANT +#define USE_INDEX_ORDERING +//#define ONE_INST_PER_QUANT_PER_ROUND + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; + +void printRepresentative( const char* c, QuantifiersEngine* qe, Node r ){ + if( r.getType()==NodeManager::currentNM()->booleanType() ){ + if( qe->getEqualityQuery()->areEqual( r, NodeManager::currentNM()->mkConst( true ) ) ){ + Debug( c ) << "true"; + }else{ + Debug( c ) << "false"; + } + }else{ + Debug( c ) << qe->getEqualityQuery()->getRepresentative( r ); + } +} + +RepAlphabet::RepAlphabet( RepAlphabet& ra, QuantifiersEngine* qe ){ + //translate to current representatives + for( std::map< TypeNode, std::vector< Node > >::iterator it = ra.d_type_reps.begin(); it != ra.d_type_reps.end(); ++it ){ + std::vector< Node > reps; + for( int i=0; i<(int)it->second.size(); i++ ){ + //reps.push_back( ie->getEqualityQuery()->getRepresentative( it->second[i] ) ); + reps.push_back( it->second[i] ); + } + set( it->first, reps ); + } +} + +void RepAlphabet::set( TypeNode t, std::vector< Node >& reps ){ + d_type_reps[t].insert( d_type_reps[t].begin(), reps.begin(), reps.end() ); + for( int i=0; i<(int)reps.size(); i++ ){ + d_tmap[ reps[i] ] = i; + } +} + +void RepAlphabet::debugPrint( const char* c, QuantifiersEngine* qe ){ + for( std::map< TypeNode, std::vector< Node > >::iterator it = d_type_reps.begin(); it != d_type_reps.end(); ++it ){ + Debug( c ) << it->first << " : " << std::endl; + for( int i=0; i<(int)it->second.size(); i++ ){ + Debug( c ) << " " << i << ": " << it->second[i] << std::endl; + Debug( c ) << " eq_class( " << it->second[i] << " ) : "; + ((uf::InstantiatorTheoryUf*)qe->getInstantiator( THEORY_UF ))->outputEqClass( c, it->second[i] ); + Debug( c ) << std::endl; + } + } +} + +RepAlphabetIterator::RepAlphabetIterator( QuantifiersEngine* qe, Node f, ModelEngine* model ){ + for( size_t i=0; i<f[0].getNumChildren(); i++ ){ + d_index_order.push_back( i ); + } + initialize( qe, f, model ); +} + +RepAlphabetIterator::RepAlphabetIterator( QuantifiersEngine* qe, Node f, ModelEngine* model, std::vector< int >& indexOrder ){ + d_index_order.insert( d_index_order.begin(), indexOrder.begin(), indexOrder.end() ); + initialize( qe, f, model ); +} + +void RepAlphabetIterator::initialize( QuantifiersEngine* qe, Node f, ModelEngine* model ){ + d_f = f; + d_model = model; + //store instantiation constants + for( size_t i=0; i<f[0].getNumChildren(); i++ ){ + d_ic.push_back( qe->getInstantiationConstant( d_f, i ) ); + d_index.push_back( 0 ); + } + //make the d_var_order mapping + for( size_t i=0; i<d_index_order.size(); i++ ){ + d_var_order[d_index_order[i]] = i; + } + //for testing + d_inst_tried = 0; + d_inst_tests = 0; +} + +void RepAlphabetIterator::increment2( QuantifiersEngine* qe, int counter ){ + Assert( !isFinished() ); + //increment d_index + while( counter>=0 && d_index[counter]==(int)(d_model->getReps()->d_type_reps[d_f[0][d_index_order[counter]].getType()].size()-1) ){ + counter--; + } + if( counter==-1 ){ + d_index.clear(); + }else{ + for( int i=(int)d_index.size()-1; i>counter; i-- ){ + d_index[i] = 0; + d_model->clearEvalFailed( i ); + } + d_index[counter]++; + d_model->clearEvalFailed( counter ); + } +} + +void RepAlphabetIterator::increment( QuantifiersEngine* qe ){ + if( !isFinished() ){ + increment2( qe, (int)d_index.size()-1 ); + } +} + +bool RepAlphabetIterator::isFinished(){ + return d_index.empty(); +} + +void RepAlphabetIterator::getMatch( QuantifiersEngine* ie, InstMatch& m ){ + for( int i=0; i<(int)d_index.size(); i++ ){ + m.d_map[ ie->getInstantiationConstant( d_f, i ) ] = getTerm( i ); + } +} + +Node RepAlphabetIterator::getTerm( int i ){ + TypeNode tn = d_f[0][d_index_order[i]].getType(); + Assert( d_model->getReps()->d_type_reps.find( tn )!=d_model->getReps()->d_type_reps.end() ); + return d_model->getReps()->d_type_reps[tn][d_index[d_index_order[i]]]; +} + +void RepAlphabetIterator::calculateTerms( QuantifiersEngine* qe ){ + d_terms.clear(); + for( int i=0; i<qe->getNumInstantiationConstants( d_f ); i++ ){ + d_terms.push_back( getTerm( i ) ); + } +} + +void RepAlphabetIterator::debugPrint( const char* c ){ + for( int i=0; i<(int)d_index.size(); i++ ){ + Debug( c ) << i << ": " << d_index[i] << ", (" << getTerm( i ) << " / " << d_ic[ i ] << std::endl; + } +} + +void RepAlphabetIterator::debugPrintSmall( const char* c ){ + Debug( c ) << "RI: "; + for( int i=0; i<(int)d_index.size(); i++ ){ + Debug( c ) << d_index[i] << ": " << getTerm( i ) << " "; + } + Debug( c ) << std::endl; +} + +//set value function +void UfModelTree::setValue( QuantifiersEngine* qe, Node n, Node v, std::vector< int >& indexOrder, bool ground, int argIndex ){ + if( d_data.empty() ){ + d_value = v; + }else if( !d_value.isNull() && d_value!=v ){ + d_value = Node::null(); + } + if( argIndex<(int)n.getNumChildren() ){ + //take r = null when argument is the model basis + Node r; + if( ground || !n[ indexOrder[argIndex] ].getAttribute(ModelBasisAttribute()) ){ + r = qe->getEqualityQuery()->getRepresentative( n[ indexOrder[argIndex] ] ); + } + d_data[ r ].setValue( qe, n, v, indexOrder, ground, argIndex+1 ); + } +} + +//get value function +Node UfModelTree::getValue( QuantifiersEngine* qe, Node n, std::vector< int >& indexOrder, int& depIndex, int argIndex ){ + if( !d_value.isNull() && isTotal( n.getOperator(), argIndex ) ){ + //Notice() << "Constant, return " << d_value << ", depIndex = " << argIndex << std::endl; + depIndex = argIndex; + return d_value; + }else{ + Node val; + int childDepIndex[2] = { argIndex, argIndex }; + for( int i=0; i<2; i++ ){ + //first check the argument, then check default + Node r; + if( i==0 ){ + r = qe->getEqualityQuery()->getRepresentative( n[ indexOrder[argIndex] ] ); + } + std::map< Node, UfModelTree >::iterator it = d_data.find( r ); + if( it!=d_data.end() ){ + val = it->second.getValue( qe, n, indexOrder, childDepIndex[i], argIndex+1 ); + if( !val.isNull() ){ + break; + } + }else{ + //argument is not a defined argument: thus, it depends on this argument + childDepIndex[i] = argIndex+1; + } + } + //update depIndex + depIndex = childDepIndex[0]>childDepIndex[1] ? childDepIndex[0] : childDepIndex[1]; + //Notice() << "Return " << val << ", depIndex = " << depIndex; + //Notice() << " ( " << childDepIndex[0] << ", " << childDepIndex[1] << " )" << std::endl; + return val; + } +} + +//simplify function +void UfModelTree::simplify( Node op, Node defaultVal, int argIndex ){ + if( argIndex<(int)op.getType().getNumChildren()-1 ){ + std::vector< Node > eraseData; + //first process the default argument + Node r; + std::map< Node, UfModelTree >::iterator it = d_data.find( r ); + if( it!=d_data.end() ){ + if( !defaultVal.isNull() && it->second.d_value==defaultVal ){ + eraseData.push_back( r ); + }else{ + it->second.simplify( op, defaultVal, argIndex+1 ); + if( !it->second.d_value.isNull() && it->second.isTotal( op, argIndex+1 ) ){ + defaultVal = it->second.d_value; + }else{ + defaultVal = Node::null(); + } + } + } + //now see if any children can be removed, and simplify the ones that cannot + for( std::map< Node, UfModelTree >::iterator it = d_data.begin(); it != d_data.end(); ++it ){ + if( !it->first.isNull() ){ + if( !defaultVal.isNull() && it->second.d_value==defaultVal ){ + eraseData.push_back( it->first ); + }else{ + it->second.simplify( op, defaultVal, argIndex+1 ); + } + } + } + for( int i=0; i<(int)eraseData.size(); i++ ){ + d_data.erase( eraseData[i] ); + } + } +} + +//is total function +bool UfModelTree::isTotal( Node op, int argIndex ){ + if( argIndex==(int)(op.getType().getNumChildren()-1) ){ + return !d_value.isNull(); + }else{ + Node r; + std::map< Node, UfModelTree >::iterator it = d_data.find( r ); + if( it!=d_data.end() ){ + return it->second.isTotal( op, argIndex+1 ); + }else{ + return false; + } + } +} + +Node UfModelTree::getConstantValue( QuantifiersEngine* qe, Node n, std::vector< int >& indexOrder, int argIndex ){ + return d_value; +} + +void indent( const char* c, int ind ){ + for( int i=0; i<ind; i++ ){ + Debug( c ) << " "; + } +} + +void UfModelTree::debugPrint( const char* c, QuantifiersEngine* qe, std::vector< int >& indexOrder, int ind, int arg ){ + if( !d_data.empty() ){ + for( std::map< Node, UfModelTree >::iterator it = d_data.begin(); it != d_data.end(); ++it ){ + if( !it->first.isNull() ){ + indent( c, ind ); + Debug( c ) << "if x_" << indexOrder[arg] << " == " << it->first << std::endl; + it->second.debugPrint( c, qe, indexOrder, ind+2, arg+1 ); + } + } + if( d_data.find( Node::null() )!=d_data.end() ){ + d_data[ Node::null() ].debugPrint( c, qe, indexOrder, ind, arg+1 ); + } + }else{ + indent( c, ind ); + Debug( c ) << "return "; + printRepresentative( c, qe, d_value ); + //Debug( c ) << " { "; + //for( int i=0; i<(int)d_explicit.size(); i++ ){ + // Debug( c ) << d_explicit[i] << " "; + //} + //Debug( c ) << "}"; + Debug( c ) << std::endl; + } +} + +UfModel::UfModel( Node op, ModelEngine* me ) : d_op( op ), d_me( me ), + d_model_constructed( false ), d_reconsider_model( false ){ + + d_tree = UfModelTreeOrdered( op ); TypeNode tn = d_op.getType(); tn = tn[(int)tn.getNumChildren()-1]; Assert( tn==NodeManager::currentNM()->booleanType() || uf::StrongSolverTheoryUf::isRelevantType( tn ) ); //look at ground assertions + for( int i=0; i<(int)d_me->getQuantifiersEngine()->getTermDatabase()->d_op_map[ d_op ].size(); i++ ){ + Node n = d_me->getQuantifiersEngine()->getTermDatabase()->d_op_map[ d_op ][i]; + bool add = true; + if( n.getAttribute(NoMatchAttribute()) ){ + add = false; + //determine if it has model basis attribute + for( int j=0; j<(int)n.getNumChildren(); j++ ){ + if( n[j].getAttribute(ModelBasisAttribute()) ){ + add = true; + break; + } + } + } + if( add ){ + d_ground_asserts.push_back( n ); + Node r = d_me->getQuantifiersEngine()->getEqualityQuery()->getRepresentative( n ); + d_ground_asserts_reps.push_back( r ); + } + } + //determine if it is constant + if( !d_ground_asserts.empty() ){ + bool isConstant = true; + for( int i=1; i<(int)d_ground_asserts.size(); i++ ){ + if( d_ground_asserts_reps[0]!=d_ground_asserts_reps[i] ){ + isConstant = false; + break; + } + } + if( isConstant ){ + //set constant value + Node t = d_me->getModelBasisApplyUfTerm( d_op ); + Node r = d_ground_asserts_reps[0]; + setValue( t, r, false ); + setModel(); + d_reconsider_model = true; + Debug("fmf-model-cons") << "Function " << d_op << " is the constant function "; + printRepresentative( "fmf-model-cons", d_me->getQuantifiersEngine(), r ); + Debug("fmf-model-cons") << std::endl; + } + } +} + +void UfModel::setValue( Node n, Node v, bool ground ){ + d_set_values[ ground ? 1 : 0 ][n] = v; +} + +void UfModel::setModel(){ + makeModel( d_me->getQuantifiersEngine(), d_tree ); + d_model_constructed = true; +} + +void UfModel::clearModel(){ + for( int k=0; k<2; k++ ){ + d_set_values[k].clear(); + } + d_tree.clear(); + d_model_constructed = false; +} + +Node UfModel::getConstantValue( QuantifiersEngine* qe, Node n ){ + if( d_model_constructed ){ + return d_tree.getConstantValue( qe, n ); + }else{ + return Node::null(); + } +} + +bool UfModel::isConstant(){ + Node gn = d_me->getModelBasisApplyUfTerm( d_op ); + Node n = getConstantValue( d_me->getQuantifiersEngine(), gn ); + return !n.isNull(); +} + +void UfModel::buildModel(){ +#ifdef RECONSIDER_FUNC_CONSTANT + if( d_model_constructed ){ + if( d_reconsider_model ){ + //if we are allowed to reconsider default value, then see if the default value can be improved + Node t = d_me->getModelBasisApplyUfTerm( d_op ); + Node v = d_set_values[0][t]; + if( d_value_pro_con[1][v].size()>d_value_pro_con[0][v].size() ){ + Debug("fmf-model-cons-debug") << "Consider changing the default value for " << d_op << std::endl; + clearModel(); + } + } + } +#endif + //now, construct models for each uninterpretted function/predicate + if( !d_model_constructed ){ + Debug("fmf-model-cons") << "Construct model for " << d_op << "..." << std::endl; + //now, set the values in the model + for( int i=0; i<(int)d_ground_asserts.size(); i++ ){ + Node n = d_ground_asserts[i]; + Node v = d_ground_asserts_reps[i]; + //if this assertion did not help the model, just consider it ground + //set n = v in the model tree + Debug("fmf-model-cons") << " Set " << n << " = "; + printRepresentative( "fmf-model-cons", d_me->getQuantifiersEngine(), v ); + Debug("fmf-model-cons") << std::endl; + //set it as ground value + setValue( n, v ); + } + //set the default value + //chose defaultVal based on heuristic (the best proportion of "pro" responses) + Node defaultVal; + double maxScore = -1; + for( int i=0; i<(int)d_values.size(); i++ ){ + Node v = d_values[i]; + double score = ( 1.0 + (double)d_value_pro_con[0][v].size() )/( 1.0 + (double)d_value_pro_con[1][v].size() ); + Debug("fmf-model-cons") << " - score( "; + printRepresentative( "fmf-model-cons", d_me->getQuantifiersEngine(), v ); + Debug("fmf-model-cons") << " ) = " << score << std::endl; + if( score>maxScore ){ + defaultVal = v; + maxScore = score; + } + } +#ifdef RECONSIDER_FUNC_DEFAULT_VALUE + if( maxScore<1.0 ){ + //consider finding another value, if possible + Debug("fmf-model-cons-debug") << "Poor choice for default value, score = " << maxScore << std::endl; + TypeNode tn = d_op.getType(); + Node newDefaultVal = d_me->getArbitraryElement( tn[(int)tn.getNumChildren()-1], d_values ); + if( !newDefaultVal.isNull() ){ + defaultVal = newDefaultVal; + Debug("fmf-model-cons-debug") << "-> Change default value to "; + printRepresentative( "fmf-model-cons-debug", d_me->getQuantifiersEngine(), defaultVal ); + Debug("fmf-model-cons-debug") << std::endl; + }else{ + Debug("fmf-model-cons-debug") << "-> Could not find arbitrary element of type " << tn[(int)tn.getNumChildren()-1] << std::endl; + Debug("fmf-model-cons-debug") << " Excluding: "; + for( int i=0; i<(int)d_values.size(); i++ ){ + Debug("fmf-model-cons-debug") << d_values[i] << " "; + } + Debug("fmf-model-cons-debug") << std::endl; + } + } +#endif + Assert( !defaultVal.isNull() ); + //get the default term (this term must be defined non-ground in model) + Node defaultTerm = d_me->getModelBasisApplyUfTerm( d_op ); + Debug("fmf-model-cons") << " Choose "; + printRepresentative("fmf-model-cons", d_me->getQuantifiersEngine(), defaultVal ); + Debug("fmf-model-cons") << " as default value (" << defaultTerm << ")" << std::endl; + Debug("fmf-model-cons") << " # quantifiers pro = " << d_value_pro_con[0][defaultVal].size() << std::endl; + Debug("fmf-model-cons") << " # quantifiers con = " << d_value_pro_con[1][defaultVal].size() << std::endl; + setValue( defaultTerm, defaultVal, false ); + Debug("fmf-model-cons") << " Making model..."; + setModel(); + Debug("fmf-model-cons") << " Finished constructing model for " << d_op << "." << std::endl; + } +} + +void UfModel::setValuePreference( Node f, Node n, bool isPro ){ + Node v = d_me->getQuantifiersEngine()->getEqualityQuery()->getRepresentative( n ); + //Notice() << "Set value preference " << n << " = " << v << " " << isPro << std::endl; + if( std::find( d_values.begin(), d_values.end(), v )==d_values.end() ){ + d_values.push_back( v ); + } + int index = isPro ? 0 : 1; + if( std::find( d_value_pro_con[index][v].begin(), d_value_pro_con[index][v].end(), f )==d_value_pro_con[index][v].end() ){ + d_value_pro_con[index][v].push_back( f ); + } +} + +void UfModel::makeModel( QuantifiersEngine* qe, UfModelTreeOrdered& tree ){ + for( int k=0; k<2; k++ ){ + for( std::map< Node, Node >::iterator it = d_set_values[k].begin(); it != d_set_values[k].end(); ++it ){ + tree.setValue( qe, it->first, it->second, k==1 ); + } + } + tree.simplify(); +} + +void UfModel::debugPrint( const char* c ){ + //Debug( c ) << "Function " << d_op << std::endl; + //Debug( c ) << " Type: " << d_op.getType() << std::endl; + //Debug( c ) << " Ground asserts:" << std::endl; + //for( int i=0; i<(int)d_ground_asserts.size(); i++ ){ + // Debug( c ) << " " << d_ground_asserts[i] << " = "; + // printRepresentative( c, d_me->getQuantifiersEngine(), d_ground_asserts[i] ); + // Debug( c ) << std::endl; + //} + //Debug( c ) << " Model:" << std::endl; + + TypeNode t = d_op.getType(); + Debug( c ) << d_op << "( "; + for( int i=0; i<(int)(t.getNumChildren()-1); i++ ){ + Debug( c ) << "x_" << i << " : " << t[i]; + if( i<(int)(t.getNumChildren()-2) ){ + Debug( c ) << ", "; + } + } + Debug( c ) << " ) : " << t[(int)t.getNumChildren()-1] << std::endl; + if( d_tree.isEmpty() ){ + Debug( c ) << " [undefined]" << std::endl; + }else{ + d_tree.debugPrint( c, d_me->getQuantifiersEngine(), 3 ); + Debug( c ) << std::endl; + } + //Debug( c ) << " Phase reqs:" << std::endl; //for( int i=0; i<2; i++ ){ + // for( std::map< Node, std::vector< Node > >::iterator it = d_reqs[i].begin(); it != d_reqs[i].end(); ++it ){ + // Debug( c ) << " " << it->first << std::endl; + // for( int j=0; j<(int)it->second.size(); j++ ){ + // Debug( c ) << " " << it->second[j] << " -> " << (i==1) << std::endl; + // } + // } + //} + //Debug( c ) << std::endl; + //for( int i=0; i<2; i++ ){ + // for( std::map< Node, std::map< Node, std::vector< Node > > >::iterator it = d_eq_reqs[i].begin(); it != d_eq_reqs[i].end(); ++it ){ + // Debug( c ) << " " << "For " << it->first << ":" << std::endl; + // for( std::map< Node, std::vector< Node > >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){ + // for( int j=0; j<(int)it2->second.size(); j++ ){ + // Debug( c ) << " " << it2->first << ( i==1 ? "==" : "!=" ) << it2->second[j] << std::endl; + // } + // } + // } + //} +} + +//Model Engine constructor +ModelEngine::ModelEngine( TheoryQuantifiers* th ){ + d_th = th; + d_quantEngine = th->getQuantifiersEngine(); + d_ss = ((uf::TheoryUF*)d_quantEngine->getTheoryEngine()->getTheory( THEORY_UF ))->getStrongSolver(); +} + +void ModelEngine::check( Theory::Effort e ){ + if( e==Theory::EFFORT_LAST_CALL ){ + bool quantsInit = true; + //first, check if we can minimize the model further + if( !d_ss->minimize() ){ + return; + } + if( useModel() ){ + //now, check if any quantifiers are un-initialized + for( int i=0; i<d_quantEngine->getNumAssertedQuantifiers(); i++ ){ + Node f = d_quantEngine->getAssertedQuantifier( i ); + if( !initializeQuantifier( f ) ){ + quantsInit = false; + } + } + } + if( quantsInit ){ +#ifdef ME_PRINT_PROCESS_TIMES + Notice() << "---Instantiation Round---" << std::endl; +#endif + Debug("fmf-model-debug") << "---Begin Instantiation Round---" << std::endl; + ++(d_statistics.d_inst_rounds); + d_quantEngine->getTermDatabase()->reset( e ); + //build the representatives + Debug("fmf-model-debug") << "Building representatives..." << std::endl; + buildRepresentatives(); + if( useModel() ){ + //initialize the model + Debug("fmf-model-debug") << "Initializing model..." << std::endl; + initializeModel(); + //analyze the quantifiers + Debug("fmf-model-debug") << "Analyzing quantifiers..." << std::endl; + analyzeQuantifiers(); + //build the model + Debug("fmf-model-debug") << "Building model..." << std::endl; + for( std::map< Node, UfModel >::iterator it = d_uf_model.begin(); it != d_uf_model.end(); ++it ){ + it->second.buildModel(); + } + } + //print debug + debugPrint("fmf-model-complete"); + //try exhaustive instantiation + Debug("fmf-model-debug") << "Do exhaustive instantiation..." << std::endl; + int addedLemmas = 0; + for( int i=0; i<d_quantEngine->getNumAssertedQuantifiers(); i++ ){ + Node f = d_quantEngine->getAssertedQuantifier( i ); + if( d_quant_sat.find( f )==d_quant_sat.end() ){ + addedLemmas += instantiateQuantifier( f ); + } + } +#ifdef ME_PRINT_PROCESS_TIMES + Notice() << "Added Lemmas = " << addedLemmas << std::endl; +#endif + if( addedLemmas==0 ){ + //debugPrint("fmf-consistent"); + //for( std::map< Node, UfModel >::iterator it = d_uf_model.begin(); it != d_uf_model.end(); ++it ){ + // it->second.simplify(); + //} + Debug("fmf-consistent") << std::endl; + debugPrint("fmf-consistent"); + } + } + d_quantEngine->flushLemmas( &d_th->getOutputChannel() ); + } +} + +void ModelEngine::registerQuantifier( Node f ){ + +} + +void ModelEngine::assertNode( Node f ){ + +} + +bool ModelEngine::useModel() { + return Options::current()->fmfModelBasedInst; +} + +bool ModelEngine::initializeQuantifier( Node f ){ + if( d_quant_init.find( f )==d_quant_init.end() ){ + d_quant_init[f] = true; + Debug("inst-fmf-init") << "Initialize " << f << std::endl; + //add the model basis instantiation + // This will help produce the necessary information for model completion. + // We do this by extending distinguish ground assertions (those + // containing terms with "model basis" attribute) to hold for all cases. + + ////first, check if any variables are required to be equal + //for( std::map< Node, bool >::iterator it = d_quantEngine->d_phase_reqs[f].begin(); + // it != d_quantEngine->d_phase_reqs[f].end(); ++it ){ + // Node n = it->first; + // if( n.getKind()==EQUAL && n[0].getKind()==INST_CONSTANT && n[1].getKind()==INST_CONSTANT ){ + // Notice() << "Unhandled phase req: " << n << std::endl; + // } + //} + + std::vector< Node > terms; + for( int j=0; j<(int)f[0].getNumChildren(); j++ ){ + terms.push_back( getModelBasisTerm( f[0][j].getType() ) ); + } + ++(d_statistics.d_num_quants_init); + if( d_quantEngine->addInstantiation( f, terms ) ){ + return false; + }else{ + //usually shouldn't happen + //Notice() << "No model basis for " << f << std::endl; + ++(d_statistics.d_num_quants_init_fail); + } + } + return true; +} + +void ModelEngine::buildRepresentatives(){ + d_ra.clear(); + //collect all representatives for all types and store as representative alphabet + for( int i=0; i<d_ss->getNumCardinalityTypes(); i++ ){ + TypeNode tn = d_ss->getCardinalityType( i ); + std::vector< Node > reps; + d_ss->getRepresentatives( tn, reps ); + Assert( !reps.empty() ); + //if( (int)reps.size()!=d_ss->getCardinality( tn ) ){ + // Notice() << "InstStrategyFinteModelFind::processResetInstantiationRound: Bad representatives for type." << std::endl; + // Notice() << " " << tn << " has cardinality " << d_ss->getCardinality( tn ); + // Notice() << " but only " << (int)reps.size() << " were given." << std::endl; + // Unimplemented( 27 ); + //} + Debug("fmf-model-debug") << " " << tn << " -> " << reps.size() << std::endl; + Debug("fmf-model-debug") << " "; + for( int i=0; i<(int)reps.size(); i++ ){ + Debug("fmf-model-debug") << reps[i] << " "; + } + Debug("fmf-model-debug") << std::endl; + //set them in the alphabet + d_ra.set( tn, reps ); +#ifdef ME_PRINT_PROCESS_TIMES + Notice() << tn << " has " << reps.size() << " representatives. " << std::endl; +#endif + } +} + +void ModelEngine::initializeModel(){ + d_uf_model.clear(); + d_quant_sat.clear(); + for( int k=0; k<2; k++ ){ + d_pro_con_quant[k].clear(); + } + + ////now analyze quantifiers (temporary) + //for( int i=0; i<(int)d_quantEngine->getNumAssertedQuantifiers(); i++ ){ + // Node f = d_quantEngine->getAssertedQuantifier( i ); + // Debug("fmf-model-req") << "Phase requirements for " << f << ": " << std::endl; + // for( std::map< Node, bool >::iterator it = d_quantEngine->d_phase_reqs[f].begin(); + // it != d_quantEngine->d_phase_reqs[f].end(); ++it ){ + // Node n = it->first; + // Debug("fmf-model-req") << " " << n << " -> " << it->second << std::endl; + // if( n.getKind()==APPLY_UF ){ + // processPredicate( f, n, it->second ); + // }else if( n.getKind()==EQUAL ){ + // processEquality( f, n, it->second ); + // } + // } + //} + for( int i=0; i<(int)d_quantEngine->getNumAssertedQuantifiers(); i++ ){ + Node f = d_quantEngine->getAssertedQuantifier( i ); + initializeUf( f[1] ); + //register model basis terms + std::vector< Node > vars; + std::vector< Node > subs; + for( int j=0; j<(int)f[0].getNumChildren(); j++ ){ + vars.push_back( d_quantEngine->getInstantiationConstant( f, j ) ); + subs.push_back( getModelBasisTerm( f[0][j].getType() ) ); + } + Node n = d_quantEngine->getCounterexampleBody( f ); + Node gn = n.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + registerModelBasis( n, gn ); + } +} + +void ModelEngine::analyzeQuantifiers(){ + int quantSatInit = 0; + int nquantSatInit = 0; + //analyze the preferences of each quantifier + for( int i=0; i<(int)d_quantEngine->getNumAssertedQuantifiers(); i++ ){ + Node f = d_quantEngine->getAssertedQuantifier( i ); + Debug("fmf-model-prefs") << "Analyze quantifier " << f << std::endl; + std::vector< Node > pro_con[2]; + std::vector< Node > pro_con_patterns[2]; + //check which model basis terms have good and bad definitions according to f + for( std::map< Node, bool >::iterator it = d_quantEngine->d_phase_reqs[f].begin(); + it != d_quantEngine->d_phase_reqs[f].end(); ++it ){ + Node n = it->first; + Node gn = d_model_basis[n]; + Debug("fmf-model-req") << " Req: " << n << " -> " << it->second << std::endl; + int pref = 0; + bool isConst = true; + std::vector< Node > uf_terms; + std::vector< Node > uf_term_patterns; + if( gn.getKind()==APPLY_UF ){ + if( d_quantEngine->getEqualityQuery()->hasTerm( gn ) ){ + uf_terms.push_back( gn ); + uf_term_patterns.push_back( n ); + bool phase = areEqual( gn, NodeManager::currentNM()->mkConst( true ) ); + pref = phase!=it->second ? 1 : -1; + } + }else if( gn.getKind()==EQUAL ){ + bool success = true; + for( int j=0; j<2; j++ ){ + if( n[j].getKind()==APPLY_UF ){ + Node op = gn[j].getOperator(); + if( d_uf_model.find( op )!=d_uf_model.end() ){ + Assert( gn[j].getKind()==APPLY_UF ); + uf_terms.push_back( gn[j] ); + uf_term_patterns.push_back( n[j] ); + }else{ + //found undefined uf operator + success = false; + } + }else if( n[j].hasAttribute(InstConstantAttribute()) ){ + isConst = false; + } + } + if( success && !uf_terms.empty() ){ + if( (!it->second && areEqual( gn[0], gn[1] )) || (it->second && areDisequal( gn[0], gn[1] )) ){ + pref = 1; + }else if( (it->second && areEqual( gn[0], gn[1] )) || (!it->second && areDisequal( gn[0], gn[1] )) ){ + pref = -1; + } + } + } + if( pref!=0 ){ + Debug("fmf-model-prefs") << " It is " << ( pref==1 ? "pro" : "con" ); + Debug("fmf-model-prefs") << " the definition of " << n << std::endl; + if( pref==1 ){ + if( isConst ){ + for( int j=0; j<(int)uf_terms.size(); j++ ){ + //the only uf that is initialized are those that are constant + Node op = uf_terms[j].getOperator(); + Assert( d_uf_model.find( op )!=d_uf_model.end() ); + if( !d_uf_model[op].isConstant() ){ + isConst = false; + break; + } + } + if( isConst ){ + d_quant_sat[f] = true; + //we only need to consider current term definition(s) for this quantifier to be satisified, ignore the others + for( int k=0; k<2; k++ ){ + pro_con[k].clear(); + pro_con_patterns[k].clear(); + } + //instead, just note to the model for each uf term that f is pro its definition + for( int j=0; j<(int)uf_terms.size(); j++ ){ + Node op = uf_terms[j].getOperator(); + d_uf_model[op].d_reconsider_model = false; + } + } + } + } + if( d_quant_sat.find( f )!=d_quant_sat.end() ){ + Debug("fmf-model-prefs") << " * Constant SAT due to definition of " << n << std::endl; + break; + }else{ + int pcIndex = pref==1 ? 0 : 1; + for( int j=0; j<(int)uf_terms.size(); j++ ){ + pro_con[pcIndex].push_back( uf_terms[j] ); + pro_con_patterns[pcIndex].push_back( uf_term_patterns[j] ); + } + } + } + } + if( d_quant_sat.find( f )!=d_quant_sat.end() ){ + quantSatInit++; + d_statistics.d_pre_sat_quant += quantSatInit; + }else{ + nquantSatInit++; + d_statistics.d_pre_nsat_quant += quantSatInit; + } + //add quantifier's preferences to vector + for( int k=0; k<2; k++ ){ + for( int j=0; j<(int)pro_con[k].size(); j++ ){ + Node op = pro_con[k][j].getOperator(); + d_uf_model[op].setValuePreference( f, pro_con[k][j], k==0 ); + d_pro_con_quant[k][ f ].push_back( pro_con_patterns[k][j] ); + } + } + } + Debug("fmf-model-prefs") << "Pre-Model Completion: Quantifiers SAT: " << quantSatInit << " / " << (quantSatInit+nquantSatInit) << std::endl; +} + +int ModelEngine::instantiateQuantifier( Node f ){ + int addedLemmas = 0; + Debug("inst-fmf-ei") << "Add matches for " << f << "..." << std::endl; + Debug("inst-fmf-ei") << " Instantiation Constants: "; + for( size_t i=0; i<f[0].getNumChildren(); i++ ){ + Debug("inst-fmf-ei") << d_quantEngine->getInstantiationConstant( f, i ) << " "; + } + Debug("inst-fmf-ei") << std::endl; + for( int k=0; k<2; k++ ){ + Debug("inst-fmf-ei") << " " << ( k==0 ? "Pro" : "Con" ) << " definitions:" << std::endl; + for( int i=0; i<(int)d_pro_con_quant[k][f].size(); i++ ){ + Debug("inst-fmf-ei") << " " << d_pro_con_quant[k][f][i] << std::endl; + } + } + if( d_pro_con_quant[0][f].empty() ){ + Debug("inst-fmf-ei") << "WARNING: " << f << " has no pros for default literal definitions" << std::endl; + } + d_eval_failed_lits.clear(); + d_eval_failed.clear(); + d_eval_term_model.clear(); + //d_eval_term_vals.clear(); + //d_eval_term_fv_deps.clear(); + RepAlphabetIterator riter( d_quantEngine, f, this ); + increment( &riter ); +#ifdef ONE_INST_PER_QUANT_PER_ROUND + while( !riter.isFinished() && addedLemmas==0 ){ +#else + while( !riter.isFinished() ){ +#endif + InstMatch m; + riter.getMatch( d_quantEngine, m ); + Debug("fmf-model-eval") << "* Add instantiation " << std::endl; + riter.d_inst_tried++; + if( d_quantEngine->addInstantiation( f, m ) ){ + addedLemmas++; + } + riter.increment( d_quantEngine ); + increment( &riter ); + } + if( Debug.isOn("inst-fmf-ei") ){ + int totalInst = 1; + for( int i=0; i<(int)f[0].getNumChildren(); i++ ){ + totalInst = totalInst * (int)getReps()->d_type_reps[ f[0][i].getType() ].size(); + } + Debug("inst-fmf-ei") << "Finished: " << std::endl; + Debug("inst-fmf-ei") << " Inst Skipped: " << (totalInst-riter.d_inst_tried) << std::endl; + Debug("inst-fmf-ei") << " Inst Tried: " << riter.d_inst_tried << std::endl; + Debug("inst-fmf-ei") << " Inst Added: " << addedLemmas << std::endl; + Debug("inst-fmf-ei") << " # Tests: " << riter.d_inst_tests << std::endl; + } + return addedLemmas; +} + +void ModelEngine::registerModelBasis( Node n, Node gn ){ + if( d_model_basis.find( n )==d_model_basis.end() ){ + d_model_basis[n] = gn; + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + registerModelBasis( n[i], gn[i] ); + } + } +} + +Node ModelEngine::getArbitraryElement( TypeNode tn, std::vector< Node >& exclude ){ + Node retVal; + if( tn==NodeManager::currentNM()->booleanType() ){ + if( exclude.empty() ){ + retVal = NodeManager::currentNM()->mkConst( false ); + }else if( exclude.size()==1 ){ + retVal = NodeManager::currentNM()->mkConst( areEqual( exclude[0], NodeManager::currentNM()->mkConst( false ) ) ); + } + }else if( d_ra.d_type_reps.find( tn )!=d_ra.d_type_reps.end() ){ + for( int i=0; i<(int)d_ra.d_type_reps[tn].size(); i++ ){ + if( std::find( exclude.begin(), exclude.end(), d_ra.d_type_reps[tn][i] )==exclude.end() ){ + retVal = d_ra.d_type_reps[tn][i]; + break; + } + } + } + if( !retVal.isNull() ){ + return d_quantEngine->getEqualityQuery()->getRepresentative( retVal ); + }else{ + return Node::null(); + } +} + +Node ModelEngine::getModelBasisTerm( TypeNode tn, int i ){ + return d_ss->getCardinalityTerm( tn ); +} + +Node ModelEngine::getModelBasisApplyUfTerm( Node op ){ + if( d_model_basis_term.find( op )==d_model_basis_term.end() ){ + TypeNode t = op.getType(); + std::vector< Node > children; + children.push_back( op ); + for( int i=0; i<(int)t.getNumChildren()-1; i++ ){ + children.push_back( getModelBasisTerm( t[i] ) ); + } + d_model_basis_term[op] = NodeManager::currentNM()->mkNode( APPLY_UF, children ); + } + return d_model_basis_term[op]; +} + +bool ModelEngine::isModelBasisTerm( Node op, Node n ){ + if( n.getOperator()==op ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( !n[i].getAttribute(ModelBasisAttribute()) ){ + return false; + } + } + return true; + }else{ + return false; + } +} + +void ModelEngine::initializeUf( Node n ){ + std::vector< Node > terms; + collectUfTerms( n, terms ); + for( int i=0; i<(int)terms.size(); i++ ){ + initializeUfModel( terms[i].getOperator() ); + } +} + +void ModelEngine::collectUfTerms( Node n, std::vector< Node >& terms ){ + if( n.getKind()==APPLY_UF ){ + terms.push_back( n ); + } + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + collectUfTerms( n[i], terms ); + } +} + +void ModelEngine::initializeUfModel( Node op ){ + if( d_uf_model.find( op )==d_uf_model.end() ){ + TypeNode tn = op.getType(); + tn = tn[ (int)tn.getNumChildren()-1 ]; + if( tn==NodeManager::currentNM()->booleanType() || uf::StrongSolverTheoryUf::isRelevantType( tn ) ){ + d_uf_model[ op ] = UfModel( op, this ); + } + } +} + +void ModelEngine::makeEvalTermModel( Node n ){ + if( d_eval_term_model.find( n )==d_eval_term_model.end() ){ + makeEvalTermIndexOrder( n ); + if( !d_eval_term_use_default_model[n] ){ + Node op = n.getOperator(); + d_eval_term_model[n] = UfModelTreeOrdered( op, d_eval_term_index_order[n] ); + d_uf_model[op].makeModel( d_quantEngine, d_eval_term_model[n] ); + Debug("fmf-model-index-order") << "Make model for " << n << " : " << std::endl; + d_eval_term_model[n].debugPrint( "fmf-model-index-order", d_quantEngine, 2 ); + } + } +} + +struct sortGetMaxVariableNum { + std::map< Node, int > d_max_var_num; + int computeMaxVariableNum( Node n ){ + if( n.getKind()==INST_CONSTANT ){ + return n.getAttribute(InstVarNumAttribute()); + }else if( n.hasAttribute(InstConstantAttribute()) ){ + int maxVal = -1; + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + int val = getMaxVariableNum( n[i] ); + if( val>maxVal ){ + maxVal = val; + } + } + return maxVal; + }else{ + return -1; + } + } + int getMaxVariableNum( Node n ){ + std::map< Node, int >::iterator it = d_max_var_num.find( n ); + if( it==d_max_var_num.end() ){ + int num = computeMaxVariableNum( n ); + d_max_var_num[n] = num; + return num; + }else{ + return it->second; + } + } + bool operator() (Node i,Node j) { return (getMaxVariableNum(i)<getMaxVariableNum(j));} +}; + +void ModelEngine::makeEvalTermIndexOrder( Node n ){ + if( d_eval_term_index_order.find( n )==d_eval_term_index_order.end() ){ + //sort arguments in order of least significant vs. most significant variable in default ordering + std::map< Node, std::vector< int > > argIndex; + std::vector< Node > args; + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( argIndex.find( n[i] )==argIndex.end() ){ + args.push_back( n[i] ); + } + argIndex[n[i]].push_back( i ); + } + sortGetMaxVariableNum sgmvn; + std::sort( args.begin(), args.end(), sgmvn ); + for( int i=0; i<(int)args.size(); i++ ){ + for( int j=0; j<(int)argIndex[ args[i] ].size(); j++ ){ + d_eval_term_index_order[n].push_back( argIndex[ args[i] ][j] ); + } + } + bool useDefault = true; + for( int i=0; i<(int)d_eval_term_index_order[n].size(); i++ ){ + if( i!=d_eval_term_index_order[n][i] ){ + useDefault = false; + break; + } + } + d_eval_term_use_default_model[n] = useDefault; + Debug("fmf-model-index-order") << "Will consider the following index ordering for " << n << " : "; + for( int i=0; i<(int)d_eval_term_index_order[n].size(); i++ ){ + Debug("fmf-model-index-order") << d_eval_term_index_order[n][i] << " "; + } + Debug("fmf-model-index-order") << std::endl; + } +} + +//void ModelEngine::processPredicate( Node f, Node p, bool phase ){ +// Node op = p.getOperator(); +// initializeUfModel( op ); +// d_uf_model[ op ].addRequirement( f, p, phase ); +//} +// +//void ModelEngine::processEquality( Node f, Node eq, bool phase ){ +// for( int i=0; i<2; i++ ){ +// int j = i==0 ? 1 : 0; +// if( eq[i].getKind()==APPLY_UF && eq[i].hasAttribute(InstConstantAttribute()) ){ +// Node op = eq[i].getOperator(); +// initializeUfModel( op ); +// d_uf_model[ op ].addEqRequirement( f, eq[i], eq[j], phase ); +// } +// } +//} + +void ModelEngine::increment( RepAlphabetIterator* rai ){ + if( useModel() ){ + bool success; + do{ + success = true; + if( !rai->isFinished() ){ + //see if instantiation is already true in current model + Debug("fmf-model-eval") << "Evaulating "; + rai->debugPrintSmall("fmf-model-eval"); + //calculate represenative terms we are currently considering + rai->calculateTerms( d_quantEngine ); + rai->d_inst_tests++; + //if eVal is not (int)rai->d_index.size(), then the instantiation is already true in the model, + // and eVal is the highest index in rai which we can safely iterate + int depIndex; + if( evaluate( rai, d_quantEngine->getCounterexampleBody( rai->d_f ), depIndex )==1 ){ + Debug("fmf-model-eval") << " Returned success with depIndex = " << depIndex << std::endl; + //Notice() << "Returned eVal = " << eVal << "/" << rai->d_index.size() << std::endl; + if( depIndex<(int)rai->d_index.size() ){ +#ifdef DISABLE_EVAL_SKIP_MULTIPLE + depIndex = (int)rai->d_index.size()-1; +#endif + rai->increment2( d_quantEngine, depIndex ); + success = false; + } + }else{ + Debug("fmf-model-eval") << " Returned failure." << std::endl; + } + } + }while( !success ); + } +} + +//if evaluate( rai, n, phaseReq ) = eVal, +// if eVal = rai->d_index.size() +// then the formula n instantiated with rai cannot be proven to be equal to phaseReq +// otherwise, +// each n{rai->d_index[0]/x_0...rai->d_index[eVal]/x_eVal, */x_(eVal+1) ... */x_n } is equal to phaseReq in the current model +int ModelEngine::evaluate( RepAlphabetIterator* rai, Node n, int& depIndex ){ + ++(d_statistics.d_eval_formulas); + //Debug("fmf-model-eval-debug") << "Evaluate " << n << " " << phaseReq << std::endl; + if( n.getKind()==NOT ){ + int val = evaluate( rai, n[0], depIndex ); + return val==1 ? -1 : ( val==-1 ? 1 : 0 ); + }else if( n.getKind()==OR || n.getKind()==AND || n.getKind()==IMPLIES ){ + int baseVal = n.getKind()==AND ? 1 : -1; + int eVal = baseVal; + int posDepIndex = (int)rai->d_index.size(); + int negDepIndex = -1; + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + //evaluate subterm + int childDepIndex; + Node nn = ( i==0 && n.getKind()==IMPLIES ) ? n[i].notNode() : n[i]; + int eValT = evaluate( rai, nn, childDepIndex ); + if( eValT==baseVal ){ + if( eVal==baseVal ){ + if( childDepIndex>negDepIndex ){ + negDepIndex = childDepIndex; + } + } + }else if( eValT==-baseVal ){ + eVal = -baseVal; + if( childDepIndex<posDepIndex ){ + posDepIndex = childDepIndex; + if( posDepIndex==-1 ){ + break; + } + } + }else if( eValT==0 ){ + if( eVal==baseVal ){ + eVal = 0; + } + } + } + if( eVal!=0 ){ + depIndex = eVal==-baseVal ? posDepIndex : negDepIndex; + return eVal; + }else{ + return 0; + } + }else if( n.getKind()==IFF || n.getKind()==XOR ){ + int depIndex1; + int eVal = evaluate( rai, n[0], depIndex1 ); + if( eVal!=0 ){ + int depIndex2; + int eVal2 = evaluate( rai, n.getKind()==XOR ? n[1].notNode() : n[1], depIndex2 ); + if( eVal2!=0 ){ + depIndex = depIndex1>depIndex2 ? depIndex1 : depIndex2; + return eVal==eVal2 ? 1 : -1; + } + } + return 0; + }else if( n.getKind()==ITE ){ + int depIndex1; + int eVal = evaluate( rai, n[0], depIndex1 ); + if( eVal==0 ){ + //DO_THIS: evaluate children to see if they are the same value? + return 0; + }else{ + int depIndex2; + int eValT = evaluate( rai, n[eVal==1 ? 1 : 2], depIndex2 ); + depIndex = depIndex1>depIndex2 ? depIndex1 : depIndex2; + return eValT; + } + }else if( n.getKind()==FORALL ){ + return 0; + }else{ + ////if we know we will fail again, immediately return + //if( d_eval_failed.find( n )!=d_eval_failed.end() ){ + // if( d_eval_failed[n] ){ + // return -1; + // } + //} + //Debug("fmf-model-eval-debug") << "Evaluate literal " << n << std::endl; + //this should be a literal + Node gn = n.substitute( rai->d_ic.begin(), rai->d_ic.end(), rai->d_terms.begin(), rai->d_terms.end() ); + //Debug("fmf-model-eval-debug") << " Ground version = " << gn << std::endl; + int retVal = 0; + std::vector< Node > fv_deps; + if( n.getKind()==APPLY_UF ){ + //case for boolean predicates + Node val = evaluateTerm( n, gn, fv_deps ); + if( d_quantEngine->getEqualityQuery()->hasTerm( val ) ){ + if( areEqual( val, NodeManager::currentNM()->mkConst( true ) ) ){ + retVal = 1; + }else{ + retVal = -1; + } + } + }else if( n.getKind()==EQUAL ){ + //case for equality + retVal = evaluateEquality( n[0], n[1], gn[0], gn[1], fv_deps ); + } + if( retVal!=0 ){ + int maxIndex = -1; + for( int i=0; i<(int)fv_deps.size(); i++ ){ + int index = rai->d_var_order[ fv_deps[i].getAttribute(InstVarNumAttribute()) ]; + if( index>maxIndex ){ + maxIndex = index; + if( index==(int)rai->d_index.size()-1 ){ + break; + } + } + } + Debug("fmf-model-eval-debug") << "Evaluate literal: return " << retVal << ", depends = " << maxIndex << std::endl; + depIndex = maxIndex; + } + return retVal; + } +} + +int ModelEngine::evaluateEquality( Node n1, Node n2, Node gn1, Node gn2, std::vector< Node >& fv_deps ){ + ++(d_statistics.d_eval_eqs); + Debug("fmf-model-eval-debug") << "Evaluate equality: " << std::endl; + Debug("fmf-model-eval-debug") << " " << n1 << " = " << n2 << std::endl; + Debug("fmf-model-eval-debug") << " " << gn1 << " = " << gn2 << std::endl; + Node val1 = evaluateTerm( n1, gn1, fv_deps ); + Node val2 = evaluateTerm( n2, gn2, fv_deps ); + Debug("fmf-model-eval-debug") << " Values : "; + printRepresentative( "fmf-model-eval-debug", d_quantEngine, val1 ); + Debug("fmf-model-eval-debug") << " = "; + printRepresentative( "fmf-model-eval-debug", d_quantEngine, val2 ); + Debug("fmf-model-eval-debug") << std::endl; + int retVal = 0; + if( areEqual( val1, val2 ) ){ + retVal = 1; + }else if( areDisequal( val1, val2 ) ){ + retVal = -1; + } + if( retVal!=0 ){ + Debug("fmf-model-eval-debug") << " ---> Success, value = " << (retVal==1) << std::endl; + }else{ + Debug("fmf-model-eval-debug") << " ---> Failed" << std::endl; + } + Debug("fmf-model-eval-debug") << " Value depends on variables: "; + for( int i=0; i<(int)fv_deps.size(); i++ ){ + Debug("fmf-model-eval-debug") << fv_deps[i] << " "; + } + Debug("fmf-model-eval-debug") << std::endl; + return retVal; +} + +Node ModelEngine::evaluateTerm( Node n, Node gn, std::vector< Node >& fv_deps ){ + if( n.hasAttribute(InstConstantAttribute()) ){ + if( n.getKind()==INST_CONSTANT ){ + fv_deps.push_back( n ); + return gn; + //}else if( d_eval_term_fv_deps.find( n )!=d_eval_term_fv_deps.end() && + // d_eval_term_fv_deps[n].find( gn )!=d_eval_term_fv_deps[n].end() ){ + // fv_deps.insert( fv_deps.end(), d_eval_term_fv_deps[n][gn].begin(), d_eval_term_fv_deps[n][gn].end() ); + // return d_eval_term_vals[gn]; + }else{ + //Debug("fmf-model-eval-debug") << "Evaluate term " << n << " (" << gn << ")" << std::endl; + //first we must evaluate the arguments + Node val = gn; + if( n.getKind()==APPLY_UF ){ + Node op = gn.getOperator(); + //if it is a defined UF, then consult the interpretation + Node gnn = gn; + ++(d_statistics.d_eval_uf_terms); + int depIndex = 0; + //first we must evaluate the arguments + bool childrenChanged = false; + std::vector< Node > children; + children.push_back( op ); + std::map< int, std::vector< Node > > children_var_deps; + //for each argument, calculate its value, and the variables its value depends upon + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + Node nn = evaluateTerm( n[i], gn[i], children_var_deps[i] ); + children.push_back( nn ); + childrenChanged = childrenChanged || nn!=gn[i]; + } + //remake gn if changed + if( childrenChanged ){ + gnn = NodeManager::currentNM()->mkNode( APPLY_UF, children ); + } + if( d_uf_model.find( op )!=d_uf_model.end() ){ +#ifdef USE_INDEX_ORDERING + //make the term model specifically for n + makeEvalTermModel( n ); + //now, consult the model + if( d_eval_term_use_default_model[n] ){ + val = d_uf_model[op].d_tree.getValue( d_quantEngine, gnn, depIndex ); + }else{ + val = d_eval_term_model[ n ].getValue( d_quantEngine, gnn, depIndex ); + } + //Debug("fmf-model-eval-debug") << "Evaluate term " << n << " (" << gn << ", " << gnn << ")" << std::endl; + //d_eval_term_model[ n ].debugPrint("fmf-model-eval-debug", d_quantEngine ); + Assert( !val.isNull() ); +#else + //now, consult the model + val = d_uf_model[op].d_tree.getValue( d_quantEngine, gnn, depIndex ); +#endif + }else{ + d_eval_term_use_default_model[n] = true; + val = gnn; + depIndex = (int)n.getNumChildren(); + } + Debug("fmf-model-eval-debug") << "Evaluate term " << n << " = " << gn << " = " << gnn << " = "; + printRepresentative( "fmf-model-eval-debug", d_quantEngine, val ); + Debug("fmf-model-eval-debug") << ", depIndex = " << depIndex << std::endl; + if( !val.isNull() ){ +#ifdef USE_INDEX_ORDERING + for( int i=0; i<depIndex; i++ ){ + int index = d_eval_term_use_default_model[n] ? i : d_eval_term_index_order[n][i]; + Debug("fmf-model-eval-debug") << "Add variables from " << index << "..." << std::endl; + fv_deps.insert( fv_deps.end(), children_var_deps[index].begin(), + children_var_deps[index].end() ); + } +#else + //calculate the free variables that the value depends on : take those in children_var_deps[0...depIndex-1] + for( std::map< int, std::vector< Node > >::iterator it = children_var_deps.begin(); it != children_var_deps.end(); ++it ){ + if( it->first<depIndex ){ + fv_deps.insert( fv_deps.end(), it->second.begin(), it->second.end() ); + } + } +#endif + } + ////cache the result + //d_eval_term_vals[gn] = val; + //d_eval_term_fv_deps[n][gn].insert( d_eval_term_fv_deps[n][gn].end(), fv_deps.begin(), fv_deps.end() ); + } + return val; + } + }else{ + return n; + } +} + +void ModelEngine::clearEvalFailed( int index ){ + for( int i=0; i<(int)d_eval_failed_lits[index].size(); i++ ){ + d_eval_failed[ d_eval_failed_lits[index][i] ] = false; + } + d_eval_failed_lits[index].clear(); +} + +bool ModelEngine::areEqual( Node a, Node b ){ + return d_quantEngine->getEqualityQuery()->areEqual( a, b ); +} + +bool ModelEngine::areDisequal( Node a, Node b ){ + return d_quantEngine->getEqualityQuery()->areDisequal( a, b ); +} + +void ModelEngine::debugPrint( const char* c ){ + Debug( c ) << "---Current Model---" << std::endl; + Debug( c ) << "Representatives: " << std::endl; + d_ra.debugPrint( c, d_quantEngine ); + Debug( c ) << "Quantifiers: " << std::endl; + for( int i=0; i<(int)d_quantEngine->getNumAssertedQuantifiers(); i++ ){ + Node f = d_quantEngine->getAssertedQuantifier( i ); + Debug( c ) << " "; + if( d_quant_sat.find( f )!=d_quant_sat.end() ){ + Debug( c ) << "*SAT* "; + }else{ + Debug( c ) << " "; + } + Debug( c ) << f << std::endl; + } + Debug( c ) << "Functions: " << std::endl; + for( std::map< Node, UfModel >::iterator it = d_uf_model.begin(); it != d_uf_model.end(); ++it ){ + it->second.debugPrint( c ); + Debug( c ) << std::endl; + } +} + +ModelEngine::Statistics::Statistics(): + d_inst_rounds("ModelEngine::Inst_Rounds", 0), + d_pre_sat_quant("ModelEngine::Status_quant_pre_sat", 0), + d_pre_nsat_quant("ModelEngine::Status_quant_pre_non_sat", 0), + d_eval_formulas("ModelEngine::Eval_Formulas", 0 ), + d_eval_eqs("ModelEngine::Eval_Equalities", 0 ), + d_eval_uf_terms("ModelEngine::Eval_Uf_Terms", 0 ), + d_num_quants_init("ModelEngine::Num_Quants", 0 ), + d_num_quants_init_fail("ModelEngine::Num_Quants_No_Basis", 0 ) +{ + StatisticsRegistry::registerStat(&d_inst_rounds); + StatisticsRegistry::registerStat(&d_pre_sat_quant); + StatisticsRegistry::registerStat(&d_pre_nsat_quant); + StatisticsRegistry::registerStat(&d_eval_formulas); + StatisticsRegistry::registerStat(&d_eval_eqs); + StatisticsRegistry::registerStat(&d_eval_uf_terms); + StatisticsRegistry::registerStat(&d_num_quants_init); + StatisticsRegistry::registerStat(&d_num_quants_init_fail); +} + +ModelEngine::Statistics::~Statistics(){ + StatisticsRegistry::unregisterStat(&d_inst_rounds); + StatisticsRegistry::unregisterStat(&d_pre_sat_quant); + StatisticsRegistry::unregisterStat(&d_pre_nsat_quant); + StatisticsRegistry::unregisterStat(&d_eval_formulas); + StatisticsRegistry::unregisterStat(&d_eval_eqs); + StatisticsRegistry::unregisterStat(&d_eval_uf_terms); + StatisticsRegistry::unregisterStat(&d_num_quants_init); + StatisticsRegistry::unregisterStat(&d_num_quants_init_fail); +} diff --git a/src/theory/quantifiers/model_engine.h b/src/theory/quantifiers/model_engine.h new file mode 100644 index 000000000..4031efdf9 --- /dev/null +++ b/src/theory/quantifiers/model_engine.h @@ -0,0 +1,369 @@ +/********************* */ +/*! \file instantiation_engine.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Instantiation Engine classes + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__MODEL_ENGINE_H +#define __CVC4__MODEL_ENGINE_H + +#include "theory/quantifiers_engine.h" +#include "theory/quantifiers/theory_quantifiers.h" + +namespace CVC4 { +namespace theory { + +namespace uf{ + class StrongSolverTheoryUf; +} + +namespace quantifiers { + +/** this class stores a representative alphabet */ +class RepAlphabet { +public: + RepAlphabet(){} + RepAlphabet( RepAlphabet& ra, QuantifiersEngine* qe ); + ~RepAlphabet(){} + std::map< TypeNode, std::vector< Node > > d_type_reps; + std::map< Node, int > d_tmap; + /** clear the alphabet */ + void clear(){ + d_type_reps.clear(); + d_tmap.clear(); + } + /** set the representatives for type */ + void set( TypeNode t, std::vector< Node >& reps ); + /** returns index in d_type_reps for node n */ + int getIndexFor( Node n ) { return d_tmap.find( n )!=d_tmap.end() ? d_tmap[n] : -1; } + /** debug print */ + void debugPrint( const char* c, QuantifiersEngine* qe ); +}; + +class ModelEngine; + +/** this class iterates over a RepAlphabet */ +class RepAlphabetIterator { +private: + void initialize( QuantifiersEngine* qe, Node f, ModelEngine* model ); +public: + RepAlphabetIterator( QuantifiersEngine* qe, Node f, ModelEngine* model ); + RepAlphabetIterator( QuantifiersEngine* qe, Node f, ModelEngine* model, std::vector< int >& indexOrder ); + ~RepAlphabetIterator(){} + //pointer to quantifier + Node d_f; + //pointer to model + ModelEngine* d_model; + //index we are considering + std::vector< int > d_index; + //ordering for variables we are indexing over + // for example, given reps = { a, b } and quantifier forall( x, y, z ) P( x, y, z ) with d_index_order = { 2, 0, 1 }, + // then we consider instantiations in this order: + // a/x a/y a/z + // a/x b/y a/z + // b/x a/y a/z + // b/x b/y a/z + // ... + std::vector< int > d_index_order; + //variables to index they are considered at + // for example, if d_index_order = { 2, 0, 1 } + // then d_var_order = { 0 -> 1, 1 -> 2, 2 -> 0 } + std::map< int, int > d_var_order; + //the instantiation constants of d_f + std::vector< Node > d_ic; + //the current terms we are considering + std::vector< Node > d_terms; +public: + /** increment the iterator */ + void increment2( QuantifiersEngine* qe, int counter ); + void increment( QuantifiersEngine* qe ); + /** is the iterator finished? */ + bool isFinished(); + /** produce the match that this iterator represents */ + void getMatch( QuantifiersEngine* qe, InstMatch& m ); + /** get the i_th term we are considering */ + Node getTerm( int i ); + /** get the number of terms we are considering */ + int getNumTerms() { return d_f[0].getNumChildren(); } + /** refresh d_term to be current with d_index */ + void calculateTerms( QuantifiersEngine* qe ); + /** debug print */ + void debugPrint( const char* c ); + void debugPrintSmall( const char* c ); + //for debugging + int d_inst_tried; + int d_inst_tests; +}; + + +class UfModelTree +{ +public: + UfModelTree(){} + /** the data */ + std::map< Node, UfModelTree > d_data; + /** the value of this tree node (if all paths lead to same value) */ + Node d_value; +public: + //is this model tree empty? + bool isEmpty() { return d_data.empty(); } + //clear + void clear(){ + d_data.clear(); + d_value = Node::null(); + } + /** setValue function + * + * For each argument of n with ModelBasisAttribute() set to true will be considered default arguments if ground=false + * + */ + void setValue( QuantifiersEngine* qe, Node n, Node v, std::vector< int >& indexOrder, bool ground, int argIndex ); + /** getValue function + * + * returns $val, the value of ground term n + * Say n is f( t_0...t_n ) + * depIndex is the index for which every term of the form f( t_0 ... t_depIndex, *,... * ) is equal to $val + * for example, if g( x_0, x_1, x_2 ) := lambda x_0 x_1 x_2. if( x_1==a ) b else c, + * then g( a, a, a ) would return b with depIndex = 1 + * If ground = true, we are asking whether the term n is constant (assumes that all non-model basis arguments are ground) + * + */ + Node getValue( QuantifiersEngine* qe, Node n, std::vector< int >& indexOrder, int& depIndex, int argIndex ); + ///** getConstant Value function + // * + // * given term n, where n may contain model basis arguments + // * if n is constant for its entire domain, then this function returns the value of its domain + // * otherwise, it returns null + // * for example, if f( x_0, x_1 ) := if( x_0 = a ) b else if( x_1 = a ) a else b, + // * then f( a, e ) would return b, while f( e, a ) would return null + // * + // */ + Node getConstantValue( QuantifiersEngine* qe, Node n, std::vector< int >& indexOrder, int argIndex ); + /** simplify function */ + void simplify( Node op, Node defaultVal, int argIndex ); + // is total ? + bool isTotal( Node op, int argIndex ); +public: + void debugPrint( const char* c, QuantifiersEngine* qe, std::vector< int >& indexOrder, int ind = 0, int arg = 0 ); +}; + +class UfModelTreeOrdered +{ +private: + Node d_op; + std::vector< int > d_index_order; + UfModelTree d_tree; +public: + UfModelTreeOrdered(){} + UfModelTreeOrdered( Node op ) : d_op( op ){ + TypeNode tn = d_op.getType(); + for( int i=0; i<(int)(tn.getNumChildren()-1); i++ ){ + d_index_order.push_back( i ); + } + } + UfModelTreeOrdered( Node op, std::vector< int >& indexOrder ) : d_op( op ){ + d_index_order.insert( d_index_order.end(), indexOrder.begin(), indexOrder.end() ); + } + bool isEmpty() { return d_tree.isEmpty(); } + void clear() { d_tree.clear(); } + void setValue( QuantifiersEngine* qe, Node n, Node v, bool ground = true ){ + d_tree.setValue( qe, n, v, d_index_order, ground, 0 ); + } + Node getValue( QuantifiersEngine* qe, Node n, int& depIndex ){ + return d_tree.getValue( qe, n, d_index_order, depIndex, 0 ); + } + Node getConstantValue( QuantifiersEngine* qe, Node n ) { + return d_tree.getConstantValue( qe, n, d_index_order, 0 ); + } + void simplify() { d_tree.simplify( d_op, Node::null(), 0 ); } + bool isTotal() { return d_tree.isTotal( d_op, 0 ); } +public: + void debugPrint( const char* c, QuantifiersEngine* qe, int ind = 0 ){ + d_tree.debugPrint( c, qe, d_index_order, ind ); + } +}; + +class UfModel +{ +//public: + //std::map< Node, std::vector< Node > > d_reqs[2]; + //std::map< Node, std::map< Node, std::vector< Node > > > d_eq_reqs[2]; + ///** add requirement */ + //void addRequirement( Node f, Node p, bool phase ) { d_reqs[ phase ? 1 : 0 ][ f ].push_back( p ); } + ///** add equality requirement */ + //void addEqRequirement( Node f, Node t, Node te, bool phase ) { d_eq_reqs[ phase ? 1 : 0 ][ f ][ t ].push_back( te ); } +private: + Node d_op; + ModelEngine* d_me; + std::vector< Node > d_ground_asserts; + std::vector< Node > d_ground_asserts_reps; + bool d_model_constructed; + //store for set values + std::map< Node, Node > d_set_values[2]; + // preferences for default values + std::vector< Node > d_values; + std::map< Node, std::vector< Node > > d_value_pro_con[2]; + /** set value */ + void setValue( Node n, Node v, bool ground = true ); + /** set model */ + void setModel(); + /** clear model */ + void clearModel(); +public: + UfModel(){} + UfModel( Node op, ModelEngine* qe ); + ~UfModel(){} + //data structure that stores the model + UfModelTreeOrdered d_tree; + //quantifiers that are satisfied because of the constant definition of d_op + bool d_reconsider_model; +public: + /** debug print */ + void debugPrint( const char* c ); + /** get constant value */ + Node getConstantValue( QuantifiersEngine* qe, Node n ); + /** is empty */ + bool isEmpty() { return d_ground_asserts.empty(); } + /** is constant */ + bool isConstant(); +public: + /** build model */ + void buildModel(); + /** make model */ + void makeModel( QuantifiersEngine* qe, UfModelTreeOrdered& tree ); +public: + /** set value preference */ + void setValuePreference( Node f, Node n, bool isPro ); +}; + + + + +class ModelEngine : public QuantifiersModule +{ + friend class UfModel; + friend class RepAlphabetIterator; +private: + TheoryQuantifiers* d_th; + QuantifiersEngine* d_quantEngine; + uf::StrongSolverTheoryUf* d_ss; + //which quantifiers have been initialized + std::map< Node, bool > d_quant_init; + //map from ops to model basis terms + std::map< Node, Node > d_model_basis_term; + //map from instantiation terms to their model basis equivalent + std::map< Node, Node > d_model_basis; + //the model we are working with + RepAlphabet d_ra; + std::map< Node, UfModel > d_uf_model; + ////map from model basis terms to quantifiers that are pro/con their definition + //std::map< Node, std::vector< Node > > d_quant_pro_con[2]; + //map from quantifiers to model basis terms that are pro the definition of + std::map< Node, std::vector< Node > > d_pro_con_quant[2]; + //map from quantifiers to if are constant SAT + std::map< Node, bool > d_quant_sat; +private: + int evaluate( RepAlphabetIterator* rai, Node n, int& depIndex ); + int evaluateEquality( Node n1, Node n2, Node gn1, Node gn2, std::vector< Node >& fv_deps ); + Node evaluateTerm( Node n, Node gn, std::vector< Node >& fv_deps ); + //temporary storing which literals have failed + void clearEvalFailed( int index ); + std::map< Node, bool > d_eval_failed; + std::map< int, std::vector< Node > > d_eval_failed_lits; + ////temporary storing for values/free variable dependencies + //std::map< Node, Node > d_eval_term_vals; + //std::map< Node, std::map< Node, std::vector< Node > > > d_eval_term_fv_deps; +private: + //map from terms to the models used to calculate their value + std::map< Node, UfModelTreeOrdered > d_eval_term_model; + std::map< Node, bool > d_eval_term_use_default_model; + void makeEvalTermModel( Node n ); + //index ordering to use for each term + std::map< Node, std::vector< int > > d_eval_term_index_order; + int getMaxVariableNum( int n ); + void makeEvalTermIndexOrder( Node n ); +public: + void increment( RepAlphabetIterator* rai ); +private: + //queries about equality + bool areEqual( Node a, Node b ); + bool areDisequal( Node a, Node b ); +private: + bool useModel(); +private: + //initialize quantifiers, return false if lemma needed to be added + bool initializeQuantifier( Node f ); + //build representatives + void buildRepresentatives(); + //initialize model + void initializeModel(); + //analyze quantifiers + void analyzeQuantifiers(); + //instantiate quantifier, return number of lemmas produced + int instantiateQuantifier( Node f ); +private: + //register instantiation terms with their corresponding model basis terms + void registerModelBasis( Node n, Node gn ); + //for building UF model + void initializeUf( Node n ); + void collectUfTerms( Node n, std::vector< Node >& terms ); + void initializeUfModel( Node op ); + //void processPredicate( Node f, Node p, bool phase ); + //void processEquality( Node f, Node eq, bool phase ); +public: + ModelEngine( TheoryQuantifiers* th ); + ~ModelEngine(){} + //get quantifiers engine + QuantifiersEngine* getQuantifiersEngine() { return d_quantEngine; } + //get representatives + RepAlphabet* getReps() { return &d_ra; } + //get arbitrary element + Node getArbitraryElement( TypeNode tn, std::vector< Node >& exclude ); + //get model basis term + Node getModelBasisTerm( TypeNode tn, int i = 0 ); + //get model basis term for op + Node getModelBasisApplyUfTerm( Node op ); + //is model basis term for op + bool isModelBasisTerm( Node op, Node n ); +public: + void check( Theory::Effort e ); + void registerQuantifier( Node f ); + void assertNode( Node f ); + Node explain(TNode n){ return Node::null(); } + void propagate( Theory::Effort level ){} + void debugPrint( const char* c ); +public: + /** statistics class */ + class Statistics { + public: + IntStat d_inst_rounds; + IntStat d_pre_sat_quant; + IntStat d_pre_nsat_quant; + IntStat d_eval_formulas; + IntStat d_eval_eqs; + IntStat d_eval_uf_terms; + IntStat d_num_quants_init; + IntStat d_num_quants_init_fail; + Statistics(); + ~Statistics(); + }; + Statistics d_statistics; +}; + +} +} +} + +#endif diff --git a/src/theory/quantifiers/quantifiers_rewriter.cpp b/src/theory/quantifiers/quantifiers_rewriter.cpp new file mode 100644 index 000000000..0fba9d59e --- /dev/null +++ b/src/theory/quantifiers/quantifiers_rewriter.cpp @@ -0,0 +1,722 @@ +/********************* */ +/*! \file theory_quantifiers_rewriter.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of QuantifiersRewriter class + **/ + +#include "theory/quantifiers/quantifiers_rewriter.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; + +bool QuantifiersRewriter::isClause( Node n ){ + if( isLiteral( n ) ){ + return true; + }else if( n.getKind()==NOT ){ + return isCube( n[0] ); + }else if( n.getKind()==OR ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( !isClause( n[i] ) ){ + return false; + } + } + return true; + }else if( n.getKind()==IMPLIES ){ + return isCube( n[0] ) && isClause( n[1] ); + }else{ + return false; + } +} + +bool QuantifiersRewriter::isLiteral( Node n ){ + switch( n.getKind() ){ + case NOT: + return isLiteral( n[0] ); + break; + case OR: + case AND: + case IMPLIES: + case XOR: + case ITE: + case IFF: + return false; + break; + case EQUAL: + return n[0].getType()!=NodeManager::currentNM()->booleanType(); + break; + default: + break; + } + return true; +} + +bool QuantifiersRewriter::isCube( Node n ){ + if( isLiteral( n ) ){ + return true; + }else if( n.getKind()==NOT ){ + return isClause( n[0] ); + }else if( n.getKind()==AND ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( !isCube( n[i] ) ){ + return false; + } + } + return true; + }else{ + return false; + } +} + +void QuantifiersRewriter::addNodeToOrBuilder( Node n, NodeBuilder<>& t ){ + if( n.getKind()==OR ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + t << n[i]; + } + }else{ + t << n; + } +} + +void QuantifiersRewriter::computeArgs( std::map< Node, bool >& active, Node n ){ + if( active.find( n )!=active.end() ){ + active[n] = true; + }else{ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + computeArgs( active, n[i] ); + } + } +} + +void QuantifiersRewriter::computeArgs( std::vector< Node >& args, std::vector< Node >& activeArgs, Node n ){ + std::map< Node, bool > active; + for( int i=0; i<(int)args.size(); i++ ){ + active[ args[i] ] = false; + } + //Notice() << "For " << n << " : " << std::endl; + computeArgs( active, n ); + activeArgs.clear(); + for( std::map< Node, bool >::iterator it = active.begin(); it != active.end(); ++it ){ + Node n = it->first; + //Notice() << " " << it->first << " is " << it->second << std::endl; + if( it->second ){ //only add bound variables that occur in body + activeArgs.push_back( it->first ); + } + } +} + +bool QuantifiersRewriter::hasArg( std::vector< Node >& args, Node n ){ + if( std::find( args.begin(), args.end(), n )!=args.end() ){ + return true; + }else{ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( hasArg( args, n[i] ) ){ + return true; + } + } + return false; + } +} + +void QuantifiersRewriter::setNestedQuantifiers( Node n, Node q ){ + if( n.getKind()==FORALL || n.getKind()==EXISTS ){ + Debug("quantifiers-rewrite-debug") << "Set nested quant attribute " << n << std::endl; + NestedQuantAttribute nqai; + n.setAttribute(nqai,q); + } + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + setNestedQuantifiers( n[i], q ); + } +} + +RewriteResponse QuantifiersRewriter::preRewrite(TNode in) { + Debug("quantifiers-rewrite-debug") << "pre-rewriting " << in << " " << in.hasAttribute(NestedQuantAttribute()) << std::endl; + if( in.getKind()==kind::EXISTS || in.getKind()==kind::FORALL ){ + if( !in.hasAttribute(NestedQuantAttribute()) ){ + setNestedQuantifiers( in[ 1 ], in ); + } + std::vector< Node > args; + for( int i=0; i<(int)in[0].getNumChildren(); i++ ){ + args.push_back( in[0][i] ); + } + Node body = in[1]; + bool doRewrite = false; + while( body.getNumChildren()>=2 && body.getKind()==in.getKind() ){ + for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ + args.push_back( body[0][i] ); + } + body = body[1]; + doRewrite = true; + } + if( doRewrite ){ + std::vector< Node > children; + children.push_back( NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST,args) ); + children.push_back( body ); + if( in.getNumChildren()==3 ){ + children.push_back( in[2] ); + } + Node n = NodeManager::currentNM()->mkNode( in.getKind(), children ); + if( in!=n ){ + if( in.hasAttribute(NestedQuantAttribute()) ){ + setNestedQuantifiers( n, in.getAttribute(NestedQuantAttribute()) ); + } + Debug("quantifiers-pre-rewrite") << "*** pre-rewrite " << in << std::endl; + Debug("quantifiers-pre-rewrite") << " to " << std::endl; + Debug("quantifiers-pre-rewrite") << n << std::endl; + } + return RewriteResponse(REWRITE_DONE, n); + } + } + return RewriteResponse(REWRITE_DONE, in); +} + +RewriteResponse QuantifiersRewriter::postRewrite(TNode in) { + Debug("quantifiers-rewrite-debug") << "post-rewriting " << in << " " << in.hasAttribute(NestedQuantAttribute()) << std::endl; + if( in.getKind()==kind::EXISTS || in.getKind()==kind::FORALL ){ + //get the arguments + std::vector< Node > args; + for( int i=0; i<(int)in[0].getNumChildren(); i++ ){ + args.push_back( in[0][i] ); + } + //get the body + Node body = in[1]; + if( in.getKind()==EXISTS ){ + body = body.getKind()==NOT ? body[0] : body.notNode(); + } + //get the instantiation pattern list + Node ipl; + if( in.getNumChildren()==3 ){ + ipl = in[2]; + } + bool isNested = in.hasAttribute(NestedQuantAttribute()); + //compute miniscoping first + Node n = body;//computeNNF( body ); TODO: compute NNF here (current bad idea since arithmetic rewrites equalities) + if( body!=n ){ + Notice() << "NNF " << body << " -> " << n << std::endl; + } + n = computeMiniscoping( args, n, ipl, isNested ); + if( in.getKind()==kind::EXISTS ){ + n = n.getKind()==NOT ? n[0] : n.notNode(); + } + //compute other rewrite options for each produced quantifier + n = rewriteQuants( n, isNested, true ); + //print if changed + if( in!=n ){ + if( in.hasAttribute(NestedQuantAttribute()) ){ + setNestedQuantifiers( n, in.getAttribute(NestedQuantAttribute()) ); + } + Debug("quantifiers-rewrite") << "*** rewrite " << in << std::endl; + Debug("quantifiers-rewrite") << " to " << std::endl; + Debug("quantifiers-rewrite") << n << std::endl; + if( in.hasAttribute(InstConstantAttribute()) ){ + InstConstantAttribute ica; + n.setAttribute(ica,in.getAttribute(InstConstantAttribute()) ); + } + } + return RewriteResponse(REWRITE_DONE, n ); + } + return RewriteResponse(REWRITE_DONE, in); +} + +Node QuantifiersRewriter::computeNNF( Node body ){ + if( body.getKind()==NOT ){ + if( body[0].getKind()==NOT ){ + return computeNNF( body[0][0] ); + }else if( isLiteral( body[0] ) ){ + return body; + }else{ + std::vector< Node > children; + Kind k = body[0].getKind(); + if( body[0].getKind()==OR || body[0].getKind()==IMPLIES || body[0].getKind()==AND ){ + for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ + Node nn = body[0].getKind()==IMPLIES && i==0 ? body[0][i] : body[0][i].notNode(); + children.push_back( computeNNF( nn ) ); + } + k = body[0].getKind()==AND ? OR : AND; + }else if( body[0].getKind()==XOR || body[0].getKind()==IFF ){ + for( int i=0; i<2; i++ ){ + Node nn = i==0 ? body[0][i] : body[0][i].notNode(); + children.push_back( computeNNF( nn ) ); + } + }else if( body[0].getKind()==ITE ){ + for( int i=0; i<3; i++ ){ + Node nn = i==0 ? body[0][i] : body[0][i].notNode(); + children.push_back( computeNNF( nn ) ); + } + }else{ + Notice() << "Unhandled Quantifiers NNF: " << body << std::endl; + return body; + } + return NodeManager::currentNM()->mkNode( k, children ); + } + }else if( isLiteral( body ) ){ + return body; + }else{ + std::vector< Node > children; + bool childrenChanged = false; + for( int i=0; i<(int)body.getNumChildren(); i++ ){ + Node nc = computeNNF( body[i] ); + children.push_back( nc ); + childrenChanged = childrenChanged || nc!=body[i]; + } + if( childrenChanged ){ + return NodeManager::currentNM()->mkNode( body.getKind(), children ); + }else{ + return body; + } + } +} + +Node QuantifiersRewriter::computeVarElimination( Node body, std::vector< Node >& args, Node& ipl ){ + //Notice() << "Compute var elimination for " << f << std::endl; + std::map< Node, bool > litPhaseReq; + QuantifiersEngine::computePhaseReqs( body, false, litPhaseReq ); + std::vector< Node > vars; + std::vector< Node > subs; + for( std::map< Node, bool >::iterator it = litPhaseReq.begin(); it != litPhaseReq.end(); ++it ){ + //Notice() << " " << it->first << " -> " << ( it->second ? "true" : "false" ) << std::endl; + if( it->first.getKind()==EQUAL ){ + if( it->second ){ + for( int i=0; i<2; i++ ){ + int j = i==0 ? 1 : 0; + std::vector< Node >::iterator ita = std::find( args.begin(), args.end(), it->first[i] ); + if( ita!=args.end() ){ + std::vector< Node > temp; + temp.push_back( it->first[i] ); + if( !hasArg( temp, it->first[j] ) ){ + vars.push_back( it->first[i] ); + subs.push_back( it->first[j] ); + args.erase( ita ); + break; + } + } + } + if( !vars.empty() ){ + break; + } + } + } + } + if( !vars.empty() ){ + //Notice() << "VE " << vars.size() << "/" << n[0].getNumChildren() << std::endl; + //remake with eliminated nodes + body = body.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + body = Rewriter::rewrite( body ); + if( !ipl.isNull() ){ + ipl = ipl.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + } + } + return body; +} + +Node QuantifiersRewriter::computeClause( Node n ){ + Assert( isClause( n ) ); + if( isLiteral( n ) ){ + return n; + }else{ + NodeBuilder<> t(OR); + if( n.getKind()==NOT ){ + if( n[0].getKind()==NOT ){ + return computeClause( n[0][0] ); + }else{ + //De-Morgan's law + Assert( n[0].getKind()==AND ); + for( int i=0; i<(int)n[0].getNumChildren(); i++ ){ + Node nn = computeClause( n[0][i].notNode() ); + addNodeToOrBuilder( nn, t ); + } + } + }else{ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + Node nc = ( ( i==0 && n.getKind()==IMPLIES ) ? n[i].notNode() : n[i] ); + Node nn = computeClause( nc ); + addNodeToOrBuilder( nn, t ); + } + } + return t.constructNode(); + } +} + +Node QuantifiersRewriter::computeCNF( Node n, std::vector< Node >& args, NodeBuilder<>& defs, bool forcePred ){ + if( isLiteral( n ) ){ + return n; + }else if( !forcePred && isClause( n ) ){ + return computeClause( n ); + }else{ + Kind k = ( n.getKind()==IMPLIES ? OR : ( n.getKind()==XOR ? IFF : n.getKind() ) ); + NodeBuilder<> t(k); + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + Node nc = ( i==0 && ( n.getKind()==IMPLIES || n.getKind()==XOR ) ) ? n[i].notNode() : n[i]; + Node ncnf = computeCNF( nc, args, defs, k!=OR ); + if( k==OR ){ + addNodeToOrBuilder( ncnf, t ); + }else{ + t << ncnf; + } + } + if( !forcePred && k==OR ){ + return t.constructNode(); + }else{ + //compute the free variables + Node nt = t; + std::vector< Node > activeArgs; + computeArgs( args, activeArgs, nt ); + std::vector< TypeNode > argTypes; + for( int i=0; i<(int)activeArgs.size(); i++ ){ + argTypes.push_back( activeArgs[i].getType() ); + } + //create the predicate + Assert( argTypes.size()>0 ); + TypeNode typ = NodeManager::currentNM()->mkFunctionType( argTypes, NodeManager::currentNM()->booleanType() ); + std::stringstream ss; + ss << "cnf_" << n.getKind() << "_" << n.getId(); + Node op = NodeManager::currentNM()->mkVar( ss.str(), typ ); + std::vector< Node > predArgs; + predArgs.push_back( op ); + predArgs.insert( predArgs.end(), activeArgs.begin(), activeArgs.end() ); + Node pred = NodeManager::currentNM()->mkNode( APPLY_UF, predArgs ); + Debug("quantifiers-rewrite-cnf-debug") << "Made predicate " << pred << " for " << nt << std::endl; + //create the bound var list + Node bvl = NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, activeArgs ); + //now, look at the structure of nt + if( nt.getKind()==NOT ){ + //case for NOT + for( int i=0; i<2; i++ ){ + NodeBuilder<> tt(OR); + tt << ( i==0 ? nt[0].notNode() : nt[0] ); + tt << ( i==0 ? pred.notNode() : pred ); + defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); + } + }else if( nt.getKind()==OR ){ + //case for OR + for( int i=0; i<(int)nt.getNumChildren(); i++ ){ + NodeBuilder<> tt(OR); + tt << nt[i].notNode() << pred; + defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); + } + NodeBuilder<> tt(OR); + for( int i=0; i<(int)nt.getNumChildren(); i++ ){ + tt << nt[i]; + } + tt << pred.notNode(); + defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); + }else if( nt.getKind()==AND ){ + //case for AND + for( int i=0; i<(int)nt.getNumChildren(); i++ ){ + NodeBuilder<> tt(OR); + tt << nt[i] << pred.notNode(); + defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); + } + NodeBuilder<> tt(OR); + for( int i=0; i<(int)nt.getNumChildren(); i++ ){ + tt << nt[i].notNode(); + } + tt << pred; + defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); + }else if( nt.getKind()==IFF ){ + //case for IFF + for( int i=0; i<4; i++ ){ + NodeBuilder<> tt(OR); + tt << ( ( i==0 || i==3 ) ? nt[0].notNode() : nt[0] ); + tt << ( ( i==1 || i==3 ) ? nt[1].notNode() : nt[1] ); + tt << ( ( i==0 || i==1 ) ? pred.notNode() : pred ); + defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); + } + }else if( nt.getKind()==ITE ){ + //case for ITE + for( int j=1; j<=2; j++ ){ + for( int i=0; i<2; i++ ){ + NodeBuilder<> tt(OR); + tt << ( ( j==1 ) ? nt[0].notNode() : nt[0] ); + tt << ( ( i==1 ) ? nt[j].notNode() : nt[j] ); + tt << ( ( i==0 ) ? pred.notNode() : pred ); + defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); + } + } + for( int i=0; i<2; i++ ){ + NodeBuilder<> tt(OR); + tt << ( i==0 ? nt[1].notNode() : nt[1] ); + tt << ( i==0 ? nt[2].notNode() : nt[2] ); + tt << ( i==1 ? pred.notNode() : pred ); + defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); + } + }else{ + Notice() << "Unhandled Quantifiers CNF: " << nt << std::endl; + } + return pred; + } + } +} + +Node QuantifiersRewriter::computePrenex( Node body, std::vector< Node >& args, bool pol, bool polReq ){ + if( body.getKind()==FORALL ){ + if( pol==polReq ){ + std::vector< Node > terms; + std::vector< Node > subs; + if( polReq ){ + //for doing prenexing of same-signed quantifiers + //must rename each variable that already exists + for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ + //if( std::find( args.begin(), args.end(), body[0][i] )!=args.end() ){ + terms.push_back( body[0][i] ); + subs.push_back( NodeManager::currentNM()->mkVar( body[0][i].getType() ) ); + } + args.insert( args.end(), subs.begin(), subs.end() ); + }else{ + std::vector< TypeNode > argTypes; + for( int i=0; i<(int)args.size(); i++ ){ + argTypes.push_back( args[i].getType() ); + } + //for doing pre-skolemization of opposite-signed quantifiers + for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ + terms.push_back( body[0][i] ); + //make the new function symbol + TypeNode typ = NodeManager::currentNM()->mkFunctionType( argTypes, body[0][i].getType() ); + Node op = NodeManager::currentNM()->mkVar( typ ); + std::vector< Node > funcArgs; + funcArgs.push_back( op ); + funcArgs.insert( funcArgs.end(), args.begin(), args.end() ); + subs.push_back( NodeManager::currentNM()->mkNode( APPLY_UF, funcArgs ) ); + } + } + Node newBody = body[1]; + newBody = newBody.substitute( terms.begin(), terms.end(), subs.begin(), subs.end() ); + return newBody; + }else{ + return body; + } + }else if( body.getKind()==ITE || body.getKind()==XOR || body.getKind()==IFF ){ + return body; + }else{ + Assert( body.getKind()!=EXISTS ); + bool childrenChanged = false; + std::vector< Node > newChildren; + for( int i=0; i<(int)body.getNumChildren(); i++ ){ + bool newPol = ( body.getKind()==NOT || ( body.getKind()==IMPLIES && i==0 ) ) ? !pol : pol; + Node n = computePrenex( body[i], args, newPol, polReq ); + newChildren.push_back( n ); + if( n!=body[i] ){ + childrenChanged = true; + } + } + if( childrenChanged ){ + if( body.getKind()==NOT && newChildren[0].getKind()==NOT ){ + return newChildren[0][0]; + }else{ + return NodeManager::currentNM()->mkNode( body.getKind(), newChildren ); + } + }else{ + return body; + } + } +} + +//general method for computing various rewrites +Node QuantifiersRewriter::computeOperation( Node f, int computeOption ){ + std::vector< Node > args; + for( int i=0; i<(int)f[0].getNumChildren(); i++ ){ + args.push_back( f[0][i] ); + } + NodeBuilder<> defs(kind::AND); + Node n = f[1]; + Node ipl; + if( f.getNumChildren()==3 ){ + ipl = f[2]; + } + if( computeOption==COMPUTE_NNF ){ + n = computeNNF( n ); + }else if( computeOption==COMPUTE_PRENEX || computeOption==COMPUTE_PRE_SKOLEM ){ + n = computePrenex( n, args, true, computeOption==COMPUTE_PRENEX ); + }else if( computeOption==COMPUTE_VAR_ELIMINATION ){ + Node prev; + do{ + prev = n; + n = computeVarElimination( n, args, ipl ); + }while( prev!=n && !args.empty() ); + }else if( computeOption==COMPUTE_CNF ){ + //n = computeNNF( n ); + n = computeCNF( n, args, defs, false ); + ipl = Node::null(); + } + if( f[1]==n && args.size()==long(f[0].getNumChildren()) ){ + return f; + }else{ + if( args.empty() ){ + defs << n; + }else{ + std::vector< Node > children; + children.push_back( NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, args ) ); + children.push_back( n ); + if( !ipl.isNull() ){ + children.push_back( ipl ); + } + defs << NodeManager::currentNM()->mkNode(kind::FORALL, children ); + } + return defs.getNumChildren()==1 ? defs.getChild( 0 ) : defs.constructNode(); + } +} + +Node QuantifiersRewriter::mkForAll( std::vector< Node >& args, Node body, Node ipl ){ + std::vector< Node > activeArgs; + computeArgs( args, activeArgs, body ); + if( activeArgs.empty() ){ + return body; + }else{ + std::vector< Node > children; + children.push_back( NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, activeArgs ) ); + children.push_back( body ); + if( !ipl.isNull() ){ + children.push_back( ipl ); + } + return NodeManager::currentNM()->mkNode( kind::FORALL, children ); + } +} + +Node QuantifiersRewriter::computeMiniscoping( std::vector< Node >& args, Node body, Node ipl, bool isNested ){ + //Notice() << "rewrite quant " << body << std::endl; + if( body.getKind()==FORALL ){ + //combine arguments + std::vector< Node > newArgs; + for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ + newArgs.push_back( body[0][i] ); + } + newArgs.insert( newArgs.end(), args.begin(), args.end() ); + return mkForAll( newArgs, body[ 1 ], ipl ); + }else if( !isNested ){ + if( body.getKind()==NOT ){ + //push not downwards + if( body[0].getKind()==NOT ){ + return computeMiniscoping( args, body[0][0], ipl ); + }else if( body[0].getKind()==AND ){ + if( doMiniscopingNoFreeVar() ){ + NodeBuilder<> t(kind::OR); + for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ + t << ( body[0][i].getKind()==NOT ? body[0][i][0] : body[0][i].notNode() ); + } + return computeMiniscoping( args, t.constructNode(), ipl ); + } + }else if( body[0].getKind()==OR || body[0].getKind()==IMPLIES ){ + if( doMiniscopingAnd() ){ + NodeBuilder<> t(kind::AND); + for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ + Node trm = ( body[0].getKind()==IMPLIES && i==0 ) ? body[0][i] : ( body[0][i].getKind()==NOT ? body[0][i][0] : body[0][i].notNode() ); + t << computeMiniscoping( args, trm, ipl ); + } + return t.constructNode(); + } + } + }else if( body.getKind()==AND ){ + if( doMiniscopingAnd() ){ + //break apart + NodeBuilder<> t(kind::AND); + for( int i=0; i<(int)body.getNumChildren(); i++ ){ + t << computeMiniscoping( args, body[i], ipl ); + } + Node retVal = t; + return retVal; + } + }else if( body.getKind()==OR || body.getKind()==IMPLIES ){ + if( doMiniscopingNoFreeVar() ){ + Node newBody = body; + NodeBuilder<> body_split(kind::OR); + NodeBuilder<> tb(kind::OR); + for( int i=0; i<(int)body.getNumChildren(); i++ ){ + Node trm = ( body.getKind()==IMPLIES && i==0 ) ? ( body[i].getKind()==NOT ? body[i][0] : body[i].notNode() ) : body[i]; + if( hasArg( args, body[i] ) ){ + tb << trm; + }else{ + body_split << trm; + } + } + if( tb.getNumChildren()==0 ){ + return body_split; + }else if( body_split.getNumChildren()>0 ){ + newBody = tb.getNumChildren()==1 ? tb.getChild( 0 ) : tb; + body_split << mkForAll( args, newBody, ipl ); + return body_split.getNumChildren()==1 ? body_split.getChild( 0 ) : body_split; + } + } + } + } + return mkForAll( args, body, ipl ); +} + +Node QuantifiersRewriter::rewriteQuants( Node n, bool isNested, bool duringRewrite ){ + if( n.getKind()==FORALL ){ + return rewriteQuant( n, isNested, duringRewrite ); + }else if( isLiteral( n ) ){ + return n; + }else{ + NodeBuilder<> tt(n.getKind()); + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + tt << rewriteQuants( n[i], isNested, duringRewrite ); + } + return tt.constructNode(); + } +} + +Node QuantifiersRewriter::rewriteQuant( Node n, bool isNested, bool duringRewrite ){ + Node prev = n; + for( int op=0; op<COMPUTE_LAST; op++ ){ + if( doOperation( n, isNested, op, duringRewrite ) ){ + Node prev2 = n; + n = computeOperation( n, op ); + if( prev2!=n ){ + Debug("quantifiers-rewrite-op") << "Rewrite op " << op << ": rewrite " << prev2 << std::endl; + Debug("quantifiers-rewrite-op") << " to " << std::endl; + Debug("quantifiers-rewrite-op") << n << std::endl; + } + } + } + if( prev==n ){ + return n; + }else{ + //rewrite again until fix point is reached + return rewriteQuant( n, isNested, duringRewrite ); + } +} + +bool QuantifiersRewriter::doMiniscopingNoFreeVar(){ + return Options::current()->miniscopeQuantFreeVar; +} + +bool QuantifiersRewriter::doMiniscopingAnd(){ + if( Options::current()->miniscopeQuant ){ + return true; + }else{ + if( Options::current()->cbqi ){ + return true; + }else{ + return false; + } + } +} + +bool QuantifiersRewriter::doOperation( Node f, bool isNested, int computeOption, bool duringRewrite ){ + if( computeOption==COMPUTE_NNF ){ + return false;//TODO: compute NNF (current bad idea since arithmetic rewrites equalities) + }else if( computeOption==COMPUTE_PRE_SKOLEM ){ + return Options::current()->preSkolemQuant && !duringRewrite; + }else if( computeOption==COMPUTE_PRENEX ){ + return Options::current()->prenexQuant; + }else if( computeOption==COMPUTE_VAR_ELIMINATION ){ + return Options::current()->varElimQuant; + }else if( computeOption==COMPUTE_CNF ){ + return Options::current()->cnfQuant && !duringRewrite;// || Options::current()->finiteModelFind; + }else{ + return false; + } +} diff --git a/src/theory/quantifiers/quantifiers_rewriter.h b/src/theory/quantifiers/quantifiers_rewriter.h new file mode 100644 index 000000000..8c037d30b --- /dev/null +++ b/src/theory/quantifiers/quantifiers_rewriter.h @@ -0,0 +1,88 @@ +/********************* */ +/*! \file quantifiers_rewriter.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: mdeters + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Rewriter for the theory of inductive quantifiers + ** + ** Rewriter for the theory of inductive quantifiers. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__QUANTIFIERS__QUANTIFIERS_REWRITER_H +#define __CVC4__THEORY__QUANTIFIERS__QUANTIFIERS_REWRITER_H + +#include "theory/rewriter.h" +#include "theory/quantifiers_engine.h" + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +// attribute for "contains instantiation constants from" +struct NestedQuantAttributeId {}; +typedef expr::Attribute<NestedQuantAttributeId, Node> NestedQuantAttribute; + +class QuantifiersRewriter { +public: + static bool isClause( Node n ); + static bool isLiteral( Node n ); + static bool isCube( Node n ); +private: + static void addNodeToOrBuilder( Node n, NodeBuilder<>& t ); + static Node mkForAll( std::vector< Node >& args, Node body, Node ipl ); + static void computeArgs( std::vector< Node >& args, std::vector< Node >& activeArgs, Node n ); + static bool hasArg( std::vector< Node >& args, Node n ); + static void setNestedQuantifiers( Node n, Node q ); + static void computeArgs( std::map< Node, bool >& active, Node n ); + static Node computeClause( Node n ); +private: + static Node computeMiniscoping( std::vector< Node >& args, Node body, Node ipl, bool isNested = false ); + static Node computeNNF( Node body ); + static Node computeVarElimination( Node body, std::vector< Node >& args, Node& ipl ); + static Node computeCNF( Node body, std::vector< Node >& args, NodeBuilder<>& defs, bool forcePred ); + static Node computePrenex( Node body, std::vector< Node >& args, bool pol, bool polReq ); +private: + enum{ + COMPUTE_NNF = 0, + COMPUTE_PRE_SKOLEM, + COMPUTE_PRENEX, + COMPUTE_VAR_ELIMINATION, + //COMPUTE_FLATTEN_ARGS_UF, + COMPUTE_CNF, + COMPUTE_LAST + }; + static Node computeOperation( Node f, int computeOption ); +public: + static RewriteResponse preRewrite(TNode in); + static RewriteResponse postRewrite(TNode in); + static Node rewriteEquality(TNode equality) { + return postRewrite(equality).node; + } + static inline void init() {} + static inline void shutdown() {} +private: + /** options */ + static bool doMiniscopingNoFreeVar(); + static bool doMiniscopingAnd(); + static bool doOperation( Node f, bool isNested, int computeOption, bool duringRewrite = true ); +public: + static Node rewriteQuants( Node n, bool isNested = false, bool duringRewrite = true ); + static Node rewriteQuant( Node n, bool isNested = false, bool duringRewrite = true ); +};/* class QuantifiersRewriter */ + +}/* CVC4::theory::quantifiers namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__QUANTIFIERS__QUANTIFIERS_REWRITER_H */ + diff --git a/src/theory/quantifiers/theory_quantifiers.cpp b/src/theory/quantifiers/theory_quantifiers.cpp new file mode 100644 index 000000000..ead47e4b0 --- /dev/null +++ b/src/theory/quantifiers/theory_quantifiers.cpp @@ -0,0 +1,199 @@ +/********************* */ +/*! \file theory_quantifiers.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of the theory of quantifiers + ** + ** Implementation of the theory of quantifiers. + **/ + + +#include "theory/quantifiers/theory_quantifiers.h" +#include "theory/valuation.h" +#include "theory/quantifiers_engine.h" +#include "theory/quantifiers/instantiation_engine.h" +#include "theory/quantifiers/model_engine.h" +#include "expr/kind.h" +#include "util/Assert.h" +#include <map> +#include <time.h> +#include "theory/quantifiers/theory_quantifiers_instantiator.h" + +#define USE_FLIP_DECISION + +//static bool clockSet = false; +//double initClock; + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; + +TheoryQuantifiers::TheoryQuantifiers(Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) : + Theory(THEORY_QUANTIFIERS, c, u, out, valuation, logicInfo, qe), + d_numRestarts(0){ + d_numInstantiations = 0; + d_baseDecLevel = -1; + if( Options::current()->finiteModelFind ){ + qe->addModule( new ModelEngine( this ) ); + }else{ + qe->addModule( new InstantiationEngine( this ) ); + } +} + + +TheoryQuantifiers::~TheoryQuantifiers() { +} + +void TheoryQuantifiers::addSharedTerm(TNode t) { + Debug("quantifiers-other") << "TheoryQuantifiers::addSharedTerm(): " + << t << endl; +} + + +void TheoryQuantifiers::notifyEq(TNode lhs, TNode rhs) { + Debug("quantifiers-other") << "TheoryQuantifiers::notifyEq(): " + << lhs << " = " << rhs << endl; + +} + +void TheoryQuantifiers::preRegisterTerm(TNode n) { + Debug("quantifiers-prereg") << "TheoryQuantifiers::preRegisterTerm() " << n << endl; + if( n.getKind()==FORALL && !n.hasAttribute(InstConstantAttribute()) ){ + getQuantifiersEngine()->registerQuantifier( n ); + } +} + + +void TheoryQuantifiers::presolve() { + Debug("quantifiers-presolve") << "TheoryQuantifiers::presolve()" << endl; +} + +Node TheoryQuantifiers::getValue(TNode n) { + //NodeManager* nodeManager = NodeManager::currentNM(); + switch(n.getKind()) { + case FORALL: + case EXISTS: + bool value; + if( d_valuation.hasSatValue( n, value ) ){ + return NodeManager::currentNM()->mkConst(value); + }else{ + return NodeManager::currentNM()->mkConst(false); //FIX_THIS? + } + break; + default: + Unhandled(n.getKind()); + } +} + +void TheoryQuantifiers::check(Effort e) { + CodeTimer codeTimer(d_theoryTime); + + Debug("quantifiers-check") << "quantifiers::check(" << e << ")" << std::endl; + while(!done()) { + Node assertion = get(); + Debug("quantifiers-assert") << "quantifiers::assert(): " << assertion << std::endl; + switch(assertion.getKind()) { + case kind::FORALL: + assertUniversal( assertion ); + break; + case kind::NOT: + { + switch( assertion[0].getKind()) { + case kind::FORALL: + assertExistential( assertion ); + break; + default: + Unhandled(assertion[0].getKind()); + break; + } + } + break; + default: + Unhandled(assertion.getKind()); + break; + } + } + // call the quantifiers engine to check + getQuantifiersEngine()->check( e ); +} + +void TheoryQuantifiers::propagate(Effort level){ + CodeTimer codeTimer(d_theoryTime); + + getQuantifiersEngine()->propagate( level ); +} + +void TheoryQuantifiers::assertUniversal( Node n ){ + Assert( n.getKind()==FORALL ); + if( !n.hasAttribute(InstConstantAttribute()) ){ + getQuantifiersEngine()->registerQuantifier( n ); + getQuantifiersEngine()->assertNode( n ); + } +} + +void TheoryQuantifiers::assertExistential( Node n ){ + Assert( n.getKind()== NOT && n[0].getKind()==FORALL ); + if( !n[0].hasAttribute(InstConstantAttribute()) ){ + if( d_skolemized.find( n )==d_skolemized.end() ){ + Node body = getQuantifiersEngine()->getSkolemizedBody( n[0] ); + NodeBuilder<> nb(kind::OR); + nb << n[0] << body.notNode(); + Node lem = nb; + Debug("quantifiers-sk") << "Skolemize lemma : " << lem << std::endl; + d_out->lemma( lem ); + d_skolemized[n] = true; + } + } +} + +bool TheoryQuantifiers::flipDecision(){ +#ifndef USE_FLIP_DECISION + return false; +#else + //Debug("quantifiers-flip") << "No instantiation given, flip decision, level = " << d_valuation.getDecisionLevel() << std::endl; + //for( int i=1; i<=(int)d_valuation.getDecisionLevel(); i++ ){ + // Debug("quantifiers-flip") << " " << d_valuation.getDecision( i ) << std::endl; + //} + //if( d_valuation.getDecisionLevel()>0 ){ + // double r = double(rand())/double(RAND_MAX); + // unsigned decisionLevel = (unsigned)(r*d_valuation.getDecisionLevel()); + // d_out->flipDecision( decisionLevel ); + // return true; + //}else{ + // return false; + //} + + if( !d_out->flipDecision() ){ + return restart(); + } + return true; +#endif +} + +bool TheoryQuantifiers::restart(){ + static const int restartLimit = 0; + if( d_numRestarts==restartLimit ){ + Debug("quantifiers-flip") << "No more restarts." << std::endl; + return false; + }else{ + d_numRestarts++; + Debug("quantifiers-flip") << "Do restart." << std::endl; + return true; + } +} + +void TheoryQuantifiers::performCheck(Effort e){ + getQuantifiersEngine()->check( e ); +} diff --git a/src/theory/quantifiers/theory_quantifiers.h b/src/theory/quantifiers/theory_quantifiers.h new file mode 100644 index 000000000..05c3b9695 --- /dev/null +++ b/src/theory/quantifiers/theory_quantifiers.h @@ -0,0 +1,77 @@ +/********************* */ +/*! \file theory_quantifiers.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Theory of quantifiers. + ** + ** Theory of quantifiers. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__QUANTIFIERS__THEORY_QUANTIFIERS_H +#define __CVC4__THEORY__QUANTIFIERS__THEORY_QUANTIFIERS_H + +#include "theory/theory.h" +#include "util/hash.h" +#include "util/stats.h" + +#include <ext/hash_set> +#include <iostream> +#include <map> + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +class TheoryEngine; + +class TheoryQuantifiers : public Theory { +private: + typedef context::CDHashMap< Node, bool, NodeHashFunction > BoolMap; + /** quantifiers that have been skolemized */ + std::map< Node, bool > d_skolemized; + /** number of instantiations */ + int d_numInstantiations; + int d_baseDecLevel; + /** number of restarts */ + int d_numRestarts; + + KEEP_STATISTIC(TimerStat, d_theoryTime, "theory::quantifiers::theoryTime"); + +public: + TheoryQuantifiers(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe); + ~TheoryQuantifiers(); + + void addSharedTerm(TNode t); + void notifyEq(TNode lhs, TNode rhs); + void preRegisterTerm(TNode n); + void presolve(); + void check(Effort e); + void propagate(Effort level); + Node getValue(TNode n); + void shutdown() { } + std::string identify() const { return std::string("TheoryQuantifiers"); } + bool flipDecision(); +private: + void assertUniversal( Node n ); + void assertExistential( Node n ); + bool restart(); +public: + void performCheck(Effort e); +};/* class TheoryQuantifiers */ + +}/* CVC4::theory::quantifiers namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__QUANTIFIERS__THEORY_QUANTIFIERS_H */ diff --git a/src/theory/quantifiers/theory_quantifiers_instantiator.cpp b/src/theory/quantifiers/theory_quantifiers_instantiator.cpp new file mode 100644 index 000000000..a5b6cc3bc --- /dev/null +++ b/src/theory/quantifiers/theory_quantifiers_instantiator.cpp @@ -0,0 +1,76 @@ +/********************* */ +/*! \file theory_quantifiers_instantiator.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of theory_quantifiers_instantiator class + **/ + +#include "theory/quantifiers/theory_quantifiers_instantiator.h" +#include "theory/quantifiers/theory_quantifiers.h" +#include "theory/theory_engine.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; + +InstantiatorTheoryQuantifiers::InstantiatorTheoryQuantifiers(context::Context* c, QuantifiersEngine* ie, Theory* th) : +Instantiator( c, ie, th ){ + +} + +void InstantiatorTheoryQuantifiers::assertNode( Node assertion ){ + Debug("quant-quant-assert") << "InstantiatorTheoryQuantifiers::check: " << assertion << std::endl; + if( Options::current()->cbqi ){ + if( assertion.hasAttribute(InstConstantAttribute()) ){ + Debug("quant-quant-assert") << " -> has constraints from " << assertion.getAttribute(InstConstantAttribute()) << std::endl; + setHasConstraintsFrom( assertion.getAttribute(InstConstantAttribute()) ); + }else if( assertion.getKind()==NOT && assertion[0].hasAttribute(InstConstantAttribute()) ){ + Debug("quant-quant-assert") << " -> has constraints from " << assertion[0].getAttribute(InstConstantAttribute()) << std::endl; + setHasConstraintsFrom( assertion[0].getAttribute(InstConstantAttribute()) ); + } + } +} + +void InstantiatorTheoryQuantifiers::processResetInstantiationRound( Theory::Effort effort ){ + +} + + +int InstantiatorTheoryQuantifiers::process( Node f, Theory::Effort effort, int e, int limitInst ){ + Debug("quant-quant") << "Quant: Try to solve (" << e << ") for " << f << "... " << std::endl; + if( e<5 ){ + return InstStrategy::STATUS_UNFINISHED; + }else if( e==5 ){ + //add random addition + if( isOwnerOf( f ) ){ + InstMatch m; + if( d_quantEngine->addInstantiation( f, m ) ){ + ++(d_statistics.d_instantiations); + } + } + } + return InstStrategy::STATUS_UNKNOWN; +} + +InstantiatorTheoryQuantifiers::Statistics::Statistics(): + d_instantiations("InstantiatorTheoryQuantifiers::Instantiations_Total", 0) +{ + StatisticsRegistry::registerStat(&d_instantiations); +} + +InstantiatorTheoryQuantifiers::Statistics::~Statistics(){ + StatisticsRegistry::unregisterStat(&d_instantiations); +} + diff --git a/src/theory/quantifiers/theory_quantifiers_instantiator.h b/src/theory/quantifiers/theory_quantifiers_instantiator.h new file mode 100644 index 000000000..dda3bfeaa --- /dev/null +++ b/src/theory/quantifiers/theory_quantifiers_instantiator.h @@ -0,0 +1,60 @@ +/********************* */ +/*! \file instantiator_quantifiers_instantiator.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief instantiator_quantifiers_instantiator + **/ + + +#include "cvc4_private.h" + +#ifndef __CVC4__INSTANTIATOR_QUANTIFIERS_H +#define __CVC4__INSTANTIATOR_QUANTIFIERS_H + +#include "theory/quantifiers_engine.h" + +#include "util/stats.h" + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +class InstantiatorTheoryQuantifiers : public Instantiator{ + friend class QuantifiersEngine; +public: + InstantiatorTheoryQuantifiers(context::Context* c, QuantifiersEngine* ie, Theory* th); + ~InstantiatorTheoryQuantifiers() {} + + /** check function, assertion is asserted to theory */ + void assertNode( Node assertion ); + /** identify */ + std::string identify() const { return std::string("InstantiatorTheoryQuantifiers"); } +private: + /** reset instantiation */ + void processResetInstantiationRound( Theory::Effort effort ); + /** process at effort */ + int process( Node f, Theory::Effort effort, int e, int limitInst ); + + class Statistics { + public: + IntStat d_instantiations; + Statistics(); + ~Statistics(); + }; + Statistics d_statistics; +};/* class InstantiatiorTheoryArith */ + +} +} +} + +#endif
\ No newline at end of file diff --git a/src/theory/quantifiers/theory_quantifiers_type_rules.h b/src/theory/quantifiers/theory_quantifiers_type_rules.h new file mode 100644 index 000000000..ceec36d7b --- /dev/null +++ b/src/theory/quantifiers/theory_quantifiers_type_rules.h @@ -0,0 +1,113 @@ +/********************* */ +/*! \file theory_quantifiers_type_rules.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Theory of quantifiers + ** + ** Theory of quantifiers. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__QUANTIFIERS__THEORY_QUANTIFIERS_TYPE_RULES_H +#define __CVC4__THEORY__QUANTIFIERS__THEORY_QUANTIFIERS_TYPE_RULES_H + +#include "util/matcher.h" + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +struct QuantifierForallTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw(TypeCheckingExceptionPrivate) { + Debug("typecheck-q") << "type check for fa " << n << std::endl; + Assert(n.getKind() == kind::FORALL && n.getNumChildren()>0 ); + if( check ){ + if( n[ 0 ].getType(check)!=nodeManager->boundVarListType() ){ + throw TypeCheckingExceptionPrivate(n, "first argument of universal quantifier is not bound var list"); + } + if( n[ 1 ].getType(check)!=nodeManager->booleanType() ){ + throw TypeCheckingExceptionPrivate(n, "body of universal quantifier is not boolean"); + } + if( n.getNumChildren()==3 && n[2].getType(check)!=nodeManager->instPatternListType() ){ + throw TypeCheckingExceptionPrivate(n, "third argument of universal quantifier is not instantiation pattern list"); + } + } + return nodeManager->booleanType(); + } +};/* struct QuantifierForallTypeRule */ + +struct QuantifierExistsTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw(TypeCheckingExceptionPrivate) { + Debug("typecheck-q") << "type check for ex " << n << std::endl; + Assert(n.getKind() == kind::EXISTS && n.getNumChildren()>0 ); + if( check ){ + if( n[ 0 ].getType(check)!=nodeManager->boundVarListType() ){ + throw TypeCheckingExceptionPrivate(n, "first argument of existential quantifier is not bound var list"); + } + if( n[ 1 ].getType(check)!=nodeManager->booleanType() ){ + throw TypeCheckingExceptionPrivate(n, "body of existential quantifier is not boolean"); + } + if( n.getNumChildren()==3 && n[2].getType(check)!=nodeManager->instPatternListType() ){ + throw TypeCheckingExceptionPrivate(n, "third argument of existential quantifier is not instantiation pattern list"); + } + } + return nodeManager->booleanType(); + } +};/* struct QuantifierExistsTypeRule */ + +struct QuantifierBoundVarListTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw(TypeCheckingExceptionPrivate) { + Assert(n.getKind() == kind::BOUND_VAR_LIST ); + if( check ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( n[i].getKind()!=kind::VARIABLE ){ + throw TypeCheckingExceptionPrivate(n, "argument of bound var list is not variable"); + } + } + } + return nodeManager->boundVarListType(); + } +};/* struct QuantifierBoundVarListTypeRule */ + +struct QuantifierInstPatternTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw(TypeCheckingExceptionPrivate) { + Assert(n.getKind() == kind::INST_PATTERN ); + return nodeManager->instPatternType(); + } +};/* struct QuantifierInstPatternTypeRule */ + + +struct QuantifierInstPatternListTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw(TypeCheckingExceptionPrivate) { + Assert(n.getKind() == kind::INST_PATTERN_LIST ); + if( check ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( n[i].getKind()!=kind::INST_PATTERN ){ + throw TypeCheckingExceptionPrivate(n, "argument of inst pattern list is not inst pattern"); + } + } + } + return nodeManager->instPatternListType(); + } +};/* struct QuantifierInstPatternListTypeRule */ + +}/* CVC4::theory::quantifiers namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__QUANTIFIERS__THEORY_QUANTIFIERS_TYPE_RULES_H */ diff --git a/src/theory/quantifiers_engine.cpp b/src/theory/quantifiers_engine.cpp new file mode 100644 index 000000000..ddb085632 --- /dev/null +++ b/src/theory/quantifiers_engine.cpp @@ -0,0 +1,788 @@ +/********************* */ +/*! \file quantifiers_engine.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of quantifiers engine class + **/ + +#include "theory/quantifiers_engine.h" +#include "theory/theory_engine.h" +#include "theory/uf/theory_uf_instantiator.h" +#include "theory/uf/theory_uf_strong_solver.h" +#include "theory/uf/equality_engine.h" +#include "theory/quantifiers/quantifiers_rewriter.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; + +//#define COMPUTE_RELEVANCE +//#define REWRITE_ASSERTED_QUANTIFIERS + + /** reset instantiation */ +void InstStrategy::resetInstantiationRound( Theory::Effort effort ){ + d_no_instantiate_temp.clear(); + d_no_instantiate_temp.insert( d_no_instantiate_temp.begin(), d_no_instantiate.begin(), d_no_instantiate.end() ); + processResetInstantiationRound( effort ); +} +/** do instantiation round method */ +int InstStrategy::doInstantiation( Node f, Theory::Effort effort, int e, int limitInst ){ + if( shouldInstantiate( f ) ){ + int origLemmas = d_quantEngine->getNumLemmasWaiting(); + int retVal = process( f, effort, e, limitInst ); + if( d_quantEngine->getNumLemmasWaiting()!=origLemmas ){ + for( int i=0; i<(int)d_priority_over.size(); i++ ){ + d_priority_over[i]->d_no_instantiate_temp.push_back( f ); + } + } + return retVal; + }else{ + return STATUS_UNKNOWN; + } +} + +bool TermArgTrie::addTerm2( QuantifiersEngine* qe, Node n, int argIndex ){ + if( argIndex<(int)n.getNumChildren() ){ + Node r = qe->getEqualityQuery()->getRepresentative( n[ argIndex ] ); + std::map< Node, TermArgTrie >::iterator it = d_data.find( r ); + if( it==d_data.end() ){ + d_data[r].addTerm2( qe, n, argIndex+1 ); + return true; + }else{ + return it->second.addTerm2( qe, n, argIndex+1 ); + } + }else{ + //store n in d_data (this should be interpretted as the "data" and not as a reference to a child) + d_data[n].d_data.clear(); + return false; + } +} + +void TermDb::addTerm( Node n, std::vector< Node >& added, bool withinQuant ){ + //don't add terms in quantifier bodies + if( !withinQuant || Options::current()->registerQuantBodyTerms ){ + if( d_processed.find( n )==d_processed.end() ){ + d_processed[n] = true; + //if this is an atomic trigger, consider adding it + if( Trigger::isAtomicTrigger( n ) ){ + if( !n.hasAttribute(InstConstantAttribute()) ){ + Debug("term-db") << "register trigger term " << n << std::endl; + //Notice() << "register trigger term " << n << std::endl; + Node op = n.getOperator(); + d_op_map[op].push_back( n ); + d_type_map[ n.getType() ].push_back( n ); + added.push_back( n ); + + uf::InstantiatorTheoryUf* d_ith = (uf::InstantiatorTheoryUf*)d_quantEngine->getInstantiator( THEORY_UF ); + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + addTerm( n[i], added, withinQuant ); + if( Options::current()->efficientEMatching ){ + if( d_parents[n[i]][op].empty() ){ + //must add parent to equivalence class info + Node nir = d_ith->getRepresentative( n[i] ); + uf::EqClassInfo* eci_nir = d_ith->getEquivalenceClassInfo( nir ); + if( eci_nir ){ + eci_nir->d_pfuns[ op ] = true; + } + } + //add to parent structure + if( std::find( d_parents[n[i]][op][i].begin(), d_parents[n[i]][op][i].end(), n )==d_parents[n[i]][op][i].end() ){ + d_parents[n[i]][op][i].push_back( n ); + } + } + } + if( Options::current()->efficientEMatching ){ + //new term, add n to candidate generators + for( int i=0; i<(int)d_ith->d_cand_gens[op].size(); i++ ){ + d_ith->d_cand_gens[op][i]->addCandidate( n ); + } + } + if( Options::current()->eagerInstQuant ){ + if( !n.hasAttribute(InstLevelAttribute()) && n.getAttribute(InstLevelAttribute())==0 ){ + int addedLemmas = 0; + for( int i=0; i<(int)d_ith->d_op_triggers[op].size(); i++ ){ + addedLemmas += d_ith->d_op_triggers[op][i]->addTerm( n ); + } + //std::cout << "Terms, added lemmas: " << addedLemmas << std::endl; + d_quantEngine->flushLemmas( &d_quantEngine->getTheoryEngine()->getTheory( THEORY_QUANTIFIERS )->getOutputChannel() ); + } + } + } + } + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + addTerm( n[i], added, withinQuant ); + } + } + } +} + +void TermDb::reset( Theory::Effort effort ){ + int nonCongruentCount = 0; + int congruentCount = 0; + int alreadyCongruentCount = 0; + //rebuild d_func/pred_map_trie for each operation, this will calculate all congruent terms + for( std::map< Node, std::vector< Node > >::iterator it = d_op_map.begin(); it != d_op_map.end(); ++it ){ + if( !it->second.empty() ){ + if( it->second[0].getType()==NodeManager::currentNM()->booleanType() ){ + d_pred_map_trie[ 0 ][ it->first ].d_data.clear(); + d_pred_map_trie[ 1 ][ it->first ].d_data.clear(); + }else{ + d_func_map_trie[ it->first ].d_data.clear(); + for( int i=0; i<(int)it->second.size(); i++ ){ + Node n = it->second[i]; + if( !n.getAttribute(NoMatchAttribute()) ){ + if( !d_func_map_trie[ it->first ].addTerm( d_quantEngine, n ) ){ + NoMatchAttribute nma; + n.setAttribute(nma,true); + congruentCount++; + }else{ + nonCongruentCount++; + } + }else{ + congruentCount++; + alreadyCongruentCount++; + } + } + } + } + } + for( int i=0; i<2; i++ ){ + Node n = NodeManager::currentNM()->mkConst( i==1 ); + eq::EqClassIterator eqc( d_quantEngine->getEqualityQuery()->getRepresentative( n ), + ((uf::TheoryUF*)d_quantEngine->getTheoryEngine()->getTheory( THEORY_UF ))->getEqualityEngine() ); + while( !eqc.isFinished() ){ + Node en = (*eqc); + if( en.getKind()==APPLY_UF && !en.hasAttribute(InstConstantAttribute()) ){ + if( !en.getAttribute(NoMatchAttribute()) ){ + Node op = en.getOperator(); + if( !d_pred_map_trie[i][op].addTerm( d_quantEngine, en ) ){ + NoMatchAttribute nma; + en.setAttribute(nma,true); + congruentCount++; + }else{ + nonCongruentCount++; + } + }else{ + alreadyCongruentCount++; + } + } + ++eqc; + } + } + Debug("term-db-cong") << "TermDb: Reset" << std::endl; + Debug("term-db-cong") << "Congruent/Non-Congruent = "; + Debug("term-db-cong") << congruentCount << "(" << alreadyCongruentCount << ") / " << nonCongruentCount << std::endl; +} + + + +QuantifiersEngine::QuantifiersEngine(context::Context* c, TheoryEngine* te): +d_te( te ), +d_forall_asserts( c ), +d_active( c ){ + d_eq_query = NULL; + d_term_db = new TermDb( this ); +} + +Instantiator* QuantifiersEngine::getInstantiator( int id ){ + return d_te->getTheory( id )->getInstantiator(); +} + +void QuantifiersEngine::check( Theory::Effort e ){ + CodeTimer codeTimer(d_time); + + if( e==Theory::EFFORT_LAST_CALL ){ + ++(d_statistics.d_instantiation_rounds_lc); + }else if( e==Theory::EFFORT_FULL ){ + ++(d_statistics.d_instantiation_rounds); + } + for( int i=0; i<(int)d_modules.size(); i++ ){ + d_modules[i]->check( e ); + } + //if( e==Theory::EFFORT_FULL ){ + // Notice() << "Done instantiation Round" << std::endl; + //} +} + +std::vector<Node> QuantifiersEngine::createInstVariable( std::vector<Node> & vars ){ + std::vector<Node> inst_constant; + inst_constant.reserve(vars.size()); + for( std::vector<Node>::const_iterator v = vars.begin(); + v != vars.end(); ++v ){ + //make instantiation constants + Node ic = NodeManager::currentNM()->mkInstConstant( (*v).getType() ); + inst_constant.push_back( ic ); + }; + return inst_constant; +} + +void QuantifiersEngine::makeInstantiationConstantsFor( Node f ){ + if( d_inst_constants.find( f )==d_inst_constants.end() ){ + Debug("quantifiers-engine") << "Instantiation constants for " << f << " : " << std::endl; + for( int i=0; i<(int)f[0].getNumChildren(); i++ ){ + d_vars[f].push_back( f[0][i] ); + //make instantiation constants + Node ic = NodeManager::currentNM()->mkInstConstant( f[0][i].getType() ); + d_inst_constants_map[ic] = f; + d_inst_constants[ f ].push_back( ic ); + Debug("quantifiers-engine") << " " << ic << std::endl; + //set the var number attribute + InstVarNumAttribute ivna; + ic.setAttribute(ivna,i); + } + } +} + +void QuantifiersEngine::registerQuantifier( Node f ){ + if( std::find( d_quants.begin(), d_quants.end(), f )==d_quants.end() ){ + std::vector< Node > quants; +#ifdef REWRITE_ASSERTED_QUANTIFIERS + //do assertion-time rewriting of quantifier + Node nf = quantifiers::QuantifiersRewriter::rewriteQuant( f, false, false ); + if( nf!=f ){ + Debug("quantifiers-rewrite") << "*** assert-rewrite " << f << std::endl; + Debug("quantifiers-rewrite") << " to " << std::endl; + Debug("quantifiers-rewrite") << nf << std::endl; + //we will instead register all the rewritten quantifiers + if( nf.getKind()==FORALL ){ + quants.push_back( nf ); + }else if( nf.getKind()==AND ){ + for( int i=0; i<(int)nf.getNumChildren(); i++ ){ + quants.push_back( nf[i] ); + } + }else{ + //unhandled: rewrite must go to a quantifier, or conjunction of quantifiers + Assert( false ); + } + }else{ + quants.push_back( f ); + } +#else + quants.push_back( f ); +#endif + for( int q=0; q<(int)quants.size(); q++ ){ + d_quant_rewritten[f].push_back( quants[q] ); + d_rewritten_quant[ quants[q] ] = f; + ++(d_statistics.d_num_quant); + Assert( quants[q].getKind()==FORALL ); + //register quantifier + d_quants.push_back( quants[q] ); + //make instantiation constants for quants[q] + makeInstantiationConstantsFor( quants[q] ); + //compute symbols in quants[q] + std::vector< Node > syms; + computeSymbols( quants[q][1], syms ); + d_syms[quants[q]].insert( d_syms[quants[q]].begin(), syms.begin(), syms.end() ); + //set initial relevance + int minRelevance = -1; + for( int i=0; i<(int)syms.size(); i++ ){ + d_syms_quants[ syms[i] ].push_back( quants[q] ); + int r = getRelevance( syms[i] ); + if( r!=-1 && ( minRelevance==-1 || r<minRelevance ) ){ + minRelevance = r; + } + } +#ifdef COMPUTE_RELEVANCE + if( minRelevance!=-1 ){ + setRelevance( quants[q], minRelevance+1 ); + } +#endif + //register with each module + for( int i=0; i<(int)d_modules.size(); i++ ){ + d_modules[i]->registerQuantifier( quants[q] ); + } + Node ceBody = getCounterexampleBody( quants[q] ); + generatePhaseReqs( quants[q], ceBody ); + //also register it with the strong solver + if( Options::current()->finiteModelFind ){ + ((uf::TheoryUF*)d_te->getTheory( THEORY_UF ))->getStrongSolver()->registerQuantifier( quants[q] ); + } + } + } +} + +void QuantifiersEngine::registerPattern( std::vector<Node> & pattern) { + for(std::vector<Node>::iterator p = pattern.begin(); p != pattern.end(); ++p){ + std::vector< Node > added; + d_term_db->addTerm(*p,added); + } +} + +void QuantifiersEngine::assertNode( Node f ){ + Assert( f.getKind()==FORALL ); + for( int j=0; j<(int)d_quant_rewritten[f].size(); j++ ){ + d_forall_asserts.push_back( d_quant_rewritten[f][j] ); + for( int i=0; i<(int)d_modules.size(); i++ ){ + d_modules[i]->assertNode( d_quant_rewritten[f][j] ); + } + } +} + +void QuantifiersEngine::propagate( Theory::Effort level ){ + CodeTimer codeTimer(d_time); + + for( int i=0; i<(int)d_modules.size(); i++ ){ + d_modules[i]->propagate( level ); + } +} + +void QuantifiersEngine::addTermToDatabase( Node n, bool withinQuant ){ + if( d_term_db ){ + std::vector< Node > added; + d_term_db->addTerm( n, added, withinQuant ); +#ifdef COMPUTE_RELEVANCE + for( int i=0; i<(int)added.size(); i++ ){ + if( !withinQuant ){ + setRelevance( added[i].getOperator(), 0 ); + } + } +#endif + }else{ + Notice() << "Warning: no term database for quantifier engine." << std::endl; + } +} + +bool QuantifiersEngine::addLemma( Node lem ){ + //AJR: the following check is necessary until FULL_CHECK is guarenteed after d_out->lemma(...) + Debug("inst-engine-debug") << "Adding lemma : " << lem << std::endl; + lem = Rewriter::rewrite(lem); + if( d_lemmas_produced.find( lem )==d_lemmas_produced.end() ){ + //d_curr_out->lemma( lem ); + d_lemmas_produced[ lem ] = true; + d_lemmas_waiting.push_back( lem ); + Debug("inst-engine-debug") << "Added lemma : " << lem << std::endl; + return true; + }else{ + Debug("inst-engine-debug") << "Duplicate." << std::endl; + return false; + } +} + +bool QuantifiersEngine::addInstantiation( Node f, std::vector< Node >& terms ) +{ + //Notice() << "***& Instantiate " << f << " with " << std::endl; + //for( int i=0; i<(int)terms.size(); i++ ){ + // Notice() << " " << terms[i] << std::endl; + //} + Assert( f.getKind()==FORALL ); + Assert( !f.hasAttribute(InstConstantAttribute()) ); + Assert( d_vars[f].size()==terms.size() && d_vars[f].size()==f[0].getNumChildren() ); + Node body = f[ 1 ].substitute( d_vars[f].begin(), d_vars[f].end(), + terms.begin(), terms.end() ); + NodeBuilder<> nb(kind::OR); + nb << d_rewritten_quant[f].notNode() << body; + Node lem = nb; + if( addLemma( lem ) ){ + //Notice() << " Added lemma : " << body << std::endl; + //Notice() << "***& Instantiate " << f << " with " << std::endl; + //for( int i=0; i<(int)terms.size(); i++ ){ + // Notice() << " " << terms[i] << std::endl; + //} + + //Notice() << "**INST" << std::endl; + Debug("inst") << "*** Instantiate " << f << " with " << std::endl; + //Notice() << "*** Instantiate " << f << " with " << std::endl; + uint64_t maxInstLevel = 0; + for( int i=0; i<(int)terms.size(); i++ ){ + if( terms[i].hasAttribute(InstConstantAttribute()) ){ + Debug("inst")<< "***& Bad Instantiate " << f << " with " << std::endl; + for( int i=0; i<(int)terms.size(); i++ ){ + Debug("inst") << " " << terms[i] << std::endl; + } + Unreachable("Bad instantiation"); + }else{ + Debug("inst") << " " << terms[i]; + //Notice() << " " << terms[i] << std::endl; + //Debug("inst-engine") << " " << terms[i].getAttribute(InstLevelAttribute()); + Debug("inst") << std::endl; + if( terms[i].hasAttribute(InstLevelAttribute()) ){ + if( terms[i].getAttribute(InstLevelAttribute())>maxInstLevel ){ + maxInstLevel = terms[i].getAttribute(InstLevelAttribute()); + } + }else{ + setInstantiationLevelAttr( terms[i], 0 ); + } + } + } + setInstantiationLevelAttr( body, maxInstLevel+1 ); + ++(d_statistics.d_instantiations); + d_statistics.d_total_inst_var += (int)terms.size(); + d_statistics.d_max_instantiation_level.maxAssign( maxInstLevel+1 ); + return true; + }else{ + ++(d_statistics.d_inst_duplicate); + return false; + } +} + +bool QuantifiersEngine::addInstantiation( Node f, InstMatch& m, bool addSplits ){ + m.makeComplete( f, this ); + m.makeRepresentative( this ); + Debug("quant-duplicate") << "After make rep: " << m << std::endl; + if( !d_inst_match_trie[f].addInstMatch( this, f, m, true ) ){ + Debug("quant-duplicate") << " -> Already exists." << std::endl; + ++(d_statistics.d_inst_duplicate); + return false; + } + Debug("quant-duplicate") << " -> Does not exist." << std::endl; + std::vector< Node > match; + m.computeTermVec( d_inst_constants[f], match ); + + //old.... + //m.makeRepresentative( d_eq_query ); + //std::vector< Node > match; + //m.computeTermVec( this, d_inst_constants[f], match ); + + //Notice() << "*** Instantiate " << m->getQuantifier() << " with " << std::endl; + //for( int i=0; i<(int)m->d_match.size(); i++ ){ + // Notice() << " " << m->d_match[i] << std::endl; + //} + + if( addInstantiation( f, match ) ){ + //d_statistics.d_total_inst_var_unspec.setData( d_statistics.d_total_inst_var_unspec.getData() + (int)d_inst_constants[f].size() - m.d_map.size()/2 ); + //if( d_inst_constants[f].size()!=m.d_map.size() ){ + // //Notice() << "Unspec. " << std::endl; + // //Notice() << "*** Instantiate " << m->getQuantifier() << " with " << std::endl; + // //for( int i=0; i<(int)m->d_match.size(); i++ ){ + // // Notice() << " " << m->d_match[i] << std::endl; + // //} + // ++(d_statistics.d_inst_unspec); + //} + //if( addSplits ){ + // for( std::map< Node, Node >::iterator it = m->d_splits.begin(); it != m->d_splits.end(); ++it ){ + // addSplitEquality( it->first, it->second, true, true ); + // } + //} + return true; + } + return false; +} + +bool QuantifiersEngine::addSplit( Node n, bool reqPhase, bool reqPhasePol ){ + n = Rewriter::rewrite( n ); + Node lem = NodeManager::currentNM()->mkNode( OR, n, n.notNode() ); + if( addLemma( lem ) ){ + ++(d_statistics.d_splits); + Debug("inst") << "*** Add split " << n<< std::endl; + //if( reqPhase ){ + // d_curr_out->requirePhase( n, reqPhasePol ); + //} + return true; + } + return false; +} + +bool QuantifiersEngine::addSplitEquality( Node n1, Node n2, bool reqPhase, bool reqPhasePol ){ + //Assert( !n1.hasAttribute(InstConstantAttribute()) ); + //Assert( !n2.hasAttribute(InstConstantAttribute()) ); + //Assert( !areEqual( n1, n2 ) ); + //Assert( !areDisequal( n1, n2 ) ); + Kind knd = n1.getType()==NodeManager::currentNM()->booleanType() ? IFF : EQUAL; + Node fm = NodeManager::currentNM()->mkNode( knd, n1, n2 ); + return addSplit( fm ); +} + +void QuantifiersEngine::flushLemmas( OutputChannel* out ){ + for( int i=0; i<(int)d_lemmas_waiting.size(); i++ ){ + out->lemma( d_lemmas_waiting[i] ); + } + d_lemmas_waiting.clear(); +} + +Node QuantifiersEngine::getCounterexampleBody( Node f ){ + std::map< Node, Node >::iterator it = d_counterexample_body.find( f ); + if( it==d_counterexample_body.end() ){ + makeInstantiationConstantsFor( f ); + Node n = getSubstitutedNode( f[1], f ); + d_counterexample_body[ f ] = n; + return n; + }else{ + return it->second; + } +} + +Node QuantifiersEngine::getSkolemizedBody( Node f ){ + Assert( f.getKind()==FORALL ); + if( d_skolem_body.find( f )==d_skolem_body.end() ){ + std::vector< Node > vars; + for( int i=0; i<(int)f[0].getNumChildren(); i++ ){ + Node skv = NodeManager::currentNM()->mkSkolem( f[0][i].getType() ); + d_skolem_constants[ f ].push_back( skv ); + vars.push_back( f[0][i] ); + } + d_skolem_body[ f ] = f[ 1 ].substitute( vars.begin(), vars.end(), + d_skolem_constants[ f ].begin(), d_skolem_constants[ f ].end() ); + if( f.hasAttribute(InstLevelAttribute()) ){ + setInstantiationLevelAttr( d_skolem_body[ f ], f.getAttribute(InstLevelAttribute()) ); + } + } + return d_skolem_body[ f ]; +} + +void QuantifiersEngine::getPhaseReqTerms( Node f, std::vector< Node >& nodes ){ + if( Options::current()->literalMatchMode!=Options::LITERAL_MATCH_NONE ){ + bool printed = false; + // doing literal-based matching (consider polarity of literals) + for( int i=0; i<(int)nodes.size(); i++ ){ + Node prev = nodes[i]; + bool nodeChanged = false; + if( isPhaseReq( f, nodes[i] ) ){ + bool preq = getPhaseReq( f, nodes[i] ); + nodes[i] = NodeManager::currentNM()->mkNode( IFF, nodes[i], NodeManager::currentNM()->mkConst<bool>(preq) ); + nodeChanged = true; + } + //else if( qe->isPhaseReqEquality( f, trNodes[i] ) ){ + // Node req = qe->getPhaseReqEquality( f, trNodes[i] ); + // trNodes[i] = NodeManager::currentNM()->mkNode( EQUAL, trNodes[i], req ); + //} + if( nodeChanged ){ + if( !printed ){ + Debug("literal-matching") << "LitMatch for " << f << ":" << std::endl; + printed = true; + } + Debug("literal-matching") << " Make " << prev << " -> " << nodes[i] << std::endl; + Assert( prev.hasAttribute(InstConstantAttribute()) ); + setInstantiationConstantAttr( nodes[i], prev.getAttribute(InstConstantAttribute()) ); + ++(d_statistics.d_lit_phase_req); + }else{ + ++(d_statistics.d_lit_phase_nreq); + } + } + }else{ + d_statistics.d_lit_phase_nreq += (int)nodes.size(); + } +} + +void QuantifiersEngine::computePhaseReqs2( Node n, bool polarity, std::map< Node, int >& phaseReqs ){ + bool newReqPol = false; + bool newPolarity; + if( n.getKind()==NOT ){ + newReqPol = true; + newPolarity = !polarity; + }else if( n.getKind()==OR || n.getKind()==IMPLIES ){ + if( !polarity ){ + newReqPol = true; + newPolarity = false; + } + }else if( n.getKind()==AND ){ + if( polarity ){ + newReqPol = true; + newPolarity = true; + } + }else{ + int val = polarity ? 1 : -1; + if( phaseReqs.find( n )==phaseReqs.end() ){ + phaseReqs[n] = val; + }else if( val!=phaseReqs[n] ){ + phaseReqs[n] = 0; + } + } + if( newReqPol ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( n.getKind()==IMPLIES && i==0 ){ + computePhaseReqs2( n[i], !newPolarity, phaseReqs ); + }else{ + computePhaseReqs2( n[i], newPolarity, phaseReqs ); + } + } + } +} + +void QuantifiersEngine::computePhaseReqs( Node n, bool polarity, std::map< Node, bool >& phaseReqs ){ + std::map< Node, int > phaseReqs2; + computePhaseReqs2( n, polarity, phaseReqs2 ); + for( std::map< Node, int >::iterator it = phaseReqs2.begin(); it != phaseReqs2.end(); ++it ){ + if( it->second==1 ){ + phaseReqs[ it->first ] = true; + }else if( it->second==-1 ){ + phaseReqs[ it->first ] = false; + } + } +} + +void QuantifiersEngine::generatePhaseReqs( Node f, Node n ){ + computePhaseReqs( n, false, d_phase_reqs[f] ); + Debug("inst-engine-phase-req") << "Phase requirements for " << f << ":" << std::endl; + //now, compute if any patterns are equality required + for( std::map< Node, bool >::iterator it = d_phase_reqs[f].begin(); it != d_phase_reqs[f].end(); ++it ){ + Debug("inst-engine-phase-req") << " " << it->first << " -> " << it->second << std::endl; + if( it->first.getKind()==EQUAL ){ + if( it->first[0].hasAttribute(InstConstantAttribute()) ){ + if( !it->first[1].hasAttribute(InstConstantAttribute()) ){ + d_phase_reqs_equality_term[f][ it->first[0] ] = it->first[1]; + d_phase_reqs_equality[f][ it->first[0] ] = it->second; + Debug("inst-engine-phase-req") << " " << it->first[0] << ( it->second ? " == " : " != " ) << it->first[1] << std::endl; + } + }else if( it->first[1].hasAttribute(InstConstantAttribute()) ){ + d_phase_reqs_equality_term[f][ it->first[1] ] = it->first[0]; + d_phase_reqs_equality[f][ it->first[1] ] = it->second; + Debug("inst-engine-phase-req") << " " << it->first[1] << ( it->second ? " == " : " != " ) << it->first[0] << std::endl; + } + } + } + +} + +Node QuantifiersEngine::getSubstitutedNode( Node n, Node f ){ + return convertNodeToPattern(n,f,d_vars[f],d_inst_constants[ f ]); +} + +Node QuantifiersEngine::convertNodeToPattern( Node n, Node f, const std::vector<Node> & vars, + const std::vector<Node> & inst_constants){ + Node n2 = n.substitute( vars.begin(), vars.end(), + inst_constants.begin(), + inst_constants.end() ); + setInstantiationConstantAttr( n2, f ); + return n2; +} + + +void QuantifiersEngine::setInstantiationLevelAttr( Node n, uint64_t level ){ + if( !n.hasAttribute(InstLevelAttribute()) ){ + InstLevelAttribute ila; + n.setAttribute(ila,level); + } + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + setInstantiationLevelAttr( n[i], level ); + } +} + + +void QuantifiersEngine::setInstantiationConstantAttr( Node n, Node f ){ + if( !n.hasAttribute(InstConstantAttribute()) ){ + bool setAttr = false; + if( n.getKind()==INST_CONSTANT ){ + setAttr = true; + }else{ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + setInstantiationConstantAttr( n[i], f ); + if( n[i].hasAttribute(InstConstantAttribute()) ){ + setAttr = true; + } + } + } + if( setAttr ){ + InstConstantAttribute ica; + n.setAttribute(ica,f); + //also set the no-match attribute + NoMatchAttribute nma; + n.setAttribute(nma,true); + } + } +} + +QuantifiersEngine::Statistics::Statistics(): + d_num_quant("QuantifiersEngine::Num_Quantifiers", 0), + d_instantiation_rounds("QuantifiersEngine::Rounds_Instantiation_Full", 0), + d_instantiation_rounds_lc("QuantifiersEngine::Rounds_Instantiation_Last_Call", 0), + d_instantiations("QuantifiersEngine::Instantiations_Total", 0), + d_max_instantiation_level("QuantifiersEngine::Max_Instantiation_Level", 0), + d_splits("QuantifiersEngine::Total_Splits", 0), + d_total_inst_var("QuantifiersEngine::Vars_Inst", 0), + d_total_inst_var_unspec("QuantifiersEngine::Vars_Inst_Unspecified", 0), + d_inst_unspec("QuantifiersEngine::Unspecified_Inst", 0), + d_inst_duplicate("QuantifiersEngine::Duplicate_Inst", 0), + d_lit_phase_req("QuantifiersEngine::lit_phase_req", 0), + d_lit_phase_nreq("QuantifiersEngine::lit_phase_nreq", 0), + d_triggers("QuantifiersEngine::Triggers", 0), + d_simple_triggers("QuantifiersEngine::Triggers_Simple", 0), + d_multi_triggers("QuantifiersEngine::Triggers_Multi", 0), + d_multi_trigger_instantiations("QuantifiersEngine::Multi_Trigger_Instantiations", 0) +{ + StatisticsRegistry::registerStat(&d_num_quant); + StatisticsRegistry::registerStat(&d_instantiation_rounds); + StatisticsRegistry::registerStat(&d_instantiation_rounds_lc); + StatisticsRegistry::registerStat(&d_instantiations); + StatisticsRegistry::registerStat(&d_max_instantiation_level); + StatisticsRegistry::registerStat(&d_splits); + StatisticsRegistry::registerStat(&d_total_inst_var); + StatisticsRegistry::registerStat(&d_total_inst_var_unspec); + StatisticsRegistry::registerStat(&d_inst_unspec); + StatisticsRegistry::registerStat(&d_inst_duplicate); + StatisticsRegistry::registerStat(&d_lit_phase_req); + StatisticsRegistry::registerStat(&d_lit_phase_nreq); + StatisticsRegistry::registerStat(&d_triggers); + StatisticsRegistry::registerStat(&d_simple_triggers); + StatisticsRegistry::registerStat(&d_multi_triggers); + StatisticsRegistry::registerStat(&d_multi_trigger_instantiations); +} + +QuantifiersEngine::Statistics::~Statistics(){ + StatisticsRegistry::unregisterStat(&d_num_quant); + StatisticsRegistry::unregisterStat(&d_instantiation_rounds); + StatisticsRegistry::unregisterStat(&d_instantiation_rounds_lc); + StatisticsRegistry::unregisterStat(&d_instantiations); + StatisticsRegistry::unregisterStat(&d_max_instantiation_level); + StatisticsRegistry::unregisterStat(&d_splits); + StatisticsRegistry::unregisterStat(&d_total_inst_var); + StatisticsRegistry::unregisterStat(&d_total_inst_var_unspec); + StatisticsRegistry::unregisterStat(&d_inst_unspec); + StatisticsRegistry::unregisterStat(&d_inst_duplicate); + StatisticsRegistry::unregisterStat(&d_lit_phase_req); + StatisticsRegistry::unregisterStat(&d_lit_phase_nreq); + StatisticsRegistry::unregisterStat(&d_triggers); + StatisticsRegistry::unregisterStat(&d_simple_triggers); + StatisticsRegistry::unregisterStat(&d_multi_triggers); + StatisticsRegistry::unregisterStat(&d_multi_trigger_instantiations); +} + +Node QuantifiersEngine::getFreeVariableForInstConstant( Node n ){ + TypeNode tn = n.getType(); + if( d_free_vars.find( tn )==d_free_vars.end() ){ + //if integer or real, make zero + if( tn==NodeManager::currentNM()->integerType() || tn==NodeManager::currentNM()->realType() ){ + Rational z(0); + d_free_vars[tn] = NodeManager::currentNM()->mkConst( z ); + }else{ + if( d_term_db->d_type_map[ tn ].empty() ){ + d_free_vars[tn] = NodeManager::currentNM()->mkVar( tn ); + }else{ + d_free_vars[tn] =d_term_db->d_type_map[ tn ][ 0 ]; + } + } + } + return d_free_vars[tn]; +} + +/** compute symbols */ +void QuantifiersEngine::computeSymbols( Node n, std::vector< Node >& syms ){ + if( n.getKind()==APPLY_UF ){ + Node op = n.getOperator(); + if( std::find( syms.begin(), syms.end(), op )==syms.end() ){ + syms.push_back( op ); + } + } + if( n.getKind()!=FORALL ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + computeSymbols( n[i], syms ); + } + } +} + +/** set relevance */ +void QuantifiersEngine::setRelevance( Node s, int r ){ + int rOld = getRelevance( s ); + if( rOld==-1 || r<rOld ){ + d_relevance[s] = r; + if( s.getKind()==FORALL ){ + for( int i=0; i<(int)d_syms[s].size(); i++ ){ + setRelevance( d_syms[s][i], r ); + } + }else{ + for( int i=0; i<(int)d_syms_quants[s].size(); i++ ){ + setRelevance( d_syms_quants[s][i], r+1 ); + } + } + } +} diff --git a/src/theory/quantifiers_engine.h b/src/theory/quantifiers_engine.h new file mode 100644 index 000000000..d0c5fb00b --- /dev/null +++ b/src/theory/quantifiers_engine.h @@ -0,0 +1,388 @@ +/********************* */ +/*! \file quantifiers_engine.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Theory instantiator, Instantiation Engine classes + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__QUANTIFIERS_ENGINE_H +#define __CVC4__THEORY__QUANTIFIERS_ENGINE_H + +#include "theory/theory.h" +#include "util/hash.h" +#include "theory/trigger.h" + +#include "util/stats.h" + +#include <ext/hash_set> +#include <iostream> +#include <map> + +namespace CVC4 { + +class TheoryEngine; + +// attribute for "contains instantiation constants from" +struct InstConstantAttributeId {}; +typedef expr::Attribute<InstConstantAttributeId, Node> InstConstantAttribute; + +struct InstLevelAttributeId {}; +typedef expr::Attribute<InstLevelAttributeId, uint64_t> InstLevelAttribute; + +struct InstVarNumAttributeId {}; +typedef expr::Attribute<InstVarNumAttributeId, uint64_t> InstVarNumAttribute; + +namespace theory { + +class QuantifiersEngine; + +class InstStrategy { +public: + enum Status { + STATUS_UNFINISHED, + STATUS_UNKNOWN, + STATUS_SAT, + };/* enum Status */ +protected: + /** reference to the instantiation engine */ + QuantifiersEngine* d_quantEngine; +protected: + /** giving priorities */ + std::vector< InstStrategy* > d_priority_over; + /** do not instantiate list */ + std::vector< Node > d_no_instantiate; + std::vector< Node > d_no_instantiate_temp; + /** reset instantiation */ + virtual void processResetInstantiationRound( Theory::Effort effort ) = 0; + /** process method */ + virtual int process( Node f, Theory::Effort effort, int e, int limitInst = 0 ) = 0; +public: + InstStrategy( QuantifiersEngine* ie ) : d_quantEngine( ie ){} + virtual ~InstStrategy(){} + + /** reset instantiation */ + void resetInstantiationRound( Theory::Effort effort ); + /** do instantiation round method */ + int doInstantiation( Node f, Theory::Effort effort, int e, int limitInst = 0 ); + /** update status */ + static void updateStatus( int& currStatus, int addStatus ){ + if( addStatus==STATUS_UNFINISHED ){ + currStatus = STATUS_UNFINISHED; + }else if( addStatus==STATUS_UNKNOWN ){ + if( currStatus==STATUS_SAT ){ + currStatus = STATUS_UNKNOWN; + } + } + } + /** identify */ + virtual std::string identify() const { return std::string("Unknown"); } +public: + /** set priority */ + void setPriorityOver( InstStrategy* is ) { d_priority_over.push_back( is ); } + /** set no instantiate */ + void setNoInstantiate( Node n ) { d_no_instantiate.push_back( n ); } + /** should instantiate */ + bool shouldInstantiate( Node n ) { + return std::find( d_no_instantiate_temp.begin(), d_no_instantiate_temp.end(), n )==d_no_instantiate_temp.end(); + } +};/* class InstStrategy */ + +class QuantifiersModule { +public: + QuantifiersModule(){} + ~QuantifiersModule(){} + /* Call during check registerQuantifier has already been called */ + virtual void check( Theory::Effort e ) = 0; + /* Called for new quantifiers */ + virtual void registerQuantifier( Node n ) = 0; + virtual void assertNode( Node n ) = 0; + virtual void propagate( Theory::Effort level ) = 0; + virtual Node explain(TNode n) = 0; +};/* class QuantifiersModule */ + +class TermArgTrie { +private: + bool addTerm2( QuantifiersEngine* qe, Node n, int argIndex ); +public: + /** the data */ + std::map< Node, TermArgTrie > d_data; +public: + bool addTerm( QuantifiersEngine* qe, Node n ) { return addTerm2( qe, n, 0 ); } +};/* class TermArgTrie */ + +class TermDb { +private: + /** reference to the quantifiers engine */ + QuantifiersEngine* d_quantEngine; + /** calculated no match terms */ + bool d_matching_active; + /** terms processed */ + std::map< Node, bool > d_processed; +public: + TermDb( QuantifiersEngine* qe ) : d_quantEngine( qe ), d_matching_active( true ){} + ~TermDb(){} + /** map from APPLY_UF operators to ground terms for that operator */ + std::map< Node, std::vector< Node > > d_op_map; + /** map from APPLY_UF functions to trie */ + std::map< Node, TermArgTrie > d_func_map_trie; + /** map from APPLY_UF predicates to trie */ + std::map< Node, TermArgTrie > d_pred_map_trie[2]; + /** map from type nodes to terms of that type */ + std::map< TypeNode, std::vector< Node > > d_type_map; + /** add a term to the database */ + void addTerm( Node n, std::vector< Node >& added, bool withinQuant = false ); + /** reset (calculate which terms are active) */ + void reset( Theory::Effort effort ); + /** set active */ + void setMatchingActive( bool a ) { d_matching_active = a; } + /** get active */ + bool getMatchingActive() { return d_matching_active; } +public: + /** parent structure (for efficient E-matching): + n -> op -> index -> L + map from node "n" to a list of nodes "L", where each node n' in L + has operator "op", and n'["index"] = n. + for example, d_parents[n][f][1] = { f( t1, n ), f( t2, n ), ... } + */ + std::map< Node, std::map< Node, std::map< int, std::vector< Node > > > > d_parents; +};/* class TermDb */ + +namespace quantifiers { + class InstantiationEngine; +}/* CVC4::theory::quantifiers */ + +class QuantifiersEngine { + friend class quantifiers::InstantiationEngine; + friend class InstMatch; +private: + typedef context::CDHashMap< Node, bool, NodeHashFunction > BoolMap; + /** reference to theory engine object */ + TheoryEngine* d_te; + /** vector of modules for quantifiers */ + std::vector< QuantifiersModule* > d_modules; + /** equality query class */ + EqualityQuery* d_eq_query; + + /** list of all quantifiers */ + std::vector< Node > d_quants; + /** list of quantifiers asserted in the current context */ + context::CDList<Node> d_forall_asserts; + /** map from universal quantifiers to the list of variables */ + std::map< Node, std::vector< Node > > d_vars; + /** map from universal quantifiers to the list of skolem constants */ + std::map< Node, std::vector< Node > > d_skolem_constants; + /** map from universal quantifiers to their skolemized body */ + std::map< Node, Node > d_skolem_body; + /** map from universal quantifiers to their bound body */ + std::map< Node, Node > d_bound_body; + /** instantiation constants to universal quantifiers */ + std::map< Node, Node > d_inst_constants_map; + /** map from universal quantifiers to their counterexample body */ + std::map< Node, Node > d_counterexample_body; + /** map from universal quantifiers to the list of instantiation constants */ + std::map< Node, std::vector< Node > > d_inst_constants; + /** map from quantifiers to whether they are active */ + BoolMap d_active; + /** lemmas produced */ + std::map< Node, bool > d_lemmas_produced; + /** lemmas waiting */ + std::vector< Node > d_lemmas_waiting; + /** inst matches produced for each quantifier */ + std::map< Node, InstMatchTrie > d_inst_match_trie; + /** free variable for instantiation constant type */ + std::map< TypeNode, Node > d_free_vars; + /** owner of quantifiers */ + std::map< Node, Theory* > d_owner; + /** term database */ + TermDb* d_term_db; + /** universal quantifiers that have been rewritten */ + std::map< Node, std::vector< Node > > d_quant_rewritten; + /** map from rewritten universal quantifiers to the quantifier they are the consequence of */ + std::map< Node, Node > d_rewritten_quant; +private: + /** for computing relavance */ + /** map from quantifiers to symbols they contain */ + std::map< Node, std::vector< Node > > d_syms; + /** map from symbols to quantifiers */ + std::map< Node, std::vector< Node > > d_syms_quants; + /** relevance for quantifiers and symbols */ + std::map< Node, int > d_relevance; + /** compute symbols */ + void computeSymbols( Node n, std::vector< Node >& syms ); +private: + /** helper functions compute phase requirements */ + static void computePhaseReqs2( Node n, bool polarity, std::map< Node, int >& phaseReqs ); + /** set instantiation level attr */ + void setInstantiationLevelAttr( Node n, uint64_t level ); + /** set instantiation constant attr */ + void setInstantiationConstantAttr( Node n, Node f ); + /** make instantiation constants for */ + void makeInstantiationConstantsFor( Node f ); + + KEEP_STATISTIC(TimerStat, d_time, "theory::QuantifiersEngine::time"); + +public: + QuantifiersEngine(context::Context* c, TheoryEngine* te); + ~QuantifiersEngine(){} + /** get instantiator for id */ + Instantiator* getInstantiator( int id ); + /** get theory engine */ + TheoryEngine* getTheoryEngine() { return d_te; } + /** get equality query object */ + EqualityQuery* getEqualityQuery() { return d_eq_query; } + /** set equality query object */ + void setEqualityQuery( EqualityQuery* eq ) { d_eq_query = eq; } +public: + /** add module */ + void addModule( QuantifiersModule* qm ) { d_modules.push_back( qm ); } + /** check at level */ + void check( Theory::Effort e ); + /** register (non-rewritten) quantifier */ + void registerQuantifier( Node f ); + /** register (non-rewritten) quantifier */ + void registerPattern( std::vector<Node> & pattern); + /** assert (universal) quantifier */ + void assertNode( Node f ); + /** propagate */ + void propagate( Theory::Effort level ); +public: + /** add lemma lem */ + bool addLemma( Node lem ); + /** instantiate f with arguments terms */ + bool addInstantiation( Node f, std::vector< Node >& terms ); + /** do instantiation specified by m */ + bool addInstantiation( Node f, InstMatch& m, bool addSplits = false ); + /** split on node n */ + bool addSplit( Node n, bool reqPhase = false, bool reqPhasePol = true ); + /** add split equality */ + bool addSplitEquality( Node n1, Node n2, bool reqPhase = false, bool reqPhasePol = true ); + /** has added lemma */ + bool hasAddedLemma() { return !d_lemmas_waiting.empty(); } + /** flush lemmas */ + void flushLemmas( OutputChannel* out ); + /** get number of waiting lemmas */ + int getNumLemmasWaiting() { return (int)d_lemmas_waiting.size(); } +public: + /** get number of quantifiers */ + int getNumQuantifiers() { return (int)d_quants.size(); } + /** get quantifier */ + Node getQuantifier( int i ) { return d_quants[i]; } + /** get number of asserted quantifiers */ + int getNumAssertedQuantifiers() { return (int)d_forall_asserts.size(); } + /** get asserted quantifier */ + Node getAssertedQuantifier( int i ) { return d_forall_asserts[i]; } + /** get instantiation constants */ + void getInstantiationConstantsFor( Node f, std::vector< Node >& ics ) { + ics.insert( ics.begin(), d_inst_constants[f].begin(), d_inst_constants[f].end() ); + } + /** get the i^th instantiation constant of f */ + Node getInstantiationConstant( Node f, int i ) { return d_inst_constants[f][i]; } + /** get number of instantiation constants for f */ + int getNumInstantiationConstants( Node f ) { return (int)d_inst_constants[f].size(); } + std::vector<Node> createInstVariable( std::vector<Node> & vars ); +public: + /** get the ce body f[e/x] */ + Node getCounterexampleBody( Node f ); + /** get the skolemized body f[e/x] */ + Node getSkolemizedBody( Node f ); + /** set active */ + void setActive( Node n, bool val ) { d_active[n] = val; } + /** get active */ + bool getActive( Node n ) { return d_active.find( n )!=d_active.end() && d_active[n]; } +public: + /** phase requirements for each quantifier for each instantiation literal */ + std::map< Node, std::map< Node, bool > > d_phase_reqs; + std::map< Node, std::map< Node, bool > > d_phase_reqs_equality; + std::map< Node, std::map< Node, Node > > d_phase_reqs_equality_term; +public: + /** is phase required */ + bool isPhaseReq( Node f, Node lit ) { return d_phase_reqs[f].find( lit )!=d_phase_reqs[f].end(); } + /** get phase requirement */ + bool getPhaseReq( Node f, Node lit ) { return d_phase_reqs[f].find( lit )==d_phase_reqs[f].end() ? false : d_phase_reqs[f][ lit ]; } + /** get term req terms */ + void getPhaseReqTerms( Node f, std::vector< Node >& nodes ); + /** helper functions compute phase requirements */ + static void computePhaseReqs( Node n, bool polarity, std::map< Node, bool >& phaseReqs ); + /** compute phase requirements */ + void generatePhaseReqs( Node f, Node n ); +public: + /** returns node n with bound vars of f replaced by instantiation constants of f + node n : is the futur pattern + node f : is the quantifier containing which bind the variable + return a pattern where the variable are replaced by variable for + instantiation. + */ + Node getSubstitutedNode( Node n, Node f ); + /** same as before but node f is just linked to the new pattern by the + applied attribute + vars the bind variable + nvars the same variable but with an attribute + */ + Node convertNodeToPattern( Node n, Node f, + const std::vector<Node> & vars, + const std::vector<Node> & nvars); + /** get free variable for instantiation constant */ + Node getFreeVariableForInstConstant( Node n ); + /** get bound variable for variable */ + Node getBoundVariableForVariable( Node n ); +public: + /** has owner */ + bool hasOwner( Node f ) { return d_owner.find( f )!=d_owner.end(); } + /** get owner */ + Theory* getOwner( Node f ) { return d_owner[f]; } + /** set owner */ + void setOwner( Node f, Theory* t ) { d_owner[f] = t; } +public: + /** get term database */ + TermDb* getTermDatabase() { return d_term_db; } + /** add term to database */ + void addTermToDatabase( Node n, bool withinQuant = false ); +private: + /** set relevance */ + void setRelevance( Node s, int r ); +public: + /** get relevance */ + int getRelevance( Node s ) { return d_relevance.find( s )==d_relevance.end() ? -1 : d_relevance[s]; } + /** get number of quantifiers for symbol s */ + int getNumQuantifiersForSymbol( Node s ) { return (int)d_syms_quants[s].size(); } +public: + /** statistics class */ + class Statistics { + public: + IntStat d_num_quant; + IntStat d_instantiation_rounds; + IntStat d_instantiation_rounds_lc; + IntStat d_instantiations; + IntStat d_max_instantiation_level; + IntStat d_splits; + IntStat d_total_inst_var; + IntStat d_total_inst_var_unspec; + IntStat d_inst_unspec; + IntStat d_inst_duplicate; + IntStat d_lit_phase_req; + IntStat d_lit_phase_nreq; + IntStat d_triggers; + IntStat d_simple_triggers; + IntStat d_multi_triggers; + IntStat d_multi_trigger_instantiations; + Statistics(); + ~Statistics(); + };/* class QuantifiersEngine::Statistics */ + Statistics d_statistics; +};/* class QuantifiersEngine */ + +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__QUANTIFIERS_ENGINE_H */ diff --git a/src/theory/rewriterules/Makefile b/src/theory/rewriterules/Makefile new file mode 100644 index 000000000..4b1d4fc55 --- /dev/null +++ b/src/theory/rewriterules/Makefile @@ -0,0 +1,4 @@ +topdir = ../../.. +srcdir = src/theory/rewriterules + +include $(topdir)/Makefile.subdir diff --git a/src/theory/rewriterules/Makefile.am b/src/theory/rewriterules/Makefile.am new file mode 100644 index 000000000..46cffda11 --- /dev/null +++ b/src/theory/rewriterules/Makefile.am @@ -0,0 +1,19 @@ +AM_CPPFLAGS = \ + -D__BUILDING_CVC4LIB \ + -I@srcdir@/../../include -I@srcdir@/../.. -I@builddir@/../.. +AM_CXXFLAGS = -Wall -Wno-unknown-pragmas $(FLAG_VISIBILITY_HIDDEN) + +noinst_LTLIBRARIES = librewriterules.la + +librewriterules_la_SOURCES = \ + theory_rewriterules_rules.h \ + theory_rewriterules_rules.cpp \ + theory_rewriterules.h \ + theory_rewriterules.cpp \ + theory_rewriterules_rewriter.h \ + theory_rewriterules_type_rules.h \ + theory_rewriterules_preprocess.h \ + theory_rewriterules_params.h + +EXTRA_DIST = \ + kinds diff --git a/src/theory/rewriterules/README.WHATS-NEXT b/src/theory/rewriterules/README.WHATS-NEXT new file mode 100644 index 000000000..eda7dcbe6 --- /dev/null +++ b/src/theory/rewriterules/README.WHATS-NEXT @@ -0,0 +1,29 @@ +Congratulations, you now have a new theory of rewriterules ! + +Your next steps will likely be: + +* to specify theory constants, types, and operators in your \`kinds' file +* to add typing rules to theory_${dir}_type_rules.h for your operators + and constants +* to write code in theory_${dir}_rewriter.h to implement a normal form + for your theory's terms +* to write parser rules in src/parser/cvc/Cvc.g to support the CVC input + language, src/parser/smt/Smt.g to support the (deprecated) SMT-LIBv1 + language, and src/parser/smt2/Smt2.g to support SMT-LIBv2 +* to write printer code in src/printer/*/*_printer* to support printing + your theory terms and types in various output languages + +and finally: + +* to implement a decision procedure for your theory by implementing + TheoryRewriterules::check() in theory_rewriterules.cpp. Before writing the actual + code, you will need : + + * to determine which datastructures are context dependent and use for them + context dependent datastructures (context/cd*.h) + * to choose which work will be done at QUICK_CHECK, STANDARD or at + FULL_EFFORT. + + +Good luck, and please contact cvc4-devel@cs.nyu.edu for assistance +should you need it! diff --git a/src/theory/rewriterules/kinds b/src/theory/rewriterules/kinds new file mode 100644 index 000000000..01fbda51e --- /dev/null +++ b/src/theory/rewriterules/kinds @@ -0,0 +1,37 @@ +# kinds -*- sh -*- +# +# For documentation on this file format, please refer to +# src/theory/builtin/kinds. +# + +theory THEORY_REWRITERULES ::CVC4::theory::rewriterules::TheoryRewriteRules "theory/rewriterules/theory_rewriterules.h" +typechecker "theory/rewriterules/theory_rewriterules_type_rules.h" +rewriter ::CVC4::theory::rewriterules::TheoryRewriterulesRewriter "theory/rewriterules/theory_rewriterules_rewriter.h" + +properties check + +# Theory content goes here. + +# constants... + +# types... +sort RRHB_TYPE \ + Cardinality::INTEGERS \ + not-well-founded \ + "head and body of the rule type" + +# operators... + +# variables, guards, RR_REWRITE/REDUCTION_RULE/DEDUCTION_RULE +operator REWRITE_RULE 3 "generale rewrite rule" +#HEAD/BODY/TRIGGER +operator RR_REWRITE 2:3 "actual rewrite rule" +operator RR_REDUCTION 2:3 "actual reduction rule" +operator RR_DEDUCTION 2:3 "actual deduction rule" + +typerule REWRITE_RULE ::CVC4::theory::rewriterules::RewriteRuleTypeRule +typerule RR_REWRITE ::CVC4::theory::rewriterules::RRRewriteTypeRule +typerule RR_REDUCTION ::CVC4::theory::rewriterules::RRRedDedTypeRule +typerule RR_DEDUCTION ::CVC4::theory::rewriterules::RRRedDedTypeRule + +endtheory diff --git a/src/theory/rewriterules/theory_rewriterules.cpp b/src/theory/rewriterules/theory_rewriterules.cpp new file mode 100644 index 000000000..0072a36e9 --- /dev/null +++ b/src/theory/rewriterules/theory_rewriterules.cpp @@ -0,0 +1,519 @@ +/********************* */ +/*! \file rewrite_engine.cpp + ** \verbatim + ** Original author: ajreynolds + ** Major contributors: bobot + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 + ** The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief [[ Deals with rewrite rules ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#include "theory/rewriterules/theory_rewriterules.h" +#include "theory/rewriterules/theory_rewriterules_rules.h" +#include "theory/rewriterules/theory_rewriterules_params.h" + +#include "theory/rewriterules/theory_rewriterules_preprocess.h" +#include "theory/rewriter.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::rewriterules; + + +namespace CVC4 { +namespace theory { +namespace rewriterules { + + +inline std::ostream& operator <<(std::ostream& stream, const RuleInst& ri) { + ri.toStream(stream); + return stream; +} + +static const RuleInst* RULEINST_TRUE = (RuleInst*) 1; +static const RuleInst* RULEINST_FALSE = (RuleInst*) 2; + + /** Rule an instantiation with the given match */ +RuleInst::RuleInst(TheoryRewriteRules & re, const RewriteRule * r, + std::vector<Node> & inst_subst, + Node matched): + rule(r), d_matched(matched) +{ + Assert(r != NULL); + Assert(!r->directrr || !d_matched.isNull()); + subst.swap(inst_subst); +}; + +Node RuleInst::substNode(const TheoryRewriteRules & re, TNode r, + TCache cache ) const { + Assert(this != RULEINST_TRUE && this != RULEINST_FALSE); + return r.substitute (rule->free_vars.begin(),rule->free_vars.end(), + subst.begin(),subst.end(),cache); +}; +size_t RuleInst::findGuard(TheoryRewriteRules & re, size_t start)const{ + TCache cache; + Assert(this != RULEINST_TRUE && this != RULEINST_FALSE); + while (start < (rule->guards).size()){ + Node g = substNode(re,rule->guards[start],cache); + switch(re.addWatchIfDontKnow(g,this,start)){ + case ATRUE: + Debug("rewriterules") << g << "is true" << std::endl; + ++start; + continue; + case AFALSE: + Debug("rewriterules") << g << "is false" << std::endl; + return -1; + case ADONTKNOW: + Debug("rewriterules") << g << "is unknown" << std::endl; + return start; + } + } + /** All the guards are verified */ + re.propagateRule(this,cache); + return start; +}; + +bool RuleInst::alreadyRewritten(TheoryRewriteRules & re) const{ + Assert(this != RULEINST_TRUE && this != RULEINST_FALSE); + static NoMatchAttribute rewrittenNodeAttribute; + TCache cache; + for(std::vector<Node>::const_iterator + iter = rule->to_remove.begin(); + iter != rule->to_remove.end(); ++iter){ + if (substNode(re,*iter,cache).getAttribute(rewrittenNodeAttribute)) + return true; + }; + return false; +} + +void RuleInst::toStream(std::ostream& out) const{ + if(this == RULEINST_TRUE){ out << "TRUE"; return;}; + if(this == RULEINST_FALSE){ out << "FALSE"; return;}; + out << "(" << *rule << ") "; + for(std::vector<Node>::const_iterator + iter = subst.begin(); iter != subst.end(); ++iter){ + out << *iter << " "; + }; +} + + +void Guarded::nextGuard(TheoryRewriteRules & re)const{ + Assert(inst != RULEINST_TRUE && inst != RULEINST_FALSE); + if(simulateRewritting && inst->alreadyRewritten(re)) return; + inst->findGuard(re,d_guard+1); +}; + +/** start indicate the first guard which is not true */ +Guarded::Guarded(const RuleInst* ri, const size_t start) : + d_guard(start),inst(ri) {}; +Guarded::Guarded(const Guarded & g) : + d_guard(g.d_guard),inst(g.inst) {}; +Guarded::Guarded() : + //dumb value + d_guard(-1),inst(RULEINST_TRUE) {}; + +TheoryRewriteRules::TheoryRewriteRules(context::Context* c, + context::UserContext* u, + OutputChannel& out, + Valuation valuation, + const LogicInfo& logicInfo, + QuantifiersEngine* qe) : + Theory(THEORY_REWRITERULES, c, u, out, valuation, logicInfo, qe), + d_rules(c), d_ruleinsts(c), d_guardeds(c), d_checkLevel(c,0), + d_explanations(c), d_ruleinsts_to_add() + { + d_true = NodeManager::currentNM()->mkConst<bool>(true); + d_false = NodeManager::currentNM()->mkConst<bool>(false); + Debug("rewriterules") << Node::setdepth(-1); + Debug("rewriterules-rewrite") << Node::setdepth(-1); +} + +void TheoryRewriteRules::addMatchRuleTrigger(const RewriteRule * r, + InstMatch & im, + bool delay){ + ++r->nb_matched; + if(rewrite_instantiation) im.applyRewrite(); + if(representative_instantiation) + im.makeRepresentative( getQuantifiersEngine() ); + + if(!cache_match || !r->inCache(*this,im)){ + ++r->nb_applied; + std::vector<Node> subst; + im.computeTermVec(getQuantifiersEngine(), r->inst_vars , subst); + RuleInst * ri = new RuleInst(*this,r,subst, + r->directrr ? im.d_matched : Node::null()); + Debug("rewriterules") << "One matching found" + << (delay? "(delayed)":"") + << ":" << *ri << std::endl; + // Find the first non verified guard, don't save the rule if the + // rule can already be fired In fact I save it otherwise strange + // things append. + Assert(ri->rule != NULL); + if(delay) d_ruleinsts_to_add.push_back(ri); + else{ + if(simulateRewritting && ri->alreadyRewritten(*this)) return; + if(ri->findGuard(*this, 0) != (r->guards).size()) + d_ruleinsts.push_back(ri); + else delete(ri); + }; + }; +} + +void TheoryRewriteRules::check(Effort level) { + CodeTimer codeTimer(d_theoryTime); + + Assert(d_ruleinsts_to_add.empty()); + + while(!done()) { + // Get all the assertions + // TODO: Test that it have already been ppAsserted + get(); + // Assertion assertion = get(); + // TNode fact = assertion.assertion; + + // Debug("rewriterules") << "TheoryRewriteRules::check(): processing " << fact << std::endl; + // if (getValuation().getDecisionLevel()>0) + // Unhandled(getValuation().getDecisionLevel()); + // addRewriteRule(fact); + + }; + + Debug("rewriterules") << "Check:" << d_checkLevel << (level==EFFORT_FULL? " EFFORT_FULL":"") << std::endl; + + /** Test each rewrite rule */ + for(size_t rid = 0, end = d_rules.size(); rid < end; ++rid) { + RewriteRule * r = d_rules[rid]; + if (level!=EFFORT_FULL && r->d_split) continue; + Debug("rewriterules") << " rule: " << r << std::endl; + Trigger & tr = r->trigger; + //reset instantiation round for trigger (set up match production) + tr.resetInstantiationRound(); + //begin iterating from the first match produced by the trigger + tr.reset( Node::null() ); + + /** Test the possible matching one by one */ + InstMatch im; + while(tr.getNextMatch( im )){ + addMatchRuleTrigger(r, im, true); + im.clear(); + } + } + + const bool do_notification = d_checkLevel == 0 || level==EFFORT_FULL; + bool polldone = false; + + GuardedMap::const_iterator p = d_guardeds.begin(); + do{ + + + //dequeue instantiated rules + for(; !d_ruleinsts_to_add.empty();){ + RuleInst * ri = d_ruleinsts_to_add.back(); d_ruleinsts_to_add.pop_back(); + if(simulateRewritting && ri->alreadyRewritten(*this)) break; + if(ri->findGuard(*this, 0) != ri->rule->guards.size()) + d_ruleinsts.push_back(ri); + else delete(ri); + }; + + if(do_notification) + /** Temporary way. Poll value */ + for (; p != d_guardeds.end(); ++p){ + TNode g = (*p).first; + const GList * const l = (*p).second; + const Guarded & glast = l->back(); + // Notice() << "Polled?:" << g << std::endl; + if(glast.inst == RULEINST_TRUE||glast.inst == RULEINST_FALSE) continue; + // Notice() << "Polled!:" << g << "->" << (glast.inst == RULEINST_TRUE||glast.inst == RULEINST_FALSE) << std::endl; + bool value; + if(getValuation().hasSatValue(g,value)){ + if(value) polldone = true; //One guard is true so pass n check + Debug("rewriterules") << "Poll value:" << g + << " is " << (value ? "true" : "false") << std::endl; + notification(g,value); + //const Guarded & glast2 = (*l)[l->size()-1]; + // Notice() << "Polled!!:" << g << "->" << (glast2.inst == RULEINST_TRUE||glast2.inst == RULEINST_FALSE) << std::endl; + }; + }; + + }while(!d_ruleinsts_to_add.empty() || + (p != d_guardeds.end() && do_notification)); + + if(polldone) d_checkLevel = checkSlowdown; + else d_checkLevel = d_checkLevel > 0 ? (d_checkLevel - 1) : 0; + + /** Narrowing by splitting on the guards */ + /** Perhaps only when no notification? */ + if(narrowing_full_effort && level==EFFORT_FULL){ + for (GuardedMap::const_iterator p = d_guardeds.begin(); + p != d_guardeds.end(); ++p){ + TNode g = (*p).first; + const GList * const l = (*p).second; + const Guarded & glast = (*l)[l->size()-1]; + if(glast.inst == RULEINST_TRUE||glast.inst == RULEINST_FALSE) + continue; + // If it has a value it should already has been notified + bool value; value = value; // avoiding the warning in non debug mode + Assert(!getValuation().hasSatValue(g,value)); + Debug("rewriterules") << "Narrowing on:" << g << std::endl; + /** Can split on already rewritten instrule... but... */ + getOutputChannel().split(g); + } + } + + Assert(d_ruleinsts_to_add.empty()); + Debug("rewriterules") << "Check done:" << d_checkLevel << std::endl; + +}; + +void TheoryRewriteRules::registerQuantifier( Node n ){}; + +Trigger TheoryRewriteRules::createTrigger( TNode n, std::vector<Node> & pattern ) { + // Debug("rewriterules") << "createTrigger:"; + getQuantifiersEngine()->registerPattern(pattern); + return *Trigger::mkTrigger(getQuantifiersEngine(),n,pattern, + match_gen_kind, true); +}; + +bool TheoryRewriteRules::notifyIfKnown(const GList * const ltested, + GList * const lpropa) { + Assert(ltested->size() > 0); + const Guarded & glast = (*ltested)[ltested->size()-1]; + if(glast.inst == RULEINST_TRUE || glast.inst == RULEINST_FALSE){ + notification(lpropa,glast.inst == RULEINST_TRUE); + return true; + }; + return false; +}; + +void TheoryRewriteRules::notification(GList * const lpropa, bool b){ + if (b){ + for(GList::const_iterator g = lpropa->begin(); + g != lpropa->end(); ++g) { + (*g).nextGuard(*this); + }; + lpropa->push_back(Guarded(RULEINST_TRUE,0)); + }else{ + lpropa->push_back(Guarded(RULEINST_FALSE,0)); + }; + Assert(lpropa->back().inst == RULEINST_TRUE || + lpropa->back().inst == RULEINST_FALSE); +}; + + + +Answer TheoryRewriteRules::addWatchIfDontKnow(Node g0, const RuleInst* ri, + const size_t gid){ + /** TODO: Should use the representative of g, but should I keep the + mapping for myself? */ + /* If it false in one model (current valuation) it's false for all */ + if (useCurrentModel){ + Node val = getValuation().getValue(g0); + Debug("rewriterules") << "getValue:" << g0 << " = " + << val << " is " << (val == d_false) << std::endl; + if (val == d_false) return AFALSE; + }; + /** Currently create a node with a literal */ + Node g = getValuation().ensureLiteral(g0); + GuardedMap::iterator l_i = d_guardeds.find(g); + GList* l; + if( l_i == d_guardeds.end() ) { + /** Normally Not watched so IDONTNOW but since we poll, we can poll now */ + bool value; + if(getValuation().hasSatValue(g,value)){ + if(value) return ATRUE; + else return AFALSE; + }; + //Not watched so IDONTNOW + l = new(getSatContext()->getCMM()) + GList(true, getSatContext());//, + //ContextMemoryAllocator<Guarded>(getContext()->getCMM())); + d_guardeds.insert(g ,l);//.insertDataFromContextMemory(g, l); + /* TODO Add register propagation */ + } else { + l = (*l_i).second; + Assert(l->size() > 0); + const Guarded & glast = (*l)[l->size()-1]; + if(glast.inst == RULEINST_TRUE) return ATRUE; + if(glast.inst == RULEINST_FALSE) return AFALSE; + + }; + /** I DONT KNOW because not watched or because not true nor false */ + l->push_back(Guarded(ri,gid)); + return ADONTKNOW; +}; + +void TheoryRewriteRules::notification(Node g, bool b){ + GuardedMap::const_iterator l = d_guardeds.find(g); + /** Should be a propagated node already known */ + Assert(l != d_guardeds.end()); + notification((*l).second,b); +} + + +void TheoryRewriteRules::notifyEq(TNode lhs, TNode rhs) { + GuardedMap::const_iterator ilhs = d_guardeds.find(lhs); + GuardedMap::const_iterator irhs = d_guardeds.find(rhs); + /** Should be a propagated node already known */ + Assert(ilhs != d_guardeds.end()); + if( irhs == d_guardeds.end() ) { + /** Not watched so points to the list directly */ + d_guardeds.insertDataFromContextMemory(rhs, (*ilhs).second); + } else { + GList * const llhs = (*ilhs).second; + GList * const lrhs = (*irhs).second; + if(!(notifyIfKnown(llhs,lrhs) || notifyIfKnown(lrhs,llhs))){ + /** If none of the two is known */ + for(GList::const_iterator g = llhs->begin(); g != llhs->end(); ++g){ + lrhs->push_back(*g); + }; + }; + }; +}; + + +void TheoryRewriteRules::propagateRule(const RuleInst * inst, TCache cache){ + // Debug("rewriterules") << "A rewrite rules is verified. Add lemma:"; + Debug("rewriterules") << "propagateRule" << *inst << std::endl; + const RewriteRule * rule = inst->rule; + ++rule->nb_applied; + // Can be more something else than an equality in fact (eg. propagation rule) + Node equality = inst->substNode(*this,rule->body,cache); + if(propagate_as_lemma){ + Node lemma = equality; + if(rule->guards.size() > 0){ + // TODO: problem with reduction rule => instead of <=> + lemma = substGuards(inst, cache).impNode(equality); + }; + if(rule->directrr){ + TypeNode booleanType = NodeManager::currentNM()->booleanType(); + // if the rule is directly applied by the rewriter, + // we should take care to use the representative that can't be directly rewritable: + // If "car(a)" is somewhere and we know that "a = cons(x,l)" we shouldn't + // add the constraint car(cons(x,l) = x because it is rewritten to x = x. + // But we should say cons(a) = x + Assert(lemma.getKind() == kind::EQUAL || + lemma.getKind() == kind::IMPLIES); + Assert(!inst->d_matched.isNull()); + Assert( inst->d_matched.getOperator() == lemma[0].getOperator() ); + // replace the left hand side by the term really matched + Debug("rewriterules-directrr") << "lemma:" << lemma << " :: " << inst->d_matched; + Node hyp; + NodeBuilder<2> nb; + if(inst->d_matched.getNumChildren() == 1){ + nb.clear( inst->d_matched[0].getType(false) == booleanType ? + kind::IFF : kind::EQUAL ); + nb << inst->d_matched[0] << lemma[0][0]; + hyp = nb; + }else{ + NodeBuilder<> andb(kind::AND); + for(size_t i = 0, + iend = inst->d_matched.getNumChildren(); i < iend; ++i){ + nb.clear( inst->d_matched[i].getType(false) == booleanType ? + kind::IFF : kind::EQUAL ); + nb << inst->d_matched[i] << lemma[0][i]; + andb << static_cast<Node>(nb); + } + hyp = andb; + }; + nb.clear(lemma.getKind()); + nb << inst->d_matched << lemma[1]; + lemma = hyp.impNode(static_cast<Node>(nb)); + Debug("rewriterules-directrr") << " -> " << lemma << std::endl; + }; + // Debug("rewriterules") << "lemma:" << lemma << std::endl; + getOutputChannel().lemma(lemma); + }else{ + Assert(!direct_rewrite); + Node lemma_lit = getValuation().ensureLiteral(equality); + ExplanationMap::const_iterator p = d_explanations.find(lemma_lit); + if(p!=d_explanations.end()) return; //Already propagated + bool value; + if(getValuation().hasSatValue(lemma_lit,value)){ + /* Already assigned */ + if (!value){ + Node conflict = substGuards(inst,cache,lemma_lit); + getOutputChannel().conflict(conflict); + }; + }else{ + getOutputChannel().propagate(lemma_lit); + d_explanations.insert(lemma_lit, *inst); + }; + }; + + if(simulateRewritting){ + static NoMatchAttribute rewrittenNodeAttribute; + // Tag the rewritted terms + for(std::vector<Node>::iterator i = rule->to_remove.begin(); + i == rule->to_remove.end(); ++i){ + (*i).setAttribute(rewrittenNodeAttribute,true); + }; + }; + + //Verify that this instantiation can't immediately fire another rule + for(RewriteRule::BodyMatch::const_iterator p = rule->body_match.begin(); + p != rule->body_match.end(); ++p){ + RewriteRule * r = (*p).second; + // Debug("rewriterules") << " rule: " << r << std::endl; + // Use trigger2 since we can be in check + Trigger & tr = r->trigger_for_body_match; + InstMatch im; + TNode m = inst->substNode(*this,(*p).first, cache); + tr.getMatch(m,im); + if(!im.empty()){ + im.d_matched = m; + addMatchRuleTrigger(r, im); + } + } +}; + + +Node TheoryRewriteRules::substGuards(const RuleInst *inst, + TCache cache, + /* Already substituted */ + Node last){ + const RewriteRule * r = inst->rule; + /** No guards */ + const size_t size = r->guards.size(); + if(size == 0) return (last.isNull()?d_true:last); + /** One guard */ + if(size == 1 && last.isNull()) return inst->substNode(*this,r->guards[0],cache); + /** Guards */ /* TODO remove the duplicate with a set like in uf? */ + NodeBuilder<> conjunction(kind::AND); + for(std::vector<Node>::const_iterator p = r->guards.begin(); + p != r->guards.end(); ++p) { + Assert(!p->isNull()); + conjunction << inst->substNode(*this,*p,cache); + }; + if (!last.isNull()) conjunction << last; + return conjunction; +} + +Node TheoryRewriteRules::explain(TNode n){ + ExplanationMap::const_iterator p = d_explanations.find(n); + Assert(p!=d_explanations.end(),"I forget the explanation..."); + RuleInst i = (*p).second; + //Notice() << n << "<-" << *(i.rule) << std::endl; + return substGuards(&i, TCache ()); +} + +Theory::PPAssertStatus TheoryRewriteRules::ppAssert(TNode in, SubstitutionMap& outSubstitutions) { + addRewriteRule(in); + return PP_ASSERT_STATUS_UNSOLVED; +} + +}/* CVC4::theory::rewriterules namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/rewriterules/theory_rewriterules.h b/src/theory/rewriterules/theory_rewriterules.h new file mode 100644 index 000000000..499535687 --- /dev/null +++ b/src/theory/rewriterules/theory_rewriterules.h @@ -0,0 +1,264 @@ +/********************* */ +/*! \file rewrite_engine.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: bobot + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 + ** The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Rewrite Engine classes + **/ + + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_H +#define __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_H + +#include "context/cdlist.h" +#include "context/cdqueue.h" +#include "theory/valuation.h" +#include "theory/theory.h" +#include "theory/theory_engine.h" +#include "theory/quantifiers_engine.h" +#include "context/context_mm.h" +#include "theory/inst_match_impl.h" +#include "util/stats.h" +#include "theory/rewriterules/theory_rewriterules_preprocess.h" + +namespace CVC4 { +namespace theory { +namespace rewriterules { + +typedef std::hash_map<TNode, TNode, TNodeHashFunction> TCache; + + enum Answer {ATRUE, AFALSE, ADONTKNOW}; + + class TheoryRewriteRules; /** forward */ + + class RewriteRule{ + public: + // constant + const bool d_split; + mutable Trigger trigger; + std::vector<Node> guards; + mutable std::vector<Node> to_remove; /** terms to remove */ + const Node body; + const TNode new_terms; /** new terms included in the body */ + std::vector<Node> free_vars; /* free variable in the rule */ + std::vector<Node> inst_vars; /* corresponding vars in the triggers */ + /* After instantiating the body new match can appear (TNode + because is a subterm of a body on the assicaited rewrite + rule) */ + typedef context::CDList< std::pair<TNode,RewriteRule* > > BodyMatch; + mutable BodyMatch body_match; + mutable Trigger trigger_for_body_match; // used because we can be matching + // trigger when we need new match. + // So currently we use another + // trigger for that. + + //context dependent + typedef InstMatchTrie2<true> CacheNode; + mutable CacheNode d_cache; + + const bool directrr; + + RewriteRule(TheoryRewriteRules & re, + Trigger & tr, Trigger & tr2, + std::vector<Node> & g, Node b, TNode nt, + std::vector<Node> & fv,std::vector<Node> & iv, + std::vector<Node> & to_r, bool drr); + RewriteRule(const RewriteRule & r) CVC4_UNUSED; + RewriteRule& operator=(const RewriteRule &) CVC4_UNUSED; + ~RewriteRule(); + + bool noGuard()const throw () { return guards.size() == 0; }; + bool inCache(TheoryRewriteRules & re, InstMatch & im)const; + + void toStream(std::ostream& out) const; + + /* statistics */ + mutable size_t nb_matched; + mutable size_t nb_applied; + mutable size_t nb_propagated; + + }; + + class RuleInst{ + public: + /** The rule has at least one guard */ + const RewriteRule* rule; + + /** the substitution */ + std::vector<Node> subst; + + /** the term matched (not null if mono-pattern and direct-rr) */ + Node d_matched; + + /** Rule an instantiation with the given match */ + RuleInst(TheoryRewriteRules & re, const RewriteRule* r, + std::vector<Node> & inst_subst, + Node matched); + RuleInst():rule(NULL){} // Dumb + + Node substNode(const TheoryRewriteRules & re, TNode r, TCache cache) const; + size_t findGuard(TheoryRewriteRules & re, size_t start)const; + + void toStream(std::ostream& out) const; + + bool alreadyRewritten(TheoryRewriteRules & re) const; + }; + +/** A pair? */ + class Guarded { + public: + /** The backtracking is done somewhere else */ + const size_t d_guard; /* the id of the guard */ + + /** The shared instantiation data */ + const RuleInst* inst; + + void nextGuard(TheoryRewriteRules & re)const; + + /** start indicate the first guard which is not true */ + Guarded(const RuleInst* ri, const size_t start); + Guarded(const Guarded & g); + /** Should be ssigned by a good garded after */ + Guarded(); + + ~Guarded(){}; + void destroy(){}; + }; + +template<class T> +class CleanUpPointer{ +public: + inline void operator()(T** e){ + delete(*e); + }; +}; + +class TheoryRewriteRules : public Theory { +private: + + KEEP_STATISTIC(TimerStat, d_theoryTime, "theory::rewriterules::theoryTime"); + + /** list of all rewrite rules */ + /* std::vector< Node > d_rules; */ + // typedef std::vector< std::pair<Node, Trigger > > Rules; + typedef context::CDList< RewriteRule *, + CleanUpPointer<RewriteRule >, + std::allocator< RewriteRule * > > Rules; + Rules d_rules; + typedef context::CDList< RuleInst *, + CleanUpPointer<RuleInst>, + std::allocator< RuleInst * > > RuleInsts; + RuleInsts d_ruleinsts; + + /** The GList* will lead too memory leaks since that doesn't use + CDChunckList */ + typedef context::CDList< Guarded > GList; + typedef context::CDHashMap<Node, GList *, NodeHashFunction> GuardedMap; + GuardedMap d_guardeds; + + /* In order to not monopolize, the system slow down himself: If a + guard stored in d_guardeds become true or false, it waits + checkSlowdown(=10) checks before checking again if some guard take a + value. At FULL_EFFORT regardless of d_checkLevel it check the + guards + */ + context::CDO<size_t> d_checkLevel; + + /** explanation */ + typedef context::CDHashMap<Node, RuleInst , NodeHashFunction> ExplanationMap; + ExplanationMap d_explanations; + + /** new instantiation must be cleared at each conflict used only + inside check */ + typedef std::vector< RuleInst* > QRuleInsts; + QRuleInsts d_ruleinsts_to_add; + public: + /** true and false for predicate */ + Node d_true; + Node d_false; + + /** Constructs a new instance of TheoryRewriteRules + w.r.t. the provided context.*/ + TheoryRewriteRules(context::Context* c, + context::UserContext* u, + OutputChannel& out, + Valuation valuation, + const LogicInfo& logicInfo, + QuantifiersEngine* qe); + + /** Usual function for theories */ + void check(Theory::Effort e); + Node explain(TNode n); + void notifyEq(TNode lhs, TNode rhs); + std::string identify() const { + return "THEORY_REWRITERULES"; + } + + Theory::PPAssertStatus ppAssert(TNode in, SubstitutionMap& outSubstitutions); + + bool ppDontRewriteSubterm(TNode atom){ return true; } + + + private: + void registerQuantifier( Node n ); + + public: + /* TODO modify when notification will be available */ + void notification( Node n, bool b); + + Trigger createTrigger( TNode n, std::vector<Node> & pattern ); + + /** return if the guard (already substituted) is known true or false + or unknown. In the last case it add the Guarded(rid,gid) to the watch + list of this guard */ + Answer addWatchIfDontKnow(Node g, const RuleInst* r, const size_t gid); + + /** An instantiation of a rule is fired (all guards true) we + propagate the body. That can be done by theory propagation if + possible or by lemmas. + */ + void propagateRule(const RuleInst * r, TCache cache); + + /** Auxillary functions */ +private: + /** A guard is verify, notify the Guarded */ + void notification(GList * const lpropa, bool b); + /* If two guards becomes equals we should notify if one of them is + already true */ + bool notifyIfKnown(const GList * const ltested, GList * const lpropa); + + Node substGuards(const RuleInst * inst, + TCache cache, + Node last = Node::null()); + + void addRewriteRule(const Node r); + void computeMatchBody ( const RewriteRule * r, size_t start = 0); + void addMatchRuleTrigger(const RewriteRule* r, + InstMatch & im, bool delay = true); + + /* rewrite pattern */ + typedef std::hash_map< Node, rewriter::RRPpRewrite*, NodeHashFunction > RegisterRRPpRewrite; + RegisterRRPpRewrite d_registeredRRPpRewrite; + + bool addRewritePattern(TNode pattern, TNode body, + rewriter::Subst & pvars, + rewriter::Subst & vars); + +};/* class TheoryRewriteRules */ + +}/* CVC4::theory::rewriterules namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_H */ diff --git a/src/theory/rewriterules/theory_rewriterules_params.h b/src/theory/rewriterules/theory_rewriterules_params.h new file mode 100644 index 000000000..ecb5385f9 --- /dev/null +++ b/src/theory/rewriterules/theory_rewriterules_params.h @@ -0,0 +1,95 @@ +/********************* */ +/*! \file rewrite_engine.cpp + ** \verbatim + ** Original author: bobot + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 + ** The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Parameters for the rewrite rules theory + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + + +#ifndef __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_PARAMS_H +#define __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_PARAMS_H + +namespace CVC4 { +namespace theory { +namespace rewriterules { + +/** + Specify if the propagation is done by lemma or by real theory propagation + */ +static const bool propagate_as_lemma = true; + +/** + Cache the instantiation of rules in order to remove duplicate + */ +static const bool cache_match = true; + +/** + Compute when the rules are created which terms in the body can lead + to new instantiation (try only single trigger). During propagation + we check if the instantiation of these terms are known terms. + */ +static const bool compute_opt = true; + +/** + rewrite the matching found + */ +static const bool rewrite_instantiation = true; + +/** + use the representative for the matching found + */ +static const bool representative_instantiation = false; + +/** + Wait the specified number of check after a new propagation (a + previous unknown guards becomes true) is found before verifying again the guards. + + Allow to break loop with other theories. + */ +static const size_t checkSlowdown = 10; + +/** + Use the current model to eliminate guard before asking for notification + */ +static const bool useCurrentModel = false; + +/** + Simulate rewritting by tagging rewritten terms. + */ +static const bool simulateRewritting = false; + +/** + Choose the kind of matching to use: + - InstMatchGenerator::MATCH_GEN_DEFAULT 0 + - InstMatchGenerator::MATCH_GEN_EFFICIENT_E_MATCH 1 +*/ +static const int match_gen_kind = 0; + +/** + Do narrowing at full effort +*/ +static const bool narrowing_full_effort = false; + +/** + Direct rewrite: Add rewrite rules directly in the rewriter. + */ +static const bool direct_rewrite = true; + +}/* CVC4::theory::rewriterules namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_PARAMS_H */ diff --git a/src/theory/rewriterules/theory_rewriterules_preprocess.h b/src/theory/rewriterules/theory_rewriterules_preprocess.h new file mode 100644 index 000000000..f17e30758 --- /dev/null +++ b/src/theory/rewriterules/theory_rewriterules_preprocess.h @@ -0,0 +1,176 @@ +/********************* */ +/*! \file rewriterules.h + ** \verbatim + ** Original author: bobot + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief One utilitise for rewriterules definition + ** + **/ + + +#ifndef __CVC4__REWRITERULES_H +#define __CVC4__REWRITERULES_H + +#include <vector> +#include <hash_set> +#include <hash_map> +#include "expr/expr.h" +#include "expr/node.h" +#include "theory/rewriterules/theory_rewriterules_params.h" +#include "theory/uf/theory_uf.h" + +namespace CVC4 { +namespace theory { +namespace rewriterules { + +namespace rewriter{ + + typedef Node TMPNode; + typedef std::vector<Node> Subst; + typedef std::vector<Expr> ESubst; + typedef std::vector<TMPNode> TSubst; + + struct Step{ + + /** match the node and add in Vars the found variables */ + virtual Node run(TMPNode node) = 0; + virtual bool add(TMPNode pattern, TMPNode body, Subst & pvars, Subst & vars) = 0; + }; + + struct FinalStep : Step { + Node body; + TSubst vars; + + Node subst(Subst & subst){ + return body.substitute(vars.begin(), vars.end(), + subst.begin(), subst.end()); + } + + }; + + typedef std::hash_map< Node, int, NodeHashFunction > PVars; + + struct Pattern : FinalStep{ + Node pattern; + PVars pattern_vars; + + Node run(TMPNode node){ + Subst vars = Subst(pattern_vars.size(),Node::null()); + + typedef std::vector<std::pair<TMPNode,TMPNode> > tstack; + tstack stack(1,std::make_pair(node,pattern)); // t * pat + + while(!stack.empty()){ + const std::pair<TMPNode,TMPNode> p = stack.back(); stack.pop_back(); + const TMPNode & t = p.first; + const TMPNode & pat = p.second; + + // pat is a variable + if( pat.getKind() == kind::INST_CONSTANT || + pat.getKind() == kind::VARIABLE){ + PVars::iterator i = pattern_vars.find(pat); + Assert(i != pattern_vars.end()); + if(vars[i->second].isNull()) vars[i->second] = t; + if(vars[i->second] == t) continue; + return Node::null(); + }; + + // t is not an UF application + if( t.getKind() != kind::APPLY_UF ){ + if (t == pat) continue; + else return Node::null(); + }; + + //different UF_application + if( t.getOperator() != pat.getOperator() ) return Node::null(); + + //put the children on the stack + for( size_t i=0; i < pat.getNumChildren(); i++ ){ + stack.push_back(std::make_pair(t[i],pat[i])); + }; + } + + // Matching is done + return subst(vars); + } + + bool add(TMPNode pattern, TMPNode body, Subst & pvars, Subst & vars){ + return false; + } + + }; + + + struct Args : Step { + typedef std::vector<Pattern> Patterns; + Patterns d_matches; + + Node run(TMPNode node){ + Node n; + for (Patterns::iterator i = d_matches.begin(); + i != d_matches.end() && n.isNull(); ++i){ + Debug("rewriterules-rewrite") << "test?" << i->pattern << std::endl; + n = i->run(node); + } + return n; + } + + bool add(TMPNode pattern, TMPNode body, Subst & pvars, Subst & vars){ + Debug("rewriterules-rewrite") << "theoryrewrite::Args::add" << "(" + << d_matches.size() << ")" + << pattern << "->" << body << std::endl; + d_matches.push_back(Pattern()); + Pattern & p = d_matches.back(); + p.body = body; + p.vars.reserve(vars.size()); + for( size_t i=0; i < vars.size(); i++ ){ + p.vars.push_back(vars[i]); + }; + p.pattern = pattern; + for( size_t i=0; i < pvars.size(); i++ ){ + p.pattern_vars[pvars[i]] = i; + }; + return true; + }; + + void clear(){ + d_matches.clear(); + } + }; + +class RRPpRewrite : public uf::TheoryUF::PpRewrite { + Args d_pattern; +public: + Node ppRewrite(TNode node){ + Debug("rewriterules-rewrite") << "rewrite?" << node << std::endl; + Node t = d_pattern.run(node); + Debug("rewriterules-rewrite") << "rewrite:" << node + << (t.isNull()? " to": " to ") + << t << std::endl; + if (t.isNull()) return node; + else return t; + } + + bool addRewritePattern(TMPNode pattern, TMPNode body, + Subst & pvars, Subst & vars){ + return d_pattern.add(pattern,body,pvars,vars); + } + +}; + + + +} + +} +} +} +#endif /* __CVC4__REWRITERULES_H */ diff --git a/src/theory/rewriterules/theory_rewriterules_rewriter.h b/src/theory/rewriterules/theory_rewriterules_rewriter.h new file mode 100644 index 000000000..d9da095f7 --- /dev/null +++ b/src/theory/rewriterules/theory_rewriterules_rewriter.h @@ -0,0 +1,104 @@ +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_REWRITER_H +#define __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_REWRITER_H + +#include "theory/rewriter.h" +#include "theory/rewriter_attributes.h" + +namespace CVC4 { +namespace theory { +namespace rewriterules { + +class TheoryRewriterulesRewriter { +public: + + /** + * Rewrite a node into the normal form for the theory of rewriterules. + * Called in post-order (really reverse-topological order) when + * traversing the expression DAG during rewriting. This is the + * main function of the rewriter, and because of the ordering, + * it can assume its children are all rewritten already. + * + * This function can return one of three rewrite response codes + * along with the rewritten node: + * + * REWRITE_DONE indicates that no more rewriting is needed. + * REWRITE_AGAIN means that the top-level expression should be + * rewritten again, but that its children are in final form. + * REWRITE_AGAIN_FULL means that the entire returned expression + * should be rewritten again (top-down with preRewrite(), then + * bottom-up with postRewrite()). + * + * Even if this function returns REWRITE_DONE, if the returned + * expression belongs to a different theory, it will be fully + * rewritten by that theory's rewriter. + */ + static RewriteResponse postRewrite(TNode node) { + + // Implement me! + + // This default implementation + return RewriteResponse(REWRITE_DONE, node); + } + + /** + * Rewrite a node into the normal form for the theory of rewriterules + * in pre-order (really topological order)---meaning that the + * children may not be in the normal form. This is an optimization + * for theories with cancelling terms (e.g., 0 * (big-nasty-expression) + * in arithmetic rewrites to 0 without the need to look at the big + * nasty expression). Since it's only an optimization, the + * implementation here can do nothing. + */ + static RewriteResponse preRewrite(TNode node) { + // do nothing + return RewriteResponse(REWRITE_DONE, node); + } + + /** + * Rewrite an equality, in case special handling is required. + */ + static Node rewriteEquality(TNode equality) { + // often this will suffice + return postRewrite(equality).node; + } + + /** + * Initialize the rewriter. + */ + static inline void init() { + // nothing to do + } + + /** + * Shut down the rewriter. + */ + static inline void shutdown() { + // nothing to do + } + +};/* class TheoryRewriterulesRewriter */ + +}/* CVC4::theory::rewriterules namespace */ + +template<> +struct RewriteAttibute<THEORY_REWRITERULES> { + static Node getPreRewriteCache(TNode node) throw() { + return node; + } + + static void setPreRewriteCache(TNode node, TNode cache) throw() { } + + static Node getPostRewriteCache(TNode node) throw() { + return node; + } + + static void setPostRewriteCache(TNode node, TNode cache) throw() { } + +}; + +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_REWRITER_H */ diff --git a/src/theory/rewriterules/theory_rewriterules_rules.cpp b/src/theory/rewriterules/theory_rewriterules_rules.cpp new file mode 100644 index 000000000..9847f1727 --- /dev/null +++ b/src/theory/rewriterules/theory_rewriterules_rules.cpp @@ -0,0 +1,303 @@ +/********************* */ +/*! \file rewrite_engine.cpp + ** \verbatim + ** Original author: ajreynolds + ** Major contributors: bobot + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 + ** The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief [[ Deals with rewrite rules ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#include "theory/rewriterules/theory_rewriterules_rules.h" +#include "theory/rewriterules/theory_rewriterules_params.h" +#include "theory/rewriterules/theory_rewriterules_preprocess.h" +#include "theory/rewriterules/theory_rewriterules.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::rewriterules; + + +namespace CVC4 { +namespace theory { +namespace rewriterules { + +void TheoryRewriteRules::computeMatchBody ( const RewriteRule * rule, + size_t start){ + std::vector<TNode> stack(1,rule->new_terms); + + while(!stack.empty()){ + Node t = stack.back(); stack.pop_back(); + + // We don't want to consider variable in t + if( std::find(rule->free_vars.begin(), rule->free_vars.end(), t) + != rule->free_vars.end()) continue; + // t we want to consider only UF function + if( t.getKind() == APPLY_UF ){ + for(size_t rid = start, end = d_rules.size(); rid < end; ++rid) { + RewriteRule * r = d_rules[rid]; + if(r->d_split) continue; + Trigger & tr = const_cast<Trigger &> (r->trigger_for_body_match); + if(!tr.nonunifiable(t, rule->free_vars)){ + rule->body_match.push_back(std::make_pair(t,r)); + } + } + } + + //put the children on the stack + for( size_t i=0; i < t.getNumChildren(); i++ ){ + stack.push_back(t[i]); + }; + + } +} + +inline void addPattern(TheoryRewriteRules & re, + TNode tri, + std::vector<Node> & pattern, + std::vector<Node> & vars, + std::vector<Node> & inst_constants, + TNode r){ + if (tri.getKind() == kind::NOT && tri[0].getKind() == kind::APPLY_UF) + tri = tri[0]; + pattern.push_back(re.getQuantifiersEngine()-> + convertNodeToPattern(tri,r,vars,inst_constants)); +} + +/*** Check that triggers contains all the variables */ +void checkPatternVarsAux(TNode pat,const std::vector<Node> & vars, + std::vector<bool> & seen){ + for(size_t id=0;id < vars.size(); ++id){ + if(pat == vars[id]){ + seen[id]=true; + break; + }; + }; + for(Node::iterator i = pat.begin(); i != pat.end(); ++i) { + checkPatternVarsAux(*i,vars,seen); + }; +} + +bool checkPatternVars(const std::vector<Node> & pattern, + const std::vector<Node> & vars){ + std::vector<bool> seen(vars.size(),false); + for(std::vector<Node>::const_iterator i = pattern.begin(); + i != pattern.end(); ++i) { + checkPatternVarsAux(*i,vars,seen); + }; + return (find(seen.begin(),seen.end(),false) == seen.end()); +} + +/** Main function for construction of RewriteRule */ +void TheoryRewriteRules::addRewriteRule(const Node r) +{ + Assert(r.getKind() == kind::REWRITE_RULE); + /* Replace variables by Inst_* variable and tag the terms that + contain them */ + std::vector<Node> vars; + vars.reserve(r[0].getNumChildren()); + for( Node::const_iterator v = r[0].begin(); v != r[0].end(); ++v ){ + vars.push_back(*v); + }; + /* Instantiation version */ + std::vector<Node> inst_constants = + getQuantifiersEngine()->createInstVariable(vars); + /* Body/Remove_term/Guards/Triggers */ + Node body = r[2][1]; + TNode new_terms = r[2][1]; + std::vector<Node> guards; + std::vector<Node> pattern; + std::vector<Node> to_remove; /* "remove" the terms from the database + when fired */ + /* shortcut */ + TNode head = r[2][0]; + switch(r[2].getKind()){ + case kind::RR_REWRITE: + /* Equality */ + to_remove.push_back(head); + addPattern(*this,head,pattern,vars,inst_constants,r); + body = head.eqNode(body); + break; + case kind::RR_REDUCTION: + /** Add head to remove */ + to_remove.push_back(head); + case kind::RR_DEDUCTION: + /** Add head to guards and pattern */ + switch(head.getKind()){ + case kind::AND: + guards.reserve(head.getNumChildren()); + for(Node::iterator i = head.begin(); i != head.end(); ++i) { + guards.push_back(*i); + addPattern(*this,*i,pattern,vars,inst_constants,r); + }; + break; + default: + if (head != d_true){ + guards.push_back(head); + addPattern(*this,head,pattern,vars,inst_constants,r); + }; + /** otherwise guards is empty */ + }; + break; + default: + Unreachable("RewriteRules can be of only threee kinds"); + }; + /* Add the other guards */ + TNode g = r[1]; + switch(g.getKind()){ + case kind::AND: + guards.reserve(g.getNumChildren()); + for(Node::iterator i = g.begin(); i != g.end(); ++i) { + guards.push_back(*i); + }; + break; + default: + if (g != d_true) guards.push_back(g); + /** otherwise guards is empty */ + }; + /* Add the other triggers */ + if( r[2].getNumChildren() >= 3 ) + for(Node::iterator i = r[2][2].begin(); i != r[2][2].end(); ++i) { + // todo test during typing that its a good term (no not, atom, or term...) + addPattern(*this,(*i)[0],pattern,vars,inst_constants,r); + }; + // Assert(pattern.size() == 1, "currently only single pattern are supported"); + //Every variable must be seen in the pattern + if (!checkPatternVars(pattern,inst_constants)){ + Warning() << "The rule" << r << + " has been removed since it doesn't contain every variables." + << std::endl; + return; + } + + //Add to direct rewrite rule + bool directrr = false; + if(direct_rewrite && + ((guards.size() == 0 && r[2].getKind() == kind::RR_REWRITE) || + (guards.size() == 1 && r[2].getKind() == kind::RR_REDUCTION)) + && pattern.size() == 1){ + directrr = addRewritePattern(pattern[0],new_terms, inst_constants, vars); + } + + // final construction + Trigger trigger = createTrigger(r,pattern); + Trigger trigger2 = createTrigger(r,pattern); //Hack + RewriteRule * rr = new RewriteRule(*this, trigger, trigger2, + guards, body, new_terms, + vars, inst_constants, to_remove, + directrr); + /** other -> rr */ + if(compute_opt && !rr->d_split) computeMatchBody(rr); + d_rules.push_back(rr); + /** rr -> all (including himself) */ + if(compute_opt && !rr->d_split) + for(size_t rid = 0, end = d_rules.size(); rid < end; ++rid) + computeMatchBody(d_rules[rid], + d_rules.size() - 1); + + Debug("rewriterules") << "created rewriterule"<< (rr->d_split?"(split)":"") << ":(" << d_rules.size() - 1 << ")" + << *rr << std::endl; + +} + + +bool willDecide(TNode node, bool positive = true){ + /* TODO something better */ + switch(node.getKind()) { + case AND: + return !positive; + case OR: + return positive; + case IFF: + return true; + case XOR: + return true; + case IMPLIES: + return false; + case ITE: + return true; + case NOT: + return willDecide(node[0],!positive); + default: + return false; + } +} + + +RewriteRule::RewriteRule(TheoryRewriteRules & re, + Trigger & tr, Trigger & tr2, + std::vector<Node> & g, Node b, TNode nt, + std::vector<Node> & fv,std::vector<Node> & iv, + std::vector<Node> & to_r, bool drr) : + d_split(willDecide(b)), + trigger(tr), body(b), new_terms(nt), free_vars(), inst_vars(), + body_match(re.getSatContext()),trigger_for_body_match(tr2), + d_cache(re.getSatContext(),re.getQuantifiersEngine()), directrr(drr){ + free_vars.swap(fv); inst_vars.swap(iv); guards.swap(g); to_remove.swap(to_r); +}; + + +bool RewriteRule::inCache(TheoryRewriteRules & re, InstMatch & im)const{ + return !d_cache.addInstMatch(im); +}; + +/** A rewrite rule */ +void RewriteRule::toStream(std::ostream& out) const{ + for(std::vector<Node>::const_iterator + iter = guards.begin(); iter != guards.end(); ++iter){ + out << *iter; + }; + out << "=>" << body << std::endl; + out << "["; + for(BodyMatch::const_iterator + iter = body_match.begin(); iter != body_match.end(); ++iter){ + out << (*iter).first << "(" << (*iter).second << ")" << ","; + }; + out << "]" << (directrr?"*":"") << std::endl; +} + +RewriteRule::~RewriteRule(){ + Debug("rewriterule-stats") << *this + << " (" << nb_matched + << "," << nb_applied + << "," << nb_propagated + << ")" << std::endl; +} + +bool TheoryRewriteRules::addRewritePattern(TNode pattern, TNode body, + rewriter::Subst & pvars, + rewriter::Subst & vars){ + Assert(pattern.getKind() == kind::APPLY_UF); + TNode op = pattern.getOperator(); + TheoryRewriteRules::RegisterRRPpRewrite::iterator i = + d_registeredRRPpRewrite.find(op); + + rewriter::RRPpRewrite * p; + if (i == d_registeredRRPpRewrite.end()){ + p = new rewriter::RRPpRewrite(); + d_registeredRRPpRewrite.insert(std::make_pair(op,p)); + ((uf::TheoryUF*)getQuantifiersEngine()->getTheoryEngine()->getTheory( THEORY_UF ))-> + registerPpRewrite(op,p); + } else p = i->second; + + return p->addRewritePattern(pattern,body,pvars,vars); + +} + + +}/* CVC4::theory::rewriterules namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/rewriterules/theory_rewriterules_rules.h b/src/theory/rewriterules/theory_rewriterules_rules.h new file mode 100644 index 000000000..a8e70f3e6 --- /dev/null +++ b/src/theory/rewriterules/theory_rewriterules_rules.h @@ -0,0 +1,40 @@ +/********************* */ +/*! \file rewrite_engine.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: bobot + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 + ** The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Rewrite Engine classes + **/ + + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_RULES_H +#define __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_RULES_H + +#include "theory/rewriterules/theory_rewriterules.h" +#include "theory/rewriterules/theory_rewriterules_params.h" + +namespace CVC4 { +namespace theory { +namespace rewriterules { + +inline std::ostream& operator <<(std::ostream& stream, const RewriteRule& r) { + r.toStream(stream); + return stream; +} + +}/* CVC4::theory::rewriterules namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_RULES_H */ diff --git a/src/theory/rewriterules/theory_rewriterules_type_rules.h b/src/theory/rewriterules/theory_rewriterules_type_rules.h new file mode 100644 index 000000000..5a0e8c5e0 --- /dev/null +++ b/src/theory/rewriterules/theory_rewriterules_type_rules.h @@ -0,0 +1,98 @@ +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_TYPE_RULES_H +#define __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_TYPE_RULES_H + +#include "node_manager.h" + +namespace CVC4 { +namespace theory { +namespace rewriterules { + +class RewriteRuleTypeRule { +public: + + /** + * Compute the type for (and optionally typecheck) a term belonging + * to the theory of rewriterules. + * + * @param check if true, the node's type should be checked as well + * as computed. + */ + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, + bool check) + throw(TypeCheckingExceptionPrivate) { + Debug("typecheck-r") << "type check for rr " << n << std::endl; + Assert(n.getKind() == kind::REWRITE_RULE && n.getNumChildren()==3 ); + if( check ){ + if( n[ 0 ].getType(check)!=nodeManager->boundVarListType() ){ + throw TypeCheckingExceptionPrivate(n, + "first argument of rewrite rule is not bound var list"); + } + if( n[ 1 ].getType(check)!=nodeManager->booleanType() ){ + throw TypeCheckingExceptionPrivate(n, + "guard of rewrite rule is not an actual guard"); + } + if( n[2].getType(check) != + TypeNode(nodeManager->mkTypeConst<TypeConstant>(RRHB_TYPE))){ + throw TypeCheckingExceptionPrivate(n, + "not a correct rewrite rule"); + } + } + return nodeManager->booleanType(); + } +};/* class RewriterulesTypeRule */ + + +class RRRewriteTypeRule { +public: + + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw(TypeCheckingExceptionPrivate) { + Assert(n.getKind() == kind::RR_REWRITE ); + if( check ){ + if( n[0].getType(check)!=n[1].getType(check) ){ + throw TypeCheckingExceptionPrivate(n, + "terms of rewrite rule are not equal"); + } + if( n.getNumChildren() == 3 && n[2].getType(check)!=nodeManager->instPatternListType() ){ + throw TypeCheckingExceptionPrivate(n, "third argument of rewrite rule is not instantiation pattern list"); + } + if( n[0].getKind()!=kind::APPLY_UF ){ + throw TypeCheckingExceptionPrivate(n[0], "head of rewrite rules must start with an uninterpreted symbols. If you want to write a propagation rule, add the guard [true] for disambiguation"); + } + } + return TypeNode(nodeManager->mkTypeConst<TypeConstant>(RRHB_TYPE)); + } +};/* struct QuantifierReductionRuleRule */ + +class RRRedDedTypeRule { +public: + + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw(TypeCheckingExceptionPrivate) { + Assert(n.getKind() == kind::RR_REDUCTION || + n.getKind() == kind::RR_DEDUCTION ); + if( check ){ + if( n[ 0 ].getType(check)!=nodeManager->booleanType() ){ + throw TypeCheckingExceptionPrivate(n, "head of reduction rule is not boolean"); + } + if( n[ 1 ].getType(check)!=nodeManager->booleanType() ){ + throw TypeCheckingExceptionPrivate(n, "body of reduction rule is not boolean"); + } + if( n.getNumChildren() == 3 && n[2].getType(check)!=nodeManager->instPatternListType() ){ + throw TypeCheckingExceptionPrivate(n, "third argument of rewrite rule is not instantiation pattern list"); + } + if( n.getNumChildren() < 3 && n[ 0 ] == nodeManager->mkConst<bool>(true) ){ + throw TypeCheckingExceptionPrivate(n, "A rewrite rule must have one head or one trigger at least"); + } + } + return TypeNode(nodeManager->mkTypeConst<TypeConstant>(RRHB_TYPE)); + } +};/* struct QuantifierReductionRuleRule */ + +}/* CVC4::theory::rewriterules namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_TYPE_RULES_H */ diff --git a/src/theory/shared_terms_database.cpp b/src/theory/shared_terms_database.cpp index b081e27ef..98ab3f10d 100644 --- a/src/theory/shared_terms_database.cpp +++ b/src/theory/shared_terms_database.cpp @@ -52,7 +52,7 @@ void SharedTermsDatabase::addEqualityToPropagate(TNode equality) { void SharedTermsDatabase::addSharedTerm(TNode atom, TNode term, Theory::Set theories) { - Debug("register") << "SharedTermsDatabase::addSharedTerm(" << atom << ", " << term << ", " << Theory::setToString(theories) << ")" << std::endl; + Debug("register") << "SharedTermsDatabase::addSharedTerm(" << atom << ", " << term << ", " << Theory::setToString(theories) << ")" << std::endl; std::pair<TNode, TNode> search_pair(atom, term); SharedTermsTheoriesMap::iterator find = d_termsToTheories.find(search_pair); @@ -64,18 +64,18 @@ void SharedTermsDatabase::addSharedTerm(TNode atom, TNode term, Theory::Set theo d_termsToTheories[search_pair] = theories; } else { Assert(theories != (*find).second); - d_termsToTheories[search_pair] = Theory::setUnion(theories, (*find).second); + d_termsToTheories[search_pair] = Theory::setUnion(theories, (*find).second); } } SharedTermsDatabase::shared_terms_iterator SharedTermsDatabase::begin(TNode atom) const { Assert(hasSharedTerms(atom)); - return d_atomsToTerms.find(atom)->second.begin(); + return d_atomsToTerms.find(atom)->second.begin(); } SharedTermsDatabase::shared_terms_iterator SharedTermsDatabase::end(TNode atom) const { Assert(hasSharedTerms(atom)); - return d_atomsToTerms.find(atom)->second.end(); + return d_atomsToTerms.find(atom)->second.end(); } bool SharedTermsDatabase::hasSharedTerms(TNode atom) const { @@ -89,24 +89,24 @@ void SharedTermsDatabase::backtrack() { list.pop_back(); if (list.empty()) { d_atomsToTerms.erase(atom); - } + } } d_addedSharedTerms.resize(d_addedSharedTermsSize); } Theory::Set SharedTermsDatabase::getTheoriesToNotify(TNode atom, TNode term) const { - // Get the theories that share this term from this atom + // Get the theories that share this term from this atom std::pair<TNode, TNode> search_pair(atom, term); SharedTermsTheoriesMap::iterator find = d_termsToTheories.find(search_pair); - Assert(find != d_termsToTheories.end()); - + Assert(find != d_termsToTheories.end()); + // Get the theories that were already notified Theory::Set alreadyNotified = 0; AlreadyNotifiedMap::iterator theoriesFind = d_alreadyNotifiedMap.find(term); if (theoriesFind != d_alreadyNotifiedMap.end()) { alreadyNotified = (*theoriesFind).second; } - + // Return the ones that haven't been notified yet return Theory::setDifference((*find).second, alreadyNotified); } diff --git a/src/theory/shared_terms_database.h b/src/theory/shared_terms_database.h index fccd2e6bc..c7da8a463 100644 --- a/src/theory/shared_terms_database.h +++ b/src/theory/shared_terms_database.h @@ -2,7 +2,7 @@ /*! \file node_visitor.h ** \verbatim ** Original author: dejan - ** Major contributors: + ** Major contributors: ** Minor contributors (to current version): ** This file is part of the CVC4 prototype. ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) @@ -40,7 +40,7 @@ private: /** The context */ context::Context* d_context; - + /** Some statistics */ IntStat d_statSharedTerms; @@ -49,13 +49,13 @@ private: /** A map from atoms to a list of shared terms */ SharedTermsMap d_atomsToTerms; - + /** Each time we add a shared term, we add it's parent to this list */ std::vector<TNode> d_addedSharedTerms; - + /** Context-dependent size of the d_addedSharedTerms list */ context::CDO<unsigned> d_addedSharedTermsSize; - + /** A map from atoms and subterms to the theories that use it */ typedef context::CDHashMap<std::pair<Node, TNode>, theory::Theory::Set, TNodePairHashFunction> SharedTermsTheoriesMap; SharedTermsTheoriesMap d_termsToTheories; @@ -95,6 +95,11 @@ private: void eqNotifyConstantTermMerge(TNode t1, TNode t2) { d_sharedTerms.conflict(t1, t2, true); } + + void eqNotifyNewClass(TNode t) { } + void eqNotifyPreMerge(TNode t1, TNode t2) { } + void eqNotifyPostMerge(TNode t1, TNode t2) { } + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { } }; /** The notify class for d_equalityEngine */ @@ -147,7 +152,7 @@ public: SharedTermsDatabase(TheoryEngine* theoryEngine, context::Context* context); ~SharedTermsDatabase() throw(AssertionException); - + /** * Asserts the equality to the shared terms database, */ @@ -185,12 +190,12 @@ public: bool hasSharedTerms(TNode atom) const; /** - * Iterator pointing to the first shared term belonging to the given atom. + * Iterator pointing to the first shared term belonging to the given atom. */ shared_terms_iterator begin(TNode atom) const; /** - * Iterator pointing to the end of the list of shared terms belonging to the given atom. + * Iterator pointing to the end of the list of shared terms belonging to the given atom. */ shared_terms_iterator end(TNode atom) const; diff --git a/src/theory/term_registration_visitor.cpp b/src/theory/term_registration_visitor.cpp index 22b87c32f..b0b712356 100644 --- a/src/theory/term_registration_visitor.cpp +++ b/src/theory/term_registration_visitor.cpp @@ -2,10 +2,10 @@ /*! \file term_registration_visitor.h ** \verbatim ** Original author: dejan - ** Major contributors: - ** Minor contributors (to current version): + ** Major contributors: none + ** Minor contributors (to current version): none ** This file is part of the CVC4 prototype. - ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Copyright (c) 2009-2012 The Analysis of Computer Systems Group (ACSys) ** Courant Institute of Mathematical Sciences ** New York University ** See the file COPYING in the top-level source directory for licensing @@ -32,6 +32,14 @@ bool PreRegisterVisitor::alreadyVisited(TNode current, TNode parent) { Debug("register::internal") << "PreRegisterVisitor::alreadyVisited(" << current << "," << parent << ")" << std::endl; + if( ( parent.getKind() == kind::FORALL || + parent.getKind() == kind::EXISTS || + parent.getKind() == kind::REWRITE_RULE ) && + current != parent ) { + Debug("register::internal") << "quantifier:true" << std::endl; + return true; + } + TheoryId currentTheoryId = Theory::theoryOf(current); TheoryId parentTheoryId = Theory::theoryOf(parent); @@ -89,13 +97,21 @@ void PreRegisterVisitor::visit(TNode current, TNode parent) { if (!Theory::setContains(currentTheoryId, visitedTheories)) { visitedTheories = Theory::setInsert(currentTheoryId, visitedTheories); d_visited[current] = visitedTheories; - d_engine->theoryOf(currentTheoryId)->preRegisterTerm(current); + Theory* th = d_engine->theoryOf(currentTheoryId); + th->preRegisterTerm(current); + if(th->getInstantiator() != NULL) { + th->getInstantiator()->preRegisterTerm(current); + } Debug("register::internal") << "PreRegisterVisitor::visit(" << current << "," << parent << "): adding " << currentTheoryId << std::endl; } if (!Theory::setContains(parentTheoryId, visitedTheories)) { visitedTheories = Theory::setInsert(parentTheoryId, visitedTheories); d_visited[current] = visitedTheories; - d_engine->theoryOf(parentTheoryId)->preRegisterTerm(current); + Theory* th = d_engine->theoryOf(parentTheoryId); + th->preRegisterTerm(current); + if(th->getInstantiator() != NULL) { + th->getInstantiator()->preRegisterTerm(current); + } Debug("register::internal") << "PreRegisterVisitor::visit(" << current << "," << parent << "): adding " << parentTheoryId << std::endl; } if (useType) { @@ -103,7 +119,11 @@ void PreRegisterVisitor::visit(TNode current, TNode parent) { if (!Theory::setContains(typeTheoryId, visitedTheories)) { visitedTheories = Theory::setInsert(typeTheoryId, visitedTheories); d_visited[current] = visitedTheories; - d_engine->theoryOf(typeTheoryId)->preRegisterTerm(current); + Theory* th = d_engine->theoryOf(typeTheoryId); + th->preRegisterTerm(current); + if(th->getInstantiator() != NULL) { + th->getInstantiator()->preRegisterTerm(current); + } Debug("register::internal") << "PreRegisterVisitor::visit(" << current << "," << parent << "): adding " << parentTheoryId << std::endl; } } @@ -135,6 +155,13 @@ bool SharedTermsVisitor::alreadyVisited(TNode current, TNode parent) const { Debug("register::internal") << "SharedTermsVisitor::alreadyVisited(" << current << "," << parent << ")" << std::endl; + if( ( parent.getKind() == kind::FORALL || + parent.getKind() == kind::EXISTS || + parent.getKind() == kind::REWRITE_RULE) && + current != parent ) { + Debug("register::internal") << "quantifier:true" << std::endl; + return true; + } TNodeVisitedMap::const_iterator find = d_visited.find(current); // If node is not visited at all, just return false diff --git a/src/theory/theory.cpp b/src/theory/theory.cpp index 1ed1f99ff..7555282d8 100644 --- a/src/theory/theory.cpp +++ b/src/theory/theory.cpp @@ -18,6 +18,8 @@ #include "theory/theory.h" #include "util/Assert.h" +#include "theory/quantifiers_engine.h" +#include "theory/instantiator_default.h" #include <vector> @@ -37,21 +39,32 @@ std::ostream& operator<<(std::ostream& os, Theory::Effort level){ os << "EFFORT_FULL"; break; case Theory::EFFORT_COMBINATION: os << "EFFORT_COMBINATION"; break; + case Theory::EFFORT_LAST_CALL: + os << "EFFORT_LAST_CALL"; break; default: Unreachable(); } return os; }/* ostream& operator<<(ostream&, Theory::Effort) */ +Theory::~Theory() { + if(d_inst != NULL) { + delete d_inst; + d_inst = NULL; + } + + StatisticsRegistry::unregisterStat(&d_computeCareGraphTime); +} + void Theory::addSharedTermInternal(TNode n) { - Debug("sharing") << "Theory::addSharedTerm<" << getId() << ">(" << n << ")" << std::endl; - Debug("theory::assertions") << "Theory::addSharedTerm<" << getId() << ">(" << n << ")" << std::endl; + Debug("sharing") << "Theory::addSharedTerm<" << getId() << ">(" << n << ")" << endl; + Debug("theory::assertions") << "Theory::addSharedTerm<" << getId() << ">(" << n << ")" << endl; d_sharedTerms.push_back(n); addSharedTerm(n); } void Theory::computeCareGraph() { - Debug("sharing") << "Theory::computeCareGraph<" << getId() << ">()" << std::endl; + Debug("sharing") << "Theory::computeCareGraph<" << getId() << ">()" << endl; for (unsigned i = 0; i < d_sharedTerms.size(); ++ i) { TNode a = d_sharedTerms[i]; TypeNode aType = a.getType(); @@ -71,7 +84,7 @@ void Theory::computeCareGraph() { addCarePair(a, b); break; } - } + } } } @@ -85,8 +98,97 @@ void Theory::printFacts(std::ostream& os) const { } void Theory::debugPrintFacts() const{ - cout << "Theory::debugPrintFacts()" << endl; - printFacts(cout); + DebugChannel.getStream() << "Theory::debugPrintFacts()" << endl; + printFacts(DebugChannel.getStream()); +} + +Instantiator::Instantiator(context::Context* c, QuantifiersEngine* qe, Theory* th) : + d_quantEngine(qe), + d_th(th) { +} + +Instantiator::~Instantiator() { +} + +void Instantiator::resetInstantiationRound(Theory::Effort effort) { + for(int i = 0; i < (int) d_instStrategies.size(); ++i) { + if(isActiveStrategy(d_instStrategies[i])) { + d_instStrategies[i]->resetInstantiationRound(effort); + } + } + processResetInstantiationRound(effort); +} + +int Instantiator::doInstantiation(Node f, Theory::Effort effort, int e, int limitInst) { + if(hasConstraintsFrom(f)) { + int origLemmas = d_quantEngine->getNumLemmasWaiting(); + int status = process(f, effort, e, limitInst); + if(limitInst <= 0 || (d_quantEngine->getNumLemmasWaiting()-origLemmas) < limitInst) { + if(d_instStrategies.empty()) { + Debug("inst-engine-inst") << "There are no instantiation strategies allocated." << endl; + } else { + for(int i = 0; i < (int) d_instStrategies.size(); ++i) { + if(isActiveStrategy(d_instStrategies[i])) { + Debug("inst-engine-inst") << d_instStrategies[i]->identify() << " process " << effort << endl; + //call the instantiation strategy's process method + int s_limitInst = limitInst > 0 ? limitInst - (d_quantEngine->getNumLemmasWaiting() - origLemmas) : 0; + int s_status = d_instStrategies[i]->doInstantiation(f, effort, e, s_limitInst); + Debug("inst-engine-inst") << " -> status is " << s_status << endl; + if(limitInst > 0 && (d_quantEngine->getNumLemmasWaiting() - origLemmas) >= limitInst) { + Assert( (d_quantEngine->getNumLemmasWaiting() - origLemmas) == limitInst ); + i = (int) d_instStrategies.size(); + status = InstStrategy::STATUS_UNKNOWN; + } else { + InstStrategy::updateStatus(status, s_status); + } + } else { + Debug("inst-engine-inst") << d_instStrategies[i]->identify() << " is not active." << endl; + } + } + } + } + return status; + } else { + Debug("inst-engine-inst") << "We have no constraints from this quantifier." << endl; + return InstStrategy::STATUS_SAT; + } +} + +//void Instantiator::doInstantiation(int effort) { +// d_status = InstStrategy::STATUS_SAT; +// for( int q = 0; q < d_quantEngine->getNumQuantifiers(); ++q ) { +// Node f = d_quantEngine->getQuantifier(q); +// if( d_quantEngine->getActive(f) && hasConstraintsFrom(f) ) { +// int d_quantStatus = process( f, effort ); +// InstStrategy::updateStatus( d_status, d_quantStatus ); +// for( int i = 0; i < (int)d_instStrategies.size(); ++i ) { +// if( isActiveStrategy( d_instStrategies[i] ) ) { +// Debug("inst-engine-inst") << d_instStrategies[i]->identify() << " process " << effort << endl; +// //call the instantiation strategy's process method +// d_quantStatus = d_instStrategies[i]->process( f, effort ); +// Debug("inst-engine-inst") << " -> status is " << d_quantStatus << endl; +// InstStrategy::updateStatus( d_status, d_quantStatus ); +// } +// } +// } +// } +//} + +void Instantiator::setHasConstraintsFrom(Node f) { + d_hasConstraints[f] = true; + if(! d_quantEngine->hasOwner(f)) { + d_quantEngine->setOwner(f, getTheory()); + } else if(d_quantEngine->getOwner(f) != getTheory()) { + d_quantEngine->setOwner(f, NULL); + } +} + +bool Instantiator::hasConstraintsFrom(Node f) { + return d_hasConstraints.find(f) != d_hasConstraints.end() && d_hasConstraints[f]; +} + +bool Instantiator::isOwnerOf(Node f) { + return d_quantEngine->hasOwner(f) && d_quantEngine->getOwner(f) == getTheory(); } }/* CVC4::theory namespace */ diff --git a/src/theory/theory.h b/src/theory/theory.h index 020a2b194..8c830f8a2 100644 --- a/src/theory/theory.h +++ b/src/theory/theory.h @@ -46,6 +46,10 @@ class TheoryEngine; namespace theory { +class Instantiator; +class InstStrategy; +class QuantifiersEngine; + /** * Information about an assertion for the theories. */ @@ -72,7 +76,8 @@ struct Assertion { operator Node () const { return assertion; } -}; + +};/* struct Assertion */ /** * A (oredered) pair of terms a theory cares about. @@ -84,7 +89,7 @@ struct CarePair { public: - CarePair(TNode a, TNode b, TheoryId theory) + CarePair(TNode a, TNode b, TheoryId theory) : a(a < b ? a : b), b(a < b ? b : a), theory(theory) {} bool operator == (const CarePair& other) const { @@ -99,7 +104,7 @@ public: return b < other.b; } -}; +};/* struct CarePair */ /** * A set of care pairs. @@ -172,6 +177,18 @@ private: */ CareGraph* d_careGraph; + /** + * Reference to the quantifiers engine (or NULL, if quantifiers are + * not supported or not enabled). + */ + QuantifiersEngine* d_quantEngine; + + /** + * The instantiator for this theory, or NULL if quantifiers are not + * supported or not enabled. + */ + Instantiator* d_inst; + // === STATISTICS === /** time spent in theory combination */ TimerStat d_computeCareGraphTime; @@ -182,6 +199,12 @@ private: return ss.str(); } + /** + * Construct and return the instantiator for the given theory. + * If there is no instantiator class, NULL is returned. + */ + theory::Instantiator* makeInstantiator(context::Context* c, theory::QuantifiersEngine* qe); + protected: /** @@ -208,7 +231,7 @@ protected: * Construct a Theory. */ Theory(TheoryId id, context::Context* satContext, context::UserContext* userContext, - OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) throw() + OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) throw() : d_id(id) , d_satContext(satContext) , d_userContext(userContext) @@ -217,6 +240,8 @@ protected: , d_factsHead(satContext, 0) , d_sharedTermsIndex(satContext, 0) , d_careGraph(0) + , d_quantEngine(qe) + , d_inst(makeInstantiator(satContext, qe)) , d_computeCareGraphTime(statName(id, "computeCareGraphTime")) , d_sharedTerms(satContext) , d_out(&out) @@ -252,19 +277,7 @@ protected: * * @return the next assertion in the assertFact() queue */ - Assertion get() { - Assert( !done(), "Theory`() called with assertion queue empty!" ); - - // Get the assertion - Assertion fact = d_facts[d_factsHead]; - d_factsHead = d_factsHead + 1; - Trace("theory") << "Theory::get() => " << fact << " (" << d_facts.size() - d_factsHead << " left)" << std::endl; - if(Dump.isOn("state")) { - Dump("state") << AssertCommand(fact.assertion.toExpr()); - } - - return fact; - } + inline Assertion get(); const LogicInfo& getLogicInfo() const { return d_logicInfo; @@ -348,13 +361,9 @@ public: } /** - * Destructs a Theory. This implementation does nothing, but we - * need a virtual destructor for safety in case subclasses have a - * destructor. + * Destructs a Theory. */ - virtual ~Theory() { - StatisticsRegistry::unregisterStat(&d_computeCareGraphTime); - } + virtual ~Theory(); /** * Subclasses of Theory may add additional efforts. DO NOT CHECK @@ -376,7 +385,11 @@ public: * Combination effort means that the individual theories are already satisfied, and * it is time to put some effort into propagation of shared term equalities */ - EFFORT_COMBINATION = 150 + EFFORT_COMBINATION = 150, + /** + * Last call effort, reserved for quantifiers. + */ + EFFORT_LAST_CALL = 200 };/* enum Effort */ static inline bool standardEffortOrMore(Effort e) CVC4_CONST_FUNCTION @@ -385,7 +398,7 @@ public: { return e >= EFFORT_STANDARD && e < EFFORT_FULL; } static inline bool fullEffort(Effort e) CVC4_CONST_FUNCTION { return e == EFFORT_FULL; } - static inline bool combination(Effort e) CVC4_CONST_FUNCTION + static inline bool combination(Effort e) CVC4_CONST_FUNCTION { return e == EFFORT_COMBINATION; } /** @@ -424,6 +437,41 @@ public: } /** + * Get the valuation associated to this theory. + */ + Valuation& getValuation() { + return d_valuation; + } + + /** + * Get the quantifiers engine associated to this theory. + */ + QuantifiersEngine* getQuantifiersEngine() { + return d_quantEngine; + } + + /** + * Get the quantifiers engine associated to this theory (const version). + */ + const QuantifiersEngine* getQuantifiersEngine() const { + return d_quantEngine; + } + + /** + * Get the theory instantiator. + */ + Instantiator* getInstantiator() { + return d_inst; + } + + /** + * Get the theory instantiator (const version). + */ + const Instantiator* getInstantiator() const { + return d_inst; + } + + /** * Pre-register a term. Done one time for a Node, ever. */ virtual void preRegisterTerm(TNode) { } @@ -455,7 +503,7 @@ public: } /** - * Return the status of two terms in the current context. Should be implemented in + * Return the status of two terms in the current context. Should be implemented in * sub-theories to enable more efficient theory-combination. */ virtual EqualityStatus getEqualityStatus(TNode a, TNode b) { return EQUALITY_UNKNOWN; } @@ -573,6 +621,11 @@ public: virtual Node ppRewrite(TNode atom) { return atom; } /** + * Don't preprocess subterm of this term + */ + virtual bool ppDontRewriteSubterm(TNode atom) { return false; } + + /** * A Theory is called with presolve exactly one time per user * check-sat. presolve() is called after preregistration, * rewriting, and Boolean propagation, (other theories' @@ -664,7 +717,7 @@ public: static inline Set setIntersection(Set a, Set b) { return a & b; - } + } static inline Set setUnion(Set a, Set b) { return a | b; @@ -735,6 +788,84 @@ public: std::ostream& operator<<(std::ostream& os, Theory::Effort level); +class Instantiator { + friend class QuantifiersEngine; +protected: + /** reference to the quantifiers engine */ + QuantifiersEngine* d_quantEngine; + /** reference to the theory that it looks at */ + Theory* d_th; + /** instantiation strategies */ + std::vector< InstStrategy* > d_instStrategies; + /** instantiation strategies active */ + std::map< InstStrategy*, bool > d_instStrategyActive; + /** has constraints from quantifier */ + std::map< Node, bool > d_hasConstraints; + /** is instantiation strategy active */ + bool isActiveStrategy( InstStrategy* is ) { + return d_instStrategyActive.find( is )!=d_instStrategyActive.end() && d_instStrategyActive[is]; + } + /** add inst strategy */ + void addInstStrategy( InstStrategy* is ){ + d_instStrategies.push_back( is ); + d_instStrategyActive[is] = true; + } + /** reset instantiation round */ + virtual void processResetInstantiationRound( Theory::Effort effort ) = 0; + /** process quantifier */ + virtual int process( Node f, Theory::Effort effort, int e, int limitInst = 0 ) = 0; +public: + /** set has constraints from quantifier f */ + void setHasConstraintsFrom( Node f ); + /** has constraints from */ + bool hasConstraintsFrom( Node f ); + /** is full owner of quantifier f? */ + bool isOwnerOf( Node f ); +public: + Instantiator(context::Context* c, QuantifiersEngine* qe, Theory* th); + virtual ~Instantiator(); + + /** get quantifiers engine */ + QuantifiersEngine* getQuantifiersEngine() { return d_quantEngine; } + /** get corresponding theory for this instantiator */ + Theory* getTheory() { return d_th; } + /** Pre-register a term. */ + virtual void preRegisterTerm( Node t ) { } + /** assertNode function, assertion was asserted to theory */ + virtual void assertNode( Node assertion ){} + /** reset instantiation round */ + void resetInstantiationRound( Theory::Effort effort ); + /** do instantiation method*/ + int doInstantiation( Node f, Theory::Effort effort, int e, int limitInst = 0 ); + /** identify */ + virtual std::string identify() const { return std::string("Unknown"); } + /** print debug information */ + virtual void debugPrint( const char* c ) {} + /** get status */ + //int getStatus() { return d_status; } +};/* class Instantiator */ + +inline Assertion Theory::get() { + Assert( !done(), "Theory::get() called with assertion queue empty!" ); + + // Get the assertion + Assertion fact = d_facts[d_factsHead]; + d_factsHead = d_factsHead + 1; + + Trace("theory") << "Theory::get() => " << fact << " (" << d_facts.size() - d_factsHead << " left)" << std::endl; + + if(Dump.isOn("state")) { + Dump("state") << AssertCommand(fact.assertion.toExpr()); + } + + // if quantifiers are turned on and we have an instantiator, notify it + if(getLogicInfo().isQuantified() && getInstantiator() != NULL) { + getInstantiator()->assertNode(fact); + } + + return fact; +} + }/* CVC4::theory namespace */ inline std::ostream& operator<<(std::ostream& out, diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp index 97c17222c..7ea9e063e 100644 --- a/src/theory/theory_engine.cpp +++ b/src/theory/theory_engine.cpp @@ -32,6 +32,9 @@ #include "util/node_visitor.h" #include "util/ite_removal.h" +#include "theory/quantifiers_engine.h" +#include "theory/quantifiers/theory_quantifiers.h" + using namespace std; using namespace CVC4; @@ -46,6 +49,7 @@ TheoryEngine::TheoryEngine(context::Context* context, d_userContext(userContext), d_logicInfo(logicInfo), d_sharedTerms(this, context), + d_quantEngine(NULL), d_ppCache(), d_possiblePropagations(context), d_hasPropagated(context), @@ -69,6 +73,10 @@ TheoryEngine::TheoryEngine(context::Context* context, d_theoryTable[theoryId] = NULL; d_theoryOut[theoryId] = NULL; } + + // initialize the quantifiers engine + d_quantEngine = new QuantifiersEngine(context, this); + Rewriter::init(); StatisticsRegistry::registerStat(&d_combineTheoriesTime); d_true = NodeManager::currentNM()->mkConst<bool>(true); @@ -85,6 +93,8 @@ TheoryEngine::~TheoryEngine() { } } + delete d_quantEngine; + StatisticsRegistry::unregisterStat(&d_combineTheoriesTime); } @@ -320,6 +330,22 @@ void TheoryEngine::check(Theory::Effort effort) { } } + // Must consult quantifiers theory for last call to ensure sat, or otherwise add a lemma + if( effort == Theory::EFFORT_FULL && + d_logicInfo.isQuantified() && + ! d_inConflict && + ! d_lemmasAdded ) { + ((theory::quantifiers::TheoryQuantifiers*) d_theoryTable[THEORY_QUANTIFIERS])->performCheck(Theory::EFFORT_LAST_CALL); + // if we have given up, then possibly flip decision + if(Options::current()->flipDecision) { + if(d_incomplete && !d_inConflict && !d_lemmasAdded) { + if( ((theory::quantifiers::TheoryQuantifiers*) d_theoryTable[THEORY_QUANTIFIERS])->flipDecision() ) { + d_incomplete = false; + } + } + } + } + Debug("theory") << "TheoryEngine::check(" << effort << "): done, we are " << (d_inConflict ? "unsat" : "sat") << (d_lemmasAdded ? " with new lemmas" : " with no new lemmas") << std::endl; } catch(const theory::Interrupted&) { @@ -596,7 +622,7 @@ void TheoryEngine::shutdown() { // Shutdown all the theories for(TheoryId theoryId = theory::THEORY_FIRST; theoryId < theory::THEORY_LAST; ++theoryId) { if(d_theoryTable[theoryId]) { - theoryOf(static_cast<TheoryId>(theoryId))->shutdown(); + theoryOf(theoryId)->shutdown(); } } @@ -624,15 +650,21 @@ Node TheoryEngine::ppTheoryRewrite(TNode term) return theoryOf(term)->ppRewrite(term); } Trace("theory-pp") << "ppTheoryRewrite { " << term << endl; - NodeBuilder<> newNode(term.getKind()); - if (term.getMetaKind() == kind::metakind::PARAMETERIZED) { - newNode << term.getOperator(); - } - unsigned i; - for (i = 0; i < nc; ++i) { - newNode << ppTheoryRewrite(term[i]); + + Node newTerm; + if (theoryOf(term)->ppDontRewriteSubterm(term)) { + newTerm = term; + } else { + NodeBuilder<> newNode(term.getKind()); + if (term.getMetaKind() == kind::metakind::PARAMETERIZED) { + newNode << term.getOperator(); + } + unsigned i; + for (i = 0; i < nc; ++i) { + newNode << ppTheoryRewrite(term[i]); + } + newTerm = Rewriter::rewrite(Node(newNode)); } - Node newTerm = Rewriter::rewrite(Node(newNode)); Node newTerm2 = theoryOf(newTerm)->ppRewrite(newTerm); if (newTerm != newTerm2) { newTerm = ppTheoryRewrite(Rewriter::rewrite(newTerm2)); diff --git a/src/theory/theory_engine.h b/src/theory/theory_engine.h index ed95149b3..ff7648843 100644 --- a/src/theory/theory_engine.h +++ b/src/theory/theory_engine.h @@ -72,6 +72,10 @@ struct NodeTheoryPairHashFunction { } };/* struct NodeTheoryPairHashFunction */ +namespace theory { + class Instantiator; +}/* CVC4::theory namespace */ + /** * This is essentially an abstraction for a collection of theories. A * TheoryEngine provides services to a PropEngine, making various @@ -115,6 +119,11 @@ class TheoryEngine { */ SharedTermsDatabase d_sharedTerms; + /** + * The quantifiers engine + */ + theory::QuantifiersEngine* d_quantEngine; + typedef std::hash_map<Node, Node, NodeHashFunction> NodeMap; typedef std::hash_map<TNode, Node, TNodeHashFunction> TNodeMap; @@ -151,18 +160,22 @@ class TheoryEngine { public: - IntStat conflicts, propagations, lemmas, propagationsAsDecisions; + IntStat conflicts, propagations, lemmas, propagationsAsDecisions, requirePhase, flipDecision; Statistics(theory::TheoryId theory): conflicts(mkName("theory<", theory, ">::conflicts"), 0), propagations(mkName("theory<", theory, ">::propagations"), 0), lemmas(mkName("theory<", theory, ">::lemmas"), 0), - propagationsAsDecisions(mkName("theory<", theory, ">::propagationsAsDecisions"), 0) + propagationsAsDecisions(mkName("theory<", theory, ">::propagationsAsDecisions"), 0), + requirePhase(mkName("theory<", theory, ">::requirePhase"), 0), + flipDecision(mkName("theory<", theory, ">::flipDecision"), 0) { StatisticsRegistry::registerStat(&conflicts); StatisticsRegistry::registerStat(&propagations); StatisticsRegistry::registerStat(&lemmas); StatisticsRegistry::registerStat(&propagationsAsDecisions); + StatisticsRegistry::registerStat(&requirePhase); + StatisticsRegistry::registerStat(&flipDecision); } ~Statistics() { @@ -170,6 +183,8 @@ class TheoryEngine { StatisticsRegistry::unregisterStat(&propagations); StatisticsRegistry::unregisterStat(&lemmas); StatisticsRegistry::unregisterStat(&propagationsAsDecisions); + StatisticsRegistry::unregisterStat(&requirePhase); + StatisticsRegistry::unregisterStat(&flipDecision); } };/* class TheoryEngine::Statistics */ @@ -234,6 +249,21 @@ class TheoryEngine { return d_engine->lemma(lemma, false, removable); } + void requirePhase(TNode n, bool phase) + throw(theory::Interrupted, AssertionException) { + Debug("theory") << "EngineOutputChannel::requirePhase(" + << n << ", " << phase << ")" << std::endl; + ++ d_statistics.requirePhase; + d_engine->d_propEngine->requirePhase(n, phase); + } + + bool flipDecision() + throw(theory::Interrupted, AssertionException) { + Debug("theory") << "EngineOutputChannel::flipDecision()" << std::endl; + ++ d_statistics.flipDecision; + return d_engine->d_propEngine->flipDecision(); + } + void setIncomplete() throw(AssertionException) { d_engine->setIncomplete(d_theory); } @@ -380,7 +410,7 @@ public: inline void addTheory(theory::TheoryId theoryId) { Assert(d_theoryTable[theoryId] == NULL && d_theoryOut[theoryId] == NULL); d_theoryOut[theoryId] = new EngineOutputChannel(this, theoryId); - d_theoryTable[theoryId] = new TheoryClass(d_context, d_userContext, *d_theoryOut[theoryId], theory::Valuation(this), d_logicInfo); + d_theoryTable[theoryId] = new TheoryClass(d_context, d_userContext, *d_theoryOut[theoryId], theory::Valuation(this), d_logicInfo, getQuantifiersEngine()); } inline void setPropEngine(prop::PropEngine* propEngine) { @@ -400,6 +430,13 @@ public: return d_propEngine; } + /** + * Get a pointer to the underlying quantifiers engine. + */ + theory::QuantifiersEngine* getQuantifiersEngine() const { + return d_quantEngine; + } + private: /** @@ -610,6 +647,11 @@ public: return d_theoryTable[theoryId]; } + /** Get the theory for id */ + theory::Theory* getTheory(int id) { + return d_theoryTable[id]; + } + /** * Returns the equality status of the two terms, from the theory * that owns the domain type. The types of a and b must be the same. diff --git a/src/theory/theory_test_utils.h b/src/theory/theory_test_utils.h index bf1370090..44f009bc0 100644 --- a/src/theory/theory_test_utils.h +++ b/src/theory/theory_test_utils.h @@ -94,6 +94,13 @@ public: return LemmaStatus(Node::null(), 0); } + void requirePhase(TNode, bool) throw(Interrupted, AssertionException) { + } + + bool flipDecision() throw(Interrupted, AssertionException) { + return true; + } + void setIncomplete() throw(AssertionException) {} void clear() { diff --git a/src/theory/trigger.cpp b/src/theory/trigger.cpp new file mode 100644 index 000000000..d665fef91 --- /dev/null +++ b/src/theory/trigger.cpp @@ -0,0 +1,555 @@ +/********************* */ +/*! \file trigger.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of trigger class + **/ + +#include "theory/trigger.h" +#include "theory/theory_engine.h" +#include "theory/quantifiers_engine.h" +#include "theory/uf/theory_uf_instantiator.h" +#include "theory/uf/theory_uf_candidate_generator.h" +#include "theory/uf/equality_engine.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; + +//#define NESTED_PATTERN_SELECTION + +Trigger* Trigger::TrTrie::getTrigger2( std::vector< Node >& nodes ){ + if( nodes.empty() ){ + return d_tr; + }else{ + Node n = nodes.back(); + nodes.pop_back(); + if( d_children.find( n )!=d_children.end() ){ + return d_children[n]->getTrigger2( nodes ); + }else{ + return NULL; + } + } +} +void Trigger::TrTrie::addTrigger2( std::vector< Node >& nodes, Trigger* t ){ + if( nodes.empty() ){ + d_tr = t; + }else{ + Node n = nodes.back(); + nodes.pop_back(); + if( d_children.find( n )==d_children.end() ){ + d_children[n] = new TrTrie; + } + d_children[n]->addTrigger2( nodes, t ); + } +} + +/** trigger static members */ +std::map< TNode, std::vector< TNode > > Trigger::d_var_contains; +Trigger::TrTrie Trigger::d_tr_trie; + +/** trigger class constructor */ +Trigger::Trigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, int matchOption, bool smartTriggers ) : +d_quantEngine( qe ), d_f( f ){ + d_nodes.insert( d_nodes.begin(), nodes.begin(), nodes.end() ); + if( smartTriggers ){ + if( d_nodes.size()==1 ){ + if( isSimpleTrigger( d_nodes[0] ) ){ + d_mg = new InstMatchGeneratorSimple( f, d_nodes[0] ); + }else{ + d_mg = new InstMatchGenerator( d_nodes[0], qe, matchOption ); + } + }else{ + d_mg = new InstMatchGeneratorMulti( f, d_nodes, qe, matchOption ); + } + }else{ + d_mg = new InstMatchGenerator( d_nodes, qe, matchOption ); + } + Debug("trigger") << "Trigger for " << f << ": " << std::endl; + for( int i=0; i<(int)d_nodes.size(); i++ ){ + Debug("trigger") << " " << d_nodes[i] << std::endl; + } + Debug("trigger") << std::endl; + if( d_nodes.size()==1 ){ + if( isSimpleTrigger( d_nodes[0] ) ){ + ++(qe->d_statistics.d_triggers); + }else{ + ++(qe->d_statistics.d_simple_triggers); + } + }else{ + Debug("multi-trigger") << "Multi-trigger " << (*this) << std::endl; + //Notice() << "Multi-trigger for " << f << " : " << std::endl; + //Notice() << " " << (*this) << std::endl; + ++(qe->d_statistics.d_multi_triggers); + } + //Notice() << "Trigger : " << (*this) << " for " << f << std::endl; + if( Options::current()->eagerInstQuant ){ + Theory* th_uf = qe->getTheoryEngine()->getTheory( theory::THEORY_UF ); + uf::InstantiatorTheoryUf* ith = (uf::InstantiatorTheoryUf*)th_uf->getInstantiator(); + for( int i=0; i<(int)d_nodes.size(); i++ ){ + ith->registerTrigger( this, d_nodes[i].getOperator() ); + } + } +} +void Trigger::computeVarContains( Node n ) { + if( d_var_contains.find( n )==d_var_contains.end() ){ + d_var_contains[n].clear(); + computeVarContains2( n, n ); + } +} + +void Trigger::computeVarContains2( Node n, Node parent ){ + if( n.getKind()==INST_CONSTANT ){ + if( std::find( d_var_contains[parent].begin(), d_var_contains[parent].end(), n )==d_var_contains[parent].end() ){ + d_var_contains[parent].push_back( n ); + } + }else{ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + computeVarContains2( n[i], parent ); + } + } +} + +void Trigger::resetInstantiationRound(){ + d_mg->resetInstantiationRound( d_quantEngine ); +} + +void Trigger::reset( Node eqc ){ + d_mg->reset( eqc, d_quantEngine ); +} + +bool Trigger::getNextMatch( InstMatch& m ){ + bool retVal = d_mg->getNextMatch( m, d_quantEngine ); + //m.makeInternal( d_quantEngine->getEqualityQuery() ); + return retVal; +} + +bool Trigger::getMatch( Node t, InstMatch& m ){ + //FIXME: this assumes d_mg is an inst match generator + return ((InstMatchGenerator*)d_mg)->getMatch( t, m, d_quantEngine ); +} + +int Trigger::addTerm( Node t ){ + return d_mg->addTerm( d_f, t, d_quantEngine ); +} + +int Trigger::addInstantiations( InstMatch& baseMatch, int instLimit, bool addSplits ){ + int addedLemmas = d_mg->addInstantiations( d_f, baseMatch, d_quantEngine, instLimit, addSplits ); + if( addedLemmas>0 ){ + Debug("inst-trigger") << "Added " << addedLemmas << " lemmas, trigger was "; + for( int i=0; i<(int)d_nodes.size(); i++ ){ + Debug("inst-trigger") << d_nodes[i] << " "; + } + Debug("inst-trigger") << std::endl; + } + return addedLemmas; +} + +Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, int matchOption, bool keepAll, int trOption, + bool smartTriggers ){ + std::vector< Node > trNodes; + if( !keepAll ){ + //only take nodes that contribute variables to the trigger when added + std::vector< Node > temp; + temp.insert( temp.begin(), nodes.begin(), nodes.end() ); + std::map< Node, bool > vars; + std::map< Node, std::vector< Node > > patterns; + for( int i=0; i<(int)temp.size(); i++ ){ + bool foundVar = false; + computeVarContains( temp[i] ); + for( int j=0; j<(int)d_var_contains[ temp[i] ].size(); j++ ){ + Node v = d_var_contains[ temp[i] ][j]; + if( v.getAttribute(InstConstantAttribute())==f ){ + if( vars.find( v )==vars.end() ){ + vars[ v ] = true; + foundVar = true; + } + } + } + if( foundVar ){ + trNodes.push_back( temp[i] ); + for( int j=0; j<(int)d_var_contains[ temp[i] ].size(); j++ ){ + Node v = d_var_contains[ temp[i] ][j]; + patterns[ v ].push_back( temp[i] ); + } + } + } + //now, minimalize the trigger + for( int i=0; i<(int)trNodes.size(); i++ ){ + bool keepPattern = false; + Node n = trNodes[i]; + for( int j=0; j<(int)d_var_contains[ n ].size(); j++ ){ + Node v = d_var_contains[ n ][j]; + if( patterns[v].size()==1 ){ + keepPattern = true; + break; + } + } + if( !keepPattern ){ + //remove from pattern vector + for( int j=0; j<(int)d_var_contains[ n ].size(); j++ ){ + Node v = d_var_contains[ n ][j]; + for( int k=0; k<(int)patterns[v].size(); k++ ){ + if( patterns[v][k]==n ){ + patterns[v].erase( patterns[v].begin() + k, patterns[v].begin() + k + 1 ); + break; + } + } + } + //remove from trigger nodes + trNodes.erase( trNodes.begin() + i, trNodes.begin() + i + 1 ); + i--; + } + } + }else{ + trNodes.insert( trNodes.begin(), nodes.begin(), nodes.end() ); + } + + //check for duplicate? + if( trOption==TR_MAKE_NEW ){ + //static int trNew = 0; + //static int trOld = 0; + //Trigger* t = d_tr_trie.getTrigger( trNodes ); + //if( t ){ + // trOld++; + //}else{ + // trNew++; + //} + //if( (trNew+trOld)%100==0 ){ + // Notice() << "Trigger new old = " << trNew << " " << trOld << std::endl; + //} + }else{ + Trigger* t = d_tr_trie.getTrigger( trNodes ); + if( t ){ + if( trOption==TR_GET_OLD ){ + //just return old trigger + return t; + }else{ + return NULL; + } + } + } + Trigger* t = new Trigger( qe, f, trNodes, matchOption, smartTriggers ); + d_tr_trie.addTrigger( trNodes, t ); + return t; +} +Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, Node n, int matchOption, bool keepAll, int trOption, bool smartTriggers ){ + std::vector< Node > nodes; + nodes.push_back( n ); + return mkTrigger( qe, f, nodes, matchOption, keepAll, trOption, smartTriggers ); +} + +bool Trigger::isUsableTrigger( std::vector< Node >& nodes, Node f ){ + for( int i=0; i<(int)nodes.size(); i++ ){ + if( !isUsableTrigger( nodes[i], f ) ){ + return false; + } + } + return true; +} + +bool Trigger::isUsable( Node n, Node f ){ + if( n.getAttribute(InstConstantAttribute())==f ){ + if( !isAtomicTrigger( n ) && n.getKind()!=INST_CONSTANT ){ + std::map< Node, Node > coeffs; + return getPatternArithmetic( f, n, coeffs ); + }else{ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( !isUsable( n[i], f ) ){ + return false; + } + } + return true; + } + }else{ + return true; + } +} + +bool Trigger::isUsableTrigger( Node n, Node f ){ + //return n.getAttribute(InstConstantAttribute())==f && n.getKind()==APPLY_UF; + return n.getAttribute(InstConstantAttribute())==f && isAtomicTrigger( n ) && isUsable( n, f ); +} + +bool Trigger::isAtomicTrigger( Node n ){ + return n.getKind()==APPLY_UF || n.getKind()==SELECT || n.getKind()==STORE; +} +bool Trigger::isSimpleTrigger( Node n ){ + if( isAtomicTrigger( n ) ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( n[i].getKind()!=INST_CONSTANT && n[i].hasAttribute(InstConstantAttribute()) ){ + return false; + } + } + return true; + }else{ + return false; + } +} + +/** filter all nodes that have instances */ +void Trigger::filterInstances( std::vector< Node >& nodes ){ + std::vector< bool > active; + active.resize( nodes.size(), true ); + for( int i=0; i<(int)nodes.size(); i++ ){ + for( int j=i+1; j<(int)nodes.size(); j++ ){ + if( active[i] && active[j] ){ + int result = isInstanceOf( nodes[i], nodes[j] ); + if( result==1 ){ + active[j] = false; + }else if( result==-1 ){ + active[i] = false; + } + } + } + } + std::vector< Node > temp; + for( int i=0; i<(int)nodes.size(); i++ ){ + if( active[i] ){ + temp.push_back( nodes[i] ); + } + } + nodes.clear(); + nodes.insert( nodes.begin(), temp.begin(), temp.end() ); +} + + +bool Trigger::collectPatTerms2( QuantifiersEngine* qe, Node f, Node n, std::map< Node, bool >& patMap, int tstrt ){ + if( patMap.find( n )==patMap.end() ){ + patMap[ n ] = false; + if( tstrt==TS_MIN_TRIGGER ){ + if( n.getKind()==FORALL ){ +#ifdef NESTED_PATTERN_SELECTION + //return collectPatTerms2( qe, f, qe->getOrCreateCounterexampleBody( n ), patMap, tstrt ); + return collectPatTerms2( qe, f, qe->getBoundBody( n ), patMap, tstrt ); +#else + return false; +#endif + }else{ + bool retVal = false; + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( collectPatTerms2( qe, f, n[i], patMap, tstrt ) ){ + retVal = true; + } + } + if( retVal ){ + return true; + }else if( isUsableTrigger( n, f ) ){ + patMap[ n ] = true; + return true; + }else{ + return false; + } + } + }else{ + bool retVal = false; + if( isUsableTrigger( n, f ) ){ + patMap[ n ] = true; + if( tstrt==TS_MAX_TRIGGER ){ + return true; + }else{ + retVal = true; + } + } + if( n.getKind()==FORALL ){ +#ifdef NESTED_PATTERN_SELECTION + //if( collectPatTerms2( qe, f, qe->getOrCreateCounterexampleBody( n ), patMap, tstrt ) ){ + // retVal = true; + //} + if( collectPatTerms2( qe, f, qe->getBoundBody( n ), patMap, tstrt ) ){ + retVal = true; + } +#endif + }else{ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( collectPatTerms2( qe, f, n[i], patMap, tstrt ) ){ + retVal = true; + } + } + } + return retVal; + } + }else{ + return patMap[ n ]; + } +} + +void Trigger::collectPatTerms( QuantifiersEngine* qe, Node f, Node n, std::vector< Node >& patTerms, int tstrt, bool filterInst ){ + std::map< Node, bool > patMap; + if( filterInst ){ + //immediately do not consider any term t for which another term is an instance of t + std::vector< Node > patTerms2; + collectPatTerms( qe, f, n, patTerms2, TS_ALL, false ); + std::vector< Node > temp; + temp.insert( temp.begin(), patTerms2.begin(), patTerms2.end() ); + filterInstances( temp ); + if( temp.size()!=patTerms2.size() ){ + Debug("trigger-filter-instance") << "Filtered an instance: " << std::endl; + Debug("trigger-filter-instance") << "Old: "; + for( int i=0; i<(int)patTerms2.size(); i++ ){ + Debug("trigger-filter-instance") << patTerms2[i] << " "; + } + Debug("trigger-filter-instance") << std::endl << "New: "; + for( int i=0; i<(int)temp.size(); i++ ){ + Debug("trigger-filter-instance") << temp[i] << " "; + } + Debug("trigger-filter-instance") << std::endl; + } + if( tstrt==TS_ALL ){ + patTerms.insert( patTerms.begin(), temp.begin(), temp.end() ); + return; + }else{ + //do not consider terms that have instances + for( int i=0; i<(int)patTerms2.size(); i++ ){ + if( std::find( temp.begin(), temp.end(), patTerms2[i] )==temp.end() ){ + patMap[ patTerms2[i] ] = false; + } + } + } + } + collectPatTerms2( qe, f, n, patMap, tstrt ); + for( std::map< Node, bool >::iterator it = patMap.begin(); it != patMap.end(); ++it ){ + if( it->second ){ + patTerms.push_back( it->first ); + } + } +} + +/** is n1 an instance of n2 or vice versa? */ +int Trigger::isInstanceOf( Node n1, Node n2 ){ + if( n1==n2 ){ + return 1; + }else if( n1.getKind()==n2.getKind() ){ + if( n1.getKind()==APPLY_UF ){ + if( n1.getOperator()==n2.getOperator() ){ + int result = 0; + for( int i=0; i<(int)n1.getNumChildren(); i++ ){ + if( n1[i]!=n2[i] ){ + int cResult = isInstanceOf( n1[i], n2[i] ); + if( cResult==0 ){ + return 0; + }else if( cResult!=result ){ + if( result!=0 ){ + return 0; + }else{ + result = cResult; + } + } + } + } + return result; + } + } + return 0; + }else if( n2.getKind()==INST_CONSTANT ){ + computeVarContains( n1 ); + //if( std::find( d_var_contains[ n1 ].begin(), d_var_contains[ n1 ].end(), n2 )!=d_var_contains[ n1 ].end() ){ + // return 1; + //} + if( d_var_contains[ n1 ].size()==1 && d_var_contains[ n1 ][ 0 ]==n2 ){ + return 1; + } + }else if( n1.getKind()==INST_CONSTANT ){ + computeVarContains( n2 ); + //if( std::find( d_var_contains[ n2 ].begin(), d_var_contains[ n2 ].end(), n1 )!=d_var_contains[ n2 ].end() ){ + // return -1; + //} + if( d_var_contains[ n2 ].size()==1 && d_var_contains[ n2 ][ 0 ]==n1 ){ + return 1; + } + } + return 0; +} + +bool Trigger::isVariableSubsume( Node n1, Node n2 ){ + if( n1==n2 ){ + return true; + }else{ + //Notice() << "is variable subsume ? " << n1 << " " << n2 << std::endl; + computeVarContains( n1 ); + computeVarContains( n2 ); + for( int i=0; i<(int)d_var_contains[n2].size(); i++ ){ + if( std::find( d_var_contains[n1].begin(), d_var_contains[n1].end(), d_var_contains[n2][i] )==d_var_contains[n1].end() ){ + //Notice() << "no" << std::endl; + return false; + } + } + //Notice() << "yes" << std::endl; + return true; + } +} + +void Trigger::getVarContains( Node f, std::vector< Node >& pats, std::map< Node, std::vector< Node > >& varContains ){ + for( int i=0; i<(int)pats.size(); i++ ){ + computeVarContains( pats[i] ); + varContains[ pats[i] ].clear(); + for( int j=0; j<(int)d_var_contains[pats[i]].size(); j++ ){ + if( d_var_contains[pats[i]][j].getAttribute(InstConstantAttribute())==f ){ + varContains[ pats[i] ].push_back( d_var_contains[pats[i]][j] ); + } + } + } +} + +void Trigger::getVarContainsNode( Node f, Node n, std::vector< Node >& varContains ){ + computeVarContains( n ); + for( int j=0; j<(int)d_var_contains[n].size(); j++ ){ + if( d_var_contains[n][j].getAttribute(InstConstantAttribute())==f ){ + varContains.push_back( d_var_contains[n][j] ); + } + } +} + +bool Trigger::getPatternArithmetic( Node f, Node n, std::map< Node, Node >& coeffs ){ + if( n.getKind()==PLUS ){ + Assert( coeffs.empty() ); + NodeBuilder<> t(kind::PLUS); + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( n[i].hasAttribute(InstConstantAttribute()) ){ + if( n[i].getKind()==INST_CONSTANT ){ + if( n[i].getAttribute(InstConstantAttribute())==f ){ + coeffs[ n[i] ] = Node::null(); + }else{ + coeffs.clear(); + return false; + } + }else if( !getPatternArithmetic( f, n[i], coeffs ) ){ + coeffs.clear(); + return false; + } + }else{ + t << n[i]; + } + } + if( t.getNumChildren()==0 ){ + coeffs[ Node::null() ] = NodeManager::currentNM()->mkConst( Rational(0) ); + }else if( t.getNumChildren()==1 ){ + coeffs[ Node::null() ] = t.getChild( 0 ); + }else{ + coeffs[ Node::null() ] = t; + } + return true; + }else if( n.getKind()==MULT ){ + if( n[0].getKind()==INST_CONSTANT && n[0].getAttribute(InstConstantAttribute())==f ){ + Assert( !n[1].hasAttribute(InstConstantAttribute()) ); + coeffs[ n[0] ] = n[1]; + return true; + }else if( n[1].getKind()==INST_CONSTANT && n[1].getAttribute(InstConstantAttribute())==f ){ + Assert( !n[0].hasAttribute(InstConstantAttribute()) ); + coeffs[ n[1] ] = n[0]; + return true; + } + } + return false; +} diff --git a/src/theory/trigger.h b/src/theory/trigger.h new file mode 100644 index 000000000..457df0ab4 --- /dev/null +++ b/src/theory/trigger.h @@ -0,0 +1,170 @@ +/********************* */ +/*! \file trigger.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief trigger class + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__TRIGGER_H +#define __CVC4__TRIGGER_H + +#include "theory/inst_match.h" + +namespace CVC4 { +namespace theory { + +//a collect of nodes representing a trigger +class Trigger { +private: + /** computation of variable contains */ + static std::map< TNode, std::vector< TNode > > d_var_contains; + static void computeVarContains( Node n ); + static void computeVarContains2( Node n, Node parent ); +private: + /** the quantifiers engine */ + QuantifiersEngine* d_quantEngine; + /** the quantifier this trigger is for */ + Node d_f; + /** match generators */ + IMGenerator* d_mg; +private: + /** a trie of triggers */ + class TrTrie { + private: + Trigger* getTrigger2( std::vector< Node >& nodes ); + void addTrigger2( std::vector< Node >& nodes, Trigger* t ); + public: + TrTrie() : d_tr( NULL ){} + Trigger* d_tr; + std::map< TNode, TrTrie* > d_children; + Trigger* getTrigger( std::vector< Node >& nodes ){ + std::vector< Node > temp; + temp.insert( temp.begin(), nodes.begin(), nodes.end() ); + std::sort( temp.begin(), temp.end() ); + return getTrigger2( temp ); + } + void addTrigger( std::vector< Node >& nodes, Trigger* t ){ + std::vector< Node > temp; + temp.insert( temp.begin(), nodes.begin(), nodes.end() ); + std::sort( temp.begin(), temp.end() ); + return addTrigger2( temp, t ); + } + };/* class Trigger::TrTrie */ + /** all triggers will be stored in this trie */ + static TrTrie d_tr_trie; +private: + /** trigger constructor */ + Trigger( QuantifiersEngine* ie, Node f, std::vector< Node >& nodes, int matchOption = 0, bool smartTriggers = false ); +public: + ~Trigger(){} +public: + std::vector< Node > d_nodes; +public: + void debugPrint( const char* c ); + IMGenerator* getGenerator() { return d_mg; } +public: + /** reset instantiation round (call this whenever equivalence classes have changed) */ + void resetInstantiationRound(); + /** reset, eqc is the equivalence class to search in (search in any if eqc=null) */ + void reset( Node eqc ); + /** get next match. must call reset( eqc ) once before this function. */ + bool getNextMatch( InstMatch& m ); + /** get the match against ground term or formula t. + the trigger and t should have the same shape. + Currently the trigger should not be a multi-trigger. + */ + bool getMatch( Node t, InstMatch& m); + /** add ground term t, called when t is added to the TermDb */ + int addTerm( Node t ); + /** return true if whatever Node is subsituted for the variables the + given Node can't match the pattern */ + bool nonunifiable( TNode t, const std::vector<Node> & vars){ + return d_mg->nonunifiable(t,vars); + } + /** return whether this is a multi-trigger */ + bool isMultiTrigger() { return d_nodes.size()>1; } +public: + /** add all available instantiations exhaustively, in any equivalence class + if limitInst>0, limitInst is the max # of instantiations to try */ + int addInstantiations( InstMatch& baseMatch, int instLimit = 0, bool addSplits = false ); + /** mkTrigger method + ie : quantifier engine; + f : forall something .... + nodes : (multi-)trigger + matchOption : which policy to use for creating matches (one of InstMatchGenerator::MATCH_GEN_* ) + keepAll: don't remove unneeded patterns; + trOption : policy for dealing with triggers that already existed (see below) + */ + enum{ + TR_MAKE_NEW, //make new trigger even if it already may exist + TR_GET_OLD, //return a previous trigger if it had already been created + TR_RETURN_NULL //return null if a duplicate is found + }; + static Trigger* mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, + int matchOption = 0, bool keepAll = true, int trOption = TR_MAKE_NEW, + bool smartTriggers = false ); + static Trigger* mkTrigger( QuantifiersEngine* qe, Node f, Node n, + int matchOption = 0, bool keepAll = true, int trOption = TR_MAKE_NEW, + bool smartTriggers = false ); +private: + /** is subterm of trigger usable */ + static bool isUsable( Node n, Node f ); + /** collect all APPLY_UF pattern terms for f in n */ + static bool collectPatTerms2( QuantifiersEngine* qe, Node f, Node n, std::map< Node, bool >& patMap, int tstrt ); +public: + //different strategies for choosing trigger terms + enum { + TS_MAX_TRIGGER = 0, + TS_MIN_TRIGGER, + TS_ALL, + }; + static void collectPatTerms( QuantifiersEngine* qe, Node f, Node n, std::vector< Node >& patTerms, int tstrt, bool filterInst = false ); +public: + /** is usable trigger */ + static bool isUsableTrigger( std::vector< Node >& nodes, Node f ); + static bool isUsableTrigger( Node n, Node f ); + static bool isAtomicTrigger( Node n ); + static bool isSimpleTrigger( Node n ); + /** filter all nodes that have instances */ + static void filterInstances( std::vector< Node >& nodes ); + /** -1: n1 is an instance of n2, 1: n1 is an instance of n2 */ + static int isInstanceOf( Node n1, Node n2 ); + /** variables subsume, return true if n1 contains all free variables in n2 */ + static bool isVariableSubsume( Node n1, Node n2 ); + /** get var contains */ + static void getVarContains( Node f, std::vector< Node >& pats, std::map< Node, std::vector< Node > >& varContains ); + static void getVarContainsNode( Node f, Node n, std::vector< Node >& varContains ); + /** get pattern arithmetic */ + static bool getPatternArithmetic( Node f, Node n, std::map< Node, Node >& coeffs ); + + inline void toStream(std::ostream& out) const { + out << "TRIGGER( "; + for( int i=0; i<(int)d_nodes.size(); i++ ){ + if( i>0 ){ out << ", "; } + out << d_nodes[i]; + } + out << " )"; + } +}; + +inline std::ostream& operator<<(std::ostream& out, const Trigger & tr) { + tr.toStream(out); + return out; +} + +}/* CVC4::theory namespace */ + +}/* CVC4 namespace */ + +#endif /* __CVC4__TRIGGER_H */ diff --git a/src/theory/uf/Makefile.am b/src/theory/uf/Makefile.am index 50147b997..2c9cd3b80 100644 --- a/src/theory/uf/Makefile.am +++ b/src/theory/uf/Makefile.am @@ -14,6 +14,14 @@ libuf_la_SOURCES = \ equality_engine_types.h \ equality_engine.cpp \ symmetry_breaker.h \ - symmetry_breaker.cpp + symmetry_breaker.cpp \ + theory_uf_instantiator.h \ + theory_uf_instantiator.cpp \ + theory_uf_strong_solver.h \ + theory_uf_strong_solver.cpp \ + theory_uf_candidate_generator.h \ + theory_uf_candidate_generator.cpp \ + inst_strategy.h \ + inst_strategy.cpp EXTRA_DIST = kinds diff --git a/src/theory/uf/equality_engine.cpp b/src/theory/uf/equality_engine.cpp index 25645c472..96c8e8b59 100644 --- a/src/theory/uf/equality_engine.cpp +++ b/src/theory/uf/equality_engine.cpp @@ -211,6 +211,11 @@ EqualityNodeId EqualityEngine::newNode(TNode node) { Debug("equality") << d_name << "::eq::newNode(" << node << ") => " << newId << std::endl; + // notify e.g. the UF theory strong solver + if (d_performNotify) { + d_notify.eqNotifyNewClass(node); + } + return newId; } @@ -346,7 +351,12 @@ void EqualityEngine::assertEquality(TNode eq, bool polarity, TNode reason) { if (hasTerm(eq[0]) && hasTerm(eq[1]) && areDisequal(eq[0], eq[1], false)) { return; } - + + // notify the theory + if (d_performNotify) { + d_notify.eqNotifyDisequal(eq[0], eq[1], reason); + } + Debug("equality::trigger") << d_name << "::eq::addEquality(" << eq << "," << (polarity ? "true" : "false") << ")" << std::endl; assertEqualityInternal(eq, d_false, reason); @@ -437,6 +447,21 @@ bool EqualityEngine::merge(EqualityNode& class1, EqualityNode& class2, std::vect EqualityNodeId class1Id = class1.getFind(); EqualityNodeId class2Id = class2.getFind(); + Node n1 = d_nodes[class1Id]; + Node n2 = d_nodes[class2Id]; + EqualityNode cc1 = getEqualityNode(n1); + EqualityNode cc2 = getEqualityNode(n2); + bool doNotify = false; + // notify the theory + // the second part of this check is needed due to the internal implementation of this class. + // It ensures that we are merging terms and not operators. + if (d_performNotify && class1Id==cc1.getFind() && class2Id==cc2.getFind()) { + doNotify = true; + } + if (doNotify) { + d_notify.eqNotifyPreMerge(n1, n2); + } + // Check for constant merges bool class1isConstant = d_isConstant[class1Id]; bool class2isConstant = d_isConstant[class2Id]; @@ -559,7 +584,12 @@ bool EqualityEngine::merge(EqualityNode& class1, EqualityNode& class2, std::vect // Now merge the lists class1.merge<true>(class2); - + + // notify the theory + if (doNotify) { + d_notify.eqNotifyPostMerge(n1, n2); + } + // Go through the trigger term disequalities and propagate if (!propagateTriggerTermDisequalities(class1OnlyTags, class1triggerRef, class2disequalitiesToNotify)) { return false; diff --git a/src/theory/uf/equality_engine.h b/src/theory/uf/equality_engine.h index 8cf159cd7..cb0c81872 100644 --- a/src/theory/uf/equality_engine.h +++ b/src/theory/uf/equality_engine.h @@ -39,6 +39,9 @@ namespace CVC4 { namespace theory { namespace eq { +class EqClassesIterator; +class EqClassIterator; + /** * Interface for equality engine notifications. All the notifications * are safe as TNodes, but not necessarily for negations. @@ -62,7 +65,7 @@ public: /** * Notifies about a trigger predicate that became true or false. * - * @param predicate the trigger predicate that bacame true or false + * @param predicate the trigger predicate that became true or false * @param value the value of the predicate */ virtual bool eqNotifyTriggerPredicate(TNode predicate, bool value) = 0; @@ -82,10 +85,43 @@ public: * can do is ask for explanations. * * @param t1 a constant term - * @param t2 a constnat term + * @param t2 a constant term */ virtual void eqNotifyConstantTermMerge(TNode t1, TNode t2) = 0; -}; + + /** + * Notifies about the creation of a new equality class. + * + * @param t the term forming the new class + */ + virtual void eqNotifyNewClass(TNode t) = 0; + + /** + * Notifies about the merge of two classes (just before the merge). + * + * @param t1 a term + * @param t2 a term + */ + virtual void eqNotifyPreMerge(TNode t1, TNode t2) = 0; + + /** + * Notifies about the merge of two classes (just after the merge). + * + * @param t1 a term + * @param t2 a term + */ + virtual void eqNotifyPostMerge(TNode t1, TNode t2) = 0; + + /** + * Notifies about the disequality of two terms. + * + * @param t1 a term + * @param t2 a term + * @param reason the reason + */ + virtual void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) = 0; + +};/* class EqualityEngineNotify */ /** * Implementation of the notification interface that ignores all the @@ -97,7 +133,11 @@ public: bool eqNotifyTriggerPredicate(TNode predicate, bool value) { return true; } bool eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value) { return true; } void eqNotifyConstantTermMerge(TNode t1, TNode t2) { } -}; + void eqNotifyNewClass(TNode t) { } + void eqNotifyPreMerge(TNode t1, TNode t2) { } + void eqNotifyPostMerge(TNode t1, TNode t2) { } + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { } +};/* class EqualityEngineNotifyNone */ /** @@ -106,6 +146,9 @@ public: */ class EqualityEngine : public context::ContextNotifyObj { + friend class EqClassesIterator; + friend class EqClassIterator; + /** Default implementation of the notification object */ static EqualityEngineNotifyNone s_notifyNone; @@ -140,14 +183,14 @@ public: StatisticsRegistry::unregisterStat(&functionTermsCount); StatisticsRegistry::unregisterStat(&constantTermsCount); } - }; + };/* struct EqualityEngine::statistics */ /** * Store the application lookup, with enough information to backtrack */ void storeApplicationLookup(FunctionApplication& funNormalized, EqualityNodeId funId); -private: +//private: /** The context we are using */ context::Context* d_context; @@ -212,7 +255,7 @@ private: /** Equality constructor */ Equality(EqualityNodeId lhs = null_id, EqualityNodeId rhs = null_id) : lhs(lhs), rhs(rhs) {} - }; + };/* struct EqualityEngine::Equality */ /** The ids of the classes we have merged */ std::vector<Equality> d_assertedEqualities; @@ -253,7 +296,7 @@ private: /** The reason of this edge */ TNode getReason() const { return d_reason; } -}; + };/* class EqualityEngine::EqualityEdge */ /** * All the equality edges (twice as many as the number of asserted equalities. If an equality @@ -268,7 +311,7 @@ private: std::string edgesToString(EqualityEdgeId edgeId) const; /** - * Map from a node to it's first edge in the equality graph. Edges are added to the front of the + * Map from a node to its first edge in the equality graph. Edges are added to the front of the * list which makes the insertion/backtracking easy. */ std::vector<EqualityEdgeId> d_equalityGraph; @@ -297,7 +340,7 @@ private: */ bool merge(EqualityNode& class1, EqualityNode& class2, std::vector<TriggerId>& triggers); - /** Undo the mereg of class2 into class1 */ + /** Undo the merge of class2 into class1 */ void undoMerge(EqualityNode& class1, EqualityNode& class2, EqualityNodeId class2Id); /** Backtrack the information if necessary */ @@ -314,7 +357,7 @@ private: Trigger(EqualityNodeId classId = null_id, TriggerId nextTrigger = null_trigger) : classId(classId), nextTrigger(nextTrigger) {} - }; + };/* struct EqualityEngine::Trigger */ /** * Vector of triggers. Triggers come in pairs for an @@ -436,7 +479,7 @@ private: EqualityNodeId getTrigger(TheoryId tag) const { return triggers[Theory::setIndex(tag, tags)]; } - }; + };/* struct EqualityEngine::TriggerTermSet */ /** Internal tags for creating a new set */ Theory::Set d_newSetTags; @@ -451,7 +494,7 @@ private: char* d_triggerDatabase; /** Allocated size of the trigger term database */ - DefaultSizeType d_triggerDatabaseAllocatedSize ; + DefaultSizeType d_triggerDatabaseAllocatedSize; /** Reference for the trigger terms set */ typedef DefaultSizeType TriggerTermSetRef; @@ -482,7 +525,7 @@ private: TriggerTermSetRef oldValue; TriggerSetUpdate(EqualityNodeId classId = null_id, TriggerTermSetRef oldValue = null_set_id) : classId(classId), oldValue(oldValue) {} - }; + };/* struct EqualityEngine::TriggerSetUpdate */ /** * List of trigger updates for backtracking. @@ -685,7 +728,7 @@ public: * Add term to the set of trigger terms with a corresponding tag. The notify class will get * notified when two trigger terms with the same tag become equal or dis-equal. The notification * will not happen on all the terms, but only on the ones that are represent the class. Note that - * a term can be added more than once with different tags, and each tag apperance will merit + * a term can be added more than once with different tags, and each tag appearance will merit * it's own notification. * * @param t the trigger term @@ -743,7 +786,97 @@ public: }; -} // Namespace uf +class EqClassesIterator { + + eq::EqualityEngine* d_ee; + size_t d_it; + +public: + + EqClassesIterator() { } + EqClassesIterator(eq::EqualityEngine* ee) : d_ee(ee) { + d_it = 0; + if ( d_it < d_ee->d_nodesCount && + d_ee->getRepresentative(d_ee->d_nodes[d_it]) != d_ee->d_nodes[d_it] ) { + ++*this; + } + } + Node operator*() { + return d_ee->d_nodes[d_it]; + } + bool operator==(const EqClassesIterator& i) { + return d_ee == i.d_ee && d_it == i.d_it; + } + bool operator!=(const EqClassesIterator& i) { + return !(*this == i); + } + EqClassesIterator& operator++() { + Node orig = d_ee->d_nodes[d_it]; + ++d_it; + while ( d_it<d_ee->d_nodesCount && + ( d_ee->getRepresentative(d_ee->d_nodes[d_it]) != d_ee->d_nodes[d_it] + || d_ee->d_nodes[d_it] == orig ) ) { // this line is necessary for ignoring duplicates + ++d_it; + } + return *this; + } + EqClassesIterator operator++(int) { + EqClassesIterator i = *this; + ++*this; + return i; + } + bool isFinished() { + return d_it>=d_ee->d_nodesCount; + } +};/* class EqClassesIterator */ + +class EqClassIterator { + + Node d_rep; + eq::EqualityNode d_curr; + Node d_curr_node; + eq::EqualityEngine* d_ee; + +public: + + EqClassIterator() { } + EqClassIterator(Node eqc, eq::EqualityEngine* ee) : d_ee(ee) { + Assert( d_ee->getRepresentative(eqc) == eqc ); + d_rep = eqc; + d_curr_node = eqc; + d_curr = d_ee->getEqualityNode(eqc); + } + Node operator*() { + return d_curr_node; + } + bool operator==(const EqClassIterator& i) { + return d_ee == i.d_ee && d_curr_node == i.d_curr_node; + } + bool operator!=(const EqClassIterator& i) { + return !(*this == i); + } + EqClassIterator& operator++() { + Node next = d_ee->d_nodes[ d_curr.getNext() ]; + Assert( d_rep==d_ee->getRepresentative(next) ); + if (d_rep != next) { // we end when we have cycled back to the original representative + d_curr_node = next; + d_curr = d_ee->getEqualityNode(d_curr.getNext()); + } else { + d_curr_node = Node::null(); + } + return *this; + } + EqClassIterator operator++(int) { + EqClassIterator i = *this; + ++*this; + return i; + } + bool isFinished() { + return d_curr_node == Node::null(); + } +};/* class EqClassIterator */ + +} // Namespace eq } // Namespace theory } // Namespace CVC4 diff --git a/src/theory/uf/inst_strategy.cpp b/src/theory/uf/inst_strategy.cpp new file mode 100644 index 000000000..2ca2dcb5a --- /dev/null +++ b/src/theory/uf/inst_strategy.cpp @@ -0,0 +1,412 @@ +/********************* */ +/*! \file inst_strategy.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of theory uf instantiation strategies + **/ + +#include "theory/uf/inst_strategy.h" + +#include "theory/uf/theory_uf_instantiator.h" +#include "theory/theory_engine.h" +#include "theory/uf/theory_uf.h" +#include "theory/uf/equality_engine.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::uf; + +#define USE_SINGLE_TRIGGER_BEFORE_MULTI_TRIGGER +//#define MULTI_TRIGGER_FULL_EFFORT_HALF +#define MULTI_MULTI_TRIGGERS + +struct sortQuantifiersForSymbol { + QuantifiersEngine* d_qe; + bool operator() (Node i, Node j) { + int nqfsi = d_qe->getNumQuantifiersForSymbol( i.getOperator() ); + int nqfsj = d_qe->getNumQuantifiersForSymbol( j.getOperator() ); + if( nqfsi<nqfsj ){ + return true; + }else if( nqfsi>nqfsj ){ + return false; + }else{ + return false; + } + } +}; + + +void InstStrategyCheckCESolved::processResetInstantiationRound( Theory::Effort effort ){ + for( std::map< Node, bool >::iterator it = d_solved.begin(); it != d_solved.end(); ++it ){ + calcSolved( it->first ); + } +} + +int InstStrategyCheckCESolved::process( Node f, Theory::Effort effort, int e, int instLimit ){ + if( e==0 ){ + //calc solved if not done so already + if( d_solved.find( f )==d_solved.end() ){ + calcSolved( f ); + } + //check if f is counterexample-solved + Debug("quant-uf-strategy") << "Try CE-solved.." << std::endl; + if( d_solved[ f ] ){ + if( d_quantEngine->addInstantiation( f, d_th->d_baseMatch[f] ) ){ + ++(d_th->d_statistics.d_instantiations_ce_solved); + //d_quantEngine->d_hasInstantiated[f] = true; + } + d_solved[f] = false; + } + Debug("quant-uf-strategy") << "done." << std::endl; + } + return STATUS_UNKNOWN; +} + +void InstStrategyCheckCESolved::calcSolved( Node f ){ + d_th->d_baseMatch[f].clear(); + d_solved[ f ]= true; + //check if instantiation constants are solved for + for( int j = 0; j<(int)d_quantEngine->getNumInstantiationConstants( f ); j++ ){ + Node i = d_quantEngine->getInstantiationConstant( f, j ); + Node rep = d_th->getInternalRepresentative( i ); + if( !rep.hasAttribute(InstConstantAttribute()) ){ + d_th->d_baseMatch[f].d_map[ i ] = rep; + }else{ + d_solved[ f ] = false; + } + } +} + +void InstStrategyUserPatterns::processResetInstantiationRound( Theory::Effort effort ){ + //reset triggers + for( std::map< Node, std::vector< Trigger* > >::iterator it = d_user_gen.begin(); it != d_user_gen.end(); ++it ){ + for( int i=0; i<(int)it->second.size(); i++ ){ + it->second[i]->resetInstantiationRound(); + it->second[i]->reset( Node::null() ); + } + } +} + +int InstStrategyUserPatterns::process( Node f, Theory::Effort effort, int e, int instLimit ){ + if( e==0 ){ + return STATUS_UNFINISHED; + }else if( e==1 ){ + d_counter[f]++; + Debug("quant-uf-strategy") << "Try user-provided patterns..." << std::endl; + //Notice() << "Try user-provided patterns..." << std::endl; + for( int i=0; i<(int)d_user_gen[f].size(); i++ ){ + bool processTrigger = true; + if( effort!=Theory::EFFORT_LAST_CALL && d_user_gen[f][i]->isMultiTrigger() ){ +//#ifdef MULTI_TRIGGER_FULL_EFFORT_HALF +// processTrigger = d_counter[f]%2==0; +//#endif + } + if( processTrigger ){ + //if( d_user_gen[f][i]->isMultiTrigger() ) + //Notice() << " Process (user) " << (*d_user_gen[f][i]) << " for " << f << "..." << std::endl; + int numInst = d_user_gen[f][i]->addInstantiations( d_th->d_baseMatch[f], instLimit ); + //if( d_user_gen[f][i]->isMultiTrigger() ) + //Notice() << " Done, numInst = " << numInst << "." << std::endl; + d_th->d_statistics.d_instantiations_user_pattern += numInst; + if( d_user_gen[f][i]->isMultiTrigger() ){ + d_quantEngine->d_statistics.d_multi_trigger_instantiations += numInst; + } + //d_quantEngine->d_hasInstantiated[f] = true; + } + } + Debug("quant-uf-strategy") << "done." << std::endl; + //Notice() << "done" << std::endl; + } + return STATUS_UNKNOWN; +} + +void InstStrategyUserPatterns::addUserPattern( Node f, Node pat ){ + //add to generators + std::vector< Node > nodes; + for( int i=0; i<(int)pat.getNumChildren(); i++ ){ + nodes.push_back( pat[i] ); + } + if( Trigger::isUsableTrigger( nodes, f ) ){ + //extend to literal matching + d_quantEngine->getPhaseReqTerms( f, nodes ); + //check match option + int matchOption = Options::current()->efficientEMatching ? InstMatchGenerator::MATCH_GEN_EFFICIENT_E_MATCH : 0; + d_user_gen[f].push_back( Trigger::mkTrigger( d_quantEngine, f, nodes, matchOption, true, Trigger::TR_MAKE_NEW, + Options::current()->smartTriggers ) ); + } +} + +void InstStrategyAutoGenTriggers::processResetInstantiationRound( Theory::Effort effort ){ + //reset triggers + for( std::map< Node, std::map< Trigger*, bool > >::iterator it = d_auto_gen_trigger.begin(); it != d_auto_gen_trigger.end(); ++it ){ + for( std::map< Trigger*, bool >::iterator itt = it->second.begin(); itt != it->second.end(); ++itt ){ + itt->first->resetInstantiationRound(); + itt->first->reset( Node::null() ); + } + } +} + +int InstStrategyAutoGenTriggers::process( Node f, Theory::Effort effort, int e, int instLimit ){ + int peffort = f.getNumChildren()==3 ? 2 : 1; + //int peffort = f.getNumChildren()==3 ? 2 : 1; + //int peffort = 1; + if( e<peffort ){ + return STATUS_UNFINISHED; + }else{ + bool gen = false; + if( e==peffort ){ + if( d_counter.find( f )==d_counter.end() ){ + d_counter[f] = 0; + gen = true; + }else{ + d_counter[f]++; + gen = d_regenerate && d_counter[f]%d_regenerate_frequency==0; + } + }else{ + gen = true; + } + if( gen ){ + generateTriggers( f ); + } + Debug("quant-uf-strategy") << "Try auto-generated triggers... " << d_tr_strategy << " " << e << std::endl; + //Notice() << "Try auto-generated triggers..." << std::endl; + for( std::map< Trigger*, bool >::iterator itt = d_auto_gen_trigger[f].begin(); itt != d_auto_gen_trigger[f].end(); ++itt ){ + Trigger* tr = itt->first; + if( tr ){ + bool processTrigger = itt->second; + if( effort!=Theory::EFFORT_LAST_CALL && tr->isMultiTrigger() ){ +#ifdef MULTI_TRIGGER_FULL_EFFORT_HALF + processTrigger = d_counter[f]%2==0; +#endif + } + if( processTrigger ){ + //if( tr->isMultiTrigger() ) + Debug("quant-uf-strategy-auto-gen-triggers") << " Process " << (*tr) << "..." << std::endl; + int numInst = tr->addInstantiations( d_th->d_baseMatch[f], instLimit ); + //if( tr->isMultiTrigger() ) + Debug("quant-uf-strategy-auto-gen-triggers") << " Done, numInst = " << numInst << "." << std::endl; + if( d_tr_strategy==Trigger::TS_MIN_TRIGGER ){ + d_th->d_statistics.d_instantiations_auto_gen_min += numInst; + }else{ + d_th->d_statistics.d_instantiations_auto_gen += numInst; + } + if( tr->isMultiTrigger() ){ + d_quantEngine->d_statistics.d_multi_trigger_instantiations += numInst; + } + //d_quantEngine->d_hasInstantiated[f] = true; + } + } + } + Debug("quant-uf-strategy") << "done." << std::endl; + //Notice() << "done" << std::endl; + } + return STATUS_UNKNOWN; +} + +void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ + Debug("auto-gen-trigger") << "Generate trigger for " << f << std::endl; + if( d_patTerms[0].find( f )==d_patTerms[0].end() ){ + //determine all possible pattern terms based on trigger term selection strategy d_tr_strategy + d_patTerms[0][f].clear(); + d_patTerms[1][f].clear(); + std::vector< Node > patTermsF; + Trigger::collectPatTerms( d_quantEngine, f, d_quantEngine->getCounterexampleBody( f ), patTermsF, d_tr_strategy, true ); + Debug("auto-gen-trigger") << "Collected pat terms for " << d_quantEngine->getCounterexampleBody( f ) << std::endl; + Debug("auto-gen-trigger") << " "; + for( int i=0; i<(int)patTermsF.size(); i++ ){ + Debug("auto-gen-trigger") << patTermsF[i] << " "; + } + Debug("auto-gen-trigger") << std::endl; + //extend to literal matching + d_quantEngine->getPhaseReqTerms( f, patTermsF ); + //sort into single/multi triggers + std::map< Node, std::vector< Node > > varContains; + Trigger::getVarContains( f, patTermsF, varContains ); + for( std::map< Node, std::vector< Node > >::iterator it = varContains.begin(); it != varContains.end(); ++it ){ + if( it->second.size()==f[0].getNumChildren() ){ + d_patTerms[0][f].push_back( it->first ); + d_is_single_trigger[ it->first ] = true; + }else{ + d_patTerms[1][f].push_back( it->first ); + d_is_single_trigger[ it->first ] = false; + } + } + d_made_multi_trigger[f] = false; + Debug("auto-gen-trigger") << "Single triggers for " << f << " : " << std::endl; + Debug("auto-gen-trigger") << " "; + for( int i=0; i<(int)d_patTerms[0][f].size(); i++ ){ + Debug("auto-gen-trigger") << d_patTerms[0][f][i] << " "; + } + Debug("auto-gen-trigger") << std::endl; + Debug("auto-gen-trigger") << "Multi-trigger term pool for " << f << " : " << std::endl; + Debug("auto-gen-trigger") << " "; + for( int i=0; i<(int)d_patTerms[1][f].size(); i++ ){ + Debug("auto-gen-trigger") << d_patTerms[1][f][i] << " "; + } + Debug("auto-gen-trigger") << std::endl; + } + + //populate candidate pattern term vector for the current trigger + std::vector< Node > patTerms; +#ifdef USE_SINGLE_TRIGGER_BEFORE_MULTI_TRIGGER + //try to add single triggers first + for( int i=0; i<(int)d_patTerms[0][f].size(); i++ ){ + if( !d_single_trigger_gen[d_patTerms[0][f][i]] ){ + patTerms.push_back( d_patTerms[0][f][i] ); + } + } + //if no single triggers exist, add multi trigger terms + if( patTerms.empty() ){ + patTerms.insert( patTerms.begin(), d_patTerms[1][f].begin(), d_patTerms[1][f].end() ); + } +#else + patTerms.insert( patTerms.begin(), d_patTerms[0][f].begin(), d_patTerms[0][f].end() ); + patTerms.insert( patTerms.begin(), d_patTerms[1][f].begin(), d_patTerms[1][f].end() ); +#endif + + if( !patTerms.empty() ){ + Debug("auto-gen-trigger") << "Generate trigger for " << f << std::endl; + //sort terms based on relevance + if( d_rlv_strategy==RELEVANCE_DEFAULT ){ + sortQuantifiersForSymbol sqfs; + sqfs.d_qe = d_quantEngine; + //sort based on # occurrences (this will cause Trigger to select rarer symbols) + std::sort( patTerms.begin(), patTerms.end(), sqfs ); + Debug("relevant-trigger") << "Terms based on relevance: " << std::endl; + for( int i=0; i<(int)patTerms.size(); i++ ){ + Debug("relevant-trigger") << " " << patTerms[i] << " ("; + Debug("relevant-trigger") << d_quantEngine->getNumQuantifiersForSymbol( patTerms[i].getOperator() ) << ")" << std::endl; + } + //Notice() << "Terms based on relevance: " << std::endl; + //for( int i=0; i<(int)patTerms.size(); i++ ){ + // Notice() << " " << patTerms[i] << " ("; + // Notice() << d_quantEngine->getNumQuantifiersForSymbol( patTerms[i].getOperator() ) << ")" << std::endl; + //} + } + //now, generate the trigger... + int matchOption = Options::current()->efficientEMatching ? InstMatchGenerator::MATCH_GEN_EFFICIENT_E_MATCH : 0; + Trigger* tr = NULL; + if( d_is_single_trigger[ patTerms[0] ] ){ + tr = Trigger::mkTrigger( d_quantEngine, f, patTerms[0], matchOption, false, Trigger::TR_RETURN_NULL, + Options::current()->smartTriggers ); + d_single_trigger_gen[ patTerms[0] ] = true; + }else{ + //if we are re-generating triggers, shuffle based on some method + if( d_made_multi_trigger[f] ){ +#ifndef MULTI_MULTI_TRIGGERS + return; +#endif + std::random_shuffle( patTerms.begin(), patTerms.end() ); //shuffle randomly + }else{ + d_made_multi_trigger[f] = true; + } + //will possibly want to get an old trigger + tr = Trigger::mkTrigger( d_quantEngine, f, patTerms, matchOption, false, Trigger::TR_GET_OLD, + Options::current()->smartTriggers ); + } + if( tr ){ + if( tr->isMultiTrigger() ){ + //disable all other multi triggers + for( std::map< Trigger*, bool >::iterator it = d_auto_gen_trigger[f].begin(); it != d_auto_gen_trigger[f].end(); ++it ){ + if( it->first->isMultiTrigger() ){ + d_auto_gen_trigger[f][ it->first ] = false; + } + } + } + //making it during an instantiation round, so must reset + if( d_auto_gen_trigger[f].find( tr )==d_auto_gen_trigger[f].end() ){ + tr->resetInstantiationRound(); + tr->reset( Node::null() ); + } + d_auto_gen_trigger[f][tr] = true; + //if we are generating additional triggers... + if( d_generate_additional && d_is_single_trigger[ patTerms[0] ] ){ + int index = 0; + if( index<(int)patTerms.size() ){ + //Notice() << "check add additional" << std::endl; + //check if similar patterns exist, and if so, add them additionally + int nqfs_curr = d_quantEngine->getNumQuantifiersForSymbol( patTerms[0].getOperator() ); + index++; + bool success = true; + while( success && index<(int)patTerms.size() && d_is_single_trigger[ patTerms[index] ] ){ + success = false; + if( d_quantEngine->getNumQuantifiersForSymbol( patTerms[index].getOperator() )<=nqfs_curr ){ + d_single_trigger_gen[ patTerms[index] ] = true; + Trigger* tr2 = Trigger::mkTrigger( d_quantEngine, f, patTerms[index], matchOption, false, Trigger::TR_RETURN_NULL, + Options::current()->smartTriggers ); + if( tr2 ){ + //Notice() << "Add additional trigger " << patTerms[index] << std::endl; + tr2->resetInstantiationRound(); + tr2->reset( Node::null() ); + d_auto_gen_trigger[f][tr2] = true; + } + success = true; + } + index++; + } + //Notice() << "done check add additional" << std::endl; + } + } + } + } +} + +#if 0 + +void InstStrategyAddFailSplits::processResetInstantiationRound( Theory::Effort effort ){ +} + +int InstStrategyAddFailSplits::process( Node f, Theory::Effort effort, int e, int instLimit ){ + if( e<4 ){ + return STATUS_UNFINISHED; + }else{ + for( std::map< Node, std::map< Node, std::vector< InstMatchGenerator* > > >::iterator it = InstMatchGenerator::d_match_fails.begin(); + it != InstMatchGenerator::d_match_fails.end(); ++it ){ + for( std::map< Node, std::vector< InstMatchGenerator* > >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){ + if( !it2->second.empty() ){ + Node n1 = it->first; + Node n2 = it2->first; + if( !d_quantEngine->getEqualityQuery()->areEqual( n1, n2 ) && !d_quantEngine->getEqualityQuery()->areDisequal( n1, n2 ) ){ + d_quantEngine->addSplitEquality( n1, n2, true ); + } + it2->second.clear(); + } + } + } + return STATUS_UNKNOWN; + } +} + +#endif /* 0 */ + +void InstStrategyFreeVariable::processResetInstantiationRound( Theory::Effort effort ){ +} + +int InstStrategyFreeVariable::process( Node f, Theory::Effort effort, int e, int instLimit ){ + if( e<5 ){ + return STATUS_UNFINISHED; + }else{ + if( d_guessed.find( f )==d_guessed.end() ){ + d_guessed[f] = true; + Debug("quant-uf-alg") << "Add guessed instantiation" << std::endl; + InstMatch m; + if( d_quantEngine->addInstantiation( f, m ) ){ + ++(d_th->d_statistics.d_instantiations_guess); + //d_quantEngine->d_hasInstantiated[f] = true; + } + } + return STATUS_UNKNOWN; + } +} diff --git a/src/theory/uf/inst_strategy.h b/src/theory/uf/inst_strategy.h new file mode 100644 index 000000000..906169811 --- /dev/null +++ b/src/theory/uf/inst_strategy.h @@ -0,0 +1,179 @@ +/********************* */ +/*! \file inst_strategy.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Theory uf instantiation strategies + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__INST_STRATEGY_H +#define __CVC4__INST_STRATEGY_H + +#include "theory/quantifiers_engine.h" + +#include "context/context.h" +#include "context/context_mm.h" + +#include "util/stats.h" +#include "theory/uf/theory_uf.h" + +namespace CVC4 { +namespace theory { +namespace uf { + +class InstantiatorTheoryUf; + +//instantiation strategies + +class InstStrategyCheckCESolved : public InstStrategy{ +private: + /** InstantiatorTheoryUf class */ + InstantiatorTheoryUf* d_th; + /** is solved? */ + std::map< Node, bool > d_solved; + /** calc if f is solved */ + void calcSolved( Node f ); + /** process functions */ + void processResetInstantiationRound( Theory::Effort effort ); + int process( Node f, Theory::Effort effort, int e, int instLimit ); +public: + InstStrategyCheckCESolved( InstantiatorTheoryUf* th, QuantifiersEngine* ie ) : + InstStrategy( ie ), d_th( th ){} + ~InstStrategyCheckCESolved(){} + /** identify */ + std::string identify() const { return std::string("CheckCESolved"); } +};/* class InstStrategyCheckCESolved */ + +class InstStrategyUserPatterns : public InstStrategy{ +private: + /** InstantiatorTheoryUf class */ + InstantiatorTheoryUf* d_th; + /** explicitly provided patterns */ + std::map< Node, std::vector< Trigger* > > d_user_gen; + /** counter for quantifiers */ + std::map< Node, int > d_counter; + /** process functions */ + void processResetInstantiationRound( Theory::Effort effort ); + int process( Node f, Theory::Effort effort, int e, int instLimit ); +public: + InstStrategyUserPatterns( InstantiatorTheoryUf* th, QuantifiersEngine* ie ) : + InstStrategy( ie ), d_th( th ){} + ~InstStrategyUserPatterns(){} +public: + /** add pattern */ + void addUserPattern( Node f, Node pat ); + /** get num patterns */ + int getNumUserGenerators( Node f ) { return (int)d_user_gen[f].size(); } + /** get user pattern */ + Trigger* getUserGenerator( Node f, int i ) { return d_user_gen[f][ i ]; } + /** identify */ + std::string identify() const { return std::string("UserPatterns"); } +};/* class InstStrategyUserPatterns */ + +class InstStrategyAutoGenTriggers : public InstStrategy{ +public: + enum { + RELEVANCE_NONE, + RELEVANCE_DEFAULT, + }; +private: + /** InstantiatorTheoryUf class */ + InstantiatorTheoryUf* d_th; + /** trigger generation strategy */ + int d_tr_strategy; + /** relevance strategy */ + int d_rlv_strategy; + /** regeneration */ + bool d_regenerate; + int d_regenerate_frequency; + /** generate additional triggers */ + bool d_generate_additional; + /** triggers for each quantifier */ + std::map< Node, std::map< Trigger*, bool > > d_auto_gen_trigger; + std::map< Node, int > d_counter; + /** single, multi triggers for each quantifier */ + std::map< Node, std::vector< Node > > d_patTerms[2]; + std::map< Node, bool > d_is_single_trigger; + std::map< Node, bool > d_single_trigger_gen; + std::map< Node, bool > d_made_multi_trigger; +private: + /** process functions */ + void processResetInstantiationRound( Theory::Effort effort ); + int process( Node f, Theory::Effort effort, int e, int instLimit ); + /** generate triggers */ + void generateTriggers( Node f ); +public: + InstStrategyAutoGenTriggers( InstantiatorTheoryUf* th, QuantifiersEngine* ie, int tstrt, int rstrt, int rgfr = -1 ) : + InstStrategy( ie ), d_th( th ), d_tr_strategy( tstrt ), d_rlv_strategy( rstrt ), d_generate_additional( false ){ + setRegenerateFrequency( rgfr ); + } + ~InstStrategyAutoGenTriggers(){} +public: + /** get auto-generated trigger */ + Trigger* getAutoGenTrigger( Node f ); + /** identify */ + std::string identify() const { return std::string("AutoGenTriggers"); } + /** set regenerate frequency, if fr<0, turn off regenerate */ + void setRegenerateFrequency( int fr ){ + if( fr<0 ){ + d_regenerate = false; + }else{ + d_regenerate_frequency = fr; + d_regenerate = true; + } + } + /** set generate additional */ + void setGenerateAdditional( bool val ) { d_generate_additional = val; } +};/* class InstStrategyAutoGenTriggers */ + +#if 0 + +class InstStrategyAddFailSplits : public InstStrategy{ +private: + /** InstantiatorTheoryUf class */ + InstantiatorTheoryUf* d_th; + /** process functions */ + void processResetInstantiationRound( Theory::Effort effort ); + int process( Node f, Theory::Effort effort, int e, int instLimit ); +public: + InstStrategyAddFailSplits( InstantiatorTheoryUf* th, QuantifiersEngine* ie ) : + InstStrategy( ie ), d_th( th ){} + ~InstStrategyAddFailSplits(){} + /** identify */ + std::string identify() const { return std::string("AddFailSplits"); } +};/* class InstStrategyAddFailSplits */ + +#endif /* 0 */ + +class InstStrategyFreeVariable : public InstStrategy{ +private: + /** InstantiatorTheoryUf class */ + InstantiatorTheoryUf* d_th; + /** guessed instantiations */ + std::map< Node, bool > d_guessed; + /** process functions */ + void processResetInstantiationRound( Theory::Effort effort ); + int process( Node f, Theory::Effort effort, int e, int instLimit ); +public: + InstStrategyFreeVariable( InstantiatorTheoryUf* th, QuantifiersEngine* ie ) : + InstStrategy( ie ), d_th( th ){} + ~InstStrategyFreeVariable(){} + /** identify */ + std::string identify() const { return std::string("FreeVariable"); } +};/* class InstStrategyFreeVariable */ + +}/* CVC4::theory::uf namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif diff --git a/src/theory/uf/kinds b/src/theory/uf/kinds index 2de3715e1..ec353dc59 100644 --- a/src/theory/uf/kinds +++ b/src/theory/uf/kinds @@ -6,6 +6,7 @@ theory THEORY_UF ::CVC4::theory::uf::TheoryUF "theory/uf/theory_uf.h" typechecker "theory/uf/theory_uf_type_rules.h" +instantiator ::CVC4::theory::uf::InstantiatorTheoryUf "theory/uf/theory_uf_instantiator.h" properties stable-infinite parametric properties check propagate staticLearning presolve @@ -15,4 +16,8 @@ parameterized APPLY_UF VARIABLE 1: "uninterpreted function application" typerule APPLY_UF ::CVC4::theory::uf::UfTypeRule +operator CARDINALITY_CONSTRAINT 2 "cardinality constraint" + +typerule CARDINALITY_CONSTRAINT ::CVC4::theory::uf::CardinalityConstraintTypeRule + endtheory diff --git a/src/theory/uf/symmetry_breaker.cpp b/src/theory/uf/symmetry_breaker.cpp index 5761ee4f5..26678f21d 100644 --- a/src/theory/uf/symmetry_breaker.cpp +++ b/src/theory/uf/symmetry_breaker.cpp @@ -443,6 +443,16 @@ bool SymmetryBreaker::invariantByPermutations(const Permutation& p) { Assert(p.size() > 1); + // check that the types match + Permutation::iterator permIt = p.begin(); + TypeNode type = (*permIt++).getType(); + do { + if(type != (*permIt++).getType()) { + Debug("ufsymm") << "UFSYMM types don't match, aborting.." << endl; + return false; + } + } while(permIt != p.end()); + // check P_swap vector<Node> subs; vector<Node> repls; diff --git a/src/theory/uf/theory_uf.cpp b/src/theory/uf/theory_uf.cpp index 7583f8ee7..dc7bb7c92 100644 --- a/src/theory/uf/theory_uf.cpp +++ b/src/theory/uf/theory_uf.cpp @@ -18,6 +18,8 @@ **/ #include "theory/uf/theory_uf.h" +#include "theory/uf/theory_uf_instantiator.h" +#include "theory/uf/theory_uf_strong_solver.h" using namespace std; using namespace CVC4; @@ -25,8 +27,8 @@ using namespace CVC4::theory; using namespace CVC4::theory::uf; /** Constructs a new instance of TheoryUF w.r.t. the provided context.*/ -TheoryUF::TheoryUF(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) : - Theory(THEORY_UF, c, u, out, valuation, logicInfo), +TheoryUF::TheoryUF(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) : + Theory(THEORY_UF, c, u, out, valuation, logicInfo, qe), d_notify(*this), d_equalityEngine(d_notify, c, "theory::uf::TheoryUF"), d_conflict(c, false), @@ -36,6 +38,12 @@ TheoryUF::TheoryUF(context::Context* c, context::UserContext* u, OutputChannel& { // The kinds we are treating as function application in congruence d_equalityEngine.addFunctionKind(kind::APPLY_UF); + + if (Options::current()->finiteModelFind) { + d_thss = new StrongSolverTheoryUf(c, u, out, this); + } else { + d_thss = NULL; + } }/* TheoryUF::TheoryUF() */ static Node mkAnd(const std::vector<TNode>& conjunctions) { @@ -62,29 +70,46 @@ static Node mkAnd(const std::vector<TNode>& conjunctions) { void TheoryUF::check(Effort level) { - while (!done() && !d_conflict) + while (!done() && !d_conflict) { // Get all the assertions Assertion assertion = get(); TNode fact = assertion.assertion; Debug("uf") << "TheoryUF::check(): processing " << fact << std::endl; + if (d_thss != NULL) { + bool isDecision = d_valuation.isSatLiteral(fact) && d_valuation.isDecision(fact); + d_thss->assertNode(fact, isDecision); + } // Do the work bool polarity = fact.getKind() != kind::NOT; TNode atom = polarity ? fact : fact[0]; if (atom.getKind() == kind::EQUAL) { d_equalityEngine.assertEquality(atom, polarity, fact); + } else if (atom.getKind() == kind::CARDINALITY_CONSTRAINT) { + // do nothing } else { d_equalityEngine.assertPredicate(atom, polarity, fact); } } + + if (d_thss != NULL) { + if (! d_conflict) { + d_thss->check(level); + } + } + }/* TheoryUF::check() */ void TheoryUF::preRegisterTerm(TNode node) { Debug("uf") << "TheoryUF::preRegisterTerm(" << node << ")" << std::endl; + if (d_thss != NULL) { + d_thss->preRegisterTerm(node); + } + switch (node.getKind()) { case kind::EQUAL: // Add the trigger for equality @@ -124,6 +149,12 @@ bool TheoryUF::propagate(TNode literal) { return ok; }/* TheoryUF::propagate(TNode) */ +void TheoryUF::propagate(Effort effort) { + if (d_thss != NULL) { + return d_thss->propagate(effort); + } +} + void TheoryUF::explain(TNode literal, std::vector<TNode>& assumptions) { // Do the work bool polarity = literal.getKind() != kind::NOT; @@ -395,3 +426,54 @@ void TheoryUF::conflict(TNode a, TNode b) { d_out->conflict(d_conflictNode); d_conflict = true; } + +void TheoryUF::eqNotifyNewClass(TNode t) { + if (d_thss != NULL) { + d_thss->newEqClass(t); + } + // this can be called very early, during initialization + if (!getLogicInfo().isLocked() || getLogicInfo().isQuantified()) { + ((InstantiatorTheoryUf*) getInstantiator())->newEqClass(t); + } +} + +void TheoryUF::eqNotifyPreMerge(TNode t1, TNode t2) { + if (getLogicInfo().isQuantified()) { + ((InstantiatorTheoryUf*) getInstantiator())->merge(t1, t2); + } +} + +void TheoryUF::eqNotifyPostMerge(TNode t1, TNode t2) { + if (d_thss != NULL) { + d_thss->merge(t1, t2); + } +} + +void TheoryUF::eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { + if (d_thss != NULL) { + d_thss->assertDisequal(t1, t2, reason); + } + if (getLogicInfo().isQuantified()) { + ((InstantiatorTheoryUf*) getInstantiator())->assertDisequal(t1, t2, reason); + } +} + +Node TheoryUF::ppRewrite(TNode node) { + + if (node.getKind() != kind::APPLY_UF) { + return node; + } + + // perform the callbacks requested by TheoryUF::registerPpRewrite() + RegisterPpRewrites::iterator c = d_registeredPpRewrites.find(node.getOperator()); + if (c == d_registeredPpRewrites.end()) { + return node; + } else { + Node res = c->second->ppRewrite(node); + if (res != node) { + return ppRewrite(res); + } else { + return res; + } + } +} diff --git a/src/theory/uf/theory_uf.h b/src/theory/uf/theory_uf.h index eceead38a..a55ef92b5 100644 --- a/src/theory/uf/theory_uf.h +++ b/src/theory/uf/theory_uf.h @@ -36,7 +36,15 @@ namespace CVC4 { namespace theory { namespace uf { +class UfTermDb; +class InstantiatorTheoryUf; +class StrongSolverTheoryUf; + class TheoryUF : public Theory { + + friend class InstantiatorTheoryUf; + friend class StrongSolverTheoryUf; + public: class NotifyClass : public eq::EqualityEngineNotify { @@ -76,13 +84,43 @@ public: Debug("uf") << "NotifyClass::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << ")" << std::endl; d_uf.conflict(t1, t2); } - }; + + void eqNotifyNewClass(TNode t) { + Debug("uf") << "NotifyClass::eqNotifyNewClass(" << t << std::endl; + d_uf.eqNotifyNewClass(t); + } + + void eqNotifyPreMerge(TNode t1, TNode t2) { + Debug("uf") << "NotifyClass::eqNotifyPreMerge(" << t1 << ", " << t2 << std::endl; + d_uf.eqNotifyPreMerge(t1, t2); + } + + void eqNotifyPostMerge(TNode t1, TNode t2) { + Debug("uf") << "NotifyClass::eqNotifyPostMerge(" << t1 << ", " << t2 << std::endl; + d_uf.eqNotifyPostMerge(t1, t2); + } + + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { + Debug("uf") << "NotifyClass::eqNotifyDisequal(" << t1 << ", " << t2 << ", " << reason << std::endl; + d_uf.eqNotifyDisequal(t1, t2, reason); + } + + };/* class TheoryUF::NotifyClass */ + + /** A callback class for ppRewrite(). See registerPpRewrite(), below. */ + class PpRewrite { + public: + virtual Node ppRewrite(TNode node) = 0; + };/* class TheoryUF::PpRewrite */ private: /** The notify class */ NotifyClass d_notify; + /** The associated theory strong solver (or NULL if none) */ + StrongSolverTheoryUf* d_thss; + /** Equaltity engine */ eq::EqualityEngine d_equalityEngine; @@ -118,10 +156,37 @@ private: /** Conflict when merging two constants */ void conflict(TNode a, TNode b); + /** called when a new equivalance class is created */ + void eqNotifyNewClass(TNode t); + + /** called when two equivalance classes will merge */ + void eqNotifyPreMerge(TNode t1, TNode t2); + + /** called when two equivalance classes have merged */ + void eqNotifyPostMerge(TNode t1, TNode t2); + + /** called when two equivalence classes are made disequal */ + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason); + + /** a registry type for keeping Node-specific callbacks for ppRewrite() */ + typedef std::hash_map<Node, PpRewrite*, NodeHashFunction> RegisterPpRewrites; + + /** a collection of callbacks to issue while doing a ppRewrite() */ + RegisterPpRewrites d_registeredPpRewrites; + public: /** Constructs a new instance of TheoryUF w.r.t. the provided context.*/ - TheoryUF(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo); + TheoryUF(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe); + + ~TheoryUF() { + // destruct all ppRewrite() callbacks + for(RegisterPpRewrites::iterator i = d_registeredPpRewrites.begin(); + i != d_registeredPpRewrites.end(); + ++i) { + delete i->second; + } + } void check(Effort); void preRegisterTerm(TNode term); @@ -133,7 +198,7 @@ public: void addSharedTerm(TNode n); void computeCareGraph(); - void propagate(Effort effort) {} + void propagate(Effort effort); EqualityStatus getEqualityStatus(TNode a, TNode b); @@ -141,6 +206,24 @@ public: return "THEORY_UF"; } + eq::EqualityEngine* getEqualityEngine() { + return &d_equalityEngine; + } + + StrongSolverTheoryUf* getStrongSolver() { + return d_thss; + } + + Node ppRewrite(TNode node); + + /** + * Register a ppRewrite() callback on "op." TheoryUF owns + * the callback, and will delete it when it is destructed. + */ + void registerPpRewrite(TNode op, PpRewrite* callback) { + d_registeredPpRewrites.insert(std::make_pair(op, callback)); + } + };/* class TheoryUF */ }/* CVC4::theory::uf namespace */ diff --git a/src/theory/uf/theory_uf_candidate_generator.cpp b/src/theory/uf/theory_uf_candidate_generator.cpp new file mode 100644 index 000000000..e8aa98aa7 --- /dev/null +++ b/src/theory/uf/theory_uf_candidate_generator.cpp @@ -0,0 +1,170 @@ +/********************* */ +/*! \file theory_uf_candidate_generator.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of theory uf candidate generator class + **/ + +#include "theory/uf/theory_uf_candidate_generator.h" +#include "theory/theory_engine.h" +#include "theory/uf/theory_uf.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::uf; + +CandidateGeneratorTheoryUf::CandidateGeneratorTheoryUf( InstantiatorTheoryUf* ith, Node op ) : + d_op( op ), d_ith( ith ), d_term_iter( -2 ){ + Assert( !d_op.isNull() ); +} +void CandidateGeneratorTheoryUf::resetInstantiationRound(){ + d_term_iter_limit = d_ith->getQuantifiersEngine()->getTermDatabase()->d_op_map[d_op].size(); +} + +void CandidateGeneratorTheoryUf::reset( Node eqc ){ + if( eqc.isNull() ){ + d_term_iter = 0; + }else{ + //create an equivalence class iterator in eq class eqc + if( ((TheoryUF*)d_ith->getTheory())->getEqualityEngine()->hasTerm( eqc ) ){ + eqc = ((TheoryUF*)d_ith->getTheory())->getEqualityEngine()->getRepresentative( eqc ); + d_eqc = eq::EqClassIterator( eqc, ((TheoryUF*)d_ith->getTheory())->getEqualityEngine() ); + d_retNode = Node::null(); + }else{ + d_retNode = eqc; + } + d_term_iter = -1; + } +} + +Node CandidateGeneratorTheoryUf::getNextCandidate(){ + if( d_term_iter>=0 ){ + //get next candidate term in the uf term database + while( d_term_iter<d_term_iter_limit ){ + Node n = d_ith->getQuantifiersEngine()->getTermDatabase()->d_op_map[d_op][d_term_iter]; + d_term_iter++; + if( isLegalCandidate( n ) ){ + return n; + } + } + }else if( d_term_iter==-1 ){ + if( d_retNode.isNull() ){ + //get next candidate term in equivalence class + while( !d_eqc.isFinished() ){ + Node n = (*d_eqc); + ++d_eqc; + if( n.getKind()==APPLY_UF && n.getOperator()==d_op ){ + if( isLegalCandidate( n ) ){ + return n; + } + } + } + }else{ + Node ret; + if( d_retNode.hasOperator() && d_retNode.getOperator()==d_op ){ + ret = d_retNode; + } + d_term_iter = -2; //done returning matches + return ret; + } + } + return Node::null(); +} + + +//CandidateGeneratorTheoryUfDisequal::CandidateGeneratorTheoryUfDisequal( InstantiatorTheoryUf* ith, Node eqc ) : +// d_ith( ith ), d_eq_class( eqc ){ +// d_eci = NULL; +//} +//void CandidateGeneratorTheoryUfDisequal::resetInstantiationRound(){ +// +//} +////we will iterate over all terms that are disequal from eqc +//void CandidateGeneratorTheoryUfDisequal::reset( Node eqc ){ +// //Assert( !eqc.isNull() ); +// ////begin iterating over equivalence classes that are disequal from eqc +// //d_eci = d_ith->getEquivalenceClassInfo( eqc ); +// //if( d_eci ){ +// // d_eqci_iter = d_eci->d_disequal.begin(); +// //} +//} +//Node CandidateGeneratorTheoryUfDisequal::getNextCandidate(){ +// //if( d_eci ){ +// // while( d_eqci_iter != d_eci->d_disequal.end() ){ +// // if( (*d_eqci_iter).second ){ +// // //we have an equivalence class that is disequal from eqc +// // d_cg->reset( (*d_eqci_iter).first ); +// // Node n = d_cg->getNextCandidate(); +// // //if there is a candidate in this equivalence class, return it +// // if( !n.isNull() ){ +// // return n; +// // } +// // } +// // ++d_eqci_iter; +// // } +// //} +// return Node::null(); +//} + + +CandidateGeneratorTheoryUfLitEq::CandidateGeneratorTheoryUfLitEq( InstantiatorTheoryUf* ith, Node mpat ) : + d_match_pattern( mpat ), d_ith( ith ){ + +} +void CandidateGeneratorTheoryUfLitEq::resetInstantiationRound(){ + +} +void CandidateGeneratorTheoryUfLitEq::reset( Node eqc ){ + d_eq = eq::EqClassesIterator( ((TheoryUF*)d_ith->getTheory())->getEqualityEngine() ); +} +Node CandidateGeneratorTheoryUfLitEq::getNextCandidate(){ + while( d_eq.isFinished() ){ + Node n = (*d_eq); + ++d_eq; + if( n.getType()==d_match_pattern[0].getType() ){ + //an equivalence class with the same type as the pattern, return reflexive equality + return NodeManager::currentNM()->mkNode( d_match_pattern.getKind(), n, n ); + } + } + return Node::null(); +} + + +CandidateGeneratorTheoryUfLitDeq::CandidateGeneratorTheoryUfLitDeq( InstantiatorTheoryUf* ith, Node mpat ) : + d_match_pattern( mpat ), d_ith( ith ){ + +} +void CandidateGeneratorTheoryUfLitDeq::resetInstantiationRound(){ + +} +void CandidateGeneratorTheoryUfLitDeq::reset( Node eqc ){ + Node false_term = ((TheoryUF*)d_ith->getTheory())->getEqualityEngine()->getRepresentative( + NodeManager::currentNM()->mkConst<bool>(false) ); + d_eqc_false = eq::EqClassIterator( false_term, ((TheoryUF*)d_ith->getTheory())->getEqualityEngine() ); +} +Node CandidateGeneratorTheoryUfLitDeq::getNextCandidate(){ + //get next candidate term in equivalence class + while( !d_eqc_false.isFinished() ){ + Node n = (*d_eqc_false); + ++d_eqc_false; + if( n.getKind()==d_match_pattern.getKind() ){ + //found an iff or equality, try to match it + //DO_THIS: cache to avoid redundancies? + //DO_THIS: do we need to try the symmetric equality for n? or will it also exist in the eq class of false? + return n; + } + } + return Node::null(); +} diff --git a/src/theory/uf/theory_uf_candidate_generator.h b/src/theory/uf/theory_uf_candidate_generator.h new file mode 100644 index 000000000..948573439 --- /dev/null +++ b/src/theory/uf/theory_uf_candidate_generator.h @@ -0,0 +1,115 @@ +/********************* */ +/*! \file theory_uf_candidate generator.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Theory uf candidate generator + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY_UF_CANDIDATE_GENERATOR_H +#define __CVC4__THEORY_UF_CANDIDATE_GENERATOR_H + +#include "theory/quantifiers_engine.h" +#include "theory/uf/theory_uf_instantiator.h" + +namespace CVC4 { +namespace theory { +namespace uf { + +class CandidateGeneratorTheoryUfDisequal; + +class CandidateGeneratorTheoryUf : public CandidateGenerator +{ + friend class CandidateGeneratorTheoryUfDisequal; +private: + //operator you are looking for + Node d_op; + //instantiator pointer + InstantiatorTheoryUf* d_ith; + //the equality class iterator + eq::EqClassIterator d_eqc; + int d_term_iter; + int d_term_iter_limit; +private: + Node d_retNode; +public: + CandidateGeneratorTheoryUf( InstantiatorTheoryUf* ith, Node op ); + ~CandidateGeneratorTheoryUf(){} + + void resetInstantiationRound(); + void reset( Node eqc ); + Node getNextCandidate(); +}; + +//class CandidateGeneratorTheoryUfDisequal : public CandidateGenerator +//{ +//private: +// //equivalence class +// Node d_eq_class; +// //equivalence class info +// EqClassInfo* d_eci; +// //equivalence class iterator +// EqClassInfo::BoolMap::const_iterator d_eqci_iter; +// //instantiator pointer +// InstantiatorTheoryUf* d_ith; +//public: +// CandidateGeneratorTheoryUfDisequal( InstantiatorTheoryUf* ith, Node eqc ); +// ~CandidateGeneratorTheoryUfDisequal(){} +// +// void resetInstantiationRound(); +// void reset( Node eqc ); //should be what you want to be disequal from +// Node getNextCandidate(); +//}; + +class CandidateGeneratorTheoryUfLitEq : public CandidateGenerator +{ +private: + //the equality classes iterator + eq::EqClassesIterator d_eq; + //equality you are trying to match equalities for + Node d_match_pattern; + //einstantiator pointer + InstantiatorTheoryUf* d_ith; +public: + CandidateGeneratorTheoryUfLitEq( InstantiatorTheoryUf* ith, Node mpat ); + ~CandidateGeneratorTheoryUfLitEq(){} + + void resetInstantiationRound(); + void reset( Node eqc ); + Node getNextCandidate(); +}; + +class CandidateGeneratorTheoryUfLitDeq : public CandidateGenerator +{ +private: + //the equality class iterator for false + eq::EqClassIterator d_eqc_false; + //equality you are trying to match disequalities for + Node d_match_pattern; + //einstantiator pointer + InstantiatorTheoryUf* d_ith; +public: + CandidateGeneratorTheoryUfLitDeq( InstantiatorTheoryUf* ith, Node mpat ); + ~CandidateGeneratorTheoryUfLitDeq(){} + + void resetInstantiationRound(); + void reset( Node eqc ); + Node getNextCandidate(); +}; + + +} +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY_UF_INSTANTIATOR_H */ diff --git a/src/theory/uf/theory_uf_instantiator.cpp b/src/theory/uf/theory_uf_instantiator.cpp new file mode 100644 index 000000000..9fdcb5952 --- /dev/null +++ b/src/theory/uf/theory_uf_instantiator.cpp @@ -0,0 +1,520 @@ +/********************* */ +/*! \file theory_uf_instantiator.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of theory uf instantiator class + **/ + +#include "theory/uf/theory_uf_instantiator.h" +#include "theory/theory_engine.h" +#include "theory/uf/theory_uf.h" +#include "theory/uf/equality_engine.h" +//#include "theory/uf/inst_strategy_model_find.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::uf; + +EqClassInfo::EqClassInfo( context::Context* c ) : d_funs( c ), d_pfuns( c ), d_disequal( c ){ + +} + +//set member +void EqClassInfo::setMember( Node n, TermDb* db ){ + if( n.getKind()==APPLY_UF ){ + d_funs[n.getOperator()] = true; + } + //add parent functions + for( std::map< Node, std::map< int, std::vector< Node > > >::iterator it = db->d_parents[n].begin(); + it != db->d_parents[n].end(); ++it ){ + d_pfuns[ it->first ] = true; + } +} + +//get has function +bool EqClassInfo::hasFunction( Node op ){ + return d_funs.find( op )!=d_funs.end(); +} + +bool EqClassInfo::hasParent( Node op ){ + return d_pfuns.find( op )!=d_pfuns.end(); +} + +//merge with another eq class info +void EqClassInfo::merge( EqClassInfo* eci ){ + for( BoolMap::iterator it = eci->d_funs.begin(); it != eci->d_funs.end(); it++ ) { + d_funs[ (*it).first ] = true; + } + for( BoolMap::iterator it = eci->d_pfuns.begin(); it != eci->d_pfuns.end(); it++ ) { + d_pfuns[ (*it).first ] = true; + } +} + +InstantiatorTheoryUf::InstantiatorTheoryUf(context::Context* c, CVC4::theory::QuantifiersEngine* qe, Theory* th) : +Instantiator( c, qe, th ) +{ + qe->setEqualityQuery( new EqualityQueryInstantiatorTheoryUf( this ) ); + + if( Options::current()->finiteModelFind ){ + //if( Options::current()->cbqi ){ + // addInstStrategy( new InstStrategyCheckCESolved( this, qe ) ); + //} + //addInstStrategy( new InstStrategyFiniteModelFind( c, this, ((TheoryUF*)th)->getStrongSolver(), qe ) ); + qe->getTermDatabase()->setMatchingActive( false ); + }else{ + if( Options::current()->cbqi ){ + addInstStrategy( new InstStrategyCheckCESolved( this, qe ) ); + } + if( Options::current()->userPatternsQuant ){ + d_isup = new InstStrategyUserPatterns( this, qe ); + addInstStrategy( d_isup ); + }else{ + d_isup = NULL; + } + InstStrategyAutoGenTriggers* i_ag = new InstStrategyAutoGenTriggers( this, qe, Trigger::TS_ALL, + InstStrategyAutoGenTriggers::RELEVANCE_DEFAULT, 3 ); + i_ag->setGenerateAdditional( true ); + addInstStrategy( i_ag ); + //addInstStrategy( new InstStrategyAddFailSplits( this, ie ) ); + addInstStrategy( new InstStrategyFreeVariable( this, qe ) ); + //d_isup->setPriorityOver( i_ag ); + //d_isup->setPriorityOver( i_agm ); + //i_ag->setPriorityOver( i_agm ); + } +} + +void InstantiatorTheoryUf::preRegisterTerm( Node t ){ + //d_quantEngine->addTermToDatabase( t ); +} + +void InstantiatorTheoryUf::assertNode( Node assertion ) +{ + Debug("quant-uf-assert") << "InstantiatorTheoryUf::check: " << assertion << std::endl; + //preRegisterTerm( assertion ); + d_quantEngine->addTermToDatabase( assertion ); + if( Options::current()->cbqi ){ + if( assertion.hasAttribute(InstConstantAttribute()) ){ + setHasConstraintsFrom( assertion.getAttribute(InstConstantAttribute()) ); + }else if( assertion.getKind()==NOT && assertion[0].hasAttribute(InstConstantAttribute()) ){ + setHasConstraintsFrom( assertion[0].getAttribute(InstConstantAttribute()) ); + } + } +} + +void InstantiatorTheoryUf::addUserPattern( Node f, Node pat ){ + if( d_isup ){ + d_isup->addUserPattern( f, pat ); + } + setHasConstraintsFrom( f ); +} + + +void InstantiatorTheoryUf::processResetInstantiationRound( Theory::Effort effort ){ + d_ground_reps.clear(); +} + +int InstantiatorTheoryUf::process( Node f, Theory::Effort effort, int e, int instLimit ){ + Debug("quant-uf") << "UF: Try to solve (" << e << ") for " << f << "... " << std::endl; + return InstStrategy::STATUS_SAT; +} + +void InstantiatorTheoryUf::debugPrint( const char* c ) +{ + +} + +bool InstantiatorTheoryUf::hasTerm( Node a ){ + return ((TheoryUF*)d_th)->d_equalityEngine.hasTerm( a ); +} + +bool InstantiatorTheoryUf::areEqual( Node a, Node b ){ + if( hasTerm( a ) && hasTerm( b ) ){ + return ((TheoryUF*)d_th)->d_equalityEngine.areEqual( a, b ); + }else{ + return a==b; + } +} + +bool InstantiatorTheoryUf::areDisequal( Node a, Node b ){ + if( hasTerm( a ) && hasTerm( b ) ){ + return ((TheoryUF*)d_th)->d_equalityEngine.areDisequal( a, b, false ); + }else{ + return false; + } +} + +Node InstantiatorTheoryUf::getRepresentative( Node a ){ + if( hasTerm( a ) ){ + return ((TheoryUF*)d_th)->d_equalityEngine.getRepresentative( a ); + }else{ + return a; + } +} + +Node InstantiatorTheoryUf::getInternalRepresentative( Node a ){ + if( d_ground_reps.find( a )==d_ground_reps.end() ){ + if( !hasTerm( a ) ){ + return a; + }else{ + Node rep = getRepresentative( a ); + if( !rep.hasAttribute(InstConstantAttribute()) ){ + //return the representative of a + d_ground_reps[a] = rep; + return rep; + }else{ + //otherwise, must search eq class + eq::EqClassIterator eqc_iter( rep, &((TheoryUF*)d_th)->d_equalityEngine ); + rep = Node::null(); + while( !eqc_iter.isFinished() ){ + if( !(*eqc_iter).hasAttribute(InstConstantAttribute()) ){ + d_ground_reps[ a ] = *eqc_iter; + return *eqc_iter; + } + eqc_iter++; + } + d_ground_reps[ a ] = a; + } + } + } + return d_ground_reps[a]; +} + +InstantiatorTheoryUf::Statistics::Statistics(): + //d_instantiations("InstantiatorTheoryUf::Total_Instantiations", 0), + d_instantiations_ce_solved("InstantiatorTheoryUf::Instantiations_CE-Solved", 0), + d_instantiations_e_induced("InstantiatorTheoryUf::Instantiations_E-Induced", 0), + d_instantiations_user_pattern("InstantiatorTheoryUf::Instantiations_User_Pattern", 0), + d_instantiations_guess("InstantiatorTheoryUf::Instantiations_Free_Var", 0), + d_instantiations_auto_gen("InstantiatorTheoryUf::Instantiations_Auto_Gen", 0), + d_instantiations_auto_gen_min("InstantiatorTheoryUf::Instantiations_Auto_Gen_Min", 0), + d_splits("InstantiatorTheoryUf::Total_Splits", 0) +{ + //StatisticsRegistry::registerStat(&d_instantiations); + StatisticsRegistry::registerStat(&d_instantiations_ce_solved); + StatisticsRegistry::registerStat(&d_instantiations_e_induced); + StatisticsRegistry::registerStat(&d_instantiations_user_pattern ); + StatisticsRegistry::registerStat(&d_instantiations_guess ); + StatisticsRegistry::registerStat(&d_instantiations_auto_gen ); + StatisticsRegistry::registerStat(&d_instantiations_auto_gen_min ); + StatisticsRegistry::registerStat(&d_splits); +} + +InstantiatorTheoryUf::Statistics::~Statistics(){ + //StatisticsRegistry::unregisterStat(&d_instantiations); + StatisticsRegistry::unregisterStat(&d_instantiations_ce_solved); + StatisticsRegistry::unregisterStat(&d_instantiations_e_induced); + StatisticsRegistry::unregisterStat(&d_instantiations_user_pattern ); + StatisticsRegistry::unregisterStat(&d_instantiations_guess ); + StatisticsRegistry::unregisterStat(&d_instantiations_auto_gen ); + StatisticsRegistry::unregisterStat(&d_instantiations_auto_gen_min ); + StatisticsRegistry::unregisterStat(&d_splits); +} + +/** new node */ +void InstantiatorTheoryUf::newEqClass( TNode n ){ + d_quantEngine->addTermToDatabase( n ); +} + +/** merge */ +void InstantiatorTheoryUf::merge( TNode a, TNode b ){ + if( Options::current()->efficientEMatching ){ + if( a.getKind()!=IFF && a.getKind()!=EQUAL && b.getKind()!=IFF && b.getKind()!=EQUAL ){ + Debug("efficient-e-match") << "Merging " << a << " with " << b << std::endl; + + //determine new candidates for instantiation + computeCandidatesPcPairs( a, b ); + computeCandidatesPcPairs( b, a ); + computeCandidatesPpPairs( a, b ); + computeCandidatesPpPairs( b, a ); + } + //merge eqc_ops of b into a + EqClassInfo* eci_a = getOrCreateEquivalenceClassInfo( a ); + EqClassInfo* eci_b = getOrCreateEquivalenceClassInfo( b ); + eci_a->merge( eci_b ); + } +} + +/** assert terms are disequal */ +void InstantiatorTheoryUf::assertDisequal( TNode a, TNode b, TNode reason ){ + +} + +EqClassInfo* InstantiatorTheoryUf::getEquivalenceClassInfo( Node n ) { + return d_eqc_ops.find( n )==d_eqc_ops.end() ? NULL : d_eqc_ops[n]; +} +EqClassInfo* InstantiatorTheoryUf::getOrCreateEquivalenceClassInfo( Node n ){ + Assert( n==getRepresentative( n ) ); + if( d_eqc_ops.find( n )==d_eqc_ops.end() ){ + EqClassInfo* eci = new EqClassInfo( d_th->getSatContext() ); + eci->setMember( n, d_quantEngine->getTermDatabase() ); + d_eqc_ops[n] = eci; + } + return d_eqc_ops[n]; +} + +void InstantiatorTheoryUf::computeCandidatesPcPairs( Node a, Node b ){ + Debug("efficient-e-match") << "Compute candidates for pc pairs..." << std::endl; + Debug("efficient-e-match") << " Eq class = ["; + outputEqClass( "efficient-e-match", a); + Debug("efficient-e-match") << "]" << std::endl; + EqClassInfo* eci_a = getOrCreateEquivalenceClassInfo( a ); + EqClassInfo* eci_b = getOrCreateEquivalenceClassInfo( a ); + for( BoolMap::iterator it = eci_a->d_funs.begin(); it != eci_a->d_funs.end(); it++ ) { + //the child function: a member of eq_class( a ) has top symbol g, in other words g is in funs( a ) + Node g = (*it).first; + Debug("efficient-e-match") << " Checking application " << g << std::endl; + //look at all parent/child pairs + for( std::map< Node, std::map< Node, std::vector< InvertedPathString > > >::iterator itf = d_pc_pairs[g].begin(); + itf != d_pc_pairs[g].end(); ++itf ){ + //f/g is a parent/child pair + Node f = itf->first; + if( eci_b->hasParent( f ) || true ){ + //DO_THIS: determine if f in pfuns( b ), only do the follow if so + Debug("efficient-e-match") << " Checking parent application " << f << std::endl; + //scan through the list of inverted path strings/candidate generators + for( std::map< Node, std::vector< InvertedPathString > >::iterator cit = itf->second.begin(); + cit != itf->second.end(); ++cit ){ + Node pat = cit->first; + Debug("efficient-e-match") << " Checking pattern " << pat << std::endl; + for( int c=0; c<(int)cit->second.size(); c++ ){ + Debug("efficient-e-match") << " Check inverted path string for pattern "; + outputInvertedPathString( "efficient-e-match", cit->second[c] ); + Debug("efficient-e-match") << std::endl; + + //collect all new relevant terms + std::vector< Node > terms; + terms.push_back( b ); + collectTermsIps( cit->second[c], terms ); + if( !terms.empty() ){ + Debug("efficient-e-match") << " -> Added terms (" << (int)terms.size() << "): "; + //add them as candidates to the candidate generator + for( int t=0; t<(int)terms.size(); t++ ){ + Debug("efficient-e-match") << terms[t] << " "; + //Notice() << "Add candidate (PC) " << terms[t] << std::endl; + for( int cg=0; cg<(int)d_pat_cand_gens[pat].size(); cg++ ){ + d_pat_cand_gens[pat][cg]->addCandidate( terms[t] ); + } + } + Debug("efficient-e-match") << std::endl; + } + } + } + } + } + } +} + +void InstantiatorTheoryUf::computeCandidatesPpPairs( Node a, Node b ){ + Debug("efficient-e-match") << "Compute candidates for pp pairs..." << std::endl; + EqClassInfo* eci_a = getOrCreateEquivalenceClassInfo( a ); + EqClassInfo* eci_b = getOrCreateEquivalenceClassInfo( a ); + for( std::map< Node, std::map< Node, std::map< Node, std::vector< IpsPair > > > >::iterator it = d_pp_pairs.begin(); + it != d_pp_pairs.end(); ++it ){ + Node f = it->first; + if( eci_a->hasParent( f ) ){ + Debug("efficient-e-match") << " Checking parent application " << f << std::endl; + for( std::map< Node, std::map< Node, std::vector< IpsPair > > >::iterator it2 = it->second.begin(); + it2 != it->second.end(); ++it2 ){ + Node g = it2->first; + if( eci_b->hasParent( g ) ){ + Debug("efficient-e-match") << " Checking parent application " << g << std::endl; + //if f in pfuns( a ) and g is in pfuns( b ), only do the follow if so + for( std::map< Node, std::vector< IpsPair > >::iterator cit = it2->second.begin(); + cit != it2->second.end(); ++cit ){ + Node pat = cit->first; + for( int c=0; c<(int)cit->second.size(); c++ ){ + std::vector< Node > a_terms; + a_terms.push_back( a ); + if( !a_terms.empty() ){ + collectTermsIps( cit->second[c].first, a_terms ); + std::vector< Node > b_terms; + b_terms.push_back( b ); + collectTermsIps( cit->second[c].first, b_terms ); + //take intersection + for( int t=0; t<(int)a_terms.size(); t++ ){ + if( std::find( b_terms.begin(), b_terms.end(), a_terms[t] )!=b_terms.end() ){ + //Notice() << "Add candidate (PP) " << a_terms[t] << std::endl; + Debug("efficient-e-match") << " -> Add term " << a_terms[t] << std::endl; + //add to all candidate generators having this term + for( int cg=0; cg<(int)d_pat_cand_gens[pat].size(); cg++ ){ + d_pat_cand_gens[pat][cg]->addCandidate( a_terms[t] ); + } + } + } + } + } + } + } + } + } + } +} + +void InstantiatorTheoryUf::collectTermsIps( InvertedPathString& ips, std::vector< Node >& terms, int index ){ + if( index<(int)ips.size() && !terms.empty() ){ + Debug("efficient-e-match-debug") << "> Process " << index << std::endl; + Node f = ips[index].first; + int arg = ips[index].second; + + //for each term in terms, determine if any term (modulo equality) has parent "f" from position "arg" + bool addRep = ( index!=(int)ips.size()-1 ); + std::vector< Node > newTerms; + for( int t=0; t<(int)terms.size(); t++ ){ + collectParentsTermsIps( terms[t], f, arg, newTerms, addRep ); + } + terms.clear(); + terms.insert( terms.begin(), newTerms.begin(), newTerms.end() ); + + Debug("efficient-e-match-debug") << "> Terms are now: "; + for( int t=0; t<(int)terms.size(); t++ ){ + Debug("efficient-e-match-debug") << terms[t] << " "; + } + Debug("efficient-e-match-debug") << std::endl; + + collectTermsIps( ips, terms, index+1 ); + } +} + +bool InstantiatorTheoryUf::collectParentsTermsIps( Node n, Node f, int arg, std::vector< Node >& terms, bool addRep, bool modEq ){ + bool addedTerm = false; + if( ((TheoryUF*)d_th)->d_equalityEngine.hasTerm( n ) && modEq ){ + Assert( getRepresentative( n )==n ); + //collect modulo equality + //DO_THIS: this should (if necessary) compute a current set of (f, arg) parents for n and cache it + eq::EqClassIterator eqc_iter( getRepresentative( n ), &((TheoryUF*)d_th)->d_equalityEngine ); + while( !eqc_iter.isFinished() ){ + if( collectParentsTermsIps( (*eqc_iter), f, arg, terms, addRep, false ) ){ + //if only one argument, we know we can stop (since all others added will be congruent) + if( f.getType().getNumChildren()==2 ){ + return true; + } + addedTerm = true; + } + eqc_iter++; + } + }else{ + TermDb* db = d_quantEngine->getTermDatabase(); + //see if parent f exists from argument arg + if( db->d_parents.find( n )!=db->d_parents.end() ){ + if( db->d_parents[n].find( f )!=db->d_parents[n].end() ){ + if( db->d_parents[n][f].find( arg )!=db->d_parents[n][f].end() ){ + for( int i=0; i<(int)db->d_parents[n][f][arg].size(); i++ ){ + Node t = db->d_parents[n][f][arg][i]; + if( addRep ){ + t = getRepresentative( t ); + } + if( std::find( terms.begin(), terms.end(), t )==terms.end() ){ + terms.push_back( t ); + } + addedTerm = true; + } + } + } + } + } + return addedTerm; +} + +void InstantiatorTheoryUf::registerPatternElementPairs2( Node opat, Node pat, InvertedPathString& ips, + std::map< Node, std::vector< std::pair< Node, InvertedPathString > > >& ips_map ){ + Assert( pat.getKind()==APPLY_UF ); + //add information for possible pp-pair + for( int i=0; i<(int)pat.getNumChildren(); i++ ){ + if( pat[i].getKind()==INST_CONSTANT ){ + ips_map[ pat[i] ].push_back( std::pair< Node, InvertedPathString >( pat.getOperator(), InvertedPathString( ips ) ) ); + } + } + ips.push_back( std::pair< Node, int >( pat.getOperator(), 0 ) ); + for( int i=0; i<(int)pat.getNumChildren(); i++ ){ + if( pat[i].getKind()==APPLY_UF ){ + ips.back().second = i; + registerPatternElementPairs2( opat, pat[i], ips, ips_map ); + Debug("pattern-element-opt") << "Found pc-pair ( " << pat.getOperator() << ", " << pat[i].getOperator() << " )" << std::endl; + Debug("pattern-element-opt") << " Path = "; + outputInvertedPathString( "pattern-element-opt", ips ); + Debug("pattern-element-opt") << std::endl; + //pat.getOperator() and pat[i].getOperator() are a pc-pair + d_pc_pairs[ pat[i].getOperator() ][ pat.getOperator() ][opat].push_back( InvertedPathString( ips ) ); + } + } + ips.pop_back(); +} + +void InstantiatorTheoryUf::registerPatternElementPairs( Node pat ){ + InvertedPathString ips; + std::map< Node, std::vector< std::pair< Node, InvertedPathString > > > ips_map; + registerPatternElementPairs2( pat, pat, ips, ips_map ); + for( std::map< Node, std::vector< std::pair< Node, InvertedPathString > > >::iterator it = ips_map.begin(); it != ips_map.end(); ++it ){ + for( int j=0; j<(int)it->second.size(); j++ ){ + for( int k=j+1; k<(int)it->second.size(); k++ ){ + //found a pp-pair + Debug("pattern-element-opt") << "Found pp-pair ( " << it->second[j].first << ", " << it->second[k].first << " )" << std::endl; + Debug("pattern-element-opt") << " Paths = "; + outputInvertedPathString( "pattern-element-opt", it->second[j].second ); + Debug("pattern-element-opt") << " and "; + outputInvertedPathString( "pattern-element-opt", it->second[k].second ); + Debug("pattern-element-opt") << std::endl; + d_pp_pairs[ it->second[j].first ][ it->second[k].first ][pat].push_back( IpsPair( it->second[j].second, it->second[k].second ) ); + } + } + } +} + +void InstantiatorTheoryUf::registerCandidateGenerator( CandidateGenerator* cg, Node pat ){ + Debug("efficient-e-match") << "Register candidate generator..." << pat << std::endl; + if( d_pat_cand_gens.find( pat )==d_pat_cand_gens.end() ){ + registerPatternElementPairs( pat ); + } + d_pat_cand_gens[pat].push_back( cg ); + + //take all terms from the uf term db and add to candidate generator + Node op = pat.getOperator(); + TermDb* db = d_quantEngine->getTermDatabase(); + for( int i=0; i<(int)db->d_op_map[op].size(); i++ ){ + cg->addCandidate( db->d_op_map[op][i] ); + } + d_cand_gens[op].push_back( cg ); + + Debug("efficient-e-match") << "Done." << std::endl; +} + +void InstantiatorTheoryUf::registerTrigger( Trigger* tr, Node op ){ + if( std::find( d_op_triggers[op].begin(), d_op_triggers[op].end(), tr )==d_op_triggers[op].end() ){ + d_op_triggers[op].push_back( tr ); + } +} + +void InstantiatorTheoryUf::outputEqClass( const char* c, Node n ){ + if( ((TheoryUF*)d_th)->d_equalityEngine.hasTerm( n ) ){ + eq::EqClassIterator eqc_iter( getRepresentative( n ), + &((TheoryUF*)d_th)->d_equalityEngine ); + bool firstTime = true; + while( !eqc_iter.isFinished() ){ + if( !firstTime ){ Debug(c) << ", "; } + Debug(c) << (*eqc_iter); + firstTime = false; + eqc_iter++; + } + }else{ + Debug(c) << n; + } +} + +void InstantiatorTheoryUf::outputInvertedPathString( const char* c, InvertedPathString& ips ){ + for( int i=0; i<(int)ips.size(); i++ ){ + if( i>0 ){ Debug( c ) << "."; } + Debug( c ) << ips[i].first << "." << ips[i].second; + } +} diff --git a/src/theory/uf/theory_uf_instantiator.h b/src/theory/uf/theory_uf_instantiator.h new file mode 100644 index 000000000..e50e3823c --- /dev/null +++ b/src/theory/uf/theory_uf_instantiator.h @@ -0,0 +1,201 @@ +/********************* */ +/*! \file theory_uf_instantiator.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Theory uf instantiator + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY_UF_INSTANTIATOR_H +#define __CVC4__THEORY_UF_INSTANTIATOR_H + +#include "theory/uf/inst_strategy.h" + +#include "context/context.h" +#include "context/context_mm.h" +#include "context/cdchunk_list.h" + +#include "util/stats.h" +#include "theory/uf/theory_uf.h" + +namespace CVC4 { +namespace theory { + +class TermDb; + +namespace uf { + +class InstantiatorTheoryUf; + +//equivalence class info +class EqClassInfo +{ +public: + typedef context::CDHashMap<Node, bool, NodeHashFunction> BoolMap; + typedef context::CDChunkList<Node> NodeList; +public: + //a list of operators that occur as top symbols in this equivalence class + // Efficient E-Matching for SMT Solvers: "funs" + BoolMap d_funs; + //a list of operators f for which a term of the form f( ... t ... ) exists + // Efficient E-Matching for SMT Solvers: "pfuns" + BoolMap d_pfuns; + //a list of equivalence classes that are disequal + BoolMap d_disequal; +public: + EqClassInfo( context::Context* c ); + ~EqClassInfo(){} + //set member + void setMember( Node n, TermDb* db ); + //has function "funs" + bool hasFunction( Node op ); + //has parent "pfuns" + bool hasParent( Node op ); + //merge with another eq class info + void merge( EqClassInfo* eci ); +}; + +class InstantiatorTheoryUf : public Instantiator{ + friend class ::CVC4::theory::InstMatchGenerator; + friend class ::CVC4::theory::TermDb; +protected: + typedef context::CDHashMap<Node, bool, NodeHashFunction> BoolMap; + typedef context::CDHashMap<Node, int, NodeHashFunction> IntMap; + typedef context::CDChunkList<Node> NodeList; + typedef context::CDHashMap<Node, NodeList*, NodeHashFunction> NodeLists; + /** map to representatives used */ + std::map< Node, Node > d_ground_reps; +protected: + /** instantiation strategies */ + InstStrategyUserPatterns* d_isup; +public: + InstantiatorTheoryUf(context::Context* c, CVC4::theory::QuantifiersEngine* qe, Theory* th); + ~InstantiatorTheoryUf() {} + /** assertNode method */ + void assertNode( Node assertion ); + /** Pre-register a term. Done one time for a Node, ever. */ + void preRegisterTerm( Node t ); + /** identify */ + std::string identify() const { return std::string("InstantiatorTheoryUf"); } + /** debug print */ + void debugPrint( const char* c ); + /** add user pattern */ + void addUserPattern( Node f, Node pat ); +private: + /** reset instantiation */ + void processResetInstantiationRound( Theory::Effort effort ); + /** calculate matches for quantifier f at effort */ + int process( Node f, Theory::Effort effort, int e, int instLimit ); +public: + /** statistics class */ + class Statistics { + public: + //IntStat d_instantiations; + IntStat d_instantiations_ce_solved; + IntStat d_instantiations_e_induced; + IntStat d_instantiations_user_pattern; + IntStat d_instantiations_guess; + IntStat d_instantiations_auto_gen; + IntStat d_instantiations_auto_gen_min; + IntStat d_splits; + Statistics(); + ~Statistics(); + }; + Statistics d_statistics; +public: + /** the base match */ + std::map< Node, InstMatch > d_baseMatch; +private: + //for each equivalence class + std::map< Node, EqClassInfo* > d_eqc_ops; +public: + /** general queries about equality */ + bool hasTerm( Node a ); + bool areEqual( Node a, Node b ); + bool areDisequal( Node a, Node b ); + Node getRepresentative( Node a ); + Node getInternalRepresentative( Node a ); + /** new node */ + void newEqClass( TNode n ); + /** merge */ + void merge( TNode a, TNode b ); + /** assert terms are disequal */ + void assertDisequal( TNode a, TNode b, TNode reason ); + /** get equivalence class info */ + EqClassInfo* getEquivalenceClassInfo( Node n ); + EqClassInfo* getOrCreateEquivalenceClassInfo( Node n ); +private: + typedef std::vector< std::pair< Node, int > > InvertedPathString; + typedef std::pair< InvertedPathString, InvertedPathString > IpsPair; + /** Parent/Child Pairs (for efficient E-matching) + So, for example, if we have the pattern f( g( x ) ), then d_pc_pairs[g][f][f( g( x ) )] = { f.0 }. + */ + std::map< Node, std::map< Node, std::map< Node, std::vector< InvertedPathString > > > > d_pc_pairs; + /** Parent/Parent Pairs (for efficient E-matching) */ + std::map< Node, std::map< Node, std::map< Node, std::vector< IpsPair > > > > d_pp_pairs; + /** list of all candidate generators for each operator */ + std::map< Node, std::vector< CandidateGenerator* > > d_cand_gens; + /** map from patterns to candidate generators */ + std::map< Node, std::vector< CandidateGenerator* > > d_pat_cand_gens; + /** helper functions */ + void registerPatternElementPairs2( Node opat, Node pat, InvertedPathString& ips, + std::map< Node, std::vector< std::pair< Node, InvertedPathString > > >& ips_map ); + void registerPatternElementPairs( Node pat ); + /** compute candidates for pc pairs */ + void computeCandidatesPcPairs( Node a, Node b ); + /** compute candidates for pp pairs */ + void computeCandidatesPpPairs( Node a, Node b ); + /** collect terms based on inverted path string */ + void collectTermsIps( InvertedPathString& ips, std::vector< Node >& terms, int index = 0 ); + bool collectParentsTermsIps( Node n, Node f, int arg, std::vector< Node >& terms, bool addRep, bool modEq = true ); +public: + /** Register candidate generator cg for pattern pat. (for use with efficient e-matching) + This request will ensure that calls will be made to cg->addCandidate( n ) for all + ground terms n that are relevant for matching with pat. + */ + void registerCandidateGenerator( CandidateGenerator* cg, Node pat ); +private: + /** triggers */ + std::map< Node, std::vector< Trigger* > > d_op_triggers; +public: + /** register trigger (for eager quantifier instantiation) */ + void registerTrigger( Trigger* tr, Node op ); +public: + /** output eq class */ + void outputEqClass( const char* c, Node n ); + /** output inverted path string */ + void outputInvertedPathString( const char* c, InvertedPathString& ips ); +};/* class InstantiatorTheoryUf */ + +/** equality query object using instantiator theory uf */ +class EqualityQueryInstantiatorTheoryUf : public EqualityQuery +{ +private: + /** pointer to instantiator uf class */ + InstantiatorTheoryUf* d_ith; +public: + EqualityQueryInstantiatorTheoryUf( InstantiatorTheoryUf* ith ) : d_ith( ith ){} + ~EqualityQueryInstantiatorTheoryUf(){} + /** general queries about equality */ + bool hasTerm( Node a ) { return d_ith->hasTerm( a ); } + Node getRepresentative( Node a ) { return d_ith->getRepresentative( a ); } + bool areEqual( Node a, Node b ) { return d_ith->areEqual( a, b ); } + bool areDisequal( Node a, Node b ) { return d_ith->areDisequal( a, b ); } + Node getInternalRepresentative( Node a ) { return d_ith->getInternalRepresentative( a ); } +}; /* EqualityQueryInstantiatorTheoryUf */ + +} +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY_UF_INSTANTIATOR_H */ diff --git a/src/theory/uf/theory_uf_strong_solver.cpp b/src/theory/uf/theory_uf_strong_solver.cpp new file mode 100644 index 000000000..a793b6a57 --- /dev/null +++ b/src/theory/uf/theory_uf_strong_solver.cpp @@ -0,0 +1,1267 @@ +/********************* */ +/*! \file theory_uf_strong_solver.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of theory uf strong solver class + **/ + +#include "theory/uf/theory_uf_strong_solver.h" +#include "theory/uf/theory_uf.h" +#include "theory/uf/equality_engine.h" +#include "theory/uf/theory_uf_instantiator.h" +#include "theory/theory_engine.h" + +//#define USE_REGION_SAT + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::uf; + +void StrongSolverTheoryUf::ConflictFind::Region::takeNode( StrongSolverTheoryUf::ConflictFind::Region* r, Node n ){ + //Debug("uf-ss") << "takeNode " << r << " " << n << std::endl; + //Debug("uf-ss") << "r : " << std::endl; + //r->debugPrint("uf-ss"); + //Debug("uf-ss") << "this : " << std::endl; + //debugPrint("uf-ss"); + Assert( !hasRep( n ) ); + Assert( r->hasRep( n ) ); + //add representative + setRep( n, true ); + //take disequalities from r + RegionNodeInfo* rni = r->d_nodes[n]; + for( int t=0; t<2; t++ ){ + RegionNodeInfo::DiseqList* del = rni->d_disequalities[t]; + for( NodeBoolMap::iterator it = del->d_disequalities.begin(); it != del->d_disequalities.end(); ++it ){ + if( (*it).second ){ + r->setDisequal( n, (*it).first, t, false ); + if( t==0 ){ + if( hasRep( (*it).first ) ){ + setDisequal( (*it).first, n, 0, false ); + setDisequal( (*it).first, n, 1, true ); + setDisequal( n, (*it).first, 1, true ); + }else{ + setDisequal( n, (*it).first, 0, true ); + } + }else{ + r->setDisequal( (*it).first, n, 1, false ); + r->setDisequal( (*it).first, n, 0, true ); + setDisequal( n, (*it).first, 0, true ); + } + } + } + } + //remove representative + r->setRep( n, false ); + //Debug("uf-ss") << "done takeNode " << r << " " << n << std::endl; +} + +void StrongSolverTheoryUf::ConflictFind::Region::combine( StrongSolverTheoryUf::ConflictFind::Region* r ){ + //take all nodes from r + for( std::map< Node, RegionNodeInfo* >::iterator it = r->d_nodes.begin(); it != r->d_nodes.end(); ++it ){ + if( it->second->d_valid ){ + setRep( it->first, true ); + } + } + for( std::map< Node, RegionNodeInfo* >::iterator it = r->d_nodes.begin(); it != r->d_nodes.end(); ++it ){ + if( it->second->d_valid ){ + //take disequalities from r + Node n = it->first; + RegionNodeInfo* rni = it->second; + for( int t=0; t<2; t++ ){ + RegionNodeInfo::DiseqList* del = rni->d_disequalities[t]; + for( NodeBoolMap::iterator it2 = del->d_disequalities.begin(); it2 != del->d_disequalities.end(); ++it2 ){ + if( (*it2).second ){ + if( t==0 && hasRep( (*it2).first ) ){ + setDisequal( (*it2).first, n, 0, false ); + setDisequal( (*it2).first, n, 1, true ); + setDisequal( n, (*it2).first, 1, true ); + }else{ + setDisequal( n, (*it2).first, t, true ); + } + } + } + } + } + } + r->d_valid = false; +} + +void StrongSolverTheoryUf::ConflictFind::Region::setRep( Node n, bool valid ){ + Assert( hasRep( n )!=valid ); + if( d_nodes.find( n )==d_nodes.end() && valid ){ + d_nodes[n] = new RegionNodeInfo( d_cf->d_th->getSatContext() ); + } + d_nodes[n]->d_valid = valid; + d_reps_size = d_reps_size + ( valid ? 1 : -1 ); + if( d_testClique.find( n )!=d_testClique.end() && d_testClique[n] ){ + Assert( !valid ); + d_testClique[n] = false; + d_testCliqueSize = d_testCliqueSize - 1; + //remove all splits involving n + for( NodeBoolMap::iterator it = d_splits.begin(); it != d_splits.end(); ++it ){ + if( (*it).second ){ + if( (*it).first[0]==n || (*it).first[1]==n ){ + d_splits[ (*it).first ] = false; + d_splitsSize = d_splitsSize - 1; + } + } + } + } +} + +/** setEqual */ +void StrongSolverTheoryUf::ConflictFind::Region::setEqual( Node a, Node b ){ + Assert( hasRep( a ) && hasRep( b ) ); + //move disequalities of b over to a + for( int t=0; t<2; t++ ){ + RegionNodeInfo::DiseqList* del = d_nodes[b]->d_disequalities[t]; + for( NodeBoolMap::iterator it = del->d_disequalities.begin(); it != del->d_disequalities.end(); ++it ){ + if( (*it).second ){ + Node n = (*it).first; + Region* nr = d_cf->d_regions[ d_cf->d_regions_map[ n ] ]; + if( !isDisequal( a, n, t ) ){ + setDisequal( a, n, t, true ); + nr->setDisequal( n, a, t, true ); + } + setDisequal( b, n, t, false ); + nr->setDisequal( n, b, t, false ); + } + } + } + //remove b from representatives + setRep( b, false ); +} + +void StrongSolverTheoryUf::ConflictFind::Region::setDisequal( Node n1, Node n2, int type, bool valid ){ + //Debug("uf-ss-region-debug") << "set disequal " << n1 << " " << n2 << " " << type << " " << valid << std::endl; + //debugPrint("uf-ss-region-debug"); + //Assert( isDisequal( n1, n2, type )!=valid ); + if( isDisequal( n1, n2, type )!=valid ){ //DO_THIS: make assertion + d_nodes[ n1 ]->d_disequalities[type]->setDisequal( n2, valid ); + if( type==0 ){ + d_total_diseq_external = d_total_diseq_external + ( valid ? 1 : -1 ); + }else{ + d_total_diseq_internal = d_total_diseq_internal + ( valid ? 1 : -1 ); + if( valid ){ + //if they are both a part of testClique, then remove split + if( d_testClique.find( n1 )!=d_testClique.end() && d_testClique[n1] && + d_testClique.find( n2 )!=d_testClique.end() && d_testClique[n2] ){ + Node eq = NodeManager::currentNM()->mkNode( EQUAL, n1, n2 ); + if( d_splits.find( eq )!=d_splits.end() && d_splits[ eq ] ){ + Debug("uf-ss-debug") << "removing split for " << n1 << " " << n2 << std::endl; + d_splits[ eq ] = false; + d_splitsSize = d_splitsSize - 1; + } + } + } + } + } +} + +bool StrongSolverTheoryUf::ConflictFind::Region::isDisequal( Node n1, Node n2, int type ){ + RegionNodeInfo::DiseqList* del = d_nodes[ n1 ]->d_disequalities[type]; + return del->d_disequalities.find( n2 )!=del->d_disequalities.end() && del->d_disequalities[n2]; +} + +bool StrongSolverTheoryUf::ConflictFind::Region::getMustCombine( int cardinality ){ + if( d_total_diseq_external>=long(cardinality) ){ + //The number of external disequalities is greater than or equal to cardinality. + //Thus, a clique of size cardinality+1 may exist between nodes in d_regions[i] and other regions + //Check if this is actually the case: must have n nodes with outgoing degree (cardinality+1-n) for some n>0 + std::vector< int > degrees; + for( std::map< Node, RegionNodeInfo* >::iterator it = d_nodes.begin(); it != d_nodes.end(); ++it ){ + RegionNodeInfo* rni = it->second; + if( rni->d_valid ){ + if( rni->getNumDisequalities()>=cardinality ){ + int outDeg = rni->getNumExternalDisequalities(); + if( outDeg>=cardinality ){ + //we have 1 node of degree greater than (cardinality) + return true; + }else if( outDeg>0 ){ + degrees.push_back( outDeg ); + if( (int)degrees.size()>=cardinality ){ + //we have (cardinality) nodes of degree 1 + return true; + } + } + } + } + } + std::sort( degrees.begin(), degrees.end() ); + for( int i=0; i<(int)degrees.size(); i++ ){ + if( degrees[i]>=cardinality+1-((int)degrees.size()-i) ){ + return true; + } + } + } + return false; +} + +struct sortInternalDegree { + StrongSolverTheoryUf::ConflictFind::Region* r; + bool operator() (Node i,Node j) { return (r->d_nodes[i]->getNumInternalDisequalities()>r->d_nodes[j]->getNumInternalDisequalities());} +}; + + +bool StrongSolverTheoryUf::ConflictFind::Region::check( Theory::Effort level, int cardinality, std::vector< Node >& clique ){ + if( d_reps_size>long(cardinality) ){ + if( d_reps_size>long(cardinality) && d_total_diseq_internal==d_reps_size*( d_reps_size - 1 ) ){ + //quick clique check, all reps form a clique + for( std::map< Node, RegionNodeInfo* >::iterator it = d_nodes.begin(); it != d_nodes.end(); ++it ){ + if( it->second->d_valid ){ + clique.push_back( it->first ); + } + } + return true; + }else{ + //build test clique, up to size cardinality+1 + if( d_testCliqueSize<=long(cardinality) ){ + std::vector< Node > newClique; + if( d_testCliqueSize<long(cardinality) ){ + for( std::map< Node, RegionNodeInfo* >::iterator it = d_nodes.begin(); it != d_nodes.end(); ++it ){ + //if not in the test clique, add it to the set of new members + if( it->second->d_valid && ( d_testClique.find( it->first )==d_testClique.end() || !d_testClique[ it->first ] ) ){ + newClique.push_back( it->first ); + } + } + //choose remaining nodes with the highest degrees + sortInternalDegree sidObj; + sidObj.r = this; + std::sort( newClique.begin(), newClique.end(), sidObj ); + newClique.erase( newClique.begin() + ( cardinality - d_testCliqueSize ) + 1, newClique.end() ); + }else{ + //scan for the highest degree + int maxDeg = -1; + Node maxNode; + for( std::map< Node, RegionNodeInfo* >::iterator it = d_nodes.begin(); it != d_nodes.end(); ++it ){ + //if not in the test clique, add it to the set of new members + if( it->second->d_valid && ( d_testClique.find( it->first )==d_testClique.end() || !d_testClique[ it->first ] ) ){ + if( it->second->getNumInternalDisequalities()>maxDeg ){ + maxDeg = it->second->getNumInternalDisequalities(); + maxNode = it->first; + } + } + } + Assert( maxNode!=Node::null() ); + newClique.push_back( maxNode ); + } + //check splits internal to new members + for( int j=0; j<(int)newClique.size(); j++ ){ + Debug("uf-ss-debug") << "Choose to add clique member " << newClique[j] << std::endl; + for( int k=(j+1); k<(int)newClique.size(); k++ ){ + if( !isDisequal( newClique[j], newClique[k], 1 ) ){ + d_splits[ NodeManager::currentNM()->mkNode( EQUAL, newClique[j], newClique[k] ) ] = true; + d_splitsSize = d_splitsSize + 1; + } + } + //check disequalities with old members + for( NodeBoolMap::iterator it = d_testClique.begin(); it != d_testClique.end(); ++it ){ + if( (*it).second ){ + if( !isDisequal( (*it).first, newClique[j], 1 ) ){ + d_splits[ NodeManager::currentNM()->mkNode( EQUAL, (*it).first, newClique[j] ) ] = true; + d_splitsSize = d_splitsSize + 1; + } + } + } + } + //add new clique members to test clique + for( int j=0; j<(int)newClique.size(); j++ ){ + d_testClique[ newClique[j] ] = true; + d_testCliqueSize = d_testCliqueSize + 1; + } + } + Assert( d_testCliqueSize==long(cardinality+1) ); + if( d_splitsSize==0 ){ + //test clique is a clique + for( NodeBoolMap::iterator it = d_testClique.begin(); it != d_testClique.end(); ++it ){ + if( (*it).second ){ + clique.push_back( (*it).first ); + } + } + return true; + } + } + } + return false; +} + +Node StrongSolverTheoryUf::ConflictFind::Region::getBestSplit(){ + //take the first split you find + for( NodeBoolMap::iterator it = d_splits.begin(); it != d_splits.end(); ++it ){ + if( (*it).second ){ + return (*it).first; + } + } + return Node::null(); +} + +void StrongSolverTheoryUf::ConflictFind::Region::addSplit( OutputChannel* out ){ + Node s = getBestSplit(); + //add lemma to output channel + Assert( s!=Node::null() && s.getKind()==EQUAL ); + s = Rewriter::rewrite( s ); + Debug("uf-ss-lemma") << "*** Split on " << s << std::endl; + //Debug("uf-ss-lemma") << d_th->getEqualityEngine()->areEqual( s[0], s[1] ) << " "; + //Debug("uf-ss-lemma") << d_th->getEqualityEngine()->areDisequal( s[0], s[1] ) << std::endl; + //Debug("uf-ss-lemma") << s[0].getType() << " " << s[1].getType() << std::endl; + debugPrint("uf-ss-temp"); + //Notice() << "*** Split on " << s << std::endl; + //split on the equality s + out->split( s ); + //tell the sat solver to explore the equals branch first + out->requirePhase( s, true ); +} + +void StrongSolverTheoryUf::ConflictFind::Region::getRepresentatives( std::vector< Node >& reps ){ + for( std::map< Node, RegionNodeInfo* >::iterator it = d_nodes.begin(); it != d_nodes.end(); ++it ){ + RegionNodeInfo* rni = it->second; + if( rni->d_valid ){ + reps.push_back( it->first ); + } + } +} + +bool StrongSolverTheoryUf::ConflictFind::Region::minimize( OutputChannel* out ){ + if( hasSplits() ){ + addSplit( out ); + return false; + }else{ + return true; + } +} + +void StrongSolverTheoryUf::ConflictFind::Region::getNumExternalDisequalities( std::map< Node, int >& num_ext_disequalities ){ + for( std::map< Node, RegionNodeInfo* >::iterator it = d_nodes.begin(); it != d_nodes.end(); ++it ){ + RegionNodeInfo* rni = it->second; + if( rni->d_valid ){ + RegionNodeInfo::DiseqList* del = rni->d_disequalities[0]; + for( NodeBoolMap::iterator it2 = del->d_disequalities.begin(); it2 != del->d_disequalities.end(); ++it2 ){ + if( (*it2).second ){ + num_ext_disequalities[ (*it2).first ]++; + } + } + } + } +} + +void StrongSolverTheoryUf::ConflictFind::Region::debugPrint( const char* c, bool incClique ){ + Debug( c ) << "Num reps: " << d_reps_size << std::endl; + for( std::map< Node, RegionNodeInfo* >::iterator it = d_nodes.begin(); it != d_nodes.end(); ++it ){ + RegionNodeInfo* rni = it->second; + if( rni->d_valid ){ + Node n = it->first; + Debug( c ) << " " << n << std::endl; + for( int i=0; i<2; i++ ){ + Debug( c ) << " " << ( i==0 ? "Ext" : "Int" ) << " disequal:"; + RegionNodeInfo::DiseqList* del = rni->d_disequalities[i]; + for( NodeBoolMap::iterator it2 = del->d_disequalities.begin(); it2 != del->d_disequalities.end(); ++it2 ){ + if( (*it2).second ){ + Debug( c ) << " " << (*it2).first; + } + } + Debug( c ) << ", total = " << del->d_size << std::endl; + } + } + } + Debug( c ) << "Total disequal: " << d_total_diseq_external << " external," << std::endl; + Debug( c ) << " " << d_total_diseq_internal<< " internal." << std::endl; + + if( incClique ){ + Debug( c ) << "Candidate clique members: " << std::endl; + Debug( c ) << " "; + for( NodeBoolMap::iterator it = d_testClique.begin(); it != d_testClique.end(); ++ it ){ + if( (*it).second ){ + Debug( c ) << (*it).first << " "; + } + } + Debug( c ) << ", size = " << d_testCliqueSize << std::endl; + Debug( c ) << "Required splits: " << std::endl; + Debug( c ) << " "; + for( NodeBoolMap::iterator it = d_splits.begin(); it != d_splits.end(); ++ it ){ + if( (*it).second ){ + Debug( c ) << (*it).first << " "; + } + } + Debug( c ) << ", size = " << d_splitsSize << std::endl; + } +} + +void StrongSolverTheoryUf::ConflictFind::combineRegions( int ai, int bi ){ + Debug("uf-ss-region") << "uf-ss: Combine Region #" << bi << " with Region #" << ai << std::endl; + Assert( isValid( ai ) && isValid( bi ) ); + for( std::map< Node, Region::RegionNodeInfo* >::iterator it = d_regions[bi]->d_nodes.begin(); it != d_regions[bi]->d_nodes.end(); ++it ){ + Region::RegionNodeInfo* rni = it->second; + if( rni->d_valid ){ + d_regions_map[ it->first ] = ai; + } + } + //update regions disequal DO_THIS? + d_regions[ai]->combine( d_regions[bi] ); + d_regions[bi]->d_valid = false; +} + +void StrongSolverTheoryUf::ConflictFind::moveNode( Node n, int ri ){ + Debug("uf-ss-region") << "uf-ss: Move node " << n << " to Region #" << ri << std::endl; + Assert( isValid( d_regions_map[ n ] ) ); + Assert( isValid( ri ) ); + ////update regions disequal DO_THIS? + //Region::RegionNodeInfo::DiseqList* del = d_regions[ d_regions_map[n] ]->d_nodes[n]->d_disequalities[0]; + //for( NodeBoolMap::iterator it = del->d_disequalities.begin(); it != del->d_disequalities.end(); ++it ){ + // if( (*it).second ){ + // } + //} + //move node to region ri + d_regions[ri]->takeNode( d_regions[ d_regions_map[n] ], n ); + d_regions_map[n] = ri; +} + +int StrongSolverTheoryUf::ConflictFind::getNumDisequalitiesToRegion( Node n, int ri ){ + int ni = d_regions_map[n]; + int counter = 0; + Region::RegionNodeInfo::DiseqList* del = d_regions[ni]->d_nodes[n]->d_disequalities[0]; + for( NodeBoolMap::iterator it = del->d_disequalities.begin(); it != del->d_disequalities.end(); ++it ){ + if( (*it).second ){ + if( d_regions_map[ (*it).first ]==ri ){ + counter++; + } + } + } + return counter; +} + +void StrongSolverTheoryUf::ConflictFind::getDisequalitiesToRegions( int ri, std::map< int, int >& regions_diseq ){ + for( std::map< Node, Region::RegionNodeInfo* >::iterator it = d_regions[ri]->d_nodes.begin(); + it != d_regions[ri]->d_nodes.end(); ++it ){ + if( it->second->d_valid ){ + Region::RegionNodeInfo::DiseqList* del = it->second->d_disequalities[0]; + for( NodeBoolMap::iterator it2 = del->d_disequalities.begin(); it2 != del->d_disequalities.end(); ++it2 ){ + if( (*it2).second ){ + //if( !isValid( d_regions_map[ (*it2).first ] ) ){ + // Debug( "uf-ss-temp" ) << "^^^" << ri << " " << d_regions_map[ (*it2).first ].get() << std::endl; + // debugPrint( "uf-ss-temp" ); + //} + Assert( isValid( d_regions_map[ (*it2).first ] ) ); + //Notice() << "Found disequality with " << (*it2).first << ", region = " << d_regions_map[ (*it2).first ] << std::endl; + regions_diseq[ d_regions_map[ (*it2).first ] ]++; + } + } + } + } +} + +void StrongSolverTheoryUf::ConflictFind::explainClique( std::vector< Node >& clique, OutputChannel* out ){ + Assert( d_cardinality>0 ); + while( clique.size()>long(d_cardinality+1) ){ + clique.pop_back(); + } + //found a clique + Debug("uf-ss") << "Found a clique (cardinality=" << d_cardinality << ") :" << std::endl; + Debug("uf-ss") << " "; + for( int i=0; i<(int)clique.size(); i++ ){ + Debug("uf-ss") << clique[i] << " "; + } + Debug("uf-ss") << std::endl; + Debug("uf-ss") << "Finding clique disequalities..." << std::endl; + std::vector< Node > conflict; + //collect disequalities, and nodes that must be equal within representatives + std::map< Node, std::map< Node, bool > > explained; + std::map< Node, std::map< Node, bool > > nodesWithinRep; + for( int i=0; i<(int)d_disequalities_index; i++ ){ + //if both sides of disequality exist in clique + Node r1 = d_th->d_equalityEngine.getRepresentative( d_disequalities[i][0][0] ); + Node r2 = d_th->d_equalityEngine.getRepresentative( d_disequalities[i][0][1] ); + if( r1!=r2 && ( explained.find( r1 )==explained.end() || explained[r1].find( r2 )==explained[r1].end() ) && + std::find( clique.begin(), clique.end(), r1 )!=clique.end() && + std::find( clique.begin(), clique.end(), r2 )!=clique.end() ){ + explained[r1][r2] = true; + explained[r2][r1] = true; + conflict.push_back( d_disequalities[i] ); + nodesWithinRep[r1][ d_disequalities[i][0][0] ] = true; + nodesWithinRep[r2][ d_disequalities[i][0][1] ] = true; + if( conflict.size()==((int)clique.size()*( (int)clique.size()-1 )/2) ){ + break; + } + } + } + //Debug("uf-ss") << conflict.size() << " " << clique.size() << std::endl; + Assert( (int)conflict.size()==((int)clique.size()*( (int)clique.size()-1 )/2) ); + //Assert( (int)conflict.size()==(int)clique.size()*( (int)clique.size()-1 )/2 ); + Debug("uf-ss") << "Finding clique equalities internal to eq classes..." << std::endl; + //now, we must explain equalities within each equivalence class + for( std::map< Node, std::map< Node, bool > >::iterator it = nodesWithinRep.begin(); it != nodesWithinRep.end(); ++it ){ + if( it->second.size()>1 ){ + Node prev; + //add explanation of t1 = t2 = ... = tn + for( std::map< Node, bool >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){ + if( prev!=Node::null() ){ + //explain it2->first and prev + std::vector< TNode > expl; + d_th->d_equalityEngine.explainEquality( it2->first, prev, true, expl ); + for( int i=0; i<(int)expl.size(); i++ ){ + if( std::find( conflict.begin(), conflict.end(), expl[i] )==conflict.end() ){ + conflict.push_back( expl[i] ); + } + } + } + prev = it2->first; + } + } + } + Debug("uf-ss") << "Explanation of clique (size=" << conflict.size() << ") = " << std::endl; + for( int i=0; i<(int)conflict.size(); i++ ){ + Debug("uf-ss") << conflict[i] << " "; + } + Debug("uf-ss") << std::endl; + //now, make the conflict + Node conflictNode = conflict.size()==1 ? conflict[0] : NodeManager::currentNM()->mkNode( AND, conflict ); + //add cardinality constraint + //Node cardNode = NodeManager::currentNM()->mkNode( CARDINALITY_CONSTRAINT, d_cardinality_lemma_term, + // NodeManager::currentNM()->mkConst( Rational(d_cardinality) ) ); + Node cardNode = d_cardinality_literal[ d_cardinality ]; + conflictNode = NodeManager::currentNM()->mkNode( IMPLIES, conflictNode, cardNode.notNode() ); + Debug("uf-ss-lemma") << "*** Add clique conflict " << conflictNode << std::endl; + //Notice() << "*** Add clique conflict " << conflictNode << std::endl; + out->lemma( conflictNode ); + ++( d_th->getStrongSolver()->d_statistics.d_clique_lemmas ); + + //DO_THIS: ensure that the same clique is not reported??? Check standard effort after assertDisequal can produce same clique. +} + +/** new node */ +void StrongSolverTheoryUf::ConflictFind::newEqClass( Node n ){ + if( d_regions_map.find( n )==d_regions_map.end() ){ + d_regions_map[n] = d_regions_index; + Debug("uf-ss") << "StrongSolverTheoryUf: New Eq Class " << n << std::endl; + Debug("uf-ss-debug") << d_regions_index << " " << (int)d_regions.size() << std::endl; + if( d_regions_index<d_regions.size() ){ + d_regions[ d_regions_index ]->debugPrint("uf-ss-debug",true); + d_regions[ d_regions_index ]->d_valid = true; + //Assert( d_regions[ d_regions_index ]->d_valid ); + Assert( d_regions[ d_regions_index ]->getNumReps()==0 ); + }else{ + d_regions.push_back( new Region( this, d_th->getSatContext() ) ); + } + d_regions[ d_regions_index ]->setRep( n, true ); + d_regions_index = d_regions_index + 1; + d_reps = d_reps + 1; + } +} + +/** merge */ +void StrongSolverTheoryUf::ConflictFind::merge( Node a, Node b ){ + //Assert( a==d_th->d_equalityEngine.getRepresentative( a ) ); + //Assert( b==d_th->d_equalityEngine.getRepresentative( b ) ); + Debug("uf-ss") << "StrongSolverTheoryUf: Merging " << a << " = " << b << "..." << std::endl; + if( a!=b ){ + Assert( d_regions_map.find( a )!=d_regions_map.end() ); + Assert( d_regions_map.find( b )!=d_regions_map.end() ); + int ai = d_regions_map[a]; + int bi = d_regions_map[b]; + Debug("uf-ss") << " regions: " << ai << " " << bi << std::endl; + if( ai!=bi ){ + if( d_regions[ai]->getNumReps()==1 ){ + combineRegions( bi, ai ); + d_regions[bi]->setEqual( a, b ); + checkRegion( bi ); + }else if( d_regions[bi]->getNumReps()==1 ){ + combineRegions( ai, bi ); + d_regions[ai]->setEqual( a, b ); + checkRegion( ai ); + }else{ + // either move a to d_regions[bi], or b to d_regions[ai] + int aex = d_regions[ai]->d_nodes[a]->getNumInternalDisequalities() - getNumDisequalitiesToRegion( a, bi ); + int bex = d_regions[bi]->d_nodes[b]->getNumInternalDisequalities() - getNumDisequalitiesToRegion( b, ai ); + //based on which would produce the fewest number of external disequalities + if( aex<bex ){ + moveNode( a, bi ); + d_regions[bi]->setEqual( a, b ); + }else{ + moveNode( b, ai ); + d_regions[ai]->setEqual( a, b ); + } + checkRegion( ai ); + checkRegion( bi ); + } + }else{ + d_regions[ai]->setEqual( a, b ); + checkRegion( ai ); + } + d_reps = d_reps - 1; + d_regions_map[b] = -1; + } + Debug("uf-ss") << "Done merge." << std::endl; +} + +/** assert terms are disequal */ +void StrongSolverTheoryUf::ConflictFind::assertDisequal( Node a, Node b, Node reason ){ + //if they are not already disequal + a = d_th->d_equalityEngine.getRepresentative( a ); + b = d_th->d_equalityEngine.getRepresentative( b ); + if( !d_th->d_equalityEngine.areDisequal( a, b, true ) ){ + Debug("uf-ss") << "Assert disequal " << a << " != " << b << "..." << std::endl; + //if( reason.getKind()!=NOT || ( reason[0].getKind()!=EQUAL && reason[0].getKind()!=IFF ) || + // a!=reason[0][0] || b!=reason[0][1] ){ + // Notice() << "Assert disequal " << a << " != " << b << ", reason = " << reason << "..." << std::endl; + //} + Debug("uf-ss-disequal") << "Assert disequal " << a << " != " << b << "..." << std::endl; + //add to list of disequalities + if( d_disequalities_index<d_disequalities.size() ){ + d_disequalities[d_disequalities_index] = reason; + }else{ + d_disequalities.push_back( reason ); + } + d_disequalities_index = d_disequalities_index + 1; + //now, add disequalities to regions + Assert( d_regions_map.find( a )!=d_regions_map.end() ); + Assert( d_regions_map.find( b )!=d_regions_map.end() ); + int ai = d_regions_map[a]; + int bi = d_regions_map[b]; + Debug("uf-ss") << " regions: " << ai << " " << bi << std::endl; + if( ai==bi ){ + //internal disequality + d_regions[ai]->setDisequal( a, b, 1, true ); + d_regions[ai]->setDisequal( b, a, 1, true ); + }else{ + //external disequality + d_regions[ai]->setDisequal( a, b, 0, true ); + d_regions[bi]->setDisequal( b, a, 0, true ); + checkRegion( ai ); + checkRegion( bi ); + } + //Notice() << "done" << std::endl; + } +} + +void StrongSolverTheoryUf::ConflictFind::assertCardinality( int c, bool val ){ + Assert( d_cardinality_literal.find( c )!=d_cardinality_literal.end() ); + d_cardinality_assertions[ d_cardinality_literal[c] ] = val; + if( val ){ + d_hasCard = true; + } +} + +bool StrongSolverTheoryUf::ConflictFind::checkRegion( int ri, bool rec ){ + if( isValid(ri) ){ + Assert( d_cardinality>0 ); + //first check if region is in conflict + std::vector< Node > clique; + if( d_regions[ri]->check( Theory::EFFORT_STANDARD, d_cardinality, clique ) ){ + //explain clique + explainClique( clique, &d_th->getOutputChannel() ); + return false; + }else if( d_regions[ri]->getMustCombine( d_cardinality ) ){ + //this region must merge with another + Debug("uf-ss-check-region") << "We must combine Region #" << ri << ". " << std::endl; + d_regions[ri]->debugPrint("uf-ss-check-region"); + ////alternatively, check if we can reduce the number of external disequalities by moving single nodes + //for( std::map< Node, bool >::iterator it = d_regions[i]->d_reps.begin(); it != d_regions[i]->d_reps.end(); ++it ){ + // if( it->second ){ + // int inDeg = d_regions[i]->d_disequalities_size[1][ it-> first ]; + // int outDeg = d_regions[i]->d_disequalities_size[1][ it-> first ]; + // if( inDeg<outDeg ){ + // } + // } + //} + //take region with maximum disequality density + double maxScore = 0; + int maxRegion = -1; + std::map< int, int > regions_diseq; + getDisequalitiesToRegions( ri, regions_diseq ); + for( std::map< int, int >::iterator it = regions_diseq.begin(); it != regions_diseq.end(); ++it ){ + Debug("uf-ss-check-region") << it->first << " : " << it->second << std::endl; + } + for( std::map< int, int >::iterator it = regions_diseq.begin(); it != regions_diseq.end(); ++it ){ + Assert( it->first!=ri ); + Assert( isValid( it->first ) ); + Assert( d_regions[ it->first ]->getNumReps()>0 ); + double tempScore = double(it->second)/double(d_regions[it->first]->getNumReps() ); + if( tempScore>maxScore ){ + maxRegion = it->first; + maxScore = tempScore; + } + } + Assert( maxRegion!=-1 ); + Debug("uf-ss-check-region") << "Combine with region #" << maxRegion << ":" << std::endl; + d_regions[maxRegion]->debugPrint("uf-ss-check-region"); + combineRegions( ri, maxRegion ); + if( rec ){ + checkRegion( ri, rec ); + } + //std::vector< Node > clique; + //if( d_regions[ri]->check( Theory::EFFORT_STANDARD, cardinality, clique ) ){ + // //explain clique + // Notice() << "found clique " << std::endl; + //} + return true; + } + } + return false; +} + +bool StrongSolverTheoryUf::ConflictFind::disambiguateTerms( OutputChannel* out ){ + Debug("uf-ss-disamb") << "Disambiguate terms." << std::endl; + bool lemmaAdded = false; + //otherwise, determine ambiguous pairs of ground terms for relevant sorts + TermDb* db = d_th->getQuantifiersEngine()->getTermDatabase(); + for( std::map< Node, std::vector< Node > >::iterator it = db->d_op_map.begin(); it != db->d_op_map.end(); ++it ){ + Debug("uf-ss-disamb") << "Check " << it->first << std::endl; + if( it->second.size()>1 ){ + if( StrongSolverTheoryUf::involvesRelevantType( it->second[0] ) ){ + for( int i=0; i<(int)it->second.size(); i++ ){ + for( int j=(i+1); j<(int)it->second.size(); j++ ){ + Kind knd = it->second[i].getType()==NodeManager::currentNM()->booleanType() ? IFF : EQUAL; + Node eq = NodeManager::currentNM()->mkNode( knd, it->second[i], it->second[j] ); + eq = Rewriter::rewrite(eq); + //determine if they are ambiguous + if( d_term_amb.find( eq )==d_term_amb.end() ){ + Debug("uf-ss-disamb") << "Check disambiguate " << it->second[i] << " " << it->second[j] << std::endl; + d_term_amb[ eq ] = true; + //if they are equal + if( d_th->d_equalityEngine.areEqual( it->second[i], it->second[j] ) ){ + d_term_amb[ eq ] = false; + }else{ + //if an argument is disequal, then they are not ambiguous + for( int k=0; k<(int)it->second[i].getNumChildren(); k++ ){ + if( d_th->d_equalityEngine.areDisequal( it->second[i][k], it->second[j][k], true ) ){ + d_term_amb[ eq ] = false; + break; + } + } + } + if( d_term_amb[ eq ] ){ + Debug("uf-ss-disamb") << "Disambiguate " << it->second[i] << " " << it->second[j] << std::endl; + //must add lemma + std::vector< Node > children; + children.push_back( eq ); + for( int k=0; k<(int)it->second[i].getNumChildren(); k++ ){ + Kind knd2 = it->second[i][k].getType()==NodeManager::currentNM()->booleanType() ? IFF : EQUAL; + Node eqc = NodeManager::currentNM()->mkNode( knd2, it->second[i][k], it->second[j][k] ); + children.push_back( eqc.notNode() ); + } + Assert( children.size()>1 ); + Node lem = NodeManager::currentNM()->mkNode( OR, children ); + Debug( "uf-ss-lemma" ) << "*** Diambiguate lemma : " << lem << std::endl; + //Notice() << "*** Diambiguate lemma : " << lem << std::endl; + out->lemma( lem ); + d_term_amb[ eq ] = false; + lemmaAdded = true; + ++( d_th->getStrongSolver()->d_statistics.d_disamb_term_lemmas ); + } + } + } + } + } + } + } + Debug("uf-ss-disamb") << "Done disambiguate terms. " << lemmaAdded << std::endl; + return lemmaAdded; +} + +/** check */ +void StrongSolverTheoryUf::ConflictFind::check( Theory::Effort level, OutputChannel* out ){ + if( level>=Theory::EFFORT_STANDARD ){ + Assert( d_cardinality>0 ); + Debug("uf-ss") << "StrongSolverTheoryUf: Check " << level << " " << d_type << std::endl; + //Notice() << "StrongSolverTheoryUf: Check " << level << std::endl; + if( d_reps<=(unsigned)d_cardinality ){ + Debug("uf-ss-debug") << "We have " << d_reps << " representatives for type " << d_type << ", <= " << d_cardinality << std::endl; + if( level==Theory::EFFORT_FULL ){ + Debug("uf-ss-sat") << "We have " << d_reps << " representatives for type " << d_type << ", <= " << d_cardinality << std::endl; + //Notice() << "We have " << d_reps << " representatives for type " << d_type << ", <= " << cardinality << std::endl; + //Notice() << "Model size for " << d_type << " is " << cardinality << std::endl; + //Notice() << cardinality << " "; + } + return; + }else{ + //do a check within each region + for( int i=0; i<(int)d_regions_index; i++ ){ + if( d_regions[i]->d_valid ){ + std::vector< Node > clique; + if( d_regions[i]->check( level, d_cardinality, clique ) ){ + //explain clique + explainClique( clique, out ); + return; + }else{ + Debug("uf-ss-debug") << "No clique in Region #" << i << std::endl; + } + } + } + if( level==Theory::EFFORT_FULL ){ + Debug("uf-ss-debug") << "Add splits?" << std::endl; + //see if we have any recommended splits + bool addedLemma = false; + for( int i=0; i<(int)d_regions_index; i++ ){ + if( d_regions[i]->d_valid ){ + if( d_regions[i]->hasSplits() ){ + d_regions[i]->addSplit( out ); + addedLemma = true; + ++( d_th->getStrongSolver()->d_statistics.d_split_lemmas ); + } + } + } + if( !addedLemma ){ + Debug("uf-ss") << "No splits added." << std::endl; + if( Options::current()->fmfRegionSat ){ + //otherwise, try to disambiguate individual terms + if( !disambiguateTerms( out ) ){ + //no disequalities can be propagated + //we are in a situation where it suffices to apply a coloring to equivalence classes + //due to our invariants, we know no coloring conflicts will occur between regions, and thus + // we are SAT in this case. + Debug("uf-ss-sat") << "SAT: regions = " << getNumRegions() << std::endl; + //Notice() << "Model size for " << d_type << " is " << cardinality << ", regions = " << getNumRegions() << std::endl; + debugPrint("uf-ss-sat"); + } + }else{ + //naive strategy. combine the first two valid regions + int regIndex = -1; + for( int i=0; i<(int)d_regions_index; i++ ){ + if( d_regions[i]->d_valid ){ + if( regIndex==-1 ){ + regIndex = i; + }else{ + combineRegions( regIndex, i ); + check( level, out ); + } + } + } + } + } + } + } + } +} + +void StrongSolverTheoryUf::ConflictFind::propagate( Theory::Effort level, OutputChannel* out ){ + Assert( d_cardinality>0 ); + + //propagate the current cardinality as a decision literal + Node cn = d_cardinality_literal[ d_cardinality ]; + Debug("uf-ss-prop-as-dec") << "Propagate as decision " << d_type << ", cardinality = " << d_cardinality << std::endl; + Assert( !cn.isNull() ); + if( d_cardinality_assertions.find( cn )==d_cardinality_assertions.end() ){ + out->propagateAsDecision( d_cardinality_literal[ d_cardinality ] ); + Debug("uf-ss-prop-as-dec") << "Propagate as decision " << d_cardinality_literal[ d_cardinality ]; + Debug("uf-ss-prop-as-dec") << " " << d_cardinality_literal[ d_cardinality ][0].getType() << std::endl; + } + +} + +void StrongSolverTheoryUf::ConflictFind::debugPrint( const char* c ){ + Debug( c ) << "-- Conflict Find:" << std::endl; + Debug( c ) << "Number of reps = " << d_reps << std::endl; + Debug( c ) << "Cardinality req = " << d_cardinality << std::endl; + unsigned debugReps = 0; + for( int i=0; i<(int)d_regions_index; i++ ){ + if( d_regions[i]->d_valid ){ + Debug( c ) << "Region #" << i << ": " << std::endl; + d_regions[i]->debugPrint( c, true ); + Debug( c ) << std::endl; + for( std::map< Node, Region::RegionNodeInfo* >::iterator it = d_regions[i]->d_nodes.begin(); it != d_regions[i]->d_nodes.end(); ++it ){ + if( it->second->d_valid ){ + if( d_regions_map[ it->first ]!=i ){ + Debug( c ) << "***Bad regions map : " << it->first << " " << d_regions_map[ it->first ].get() << std::endl; + } + } + } + debugReps += d_regions[i]->getNumReps(); + } + } + if( debugReps!=d_reps ){ + Debug( c ) << "***Bad reps: " << d_reps << ", actual = " << debugReps << std::endl; + } +} + +int StrongSolverTheoryUf::ConflictFind::getNumRegions(){ + int count = 0; + for( int i=0; i<(int)d_regions_index; i++ ){ + if( d_regions[i]->d_valid ){ + count++; + } + } + return count; +} + +void StrongSolverTheoryUf::ConflictFind::setCardinality( int c, OutputChannel* out ){ + d_cardinality = c; + //add appropriate lemma + Node lem = getCardinalityLemma(); + out->lemma( lem ); + //add the appropriate lemma + Debug("uf-ss-fmf") << "Set cardinality " << d_type << " = " << c << std::endl; + Debug("uf-ss-prop-as-dec") << "Propagate as decision " << lem[0] << std::endl; + out->propagateAsDecision( lem[0] ); + d_is_cardinality_requested = true; + d_is_cardinality_requested_c = true; + //now, require old literal to be decided false + //if( d_cardinality_literal.find( c-1 )!=d_cardinality_literal.end() ){ + // Debug("uf-ss-req-phase") << "Require phase " << d_cardinality_literal[c-1] << " = false " << std::endl; + // out->requirePhase( d_cardinality_literal[c-1], false ); + //} +} + +void StrongSolverTheoryUf::ConflictFind::getRepresentatives( std::vector< Node >& reps ){ + if( !Options::current()->fmfRegionSat ){ + bool foundRegion = false; + for( int i=0; i<(int)d_regions_index; i++ ){ + //should not have multiple regions at this point + if( foundRegion ){ + Assert( !d_regions[i]->d_valid ); + } + if( d_regions[i]->d_valid ){ + //this is the only valid region + d_regions[i]->getRepresentatives( reps ); + foundRegion = true; + } + } + }else{ + Unimplemented("Build representatives for fmf region sat is not implemented"); + } +} + +bool StrongSolverTheoryUf::ConflictFind::minimize( OutputChannel* out ){ + int validRegionIndex = -1; + for( int i=0; i<(int)d_regions_index; i++ ){ + if( d_regions[i]->d_valid ){ + if( validRegionIndex!=-1 ){ + combineRegions( validRegionIndex, i ); + if( !d_regions[validRegionIndex]->minimize( out ) ){ + return false; + } + }else{ + validRegionIndex = i; + } + } + } + if( !d_regions[validRegionIndex]->minimize( out ) ){ + return false; + } + return true; +} + + +Node StrongSolverTheoryUf::ConflictFind::getCardinalityLemma(){ + if( d_cardinality_lemma.find( d_cardinality )==d_cardinality_lemma.end() ){ + if( d_cardinality_lemma_term.isNull() ){ + std::stringstream ss; + ss << "fmf_term_" << d_type; + d_cardinality_lemma_term = NodeManager::currentNM()->mkVar( ss.str(), d_type ); + ModelBasisAttribute mba; + d_cardinality_lemma_term.setAttribute(mba,true); + } + Node lem = NodeManager::currentNM()->mkNode( CARDINALITY_CONSTRAINT, d_cardinality_lemma_term, + NodeManager::currentNM()->mkConst( Rational( d_cardinality ) ) ); + lem = Rewriter::rewrite(lem); + d_cardinality_literal[ d_cardinality ] = lem; + lem = NodeManager::currentNM()->mkNode( OR, lem, lem.notNode() ); + d_cardinality_lemma[ d_cardinality ] = lem; + } + return d_cardinality_lemma[ d_cardinality ]; +} + +StrongSolverTheoryUf::StrongSolverTheoryUf(context::Context* c, context::UserContext* u, OutputChannel& out, TheoryUF* th) : +d_out( &out ), +d_th( th ), +d_conf_find_init( c ) +{ + +} + +/** new node */ +void StrongSolverTheoryUf::newEqClass( Node n ){ + TypeNode tn = n.getType(); + ConflictFind* c = getConflictFind( tn ); + if( c ){ + Debug("uf-ss-solver") << "StrongSolverTheoryUf: New eq class " << n << " " << tn << std::endl; + c->newEqClass( n ); + } + //else if( isRelevantType( tn ) ){ + // //Debug("uf-ss-solver") << "WAIT: StrongSolverTheoryUf: New eq class " << n << " " << tn << std::endl; + // //d_new_eq_class_waiting[tn].push_back( n ); + //} +} + +/** merge */ +void StrongSolverTheoryUf::merge( Node a, Node b ){ + TypeNode tn = a.getType(); + ConflictFind* c = getConflictFind( tn ); + if( c ){ + Debug("uf-ss-solver") << "StrongSolverTheoryUf: Merge " << a << " " << b << " " << tn << std::endl; + c->merge( a, b ); + } + //else if( isRelevantType( tn ) ){ + //} +} + +/** assert terms are disequal */ +void StrongSolverTheoryUf::assertDisequal( Node a, Node b, Node reason ){ + TypeNode tn = a.getType(); + ConflictFind* c = getConflictFind( tn ); + if( c ){ + Debug("uf-ss-solver") << "StrongSolverTheoryUf: Assert disequal " << a << " " << b << " " << tn << std::endl; + //Assert( d_th->d_equalityEngine.getRepresentative( a )==a ); + //Assert( d_th->d_equalityEngine.getRepresentative( b )==b ); + c->assertDisequal( a, b, reason ); + } + //else if( isRelevantType( tn ) ){ + //} +} + +/** assert a node */ +void StrongSolverTheoryUf::assertNode( Node n, bool isDecision ){ + Debug("uf-ss-assert") << "Assert " << n << " " << isDecision << std::endl; + if( n.getKind()==CARDINALITY_CONSTRAINT ){ + TypeNode tn = n[0].getType(); + Assert( d_conf_find[tn]->getCardinality()>0 ); + Assert( isRelevantType( tn ) ); + Assert( d_conf_find[tn] ); + long nCard = n[1].getConst<Rational>().getNumerator().getLong(); + d_conf_find[tn]->assertCardinality( nCard, true ); + if( nCard==d_conf_find[tn]->getCardinality() ){ + d_conf_find[tn]->d_is_cardinality_set = true; + d_conf_find[tn]->d_is_cardinality_requested = false; + d_conf_find[tn]->d_is_cardinality_requested_c = false; + } + }else if( n.getKind()==NOT && n[0].getKind()==CARDINALITY_CONSTRAINT ){ + //must add new lemma + Node nn = n[0]; + TypeNode tn = nn[0].getType(); + Assert( isRelevantType( tn ) ); + Assert( d_conf_find[tn] ); + long nCard = nn[1].getConst<Rational>().getNumerator().getLong(); + d_conf_find[tn]->assertCardinality( nCard, false ); + if( nCard==d_conf_find[tn]->getCardinality() ){ + AlwaysAssert(!isDecision, "Error: Negative cardinality node decided upon"); + Debug("uf-ss-fmf") << "No model of size " << d_conf_find[tn]->getCardinality() << " exists for type " << tn << std::endl; + //Notice() << "No model of size " << d_conf_find[tn]->getCardinality() << " exists for type " << tn << std::endl; + //increment to next cardinality + d_statistics.d_max_model_size.maxAssign( d_conf_find[tn]->getCardinality() + 1 ); + d_conf_find[tn]->setCardinality( d_conf_find[tn]->getCardinality() + 1, d_out ); + //Notice() << d_conf_find[tn]->getCardinality() << " "; + ////give up permanently on this cardinality + //d_out->lemma( n ); + } + }else{ + ////FIXME: this is too strict: theory propagations are showing up as isDecision=true, but + //// a theory propagation is not a decision. + //if( isDecision ){ + // for( std::map< TypeNode, ConflictFind* >::iterator it = d_conf_find.begin(); it != d_conf_find.end(); ++it ){ + // if( !it->second->hasCardinalityAsserted() ){ + // Notice() << "Assert " << n << " " << isDecision << std::endl; + // Notice() << "Error: constraint asserted before cardinality for " << it->first << std::endl; + // Unimplemented(); + // } + // } + //} + } +} + + +/** check */ +void StrongSolverTheoryUf::check( Theory::Effort level ){ + Debug("uf-ss-solver") << "StrongSolverTheoryUf: check " << level << std::endl; + if( level==Theory::EFFORT_FULL ){ + debugPrint( "uf-ss-debug" ); + } + for( std::map< TypeNode, ConflictFind* >::iterator it = d_conf_find.begin(); it != d_conf_find.end(); ++it ){ + it->second->check( level, d_out ); + } + Debug("uf-ss-solver") << "Done StrongSolverTheoryUf: check " << level << std::endl; +} + +/** propagate */ +void StrongSolverTheoryUf::propagate( Theory::Effort level ){ + for( std::map< TypeNode, ConflictFind* >::iterator it = d_conf_find.begin(); it != d_conf_find.end(); ++it ){ + it->second->propagate( level, d_out ); + } +} + +void StrongSolverTheoryUf::preRegisterTerm( TNode n ){ + //shouldn't have to preregister this type (it may be that there are no quantifiers over tn) FIXME + TypeNode tn = n.getType(); + if( isRelevantType( tn ) ){ + preRegisterType( tn ); + } +} + +void StrongSolverTheoryUf::registerQuantifier( Node f ){ + Debug("uf-ss-register") << "Register quantifier " << f << std::endl; + //must ensure the quantifier does not quantify over arithmetic + for( int i=0; i<(int)f[0].getNumChildren(); i++ ){ + TypeNode tn = f[0][i].getType(); + if( isRelevantType( tn ) ){ + preRegisterType( tn ); + }else{ + if( tn==NodeManager::currentNM()->integerType() || tn==NodeManager::currentNM()->realType() ){ + Debug("uf-ss-na") << "Error: Cannot perform finite model finding on arithmetic quantifier"; + Debug("uf-ss-na") << " (" << f << ")"; + Debug("uf-ss-na") << std::endl; + Unimplemented("Cannot perform finite model finding on arithmetic quantifier"); + }else if( tn.isDatatype() ){ + Debug("uf-ss-na") << "Error: Cannot perform finite model finding on datatype quantifier"; + Debug("uf-ss-na") << " (" << f << ")"; + Debug("uf-ss-na") << std::endl; + Unimplemented("Cannot perform finite model finding on datatype quantifier"); + } + } + } +} + +void StrongSolverTheoryUf::preRegisterType( TypeNode tn ){ + if( d_conf_find.find( tn )==d_conf_find.end() ){ + Debug("uf-ss-register") << "Preregister " << tn << "." << std::endl; + //enter into incremental finite model finding mode: try cardinality = 1 first + //if( !d_conf_types.empty() ){ + // Debug("uf-ss-na") << "Strong solver unimplemented for multiple sorts." << std::endl; + // Unimplemented(); + //} + d_conf_find[tn] = new ConflictFind( tn, d_th->getSatContext(), d_th ); + //assign cardinality restriction + d_statistics.d_max_model_size.maxAssign( 1 ); + d_conf_find[tn]->setCardinality( 1, d_out ); + ////add waiting equivalence classes now + //if( !d_new_eq_class_waiting[tn].empty() ){ + // Debug("uf-ss-register") << "Add " << (int)d_new_eq_class_waiting[tn].size() << " new eq classes." << std::endl; + // for( int i=0; i<(int)d_new_eq_class_waiting[tn].size(); i++ ){ + // newEqClass( d_new_eq_class_waiting[tn][i] ); + // } + // d_new_eq_class_waiting[tn].clear(); + //} + d_conf_types.push_back( tn ); + } +} + +StrongSolverTheoryUf::ConflictFind* StrongSolverTheoryUf::getConflictFind( TypeNode tn ){ + std::map< TypeNode, ConflictFind* >::iterator it = d_conf_find.find( tn ); + //pre-register the type if not done already + if( it==d_conf_find.end() ){ + if( isRelevantType( tn ) ){ + preRegisterType( tn ); + it = d_conf_find.find( tn ); + } + } + if( it!=d_conf_find.end() ){ + //initialize the type if necessary + if( d_conf_find_init.find( tn )==d_conf_find_init.end() ){ + //assign cardinality restriction + d_statistics.d_max_model_size.maxAssign( 1 ); + it->second->setCardinality( 1, d_out ); + d_conf_find_init[tn] = true; + } + return it->second; + }else{ + return NULL; + } +} + +void StrongSolverTheoryUf::notifyRestart(){ + Debug("uf-ss-prop-as-dec") << "Restart?" << std::endl; +} + +/** get cardinality for sort */ +int StrongSolverTheoryUf::getCardinality( TypeNode t ) { + ConflictFind* c = getConflictFind( t ); + if( c ){ + return c->getCardinality(); + }else{ + return -1; + } +} + +void StrongSolverTheoryUf::getRepresentatives( TypeNode t, std::vector< Node >& reps ){ + ConflictFind* c = getConflictFind( t ); + if( c ){ + c->getRepresentatives( reps ); + } +} + +Node StrongSolverTheoryUf::getCardinalityTerm( TypeNode t ){ + ConflictFind* c = getConflictFind( t ); + if( c ){ + return c->getCardinalityTerm(); + }else{ + return Node::null(); + } +} + +bool StrongSolverTheoryUf::minimize(){ + for( std::map< TypeNode, ConflictFind* >::iterator it = d_conf_find.begin(); it != d_conf_find.end(); ++it ){ + if( !it->second->minimize( d_out ) ){ + return false; + } + } + return true; +} + +//print debug +void StrongSolverTheoryUf::debugPrint( const char* c ){ + //EqClassesIterator< TheoryUF::NotifyClass > eqc_iter( &((TheoryUF*)d_th)->d_equalityEngine ); + //while( !eqc_iter.isFinished() ){ + // Debug( c ) << "Eq class [[" << (*eqc_iter) << "]]" << std::endl; + // EqClassIterator< TheoryUF::NotifyClass > eqc_iter2( *eqc_iter, &((TheoryUF*)d_th)->d_equalityEngine ); + // Debug( c ) << " "; + // while( !eqc_iter2.isFinished() ){ + // Debug( c ) << "[" << (*eqc_iter2) << "] "; + // eqc_iter2++; + // } + // Debug( c ) << std::endl; + // eqc_iter++; + //} + + for( std::map< TypeNode, ConflictFind* >::iterator it = d_conf_find.begin(); it != d_conf_find.end(); ++it ){ + Debug( c ) << "Conflict find structure for " << it->first << ": " << std::endl; + it->second->debugPrint( c ); + Debug( c ) << std::endl; + } +} + +StrongSolverTheoryUf::Statistics::Statistics(): + d_clique_lemmas("StrongSolverTheoryUf::Clique_Lemmas", 0), + d_split_lemmas("StrongSolverTheoryUf::Split_Lemmas", 0), + d_disamb_term_lemmas("StrongSolverTheoryUf::Disambiguate_Term_Lemmas", 0), + d_max_model_size("StrongSolverTheoryUf::Max_Model_Size", 0) +{ + StatisticsRegistry::registerStat(&d_clique_lemmas); + StatisticsRegistry::registerStat(&d_split_lemmas); + StatisticsRegistry::registerStat(&d_disamb_term_lemmas); + StatisticsRegistry::registerStat(&d_max_model_size); +} + +StrongSolverTheoryUf::Statistics::~Statistics(){ + StatisticsRegistry::unregisterStat(&d_clique_lemmas); + StatisticsRegistry::unregisterStat(&d_split_lemmas); + StatisticsRegistry::unregisterStat(&d_disamb_term_lemmas); + StatisticsRegistry::unregisterStat(&d_max_model_size); +} + +bool StrongSolverTheoryUf::isRelevantType( TypeNode t ){ + return t!=NodeManager::currentNM()->booleanType() && + t!=NodeManager::currentNM()->integerType() && + t!=NodeManager::currentNM()->realType() && + t!=NodeManager::currentNM()->builtinOperatorType() && + !t.isFunction() && + !t.isDatatype(); +} + +bool StrongSolverTheoryUf::involvesRelevantType( Node n ){ + if( n.getKind()==APPLY_UF ){ + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + if( isRelevantType( n[i].getType() ) ){ + return true; + } + } + } + return false; +} diff --git a/src/theory/uf/theory_uf_strong_solver.h b/src/theory/uf/theory_uf_strong_solver.h new file mode 100644 index 000000000..e36441f6d --- /dev/null +++ b/src/theory/uf/theory_uf_strong_solver.h @@ -0,0 +1,322 @@ +/********************* */ +/*! \file theory_uf_strong_solver.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Theory uf strong solver + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY_UF_STRONG_SOLVER_H +#define __CVC4__THEORY_UF_STRONG_SOLVER_H + +#include "theory/theory.h" + +#include "context/context.h" +#include "context/context_mm.h" +#include "context/cdchunk_list.h" + +#include "util/stats.h" + +namespace CVC4 { +namespace theory { + +struct ModelBasisAttributeId {}; +typedef expr::Attribute<ModelBasisAttributeId, bool> ModelBasisAttribute; + +namespace uf { + +class TheoryUF; + +class StrongSolverTheoryUf{ +protected: + typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap; + typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap; + typedef context::CDChunkList<Node> NodeList; + typedef context::CDList<bool> BoolList; + typedef context::CDHashMap<TypeNode, bool, TypeNodeHashFunction> TypeNodeBoolMap; +public: + /** information for incremental conflict/clique finding for a particular sort */ + class ConflictFind { + public: + /** a partition of the current equality graph for which cliques can occur internally */ + class Region { + public: + /** conflict find pointer */ + ConflictFind* d_cf; + /** information stored about each node in region */ + class RegionNodeInfo { + public: + /** disequality list for node */ + class DiseqList { + public: + DiseqList( context::Context* c ) : d_size( c, 0 ), d_disequalities( c ){} + ~DiseqList(){} + context::CDO< unsigned > d_size; + NodeBoolMap d_disequalities; + void setDisequal( Node n, bool valid ){ + Assert( d_disequalities.find( n )==d_disequalities.end() || d_disequalities[n]!=valid ); + d_disequalities[ n ] = valid; + d_size = d_size + ( valid ? 1 : -1 ); + } + }; + private: + DiseqList d_internal; + DiseqList d_external; + public: + /** constructor */ + RegionNodeInfo( context::Context* c ) : d_internal( c ), d_external( c ), d_valid( c, true ){ + d_disequalities[0] = &d_internal; + d_disequalities[1] = &d_external; + } + ~RegionNodeInfo(){} + context::CDO< bool > d_valid; + DiseqList* d_disequalities[2]; + + int getNumDisequalities() { return d_disequalities[0]->d_size + d_disequalities[1]->d_size; } + int getNumExternalDisequalities() { return d_disequalities[0]->d_size; } + int getNumInternalDisequalities() { return d_disequalities[1]->d_size; } + }; + ///** end class RegionNodeInfo */ + private: + //a postulated clique + NodeBoolMap d_testClique; + context::CDO< unsigned > d_testCliqueSize; + //disequalities needed for this clique to happen + NodeBoolMap d_splits; + context::CDO< unsigned > d_splitsSize; + /** get split */ + Node getBestSplit(); + private: + //number of valid representatives in this region + context::CDO< unsigned > d_reps_size; + //total disequality size (external) + context::CDO< unsigned > d_total_diseq_external; + //total disequality size (internal) + context::CDO< unsigned > d_total_diseq_internal; + public: + //constructor + Region( ConflictFind* cf, context::Context* c ) : d_cf( cf ), d_testClique( c ), d_testCliqueSize( c, 0 ), + d_splits( c ), d_splitsSize( c, 0 ), d_reps_size( c, 0 ), d_total_diseq_external( c, 0 ), + d_total_diseq_internal( c, 0 ), d_valid( c, true ) { + } + ~Region(){} + //region node infomation + std::map< Node, RegionNodeInfo* > d_nodes; + //whether region is valid + context::CDO< bool > d_valid; + public: + //get num reps + int getNumReps() { return d_reps_size; } + // has representative + bool hasRep( Node n ) { return d_nodes.find( n )!=d_nodes.end() && d_nodes[n]->d_valid; } + //take node from region + void takeNode( Region* r, Node n ); + //merge with other region + void combine( Region* r ); + /** set rep */ + void setRep( Node n, bool valid ); + /** merge */ + void setEqual( Node a, Node b ); + //set n1 != n2 to value 'valid', type is whether it is internal/external + void setDisequal( Node n1, Node n2, int type, bool valid ); + // is disequal + bool isDisequal( Node n1, Node n2, int type ); + public: + /** get must merge */ + bool getMustCombine( int cardinality ); + /** check for cliques */ + bool check( Theory::Effort level, int cardinality, std::vector< Node >& clique ); + /** has splits */ + bool hasSplits() { return d_splitsSize>0; } + /** add split */ + void addSplit( OutputChannel* out ); + /** get representatives */ + void getRepresentatives( std::vector< Node >& reps ); + /** minimize */ + bool minimize( OutputChannel* out ); + /** get external disequalities */ + void getNumExternalDisequalities( std::map< Node, int >& num_ext_disequalities ); + //print debug + void debugPrint( const char* c, bool incClique = false ); + }; + private: + /** theory uf pointer */ + TheoryUF* d_th; + /** regions used to d_region_index */ + context::CDO< unsigned > d_regions_index; + /** vector of regions */ + std::vector< Region* > d_regions; + /** map from Nodes to index of d_regions they exist in, -1 means invalid */ + NodeIntMap d_regions_map; + /** regions used to d_region_index */ + context::CDO< unsigned > d_disequalities_index; + /** list of all disequalities */ + std::vector< Node > d_disequalities; + /** number of representatives in all regions */ + context::CDO< unsigned > d_reps; + /** whether two terms are ambiguous (indexed by equalities) */ + NodeBoolMap d_term_amb; + private: + /** merge regions */ + void combineRegions( int ai, int bi ); + /** move node n to region ri */ + void moveNode( Node n, int ri ); + /** get number of disequalities from node n to region ri */ + int getNumDisequalitiesToRegion( Node n, int ri ); + /** get number of disequalities from Region r to other regions */ + void getDisequalitiesToRegions( int ri, std::map< int, int >& regions_diseq ); + /** check if we need to combine region ri */ + bool checkRegion( int ri, bool rec = true ); + /** explain clique */ + void explainClique( std::vector< Node >& clique, OutputChannel* out ); + /** is valid */ + bool isValid( int ri ) { return ri>=0 && ri<(int)d_regions_index && d_regions[ ri ]->d_valid; } + /** check ambiguous terms */ + bool disambiguateTerms( OutputChannel* out ); + private: + /** cardinality operating with */ + context::CDO< int > d_cardinality; + /** type */ + TypeNode d_type; + /** cardinality lemma term */ + Node d_cardinality_lemma_term; + /** cardinality literals */ + std::map< int, Node > d_cardinality_literal; + /** cardinality lemmas */ + std::map< int, Node > d_cardinality_lemma; + /** cardinality assertions (indexed by cardinality literals ) */ + NodeBoolMap d_cardinality_assertions; + public: + ConflictFind( TypeNode tn, context::Context* c, TheoryUF* th ) : + d_th( th ), d_regions_index( c, 0 ), d_regions_map( c ), d_disequalities_index( c, 0 ), + d_reps( c, 0 ), d_term_amb( c ), d_cardinality( c, 1 ), d_type( tn ), + d_cardinality_assertions( c ), d_is_cardinality_set( c, false ), + d_is_cardinality_requested_c( c, false ), d_is_cardinality_requested( false ), d_hasCard( c, false ){} + ~ConflictFind(){} + /** new node */ + void newEqClass( Node n ); + /** merge */ + void merge( Node a, Node b ); + /** assert terms are disequal */ + void assertDisequal( Node a, Node b, Node reason ); + /** assert cardinality */ + void assertCardinality( int c, bool val ); + /** whether cardinality has been asserted */ + bool hasCardinalityAsserted() { return d_hasCard; } + /** check */ + void check( Theory::Effort level, OutputChannel* out ); + /** propagate */ + void propagate( Theory::Effort level, OutputChannel* out ); + //print debug + void debugPrint( const char* c ); + /** set cardinality */ + void setCardinality( int c, OutputChannel* out ); + /** get cardinality */ + int getCardinality() { return d_cardinality; } + /** get representatives */ + void getRepresentatives( std::vector< Node >& reps ); + /** get model basis term */ + Node getCardinalityTerm() { return d_cardinality_lemma_term; } + /** minimize */ + bool minimize( OutputChannel* out ); + /** get cardinality lemma */ + Node getCardinalityLemma(); + public: + /** get number of regions (for debugging) */ + int getNumRegions(); + /** is cardinality set */ + context::CDO< bool > d_is_cardinality_set; + context::CDO< bool > d_is_cardinality_requested_c; + bool d_is_cardinality_requested; + /** whether a positive cardinality constraint has been asserted */ + context::CDO< bool > d_hasCard; + }; /** class ConflictFind */ +private: + /** The output channel for the strong solver. */ + OutputChannel* d_out; + /** theory uf pointer */ + TheoryUF* d_th; + /** conflict find structure, one for each type */ + std::map< TypeNode, ConflictFind* > d_conf_find; + /** all types */ + std::vector< TypeNode > d_conf_types; + /** whether conflict find data structures have been initialized */ + TypeNodeBoolMap d_conf_find_init; + /** pre register type */ + void preRegisterType( TypeNode tn ); + /** get conflict find */ + ConflictFind* getConflictFind( TypeNode tn ); +public: + StrongSolverTheoryUf(context::Context* c, context::UserContext* u, OutputChannel& out, TheoryUF* th); + ~StrongSolverTheoryUf() {} + /** new node */ + void newEqClass( Node n ); + /** merge */ + void merge( Node a, Node b ); + /** assert terms are disequal */ + void assertDisequal( Node a, Node b, Node reason ); + /** assert node */ + void assertNode( Node n, bool isDecision ); +public: + /** check */ + void check( Theory::Effort level ); + /** propagate */ + void propagate( Theory::Effort level ); + /** preregister a term */ + void preRegisterTerm( TNode n ); + /** preregister a quantifier */ + void registerQuantifier( Node f ); + /** notify restart */ + void notifyRestart(); +public: + /** identify */ + std::string identify() const { return std::string("StrongSolverTheoryUf"); } + //print debug + void debugPrint( const char* c ); +public: + /** get number of types */ + int getNumCardinalityTypes() { return (int)d_conf_types.size(); } + /** get type */ + TypeNode getCardinalityType( int i ) { return d_conf_types[i]; } + /** get cardinality for sort */ + int getCardinality( TypeNode t ); + /** get representatives */ + void getRepresentatives( TypeNode t, std::vector< Node >& reps ); + /** get cardinality term */ + Node getCardinalityTerm( TypeNode t ); + /** minimize */ + bool minimize(); + + class Statistics { + public: + IntStat d_clique_lemmas; + IntStat d_split_lemmas; + IntStat d_disamb_term_lemmas; + IntStat d_max_model_size; + Statistics(); + ~Statistics(); + }; + /** statistics class */ + Statistics d_statistics; + + /** is relavant type */ + static bool isRelevantType( TypeNode t ); + /** involves relavant type */ + static bool involvesRelevantType( Node n ); +};/* class StrongSolverTheoryUf */ + +} +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY_UF_STRONG_SOLVER_H */ diff --git a/src/theory/uf/theory_uf_type_rules.h b/src/theory/uf/theory_uf_type_rules.h index 3d7e51746..b68a11abd 100644 --- a/src/theory/uf/theory_uf_type_rules.h +++ b/src/theory/uf/theory_uf_type_rules.h @@ -59,6 +59,20 @@ public: } };/* class UfTypeRule */ +class CardinalityConstraintTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw(TypeCheckingExceptionPrivate) { + if( check ) { + TypeNode valType = n[1].getType(check); + if( valType != nodeManager->integerType() ) { + throw TypeCheckingExceptionPrivate(n, "cardinality constraint must be integer"); + } + } + return nodeManager->booleanType(); + } +};/* class UfTypeRule */ + }/* CVC4::theory::uf namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/valuation.cpp b/src/theory/valuation.cpp index cae62570c..ef02f6278 100644 --- a/src/theory/valuation.cpp +++ b/src/theory/valuation.cpp @@ -100,5 +100,9 @@ Node Valuation::ensureLiteral(TNode n) { return preprocessed; } +bool Valuation::isDecision(Node lit) const { + return d_engine->getPropEngine()->isDecision(lit); +} + }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/valuation.h b/src/theory/valuation.h index ab47dcbdd..dd3848e7f 100644 --- a/src/theory/valuation.h +++ b/src/theory/valuation.h @@ -112,6 +112,14 @@ public: */ Node ensureLiteral(TNode n) CVC4_WARN_UNUSED_RESULT; + /** + * Returns whether the given lit (which must be a SAT literal) is a decision + * literal or not. Throws an exception if lit is not a SAT literal. "lit" may + * be in either phase; that is, if "lit" is a SAT literal, this function returns + * true both for lit and the negation of lit. + */ + bool isDecision(Node lit) const; + };/* class Valuation */ }/* CVC4::theory namespace */ diff --git a/src/util/datatype.cpp b/src/util/datatype.cpp index f009bbbbe..86a43c878 100644 --- a/src/util/datatype.cpp +++ b/src/util/datatype.cpp @@ -438,7 +438,7 @@ void DatatypeConstructor::resolve(ExprManager* em, DatatypeType self, d_tester = em->mkVar(d_name.substr(d_name.find('\0') + 1), em->mkTesterType(self)); d_name.resize(d_name.find('\0')); d_constructor = em->mkVar(d_name, em->mkConstructorType(*this, self)); - //associate constructor with all selectors + // associate constructor with all selectors for(iterator i = begin(), i_end = end(); i != i_end; ++i) { (*i).d_constructor = d_constructor; } diff --git a/src/util/datatype.h b/src/util/datatype.h index d39d7110d..b701073c7 100644 --- a/src/util/datatype.h +++ b/src/util/datatype.h @@ -361,9 +361,9 @@ public: * * Datatypes may also be defined parametrically, such as this example: * - * DATATYPE
- * list[T] = cons(car : T, cdr : list[T]) | null,
- * tree = node(children : list[tree]) | leaf
+ * DATATYPE + * 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. diff --git a/src/util/ite_removal.cpp b/src/util/ite_removal.cpp index 9d2524170..9a4fc8dc2 100644 --- a/src/util/ite_removal.cpp +++ b/src/util/ite_removal.cpp @@ -79,23 +79,30 @@ Node RemoveITE::run(TNode node, std::vector<Node>& output, } // If not an ITE, go deep - vector<Node> newChildren; - bool somethingChanged = false; - if(node.getMetaKind() == kind::metakind::PARAMETERIZED) { - newChildren.push_back(node.getOperator()); - } - // Remove the ITEs from the children - for(TNode::const_iterator it = node.begin(), end = node.end(); it != end; ++it) { - Node newChild = run(*it, output, iteSkolemMap); - somethingChanged |= (newChild != *it); - newChildren.push_back(newChild); - } + if( node.getKind() != kind::FORALL && + node.getKind() != kind::EXISTS && + node.getKind() != kind::REWRITE_RULE ) { + vector<Node> newChildren; + bool somethingChanged = false; + if(node.getMetaKind() == kind::metakind::PARAMETERIZED) { + newChildren.push_back(node.getOperator()); + } + // Remove the ITEs from the children + for(TNode::const_iterator it = node.begin(), end = node.end(); it != end; ++it) { + Node newChild = run(*it, output, iteSkolemMap); + somethingChanged |= (newChild != *it); + newChildren.push_back(newChild); + } - // If changes, we rewrite - if(somethingChanged) { - cachedRewrite = nodeManager->mkNode(node.getKind(), newChildren); - nodeManager->setAttribute(node, IteRewriteAttr(), cachedRewrite); - return cachedRewrite; + // If changes, we rewrite + if(somethingChanged) { + cachedRewrite = nodeManager->mkNode(node.getKind(), newChildren); + nodeManager->setAttribute(node, IteRewriteAttr(), cachedRewrite); + return cachedRewrite; + } else { + nodeManager->setAttribute(node, IteRewriteAttr(), Node::null()); + return node; + } } else { nodeManager->setAttribute(node, IteRewriteAttr(), Node::null()); return node; diff --git a/src/util/options.cpp b/src/util/options.cpp index 26881e052..1296fa5af 100644 --- a/src/util/options.cpp +++ b/src/util/options.cpp @@ -130,6 +130,25 @@ Options::Options() : arithRewriteEqSetByUser(false), ufSymmetryBreaker(false), ufSymmetryBreakerSetByUser(false), + miniscopeQuant(true), + miniscopeQuantFreeVar(true), + prenexQuant(true), + varElimQuant(false), + cnfQuant(false), + preSkolemQuant(false), + smartTriggers(true), + registerQuantBodyTerms(false), + instWhenMode(INST_WHEN_FULL_LAST_CALL), + eagerInstQuant(false), + finiteModelFind(false), + fmfRegionSat(false), + fmfModelBasedInst(true), + efficientEMatching(false), + literalMatchMode(LITERAL_MATCH_NONE), + cbqi(false), + cbqiSetByUser(false), + userPatternsQuant(true), + flipDecision(false), lemmaOutputChannel(NULL), lemmaInputChannel(NULL), threads(2),// default should be 1 probably, but say 2 for now @@ -240,6 +259,27 @@ Additional CVC4 options:\n\ --enable-symmetry-breaker turns on UF symmetry breaker (Deharbe et al.,\n\ CADE 2011) [on by default only for QF_UF]\n\ --disable-symmetry-breaker turns off UF symmetry breaker\n\ + --disable-miniscope-quant disable miniscope quantifiers\n\ + --disable-miniscope-quant-fv disable miniscope quantifiers for ground subformulas\n\ + --disable-prenex-quant disable prenexing of quantified formulas\n\ + --var-elim-quant enable variable elimination of quantified formulas\n\ + --cnf-quant apply CNF conversion to quantified formulas\n\ + --pre-skolem-quant apply skolemization eagerly to bodies of quantified formulas\n\ + --disable-smart-triggers disable smart triggers\n\ + --register-quant-body-terms consider terms within bodies of quantified formulas for matching\n\ + --inst-when=MODE when to apply instantiation\n\ + --eager-inst-quant apply quantifier instantiation eagerly\n\ + --finite-model-find use finite model finding heuristic for quantifier instantiation\n\ + --use-fmf-region-sat use region-based SAT heuristic for finite model finding\n\ + --disable-fmf-model-inst disable model-based instantiation for finite model finding\n\ + --efficient-e-matching use efficient E-matching\n\ + --literal-matching=MODE choose literal matching mode\n\ + --enable-cbqi turns on counterexample-based quantifier instantiation [off by default]\n\ + --disable-cbqi turns off counterexample-based quantifier instantiation\n\ + --ignore-user-patterns ignore user-provided patterns for quantifier instantiation\n\ + --enable-flip-decision turns on flip decision heuristic\n\ + --disable-dio-solver turns off Linear Diophantine Equation solver (Griggio, JSAT 2012)\n\ + --disable-arith-rewrite-equalities turns off the preprocessing rewrite turning equalities into a conjunction of inequalities.\n\ --threads=N sets the number of solver threads\n\ --threadN=string configures thread N (0..#threads-1)\n\ --filter-lemma-length=N don't share lemmas strictly longer than N\n\ @@ -439,7 +479,7 @@ void Options::printLanguageHelp(std::ostream& out) { */ enum OptionValue { OPTION_VALUE_BEGIN = 256, /* avoid clashing with char options */ - SMTCOMP, + SMTCOMP, STATS, SEGV_NOSPIN, OUTPUT_LANGUAGE, @@ -496,6 +536,25 @@ enum OptionValue { DISABLE_ARITHMETIC_REWRITE_EQUALITIES, ENABLE_SYMMETRY_BREAKER, DISABLE_SYMMETRY_BREAKER, + DISABLE_MINISCOPE_QUANT, + DISABLE_MINISCOPE_QUANT_FV, + DISABLE_PRENEX_QUANT, + VAR_ELIM_QUANT, + CNF_QUANT, + PRE_SKOLEM_QUANT, + DISABLE_SMART_TRIGGERS, + REGISTER_QUANT_BODY_TERMS, + INST_WHEN, + EAGER_INST_QUANT, + FINITE_MODEL_FIND, + FMF_REGION_SAT, + DISABLE_FMF_MODEL_BASED_INST, + EFFICIENT_E_MATCHING, + LITERAL_MATCHING, + ENABLE_CBQI, + DISABLE_CBQI, + IGNORE_USER_PATTERNS, + ENABLE_FLIP_DECISION, PARALLEL_THREADS, PARALLEL_SEPARATE_OUTPUT, PORTFOLIO_FILTER_LENGTH, @@ -602,6 +661,25 @@ static struct option cmdlineOptions[] = { { "disable-arith-rewrite-equalities", no_argument, NULL, DISABLE_ARITHMETIC_REWRITE_EQUALITIES }, { "enable-symmetry-breaker", no_argument, NULL, ENABLE_SYMMETRY_BREAKER }, { "disable-symmetry-breaker", no_argument, NULL, DISABLE_SYMMETRY_BREAKER }, + { "disable-miniscope-quant", no_argument, NULL, DISABLE_MINISCOPE_QUANT }, + { "disable-miniscope-quant-fv", no_argument, NULL, DISABLE_MINISCOPE_QUANT_FV }, + { "disable-prenex-quant", no_argument, NULL, DISABLE_PRENEX_QUANT }, + { "var-elim-quant", no_argument, NULL, VAR_ELIM_QUANT }, + { "cnf-quant", no_argument, NULL, CNF_QUANT }, + { "pre-skolem-quant", no_argument, NULL, PRE_SKOLEM_QUANT }, + { "disable-smart-triggers", no_argument, NULL, DISABLE_SMART_TRIGGERS }, + { "register-quant-body-terms", no_argument, NULL, REGISTER_QUANT_BODY_TERMS }, + { "inst-when", required_argument, NULL, INST_WHEN }, + { "eager-inst-quant", no_argument, NULL, EAGER_INST_QUANT }, + { "finite-model-find", no_argument, NULL, FINITE_MODEL_FIND }, + { "use-fmf-region-sat", no_argument, NULL, FMF_REGION_SAT }, + { "disable-fmf-model-inst", no_argument, NULL, DISABLE_FMF_MODEL_BASED_INST }, + { "efficient-e-matching", no_argument, NULL, EFFICIENT_E_MATCHING }, + { "literal-matching", required_argument, NULL, LITERAL_MATCHING }, + { "enable-cbqi", no_argument, NULL, ENABLE_CBQI }, + { "disable-cbqi", no_argument, NULL, DISABLE_CBQI }, + { "ignore-user-patterns", no_argument, NULL, IGNORE_USER_PATTERNS }, + { "enable-flip-decision", no_argument, NULL, ENABLE_FLIP_DECISION }, { "threads", required_argument, NULL, PARALLEL_THREADS }, { "separate-output", no_argument, NULL, PARALLEL_SEPARATE_OUTPUT }, { "filter-lemma-length", required_argument, NULL, PORTFOLIO_FILTER_LENGTH }, @@ -984,7 +1062,7 @@ throw(OptionException) { case 'm': produceModels = true; break; - + case PRODUCE_ASSIGNMENTS: produceAssignments = true; break; @@ -1016,7 +1094,7 @@ throw(OptionException) { throw OptionException("This is not a proof-enabled build of CVC4; --proof cannot be used"); #endif /* CVC4_PROOF */ break; - + case NO_TYPE_CHECKING: typeChecking = false; earlyTypeChecking = false; @@ -1073,7 +1151,91 @@ throw(OptionException) { ufSymmetryBreaker = false; ufSymmetryBreakerSetByUser = true; break; - + case DISABLE_MINISCOPE_QUANT: + miniscopeQuant = false; + break; + case DISABLE_MINISCOPE_QUANT_FV: + miniscopeQuantFreeVar = false; + break; + case DISABLE_PRENEX_QUANT: + prenexQuant = false; + break; + case VAR_ELIM_QUANT: + varElimQuant = true; + break; + case CNF_QUANT: + cnfQuant = true; + break; + case PRE_SKOLEM_QUANT: + preSkolemQuant = true; + break; + case DISABLE_SMART_TRIGGERS: + smartTriggers = false; + break; + case REGISTER_QUANT_BODY_TERMS: + registerQuantBodyTerms = true; + break; + case INST_WHEN: + if(!strcmp(optarg, "pre-full")) { + instWhenMode = INST_WHEN_PRE_FULL; + } else if(!strcmp(optarg, "full")) { + instWhenMode = INST_WHEN_FULL; + } else if(!strcmp(optarg, "full-last-call")) { + instWhenMode = INST_WHEN_FULL_LAST_CALL; + } else if(!strcmp(optarg, "last-call")) { + instWhenMode = INST_WHEN_LAST_CALL; + } else if(!strcmp(optarg, "help")) { + //puts(instWhenHelp.c_str()); + exit(1); + } else { + throw OptionException(string("unknown option for --inst-when: `") + + optarg + "'. Try --inst-when help."); + } + break; + case EAGER_INST_QUANT: + eagerInstQuant = true; + break; + case FINITE_MODEL_FIND: + finiteModelFind = true; + break; + case FMF_REGION_SAT: + fmfRegionSat = true; + break; + case DISABLE_FMF_MODEL_BASED_INST: + fmfModelBasedInst = false; + break; + case EFFICIENT_E_MATCHING: + efficientEMatching = true; + break; + case LITERAL_MATCHING: + if(!strcmp(optarg, "none")) { + literalMatchMode = LITERAL_MATCH_NONE; + } else if(!strcmp(optarg, "predicate")) { + literalMatchMode = LITERAL_MATCH_PREDICATE; + } else if(!strcmp(optarg, "equality")) { + literalMatchMode = LITERAL_MATCH_EQUALITY; + } else if(!strcmp(optarg, "help")) { + //puts(literalMatchHelp.c_str()); + exit(1); + } else { + throw OptionException(string("unknown option for --literal-matching: `") + + optarg + "'. Try --literal-matching help."); + } + break; + case ENABLE_CBQI: + cbqi = true; + cbqiSetByUser = true; + break; + case DISABLE_CBQI: + cbqi = false; + cbqiSetByUser = true; + break; + case IGNORE_USER_PATTERNS: + userPatternsQuant = false; + break; + case ENABLE_FLIP_DECISION: + flipDecision = true; + break; case TIME_LIMIT: { int i = atoi(optarg); @@ -1141,7 +1303,7 @@ throw(OptionException) { optarg + "' is not between 0.0 and 1.0."); } break; - + case SAT_RESTART_FIRST: { int i = atoi(optarg); @@ -1151,7 +1313,7 @@ throw(OptionException) { satRestartFirst = i; break; } - + case SAT_RESTART_INC: { int i = atoi(optarg); @@ -1341,7 +1503,7 @@ throw(OptionException) { case PORTFOLIO_FILTER_LENGTH: sharingFilterByLength = atoi(optarg); - break; + break; case ':': // This can be a long or short option, and the way to get at the name of it is different. diff --git a/src/util/options.h b/src/util/options.h index 0584fdc2a..fb5f71060 100644 --- a/src/util/options.h +++ b/src/util/options.h @@ -310,6 +310,126 @@ struct CVC4_PUBLIC Options { */ bool ufSymmetryBreakerSetByUser; + /** + * Whether to mini-scope quantifiers. + * For example, forall x. ( P( x ) ^ Q( x ) ) will be rewritten to + * ( forall x. P( x ) ) ^ ( forall x. Q( x ) ) + */ + bool miniscopeQuant; + + /** + * Whether to mini-scope quantifiers based on formulas with no free variables. + * For example, forall x. ( P( x ) V Q ) will be rewritten to + * ( forall x. P( x ) ) V Q + */ + bool miniscopeQuantFreeVar; + + /** + * Whether to prenex (nested universal) quantifiers + */ + bool prenexQuant; + + /** + * Whether to variable-eliminate quantifiers. + * For example, forall x y. ( P( x, y ) V x != c ) will be rewritten to + * forall y. P( c, y ) + */ + bool varElimQuant; + + /** + * Whether to CNF quantifier bodies + */ + bool cnfQuant; + + /** + * Whether to pre-skolemize quantifier bodies. + * For example, forall x. ( P( x ) => (exists y. f( y ) = x) ) will be rewritten to + * forall x. P( x ) => f( S( x ) ) = x + */ + bool preSkolemQuant; + + /** + * Whether to use smart triggers + */ + bool smartTriggers; + + /** + * Whether to consider terms in the bodies of quantifiers for matching + */ + bool registerQuantBodyTerms; + + /** Enumeration of inst_when modes (when to instantiate). */ + 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; + /** When to perform instantiation round. */ + InstWhenMode instWhenMode; + + /** + * Whether to eagerly instantiate quantifiers + */ + bool eagerInstQuant; + + /** + * Whether to use finite model find heuristic + */ + bool finiteModelFind; + + /** + * Whether to use region-based SAT for finite model finding + */ + bool fmfRegionSat; + + /** + * Whether to use model-based exhaustive instantiation for finite model finding + */ + bool fmfModelBasedInst; + + /** + * Whether to use efficient E-matching + */ + bool efficientEMatching; + + /** Enumeration of literal matching modes. */ + 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; + + /** Which literal matching mode to use. */ + LiteralMatchMode literalMatchMode; + + /** + * Whether to do counterexample-based quantifier instantiation + */ + bool cbqi; + + /** + * Whether the user explicitly requested that counterexample-based + * quantifier instantiation be enabled or disabled. + */ + bool cbqiSetByUser; + + /** + * Whether to use user patterns for pattern-based instantiation + */ + bool userPatternsQuant; + + /** + * Whether to use flip decision (useful when cbqi=true) + */ + bool flipDecision; /** The output channel to receive notfication events for new lemmas */ LemmaOutputChannel* lemmaOutputChannel; |