diff options
Diffstat (limited to 'src')
136 files changed, 9981 insertions, 8727 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 279e52e09..d75535e15 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,7 +20,7 @@ AM_CPPFLAGS = \ AM_CXXFLAGS = -Wall -Wno-unknown-pragmas -Wno-parentheses $(FLAG_VISIBILITY_HIDDEN) SUBDIRS = lib options expr util prop/minisat prop/bvminisat . parser compat bindings main -THEORIES = builtin booleans uf arith bv arrays datatypes sets strings quantifiers rewriterules idl +THEORIES = builtin booleans uf arith bv arrays datatypes sets strings quantifiers idl lib_LTLIBRARIES = libcvc4.la @@ -116,6 +116,7 @@ libcvc4_la_SOURCES = \ smt/boolean_terms.h \ smt/boolean_terms.cpp \ smt/logic_exception.h \ + smt/logic_request.h \ smt/simplification_mode.h \ smt/simplification_mode.cpp \ smt/options_handlers.h \ @@ -307,28 +308,13 @@ libcvc4_la_SOURCES = \ theory/quantifiers/quant_conflict_find.h \ theory/quantifiers/quant_conflict_find.cpp \ theory/quantifiers/options_handlers.h \ - theory/rewriterules/theory_rewriterules_rules.h \ - theory/rewriterules/theory_rewriterules_rules.cpp \ - theory/rewriterules/theory_rewriterules.h \ - theory/rewriterules/theory_rewriterules.cpp \ - theory/rewriterules/theory_rewriterules_rewriter.h \ - theory/rewriterules/theory_rewriterules_type_rules.h \ - theory/rewriterules/theory_rewriterules_preprocess.h \ - theory/rewriterules/theory_rewriterules_params.h \ - theory/rewriterules/rr_inst_match.h \ - theory/rewriterules/rr_inst_match_impl.h \ - theory/rewriterules/rr_inst_match.cpp \ - theory/rewriterules/rr_trigger.h \ - theory/rewriterules/rr_trigger.cpp \ - theory/rewriterules/rr_candidate_generator.h \ - theory/rewriterules/rr_candidate_generator.cpp \ - theory/rewriterules/efficient_e_matching.h \ - theory/rewriterules/efficient_e_matching.cpp \ theory/arith/theory_arith_type_rules.h \ theory/arith/type_enumerator.h \ theory/arith/arithvar.h \ theory/arith/arithvar.cpp \ theory/arith/bound_counts.h \ + theory/arith/arith_ite_utils.h \ + theory/arith/arith_ite_utils.cpp \ theory/arith/arith_rewriter.h \ theory/arith/arith_rewriter.cpp \ theory/arith/arith_static_learner.h \ @@ -384,6 +370,8 @@ libcvc4_la_SOURCES = \ theory/arith/arith_unate_lemma_mode.cpp \ theory/arith/arith_propagation_mode.h \ theory/arith/arith_propagation_mode.cpp \ + theory/arith/cut_log.h \ + theory/arith/cut_log.cpp \ theory/arith/options_handlers.h \ theory/booleans/type_enumerator.h \ theory/booleans/theory_bool.h \ @@ -468,7 +456,6 @@ EXTRA_DIST = \ theory/strings/kinds \ theory/arrays/kinds \ theory/quantifiers/kinds \ - theory/rewriterules/kinds \ theory/arith/kinds \ theory/booleans/kinds \ theory/example/ecdata.h \ diff --git a/src/cvc4.i b/src/cvc4.i index aadbc374d..c0042b513 100644 --- a/src/cvc4.i +++ b/src/cvc4.i @@ -147,6 +147,8 @@ std::set<JavaInputStreamAdapter*> CVC4::JavaInputStreamAdapter::s_adapters; %typemap(throws) CVC4::parser::InputStreamException = CVC4::Exception; %typemap(throws) CVC4::parser::ParserException = CVC4::Exception; +%typemap(throws) CVC4::RationalFromDoubleException = Exception; + // Generate an error if the mapping from C++ CVC4 Exception to Java CVC4 Exception doesn't exist above %typemap(throws) SWIGTYPE, SWIGTYPE &, SWIGTYPE *, SWIGTYPE [], SWIGTYPE [ANY] %{ #error "exception $1_type doesn't map to Java correctly---please edit src/cvc4.i and add it" diff --git a/src/expr/node.cpp b/src/expr/node.cpp index c88fd187d..34a72e106 100644 --- a/src/expr/node.cpp +++ b/src/expr/node.cpp @@ -55,8 +55,13 @@ UnknownTypeException::UnknownTypeException(TNode n) throw() : /** Is this node constant? (and has that been computed yet?) */ struct IsConstTag { }; struct IsConstComputedTag { }; +struct HasBoundVarTag { }; +struct HasBoundVarComputedTag { }; typedef expr::Attribute<IsConstTag, bool> IsConstAttr; typedef expr::Attribute<IsConstComputedTag, bool> IsConstComputedAttr; +/** Attribute true for expressions with bound variables in them */ +typedef expr::Attribute<HasBoundVarTag, bool> HasBoundVarAttr; +typedef expr::Attribute<HasBoundVarComputedTag, bool> HasBoundVarComputedAttr; template <bool ref_count> bool NodeTemplate<ref_count>::isConst() const { @@ -91,7 +96,29 @@ bool NodeTemplate<ref_count>::isConst() const { } } +template <bool ref_count> +bool NodeTemplate<ref_count>::hasBoundVar() { + assertTNodeNotExpired(); + if(! getAttribute(HasBoundVarComputedAttr())) { + bool hasBv = false; + if(getKind() == kind::BOUND_VARIABLE) { + hasBv = true; + } else { + for(iterator i = begin(); i != end() && !hasBv; ++i) { + hasBv = (*i).hasBoundVar(); + } + } + setAttribute(HasBoundVarAttr(), hasBv); + setAttribute(HasBoundVarComputedAttr(), true); + Debug("bva") << *this << " has bva : " << getAttribute(HasBoundVarAttr()) << std::endl; + return hasBv; + } + return getAttribute(HasBoundVarAttr()); +} + template bool NodeTemplate<true>::isConst() const; template bool NodeTemplate<false>::isConst() const; +template bool NodeTemplate<true>::hasBoundVar(); +template bool NodeTemplate<false>::hasBoundVar(); }/* CVC4 namespace */ diff --git a/src/expr/node.h b/src/expr/node.h index 9ada7879c..ba139748e 100644 --- a/src/expr/node.h +++ b/src/expr/node.h @@ -424,6 +424,13 @@ public: // bool properlyContainsDecision(); // maybe not atomic but all children are /** + * Returns true iff this node contains a bound variable. This bound + * variable may or may not be free. + * @return true iff this node contains a bound variable. + */ + bool hasBoundVar(); + + /** * Convert this Node into an Expr using the currently-in-scope * manager. Essentially this is like an "operator Expr()" but we * don't want it to compete with implicit conversions between e.g. diff --git a/src/expr/node_manager.h b/src/expr/node_manager.h index b4d20b514..15c49efd8 100644 --- a/src/expr/node_manager.h +++ b/src/expr/node_manager.h @@ -1476,4 +1476,4 @@ NodeClass NodeManager::mkConstInternal(const T& val) { }/* CVC4 namespace */ -#endif /* __CVC4__EXPR_MANAGER_H */ +#endif /* __CVC4__NODE_MANAGER_H */ diff --git a/src/main/Makefile.am b/src/main/Makefile.am index 042b7cf45..7e87fa59a 100644 --- a/src/main/Makefile.am +++ b/src/main/Makefile.am @@ -3,7 +3,7 @@ AM_CPPFLAGS = \ -I@builddir@/.. $(ANTLR_INCLUDES) -I@srcdir@/../include -I@srcdir@/.. AM_CXXFLAGS = -Wall -Wno-unknown-pragmas -bin_PROGRAMS = cvc4 cvc4-translator +bin_PROGRAMS = cvc4 noinst_LIBRARIES = libmain.a @@ -62,12 +62,6 @@ cvc4_LDADD += \ @builddir@/../lib/libreplacements.la endif -cvc4_translator_SOURCES = \ - translator.cpp -cvc4_translator_LDADD = \ - @builddir@/../parser/libcvc4parser.la \ - @builddir@/../libcvc4.la - BUILT_SOURCES = \ $(TOKENS_FILES) diff --git a/src/main/driver_unified.cpp b/src/main/driver_unified.cpp index bf66629dd..adc40a9d1 100644 --- a/src/main/driver_unified.cpp +++ b/src/main/driver_unified.cpp @@ -42,7 +42,6 @@ #include "smt/options.h" #include "theory/uf/options.h" #include "util/output.h" -#include "util/dump.h" #include "util/result.h" #include "util/statistics_registry.h" @@ -184,12 +183,10 @@ int runCvc4(int argc, char* argv[], Options& opts) { ChatChannel.setStream(CVC4::null_os); MessageChannel.setStream(CVC4::null_os); WarningChannel.setStream(CVC4::null_os); - DumpChannel.setStream(CVC4::null_os); } // important even for muzzled builds (to get result output right) *opts[options::out] << Expr::setlanguage(opts[options::outputLanguage]); - DumpChannel.getStream() << Expr::setlanguage(opts[options::outputLanguage]); // Create the expression manager using appropriate options ExprManager* exprMgr; diff --git a/src/main/portfolio_util.cpp b/src/main/portfolio_util.cpp index a2d0ff700..e4fcf6024 100644 --- a/src/main/portfolio_util.cpp +++ b/src/main/portfolio_util.cpp @@ -34,7 +34,7 @@ vector<Options> parseThreadSpecificOptions(Options opts) * Use satRandomSeed for generating random numbers, in particular * satRandomSeed-s */ - srand((unsigned int)(-opts[options::satRandomSeed])); + srand(-opts[options::satRandomSeed]); for(unsigned i = 0; i < numThreads; ++i) { threadOptions.push_back(opts); @@ -45,7 +45,7 @@ vector<Options> parseThreadSpecificOptions(Options opts) // If the random-seed is negative, pick a random seed randomly if(opts[options::satRandomSeed] < 0) { - tOpts.set(options::satRandomSeed, (double)rand()); + tOpts.set(options::satRandomSeed, unsigned(rand())); } if(i < opts[options::threadArgv].size() && diff --git a/src/main/translator.cpp b/src/main/translator.cpp deleted file mode 100644 index 7cc90bbd1..000000000 --- a/src/main/translator.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/********************* */ -/*! \file translator.cpp - ** \verbatim - ** Original author: Morgan Deters - ** Major contributors: none - ** Minor contributors (to current version): none - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2014 New York University and The University of Iowa - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief Main driver for CVC4 translator executable - ** - ** Main driver for CVC4 translator executable. - **/ - -#include <iostream> -#include <fstream> -#include <getopt.h> -#include <cstring> -#include "util/language.h" -#include "expr/command.h" -#include "expr/expr.h" -#include "parser/parser_builder.h" -#include "parser/parser.h" - -using namespace std; -using namespace CVC4; -using namespace CVC4::language; -using namespace CVC4::parser; - -enum { - INPUT_LANG = 'L', - OUTPUT_LANG = 'O', - OUTPUT_FILE = 'o', - HELP = 'h' -};/* enum */ - -const struct option longopts[] = { - { "output-lang", required_argument, NULL, OUTPUT_LANG }, - { "output-language", required_argument, NULL, OUTPUT_LANG }, - { "lang", required_argument, NULL, INPUT_LANG }, - { "language", required_argument, NULL, INPUT_LANG }, - { "out", required_argument, NULL, OUTPUT_FILE }, - { "help", no_argument, NULL, HELP }, - { NULL, no_argument, NULL, 0 }, -};/* longopts */ - -static void showHelp() { - cerr << "cvc4-translator translation tool" << endl - << " --output-language | -O set output language (default smt2)" << endl - << " --input-language | -L set input language (default auto)" << endl - << " --out | -o set output file (- for stdout)" << endl - << " --help | -h this help" << endl - << "Options and input filenames can be intermixed, and order is important." << endl - << "For instance, \"-O smt2 x -O cvc4 y\" reads file x in smt2 format and" - << "file y in cvc4 format and writes all output to stdout." << endl - << "Some canonicalization may occur." << endl - << "Comments and formatting are not preserved." << endl; -} - -static void readFile(const char* filename, InputLanguage fromLang, OutputLanguage toLang, ostream* out) { - if(fromLang == input::LANG_AUTO) { - unsigned len = strlen(filename); - if(len >= 5 && !strcmp(".smt2", filename + len - 5)) { - fromLang = language::input::LANG_SMTLIB_V2; - } else if(len >= 4 && !strcmp(".smt", filename + len - 4)) { - fromLang = language::input::LANG_SMTLIB_V1; - } else if(len >= 5 && !strcmp(".smt1", filename + len - 5)) { - fromLang = language::input::LANG_SMTLIB_V1; - } else if((len >= 2 && !strcmp(".p", filename + len - 2)) || - (len >= 5 && !strcmp(".tptp", filename + len - 5))) { - fromLang = language::input::LANG_TPTP; - } else if(( len >= 4 && !strcmp(".cvc", filename + len - 4) ) || - ( len >= 5 && !strcmp(".cvc4", filename + len - 5) )) { - fromLang = language::input::LANG_CVC4; - } else { - throw Exception("cannot determine input language to use for `" + string(filename) + "'"); - } - } - - if(toLang == output::LANG_AUTO) { - toLang = toOutputLanguage(fromLang); - } - - *out << Expr::setlanguage(toLang); - - Options opts; - opts.set(options::inputLanguage, fromLang); - ExprManager exprMgr(opts); - ParserBuilder parserBuilder(&exprMgr, filename, opts); - if(!strcmp(filename, "-")) { - parserBuilder.withFilename("<stdin>"); - parserBuilder.withLineBufferedStreamInput(cin); - } - Parser *parser = parserBuilder.build(); - while(Command* cmd = parser->nextCommand()) { - *out << cmd << endl; - if(dynamic_cast<QuitCommand*>(cmd) != NULL) { - delete cmd; - break; - } - delete cmd; - } - *out << flush; - delete parser; -} - -/** - * Translate from an input language to an output language. - */ -int main(int argc, char* argv[]) { - ostream* out = &cout; - InputLanguage fromLang = input::LANG_AUTO; - OutputLanguage toLang = output::LANG_SMTLIB_V2; - size_t files = 0; - - try { - int c; - while((c = getopt_long(argc, argv, "-L:O:o:h", longopts, NULL)) != -1) { - switch(c) { - case 1: - ++files; - readFile(optarg, (!strcmp(optarg, "-") && fromLang == input::LANG_AUTO) ? input::LANG_CVC4 : fromLang, toLang, out); - break; - case INPUT_LANG: - if(!strcmp(optarg, "help")) { - Options::printLanguageHelp(cerr); - exit(1); - } - fromLang = toInputLanguage(optarg); - break; - case OUTPUT_LANG: - if(!strcmp(optarg, "help")) { - Options::printLanguageHelp(cerr); - exit(1); - } - toLang = toOutputLanguage(optarg); - break; - case OUTPUT_FILE: - out->flush(); - if(out != &cout) { - ((ofstream*)out)->close(); - delete out; - } - if(strcmp(optarg, "-")) { - out = new ofstream(optarg); - } else { - out = &cout; - } - break; - case 'h': - showHelp(); - exit(0); - case '?': - showHelp(); - exit(1); - default: - cerr << "internal error. translator failed (" - << char(c) << "," << c << ")." << endl; - exit(1); - } - } - - if(files == 0) { - readFile("-", fromLang == input::LANG_AUTO ? input::LANG_CVC4 : fromLang, toLang, out); - exit(0); - } - } catch(Exception& e) { - cerr << e << endl; - exit(1); - } - - return 0; -} diff --git a/src/options/Makefile.am b/src/options/Makefile.am index 8780922c9..18b2c42f4 100644 --- a/src/options/Makefile.am +++ b/src/options/Makefile.am @@ -21,8 +21,6 @@ OPTIONS_FILES_SRCS = \ ../theory/arrays/options.h \ ../theory/quantifiers/options.cpp \ ../theory/quantifiers/options.h \ - ../theory/rewriterules/options.cpp \ - ../theory/rewriterules/options.h \ ../theory/strings/options.cpp \ ../theory/strings/options.h \ ../prop/options.cpp \ @@ -84,8 +82,6 @@ nodist_liboptions_la_SOURCES = \ ../theory/arrays/options.h \ ../theory/quantifiers/options.cpp \ ../theory/quantifiers/options.h \ - ../theory/rewriterules/options.cpp \ - ../theory/rewriterules/options.h \ ../theory/strings/options.cpp \ ../theory/strings/options.h \ ../prop/options.cpp \ diff --git a/src/parser/smt2/Smt2.g b/src/parser/smt2/Smt2.g index f1acac6ba..f987de2f1 100644 --- a/src/parser/smt2/Smt2.g +++ b/src/parser/smt2/Smt2.g @@ -253,6 +253,13 @@ command returns [CVC4::Command* cmd = NULL] { cmd = new GetOptionCommand(AntlrInput::tokenText($KEYWORD).c_str() + 1); } | /* sort declaration */ DECLARE_SORT_TOK { PARSER_STATE->checkThatLogicIsSet(); } + { if(!PARSER_STATE->isTheoryEnabled(Smt2::THEORY_UF) && + !PARSER_STATE->isTheoryEnabled(Smt2::THEORY_ARRAYS) && + !PARSER_STATE->isTheoryEnabled(Smt2::THEORY_DATATYPES) && + !PARSER_STATE->isTheoryEnabled(Smt2::THEORY_SETS)) { + PARSER_STATE->parseError(std::string("Free sort symbols not allowed in ") + PARSER_STATE->getLogic().getLogicString()); + } + } symbol[name,CHECK_UNDECLARED,SYM_SORT] { PARSER_STATE->checkUserSymbol(name); } n=INTEGER_LITERAL @@ -295,6 +302,9 @@ command returns [CVC4::Command* cmd = NULL] sortSymbol[t,CHECK_DECLARED] { Debug("parser") << "declare fun: '" << name << "'" << std::endl; if( sorts.size() > 0 ) { + if(!PARSER_STATE->isTheoryEnabled(Smt2::THEORY_UF)) { + PARSER_STATE->parseError(std::string("Functions (of non-zero arity) cannot be declared in logic ") + PARSER_STATE->getLogic().getLogicString()); + } t = EXPR_MANAGER->mkFunctionType(sorts, t); } Expr func = PARSER_STATE->mkVar(name, t); @@ -488,6 +498,13 @@ extendedCommand[CVC4::Command*& cmd] $cmd = new DeclareFunctionCommand(name, c, t); } | DECLARE_SORTS_TOK { PARSER_STATE->checkThatLogicIsSet(); } + { if(!PARSER_STATE->isTheoryEnabled(Smt2::THEORY_UF) && + !PARSER_STATE->isTheoryEnabled(Smt2::THEORY_ARRAYS) && + !PARSER_STATE->isTheoryEnabled(Smt2::THEORY_DATATYPES) && + !PARSER_STATE->isTheoryEnabled(Smt2::THEORY_SETS)) { + PARSER_STATE->parseError(std::string("Free sort symbols not allowed in ") + PARSER_STATE->getLogic().getLogicString()); + } + } { $cmd = new CommandSequence(); } LPAREN_TOK ( symbol[name,CHECK_UNDECLARED,SYM_SORT] @@ -506,6 +523,9 @@ extendedCommand[CVC4::Command*& cmd] nonemptySortList[sorts] RPAREN_TOK { Type t; if(sorts.size() > 1) { + if(!PARSER_STATE->isTheoryEnabled(Smt2::THEORY_UF)) { + PARSER_STATE->parseError(std::string("Functions (of non-zero arity) cannot be declared in logic ") + PARSER_STATE->getLogic().getLogicString()); + } t = EXPR_MANAGER->mkFunctionType(sorts); } else { t = sorts[0]; @@ -524,6 +544,9 @@ extendedCommand[CVC4::Command*& cmd] sortList[sorts] RPAREN_TOK { Type t = EXPR_MANAGER->booleanType(); if(sorts.size() > 0) { + if(!PARSER_STATE->isTheoryEnabled(Smt2::THEORY_UF)) { + PARSER_STATE->parseError(std::string("Predicates (of non-zero arity) cannot be declared in logic ") + PARSER_STATE->getLogic().getLogicString()); + } t = EXPR_MANAGER->mkFunctionType(sorts, t); } Expr func = PARSER_STATE->mkVar(name, t); @@ -708,6 +731,16 @@ simpleSymbolicExprNoKeyword[CVC4::SExpr& sexpr] { sexpr = SExpr(Integer(AntlrInput::tokenText($INTEGER_LITERAL))); } | DECIMAL_LITERAL { sexpr = SExpr(AntlrInput::tokenToRational($DECIMAL_LITERAL)); } + | HEX_LITERAL + { assert( AntlrInput::tokenText($HEX_LITERAL).find("#x") == 0 ); + std::string hexString = AntlrInput::tokenTextSubstr($HEX_LITERAL, 2); + sexpr = Integer(hexString, 16); + } + | BINARY_LITERAL + { assert( AntlrInput::tokenText($BINARY_LITERAL).find("#b") == 0 ); + std::string binString = AntlrInput::tokenTextSubstr($BINARY_LITERAL, 2); + sexpr = Integer(binString, 2); + } | str[s,false] { sexpr = SExpr(s); } // | LPAREN_TOK STRCST_TOK diff --git a/src/parser/smt2/smt2.cpp b/src/parser/smt2/smt2.cpp index b59880a5e..64b321613 100644 --- a/src/parser/smt2/smt2.cpp +++ b/src/parser/smt2/smt2.cpp @@ -154,6 +154,12 @@ void Smt2::addTheory(Theory theory) { addOperator(kind::SET_SINGLETON, "setenum"); break; + case THEORY_DATATYPES: + Parser::addOperator(kind::APPLY_CONSTRUCTOR); + Parser::addOperator(kind::APPLY_TESTER); + Parser::addOperator(kind::APPLY_SELECTOR); + break; + case THEORY_STRINGS: defineType("String", getExprManager()->stringType()); addStringOperators(); @@ -194,6 +200,8 @@ bool Smt2::isTheoryEnabled(Theory theory) const { return d_logic.isTheoryEnabled(theory::THEORY_BV); case THEORY_CORE: return true; + case THEORY_DATATYPES: + return d_logic.isTheoryEnabled(theory::THEORY_DATATYPES); case THEORY_INTS: return d_logic.isTheoryEnabled(theory::THEORY_ARITH) && d_logic.areIntegersUsed() && ( !d_logic.areRealsUsed() ); @@ -253,6 +261,10 @@ void Smt2::setLogic(const std::string& name) { addTheory(THEORY_BITVECTORS); } + if(d_logic.isTheoryEnabled(theory::THEORY_DATATYPES)) { + addTheory(THEORY_DATATYPES); + } + if(d_logic.isTheoryEnabled(theory::THEORY_SETS)) { addTheory(THEORY_SETS); } diff --git a/src/parser/smt2/smt2.h b/src/parser/smt2/smt2.h index 969892c5f..55a06a8e3 100644 --- a/src/parser/smt2/smt2.h +++ b/src/parser/smt2/smt2.h @@ -40,6 +40,7 @@ public: THEORY_ARRAYS, THEORY_BITVECTORS, THEORY_CORE, + THEORY_DATATYPES, THEORY_INTS, THEORY_REALS, THEORY_REALS_INTS, @@ -83,6 +84,11 @@ public: */ void setLogic(const std::string& name); + /** + * Get the logic. + */ + const LogicInfo& getLogic() const { return d_logic; } + void setInfo(const std::string& flag, const SExpr& sexpr); void setOption(const std::string& flag, const SExpr& sexpr); diff --git a/src/printer/smt2/smt2_printer.cpp b/src/printer/smt2/smt2_printer.cpp index 849d5c0a5..6485670b5 100644 --- a/src/printer/smt2/smt2_printer.cpp +++ b/src/printer/smt2/smt2_printer.cpp @@ -697,6 +697,25 @@ static inline void toStream(std::ostream& out, const SExpr& sexpr) throw() { Printer::getPrinter(language::output::LANG_SMTLIB_V2)->toStream(out, sexpr); } +void Smt2Printer::toStream(std::ostream& out, const SExpr& sexpr) const throw() { + if(sexpr.isKeyword()) { + std::string s = sexpr.getValue(); + if(s.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~!@%^&*_-+=<>.?/") == std::string::npos) { + // simple unquoted symbol + out << s; + } else { + // must quote the symbol, but it cannot contain | or \, we turn those into _ + size_t p; + while((p = s.find_first_of("\\|")) != std::string::npos) { + s = s.replace(p, 1, "_"); + } + out << "|" << s << "|"; + } + } else { + this->Printer::toStream(out, sexpr); + } +} + template <class T> static bool tryToStream(std::ostream& out, const CommandStatus* s) throw(); diff --git a/src/printer/smt2/smt2_printer.h b/src/printer/smt2/smt2_printer.h index c70bb78c3..c3f8deb9c 100644 --- a/src/printer/smt2/smt2_printer.h +++ b/src/printer/smt2/smt2_printer.h @@ -45,6 +45,7 @@ public: void toStream(std::ostream& out, const Command* c, int toDepth, bool types, size_t dag) const throw(); void toStream(std::ostream& out, const CommandStatus* s) const throw(); void toStream(std::ostream& out, const Result& r) const throw(); + void toStream(std::ostream& out, const SExpr& sexpr) const throw(); };/* class Smt2Printer */ }/* CVC4::printer::smt2 namespace */ diff --git a/src/prop/bvminisat/core/SolverTypes.h b/src/prop/bvminisat/core/SolverTypes.h index 89ffc8a00..0400f956d 100644 --- a/src/prop/bvminisat/core/SolverTypes.h +++ b/src/prop/bvminisat/core/SolverTypes.h @@ -46,14 +46,14 @@ struct Lit { int x; // Use this as a constructor: - friend Lit mkLit(Var var, bool sign = false); + friend Lit mkLit(Var var, bool sign); bool operator == (Lit p) const { return x == p.x; } bool operator != (Lit p) const { return x != p.x; } bool operator < (Lit p) const { return x < p.x; } // '<' makes p, ~p adjacent in the ordering. }; -inline Lit mkLit (Var var, bool sign) { Lit p; p.x = var + var + (int)sign; return p; } +inline Lit mkLit (Var var, bool sign = false) { Lit p; p.x = var + var + (int)sign; return p; } inline Lit operator ~(Lit p) { Lit q; q.x = p.x ^ 1; return q; } inline Lit operator ^(Lit p, bool b) { Lit q; q.x = p.x ^ (unsigned int)b; return q; } inline bool sign (Lit p) { return p.x & 1; } diff --git a/src/prop/minisat/core/SolverTypes.h b/src/prop/minisat/core/SolverTypes.h index fac4c92c1..a6413e674 100644 --- a/src/prop/minisat/core/SolverTypes.h +++ b/src/prop/minisat/core/SolverTypes.h @@ -49,7 +49,7 @@ struct Lit { int x; // Use this as a constructor: - friend Lit mkLit(Var var, bool sign = false); + friend Lit mkLit(Var var, bool sign); bool operator == (Lit p) const { return x == p.x; } bool operator != (Lit p) const { return x != p.x; } @@ -57,7 +57,7 @@ struct Lit { }; -inline Lit mkLit (Var var, bool sign) { Lit p; p.x = var + var + (int)sign; return p; } +inline Lit mkLit (Var var, bool sign = false) { Lit p; p.x = var + var + (int)sign; return p; } inline Lit operator ~(Lit p) { Lit q; q.x = p.x ^ 1; return q; } inline Lit operator ^(Lit p, bool b) { Lit q; q.x = p.x ^ (unsigned int)b; return q; } inline bool sign (Lit p) { return p.x & 1; } diff --git a/src/prop/minisat/minisat.cpp b/src/prop/minisat/minisat.cpp index 98e43aaf0..c4fe58fd7 100644 --- a/src/prop/minisat/minisat.cpp +++ b/src/prop/minisat/minisat.cpp @@ -5,7 +5,7 @@ ** 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) + ** Copyright (c) 2009-2014 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 @@ -13,7 +13,7 @@ ** ** \brief SAT Solver. ** - ** Implementation of the minisat for cvc4. + ** Implementation of the minisat interface for cvc4. **/ #include "prop/minisat/minisat.h" @@ -111,8 +111,7 @@ void MinisatSatSolver::toSatClause(const Minisat::Clause& clause, Assert((unsigned)clause.size() == sat_clause.size()); } -void MinisatSatSolver::initialize(context::Context* context, TheoryProxy* theoryProxy) -{ +void MinisatSatSolver::initialize(context::Context* context, TheoryProxy* theoryProxy) { d_context = context; @@ -125,20 +124,30 @@ void MinisatSatSolver::initialize(context::Context* context, TheoryProxy* theory d_minisat = new Minisat::SimpSolver(theoryProxy, d_context, options::incrementalSolving() || options::decisionMode() != decision::DECISION_STRATEGY_INTERNAL ); + + d_statistics.init(d_minisat); +} + +// Like initialize() above, but called just before each search when in +// incremental mode +void MinisatSatSolver::setupOptions() { + // Copy options from CVC4 options structure into minisat, as appropriate + // Set up the verbosity d_minisat->verbosity = (options::verbosity() > 0) ? 1 : -1; // Set up the random decision parameters d_minisat->random_var_freq = options::satRandomFreq(); - d_minisat->random_seed = options::satRandomSeed(); + // If 0, we use whatever we like (here, the Minisat default seed) + if(options::satRandomSeed() != 0) { + d_minisat->random_seed = double(options::satRandomSeed()); + } // Give access to all possible options in the sat solver d_minisat->var_decay = options::satVarDecay(); d_minisat->clause_decay = options::satClauseDecay(); d_minisat->restart_first = options::satRestartFirst(); d_minisat->restart_inc = options::satRestartInc(); - - d_statistics.init(d_minisat); } void MinisatSatSolver::addClause(SatClause& clause, bool removable) { @@ -153,6 +162,7 @@ SatVariable MinisatSatSolver::newVar(bool isTheoryAtom, bool preRegister, bool c SatValue MinisatSatSolver::solve(unsigned long& resource) { Trace("limit") << "SatSolver::solve(): have limit of " << resource << " conflicts" << std::endl; + setupOptions(); if(resource == 0) { d_minisat->budgetOff(); } else { @@ -168,6 +178,7 @@ SatValue MinisatSatSolver::solve(unsigned long& resource) { } SatValue MinisatSatSolver::solve() { + setupOptions(); d_minisat->budgetOff(); return toSatLiteralValue(d_minisat->solve()); } diff --git a/src/prop/minisat/minisat.h b/src/prop/minisat/minisat.h index 27258b3c2..201879eb0 100644 --- a/src/prop/minisat/minisat.h +++ b/src/prop/minisat/minisat.h @@ -5,7 +5,7 @@ ** 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) + ** Copyright (c) 2009-2014 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 @@ -13,7 +13,7 @@ ** ** \brief SAT Solver. ** - ** Implementation of the minisat for cvc4. + ** Implementation of the minisat interface for cvc4. **/ #pragma once @@ -36,6 +36,8 @@ class MinisatSatSolver : public DPLLSatSolverInterface { /** Context we will be using to synchronize the sat solver */ context::Context* d_context; + void setupOptions(); + public: MinisatSatSolver(); @@ -96,11 +98,10 @@ public: Statistics(); ~Statistics(); void init(Minisat::SimpSolver* d_minisat); - }; + };/* class MinisatSatSolver::Statistics */ Statistics d_statistics; -}; - -} // prop namespace -} // cvc4 namespace +};/* class MinisatSatSolver */ +}/* CVC4::prop namespace */ +}/* CVC4 namespace */ diff --git a/src/prop/options b/src/prop/options index b300c3fb6..71091d2b5 100644 --- a/src/prop/options +++ b/src/prop/options @@ -10,7 +10,7 @@ option - --show-sat-solvers void :handler CVC4::prop::showSatSolvers :handler-in option satRandomFreq random-frequency --random-freq=P double :default 0.0 :predicate greater_equal(0.0) less_equal(1.0) sets the frequency of random decisions in the sat solver (P=0.0 by default) -option satRandomSeed random-seed --random-seed=S double :default 91648253 :read-write +option satRandomSeed random-seed --random-seed=S uint32_t :default 0 :read-write sets the random seed for the sat solver option satVarDecay double :default 0.95 :predicate less_equal(1.0) greater_equal(0.0) diff --git a/src/smt/logic_request.h b/src/smt/logic_request.h new file mode 100644 index 000000000..4985b0e65 --- /dev/null +++ b/src/smt/logic_request.h @@ -0,0 +1,53 @@ +/********************* */ +/*! \file logic_request.h + ** \verbatim + ** Original author: Morgan Deters + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2014 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief An object to request logic widening in the running SmtEngine + ** + ** An object to request logic widening in the running SmtEngine. This + ** class exists as a proxy between theory code and the SmtEngine, allowing + ** a theory to enable another theory in the SmtEngine after initialization + ** (thus the usual, public setLogic() cannot be used). This is mainly to + ** support features like uninterpreted divide-by-zero (to support the + ** partial function DIVISION), where during theory expansion, the theory + ** of uninterpreted functions needs to be added to the logic to support + ** partial functions. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__LOGIC_REQUEST_H +#define __CVC4__LOGIC_REQUEST_H + +#include "expr/kind.h" +#include "smt/smt_engine.h" + +namespace CVC4 { + +class LogicRequest { + /** The SmtEngine at play. */ + SmtEngine& d_smt; + +public: + LogicRequest(SmtEngine& smt) : d_smt(smt) { } + + /** Widen the logic to include the given theory. */ + void widenLogic(theory::TheoryId id) { + d_smt.d_logic.getUnlockedCopy(); + d_smt.d_logic = d_smt.d_logic.getUnlockedCopy(); + d_smt.d_logic.enableTheory(id); + d_smt.d_logic.lock(); + } + +};/* class LogicRequest */ + +}/* CVC4 namespace */ + +#endif /* __CVC4__LOGIC_REQUEST_H */ diff --git a/src/smt/options b/src/smt/options index b76822caf..f3429287f 100644 --- a/src/smt/options +++ b/src/smt/options @@ -5,11 +5,14 @@ module SMT "smt/options.h" SMT layer -common-option - --dump=MODE argument :handler CVC4::smt::dumpMode :handler-include "smt/options_handlers.h" +common-option - dump --dump=MODE argument :handler CVC4::smt::dumpMode :handler-include "smt/options_handlers.h" dump preprocessed assertions, etc., see --dump=help -common-option - --dump-to=FILE argument :handler CVC4::smt::dumpToFile :handler-include "smt/options_handlers.h" +common-option - dump-to --dump-to=FILE argument :handler CVC4::smt::dumpToFile :handler-include "smt/options_handlers.h" all dumping goes to FILE (instead of stdout) +expert-option forceLogic force-logic --force-logic=LOGIC LogicInfo :include "theory/logic_info.h" :handler CVC4::smt::stringToLogicInfo :handler-include "smt/options_handlers.h" :default '""' + set the logic, and override all further user attempts to change it + option simplificationMode simplification-mode --simplification=MODE SimplificationMode :handler CVC4::smt::stringToSimplificationMode :default SIMPLIFICATION_MODE_BATCH :read-write :include "smt/simplification_mode.h" :handler-include "smt/options_handlers.h" choose simplification mode, see --simplification=help alias --no-simplification = --simplification=none @@ -40,7 +43,7 @@ option produceAssignments produce-assignments --produce-assignments bool :defaul # This could go in src/main/options, but by SMT-LIBv2 spec, "interactive" # is a mode in which the assertion list must be kept. So it belongs here. -common-option interactive interactive-mode --interactive bool :read-write +common-option interactive interactive-mode --interactive bool :predicate CVC4::smt::beforeSearch :predicate-include "smt/options_handlers.h" :read-write force interactive mode option doITESimp --ite-simp bool :read-write diff --git a/src/smt/options_handlers.h b/src/smt/options_handlers.h index c9c3d6345..49f2c7943 100644 --- a/src/smt/options_handlers.h +++ b/src/smt/options_handlers.h @@ -257,6 +257,19 @@ inline void dumpMode(std::string option, std::string optarg, SmtEngine* smt) { #endif /* CVC4_DUMPING */ } +inline LogicInfo stringToLogicInfo(std::string option, std::string optarg, SmtEngine* smt) throw(OptionException) { + try { + LogicInfo logic(optarg); + if(smt != NULL) { + smt->setLogic(logic); + } + return logic; + } catch(IllegalArgumentException& e) { + throw OptionException(std::string("invalid logic specification for --force-logic: `") + + optarg + "':\n" + e.what()); + } +} + inline SimplificationMode stringToSimplificationMode(std::string option, std::string optarg, SmtEngine* smt) throw(OptionException) { if(optarg == "batch") { return SIMPLIFICATION_MODE_BATCH; diff --git a/src/smt/smt_engine.cpp b/src/smt/smt_engine.cpp index 02542b640..328a20c28 100644 --- a/src/smt/smt_engine.cpp +++ b/src/smt/smt_engine.cpp @@ -43,6 +43,7 @@ #include "smt/smt_engine.h" #include "smt/smt_engine_scope.h" #include "smt/model_postprocessor.h" +#include "smt/logic_request.h" #include "theory/theory_engine.h" #include "theory/bv/theory_bv_rewriter.h" #include "proof/proof_manager.h" @@ -80,8 +81,10 @@ #include "util/sort_inference.h" #include "theory/quantifiers/quant_conflict_find.h" #include "theory/quantifiers/macros.h" -#include "theory/datatypes/options.h" #include "theory/quantifiers/first_order_reasoning.h" +#include "theory/quantifiers/quantifiers_rewriter.h" +#include "theory/quantifiers/options.h" +#include "theory/datatypes/options.h" #include "theory/strings/theory_strings_preprocess.h" using namespace std; @@ -306,44 +309,6 @@ class SmtEnginePrivate : public NodeManagerListener { */ hash_map<Node, Node, NodeHashFunction> d_abstractValues; - /** - * Function symbol used to implement uninterpreted undefined string - * semantics. Needed to deal with partial charat/substr function. - */ - Node d_ufSubstr; - - /** - * Function symbol used to implement uninterpreted undefined string - * semantics. Needed to deal with partial str2int function. - */ - Node d_ufS2I; - - /** - * Function symbol used to implement uninterpreted division-by-zero - * semantics. Needed to deal with partial division function ("/"). - */ - Node d_divByZero; - - /** - * Maps from bit-vector width to divison-by-zero uninterpreted - * function symbols. - */ - hash_map<unsigned, Node> d_BVDivByZero; - hash_map<unsigned, Node> d_BVRemByZero; - - /** - * Function symbol used to implement uninterpreted - * int-division-by-zero semantics. Needed to deal with partial - * function "div". - */ - Node d_intDivByZero; - - /** - * Function symbol used to implement uninterpreted mod-zero - * semantics. Needed to deal with partial function "mod". - */ - Node d_modZero; - /** Number of calls of simplify assertions active. */ unsigned d_simplifyAssertionsDepth; @@ -446,9 +411,6 @@ public: d_fakeContext(), d_abstractValueMap(&d_fakeContext), d_abstractValues(), - d_divByZero(), - d_intDivByZero(), - d_modZero(), d_simplifyAssertionsDepth(0), d_iteSkolemMap(), d_iteRemover(smt.d_userContext), @@ -552,25 +514,6 @@ public: throw(TypeCheckingException, LogicException); /** - * Return the uinterpreted function symbol corresponding to division-by-zero - * for this particular bit-width - * @param k should be UREM or UDIV - * @param width - * - * @return - */ - Node getBVDivByZero(Kind k, unsigned width); - - /** - * Returns the node modeling the division-by-zero semantics of node n. - * - * @param n - * - * @return - */ - Node expandBVDivByZero(TNode n); - - /** * Expand definitions in n. */ Node expandDefinitions(TNode n, hash_map<Node, Node, NodeHashFunction>& cache) @@ -596,11 +539,6 @@ public: } /** - * Pre-skolemize quantifiers. - */ - Node preSkolemizeQuantifiers(Node n, bool polarity, std::vector<Node>& fvs); - - /** * Substitute away all AbstractValues in a node. */ Node substituteAbstractValues(TNode n) { @@ -736,6 +674,9 @@ SmtEngine::SmtEngine(ExprManager* em) throw() : } void SmtEngine::finishInit() { + // ensure that our heuristics are properly set up + setDefaults(); + d_decisionEngine = new DecisionEngine(d_context, d_userContext); d_decisionEngine->init(); // enable appropriate strategies @@ -793,24 +734,7 @@ void SmtEngine::finalOptionsAreSet() { return; } - if(options::bitvectorEagerBitblast()) { - // Eager solver should use the internal decision strategy - options::decisionMode.set(DECISION_STRATEGY_INTERNAL); - } - - if(options::checkModels()) { - if(! options::interactive()) { - Notice() << "SmtEngine: turning on interactive-mode to support check-models" << endl; - setOption("interactive-mode", SExpr("true")); - } - } - if(options::produceAssignments() && !options::produceModels()) { - Notice() << "SmtEngine: turning on produce-models to support produce-assignments" << endl; - setOption("produce-models", SExpr("true")); - } - if(! d_logic.isLocked()) { - // ensure that our heuristics are properly set up setLogicInternal(); } @@ -925,15 +849,68 @@ LogicInfo SmtEngine::getLogicInfo() const { return d_logic; } -// This function is called when d_logic has just been changed. -// The LogicInfo isn't passed in explicitly, because that might -// tempt people in the code to use the (potentially unlocked) -// version that's passed in, leading to assert-fails in certain -// uses of the CVC4 library. void SmtEngine::setLogicInternal() throw() { Assert(!d_fullyInited, "setting logic in SmtEngine but the engine has already finished initializing for this run"); - d_logic.lock(); +} + +void SmtEngine::setDefaults() { + if(options::forceLogic.wasSetByUser()) { + d_logic = options::forceLogic(); + } + + // set strings-exp + if(!d_logic.hasEverything() && d_logic.isTheoryEnabled(THEORY_STRINGS) ) { + if(! options::stringExp.wasSetByUser()) { + options::stringExp.set( true ); + Trace("smt") << "turning on strings-exp, for the theory of strings" << std::endl; + } + } + // for strings + if(options::stringExp()) { + if( !d_logic.isQuantified() ) { + d_logic = d_logic.getUnlockedCopy(); + d_logic.enableQuantifiers(); + d_logic.lock(); + Trace("smt") << "turning on quantifier logic, for strings-exp" << std::endl; + } + if(! options::finiteModelFind.wasSetByUser()) { + options::finiteModelFind.set( true ); + Trace("smt") << "turning on finite-model-find, for strings-exp" << std::endl; + } + if(! options::fmfBoundInt.wasSetByUser()) { + options::fmfBoundInt.set( true ); + Trace("smt") << "turning on fmf-bound-int, for strings-exp" << std::endl; + } + /* + if(! options::rewriteDivk.wasSetByUser()) { + options::rewriteDivk.set( true ); + Trace("smt") << "turning on rewrite-divk, for strings-exp" << std::endl; + }*/ + /* + if(! options::stringFMF.wasSetByUser()) { + options::stringFMF.set( true ); + Trace("smt") << "turning on strings-fmf, for strings-exp" << std::endl; + } + */ + } + + if(options::bitvectorEagerBitblast()) { + // Eager solver should use the internal decision strategy + options::decisionMode.set(DECISION_STRATEGY_INTERNAL); + } + + if(options::checkModels()) { + if(! options::interactive()) { + Notice() << "SmtEngine: turning on interactive-mode to support check-models" << endl; + setOption("interactive-mode", SExpr("true")); + } + } + + if(options::produceAssignments() && !options::produceModels()) { + Notice() << "SmtEngine: turning on produce-models to support produce-assignments" << endl; + setOption("produce-models", SExpr("true")); + } // Set the options for the theoryOf if(!options::theoryOfMode.wasSetByUser()) { @@ -966,7 +943,7 @@ void SmtEngine::setLogicInternal() throw() { options::fmfBoundInt.set( true ); Trace("smt") << "turning on fmf-bound-int, for strings-exp" << std::endl; } - /* + /* if(! options::rewriteDivk.wasSetByUser()) { options::rewriteDivk.set( true ); Trace("smt") << "turning on rewrite-divk, for strings-exp" << std::endl; @@ -1068,7 +1045,7 @@ void SmtEngine::setLogicInternal() throw() { if(! options::unconstrainedSimp.wasSetByUser() || options::incrementalSolving()) { // bool qf_sat = d_logic.isPure(THEORY_BOOL) && !d_logic.isQuantified(); // bool uncSimp = false && !qf_sat && !options::incrementalSolving(); - bool uncSimp = !options::incrementalSolving() && !d_logic.isQuantified() && !options::produceModels() && !options::checkModels() && + bool uncSimp = !options::incrementalSolving() && !d_logic.isQuantified() && !options::produceModels() && !options::produceAssignments() && !options::checkModels() && (d_logic.isTheoryEnabled(THEORY_ARRAY) && d_logic.isTheoryEnabled(THEORY_BV)); Trace("smt") << "setting unconstrained simplification to " << uncSimp << endl; options::unconstrainedSimp.set(uncSimp); @@ -1079,6 +1056,10 @@ void SmtEngine::setLogicInternal() throw() { Notice() << "SmtEngine: turning off produce-models to support unconstrainedSimp" << endl; setOption("produce-models", SExpr("false")); } + if (options::produceAssignments()) { + Notice() << "SmtEngine: turning off produce-assignments to support unconstrainedSimp" << endl; + setOption("produce-assignments", SExpr("false")); + } if (options::checkModels()) { Notice() << "SmtEngine: turning off check-models to support unconstrainedSimp" << endl; setOption("check-models", SExpr("false")); @@ -1188,7 +1169,7 @@ void SmtEngine::setLogicInternal() throw() { //instantiate only on last call if( options::fmfInstEngine() ){ Trace("smt") << "setting inst when mode to LAST_CALL" << endl; - options::instWhenMode.set( INST_WHEN_LAST_CALL ); + options::instWhenMode.set( quantifiers::INST_WHEN_LAST_CALL ); } } if ( options::fmfBoundInt() ){ @@ -1214,7 +1195,7 @@ void SmtEngine::setLogicInternal() throw() { //until bugs 371,431 are fixed if( ! options::minisatUseElim.wasSetByUser()){ - if( d_logic.isQuantified() || options::produceModels() || options::checkModels() ){ + if( d_logic.isQuantified() || options::produceModels() || options::produceAssignments() || options::checkModels() ){ options::minisatUseElim.set( false ); } } @@ -1223,6 +1204,10 @@ void SmtEngine::setLogicInternal() throw() { Notice() << "SmtEngine: turning off produce-models to support minisatUseElim" << endl; setOption("produce-models", SExpr("false")); } + if (options::produceAssignments()) { + Notice() << "SmtEngine: turning off produce-assignments to support minisatUseElim" << endl; + setOption("produce-assignments", SExpr("false")); + } if (options::checkModels()) { Notice() << "SmtEngine: turning off check-models to support minisatUseElim" << endl; setOption("check-models", SExpr("false")); @@ -1230,7 +1215,7 @@ void SmtEngine::setLogicInternal() throw() { } // For now, these array theory optimizations do not support model-building - if (options::produceModels() || options::checkModels()) { + if (options::produceModels() || options::produceAssignments() || options::checkModels()) { options::arraysOptimizeLinear.set(false); options::arraysLazyRIntro1.set(false); } @@ -1242,6 +1227,10 @@ void SmtEngine::setLogicInternal() throw() { Warning() << "SmtEngine: turning off produce-models because unsupported for nonlinear arith" << endl; setOption("produce-models", SExpr("false")); } + if (options::produceAssignments()) { + Warning() << "SmtEngine: turning off produce-assignments because unsupported for nonlinear arith" << endl; + setOption("produce-assignments", SExpr("false")); + } if (options::checkModels()) { Warning() << "SmtEngine: turning off check-models because unsupported for nonlinear arith" << endl; setOption("check-models", SExpr("false")); @@ -1470,55 +1459,6 @@ void SmtEngine::defineFunction(Expr func, } -Node SmtEnginePrivate::getBVDivByZero(Kind k, unsigned width) { - NodeManager* nm = d_smt.d_nodeManager; - if (k == kind::BITVECTOR_UDIV) { - if (d_BVDivByZero.find(width) == d_BVDivByZero.end()) { - // lazily create the function symbols - ostringstream os; - os << "BVUDivByZero_" << width; - Node divByZero = nm->mkSkolem(os.str(), - nm->mkFunctionType(nm->mkBitVectorType(width), nm->mkBitVectorType(width)), - "partial bvudiv", NodeManager::SKOLEM_EXACT_NAME); - d_BVDivByZero[width] = divByZero; - } - return d_BVDivByZero[width]; - } - else if (k == kind::BITVECTOR_UREM) { - if (d_BVRemByZero.find(width) == d_BVRemByZero.end()) { - ostringstream os; - os << "BVURemByZero_" << width; - Node divByZero = nm->mkSkolem(os.str(), - nm->mkFunctionType(nm->mkBitVectorType(width), nm->mkBitVectorType(width)), - "partial bvurem", NodeManager::SKOLEM_EXACT_NAME); - d_BVRemByZero[width] = divByZero; - } - return d_BVRemByZero[width]; - } - - Unreachable(); -} - - -Node SmtEnginePrivate::expandBVDivByZero(TNode n) { - // we only deal wioth the unsigned division operators as the signed ones should have been - // expanded in terms of the unsigned operators - NodeManager* nm = d_smt.d_nodeManager; - unsigned width = n.getType().getBitVectorSize(); - Node divByZero = getBVDivByZero(n.getKind(), width); - TNode num = n[0], den = n[1]; - Node den_eq_0 = nm->mkNode(kind::EQUAL, den, nm->mkConst(BitVector(width, Integer(0)))); - Node divByZeroNum = nm->mkNode(kind::APPLY_UF, divByZero, num); - Node divTotalNumDen = nm->mkNode(n.getKind() == kind::BITVECTOR_UDIV ? kind::BITVECTOR_UDIV_TOTAL : - kind::BITVECTOR_UREM_TOTAL, num, den); - Node node = nm->mkNode(kind::ITE, den_eq_0, divByZeroNum, divTotalNumDen); - if(!d_smt.d_logic.isTheoryEnabled(THEORY_UF)) { - d_smt.d_logic = d_smt.d_logic.getUnlockedCopy(); - d_smt.d_logic.enableTheory(THEORY_UF); - d_smt.d_logic.lock(); - } - return node; -} Node SmtEnginePrivate::expandDefinitions(TNode n, hash_map<Node, Node, NodeHashFunction>& cache) @@ -1527,31 +1467,36 @@ Node SmtEnginePrivate::expandDefinitions(TNode n, hash_map<Node, Node, NodeHashF stack< triple<Node, Node, bool> > worklist; stack<Node> result; worklist.push(make_triple(Node(n), Node(n), false)); + // The worklist is made of triples, each is input / original node then the output / rewritten node + // and finally a flag tracking whether the children have been explored (i.e. if this is a downward + // or upward pass). do { - n = worklist.top().first; - Node node = worklist.top().second; + n = worklist.top().first; // n is the input / original + Node node = worklist.top().second; // node is the output / result bool childrenPushed = worklist.top().third; worklist.pop(); + // Working downwards if(!childrenPushed) { Kind k = n.getKind(); + // Apart from apply, we can short circuit leaves if(k != kind::APPLY && n.getNumChildren() == 0) { - SmtEngine::DefinedFunctionMap::const_iterator i = d_smt.d_definedFunctions->find(n); - if(i != d_smt.d_definedFunctions->end()) { - // replacement must be closed - if((*i).second.getFormals().size() > 0) { - result.push(d_smt.d_nodeManager->mkNode(kind::LAMBDA, d_smt.d_nodeManager->mkNode(kind::BOUND_VAR_LIST, (*i).second.getFormals()), (*i).second.getFormula())); - continue; - } - // don't bother putting in the cache - result.push((*i).second.getFormula()); - continue; - } - // don't bother putting in the cache - result.push(n); - continue; + SmtEngine::DefinedFunctionMap::const_iterator i = d_smt.d_definedFunctions->find(n); + if(i != d_smt.d_definedFunctions->end()) { + // replacement must be closed + if((*i).second.getFormals().size() > 0) { + result.push(d_smt.d_nodeManager->mkNode(kind::LAMBDA, d_smt.d_nodeManager->mkNode(kind::BOUND_VAR_LIST, (*i).second.getFormals()), (*i).second.getFormula())); + continue; + } + // don't bother putting in the cache + result.push((*i).second.getFormula()); + continue; + } + // don't bother putting in the cache + result.push(n); + continue; } // maybe it's in the cache @@ -1563,134 +1508,7 @@ Node SmtEnginePrivate::expandDefinitions(TNode n, hash_map<Node, Node, NodeHashF } // otherwise expand it - - NodeManager* nm = d_smt.d_nodeManager; - // FIXME: this theory-specific code should be factored out of the - // SmtEngine, somehow - switch(k) { - case kind::BITVECTOR_SDIV: - case kind::BITVECTOR_SREM: - case kind::BITVECTOR_SMOD: - node = bv::TheoryBVRewriter::eliminateBVSDiv(node); - break; - - case kind::BITVECTOR_UDIV: - case kind::BITVECTOR_UREM: - node = expandBVDivByZero(node); - break; - - case kind::STRING_CHARAT: { - if(d_ufSubstr.isNull()) { - std::vector< TypeNode > argTypes; - argTypes.push_back(NodeManager::currentNM()->stringType()); - argTypes.push_back(NodeManager::currentNM()->integerType()); - argTypes.push_back(NodeManager::currentNM()->integerType()); - d_ufSubstr = NodeManager::currentNM()->mkSkolem("__ufSS", - NodeManager::currentNM()->mkFunctionType( - argTypes, NodeManager::currentNM()->stringType()), - "uf substr", - NodeManager::SKOLEM_EXACT_NAME); - } - Node lenxgti = NodeManager::currentNM()->mkNode( kind::GT, - NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, n[0] ), n[1] ); - Node zero = NodeManager::currentNM()->mkConst( ::CVC4::Rational(0) ); - Node t1greq0 = NodeManager::currentNM()->mkNode( kind::GEQ, n[1], zero); - Node cond = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::AND, lenxgti, t1greq0 )); - Node one = NodeManager::currentNM()->mkConst( ::CVC4::Rational(1) ); - Node totalf = NodeManager::currentNM()->mkNode(kind::STRING_SUBSTR_TOTAL, n[0], n[1], one); - Node uf = NodeManager::currentNM()->mkNode(kind::APPLY_UF, d_ufSubstr, n[0], n[1], one); - node = NodeManager::currentNM()->mkNode( kind::ITE, cond, totalf, uf ); - break; - } - case kind::STRING_SUBSTR: { - if(d_ufSubstr.isNull()) { - std::vector< TypeNode > argTypes; - argTypes.push_back(NodeManager::currentNM()->stringType()); - argTypes.push_back(NodeManager::currentNM()->integerType()); - argTypes.push_back(NodeManager::currentNM()->integerType()); - d_ufSubstr = NodeManager::currentNM()->mkSkolem("__ufSS", - NodeManager::currentNM()->mkFunctionType( - argTypes, NodeManager::currentNM()->stringType()), - "uf substr", - NodeManager::SKOLEM_EXACT_NAME); - } - Node lenxgti = NodeManager::currentNM()->mkNode( kind::GEQ, - NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, n[0] ), - NodeManager::currentNM()->mkNode( kind::PLUS, n[1], n[2] ) ); - Node zero = NodeManager::currentNM()->mkConst( ::CVC4::Rational(0) ); - Node t1geq0 = NodeManager::currentNM()->mkNode(kind::GEQ, n[1], zero); - Node t2geq0 = NodeManager::currentNM()->mkNode(kind::GEQ, n[2], zero); - Node cond = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::AND, lenxgti, t1geq0, t2geq0 )); - Node totalf = NodeManager::currentNM()->mkNode(kind::STRING_SUBSTR_TOTAL, n[0], n[1], n[2]); - Node uf = NodeManager::currentNM()->mkNode(kind::APPLY_UF, d_ufSubstr, n[0], n[1], n[2]); - node = NodeManager::currentNM()->mkNode( kind::ITE, cond, totalf, uf ); - break; - } - case kind::DIVISION: { - // partial function: division - if(d_divByZero.isNull()) { - d_divByZero = nm->mkSkolem("divByZero", nm->mkFunctionType(nm->realType(), nm->realType()), - "partial real division", NodeManager::SKOLEM_EXACT_NAME); - if(!d_smt.d_logic.isTheoryEnabled(THEORY_UF)) { - d_smt.d_logic = d_smt.d_logic.getUnlockedCopy(); - d_smt.d_logic.enableTheory(THEORY_UF); - d_smt.d_logic.lock(); - } - } - TNode num = n[0], den = n[1]; - Node den_eq_0 = nm->mkNode(kind::EQUAL, den, nm->mkConst(Rational(0))); - Node divByZeroNum = nm->mkNode(kind::APPLY_UF, d_divByZero, num); - Node divTotalNumDen = nm->mkNode(kind::DIVISION_TOTAL, num, den); - node = nm->mkNode(kind::ITE, den_eq_0, divByZeroNum, divTotalNumDen); - break; - } - - case kind::INTS_DIVISION: { - // partial function: integer div - if(d_intDivByZero.isNull()) { - d_intDivByZero = nm->mkSkolem("intDivByZero", nm->mkFunctionType(nm->integerType(), nm->integerType()), - "partial integer division", NodeManager::SKOLEM_EXACT_NAME); - if(!d_smt.d_logic.isTheoryEnabled(THEORY_UF)) { - d_smt.d_logic = d_smt.d_logic.getUnlockedCopy(); - d_smt.d_logic.enableTheory(THEORY_UF); - d_smt.d_logic.lock(); - } - } - TNode num = n[0], den = n[1]; - Node den_eq_0 = nm->mkNode(kind::EQUAL, den, nm->mkConst(Rational(0))); - Node intDivByZeroNum = nm->mkNode(kind::APPLY_UF, d_intDivByZero, num); - Node intDivTotalNumDen = nm->mkNode(kind::INTS_DIVISION_TOTAL, num, den); - node = nm->mkNode(kind::ITE, den_eq_0, intDivByZeroNum, intDivTotalNumDen); - break; - } - - case kind::INTS_MODULUS: { - // partial function: mod - if(d_modZero.isNull()) { - d_modZero = nm->mkSkolem("modZero", nm->mkFunctionType(nm->integerType(), nm->integerType()), - "partial modulus", NodeManager::SKOLEM_EXACT_NAME); - if(!d_smt.d_logic.isTheoryEnabled(THEORY_UF)) { - d_smt.d_logic = d_smt.d_logic.getUnlockedCopy(); - d_smt.d_logic.enableTheory(THEORY_UF); - d_smt.d_logic.lock(); - } - } - TNode num = n[0], den = n[1]; - Node den_eq_0 = nm->mkNode(kind::EQUAL, den, nm->mkConst(Rational(0))); - Node modZeroNum = nm->mkNode(kind::APPLY_UF, d_modZero, num); - Node modTotalNumDen = nm->mkNode(kind::INTS_MODULUS_TOTAL, num, den); - node = nm->mkNode(kind::ITE, den_eq_0, modZeroNum, modTotalNumDen); - break; - } - - case kind::ABS: { - Node out = nm->mkNode(kind::ITE, nm->mkNode(kind::LT, node[0], nm->mkConst(Rational(0))), nm->mkNode(kind::UMINUS, node[0]), node[0]); - cache[n] = out; - result.push(out); - continue; - } - - case kind::APPLY: { + if (k == kind::APPLY) { // application of a user-defined symbol TNode func = n.getOperator(); SmtEngine::DefinedFunctionMap::const_iterator i = @@ -1729,25 +1547,35 @@ Node SmtEnginePrivate::expandDefinitions(TNode n, hash_map<Node, Node, NodeHashF cache[n] = (n == expanded ? Node::null() : expanded); result.push(expanded); continue; - } - default: - // unknown kind for expansion, just iterate over the children - node = n; + } else { + theory::Theory* t = d_smt.d_theoryEngine->theoryOf(node); + + Assert(t != NULL); + LogicRequest req(d_smt); + node = t->expandDefinition(req, n); } // there should be children here, otherwise we short-circuited a result-push/continue, above + if (node.getNumChildren() == 0) { + Debug("expand") << "Unexpectedly no children..." << node << endl; + } + // This invariant holds at the moment but it is concievable that a new theory + // might introduce a kind which can have children before definition expansion but doesn't + // afterwards. If this happens, remove this assertion. Assert(node.getNumChildren() > 0); // the partial functions can fall through, in which case we still // consider their children - worklist.push(make_triple(Node(n), node, true)); + worklist.push(make_triple(Node(n), node, true)); // Original and rewritten result for(size_t i = 0; i < node.getNumChildren(); ++i) { - worklist.push(make_triple(node[i], node[i], false)); + worklist.push(make_triple(node[i], node[i], false)); // Rewrite the children of the result only } } else { + // Working upwards + // Reconstruct the node from it's (now rewritten) children on the stack Debug("expand") << "cons : " << node << endl; //cout << "cons : " << node << endl; @@ -1766,7 +1594,7 @@ Node SmtEnginePrivate::expandDefinitions(TNode n, hash_map<Node, Node, NodeHashF nb << expanded; } node = nb; - cache[n] = n == node ? Node::null() : node; + cache[n] = n == node ? Node::null() : node; // Only cache once all subterms are expanded result.push(node); } } while(!worklist.empty()); @@ -1776,120 +1604,6 @@ Node SmtEnginePrivate::expandDefinitions(TNode n, hash_map<Node, Node, NodeHashF return result.top(); } - -struct ContainsQuantAttributeId {}; -typedef expr::Attribute<ContainsQuantAttributeId, uint64_t> ContainsQuantAttribute; - -// check if the given node contains a universal quantifier -static bool containsQuantifiers(Node n) { - if( n.hasAttribute(ContainsQuantAttribute()) ){ - return n.getAttribute(ContainsQuantAttribute())==1; - } else if(n.getKind() == kind::FORALL) { - return true; - } else { - bool cq = false; - for( unsigned i = 0; i < n.getNumChildren(); ++i ){ - if( containsQuantifiers(n[i]) ){ - cq = true; - break; - } - } - ContainsQuantAttribute cqa; - n.setAttribute(cqa, cq ? 1 : 0); - return cq; - } -} - -Node SmtEnginePrivate::preSkolemizeQuantifiers( Node n, bool polarity, std::vector< Node >& fvs ){ - Trace("pre-sk") << "Pre-skolem " << n << " " << polarity << " " << fvs.size() << endl; - if( n.getKind()==kind::NOT ){ - Node nn = preSkolemizeQuantifiers( n[0], !polarity, fvs ); - return nn.negate(); - }else if( n.getKind()==kind::FORALL ){ - if( polarity ){ - vector< Node > children; - children.push_back( n[0] ); - //add children to current scope - vector< Node > fvss; - fvss.insert( fvss.begin(), fvs.begin(), fvs.end() ); - for( int i=0; i<(int)n[0].getNumChildren(); i++ ){ - fvss.push_back( n[0][i] ); - } - //process body - children.push_back( preSkolemizeQuantifiers( n[1], polarity, fvss ) ); - if( n.getNumChildren()==3 ){ - children.push_back( n[2] ); - } - //return processed quantifier - return NodeManager::currentNM()->mkNode( kind::FORALL, children ); - }else{ - //process body - Node nn = preSkolemizeQuantifiers( n[1], polarity, fvs ); - //now, substitute skolems for the variables - vector< TypeNode > argTypes; - for( int i=0; i<(int)fvs.size(); i++ ){ - argTypes.push_back( fvs[i].getType() ); - } - //calculate the variables and substitution - vector< Node > vars; - vector< Node > subs; - for( int i=0; i<(int)n[0].getNumChildren(); i++ ){ - vars.push_back( n[0][i] ); - } - for( int i=0; i<(int)n[0].getNumChildren(); i++ ){ - //make the new function symbol - if( argTypes.empty() ){ - Node s = NodeManager::currentNM()->mkSkolem( "sk_$$", n[0][i].getType(), "created during pre-skolemization" ); - subs.push_back( s ); - }else{ - TypeNode typ = NodeManager::currentNM()->mkFunctionType( argTypes, n[0][i].getType() ); - Node op = NodeManager::currentNM()->mkSkolem( "skop_$$", typ, "op created during pre-skolemization" ); - //DOTHIS: set attribute on op, marking that it should not be selected as trigger - vector< Node > funcArgs; - funcArgs.push_back( op ); - funcArgs.insert( funcArgs.end(), fvs.begin(), fvs.end() ); - subs.push_back( NodeManager::currentNM()->mkNode( kind::APPLY_UF, funcArgs ) ); - } - } - //apply substitution - nn = nn.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); - return nn; - } - }else{ - //check if it contains a quantifier as a subterm - //if so, we will write this node - if( containsQuantifiers( n ) ){ - if( n.getType().isBoolean() ){ - if( n.getKind()==kind::ITE || n.getKind()==kind::IFF || n.getKind()==kind::XOR || n.getKind()==kind::IMPLIES ){ - Node nn; - //must remove structure - if( n.getKind()==kind::ITE ){ - nn = NodeManager::currentNM()->mkNode( kind::AND, - NodeManager::currentNM()->mkNode( kind::OR, n[0].notNode(), n[1] ), - NodeManager::currentNM()->mkNode( kind::OR, n[0], n[2] ) ); - }else if( n.getKind()==kind::IFF || n.getKind()==kind::XOR ){ - nn = NodeManager::currentNM()->mkNode( kind::AND, - NodeManager::currentNM()->mkNode( kind::OR, n[0].notNode(), n.getKind()==kind::XOR ? n[1].notNode() : n[1] ), - NodeManager::currentNM()->mkNode( kind::OR, n[0], n.getKind()==kind::XOR ? n[1] : n[1].notNode() ) ); - }else if( n.getKind()==kind::IMPLIES ){ - nn = NodeManager::currentNM()->mkNode( kind::OR, n[0].notNode(), n[1] ); - } - return preSkolemizeQuantifiers( nn, polarity, fvs ); - }else if( n.getKind()==kind::AND || n.getKind()==kind::OR ){ - vector< Node > children; - for( int i=0; i<(int)n.getNumChildren(); i++ ){ - children.push_back( preSkolemizeQuantifiers( n[i], polarity, fvs ) ); - } - return NodeManager::currentNM()->mkNode( n.getKind(), children ); - }else{ - //must pull ite's - } - } - } - return n; - } -} - void SmtEnginePrivate::removeITEs() { d_smt.finalOptionsAreSet(); @@ -3156,13 +2870,25 @@ void SmtEnginePrivate::processAssertions() { dumpAssertions("post-strings-pp", d_assertionsToPreprocess); } if( d_smt.d_logic.isQuantified() ){ + //remove rewrite rules + for( unsigned i=0; i < d_assertionsToPreprocess.size(); i++ ) { + if( d_assertionsToPreprocess[i].getKind() == kind::REWRITE_RULE ){ + Node prev = d_assertionsToPreprocess[i]; + Trace("quantifiers-rewrite-debug") << "Rewrite rewrite rule " << prev << "..." << std::endl; + d_assertionsToPreprocess[i] = Rewriter::rewrite( quantifiers::QuantifiersRewriter::rewriteRewriteRule( d_assertionsToPreprocess[i] ) ); + Trace("quantifiers-rewrite") << "*** rr-rewrite " << prev << endl; + Trace("quantifiers-rewrite") << " ...got " << d_assertionsToPreprocess[i] << endl; + } + } + dumpAssertions("pre-skolem-quant", d_assertionsToPreprocess); if( options::preSkolemQuant() ){ //apply pre-skolemization to existential quantifiers for (unsigned i = 0; i < d_assertionsToPreprocess.size(); ++ i) { Node prev = d_assertionsToPreprocess[i]; + Trace("quantifiers-rewrite-debug") << "Pre-skolemize " << prev << "..." << std::endl; vector< Node > fvs; - d_assertionsToPreprocess[i] = Rewriter::rewrite( preSkolemizeQuantifiers( d_assertionsToPreprocess[i], true, fvs ) ); + d_assertionsToPreprocess[i] = Rewriter::rewrite( quantifiers::QuantifiersRewriter::preSkolemizeQuantifiers( prev, true, fvs ) ); if( prev!=d_assertionsToPreprocess[i] ){ Trace("quantifiers-rewrite") << "*** Pre-skolemize " << prev << endl; Trace("quantifiers-rewrite") << " ...got " << d_assertionsToPreprocess[i] << endl; @@ -3174,14 +2900,14 @@ void SmtEnginePrivate::processAssertions() { //quantifiers macro expansion bool success; do{ - QuantifierMacros qm; + quantifiers::QuantifierMacros qm; success = qm.simplify( d_assertionsToPreprocess, true ); }while( success ); } Trace("fo-rsn-enable") << std::endl; if( options::foPropQuant() ){ - FirstOrderPropagation fop; + quantifiers::FirstOrderPropagation fop; fop.simplify( d_assertionsToPreprocess ); } } @@ -3677,8 +3403,15 @@ Expr SmtEngine::getValue(const Expr& ex) const throw(ModalException, TypeCheckin // Expand, then normalize hash_map<Node, Node, NodeHashFunction> cache; n = d_private->expandDefinitions(n, cache); - n = d_private->rewriteBooleanTerms(n); - n = Rewriter::rewrite(n); + // There are two ways model values for terms are computed (for historical + // reasons). One way is that used in check-model; the other is that + // used by the Model classes. It's not clear to me exactly how these + // two are different, but they need to be unified. This ugly hack here + // is to fix bug 554 until we can revamp boolean-terms and models [MGD] + if(!n.getType().isFunction()) { + n = d_private->rewriteBooleanTerms(n); + n = Rewriter::rewrite(n); + } Trace("smt") << "--- getting value of " << n << endl; TheoryModel* m = d_theoryEngine->getModel(); @@ -3970,28 +3703,30 @@ void SmtEngine::checkModel(bool hardFailure) { Debug("boolean-terms") << "++ got " << n << endl; Notice() << "SmtEngine::checkModel(): -- substitutes to " << n << endl; - if(Theory::theoryOf(n) != THEORY_REWRITERULES) { + //AJR : FIXME need to ignore quantifiers too? + if( n.getKind() != kind::REWRITE_RULE ){ // In case it's a quantifier (or contains one), look up its value before // simplifying, or the quantifier might be irreparably altered. n = m->getValue(n); - } - - // Simplify the result. - n = d_private->simplify(n); - Notice() << "SmtEngine::checkModel(): -- simplifies to " << n << endl; - - TheoryId thy = Theory::theoryOf(n); - if(thy == THEORY_REWRITERULES) { + } else { // Note this "skip" is done here, rather than above. This is // because (1) the quantifier could in principle simplify to false, // which should be reported, and (2) checking for the quantifier // above, before simplification, doesn't catch buried quantifiers // anyway (those not at the top-level). - Notice() << "SmtEngine::checkModel(): -- skipping rewrite-rules assertion" + Notice() << "SmtEngine::checkModel(): -- skipping quantifiers/rewrite-rules assertion" << endl; continue; } + // Simplify the result. + n = d_private->simplify(n); + Notice() << "SmtEngine::checkModel(): -- simplifies to " << n << endl; + + // Replace the already-known ITEs (this is important for ground ITEs under quantifiers). + n = d_private->d_iteRemover.replace(n); + Notice() << "SmtEngine::checkModel(): -- ite replacement gives " << n << endl; + // As a last-ditch effort, ask model to simplify it. // Presently, this is only an issue for quantifiers, which can have a value // but don't show up in our substitution map above. diff --git a/src/smt/smt_engine.h b/src/smt/smt_engine.h index c34d3ecba..2991ab21b 100644 --- a/src/smt/smt_engine.h +++ b/src/smt/smt_engine.h @@ -30,7 +30,6 @@ #include "util/proof.h" #include "smt/modal_exception.h" #include "smt/logic_exception.h" -#include "util/hash.h" #include "options/options.h" #include "util/result.h" #include "util/sexpr.h" @@ -59,6 +58,7 @@ class TheoryEngine; class ProofManager; class Model; +class LogicRequest; class StatisticsRegistry; namespace context { @@ -281,6 +281,12 @@ class CVC4_PUBLIC SmtEngine { void finalOptionsAreSet(); /** + * Apply heuristics settings and other defaults. Done once, at + * finishInit() time. + */ + void setDefaults(); + + /** * Create theory engine, prop engine, decision engine. Called by * finalOptionsAreSet() */ @@ -331,6 +337,7 @@ class CVC4_PUBLIC SmtEngine { friend ::CVC4::StatisticsRegistry* ::CVC4::stats::getStatisticsRegistry(SmtEngine*); friend void ::CVC4::smt::beforeSearch(std::string, bool, SmtEngine*) throw(ModalException); friend ProofManager* ::CVC4::smt::currentProofManager(); + friend class ::CVC4::LogicRequest; // to access d_modelCommands friend class ::CVC4::Model; friend class ::CVC4::theory::TheoryModel; diff --git a/src/smt/smt_options_template.cpp b/src/smt/smt_options_template.cpp index 4edd91a8d..987d2e3c7 100644 --- a/src/smt/smt_options_template.cpp +++ b/src/smt/smt_options_template.cpp @@ -62,11 +62,15 @@ void SmtEngine::setOption(const std::string& key, const CVC4::SExpr& value) throw OptionException("command-verbosity value must be a tuple (command-name, integer)"); } + if(!value.isAtom()) { + throw OptionException("bad value for :" + key); + } + string optionarg = value.getValue(); ${smt_setoption_handlers} -#line 70 "${template}" +#line 74 "${template}" throw UnrecognizedOptionException(key); } @@ -126,7 +130,7 @@ CVC4::SExpr SmtEngine::getOption(const std::string& key) const ${smt_getoption_handlers} -#line 130 "${template}" +#line 134 "${template}" throw UnrecognizedOptionException(key); } diff --git a/src/theory/arith/approx_simplex.cpp b/src/theory/arith/approx_simplex.cpp index 1b3099842..9f6b1796e 100644 --- a/src/theory/arith/approx_simplex.cpp +++ b/src/theory/arith/approx_simplex.cpp @@ -20,8 +20,12 @@ #include "theory/arith/approx_simplex.h" #include "theory/arith/normal_form.h" #include "theory/arith/constraint.h" +#include "theory/arith/cut_log.h" +#include "theory/arith/matrix.h" #include <math.h> #include <cmath> +#include <cfloat> +#include <map> using namespace std; @@ -29,13 +33,229 @@ namespace CVC4 { namespace theory { namespace arith { -ApproximateSimplex::ApproximateSimplex() : - d_pivotLimit(std::numeric_limits<int>::max()) +struct AuxInfo { + TreeLog* tl; + int pivotLimit; + int branchLimit; + int branchDepth; + MipResult term; /* terminatation */ +}; + +enum SlackReplace { SlackUndef=0, SlackLB, SlackUB, SlackVLB, SlackVUB }; + +std::ostream& operator<<(std::ostream& out, MipResult res){ + switch(res){ + case MipUnknown: + out << "MipUnknown"; break; + case MipBingo: + out << "MipBingo"; break; + case MipClosed: + out << "MipClosed"; break; + case BranchesExhausted: + out << "BranchesExhausted"; break; + case PivotsExhauasted: + out << "PivotsExhauasted"; break; + case ExecExhausted: + out << "ExecExhausted"; break; + default: + out << "Unexpected Mip Value!"; break; + } + return out; +} +struct VirtualBound { + // Either x <= d * y or x >= d * y + ArithVar x; // variable being bounded + Kind k; // either LEQ or GEQ + Rational d; // the multiple on y + ArithVar y; // the variable that is the upper bound + ConstraintP c; // the original constraint relating x and y + + VirtualBound() + : x(ARITHVAR_SENTINEL) + , k(kind::UNDEFINED_KIND) + , d() + , y(ARITHVAR_SENTINEL) + , c(NullConstraint) + {} + VirtualBound(ArithVar toBound, Kind rel, const Rational& coeff, ArithVar bounding, ConstraintP orig) + : x(toBound) + , k(rel) + , d(coeff) + , y(bounding) + , c(orig) + { Assert(k == kind::LEQ || k == kind::GEQ); } +}; + +struct CutScratchPad { + bool d_failure; // if the construction was unsuccessful + + /* GOMORY CUTS Datastructures */ + ArithVar d_basic; // a variable that is basic in the approximate solver + DenseVector d_tabRow; // a row in the tableau not including d_basic, equal to 0 + DenseMap<ConstraintP> d_toBound; // each variable in toBound maps each variable in tabRow to either an upper/lower bound + + /* MIR CUTS Datastructures */ + DenseMap<SlackReplace> d_slacks;// The x'[i] selected for x[i] + DenseMap<VirtualBound> d_vub; // Virtual upper bounds. + DenseMap<VirtualBound> d_vlb; // Virtual lower bounds. + DenseMap<Rational> d_compRanges; + + // a sum of rows in the tableau, with possible replacements for fixed + // sum aggLhs[i] x[i] = aggRhs; + DenseVector d_agg; + // Takes agg and replaces x[i] with a slack variable x'[i] + // Takes agg and replaces x[i] with a slack variable x'[i] + // sum modLhs[i] x'[i] = modRhs; + DenseVector d_mod; + + // Takes mod, and performs c-Mir on it + // sum alpha[i] x'[i] <= beta + DenseVector d_alpha; + + /* The constructed cut */ + // sum cut[i] x[i] <= cutRhs + DenseVector d_cut; + Kind d_cutKind; + + /* The constraints used throughout construction. */ + std::set<ConstraintP> d_explanation; // use pointer equality + CutScratchPad(){ + clear(); + } + void clear(){ + d_failure = false; + d_basic = ARITHVAR_SENTINEL; + d_tabRow.purge(); + d_toBound.purge(); + + d_slacks.purge(); + d_vub.purge(); + d_vlb.purge(); + d_compRanges.purge(); + + d_agg.purge(); + d_mod.purge(); + d_alpha.purge(); + + d_cut.purge(); + d_cutKind = kind::UNDEFINED_KIND; + d_explanation.clear(); + } +}; +ApproximateStatistics::ApproximateStatistics() + // : d_relaxCalls("z::approx::relaxCalls",0) + // , d_relaxUnknowns("z::approx::relaxUnknowns",0) + // , d_relaxFeasible("z::approx::relaxFeasible",0) + // , d_relaxInfeasible("z::approx::relaxInfeasible",0) + // , d_relaxPivotsExhausted("z::approx::relaxPivotsExhausted",0) + // , d_mipCalls("z::approx::mipCalls",0) + // , d_mipUnknowns("z::approx::mipUnknowns",0) + // , d_mipBingo("z::approx::mipBingo",0) + // , d_mipClosed("z::approx::mipClosed",0) + // , d_mipBranchesExhausted("z::approx::mipBranchesExhausted",0) + // , d_mipPivotsExhausted("z::approx::mipPivotsExhausted",0) + // , d_mipExecExhausted("z::approx::mipExecExhausted",0) + // , d_gmiGen("z::approx::gmiGen",0) + // , d_gmiReplay("z::approx::gmiReplay",0) + // , d_mipGen("z::approx::mipGen",0) + // , d_mipReplay("z::approx::mipReplay",0) + : d_branchMaxDepth("z::approx::branchMaxDepth",0) + , d_branchesMaxOnAVar("z::approx::branchesMaxOnAVar",0) + //, d_branchTotal("z::approx::branchTotal",0) + //, d_branchCuts("z::approx::branchCuts",0) + + , d_gaussianElimConstructTime("z::approx::gaussianElimConstruct::time") + , d_gaussianElimConstruct("z::approx::gaussianElimConstruct::calls",0) + , d_averageGuesses("z::approx::averageGuesses") +{ + // StatisticsRegistry::registerStat(&d_relaxCalls); + // StatisticsRegistry::registerStat(&d_relaxUnknowns); + // StatisticsRegistry::registerStat(&d_relaxFeasible); + // StatisticsRegistry::registerStat(&d_relaxInfeasible); + // StatisticsRegistry::registerStat(&d_relaxPivotsExhausted); + + // StatisticsRegistry::registerStat(&d_mipCalls); + // StatisticsRegistry::registerStat(&d_mipUnknowns); + // StatisticsRegistry::registerStat(&d_mipBingo); + // StatisticsRegistry::registerStat(&d_mipClosed); + // StatisticsRegistry::registerStat(&d_mipBranchesExhausted); + // StatisticsRegistry::registerStat(&d_mipPivotsExhausted); + // StatisticsRegistry::registerStat(&d_mipExecExhausted); + + + // StatisticsRegistry::registerStat(&d_gmiGen); + // StatisticsRegistry::registerStat(&d_gmiReplay); + // StatisticsRegistry::registerStat(&d_mipGen); + // StatisticsRegistry::registerStat(&d_mipReplay); + + StatisticsRegistry::registerStat(&d_branchMaxDepth); + //StatisticsRegistry::registerStat(&d_branchTotal); + //StatisticsRegistry::registerStat(&d_branchCuts); + StatisticsRegistry::registerStat(&d_branchesMaxOnAVar); + + StatisticsRegistry::registerStat(&d_gaussianElimConstructTime); + StatisticsRegistry::registerStat(&d_gaussianElimConstruct); + + StatisticsRegistry::registerStat(&d_averageGuesses); +} + +ApproximateStatistics::~ApproximateStatistics(){ + // StatisticsRegistry::unregisterStat(&d_relaxCalls); + // StatisticsRegistry::unregisterStat(&d_relaxUnknowns); + // StatisticsRegistry::unregisterStat(&d_relaxFeasible); + // StatisticsRegistry::unregisterStat(&d_relaxInfeasible); + // StatisticsRegistry::unregisterStat(&d_relaxPivotsExhausted); + + // StatisticsRegistry::unregisterStat(&d_mipCalls); + // StatisticsRegistry::unregisterStat(&d_mipUnknowns); + // StatisticsRegistry::unregisterStat(&d_mipBingo); + // StatisticsRegistry::unregisterStat(&d_mipClosed); + // StatisticsRegistry::unregisterStat(&d_mipBranchesExhausted); + // StatisticsRegistry::unregisterStat(&d_mipPivotsExhausted); + // StatisticsRegistry::unregisterStat(&d_mipExecExhausted); + + + // StatisticsRegistry::unregisterStat(&d_gmiGen); + // StatisticsRegistry::unregisterStat(&d_gmiReplay); + // StatisticsRegistry::unregisterStat(&d_mipGen); + // StatisticsRegistry::unregisterStat(&d_mipReplay); + + StatisticsRegistry::unregisterStat(&d_branchMaxDepth); + //StatisticsRegistry::unregisterStat(&d_branchTotal); + //StatisticsRegistry::unregisterStat(&d_branchCuts); + StatisticsRegistry::unregisterStat(&d_branchesMaxOnAVar); + + StatisticsRegistry::unregisterStat(&d_gaussianElimConstructTime); + StatisticsRegistry::unregisterStat(&d_gaussianElimConstruct); + + StatisticsRegistry::unregisterStat(&d_averageGuesses); +} + +Integer ApproximateSimplex::s_defaultMaxDenom(1<<26); + +ApproximateSimplex::ApproximateSimplex(const ArithVariables& v, TreeLog& l, + ApproximateStatistics& s) + : d_vars(v) + , d_log(l) + , d_stats(s) + , d_pivotLimit(std::numeric_limits<int>::max()) + , d_branchLimit(std::numeric_limits<int>::max()) + , d_maxDepth(std::numeric_limits<int>::max()) {} -void ApproximateSimplex::setPivotLimit(int pivotLimit){ - Assert(pivotLimit >= 0); - d_pivotLimit = pivotLimit; +void ApproximateSimplex::setPivotLimit(int pl){ + Assert(pl >= 0); + d_pivotLimit = pl; +} + +void ApproximateSimplex::setBranchingDepth(int bd){ + Assert(bd >= 0); + d_maxDepth = bd; +} + +void ApproximateSimplex::setBranchOnVariableLimit(int bl){ + Assert(bl >= 0); + d_branchLimit = bl; } const double ApproximateSimplex::SMALL_FIXED_DELTA = .000000001; @@ -77,7 +297,7 @@ std::vector<Integer> ApproximateSimplex::rationalToCfe(const Rational& q, int de mods.push_back(Integer()); Integer& back = mods.back(); back = carry.floor(); - //cout << " cfe["<<i<<"]: " << back << endl; + Debug("rationalToCfe") << " cfe["<<i<<"]: " << back << endl; carry -= back; if(carry.isZero()){ break; @@ -91,24 +311,85 @@ std::vector<Integer> ApproximateSimplex::rationalToCfe(const Rational& q, int de return mods; } -Rational ApproximateSimplex::estimateWithCFE(const Rational& q, int depth){ - std::vector<Integer> cfe = rationalToCfe(q,depth); - return cfeToRational(cfe); + +Rational ApproximateSimplex::estimateWithCFE(const Rational& r, const Integer& K){ + Debug("estimateWithCFE") << "estimateWithCFE(" << r << ", " << K << ")" <<endl; + // references + // page 4: http://carlossicoli.free.fr/C/Cassels_J.W.S.-An_introduction_to_diophantine_approximation-University_Press(1965).pdf + // http://en.wikipedia.org/wiki/Continued_fraction + Assert(K >= Integer(1)); + if( r.getDenominator() <= K ){ + return r; + } + + // current numerator and denominator that has not been resolved in the cfe + Integer num = r.getNumerator(), den = r.getDenominator(); + Integer quot,rem; + + unsigned t = 0; + // For a sequence of candidate solutions q_t/p_t + // we keep only 3 time steps: 0[prev], 1[current], 2[next] + // timesteps with a fake timestep 0 (p is 0 and q is 1) + // at timestep 1 + Integer p[3]; // h + Integer q[3]; // k + // load the first 3 time steps manually + p[0] = 0; q[0] = 1; // timestep -2 + p[1] = 1; q[1] = 0; // timestep -1 + + Integer::floorQR(quot, rem, num, den); + num = den; den = rem; + + q[2] = q[0] + quot*q[1]; + p[2] = p[0] + quot*p[1]; + Debug("estimateWithCFE") << " cfe["<<t<<"]: " << p[2] <<"/"<< q[2] << endl; + while( q[2] <= K ){ + p[0] = p[1]; p[1] = p[2]; + q[0] = q[1]; q[1] = q[2]; + + + Integer::floorQR(quot, rem, num, den); + num = den; den = rem; + + p[2] = p[0]+quot*p[1]; + q[2] = q[0]+quot*q[1]; + ++t; + Debug("estimateWithCFE") << " cfe["<<t<<"]: " << p[2] <<"/"<< q[2] << endl; + } + + Integer k = (K-q[0]).floorDivideQuotient(q[1]); + Rational cand_prev(p[0]+k*p[1], q[0]+k*q[1]); + Rational cand_curr(p[1], q[1]); + Rational dist_prev = (cand_prev - r).abs(); + Rational dist_curr = (cand_curr - r).abs(); + if(dist_prev <= dist_curr){ + Debug("estimateWithCFE") << cand_prev << " is closer than " << cand_curr << endl; + return cand_prev; + }else{ + Debug("estimateWithCFE") << cand_curr << " is closer than " << cand_prev << endl; + return cand_curr; + } +} + +Rational ApproximateSimplex::estimateWithCFE(double d, const Integer& D) throw (RationalFromDoubleException){ + return estimateWithCFE(Rational::fromDouble(d), D); } -Rational ApproximateSimplex::estimateWithCFE(double d){ - return estimateWithCFE(Rational::fromDouble(d), 10); +Rational ApproximateSimplex::estimateWithCFE(double d) throw (RationalFromDoubleException){ + return estimateWithCFE(d, s_defaultMaxDenom); } class ApproxNoOp : public ApproximateSimplex { public: - ApproxNoOp(const ArithVariables& vars){} + ApproxNoOp(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s) + : ApproximateSimplex(v,l,s) + {} ~ApproxNoOp(){} - virtual ApproxResult solveRelaxation(){ - return ApproxError; + virtual LinResult solveRelaxation(){ + return LinUnknown; } - virtual Solution extractRelaxation() const{ + virtual Solution extractRelaxation() const throw (RationalFromDoubleException){ return Solution(); } @@ -116,14 +397,31 @@ public: return ArithRatPairVec(); } - virtual ApproxResult solveMIP(){ - return ApproxError; + virtual MipResult solveMIP(bool al){ + return MipUnknown; } - virtual Solution extractMIP() const{ + virtual Solution extractMIP() const throw (RationalFromDoubleException){ return Solution(); } virtual void setOptCoeffs(const ArithRatPairVec& ref){} + virtual std::vector<const CutInfo*> getValidCuts(const std::set<const NodeLog*>& nodes){ + return std::vector<const CutInfo*>(); + } + + virtual void tryCut(int nid, CutInfo& cut) throw (RationalFromDoubleException){} + + virtual std::vector<const CutInfo*> getValidCuts(const NodeLog& node) throw(RationalFromDoubleException){ + return std::vector<const CutInfo*>(); + } + + virtual ArithVar getBranchVar(const NodeLog& nl) const{ + return ARITHVAR_SENTINEL; + } + + virtual double sumInfeasibilities(bool mip) const{ + return 0.0; + } }; }/* CVC4::theory::arith namespace */ @@ -146,14 +444,33 @@ namespace CVC4 { namespace theory { namespace arith { +Kind glpk_type_to_kind(int glpk_cut_type){ + switch(glpk_cut_type){ + case GLP_LO: return kind::GEQ; + case GLP_UP: return kind::LEQ; + case GLP_FX: return kind::EQUAL; + case GLP_DB: + case GLP_FR: + default: return kind::UNDEFINED_KIND; + } +} + +class GmiInfo; +class MirInfo; +class BranchCutInfo; + class ApproxGLPK : public ApproximateSimplex { private: - glp_prob* d_prob; - const ArithVariables& d_vars; + glp_prob* d_inputProb; /* a copy of the input prob */ + glp_prob* d_realProb; /* a copy of the real relaxation output */ + glp_prob* d_mipProb; /* a copy of the integer prob */ DenseMap<int> d_colIndices; DenseMap<int> d_rowIndices; + NodeLog::RowIdMap d_rootRowIds; + //DenseMap<ArithVar> d_rowToArithVar; + DenseMap<ArithVar> d_colToArithVar; int d_instanceID; @@ -162,27 +479,127 @@ private: static int s_verbosity; + CutScratchPad d_pad; + + std::vector<Integer> d_denomGuesses; + public: - ApproxGLPK(const ArithVariables& vars); + ApproxGLPK(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s); ~ApproxGLPK(); - virtual ApproxResult solveRelaxation(); - virtual Solution extractRelaxation() const{ + virtual LinResult solveRelaxation(); + virtual Solution extractRelaxation() const throw (RationalFromDoubleException){ return extractSolution(false); } virtual ArithRatPairVec heuristicOptCoeffs() const; - virtual ApproxResult solveMIP(); - virtual Solution extractMIP() const{ + virtual MipResult solveMIP(bool al); + virtual Solution extractMIP() const throw (RationalFromDoubleException){ return extractSolution(true); } virtual void setOptCoeffs(const ArithRatPairVec& ref); + //void getValidCuts(const NodeLog& con, std::vector<const CutInfo*>& out); + virtual std::vector<const CutInfo*> getValidCuts(const NodeLog& nodes) throw (RationalFromDoubleException); + //virtual std::vector<const NodeLog*> getBranches(); + + //Node downBranchLiteral(const NodeLog& con) const; + ArithVar getBranchVar(const NodeLog& con) const; static void printGLPKStatus(int status, std::ostream& out); + + private: - Solution extractSolution(bool mip) const; + Solution extractSolution(bool mip) const throw (RationalFromDoubleException); int guessDir(ArithVar v) const; + + // get this stuff out of here + void tryCut(int nid, CutInfo& cut) throw (RationalFromDoubleException); + + ArithVar _getArithVar(int nid, int M, int ind) const; + ArithVar getArithVarFromRow(int nid, int ind) const { + if(ind >= 0){ + const NodeLog& nl = d_log.getNode(nid); + return nl.lookupRowId(ind); + } + return ARITHVAR_SENTINEL; + } + + // virtual void mapRowId(int nid, int ind, ArithVar v){ + // NodeLog& nl = d_log.getNode(nid); + // nl.mapRowId(ind, v); + // } + // virtual void applyRowsDeleted(int nid, const RowsDeleted& rd){ + // NodeLog& nl = d_log.getNode(nid); + // nl.applyRowsDeleted(rd); + // } + + ArithVar getArithVarFromStructural(int ind) const{ + if(ind >= 0){ + unsigned u = (unsigned) ind; + if(d_colToArithVar.isKey(u)){ + return d_colToArithVar[u]; + } + } + return ARITHVAR_SENTINEL; + } + + /** + * Attempts to make the row vector vec on the pad. + * If this is not in the row span of the original tableau this + * raises the failure flag. + */ + bool attemptConstructTableRow(int node, int M, const PrimitiveVec& vec); + bool guessCoefficientsConstructTableRow(int node, int M, const PrimitiveVec& vec); + bool guessCoefficientsConstructTableRow(int node, int M, const PrimitiveVec& vec, const Integer& D); + bool gaussianElimConstructTableRow(int node, int M, const PrimitiveVec& vec); + + /* This is a guess of a vector in the row span of the tableau. + * Attempt to cancel out all of the variables. + * returns true if this is constructable. + */ + bool guessIsConstructable(const DenseMap<Rational>& guess) const; + + /** + * Loads a vector of statuses into a dense map over bounds. + * returns true on failure. + */ + bool loadToBound(int node, int M, int len, int* inds, int* statuses, + DenseMap<ConstraintP>& toBound) const; + + /** checks the cut on the pad for whether it is sufficiently similar to cut. */ + bool checkCutOnPad(int nid, const CutInfo& cut) const; + + + /** turns the pad into a node and creates an explanation. */ + //std::pair<Node, Node> makeCutNodes(int nid, const CutInfo& cut) const; + + // true means failure! + // BRANCH CUTS + bool attemptBranchCut(int nid, const BranchCutInfo& br); + + // GOMORY CUTS + bool attemptGmi(int nid, const GmiInfo& gmi); + /** tries to turn the information on the pad into a cut. */ + bool constructGmiCut(); + + // MIR CUTS + bool attemptMir(int nid, const MirInfo& mir); + bool applyCMIRRule(int nid, const MirInfo& mir); + bool makeRangeForComplemented(int nid, const MirInfo& mir); + bool loadSlacksIntoPad(int nid, const MirInfo& mir); + bool loadVirtualBoundsIntoPad(int nid, const MirInfo& mir); + bool loadRowSumIntoAgg(int nid, int M, const PrimitiveVec& mir); + bool buildModifiedRow(int nid, const MirInfo& mir); + bool constructMixedKnapsack(); + bool replaceSlacksOnCuts(); + bool loadVB(int nid, int M, int j, int ri, bool wantUb, VirtualBound& tmp); + + + double sumInfeasibilities(bool mip) const{ + return sumInfeasibilities(mip? d_mipProb : d_realProb); + } + double sumInfeasibilities(glp_prob* prob, bool mip) const; }; int ApproxGLPK::s_verbosity = 0; @@ -197,11 +614,11 @@ int ApproxGLPK::s_verbosity = 0; namespace CVC4 { namespace theory { namespace arith { -ApproximateSimplex* ApproximateSimplex::mkApproximateSimplexSolver(const ArithVariables& vars){ +ApproximateSimplex* ApproximateSimplex::mkApproximateSimplexSolver(const ArithVariables& vars, TreeLog& l, ApproximateStatistics& s){ #ifdef CVC4_USE_GLPK - return new ApproxGLPK(vars); + return new ApproxGLPK(vars, l, s); #else - return new ApproxNoOp(vars); + return new ApproxNoOp(vars, l, s); #endif } bool ApproximateSimplex::enabled() { @@ -223,16 +640,38 @@ namespace CVC4 { namespace theory { namespace arith { -ApproxGLPK::ApproxGLPK(const ArithVariables& avars) : - d_vars(avars), d_solvedRelaxation(false), d_solvedMIP(false) +static CutInfoKlass fromGlpkClass(int klass){ + switch(klass){ + case GLP_RF_GMI: return GmiCutKlass; + case GLP_RF_MIR: return MirCutKlass; + case GLP_RF_COV: + case GLP_RF_CLQ: + default: return UnknownKlass; + } +} + +ApproxGLPK::ApproxGLPK(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s) + : ApproximateSimplex(v, l, s) + , d_inputProb(NULL) + , d_realProb(NULL) + , d_mipProb(NULL) + , d_solvedRelaxation(false) + , d_solvedMIP(false) { static int instance = 0; ++instance; d_instanceID = instance; - d_prob = glp_create_prob(); - glp_set_obj_dir(d_prob, GLP_MAX); - glp_set_prob_name(d_prob, "ApproximateSimplex::approximateFindModel"); + d_denomGuesses.push_back(Integer(1<<22)); + d_denomGuesses.push_back(ApproximateSimplex::s_defaultMaxDenom); + d_denomGuesses.push_back(Integer(1ul<<29)); + d_denomGuesses.push_back(Integer(1ul<<31)); + + d_inputProb = glp_create_prob(); + d_realProb = glp_create_prob(); + d_mipProb = glp_create_prob(); + glp_set_obj_dir(d_inputProb, GLP_MAX); + glp_set_prob_name(d_inputProb, "ApproximateSimplex::approximateFindModel"); int numRows = 0; int numCols = 0; @@ -241,24 +680,35 @@ ApproxGLPK::ApproxGLPK(const ArithVariables& avars) : for(ArithVariables::var_iterator vi = d_vars.var_begin(), vi_end = d_vars.var_end(); vi != vi_end; ++vi){ ArithVar v = *vi; - if(d_vars.isSlack(v)){ + if(d_vars.isAuxiliary(v)){ ++numRows; d_rowIndices.set(v, numRows); + //mapRowId(d_log.getRootId(), numRows, v); + d_rootRowIds.insert(make_pair(numRows, v)); + //d_rowToArithVar.set(numRows, v); + Debug("approx") << "Row vars: " << v << "<->" << numRows << endl; }else{ ++numCols; d_colIndices.set(v, numCols); + d_colToArithVar.set(numCols, v); + Debug("approx") << "Col vars: " << v << "<->" << numCols << endl; } } - glp_add_rows(d_prob, numRows); - glp_add_cols(d_prob, numCols); + Assert(numRows > 0); + Assert(numCols > 0); + + + + glp_add_rows(d_inputProb, numRows); + glp_add_cols(d_inputProb, numCols); // Assign the upper/lower bounds and types to each variable for(ArithVariables::var_iterator vi = d_vars.var_begin(), vi_end = d_vars.var_end(); vi != vi_end; ++vi){ ArithVar v = *vi; if(s_verbosity >= 2){ - Message() << v << " "; - d_vars.printModel(v, Message()); + //Message() << v << " "; + //d_vars.printModel(v, Message()); } int type; @@ -282,14 +732,15 @@ ApproxGLPK::ApproxGLPK(const ArithVariables& avars) : type = GLP_FR; } - if(d_vars.isSlack(v)){ + if(d_vars.isAuxiliary(v)){ int rowIndex = d_rowIndices[v]; - glp_set_row_bnds(d_prob, rowIndex, type, lb, ub); + glp_set_row_bnds(d_inputProb, rowIndex, type, lb, ub); }else{ int colIndex = d_colIndices[v]; + // is input is correct here int kind = d_vars.isInteger(v) ? GLP_IV : GLP_CV; - glp_set_col_kind(d_prob, colIndex, kind); - glp_set_col_bnds(d_prob, colIndex, type, lb, ub); + glp_set_col_kind(d_inputProb, colIndex, kind); + glp_set_col_bnds(d_inputProb, colIndex, type, lb, ub); } } @@ -333,7 +784,7 @@ ApproxGLPK::ApproxGLPK(const ArithVariables& avars) : ar[entryCounter] = coeff; } } - glp_load_matrix(d_prob, numEntries, ia, ja, ar); + glp_load_matrix(d_inputProb, numEntries, ia, ja, ar); delete[] ia; delete[] ja; @@ -410,12 +861,12 @@ ArithRatPairVec ApproxGLPK::heuristicOptCoeffs() const{ if(type != GLP_FX && type != GLP_FR){ - if(d_vars.isSlack(v)){ + if(d_vars.isAuxiliary(v)){ Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v)); uint32_t len = p.size(); d_rowCandidates.set(v, len); sumRowLength += len; - maxRowLength =std::max(maxRowLength, len); + maxRowLength = std::max(maxRowLength, len); }else if(!d_vars.isInteger(v)){ d_colCandidates.set(v, BoundCounts()); } @@ -430,7 +881,7 @@ ArithRatPairVec ApproxGLPK::heuristicOptCoeffs() const{ bool ubCap = !d_vars.hasLowerBound(v) && d_vars.hasUpperBound(v); if(lbCap || ubCap){ - Constraint b = lbCap ? d_vars.getLowerBoundConstraint(v) + ConstraintP b = lbCap ? d_vars.getLowerBoundConstraint(v) : d_vars.getUpperBoundConstraint(v); if(!(b->getValue()).noninfinitesimalIsZero()){ continue; } @@ -530,7 +981,7 @@ void ApproxGLPK::setOptCoeffs(const ArithRatPairVec& ref){ ArithVar v = (*i).first; const Rational& q = (*i).second; - if(d_vars.isSlack(v)){ + if(d_vars.isAuxiliary(v)){ // replace the variable by its definition and multiply by q Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(v)); Polynomial pq = p * q; @@ -563,7 +1014,7 @@ void ApproxGLPK::setOptCoeffs(const ArithRatPairVec& ref){ for(DenseMap<double>::const_iterator ci =nbCoeffs.begin(), ciend = nbCoeffs.end(); ci != ciend; ++ci){ Index colIndex = *ci; double coeff = nbCoeffs[colIndex]; - glp_set_obj_coef(d_prob, colIndex, coeff); + glp_set_obj_coef(d_inputProb, colIndex, coeff); } } @@ -610,11 +1061,14 @@ void ApproxGLPK::printGLPKStatus(int status, std::ostream& out){ } ApproxGLPK::~ApproxGLPK(){ - glp_delete_prob(d_prob); + glp_delete_prob(d_inputProb); + glp_delete_prob(d_realProb); + glp_delete_prob(d_mipProb); + } -ApproximateSimplex::Solution ApproxGLPK::extractSolution(bool mip) const{ +ApproximateSimplex::Solution ApproxGLPK::extractSolution(bool mip) const throw (RationalFromDoubleException){ Assert(d_solvedRelaxation); Assert(!mip || d_solvedMIP); @@ -622,12 +1076,15 @@ ApproximateSimplex::Solution ApproxGLPK::extractSolution(bool mip) const{ DenseSet& newBasis = sol.newBasis; DenseMap<DeltaRational>& newValues = sol.newValues; + glp_prob* prob = mip ? d_mipProb : d_realProb; + for(ArithVariables::var_iterator i = d_vars.var_begin(), i_end = d_vars.var_end(); i != i_end; ++i){ ArithVar vi = *i; - bool isSlack = d_vars.isSlack(vi); - int glpk_index = isSlack ? d_rowIndices[vi] : d_colIndices[vi]; + bool isAux = d_vars.isAuxiliary(vi); + int glpk_index = isAux ? d_rowIndices[vi] : d_colIndices[vi]; - int status = isSlack ? glp_get_row_stat(d_prob, glpk_index) : glp_get_col_stat(d_prob, glpk_index); + int status = isAux ? glp_get_row_stat(prob, glpk_index) + : glp_get_col_stat(prob, glpk_index); if(s_verbosity >= 2){ Message() << "assignment " << vi << endl; } @@ -667,10 +1124,14 @@ ApproximateSimplex::Solution ApproxGLPK::extractSolution(bool mip) const{ if(useDefaultAssignment){ if(s_verbosity >= 2){ Message() << "non-basic other" << endl; } - double newAssign = - mip ? - (isSlack ? glp_mip_row_val(d_prob, glpk_index) : glp_mip_col_val(d_prob, glpk_index)) - : (isSlack ? glp_get_row_prim(d_prob, glpk_index) : glp_get_col_prim(d_prob, glpk_index)); + double newAssign; + if(mip){ + newAssign = (isAux ? glp_mip_row_val(prob, glpk_index) + : glp_mip_col_val(prob, glpk_index)); + }else{ + newAssign = (isAux ? glp_get_row_prim(prob, glpk_index) + : glp_get_col_prim(prob, glpk_index)); + } const DeltaRational& oldAssign = d_vars.getAssignment(vi); @@ -718,7 +1179,51 @@ ApproximateSimplex::Solution ApproxGLPK::extractSolution(bool mip) const{ return sol; } -ApproximateSimplex::ApproxResult ApproxGLPK::solveRelaxation(){ +double ApproxGLPK::sumInfeasibilities(glp_prob* prob, bool mip) const{ + /* compute the sum of dual infeasibilities */ + double infeas = 0.0; + + for(ArithVariables::var_iterator i = d_vars.var_begin(), i_end = d_vars.var_end(); i != i_end; ++i){ + ArithVar vi = *i; + bool isAux = d_vars.isAuxiliary(vi); + int glpk_index = isAux ? d_rowIndices[vi] : d_colIndices[vi]; + + double newAssign; + if(mip){ + newAssign = (isAux ? glp_mip_row_val(prob, glpk_index) + : glp_mip_col_val(prob, glpk_index)); + }else{ + newAssign = (isAux ? glp_get_row_prim(prob, glpk_index) + : glp_get_col_prim(prob, glpk_index)); + } + + + double ub = isAux ? + glp_get_row_ub(prob, glpk_index) : glp_get_col_ub(prob, glpk_index); + + double lb = isAux ? + glp_get_row_lb(prob, glpk_index) : glp_get_col_lb(prob, glpk_index); + + if(ub != +DBL_MAX){ + if(newAssign > ub){ + double ubinf = newAssign - ub; + infeas += ubinf; + Debug("approx::soi") << "ub inf" << vi << " " << ubinf << " " << infeas << endl; + } + } + if(lb != -DBL_MAX){ + if(newAssign < lb){ + double lbinf = lb - newAssign; + infeas += lbinf; + + Debug("approx::soi") << "lb inf" << vi << " " << lbinf << " " << infeas << endl; + } + } + } + return infeas; +} + +LinResult ApproxGLPK::solveRelaxation(){ Assert(!d_solvedRelaxation); glp_smcp parm; @@ -732,51 +1237,563 @@ ApproximateSimplex::ApproxResult ApproxGLPK::solveRelaxation(){ parm.msg_lev = GLP_MSG_ALL; } - int res = glp_simplex(d_prob, &parm); + glp_erase_prob(d_realProb); + glp_copy_prob(d_realProb, d_inputProb, GLP_OFF); + + int res = glp_simplex(d_realProb, &parm); switch(res){ case 0: { - int status = glp_get_status(d_prob); + int status = glp_get_status(d_realProb); + int iterationcount = glp_get_it_cnt(d_realProb); switch(status){ case GLP_OPT: case GLP_FEAS: case GLP_UNBND: d_solvedRelaxation = true; - return ApproxSat; + return LinFeasible; case GLP_INFEAS: case GLP_NOFEAS: d_solvedRelaxation = true; - return ApproxUnsat; + return LinInfeasible; default: - return ApproxError; + { + if(iterationcount >= d_pivotLimit){ + return LinExhausted; + } + return LinUnknown; + } } } default: - return ApproxError; + return LinUnknown; + } +} + + +struct MirInfo : public CutInfo { + + /** a sum of input rows. */ + PrimitiveVec row_sum; + + /* the delta used */ + double delta; + + /* all of these are length vars == N+M*/ + int nvars; + char* cset; + char* subst; + int* vlbRows; + int* vubRows; + MirInfo(int execOrd, int ord) + : CutInfo(MirCutKlass, execOrd, ord) + , nvars(0) + , cset(NULL) + , subst(NULL) + , vlbRows(NULL) + , vubRows(NULL) + {} + + ~MirInfo(){ + clearSets(); + } + void clearSets(){ + if(cset != NULL){ + delete[] cset; + delete[] subst; + delete[] vlbRows; + delete[] vubRows; + cset = NULL; + nvars = 0; + } + } + void initSet(){ + Assert(d_N >= 0); + Assert(d_mAtCreation >= 0); + clearSets(); + + int vars = 1 + d_N + d_mAtCreation; + + cset = new char[1+vars]; + subst = new char[1+vars]; + vlbRows = new int[1+vars]; + vubRows = new int[1+vars]; + } +}; + +struct GmiInfo : public CutInfo { + int basic; + PrimitiveVec tab_row; + int* tab_statuses; + /* has the length tab_row.length */ + + GmiInfo(int execOrd, int ord) + : CutInfo(GmiCutKlass, execOrd, ord) + , basic(-1) + , tab_row() + , tab_statuses(NULL) + { + Assert(!initialized_tab()); + } + + ~GmiInfo(){ + if(initialized_tab()){ + clear_tab(); + } + } + + bool initialized_tab() const { + return tab_statuses != NULL; + } + + void init_tab(int N){ + if(initialized_tab()){ + clear_tab(); + } + tab_row.setup(N); + tab_statuses = new int[1+N]; + } + + void clear_tab() { + delete[] tab_statuses; + tab_statuses = NULL; + tab_row.clear(); + basic = -1; + } +}; + + + +static void loadCut(glp_tree *tree, CutInfo* cut){ + int ord, cut_len, cut_klass; + int N, M; + int* cut_inds; + double* cut_coeffs; + int glpk_cut_type; + double cut_rhs; + glp_prob* lp; + + lp = glp_ios_get_prob(tree); + ord = cut->poolOrdinal(); + + N = glp_get_num_cols(lp); + M = glp_get_num_rows(lp); + + cut->setDimensions(N, M); + + + + // Get the cut + cut_len = glp_ios_get_cut(tree, ord, NULL, NULL, &cut_klass, NULL, NULL); + Assert(fromGlpkClass(cut_klass) == cut->getKlass()); + + PrimitiveVec& cut_vec = cut->getCutVector(); + cut_vec.setup(cut_len); + cut_inds = cut_vec.inds; + cut_coeffs = cut_vec.coeffs; + + cut_vec.len = glp_ios_get_cut(tree, ord, cut_inds, cut_coeffs, &cut_klass, &glpk_cut_type, &cut_rhs); + Assert(fromGlpkClass(cut_klass) == cut->getKlass()); + Assert(cut_vec.len == cut_len); + + cut->setRhs(cut_rhs); + + cut->setKind( glpk_type_to_kind(glpk_cut_type) ); +} + + +static MirInfo* mirCut(glp_tree *tree, int exec_ord, int cut_ord){ + Debug("approx::mirCut") << "mirCut()" << exec_ord << endl; + + MirInfo* mir; + mir = new MirInfo(exec_ord, cut_ord); + loadCut(tree, mir); + mir->initSet(); + + + int nrows = glp_ios_cut_get_aux_nrows(tree, cut_ord); + + PrimitiveVec& row_sum = mir->row_sum; + row_sum.setup(nrows); + glp_ios_cut_get_aux_rows(tree, cut_ord, row_sum.inds, row_sum.coeffs); + + glp_ios_cut_get_mir_cset(tree, cut_ord, mir->cset); + mir->delta = glp_ios_cut_get_mir_delta(tree, cut_ord); + glp_ios_cut_get_mir_subst(tree, cut_ord, mir->subst); + glp_ios_cut_get_mir_virtual_rows(tree, cut_ord, mir->vlbRows, mir->vubRows); + + if(Debug.isOn("approx::mirCut")){ + Debug("approx::mirCut") << "mir_id: " << exec_ord << endl; + row_sum.print(Debug("approx::mirCut")); + } + + return mir; +} + +static GmiInfo* gmiCut(glp_tree *tree, int exec_ord, int cut_ord){ + Debug("approx::gmiCut") << "gmiCut()" << exec_ord << endl; + + int gmi_var; + int write_pos; + int read_pos; + int stat; + int ind; + int i; + + GmiInfo* gmi; + glp_prob* lp; + + gmi = new GmiInfo(exec_ord, cut_ord); + loadCut(tree, gmi); + + lp = glp_ios_get_prob(tree); + + int N = gmi->getN(); + int M = gmi->getMAtCreation(); + + // Get the tableau row + int nrows CVC4_UNUSED = glp_ios_cut_get_aux_nrows(tree, gmi->poolOrdinal()); + Assert(nrows == 1); + int rows[1+1]; + glp_ios_cut_get_aux_rows(tree, gmi->poolOrdinal(), rows, NULL); + gmi_var = rows[1]; + + gmi->init_tab(N); + gmi->basic = M+gmi_var; + + Debug("approx::gmiCut") + << gmi <<" " << gmi->basic << " " + << cut_ord<<" " << M <<" " << gmi_var << endl; + + PrimitiveVec& tab_row = gmi->tab_row; + Debug("approx::gmiCut") << "Is N sufficient here?" << endl; + tab_row.len = glp_eval_tab_row(lp, gmi->basic, tab_row.inds, tab_row.coeffs); + + Debug("approx::gmiCut") << "gmi_var " << gmi_var << endl; + + Debug("approx::gmiCut") << "tab_pos " << tab_row.len << endl; + write_pos = 1; + for(read_pos = 1; read_pos <= tab_row.len; ++read_pos){ + if (fabs(tab_row.coeffs[read_pos]) < 1e-10){ + }else{ + tab_row.coeffs[write_pos] = tab_row.coeffs[read_pos]; + tab_row.inds[write_pos] = tab_row.inds[read_pos]; + ++write_pos; + } + } + tab_row.len = write_pos-1; + Debug("approx::gmiCut") << "write_pos " << write_pos << endl; + Assert(tab_row.len > 0); + + for(i = 1; i <= tab_row.len; ++i){ + ind = tab_row.inds[i]; + Debug("approx::gmiCut") << "ind " << i << " " << ind << endl; + stat = (ind <= M) ? + glp_get_row_stat(lp, ind) : glp_get_col_stat(lp, ind - M); + + Debug("approx::gmiCut") << "ind " << i << " " << ind << " stat " << stat << endl; + switch (stat){ + case GLP_NL: + case GLP_NU: + case GLP_NS: + gmi->tab_statuses[i] = stat; + break; + case GLP_NF: + default: + Unreachable(); + } } + + if(Debug.isOn("approx::gmiCut")){ + gmi->print(Debug("approx::gmiCut")); + } + return gmi; } -void stopAtBingoOrPivotLimit(glp_tree *tree, void *info){ - int pivotLimit = *((int*)info); +static BranchCutInfo* branchCut(glp_tree *tree, int exec_ord, int br_var, double br_val, bool down_bad){ + //(tree, br_var, br_val, dn < 0); + double rhs; + Kind k; + if(down_bad){ + // down branch is infeasible + // x <= floor(v) is infeasible + // - so x >= ceiling(v) is implied + k = kind::GEQ; + rhs = std::ceil(br_val); + }else{ + // up branch is infeasible + // x >= ceiling(v) is infeasible + // - so x <= floor(v) is implied + k = kind::LEQ; + rhs = std::floor(br_val); + } + BranchCutInfo* br_cut = new BranchCutInfo(exec_ord, br_var, k, rhs); + return br_cut; +} + +static void glpkCallback(glp_tree *tree, void *info){ + AuxInfo* aux = (AuxInfo*)(info); + TreeLog& tl = *(aux->tl); + + int exec = tl.getExecutionOrd(); + int glpk_node_p = -1; + int node_ord = -1; + + if(tl.isActivelyLogging()){ + switch(glp_ios_reason(tree)){ + case GLP_LI_DELROW: + { + glpk_node_p = glp_ios_curr_node(tree); + node_ord = glp_ios_node_ord(tree, glpk_node_p); + + int nrows = glp_ios_rows_deleted(tree, NULL); + int* num = new int[1+nrows]; + glp_ios_rows_deleted(tree, num); + + NodeLog& node = tl.getNode(node_ord); + + RowsDeleted* rd = new RowsDeleted(exec, nrows, num); + + node.addCut(rd); + delete num; + } + break; + case GLP_ICUTADDED: + { + int cut_ord = glp_ios_pool_size(tree); + glpk_node_p = glp_ios_curr_node(tree); + node_ord = glp_ios_node_ord(tree, glpk_node_p); + Assert(cut_ord > 0); + Debug("approx") << "curr node " << glpk_node_p + << " cut ordinal " << cut_ord + << " node depth " << glp_ios_node_level(tree, glpk_node_p) + << endl; + int klass; + glp_ios_get_cut(tree, cut_ord, NULL, NULL, &klass, NULL, NULL); + + NodeLog& node = tl.getNode(node_ord); + switch(klass){ + case GLP_RF_GMI: + { + GmiInfo* gmi = gmiCut(tree, exec, cut_ord); + node.addCut(gmi); + } + break; + case GLP_RF_MIR: + { + MirInfo* mir = mirCut(tree, exec, cut_ord); + node.addCut(mir); + } + break; + case GLP_RF_COV: + Debug("approx") << "GLP_RF_COV" << endl; + break; + case GLP_RF_CLQ: + Debug("approx") << "GLP_RF_CLQ" << endl; + break; + default: + break; + } + } + break; + case GLP_ICUTSELECT: + { + glpk_node_p = glp_ios_curr_node(tree); + node_ord = glp_ios_node_ord(tree, glpk_node_p); + int cuts = glp_ios_pool_size(tree); + int* ords = new int[1+cuts]; + int* rows = new int[1+cuts]; + int N = glp_ios_selected_cuts(tree, ords, rows); + + NodeLog& nl = tl.getNode(node_ord); + Debug("approx") << glpk_node_p << " " << node_ord << " " << cuts << " " << N << std::endl; + for(int i = 1; i <= N; ++i){ + Debug("approx") << "adding to " << node_ord <<" @ i= " << i + << " ords[i] = " << ords[i] + << " rows[i] = " << rows[i] << endl; + nl.addSelected(ords[i], rows[i]); + } + delete[] ords; + delete[] rows; + nl.applySelected(); + } + break; + case GLP_LI_BRANCH: + { + // a branch was just made + int br_var; + int p, dn, up; + int p_ord, dn_ord, up_ord; + double br_val; + br_var = glp_ios_branch_log(tree, &br_val, &p, &dn, &up); + p_ord = glp_ios_node_ord(tree, p); + + dn_ord = (dn >= 0) ? glp_ios_node_ord(tree, dn) : -1; + up_ord = (up >= 0) ? glp_ios_node_ord(tree, up) : -1; + + Debug("approx::") << "branch: "<< br_var << " " << br_val << " tree " << p << " " << dn << " " << up << endl; + Debug("approx::") << "\t " << p_ord << " " << dn_ord << " " << up_ord << endl; + if(dn < 0 && up < 0){ + Debug("approx::") << "branch close " << exec << endl; + NodeLog& node = tl.getNode(p_ord); + BranchCutInfo* cut_br = branchCut(tree, exec, br_var, br_val, dn < 0); + node.addCut(cut_br); + tl.close(p_ord); + }else if(dn < 0 || up < 0){ + Debug("approx::") << "branch cut" << exec << endl; + NodeLog& node = tl.getNode(p_ord); + BranchCutInfo* cut_br = branchCut(tree, exec, br_var, br_val, dn < 0); + node.addCut(cut_br); + }else{ + Debug("approx::") << "normal branch" << endl; + tl.branch(p_ord, br_var, br_val, dn_ord, up_ord); + } + } + break; + case GLP_LI_CLOSE: + { + glpk_node_p = glp_ios_curr_node(tree); + node_ord = glp_ios_node_ord(tree, glpk_node_p); + Debug("approx::") << "close " << glpk_node_p << endl; + tl.close(node_ord); + } + break; + default: + break; + } + } + switch(glp_ios_reason(tree)){ case GLP_IBINGO: + Debug("approx::") << "bingo" << endl; + aux->term = MipBingo; glp_ios_terminate(tree); break; + case GLP_ICUTADDED: + { + tl.addCut(); + } + break; + case GLP_LI_BRANCH: + { + int p, dn, up; + int br_var = glp_ios_branch_log(tree, NULL, &p, &dn, &up); + + if(br_var >= 0){ + unsigned v = br_var; + tl.logBranch(v); + int depth = glp_ios_node_level(tree, p); + unsigned ubl = (aux->branchLimit) >= 0 ? ((unsigned)(aux->branchLimit)) : 0u; + if(tl.numBranches(v) >= ubl || depth >= (aux->branchDepth)){ + aux->term = BranchesExhausted; + glp_ios_terminate(tree); + } + } + } + break; + case GLP_LI_CLOSE: + break; default: - glp_prob* prob = glp_ios_get_prob(tree); - int iterationcount = lpx_get_int_parm(prob, LPX_K_ITCNT); - if(iterationcount > pivotLimit){ - glp_ios_terminate(tree); + { + glp_prob* prob = glp_ios_get_prob(tree); + int iterationcount = glp_get_it_cnt(prob); + if(exec > (aux->pivotLimit)){ + aux->term = ExecExhausted; + glp_ios_terminate(tree); + }else if(iterationcount > (aux->pivotLimit)){ + aux->term = PivotsExhauasted; + glp_ios_terminate(tree); + } } break; } } -ApproximateSimplex::ApproxResult ApproxGLPK::solveMIP(){ +std::vector<const CutInfo*> ApproxGLPK::getValidCuts(const NodeLog& con) throw (RationalFromDoubleException){ + std::vector<const CutInfo*> proven; + int nid = con.getNodeId(); + for(NodeLog::const_iterator j = con.begin(), jend=con.end(); j!=jend; ++j){ + CutInfo* cut = *j; + + if(cut->getKlass() != RowsDeletedKlass){ + if(!cut->reconstructed()){ + Assert(!cut->reconstructed()); + tryCut(nid, *cut); + } + } + + if(cut->proven()){ + proven.push_back(cut); + } + } + return proven; +} + +// std::vector<const CutInfo*> ApproxGLPK::getValidCuts(const std::set<const NodeLog*>& nodes){ +// // assume selected has been applied +// std::vector<const CutInfo*> proven; +// std::set<const NodeLog*>::const_iterator i, iend; +// for(i = nodes.begin(), iend=nodes.end(); i!=iend; ++i){ +// const NodeLog* nl = *i; +// getValidCuts(*nl, proven); +// } + +// return proven; +// } + +ArithVar ApproxGLPK::getBranchVar(const NodeLog& con) const{ + int br_var = con.branchVariable(); + return getArithVarFromStructural(br_var); +} + +// Node ApproxGLPK::downBranchLiteral(const NodeLog& con) const{ +// int br_var = con.branchVariable(); +// ArithVar v = getArithVarFromStructural(br_var); +// if(v != ARITHVAR_SENTINEL){ +// if(d_vars.isIntegerInput(v) && d_vars.hasNode(v)){ +// Node var = d_vars.asNode(v); +// double br_val = con.branchValue(); +// Rational val = estimateWithCFE(br_val); +// if(!val.isIntegral()){ +// NodeManager* nm = NodeManager::currentNM(); +// Node ineq = nm->mkNode(kind::LEQ, var, mkRationalNode(val)); +// return Rewriter::rewrite(ineq); +// } +// } +// } +// return Node::null(); +// } + +// std::vector<const NodeLog*> ApproxGLPK::getBranches(){ +// std::vector<const NodeLog*> branches; +// for(TreeLog::const_iterator i = d_log.begin(), iend=d_log.end(); i!=iend;++i){ +// const NodeLog& con = (*i).second; +// if(con.isBranch()){ +// branches.push_back(&con); +// } +// } +// return branches; +// } + +MipResult ApproxGLPK::solveMIP(bool activelyLog){ Assert(d_solvedRelaxation); // Explicitly disable presolving // We need the basis thus the presolver must be off! // This is default, but this is just being cautious. + AuxInfo aux; + aux.pivotLimit = d_pivotLimit; + aux.branchLimit = d_branchLimit; + aux.branchDepth = d_maxDepth; + aux.tl = &d_log; + aux.term = MipUnknown; + + d_log.reset(d_rootRowIds); + if(activelyLog){ + d_log.makeActive(); + }else{ + d_log.makeInactive(); + } + glp_iocp parm; glp_init_iocp(&parm); parm.presolve = GLP_OFF; @@ -785,36 +1802,1361 @@ ApproximateSimplex::ApproxResult ApproxGLPK::solveMIP(){ parm.gmi_cuts = GLP_ON; parm.mir_cuts = GLP_ON; parm.cov_cuts = GLP_ON; - parm.cb_func = stopAtBingoOrPivotLimit; - parm.cb_info = &d_pivotLimit; + parm.cb_func = glpkCallback; + parm.cb_info = &aux; parm.msg_lev = GLP_MSG_OFF; if(s_verbosity >= 1){ parm.msg_lev = GLP_MSG_ALL; } - int res = glp_intopt(d_prob, &parm); + + glp_erase_prob(d_mipProb); + glp_copy_prob(d_mipProb, d_realProb, GLP_OFF); + + int res = glp_intopt(d_mipProb, &parm); + + Debug("approx::solveMIP") << "res "<<res<<" aux.term "<< aux.term << endl; switch(res){ case 0: case GLP_ESTOP: { - int status = glp_mip_status(d_prob); + int status = glp_mip_status(d_mipProb); + Debug("approx::") << "status " << status << endl; switch(status){ case GLP_OPT: case GLP_FEAS: d_solvedMIP = true; - return ApproxSat; + Debug("approx::") << "bingo here!" << endl; + return MipBingo; case GLP_NOFEAS: d_solvedMIP = true; - return ApproxUnsat; + return MipClosed; default: - return ApproxError; + if(aux.term == MipBingo){ + d_solvedMIP = true; + Debug("approx::") << "bingo here?" << endl; + } + return aux.term; } } default: - return ApproxError; + return MipUnknown; + } +} + + + +// Node explainSet(const set<ConstraintP>& inp){ +// Assert(!inp.empty()); +// NodeBuilder<> nb(kind::AND); +// set<ConstraintP>::const_iterator iter, end; +// for(iter = inp.begin(), end = inp.end(); iter != end; ++iter){ +// const ConstraintP c = *iter; +// Assert(c != NullConstraint); +// c->explainForConflict(nb); +// } +// Node ret = safeConstructNary(nb); +// Node rew = Rewriter::rewrite(ret); +// if(rew.getNumChildren() < ret.getNumChildren()){ +// //Debug("approx::") << "explainSet " << ret << " " << rew << endl; +// } +// return rew; +// } + +DeltaRational sumConstraints(const DenseMap<Rational>& xs, const DenseMap<ConstraintP>& cs, bool* anyinf){ + if(anyinf != NULL){ + *anyinf = false; + } + + DeltaRational beta(0); + DenseMap<Rational>::const_iterator iter, end; + iter = xs.begin(); + end = xs.end(); + + Debug("approx::sumConstraints") << "sumConstraints"; + for(; iter != end; ++iter){ + ArithVar x = *iter; + const Rational& psi = xs[x]; + ConstraintP c = cs[x]; + Assert(c != NullConstraint); + + const DeltaRational& bound = c->getValue(); + beta += bound * psi; + Debug("approx::sumConstraints") << " +("<<bound << "*" << psi <<")"; + if(anyinf != NULL ){ + *anyinf = *anyinf || !bound.infinitesimalIsZero(); + } + } + Debug("approx::sumConstraints") << "= " << beta << endl; + + return beta; +} + +// remove fixed variables from the vector +void removeFixed(const ArithVariables& vars, DenseVector& dv, set<ConstraintP>& exp){ + DenseMap<Rational>& vec = dv.lhs; + Rational& removed = dv.rhs; + vector<ArithVar> equal; + DenseMap<Rational>::const_iterator vec_iter, vec_end; + vec_iter = vec.begin(), vec_end = vec.end(); + for(; vec_iter != vec_end; ++vec_iter){ + ArithVar x = *vec_iter; + if(vars.boundsAreEqual(x)){ + equal.push_back(x); + } + } + vector<ArithVar>::const_iterator equal_iter, equal_end; + equal_iter = equal.begin(), equal_end = equal.end(); + for(; equal_iter != equal_end; ++equal_iter){ + ArithVar x = *equal_iter; + Assert(vars.boundsAreEqual(x)); + const DeltaRational& lb = vars.getLowerBound(x); + Assert(lb.infinitesimalIsZero()); + removed -= (vec[x]) * lb.getNoninfinitesimalPart(); + + vec.remove(x); + + std::pair<ConstraintP, ConstraintP> p = vars.explainEqualBounds(x); + exp.insert(p.first); + Debug("removeFixed") << "remove fixed " << p.first << endl; + if(p.second != NullConstraint){ + exp.insert(p.second); + Debug("removeFixed") << "remove fixed " << p.second << endl; + } + } +} +void removeZeroes(DenseMap<Rational>& v){ + // Remove Slack variables + vector<ArithVar> zeroes; + DenseMap<Rational>::const_iterator i, iend; + for(i = v.begin(), iend = v.end(); i != iend; ++i){ + ArithVar x = *i; + if(v[x].isZero()){ + zeroes.push_back(x); + } + } + + vector<ArithVar>::const_iterator j, jend; + for(j = zeroes.begin(), jend = zeroes.end(); j != jend; ++j){ + ArithVar x = *j; + v.remove(x); + } +} +void removeZeroes(DenseVector& v){ + removeZeroes(v.lhs); +} + +void removeAuxillaryVariables(const ArithVariables& vars, DenseMap<Rational>& vec){ + // Remove auxillary variables + vector<ArithVar> aux; + DenseMap<Rational>::const_iterator vec_iter, vec_end; + vec_iter = vec.begin(), vec_end = vec.end(); + for(; vec_iter != vec_end; ++vec_iter){ + ArithVar x = *vec_iter; + if(vars.isAuxiliary(x)){ + aux.push_back(x); + } + } + + vector<ArithVar>::const_iterator aux_iter, aux_end; + aux_iter = aux.begin(), aux_end = aux.end(); + for(; aux_iter != aux_end; ++aux_iter){ + ArithVar s = *aux_iter; + Rational& s_coeff = vec.get(s); + Assert(vars.isAuxiliary(s)); + Assert(vars.hasNode(s)); + Node sAsNode = vars.asNode(s); + Polynomial p = Polynomial::parsePolynomial(sAsNode); + for(Polynomial::iterator j = p.begin(), p_end=p.end(); j != p_end; ++j){ + Monomial m = *j; + const Rational& ns_coeff = m.getConstant().getValue(); + Node vl = m.getVarList().getNode(); + ArithVar ns = vars.asArithVar(vl); + Rational prod = s_coeff * ns_coeff; + if(vec.isKey(ns)){ + vec.get(ns) += prod; + }else{ + vec.set(ns, prod); + } + } + s_coeff = Rational(0); // subtract s_coeff * s from vec + } + removeZeroes(vec); +} + +ArithVar ApproxGLPK::_getArithVar(int nid, int M, int ind) const{ + if(ind <= 0){ + return ARITHVAR_SENTINEL; + }else if(ind <= M){ + return getArithVarFromRow(nid, ind); + }else{ + return getArithVarFromStructural(ind - M); + } +} + + +bool ApproxGLPK::guessIsConstructable(const DenseMap<Rational>& guess) const { + // basic variable + // sum g[i] * x_i + DenseMap<Rational> g = guess; + removeAuxillaryVariables(d_vars, g); + + if(Debug.isOn("guessIsConstructable")){ + if(!g.empty()){ + Debug("approx::guessIsConstructable") << "guessIsConstructable failed " << g.size() << endl; + DenseVector::print(Debug("approx::guessIsConstructable"), g); + Debug("approx::guessIsConstructable") << endl; + } + } + return g.empty(); +} + +bool ApproxGLPK::loadToBound(int nid, int M, int len, int* inds, int* statuses, DenseMap<ConstraintP>& toBound) const{ + for(int i = 1; i <= len; ++i){ + int status = statuses[i]; + int ind = inds[i]; + ArithVar v = _getArithVar(nid, M, ind); + ConstraintP c = NullConstraint; + if(v == ARITHVAR_SENTINEL){ return true; } + + switch(status){ + case GLP_NL: + c = d_vars.getLowerBoundConstraint(v); + break; + case GLP_NU: + case GLP_NS: // upper bound sufficies for fixed variables + c = d_vars.getUpperBoundConstraint(v); + break; + case GLP_NF: + default: + return true; + } + if(c == NullConstraint){ + Debug("approx::") << "couldn't find " << v << " @ " << nid << endl; + return true; + } + Assert(c != NullConstraint); + toBound.set(v, c); } + return false; } +bool ApproxGLPK::checkCutOnPad(int nid, const CutInfo& cut) const{ + + Debug("approx::checkCutOnPad") << "checkCutOnPad(" << nid <<", " << cut.getId() <<")"<<endl; + + const DenseMap<Rational>& constructedLhs = d_pad.d_cut.lhs; + const Rational& constructedRhs = d_pad.d_cut.rhs; + hash_set<ArithVar> visited; + + if(constructedLhs.empty()){ + Debug("approx::checkCutOnPad") << "its empty?" <<endl; + return true; + } + if(cut.getKind() != d_pad.d_cutKind) { + Debug("approx::checkCutOnPad") << "rel doesn't match" << endl; + return true; + } + + const PrimitiveVec& cv = cut.getCutVector(); + for(int i = 1; i <= cv.len; ++i){ + int ind = cv.inds[i]; // this is always a structural variable + double coeff = cv.coeffs[i]; + + + + if(!d_colToArithVar.isKey(ind)){ return true; } + ArithVar x = d_colToArithVar[ind]; + //if(x == ARITHVAR_SENTINEL){ return true; } + visited.insert(x); + + + if(!constructedLhs.isKey(x)){ + if(Debug.isOn("approx::checkCutOnPad")){ + Debug("approx::checkCutOnPad") << " didn't find key for " << x << std::endl; + cut.print(Debug("approx::checkCutOnPad")); + Debug("approx::checkCutOnPad") << endl; + d_pad.d_cut.print(Debug("approx::checkCutOnPad")); + Debug("approx::checkCutOnPad") << endl; + } + return true; + } + + const Rational& onConstructed = constructedLhs[x]; + + Debug("approx::checkCutOnPad") << ind << " " << coeff << " " << endl; + Debug("approx::checkCutOnPad") << " " << x << " " << onConstructed << endl; + + if(!roughlyEqual(coeff, onConstructed.getDouble())){ + Debug("approx::checkCutOnPad") << "coeff failure" << endl; + return true; + } + } + if(visited.size() != constructedLhs.size()){ + Debug("approx::checkCutOnPad") << "size mismatch" << endl; + return true; + } + + + if(!roughlyEqual(cut.getRhs(), constructedRhs.getDouble())){ + Debug("approx::checkCutOnPad") + << "norm rhs is off " << cut.getRhs() << " " << constructedRhs << endl; + return true; + } + return false; +} + + + +bool ApproxGLPK::attemptBranchCut(int nid, const BranchCutInfo& br_cut){ + d_pad.clear(); + + const PrimitiveVec& cut_vec = br_cut.getCutVector(); + int structural = cut_vec.inds[1]; + Assert(roughlyEqual(cut_vec.coeffs[1], +1.0)); + + ArithVar x = getArithVarFromStructural(structural); + d_pad.d_failure = (x == ARITHVAR_SENTINEL); + if(d_pad.d_failure){ return true; } + + Kind brKind = br_cut.getKind(); + + d_pad.d_failure = (brKind != kind::LEQ && brKind != kind::GEQ); + if(d_pad.d_failure){ return true; } + + d_pad.d_cutKind = brKind; + d_pad.d_cut.lhs.set(x, Rational(1)); + + Rational& rhs = d_pad.d_cut.rhs; + rhs = estimateWithCFE(Rational::fromDouble(br_cut.getRhs()), Integer(1)); + d_pad.d_failure = !rhs.isIntegral(); + if(d_pad.d_failure) { return true; } + + d_pad.d_failure = checkCutOnPad(nid, br_cut); + if(d_pad.d_failure){ return true; } + + return false; +} + +bool ApproxGLPK::attemptGmi(int nid, const GmiInfo& gmi){ + d_pad.clear(); + + d_pad.d_cutKind = kind::GEQ; + + int M = gmi.getMAtCreation(); + ArithVar b = _getArithVar(nid, M, gmi.basic); + d_pad.d_failure = (b == ARITHVAR_SENTINEL); + if(d_pad.d_failure){ return true; } + + d_pad.d_failure = !d_vars.isIntegerInput(b); + if(d_pad.d_failure){ return true; } + + d_pad.d_basic = b; + + + const PrimitiveVec& tab = gmi.tab_row; + d_pad.d_failure = attemptConstructTableRow(nid, M, tab); + if(d_pad.d_failure){ return true; } + + int* statuses = gmi.tab_statuses; + DenseMap<ConstraintP>& toBound = d_pad.d_toBound; + d_pad.d_failure = loadToBound(nid, M, tab.len, tab.inds, statuses, toBound); + if(d_pad.d_failure){ return true; } + + d_pad.d_failure = constructGmiCut(); + if(d_pad.d_failure){ return true; } + + d_pad.d_failure = checkCutOnPad(nid, gmi); + if(d_pad.d_failure){ return true; } + + return false; +} + +bool ApproxGLPK::applyCMIRRule(int nid, const MirInfo& mir){ + + const DenseMap<Rational>& compRanges = d_pad.d_compRanges; + + DenseMap<Rational>& alpha = d_pad.d_alpha.lhs; + Rational& b = d_pad.d_alpha.rhs; + + Rational delta = estimateWithCFE(mir.delta); + d_pad.d_failure = (delta.sgn() <= 0); + if(d_pad.d_failure){ return true; } + + Debug("approx::mir") << "applyCMIRRule() " << delta << " " << mir.delta << endl; + + DenseMap<Rational>::const_iterator iter, iend; + iter = alpha.begin(), iend = alpha.end(); + for(; iter != iend; ++iter){ + ArithVar v = *iter; + const Rational& curr = alpha[v]; + Rational next = curr / delta; + if(compRanges.isKey(v)){ + b -= curr * compRanges[v]; + alpha.set(v, - next); + }else{ + alpha.set(v, next); + } + } + b = b / delta; + + Rational roundB = (b + Rational(1,2)).floor(); + d_pad.d_failure = (b - roundB).abs() < Rational(1,90); + // intensionally more generous than glpk here + if(d_pad.d_failure){ return true; } + + Rational one(1); + Rational fb = b.floor_frac(); + Rational one_sub_fb = one - fb; + Rational gamma = (one / one_sub_fb); + + DenseMap<Rational>& cut = d_pad.d_cut.lhs; + Rational& beta = d_pad.d_cut.rhs; + + iter = alpha.begin(), iend = alpha.end(); + for(; iter != iend; ++iter){ + ArithVar v = *iter; + const Rational& a_j = alpha[v]; + if(d_vars.isIntegerInput(v)){ + Rational floor_aj = a_j.floor(); + Rational frac_aj = a_j.floor_frac(); + if(frac_aj <= fb){ + cut.set(v, floor_aj); + }else{ + Rational tmp = ((frac_aj - fb) / one_sub_fb); + cut.set(v, floor_aj + tmp); + } + }else{ + cut.set(v, a_j * gamma); + } + } + beta = b.floor(); + + iter = cut.begin(), iend = cut.end(); + for(; iter != iend; ++iter){ + ArithVar v = *iter; + if(compRanges.isKey(v)){ + Rational neg = - cut[v]; + beta += neg * compRanges[v]; + cut.set(v, neg); + } + } + + return false; +} + +bool ApproxGLPK::attemptMir(int nid, const MirInfo& mir){ + d_pad.clear(); + + d_pad.d_cutKind = kind::LEQ; + + // virtual bounds must be done before slacks + d_pad.d_failure = loadVirtualBoundsIntoPad(nid, mir); + if(d_pad.d_failure){ return true; } + + d_pad.d_failure = loadSlacksIntoPad(nid, mir); + if(d_pad.d_failure){ return true; } + + + d_pad.d_failure = loadRowSumIntoAgg(nid, mir.getMAtCreation(), mir.row_sum); + if(d_pad.d_failure){ return true; } + + removeFixed(d_vars, d_pad.d_agg, d_pad.d_explanation); + + d_pad.d_failure = buildModifiedRow(nid, mir); + if(d_pad.d_failure){ return true; } + + d_pad.d_failure = constructMixedKnapsack(); + if(d_pad.d_failure){ return true; } + + d_pad.d_failure = makeRangeForComplemented(nid, mir); + if(d_pad.d_failure){ return true; } + + d_pad.d_failure = applyCMIRRule(nid, mir); + if(d_pad.d_failure){ return true; } + + d_pad.d_failure = replaceSlacksOnCuts(); + if(d_pad.d_failure){ return true; } + + removeAuxillaryVariables(d_vars, d_pad.d_cut.lhs); + + d_pad.d_failure = checkCutOnPad(nid, mir); + if(d_pad.d_failure){ return true; } + + return false; + //return makeCutNodes(nid, mir); +} + +bool ApproxGLPK::loadVB(int nid, int M, int j, int ri, bool wantUb, VirtualBound& tmp){ + if(ri <= 0) { return true; } + + static int instance = 0; + ++instance; + Debug("glpk::loadVB") << "loadVB() " << instance << endl; + + ArithVar rowVar = _getArithVar(nid, M, ri); + ArithVar contVar = _getArithVar(nid, M, j); + if(rowVar == ARITHVAR_SENTINEL){ return true; } + if(contVar == ARITHVAR_SENTINEL){ return true; } + + if(!d_vars.isAuxiliary(rowVar)){ return true; } + // is integer is correct here + if(d_vars.isInteger(contVar)){ return true; } + + ConstraintP lb = d_vars.getLowerBoundConstraint(rowVar); + ConstraintP ub = d_vars.getUpperBoundConstraint(rowVar); + + if(lb != NullConstraint && ub != NullConstraint){ return true; } + + ConstraintP rcon = lb == NullConstraint ? ub : lb; + if(rcon == NullConstraint) { return true; } + + if(!rcon->getValue().isZero()){ return true; } + + if(!d_vars.hasNode(rowVar)){ return true; } + Polynomial p = Polynomial::parsePolynomial(d_vars.asNode(rowVar)); + if(p.size() != 2) { return false; } + + Monomial first = p.getHead(), second = p.getTail().getHead(); + Rational c1 = first.getConstant().getValue(); + Rational c2 = second.getConstant().getValue(); + Node nx1 = first.getVarList().getNode(); + Node nx2 = second.getVarList().getNode(); + + if(!d_vars.hasArithVar(nx1)) { return true; } + if(!d_vars.hasArithVar(nx2)) { return true; } + ArithVar x1 = d_vars.asArithVar(nx1), x2 = d_vars.asArithVar(nx2); + + Assert(x1 != x2); + Assert(!c1.isZero()); + Assert(!c2.isZero()); + + Debug("glpk::loadVB") + << " lb " << lb + << " ub " << ub + << " rcon " << rcon + << " x1 " << x1 + << " x2 " << x2 + << " c1 " << c1 + << " c2 " << c2 << endl; + + ArithVar iv = (x1 == contVar) ? x2 : x1; + Rational& cc = (x1 == contVar) ? c1 : c2; + Rational& ic = (x1 == contVar) ? c2 : c1; + + Debug("glpk::loadVB") + << " cv " << contVar + << " cc " << cc + << " iv " << iv + << " c2 " << ic << endl; + + if(!d_vars.isIntegerInput(iv)){ return true; } + // cc * cv + ic * iv <= 0 or + // cc * cv + ic * iv <= 0 + + if(rcon == ub){ // multiply by -1 + cc = -cc; ic = - ic; + } + Debug("glpk::loadVB") << " cv " << contVar + << " cc " << cc + << " iv " << iv + << " c2 " << ic << endl; + + // cc * cv + ic * iv >= 0 + // cc * cv >= -ic * iv + // if cc < 0: + // cv <= -ic/cc * iv + // elif cc > 0: + // cv >= -ic/cc * iv + Assert(!cc.isZero()); + Rational d = -ic/cc; + Debug("glpk::loadVB") << d << " " << cc.sgn() << endl; + bool nowUb = cc.sgn() < 0; + if(wantUb != nowUb) { return true; } + + Kind rel = wantUb ? kind::LEQ : kind::GEQ; + + tmp = VirtualBound(contVar, rel, d, iv, rcon); + return false; +} + +bool ApproxGLPK::loadVirtualBoundsIntoPad(int nid, const MirInfo& mir){ + Assert(mir.vlbRows != NULL); + Assert(mir.vubRows != NULL); + + int N = mir.getN(); + int M = mir.getMAtCreation(); + + // Load the virtual bounds first + VirtualBound tmp; + for(int j=1; j <= N+M; ++j){ + if(!loadVB(nid, M, j, mir.vlbRows[j], false, tmp)){ + if(d_pad.d_vlb.isKey(tmp.x)){ return true; } + d_pad.d_vlb.set(tmp.x, tmp); + }else if(mir.vlbRows[j] > 0){ + Debug("approx::mir") << "expected vlb to work" << endl; + } + if(!loadVB(nid, M, j, mir.vubRows[j], true, tmp)){ + if(d_pad.d_vub.isKey(tmp.x)){ return true; } + d_pad.d_vub.set(tmp.x, tmp); + }else if(mir.vubRows[j] > 0){ + Debug("approx::mir") << "expected vub to work" << endl; + } + } + return false; +} + +bool ApproxGLPK::loadSlacksIntoPad(int nid, const MirInfo& mir){ + Assert(mir.vlbRows != NULL); + Assert(mir.vubRows != NULL); + + int N = mir.getN(); + int M = mir.getMAtCreation(); + + bool useVB; + // Load the virtual bounds first + SlackReplace rep; + bool lb; + ConstraintP b; + Debug("approx::mir") << "loadSlacksIntoPad(): N="<<N<<", M=" << M << std::endl; + for(int j=1; j <= N+M; ++j){ + ArithVar v = _getArithVar(nid, M, j); + if(v == ARITHVAR_SENTINEL){ + Debug("approx::mir") << " for: " << j << " no variable" << endl; + continue; + } + rep = SlackUndef; + char sub = mir.subst[j]; + switch(sub){ + case 'L': + case 'U': + lb = (sub == 'L'); + useVB = lb ? (mir.vlbRows[j] > 0) : (mir.vubRows[j] > 0); + if(useVB){ + if(lb ? d_pad.d_vlb.isKey(v) : d_pad.d_vub.isKey(v)){ + rep = lb ? SlackVLB : SlackVUB; + } + }else{ + b = lb ? d_vars.getLowerBoundConstraint(v) + : d_vars.getUpperBoundConstraint(v); + if(b != NullConstraint){ + if(b->getValue().infinitesimalIsZero()){ + rep = lb ? SlackLB : SlackUB; + } + } + } + + Debug("approx::mir") << " for: " << j << ", " << v; + Debug("approx::mir") << " " << ((rep != SlackUndef) ? "succ" : "fail") << " "; + Debug("approx::mir") << sub << " " << rep << " " << mir.vlbRows[j] << " " << mir.vubRows[j] + << endl; + if(rep != SlackUndef){ + d_pad.d_slacks.set(v,rep); + } + break; + case '?': + continue; + default: + Debug("approx::mir") << " for: " << j << " got subst " << (int)sub << endl; + continue; + } + } + return false; +} + +bool ApproxGLPK::replaceSlacksOnCuts(){ + vector<ArithVar> virtualVars; + + DenseMap<Rational>& cut = d_pad.d_cut.lhs; + Rational& cutRhs = d_pad.d_cut.rhs; + + DenseMap<Rational>::const_iterator iter, iend; + iter = cut.begin(), iend = cut.end(); + for(; iter != iend; ++iter){ + ArithVar x = *iter; + SlackReplace rep = d_pad.d_slacks[x]; + if(d_vars.isIntegerInput(x)){ + Assert(rep == SlackLB || rep == SlackUB); + Rational& a = cut.get(x); + + const DeltaRational& bound = (rep == SlackLB) ? + d_vars.getLowerBound(x) : d_vars.getUpperBound(x); + Assert(bound.infinitesimalIsZero()); + Rational prod = a * bound.getNoninfinitesimalPart(); + if(rep == SlackLB){ + cutRhs += prod; + }else{ + cutRhs -= prod; + a = -a; + } + }else if(rep == SlackVLB){ + virtualVars.push_back(d_pad.d_vlb[x].y); + }else if(rep == SlackVUB){ + virtualVars.push_back(d_pad.d_vub[x].y); + } + } + + for(size_t i = 0; i < virtualVars.size(); ++i){ + ArithVar x = virtualVars[i]; + if(!cut.isKey(x)){ + cut.set(x, Rational(0)); + } + } + + iter = cut.begin(), iend = cut.end(); + for(; iter != iend; ++iter){ + ArithVar x = *iter; + if(!d_vars.isIntegerInput(x)){ + SlackReplace rep = d_pad.d_slacks[x]; + Rational& a = cut.get(x); + switch(rep){ + case SlackLB: + { + const DeltaRational& bound = d_vars.getLowerBound(x); + Assert(bound.infinitesimalIsZero()); + cutRhs += a * bound.getNoninfinitesimalPart(); + } + break; + case SlackUB: + { + const DeltaRational& bound = d_vars.getUpperBound(x); + Assert(bound.infinitesimalIsZero()); + cutRhs -= a * bound.getNoninfinitesimalPart(); + a = -a; + } + break; + case SlackVLB: + case SlackVUB: + { + bool lb = (rep == SlackVLB); + const VirtualBound& vb = lb ? + d_pad.d_vlb[x] : d_pad.d_vub[x]; + ArithVar y = vb.y; + Assert(vb.x == x); + Assert(cut.isKey(y)); + Rational& ycoeff = cut.get(y); + if(lb){ + ycoeff -= a * vb.d; + }else{ + ycoeff += a * vb.d; + a = -a; + } + } + break; + default: + return true; + } + } + } + removeZeroes(cut); + return false; +} + +bool ApproxGLPK::loadRowSumIntoAgg(int nid, int M, const PrimitiveVec& row_sum){ + DenseMap<Rational>& lhs = d_pad.d_agg.lhs; + d_pad.d_agg.rhs = Rational(0); + + int len = row_sum.len; + for(int i = 1; i <= len; ++i){ + int aux_ind = row_sum.inds[i]; // auxillary index + double coeff = row_sum.coeffs[i]; + ArithVar x = _getArithVar(nid, M, aux_ind); + if(x == ARITHVAR_SENTINEL){ return true; } + Rational c = estimateWithCFE(coeff); + if(lhs.isKey(x)){ + lhs.get(x) -= c; + }else{ + lhs.set(x, -c); + } + } + + Debug("approx::mir") << "beg loadRowSumIntoAgg() 1" << endl; + if(Debug.isOn("approx::mir")) { DenseVector::print(Debug("approx::mir"), lhs); } + removeAuxillaryVariables(d_vars, lhs); + Debug("approx::mir") << "end loadRowSumIntoAgg() 1" << endl; + + if(Debug.isOn("approx::mir")){ + Debug("approx::mir") << "loadRowSumIntoAgg() 2" << endl; + DenseVector::print(Debug("approx::mir"), lhs); + Debug("approx::mir") << "end loadRowSumIntoAgg() 2" << endl; + } + + for(int i = 1; i <= len; ++i){ + int aux_ind = row_sum.inds[i]; // auxillary index + double coeff = row_sum.coeffs[i]; + ArithVar x = _getArithVar(nid, M, aux_ind); + Assert(x != ARITHVAR_SENTINEL); + Rational c = estimateWithCFE(coeff); + Assert(!lhs.isKey(x)); + lhs.set(x, c); + } + + if(Debug.isOn("approx::mir")){ + Debug("approx::mir") << "loadRowSumIntoAgg() 2" << endl; + DenseVector::print(Debug("approx::mir"), lhs); + Debug("approx::mir") << "end loadRowSumIntoAgg() 3" << endl; + } + return false; +} + +bool ApproxGLPK::buildModifiedRow(int nid, const MirInfo& mir){ + const DenseMap<Rational>& agg = d_pad.d_agg.lhs; + const Rational& aggRhs = d_pad.d_agg.rhs; + DenseMap<Rational>& mod = d_pad.d_mod.lhs; + Rational& modRhs = d_pad.d_mod.rhs; + + Debug("approx::mir") + << "buildModifiedRow()" + << " |agg|=" << d_pad.d_agg.lhs.size() + << " |mod|=" << d_pad.d_mod.lhs.size() + << " |slacks|=" << d_pad.d_slacks.size() + << " |vlb|=" << d_pad.d_vub.size() + << " |vub|=" << d_pad.d_vlb.size() << endl; + + mod.addAll(agg); + modRhs = aggRhs; + DenseMap<Rational>::const_iterator iter, iend; + for(iter = agg.begin(), iend = agg.end(); iter != iend; ++iter){ + ArithVar x = *iter; + const Rational& c = mod[x]; + if(!d_pad.d_slacks.isKey(x)){ + Debug("approx::mir") << "missed x: " << x << endl; + return true; + } + SlackReplace rep = d_pad.d_slacks[x]; + switch(rep){ + case SlackLB: // skip for now + case SlackUB: + break; + case SlackVLB: /* x[k] = lb[k] * x[kk] + x'[k] */ + case SlackVUB: /* x[k] = ub[k] * x[kk] - x'[k] */ + { + Assert(!d_vars.isIntegerInput(x)); + bool ub = (rep == SlackVUB); + const VirtualBound& vb = + ub ? d_pad.d_vub[x] : d_pad.d_vlb[x]; + Assert(vb.x == x); + ArithVar y = vb.y; + Rational prod = c * vb.d; + if(mod.isKey(y)){ + mod.get(x) += prod; + }else{ + mod.set(y, prod); + } + if(ub){ + mod.set(x, -c); + } + Assert(vb.c != NullConstraint); + d_pad.d_explanation.insert(vb.c); + } + break; + default: + return true; + } + } + removeZeroes(mod); /* if something cancelled we don't want it in the explanation */ + for(iter = mod.begin(), iend = mod.end(); iter != iend; ++iter){ + ArithVar x = *iter; + if(!d_pad.d_slacks.isKey(x)){ return true; } + + SlackReplace rep = d_pad.d_slacks[x]; + switch(rep){ + case SlackLB: /* x = lb + x' */ + case SlackUB: /* x = ub - x' */ + { + bool ub = (rep == SlackUB); + ConstraintP b = ub ? d_vars.getUpperBoundConstraint(x): + d_vars.getLowerBoundConstraint(x); + + Assert(b != NullConstraint); + Assert(b->getValue().infinitesimalIsZero()); + const Rational& c = mod.get(x); + modRhs -= c * b->getValue().getNoninfinitesimalPart(); + if(ub){ + mod.set(x, -c); + } + d_pad.d_explanation.insert(b); + } + break; + case SlackVLB: /* handled earlier */ + case SlackVUB: + break; + default: + return true; + } + } + removeZeroes(mod); + return false; +} + +bool ApproxGLPK::makeRangeForComplemented(int nid, const MirInfo& mir){ + DenseMap<Rational>& alpha = d_pad.d_alpha.lhs; + int M = mir.getMAtCreation(); + int N = mir.getN(); + DenseMap<Rational>& compRanges = d_pad.d_compRanges; + + int complemented = 0; + + for(int j = 1; j <= M + N; ++j){ + if(mir.cset[j] != 0){ + complemented++; + ArithVar x = _getArithVar(nid, M, j); + if(!alpha.isKey(x)){ return true; } + if(!d_vars.isIntegerInput(x)){ return true; } + Assert(d_pad.d_slacks.isKey(x)); + Assert(d_pad.d_slacks[x] == SlackLB || d_pad.d_slacks[x] == SlackUB); + + ConstraintP lb = d_vars.getLowerBoundConstraint(x); + ConstraintP ub = d_vars.getUpperBoundConstraint(x); + + if(lb == NullConstraint) { return true; } + if(ub == NullConstraint) { return true; } + + if(!lb->getValue().infinitesimalIsZero()){ + return true; + } + if(!ub->getValue().infinitesimalIsZero()){ + return true; + } + + const Rational& uval = ub->getValue().getNoninfinitesimalPart(); + const Rational& lval = lb->getValue().getNoninfinitesimalPart(); + + d_pad.d_explanation.insert(lb); + d_pad.d_explanation.insert(ub); + + Rational u = uval - lval; + // u is the same for both rep == LP and rep == UB + if(compRanges.isKey(x)) { return true; } + compRanges.set(x,u); + } + } + + Debug("approx::mir") << "makeRangeForComplemented()" << complemented << endl; + return false; +} + + +bool ApproxGLPK::constructMixedKnapsack(){ + const DenseMap<Rational>& mod = d_pad.d_mod.lhs; + const Rational& modRhs = d_pad.d_mod.rhs; + DenseMap<Rational>& alpha = d_pad.d_alpha.lhs; + Rational& beta = d_pad.d_alpha.rhs; + + Assert(alpha.empty()); + beta = modRhs; + + unsigned intVars = 0; + unsigned remain = 0; + unsigned dropped = 0; + DenseMap<Rational>::const_iterator iter, iend; + for(iter = mod.begin(), iend = mod.end(); iter != iend; ++iter){ + ArithVar v = *iter; + const Rational& c = mod[v]; + Assert(!c.isZero()); + if(d_vars.isIntegerInput(v)){ + intVars++; + alpha.set(v, c); + }else if(c.sgn() < 0){ + remain++; + alpha.set(v, c); + }else{ + dropped++; + } + } + + Debug("approx::mir") + << "constructMixedKnapsack() " + <<" dropped " << dropped + <<" remain " << remain + <<" intVars " << intVars + << endl; + return intVars == 0; // if this is 0 we have failed +} + +bool ApproxGLPK::attemptConstructTableRow(int nid, int M, const PrimitiveVec& vec){ + bool failed = guessCoefficientsConstructTableRow(nid, M, vec); + if(failed){ + failed = gaussianElimConstructTableRow(nid, M, vec); + } + + return failed; +} + +bool ApproxGLPK::gaussianElimConstructTableRow(int nid, int M, const PrimitiveVec& vec){ + TimerStat::CodeTimer codeTimer(d_stats.d_gaussianElimConstructTime); + ++d_stats.d_gaussianElimConstruct; + + ArithVar basic = d_pad.d_basic; + DenseMap<Rational>& tab = d_pad.d_tabRow.lhs; + tab.purge(); + d_pad.d_tabRow.rhs = Rational(0); + Assert(basic != ARITHVAR_SENTINEL); + Assert(tab.empty()); + Assert(d_pad.d_tabRow.rhs.isZero()); + + if(d_vars.isAuxiliary(basic)) { return true; } + + if(Debug.isOn("gaussianElimConstructTableRow")){ + Debug("gaussianElimConstructTableRow") << "1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + vec.print(Debug("gaussianElimConstructTableRow")); + Debug("gaussianElimConstructTableRow") << "match " << basic << "("<<d_vars.asNode(basic)<<")"<<endl; + } + + set<ArithVar> onrow; + for(int i = 1; i <= vec.len; ++i){ + int ind = vec.inds[i]; + ArithVar var = _getArithVar(nid, M, ind); + if(var == ARITHVAR_SENTINEL){ + Debug("gaussianElimConstructTableRow") << "couldn't find" << ind << " " << M << " " << nid << endl; + return true; + } + onrow.insert(var); + } + + + Debug("gaussianElimConstructTableRow") << "2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + + Matrix<Rational> A; + A.increaseSizeTo(d_vars.getNumberOfVariables()); + std::vector< std::pair<RowIndex, ArithVar> > rows; + set<ArithVar>::const_iterator i, iend; + // load the rows for auxiliary variables into A + for(i=onrow.begin(), iend=onrow.end(); i!=iend; ++i){ + ArithVar v = *i; + if(d_vars.isAuxiliary(v)){ + Assert(d_vars.hasNode(v)); + + vector<Rational> coeffs; + vector<ArithVar> vars; + + coeffs.push_back(Rational(-1)); + vars.push_back(v); + + Node n = d_vars.asNode(v); + Polynomial p = Polynomial::parsePolynomial(n); + Polynomial::iterator j = p.begin(), jend=p.end(); + for(j=p.begin(), jend=p.end(); j!=jend; ++j){ + Monomial m = *j; + if(m.isConstant()) { return true; } + VarList vl = m.getVarList(); + if(!d_vars.hasArithVar(vl.getNode())){ return true; } + ArithVar x = d_vars.asArithVar(vl.getNode()); + const Rational& q = m.getConstant().getValue(); + coeffs.push_back(q); vars.push_back(x); + } + RowIndex rid = A.addRow(coeffs, vars); + rows.push_back(make_pair(rid, ARITHVAR_SENTINEL)); + } + } + Debug("gaussianElimConstructTableRow") << "3 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + + for(size_t i=0; i < rows.size(); ++i){ + RowIndex rid = rows[i].first; + Assert(rows[i].second == ARITHVAR_SENTINEL); + + // substitute previous rows + for(size_t j=0; j < i; j++){ + RowIndex prevRow = rows[j].first; + ArithVar other = rows[j].second; + Assert(other != ARITHVAR_SENTINEL); + const Matrix<Rational>::Entry& e = A.findEntry(rid, other); + if(!e.blank()){ + // r_p : 0 = -1 * other + sum a_i x_i + // rid : 0 = e * other + sum b_i x_i + // rid += e * r_p + // : 0 = 0 * other + ... + Assert(!e.getCoefficient().isZero()); + + Rational cp = e.getCoefficient(); + Debug("gaussianElimConstructTableRow") + << "on " << rid << " subst " << cp << "*" << prevRow << " " << other << endl; + A.rowPlusRowTimesConstant(rid, prevRow, cp); + } + } + if(Debug.isOn("gaussianElimConstructTableRow")){ + A.printMatrix(Debug("gaussianElimConstructTableRow")); + } + + // solve the row for anything other than non-basics + bool solveForBasic = (i + 1 == rows.size()); + Rational q; + ArithVar s = ARITHVAR_SENTINEL; + Matrix<Rational>::RowIterator k = A.getRow(rid).begin(); + Matrix<Rational>::RowIterator k_end = A.getRow(rid).end(); + for(; k != k_end; ++k){ + const Matrix<Rational>::Entry& e = *k; + ArithVar colVar = e.getColVar(); + bool selectColVar = false; + if(colVar == basic){ + selectColVar = solveForBasic; + }else if(onrow.find(colVar) == onrow.end()) { + selectColVar = true; + } + if(selectColVar){ + s = colVar; + q = e.getCoefficient(); + } + } + if(s == ARITHVAR_SENTINEL || q.isZero()){ + Debug("gaussianElimConstructTableRow") << "3 fail gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + return true; + }else{ + // 0 = q * s + sum c_i * x_i + Rational mult = -(q.inverse()); + Debug("gaussianElimConstructTableRow") << "selecting " << s << " : " << mult << endl; + Debug("gaussianElimConstructTableRow") << "selecting " << rid << " " << s << endl; + //cout << "selecting " << s << " : complexity " << mult.complexity() << " " << mult << endl; + //cout << "selecting " << rid << " " << s << endl; + A.multiplyRowByConstant(rid, mult); + rows[i].second = s; + } + } + Debug("gaussianElimConstructTableRow") << "4 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + + if(rows.empty()) { + Debug("gaussianElimConstructTableRow") << "4 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + return true; + } + RowIndex rid_last = rows.back().first; + ArithVar rid_var = rows.back().second; + if(rid_var != basic){ + Debug("gaussianElimConstructTableRow") << "4 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + return true; + } + + Assert(tab.empty()); + + Matrix<Rational>::RowIterator k = A.getRow(rid_last).begin(); + Matrix<Rational>::RowIterator k_end = A.getRow(rid_last).end(); + for(; k != k_end; ++k){ + const Matrix<Rational>::Entry& e = *k; + tab.set(e.getColVar(), e.getCoefficient()); + } + Debug("gaussianElimConstructTableRow") << "5 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + if(!tab.isKey(basic)){ + Debug("gaussianElimConstructTableRow") << "5 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + return true; + } + if(tab[basic] != Rational(-1)){ + Debug("gaussianElimConstructTableRow") << "5 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + return true; + } + + tab.remove(basic); + Debug("gaussianElimConstructTableRow") << "6 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + + if(vec.len < 0 ){ + Debug("gaussianElimConstructTableRow") << "6 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + return true; + } + if(tab.size() != ((unsigned)vec.len) ) { + Debug("gaussianElimConstructTableRow") << "6 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<< tab.size() << " " << vec.len << endl; + return true; + } + + Debug("gaussianElimConstructTableRow") << "7 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + + for(int i = 1; i <= vec.len; ++i){ + int ind = vec.inds[i]; + double coeff = vec.coeffs[i]; + ArithVar var = _getArithVar(nid, M, ind); + Assert(var != ARITHVAR_SENTINEL); + if(!tab.isKey(var)){ + Debug("gaussianElimConstructTableRow") << "7 fail 1 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")"<<endl; + return true; + } + + double est = tab[var].getDouble(); + + if(!ApproximateSimplex::roughlyEqual(coeff, est)){ + Debug("gaussianElimConstructTableRow") << "7 fail 2 gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")" + << " boink on " << ind << " " << var << " " << est <<endl; + return true; + } + Debug("gaussianElimConstructTableRow") << var << " cfe " << coeff << endl; + } + + Debug("gaussianElimConstructTableRow") + << "gaussianElimConstructTableRow("<<nid <<", "<< basic<< ")" + << " superduper" << endl; + + return false; +} +bool ApproxGLPK::guessCoefficientsConstructTableRow(int nid, int M, const PrimitiveVec& vec){ + for(size_t i=0; i < d_denomGuesses.size(); ++i){ + const Integer& D = d_denomGuesses[i]; + if(!guessCoefficientsConstructTableRow(nid, M, vec, D)){ + d_stats.d_averageGuesses.addEntry(i+1); + Debug("approx::gmi") << "guesseditat " << i << " D=" << D << endl; + return false; + } + } + return true; +} +bool ApproxGLPK::guessCoefficientsConstructTableRow(int nid, int M, const PrimitiveVec& vec, const Integer& D){ + ArithVar basic = d_pad.d_basic; + DenseMap<Rational>& tab = d_pad.d_tabRow.lhs; + tab.purge(); + d_pad.d_tabRow.rhs = Rational(0); + Assert(basic != ARITHVAR_SENTINEL); + Assert(tab.empty()); + Assert(d_pad.d_tabRow.rhs.isZero()); + + if(Debug.isOn("guessCoefficientsConstructTableRow")){ + Debug("guessCoefficientsConstructTableRow") << "attemptConstructTableRow("<<nid <<", "<< basic<<",...," << D<< ")"<<endl; + vec.print(Debug("guessCoefficientsConstructTableRow")); + Debug("guessCoefficientsConstructTableRow") << "match " << basic << "("<<d_vars.asNode(basic)<<")"<<endl; + } + + tab.set(basic, Rational(-1)); + for(int i = 1; i <= vec.len; ++i){ + int ind = vec.inds[i]; + double coeff = vec.coeffs[i]; + ArithVar var = _getArithVar(nid, M, ind); + if(var == ARITHVAR_SENTINEL){ + Debug("guessCoefficientsConstructTableRow") << "couldn't find" << ind << " " << M << " " << nid << endl; + return true; + } + Debug("guessCoefficientsConstructTableRow") << "match " << ind << "," << var << "("<<d_vars.asNode(var)<<")"<<endl; + + Rational cfe = estimateWithCFE(coeff, D); + tab.set(var, cfe); + Debug("guessCoefficientsConstructTableRow") << var << " cfe " << cfe << endl; + } + if(!guessIsConstructable(tab)){ + Debug("guessCoefficientsConstructTableRow") << "failed to construct with " << D << endl; + return true; + } + tab.remove(basic); + return false; +} + +/* Maps an ArithVar to either an upper/lower bound */ +bool ApproxGLPK::constructGmiCut(){ + const DenseMap<Rational>& tabRow = d_pad.d_tabRow.lhs; + const DenseMap<ConstraintP>& toBound = d_pad.d_toBound; + DenseMap<Rational>& cut = d_pad.d_cut.lhs; + std::set<ConstraintP>& explanation = d_pad.d_explanation; + Rational& rhs = d_pad.d_cut.rhs; + + DenseMap<Rational>::const_iterator iter, end; + Assert(cut.empty()); + + // compute beta for a "fake" assignment + bool anyInf; + DeltaRational dbeta = sumConstraints(tabRow, toBound, &anyInf); + const Rational& beta = dbeta.getNoninfinitesimalPart(); + Debug("approx::gmi") << dbeta << endl; + if(anyInf || beta.isIntegral()){ return true; } + + Rational one = Rational(1); + Rational fbeta = beta.floor_frac(); + rhs = fbeta; + Assert(fbeta.sgn() > 0); + Assert(fbeta < one); + Rational one_sub_fbeta = one - fbeta; + for(iter = tabRow.begin(), end = tabRow.end(); iter != end; ++iter){ + ArithVar x = *iter; + const Rational& psi = tabRow[x]; + ConstraintP c = toBound[x]; + const Rational& bound = c->getValue().getNoninfinitesimalPart(); + if(d_vars.boundsAreEqual(x)){ + // do not add a coefficient + // implictly substitute the variable w/ its constraint + std::pair<ConstraintP, ConstraintP> exp = d_vars.explainEqualBounds(x); + explanation.insert(exp.first); + if(exp.second != NullConstraint){ + explanation.insert(exp.second); + } + }else if(d_vars.isIntegerInput(x) && psi.isIntegral()){ + // do not add a coefficient + // nothing to explain + Debug("approx::gmi") << "skipping " << x << endl; + }else{ + explanation.insert(c); + Rational phi; + Rational alpha = (c->isUpperBound() ? psi : -psi); + + // x - ub <= 0 and lb - x <= 0 + if(d_vars.isIntegerInput(x)){ + Assert(!psi.isIntegral()); + // alpha = slack_sgn * psi + Rational falpha = alpha.floor_frac(); + Assert(falpha.sgn() > 0); + Assert(falpha < one); + phi = (falpha <= fbeta) ? + falpha : ((fbeta / one_sub_fbeta) * (one - falpha)); + }else{ + phi = (alpha >= 0) ? + alpha : ((fbeta / one_sub_fbeta) * (- alpha)); + } + Assert(phi.sgn() != 0); + if(c->isUpperBound()){ + cut.set(x, -phi); + rhs -= phi * bound; + }else{ + cut.set(x, phi); + rhs += phi * bound; + } + } + } + if(Debug.isOn("approx::gmi")){ + Debug("approx::gmi") << "pre removeSlackVariables"; + d_pad.d_cut.print(Debug("approx::gmi")); + Debug("approx::gmi") << endl; + } + removeAuxillaryVariables(d_vars, cut); + + if(Debug.isOn("approx::gmi")){ + Debug("approx::gmi") << "post removeAuxillaryVariables"; + d_pad.d_cut.print(Debug("approx::gmi")); + Debug("approx::gmi") << endl; + } + removeFixed(d_vars, d_pad.d_cut, explanation); + + if(Debug.isOn("approx::gmi")){ + Debug("approx::gmi") << "post removeFixed"; + d_pad.d_cut.print(Debug("approx::gmi")); + Debug("approx::gmi") << endl; + } + return false; +} + +void ApproxGLPK::tryCut(int nid, CutInfo& cut) throw (RationalFromDoubleException){ + Assert(!cut.reconstructed()); + Assert(cut.getKlass() != RowsDeletedKlass); + bool failure = false; + switch(cut.getKlass()){ + case GmiCutKlass: + failure = attemptGmi(nid, static_cast<const GmiInfo&>(cut)); + break; + case MirCutKlass: + failure = attemptMir(nid, static_cast<const MirInfo&>(cut)); + break; + case BranchCutKlass: + failure = attemptBranchCut(nid, dynamic_cast<const BranchCutInfo&>(cut)); + break; + default: + break; + } + Assert(failure == d_pad.d_failure); + + if(!failure){ + // move the pad to the cut + cut.setReconstruction(d_pad.d_cut); + + if(cut.getKlass() != BranchCutKlass){ + std::set<ConstraintP>& exp = d_pad.d_explanation; + ConstraintCPVec asvec(exp.begin(), exp.end()); + cut.swapExplanation(asvec); + } + }else{ + Debug("approx") << "failure " << cut.getKlass() << endl; + } +} + + }/* CVC4::theory::arith namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/arith/approx_simplex.h b/src/theory/arith/approx_simplex.h index b32fdef2d..15996fef8 100644 --- a/src/theory/arith/approx_simplex.h +++ b/src/theory/arith/approx_simplex.h @@ -22,7 +22,9 @@ #include "util/statistics_registry.h" #include "theory/arith/arithvar.h" -#include "theory/arith/linear_equality.h" +#include "util/rational.h" +#include "theory/arith/delta_rational.h" +//#include "theory/arith/linear_equality.h" #include "util/dense_map.h" #include <vector> @@ -30,10 +32,81 @@ namespace CVC4 { namespace theory { namespace arith { +enum LinResult { + LinUnknown, /* Unknown error */ + LinFeasible, /* Relaxation is feasible */ + LinInfeasible, /* Relaxation is infeasible/all integer branches closed */ + LinExhausted +}; + +enum MipResult { + MipUnknown, /* Unknown error */ + MipBingo, /* Integer feasible */ + MipClosed, /* All integer branches closed */ + BranchesExhausted, /* Exhausted number of branches */ + PivotsExhauasted, /* Exhausted number of pivots */ + ExecExhausted /* Exhausted total operations */ +}; +std::ostream& operator<<(std::ostream& out, MipResult res); + +class ApproximateStatistics { +public: + // IntStat d_relaxCalls; + // IntStat d_relaxUnknowns; + // IntStat d_relaxFeasible; + // IntStat d_relaxInfeasible; + // IntStat d_relaxPivotsExhausted; + + // IntStat d_mipCalls; + // IntStat d_mipUnknowns; + // IntStat d_mipBingo; + // IntStat d_mipClosed; + // IntStat d_mipBranchesExhausted; + // IntStat d_mipPivotsExhausted; + // IntStat d_mipExecExhausted; + + + // IntStat d_gmiGen; + // IntStat d_gmiReplay; + // IntStat d_mipGen; + // IntStat d_mipReplay; + + IntStat d_branchMaxDepth; + IntStat d_branchesMaxOnAVar; + + TimerStat d_gaussianElimConstructTime; + IntStat d_gaussianElimConstruct; + AverageStat d_averageGuesses; + + ApproximateStatistics(); + ~ApproximateStatistics(); +}; + + +class NodeLog; +class TreeLog; +class ArithVariables; +class CutInfo; +class RowsDeleted; class ApproximateSimplex{ protected: + const ArithVariables& d_vars; + TreeLog& d_log; + ApproximateStatistics& d_stats; + int d_pivotLimit; + /* the maximum pivots allowed in a query. */ + + int d_branchLimit; + /* maximum branches allowed on a variable */ + + int d_maxDepth; + /* maxmimum branching depth allowed.*/ + + static Integer s_defaultMaxDenom; + /* Default denominator for diophatine approximation. + * 2^{26}*/ public: @@ -43,31 +116,48 @@ public: * If glpk is enabled, return a subclass that can do something. * If glpk is disabled, return a subclass that does nothing. */ - static ApproximateSimplex* mkApproximateSimplexSolver(const ArithVariables& vars); - ApproximateSimplex(); + static ApproximateSimplex* mkApproximateSimplexSolver(const ArithVariables& vars, TreeLog& l, ApproximateStatistics& s); + ApproximateSimplex(const ArithVariables& v, TreeLog& l, ApproximateStatistics& s); virtual ~ApproximateSimplex(){} + /* the maximum pivots allowed in a query. */ + void setPivotLimit(int pl); + + /* maximum branches allowed on a variable */ + void setBranchOnVariableLimit(int bl); + + /* maximum branches allowed on a variable */ + void setBranchingDepth(int bd); + /** A result is either sat, unsat or unknown.*/ - enum ApproxResult {ApproxError, ApproxSat, ApproxUnsat}; + //enum ApproxResult {ApproxError, ApproxSat, ApproxUnsat}; struct Solution { DenseSet newBasis; DenseMap<DeltaRational> newValues; Solution() : newBasis(), newValues(){} }; - /** Sets a deterministic effort limit. */ - void setPivotLimit(int pivotLimit); + virtual ArithVar getBranchVar(const NodeLog& nl) const = 0; + //virtual void mapRowId(int nid, int ind, ArithVar v) = 0; + //virtual void applyRowsDeleted(int nid, const RowsDeleted& rd) = 0; /** Sets a maximization criteria for the approximate solver.*/ virtual void setOptCoeffs(const ArithRatPairVec& ref) = 0; virtual ArithRatPairVec heuristicOptCoeffs() const = 0; - virtual ApproxResult solveRelaxation() = 0; - virtual Solution extractRelaxation() const = 0; + virtual LinResult solveRelaxation() = 0; + virtual Solution extractRelaxation() const throw(RationalFromDoubleException) = 0; + + virtual MipResult solveMIP(bool activelyLog) = 0; + virtual Solution extractMIP() const throw(RationalFromDoubleException) = 0; + + virtual std::vector<const CutInfo*> getValidCuts(const NodeLog& node) throw(RationalFromDoubleException) = 0; + //virtual std::vector<const NodeLog*> getBranches() = 0; - virtual ApproxResult solveMIP() = 0; - virtual Solution extractMIP() const = 0; + //virtual Node downBranchLiteral(const NodeLog& con) const = 0; + + virtual void tryCut(int nid, CutInfo& cut) throw(RationalFromDoubleException) = 0; /** UTILITIES FOR DEALING WITH ESTIMATES */ @@ -82,7 +172,8 @@ public: * cuts off the estimate once the value is approximately zero. * This is designed for removing rounding artifacts. */ - static Rational estimateWithCFE(double d); + static Rational estimateWithCFE(double d) throw(RationalFromDoubleException); + static Rational estimateWithCFE(double d, const Integer& D) throw(RationalFromDoubleException); /** * Converts a rational to a continued fraction expansion representation @@ -95,7 +186,10 @@ public: static Rational cfeToRational(const std::vector<Integer>& exp); /** Estimates a rational as a continued fraction expansion.*/ - static Rational estimateWithCFE(const Rational& q, int depth); + //static Rational estimateWithCFE(const Rational& q, int depth); + static Rational estimateWithCFE(const Rational& q, const Integer& K); + + virtual double sumInfeasibilities(bool mip) const = 0; };/* class ApproximateSimplex */ diff --git a/src/theory/arith/arith_ite_utils.cpp b/src/theory/arith/arith_ite_utils.cpp new file mode 100644 index 000000000..4f182fb69 --- /dev/null +++ b/src/theory/arith/arith_ite_utils.cpp @@ -0,0 +1,436 @@ +#include "theory/arith/arith_ite_utils.h" +#include "theory/arith/normal_form.h" +#include "theory/arith/arith_utilities.h" +#include "theory/ite_utilities.h" +#include "theory/theory_model.h" +#include "theory/rewriter.h" +#include "theory/substitutions.h" +#include <ostream> + +using namespace std; + +namespace CVC4 { +namespace theory { +namespace arith { + +Node ArithIteUtils::applyReduceVariablesInItes(Node n){ + NodeBuilder<> nb(n.getKind()); + if(n.getMetaKind() == kind::metakind::PARAMETERIZED) { + nb << (n.getOperator()); + } + for(Node::iterator it = n.begin(), end = n.end(); it != end; ++it){ + nb << reduceVariablesInItes(*it); + } + Node res = nb; + return res; +} + +Node ArithIteUtils::reduceVariablesInItes(Node n){ + using namespace CVC4::kind; + if(d_reduceVar.find(n) != d_reduceVar.end()){ + Node res = d_reduceVar[n]; + return res.isNull() ? n : res; + } + + switch(n.getKind()){ + case ITE:{ + Node c = n[0], t = n[1], e = n[2]; + if(n.getType().isReal()){ + Node rc = reduceVariablesInItes(c); + Node rt = reduceVariablesInItes(t); + Node re = reduceVariablesInItes(e); + + Node vt = d_varParts[t]; + Node ve = d_varParts[e]; + Node vpite = (vt == ve) ? vt : Node::null(); + + if(vpite.isNull()){ + Node rite = rc.iteNode(rt, re); + // do not apply + d_reduceVar[n] = rite; + d_constants[n] = mkRationalNode(Rational(0)); + d_varParts[n] = rite; // treat the ite as a variable + return rite; + }else{ + NodeManager* nm = NodeManager::currentNM(); + Node constantite = rc.iteNode(d_constants[t], d_constants[e]); + Node sum = nm->mkNode(kind::PLUS, vpite, constantite); + d_reduceVar[n] = sum; + d_constants[n] = constantite; + d_varParts[n] = vpite; + return sum; + } + }else{ // non-arith ite + if(!d_contains.containsTermITE(n)){ + // don't bother adding to d_reduceVar + return n; + }else{ + Node newIte = applyReduceVariablesInItes(n); + d_reduceVar[n] = (n == newIte) ? Node::null(): newIte; + return newIte; + } + } + }break; + default: + if(n.getType().isReal() && Polynomial::isMember(n)){ + Node newn = Node::null(); + if(!d_contains.containsTermITE(n)){ + newn = n; + }else if(n.getNumChildren() > 0){ + newn = applyReduceVariablesInItes(n); + newn = Rewriter::rewrite(newn); + Assert(Polynomial::isMember(newn)); + }else{ + newn = n; + } + + Polynomial p = Polynomial::parsePolynomial(newn); + if(p.isConstant()){ + d_constants[n] = newn; + d_varParts[n] = mkRationalNode(Rational(0)); + // don't bother adding to d_reduceVar + return newn; + }else if(!p.containsConstant()){ + d_constants[n] = mkRationalNode(Rational(0)); + d_varParts[n] = newn; + d_reduceVar[n] = p.getNode(); + return p.getNode(); + }else{ + Monomial mc = p.getHead(); + d_constants[n] = mc.getConstant().getNode(); + d_varParts[n] = p.getTail().getNode(); + d_reduceVar[n] = newn; + return newn; + } + }else{ + if(!d_contains.containsTermITE(n)){ + return n; + } + if(n.getNumChildren() > 0){ + Node res = applyReduceVariablesInItes(n); + d_reduceVar[n] = res; + return res; + }else{ + return n; + } + } + break; + } + Unreachable(); + return Node::null(); +} + +ArithIteUtils::ArithIteUtils(ContainsTermITEVisitor& contains, + context::Context* uc, + TheoryModel* model) + : d_contains(contains) + , d_subs(NULL) + , d_model(model) + , d_one(1) + , d_subcount(uc, 0) + , d_skolems(uc) + , d_implies() + , d_skolemsAdded() + , d_orBinEqs() +{ + d_subs = new SubstitutionMap(uc); +} + +ArithIteUtils::~ArithIteUtils(){ + delete d_subs; + d_subs = NULL; +} + +void ArithIteUtils::clear(){ + d_reduceVar.clear(); + d_constants.clear(); + d_varParts.clear(); +} + +const Integer& ArithIteUtils::gcdIte(Node n){ + if(d_gcds.find(n) != d_gcds.end()){ + return d_gcds[n]; + } + if(n.getKind() == kind::CONST_RATIONAL){ + const Rational& q = n.getConst<Rational>(); + if(q.isIntegral()){ + d_gcds[n] = q.getNumerator(); + return d_gcds[n]; + }else{ + return d_one; + } + }else if(n.getKind() == kind::ITE && n.getType().isReal()){ + const Integer& tgcd = gcdIte(n[1]); + if(tgcd.isOne()){ + d_gcds[n] = d_one; + return d_one; + }else{ + const Integer& egcd = gcdIte(n[2]); + Integer ite_gcd = tgcd.gcd(egcd); + d_gcds[n] = ite_gcd; + return d_gcds[n]; + } + } + return d_one; +} + +Node ArithIteUtils::reduceIteConstantIteByGCD_rec(Node n, const Rational& q){ + if(n.isConst()){ + Assert(n.getKind() == kind::CONST_RATIONAL); + return mkRationalNode(n.getConst<Rational>() * q); + }else{ + Assert(n.getKind() == kind::ITE); + Assert(n.getType().isInteger()); + Node rc = reduceConstantIteByGCD(n[0]); + Node rt = reduceIteConstantIteByGCD_rec(n[1], q); + Node re = reduceIteConstantIteByGCD_rec(n[2], q); + return rc.iteNode(rt, re); + } +} + +Node ArithIteUtils::reduceIteConstantIteByGCD(Node n){ + Assert(n.getKind() == kind::ITE); + Assert(n.getType().isReal()); + const Integer& gcd = gcdIte(n); + if(gcd.isOne()){ + Node newIte = reduceConstantIteByGCD(n[0]).iteNode(n[1],n[2]); + d_reduceGcd[n] = newIte; + return newIte; + }else if(gcd.isZero()){ + Node zeroNode = mkRationalNode(Rational(0)); + d_reduceGcd[n] = zeroNode; + return zeroNode; + }else{ + Rational divBy(Integer(1), gcd); + Node redite = reduceIteConstantIteByGCD_rec(n, divBy); + Node gcdNode = mkRationalNode(Rational(gcd)); + Node multIte = NodeManager::currentNM()->mkNode(kind::MULT, gcdNode, redite); + d_reduceGcd[n] = multIte; + return multIte; + } +} + +Node ArithIteUtils::reduceConstantIteByGCD(Node n){ + if(d_reduceGcd.find(n) != d_reduceGcd.end()){ + return d_reduceGcd[n]; + } + if(n.getKind() == kind::ITE && n.getType().isReal()){ + return reduceIteConstantIteByGCD(n); + } + + if(n.getNumChildren() > 0){ + NodeBuilder<> nb(n.getKind()); + if(n.getMetaKind() == kind::metakind::PARAMETERIZED) { + nb << (n.getOperator()); + } + bool anychange = false; + for(Node::iterator it = n.begin(), end = n.end(); it != end; ++it){ + Node child = *it; + Node redchild = reduceConstantIteByGCD(child); + anychange = anychange || (child != redchild); + nb << redchild; + } + if(anychange){ + Node res = nb; + d_reduceGcd[n] = res; + return res; + }else{ + d_reduceGcd[n] = n; + return n; + } + }else{ + return n; + } +} + +unsigned ArithIteUtils::getSubCount() const{ + return d_subcount; +} + +void ArithIteUtils::addSubstitution(TNode f, TNode t){ + Debug("arith::ite") << "adding " << f << " -> " << t << endl; + d_subcount = d_subcount + 1; + d_subs->addSubstitution(f, t); + d_model->addSubstitution(f, t); +} + +Node ArithIteUtils::applySubstitutions(TNode f){ + return d_subs->apply(f); +} + +Node ArithIteUtils::selectForCmp(Node n) const{ + if(n.getKind() == kind::ITE){ + if(d_skolems.find(n[0]) != d_skolems.end()){ + return selectForCmp(n[1]); + } + } + return n; +} + +void ArithIteUtils::learnSubstitutions(const std::vector<Node>& assertions){ + for(size_t i=0, N=assertions.size(); i < N; ++i){ + collectAssertions(assertions[i]); + } + bool solvedSomething; + do{ + solvedSomething = false; + size_t readPos = 0, writePos = 0, N = d_orBinEqs.size(); + for(; readPos < N; readPos++){ + Node curr = d_orBinEqs[readPos]; + bool solved = solveBinOr(curr); + if(solved){ + solvedSomething = true; + }else{ + // didn't solve, push back + d_orBinEqs[writePos] = curr; + writePos++; + } + } + Assert(writePos <= N); + d_orBinEqs.resize(writePos); + }while(solvedSomething); + + for(size_t i = 0, N=d_skolemsAdded.size(); i<N; ++i){ + Node sk = d_skolemsAdded[i]; + Node to = d_skolems[sk]; + if(!to.isNull()){ + Node fp = applySubstitutions(to); + addSubstitution(sk, fp); + } + } + d_implies.clear(); + d_skolemsAdded.clear(); + d_orBinEqs.clear(); +} + +void ArithIteUtils::addImplications(Node x, Node y){ + // (or x y) + // (=> (not x) y) + // (=> (not y) x) + + Node xneg = x.negate(); + Node yneg = y.negate(); + d_implies[xneg].insert(y); + d_implies[yneg].insert(x); +} + +void ArithIteUtils::collectAssertions(TNode assertion){ + if(assertion.getKind() == kind::OR){ + if(assertion.getNumChildren() == 2){ + TNode left = assertion[0], right = assertion[1]; + addImplications(left, right); + if(left.getKind() == kind::EQUAL && right.getKind() == kind::EQUAL){ + if(left[0].getType().isInteger() && right[0].getType().isInteger()){ + d_orBinEqs.push_back(assertion); + } + } + } + }else if(assertion.getKind() == kind::AND){ + for(unsigned i=0, N=assertion.getNumChildren(); i < N; ++i){ + collectAssertions(assertion[i]); + } + } +} + +Node ArithIteUtils::findIteCnd(TNode tb, TNode fb) const{ + Node negtb = tb.negate(); + Node negfb = fb.negate(); + ImpMap::const_iterator ti = d_implies.find(negtb); + ImpMap::const_iterator fi = d_implies.find(negfb); + + if(ti != d_implies.end() && fi != d_implies.end()){ + const std::set<Node>& negtimp = ti->second; + const std::set<Node>& negfimp = fi->second; + + // (or (not x) y) + // (or x z) + // (or y z) + // --- + // (ite x y z) return x + // --- + // (not y) => (not x) + // (not z) => x + std::set<Node>::const_iterator ci = negtimp.begin(), cend = negtimp.end(); + for(; ci != cend; ci++){ + Node impliedByNotTB = *ci; + Node impliedByNotTBNeg = impliedByNotTB.negate(); + if(negfimp.find(impliedByNotTBNeg) != negfimp.end()){ + return impliedByNotTBNeg; // implies tb + } + } + } + + return Node::null(); +} + +bool ArithIteUtils::solveBinOr(TNode binor){ + Assert(binor.getKind() == kind::OR); + Assert(binor.getNumChildren() == 2); + Assert(binor[0].getKind() == kind::EQUAL); + Assert(binor[1].getKind() == kind::EQUAL); + + Node n = applySubstitutions(binor); + Assert(n.getKind() == kind::OR); + Assert(binor.getNumChildren() == 2); + TNode l = n[0]; + TNode r = n[1]; + + Assert(l.getKind() == kind::EQUAL); + Assert(r.getKind() == kind::EQUAL); + + Debug("arith::ite") << "bin or " << n << endl; + + bool lArithEq = l.getKind() == kind::EQUAL && l[0].getType().isInteger(); + bool rArithEq = r.getKind() == kind::EQUAL && r[0].getType().isInteger(); + + if(lArithEq && rArithEq){ + TNode sel = Node::null(); + TNode otherL = Node::null(); + TNode otherR = Node::null(); + if(l[0] == r[0]) { + sel = l[0]; otherL = l[1]; otherR = r[1]; + }else if(l[0] == r[1]){ + sel = l[0]; otherL = l[1]; otherR = r[0]; + }else if(l[1] == r[0]){ + sel = l[1]; otherL = l[0]; otherR = r[1]; + }else if(l[1] == r[1]){ + sel = l[1]; otherL = l[0]; otherR = r[0]; + } + Debug("arith::ite") << "selected " << sel << endl; + if(sel.isVar() && sel.getKind() != kind::SKOLEM){ + + Debug("arith::ite") << "others l:" << otherL << " r " << otherR << endl; + Node useForCmpL = selectForCmp(otherL); + Node useForCmpR = selectForCmp(otherR); + + Assert(Polynomial::isMember(sel)); + Assert(Polynomial::isMember(useForCmpL)); + Assert(Polynomial::isMember(useForCmpR)); + Polynomial lside = Polynomial::parsePolynomial( useForCmpL ); + Polynomial rside = Polynomial::parsePolynomial( useForCmpR ); + Polynomial diff = lside-rside; + + Debug("arith::ite") << "diff: " << diff.getNode() << endl; + if(diff.isConstant()){ + // a: (sel = otherL) or (sel = otherR), otherL-otherR = c + + NodeManager* nm = NodeManager::currentNM(); + + Node cnd = findIteCnd(binor[0], binor[1]); + + Node sk = nm->mkSkolem("deor$$", nm->booleanType()); + Node ite = sk.iteNode(otherL, otherR); + d_skolems.insert(sk, cnd); + d_skolemsAdded.push_back(sk); + addSubstitution(sel, ite); + return true; + } + } + } + return false; +} + + +}/* CVC4::theory::arith namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/arith/arith_ite_utils.h b/src/theory/arith/arith_ite_utils.h new file mode 100644 index 000000000..5bdcd52da --- /dev/null +++ b/src/theory/arith/arith_ite_utils.h @@ -0,0 +1,100 @@ + + + + + +// Pass 1: label the ite as (constant) or (+ constant variable) + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__ARITH__ARITH_ITE_UTILS_H +#define __CVC4__THEORY__ARITH__ARITH_ITE_UTILS_H + +#include "expr/node.h" +#include <ext/hash_map> +#include <ext/hash_set> +#include "context/cdo.h" +#include "context/cdtrail_hashmap.h" + +namespace CVC4 { +namespace theory { +class ContainsTermITEVisitor; +class SubstitutionMap; +class TheoryModel; + +namespace arith { + +class ArithIteUtils { + ContainsTermITEVisitor& d_contains; + SubstitutionMap* d_subs; + TheoryModel* d_model; + + typedef std::hash_map<Node, Node, NodeHashFunction> NodeMap; + // cache for reduce vars + NodeMap d_reduceVar; // if reduceVars[n].isNull(), treat reduceVars[n] == n + + // reduceVars[n] = d_constants[n] + d_varParts[n] + NodeMap d_constants; // d_constants[n] is a constant ite tree + NodeMap d_varParts; // d_varParts[n] is a polynomial + + + NodeMap d_reduceGcd; + typedef std::hash_map<Node, Integer, NodeHashFunction> NodeIntegerMap; + NodeIntegerMap d_gcds; + + Integer d_one; + + context::CDO<unsigned> d_subcount; + typedef context::CDTrailHashMap<Node, Node, NodeHashFunction> CDNodeMap; + CDNodeMap d_skolems; + + typedef std::map<Node, std::set<Node> > ImpMap; + ImpMap d_implies; + + std::vector<Node> d_skolemsAdded; + + std::vector<Node> d_orBinEqs; + +public: + ArithIteUtils(ContainsTermITEVisitor& contains, + context::Context* userContext, + TheoryModel* model); + ~ArithIteUtils(); + + //(ite ?v_2 ?v_1 (ite ?v_3 (- ?v_1 128) (- ?v_1 256))) + + /** removes common sums variables sums from term ites. */ + Node reduceVariablesInItes(Node n); + + Node reduceConstantIteByGCD(Node n); + + void clear(); + + Node applySubstitutions(TNode f); + unsigned getSubCount() const; + + void learnSubstitutions(const std::vector<Node>& assertions); + +private: + /* applies this to all children of n and constructs the result */ + Node applyReduceVariablesInItes(Node n); + + const Integer& gcdIte(Node n); + Node reduceIteConstantIteByGCD_rec(Node n, const Rational& q); + Node reduceIteConstantIteByGCD(Node n); + + void addSubstitution(TNode f, TNode t); + Node selectForCmp(Node n) const; + + void collectAssertions(TNode assertion); + void addImplications(Node x, Node y); + Node findIteCnd(TNode tb, TNode fb) const; + bool solveBinOr(TNode binor); + +}; /* class ArithIteUtils */ + +}/* CVC4::theory::arith namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__ARITH__ARITH_ITE_UTILS_H */ diff --git a/src/theory/arith/arith_rewriter.cpp b/src/theory/arith/arith_rewriter.cpp index e1cab0356..5aa904aed 100644 --- a/src/theory/arith/arith_rewriter.cpp +++ b/src/theory/arith/arith_rewriter.cpp @@ -222,36 +222,94 @@ RewriteResponse ArithRewriter::postRewriteTerm(TNode t){ RewriteResponse ArithRewriter::preRewriteMult(TNode t){ Assert(t.getKind()== kind::MULT); - // Rewrite multiplications with a 0 argument and to 0 - Rational qZero(0); + if(t.getNumChildren() == 2){ + if(t[0].getKind() == kind::CONST_RATIONAL + && t[0].getConst<Rational>().isOne()){ + return RewriteResponse(REWRITE_DONE, t[1]); + } + if(t[1].getKind() == kind::CONST_RATIONAL + && t[1].getConst<Rational>().isOne()){ + return RewriteResponse(REWRITE_DONE, t[0]); + } + } + // Rewrite multiplications with a 0 argument and to 0 for(TNode::iterator i = t.begin(); i != t.end(); ++i) { if((*i).getKind() == kind::CONST_RATIONAL) { - if((*i).getConst<Rational>() == qZero) { - return RewriteResponse(REWRITE_DONE, mkRationalNode(qZero)); + if((*i).getConst<Rational>().isZero()) { + TNode zero = (*i); + return RewriteResponse(REWRITE_DONE, zero); } } } return RewriteResponse(REWRITE_DONE, t); } + +static bool canFlatten(Kind k, TNode t){ + for(TNode::iterator i = t.begin(); i != t.end(); ++i) { + TNode child = *i; + if(child.getKind() == k){ + return true; + } + } + return false; +} + +static void flatten(std::vector<TNode>& pb, Kind k, TNode t){ + if(t.getKind() == k){ + for(TNode::iterator i = t.begin(); i != t.end(); ++i) { + TNode child = *i; + if(child.getKind() == k){ + flatten(pb, k, child); + }else{ + pb.push_back(child); + } + } + }else{ + pb.push_back(t); + } +} + +static Node flatten(Kind k, TNode t){ + std::vector<TNode> pb; + flatten(pb, k, t); + Assert(pb.size() >= 2); + return NodeManager::currentNM()->mkNode(k, pb); +} + RewriteResponse ArithRewriter::preRewritePlus(TNode t){ Assert(t.getKind()== kind::PLUS); - return RewriteResponse(REWRITE_DONE, t); + if(canFlatten(kind::PLUS, t)){ + return RewriteResponse(REWRITE_DONE, flatten(kind::PLUS, t)); + }else{ + return RewriteResponse(REWRITE_DONE, t); + } } RewriteResponse ArithRewriter::postRewritePlus(TNode t){ Assert(t.getKind()== kind::PLUS); - Polynomial res = Polynomial::mkZero(); + std::vector<Monomial> monomials; + std::vector<Polynomial> polynomials; for(TNode::iterator i = t.begin(), end = t.end(); i != end; ++i){ - Node curr = *i; - Polynomial currPoly = Polynomial::parsePolynomial(curr); + TNode curr = *i; + if(Monomial::isMember(curr)){ + monomials.push_back(Monomial::parseMonomial(curr)); + }else{ + polynomials.push_back(Polynomial::parsePolynomial(curr)); + } + } - res = res + currPoly; + if(!monomials.empty()){ + Monomial::sort(monomials); + Monomial::combineAdjacentMonomials(monomials); + polynomials.push_back(Polynomial::mkPolynomial(monomials)); } + Polynomial res = Polynomial::sumPolynomials(polynomials); + return RewriteResponse(REWRITE_DONE, res.getNode()); } diff --git a/src/theory/arith/arith_static_learner.cpp b/src/theory/arith/arith_static_learner.cpp index 8f6f75295..b9260c906 100644 --- a/src/theory/arith/arith_static_learner.cpp +++ b/src/theory/arith/arith_static_learner.cpp @@ -21,6 +21,8 @@ #include "theory/arith/arith_static_learner.h" #include "theory/arith/options.h" +#include "theory/arith/normal_form.h" + #include "expr/expr.h" #include "expr/convenience_node_builders.h" @@ -38,7 +40,11 @@ ArithStaticLearner::ArithStaticLearner(context::Context* userContext) : d_minMap(userContext), d_maxMap(userContext), d_statistics() -{} +{ +} + +ArithStaticLearner::~ArithStaticLearner(){ +} ArithStaticLearner::Statistics::Statistics(): d_iteMinMaxApplications("theory::arith::iteMinMaxApplications", 0), @@ -98,14 +104,17 @@ void ArithStaticLearner::staticLearning(TNode n, NodeBuilder<>& learned){ } - - - void ArithStaticLearner::process(TNode n, NodeBuilder<>& learned, const TNodeSet& defTrue){ Debug("arith::static") << "===================== looking at " << n << endl; switch(n.getKind()){ case ITE: + if(n.hasBoundVar()) { + // Unsafe with non-ground ITEs; do nothing + Debug("arith::static") << "(potentially) non-ground ITE, ignoring..." << endl; + break; + } + if(n[0].getKind() != EQUAL && isRelationOperator(n[0].getKind()) ){ iteMinMax(n, learned); @@ -116,49 +125,12 @@ void ArithStaticLearner::process(TNode n, NodeBuilder<>& learned, const TNodeSet iteConstant(n, learned); } break; + case CONST_RATIONAL: // Mark constants as minmax d_minMap.insert(n, n.getConst<Rational>()); d_maxMap.insert(n, n.getConst<Rational>()); break; - case OR: { - // Look for things like "x = 0 OR x = 1" (that are defTrue) and - // turn them into a pseudoboolean. We catch "x >= 0 - if(defTrue.find(n) == defTrue.end() || - n.getNumChildren() != 2 || - n[0].getKind() != EQUAL || - n[1].getKind() != EQUAL) { - break; - } - Node var, c1, c2; - if(n[0][0].isVar() && - n[0][1].isConst()) { - var = n[0][0]; - c1 = n[0][1]; - } else if(n[0][1].isVar() && - n[0][0].isConst()) { - var = n[0][1]; - c1 = n[0][0]; - } else { - break; - } - if(!var.getType().isInteger() || - !c1.getType().isReal()) { - break; - } - if(var == n[1][0]) { - c2 = n[1][1]; - } else if(var == n[1][1]) { - c2 = n[1][0]; - } else { - break; - } - if(!c2.getType().isReal()) { - break; - } - - break; - } default: // Do nothing break; } diff --git a/src/theory/arith/arith_static_learner.h b/src/theory/arith/arith_static_learner.h index d8407eeba..2615cdcd6 100644 --- a/src/theory/arith/arith_static_learner.h +++ b/src/theory/arith/arith_static_learner.h @@ -25,7 +25,6 @@ #include "theory/arith/arith_utilities.h" #include "context/context.h" -#include "context/cdlist.h" #include "context/cdtrail_hashmap.h" #include <set> @@ -45,6 +44,7 @@ private: public: ArithStaticLearner(context::Context* userContext); + ~ArithStaticLearner(); void staticLearning(TNode n, NodeBuilder<>& learned); void addBound(TNode n); diff --git a/src/theory/arith/arith_utilities.h b/src/theory/arith/arith_utilities.h index 11626c1de..98aa43e71 100644 --- a/src/theory/arith/arith_utilities.h +++ b/src/theory/arith/arith_utilities.h @@ -238,6 +238,25 @@ inline Node flattenAnd(Node n){ return NodeManager::currentNM()->mkNode(kind::AND, out); } +inline Node getIdentity(Kind k){ + switch(k){ + case kind::AND: + return NodeManager::currentNM()->mkConst<bool>(true); + case kind::PLUS: + return NodeManager::currentNM()->mkConst(Rational(1)); + default: + Unreachable(); + } +} + +inline Node safeConstructNary(NodeBuilder<>& nb){ + switch(nb.getNumChildren()){ + case 0: return getIdentity(nb.getKind()); + case 1: return nb[0]; + default: return (Node)nb; + } +} + }/* CVC4::theory::arith namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/arith/callbacks.cpp b/src/theory/arith/callbacks.cpp index d4a445b70..c4b64682f 100644 --- a/src/theory/arith/callbacks.cpp +++ b/src/theory/arith/callbacks.cpp @@ -22,6 +22,9 @@ namespace CVC4 { namespace theory { namespace arith { +SetupLiteralCallBack::SetupLiteralCallBack(TheoryArithPrivate& ta) + : d_arith(ta) +{} void SetupLiteralCallBack::operator()(TNode lit){ TNode atom = (lit.getKind() == kind::NOT) ? lit[0] : lit; if(!d_arith.isSetup(atom)){ @@ -29,30 +32,72 @@ void SetupLiteralCallBack::operator()(TNode lit){ } } +DeltaComputeCallback::DeltaComputeCallback(const TheoryArithPrivate& ta) + : d_ta(ta) +{} Rational DeltaComputeCallback::operator()() const{ return d_ta.deltaValueForTotalOrder(); } +TempVarMalloc::TempVarMalloc(TheoryArithPrivate& ta) +: d_ta(ta) +{} ArithVar TempVarMalloc::request(){ Node skolem = mkRealSkolem("tmpVar"); - return d_ta.requestArithVar(skolem, false); + return d_ta.requestArithVar(skolem, false, true); } void TempVarMalloc::release(ArithVar v){ d_ta.releaseArithVar(v); } +BasicVarModelUpdateCallBack::BasicVarModelUpdateCallBack(TheoryArithPrivate& ta) + : d_ta(ta) +{} void BasicVarModelUpdateCallBack::operator()(ArithVar x){ d_ta.signal(x); } -void RaiseConflict::operator()(Node n){ - d_ta.raiseConflict(n); +RaiseConflict::RaiseConflict(TheoryArithPrivate& ta, ConstraintCPVec& buf ) + : d_ta(ta) + , d_construction(buf) +{} + +/* Adds a constraint to the constraint under construction. */ +void RaiseConflict::addConstraint(ConstraintCP c){ + d_construction.push_back(c); +} +/* Turns the vector under construction into a conflict */ +void RaiseConflict::commitConflict(){ + Assert(!d_construction.empty()); + sendConflict(d_construction); + d_construction.clear(); +} + +void RaiseConflict::sendConflict(const ConstraintCPVec& vec){ + d_ta.raiseConflict(vec); +} + +/* If you are not an equality engine, don't use this! */ +void RaiseConflict::blackBoxConflict(Node n){ + d_ta.blackBoxConflict(n); } + +BoundCountingLookup::BoundCountingLookup(TheoryArithPrivate& ta) +: d_ta(ta) +{} + const BoundsInfo& BoundCountingLookup::boundsInfo(ArithVar basic) const{ return d_ta.boundsInfo(basic); } +BoundCounts BoundCountingLookup::atBounds(ArithVar basic) const{ + return boundsInfo(basic).atBounds(); +} +BoundCounts BoundCountingLookup::hasBounds(ArithVar basic) const { + return boundsInfo(basic).hasBounds(); +} + }/* CVC4::theory::arith namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/arith/callbacks.h b/src/theory/arith/callbacks.h index fd9369bf1..c4c79ad75 100644 --- a/src/theory/arith/callbacks.h +++ b/src/theory/arith/callbacks.h @@ -24,6 +24,7 @@ #include "theory/arith/theory_arith_private_forward.h" #include "theory/arith/arithvar.h" #include "theory/arith/bound_counts.h" +#include "theory/arith/constraint_forward.h" namespace CVC4 { namespace theory { @@ -67,7 +68,7 @@ class SetupLiteralCallBack : public TNodeCallBack { private: TheoryArithPrivate& d_arith; public: - SetupLiteralCallBack(TheoryArithPrivate& ta) : d_arith(ta){} + SetupLiteralCallBack(TheoryArithPrivate& ta); void operator()(TNode lit); }; @@ -75,7 +76,7 @@ class DeltaComputeCallback : public RationalCallBack { private: const TheoryArithPrivate& d_ta; public: - DeltaComputeCallback(const TheoryArithPrivate& ta) : d_ta(ta){} + DeltaComputeCallback(const TheoryArithPrivate& ta); Rational operator()() const; }; @@ -83,7 +84,7 @@ class BasicVarModelUpdateCallBack : public ArithVarCallBack{ private: TheoryArithPrivate& d_ta; public: - BasicVarModelUpdateCallBack(TheoryArithPrivate& ta) : d_ta(ta) {} + BasicVarModelUpdateCallBack(TheoryArithPrivate& ta); void operator()(ArithVar x); }; @@ -91,31 +92,37 @@ class TempVarMalloc : public ArithVarMalloc { private: TheoryArithPrivate& d_ta; public: - TempVarMalloc(TheoryArithPrivate& ta) : d_ta(ta) {} + TempVarMalloc(TheoryArithPrivate& ta); ArithVar request(); void release(ArithVar v); }; -class RaiseConflict : public NodeCallBack { +class RaiseConflict { private: TheoryArithPrivate& d_ta; + ConstraintCPVec& d_construction; public: - RaiseConflict(TheoryArithPrivate& ta) : d_ta(ta) {} - void operator()(Node n); + RaiseConflict(TheoryArithPrivate& ta, ConstraintCPVec& d_construction); + + /* Adds a constraint to the constraint under construction. */ + void addConstraint(ConstraintCP c); + /* Turns the vector under construction into a conflict */ + void commitConflict(); + + void sendConflict(const ConstraintCPVec& vec); + + /* If you are not an equality engine, don't use this! */ + void blackBoxConflict(Node n); }; class BoundCountingLookup { private: TheoryArithPrivate& d_ta; public: - BoundCountingLookup(TheoryArithPrivate& ta) : d_ta(ta) {} + BoundCountingLookup(TheoryArithPrivate& ta); const BoundsInfo& boundsInfo(ArithVar basic) const; - BoundCounts atBounds(ArithVar basic) const{ - return boundsInfo(basic).atBounds(); - } - BoundCounts hasBounds(ArithVar basic) const { - return boundsInfo(basic).hasBounds(); - } + BoundCounts atBounds(ArithVar basic) const; + BoundCounts hasBounds(ArithVar basic) const; }; }/* CVC4::theory::arith namespace */ diff --git a/src/theory/arith/congruence_manager.cpp b/src/theory/arith/congruence_manager.cpp index a828b9e7f..d1d11c86e 100644 --- a/src/theory/arith/congruence_manager.cpp +++ b/src/theory/arith/congruence_manager.cpp @@ -65,11 +65,105 @@ ArithCongruenceManager::Statistics::~Statistics(){ StatisticsRegistry::unregisterStat(&d_conflicts); } +ArithCongruenceManager::ArithCongruenceNotify::ArithCongruenceNotify(ArithCongruenceManager& acm) + : d_acm(acm) +{} + +bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerEquality(TNode equality, bool value) { + Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyTriggerEquality(" << equality << ", " << (value ? "true" : "false") << ")" << std::endl; + if (value) { + return d_acm.propagate(equality); + } else { + return d_acm.propagate(equality.notNode()); + } +} +bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerPredicate(TNode predicate, bool value) { + Unreachable(); +} + +bool ArithCongruenceManager::ArithCongruenceNotify::eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value) { + Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyTriggerTermEquality(" << t1 << ", " << t2 << ", " << (value ? "true" : "false") << ")" << std::endl; + if (value) { + return d_acm.propagate(t1.eqNode(t2)); + } else { + return d_acm.propagate(t1.eqNode(t2).notNode()); + } +} +void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyConstantTermMerge(TNode t1, TNode t2) { + Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << std::endl; + if (t1.getKind() == kind::CONST_BOOLEAN) { + d_acm.propagate(t1.iffNode(t2)); + } else { + d_acm.propagate(t1.eqNode(t2)); + } +} +void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyNewClass(TNode t) { +} +void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyPreMerge(TNode t1, TNode t2) { +} +void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyPostMerge(TNode t1, TNode t2) { +} +void ArithCongruenceManager::ArithCongruenceNotify::eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { +} + +void ArithCongruenceManager::raiseConflict(Node conflict){ + Assert(!inConflict()); + Debug("arith::conflict") << "difference manager conflict " << conflict << std::endl; + d_inConflict.raise(); + d_raiseConflict.blackBoxConflict(conflict); +} +bool ArithCongruenceManager::inConflict() const{ + return d_inConflict.isRaised(); +} + +bool ArithCongruenceManager::hasMorePropagations() const { + return !d_propagatations.empty(); +} +const Node ArithCongruenceManager::getNextPropagation() { + Assert(hasMorePropagations()); + Node prop = d_propagatations.front(); + d_propagatations.dequeue(); + return prop; +} + +bool ArithCongruenceManager::canExplain(TNode n) const { + return d_explanationMap.find(n) != d_explanationMap.end(); +} + void ArithCongruenceManager::setMasterEqualityEngine(eq::EqualityEngine* eq) { d_ee.setMasterEqualityEngine(eq); } -void ArithCongruenceManager::watchedVariableIsZero(Constraint lb, Constraint ub){ +Node ArithCongruenceManager::externalToInternal(TNode n) const{ + Assert(canExplain(n)); + ExplainMap::const_iterator iter = d_explanationMap.find(n); + size_t pos = (*iter).second; + return d_propagatations[pos]; +} + +void ArithCongruenceManager::pushBack(TNode n){ + d_explanationMap.insert(n, d_propagatations.size()); + d_propagatations.enqueue(n); + + ++(d_statistics.d_propagations); +} +void ArithCongruenceManager::pushBack(TNode n, TNode r){ + d_explanationMap.insert(r, d_propagatations.size()); + d_explanationMap.insert(n, d_propagatations.size()); + d_propagatations.enqueue(n); + + ++(d_statistics.d_propagations); +} +void ArithCongruenceManager::pushBack(TNode n, TNode r, TNode w){ + d_explanationMap.insert(w, d_propagatations.size()); + d_explanationMap.insert(r, d_propagatations.size()); + d_explanationMap.insert(n, d_propagatations.size()); + d_propagatations.enqueue(n); + + ++(d_statistics.d_propagations); +} + +void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP lb, ConstraintCP ub){ Assert(lb->isLowerBound()); Assert(ub->isUpperBound()); Assert(lb->getVariable() == ub->getVariable()); @@ -79,13 +173,13 @@ void ArithCongruenceManager::watchedVariableIsZero(Constraint lb, Constraint ub) ++(d_statistics.d_watchedVariableIsZero); ArithVar s = lb->getVariable(); - Node reason = ConstraintValue::explainConflict(lb,ub); + Node reason = Constraint_::externalExplainByAssertions(lb,ub); d_keepAlive.push_back(reason); assertionToEqualityEngine(true, s, reason); } -void ArithCongruenceManager::watchedVariableIsZero(Constraint eq){ +void ArithCongruenceManager::watchedVariableIsZero(ConstraintCP eq){ Assert(eq->isEquality()); Assert(eq->getValue().sgn() == 0); @@ -96,20 +190,20 @@ void ArithCongruenceManager::watchedVariableIsZero(Constraint eq){ //Explain for conflict is correct as these proofs are generated //and stored eagerly //These will be safe for propagation later as well - Node reason = eq->explainForConflict(); + Node reason = eq->externalExplainByAssertions(); d_keepAlive.push_back(reason); assertionToEqualityEngine(true, s, reason); } -void ArithCongruenceManager::watchedVariableCannotBeZero(Constraint c){ +void ArithCongruenceManager::watchedVariableCannotBeZero(ConstraintCP c){ ++(d_statistics.d_watchedVariableIsNotZero); ArithVar s = c->getVariable(); //Explain for conflict is correct as these proofs are generated and stored eagerly //These will be safe for propagation later as well - Node reason = c->explainForConflict(); + Node reason = c->externalExplainByAssertions(); d_keepAlive.push_back(reason); assertionToEqualityEngine(false, s, reason); @@ -142,7 +236,7 @@ bool ArithCongruenceManager::propagate(TNode x){ Assert(rewritten.getKind() != kind::CONST_BOOLEAN); - Constraint c = d_constraintDatabase.lookup(rewritten); + ConstraintP c = d_constraintDatabase.lookup(rewritten); if(c == NullConstraint){ //using setup as there may not be a corresponding congruence literal yet d_setupLiteral(rewritten); @@ -158,7 +252,8 @@ bool ArithCongruenceManager::propagate(TNode x){ if(c->negationHasProof()){ Node expC = explainInternal(x); - Node neg = c->getNegation()->explainForConflict(); + ConstraintCP negC = c->getNegation(); + Node neg = negC->externalExplainByAssertions(); Node conf = expC.andNode(neg); Node final = flattenAnd(conf); @@ -288,7 +383,7 @@ void ArithCongruenceManager::assertionToEqualityEngine(bool isEquality, ArithVar } } -void ArithCongruenceManager::equalsConstant(Constraint c){ +void ArithCongruenceManager::equalsConstant(ConstraintCP c){ Assert(c->isEquality()); ++(d_statistics.d_equalsConstantCalls); @@ -303,13 +398,13 @@ void ArithCongruenceManager::equalsConstant(Constraint c){ Node eq = xAsNode.eqNode(asRational); d_keepAlive.push_back(eq); - Node reason = c->explainForConflict(); + Node reason = c->externalExplainByAssertions(); d_keepAlive.push_back(reason); d_ee.assertEquality(eq, true, reason); } -void ArithCongruenceManager::equalsConstant(Constraint lb, Constraint ub){ +void ArithCongruenceManager::equalsConstant(ConstraintCP lb, ConstraintCP ub){ Assert(lb->isLowerBound()); Assert(ub->isUpperBound()); Assert(lb->getVariable() == ub->getVariable()); @@ -319,7 +414,7 @@ void ArithCongruenceManager::equalsConstant(Constraint lb, Constraint ub){ << ub << std::endl; ArithVar x = lb->getVariable(); - Node reason = ConstraintValue::explainConflict(lb,ub); + Node reason = Constraint_::externalExplainByAssertions(lb,ub); Node xAsNode = d_avariables.asNode(x); Node asRational = mkRationalNode(lb->getValue().getNoninfinitesimalPart()); diff --git a/src/theory/arith/congruence_manager.h b/src/theory/arith/congruence_manager.h index b4e009169..8e369ff9a 100644 --- a/src/theory/arith/congruence_manager.h +++ b/src/theory/arith/congruence_manager.h @@ -57,43 +57,19 @@ private: private: ArithCongruenceManager& d_acm; public: - ArithCongruenceNotify(ArithCongruenceManager& acm): d_acm(acm) {} - - bool eqNotifyTriggerEquality(TNode equality, bool value) { - Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyTriggerEquality(" << equality << ", " << (value ? "true" : "false") << ")" << std::endl; - if (value) { - return d_acm.propagate(equality); - } else { - return d_acm.propagate(equality.notNode()); - } - } - - bool eqNotifyTriggerPredicate(TNode predicate, bool value) { - Unreachable(); - } - - bool eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value) { - Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyTriggerTermEquality(" << t1 << ", " << t2 << ", " << (value ? "true" : "false") << ")" << std::endl; - if (value) { - return d_acm.propagate(t1.eqNode(t2)); - } else { - return d_acm.propagate(t1.eqNode(t2).notNode()); - } - } - - void eqNotifyConstantTermMerge(TNode t1, TNode t2) { - Debug("arith::congruences") << "ArithCongruenceNotify::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << std::endl; - if (t1.getKind() == kind::CONST_BOOLEAN) { - d_acm.propagate(t1.iffNode(t2)); - } else { - 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(ArithCongruenceManager& acm); + + bool eqNotifyTriggerEquality(TNode equality, bool value); + + 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); }; ArithCongruenceNotify d_notify; @@ -117,66 +93,27 @@ private: eq::EqualityEngine d_ee; - void raiseConflict(Node conflict){ - Assert(!inConflict()); - Debug("arith::conflict") << "difference manager conflict " << conflict << std::endl; - d_inConflict.raise(); - d_raiseConflict(conflict); - } + void raiseConflict(Node conflict); public: - bool inConflict() const{ - return d_inConflict.isRaised(); - }; + bool inConflict() const; - bool hasMorePropagations() const { - return !d_propagatations.empty(); - } + bool hasMorePropagations() const; - const Node getNextPropagation() { - Assert(hasMorePropagations()); - Node prop = d_propagatations.front(); - d_propagatations.dequeue(); - return prop; - } + const Node getNextPropagation(); - bool canExplain(TNode n) const { - return d_explanationMap.find(n) != d_explanationMap.end(); - } + bool canExplain(TNode n) const; void setMasterEqualityEngine(eq::EqualityEngine* eq); private: - Node externalToInternal(TNode n) const{ - Assert(canExplain(n)); - ExplainMap::const_iterator iter = d_explanationMap.find(n); - size_t pos = (*iter).second; - return d_propagatations[pos]; - } - - void pushBack(TNode n){ - d_explanationMap.insert(n, d_propagatations.size()); - d_propagatations.enqueue(n); - - ++(d_statistics.d_propagations); - } - - void pushBack(TNode n, TNode r){ - d_explanationMap.insert(r, d_propagatations.size()); - d_explanationMap.insert(n, d_propagatations.size()); - d_propagatations.enqueue(n); + Node externalToInternal(TNode n) const; - ++(d_statistics.d_propagations); - } + void pushBack(TNode n); - void pushBack(TNode n, TNode r, TNode w){ - d_explanationMap.insert(w, d_propagatations.size()); - d_explanationMap.insert(r, d_propagatations.size()); - d_explanationMap.insert(n, d_propagatations.size()); - d_propagatations.enqueue(n); + void pushBack(TNode n, TNode r); - ++(d_statistics.d_propagations); - } + void pushBack(TNode n, TNode r, TNode w); bool propagate(TNode x); void explain(TNode literal, std::vector<TNode>& assumptions); @@ -207,21 +144,21 @@ public: } /** Assert an equality. */ - void watchedVariableIsZero(Constraint eq); + void watchedVariableIsZero(ConstraintCP eq); /** Assert a conjunction from lb and ub. */ - void watchedVariableIsZero(Constraint lb, Constraint ub); + void watchedVariableIsZero(ConstraintCP lb, ConstraintCP ub); /** Assert that the value cannot be zero. */ - void watchedVariableCannotBeZero(Constraint c); + void watchedVariableCannotBeZero(ConstraintCP c); /** Assert that the value cannot be zero. */ - void watchedVariableCannotBeZero(Constraint c, Constraint d); + void watchedVariableCannotBeZero(ConstraintCP c, ConstraintCP d); /** Assert that the value is congruent to a constant. */ - void equalsConstant(Constraint eq); - void equalsConstant(Constraint lb, Constraint ub); + void equalsConstant(ConstraintCP eq); + void equalsConstant(ConstraintCP lb, ConstraintCP ub); void addSharedTerm(Node x); diff --git a/src/theory/arith/constraint.cpp b/src/theory/arith/constraint.cpp index 78b9d3494..acbd4a04b 100644 --- a/src/theory/arith/constraint.cpp +++ b/src/theory/arith/constraint.cpp @@ -64,7 +64,7 @@ ConstraintType constraintTypeOfComparison(const Comparison& cmp){ } } -ConstraintValue::ConstraintValue(ArithVar x, ConstraintType t, const DeltaRational& v) +Constraint_::Constraint_(ArithVar x, ConstraintType t, const DeltaRational& v) : d_variable(x), d_type(t), d_value(v), @@ -72,7 +72,7 @@ ConstraintValue::ConstraintValue(ArithVar x, ConstraintType t, const DeltaRatio d_literal(Node::null()), d_negation(NullConstraint), d_canBePropagated(false), - _d_assertionOrder(AssertionOrderSentinel), + d_assertionOrder(AssertionOrderSentinel), d_witness(TNode::null()), d_proof(ProofIdSentinel), d_split(false), @@ -82,7 +82,7 @@ ConstraintValue::ConstraintValue(ArithVar x, ConstraintType t, const DeltaRatio } -std::ostream& operator<<(std::ostream& o, const Constraint c){ +std::ostream& operator<<(std::ostream& o, const ConstraintP c){ if(c == NullConstraint){ return o << "NullConstraint"; }else{ @@ -105,7 +105,7 @@ std::ostream& operator<<(std::ostream& o, const ConstraintType t){ } } -std::ostream& operator<<(std::ostream& o, const ConstraintValue& c){ +std::ostream& operator<<(std::ostream& o, const Constraint_& c){ o << c.getVariable() << ' ' << c.getType() << ' ' << c.getValue(); if(c.hasLiteral()){ o << "(node " << c.getLiteral() << ')'; @@ -143,11 +143,67 @@ std::ostream& operator<<(std::ostream& o, const ValueCollection& vc){ return o << "}"; } -void ConstraintValue::debugPrint() const { +std::ostream& operator<<(std::ostream& o, const ConstraintCPVec& v){ + o << "[" << v.size() << "x"; + ConstraintCPVec::const_iterator i, end; + for(i=v.begin(), end=v.end(); i != end; ++i){ + ConstraintCP c = *i; + o << ", " << (*c); + } + o << "]"; + return o; +} + +void Constraint_::debugPrint() const { Message() << *this << endl; } -void ValueCollection::push_into(std::vector<Constraint>& vec) const { + +ValueCollection::ValueCollection() + : d_lowerBound(NullConstraint), + d_upperBound(NullConstraint), + d_equality(NullConstraint), + d_disequality(NullConstraint) +{} + +bool ValueCollection::hasLowerBound() const{ + return d_lowerBound != NullConstraint; +} + +bool ValueCollection::hasUpperBound() const{ + return d_upperBound != NullConstraint; +} + +bool ValueCollection::hasEquality() const{ + return d_equality != NullConstraint; +} + +bool ValueCollection::hasDisequality() const { + return d_disequality != NullConstraint; +} + +ConstraintP ValueCollection::getLowerBound() const { + Assert(hasLowerBound()); + return d_lowerBound; +} + +ConstraintP ValueCollection::getUpperBound() const { + Assert(hasUpperBound()); + return d_upperBound; +} + +ConstraintP ValueCollection::getEquality() const { + Assert(hasEquality()); + return d_equality; +} + +ConstraintP ValueCollection::getDisequality() const { + Assert(hasDisequality()); + return d_disequality; +} + + +void ValueCollection::push_into(std::vector<ConstraintP>& vec) const { Debug("arith::constraint") << "push_into " << *this << endl; if(hasEquality()){ vec.push_back(d_equality); @@ -163,7 +219,7 @@ void ValueCollection::push_into(std::vector<Constraint>& vec) const { } } -ValueCollection ValueCollection::mkFromConstraint(Constraint c){ +ValueCollection ValueCollection::mkFromConstraint(ConstraintP c){ ValueCollection ret; Assert(ret.empty()); switch(c->getType()){ @@ -210,7 +266,7 @@ const DeltaRational& ValueCollection::getValue() const{ return nonNull()->getValue(); } -void ValueCollection::add(Constraint c){ +void ValueCollection::add(ConstraintP c){ Assert(c != NullConstraint); Assert(empty() || getVariable() == c->getVariable()); @@ -238,7 +294,7 @@ void ValueCollection::add(Constraint c){ } } -Constraint ValueCollection::getConstraintOfType(ConstraintType t) const{ +ConstraintP ValueCollection::getConstraintOfType(ConstraintType t) const{ switch(t){ case LowerBound: Assert(hasLowerBound()); @@ -288,7 +344,7 @@ bool ValueCollection::empty() const{ hasDisequality()); } -Constraint ValueCollection::nonNull() const{ +ConstraintP ValueCollection::nonNull() const{ //This can be optimized by caching, but this is not necessary yet! /* "Premature optimization is the root of all evil." */ if(hasLowerBound()){ @@ -304,18 +360,18 @@ Constraint ValueCollection::nonNull() const{ } } -bool ConstraintValue::initialized() const { +bool Constraint_::initialized() const { return d_database != NULL; } -void ConstraintValue::initialize(ConstraintDatabase* db, SortedConstraintMapIterator v, Constraint negation){ +void Constraint_::initialize(ConstraintDatabase* db, SortedConstraintMapIterator v, ConstraintP negation){ Assert(!initialized()); d_database = db; d_variablePosition = v; d_negation = negation; } -ConstraintValue::~ConstraintValue() { +Constraint_::~Constraint_() { Assert(safeToGarbageCollect()); if(initialized()){ @@ -336,12 +392,12 @@ ConstraintValue::~ConstraintValue() { } } -const ValueCollection& ConstraintValue::getValueCollection() const{ +const ValueCollection& Constraint_::getValueCollection() const{ return d_variablePosition->second; } -Constraint ConstraintValue::getCeiling() { - Debug("getCeiling") << "ConstraintValue::getCeiling on " << *this << endl; +ConstraintP Constraint_::getCeiling() { + Debug("getCeiling") << "Constraint_::getCeiling on " << *this << endl; Assert(getValue().getInfinitesimalPart().sgn() > 0); DeltaRational ceiling(getValue().ceiling()); @@ -350,7 +406,7 @@ Constraint ConstraintValue::getCeiling() { return d_database->getConstraint(getVariable(), getType(), ceiling); } -Constraint ConstraintValue::getFloor() { +ConstraintP Constraint_::getFloor() { Assert(getValue().getInfinitesimalPart().sgn() < 0); DeltaRational floor(Rational(getValue().floor())); @@ -359,19 +415,26 @@ Constraint ConstraintValue::getFloor() { return d_database->getConstraint(getVariable(), getType(), floor); } -void ConstraintValue::setCanBePropagated() { +void Constraint_::setCanBePropagated() { Assert(!canBePropagated()); d_database->pushCanBePropagatedWatch(this); } -void ConstraintValue::setAssertedToTheTheory(TNode witness) { +void Constraint_::setAssertedToTheTheoryWithNegationTrue(TNode witness) { + Assert(hasLiteral()); + Assert(!assertedToTheTheory()); + Assert(d_negation->hasProof()); + d_database->pushAssertionOrderWatch(this, witness); +} + +void Constraint_::setAssertedToTheTheory(TNode witness) { Assert(hasLiteral()); Assert(!assertedToTheTheory()); Assert(!d_negation->assertedToTheTheory()); d_database->pushAssertionOrderWatch(this, witness); } -bool ConstraintValue::satisfiedBy(const DeltaRational& dr) const { +bool Constraint_::satisfiedBy(const DeltaRational& dr) const { switch(getType()){ case LowerBound: return getValue() <= dr; @@ -385,19 +448,19 @@ bool ConstraintValue::satisfiedBy(const DeltaRational& dr) const { Unreachable(); } -// bool ConstraintValue::isPsuedoConstraint() const { -// return d_proof == d_database->d_psuedoConstraintProof; -// } +bool Constraint_::isInternalDecision() const { + return d_proof == d_database->d_internalDecisionProof; +} -bool ConstraintValue::isSelfExplaining() const { +bool Constraint_::isSelfExplaining() const { return d_proof == d_database->d_selfExplainingProof; } -bool ConstraintValue::hasEqualityEngineProof() const { +bool Constraint_::hasEqualityEngineProof() const { return d_proof == d_database->d_equalityEngineProof; } -bool ConstraintValue::sanityChecking(Node n) const { +bool Constraint_::sanityChecking(Node n) const { Comparison cmp = Comparison::parseNormalForm(n); Kind k = cmp.comparisonKind(); Polynomial pleft = cmp.normalizedVariablePart(); @@ -441,7 +504,7 @@ bool ConstraintValue::sanityChecking(Node n) const { } } -Constraint ConstraintValue::makeNegation(ArithVar v, ConstraintType t, const DeltaRational& r){ +ConstraintP Constraint_::makeNegation(ArithVar v, ConstraintType t, const DeltaRational& r){ switch(t){ case LowerBound: { @@ -450,12 +513,12 @@ Constraint ConstraintValue::makeNegation(ArithVar v, ConstraintType t, const Del Assert(r.getInfinitesimalPart() == 1); // make (not (v > r)), which is (v <= r) DeltaRational dropInf(r.getNoninfinitesimalPart(), 0); - return new ConstraintValue(v, UpperBound, dropInf); + return new Constraint_(v, UpperBound, dropInf); }else{ Assert(r.infinitesimalSgn() == 0); // make (not (v >= r)), which is (v < r) DeltaRational addInf(r.getNoninfinitesimalPart(), -1); - return new ConstraintValue(v, UpperBound, addInf); + return new Constraint_(v, UpperBound, addInf); } } case UpperBound: @@ -465,18 +528,18 @@ Constraint ConstraintValue::makeNegation(ArithVar v, ConstraintType t, const Del Assert(r.getInfinitesimalPart() == -1); // make (not (v < r)), which is (v >= r) DeltaRational dropInf(r.getNoninfinitesimalPart(), 0); - return new ConstraintValue(v, LowerBound, dropInf); + return new Constraint_(v, LowerBound, dropInf); }else{ Assert(r.infinitesimalSgn() == 0); // make (not (v <= r)), which is (v > r) DeltaRational addInf(r.getNoninfinitesimalPart(), 1); - return new ConstraintValue(v, LowerBound, addInf); + return new Constraint_(v, LowerBound, addInf); } } case Equality: - return new ConstraintValue(v, Disequality, r); + return new Constraint_(v, Disequality, r); case Disequality: - return new ConstraintValue(v, Equality, r); + return new Constraint_(v, Equality, r); default: Unreachable(); return NullConstraint; @@ -500,11 +563,42 @@ ConstraintDatabase::ConstraintDatabase(context::Context* satContext, context::Co d_equalityEngineProof = d_proofs.size(); d_proofs.push_back(NullConstraint); - // d_pseudoConstraintProof = d_proofs.size(); - // d_proofs.push_back(NullConstraint); + d_internalDecisionProof = d_proofs.size(); + d_proofs.push_back(NullConstraint); +} + +SortedConstraintMap& ConstraintDatabase::getVariableSCM(ArithVar v) const{ + Assert(variableDatabaseIsSetup(v)); + return d_varDatabases[v]->d_constraints; +} + +void ConstraintDatabase::pushSplitWatch(ConstraintP c){ + Assert(!c->d_split); + c->d_split = true; + d_watches->d_splitWatches.push_back(c); +} + + +void ConstraintDatabase::pushCanBePropagatedWatch(ConstraintP c){ + Assert(!c->d_canBePropagated); + c->d_canBePropagated = true; + d_watches->d_canBePropagatedWatches.push_back(c); +} + +void ConstraintDatabase::pushAssertionOrderWatch(ConstraintP c, TNode witness){ + Assert(!c->assertedToTheTheory()); + c->d_assertionOrder = d_watches->d_assertionOrderWatches.size(); + c->d_witness = witness; + d_watches->d_assertionOrderWatches.push_back(c); } -Constraint ConstraintDatabase::getConstraint(ArithVar v, ConstraintType t, const DeltaRational& r){ +void ConstraintDatabase::pushProofWatch(ConstraintP c, ProofId pid){ + Assert(c->d_proof == ProofIdSentinel); + c->d_proof = pid; + d_watches->d_proofWatches.push_back(c); +} + +ConstraintP ConstraintDatabase::getConstraint(ArithVar v, ConstraintType t, const DeltaRational& r){ //This must always return a constraint. SortedConstraintMap& scm = getVariableSCM(v); @@ -516,8 +610,8 @@ Constraint ConstraintDatabase::getConstraint(ArithVar v, ConstraintType t, const if(vc.hasConstraintOfType(t)){ return vc.getConstraintOfType(t); }else{ - Constraint c = new ConstraintValue(v, t, r); - Constraint negC = ConstraintValue::makeNegation(v, t, r); + ConstraintP c = new Constraint_(v, t, r); + ConstraintP negC = Constraint_::makeNegation(v, t, r); SortedConstraintMapIterator negPos; if(t == Equality || t == Disequality){ @@ -539,6 +633,15 @@ Constraint ConstraintDatabase::getConstraint(ArithVar v, ConstraintType t, const return c; } } + +ConstraintP ConstraintDatabase::ensureConstraint(ValueCollection& vc, ConstraintType t){ + if(vc.hasConstraintOfType(t)){ + return vc.getConstraintOfType(t); + }else{ + return getConstraint(vc.getVariable(), t, vc.getValue()); + } +} + bool ConstraintDatabase::emptyDatabase(const std::vector<PerVariableDatabase>& vec){ std::vector<PerVariableDatabase>::const_iterator first = vec.begin(); std::vector<PerVariableDatabase>::const_iterator last = vec.end(); @@ -550,7 +653,7 @@ ConstraintDatabase::~ConstraintDatabase(){ delete d_watches; - std::vector<Constraint> constraintList; + std::vector<ConstraintP> constraintList; while(!d_varDatabases.empty()){ PerVariableDatabase* back = d_varDatabases.back(); @@ -561,7 +664,7 @@ ConstraintDatabase::~ConstraintDatabase(){ (i->second).push_into(constraintList); } while(!constraintList.empty()){ - Constraint c = constraintList.back(); + ConstraintP c = constraintList.back(); constraintList.pop_back(); delete c; } @@ -586,17 +689,25 @@ ConstraintDatabase::Statistics::~Statistics(){ StatisticsRegistry::unregisterStat(&d_unatePropagateImplications); } +void ConstraintDatabase::deleteConstraintAndNegation(ConstraintP c){ + Assert(c->safeToGarbageCollect()); + ConstraintP neg = c->getNegation(); + Assert(neg->safeToGarbageCollect()); + delete c; + delete neg; +} + void ConstraintDatabase::addVariable(ArithVar v){ if(d_reclaimable.isMember(v)){ SortedConstraintMap& scm = getVariableSCM(v); - std::vector<Constraint> constraintList; + std::vector<ConstraintP> constraintList; for(SortedConstraintMapIterator i = scm.begin(), end = scm.end(); i != end; ++i){ (i->second).push_into(constraintList); } while(!constraintList.empty()){ - Constraint c = constraintList.back(); + ConstraintP c = constraintList.back(); constraintList.pop_back(); Assert(c->safeToGarbageCollect()); delete c; @@ -605,6 +716,7 @@ void ConstraintDatabase::addVariable(ArithVar v){ d_reclaimable.remove(v); }else{ + Debug("arith::constraint") << "about to fail" << v << " " << d_varDatabases.size() << endl; Assert(v == d_varDatabases.size()); d_varDatabases.push_back(new PerVariableDatabase(v)); } @@ -615,20 +727,20 @@ void ConstraintDatabase::removeVariable(ArithVar v){ d_reclaimable.add(v); } -bool ConstraintValue::safeToGarbageCollect() const{ +bool Constraint_::safeToGarbageCollect() const{ return !isSplit() && !canBePropagated() && !hasProof() && !assertedToTheTheory(); } -Node ConstraintValue::split(){ +Node Constraint_::split(){ Assert(isEquality() || isDisequality()); bool isEq = isEquality(); - Constraint eq = isEq ? this : d_negation; - Constraint diseq = isEq ? d_negation : this; + ConstraintP eq = isEq ? this : d_negation; + ConstraintP diseq = isEq ? d_negation : this; TNode eqNode = eq->getLiteral(); Assert(eqNode.getKind() == kind::EQUAL); @@ -651,26 +763,26 @@ bool ConstraintDatabase::hasLiteral(TNode literal) const { return lookup(literal) != NullConstraint; } -// Constraint ConstraintDatabase::addLiteral(TNode literal){ +// ConstraintP ConstraintDatabase::addLiteral(TNode literal){ // Assert(!hasLiteral(literal)); // bool isNot = (literal.getKind() == NOT); // TNode atom = isNot ? literal[0] : literal; -// Constraint atomC = addAtom(atom); +// ConstraintP atomC = addAtom(atom); // return isNot ? atomC->d_negation : atomC; // } -// Constraint ConstraintDatabase::allocateConstraintForComparison(ArithVar v, const Comparison cmp){ +// ConstraintP ConstraintDatabase::allocateConstraintForComparison(ArithVar v, const Comparison cmp){ // Debug("arith::constraint") << "allocateConstraintForLiteral(" << v << ", "<< cmp <<")" << endl; // Kind kind = cmp.comparisonKind(); // ConstraintType type = constraintTypeOfLiteral(kind); // DeltaRational dr = cmp.getDeltaRational(); -// return new ConstraintValue(v, type, dr); +// return new Constraint_(v, type, dr); // } -Constraint ConstraintDatabase::addLiteral(TNode literal){ +ConstraintP ConstraintDatabase::addLiteral(TNode literal){ Assert(!hasLiteral(literal)); bool isNot = (literal.getKind() == NOT); Node atomNode = (isNot ? literal[0] : literal); @@ -688,7 +800,7 @@ Constraint ConstraintDatabase::addLiteral(TNode literal){ DeltaRational posDR = posCmp.normalizedDeltaRational(); - Constraint posC = new ConstraintValue(v, posType, posDR); + ConstraintP posC = new Constraint_(v, posType, posDR); Debug("arith::constraint") << "addliteral( literal ->" << literal << ")" << endl; Debug("arith::constraint") << "addliteral( posC ->" << posC << ")" << endl; @@ -702,9 +814,9 @@ Constraint ConstraintDatabase::addLiteral(TNode literal){ // If the attempt fails, i points to a pre-existing ValueCollection if(posI->second.hasConstraintOfType(posC->getType())){ - //This is the situation where the Constraint exists, but + //This is the situation where the ConstraintP exists, but //the literal has not been associated with it. - Constraint hit = posI->second.getConstraintOfType(posC->getType()); + ConstraintP hit = posI->second.getConstraintOfType(posC->getType()); Debug("arith::constraint") << "hit " << hit << endl; Debug("arith::constraint") << "posC " << posC << endl; @@ -719,7 +831,7 @@ Constraint ConstraintDatabase::addLiteral(TNode literal){ ConstraintType negType = constraintTypeOfComparison(negCmp); DeltaRational negDR = negCmp.normalizedDeltaRational(); - Constraint negC = new ConstraintValue(v, negType, negDR); + ConstraintP negC = new Constraint_(v, negType, negDR); SortedConstraintMapIterator negI; @@ -771,7 +883,7 @@ Constraint ConstraintDatabase::addLiteral(TNode literal){ // } // } -Constraint ConstraintDatabase::lookup(TNode literal) const{ +ConstraintP ConstraintDatabase::lookup(TNode literal) const{ NodetoConstraintMap::const_iterator iter = d_nodetoConstraintMap.find(literal); if(iter == d_nodetoConstraintMap.end()){ return NullConstraint; @@ -780,11 +892,19 @@ Constraint ConstraintDatabase::lookup(TNode literal) const{ } } -void ConstraintValue::selfExplaining(){ +void Constraint_::selfExplainingWithNegationTrue(){ + Assert(!hasProof()); + Assert(getNegation()->hasProof()); + Assert(hasLiteral()); + Assert(assertedToTheTheory()); + d_database->pushProofWatch(this, d_database->d_selfExplainingProof); +} + +void Constraint_::selfExplaining(){ markAsTrue(); } -void ConstraintValue::propagate(){ +void Constraint_::propagate(){ Assert(hasProof()); Assert(canBePropagated()); Assert(!assertedToTheTheory()); @@ -793,7 +913,7 @@ void ConstraintValue::propagate(){ d_database->d_toPropagate.push(this); } -void ConstraintValue::propagate(Constraint a){ +void Constraint_::propagate(ConstraintCP a){ Assert(!hasProof()); Assert(canBePropagated()); @@ -801,7 +921,7 @@ void ConstraintValue::propagate(Constraint a){ propagate(); } -void ConstraintValue::propagate(Constraint a, Constraint b){ +void Constraint_::propagate(ConstraintCP a, ConstraintCP b){ Assert(!hasProof()); Assert(canBePropagated()); @@ -809,7 +929,7 @@ void ConstraintValue::propagate(Constraint a, Constraint b){ propagate(); } -void ConstraintValue::propagate(const std::vector<Constraint>& b){ +void Constraint_::propagate(const ConstraintCPVec& b){ Assert(!hasProof()); Assert(canBePropagated()); @@ -817,9 +937,8 @@ void ConstraintValue::propagate(const std::vector<Constraint>& b){ propagate(); } -void ConstraintValue::impliedBy(Constraint a){ - Assert(!isTrue()); - Assert(!getNegation()->isTrue()); +void Constraint_::impliedBy(ConstraintCP a){ + Assert(truthIsUnknown()); markAsTrue(a); if(canBePropagated()){ @@ -827,9 +946,8 @@ void ConstraintValue::impliedBy(Constraint a){ } } -void ConstraintValue::impliedBy(Constraint a, Constraint b){ - Assert(!isTrue()); - Assert(!getNegation()->isTrue()); +void Constraint_::impliedBy(ConstraintCP a, ConstraintCP b){ + Assert(truthIsUnknown()); markAsTrue(a, b); if(canBePropagated()){ @@ -837,9 +955,8 @@ void ConstraintValue::impliedBy(Constraint a, Constraint b){ } } -void ConstraintValue::impliedBy(const std::vector<Constraint>& b){ - Assert(!isTrue()); - Assert(!getNegation()->isTrue()); +void Constraint_::impliedBy(const ConstraintCPVec& b){ + Assert(truthIsUnknown()); markAsTrue(b); if(canBePropagated()){ @@ -847,30 +964,29 @@ void ConstraintValue::impliedBy(const std::vector<Constraint>& b){ } } -// void ConstraintValue::setPseudoConstraint(){ -// Assert(truthIsUnknown()); -// Assert(!hasLiteral()); +void Constraint_::setInternalDecision(){ + Assert(truthIsUnknown()); + Assert(!assertedToTheTheory()); -// d_database->pushProofWatch(this, d_database->d_pseudoConstraintProof); -// } + d_database->pushProofWatch(this, d_database->d_internalDecisionProof); +} -void ConstraintValue::setEqualityEngineProof(){ +void Constraint_::setEqualityEngineProof(){ Assert(truthIsUnknown()); Assert(hasLiteral()); d_database->pushProofWatch(this, d_database->d_equalityEngineProof); } -void ConstraintValue::markAsTrue(){ +void Constraint_::markAsTrue(){ Assert(truthIsUnknown()); Assert(hasLiteral()); Assert(assertedToTheTheory()); d_database->pushProofWatch(this, d_database->d_selfExplainingProof); } -void ConstraintValue::markAsTrue(Constraint imp){ +void Constraint_::markAsTrue(ConstraintCP imp){ Assert(truthIsUnknown()); Assert(imp->hasProof()); - //Assert(!imp->isPseudoConstraint()); d_database->d_proofs.push_back(NullConstraint); d_database->d_proofs.push_back(imp); @@ -878,12 +994,10 @@ void ConstraintValue::markAsTrue(Constraint imp){ d_database->pushProofWatch(this, proof); } -void ConstraintValue::markAsTrue(Constraint impA, Constraint impB){ +void Constraint_::markAsTrue(ConstraintCP impA, ConstraintCP impB){ Assert(truthIsUnknown()); Assert(impA->hasProof()); Assert(impB->hasProof()); - //Assert(!impA->isPseudoConstraint()); - //Assert(!impB->isPseudoConstraint()); d_database->d_proofs.push_back(NullConstraint); d_database->d_proofs.push_back(impA); @@ -893,12 +1007,12 @@ void ConstraintValue::markAsTrue(Constraint impA, Constraint impB){ d_database->pushProofWatch(this, proof); } -void ConstraintValue::markAsTrue(const vector<Constraint>& a){ +void Constraint_::markAsTrue(const ConstraintCPVec& a){ Assert(truthIsUnknown()); Assert(a.size() >= 1); d_database->d_proofs.push_back(NullConstraint); - for(vector<Constraint>::const_iterator i = a.begin(), end = a.end(); i != end; ++i){ - Constraint c_i = *i; + for(ConstraintCPVec::const_iterator i = a.begin(), end = a.end(); i != end; ++i){ + ConstraintCP c_i = *i; Assert(c_i->hasProof()); //Assert(!c_i->isPseudoConstraint()); d_database->d_proofs.push_back(c_i); @@ -909,12 +1023,12 @@ void ConstraintValue::markAsTrue(const vector<Constraint>& a){ d_database->pushProofWatch(this, proof); } -SortedConstraintMap& ConstraintValue::constraintSet() const{ +SortedConstraintMap& Constraint_::constraintSet() const{ Assert(d_database->variableDatabaseIsSetup(d_variable)); return (d_database->d_varDatabases[d_variable])->d_constraints; } -bool ConstraintValue::proofIsEmpty() const{ +bool Constraint_::proofIsEmpty() const{ Assert(hasProof()); bool result = d_database->d_proofs[d_proof] == NullConstraint; //Assert((!result) || isSelfExplaining() || hasEqualityEngineProof() || isPseudoConstraint()); @@ -922,32 +1036,76 @@ bool ConstraintValue::proofIsEmpty() const{ return result; } -Node ConstraintValue::makeImplication(const std::vector<Constraint>& b) const{ - Node antecedent = makeConjunction(b); +Node Constraint_::externalImplication(const ConstraintCPVec& b) const{ + Assert(hasLiteral()); + Node antecedent = externalExplainByAssertions(b); Node implied = getLiteral(); return antecedent.impNode(implied); } -Node ConstraintValue::makeConjunction(const std::vector<Constraint>& b){ - NodeBuilder<> nb(kind::AND); - for(vector<Constraint>::const_iterator i = b.begin(), end = b.end(); i != end; ++i){ - Constraint b_i = *i; - b_i->explainBefore(nb, AssertionOrderSentinel); +Node Constraint_::externalExplainByAssertions(const ConstraintCPVec& b){ + return externalExplain(b, AssertionOrderSentinel); +} + +struct ConstraintCPHash { + /* Todo replace with an id */ + size_t operator()(ConstraintCP c) const{ + Assert(sizeof(ConstraintCP) > 0); + return ((size_t)c)/sizeof(ConstraintCP); } - if(nb.getNumChildren() >= 2){ - return nb; - }else if(nb.getNumChildren() == 1){ - return nb[0]; - }else{ - return mkBoolNode(true); +}; + +void Constraint_::assertionFringe(ConstraintCPVec& v){ + hash_set<ConstraintCP, ConstraintCPHash> visited; + size_t writePos = 0; + + if(!v.empty()){ + const ConstraintDatabase* db = v.back()->d_database; + const CDConstraintList& proofs = db->d_proofs; + for(size_t i = 0; i < v.size(); ++i){ + ConstraintCP vi = v[i]; + if(visited.find(vi) == visited.end()){ + Assert(vi->hasProof()); + visited.insert(vi); + if(vi->onFringe()){ + v[writePos] = vi; + writePos++; + }else{ + Assert(!vi->isSelfExplaining()); + ProofId p = vi->d_proof; + ConstraintCP antecedent = proofs[p]; + while(antecedent != NullConstraint){ + v.push_back(antecedent); + --p; + antecedent = proofs[p]; + } + } + } + } + v.resize(writePos); + } +} + +void Constraint_::assertionFringe(ConstraintCPVec& o, const ConstraintCPVec& i){ + o.insert(o.end(), i.begin(), i.end()); + assertionFringe(o); +} + +Node Constraint_::externalExplain(const ConstraintCPVec& v, AssertionOrder order){ + NodeBuilder<> nb(kind::AND); + ConstraintCPVec::const_iterator i, end; + for(i = v.begin(), end = v.end(); i != end; ++i){ + ConstraintCP v_i = *i; + v_i->externalExplain(nb, order); } + return safeConstructNary(nb); } -void ConstraintValue::explainBefore(NodeBuilder<>& nb, AssertionOrder order) const{ +void Constraint_::externalExplain(NodeBuilder<>& nb, AssertionOrder order) const{ Assert(hasProof()); Assert(!isSelfExplaining() || assertedToTheTheory()); - + Assert(!isInternalDecision()); if(assertedBefore(order)){ nb << getWitness(); @@ -956,56 +1114,61 @@ void ConstraintValue::explainBefore(NodeBuilder<>& nb, AssertionOrder order) con }else{ Assert(!isSelfExplaining()); ProofId p = d_proof; - Constraint antecedent = d_database->d_proofs[p]; + ConstraintCP antecedent = d_database->d_proofs[p]; for(; antecedent != NullConstraint; antecedent = d_database->d_proofs[--p] ){ - antecedent->explainBefore(nb, order); + antecedent->externalExplain(nb, order); } } } -Node ConstraintValue::explainBefore(AssertionOrder order) const{ + +Node Constraint_::externalExplain(AssertionOrder order) const{ Assert(hasProof()); Assert(!isSelfExplaining() || assertedBefore(order)); + Assert(!isInternalDecision()); if(assertedBefore(order)){ return getWitness(); }else if(hasEqualityEngineProof()){ return d_database->eeExplain(this); }else{ Assert(!proofIsEmpty()); - //Force the selection of the layer above if the node is assertedToTheTheory()! + //Force the selection of the layer above if the node is + // assertedToTheTheory()! if(d_database->d_proofs[d_proof-1] == NullConstraint){ - Constraint antecedent = d_database->d_proofs[d_proof]; - return antecedent->explainBefore(order); + ConstraintCP antecedent = d_database->d_proofs[d_proof]; + return antecedent->externalExplain(order); }else{ NodeBuilder<> nb(kind::AND); Assert(!isSelfExplaining()); ProofId p = d_proof; - Constraint antecedent = d_database->d_proofs[p]; - for(; antecedent != NullConstraint; antecedent = d_database->d_proofs[--p] ){ - antecedent->explainBefore(nb, order); + ConstraintCP antecedent = d_database->d_proofs[p]; + while(antecedent != NullConstraint){ + antecedent->externalExplain(nb, order); + --p; + antecedent = d_database->d_proofs[p]; } return nb; } } } -Node ConstraintValue::explainConflict(Constraint a, Constraint b){ +Node Constraint_::externalExplainByAssertions(ConstraintCP a, ConstraintCP b){ NodeBuilder<> nb(kind::AND); - a->explainForConflict(nb); - b->explainForConflict(nb); + a->externalExplainByAssertions(nb); + b->externalExplainByAssertions(nb); return nb; } -Node ConstraintValue::explainConflict(Constraint a, Constraint b, Constraint c){ +Node Constraint_::externalExplainByAssertions(ConstraintCP a, ConstraintCP b, ConstraintCP c){ NodeBuilder<> nb(kind::AND); - a->explainForConflict(nb); - b->explainForConflict(nb); - c->explainForConflict(nb); + a->externalExplainByAssertions(nb); + b->externalExplainByAssertions(nb); + c->externalExplainByAssertions(nb); return nb; } -Constraint ConstraintValue::getStrictlyWeakerLowerBound(bool hasLiteral, bool asserted) const { +ConstraintP Constraint_::getStrictlyWeakerLowerBound(bool hasLiteral, bool asserted) const { Assert(initialized()); Assert(!asserted || hasLiteral); @@ -1016,7 +1179,7 @@ Constraint ConstraintValue::getStrictlyWeakerLowerBound(bool hasLiteral, bool as --i; const ValueCollection& vc = i->second; if(vc.hasLowerBound()){ - Constraint weaker = vc.getLowerBound(); + ConstraintP weaker = vc.getLowerBound(); // asserted -> hasLiteral // hasLiteral -> weaker->hasLiteral() @@ -1030,7 +1193,7 @@ Constraint ConstraintValue::getStrictlyWeakerLowerBound(bool hasLiteral, bool as return NullConstraint; } -Constraint ConstraintValue::getStrictlyWeakerUpperBound(bool hasLiteral, bool asserted) const { +ConstraintP Constraint_::getStrictlyWeakerUpperBound(bool hasLiteral, bool asserted) const { SortedConstraintMapConstIterator i = d_variablePosition; const SortedConstraintMap& scm = constraintSet(); SortedConstraintMapConstIterator i_end = scm.end(); @@ -1039,7 +1202,7 @@ Constraint ConstraintValue::getStrictlyWeakerUpperBound(bool hasLiteral, bool as for(; i != i_end; ++i){ const ValueCollection& vc = i->second; if(vc.hasUpperBound()){ - Constraint weaker = vc.getUpperBound(); + ConstraintP weaker = vc.getUpperBound(); if((!hasLiteral || (weaker->hasLiteral())) && (!asserted || ( weaker->assertedToTheTheory()))){ return weaker; @@ -1050,7 +1213,7 @@ Constraint ConstraintValue::getStrictlyWeakerUpperBound(bool hasLiteral, bool as return NullConstraint; } -Constraint ConstraintDatabase::getBestImpliedBound(ArithVar v, ConstraintType t, const DeltaRational& r) const { +ConstraintP ConstraintDatabase::getBestImpliedBound(ArithVar v, ConstraintType t, const DeltaRational& r) const { Assert(variableDatabaseIsSetup(v)); Assert(t == UpperBound || t == LowerBound); @@ -1110,12 +1273,12 @@ Constraint ConstraintDatabase::getBestImpliedBound(ArithVar v, ConstraintType t, } } } -Node ConstraintDatabase::eeExplain(const ConstraintValue* const c) const{ +Node ConstraintDatabase::eeExplain(const Constraint_* const c) const{ Assert(c->hasLiteral()); return d_congruenceManager.explain(c->getLiteral()); } -void ConstraintDatabase::eeExplain(const ConstraintValue* const c, NodeBuilder<>& nb) const{ +void ConstraintDatabase::eeExplain(const Constraint_* const c, NodeBuilder<>& nb) const{ Assert(c->hasLiteral()); d_congruenceManager.explain(c->getLiteral(), nb); } @@ -1133,7 +1296,7 @@ ConstraintDatabase::Watches::Watches(context::Context* satContext, context::Cont {} -void ConstraintValue::setLiteral(Node n) { +void Constraint_::setLiteral(Node n) { Assert(!hasLiteral()); Assert(sanityChecking(n)); d_literal = n; @@ -1142,7 +1305,7 @@ void ConstraintValue::setLiteral(Node n) { map.insert(make_pair(d_literal, this)); } -void implies(std::vector<Node>& out, Constraint a, Constraint b){ +void implies(std::vector<Node>& out, ConstraintP a, ConstraintP b){ Node la = a->getLiteral(); Node lb = b->getLiteral(); @@ -1153,7 +1316,7 @@ void implies(std::vector<Node>& out, Constraint a, Constraint b){ out.push_back(orderOr); } -void mutuallyExclusive(std::vector<Node>& out, Constraint a, Constraint b){ +void mutuallyExclusive(std::vector<Node>& out, ConstraintP a, ConstraintP b){ Node la = a->getLiteral(); Node lb = b->getLiteral(); @@ -1169,13 +1332,13 @@ void ConstraintDatabase::outputUnateInequalityLemmas(std::vector<Node>& out, Ari SortedConstraintMap& scm = getVariableSCM(v); SortedConstraintMapConstIterator scm_iter = scm.begin(); SortedConstraintMapConstIterator scm_end = scm.end(); - Constraint prev = NullConstraint; + ConstraintP prev = NullConstraint; //get transitive unates //Only lower bounds or upperbounds should be done. for(; scm_iter != scm_end; ++scm_iter){ const ValueCollection& vc = scm_iter->second; if(vc.hasUpperBound()){ - Constraint ub = vc.getUpperBound(); + ConstraintP ub = vc.getUpperBound(); if(ub->hasLiteral()){ if(prev != NullConstraint){ implies(out, prev, ub); @@ -1188,7 +1351,7 @@ void ConstraintDatabase::outputUnateInequalityLemmas(std::vector<Node>& out, Ari void ConstraintDatabase::outputUnateEqualityLemmas(std::vector<Node>& out, ArithVar v) const{ - vector<Constraint> equalities; + vector<ConstraintP> equalities; SortedConstraintMap& scm = getVariableSCM(v); SortedConstraintMapConstIterator scm_iter = scm.begin(); @@ -1197,34 +1360,34 @@ void ConstraintDatabase::outputUnateEqualityLemmas(std::vector<Node>& out, Arith for(; scm_iter != scm_end; ++scm_iter){ const ValueCollection& vc = scm_iter->second; if(vc.hasEquality()){ - Constraint eq = vc.getEquality(); + ConstraintP eq = vc.getEquality(); if(eq->hasLiteral()){ equalities.push_back(eq); } } } - vector<Constraint>::const_iterator i, j, eq_end = equalities.end(); + vector<ConstraintP>::const_iterator i, j, eq_end = equalities.end(); for(i = equalities.begin(); i != eq_end; ++i){ - Constraint at_i = *i; + ConstraintP at_i = *i; for(j= i + 1; j != eq_end; ++j){ - Constraint at_j = *j; + ConstraintP at_j = *j; mutuallyExclusive(out, at_i, at_j); } } for(i = equalities.begin(); i != eq_end; ++i){ - Constraint eq = *i; + ConstraintP eq = *i; const ValueCollection& vc = eq->getValueCollection(); Assert(vc.hasEquality() && vc.getEquality()->hasLiteral()); bool hasLB = vc.hasLowerBound() && vc.getLowerBound()->hasLiteral(); bool hasUB = vc.hasUpperBound() && vc.getUpperBound()->hasLiteral(); - Constraint lb = hasLB ? + ConstraintP lb = hasLB ? vc.getLowerBound() : eq->getStrictlyWeakerLowerBound(true, false); - Constraint ub = hasUB ? + ConstraintP ub = hasUB ? vc.getUpperBound() : eq->getStrictlyWeakerUpperBound(true, false); if(hasUB && hasLB && !eq->isSplit()){ @@ -1251,21 +1414,20 @@ void ConstraintDatabase::outputUnateInequalityLemmas(std::vector<Node>& lemmas) } } -void ConstraintDatabase::raiseUnateConflict(Constraint ant, Constraint cons){ +void ConstraintDatabase::raiseUnateConflict(ConstraintP ant, ConstraintP cons){ Assert(ant->hasProof()); - Constraint negCons = cons->getNegation(); + ConstraintP negCons = cons->getNegation(); Assert(negCons->hasProof()); Debug("arith::unate::conf") << ant << "implies " << cons << endl; Debug("arith::unate::conf") << negCons << " is true." << endl; - - Node conf = ConstraintValue::explainConflict(ant, negCons); - Debug("arith::unate::conf") << conf << std::endl; - d_raiseConflict(conf); + d_raiseConflict.addConstraint(ant); + d_raiseConflict.addConstraint(negCons); + d_raiseConflict.commitConflict(); } -void ConstraintDatabase::unatePropLowerBound(Constraint curr, Constraint prev){ +void ConstraintDatabase::unatePropLowerBound(ConstraintP curr, ConstraintP prev){ Debug("arith::unate") << "unatePropLowerBound " << curr << " " << prev << endl; Assert(curr != prev); Assert(curr != NullConstraint); @@ -1297,7 +1459,7 @@ void ConstraintDatabase::unatePropLowerBound(Constraint curr, Constraint prev){ //Don't worry about implying the negation of upperbound. //These should all be handled by propagating the LowerBounds! if(vc.hasLowerBound()){ - Constraint lb = vc.getLowerBound(); + ConstraintP lb = vc.getLowerBound(); if(lb->negationHasProof()){ raiseUnateConflict(curr, lb); return; @@ -1309,7 +1471,7 @@ void ConstraintDatabase::unatePropLowerBound(Constraint curr, Constraint prev){ } } if(vc.hasDisequality()){ - Constraint dis = vc.getDisequality(); + ConstraintP dis = vc.getDisequality(); if(dis->negationHasProof()){ raiseUnateConflict(curr, dis); return; @@ -1323,7 +1485,7 @@ void ConstraintDatabase::unatePropLowerBound(Constraint curr, Constraint prev){ } } -void ConstraintDatabase::unatePropUpperBound(Constraint curr, Constraint prev){ +void ConstraintDatabase::unatePropUpperBound(ConstraintP curr, ConstraintP prev){ Debug("arith::unate") << "unatePropUpperBound " << curr << " " << prev << endl; Assert(curr != prev); Assert(curr != NullConstraint); @@ -1348,7 +1510,7 @@ void ConstraintDatabase::unatePropUpperBound(Constraint curr, Constraint prev){ //Don't worry about implying the negation of upperbound. //These should all be handled by propagating the UpperBounds! if(vc.hasUpperBound()){ - Constraint ub = vc.getUpperBound(); + ConstraintP ub = vc.getUpperBound(); if(ub->negationHasProof()){ raiseUnateConflict(curr, ub); return; @@ -1359,7 +1521,7 @@ void ConstraintDatabase::unatePropUpperBound(Constraint curr, Constraint prev){ } } if(vc.hasDisequality()){ - Constraint dis = vc.getDisequality(); + ConstraintP dis = vc.getDisequality(); if(dis->negationHasProof()){ raiseUnateConflict(curr, dis); return; @@ -1373,7 +1535,7 @@ void ConstraintDatabase::unatePropUpperBound(Constraint curr, Constraint prev){ } } -void ConstraintDatabase::unatePropEquality(Constraint curr, Constraint prevLB, Constraint prevUB){ +void ConstraintDatabase::unatePropEquality(ConstraintP curr, ConstraintP prevLB, ConstraintP prevUB){ Debug("arith::unate") << "unatePropEquality " << curr << " " << prevLB << " " << prevUB << endl; Assert(curr != prevLB); Assert(curr != prevUB); @@ -1405,7 +1567,7 @@ void ConstraintDatabase::unatePropEquality(Constraint curr, Constraint prevLB, C //Don't worry about implying the negation of upperbound. //These should all be handled by propagating the LowerBounds! if(vc.hasLowerBound()){ - Constraint lb = vc.getLowerBound(); + ConstraintP lb = vc.getLowerBound(); if(lb->negationHasProof()){ raiseUnateConflict(curr, lb); return; @@ -1416,7 +1578,7 @@ void ConstraintDatabase::unatePropEquality(Constraint curr, Constraint prevLB, C } } if(vc.hasDisequality()){ - Constraint dis = vc.getDisequality(); + ConstraintP dis = vc.getDisequality(); if(dis->negationHasProof()){ raiseUnateConflict(curr, dis); return; @@ -1440,7 +1602,7 @@ void ConstraintDatabase::unatePropEquality(Constraint curr, Constraint prevLB, C //Don't worry about implying the negation of upperbound. //These should all be handled by propagating the UpperBounds! if(vc.hasUpperBound()){ - Constraint ub = vc.getUpperBound(); + ConstraintP ub = vc.getUpperBound(); if(ub->negationHasProof()){ raiseUnateConflict(curr, ub); return; @@ -1452,7 +1614,7 @@ void ConstraintDatabase::unatePropEquality(Constraint curr, Constraint prevLB, C } } if(vc.hasDisequality()){ - Constraint dis = vc.getDisequality(); + ConstraintP dis = vc.getDisequality(); if(dis->negationHasProof()){ raiseUnateConflict(curr, dis); return; diff --git a/src/theory/arith/constraint.h b/src/theory/arith/constraint.h index 4966115d2..18e53660f 100644 --- a/src/theory/arith/constraint.h +++ b/src/theory/arith/constraint.h @@ -94,9 +94,9 @@ namespace arith { enum ConstraintType {LowerBound, Equality, UpperBound, Disequality}; -typedef context::CDList<Constraint> CDConstraintList; +typedef context::CDList<ConstraintCP> CDConstraintList; -typedef __gnu_cxx::hash_map<Node, Constraint, NodeHashFunction> NodetoConstraintMap; +typedef __gnu_cxx::hash_map<Node, ConstraintP, NodeHashFunction> NodetoConstraintMap; typedef size_t ProofId; static ProofId ProofIdSentinel = std::numeric_limits<ProofId>::max(); @@ -111,54 +111,29 @@ static AssertionOrder AssertionOrderSentinel = std::numeric_limits<AssertionOrde class ValueCollection { private: - Constraint d_lowerBound; - Constraint d_upperBound; - Constraint d_equality; - Constraint d_disequality; + ConstraintP d_lowerBound; + ConstraintP d_upperBound; + ConstraintP d_equality; + ConstraintP d_disequality; public: - ValueCollection() - : d_lowerBound(NullConstraint), - d_upperBound(NullConstraint), - d_equality(NullConstraint), - d_disequality(NullConstraint) - {} + ValueCollection(); - static ValueCollection mkFromConstraint(Constraint c); + static ValueCollection mkFromConstraint(ConstraintP c); - bool hasLowerBound() const{ - return d_lowerBound != NullConstraint; - } - bool hasUpperBound() const{ - return d_upperBound != NullConstraint; - } - bool hasEquality() const{ - return d_equality != NullConstraint; - } - bool hasDisequality() const { - return d_disequality != NullConstraint; - } + bool hasLowerBound() const; + bool hasUpperBound() const; + bool hasEquality() const; + bool hasDisequality() const; bool hasConstraintOfType(ConstraintType t) const; - Constraint getLowerBound() const { - Assert(hasLowerBound()); - return d_lowerBound; - } - Constraint getUpperBound() const { - Assert(hasUpperBound()); - return d_upperBound; - } - Constraint getEquality() const { - Assert(hasEquality()); - return d_equality; - } - Constraint getDisequality() const { - Assert(hasDisequality()); - return d_disequality; - } + ConstraintP getLowerBound() const; + ConstraintP getUpperBound() const; + ConstraintP getEquality() const; + ConstraintP getDisequality() const; - Constraint getConstraintOfType(ConstraintType t) const; + ConstraintP getConstraintOfType(ConstraintType t) const; /** Returns true if any of the constraints are non-null. */ bool empty() const; @@ -174,11 +149,11 @@ public: * Adds a constraint to the set. * The collection must not have a constraint of that type already. */ - void add(Constraint c); + void add(ConstraintP c); - void push_into(std::vector<Constraint>& vec) const; + void push_into(std::vector<ConstraintP>& vec) const; - Constraint nonNull() const; + ConstraintP nonNull() const; ArithVar getVariable() const; const DeltaRational& getValue() const; @@ -220,7 +195,7 @@ struct PerVariableDatabase{ } }; -class ConstraintValue { +class Constraint_ { private: /** The ArithVar associated with the constraint. */ const ArithVar d_variable; @@ -246,7 +221,7 @@ private: Node d_literal; /** Pointer to the negation of the Constraint. */ - Constraint d_negation; + ConstraintP d_negation; /** * This is true if the associated node can be propagated. @@ -269,10 +244,11 @@ private: * Sat Context Dependent. * This is initially AssertionOrderSentinel. */ - AssertionOrder _d_assertionOrder; + AssertionOrder d_assertionOrder; + /** * This is guaranteed to be on the fact queue. - * For example if x + y = x + 1 is on the fact queue, then use this + * For example if x + y = x + 1 is on the fact queue, then use this */ TNode d_witness; @@ -309,13 +285,13 @@ private: * Because of circular dependencies a Constraint is not fully valid until * initialize has been called on it. */ - ConstraintValue(ArithVar x, ConstraintType t, const DeltaRational& v); + Constraint_(ArithVar x, ConstraintType t, const DeltaRational& v); /** * Destructor for a constraint. * This should only be called if safeToGarbageCollect() is true. */ - ~ConstraintValue(); + ~Constraint_(); bool initialized() const; @@ -323,12 +299,12 @@ private: * This initializes the fields that cannot be set in the constructor due to * circular dependencies. */ - void initialize(ConstraintDatabase* db, SortedConstraintMapIterator v, Constraint negation); + void initialize(ConstraintDatabase* db, SortedConstraintMapIterator v, ConstraintP negation); class ProofCleanup { public: - inline void operator()(Constraint* p){ - Constraint constraint = *p; + inline void operator()(ConstraintP* p){ + ConstraintP constraint = *p; Assert(constraint->d_proof != ProofIdSentinel); constraint->d_proof = ProofIdSentinel; } @@ -336,8 +312,8 @@ private: class CanBePropagatedCleanup { public: - inline void operator()(Constraint* p){ - Constraint constraint = *p; + inline void operator()(ConstraintP* p){ + ConstraintP constraint = *p; Assert(constraint->d_canBePropagated); constraint->d_canBePropagated = false; } @@ -345,10 +321,10 @@ private: class AssertionOrderCleanup { public: - inline void operator()(Constraint* p){ - Constraint constraint = *p; + inline void operator()(ConstraintP* p){ + ConstraintP constraint = *p; Assert(constraint->assertedToTheTheory()); - constraint->_d_assertionOrder = AssertionOrderSentinel; + constraint->d_assertionOrder = AssertionOrderSentinel; constraint->d_witness = TNode::null(); Assert(!constraint->assertedToTheTheory()); } @@ -356,8 +332,8 @@ private: class SplitCleanup { public: - inline void operator()(Constraint* p){ - Constraint constraint = *p; + inline void operator()(ConstraintP* p){ + ConstraintP constraint = *p; Assert(constraint->d_split); constraint->d_split = false; } @@ -389,7 +365,7 @@ public: return d_value; } - Constraint getNegation() const { + ConstraintP getNegation() const { return d_negation; } @@ -421,7 +397,7 @@ public: /** * Splits the node in the user context. - * Returns a lemma that is assumed to be true fro the rest of the user context. + * Returns a lemma that is assumed to be true for the rest of the user context. * Constraint must be an equality or disequality. */ Node split(); @@ -441,8 +417,8 @@ public: } bool assertedToTheTheory() const { - Assert((_d_assertionOrder < AssertionOrderSentinel) != d_witness.isNull()); - return _d_assertionOrder < AssertionOrderSentinel; + Assert((d_assertionOrder < AssertionOrderSentinel) != d_witness.isNull()); + return d_assertionOrder < AssertionOrderSentinel; } TNode getWitness() const { Assert(assertedToTheTheory()); @@ -450,12 +426,17 @@ public: } bool assertedBefore(AssertionOrder time) const { - return _d_assertionOrder < time; + return d_assertionOrder < time; } - + /** Sets the witness literal for a node being on the assertion stack. + * The negation of the node cannot be true. */ void setAssertedToTheTheory(TNode witness); + /** Sets the witness literal for a node being on the assertion stack. + * The negation of the node must be true! + * This is for conflict generation specificially! */ + void setAssertedToTheTheoryWithNegationTrue(TNode witness); bool hasLiteral() const { return !d_literal.isNull(); @@ -474,6 +455,8 @@ public: */ void selfExplaining(); + void selfExplainingWithNegationTrue(); + /** Returns true if the node is selfExplaining.*/ bool isSelfExplaining() const; @@ -485,12 +468,17 @@ public: /** - * There cannot be a literal associated with this constraint. - * The explanation is the constant true. - * explainInto() does nothing. + * A sets the constraint to be an internal decision. + * + * This does not need to have a witness or an associated literal. + * This is always itself in the explanation fringe for both conflicts + * and propagation. + * This cannot be converted back into a Node conflict or explanation. + * + * This cannot have a proof or be asserted to the theory! */ - //void setPseudoConstraint(); - //bool isPseudoConstraint() const; + void setInternalDecision(); + bool isInternalDecision() const; /** * Returns a explanation of the constraint that is appropriate for conflicts. @@ -500,8 +488,8 @@ public: * This is the minimum fringe of the implication tree s.t. * every constraint is assertedToTheTheory() or hasEqualityEngineProof(). */ - Node explainForConflict() const{ - return explainBefore(AssertionOrderSentinel); + Node externalExplainByAssertions() const { + return externalExplain(AssertionOrderSentinel); } /** @@ -515,13 +503,38 @@ public: * This is not appropriate for propagation! * Use explainForPropagation() instead. */ - void explainForConflict(NodeBuilder<>& nb) const{ - explainBefore(nb, AssertionOrderSentinel); + void externalExplainByAssertions(NodeBuilder<>& nb) const{ + externalExplain(nb, AssertionOrderSentinel); } + /* Equivalent to calling externalExplainByAssertions on all constraints in b */ + static Node externalExplainByAssertions(const ConstraintCPVec& b); + /* utilities for calling externalExplainByAssertions on 2 constraints */ + static Node externalExplainByAssertions(ConstraintCP a, ConstraintCP b); + static Node externalExplainByAssertions(ConstraintCP a, ConstraintCP b, ConstraintCP c); + //static Node externalExplainByAssertions(ConstraintCP a); + + /** + * This is the minimum fringe of the implication tree s.t. every constraint is + * - assertedToTheTheory(), + * - isInternalDecision() or + * - hasEqualityEngineProof(). + */ + static void assertionFringe(ConstraintCPVec& v); + static void assertionFringe(ConstraintCPVec& out, const ConstraintCPVec& in); + /** Utility function built from explainForConflict. */ - static Node explainConflict(Constraint a, Constraint b); - static Node explainConflict(Constraint a, Constraint b, Constraint c); + //static Node explainConflict(ConstraintP a, ConstraintP b); + //static Node explainConflict(ConstraintP a, ConstraintP b, Constraint c); + + //static Node explainConflictForEE(ConstraintCP a, ConstraintCP b); + //static Node explainConflictForEE(ConstraintCP a); + //static Node explainConflictForDio(ConstraintCP a); + //static Node explainConflictForDio(ConstraintCP a, ConstraintCP b); + + bool onFringe() const { + return assertedToTheTheory() || isInternalDecision() || hasEqualityEngineProof(); + } /** * Returns an explanation of a propagation by the ConstraintDatabase. @@ -531,14 +544,20 @@ public: * This is the minimum fringe of the implication tree (excluding the constraint itself) * s.t. every constraint is assertedToTheTheory() or hasEqualityEngineProof(). */ - Node explainForPropagation() const { + Node externalExplainForPropagation() const { Assert(hasProof()); Assert(!isSelfExplaining()); - return explainBefore(_d_assertionOrder); + return externalExplain(d_assertionOrder); } + // void externalExplainForPropagation(NodeBuilder<>& nb) const{ + // Assert(hasProof()); + // Assert(!isSelfExplaining()); + // externalExplain(nb, d_assertionOrder); + // } + private: - Node explainBefore(AssertionOrder order) const; + Node externalExplain(AssertionOrder order) const; /** * Returns an explanation of that was assertedBefore(order). @@ -548,7 +567,9 @@ private: * This is the minimum fringe of the implication tree * s.t. every constraint is assertedBefore(order) or hasEqualityEngineProof(). */ - void explainBefore(NodeBuilder<>& nb, AssertionOrder order) const; + void externalExplain(NodeBuilder<>& nb, AssertionOrder order) const; + + static Node externalExplain(const ConstraintCPVec& b, AssertionOrder order); public: bool hasProof() const { @@ -558,26 +579,38 @@ public: return d_negation->hasProof(); } + /* Neither the contraint has a proof nor the negation has a proof.*/ bool truthIsUnknown() const { return !hasProof() && !negationHasProof(); } + /* This is a synonym for hasProof(). */ bool isTrue() const { return hasProof(); } - Constraint getCeiling(); + /** + * Returns the constraint that corresponds to taking + * x r ceiling(getValue()) where r is the node's getType(). + * Esstentially this is an up branch. + */ + ConstraintP getCeiling(); - Constraint getFloor(); + /** + * Returns the constraint that corresponds to taking + * x r floor(getValue()) where r is the node's getType(). + * Esstentially this is a down branch. + */ + ConstraintP getFloor(); - static Constraint makeNegation(ArithVar v, ConstraintType t, const DeltaRational& r); + static ConstraintP makeNegation(ArithVar v, ConstraintType t, const DeltaRational& r); const ValueCollection& getValueCollection() const; - Constraint getStrictlyWeakerUpperBound(bool hasLiteral, bool mustBeAsserted) const; - Constraint getStrictlyWeakerLowerBound(bool hasLiteral, bool mustBeAsserted) const; + ConstraintP getStrictlyWeakerUpperBound(bool hasLiteral, bool mustBeAsserted) const; + ConstraintP getStrictlyWeakerLowerBound(bool hasLiteral, bool mustBeAsserted) const; /** * Marks the node as having a proof a. @@ -587,19 +620,23 @@ public: * canBePropagated() * !assertedToTheTheory() */ - void propagate(Constraint a); - void propagate(Constraint a, Constraint b); - void propagate(const std::vector<Constraint>& b); + void propagate(ConstraintCP a); + void propagate(ConstraintCP a, ConstraintCP b); + //void propagate(const std::vector<Constraint>& b); + void propagate(const ConstraintCPVec& b); + /** * The only restriction is that this is not known be true. * This propagates if there is a node. */ - void impliedBy(Constraint a); - void impliedBy(Constraint a, Constraint b); - void impliedBy(const std::vector<Constraint>& b); + void impliedBy(ConstraintCP a); + void impliedBy(ConstraintCP a, ConstraintCP b); + //void impliedBy(const std::vector<Constraint>& b); + void impliedBy(const ConstraintCPVec& b); - Node makeImplication(const std::vector<Constraint>& b) const; - static Node makeConjunction(const std::vector<Constraint>& b); + Node externalImplication(const ConstraintCPVec& b) const; + static Node externalConjunction(const ConstraintCPVec& b); + //static Node makeConflictNode(const ConstraintCPVec& b); /** The node must have a proof already and be eligible for propagation! */ void propagate(); @@ -617,10 +654,11 @@ private: * Marks the node as having a proof a. * This is safe if the node does not have */ - void markAsTrue(Constraint a); + void markAsTrue(ConstraintCP a); - void markAsTrue(Constraint a, Constraint b); - void markAsTrue(const std::vector<Constraint>& b); + void markAsTrue(ConstraintCP a, ConstraintCP b); + //void markAsTrue(const std::vector<Constraint>& b); + void markAsTrue(const ConstraintCPVec& b); void debugPrint() const; @@ -634,11 +672,11 @@ private: }; /* class ConstraintValue */ -std::ostream& operator<<(std::ostream& o, const ConstraintValue& c); -std::ostream& operator<<(std::ostream& o, const Constraint c); +std::ostream& operator<<(std::ostream& o, const Constraint_& c); +std::ostream& operator<<(std::ostream& o, const ConstraintP c); std::ostream& operator<<(std::ostream& o, const ConstraintType t); std::ostream& operator<<(std::ostream& o, const ValueCollection& c); - +std::ostream& operator<<(std::ostream& o, const ConstraintCPVec& v); class ConstraintDatabase { @@ -650,20 +688,17 @@ private: */ std::vector<PerVariableDatabase*> d_varDatabases; - SortedConstraintMap& getVariableSCM(ArithVar v) const{ - Assert(variableDatabaseIsSetup(v)); - return d_varDatabases[v]->d_constraints; - } + SortedConstraintMap& getVariableSCM(ArithVar v) const; /** Maps literals to constraints.*/ NodetoConstraintMap d_nodetoConstraintMap; /** * A queue of propagated constraints. - * - * As Constraint are pointers, the elements of the queue do not require destruction. + * ConstraintCP are pointers. + * The elements of the queue do not require destruction. */ - context::CDQueue<Constraint> d_toPropagate; + context::CDQueue<ConstraintCP> d_toPropagate; /** * Proof Lists. @@ -701,17 +736,16 @@ private: ProofId d_equalityEngineProof; /** - * Marks a node as being true always. - * This is only okay for purely internal things. - * - * This is a special proof that is always a member of the list. + * Marks a constraint as being proved by making an internal + * decision. Such nodes cannot be used in external explanations + * but can be used internally. */ - //ProofId d_pseudoConstraintProof; + ProofId d_internalDecisionProof; - typedef context::CDList<Constraint, ConstraintValue::ProofCleanup> ProofCleanupList; - typedef context::CDList<Constraint, ConstraintValue::CanBePropagatedCleanup> CBPList; - typedef context::CDList<Constraint, ConstraintValue::AssertionOrderCleanup> AOList; - typedef context::CDList<Constraint, ConstraintValue::SplitCleanup> SplitList; + typedef context::CDList<ConstraintP, Constraint_::ProofCleanup> ProofCleanupList; + typedef context::CDList<ConstraintP, Constraint_::CanBePropagatedCleanup> CBPList; + typedef context::CDList<ConstraintP, Constraint_::AssertionOrderCleanup> AOList; + typedef context::CDList<ConstraintP, Constraint_::SplitCleanup> SplitList; /** * The watch lists are collected together as they need to be garbage collected @@ -744,30 +778,10 @@ private: }; Watches* d_watches; - void pushSplitWatch(Constraint c){ - Assert(!c->d_split); - c->d_split = true; - d_watches->d_splitWatches.push_back(c); - } - - void pushCanBePropagatedWatch(Constraint c){ - Assert(!c->d_canBePropagated); - c->d_canBePropagated = true; - d_watches->d_canBePropagatedWatches.push_back(c); - } - - void pushAssertionOrderWatch(Constraint c, TNode witness){ - Assert(!c->assertedToTheTheory()); - c->_d_assertionOrder = d_watches->d_assertionOrderWatches.size(); - c->d_witness = witness; - d_watches->d_assertionOrderWatches.push_back(c); - } - - void pushProofWatch(Constraint c, ProofId pid){ - Assert(c->d_proof == ProofIdSentinel); - c->d_proof = pid; - d_watches->d_proofWatches.push_back(c); - } + void pushSplitWatch(ConstraintP c); + void pushCanBePropagatedWatch(ConstraintP c); + void pushAssertionOrderWatch(ConstraintP c, TNode witness); + void pushProofWatch(ConstraintP c, ProofId pid); /** Returns true if all of the entries of the vector are empty. */ static bool emptyDatabase(const std::vector<PerVariableDatabase>& vec); @@ -786,7 +800,7 @@ private: RaiseConflict d_raiseConflict; - friend class ConstraintValue; + friend class Constraint_; public: @@ -799,13 +813,13 @@ public: ~ConstraintDatabase(); /** Adds a literal to the database. */ - Constraint addLiteral(TNode lit); + ConstraintP addLiteral(TNode lit); /** * If hasLiteral() is true, returns the constraint. * Otherwise, returns NullConstraint. */ - Constraint lookup(TNode literal) const; + ConstraintP lookup(TNode literal) const; /** * Returns true if the literal has been added to the database. @@ -818,10 +832,10 @@ public: return !d_toPropagate.empty(); } - Constraint nextPropagation(){ + ConstraintCP nextPropagation(){ Assert(hasMorePropagations()); - Constraint p = d_toPropagate.front(); + ConstraintCP p = d_toPropagate.front(); d_toPropagate.pop(); return p; @@ -831,8 +845,8 @@ public: bool variableDatabaseIsSetup(ArithVar v) const; void removeVariable(ArithVar v); - Node eeExplain(ConstConstraint c) const; - void eeExplain(ConstConstraint c, NodeBuilder<>& nb) const; + Node eeExplain(ConstraintCP c) const; + void eeExplain(ConstraintCP c, NodeBuilder<>& nb) const; /** * Returns a constraint with the variable v, the constraint type t, and a value @@ -843,8 +857,13 @@ public: * The returned value v is dominated: * If t is UpperBound, r <= v * If t is LowerBound, r >= v + * + * variableDatabaseIsSetup(v) must be true. */ - Constraint getBestImpliedBound(ArithVar v, ConstraintType t, const DeltaRational& r) const; + ConstraintP getBestImpliedBound(ArithVar v, ConstraintType t, const DeltaRational& r) const; + + /** Returns the constraint, if it exists */ + ConstraintP lookupConstraint(ArithVar v, ConstraintType t, const DeltaRational& r) const; /** * Returns a constraint with the variable v, the constraint type t and the value r. @@ -852,22 +871,18 @@ public: * If there is no such constraint, this constraint is added to the database. * */ - Constraint getConstraint(ArithVar v, ConstraintType t, const DeltaRational& r); + ConstraintP getConstraint(ArithVar v, ConstraintType t, const DeltaRational& r); /** * Returns a constraint of the given type for the value and variable * for the given ValueCollection, vc. * This is made if there is no such constraint. */ - Constraint ensureConstraint(ValueCollection& vc, ConstraintType t){ - if(vc.hasConstraintOfType(t)){ - return vc.getConstraintOfType(t); - }else{ - return getConstraint(vc.getVariable(), t, vc.getValue()); - } - } + ConstraintP ensureConstraint(ValueCollection& vc, ConstraintType t); + void deleteConstraintAndNegation(ConstraintP c); + /** * Outputs a minimal set of unate implications onto the vector for the variable. * This outputs lemmas of the general forms @@ -887,12 +902,12 @@ public: void outputUnateInequalityLemmas(std::vector<Node>& lemmas, ArithVar v) const; - void unatePropLowerBound(Constraint curr, Constraint prev); - void unatePropUpperBound(Constraint curr, Constraint prev); - void unatePropEquality(Constraint curr, Constraint prevLB, Constraint prevUB); + void unatePropLowerBound(ConstraintP curr, ConstraintP prev); + void unatePropUpperBound(ConstraintP curr, ConstraintP prev); + void unatePropEquality(ConstraintP curr, ConstraintP prevLB, ConstraintP prevUB); private: - void raiseUnateConflict(Constraint ant, Constraint cons); + void raiseUnateConflict(ConstraintP ant, ConstraintP cons); DenseSet d_reclaimable; @@ -907,6 +922,7 @@ private: }; /* ConstraintDatabase */ + }/* CVC4::theory::arith namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/arith/constraint_forward.h b/src/theory/arith/constraint_forward.h index f01c64b60..19326c0b3 100644 --- a/src/theory/arith/constraint_forward.h +++ b/src/theory/arith/constraint_forward.h @@ -16,23 +16,26 @@ ** minimize interaction between header files. **/ -#include "cvc4_private.h" - #ifndef __CVC4__THEORY__ARITH__CONSTRAINT_FORWARD_H #define __CVC4__THEORY__ARITH__CONSTRAINT_FORWARD_H +#include "cvc4_private.h" +#include <vector> + namespace CVC4 { namespace theory { namespace arith { -class ConstraintValue; -typedef ConstraintValue* Constraint; -typedef const ConstraintValue* const ConstConstraint; +class Constraint_; +typedef Constraint_* ConstraintP; +typedef const Constraint_* ConstraintCP; -static const Constraint NullConstraint = NULL; +const ConstraintP NullConstraint = NULL; class ConstraintDatabase; +typedef std::vector<ConstraintCP> ConstraintCPVec; + }/* CVC4::theory::arith namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/arith/cut_log.cpp b/src/theory/arith/cut_log.cpp new file mode 100644 index 000000000..f933516ba --- /dev/null +++ b/src/theory/arith/cut_log.cpp @@ -0,0 +1,691 @@ +#include "cvc4autoconfig.h" + + +#include "theory/arith/cut_log.h" +#include "theory/arith/approx_simplex.h" +#include "theory/arith/normal_form.h" +#include "theory/arith/constraint.h" +#include <math.h> +#include <cmath> +#include <map> +#include <limits.h> + +using namespace std; + +namespace CVC4 { +namespace theory { +namespace arith { + +NodeLog::const_iterator NodeLog::begin() const { return d_cuts.begin(); } +NodeLog::const_iterator NodeLog::end() const { return d_cuts.end(); } + +NodeLog& TreeLog::getNode(int nid) { + ToNodeMap::iterator i = d_toNode.find(nid); + Assert(i != d_toNode.end()); + return (*i).second; +} + +TreeLog::const_iterator TreeLog::begin() const { return d_toNode.begin(); } +TreeLog::const_iterator TreeLog::end() const { return d_toNode.end(); } + +int TreeLog::getExecutionOrd(){ + int res = next_exec_ord; + ++next_exec_ord; + return res; +} +void TreeLog::makeInactive(){ d_active = false; } +void TreeLog::makeActive(){ d_active = true; } +bool TreeLog::isActivelyLogging() const { return d_active; } + + +PrimitiveVec::PrimitiveVec() + : len(0) + , inds(NULL) + , coeffs(NULL) +{} + +PrimitiveVec::~PrimitiveVec(){ + clear(); +} +bool PrimitiveVec::initialized() const { + return inds != NULL; +} +void PrimitiveVec::clear() { + if(initialized()){ + delete[] inds; + delete[] coeffs; + len = 0; + inds = NULL; + coeffs = NULL; + } +} +void PrimitiveVec::setup(int l){ + Assert(!initialized()); + len = l; + inds = new int[1+len]; + coeffs = new double[1+len]; +} +void PrimitiveVec::print(std::ostream& out) const{ + Assert(initialized()); + out << len << " "; + out.precision(15); + for(int i = 1; i <= len; ++i){ + out << "["<< inds[i] <<", " << coeffs[i]<<"]"; + } +} +std::ostream& operator<<(std::ostream& os, const PrimitiveVec& pv){ + pv.print(os); + return os; +} + +CutInfo::CutInfo(CutInfoKlass kl, int eid, int o) + : d_klass(kl) + , d_execOrd(eid) + , d_poolOrd(o) + , d_cutType(kind::UNDEFINED_KIND) + , d_cutRhs() + , d_cutVec() + , d_mAtCreation(-1) + , d_rowId(-1) + , d_exactPrecision(NULL) + , d_explanation(NULL) +{} + +CutInfo::~CutInfo(){ + if(d_exactPrecision == NULL){ delete d_exactPrecision; } + if(d_explanation == NULL){ delete d_explanation; } +} + +int CutInfo::getId() const { + return d_execOrd; +} + +int CutInfo::getRowId() const{ + return d_rowId; +} + +void CutInfo::setRowId(int rid){ + d_rowId = rid; +} + +void CutInfo::print(ostream& out) const{ + out << "[CutInfo " << d_execOrd << " " << d_poolOrd + << " " << d_klass << " " << d_cutType << " " << d_cutRhs + << " "; + d_cutVec.print(out); + out << "]" << endl; +} + +PrimitiveVec& CutInfo::getCutVector(){ + return d_cutVec; +} + +const PrimitiveVec& CutInfo::getCutVector() const{ + return d_cutVec; +} + +// void CutInfo::init_cut(int l){ +// cut_vec.setup(l); +// } + +Kind CutInfo::getKind() const{ + return d_cutType; +} + +void CutInfo::setKind(Kind k){ + Assert(k == kind::LEQ || k == kind::GEQ); + d_cutType = k; +} + +double CutInfo::getRhs() const{ + return d_cutRhs; +} + +void CutInfo::setRhs(double r){ + d_cutRhs = r; +} + +bool CutInfo::reconstructed() const{ + return d_exactPrecision != NULL; +} + +CutInfoKlass CutInfo::getKlass() const{ + return d_klass; +} + +int CutInfo::poolOrdinal() const{ + return d_poolOrd; +} + +void CutInfo::setDimensions(int N, int M){ + d_mAtCreation = M; + d_N = N; +} + +int CutInfo::getN() const{ + return d_N; +} + +int CutInfo::getMAtCreation() const{ + return d_mAtCreation; +} + +/* Returns true if the cut has an explanation. */ +bool CutInfo::proven() const{ + return d_explanation != NULL; +} + +bool CutInfo::operator<(const CutInfo& o) const{ + return d_execOrd < o.d_execOrd; +} + + +void CutInfo::setReconstruction(const DenseVector& ep){ + Assert(!reconstructed()); + d_exactPrecision = new DenseVector(ep); +} + +void CutInfo::setExplanation(const ConstraintCPVec& ex){ + Assert(reconstructed()); + if(d_explanation == NULL){ + d_explanation = new ConstraintCPVec(ex); + }else{ + *d_explanation = ex; + } +} + +void CutInfo::swapExplanation(ConstraintCPVec& ex){ + Assert(reconstructed()); + Assert(!proven()); + if(d_explanation == NULL){ + d_explanation = new ConstraintCPVec(); + } + d_explanation->swap(ex); +} + +const DenseVector& CutInfo::getReconstruction() const { + Assert(reconstructed()); + return *d_exactPrecision; +} + +void CutInfo::clearReconstruction(){ + if(proven()){ + delete d_explanation; + d_explanation = NULL; + } + + if(reconstructed()){ + delete d_exactPrecision; + d_exactPrecision = NULL; + } + + Assert(!reconstructed()); + Assert(!proven()); +} + +const ConstraintCPVec& CutInfo::getExplanation() const { + Assert(proven()); + return *d_explanation; +} + +std::ostream& operator<<(std::ostream& os, const CutInfo& ci){ + ci.print(os); + return os; +} + +std::ostream& operator<<(std::ostream& out, CutInfoKlass kl){ + switch(kl){ + case MirCutKlass: + out << "MirCutKlass"; break; + case GmiCutKlass: + out << "GmiCutKlass"; break; + case BranchCutKlass: + out << "BranchCutKlass"; break; + case RowsDeletedKlass: + out << "RowDeletedKlass"; break; + case UnknownKlass: + out << "UnknownKlass"; break; + default: + out << "unexpected CutInfoKlass"; break; + } + return out; +} +bool NodeLog::isBranch() const{ + return d_brVar >= 0; +} + +NodeLog::NodeLog() + : d_nid(-1) + , d_parent(NULL) + , d_tl(NULL) + , d_cuts() + , d_rowIdsSelected() + , d_stat(Open) + , d_brVar(-1) + , d_brVal(0.0) + , d_downId(-1) + , d_upId(-1) + , d_rowId2ArithVar() +{} + +NodeLog::NodeLog(TreeLog* tl, int node, const RowIdMap& m) + : d_nid(node) + , d_parent(NULL) + , d_tl(tl) + , d_cuts() + , d_rowIdsSelected() + , d_stat(Open) + , d_brVar(-1) + , d_brVal(0.0) + , d_downId(-1) + , d_upId(-1) + , d_rowId2ArithVar(m) +{} + +NodeLog::NodeLog(TreeLog* tl, NodeLog* parent, int node) + : d_nid(node) + , d_parent(parent) + , d_tl(tl) + , d_cuts() + , d_rowIdsSelected() + , d_stat(Open) + , d_brVar(-1) + , d_brVal(0.0) + , d_downId(-1) + , d_upId(-1) + , d_rowId2ArithVar() +{} + +NodeLog::~NodeLog(){ + CutSet::iterator i = d_cuts.begin(), iend = d_cuts.end(); + for(; i != iend; ++i){ + CutInfo* c = *i; + delete c; + } + d_cuts.clear(); + Assert(d_cuts.empty()); +} + +std::ostream& operator<<(std::ostream& os, const NodeLog& nl){ + nl.print(os); + return os; +} + +void NodeLog::copyParentRowIds() { + Assert(d_parent != NULL); + d_rowId2ArithVar = d_parent->d_rowId2ArithVar; +} + +int NodeLog::branchVariable() const { + return d_brVar; +} +double NodeLog::branchValue() const{ + return d_brVal; +} +int NodeLog::getNodeId() const { + return d_nid; +} +int NodeLog::getDownId() const{ + return d_downId; +} +int NodeLog::getUpId() const{ + return d_upId; +} +void NodeLog::addSelected(int ord, int sel){ + Assert(d_rowIdsSelected.find(ord) == d_rowIdsSelected.end()); + d_rowIdsSelected[ord] = sel; + Debug("approx::nodelog") << "addSelected("<< ord << ", "<< sel << ")" << endl; +} +void NodeLog::applySelected() { + CutSet::iterator iter = d_cuts.begin(), iend = d_cuts.end(), todelete; + while(iter != iend){ + CutInfo* curr = *iter; + int poolOrd = curr->poolOrdinal(); + if(curr->getRowId() >= 0 ){ + // selected previously, kip + ++iter; + }else if(curr->getKlass() == RowsDeletedKlass){ + // skip + ++iter; + }else if(curr->getKlass() == BranchCutKlass){ + // skip + ++iter; + }else if(d_rowIdsSelected.find(poolOrd) == d_rowIdsSelected.end()){ + todelete = iter; + ++iter; + d_cuts.erase(todelete); + delete curr; + }else{ + Debug("approx::nodelog") << "applySelected " << curr->getId() << " " << poolOrd << "->" << d_rowIdsSelected[poolOrd] << endl; + curr->setRowId( d_rowIdsSelected[poolOrd] ); + ++iter; + } + } + d_rowIdsSelected.clear(); +} + +void NodeLog::applyRowsDeleted(const RowsDeleted& rd) { + std::map<int, CutInfo*> currInOrd; //sorted + + const PrimitiveVec& cv = rd.getCutVector(); + std::vector<int> sortedRemoved (cv.inds+1, cv.inds+cv.len+1); + sortedRemoved.push_back(INT_MAX); + std::sort(sortedRemoved.begin(), sortedRemoved.end()); + + if(Debug.isOn("approx::nodelog")){ + Debug("approx::nodelog") << "Removing #" << sortedRemoved.size()<< "..."; + for(unsigned k = 0; k<sortedRemoved.size(); k++){ + Debug("approx::nodelog") << ", " << sortedRemoved[k]; + } + Debug("approx::nodelog") << endl; + Debug("approx::nodelog") << "cv.len" << cv.len << endl; + } + + int min = sortedRemoved.front(); + + CutSet::iterator iter = d_cuts.begin(), iend = d_cuts.end(); + while(iter != iend){ + CutInfo* curr= *iter; + if(curr->getId() < rd.getId()){ + if(d_rowId2ArithVar.find(curr->getRowId()) != d_rowId2ArithVar.end()){ + if(curr->getRowId() >= min){ + currInOrd.insert(make_pair(curr->getRowId(), curr)); + } + } + } + ++iter; + } + + RowIdMap::const_iterator i, end; + i=d_rowId2ArithVar.begin(), end = d_rowId2ArithVar.end(); + for(; i != end; ++i){ + int key = (*i).first; + if(key >= min){ + if(currInOrd.find(key) == currInOrd.end()){ + CutInfo* null = NULL; + currInOrd.insert(make_pair(key, null)); + } + } + } + + + + std::map<int, CutInfo*>::iterator j, jend; + + int posInSorted = 0; + for(j = currInOrd.begin(), jend=currInOrd.end(); j!=jend; ++j){ + int origOrd = (*j).first; + ArithVar v = d_rowId2ArithVar[origOrd]; + int headRemovedOrd = sortedRemoved[posInSorted]; + while(headRemovedOrd < origOrd){ + ++posInSorted; + headRemovedOrd = sortedRemoved[posInSorted]; + } + // headRemoveOrd >= origOrd + Assert(headRemovedOrd >= origOrd); + + CutInfo* ci = (*j).second; + if(headRemovedOrd == origOrd){ + + if(ci == NULL){ + Debug("approx::nodelog") << "deleting from above because of " << rd << endl; + Debug("approx::nodelog") << "had " << origOrd << " <-> " << v << endl; + d_rowId2ArithVar.erase(origOrd); + }else{ + Debug("approx::nodelog") << "deleting " << ci << " because of " << rd << endl; + Debug("approx::nodelog") << "had " << origOrd << " <-> " << v << endl; + d_rowId2ArithVar.erase(origOrd); + ci->setRowId(-1); + } + }else{ + Assert(headRemovedOrd > origOrd); + // headRemoveOrd > origOrd + int newOrd = origOrd - posInSorted; + Assert(newOrd > 0); + if(ci == NULL){ + Debug("approx::nodelog") << "shifting above down due to " << rd << endl; + Debug("approx::nodelog") << "had " << origOrd << " <-> " << v << endl; + Debug("approx::nodelog") << "now have " << newOrd << " <-> " << v << endl; + d_rowId2ArithVar.erase(origOrd); + mapRowId(newOrd, v); + }else{ + Debug("approx::nodelog") << "shifting " << ci << " down due to " << rd << endl; + Debug("approx::nodelog") << "had " << origOrd << " <-> " << v << endl; + Debug("approx::nodelog") << "now have " << newOrd << " <-> " << v << endl; + ci->setRowId(newOrd); + d_rowId2ArithVar.erase(origOrd); + mapRowId(newOrd, v); + } + } + } + +} + +// void NodeLog::adjustRowId(CutInfo& ci, const RowsDeleted& rd) { +// int origRowId = ci.getRowId(); +// int newRowId = ci.getRowId(); +// ArithVar v = d_rowId2ArithVar[origRowId]; + +// const PrimitiveVec& cv = rd.getCutVector(); + +// for(int j = 1, N = cv.len; j <= N; j++){ +// int ind = cv.inds[j]; +// if(ind == origRowId){ +// newRowId = -1; +// break; +// }else if(ind < origRowId){ +// newRowId--; +// } +// } + +// if(newRowId < 0){ +// cout << "deleting " << ci << " because of " << rd << endl; +// cout << "had " << origRowId << " <-> " << v << endl; +// d_rowId2ArithVar.erase(origRowId); +// ci.setRowId(-1); +// }else if(newRowId != origRowId){ +// cout << "adjusting " << ci << " because of " << rd << endl; +// cout << "had " << origRowId << " <-> " << v << endl; +// cout << "now have " << newRowId << " <-> " << v << endl; +// d_rowId2ArithVar.erase(origRowId); +// ci.setRowId(newRowId); +// mapRowId(newRowId, v); +// }else{ +// cout << "row id unchanged " << ci << " because of " << rd << endl; +// } +// } + + +ArithVar NodeLog::lookupRowId(int rowId) const{ + RowIdMap::const_iterator i = d_rowId2ArithVar.find(rowId); + if(i == d_rowId2ArithVar.end()){ + return ARITHVAR_SENTINEL; + }else{ + return (*i).second; + } +} + +void NodeLog::mapRowId(int rowId, ArithVar v){ + Assert(lookupRowId(rowId) == ARITHVAR_SENTINEL); + Debug("approx::nodelog") + << "On " << getNodeId() + << " adding row id " << rowId << " <-> " << v << endl; + d_rowId2ArithVar[rowId] = v; +} + + + +void NodeLog::addCut(CutInfo* ci){ + Assert(ci != NULL); + d_cuts.insert(ci); +} + +void NodeLog::print(ostream& o) const{ + o << "[n" << getNodeId(); + for(const_iterator iter = begin(), iend = end(); iter != iend; ++iter ){ + CutInfo* cut = *iter; + o << ", " << cut->poolOrdinal(); + if(cut->getRowId() >= 0){ + o << " " << cut->getRowId(); + } + } + o << "]" << std::endl; +} + +void NodeLog::closeNode(){ + Assert(d_stat == Open); + d_stat = Closed; +} + +void NodeLog::setBranch(int br, double val, int d, int u){ + Assert(d_stat == Open); + d_brVar = br; + d_brVal = val; + d_downId = d; + d_upId = u; + d_stat = Branched; +} + +TreeLog::TreeLog() + : next_exec_ord(0) + , d_toNode() + , d_branches() + , d_numCuts(0) + , d_active(false) +{ + NodeLog::RowIdMap empty; + reset(empty); +} + +int TreeLog::getRootId() const{ + return 1; +} + +NodeLog& TreeLog::getRootNode(){ + return getNode(getRootId()); +} + +void TreeLog::clear(){ + next_exec_ord = 0; + d_toNode.clear(); + d_branches.purge(); + + d_numCuts = 0; + + // add root +} + +void TreeLog::reset(const NodeLog::RowIdMap& m){ + clear(); + d_toNode.insert(make_pair(getRootId(), NodeLog(this, getRootId(), m))); +} + +void TreeLog::addCut(){ d_numCuts++; } +uint32_t TreeLog::cutCount() const { return d_numCuts; } +void TreeLog::logBranch(uint32_t x){ + d_branches.add(x); +} +uint32_t TreeLog::numBranches(uint32_t x){ + return d_branches.count(x); +} + +void TreeLog::branch(int nid, int br, double val, int dn, int up){ + NodeLog& nl = getNode(nid); + nl.setBranch(br, val, dn, up); + + d_toNode.insert(make_pair(dn, NodeLog(this, &nl, dn))); + d_toNode.insert(make_pair(up, NodeLog(this, &nl, up))); +} + +void TreeLog::close(int nid){ + NodeLog& nl = getNode(nid); + nl.closeNode(); +} + + + +// void TreeLog::applySelected() { +// std::map<int, NodeLog>::iterator iter, end; +// for(iter = d_toNode.begin(), end = d_toNode.end(); iter != end; ++iter){ +// NodeLog& onNode = (*iter).second; +// //onNode.applySelected(); +// } +// } + +void TreeLog::print(ostream& o) const{ + o << "TreeLog: " << d_toNode.size() << std::endl; + for(const_iterator iter = begin(), iend = end(); iter != iend; ++iter){ + const NodeLog& onNode = (*iter).second; + onNode.print(o); + } +} + +void TreeLog::applyRowsDeleted(int nid, const RowsDeleted& rd){ + NodeLog& nl = getNode(nid); + nl.applyRowsDeleted(rd); +} + +void TreeLog::mapRowId(int nid, int ind, ArithVar v){ + NodeLog& nl = getNode(nid); + nl.mapRowId(ind, v); +} + +void DenseVector::purge() { + lhs.purge(); + rhs = Rational(0); +} + +RowsDeleted::RowsDeleted(int execOrd, int nrows, const int num[]) + : CutInfo(RowsDeletedKlass, execOrd, 0) +{ + d_cutVec.setup(nrows); + for(int j=1; j <= nrows; j++){ + d_cutVec.coeffs[j] = 0; + d_cutVec.inds[j] = num[j]; + } +} + +BranchCutInfo::BranchCutInfo(int execOrd, int br, Kind dir, double val) + : CutInfo(BranchCutKlass, execOrd, 0) +{ + d_cutVec.setup(1); + d_cutVec.inds[1] = br; + d_cutVec.coeffs[1] = +1.0; + d_cutRhs = val; + d_cutType = dir; +} + +void TreeLog::printBranchInfo(ostream& os) const{ + uint32_t total = 0; + DenseMultiset::const_iterator iter = d_branches.begin(), iend = d_branches.end(); + for(; iter != iend; ++iter){ + uint32_t el = *iter; + total += el; + } + os << "printBranchInfo() : " << total << endl; + iter = d_branches.begin(), iend = d_branches.end(); + for(; iter != iend; ++iter){ + uint32_t el = *iter; + os << "["<<el <<", " << d_branches.count(el) << "]"; + } + os << endl; +} + + +void DenseVector::print(std::ostream& os) const { + os << rhs << " + "; + print(os, lhs); +} +void DenseVector::print(ostream& out, const DenseMap<Rational>& v){ + out << "[DenseVec len " << v.size(); + DenseMap<Rational>::const_iterator iter, end; + for(iter = v.begin(), end = v.end(); iter != end; ++iter){ + ArithVar x = *iter; + out << ", "<< x << " " << v[x]; + } + out << "]"; +} + +}/* CVC4::theory::arith namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/arith/cut_log.h b/src/theory/arith/cut_log.h new file mode 100644 index 000000000..123313617 --- /dev/null +++ b/src/theory/arith/cut_log.h @@ -0,0 +1,281 @@ + +#include "cvc4_private.h" + +#pragma once + +#include "expr/kind.h" +#include "util/statistics_registry.h" +#include "theory/arith/arithvar.h" +#include "theory/arith/constraint_forward.h" +#include "util/dense_map.h" +#include <vector> +#include <map> +#include <set> +#include <ext/hash_map> + +namespace CVC4 { +namespace theory { +namespace arith { + +/** A low level vector of indexed doubles. */ +struct PrimitiveVec { + int len; + int* inds; + double* coeffs; + PrimitiveVec(); + ~PrimitiveVec(); + bool initialized() const; + void clear(); + void setup(int l); + void print(std::ostream& out) const; +}; +std::ostream& operator<<(std::ostream& os, const PrimitiveVec& pv); + +struct DenseVector { + DenseMap<Rational> lhs; + Rational rhs; + void purge(); + void print(std::ostream& os) const; + + static void print(std::ostream& os, const DenseMap<Rational>& lhs); +}; + +/** The different kinds of cuts. */ +enum CutInfoKlass{ MirCutKlass, GmiCutKlass, BranchCutKlass, + RowsDeletedKlass, + UnknownKlass}; +std::ostream& operator<<(std::ostream& os, CutInfoKlass kl); + +/** A general class for describing a cut. */ +class CutInfo { +protected: + CutInfoKlass d_klass; + int d_execOrd; + + int d_poolOrd; /* cut's ordinal in the current node pool */ + Kind d_cutType; /* Lowerbound, upperbound or undefined. */ + double d_cutRhs; /* right hand side of the cut */ + PrimitiveVec d_cutVec; /* vector of the cut */ + + /** + * The number of rows at the time the cut was made. + * This is required to descramble indices after the fact! + */ + int d_mAtCreation; + + /** This is the number of structural variables. */ + int d_N; + + /** if selected, make this non-zero */ + int d_rowId; + + /* If the cut has been successfully created, + * the cut is stored in exact precision in d_exactPrecision. + * If the cut has not yet been proven, this is null. + */ + DenseVector* d_exactPrecision; + + ConstraintCPVec* d_explanation; + +public: + CutInfo(CutInfoKlass kl, int cutid, int ordinal); + + virtual ~CutInfo(); + + int getId() const; + + int getRowId() const; + void setRowId(int rid); + + void print(std::ostream& out) const; + //void init_cut(int l); + PrimitiveVec& getCutVector(); + const PrimitiveVec& getCutVector() const; + + Kind getKind() const; + void setKind(Kind k); + + + void setRhs(double r); + double getRhs() const; + + CutInfoKlass getKlass() const; + int poolOrdinal() const; + + void setDimensions(int N, int M); + int getN() const; + int getMAtCreation() const; + + bool operator<(const CutInfo& o) const; + + /* Returns true if the cut was successfully made in exact precision.*/ + bool reconstructed() const; + + /* Returns true if the cut has an explanation. */ + bool proven() const; + + void setReconstruction(const DenseVector& ep); + void setExplanation(const ConstraintCPVec& ex); + void swapExplanation(ConstraintCPVec& ex); + + const DenseVector& getReconstruction() const; + const ConstraintCPVec& getExplanation() const; + + void clearReconstruction(); +}; +std::ostream& operator<<(std::ostream& os, const CutInfo& ci); + +struct BranchCutInfo : public CutInfo { + BranchCutInfo(int execOrd, int br, Kind dir, double val); +}; + +struct RowsDeleted : public CutInfo { + RowsDeleted(int execOrd, int nrows, const int num[]); +}; + +class TreeLog; + +class NodeLog { +private: + int d_nid; + NodeLog* d_parent; /* If null this is the root */ + TreeLog* d_tl; /* TreeLog containing the node. */ + + struct CmpCutPointer{ + int operator()(const CutInfo* a, const CutInfo* b) const{ + return *a < *b; + } + }; + typedef std::set<CutInfo*, CmpCutPointer> CutSet; + CutSet d_cuts; + std::map<int, int> d_rowIdsSelected; + + enum Status {Open, Closed, Branched}; + Status d_stat; + + int d_brVar; // branching variable + double d_brVal; + int d_downId; + int d_upId; + +public: + typedef __gnu_cxx::hash_map<int, ArithVar> RowIdMap; +private: + RowIdMap d_rowId2ArithVar; + +public: + NodeLog(); /* default constructor. */ + NodeLog(TreeLog* tl, int node, const RowIdMap& m); /* makes a root node. */ + NodeLog(TreeLog* tl, NodeLog* parent, int node);/* makes a non-root node. */ + + ~NodeLog(); + + int getNodeId() const; + void addSelected(int ord, int sel); + void applySelected(); + void addCut(CutInfo* ci); + void print(std::ostream& o) const; + + bool isRoot() const; + const NodeLog& getParent() const; + + void copyParentRowIds(); + + bool isBranch() const; + int branchVariable() const; + double branchValue() const; + + typedef CutSet::const_iterator const_iterator; + const_iterator begin() const; + const_iterator end() const; + + void setBranch(int br, double val, int dn, int up); + void closeNode(); + + int getDownId() const; + int getUpId() const; + + /** + * Looks up a row id to the appropraite arith variable. + * Be careful these are deleted in context during replay! + * failure returns ARITHVAR_SENTINEL */ + ArithVar lookupRowId(int rowId) const; + + /** + * Maps a row id to an arithvar. + * Be careful these are deleted in context during replay! + */ + void mapRowId(int rowid, ArithVar v); + void applyRowsDeleted(const RowsDeleted& rd); + +}; +std::ostream& operator<<(std::ostream& os, const NodeLog& nl); + +class ApproximateSimplex; +class TreeLog { +private: + ApproximateSimplex* d_generator; + + int next_exec_ord; + typedef std::map<int, NodeLog> ToNodeMap; + ToNodeMap d_toNode; + DenseMultiset d_branches; + + uint32_t d_numCuts; + + bool d_active; + +public: + TreeLog(); + + NodeLog& getNode(int nid); + void branch(int nid, int br, double val, int dn, int up); + void close(int nid); + + //void applySelected(); + void print(std::ostream& o) const; + + typedef ToNodeMap::const_iterator const_iterator; + const_iterator begin() const; + const_iterator end() const; + + int getExecutionOrd(); + + void reset(const NodeLog::RowIdMap& m); + + // Applies rd tp to the node with id nid + void applyRowsDeleted(int nid, const RowsDeleted& rd); + + // Synonym for getNode(nid).mapRowId(ind, v) + void mapRowId(int nid, int ind, ArithVar v); + +private: + void clear(); + +public: + void makeInactive(); + void makeActive(); + + bool isActivelyLogging() const; + + void addCut(); + uint32_t cutCount() const; + + void logBranch(uint32_t x); + uint32_t numBranches(uint32_t x); + + int getRootId() const; + + uint32_t numNodes() const{ + return d_toNode.size(); + } + + NodeLog& getRootNode(); + void printBranchInfo(std::ostream& os) const; +}; + + + +}/* CVC4::theory::arith namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/arith/dio_solver.cpp b/src/theory/arith/dio_solver.cpp index 39c2d859b..c9f9df727 100644 --- a/src/theory/arith/dio_solver.cpp +++ b/src/theory/arith/dio_solver.cpp @@ -281,7 +281,7 @@ void DioSolver::moveMinimumByAbsToQueueFront(){ size_t N = d_currentF.size(); for(size_t i=1; i < N; ++i){ Monomial curr = d_trail[d_currentF[i]].d_minimalMonomial; - if(curr.absLessThan(minMonomial)){ + if(curr.absCmp(minMonomial) < 0){ indexInQueue = i; minMonomial = curr; } diff --git a/src/theory/arith/dio_solver.h b/src/theory/arith/dio_solver.h index 5039f826c..32b8382fa 100644 --- a/src/theory/arith/dio_solver.h +++ b/src/theory/arith/dio_solver.h @@ -102,17 +102,17 @@ private: }; context::CDList<Constraint> d_trail; - /** Compare by d_minimal. */ - struct TrailMinimalCoefficientOrder { - const context::CDList<Constraint>& d_trail; - TrailMinimalCoefficientOrder(const context::CDList<Constraint>& trail): - d_trail(trail) - {} - - bool operator()(TrailIndex i, TrailIndex j){ - return d_trail[i].d_minimalMonomial.absLessThan(d_trail[j].d_minimalMonomial); - } - }; + // /** Compare by d_minimal. */ + // struct TrailMinimalCoefficientOrder { + // const context::CDList<Constraint>& d_trail; + // TrailMinimalCoefficientOrder(const context::CDList<Constraint>& trail): + // d_trail(trail) + // {} + + // bool operator()(TrailIndex i, TrailIndex j){ + // return d_trail[i].d_minimalMonomial.absLessThan(d_trail[j].d_minimalMonomial); + // } + // }; /** * A substitution is stored as a constraint in the trail together with diff --git a/src/theory/arith/error_set.cpp b/src/theory/arith/error_set.cpp index dea78acf7..6d341ed12 100644 --- a/src/theory/arith/error_set.cpp +++ b/src/theory/arith/error_set.cpp @@ -39,7 +39,7 @@ ErrorInformation::ErrorInformation() Debug("arith::error::mem") << "def constructor " << d_variable << " " << d_amount << endl; } -ErrorInformation::ErrorInformation(ArithVar var, Constraint vio, int sgn) +ErrorInformation::ErrorInformation(ArithVar var, ConstraintP vio, int sgn) : d_variable(var) , d_violated(vio) , d_sgn(sgn) @@ -105,7 +105,7 @@ ErrorInformation& ErrorInformation::operator=(const ErrorInformation& ei){ return *this; } -void ErrorInformation::reset(Constraint c, int sgn){ +void ErrorInformation::reset(ConstraintP c, int sgn){ Assert(!isRelaxed()); Assert(c != NullConstraint); d_violated = c; @@ -272,7 +272,7 @@ void ErrorSet::transitionVariableOutOfError(ArithVar v) { ErrorInformation& ei = d_errInfo.get(v); Assert(ei.debugInitialized()); if(ei.isRelaxed()){ - Constraint viol = ei.getViolated(); + ConstraintP viol = ei.getViolated(); if(ei.sgn() > 0){ d_variables.setLowerBoundConstraint(viol); }else{ @@ -293,7 +293,7 @@ void ErrorSet::transitionVariableIntoError(ArithVar v) { Assert(inconsistent(v)); bool vilb = d_variables.cmpAssignmentLowerBound(v) < 0; int sgn = vilb ? 1 : -1; - Constraint c = vilb ? + ConstraintP c = vilb ? d_variables.getLowerBoundConstraint(v) : d_variables.getUpperBoundConstraint(v); d_errInfo.set(v, ErrorInformation(v, c, sgn)); ErrorInformation& ei = d_errInfo.get(v); @@ -373,7 +373,7 @@ int ErrorSet::popSignal() { Assert(!vilb || !viub); int currSgn = vilb ? 1 : -1; if(currSgn != prevSgn){ - Constraint curr = vilb ? d_variables.getLowerBoundConstraint(back) + ConstraintP curr = vilb ? d_variables.getLowerBoundConstraint(back) : d_variables.getUpperBoundConstraint(back); ei.reset(curr, currSgn); } diff --git a/src/theory/arith/error_set.h b/src/theory/arith/error_set.h index d1b692cb4..b87282ba0 100644 --- a/src/theory/arith/error_set.h +++ b/src/theory/arith/error_set.h @@ -120,7 +120,7 @@ private: * This needs to be saved in case that the * violated constraint */ - Constraint d_violated; + ConstraintP d_violated; /** * This is the sgn of the first derivate the variable must move to satisfy @@ -155,12 +155,12 @@ private: public: ErrorInformation(); - ErrorInformation(ArithVar var, Constraint vio, int sgn); + ErrorInformation(ArithVar var, ConstraintP vio, int sgn); ~ErrorInformation(); ErrorInformation(const ErrorInformation& ei); ErrorInformation& operator=(const ErrorInformation& ei); - void reset(Constraint c, int sgn); + void reset(ConstraintP c, int sgn); inline ArithVar getVariable() const { return d_variable; } @@ -192,7 +192,7 @@ public: } inline const FocusSetHandle& getHandle() const{ return d_handle; } - inline Constraint getViolated() const { return d_violated; } + inline ConstraintP getViolated() const { return d_violated; } bool debugInitialized() const { return @@ -389,7 +389,7 @@ public: return d_errInfo[a].getMetric(); } - Constraint getViolated(ArithVar a) const { + ConstraintP getViolated(ArithVar a) const { return d_errInfo[a].getViolated(); } diff --git a/src/theory/arith/fc_simplex.cpp b/src/theory/arith/fc_simplex.cpp index c0bffdf07..70a322959 100644 --- a/src/theory/arith/fc_simplex.cpp +++ b/src/theory/arith/fc_simplex.cpp @@ -536,7 +536,7 @@ void FCSimplexDecisionProcedure::updateAndSignal(const UpdateInfo& selected, Wit } if(selected.describesPivot()){ - Constraint limiting = selected.limiting(); + ConstraintP limiting = selected.limiting(); ArithVar basic = limiting->getVariable(); Assert(d_linEq.basicIsTracked(basic)); d_linEq.pivotAndUpdate(basic, nonbasic, limiting->getValue()); diff --git a/src/theory/arith/linear_equality.cpp b/src/theory/arith/linear_equality.cpp index 5817a3629..f4c1ae10c 100644 --- a/src/theory/arith/linear_equality.cpp +++ b/src/theory/arith/linear_equality.cpp @@ -494,7 +494,7 @@ const Tableau::Entry* LinearEqualityModule::rowLacksBound(RowIndex ridx, bool ro return NULL; } -void LinearEqualityModule::propagateBasicFromRow(Constraint c){ +void LinearEqualityModule::propagateBasicFromRow(ConstraintP c){ Assert(c != NullConstraint); Assert(c->isUpperBound() || c->isLowerBound()); Assert(!c->assertedToTheTheory()); @@ -504,12 +504,12 @@ void LinearEqualityModule::propagateBasicFromRow(Constraint c){ ArithVar basic = c->getVariable(); RowIndex ridx = d_tableau.basicToRowIndex(basic); - vector<Constraint> bounds; + ConstraintCPVec bounds; propagateRow(bounds, ridx, upperBound, c); c->impliedBy(bounds); } -void LinearEqualityModule::propagateRow(vector<Constraint>& into, RowIndex ridx, bool rowUp, Constraint c){ +void LinearEqualityModule::propagateRow(ConstraintCPVec& into, RowIndex ridx, bool rowUp, ConstraintP c){ Assert(!c->assertedToTheTheory()); Assert(c->canBePropagated()); Assert(!c->hasProof()); @@ -529,7 +529,7 @@ void LinearEqualityModule::propagateRow(vector<Constraint>& into, RowIndex ridx, int sgn = a_ij.sgn(); Assert(sgn != 0); bool selectUb = rowUp ? (sgn > 0) : (sgn < 0); - Constraint bound = selectUb + ConstraintCP bound = selectUb ? d_variables.getUpperBoundConstraint(nonbasic) : d_variables.getLowerBoundConstraint(nonbasic); @@ -541,12 +541,12 @@ void LinearEqualityModule::propagateRow(vector<Constraint>& into, RowIndex ridx, << v << ") done" << endl; } -Constraint LinearEqualityModule::weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic) const { +ConstraintP LinearEqualityModule::weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic) const { int sgn = coeff.sgn(); bool ub = aboveUpper?(sgn < 0) : (sgn > 0); - Constraint c = ub ? + ConstraintP c = ub ? d_variables.getUpperBoundConstraint(v) : d_variables.getLowerBoundConstraint(v); @@ -556,7 +556,7 @@ Constraint LinearEqualityModule::weakestExplanation(bool aboveUpper, DeltaRation weakened = false; - Constraint weaker = ub? + ConstraintP weaker = ub? c->getStrictlyWeakerUpperBound(true, true): c->getStrictlyWeakerLowerBound(true, true); @@ -591,7 +591,7 @@ Constraint LinearEqualityModule::weakestExplanation(bool aboveUpper, DeltaRation return c; } -Node LinearEqualityModule::minimallyWeakConflict(bool aboveUpper, ArithVar basicVar) const { +void LinearEqualityModule::minimallyWeakConflict(bool aboveUpper, ArithVar basicVar, RaiseConflict& rc) const { TimerStat::CodeTimer codeTimer(d_statistics.d_weakenTime); const DeltaRational& assignment = d_variables.getAssignment(basicVar); @@ -606,29 +606,25 @@ Node LinearEqualityModule::minimallyWeakConflict(bool aboveUpper, ArithVar basic surplus = d_variables.getLowerBound(basicVar) - assignment; } - NodeBuilder<> conflict(kind::AND); bool anyWeakenings = false; for(Tableau::RowIterator i = d_tableau.basicRowIterator(basicVar); !i.atEnd(); ++i){ const Tableau::Entry& entry = *i; ArithVar v = entry.getColVar(); const Rational& coeff = entry.getCoefficient(); bool weakening = false; - Constraint c = weakestExplanation(aboveUpper, surplus, v, coeff, weakening, basicVar); + ConstraintP c = weakestExplanation(aboveUpper, surplus, v, coeff, weakening, basicVar); Debug("weak") << "weak : " << weakening << " " << c->assertedToTheTheory() << " " << d_variables.getAssignment(v) << " " - << c << endl - << c->explainForConflict() << endl; + << c << endl; anyWeakenings = anyWeakenings || weakening; - Debug("weak") << "weak : " << c->explainForConflict() << endl; - c->explainForConflict(conflict); + rc.addConstraint(c); } ++d_statistics.d_weakeningAttempts; if(anyWeakenings){ ++d_statistics.d_weakeningSuccesses; } - return conflict; } ArithVar LinearEqualityModule::minVarOrder(ArithVar x, ArithVar y) const { @@ -787,7 +783,7 @@ bool LinearEqualityModule::basicsAtBounds(const UpdateInfo& u) const { int coeffSgn = u.getCoefficient().sgn(); int nbdir = u.nonbasicDirection(); - Constraint c = u.limiting(); + ConstraintP c = u.limiting(); int toUB = (c->getType() == UpperBound || c->getType() == Equality) ? 1 : 0; int toLB = (c->getType() == LowerBound || @@ -886,7 +882,7 @@ bool LinearEqualityModule::accumulateBorder(const Tableau::Entry& entry, bool ub Assert(basicIsTracked(currBasic)); - Constraint bound = ub ? + ConstraintP bound = ub ? d_variables.getUpperBoundConstraint(currBasic): d_variables.getLowerBoundConstraint(currBasic); @@ -1003,7 +999,7 @@ UpdateInfo LinearEqualityModule::mkConflictUpdate(const Tableau::Entry& entry, b ArithVar currBasic = d_tableau.rowIndexToBasic(entry.getRowIndex()); ArithVar nb = entry.getColVar(); - Constraint bound = ub ? + ConstraintP bound = ub ? d_variables.getUpperBoundConstraint(currBasic): d_variables.getLowerBoundConstraint(currBasic); @@ -1031,14 +1027,14 @@ UpdateInfo LinearEqualityModule::speculativeUpdate(ArithVar nb, const Rational& Debug("speculativeUpdate") << "focusCoeff " << focusCoeff << endl; if(d_variables.hasUpperBound(nb)){ - Constraint ub = d_variables.getUpperBoundConstraint(nb); + ConstraintP ub = d_variables.getUpperBoundConstraint(nb); d_upperBoundDifference = ub->getValue() - d_variables.getAssignment(nb); Border border(ub, d_upperBoundDifference, false, NULL, true); Debug("handleBorders") << "push back increasing " << border << endl; d_increasing.push_back(border); } if(d_variables.hasLowerBound(nb)){ - Constraint lb = d_variables.getLowerBoundConstraint(nb); + ConstraintP lb = d_variables.getLowerBoundConstraint(nb); d_lowerBoundDifference = lb->getValue() - d_variables.getAssignment(nb); Border border(lb, d_lowerBoundDifference, false, NULL, false); Debug("handleBorders") << "push back decreasing " << border << endl; diff --git a/src/theory/arith/linear_equality.h b/src/theory/arith/linear_equality.h index 293a0ddad..804ad29ac 100644 --- a/src/theory/arith/linear_equality.h +++ b/src/theory/arith/linear_equality.h @@ -46,7 +46,7 @@ namespace arith { struct Border{ // The constraint for the border - Constraint d_bound; + ConstraintP d_bound; // The change to the nonbasic to reach the border DeltaRational d_diff; @@ -65,11 +65,11 @@ struct Border{ d_bound(NullConstraint) // ignore the other values {} - Border(Constraint l, const DeltaRational& diff, bool areFixing, const Tableau::Entry* en, bool ub): + Border(ConstraintP l, const DeltaRational& diff, bool areFixing, const Tableau::Entry* en, bool ub): d_bound(l), d_diff(diff), d_areFixing(areFixing), d_entry(en), d_upperbound(ub) {} - Border(Constraint l, const DeltaRational& diff, bool areFixing, bool ub): + Border(ConstraintP l, const DeltaRational& diff, bool areFixing, bool ub): d_bound(l), d_diff(diff), d_areFixing(areFixing), d_entry(NULL), d_upperbound(ub) {} bool operator<(const Border& other) const{ @@ -414,13 +414,13 @@ public: * The constraint on a basic variable b is implied by the constraints * on its row. This is a wrapper for propagateRow(). */ - void propagateBasicFromRow(Constraint c); + void propagateBasicFromRow(ConstraintP c); /** * Exports either the explanation of an upperbound or a lower bound * of the basic variable basic, using the non-basic variables in the row. */ - void propagateRow(std::vector<Constraint>& into, RowIndex ridx, bool rowUp, Constraint c); + void propagateRow(ConstraintCPVec& into, RowIndex ridx, bool rowUp, ConstraintP c); /** * Computes the value of a basic variable using the assignments @@ -592,26 +592,26 @@ private: * with the weakest possible constraint that is consistent with the surplus * surplus. */ - Constraint weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, + ConstraintP weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic) const; public: /** * Constructs a minimally weak conflict for the basic variable basicVar. */ - Node minimallyWeakConflict(bool aboveUpper, ArithVar basicVar) const; + void minimallyWeakConflict(bool aboveUpper, ArithVar basicVar, RaiseConflict& rc) const; /** * Given a non-basic variable that is know to have a conflict on it, * construct and return a conflict. * Follows section 4.2 in the CAV06 paper. */ - inline Node generateConflictAboveUpperBound(ArithVar conflictVar) const { - return minimallyWeakConflict(true, conflictVar); + inline void generateConflictAboveUpperBound(ArithVar conflictVar, RaiseConflict& rc) const { + minimallyWeakConflict(true, conflictVar, rc); } - inline Node generateConflictBelowLowerBound(ArithVar conflictVar) const { - return minimallyWeakConflict(false, conflictVar); + inline void generateConflictBelowLowerBound(ArithVar conflictVar, RaiseConflict& rc) const { + minimallyWeakConflict(false, conflictVar, rc); } /** diff --git a/src/theory/arith/matrix.h b/src/theory/arith/matrix.h index d93b6986e..084281c04 100644 --- a/src/theory/arith/matrix.h +++ b/src/theory/arith/matrix.h @@ -362,16 +362,18 @@ public: template <class T> class Matrix { -protected: - +public: typedef MatrixEntry<T> Entry; +protected: typedef CVC4::theory::arith::RowVector<T> RowVectorT; - typedef typename RowVectorT::const_iterator RowIterator; - typedef CVC4::theory::arith::ColumnVector<T> ColumnVectorT; + +public: + typedef typename RowVectorT::const_iterator RowIterator; typedef typename ColumnVectorT::const_iterator ColIterator; +protected: // RowTable : RowID |-> RowVector typedef std::vector< RowVectorT > RowTable; RowTable d_rows; @@ -532,6 +534,12 @@ public: d_columns.push_back(ColumnVector<T>(&d_entries)); } + void increaseSizeTo(size_t s){ + while(getNumColumns() < s){ + increaseSize(); + } + } + const RowVector<T>& getRow(RowIndex r) const { Assert(r < d_rows.size()); return d_rows[r]; @@ -600,7 +608,33 @@ public: d_mergeBuffer.purge(); } - /** to += mult * buffer. */ + /* to *= mult */ + void multiplyRowByConstant(RowIndex to, const T& mult){ + RowIterator i = getRow(to).begin(); + RowIterator i_end = getRow(to).end(); + for( ; i != i_end; ++i){ + EntryID id = i.getID(); + Entry& entry = d_entries.get(id); + T& coeff = entry.getCoefficient(); + coeff *= mult; + } + } + + /** to += mult * from. + * Use the more efficient rowPlusBufferTimesConstant() for + * repeated use. + */ + void rowPlusRowTimesConstant(RowIndex to, RowIndex from, const T& mult){ + Assert(to != from); + loadRowIntoBuffer(from); + rowPlusBufferTimesConstant(to, mult); + clearBuffer(); + } + + /** to += mult * buffer. + * Invalidates coefficients on the row. + * (mult should never be a direct copy of a coefficient!) + */ void rowPlusBufferTimesConstant(RowIndex to, const T& mult){ Assert(d_rowInMergeBuffer != ROW_INDEX_SENTINEL); Assert(to != ROW_INDEX_SENTINEL); diff --git a/src/theory/arith/normal_form.cpp b/src/theory/arith/normal_form.cpp index 4edc55cca..3adb72f37 100644 --- a/src/theory/arith/normal_form.cpp +++ b/src/theory/arith/normal_form.cpp @@ -18,6 +18,7 @@ #include "theory/arith/normal_form.h" #include "theory/arith/arith_utilities.h" #include <list> +#include "theory/theory.h" using namespace std; @@ -25,6 +26,47 @@ namespace CVC4 { namespace theory { namespace arith { +Constant Constant::mkConstant(const Rational& rat) { + return Constant(mkRationalNode(rat)); +} + +size_t Variable::getComplexity() const{ + return 1u; +} + +size_t VarList::getComplexity() const{ + if(empty()){ + return 1; + }else if(singleton()){ + return 1; + }else{ + return size() + 1; + } +} + +size_t Monomial::getComplexity() const{ + return getConstant().getComplexity() + getVarList().getComplexity(); +} + +size_t Polynomial::getComplexity() const{ + size_t cmp = 0; + iterator i = begin(), e = end(); + for(; i != e; ++i){ + Monomial m = *i; + cmp += m.getComplexity(); + } + return cmp; +} + +size_t Constant::getComplexity() const{ + return getValue().complexity(); +} + +bool Variable::isLeafMember(Node n){ + return (!isRelationOperator(n.getKind())) && + (Theory::isLeafOf(n, theory::THEORY_ARITH)); +} + bool Variable::isDivMember(Node n){ switch(n.getKind()){ case kind::DIVISION: @@ -39,6 +81,8 @@ bool Variable::isDivMember(Node n){ } } + + bool VarList::isSorted(iterator start, iterator end) { return __gnu_cxx::is_sorted(start, end); } @@ -161,27 +205,76 @@ Monomial Monomial::operator*(const Monomial& mono) const { return Monomial::mkMonomial(newConstant, newVL); } -vector<Monomial> Monomial::sumLikeTerms(const std::vector<Monomial> & monos) { +// vector<Monomial> Monomial::sumLikeTerms(const std::vector<Monomial> & monos) { +// Assert(isSorted(monos)); +// vector<Monomial> outMonomials; +// typedef vector<Monomial>::const_iterator iterator; +// for(iterator rangeIter = monos.begin(), end=monos.end(); rangeIter != end;) { +// Rational constant = (*rangeIter).getConstant().getValue(); +// VarList varList = (*rangeIter).getVarList(); +// ++rangeIter; +// while(rangeIter != end && varList == (*rangeIter).getVarList()) { +// constant += (*rangeIter).getConstant().getValue(); +// ++rangeIter; +// } +// if(constant != 0) { +// Constant asConstant = Constant::mkConstant(constant); +// Monomial nonZero = Monomial::mkMonomial(asConstant, varList); +// outMonomials.push_back(nonZero); +// } +// } + +// Assert(isStrictlySorted(outMonomials)); +// return outMonomials; +// } + +void Monomial::sort(std::vector<Monomial>& m){ + if(!isSorted(m)){ + std::sort(m.begin(), m.end()); + } +} + +void Monomial::combineAdjacentMonomials(std::vector<Monomial>& monos) { Assert(isSorted(monos)); - vector<Monomial> outMonomials; - typedef vector<Monomial>::const_iterator iterator; - for(iterator rangeIter = monos.begin(), end=monos.end(); rangeIter != end;) { - Rational constant = (*rangeIter).getConstant().getValue(); - VarList varList = (*rangeIter).getVarList(); - ++rangeIter; - while(rangeIter != end && varList == (*rangeIter).getVarList()) { - constant += (*rangeIter).getConstant().getValue(); - ++rangeIter; + size_t writePos, readPos, N; + for(writePos = 0, readPos = 0, N = monos.size(); readPos < N;){ + Monomial& atRead = monos[readPos]; + const VarList& varList = atRead.getVarList(); + + size_t rangeEnd = readPos+1; + for(; rangeEnd < N; rangeEnd++){ + if(!(varList == monos[rangeEnd].getVarList())){ break; } } - if(constant != 0) { - Constant asConstant = Constant::mkConstant(constant); - Monomial nonZero = Monomial::mkMonomial(asConstant, varList); - outMonomials.push_back(nonZero); + // monos[i] for i in [readPos, rangeEnd) has the same var list + if(readPos+1 == rangeEnd){ // no addition needed + if(!atRead.getConstant().isZero()){ + Monomial cpy = atRead; // being paranoid here + monos[writePos] = cpy; + writePos++; + } + }else{ + Rational constant(monos[readPos].getConstant().getValue()); + for(size_t i=readPos+1; i < rangeEnd; ++i){ + constant += monos[i].getConstant().getValue(); + } + if(!constant.isZero()){ + Constant asConstant = Constant::mkConstant(constant); + Monomial nonZero = Monomial::mkMonomial(asConstant, varList); + monos[writePos] = nonZero; + writePos++; + } } + Assert(rangeEnd>readPos); + readPos = rangeEnd; } - - Assert(isStrictlySorted(outMonomials)); - return outMonomials; + if(writePos > 0 ){ + Monomial cp = monos[0]; + Assert(writePos <= N); + monos.resize(writePos, cp); + }else{ + monos.clear(); + } + Assert(isStrictlySorted(monos)); } void Monomial::print() const { @@ -199,12 +292,56 @@ Polynomial Polynomial::operator+(const Polynomial& vl) const { std::vector<Monomial> sortedMonos; merge_ranges(begin(), end(), vl.begin(), vl.end(), sortedMonos); - std::vector<Monomial> combined = Monomial::sumLikeTerms(sortedMonos); + Monomial::combineAdjacentMonomials(sortedMonos); + //std::vector<Monomial> combined = Monomial::sumLikeTerms(sortedMonos); - Polynomial result = mkPolynomial(combined); + Polynomial result = mkPolynomial(sortedMonos); return result; } + +Polynomial Polynomial::sumPolynomials(const std::vector<Polynomial>& ps){ + if(ps.empty()){ + return mkZero(); + }else if(ps.size() <= 4){ + // if there are few enough polynomials just add them + Polynomial p = ps[0]; + for(size_t i = 1; i < ps.size(); ++i){ + p = p + ps[i]; + } + return p; + }else{ + // general case + std::map<Node, Rational> coeffs; + for(size_t i = 0, N = ps.size(); i<N; ++i){ + const Polynomial& p = ps[i]; + for(iterator pi = p.begin(), pend = p.end(); pi != pend; ++pi) { + Monomial m = *pi; + coeffs[m.getVarList().getNode()] += m.getConstant().getValue(); + } + } + std::vector<Monomial> monos; + std::map<Node, Rational>::const_iterator ci = coeffs.begin(), cend = coeffs.end(); + for(; ci != cend; ++ci){ + if(!(*ci).second.isZero()){ + Constant c = Constant::mkConstant((*ci).second); + Node n = (*ci).first; + VarList vl = VarList::parseVarList(n); + if(vl.empty()){ + monos.push_back(Monomial(c)); + }else{ + monos.push_back(Monomial(c, vl)); + } + } + } + Monomial::sort(monos); + Monomial::combineAdjacentMonomials(monos); + + Polynomial result = mkPolynomial(monos); + return result; + } +} + Polynomial Polynomial::operator-(const Polynomial& vl) const { Constant negOne = Constant::mkConstant(Rational(-1)); @@ -257,7 +394,7 @@ Polynomial Polynomial::operator*(const Monomial& mono) const { // Suppose this = (+ x y), mono = x, (* x y).getId() < (* x x).getId() // newMonos = <(* x x), (* x y)> after this loop. // This is not sorted according to the current VarList order. - std::sort(newMonos.begin(), newMonos.end()); + Monomial::sort(newMonos); return Polynomial::mkPolynomial(newMonos); } } @@ -281,7 +418,7 @@ Monomial Polynomial::selectAbsMinimum() const { ++iter; for(; iter != end(); ++iter){ Monomial curr = *iter; - if(curr.absLessThan(min)){ + if(curr.absCmp(min) < 0){ min = curr; } } @@ -315,10 +452,16 @@ Integer Polynomial::numeratorGCD() const { Assert(i!=e); Integer d = (*i).getConstant().getValue().getNumerator().abs(); + if(d.isOne()){ + return d; + } ++i; for(; i!=e; ++i){ Integer c = (*i).getConstant().getValue().getNumerator(); d = d.gcd(c); + if(d.isOne()){ + return d; + } } return d; } @@ -615,6 +758,22 @@ bool Comparison::rightIsConstant() const { } } +size_t Comparison::getComplexity() const{ + switch(comparisonKind()){ + case kind::CONST_BOOLEAN: return 1; + case kind::LT: + case kind::LEQ: + case kind::DISTINCT: + case kind::EQUAL: + case kind::GT: + case kind::GEQ: + return getLeft().getComplexity() + getRight().getComplexity(); + default: + Unhandled(comparisonKind()); + return -1; + } +} + Polynomial Comparison::getLeft() const { TNode left; Kind k = comparisonKind(); @@ -804,10 +963,10 @@ bool Comparison::isNormalEqualityOrDisequality() const { }else{ Monomial absMinRight = varRight.selectAbsMinimum(); Debug("nf::tmp") << mleft.getNode() << " " << absMinRight.getNode() << endl; - if( mleft.absLessThan(absMinRight) ){ + if( mleft.absCmp(absMinRight) < 0){ return true; }else{ - return (!absMinRight.absLessThan(mleft)) && mleft < absMinRight; + return (!(absMinRight.absCmp(mleft)< 0)) && mleft < absMinRight; } } } diff --git a/src/theory/arith/normal_form.h b/src/theory/arith/normal_form.h index cd5f047b5..f098d8b54 100644 --- a/src/theory/arith/normal_form.h +++ b/src/theory/arith/normal_form.h @@ -23,8 +23,8 @@ #include "expr/node.h" #include "expr/node_self_iterator.h" #include "util/rational.h" -#include "theory/theory.h" -#include "theory/arith/arith_utilities.h" +#include "theory/arith/delta_rational.h" +//#include "theory/arith/arith_utilities.h" #include <list> #include <algorithm> @@ -247,11 +247,11 @@ public: // by a variable. return true; default: - return (!isRelationOperator(k)) && - (Theory::isLeafOf(n, theory::THEORY_ARITH)); + return isLeafMember(n); } } + static bool isLeafMember(Node n); static bool isDivMember(Node n); bool isDivLike() const{ return isDivMember(getNode()); @@ -286,6 +286,7 @@ public: bool operator==(const Variable& v) const { return getNode() == v.getNode();} + size_t getComplexity() const; };/* class Variable */ @@ -306,9 +307,7 @@ public: return Constant(n); } - static Constant mkConstant(const Rational& rat) { - return Constant(mkRationalNode(rat)); - } + static Constant mkConstant(const Rational& rat); static Constant mkZero() { return mkConstant(Rational(0)); @@ -322,6 +321,7 @@ public: return getNode().getConst<Rational>(); } + static int absCmp(const Constant& a, const Constant& b); bool isIntegral() const { return getValue().isIntegral(); } int sgn() const { return getValue().sgn(); } @@ -373,6 +373,8 @@ public: return getValue().getNumerator().length(); } + size_t getComplexity() const; + };/* class Constant */ @@ -563,6 +565,7 @@ public: } return true; } + size_t getComplexity() const; private: bool isSorted(iterator start, iterator end); @@ -687,6 +690,9 @@ public: return isSorted(m) && std::adjacent_find(m.begin(),m.end()) == m.end(); } + static void sort(std::vector<Monomial>& m); + static void combineAdjacentMonomials(std::vector<Monomial>& m); + /** * The variable product */ @@ -717,11 +723,14 @@ public: * Given a sorted list of monomials, this function transforms this * into a strictly sorted list of monomials that does not contain zero. */ - static std::vector<Monomial> sumLikeTerms(const std::vector<Monomial>& monos); + //static std::vector<Monomial> sumLikeTerms(const std::vector<Monomial>& monos); - bool absLessThan(const Monomial& other) const{ - return getConstant().abs() < other.getConstant().abs(); + int absCmp(const Monomial& other) const{ + return getConstant().getValue().absCmp(other.getConstant().getValue()); } + // bool absLessThan(const Monomial& other) const{ + // return getConstant().abs() < other.getConstant().abs(); + // } uint32_t coefficientLength() const{ return getConstant().length(); @@ -730,6 +739,7 @@ public: void print() const; static void printList(const std::vector<Monomial>& list); + size_t getComplexity() const; };/* class Monomial */ class SumPair; @@ -938,9 +948,12 @@ public: return true; } + static Polynomial sumPolynomials(const std::vector<Polynomial>& polynomials); + /** Returns true if the polynomial contains a non-linear monomial.*/ bool isNonlinear() const; + /** * Selects a minimal monomial in the polynomial by the absolute value of * the coefficient. @@ -1058,6 +1071,8 @@ public: return getHead().getVarList(); } + size_t getComplexity() const; + friend class SumPair; friend class Comparison; @@ -1173,6 +1188,10 @@ public: return getConstant().isZero() && isConstant(); } + uint32_t size() const{ + return getPolynomial().size(); + } + bool isNonlinear() const{ return getPolynomial().isNonlinear(); } @@ -1368,6 +1387,8 @@ public: return parse.isNormalForm(); } + size_t getComplexity() const; + SumPair toSumPair() const; Polynomial normalizedVariablePart() const; diff --git a/src/theory/arith/options b/src/theory/arith/options index 3fc08e18e..cf35265d6 100644 --- a/src/theory/arith/options +++ b/src/theory/arith/options @@ -85,8 +85,11 @@ option restrictedPivots --restrict-pivots bool :default true :read-write option collectPivots --collect-pivot-stats bool :default false :read-write collect the pivot history -option fancyFinal --fancy-final bool :default false :read-write - tuning how final check works for really hard problems +option useApprox --use-approx bool :default false :read-write + attempt to use an approximate solver + +option maxApproxDepth --approx-branch-depth int16_t :default 200 :read-write + maximum branch depth the approximate solver is allowed to take option exportDioDecompositions --dio-decomps bool :default false :read-write let skolem variables for integer divisibility constraints leak from the dio solver @@ -103,4 +106,46 @@ option soiQuickExplain --soi-qe bool :default false :read-write option rewriteDivk rewrite-divk --rewrite-divk bool :default false :read-write rewrite division and mod when by a constant into linear terms +option trySolveIntStandardEffort --se-solve-int bool :default false + attempt to use the approximate solve integer method on standard effort + +option replayFailureLemma --lemmas-on-replay-failure bool :default false + attempt to use external lemmas if approximate solve integer failed + +option dioSolverTurns --dio-turns int :default 10 + turns in a row dio solver cutting gets + +option rrTurns --rr-turns int :default 3 + round robin turn + +option dioRepeat --dio-repeat bool :default false + handle dio solver constraints in mass or one at a time + +option replayEarlyCloseDepths --replay-early-close-depth int :default 1 + multiples of the depths to try to close the approx log eagerly + +option replayFailurePenalty --replay-failure-penalty int :default 100 + number of solve integer attempts to skips after a numeric failure + +option replayNumericFailurePenalty --replay-num-err-penalty int :default 4194304 + number of solve integer attempts to skips after a numeric failure + +option replayRejectCutSize --replay-reject-cut unsigned :default 25500 + maximum complexity of any coefficient while replaying cuts + +option lemmaRejectCutSize --replay-lemma-reject-cut unsigned :default 25500 + maximum complexity of any coefficient while outputing replaying cut lemmas + +option soiApproxMajorFailure --replay-soi-major-threshold double :default .01 + threshold for a major tolerance failure by the approximate solver + +option soiApproxMajorFailurePen --replay-soi-major-threshold-pen int :default 50 + threshold for a major tolerance failure by the approximate solver + +option soiApproxMinorFailure --replay-soi-minor-threshold double :default .0001 + threshold for a minor tolerance failure by the approximate solver + +option soiApproxMinorFailurePen --replay-soi-minor-threshold-pen int :default 10 + threshold for a minor tolerance failure by the approximate solver + endmodule diff --git a/src/theory/arith/partial_model.cpp b/src/theory/arith/partial_model.cpp index 3fae3751c..8f08de36c 100644 --- a/src/theory/arith/partial_model.cpp +++ b/src/theory/arith/partial_model.cpp @@ -33,7 +33,6 @@ ArithVariables::ArithVariables(context::Context* c, DeltaComputeCallback deltaCo d_numberOfVariables(0), d_pool(), d_released(), - d_releasedIterator(d_released.begin()), d_nodeToArithVarMap(), d_boundsQueue(), d_enqueueingBoundCounts(true), @@ -44,6 +43,87 @@ ArithVariables::ArithVariables(context::Context* c, DeltaComputeCallback deltaCo d_deltaComputingFunc(deltaComputingFunc) { } +ArithVar ArithVariables::getNumberOfVariables() const { + return d_numberOfVariables; +} + + +bool ArithVariables::hasArithVar(TNode x) const { + return d_nodeToArithVarMap.find(x) != d_nodeToArithVarMap.end(); +} + +bool ArithVariables::hasNode(ArithVar a) const { + return d_vars.isKey(a); +} + +ArithVar ArithVariables::asArithVar(TNode x) const{ + Assert(hasArithVar(x)); + Assert((d_nodeToArithVarMap.find(x))->second <= ARITHVAR_SENTINEL); + return (d_nodeToArithVarMap.find(x))->second; +} + +Node ArithVariables::asNode(ArithVar a) const{ + Assert(hasNode(a)); + return d_vars[a].d_node; +} + +ArithVariables::var_iterator::var_iterator() + : d_vars(NULL) + , d_wrapped() +{} + +ArithVariables::var_iterator::var_iterator(const VarInfoVec* vars, VarInfoVec::const_iterator ci) + : d_vars(vars), d_wrapped(ci) +{ + nextInitialized(); +} + +ArithVariables::var_iterator& ArithVariables::var_iterator::operator++(){ + ++d_wrapped; + nextInitialized(); + return *this; +} +bool ArithVariables::var_iterator::operator==(const ArithVariables::var_iterator& other) const{ + return d_wrapped == other.d_wrapped; +} +bool ArithVariables::var_iterator::operator!=(const ArithVariables::var_iterator& other) const{ + return d_wrapped != other.d_wrapped; +} +ArithVar ArithVariables::var_iterator::operator*() const{ + return *d_wrapped; +} + +void ArithVariables::var_iterator::nextInitialized(){ + VarInfoVec::const_iterator end = d_vars->end(); + while(d_wrapped != end && + !((*d_vars)[*d_wrapped].initialized())){ + ++d_wrapped; + } +} + +ArithVariables::var_iterator ArithVariables::var_begin() const { + return var_iterator(&d_vars, d_vars.begin()); +} + +ArithVariables::var_iterator ArithVariables::var_end() const { + return var_iterator(&d_vars, d_vars.end()); +} +bool ArithVariables::isInteger(ArithVar x) const { + return d_vars[x].d_type >= ATInteger; +} + +/** Is the assignment to x integral? */ +bool ArithVariables::integralAssignment(ArithVar x) const { + return getAssignment(x).isIntegral(); +} +bool ArithVariables::isAuxiliary(ArithVar x) const { + return d_vars[x].d_auxiliary; +} + +bool ArithVariables::isIntegerInput(ArithVar x) const { + return isInteger(x) && !isAuxiliary(x); +} + ArithVariables::VarInfo::VarInfo() : d_var(ARITHVAR_SENTINEL), d_assignment(0), @@ -53,14 +133,14 @@ ArithVariables::VarInfo::VarInfo() d_cmpAssignmentUB(-1), d_pushCount(0), d_node(Node::null()), - d_slack(false) + d_auxiliary(false) { } bool ArithVariables::VarInfo::initialized() const { return d_var != ARITHVAR_SENTINEL; } -void ArithVariables::VarInfo::initialize(ArithVar v, Node n, bool slack){ +void ArithVariables::VarInfo::initialize(ArithVar v, Node n, bool aux){ Assert(!initialized()); Assert(d_lb == NullConstraint); Assert(d_ub == NullConstraint); @@ -68,9 +148,9 @@ void ArithVariables::VarInfo::initialize(ArithVar v, Node n, bool slack){ Assert(d_cmpAssignmentUB < 0); d_var = v; d_node = n; - d_slack = slack; + d_auxiliary = aux; - if(d_slack){ + if(d_auxiliary){ //The type computation is not quite accurate for Rationals that are //integral. //We'll use the isIntegral check from the polynomial package instead. @@ -112,6 +192,10 @@ bool ArithVariables::VarInfo::setAssignment(const DeltaRational& a, BoundsInfo& void ArithVariables::releaseArithVar(ArithVar v){ VarInfo& vi = d_vars.get(v); + + size_t removed CVC4_UNUSED = d_nodeToArithVarMap.erase(vi.d_node); + Assert(removed == 1); + vi.uninitialize(); if(d_safeAssignment.isKey(v)){ @@ -124,7 +208,7 @@ void ArithVariables::releaseArithVar(ArithVar v){ } } -bool ArithVariables::VarInfo::setUpperBound(Constraint ub, BoundsInfo& prev){ +bool ArithVariables::VarInfo::setUpperBound(ConstraintP ub, BoundsInfo& prev){ Assert(initialized()); bool wasNull = d_ub == NullConstraint; bool isNull = ub == NullConstraint; @@ -140,7 +224,7 @@ bool ArithVariables::VarInfo::setUpperBound(Constraint ub, BoundsInfo& prev){ return ubChanged; } -bool ArithVariables::VarInfo::setLowerBound(Constraint lb, BoundsInfo& prev){ +bool ArithVariables::VarInfo::setLowerBound(ConstraintP lb, BoundsInfo& prev){ Assert(initialized()); bool wasNull = d_lb == NullConstraint; bool isNull = lb == NullConstraint; @@ -177,23 +261,22 @@ bool ArithVariables::VarInfo::canBeReclaimed() const{ return d_pushCount == 0; } +bool ArithVariables::canBeReleased(ArithVar v) const{ + return d_vars[v].canBeReclaimed(); +} + void ArithVariables::attemptToReclaimReleased(){ - std::list<ArithVar>::iterator i_end = d_released.end(); - for(int iter = 0; iter < 20 && d_releasedIterator != i_end; ++d_releasedIterator){ - ArithVar v = *d_releasedIterator; - VarInfo& vi = d_vars.get(v); - if(vi.canBeReclaimed()){ + size_t readPos = 0, writePos = 0, N = d_released.size(); + for(; readPos < N; ++readPos){ + ArithVar v = d_released[readPos]; + if(canBeReleased(v)){ d_pool.push_back(v); - std::list<ArithVar>::iterator curr = d_releasedIterator; - ++d_releasedIterator; - d_released.erase(curr); }else{ - ++d_releasedIterator; + d_released[writePos] = v; + writePos++; } } - if(d_releasedIterator == i_end){ - d_releasedIterator = d_released.begin(); - } + d_released.resize(writePos); } ArithVar ArithVariables::allocateVariable(){ @@ -232,6 +315,21 @@ bool ArithVariables::boundsAreEqual(ArithVar x) const{ } } + +std::pair<ConstraintP, ConstraintP> ArithVariables::explainEqualBounds(ArithVar x) const{ + Assert(boundsAreEqual(x)); + + ConstraintP lb = getLowerBoundConstraint(x); + ConstraintP ub = getUpperBoundConstraint(x); + if(lb->isEquality()){ + return make_pair(lb, NullConstraint); + }else if(ub->isEquality()){ + return make_pair(ub, NullConstraint); + }else{ + return make_pair(lb, ub); + } +} + void ArithVariables::setAssignment(ArithVar x, const DeltaRational& r){ Debug("partial_model") << "pm: updating the assignment to" << x << " now " << r <<endl; @@ -266,15 +364,15 @@ void ArithVariables::setAssignment(ArithVar x, const DeltaRational& safe, const } } -void ArithVariables::initialize(ArithVar x, Node n, bool slack){ +void ArithVariables::initialize(ArithVar x, Node n, bool aux){ VarInfo& vi = d_vars.get(x); - vi.initialize(x, n, slack); + vi.initialize(x, n, aux); d_nodeToArithVarMap[n] = x; } -ArithVar ArithVariables::allocate(Node n, bool slack){ +ArithVar ArithVariables::allocate(Node n, bool aux){ ArithVar v = allocateVariable(); - initialize(v, n, slack); + initialize(v, n, aux); return v; } @@ -333,7 +431,7 @@ const DeltaRational& ArithVariables::getAssignment(ArithVar x) const{ } -void ArithVariables::setLowerBoundConstraint(Constraint c){ +void ArithVariables::setLowerBoundConstraint(ConstraintP c){ AssertArgument(c != NullConstraint, "Cannot set a lower bound to NullConstraint."); AssertArgument(c->isEquality() || c->isLowerBound(), "Constraint type must be set to an equality or UpperBound."); @@ -351,7 +449,7 @@ void ArithVariables::setLowerBoundConstraint(Constraint c){ } } -void ArithVariables::setUpperBoundConstraint(Constraint c){ +void ArithVariables::setUpperBoundConstraint(ConstraintP c){ AssertArgument(c != NullConstraint, "Cannot set a upper bound to NullConstraint."); AssertArgument(c->isEquality() || c->isUpperBound(), "Constraint type must be set to an equality or UpperBound."); @@ -450,6 +548,14 @@ void ArithVariables::commitAssignmentChanges(){ clearSafeAssignments(false); } +bool ArithVariables::lowerBoundIsZero(ArithVar x){ + return hasLowerBound(x) && getLowerBound(x).sgn() == 0; +} + +bool ArithVariables::upperBoundIsZero(ArithVar x){ + return hasUpperBound(x) && getUpperBound(x).sgn() == 0; +} + void ArithVariables::printEntireModel(std::ostream& out) const{ out << "---Printing Model ---" << std::endl; for(var_iterator i = var_begin(), iend = var_end(); i != iend; ++i){ @@ -474,6 +580,10 @@ void ArithVariables::printModel(ArithVar x, std::ostream& out) const{ out << getUpperBound(x) << " "; out << getUpperBoundConstraint(x) << " "; } + + if(isInteger(x) && !integralAssignment(x)){ + out << "(not an integer)" << endl; + } out << endl; } @@ -540,10 +650,36 @@ void ArithVariables::processBoundsQueue(BoundUpdateCallback& changed){ } } +void ArithVariables::invalidateDelta() { + d_deltaIsSafe = false; +} + +void ArithVariables::setDelta(const Rational& d){ + d_delta = d; + d_deltaIsSafe = true; +} + +void ArithVariables::startQueueingBoundCounts(){ + d_enqueueingBoundCounts = true; +} +void ArithVariables::stopQueueingBoundCounts(){ + d_enqueueingBoundCounts = false; +} + +bool ArithVariables::inMaps(ArithVar x) const{ + return x < getNumberOfVariables(); +} + +ArithVariables::LowerBoundCleanUp::LowerBoundCleanUp(ArithVariables* pm) + : d_pm(pm) +{} void ArithVariables::LowerBoundCleanUp::operator()(AVCPair* p){ d_pm->popLowerBound(p); } +ArithVariables::UpperBoundCleanUp::UpperBoundCleanUp(ArithVariables* pm) + : d_pm(pm) +{} void ArithVariables::UpperBoundCleanUp::operator()(AVCPair* p){ d_pm->popUpperBound(p); } diff --git a/src/theory/arith/partial_model.h b/src/theory/arith/partial_model.h index c497adb75..33af3d4ef 100644 --- a/src/theory/arith/partial_model.h +++ b/src/theory/arith/partial_model.h @@ -9,10 +9,11 @@ ** See the file COPYING in the top-level source directory for licensing ** information.\endverbatim ** - ** \brief [[ Add one-line brief description here ]] + ** \brief Datastructures that track variable by variable information. ** - ** [[ Add lengthier description here ]] - ** \todo document this file + ** This is a datastructure that tracks variable specific information. + ** This is partially context dependent to back track upper/lower bounds + ** and information derived from these. **/ #include "cvc4_private.h" @@ -50,40 +51,44 @@ private: ArithVar d_var; DeltaRational d_assignment; - Constraint d_lb; - Constraint d_ub; + ConstraintP d_lb; + ConstraintP d_ub; int d_cmpAssignmentLB; int d_cmpAssignmentUB; unsigned d_pushCount; ArithType d_type; Node d_node; - bool d_slack; + bool d_auxiliary; public: VarInfo(); bool setAssignment(const DeltaRational& r, BoundsInfo& prev); - bool setLowerBound(Constraint c, BoundsInfo& prev); - bool setUpperBound(Constraint c, BoundsInfo& prev); + bool setLowerBound(ConstraintP c, BoundsInfo& prev); + bool setUpperBound(ConstraintP c, BoundsInfo& prev); /** Returns true if this VarInfo has been initialized. */ bool initialized() const; /** * Initializes the VarInfo with the ArithVar index it is associated with, - * the node that the variable represents, and whether it is a slack variable. + * the node that the variable represents, and whether it is an auxillary + * variable. */ - void initialize(ArithVar v, Node n, bool slack); + void initialize(ArithVar v, Node n, bool aux); + /** Uninitializes the VarInfo. */ void uninitialize(); bool canBeReclaimed() const; - /** Indicator variables for if the assignment is equal to the upper and lower bounds. */ + /** Indicator variables for if the assignment is equal to the upper + * and lower bounds. */ BoundCounts atBoundCounts() const; - /** Combination of indicator variables for whether it has upper and lower bounds. */ + /** Combination of indicator variables for whether it has upper and + * lower bounds. */ BoundCounts hasBoundCounts() const; /** Stores both atBoundCounts() and hasBoundCounts(). */ @@ -92,21 +97,22 @@ private: /**Maps from ArithVar -> VarInfo */ typedef DenseMap<VarInfo> VarInfoVec; + /** This maps an ArithVar to its Variable information.*/ VarInfoVec d_vars; - // Partial Map from Arithvar -> PreviousAssignment + /** Partial Map from Arithvar -> PreviousAssignment */ DenseMap<DeltaRational> d_safeAssignment; - // if d_vars.isKey(x), then x < d_numberOfVariables + /** if d_vars.isKey(x), then x < d_numberOfVariables */ ArithVar d_numberOfVariables; /** [0, d_numberOfVariables) \intersect d_vars.keys == d_pool */ // Everything in the pool is fair game. // There must be NO outstanding assertions std::vector<ArithVar> d_pool; - std::list<ArithVar> d_released; - std::list<ArithVar>::iterator d_releasedIterator; + std::vector<ArithVar> d_released; + //std::list<ArithVar>::iterator d_releasedIterator; // Reverse Map from Node to ArithVar // Inverse of d_vars[x].d_node @@ -118,36 +124,29 @@ private: /** * If this is true, record the incoming changes to the bound information. - * If this is false, the responsibility of recording the changes is LinearEqualities's. + * If this is false, the responsibility of recording the changes is + * LinearEqualities's. */ bool d_enqueueingBoundCounts; public: - inline ArithVar getNumberOfVariables() const { - return d_numberOfVariables; - } + /** Returns the number of variables. */ + ArithVar getNumberOfVariables() const; - inline bool hasArithVar(TNode x) const { - return d_nodeToArithVarMap.find(x) != d_nodeToArithVarMap.end(); - } + /** Returns true if the node has an associated variables. */ + bool hasArithVar(TNode x) const; - inline bool hasNode(ArithVar a) const { - return d_vars.isKey(a); - } - - inline ArithVar asArithVar(TNode x) const{ - Assert(hasArithVar(x)); - Assert((d_nodeToArithVarMap.find(x))->second <= ARITHVAR_SENTINEL); - return (d_nodeToArithVarMap.find(x))->second; - } + /** Returns true if the variable has a defining node. */ + bool hasNode(ArithVar a) const; + /** Returns the ArithVar associated with a node. */ + ArithVar asArithVar(TNode x) const; - inline Node asNode(ArithVar a) const{ - Assert(hasNode(a)); - return d_vars[a].d_node; - } + /** Returns the node associated with an ArithVar. */ + Node asNode(ArithVar a) const; + /** Allocates a freshly allocated variables. */ ArithVar allocateVariable(); class var_iterator { @@ -155,68 +154,47 @@ private: const VarInfoVec* d_vars; VarInfoVec::const_iterator d_wrapped; public: - var_iterator(){} - var_iterator(const VarInfoVec* vars, VarInfoVec::const_iterator ci) - : d_vars(vars), d_wrapped(ci) - { - nextInitialized(); - } - - var_iterator& operator++(){ - ++d_wrapped; - nextInitialized(); - return *this; - } - bool operator==(const var_iterator& other) const{ - return d_wrapped == other.d_wrapped; - } - bool operator!=(const var_iterator& other) const{ - return d_wrapped != other.d_wrapped; - } - ArithVar operator*() const{ - return *d_wrapped; - } + var_iterator(); + var_iterator(const VarInfoVec* vars, VarInfoVec::const_iterator ci); + var_iterator& operator++(); + + bool operator==(const var_iterator& other) const; + bool operator!=(const var_iterator& other) const; + ArithVar operator*() const; + private: - void nextInitialized(){ - VarInfoVec::const_iterator end = d_vars->end(); - while(d_wrapped != end && - !((*d_vars)[*d_wrapped].initialized())){ - ++d_wrapped; - } - } + void nextInitialized(); }; - var_iterator var_begin() const { - return var_iterator(&d_vars, d_vars.begin()); - } - var_iterator var_end() const { - return var_iterator(&d_vars, d_vars.end()); - } + var_iterator var_begin() const; + var_iterator var_end() const; bool canBeReleased(ArithVar v) const; void releaseArithVar(ArithVar v); void attemptToReclaimReleased(); - bool isInteger(ArithVar x) const { - return d_vars[x].d_type >= ATInteger; - } - bool isSlack(ArithVar x) const { - return d_vars[x].d_slack; - } + /** Is this variable guaranteed to have an integer assignment? + * (Should agree with the type system.) */ + bool isInteger(ArithVar x) const; - bool integralAssignment(ArithVar x) const { - return getAssignment(x).isIntegral(); - } + /** Is the assignment to x integral? */ + bool integralAssignment(ArithVar x) const; + + /* Is this variable defined as a linear sum of other variables? */ + bool isAuxiliary(ArithVar x) const; + + /* Is the variable both input and not auxiliary? */ + bool isIntegerInput(ArithVar x) const; private: - typedef std::pair<ArithVar, Constraint> AVCPair; + typedef std::pair<ArithVar, ConstraintP> AVCPair; class LowerBoundCleanUp { private: ArithVariables* d_pm; public: - LowerBoundCleanUp(ArithVariables* pm) : d_pm(pm) {} + LowerBoundCleanUp(ArithVariables* pm); void operator()(AVCPair* restore); }; @@ -224,7 +202,7 @@ private: private: ArithVariables* d_pm; public: - UpperBoundCleanUp(ArithVariables* pm) : d_pm(pm) {} + UpperBoundCleanUp(ArithVariables* pm); void operator()(AVCPair* restore); }; @@ -255,27 +233,27 @@ public: * This sets the lower bound for a variable in the current context. * This must be stronger the previous constraint. */ - void setLowerBoundConstraint(Constraint lb); + void setLowerBoundConstraint(ConstraintP lb); /** * This sets the upper bound for a variable in the current context. * This must be stronger the previous constraint. */ - void setUpperBoundConstraint(Constraint ub); + void setUpperBoundConstraint(ConstraintP ub); /** Returns the constraint for the upper bound of a variable. */ - inline Constraint getUpperBoundConstraint(ArithVar x) const{ + inline ConstraintP getUpperBoundConstraint(ArithVar x) const{ return d_vars[x].d_ub; } /** Returns the constraint for the lower bound of a variable. */ - inline Constraint getLowerBoundConstraint(ArithVar x) const{ + inline ConstraintP getLowerBoundConstraint(ArithVar x) const{ return d_vars[x].d_lb; } /* Initializes a variable to a safe value.*/ - void initialize(ArithVar x, Node n, bool slack); + void initialize(ArithVar x, Node n, bool aux); - ArithVar allocate(Node n, bool slack = false); + ArithVar allocate(Node n, bool aux = false); /* Gets the last assignment to a variable that is known to be consistent. */ const DeltaRational& getSafeAssignment(ArithVar x) const; @@ -288,13 +266,8 @@ public: void commitAssignmentChanges(); - inline bool lowerBoundIsZero(ArithVar x){ - return hasLowerBound(x) && getLowerBound(x).sgn() == 0; - } - - inline bool upperBoundIsZero(ArithVar x){ - return hasUpperBound(x) && getUpperBound(x).sgn() == 0; - } + bool lowerBoundIsZero(ArithVar x); + bool upperBoundIsZero(ArithVar x); bool boundsAreEqual(ArithVar x) const; @@ -393,17 +366,12 @@ public: const Rational& getDelta(); - inline void invalidateDelta() { - d_deltaIsSafe = false; - } + void invalidateDelta(); - void setDelta(const Rational& d){ - d_delta = d; - d_deltaIsSafe = true; - } + void setDelta(const Rational& d); - void startQueueingBoundCounts(){ d_enqueueingBoundCounts = true; } - void stopQueueingBoundCounts(){ d_enqueueingBoundCounts = false; } + void startQueueingBoundCounts(); + void stopQueueingBoundCounts(); void addToBoundQueue(ArithVar v, const BoundsInfo& prev); BoundsInfo selectBoundsInfo(ArithVar v, bool old) const; @@ -413,6 +381,15 @@ public: void printEntireModel(std::ostream& out) const; + + /** + * Precondition: assumes boundsAreEqual(x). + * If the either the lower/ upper bound is an equality, eq, + * this returns make_pair(eq, NullConstraint). + * Otherwise, this returns make_pair(lb, ub). + */ + std::pair<ConstraintP, ConstraintP> explainEqualBounds(ArithVar x) const; + private: /** @@ -423,9 +400,7 @@ private: bool debugEqualSizes(); - bool inMaps(ArithVar x) const{ - return x < getNumberOfVariables(); - } + bool inMaps(ArithVar x) const; };/* class ArithVariables */ diff --git a/src/theory/arith/simplex.cpp b/src/theory/arith/simplex.cpp index a160f4fe2..e67f4b9fc 100644 --- a/src/theory/arith/simplex.cpp +++ b/src/theory/arith/simplex.cpp @@ -77,35 +77,40 @@ bool SimplexDecisionProcedure::standardProcessSignals(TimerStat &timer, IntStat& void SimplexDecisionProcedure::reportConflict(ArithVar basic){ Assert(!d_conflictVariables.isMember(basic)); Assert(checkBasicForConflict(basic)); - Node conflict = generateConflictForBasic(basic); + RaiseConflict rc( d_conflictChannel); - static bool verbose = false; - if(verbose) { Message() << "conflict " << basic << " " << conflict << endl; } - Assert(!conflict.isNull()); - d_conflictChannel(conflict); + generateConflictForBasic(basic, rc); + + // static bool verbose = false; + // if(verbose) { Message() << "conflict " << basic << " " << conflict << endl; } + // Assert(!conflict.isNull()); + //d_conflictChannel(conflict); + rc.commitConflict(); d_conflictVariables.add(basic); } -Node SimplexDecisionProcedure::generateConflictForBasic(ArithVar basic) const { +void SimplexDecisionProcedure::generateConflictForBasic(ArithVar basic, RaiseConflict& rc) const { Assert(d_tableau.isBasic(basic)); Assert(checkBasicForConflict(basic)); if(d_variables.cmpAssignmentLowerBound(basic) < 0){ Assert(d_linEq.nonbasicsAtUpperBounds(basic)); - return d_linEq.generateConflictBelowLowerBound(basic); + return d_linEq.generateConflictBelowLowerBound(basic, rc); }else if(d_variables.cmpAssignmentUpperBound(basic) > 0){ Assert(d_linEq.nonbasicsAtLowerBounds(basic)); - return d_linEq.generateConflictAboveUpperBound(basic); + return d_linEq.generateConflictAboveUpperBound(basic, rc); }else{ Unreachable(); } } -Node SimplexDecisionProcedure::maybeGenerateConflictForBasic(ArithVar basic) const { +bool SimplexDecisionProcedure::maybeGenerateConflictForBasic(ArithVar basic) const { if(checkBasicForConflict(basic)){ - return generateConflictForBasic(basic); + RaiseConflict rc(d_conflictChannel); + generateConflictForBasic(basic, rc); + return true; }else{ - return Node::null(); + return false; } } diff --git a/src/theory/arith/simplex.h b/src/theory/arith/simplex.h index b61cadaf8..f545da51e 100644 --- a/src/theory/arith/simplex.h +++ b/src/theory/arith/simplex.h @@ -157,16 +157,16 @@ protected: * If a conflict is discovered a node summarizing the conflict is returned. * Otherwise, Node::null() is returned. */ - Node maybeGenerateConflictForBasic(ArithVar basic) const; + bool maybeGenerateConflictForBasic(ArithVar basic) const; /** Returns true if a tracked basic variable has a conflict on it. */ bool checkBasicForConflict(ArithVar b) const; /** * If a basic variable has a conflict on its row, - * this produces a minimized row. + * this produces a minimized row on the conflict channel. */ - Node generateConflictForBasic(ArithVar basic) const; + void generateConflictForBasic(ArithVar basic, RaiseConflict& rc) const; /** Gets a fresh variable from TheoryArith. */ diff --git a/src/theory/arith/simplex_update.cpp b/src/theory/arith/simplex_update.cpp index ba19d01b1..416fbe745 100644 --- a/src/theory/arith/simplex_update.cpp +++ b/src/theory/arith/simplex_update.cpp @@ -51,7 +51,7 @@ UpdateInfo::UpdateInfo(ArithVar nb, int dir): Assert(dir == 1 || dir == -1); } -UpdateInfo::UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, const Rational& r, Constraint c): +UpdateInfo::UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP c): d_nonbasic(nb), d_nonbasicDirection(delta.sgn()), d_nonbasicDelta(delta), @@ -65,7 +65,7 @@ UpdateInfo::UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, c Assert(conflict); } -UpdateInfo UpdateInfo::conflict(ArithVar nb, const DeltaRational& delta, const Rational& r, Constraint lim){ +UpdateInfo UpdateInfo::conflict(ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP lim){ return UpdateInfo(true, nb, delta, r, lim); } @@ -81,7 +81,7 @@ void UpdateInfo::updateUnbounded(const DeltaRational& delta, int ec, int f){ Assert(!describesPivot()); Assert(debugSgnAgreement()); } -void UpdateInfo::updatePureFocus(const DeltaRational& delta, Constraint c){ +void UpdateInfo::updatePureFocus(const DeltaRational& delta, ConstraintP c){ d_limiting = c; d_nonbasicDelta = delta; d_errorsChange.clear(); @@ -93,7 +93,7 @@ void UpdateInfo::updatePureFocus(const DeltaRational& delta, Constraint c){ Assert(debugSgnAgreement()); } -void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, Constraint c){ +void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, ConstraintP c){ d_limiting = c; d_nonbasicDelta = delta; d_errorsChange.clear(); @@ -103,7 +103,7 @@ void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, Cons Assert(debugSgnAgreement()); } -void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, Constraint c, int ec){ +void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, ConstraintP c, int ec){ d_limiting = c; d_nonbasicDelta = delta; d_errorsChange = ec; @@ -114,7 +114,7 @@ void UpdateInfo::updatePivot(const DeltaRational& delta, const Rational& r, Cons Assert(debugSgnAgreement()); } -void UpdateInfo::witnessedUpdate(const DeltaRational& delta, Constraint c, int ec, int fd){ +void UpdateInfo::witnessedUpdate(const DeltaRational& delta, ConstraintP c, int ec, int fd){ d_limiting = c; d_nonbasicDelta = delta; d_errorsChange = ec; @@ -125,7 +125,7 @@ void UpdateInfo::witnessedUpdate(const DeltaRational& delta, Constraint c, int e Assert(debugSgnAgreement()); } -void UpdateInfo::update(const DeltaRational& delta, const Rational& r, Constraint c, int ec, int fd){ +void UpdateInfo::update(const DeltaRational& delta, const Rational& r, ConstraintP c, int ec, int fd){ d_limiting = c; d_nonbasicDelta = delta; d_errorsChange = ec; diff --git a/src/theory/arith/simplex_update.h b/src/theory/arith/simplex_update.h index 5a313e305..e223bba7f 100644 --- a/src/theory/arith/simplex_update.h +++ b/src/theory/arith/simplex_update.h @@ -136,7 +136,7 @@ private: * - Pivot-And-Update: then this is not NullConstraint and the variable is not d_nonbasic. * - Update: then this is not NullConstraint and the variable is d_nonbasic. */ - Constraint d_limiting; + ConstraintP d_limiting; WitnessImprovement d_witness; @@ -150,7 +150,7 @@ private: } /** This private constructor allows for setting conflict to true. */ - UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, const Rational& r, Constraint lim); + UpdateInfo(bool conflict, ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP lim); public: @@ -170,7 +170,7 @@ public: void updateUnbounded(const DeltaRational& d, int ec, int f); - void updatePureFocus(const DeltaRational& d, Constraint c); + void updatePureFocus(const DeltaRational& d, ConstraintP c); //void updatePureError(const DeltaRational& d, Constraint c, int e); //void updatePure(const DeltaRational& d, Constraint c, int e, int f); @@ -178,23 +178,23 @@ public: * This updates the nonBasicDelta to d and limiting to c. * This clears errorChange() and focusDir(). */ - void updatePivot(const DeltaRational& d, const Rational& r, Constraint c); + void updatePivot(const DeltaRational& d, const Rational& r, ConstraintP c); /** * This updates the nonBasicDelta to d, limiting to c, and errorChange to e. * This clears focusDir(). */ - void updatePivot(const DeltaRational& d, const Rational& r, Constraint c, int e); + void updatePivot(const DeltaRational& d, const Rational& r, ConstraintP c, int e); /** * This updates the nonBasicDelta to d, limiting to c, errorChange to e and * focusDir to f. */ - void witnessedUpdate(const DeltaRational& d, Constraint c, int e, int f); - void update(const DeltaRational& d, const Rational& r, Constraint c, int e, int f); + void witnessedUpdate(const DeltaRational& d, ConstraintP c, int e, int f); + void update(const DeltaRational& d, const Rational& r, ConstraintP c, int e, int f); - static UpdateInfo conflict(ArithVar nb, const DeltaRational& delta, const Rational& r, Constraint lim); + static UpdateInfo conflict(ArithVar nb, const DeltaRational& delta, const Rational& r, ConstraintP lim); inline ArithVar nonbasic() const { return d_nonbasic; } inline bool uninitialized() const { @@ -283,7 +283,7 @@ public: } /** Returns the limiting constraint. */ - inline Constraint limiting() const { + inline ConstraintP limiting() const { return d_limiting; } diff --git a/src/theory/arith/soi_simplex.cpp b/src/theory/arith/soi_simplex.cpp index 2eb258d3b..ded322f18 100644 --- a/src/theory/arith/soi_simplex.cpp +++ b/src/theory/arith/soi_simplex.cpp @@ -383,7 +383,7 @@ void SumOfInfeasibilitiesSPD::updateAndSignal(const UpdateInfo& selected, Witnes } if(selected.describesPivot()){ - Constraint limiting = selected.limiting(); + ConstraintP limiting = selected.limiting(); ArithVar basic = limiting->getVariable(); Assert(d_linEq.basicIsTracked(basic)); d_linEq.pivotAndUpdate(basic, nonbasic, limiting->getValue()); @@ -765,16 +765,17 @@ std::vector< ArithVarVec > SumOfInfeasibilitiesSPD::greedyConflictSubsets(){ return subsets; } -Node SumOfInfeasibilitiesSPD::generateSOIConflict(const ArithVarVec& subset){ +void SumOfInfeasibilitiesSPD::generateSOIConflict(const ArithVarVec& subset){ Assert(d_soiVar == ARITHVAR_SENTINEL); d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, subset); - NodeBuilder<> conflict(kind::AND); + //NodeBuilder<> conflict(kind::AND); for(ArithVarVec::const_iterator iter = subset.begin(), end = subset.end(); iter != end; ++iter){ ArithVar e = *iter; - Constraint violated = d_errorSet.getViolated(e); + ConstraintP violated = d_errorSet.getViolated(e); //cout << "basic error var: " << violated << endl; - violated->explainForConflict(conflict); + d_conflictChannel.addConstraint(violated); + //violated->explainForConflict(conflict); //d_tableau.debugPrintIsBasic(e); //d_tableau.printBasicRow(e, cout); @@ -785,18 +786,19 @@ Node SumOfInfeasibilitiesSPD::generateSOIConflict(const ArithVarVec& subset){ if(v == d_soiVar){ continue; } const Rational& coeff = entry.getCoefficient(); - Constraint c = (coeff.sgn() > 0) ? + ConstraintP c = (coeff.sgn() > 0) ? d_variables.getUpperBoundConstraint(v) : d_variables.getLowerBoundConstraint(v); //cout << "nb : " << c << endl; - c->explainForConflict(conflict); + d_conflictChannel.addConstraint(c); } - Node conf = conflict; + //Node conf = conflict; tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar); d_soiVar = ARITHVAR_SENTINEL; - return conf; + d_conflictChannel.commitConflict(); + //return conf; } @@ -812,9 +814,10 @@ WitnessImprovement SumOfInfeasibilitiesSPD::SOIConflict(){ if(options::soiQuickExplain()){ quickExplain(); - Node conflict = generateSOIConflict(d_qeConflict); + generateSOIConflict(d_qeConflict); + //Node conflict = generateSOIConflict(d_qeConflict); //cout << conflict << endl; - d_conflictChannel(conflict); + //d_conflictChannel(conflict); }else{ vector<ArithVarVec> subsets = greedyConflictSubsets(); @@ -823,11 +826,12 @@ WitnessImprovement SumOfInfeasibilitiesSPD::SOIConflict(){ Assert(!subsets.empty()); for(vector<ArithVarVec>::const_iterator i = subsets.begin(), end = subsets.end(); i != end; ++i){ const ArithVarVec& subset = *i; - Node conflict = generateSOIConflict(subset); + generateSOIConflict(subset); + //Node conflict = generateSOIConflict(subset); //cout << conflict << endl; //reportConflict(conf); do not do this. We need a custom explanations! - d_conflictChannel(conflict); + //d_conflictChannel(conflict); } } Assert( d_soiVar == ARITHVAR_SENTINEL); diff --git a/src/theory/arith/soi_simplex.h b/src/theory/arith/soi_simplex.h index cee6cf81d..89df69390 100644 --- a/src/theory/arith/soi_simplex.h +++ b/src/theory/arith/soi_simplex.h @@ -171,7 +171,7 @@ private: WitnessImprovement soiRound(); WitnessImprovement SOIConflict(); std::vector< ArithVarVec > greedyConflictSubsets(); - Node generateSOIConflict(const ArithVarVec& subset); + void generateSOIConflict(const ArithVarVec& subset); // WitnessImprovement focusUsingSignDisagreements(ArithVar basic); // WitnessImprovement focusDownToLastHalf(); diff --git a/src/theory/arith/tableau.h b/src/theory/arith/tableau.h index 3e4cb819b..8cf92d075 100644 --- a/src/theory/arith/tableau.h +++ b/src/theory/arith/tableau.h @@ -81,7 +81,7 @@ public: } ArithVar rowIndexToBasic(RowIndex rid) const { - Assert(rid < d_rowIndex2basic.size()); + Assert(d_rowIndex2basic.isKey(rid)); return d_rowIndex2basic[rid]; } diff --git a/src/theory/arith/theory_arith.cpp b/src/theory/arith/theory_arith.cpp index 85d9d1f9b..960a5a066 100644 --- a/src/theory/arith/theory_arith.cpp +++ b/src/theory/arith/theory_arith.cpp @@ -38,6 +38,10 @@ void TheoryArith::preRegisterTerm(TNode n){ d_internal->preRegisterTerm(n); } +Node TheoryArith::expandDefinition(LogicRequest &logicRequest, Node node) { + return d_internal->expandDefinition(logicRequest, node); +} + void TheoryArith::setMasterEqualityEngine(eq::EqualityEngine* eq) { d_internal->setMasterEqualityEngine(eq); } diff --git a/src/theory/arith/theory_arith.h b/src/theory/arith/theory_arith.h index 428c101a6..eaa9a98c6 100644 --- a/src/theory/arith/theory_arith.h +++ b/src/theory/arith/theory_arith.h @@ -53,6 +53,8 @@ public: */ void preRegisterTerm(TNode n); + Node expandDefinition(LogicRequest &logicRequest, Node node); + void setMasterEqualityEngine(eq::EqualityEngine* eq); void setQuantifiersEngine(QuantifiersEngine* qe); diff --git a/src/theory/arith/theory_arith_private.cpp b/src/theory/arith/theory_arith_private.cpp index 40a336a4a..a63de446c 100644 --- a/src/theory/arith/theory_arith_private.cpp +++ b/src/theory/arith/theory_arith_private.cpp @@ -38,9 +38,11 @@ #include "util/statistics_registry.h" #include "util/result.h" +#include "smt/logic_request.h" #include "smt/logic_exception.h" #include "theory/arith/arithvar.h" +#include "theory/arith/cut_log.h" #include "theory/arith/delta_rational.h" #include "theory/arith/matrix.h" #include "theory/arith/arith_rewriter.h" @@ -54,6 +56,9 @@ #include "theory/arith/approx_simplex.h" #include "theory/arith/constraint.h" +#include "theory/ite_utilities.h" +#include "theory/arith/arith_ite_utils.h" + #include "theory/arith/arith_utilities.h" #include "theory/arith/delta_rational.h" #include "theory/arith/partial_model.h" @@ -66,6 +71,8 @@ #include "theory/theory_model.h" #include "theory/arith/options.h" +#include "theory/quantifiers/options.h" + #include "theory/quantifiers/bounded_integers.h" @@ -82,11 +89,17 @@ namespace CVC4 { namespace theory { namespace arith { +static Node toSumNode(const ArithVariables& vars, const DenseMap<Rational>& sum); +static double fRand(double fMin, double fMax); +static bool complexityBelow(const DenseMap<Rational>& row, uint32_t cap); + + TheoryArithPrivate::TheoryArithPrivate(TheoryArith& containing, context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) : d_containing(containing), d_nlIncomplete( false), d_rowTracking(), - d_constraintDatabase(c, u, d_partialModel, d_congruenceManager, RaiseConflict(*this)), + d_conflictBuffer(), + d_constraintDatabase(c, u, d_partialModel, d_congruenceManager, RaiseConflict(*this, d_conflictBuffer)), d_qflraStatus(Result::SAT_UNKNOWN), d_unknownsInARow(0), d_hasDoneWorkSinceCut(false), @@ -108,26 +121,97 @@ TheoryArithPrivate::TheoryArithPrivate(TheoryArith& containing, context::Context d_tableauResetDensity(1.6), d_tableauResetPeriod(10), d_conflicts(c), - d_congruenceManager(c, d_constraintDatabase, SetupLiteralCallBack(*this), d_partialModel, RaiseConflict(*this)), - d_dualSimplex(d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)), - d_fcSimplex(d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)), - d_soiSimplex(d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)), - d_attemptSolSimplex(d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)), + d_blackBoxConflict(c, Node::null()), + d_congruenceManager(c, d_constraintDatabase, SetupLiteralCallBack(*this), d_partialModel, RaiseConflict(*this, d_conflictBuffer)), + d_cmEnabled(c, true), + d_dualSimplex(d_linEq, d_errorSet, RaiseConflict(*this, d_conflictBuffer), TempVarMalloc(*this)), + d_fcSimplex(d_linEq, d_errorSet, RaiseConflict(*this, d_conflictBuffer), TempVarMalloc(*this)), + d_soiSimplex(d_linEq, d_errorSet, RaiseConflict(*this, d_conflictBuffer), TempVarMalloc(*this)), + d_attemptSolSimplex(d_linEq, d_errorSet, RaiseConflict(*this, d_conflictBuffer), TempVarMalloc(*this)), + d_pass1SDP(NULL), + d_otherSDP(NULL), + d_lastContextIntegerAttempted(c,-1), d_DELTA_ZERO(0), + d_approxCuts(c), d_fullCheckCounter(0), d_cutCount(c, 0), d_cutInContext(c), d_likelyIntegerInfeasible(c, false), d_guessedCoeffSet(c, false), d_guessedCoeffs(), + d_treeLog(NULL), + d_replayVariables(), + d_replayConstraints(), + d_lhsTmp(), + d_approxStats(NULL), + d_attemptSolveIntTurnedOff(u, 0), + d_dioSolveResources(0), + d_solveIntMaybeHelp(0u), + d_solveIntAttempts(0u), d_statistics() { srand(79); } -TheoryArithPrivate::~TheoryArithPrivate(){ } +TheoryArithPrivate::~TheoryArithPrivate(){ + if(d_treeLog != NULL){ delete d_treeLog; } + if(d_approxStats != NULL) { delete d_approxStats; } +} + +static bool contains(const ConstraintCPVec& v, ConstraintP con){ + for(unsigned i = 0, N = v.size(); i < N; ++i){ + if(v[i] == con){ + return true; + } + } + return false; +} +static void drop( ConstraintCPVec& v, ConstraintP con){ + size_t readPos, writePos, N; + for(readPos = 0, writePos = 0, N = v.size(); readPos < N; ++readPos){ + ConstraintCP curr = v[readPos]; + if(curr != con){ + v[writePos] = curr; + writePos++; + } + } + v.resize(writePos); +} +static void resolve(ConstraintCPVec& buf, ConstraintP c, const ConstraintCPVec& pos, const ConstraintCPVec& neg){ + unsigned posPos CVC4_UNUSED = pos.size(); + for(unsigned i = 0, N = pos.size(); i < N; ++i){ + if(pos[i] == c){ + posPos = i; + }else{ + buf.push_back(pos[i]); + } + } + Assert(posPos < pos.size()); + ConstraintP negc = c->getNegation(); + unsigned negPos CVC4_UNUSED = neg.size(); + for(unsigned i = 0, N = neg.size(); i < N; ++i){ + if(neg[i] == negc){ + negPos = i; + }else{ + buf.push_back(neg[i]); + } + } + Assert(negPos < neg.size()); + + // Assert(dnconf.getKind() == kind::AND); + // Assert(upconf.getKind() == kind::AND); + // Assert(dnpos < dnconf.getNumChildren()); + // Assert(uppos < upconf.getNumChildren()); + // Assert(equalUpToNegation(dnconf[dnpos], upconf[uppos])); + + // NodeBuilder<> nb(kind::AND); + // dropPosition(nb, dnconf, dnpos); + // dropPosition(nb, upconf, uppos); + // return safeConstructNary(nb); +} + void TheoryArithPrivate::setMasterEqualityEngine(eq::EqualityEngine* eq) { d_congruenceManager.setMasterEqualityEngine(eq); } @@ -177,40 +261,79 @@ TheoryArithPrivate::ModelException::ModelException(TNode n, const char* msg) thr TheoryArithPrivate::ModelException::~ModelException() throw (){ } -TheoryArithPrivate::Statistics::Statistics(): - d_statAssertUpperConflicts("theory::arith::AssertUpperConflicts", 0), - d_statAssertLowerConflicts("theory::arith::AssertLowerConflicts", 0), - d_statUserVariables("theory::arith::UserVariables", 0), - d_statSlackVariables("theory::arith::SlackVariables", 0), - d_statDisequalitySplits("theory::arith::DisequalitySplits", 0), - d_statDisequalityConflicts("theory::arith::DisequalityConflicts", 0), - d_simplifyTimer("theory::arith::simplifyTimer"), - d_staticLearningTimer("theory::arith::staticLearningTimer"), - d_presolveTime("theory::arith::presolveTime"), - d_newPropTime("theory::arith::newPropTimer"), - d_externalBranchAndBounds("theory::arith::externalBranchAndBounds",0), - d_initialTableauSize("theory::arith::initialTableauSize", 0), - d_currSetToSmaller("theory::arith::currSetToSmaller", 0), - d_smallerSetToCurr("theory::arith::smallerSetToCurr", 0), - d_restartTimer("theory::arith::restartTimer"), - d_boundComputationTime("theory::arith::bound::time"), - d_boundComputations("theory::arith::bound::boundComputations",0), - d_boundPropagations("theory::arith::bound::boundPropagations",0), - d_unknownChecks("theory::arith::status::unknowns", 0), - d_maxUnknownsInARow("theory::arith::status::maxUnknownsInARow", 0), - d_avgUnknownsInARow("theory::arith::status::avgUnknownsInARow"), - d_revertsOnConflicts("theory::arith::status::revertsOnConflicts",0), - d_commitsOnConflicts("theory::arith::status::commitsOnConflicts",0), - d_nontrivialSatChecks("theory::arith::status::nontrivialSatChecks",0), - d_satPivots("pivots::sat"), - d_unsatPivots("pivots::unsat"), - d_unknownPivots("pivots::unkown") +TheoryArithPrivate::Statistics::Statistics() + : d_statAssertUpperConflicts("theory::arith::AssertUpperConflicts", 0) + , d_statAssertLowerConflicts("theory::arith::AssertLowerConflicts", 0) + , d_statUserVariables("theory::arith::UserVariables", 0) + , d_statAuxiliaryVariables("theory::arith::AuxiliaryVariables", 0) + , d_statDisequalitySplits("theory::arith::DisequalitySplits", 0) + , d_statDisequalityConflicts("theory::arith::DisequalityConflicts", 0) + , d_simplifyTimer("theory::arith::simplifyTimer") + , d_staticLearningTimer("theory::arith::staticLearningTimer") + , d_presolveTime("theory::arith::presolveTime") + , d_newPropTime("theory::arith::newPropTimer") + , d_externalBranchAndBounds("theory::arith::externalBranchAndBounds",0) + , d_initialTableauSize("theory::arith::initialTableauSize", 0) + , d_currSetToSmaller("theory::arith::currSetToSmaller", 0) + , d_smallerSetToCurr("theory::arith::smallerSetToCurr", 0) + , d_restartTimer("theory::arith::restartTimer") + , d_boundComputationTime("theory::arith::bound::time") + , d_boundComputations("theory::arith::bound::boundComputations",0) + , d_boundPropagations("theory::arith::bound::boundPropagations",0) + , d_unknownChecks("theory::arith::status::unknowns", 0) + , d_maxUnknownsInARow("theory::arith::status::maxUnknownsInARow", 0) + , d_avgUnknownsInARow("theory::arith::status::avgUnknownsInARow") + , d_revertsOnConflicts("theory::arith::status::revertsOnConflicts",0) + , d_commitsOnConflicts("theory::arith::status::commitsOnConflicts",0) + , d_nontrivialSatChecks("theory::arith::status::nontrivialSatChecks",0) + , d_replayLogRecCount("z::approx::replay::rec",0) + , d_replayLogRecConflictEscalation("z::approx::replay::rec::escalation",0) + , d_replayLogRecEarlyExit("z::approx::replay::rec::earlyexit",0) + , d_replayBranchCloseFailures("z::approx::replay::rec::branch::closefailures",0) + , d_replayLeafCloseFailures("z::approx::replay::rec::leaf::closefailures",0) + , d_replayBranchSkips("z::approx::replay::rec::branch::skips",0) + , d_mirCutsAttempted("z::approx::cuts::mir::attempted",0) + , d_gmiCutsAttempted("z::approx::cuts::gmi::attempted",0) + , d_branchCutsAttempted("z::approx::cuts::branch::attempted",0) + , d_cutsReconstructed("z::approx::cuts::reconstructed",0) + , d_cutsReconstructionFailed("z::approx::cuts::reconstructed::failed",0) + , d_cutsProven("z::approx::cuts::proofs",0) + , d_cutsProofFailed("z::approx::cuts::proofs::failed",0) + , d_mipReplayLemmaCalls("z::approx::external::calls",0) + , d_mipExternalCuts("z::approx::external::cuts",0) + , d_mipExternalBranch("z::approx::external::branches",0) + , d_inSolveInteger("z::approx::inSolverInteger",0) + , d_branchesExhausted("z::approx::exhausted::branches",0) + , d_execExhausted("z::approx::exhausted::exec",0) + , d_pivotsExhausted("z::approx::exhausted::pivots",0) + , d_panicBranches("z::arith::paniclemmas",0) + , d_relaxCalls("z::arith::relax::calls",0) + , d_relaxLinFeas("z::arith::relax::feasible::res",0) + , d_relaxLinFeasFailures("z::arith::relax::feasible::failures",0) + , d_relaxLinInfeas("z::arith::relax::infeasible",0) + , d_relaxLinInfeasFailures("z::arith::relax::infeasible::failures",0) + , d_relaxLinExhausted("z::arith::relax::exhausted",0) + , d_relaxOthers("z::arith::relax::other",0) + , d_applyRowsDeleted("z::arith::cuts::applyRowsDeleted",0) + , d_replaySimplexTimer("z::approx::replay::simplex::timer") + , d_replayLogTimer("z::approx::replay::log::timer") + , d_solveIntTimer("z::solveInt::timer") + , d_solveRealRelaxTimer("z::solveRealRelax::timer") + , d_solveIntCalls("z::solveInt::calls", 0) + , d_solveStandardEffort("z::solveInt::calls::standardEffort", 0) + , d_approxDisabled("z::approxDisabled", 0) + , d_replayAttemptFailed("z::replayAttemptFailed",0) + , d_cutsRejectedDuringReplay("z::approx::replay::cuts::rejected", 0) + , d_cutsRejectedDuringLemmas("z::approx::external::cuts::rejected", 0) + , d_satPivots("pivots::sat") + , d_unsatPivots("pivots::unsat") + , d_unknownPivots("pivots::unkown") { StatisticsRegistry::registerStat(&d_statAssertUpperConflicts); StatisticsRegistry::registerStat(&d_statAssertLowerConflicts); StatisticsRegistry::registerStat(&d_statUserVariables); - StatisticsRegistry::registerStat(&d_statSlackVariables); + StatisticsRegistry::registerStat(&d_statAuxiliaryVariables); StatisticsRegistry::registerStat(&d_statDisequalitySplits); StatisticsRegistry::registerStat(&d_statDisequalityConflicts); StatisticsRegistry::registerStat(&d_simplifyTimer); @@ -241,6 +364,54 @@ TheoryArithPrivate::Statistics::Statistics(): StatisticsRegistry::registerStat(&d_satPivots); StatisticsRegistry::registerStat(&d_unsatPivots); StatisticsRegistry::registerStat(&d_unknownPivots); + + StatisticsRegistry::registerStat(&d_replayLogRecCount); + StatisticsRegistry::registerStat(&d_replayLogRecConflictEscalation); + StatisticsRegistry::registerStat(&d_replayLogRecEarlyExit); + StatisticsRegistry::registerStat(&d_replayBranchCloseFailures); + StatisticsRegistry::registerStat(&d_replayLeafCloseFailures); + StatisticsRegistry::registerStat(&d_replayBranchSkips); + StatisticsRegistry::registerStat(&d_mirCutsAttempted); + StatisticsRegistry::registerStat(&d_gmiCutsAttempted); + StatisticsRegistry::registerStat(&d_branchCutsAttempted); + StatisticsRegistry::registerStat(&d_cutsReconstructed); + StatisticsRegistry::registerStat(&d_cutsProven); + StatisticsRegistry::registerStat(&d_cutsProofFailed); + StatisticsRegistry::registerStat(&d_cutsReconstructionFailed); + StatisticsRegistry::registerStat(&d_mipReplayLemmaCalls); + StatisticsRegistry::registerStat(&d_mipExternalCuts); + StatisticsRegistry::registerStat(&d_mipExternalBranch); + + StatisticsRegistry::registerStat(&d_inSolveInteger); + StatisticsRegistry::registerStat(&d_branchesExhausted); + StatisticsRegistry::registerStat(&d_execExhausted); + StatisticsRegistry::registerStat(&d_pivotsExhausted); + StatisticsRegistry::registerStat(&d_panicBranches); + StatisticsRegistry::registerStat(&d_relaxCalls); + StatisticsRegistry::registerStat(&d_relaxLinFeas); + StatisticsRegistry::registerStat(&d_relaxLinFeasFailures); + StatisticsRegistry::registerStat(&d_relaxLinInfeas); + StatisticsRegistry::registerStat(&d_relaxLinInfeasFailures); + StatisticsRegistry::registerStat(&d_relaxLinExhausted); + StatisticsRegistry::registerStat(&d_relaxOthers); + + StatisticsRegistry::registerStat(&d_applyRowsDeleted); + + StatisticsRegistry::registerStat(&d_replaySimplexTimer); + StatisticsRegistry::registerStat(&d_replayLogTimer); + StatisticsRegistry::registerStat(&d_solveIntTimer); + StatisticsRegistry::registerStat(&d_solveRealRelaxTimer); + + StatisticsRegistry::registerStat(&d_solveIntCalls); + StatisticsRegistry::registerStat(&d_solveStandardEffort); + + StatisticsRegistry::registerStat(&d_approxDisabled); + + StatisticsRegistry::registerStat(&d_replayAttemptFailed); + + StatisticsRegistry::registerStat(&d_cutsRejectedDuringReplay); + StatisticsRegistry::registerStat(&d_cutsRejectedDuringLemmas); + } TheoryArithPrivate::Statistics::~Statistics(){ @@ -248,7 +419,7 @@ TheoryArithPrivate::Statistics::~Statistics(){ StatisticsRegistry::unregisterStat(&d_statAssertLowerConflicts); StatisticsRegistry::unregisterStat(&d_statUserVariables); - StatisticsRegistry::unregisterStat(&d_statSlackVariables); + StatisticsRegistry::unregisterStat(&d_statAuxiliaryVariables); StatisticsRegistry::unregisterStat(&d_statDisequalitySplits); StatisticsRegistry::unregisterStat(&d_statDisequalityConflicts); StatisticsRegistry::unregisterStat(&d_simplifyTimer); @@ -278,6 +449,66 @@ TheoryArithPrivate::Statistics::~Statistics(){ StatisticsRegistry::unregisterStat(&d_satPivots); StatisticsRegistry::unregisterStat(&d_unsatPivots); StatisticsRegistry::unregisterStat(&d_unknownPivots); + + StatisticsRegistry::unregisterStat(&d_replayLogRecCount); + StatisticsRegistry::unregisterStat(&d_replayLogRecConflictEscalation); + StatisticsRegistry::unregisterStat(&d_replayLogRecEarlyExit); + StatisticsRegistry::unregisterStat(&d_replayBranchCloseFailures); + StatisticsRegistry::unregisterStat(&d_replayLeafCloseFailures); + StatisticsRegistry::unregisterStat(&d_replayBranchSkips); + StatisticsRegistry::unregisterStat(&d_mirCutsAttempted); + StatisticsRegistry::unregisterStat(&d_gmiCutsAttempted); + StatisticsRegistry::unregisterStat(&d_branchCutsAttempted); + StatisticsRegistry::unregisterStat(&d_cutsReconstructed); + StatisticsRegistry::unregisterStat(&d_cutsProven); + StatisticsRegistry::unregisterStat(&d_cutsProofFailed); + StatisticsRegistry::unregisterStat(&d_cutsReconstructionFailed); + StatisticsRegistry::unregisterStat(&d_mipReplayLemmaCalls); + StatisticsRegistry::unregisterStat(&d_mipExternalCuts); + StatisticsRegistry::unregisterStat(&d_mipExternalBranch); + + + StatisticsRegistry::unregisterStat(&d_inSolveInteger); + StatisticsRegistry::unregisterStat(&d_branchesExhausted); + StatisticsRegistry::unregisterStat(&d_execExhausted); + StatisticsRegistry::unregisterStat(&d_pivotsExhausted); + StatisticsRegistry::unregisterStat(&d_panicBranches); + StatisticsRegistry::unregisterStat(&d_relaxCalls); + StatisticsRegistry::unregisterStat(&d_relaxLinFeas); + StatisticsRegistry::unregisterStat(&d_relaxLinFeasFailures); + StatisticsRegistry::unregisterStat(&d_relaxLinInfeas); + StatisticsRegistry::unregisterStat(&d_relaxLinInfeasFailures); + StatisticsRegistry::unregisterStat(&d_relaxLinExhausted); + StatisticsRegistry::unregisterStat(&d_relaxOthers); + + StatisticsRegistry::unregisterStat(&d_applyRowsDeleted); + + StatisticsRegistry::unregisterStat(&d_replaySimplexTimer); + StatisticsRegistry::unregisterStat(&d_replayLogTimer); + StatisticsRegistry::unregisterStat(&d_solveIntTimer); + StatisticsRegistry::unregisterStat(&d_solveRealRelaxTimer); + + StatisticsRegistry::unregisterStat(&d_solveIntCalls); + StatisticsRegistry::unregisterStat(&d_solveStandardEffort); + + StatisticsRegistry::unregisterStat(&d_approxDisabled); + + StatisticsRegistry::unregisterStat(&d_replayAttemptFailed); + + StatisticsRegistry::unregisterStat(&d_cutsRejectedDuringReplay); + StatisticsRegistry::unregisterStat(&d_cutsRejectedDuringLemmas); +} + +bool complexityBelow(const DenseMap<Rational>& row, uint32_t cap){ + DenseMap<Rational>::const_iterator riter, rend; + for(riter=row.begin(), rend=row.end(); riter != rend; ++riter){ + ArithVar v = *riter; + const Rational& q = row[v]; + if(q.complexity() > cap){ + return false; + } + } + return true; } void TheoryArithPrivate::revertOutOfConflict(){ @@ -290,25 +521,67 @@ void TheoryArithPrivate::clearUpdates(){ d_updatedBounds.purge(); } +void TheoryArithPrivate::raiseConflict(ConstraintCP a, ConstraintCP b){ + ConstraintCPVec v; + v.push_back(a); + v.push_back(b); + d_conflicts.push_back(v); +} + +void TheoryArithPrivate::raiseConflict(ConstraintCP a, ConstraintCP b, ConstraintCP c){ + ConstraintCPVec v; + v.push_back(a); + v.push_back(b); + v.push_back(c); + d_conflicts.push_back(v); +} + void TheoryArithPrivate::zeroDifferenceDetected(ArithVar x){ - Assert(d_congruenceManager.isWatchedVariable(x)); - Assert(d_partialModel.upperBoundIsZero(x)); - Assert(d_partialModel.lowerBoundIsZero(x)); + if(d_cmEnabled){ + Assert(d_congruenceManager.isWatchedVariable(x)); + Assert(d_partialModel.upperBoundIsZero(x)); + Assert(d_partialModel.lowerBoundIsZero(x)); - Constraint lb = d_partialModel.getLowerBoundConstraint(x); - Constraint ub = d_partialModel.getUpperBoundConstraint(x); + ConstraintP lb = d_partialModel.getLowerBoundConstraint(x); + ConstraintP ub = d_partialModel.getUpperBoundConstraint(x); + + if(lb->isEquality()){ + d_congruenceManager.watchedVariableIsZero(lb); + }else if(ub->isEquality()){ + d_congruenceManager.watchedVariableIsZero(ub); + }else{ + d_congruenceManager.watchedVariableIsZero(lb, ub); + } + } +} - if(lb->isEquality()){ - d_congruenceManager.watchedVariableIsZero(lb); - }else if(ub->isEquality()){ - d_congruenceManager.watchedVariableIsZero(ub); +bool TheoryArithPrivate::getSolveIntegerResource(){ + if(d_attemptSolveIntTurnedOff > 0){ + d_attemptSolveIntTurnedOff = d_attemptSolveIntTurnedOff - 1; + return false; + }else{ + return true; + } +} + +bool TheoryArithPrivate::getDioCuttingResource(){ + if(d_dioSolveResources > 0){ + d_dioSolveResources--; + if(d_dioSolveResources == 0){ + d_dioSolveResources = -options::rrTurns(); + } + return true; }else{ - d_congruenceManager.watchedVariableIsZero(lb, ub); + d_dioSolveResources++; + if(d_dioSolveResources >= 0){ + d_dioSolveResources = options::dioSolverTurns(); + } + return false; } } /* procedure AssertLower( x_i >= c_i ) */ -bool TheoryArithPrivate::AssertLower(Constraint constraint){ +bool TheoryArithPrivate::AssertLower(ConstraintP constraint){ Assert(constraint != NullConstraint); Assert(constraint->isLowerBound()); @@ -326,38 +599,43 @@ bool TheoryArithPrivate::AssertLower(Constraint constraint){ int cmpToUB = d_partialModel.cmpToUpperBound(x_i, c_i); if(cmpToUB > 0){ // c_i < \lowerbound(x_i) - Constraint ubc = d_partialModel.getUpperBoundConstraint(x_i); - Node conflict = ConstraintValue::explainConflict(ubc, constraint); - Debug("arith") << "AssertLower conflict " << conflict << endl; + ConstraintP ubc = d_partialModel.getUpperBoundConstraint(x_i); + raiseConflict(ubc, constraint); + + // Node conflict = ConstraintValue::explainConflict(ubc, constraint); + // Debug("arith") << "AssertLower conflict " << conflict << endl; + // raiseConflict(conflict); ++(d_statistics.d_statAssertLowerConflicts); - raiseConflict(conflict); return true; }else if(cmpToUB == 0){ if(isInteger(x_i)){ d_constantIntegerVariables.push_back(x_i); Debug("dio::push") << x_i << endl; } - Constraint ub = d_partialModel.getUpperBoundConstraint(x_i); - - if(!d_congruenceManager.isWatchedVariable(x_i) || c_i.sgn() != 0){ - // if it is not a watched variable report it - // if it is is a watched variable and c_i == 0, - // let zeroDifferenceDetected(x_i) catch this - d_congruenceManager.equalsConstant(constraint, ub); + ConstraintP ub = d_partialModel.getUpperBoundConstraint(x_i); + + if(d_cmEnabled){ + if(!d_congruenceManager.isWatchedVariable(x_i) || c_i.sgn() != 0){ + // if it is not a watched variable report it + // if it is is a watched variable and c_i == 0, + // let zeroDifferenceDetected(x_i) catch this + d_congruenceManager.equalsConstant(constraint, ub); + } } const ValueCollection& vc = constraint->getValueCollection(); if(vc.hasDisequality()){ Assert(vc.hasEquality()); - const Constraint eq = vc.getEquality(); - const Constraint diseq = vc.getDisequality(); + ConstraintP eq = vc.getEquality(); + ConstraintP diseq = vc.getDisequality(); if(diseq->isTrue()){ - //const Constraint ub = vc.getUpperBound(); - Node conflict = ConstraintValue::explainConflict(diseq, ub, constraint); + //const ConstraintP ub = vc.getUpperBound(); + raiseConflict(diseq, ub, constraint); + //Node conflict = ConstraintValue::explainConflict(diseq, ub, constraint); ++(d_statistics.d_statDisequalityConflicts); - Debug("eq") << " assert lower conflict " << conflict << endl; - raiseConflict(conflict); + //Debug("eq") << " assert lower conflict " << conflict << endl; + //raiseConflict(conflict); return true; }else if(!eq->isTrue()){ Debug("eq") << "lb == ub, propagate eq" << eq << endl; @@ -370,17 +648,19 @@ bool TheoryArithPrivate::AssertLower(Constraint constraint){ const ValueCollection& vc = constraint->getValueCollection(); if(vc.hasDisequality()){ - const Constraint diseq = vc.getDisequality(); + const ConstraintP diseq = vc.getDisequality(); if(diseq->isTrue()){ - const Constraint ub = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), UpperBound); + const ConstraintP ub = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), UpperBound); if(ub->hasProof()){ - Node conflict = ConstraintValue::explainConflict(diseq, ub, constraint); - Debug("eq") << " assert upper conflict " << conflict << endl; - raiseConflict(conflict); + raiseConflict(diseq, ub, constraint); return true; + // Node conflict = ConstraintValue::explainConflict(diseq, ub, constraint); + // Debug("eq") << " assert upper conflict " << conflict << endl; + // raiseConflict(conflict); + // return true; }else if(!ub->negationHasProof()){ - Constraint negUb = ub->getNegation(); + ConstraintP negUb = ub->getNegation(); negUb->impliedBy(constraint, diseq); d_learnedBounds.push_back(negUb); } @@ -393,12 +673,14 @@ bool TheoryArithPrivate::AssertLower(Constraint constraint){ d_partialModel.setLowerBoundConstraint(constraint); - if(d_congruenceManager.isWatchedVariable(x_i)){ - int sgn = c_i.sgn(); - if(sgn > 0){ - d_congruenceManager.watchedVariableCannotBeZero(constraint); - }else if(sgn == 0 && d_partialModel.upperBoundIsZero(x_i)){ - zeroDifferenceDetected(x_i); + if(d_cmEnabled){ + if(d_congruenceManager.isWatchedVariable(x_i)){ + int sgn = c_i.sgn(); + if(sgn > 0){ + d_congruenceManager.watchedVariableCannotBeZero(constraint); + }else if(sgn == 0 && d_partialModel.upperBoundIsZero(x_i)){ + zeroDifferenceDetected(x_i); + } } } @@ -428,7 +710,7 @@ bool TheoryArithPrivate::AssertLower(Constraint constraint){ } /* procedure AssertUpper( x_i <= c_i) */ -bool TheoryArithPrivate::AssertUpper(Constraint constraint){ +bool TheoryArithPrivate::AssertUpper(ConstraintP constraint){ ArithVar x_i = constraint->getVariable(); const DeltaRational& c_i = constraint->getValue(); @@ -450,34 +732,38 @@ bool TheoryArithPrivate::AssertUpper(Constraint constraint){ // cmpToLb = \lowerbound(x_i).cmp(c_i) int cmpToLB = d_partialModel.cmpToLowerBound(x_i, c_i); if( cmpToLB < 0 ){ // \upperbound(x_i) < \lowerbound(x_i) - Constraint lbc = d_partialModel.getLowerBoundConstraint(x_i); - Node conflict = ConstraintValue::explainConflict(lbc, constraint); - Debug("arith") << "AssertUpper conflict " << conflict << endl; + ConstraintP lbc = d_partialModel.getLowerBoundConstraint(x_i); + raiseConflict(lbc, constraint); + //Node conflict = ConstraintValue::explainConflict(lbc, constraint); + //Debug("arith") << "AssertUpper conflict " << conflict << endl; ++(d_statistics.d_statAssertUpperConflicts); - raiseConflict(conflict); + //raiseConflict(conflict); return true; }else if(cmpToLB == 0){ // \lowerBound(x_i) == \upperbound(x_i) if(isInteger(x_i)){ d_constantIntegerVariables.push_back(x_i); Debug("dio::push") << x_i << endl; } - Constraint lb = d_partialModel.getLowerBoundConstraint(x_i); - if(!d_congruenceManager.isWatchedVariable(x_i) || c_i.sgn() != 0){ - // if it is not a watched variable report it - // if it is is a watched variable and c_i == 0, - // let zeroDifferenceDetected(x_i) catch this - d_congruenceManager.equalsConstant(lb, constraint); + ConstraintP lb = d_partialModel.getLowerBoundConstraint(x_i); + if(d_cmEnabled){ + if(!d_congruenceManager.isWatchedVariable(x_i) || c_i.sgn() != 0){ + // if it is not a watched variable report it + // if it is is a watched variable and c_i == 0, + // let zeroDifferenceDetected(x_i) catch this + d_congruenceManager.equalsConstant(lb, constraint); + } } const ValueCollection& vc = constraint->getValueCollection(); if(vc.hasDisequality()){ Assert(vc.hasEquality()); - const Constraint diseq = vc.getDisequality(); - const Constraint eq = vc.getEquality(); + const ConstraintP diseq = vc.getDisequality(); + const ConstraintP eq = vc.getEquality(); if(diseq->isTrue()){ - Node conflict = ConstraintValue::explainConflict(diseq, lb, constraint); - Debug("eq") << " assert upper conflict " << conflict << endl; - raiseConflict(conflict); + raiseConflict(diseq, lb, constraint); + //Node conflict = ConstraintValue::explainConflict(diseq, lb, constraint); + //Debug("eq") << " assert upper conflict " << conflict << endl; + //raiseConflict(conflict); return true; }else if(!eq->isTrue()){ Debug("eq") << "lb == ub, propagate eq" << eq << endl; @@ -488,17 +774,18 @@ bool TheoryArithPrivate::AssertUpper(Constraint constraint){ }else if(cmpToLB > 0){ const ValueCollection& vc = constraint->getValueCollection(); if(vc.hasDisequality()){ - const Constraint diseq = vc.getDisequality(); + const ConstraintP diseq = vc.getDisequality(); if(diseq->isTrue()){ - const Constraint lb = + const ConstraintP lb = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), LowerBound); if(lb->hasProof()){ - Node conflict = ConstraintValue::explainConflict(diseq, lb, constraint); - Debug("eq") << " assert upper conflict " << conflict << endl; - raiseConflict(conflict); + raiseConflict(diseq, lb, constraint); + //Node conflict = ConstraintValue::explainConflict(diseq, lb, constraint); + //Debug("eq") << " assert upper conflict " << conflict << endl; + //raiseConflict(conflict); return true; }else if(!lb->negationHasProof()){ - Constraint negLb = lb->getNegation(); + ConstraintP negLb = lb->getNegation(); negLb->impliedBy(constraint, diseq); d_learnedBounds.push_back(negLb); } @@ -512,13 +799,15 @@ bool TheoryArithPrivate::AssertUpper(Constraint constraint){ d_partialModel.setUpperBoundConstraint(constraint); - if(d_congruenceManager.isWatchedVariable(x_i)){ - int sgn = c_i.sgn(); - if(sgn < 0){ - d_congruenceManager.watchedVariableCannotBeZero(constraint); - }else if(sgn == 0 && d_partialModel.lowerBoundIsZero(x_i)){ - zeroDifferenceDetected(x_i); - } + if(d_cmEnabled){ + if(d_congruenceManager.isWatchedVariable(x_i)){ + int sgn = c_i.sgn(); + if(sgn < 0){ + d_congruenceManager.watchedVariableCannotBeZero(constraint); + }else if(sgn == 0 && d_partialModel.lowerBoundIsZero(x_i)){ + zeroDifferenceDetected(x_i); + } + } } d_updatedBounds.softAdd(x_i); @@ -548,7 +837,7 @@ bool TheoryArithPrivate::AssertUpper(Constraint constraint){ /* procedure AssertEquality( x_i == c_i ) */ -bool TheoryArithPrivate::AssertEquality(Constraint constraint){ +bool TheoryArithPrivate::AssertEquality(ConstraintP constraint){ AssertArgument(constraint != NullConstraint, "AssertUpper() called on a NullConstraint."); @@ -570,18 +859,21 @@ bool TheoryArithPrivate::AssertEquality(Constraint constraint){ } if(cmpToUB > 0){ - Constraint ubc = d_partialModel.getUpperBoundConstraint(x_i); - Node conflict = ConstraintValue::explainConflict(ubc, constraint); - Debug("arith") << "AssertEquality conflicts with upper bound " << conflict << endl; - raiseConflict(conflict); + ConstraintP ubc = d_partialModel.getUpperBoundConstraint(x_i); + raiseConflict(ubc, constraint); + //Node conflict = ConstraintValue::explainConflict(ubc, constraint); + //Debug("arith") << "AssertEquality conflicts with upper bound " << conflict << endl; + //raiseConflict(conflict); return true; } if(cmpToLB < 0){ - Constraint lbc = d_partialModel.getLowerBoundConstraint(x_i); - Node conflict = ConstraintValue::explainConflict(lbc, constraint); - Debug("arith") << "AssertEquality conflicts with lower bound" << conflict << endl; - raiseConflict(conflict); + ConstraintP lbc = d_partialModel.getLowerBoundConstraint(x_i); + raiseConflict(lbc, constraint); + + // Node conflict = ConstraintValue::explainConflict(lbc, constraint); + // Debug("arith") << "AssertEquality conflicts with lower bound" << conflict << endl; + // raiseConflict(conflict); return true; } @@ -604,16 +896,18 @@ bool TheoryArithPrivate::AssertEquality(Constraint constraint){ d_partialModel.setUpperBoundConstraint(constraint); d_partialModel.setLowerBoundConstraint(constraint); - if(d_congruenceManager.isWatchedVariable(x_i)){ - int sgn = c_i.sgn(); - if(sgn == 0){ - zeroDifferenceDetected(x_i); + if(d_cmEnabled){ + if(d_congruenceManager.isWatchedVariable(x_i)){ + int sgn = c_i.sgn(); + if(sgn == 0){ + zeroDifferenceDetected(x_i); + }else{ + d_congruenceManager.watchedVariableCannotBeZero(constraint); + d_congruenceManager.equalsConstant(constraint); + } }else{ - d_congruenceManager.watchedVariableCannotBeZero(constraint); d_congruenceManager.equalsConstant(constraint); } - }else{ - d_congruenceManager.equalsConstant(constraint); } d_updatedBounds.softAdd(x_i); @@ -643,7 +937,7 @@ bool TheoryArithPrivate::AssertEquality(Constraint constraint){ /* procedure AssertDisequality( x_i != c_i ) */ -bool TheoryArithPrivate::AssertDisequality(Constraint constraint){ +bool TheoryArithPrivate::AssertDisequality(ConstraintP constraint){ AssertArgument(constraint != NullConstraint, "AssertUpper() called on a NullConstraint."); @@ -655,32 +949,35 @@ bool TheoryArithPrivate::AssertDisequality(Constraint constraint){ //Should be fine in integers Assert(!isInteger(x_i) || c_i.isIntegral()); - if(d_congruenceManager.isWatchedVariable(x_i)){ - int sgn = c_i.sgn(); - if(sgn == 0){ - d_congruenceManager.watchedVariableCannotBeZero(constraint); + if(d_cmEnabled){ + if(d_congruenceManager.isWatchedVariable(x_i)){ + int sgn = c_i.sgn(); + if(sgn == 0){ + d_congruenceManager.watchedVariableCannotBeZero(constraint); + } } } const ValueCollection& vc = constraint->getValueCollection(); if(vc.hasLowerBound() && vc.hasUpperBound()){ - const Constraint lb = vc.getLowerBound(); - const Constraint ub = vc.getUpperBound(); + const ConstraintP lb = vc.getLowerBound(); + const ConstraintP ub = vc.getUpperBound(); if(lb->isTrue() && ub->isTrue()){ //in conflict Debug("eq") << "explaining" << endl; ++(d_statistics.d_statDisequalityConflicts); - Node conflict = ConstraintValue::explainConflict(constraint, lb, ub); - raiseConflict(conflict); + raiseConflict(constraint, lb, ub); + //Node conflict = ConstraintValue::explainConflict(constraint, lb, ub); + //raiseConflict(conflict); return true; } } if(vc.hasLowerBound() ){ - const Constraint lb = vc.getLowerBound(); + const ConstraintP lb = vc.getLowerBound(); if(lb->isTrue()){ - const Constraint ub = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), UpperBound); + const ConstraintP ub = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), UpperBound); Debug("eq") << "propagate UpperBound " << constraint << lb << ub << endl; - const Constraint negUb = ub->getNegation(); + const ConstraintP negUb = ub->getNegation(); if(!negUb->isTrue()){ negUb->impliedBy(constraint, lb); d_learnedBounds.push_back(negUb); @@ -688,12 +985,12 @@ bool TheoryArithPrivate::AssertDisequality(Constraint constraint){ } } if(vc.hasUpperBound()){ - const Constraint ub = vc.getUpperBound(); + const ConstraintP ub = vc.getUpperBound(); if(ub->isTrue()){ - const Constraint lb = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), LowerBound); + const ConstraintP lb = d_constraintDatabase.ensureConstraint(const_cast<ValueCollection&>(vc), LowerBound); Debug("eq") << "propagate LowerBound " << constraint << lb << ub << endl; - const Constraint negLb = lb->getNegation(); + const ConstraintP negLb = lb->getNegation(); if(!negLb->isTrue()){ negLb->impliedBy(constraint, ub); d_learnedBounds.push_back(negLb); @@ -930,28 +1227,23 @@ Theory::PPAssertStatus TheoryArithPrivate::ppAssert(TNode in, SubstitutionMap& o Assert(elim == Rewriter::rewrite(elim)); - static const unsigned MAX_SUB_SIZE = 2; + static const unsigned MAX_SUB_SIZE = 20; if(right.size() > MAX_SUB_SIZE){ Debug("simplify") << "TheoryArithPrivate::solve(): did not substitute due to the right hand side containing too many terms: " << minVar << ":" << elim << endl; Debug("simplify") << right.size() << endl; - // cout << "TheoryArithPrivate::solve(): did not substitute due to the right hand side containing too many terms: " << minVar << ":" << elim << endl; - // cout << right.size() << endl; }else if(elim.hasSubterm(minVar)){ Debug("simplify") << "TheoryArithPrivate::solve(): can't substitute due to recursive pattern with sharing: " << minVar << ":" << elim << endl; - // cout << "TheoryArithPrivate::solve(): can't substitute due to recursive pattern with sharing: " << minVar << ":" << elim << endl; }else if (!minVar.getType().isInteger() || right.isIntegral()) { Assert(!elim.hasSubterm(minVar)); // cannot eliminate integers here unless we know the resulting // substitution is integral Debug("simplify") << "TheoryArithPrivate::solve(): substitution " << minVar << " |-> " << elim << endl; - //cout << "TheoryArithPrivate::solve(): substitution " << minVar << " |-> " << elim << endl; outSubstitutions.addSubstitution(minVar, elim); return Theory::PP_ASSERT_STATUS_SOLVED; } else { Debug("simplify") << "TheoryArithPrivate::solve(): can't substitute b/c it's integer: " << minVar << ":" << minVar.getType() << " |-> " << elim << ":" << elim.getType() << endl; - //cout << "TheoryArithPrivate::solve(): can't substitute b/c it's integer: " << minVar << ":" << minVar.getType() << " |-> " << elim << ":" << elim.getType() << endl; } } @@ -1010,7 +1302,7 @@ void TheoryArithPrivate::setupVariable(const Variable& x){ Assert(!isSetup(n)); ++(d_statistics.d_statUserVariables); - requestArithVar(n,false); + requestArithVar(n, false, false); //ArithVar varN = requestArithVar(n,false); //setupInitialValue(varN); @@ -1049,7 +1341,7 @@ void TheoryArithPrivate::setupVariableList(const VarList& vl){ d_nlIncomplete = true; ++(d_statistics.d_statUserVariables); - requestArithVar(vlNode, false); + requestArithVar(vlNode, false, false); //ArithVar av = requestArithVar(vlNode, false); //setupInitialValue(av); @@ -1242,7 +1534,7 @@ void TheoryArithPrivate::setupPolynomial(const Polynomial& poly) { vector<Rational> coefficients; asVectors(poly, coefficients, variables); - ArithVar varSlack = requestArithVar(polyNode, true); + ArithVar varSlack = requestArithVar(polyNode, true, false); d_tableau.addRow(varSlack, coefficients, variables); setupBasicValue(varSlack); d_linEq.trackRowIndex(d_tableau.basicToRowIndex(varSlack)); @@ -1267,7 +1559,7 @@ void TheoryArithPrivate::setupPolynomial(const Polynomial& poly) { } } - ++(d_statistics.d_statSlackVariables); + ++(d_statistics.d_statAuxiliaryVariables); markSetup(polyNode); } @@ -1306,9 +1598,9 @@ void TheoryArithPrivate::preRegisterTerm(TNode n) { if(!isSetup(n)){ setupAtom(n); } - Constraint c = d_constraintDatabase.lookup(n); + ConstraintP c = d_constraintDatabase.lookup(n); Assert(c != NullConstraint); - + Debug("arith::preregister") << "setup constraint" << c << endl; Assert(!c->canBePropagated()); c->setPreregistered(); @@ -1323,15 +1615,15 @@ void TheoryArithPrivate::preRegisterTerm(TNode n) { } void TheoryArithPrivate::releaseArithVar(ArithVar v){ - Assert(d_partialModel.hasNode(v)); + //Assert(d_partialModel.hasNode(v)); d_constraintDatabase.removeVariable(v); d_partialModel.releaseArithVar(v); } -ArithVar TheoryArithPrivate::requestArithVar(TNode x, bool slack){ +ArithVar TheoryArithPrivate::requestArithVar(TNode x, bool aux, bool internal){ //TODO : The VarList trick is good enough? - Assert(isLeaf(x) || VarList::isMember(x) || x.getKind() == PLUS); + Assert(isLeaf(x) || VarList::isMember(x) || x.getKind() == PLUS || internal); if(getLogicInfo().isLinear() && Variable::isDivMember(x)){ stringstream ss; ss << "A non-linear fact (involving div/mod/divisibility) was asserted to arithmetic in a linear logic: " << x << endl @@ -1342,7 +1634,7 @@ ArithVar TheoryArithPrivate::requestArithVar(TNode x, bool slack){ Assert(x.getType().isReal()); // real or integer ArithVar max = d_partialModel.getNumberOfVariables(); - ArithVar varX = d_partialModel.allocate(x, slack); + ArithVar varX = d_partialModel.allocate(x, aux); bool reclaim = max >= d_partialModel.getNumberOfVariables();; @@ -1354,7 +1646,9 @@ ArithVar TheoryArithPrivate::requestArithVar(TNode x, bool slack){ } d_constraintDatabase.addVariable(varX); - Debug("arith::arithvar") << x << " |-> " << varX << endl; + Debug("arith::arithvar") << "@" << getSatContext()->getLevel() + << " " << x << " |-> " << varX + << "(relaiming " << reclaim << ")" << endl; Assert(!d_partialModel.hasUpperBound(varX)); Assert(!d_partialModel.hasLowerBound(varX)); @@ -1370,7 +1664,7 @@ void TheoryArithPrivate::asVectors(const Polynomial& p, std::vector<Rational>& c Node n = variable.getNode(); - Debug("rewriter") << "should be var: " << n << endl; + Debug("arith::asVectors") << "should be var: " << n << endl; // TODO: This VarList::isMember(n) can be stronger Assert(isLeaf(n) || VarList::isMember(n)); @@ -1471,6 +1765,9 @@ Node TheoryArithPrivate::dioCutting(){ Comparison geq = Comparison::mkComparison(GEQ, p, c); Node lemma = NodeManager::currentNM()->mkNode(OR, leq.getNode(), geq.getNode()); Node rewrittenLemma = Rewriter::rewrite(lemma); + Debug("arith::dio::ex") << "dioCutting found the plane: " << plane.getNode() << endl; + Debug("arith::dio::ex") << "resulting in the cut: " << lemma << endl; + Debug("arith::dio::ex") << "rewritten " << rewrittenLemma << endl; Debug("arith::dio") << "dioCutting found the plane: " << plane.getNode() << endl; Debug("arith::dio") << "resulting in the cut: " << lemma << endl; Debug("arith::dio") << "rewritten " << rewrittenLemma << endl; @@ -1489,16 +1786,16 @@ Node TheoryArithPrivate::callDioSolver(){ Assert(d_partialModel.boundsAreEqual(v)); - Constraint lb = d_partialModel.getLowerBoundConstraint(v); - Constraint ub = d_partialModel.getUpperBoundConstraint(v); + ConstraintP lb = d_partialModel.getLowerBoundConstraint(v); + ConstraintP ub = d_partialModel.getUpperBoundConstraint(v); Node orig = Node::null(); if(lb->isEquality()){ - orig = lb->explainForConflict(); + orig = lb->externalExplainByAssertions(); }else if(ub->isEquality()){ - orig = ub->explainForConflict(); + orig = ub->externalExplainByAssertions(); }else { - orig = ConstraintValue::explainConflict(ub, lb); + orig = Constraint_::externalExplainByAssertions(ub, lb); } Assert(d_partialModel.assignmentIsConsistent(v)); @@ -1521,7 +1818,7 @@ Node TheoryArithPrivate::callDioSolver(){ return d_diosolver.processEquationsForConflict(); } -Constraint TheoryArithPrivate::constraintFromFactQueue(){ +ConstraintP TheoryArithPrivate::constraintFromFactQueue(){ Assert(!done()); TNode assertion = get(); @@ -1530,7 +1827,7 @@ Constraint TheoryArithPrivate::constraintFromFactQueue(){ } Kind simpleKind = Comparison::comparisonKind(assertion); - Constraint constraint = d_constraintDatabase.lookup(assertion); + ConstraintP constraint = d_constraintDatabase.lookup(assertion); if(constraint == NullConstraint){ Assert(simpleKind == EQUAL || simpleKind == DISTINCT ); bool isDistinct = simpleKind == DISTINCT; @@ -1542,7 +1839,7 @@ Constraint TheoryArithPrivate::constraintFromFactQueue(){ // if is (not true), or false Assert((reEq.getConst<bool>() && isDistinct) || (!reEq.getConst<bool>() && !isDistinct)); - raiseConflict(assertion); + blackBoxConflict(assertion); } return NullConstraint; } @@ -1563,7 +1860,7 @@ Constraint TheoryArithPrivate::constraintFromFactQueue(){ Assert(constraint != NullConstraint); if(constraint->negationHasProof()){ - Constraint negation = constraint->getNegation(); + ConstraintP negation = constraint->getNegation(); if(negation->isSelfExplaining()){ if(Debug.isOn("whytheoryenginewhy")){ debugPrintFacts(); @@ -1572,12 +1869,21 @@ Constraint TheoryArithPrivate::constraintFromFactQueue(){ Debug("arith::eq") << constraint << endl; Debug("arith::eq") << negation << endl; - NodeBuilder<> nb(kind::AND); - nb << assertion; - negation->explainForConflict(nb); - Node conflict = nb; - Debug("arith::eq") << "conflict" << conflict << endl; - raiseConflict(conflict); + constraint->setAssertedToTheTheoryWithNegationTrue(assertion); + if(!constraint->hasProof()){ + Debug("arith::constraint") << "marking as constraint as self explaining " << endl; + constraint->selfExplainingWithNegationTrue(); + }else{ + Debug("arith::constraint") << "already has proof: " << constraint->externalExplainByAssertions() << endl; + } + + raiseConflict(constraint, negation); + // NodeBuilder<> nb(kind::AND); + // nb << assertion; + // negation->explainForConflict(nb); + // Node conflict = nb; + // Debug("arith::eq") << "conflict" << conflict << endl; + // raiseConflict(conflict); return NullConstraint; } Assert(!constraint->negationHasProof()); @@ -1593,14 +1899,14 @@ Constraint TheoryArithPrivate::constraintFromFactQueue(){ Debug("arith::constraint") << "marking as constraint as self explaining " << endl; constraint->selfExplaining(); }else{ - Debug("arith::constraint") << "already has proof: " << constraint->explainForConflict() << endl; + Debug("arith::constraint") << "already has proof: " << constraint->externalExplainByAssertions() << endl; } return constraint; } } -bool TheoryArithPrivate::assertionCases(Constraint constraint){ +bool TheoryArithPrivate::assertionCases(ConstraintP constraint){ Assert(constraint->hasProof()); Assert(!constraint->negationHasProof()); @@ -1609,11 +1915,12 @@ bool TheoryArithPrivate::assertionCases(Constraint constraint){ switch(constraint->getType()){ case UpperBound: if(isInteger(x_i) && constraint->isStrictUpperBound()){ - Constraint floorConstraint = constraint->getFloor(); + ConstraintP floorConstraint = constraint->getFloor(); if(!floorConstraint->isTrue()){ if(floorConstraint->negationHasProof()){ - Node conf = ConstraintValue::explainConflict(constraint, floorConstraint->getNegation()); - raiseConflict(conf); + raiseConflict(constraint, floorConstraint->getNegation()); + //Node conf = Constraint_::explainConflict(constraint, floorConstraint->getNegation()); + //raiseConflict(conf); return true; }else{ floorConstraint->impliedBy(constraint); @@ -1626,11 +1933,12 @@ bool TheoryArithPrivate::assertionCases(Constraint constraint){ } case LowerBound: if(isInteger(x_i) && constraint->isStrictLowerBound()){ - Constraint ceilingConstraint = constraint->getCeiling(); + ConstraintP ceilingConstraint = constraint->getCeiling(); if(!ceilingConstraint->isTrue()){ if(ceilingConstraint->negationHasProof()){ - Node conf = ConstraintValue::explainConflict(constraint, ceilingConstraint->getNegation()); - raiseConflict(conf); + raiseConflict(constraint, ceilingConstraint->getNegation()); + //Node conf = Constraint_::explainConflict(constraint, ceilingConstraint->getNegation()); + //raiseConflict(conf); return true; } ceilingConstraint->impliedBy(constraint); @@ -1649,168 +1957,1165 @@ bool TheoryArithPrivate::assertionCases(Constraint constraint){ return false; } } - /** - * Looks for the next integer variable without an integer assignment in a round robin fashion. - * Changes the value of d_nextIntegerCheckVar. + * Looks for through the variables starting at d_nextIntegerCheckVar + * for the first integer variable that is between its upper and lower bounds + * that has a non-integer assignment. + * + * If assumeBounds is true, skip the check that the variable is in bounds. * - * If this returns false, d_nextIntegerCheckVar does not have an integer assignment. - * If this returns true, all integer variables have an integer assignment. + * If there is no such variable, returns ARITHVAR_SENTINEL; */ -bool TheoryArithPrivate::hasIntegerModel(){ - //if(d_variables.size() > 0){ +ArithVar TheoryArithPrivate::nextIntegerViolatation(bool assumeBounds) const { ArithVar numVars = d_partialModel.getNumberOfVariables(); + ArithVar v = d_nextIntegerCheckVar; if(numVars > 0){ const ArithVar rrEnd = d_nextIntegerCheckVar; do { - //Do not include slack variables - if(isInteger(d_nextIntegerCheckVar) && !isSlackVariable(d_nextIntegerCheckVar)) { // integer - const DeltaRational& d = d_partialModel.getAssignment(d_nextIntegerCheckVar); - if(!d.isIntegral()){ - return false; + if(isIntegerInput(v)){ + if(!d_partialModel.integralAssignment(v)){ + if( assumeBounds || d_partialModel.assignmentIsConsistent(v) ){ + return v; + } } } - } while((d_nextIntegerCheckVar = (1 + d_nextIntegerCheckVar == numVars ? 0 : 1 + d_nextIntegerCheckVar)) != rrEnd); + v= (1 + v == numVars) ? 0 : (1 + v); + }while(v != rrEnd); + } + return ARITHVAR_SENTINEL; +} + +/** + * Checks the set of integer variables I to see if each variable + * in I has an integer assignment. + */ +bool TheoryArithPrivate::hasIntegerModel(){ + ArithVar next = nextIntegerViolatation(true); + if(next != ARITHVAR_SENTINEL){ + d_nextIntegerCheckVar = next; + if(Debug.isOn("arith::hasIntegerModel")){ + Debug("arith::hasIntegerModel") << "has int model? " << next << endl; + d_partialModel.printModel(next, Debug("arith::hasIntegerModel")); + } + return false; + }else{ + return true; } - return true; } /** Outputs conflicts to the output channel. */ void TheoryArithPrivate::outputConflicts(){ - Assert(!d_conflicts.empty()); - for(size_t i = 0, i_end = d_conflicts.size(); i < i_end; ++i){ - Node conflict = d_conflicts[i]; - Debug("arith::conflict") << "d_conflicts[" << i << "] " << conflict << endl; - (d_containing.d_out)->conflict(conflict); - } -} - -void TheoryArithPrivate::branchVector(const std::vector<ArithVar>& lemmas){ - //output the lemmas - for(vector<ArithVar>::const_iterator i = lemmas.begin(); i != lemmas.end(); ++i){ - ArithVar v = *i; - Assert(!d_cutInContext.contains(v)); - d_cutInContext.insert(v); - d_cutCount = d_cutCount + 1; - Node lem = branchIntegerVariable(v); - outputLemma(lem); - ++(d_statistics.d_externalBranchAndBounds); + Assert(anyConflict()); + if(!conflictQueueEmpty()){ + Assert(!d_conflicts.empty()); + for(size_t i = 0, i_end = d_conflicts.size(); i < i_end; ++i){ + const ConstraintCPVec& vec = d_conflicts[i]; + Node conflict = Constraint_::externalExplainByAssertions(vec); + Debug("arith::conflict") << "d_conflicts[" << i << "] " << conflict << endl; + (d_containing.d_out)->conflict(conflict); + } + } + if(!d_blackBoxConflict.get().isNull()){ + Node bb = d_blackBoxConflict.get(); + Debug("arith::conflict") << "black box conflict" << bb << endl; + (d_containing.d_out)->conflict(bb); } } +void TheoryArithPrivate::outputLemma(TNode lem) { + (d_containing.d_out)->lemma(lem); +} -bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){ - Assert(d_qflraStatus != Result::SAT); +// void TheoryArithPrivate::branchVector(const std::vector<ArithVar>& lemmas){ +// //output the lemmas +// for(vector<ArithVar>::const_iterator i = lemmas.begin(); i != lemmas.end(); ++i){ +// ArithVar v = *i; +// Assert(!d_cutInContext.contains(v)); +// d_cutInContext.insert(v); +// d_cutCount = d_cutCount + 1; +// Node lem = branchIntegerVariable(v); +// outputLemma(lem); +// ++(d_statistics.d_externalBranchAndBounds); +// } +// } + +bool TheoryArithPrivate::attemptSolveInteger(Theory::Effort effortLevel, bool emmmittedLemmaOrSplit){ + int level = getSatContext()->getLevel(); + Debug("approx") + << "attemptSolveInteger " << d_qflraStatus + << " " << emmmittedLemmaOrSplit + << " " << effortLevel + << " " << d_lastContextIntegerAttempted + << " " << level + << " " << hasIntegerModel() + << endl; + + if(d_qflraStatus == Result::UNSAT){ return false; } + if(emmmittedLemmaOrSplit){ return false; } + if(!options::useApprox()){ return false; } + if(!ApproximateSimplex::enabled()){ return false; } + + if(Theory::fullEffort(effortLevel)){ + if(hasIntegerModel()){ + return false; + }else{ + return getSolveIntegerResource(); + } + } - d_partialModel.stopQueueingBoundCounts(); - UpdateTrackingCallback utcb(&d_linEq); - d_partialModel.processBoundsQueue(utcb); - d_linEq.startTrackingBoundCounts(); + if(d_lastContextIntegerAttempted <= 0){ + if(hasIntegerModel()){ + d_lastContextIntegerAttempted = getSatContext()->getLevel(); + return false; + }else{ + return getSolveIntegerResource(); + } + } - bool noPivotLimit = Theory::fullEffort(effortLevel) || - !options::restrictedPivots(); - bool emmittedConflictOrSplit = false; + if(!options::trySolveIntStandardEffort()){ return false; } + + if (d_lastContextIntegerAttempted <= (level >> 2)){ + + double d = (double)(d_solveIntMaybeHelp + 1) / (d_solveIntAttempts + 1 + level*level); + double t = fRand(0.0, 1.0); + if(t < d){ + return getSolveIntegerResource(); + } + } + return false; +} - SimplexDecisionProcedure& simplex = - options::useFC() ? (SimplexDecisionProcedure&)d_fcSimplex : - (options::useSOI() ? (SimplexDecisionProcedure&)d_soiSimplex : - (SimplexDecisionProcedure&)d_dualSimplex); +bool TheoryArithPrivate::replayLog(ApproximateSimplex* approx){ + TimerStat::CodeTimer codeTimer(d_statistics.d_replayLogTimer); - bool useFancyFinal = options::fancyFinal() && ApproximateSimplex::enabled(); + Assert(d_replayVariables.empty()); + Assert(d_replayConstraints.empty()); - if(!useFancyFinal){ - d_qflraStatus = simplex.findModel(noPivotLimit); + size_t enteringPropN = d_currentPropagationList.size(); + Assert(conflictQueueEmpty()); + TreeLog& tl = getTreeLog(); + //tl.applySelected(); /* set row ids */ + + d_replayedLemmas = false; + + std::vector<ConstraintCPVec> res; + try{ + /* use the try block for the purpose of pushing the sat context */ + context::Context::ScopedPush speculativePush(getSatContext()); + d_cmEnabled = false; + res = replayLogRec(approx, tl.getRootId(), NullConstraint, 1); + }catch(RationalFromDoubleException& rfde){ + turnOffApproxFor(options::replayNumericFailurePenalty()); + } + + for(size_t i =0, N = res.size(); i < N; ++i){ + raiseConflict(res[i]); + } + if(res.empty()){ + ++d_statistics.d_replayAttemptFailed; + } + if(d_currentPropagationList.size() > enteringPropN){ + d_currentPropagationList.resize(enteringPropN); + } + + Assert(d_replayVariables.empty()); + Assert(d_replayConstraints.empty()); + + return !conflictQueueEmpty(); +} + +std::pair<ConstraintP, ArithVar> TheoryArithPrivate::replayGetConstraint(const DenseMap<Rational>& lhs, Kind k, const Rational& rhs, bool branch) +{ + ArithVar added = ARITHVAR_SENTINEL; + Node sum = toSumNode(d_partialModel, lhs); + if(sum.isNull()){ return make_pair(NullConstraint, added); } + + Node norm = Rewriter::rewrite(sum); + DeltaRational dr(rhs); + + ConstraintType t = (k == kind::LEQ) ? UpperBound : LowerBound; + + Assert(!branch || d_partialModel.hasArithVar(norm)); + ArithVar v = ARITHVAR_SENTINEL; + if(d_partialModel.hasArithVar(norm)){ + + v = d_partialModel.asArithVar(norm); + Debug("approx::constraint") << "replayGetConstraint found " + << norm << " |-> " << v << " @ " << getSatContext()->getLevel() << endl; + Assert(!branch || d_partialModel.isIntegerInput(v)); }else{ - // Fancy final tries the following strategy - // At final check, try the preferred simplex solver with a pivot cap - // If that failed, swap the the other simplex solver - // If that failed, check if there are integer variables to cut - // If that failed, do a simplex without a pivot limit + v = requestArithVar(norm, true, true); + d_replayVariables.push_back(v); - int16_t oldCap = options::arithStandardCheckVarOrderPivots(); + added = v; - static const int32_t pass2Limit = 10; - static const int32_t relaxationLimit = 10000; - static const int32_t mipLimit = 200000; + Debug("approx::constraint") << "replayGetConstraint adding " + << norm << " |-> " << v << " @ " << getSatContext()->getLevel() << endl; - //cout << "start" << endl; - d_qflraStatus = simplex.findModel(false); - //cout << "end" << endl; - if(d_qflraStatus == Result::SAT_UNKNOWN || - (d_qflraStatus == Result::SAT && !hasIntegerModel() && !d_likelyIntegerInfeasible)){ + Polynomial poly = Polynomial::parsePolynomial(norm); + vector<ArithVar> variables; + vector<Rational> coefficients; + asVectors(poly, coefficients, variables); + d_tableau.addRow(v, coefficients, variables); + setupBasicValue(v); + d_linEq.trackRowIndex(d_tableau.basicToRowIndex(v)); + } + Assert(d_partialModel.hasArithVar(norm)); + Assert(d_partialModel.asArithVar(norm) == v); + Assert(d_constraintDatabase.variableDatabaseIsSetup(v)); + + ConstraintP imp = d_constraintDatabase.getBestImpliedBound(v, t, dr); + if(imp != NullConstraint){ + if(imp->getValue() == dr){ + Assert(added == ARITHVAR_SENTINEL); + return make_pair(imp, added); + } + } + ConstraintP newc = d_constraintDatabase.getConstraint(v, t, dr); + d_replayConstraints.push_back(newc); + return make_pair(newc, added); +} + +std::pair<ConstraintP, ArithVar> TheoryArithPrivate::replayGetConstraint(ApproximateSimplex* approx, const NodeLog& nl) throw(RationalFromDoubleException){ + Assert(nl.isBranch()); + Assert(d_lhsTmp.empty()); + + ArithVar v = approx->getBranchVar(nl); + if(v != ARITHVAR_SENTINEL && d_partialModel.isIntegerInput(v)){ + if(d_partialModel.hasNode(v)){ + d_lhsTmp.set(v, Rational(1)); + double dval = nl.branchValue(); + Rational val = ApproximateSimplex::estimateWithCFE(dval); + Rational fl(val.floor()); + pair<ConstraintP, ArithVar> p; + p = replayGetConstraint(d_lhsTmp, kind::LEQ, fl, true); + d_lhsTmp.purge(); + return p; + } + } + return make_pair(NullConstraint, ARITHVAR_SENTINEL); +} + +std::pair<ConstraintP, ArithVar> TheoryArithPrivate::replayGetConstraint(const CutInfo& ci) { + Assert(ci.reconstructed()); + const DenseMap<Rational>& lhs = ci.getReconstruction().lhs; + const Rational& rhs = ci.getReconstruction().rhs; + Kind k = ci.getKind(); + + return replayGetConstraint(lhs, k, rhs, ci.getKlass() == BranchCutKlass); +} + +// Node denseVectorToLiteral(const ArithVariables& vars, const DenseVector& dv, Kind k){ +// NodeManager* nm = NodeManager::currentNM(); +// Node sumLhs = toSumNode(vars, dv.lhs); +// Node ineq = nm->mkNode(k, sumLhs, mkRationalNode(dv.rhs) ); +// Node lit = Rewriter::rewrite(ineq); +// return lit; +// } + +Node toSumNode(const ArithVariables& vars, const DenseMap<Rational>& sum){ + NodeBuilder<> nb(kind::PLUS); + NodeManager* nm = NodeManager::currentNM(); + DenseMap<Rational>::const_iterator iter, end; + iter = sum.begin(), end = sum.end(); + for(; iter != end; ++iter){ + ArithVar x = *iter; + if(!vars.hasNode(x)){ return Node::null(); } + Node xNode = vars.asNode(x); + const Rational& q = sum[x]; + nb << nm->mkNode(kind::MULT, mkRationalNode(q), xNode); + } + return safeConstructNary(nb); +} + + +void TheoryArithPrivate::tryBranchCut(ApproximateSimplex* approx, int nid, BranchCutInfo& bci){ + Assert(conflictQueueEmpty()); + std::vector< ConstraintCPVec > conflicts; + + approx->tryCut(nid, bci); + Debug("approx::branch") << "tryBranchCut" << bci << endl; + Assert(bci.reconstructed()); + Assert(!bci.proven()); + pair<ConstraintP, ArithVar> p = replayGetConstraint(bci); + Assert(p.second == ARITHVAR_SENTINEL); + ConstraintP bc = p.first; + Assert(bc != NullConstraint); + if(bc->hasProof()){ + return; + } + + ConstraintP bcneg = bc->getNegation(); + { + context::Context::ScopedPush speculativePush(getSatContext()); + replayAssert(bcneg); + if(conflictQueueEmpty()){ + TimerStat::CodeTimer codeTimer(d_statistics.d_replaySimplexTimer); + + //test for linear feasibility + d_partialModel.stopQueueingBoundCounts(); + UpdateTrackingCallback utcb(&d_linEq); + d_partialModel.processBoundsQueue(utcb); + d_linEq.startTrackingBoundCounts(); + + SimplexDecisionProcedure& simplex = selectSimplex(true); + simplex.findModel(false); + + d_linEq.stopTrackingBoundCounts(); + d_partialModel.startQueueingBoundCounts(); + } + for(size_t i = 0, N = d_conflicts.size(); i < N; ++i){ + conflicts.push_back(d_conflicts[i]); + // remove the floor/ceiling contraint implied by bcneg + Constraint_::assertionFringe(conflicts.back()); + } + + if(Debug.isOn("approx::branch")){ + if(d_conflicts.empty()){ + entireStateIsConsistent("branchfailure"); + } + } + } + + Debug("approx::branch") << "branch constraint " << bc << endl; + for(size_t i = 0, N = conflicts.size(); i < N; ++i){ + ConstraintCPVec& conf = conflicts[i]; + + // make sure to be working on the assertion fringe! + if(!contains(conf, bcneg)){ + Debug("approx::branch") << "reraise " << conf << endl; + raiseConflict(conf); + }else if(!bci.proven()){ + drop(conf, bcneg); + bci.setExplanation(conf); + Debug("approx::branch") << "dropped " << bci << endl; + } + } +} - ApproximateSimplex* approxSolver = ApproximateSimplex::mkApproximateSimplexSolver(d_partialModel); - approxSolver->setPivotLimit(relaxationLimit); +void TheoryArithPrivate::replayAssert(ConstraintP c) { + if(!c->assertedToTheTheory()){ + if(c->negationHasProof()){ + ConstraintP neg = c->getNegation(); + raiseConflict(c, neg); + Debug("approx::replayAssert") << "replayAssertion conflict " << neg << " : " << c << endl; + }else if(!c->hasProof()){ + c->setInternalDecision(); + assertionCases(c); + Debug("approx::replayAssert") << "replayAssert " << c << " set internal" << endl; + }else{ + assertionCases(c); + Debug("approx::replayAssert") << "replayAssert " << c << " has explanation" << endl; + } + }else{ + Debug("approx::replayAssert") << "replayAssert " << c << " already asserted" << endl; + } +} + +// ConstraintCPVec TheoryArithPrivate::toExplanation(Node n) const { +// ConstraintCPVec res; +// cout << "toExplanation" << endl; +// if(n.getKind() == kind::AND){ +// for(unsigned i = 0; i < n.getNumChildren(); ++i){ +// ConstraintP c = d_constraintDatabase.lookup(n[i]); +// if(c == NullConstraint){ return std::vector<Constraint>(); } +// res.push_back(c); +// cout << "\t"<<c << endl; +// } +// }else{ +// ConstraintP c = d_constraintDatabase.lookup(n); +// if(c == NullConstraint){ return std::vector<Constraint>(); } +// res.push_back(c); +// } +// return res; +// } + +// void TheoryArithPrivate::enqueueConstraints(std::vector<Constraint>& out, Node n) const{ +// if(n.getKind() == kind::AND){ +// for(unsigned i = 0, N = n.getNumChildren(); i < N; ++i){ +// enqueueConstraints(out, n[i]); +// } +// }else{ +// ConstraintP c = d_constraintDatabase.lookup(n); +// if(c == NullConstraint){ +// cout << "failing on " << n << endl; +// } +// Assert(c != NullConstraint); +// out.push_back(c); +// } +// } + +// ConstraintCPVec TheoryArithPrivate::resolveOutPropagated(const ConstraintCPVec& v, const std::set<ConstraintCP>& propagated) const { +// cout << "resolveOutPropagated()" << conf << endl; +// std::set<ConstraintCP> final; +// std::set<ConstraintCP> processed; +// std::vector<ConstraintCP> to_process; +// enqueueConstraints(to_process, conf); +// while(!to_process.empty()){ +// ConstraintP c = to_process.back(); to_process.pop_back(); +// if(processed.find(c) != processed.end()){ +// continue; +// }else{ +// if(propagated.find(c) == propagated.end()){ +// final.insert(c); +// }else{ +// Node exp = c->explainForPropagation(); +// enqueueConstraints(to_process, exp); +// } +// processed.insert(c); +// } +// } +// cout << "final size: " << final.size() << std::endl; +// NodeBuilder<> nb(kind::AND); +// std::set<Constraint>::const_iterator iter = final.begin(), end = final.end(); +// for(; iter != end; ++iter){ +// ConstraintP c = *iter; +// c->explainForConflict(nb); +// } +// Node newConf = safeConstructNary(nb); +// cout << "resolveOutPropagated("<<conf<<", ...) ->" << newConf << endl; +// return newConf; +// } + +void TheoryArithPrivate::resolveOutPropagated(std::vector<ConstraintCPVec>& confs, const std::set<ConstraintCP>& propagated) const { + Debug("arith::resolveOutPropagated") + << "starting resolveOutPropagated() " << confs.size() << endl; + for(size_t i =0, N = confs.size(); i < N; ++i){ + ConstraintCPVec& conf = confs[i]; + size_t orig = conf.size(); + Constraint_::assertionFringe(conf); + Debug("arith::resolveOutPropagated") + << " conf["<<i<<"] " << orig << " to " << conf.size() << endl; + } + Debug("arith::resolveOutPropagated") + << "ending resolveOutPropagated() " << confs.size() << endl; +} - if(!d_guessedCoeffSet){ - d_guessedCoeffs = approxSolver->heuristicOptCoeffs(); - d_guessedCoeffSet = true; +struct SizeOrd { + bool operator()(const ConstraintCPVec& a, const ConstraintCPVec& b) const{ + return a.size() < b.size(); + } +}; +void TheoryArithPrivate::subsumption(std::vector<ConstraintCPVec>& confs) const { + int checks CVC4_UNUSED = 0; + int subsumed CVC4_UNUSED = 0; + + for(size_t i =0, N= confs.size(); i < N; ++i){ + ConstraintCPVec& conf = confs[i]; + std::sort(conf.begin(), conf.end()); + } + + std::sort(confs.begin(), confs.end(), SizeOrd()); + for(size_t i = 0; i < confs.size(); i++){ + ConstraintCPVec& a = confs[i]; + // i is not subsumed + for(size_t j = i+1; j < confs.size();){ + ConstraintCPVec& b = confs[j]; + checks++; + bool subsumes = std::includes(a.begin(), a.end(), b.begin(), b.end()); + if(subsumes){ + ConstraintCPVec& back = confs.back(); + b.swap(back); + confs.pop_back(); + subsumed++; + }else{ + j++; } - if(!d_guessedCoeffs.empty()){ - approxSolver->setOptCoeffs(d_guessedCoeffs); + } + } + Debug("arith::subsumption") << "subsumed " << subsumed << "/" << checks << endl; +} + +std::vector<ConstraintCPVec> TheoryArithPrivate::replayLogRec(ApproximateSimplex* approx, int nid, ConstraintP bc, int depth){ + ++(d_statistics.d_replayLogRecCount); + Debug("approx::replayLogRec") << "replayLogRec()" + << d_statistics.d_replayLogRecCount.getData() << std::endl; + + size_t rpvars_size = d_replayVariables.size(); + size_t rpcons_size = d_replayConstraints.size(); + std::vector<ConstraintCPVec> res; + + { /* create a block for the purpose of pushing the sat context */ + context::Context::ScopedPush speculativePush(getSatContext()); + Assert(!anyConflict()); + Assert(conflictQueueEmpty()); + set<ConstraintCP> propagated; + + TreeLog& tl = getTreeLog(); + + if(bc != NullConstraint){ + replayAssert(bc); + } + + const NodeLog& nl = tl.getNode(nid); + NodeLog::const_iterator iter = nl.begin(), end = nl.end(); + for(; conflictQueueEmpty() && iter != end; ++iter){ + CutInfo* ci = *iter; + bool reject = false; + //cout << " trying " << *ci << endl; + if(ci->getKlass() == RowsDeletedKlass){ + RowsDeleted* rd = dynamic_cast<RowsDeleted*>(ci); + + tl.applyRowsDeleted(nid, *rd); + // The previous line modifies nl + + ++d_statistics.d_applyRowsDeleted; + }else if(ci->getKlass() == BranchCutKlass){ + BranchCutInfo* bci = dynamic_cast<BranchCutInfo*>(ci); + Assert(bci != NULL); + tryBranchCut(approx, nid, *bci); + + ++d_statistics.d_branchCutsAttempted; + }else{ + approx->tryCut(nid, *ci); + if(ci->getKlass() == GmiCutKlass){ + ++d_statistics.d_gmiCutsAttempted; + }else if(ci->getKlass() == MirCutKlass){ + ++d_statistics.d_mirCutsAttempted; + } + + if(ci->reconstructed() && ci->proven()){ + const DenseMap<Rational>& row = ci->getReconstruction().lhs; + reject = !complexityBelow(row, options::replayRejectCutSize()); + } } + if(conflictQueueEmpty()){ + if(reject){ + ++d_statistics.d_cutsRejectedDuringReplay; + }else if(ci->reconstructed()){ + // success + ++d_statistics.d_cutsReconstructed; + + pair<ConstraintP, ArithVar> p = replayGetConstraint(*ci); + if(p.second != ARITHVAR_SENTINEL){ + Assert(ci->getRowId() >= 1); + tl.mapRowId(nl.getNodeId(), ci->getRowId(), p.second); + } + ConstraintP con = p.first; + if(Debug.isOn("approx::replayLogRec")){ + Debug("approx::replayLogRec") << "cut was remade " << con << " " << *ci << endl; + } - ApproximateSimplex::ApproxResult relaxRes, mipRes; - ApproximateSimplex::Solution relaxSolution, mipSolution; - relaxRes = approxSolver->solveRelaxation(); - switch(relaxRes){ - case ApproximateSimplex::ApproxSat: - { - relaxSolution = approxSolver->extractRelaxation(); + if(ci->proven()){ + ++d_statistics.d_cutsProven; - if(d_likelyIntegerInfeasible){ - d_qflraStatus = d_attemptSolSimplex.attempt(relaxSolution); + const ConstraintCPVec& exp = ci->getExplanation(); + // success + Assert(!con->negationHasProof()); + if(con->isTrue()){ + Debug("approx::replayLogRec") << "not asserted?" << endl; + }else{ + con->impliedBy(exp); + replayAssert(con); + Debug("approx::replayLogRec") << "cut prop" << endl; + } }else{ - approxSolver->setPivotLimit(mipLimit); - mipRes = approxSolver->solveMIP(); - d_errorSet.reduceToSignals(); - //Message() << "here" << endl; - if(mipRes == ApproximateSimplex::ApproxSat){ - mipSolution = approxSolver->extractMIP(); - d_qflraStatus = d_attemptSolSimplex.attempt(mipSolution); + ++d_statistics.d_cutsProofFailed; + Debug("approx::replayLogRec") << "failed to get proof " << *ci << endl; + } + }else if(ci->getKlass() != RowsDeletedKlass){ + ++d_statistics.d_cutsReconstructionFailed; + } + } + } + + /* check if the system is feasible under with the cuts */ + if(conflictQueueEmpty()){ + Assert(options::replayEarlyCloseDepths() >= 1); + if(!nl.isBranch() || depth % options::replayEarlyCloseDepths() == 0 ){ + TimerStat::CodeTimer codeTimer(d_statistics.d_replaySimplexTimer); + //test for linear feasibility + d_partialModel.stopQueueingBoundCounts(); + UpdateTrackingCallback utcb(&d_linEq); + d_partialModel.processBoundsQueue(utcb); + d_linEq.startTrackingBoundCounts(); + + SimplexDecisionProcedure& simplex = selectSimplex(true); + simplex.findModel(false); + + d_linEq.stopTrackingBoundCounts(); + d_partialModel.startQueueingBoundCounts(); + } + }else{ + ++d_statistics.d_replayLogRecConflictEscalation; + } + + if(!conflictQueueEmpty()){ + /* if a conflict has been found stop */ + for(size_t i = 0, N = d_conflicts.size(); i < N; ++i){ + res.push_back(d_conflicts[i]); + } + ++d_statistics.d_replayLogRecEarlyExit; + }else if(nl.isBranch()){ + /* if it is a branch try the branch */ + pair<ConstraintP, ArithVar> p = replayGetConstraint(approx, nl); + Assert(p.second == ARITHVAR_SENTINEL); + ConstraintP dnc = p.first; + if(dnc != NullConstraint){ + ConstraintP upc = dnc->getNegation(); + + int dnid = nl.getDownId(); + int upid = nl.getUpId(); + + NodeLog& dnlog = tl.getNode(dnid); + NodeLog& uplog = tl.getNode(upid); + dnlog.copyParentRowIds(); + uplog.copyParentRowIds(); + + std::vector<ConstraintCPVec> dnres; + std::vector<ConstraintCPVec> upres; + std::vector<size_t> containsdn; + std::vector<size_t> containsup; + if(res.empty()){ + dnres = replayLogRec(approx, dnid, dnc, depth+1); + for(size_t i = 0, N = dnres.size(); i < N; ++i){ + ConstraintCPVec& conf = dnres[i]; + if(contains(conf, dnc)){ + containsdn.push_back(i); }else{ - if(mipRes == ApproximateSimplex::ApproxUnsat){ - d_likelyIntegerInfeasible = true; - } - d_qflraStatus = d_attemptSolSimplex.attempt(relaxSolution); + res.push_back(conf); } } - options::arithStandardCheckVarOrderPivots.set(pass2Limit); - if(d_qflraStatus != Result::UNSAT){ d_qflraStatus = simplex.findModel(false); } - //Message() << "done" << endl; + }else{ + Debug("approx::replayLogRec") << "replayLogRec() skipping" << dnlog << std::endl; + ++d_statistics.d_replayBranchSkips; } - break; - case ApproximateSimplex::ApproxUnsat: - { - ApproximateSimplex::Solution sol = approxSolver->extractRelaxation(); - d_qflraStatus = d_attemptSolSimplex.attempt(sol); - options::arithStandardCheckVarOrderPivots.set(pass2Limit); + if(res.empty()){ + upres = replayLogRec(approx, upid, upc, depth+1); + + for(size_t i = 0, N = upres.size(); i < N; ++i){ + ConstraintCPVec& conf = upres[i]; + if(contains(conf, upc)){ + containsup.push_back(i); + }else{ + res.push_back(conf); + } + } + }else{ + Debug("approx::replayLogRec") << "replayLogRec() skipping" << uplog << std::endl; + ++d_statistics.d_replayBranchSkips; + } + + if(res.empty()){ + for(size_t i = 0, N = containsdn.size(); i < N; ++i){ + ConstraintCPVec& dnconf = dnres[containsdn[i]]; + for(size_t j = 0, M = containsup.size(); j < M; ++j){ + ConstraintCPVec& upconf = upres[containsup[j]]; + + res.push_back(ConstraintCPVec()); + ConstraintCPVec& back = res.back(); + resolve(back, dnc, dnconf, upconf); + } + } + if(res.size() >= 2u){ + subsumption(res); - if(d_qflraStatus != Result::UNSAT){ d_qflraStatus = simplex.findModel(false); } + if(res.size() > 100u){ + res.resize(100u); + } + } + }else{ + Debug("approx::replayLogRec") << "replayLogRec() skipping resolving" << nl << std::endl; + } + Debug("approx::replayLogRec") << "found #"<<res.size()<<" conflicts on branch " << nid << endl; + if(res.empty()){ + ++d_statistics.d_replayBranchCloseFailures; + } + + }else{ + Debug("approx::replayLogRec") << "failed to make a branch " << nid << endl; + } + }else{ + ++d_statistics.d_replayLeafCloseFailures; + Debug("approx::replayLogRec") << "failed on node " << nid << endl; + Assert(res.empty()); + } + resolveOutPropagated(res, propagated); + Debug("approx::replayLogRec") << "replayLogRec() ending" << std::endl; + + + if(options::replayFailureLemma()){ + // must be done inside the sat context to get things + // propagated at this level + if(res.empty() && nid == getTreeLog().getRootId()){ + Assert(!d_replayedLemmas); + d_replayedLemmas = replayLemmas(approx); + Assert(d_acTmp.empty()); + while(!d_approxCuts.empty()){ + Node lem = d_approxCuts.front(); + d_approxCuts.pop(); + d_acTmp.push_back(lem); + } + } + } + } /* pop the sat context */ + + /* move into the current context. */ + while(!d_acTmp.empty()){ + Node lem = d_acTmp.back(); + d_acTmp.pop_back(); + d_approxCuts.push_back(lem); + } + Assert(d_acTmp.empty()); + + + /* Garbage collect the constraints from this call */ + while(d_replayConstraints.size() > rpcons_size){ + ConstraintP c = d_replayConstraints.back(); + d_replayConstraints.pop_back(); + d_constraintDatabase.deleteConstraintAndNegation(c); + } + + /* Garbage collect the ArithVars made by this call */ + if(d_replayVariables.size() > rpvars_size){ + d_partialModel.stopQueueingBoundCounts(); + UpdateTrackingCallback utcb(&d_linEq); + d_partialModel.processBoundsQueue(utcb); + d_linEq.startTrackingBoundCounts(); + while(d_replayVariables.size() > rpvars_size){ + ArithVar v = d_replayVariables.back(); + d_replayVariables.pop_back(); + Assert(d_partialModel.canBeReleased(v)); + if(!d_tableau.isBasic(v)){ + /* if it is not basic make it basic. */ + ArithVar b = ARITHVAR_SENTINEL; + for(Tableau::ColIterator ci = d_tableau.colIterator(v); !ci.atEnd(); ++ci){ + const Tableau::Entry& e = *ci; + b = d_tableau.rowIndexToBasic(e.getRowIndex()); + break; + } + Assert(b != ARITHVAR_SENTINEL); + DeltaRational cp = d_partialModel.getAssignment(b); + if(d_partialModel.cmpAssignmentLowerBound(b) < 0){ + cp = d_partialModel.getLowerBound(b); + }else if(d_partialModel.cmpAssignmentUpperBound(b) > 0){ + cp = d_partialModel.getUpperBound(b); + } + d_linEq.pivotAndUpdate(b, v, cp); + } + Assert(d_tableau.isBasic(v)); + d_linEq.stopTrackingRowIndex(d_tableau.basicToRowIndex(v)); + d_tableau.removeBasicRow(v); + + releaseArithVar(v); + Debug("approx::vars") << "releasing " << v << endl; + } + d_linEq.stopTrackingBoundCounts(); + d_partialModel.startQueueingBoundCounts(); + d_partialModel.attemptToReclaimReleased(); + } + return res; +} + +TreeLog& TheoryArithPrivate::getTreeLog(){ + if(d_treeLog == NULL){ + d_treeLog = new TreeLog(); + } + return *d_treeLog; +} + +ApproximateStatistics& TheoryArithPrivate::getApproxStats(){ + if(d_approxStats == NULL){ + d_approxStats = new ApproximateStatistics(); + } + return *d_approxStats; +} + +Node TheoryArithPrivate::branchToNode(ApproximateSimplex* approx, const NodeLog& bn) const throw(RationalFromDoubleException) { + Assert(bn.isBranch()); + ArithVar v = approx->getBranchVar(bn); + if(v != ARITHVAR_SENTINEL && d_partialModel.isIntegerInput(v)){ + if(d_partialModel.hasNode(v)){ + Node n = d_partialModel.asNode(v); + double dval = bn.branchValue(); + Rational val = ApproximateSimplex::estimateWithCFE(dval); + Rational fl(val.floor()); + NodeManager* nm = NodeManager::currentNM(); + Node leq = nm->mkNode(kind::LEQ, n, mkRationalNode(fl)); + Node norm = Rewriter::rewrite(leq); + return norm; + } + } + return Node::null(); +} + +Node TheoryArithPrivate::cutToLiteral(ApproximateSimplex* approx, const CutInfo& ci) const{ + Assert(ci.reconstructed()); + + const DenseMap<Rational>& lhs = ci.getReconstruction().lhs; + Node sum = toSumNode(d_partialModel, lhs); + if(!sum.isNull()){ + Kind k = ci.getKind(); + Assert(k == kind::LEQ || k == kind::GEQ); + Node rhs = mkRationalNode(ci.getReconstruction().rhs); + + NodeManager* nm = NodeManager::currentNM(); + Node ineq = nm->mkNode(k, sum, rhs); + return Rewriter::rewrite(ineq); + } + return Node::null(); +} + +bool TheoryArithPrivate::replayLemmas(ApproximateSimplex* approx){ + try{ + ++(d_statistics.d_mipReplayLemmaCalls); + bool anythingnew = false; + + TreeLog& tl = getTreeLog(); + NodeLog& root = tl.getRootNode(); + root.applySelected(); /* set row ids */ + + vector<const CutInfo*> cuts = approx->getValidCuts(root); + for(size_t i =0, N =cuts.size(); i < N; ++i){ + const CutInfo* cut = cuts[i]; + Assert(cut->reconstructed()); + Assert(cut->proven()); + + const DenseMap<Rational>& row = cut->getReconstruction().lhs; + if(!complexityBelow(row, options::lemmaRejectCutSize())){ + ++(d_statistics.d_cutsRejectedDuringLemmas); + continue; + } + + Node cutConstraint = cutToLiteral(approx, *cut); + if(!cutConstraint.isNull()){ + const ConstraintCPVec& exp = cut->getExplanation(); + Node asLemma = Constraint_::externalExplainByAssertions(exp); + + Node implied = Rewriter::rewrite(cutConstraint); + anythingnew = anythingnew || !isSatLiteral(implied); + + Node implication = asLemma.impNode(implied); + // DO NOT CALL OUTPUT LEMMA! + d_approxCuts.push_back(implication); + Debug("approx::lemmas") << "cut["<<i<<"] " << implication << endl; + ++(d_statistics.d_mipExternalCuts); + } + } + if(root.isBranch()){ + Node lit = branchToNode(approx, root); + if(!lit.isNull()){ + anythingnew = anythingnew || !isSatLiteral(lit); + Node branch = lit.orNode(lit.notNode()); + d_approxCuts.push_back(branch); + ++(d_statistics.d_mipExternalBranch); + Debug("approx::lemmas") << "branching "<< root <<" as " << branch << endl; + } + } + return anythingnew; + }catch(RationalFromDoubleException& rfde){ + turnOffApproxFor(options::replayNumericFailurePenalty()); + return false; + } +} + +void TheoryArithPrivate::turnOffApproxFor(int32_t rounds){ + d_attemptSolveIntTurnedOff = d_attemptSolveIntTurnedOff + rounds; + ++(d_statistics.d_approxDisabled); +} + +bool TheoryArithPrivate::safeToCallApprox() const{ + unsigned numRows = 0; + unsigned numCols = 0; + var_iterator vi = var_begin(), vi_end = var_end(); + // Assign each variable to a row and column variable as it appears in the input + for(; vi != vi_end && !(numRows > 0 && numCols > 0); ++vi){ + ArithVar v = *vi; + + if(d_partialModel.isAuxiliary(v)){ + ++numRows; + }else{ + ++numCols; + } + } + return (numRows > 0 && numCols > 0); +} + +// solve() +// res = solveRealRelaxation(effortLevel); +// switch(res){ +// case LinFeas: +// case LinInfeas: +// return replay() +// case Unknown: +// case Error +// if() +void TheoryArithPrivate::solveInteger(Theory::Effort effortLevel){ + if(!safeToCallApprox()) { return; } + + Assert(safeToCallApprox()); + TimerStat::CodeTimer codeTimer(d_statistics.d_solveIntTimer); + + ++(d_statistics.d_solveIntCalls); + d_statistics.d_inSolveInteger.setData(1); + + if(!Theory::fullEffort(effortLevel)){ + d_solveIntAttempts++; + ++(d_statistics.d_solveStandardEffort); + } + + // if integers are attempted, + Assert(options::useApprox()); + Assert(ApproximateSimplex::enabled()); + + int level = getSatContext()->getLevel(); + d_lastContextIntegerAttempted = level; + + + static const int32_t mipLimit = 200000; + + TreeLog& tl = getTreeLog(); + ApproximateStatistics& stats = getApproxStats(); + ApproximateSimplex* approx = + ApproximateSimplex::mkApproximateSimplexSolver(d_partialModel, tl, stats); + + try{ + approx->setPivotLimit(mipLimit); + if(!d_guessedCoeffSet){ + d_guessedCoeffs = approx->heuristicOptCoeffs(); + d_guessedCoeffSet = true; + } + if(!d_guessedCoeffs.empty()){ + approx->setOptCoeffs(d_guessedCoeffs); + } + static const int32_t depthForLikelyInfeasible = 10; + int maxDepthPass1 = d_likelyIntegerInfeasible ? + depthForLikelyInfeasible : options::maxApproxDepth(); + approx->setBranchingDepth(maxDepthPass1); + approx->setBranchOnVariableLimit(100); + LinResult relaxRes = approx->solveRelaxation(); + if( relaxRes == LinFeasible ){ + MipResult mipRes = approx->solveMIP(false); + Debug("arith::solveInteger") << "mipRes " << mipRes << endl; + switch(mipRes) { + case MipBingo: + // attempt the solution + { + d_partialModel.stopQueueingBoundCounts(); + UpdateTrackingCallback utcb(&d_linEq); + d_partialModel.processBoundsQueue(utcb); + d_linEq.startTrackingBoundCounts(); + + ApproximateSimplex::Solution mipSolution; + mipSolution = approx->extractMIP(); + importSolution(mipSolution); + solveRelaxationOrPanic(effortLevel); + + // shutdown simplex + d_linEq.stopTrackingBoundCounts(); + d_partialModel.startQueueingBoundCounts(); } break; - default: + case MipClosed: + /* All integer branches closed */ + approx->setPivotLimit(2*mipLimit); + mipRes = approx->solveMIP(true); + if(mipRes == MipClosed){ + d_likelyIntegerInfeasible = true; + replayLog(approx); + } + if(!(anyConflict() || !d_approxCuts.empty())){ + turnOffApproxFor(options::replayNumericFailurePenalty()); + } + break; + case BranchesExhausted: + case ExecExhausted: + case PivotsExhauasted: + if(mipRes == BranchesExhausted){ + ++d_statistics.d_branchesExhausted; + }else if(mipRes == ExecExhausted){ + ++d_statistics.d_execExhausted; + }else if(mipRes == PivotsExhauasted){ + ++d_statistics.d_pivotsExhausted; + } + + approx->setPivotLimit(2*mipLimit); + approx->setBranchingDepth(2); + mipRes = approx->solveMIP(true); + replayLemmas(approx); + break; + case MipUnknown: break; } - delete approxSolver; } + }catch(RationalFromDoubleException& rfde){ + turnOffApproxFor(options::replayNumericFailurePenalty()); + } + delete approx; - if(d_qflraStatus == Result::SAT_UNKNOWN){ - //Message() << "got sat unknown" << endl; - vector<ArithVar> toCut = cutAllBounded(); - if(toCut.size() > 0){ - branchVector(toCut); - emmittedConflictOrSplit = true; - }else{ - //Message() << "splitting" << endl; + if(!Theory::fullEffort(effortLevel)){ + if(anyConflict() || !d_approxCuts.empty()){ + d_solveIntMaybeHelp++; + } + } + + d_statistics.d_inSolveInteger.setData(0); +} - d_qflraStatus = simplex.findModel(noPivotLimit); +SimplexDecisionProcedure& TheoryArithPrivate::selectSimplex(bool pass1){ + if(pass1){ + if(d_pass1SDP == NULL){ + if(options::useFC()){ + d_pass1SDP = (SimplexDecisionProcedure*)(&d_fcSimplex); + }else if(options::useSOI()){ + d_pass1SDP = (SimplexDecisionProcedure*)(&d_soiSimplex); + }else{ + d_pass1SDP = (SimplexDecisionProcedure*)(&d_dualSimplex); + } + } + Assert(d_pass1SDP != NULL); + return *d_pass1SDP; + }else{ + if(d_otherSDP == NULL){ + if(options::useFC()){ + d_otherSDP = (SimplexDecisionProcedure*)(&d_fcSimplex); + }else if(options::useSOI()){ + d_otherSDP = (SimplexDecisionProcedure*)(&d_soiSimplex); + }else{ + d_otherSDP = (SimplexDecisionProcedure*)(&d_soiSimplex); } } + Assert(d_otherSDP != NULL); + return *d_otherSDP; + } +} + +void TheoryArithPrivate::importSolution(const ApproximateSimplex::Solution& solution){ + if(Debug.isOn("arith::importSolution")){ + Debug("arith::importSolution") << "importSolution before " << d_qflraStatus << endl; + d_partialModel.printEntireModel(Debug("arith::importSolution")); + } + + d_qflraStatus = d_attemptSolSimplex.attempt(solution); + + if(Debug.isOn("arith::importSolution")){ + Debug("arith::importSolution") << "importSolution intermediate " << d_qflraStatus << endl; + d_partialModel.printEntireModel(Debug("arith::importSolution")); + } + + if(d_qflraStatus != Result::UNSAT){ + static const int32_t pass2Limit = 20; + int16_t oldCap = options::arithStandardCheckVarOrderPivots(); + options::arithStandardCheckVarOrderPivots.set(pass2Limit); + SimplexDecisionProcedure& simplex = selectSimplex(false); + d_qflraStatus = simplex.findModel(false); options::arithStandardCheckVarOrderPivots.set(oldCap); } + if(Debug.isOn("arith::importSolution")){ + Debug("arith::importSolution") << "importSolution after " << d_qflraStatus << endl; + d_partialModel.printEntireModel(Debug("arith::importSolution")); + } +} + +bool TheoryArithPrivate::solveRelaxationOrPanic(Theory::Effort effortLevel){ + // if at this point the linear relaxation is still unknown, + // attempt to branch an integer variable as a last ditch effort on full check + if(d_qflraStatus == Result::SAT_UNKNOWN){ + d_qflraStatus = selectSimplex(true).findModel(false); + } + + if(Theory::fullEffort(effortLevel) && d_qflraStatus == Result::SAT_UNKNOWN){ + ArithVar canBranch = nextIntegerViolatation(false); + if(canBranch != ARITHVAR_SENTINEL){ + ++d_statistics.d_panicBranches; + Node branch = branchIntegerVariable(canBranch); + Assert(branch.getKind() == kind::OR); + Node rwbranch = Rewriter::rewrite(branch[0]); + if(!isSatLiteral(rwbranch)){ + d_approxCuts.push_back(branch); + return true; + } + } + d_qflraStatus = selectSimplex(false).findModel(true); + } + return false; +} + +bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){ + TimerStat::CodeTimer codeTimer(d_statistics.d_solveRealRelaxTimer); + Assert(d_qflraStatus != Result::SAT); + + d_partialModel.stopQueueingBoundCounts(); + UpdateTrackingCallback utcb(&d_linEq); + d_partialModel.processBoundsQueue(utcb); + d_linEq.startTrackingBoundCounts(); + + bool noPivotLimit = Theory::fullEffort(effortLevel) || + !options::restrictedPivots(); + + SimplexDecisionProcedure& simplex = selectSimplex(true); + + bool useApprox = options::useApprox() && ApproximateSimplex::enabled() && getSolveIntegerResource(); + + bool noPivotLimitPass1 = noPivotLimit && !useApprox; + d_qflraStatus = simplex.findModel(noPivotLimitPass1); + + if(d_qflraStatus == Result::SAT_UNKNOWN && useApprox && safeToCallApprox()){ + // pass2: fancy-final + static const int32_t relaxationLimit = 10000; + Assert(ApproximateSimplex::enabled()); + + TreeLog& tl = getTreeLog(); + ApproximateStatistics& stats = getApproxStats(); + ApproximateSimplex* approxSolver = + ApproximateSimplex::mkApproximateSimplexSolver(d_partialModel, tl, stats); + + approxSolver->setPivotLimit(relaxationLimit); + + if(!d_guessedCoeffSet){ + d_guessedCoeffs = approxSolver->heuristicOptCoeffs(); + d_guessedCoeffSet = true; + } + if(!d_guessedCoeffs.empty()){ + approxSolver->setOptCoeffs(d_guessedCoeffs); + } + + ++d_statistics.d_relaxCalls; + + ApproximateSimplex::Solution relaxSolution; + LinResult relaxRes = approxSolver->solveRelaxation(); + try{ + Debug("solveRealRelaxation") << "solve relaxation? " << endl; + switch(relaxRes){ + case LinFeasible: + Debug("solveRealRelaxation") << "lin feasible? " << endl; + ++d_statistics.d_relaxLinFeas; + relaxSolution = approxSolver->extractRelaxation(); + importSolution(relaxSolution); + if(d_qflraStatus != Result::SAT){ + ++d_statistics.d_relaxLinFeasFailures; + } + break; + case LinInfeasible: + // todo attempt to recreate approximate conflict + ++d_statistics.d_relaxLinInfeas; + Debug("solveRealRelaxation") << "lin infeasible " << endl; + relaxSolution = approxSolver->extractRelaxation(); + importSolution(relaxSolution); + if(d_qflraStatus != Result::UNSAT){ + ++d_statistics.d_relaxLinInfeasFailures; + } + break; + case LinExhausted: + ++d_statistics.d_relaxLinExhausted; + Debug("solveRealRelaxation") << "exhuasted " << endl; + break; + case LinUnknown: + default: + ++d_statistics.d_relaxOthers; + break; + } + }catch(RationalFromDoubleException& rfde){ + turnOffApproxFor(options::replayNumericFailurePenalty()); + } + delete approxSolver; + + } + + bool emmittedConflictOrSplit = solveRelaxationOrPanic(effortLevel); + // TODO Save zeroes with no conflicts d_linEq.stopTrackingBoundCounts(); d_partialModel.startQueueingBoundCounts(); @@ -1818,8 +3123,162 @@ bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){ return emmittedConflictOrSplit; } +// LinUnknown, /* Unknown error */ +// LinFeasible, /* Relaxation is feasible */ +// LinInfeasible, /* Relaxation is infeasible/all integer branches closed */ +// LinExhausted +// // Fancy final tries the following strategy +// // At final check, try the preferred simplex solver with a pivot cap +// // If that failed, swap the the other simplex solver +// // If that failed, check if there are integer variables to cut +// // If that failed, do a simplex without a pivot limit + +// int16_t oldCap = options::arithStandardCheckVarOrderPivots(); + +// static const int32_t pass2Limit = 10; +// static const int32_t relaxationLimit = 10000; +// static const int32_t mipLimit = 200000; + +// //cout << "start" << endl; +// d_qflraStatus = simplex.findModel(false); +// //cout << "end" << endl; +// if(d_qflraStatus == Result::SAT_UNKNOWN || +// (d_qflraStatus == Result::SAT && !hasIntegerModel() && !d_likelyIntegerInfeasible)){ + +// ApproximateSimplex* approxSolver = ApproximateSimplex::mkApproximateSimplexSolver(d_partialModel, *(getTreeLog()), *(getApproxStats())); +// approxSolver->setPivotLimit(relaxationLimit); + +// if(!d_guessedCoeffSet){ +// d_guessedCoeffs = approxSolver->heuristicOptCoeffs(); +// d_guessedCoeffSet = true; +// } +// if(!d_guessedCoeffs.empty()){ +// approxSolver->setOptCoeffs(d_guessedCoeffs); +// } + +// MipResult mipRes; +// ApproximateSimplex::Solution relaxSolution, mipSolution; +// LinResult relaxRes = approxSolver->solveRelaxation(); +// switch(relaxRes){ +// case LinFeasible: +// { +// relaxSolution = approxSolver->extractRelaxation(); + +// /* If the approximate solver known to be integer infeasible +// * only redo*/ +// int maxDepth = +// d_likelyIntegerInfeasible ? 1 : options::arithMaxBranchDepth(); + + +// if(d_likelyIntegerInfeasible){ +// d_qflraStatus = d_attemptSolSimplex.attempt(relaxSolution); +// }else{ +// approxSolver->setPivotLimit(mipLimit); +// mipRes = approxSolver->solveMIP(false); +// if(mipRes == ApproximateSimplex::ApproxUnsat){ +// mipRes = approxSolver->solveMIP(true); +// } +// d_errorSet.reduceToSignals(); +// //Message() << "here" << endl; +// if(mipRes == ApproximateSimplex::ApproxSat){ +// mipSolution = approxSolver->extractMIP(); +// d_qflraStatus = d_attemptSolSimplex.attempt(mipSolution); +// }else{ +// if(mipRes == ApproximateSimplex::ApproxUnsat){ +// d_likelyIntegerInfeasible = true; +// } +// vector<Node> lemmas = approxSolver->getValidCuts(); +// for(size_t i = 0; i < lemmas.size(); ++i){ +// d_approxCuts.pushback(lemmas[i]); +// } +// d_qflraStatus = d_attemptSolSimplex.attempt(relaxSolution); +// } +// } +// options::arithStandardCheckVarOrderPivots.set(pass2Limit); +// if(d_qflraStatus != Result::UNSAT){ d_qflraStatus = simplex.findModel(false); } +// //Message() << "done" << endl; +// } +// break; +// case ApproximateSimplex::ApproxUnsat: +// { +// ApproximateSimplex::Solution sol = approxSolver->extractRelaxation(); + +// d_qflraStatus = d_attemptSolSimplex.attempt(sol); +// options::arithStandardCheckVarOrderPivots.set(pass2Limit); + +// if(d_qflraStatus != Result::UNSAT){ d_qflraStatus = simplex.findModel(false); } +// } +// break; +// default: +// break; +// } +// delete approxSolver; +// } +// } + +// if(!useFancyFinal){ +// d_qflraStatus = simplex.findModel(noPivotLimit); +// }else{ + + +// if(d_qflraStatus == Result::SAT_UNKNOWN){ +// //Message() << "got sat unknown" << endl; +// vector<ArithVar> toCut = cutAllBounded(); +// if(toCut.size() > 0){ +// //branchVector(toCut); +// emmittedConflictOrSplit = true; +// }else{ +// //Message() << "splitting" << endl; + +// d_qflraStatus = simplex.findModel(noPivotLimit); +// } +// } +// options::arithStandardCheckVarOrderPivots.set(oldCap); +// } + +// // TODO Save zeroes with no conflicts +// d_linEq.stopTrackingBoundCounts(); +// d_partialModel.startQueueingBoundCounts(); + +// return emmittedConflictOrSplit; +// } + +bool TheoryArithPrivate::hasFreshArithLiteral(Node n) const{ + switch(n.getKind()){ + case kind::LEQ: + case kind::GEQ: + case kind::GT: + case kind::LT: + return !isSatLiteral(n); + case kind::EQUAL: + if(n[0].getType().isReal()){ + return !isSatLiteral(n); + }else if(n[0].getType().isBoolean()){ + return hasFreshArithLiteral(n[0]) || + hasFreshArithLiteral(n[1]); + }else{ + return false; + } + case kind::IMPLIES: + // try the rhs first + return hasFreshArithLiteral(n[1]) || + hasFreshArithLiteral(n[0]); + default: + if(n.getType().isBoolean()){ + for(Node::iterator ni=n.begin(), nend=n.end(); ni!=nend; ++ni){ + Node child = *ni; + if(hasFreshArithLiteral(child)){ + return true; + } + } + } + return false; + } +} + void TheoryArithPrivate::check(Theory::Effort effortLevel){ Assert(d_currentPropagationList.empty()); + //cout << "TheoryArithPrivate::check " << effortLevel << std::endl; Debug("effortlevel") << "TheoryArithPrivate::check " << effortLevel << std::endl; Debug("arith") << "TheoryArithPrivate::check begun " << effortLevel << std::endl; @@ -1837,28 +3296,28 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ } while(!done()){ - Constraint curr = constraintFromFactQueue(); + ConstraintP curr = constraintFromFactQueue(); if(curr != NullConstraint){ bool res CVC4_UNUSED = assertionCases(curr); - Assert(!res || inConflict()); + Assert(!res || anyConflict()); } - if(inConflict()){ break; } + if(anyConflict()){ break; } } - if(!inConflict()){ + if(!anyConflict()){ while(!d_learnedBounds.empty()){ // we may attempt some constraints twice. this is okay! - Constraint curr = d_learnedBounds.front(); + ConstraintP curr = d_learnedBounds.front(); d_learnedBounds.pop(); Debug("arith::learned") << curr << endl; bool res CVC4_UNUSED = assertionCases(curr); - Assert(!res || inConflict()); + Assert(!res || anyConflict()); - if(inConflict()){ break; } + if(anyConflict()){ break; } } } - if(inConflict()){ + if(anyConflict()){ d_qflraStatus = Result::UNSAT; if(options::revertArithModels() && previous == Result::SAT){ ++d_statistics.d_revertsOnConflicts; @@ -1872,6 +3331,7 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ revertOutOfConflict(); } outputConflicts(); + //cout << "unate conflict 1 " << effortLevel << std::endl; return; } @@ -1884,9 +3344,33 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ Assert(d_conflicts.empty()); bool useSimplex = d_qflraStatus != Result::SAT; + Debug("arith::ems") << "ems: " << emmittedConflictOrSplit + << "pre realRelax" << endl; + if(useSimplex){ emmittedConflictOrSplit = solveRealRelaxation(effortLevel); } + Debug("arith::ems") << "ems: " << emmittedConflictOrSplit + << "post realRelax" << endl; + + + Debug("arith::ems") << "ems: " << emmittedConflictOrSplit + << "pre solveInteger" << endl; + + if(attemptSolveInteger(effortLevel, emmittedConflictOrSplit)){ + solveInteger(effortLevel); + if(anyConflict()){ + ++d_statistics.d_commitsOnConflicts; + Debug("arith::bt") << "committing here " << " " << newFacts << " " << previous << " " << d_qflraStatus << endl; + revertOutOfConflict(); + d_errorSet.clear(); + outputConflicts(); + return; + } + } + + Debug("arith::ems") << "ems: " << emmittedConflictOrSplit + << "post solveInteger" << endl; switch(d_qflraStatus){ case Result::SAT: @@ -1944,6 +3428,7 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ } outputConflicts(); emmittedConflictOrSplit = true; + Debug("arith::conflict") << "simplex conflict" << endl; if(useSimplex && options::collectPivots()){ if(options::useFC()){ @@ -1958,6 +3443,26 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ } d_statistics.d_avgUnknownsInARow.addEntry(d_unknownsInARow); + Debug("arith::ems") << "ems: " << emmittedConflictOrSplit + << "pre approx cuts" << endl; + if(!d_approxCuts.empty()){ + bool anyFresh = false; + while(!d_approxCuts.empty()){ + Node lem = d_approxCuts.front(); + d_approxCuts.pop(); + Debug("arith::approx::cuts") << "approximate cut:" << lem << endl; + anyFresh = anyFresh || hasFreshArithLiteral(lem); + Debug("arith::lemma") << "approximate cut:" << lem << endl; + outputLemma(lem); + } + if(anyFresh){ + emmittedConflictOrSplit = true; + } + } + + Debug("arith::ems") << "ems: " << emmittedConflictOrSplit + << "post approx cuts" << endl; + // This should be fine if sat or unknown if(!emmittedConflictOrSplit && (options::arithPropagationMode() == UNATE_PROP || @@ -1965,8 +3470,8 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ TimerStat::CodeTimer codeTimer(d_statistics.d_newPropTime); Assert(d_qflraStatus != Result::UNSAT); - while(!d_currentPropagationList.empty() && !inConflict()){ - Constraint curr = d_currentPropagationList.front(); + while(!d_currentPropagationList.empty() && !anyConflict()){ + ConstraintP curr = d_currentPropagationList.front(); d_currentPropagationList.pop_front(); ConstraintType t = curr->getType(); @@ -1976,23 +3481,23 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ switch(t){ case LowerBound: { - Constraint prev = d_currentPropagationList.front(); + ConstraintP prev = d_currentPropagationList.front(); d_currentPropagationList.pop_front(); d_constraintDatabase.unatePropLowerBound(curr, prev); break; } case UpperBound: { - Constraint prev = d_currentPropagationList.front(); + ConstraintP prev = d_currentPropagationList.front(); d_currentPropagationList.pop_front(); d_constraintDatabase.unatePropUpperBound(curr, prev); break; } case Equality: { - Constraint prevLB = d_currentPropagationList.front(); + ConstraintP prevLB = d_currentPropagationList.front(); d_currentPropagationList.pop_front(); - Constraint prevUB = d_currentPropagationList.front(); + ConstraintP prevUB = d_currentPropagationList.front(); d_currentPropagationList.pop_front(); d_constraintDatabase.unatePropEquality(curr, prevLB, prevUB); break; @@ -2002,14 +3507,16 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ } } - if(inConflict()){ + if(anyConflict()){ Debug("arith::unate") << "unate conflict" << endl; revertOutOfConflict(); d_qflraStatus = Result::UNSAT; outputConflicts(); emmittedConflictOrSplit = true; + //cout << "unate conflict " << endl; Debug("arith::bt") << "committing on unate conflict" << " " << newFacts << " " << previous << " " << d_qflraStatus << endl; + Debug("arith::conflict") << "unate arith conflict" << endl; } }else{ TimerStat::CodeTimer codeTimer(d_statistics.d_newPropTime); @@ -2017,12 +3524,23 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ } Assert( d_currentPropagationList.empty()); + Debug("arith::ems") << "ems: " << emmittedConflictOrSplit + << "post unate" << endl; + if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel)){ ++d_fullCheckCounter; } if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel)){ emmittedConflictOrSplit = splitDisequalities(); } + Debug("arith::ems") << "ems: " << emmittedConflictOrSplit + << "pos splitting" << endl; + + + Debug("arith") << "integer? " + << " conf/split " << emmittedConflictOrSplit + << " fulleffort " << Theory::fullEffort(effortLevel) + << " hasintmodel " << hasIntegerModel() << endl; if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel) && !hasIntegerModel()){ Node possibleConflict = Node::null(); @@ -2031,22 +3549,22 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ if(possibleConflict != Node::null()){ revertOutOfConflict(); Debug("arith::conflict") << "dio conflict " << possibleConflict << endl; - //cout << "dio conflict " << possibleConflict << endl; - raiseConflict(possibleConflict); + blackBoxConflict(possibleConflict); outputConflicts(); emmittedConflictOrSplit = true; } } if(!emmittedConflictOrSplit && d_hasDoneWorkSinceCut && options::arithDioSolver()){ - Node possibleLemma = dioCutting(); - if(!possibleLemma.isNull()){ - Debug("arith") << "dio cut " << possibleLemma << endl; - //cout << "dio cut " << possibleLemma << endl; - emmittedConflictOrSplit = true; - d_hasDoneWorkSinceCut = false; - d_cutCount = d_cutCount + 1; - outputLemma(possibleLemma); + if(getDioCuttingResource()){ + Node possibleLemma = dioCutting(); + if(!possibleLemma.isNull()){ + emmittedConflictOrSplit = true; + d_hasDoneWorkSinceCut = false; + d_cutCount = d_cutCount + 1; + Debug("arith::lemma") << "dio cut " << possibleLemma << endl; + outputLemma(possibleLemma); + } } } @@ -2056,7 +3574,10 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ ++(d_statistics.d_externalBranchAndBounds); d_cutCount = d_cutCount + 1; emmittedConflictOrSplit = true; + Debug("arith::lemma") << "rrbranch lemma" + << possibleLemma << endl; outputLemma(possibleLemma); + } } @@ -2064,10 +3585,12 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ if(d_diosolver.hasMoreDecompositionLemmas()){ while(d_diosolver.hasMoreDecompositionLemmas()){ Node decompositionLemma = d_diosolver.nextDecompositionLemma(); - Debug("arith") << "dio decomposition lemma " << decompositionLemma << endl; + Debug("arith::lemma") << "dio decomposition lemma " + << decompositionLemma << endl; outputLemma(decompositionLemma); } }else{ + Debug("arith::restart") << "arith restart!" << endl; outputRestart(); } } @@ -2077,6 +3600,15 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ setIncomplete(); } + if(Theory::fullEffort(effortLevel)){ + if(Debug.isOn("arith::consistency::final")){ + entireStateIsConsistent("arith::consistency::final"); + } + // cout << "fulleffort" << getSatContext()->getLevel() << endl; + // entireStateIsConsistent("arith::consistency::final"); + // cout << "emmittedConflictOrSplit" << emmittedConflictOrSplit << endl; + } + if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); } if(Debug.isOn("arith::print_model")) { debugPrintModel(Debug("arith::print_model")); @@ -2127,7 +3659,7 @@ std::vector<ArithVar> TheoryArithPrivate::cutAllBounded() const{ for(ArithVar iter = 0; iter != max; ++iter){ //Do not include slack variables const DeltaRational& d = d_partialModel.getAssignment(iter); - if(isInteger(iter) && !isSlackVariable(iter) && + if(isIntegerInput(iter) && !d_cutInContext.contains(iter) && d_partialModel.hasUpperBound(iter) && d_partialModel.hasLowerBound(iter) && @@ -2147,7 +3679,7 @@ Node TheoryArithPrivate::roundRobinBranch(){ ArithVar v = d_nextIntegerCheckVar; Assert(isInteger(v)); - Assert(!isSlackVariable(v)); + Assert(!isAuxiliaryVariable(v)); return branchIntegerVariable(v); } } @@ -2155,10 +3687,10 @@ Node TheoryArithPrivate::roundRobinBranch(){ bool TheoryArithPrivate::splitDisequalities(){ bool splitSomething = false; - vector<Constraint> save; + vector<ConstraintP> save; while(!d_diseqQueue.empty()){ - Constraint front = d_diseqQueue.front(); + ConstraintP front = d_diseqQueue.front(); d_diseqQueue.pop(); if(front->isSplit()){ @@ -2179,6 +3711,7 @@ bool TheoryArithPrivate::splitDisequalities(){ Debug("arith::lemma") << "Now " << Rewriter::rewrite(lemma) << endl; outputLemma(lemma); + //cout << "Now " << Rewriter::rewrite(lemma) << endl; splitSomething = true; }else if(d_partialModel.strictlyLessThanLowerBound(lhsVar, rhsValue)){ Debug("eq") << "can drop as less than lb" << front << endl; @@ -2190,7 +3723,7 @@ bool TheoryArithPrivate::splitDisequalities(){ } } } - vector<Constraint>::const_iterator i=save.begin(), i_end = save.end(); + vector<ConstraintP>::const_iterator i=save.begin(), i_end = save.end(); for(; i != i_end; ++i){ d_diseqQueue.push(*i); } @@ -2206,17 +3739,17 @@ void TheoryArithPrivate::debugPrintAssertions(std::ostream& out) const { for (var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){ ArithVar i = *vi; if (d_partialModel.hasLowerBound(i)) { - Constraint lConstr = d_partialModel.getLowerBoundConstraint(i); + ConstraintP lConstr = d_partialModel.getLowerBoundConstraint(i); out << lConstr << endl; } if (d_partialModel.hasUpperBound(i)) { - Constraint uConstr = d_partialModel.getUpperBoundConstraint(i); + ConstraintP uConstr = d_partialModel.getUpperBoundConstraint(i); out << uConstr << endl; } } - context::CDQueue<Constraint>::const_iterator it = d_diseqQueue.begin(); - context::CDQueue<Constraint>::const_iterator it_end = d_diseqQueue.end(); + context::CDQueue<ConstraintP>::const_iterator it = d_diseqQueue.begin(); + context::CDQueue<ConstraintP>::const_iterator it_end = d_diseqQueue.end(); for(; it != it_end; ++ it) { out << *it << endl; } @@ -2243,16 +3776,16 @@ Node TheoryArithPrivate::explain(TNode n) { Debug("arith::explain") << "explain @" << getSatContext()->getLevel() << ": " << n << endl; - Constraint c = d_constraintDatabase.lookup(n); + ConstraintP c = d_constraintDatabase.lookup(n); if(c != NullConstraint){ Assert(!c->isSelfExplaining()); - Node exp = c->explainForPropagation(); + Node exp = c->externalExplainForPropagation(); Debug("arith::explain") << "constraint explanation" << n << ":" << exp << endl; return exp; }else if(d_assertionsThatDoNotMatchTheirLiterals.find(n) != d_assertionsThatDoNotMatchTheirLiterals.end()){ c = d_assertionsThatDoNotMatchTheirLiterals[n]; if(!c->isSelfExplaining()){ - Node exp = c->explainForPropagation(); + Node exp = c->externalExplainForPropagation(); Debug("arith::explain") << "assertions explanation" << n << ":" << exp << endl; return exp; }else{ @@ -2285,12 +3818,13 @@ void TheoryArithPrivate::propagate(Theory::Effort e) { } while(d_constraintDatabase.hasMorePropagations()){ - Constraint c = d_constraintDatabase.nextPropagation(); + ConstraintCP c = d_constraintDatabase.nextPropagation(); Debug("arith::prop") << "next prop" << getSatContext()->getLevel() << ": " << c << endl; if(c->negationHasProof()){ - Debug("arith::prop") << "negation has proof " << c->getNegation() << endl - << c->getNegation()->explainForConflict() << endl; + Debug("arith::prop") << "negation has proof " << c->getNegation() << endl; + Debug("arith::prop") << c->getNegation()->externalExplainByAssertions() + << endl; } Assert(!c->negationHasProof(), "A constraint has been propagated on the constraint propagation queue, but the negation has been set to true. Contact Tim now!"); @@ -2311,7 +3845,7 @@ void TheoryArithPrivate::propagate(Theory::Effort e) { //equality engine in the the difference manager. Node normalized = Rewriter::rewrite(toProp); - Constraint constraint = d_constraintDatabase.lookup(normalized); + ConstraintP constraint = d_constraintDatabase.lookup(normalized); if(constraint == NullConstraint){ Debug("arith::prop") << "propagating on non-constraint? " << toProp << endl; @@ -2322,7 +3856,7 @@ void TheoryArithPrivate::propagate(Theory::Effort e) { normalized[0] : normalized.notNode(); Node lp = flattenAnd(exp.andNode(notNormalized)); Debug("arith::prop") << "propagate conflict" << lp << endl; - raiseConflict(lp); + blackBoxConflict(lp); outputConflicts(); return; }else{ @@ -2427,11 +3961,11 @@ DeltaRational TheoryArithPrivate::getDeltaValue(TNode n) const throw (DeltaRatio Rational TheoryArithPrivate::deltaValueForTotalOrder() const{ Rational min(2); std::set<DeltaRational> relevantDeltaValues; - context::CDQueue<Constraint>::const_iterator qiter = d_diseqQueue.begin(); - context::CDQueue<Constraint>::const_iterator qiter_end = d_diseqQueue.end(); + context::CDQueue<ConstraintP>::const_iterator qiter = d_diseqQueue.begin(); + context::CDQueue<ConstraintP>::const_iterator qiter_end = d_diseqQueue.end(); for(; qiter != qiter_end; ++qiter){ - Constraint curr = *qiter; + ConstraintP curr = *qiter; const DeltaRational& rhsValue = curr->getValue(); relevantDeltaValues.insert(rhsValue); @@ -2503,7 +4037,7 @@ void TheoryArithPrivate::collectModelInfo( TheoryModel* m, bool fullModel ){ for(var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){ ArithVar v = *vi; - if(!isSlackVariable(v)){ + if(!isAuxiliaryVariable(v)){ Node term = d_partialModel.asNode(v); if(theoryOf(term) == THEORY_ARITH || shared.find(term) != shared.end()){ @@ -2550,6 +4084,8 @@ void TheoryArithPrivate::notifyRestart(){ if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); } ++d_restartsCounter; + d_solveIntMaybeHelp = 0; + d_solveIntAttempts = 0; } bool TheoryArithPrivate::entireStateIsConsistent(const string& s){ @@ -2679,7 +4215,7 @@ bool TheoryArithPrivate::propagateCandidateBound(ArithVar basic, bool upperBound //implies an unknown fact. ConstraintType t = upperBound ? UpperBound : LowerBound; - Constraint bestImplied = d_constraintDatabase.getBestImpliedBound(basic, t, bound); + ConstraintP bestImplied = d_constraintDatabase.getBestImpliedBound(basic, t, bound); // Node bestImplied = upperBound ? // d_apm.getBestImpliedUpperBound(basic, bound): @@ -2694,7 +4230,7 @@ bool TheoryArithPrivate::propagateCandidateBound(ArithVar basic, bool upperBound Assert( upperBound || d_partialModel.greaterThanLowerBound(basic, bestImplied->getValue())); //slightly changed - // Constraint c = d_constraintDatabase.lookup(bestImplied); + // ConstraintP c = d_constraintDatabase.lookup(bestImplied); // Assert(c != NullConstraint); bool assertedToTheTheory = bestImplied->assertedToTheTheory(); @@ -2710,7 +4246,8 @@ bool TheoryArithPrivate::propagateCandidateBound(ArithVar basic, bool upperBound if(bestImplied->negationHasProof()){ Warning() << "the negation of " << bestImplied << " : " << endl << "has proof " << bestImplied->getNegation() << endl - << bestImplied->getNegation()->explainForConflict() << endl; + << bestImplied->getNegation()->externalExplainByAssertions() + << endl; } if(!assertedToTheTheory && canBePropagated && !hasProof ){ @@ -2724,8 +4261,11 @@ bool TheoryArithPrivate::propagateCandidateBound(ArithVar basic, bool upperBound return true; } if(Debug.isOn("arith::prop")){ - Debug("arith::prop") << "failed " << basic << " " << bound << assertedToTheTheory << " " << - canBePropagated << " " << hasProof << endl; + Debug("arith::prop") << "failed " << basic + << " " << bound + << " " << assertedToTheTheory + << " " << canBePropagated + << " " << hasProof << endl; d_partialModel.printModel(basic, Debug("arith::prop")); } } @@ -2848,7 +4388,7 @@ bool TheoryArithPrivate::propagateMightSucceed(ArithVar v, bool ub) const{ return true; } - Constraint strongestPossible = d_constraintDatabase.getBestImpliedBound(v, t, a); + ConstraintP strongestPossible = d_constraintDatabase.getBestImpliedBound(v, t, a); if(strongestPossible == NullConstraint){ return false; }else{ @@ -2944,11 +4484,7 @@ bool TheoryArithPrivate::tryToPropagate(RowIndex ridx, bool rowUp, ArithVar v, b if(weaker){ ConstraintType t = vUb ? UpperBound : LowerBound; - if(isInteger(v)){ - //cout << "maybe" << endl; - //cout << bound << endl; - } - Constraint implied = d_constraintDatabase.getBestImpliedBound(v, t, bound); + ConstraintP implied = d_constraintDatabase.getBestImpliedBound(v, t, bound); if(implied != NullConstraint){ return rowImplicationCanBeApplied(ridx, rowUp, implied); } @@ -2980,7 +4516,7 @@ Node flattenImplication(Node imp){ return nb; } -bool TheoryArithPrivate::rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, Constraint implied){ +bool TheoryArithPrivate::rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, ConstraintP implied){ Assert(implied != NullConstraint); ArithVar v = implied->getVariable(); @@ -2997,14 +4533,14 @@ bool TheoryArithPrivate::rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, C if(implied->negationHasProof()){ Warning() << "the negation of " << implied << " : " << endl << "has proof " << implied->getNegation() << endl - << implied->getNegation()->explainForConflict() << endl; + << implied->getNegation()->externalExplainByAssertions() << endl; } if(!assertedToTheTheory && canBePropagated && !hasProof ){ - vector<Constraint> explain; + ConstraintCPVec explain; d_linEq.propagateRow(explain, ridx, rowUp, implied); if(d_tableau.getRowLength(ridx) <= options::arithPropAsLemmaLength()){ - Node implication = implied->makeImplication(explain); + Node implication = implied->externalImplication(explain); Node clause = flattenImplication(implication); outputLemma(clause); }else{ @@ -3085,6 +4621,69 @@ const BoundsInfo& TheoryArithPrivate::boundsInfo(ArithVar basic) const{ return d_rowTracking[ridx]; } +Node TheoryArithPrivate::expandDefinition(LogicRequest &logicRequest, Node node) { + NodeManager* nm = NodeManager::currentNM(); + + switch(node.getKind()) { + case kind::DIVISION: { + // partial function: division + if(d_divByZero.isNull()) { + d_divByZero = nm->mkSkolem("divByZero", nm->mkFunctionType(nm->realType(), nm->realType()), + "partial real division", NodeManager::SKOLEM_EXACT_NAME); + logicRequest.widenLogic(THEORY_UF); + } + TNode num = node[0], den = node[1]; + Node den_eq_0 = nm->mkNode(kind::EQUAL, den, nm->mkConst(Rational(0))); + Node divByZeroNum = nm->mkNode(kind::APPLY_UF, d_divByZero, num); + Node divTotalNumDen = nm->mkNode(kind::DIVISION_TOTAL, num, den); + return nm->mkNode(kind::ITE, den_eq_0, divByZeroNum, divTotalNumDen); + break; + } + + case kind::INTS_DIVISION: { + // partial function: integer div + if(d_intDivByZero.isNull()) { + d_intDivByZero = nm->mkSkolem("intDivByZero", nm->mkFunctionType(nm->integerType(), nm->integerType()), + "partial integer division", NodeManager::SKOLEM_EXACT_NAME); + logicRequest.widenLogic(THEORY_UF); + } + TNode num = node[0], den = node[1]; + Node den_eq_0 = nm->mkNode(kind::EQUAL, den, nm->mkConst(Rational(0))); + Node intDivByZeroNum = nm->mkNode(kind::APPLY_UF, d_intDivByZero, num); + Node intDivTotalNumDen = nm->mkNode(kind::INTS_DIVISION_TOTAL, num, den); + return nm->mkNode(kind::ITE, den_eq_0, intDivByZeroNum, intDivTotalNumDen); + break; + } + + case kind::INTS_MODULUS: { + // partial function: mod + if(d_modZero.isNull()) { + d_modZero = nm->mkSkolem("modZero", nm->mkFunctionType(nm->integerType(), nm->integerType()), + "partial modulus", NodeManager::SKOLEM_EXACT_NAME); + logicRequest.widenLogic(THEORY_UF); + } + TNode num = node[0], den = node[1]; + Node den_eq_0 = nm->mkNode(kind::EQUAL, den, nm->mkConst(Rational(0))); + Node modZeroNum = nm->mkNode(kind::APPLY_UF, d_modZero, num); + Node modTotalNumDen = nm->mkNode(kind::INTS_MODULUS_TOTAL, num, den); + return nm->mkNode(kind::ITE, den_eq_0, modZeroNum, modTotalNumDen); + break; + } + + case kind::ABS: { + return nm->mkNode(kind::ITE, nm->mkNode(kind::LT, node[0], nm->mkConst(Rational(0))), nm->mkNode(kind::UMINUS, node[0]), node[0]); + break; + } + + default: + return node; + break; + } + + Unreachable(); +} + + }/* CVC4::theory::arith namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/arith/theory_arith_private.h b/src/theory/arith/theory_arith_private.h index 7ff93b021..68e839ef8 100644 --- a/src/theory/arith/theory_arith_private.h +++ b/src/theory/arith/theory_arith_private.h @@ -88,6 +88,10 @@ namespace quantifiers { } namespace arith { +class BranchCutInfo; +class TreeLog; +class ApproximateStatistics; + /** * Implementation of QF_LRA. * Based upon: @@ -107,6 +111,8 @@ private: BoundInfoMap d_rowTracking; + ConstraintCPVec d_conflictBuffer; + /** * The constraint database associated with the theory. * This must be declared before ArithPartialModel. @@ -122,6 +128,7 @@ private: // if unknown, the simplex priority queue cannot be emptied int d_unknownsInARow; + bool d_replayedLemmas; /** * This counter is false if nothing has been done since the last cut. @@ -174,22 +181,22 @@ private: * A superset of all of the assertions that currently are not the literal for * their constraint do not match constraint literals. Not just the witnesses. */ - context::CDInsertHashMap<Node, Constraint, NodeHashFunction> d_assertionsThatDoNotMatchTheirLiterals; + context::CDInsertHashMap<Node, ConstraintP, NodeHashFunction> d_assertionsThatDoNotMatchTheirLiterals; /** Returns true if x is of type Integer. */ inline bool isInteger(ArithVar x) const { return d_partialModel.isInteger(x); - //return d_variableTypes[x] >= ATInteger; } - /** This is the set of variables initially introduced as slack variables. */ - //std::vector<bool> d_slackVars; - /** Returns true if the variable was initially introduced as a slack variable. */ - inline bool isSlackVariable(ArithVar x) const{ - return d_partialModel.isSlack(x); - //return d_slackVars[x]; + /** Returns true if the variable was initially introduced as an auxiliary variable. */ + inline bool isAuxiliaryVariable(ArithVar x) const{ + return d_partialModel.isAuxiliary(x); + } + + inline bool isIntegerInput(ArithVar x) const { + return d_partialModel.isIntegerInput(x); } /** @@ -215,7 +222,7 @@ private: * List of all of the disequalities asserted in the current context that are not known * to be satisfied. */ - context::CDQueue<Constraint> d_diseqQueue; + context::CDQueue<ConstraintP> d_diseqQueue; /** * Constraints that have yet to be processed by proagation work list. @@ -231,9 +238,9 @@ private: * then d_cPL[1] is the previous lowerBound in d_partialModel, * and d_cPL[2] is the previous upperBound in d_partialModel. */ - std::deque<Constraint> d_currentPropagationList; + std::deque<ConstraintP> d_currentPropagationList; - context::CDQueue<Constraint> d_learnedBounds; + context::CDQueue<ConstraintP> d_learnedBounds; /** @@ -277,15 +284,31 @@ private: /** This is only used by simplex at the moment. */ - context::CDList<Node> d_conflicts; + context::CDList<ConstraintCPVec> d_conflicts; + context::CDO<Node> d_blackBoxConflict; public: - inline void raiseConflict(Node n){ d_conflicts.push_back(n); } + inline void raiseConflict(const ConstraintCPVec& cv){ + d_conflicts.push_back(cv); + } + + void raiseConflict(ConstraintCP a, ConstraintCP b); + void raiseConflict(ConstraintCP a, ConstraintCP b, ConstraintCP c); + + inline void blackBoxConflict(Node bb){ + if(d_blackBoxConflict.get().isNull()){ + d_blackBoxConflict = bb; + } + } private: + inline bool conflictQueueEmpty() const { + return d_conflicts.empty(); + } + /** Returns true iff a conflict has been raised. */ - inline bool inConflict() const { - return !d_conflicts.empty(); + inline bool anyConflict() const { + return !conflictQueueEmpty() || !d_blackBoxConflict.get().isNull(); } /** @@ -314,6 +337,7 @@ private: /** This keeps track of difference equalities. Mostly for sharing. */ ArithCongruenceManager d_congruenceManager; + context::CDO<bool> d_cmEnabled; /** This implements the Simplex decision procedure. */ DualSimplexDecisionProcedure d_dualSimplex; @@ -323,6 +347,22 @@ private: bool solveRealRelaxation(Theory::Effort effortLevel); + /* Returns true if this is heuristically a good time to try + * to solve the integers. + */ + bool attemptSolveInteger(Theory::Effort effortLevel, bool emmmittedLemmaOrSplit); + bool replayLemmas(ApproximateSimplex* approx); + void solveInteger(Theory::Effort effortLevel); + bool safeToCallApprox() const; + SimplexDecisionProcedure& selectSimplex(bool pass1); + SimplexDecisionProcedure* d_pass1SDP; + SimplexDecisionProcedure* d_otherSDP; + /* Sets d_qflraStatus */ + void importSolution(const ApproximateSimplex::Solution& solution); + bool solveRelaxationOrPanic(Theory::Effort effortLevel); + context::CDO<int> d_lastContextIntegerAttempted; + bool replayLog(ApproximateSimplex* approx); + class ModelException : public Exception { public: ModelException(TNode n, const char* msg) throw (); @@ -357,6 +397,7 @@ public: * Does non-context dependent setup for a node connected to a theory. */ void preRegisterTerm(TNode n); + Node expandDefinition(LogicRequest &logicRequest, Node node); void setMasterEqualityEngine(eq::EqualityEngine* eq); void setQuantifiersEngine(QuantifiersEngine* qe); @@ -412,16 +453,28 @@ private: /** - * Looks for the next integer variable without an integer assignment in a round robin fashion. - * Changes the value of d_nextIntegerCheckVar. + * Looks for the next integer variable without an integer assignment in a + * round-robin fashion. Changes the value of d_nextIntegerCheckVar. * - * If this returns false, d_nextIntegerCheckVar does not have an integer assignment. - * If this returns true, all integer variables have an integer assignment. + * This returns true if all integer variables have integer assignments. + * If this returns false, d_nextIntegerCheckVar does not have an integer + * assignment. */ bool hasIntegerModel(); /** - * Issues branches for non-slack integer variables with non-integer assignments. + * Looks for through the variables starting at d_nextIntegerCheckVar + * for the first integer variable that is between its upper and lower bounds + * that has a non-integer assignment. + * + * If assumeBounds is true, skip the check that the variable is in bounds. + * + * If there is no such variable, returns ARITHVAR_SENTINEL; + */ + ArithVar nextIntegerViolatation(bool assumeBounds) const; + + /** + * Issues branches for non-auxiliary integer variables with non-integer assignments. * Returns a cut for a lemma. * If there is an integer model, this returns Node::null(). */ @@ -432,8 +485,11 @@ public: * This requests a new unique ArithVar value for x. * This also does initial (not context dependent) set up for a variable, * except for setting up the initial. + * + * If aux is true, this is an auxiliary variable. + * If internal is true, x might not be unique up to a constant multiple. */ - ArithVar requestArithVar(TNode x, bool slack); + ArithVar requestArithVar(TNode x, bool aux, bool internal); public: const BoundsInfo& boundsInfo(ArithVar basic) const; @@ -443,8 +499,8 @@ private: /** Initial (not context dependent) sets up for a variable.*/ void setupBasicValue(ArithVar x); - /** Initial (not context dependent) sets up for a new slack variable.*/ - void setupSlack(TNode left); + /** Initial (not context dependent) sets up for a new auxiliary variable.*/ + void setupAuxiliary(TNode left); /** @@ -462,10 +518,10 @@ private: * a node describing this conflict is returned. * If this new bound is not in conflict, Node::null() is returned. */ - bool AssertLower(Constraint constraint); - bool AssertUpper(Constraint constraint); - bool AssertEquality(Constraint constraint); - bool AssertDisequality(Constraint constraint); + bool AssertLower(ConstraintP constraint); + bool AssertUpper(ConstraintP constraint); + bool AssertEquality(ConstraintP constraint); + bool AssertDisequality(ConstraintP constraint); /** Tracks the bounds that were updated in the current round. */ DenseSet d_updatedBounds; @@ -488,7 +544,14 @@ private: /** Attempt to perform a row propagation where every variable is a potential candidate.*/ bool attemptFull(RowIndex ridx, bool rowUp); bool tryToPropagate(RowIndex ridx, bool rowUp, ArithVar v, bool vUp, const DeltaRational& bound); - bool rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, Constraint bestImplied); + bool rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, ConstraintP bestImplied); + //void enqueueConstraints(std::vector<ConstraintCP>& out, Node n) const; + //ConstraintCPVec resolveOutPropagated(const ConstraintCPVec& v, const std::set<ConstraintCP>& propagated) const; + void resolveOutPropagated(std::vector<ConstraintCPVec>& confs, const std::set<ConstraintCP>& propagated) const; + void subsumption(std::vector<ConstraintCPVec>& confs) const; + + Node cutToLiteral(ApproximateSimplex* approx, const CutInfo& cut) const; + Node branchToNode(ApproximateSimplex* approx, const NodeLog& cut) const throw(RationalFromDoubleException); void propagateCandidates(); @@ -512,8 +575,8 @@ private: * Returns a conflict if one was found. * Returns Node::null if no conflict was found. */ - Constraint constraintFromFactQueue(); - bool assertionCases(Constraint c); + ConstraintP constraintFromFactQueue(); + bool assertionCases(ConstraintP c); /** * Returns the basic variable with the shorted row containing a non-basic variable. @@ -554,7 +617,7 @@ private: (d_containing.d_out)->setIncomplete(); d_nlIncomplete = true; } - inline void outputLemma(TNode lem) { (d_containing.d_out)->lemma(lem); } + void outputLemma(TNode lem); inline void outputPropagate(TNode lit) { (d_containing.d_out)->propagate(lit); } inline void outputRestart() { (d_containing.d_out)->demandRestart(); } @@ -565,6 +628,8 @@ private: return (d_containing.d_valuation).getSatValue(n); } + context::CDQueue<Node> d_approxCuts; + std::vector<Node> d_acTmp; /** Counts the number of fullCheck calls to arithmetic. */ uint32_t d_fullCheckCounter; @@ -581,12 +646,49 @@ private: context::CDO<bool> d_guessedCoeffSet; ArithRatPairVec d_guessedCoeffs; + + TreeLog* d_treeLog; + TreeLog& getTreeLog(); + + + ArithVarVec d_replayVariables; + std::vector<ConstraintP> d_replayConstraints; + DenseMap<Rational> d_lhsTmp; + + /* Approximate simpplex solvers are given a copy of their stats */ + ApproximateStatistics* d_approxStats; + ApproximateStatistics& getApproxStats(); + context::CDO<int32_t> d_attemptSolveIntTurnedOff; + void turnOffApproxFor(int32_t rounds); + bool getSolveIntegerResource(); + + void tryBranchCut(ApproximateSimplex* approx, int nid, BranchCutInfo& bl); + std::vector<ConstraintCPVec> replayLogRec(ApproximateSimplex* approx, int nid, ConstraintP bc, int depth); + + std::pair<ConstraintP, ArithVar> replayGetConstraint(const CutInfo& info); + std::pair<ConstraintP, ArithVar> replayGetConstraint(ApproximateSimplex* approx, const NodeLog& nl) throw(RationalFromDoubleException); + std::pair<ConstraintP, ArithVar> replayGetConstraint(const DenseMap<Rational>& lhs, Kind k, const Rational& rhs, bool branch); + + void replayAssert(ConstraintP c); + //ConstConstraintVec toExplanation(Node n) const; + + // Returns true if the node contains a literal + // that is an arithmetic literal and is not a sat literal + // No caching is done so this should likely only + // be called carefully! + bool hasFreshArithLiteral(Node n) const; + + int32_t d_dioSolveResources; + bool getDioCuttingResource(); + + uint32_t d_solveIntMaybeHelp, d_solveIntAttempts; + /** These fields are designed to be accessible to TheoryArith methods. */ class Statistics { public: IntStat d_statAssertUpperConflicts, d_statAssertLowerConflicts; - IntStat d_statUserVariables, d_statSlackVariables; + IntStat d_statUserVariables, d_statAuxiliaryVariables; IntStat d_statDisequalitySplits; IntStat d_statDisequalityConflicts; TimerStat d_simplifyTimer; @@ -614,6 +716,51 @@ private: IntStat d_commitsOnConflicts; IntStat d_nontrivialSatChecks; + IntStat d_replayLogRecCount, + d_replayLogRecConflictEscalation, + d_replayLogRecEarlyExit, + d_replayBranchCloseFailures, + d_replayLeafCloseFailures, + d_replayBranchSkips, + d_mirCutsAttempted, + d_gmiCutsAttempted, + d_branchCutsAttempted, + d_cutsReconstructed, + d_cutsReconstructionFailed, + d_cutsProven, + d_cutsProofFailed, + d_mipReplayLemmaCalls, + d_mipExternalCuts, + d_mipExternalBranch; + + IntStat d_inSolveInteger, + d_branchesExhausted, + d_execExhausted, + d_pivotsExhausted, + d_panicBranches, + d_relaxCalls, + d_relaxLinFeas, + d_relaxLinFeasFailures, + d_relaxLinInfeas, + d_relaxLinInfeasFailures, + d_relaxLinExhausted, + d_relaxOthers; + + IntStat d_applyRowsDeleted; + TimerStat d_replaySimplexTimer; + + TimerStat d_replayLogTimer, + d_solveIntTimer, + d_solveRealRelaxTimer; + + IntStat d_solveIntCalls, + d_solveStandardEffort; + + IntStat d_approxDisabled; + IntStat d_replayAttemptFailed; + + IntStat d_cutsRejectedDuringReplay; + IntStat d_cutsRejectedDuringLemmas; HistogramStat<uint32_t> d_satPivots; HistogramStat<uint32_t> d_unsatPivots; @@ -626,6 +773,26 @@ private: Statistics d_statistics; + /** + * Function symbol used to implement uninterpreted division-by-zero + * semantics. Needed to deal with partial division function ("/"). + */ + Node d_divByZero; + + /** + * Function symbol used to implement uninterpreted + * int-division-by-zero semantics. Needed to deal with partial + * function "div". + */ + Node d_intDivByZero; + + /** + * Function symbol used to implement uninterpreted mod-zero + * semantics. Needed to deal with partial function "mod". + */ + Node d_modZero; + + };/* class TheoryArithPrivate */ }/* CVC4::theory::arith namespace */ diff --git a/src/theory/arrays/theory_arrays.cpp b/src/theory/arrays/theory_arrays.cpp index b82216378..cd9fd2497 100644 --- a/src/theory/arrays/theory_arrays.cpp +++ b/src/theory/arrays/theory_arrays.cpp @@ -374,11 +374,17 @@ void TheoryArrays::explain(TNode literal, std::vector<TNode>& assumptions) { // Do the work bool polarity = literal.getKind() != kind::NOT; TNode atom = polarity ? literal : literal[0]; + //eq::EqProof * eqp = new eq::EqProof; + eq::EqProof * eqp = NULL; if (atom.getKind() == kind::EQUAL || atom.getKind() == kind::IFF) { - d_equalityEngine.explainEquality(atom[0], atom[1], polarity, assumptions); + d_equalityEngine.explainEquality(atom[0], atom[1], polarity, assumptions, eqp); } else { d_equalityEngine.explainPredicate(atom, polarity, assumptions); } + if( eqp ){ + Debug("array-pf") << " Proof is : " << std::endl; + eqp->debug_print("array-pf"); + } } @@ -435,7 +441,7 @@ void TheoryArrays::preRegisterTermInternal(TNode node) if (ni != node) { preRegisterTermInternal(ni); } - d_equalityEngine.assertEquality(ni.eqNode(s[2]), true, d_true); + d_equalityEngine.assertEquality(ni.eqNode(s[2]), true, d_true, eq::MERGED_ARRAYS_ROW1); Assert(++it == stores->end()); } } @@ -482,7 +488,7 @@ void TheoryArrays::preRegisterTermInternal(TNode node) } // Apply RIntro1 Rule - d_equalityEngine.assertEquality(ni.eqNode(v), true, d_true); + d_equalityEngine.assertEquality(ni.eqNode(v), true, d_true, eq::MERGED_ARRAYS_ROW1); } d_infoMap.addStore(node, node); @@ -1892,7 +1898,7 @@ void TheoryArrays::checkRIntro1(TNode a, TNode b) d_infoMap.setRIntro1Applied(s); Node ni = nm->mkNode(kind::SELECT, s, s[1]); preRegisterTermInternal(ni); - d_equalityEngine.assertEquality(ni.eqNode(s[2]), true, d_true); + d_equalityEngine.assertEquality(ni.eqNode(s[2]), true, d_true, eq::MERGED_ARRAYS_ROW1); } } @@ -2283,7 +2289,7 @@ void TheoryArrays::queueRowLemma(RowLemmaType lem) if (!bjExists) { preRegisterTermInternal(bj); } - d_equalityEngine.assertEquality(aj_eq_bj, true, reason); + d_equalityEngine.assertEquality(aj_eq_bj, true, reason, eq::MERGED_ARRAYS_ROW); ++d_numProp; return; } @@ -2293,7 +2299,7 @@ void TheoryArrays::queueRowLemma(RowLemmaType lem) Node i_eq_j = i.eqNode(j); Node reason = nm->mkNode(kind::OR, i_eq_j, aj_eq_bj); d_permRef.push_back(reason); - d_equalityEngine.assertEquality(i_eq_j, true, reason); + d_equalityEngine.assertEquality(i_eq_j, true, reason, eq::MERGED_ARRAYS_ROW); ++d_numProp; return; } diff --git a/src/theory/bv/theory_bv.cpp b/src/theory/bv/theory_bv.cpp index 6daf99c8b..5d5e0a97c 100644 --- a/src/theory/bv/theory_bv.cpp +++ b/src/theory/bv/theory_bv.cpp @@ -25,6 +25,7 @@ #include "theory/bv/bv_subtheory_core.h" #include "theory/bv/bv_subtheory_inequality.h" #include "theory/bv/bv_subtheory_bitblast.h" +#include "theory/bv/theory_bv_rewriter.h" #include "theory/theory_model.h" using namespace CVC4; @@ -102,6 +103,69 @@ TheoryBV::Statistics::~Statistics() { StatisticsRegistry::unregisterStat(&d_weightComputationTimer); } +Node TheoryBV::getBVDivByZero(Kind k, unsigned width) { + NodeManager* nm = NodeManager::currentNM(); + if (k == kind::BITVECTOR_UDIV) { + if (d_BVDivByZero.find(width) == d_BVDivByZero.end()) { + // lazily create the function symbols + ostringstream os; + os << "BVUDivByZero_" << width; + Node divByZero = nm->mkSkolem(os.str(), + nm->mkFunctionType(nm->mkBitVectorType(width), nm->mkBitVectorType(width)), + "partial bvudiv", NodeManager::SKOLEM_EXACT_NAME); + d_BVDivByZero[width] = divByZero; + } + return d_BVDivByZero[width]; + } + else if (k == kind::BITVECTOR_UREM) { + if (d_BVRemByZero.find(width) == d_BVRemByZero.end()) { + ostringstream os; + os << "BVURemByZero_" << width; + Node divByZero = nm->mkSkolem(os.str(), + nm->mkFunctionType(nm->mkBitVectorType(width), nm->mkBitVectorType(width)), + "partial bvurem", NodeManager::SKOLEM_EXACT_NAME); + d_BVRemByZero[width] = divByZero; + } + return d_BVRemByZero[width]; + } + + Unreachable(); +} + + +Node TheoryBV::expandDefinition(LogicRequest &logicRequest, Node node) { + Debug("bitvector-expandDefinition") << "TheoryBV::expandDefinition(" << node << ")" << std::endl; + + switch (node.getKind()) { + case kind::BITVECTOR_SDIV: + case kind::BITVECTOR_SREM: + case kind::BITVECTOR_SMOD: + return TheoryBVRewriter::eliminateBVSDiv(node); + break; + + case kind::BITVECTOR_UDIV: + case kind::BITVECTOR_UREM: { + NodeManager* nm = NodeManager::currentNM(); + unsigned width = node.getType().getBitVectorSize(); + Node divByZero = getBVDivByZero(node.getKind(), width); + TNode num = node[0], den = node[1]; + Node den_eq_0 = nm->mkNode(kind::EQUAL, den, nm->mkConst(BitVector(width, Integer(0)))); + Node divByZeroNum = nm->mkNode(kind::APPLY_UF, divByZero, num); + Node divTotalNumDen = nm->mkNode(node.getKind() == kind::BITVECTOR_UDIV ? kind::BITVECTOR_UDIV_TOTAL : + kind::BITVECTOR_UREM_TOTAL, num, den); + node = nm->mkNode(kind::ITE, den_eq_0, divByZeroNum, divTotalNumDen); + logicRequest.widenLogic(THEORY_UF); + return node; + } + break; + + default: + return node; + break; + } + + Unreachable(); +} void TheoryBV::preRegisterTerm(TNode node) { diff --git a/src/theory/bv/theory_bv.h b/src/theory/bv/theory_bv.h index 3d093c861..a5e2ac9ea 100644 --- a/src/theory/bv/theory_bv.h +++ b/src/theory/bv/theory_bv.h @@ -25,6 +25,7 @@ #include "context/cdhashset.h" #include "theory/bv/theory_bv_utils.h" #include "util/statistics_registry.h" +#include "util/hash.h" #include "theory/bv/bv_subtheory.h" namespace CVC4 { @@ -46,6 +47,8 @@ class TheoryBV : public Theory { std::vector<SubtheorySolver*> d_subtheories; __gnu_cxx::hash_map<SubTheory, SubtheorySolver*, std::hash<int> > d_subtheoryMap; + + public: TheoryBV(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo); @@ -53,6 +56,8 @@ public: void setMasterEqualityEngine(eq::EqualityEngine* eq); + Node expandDefinition(LogicRequest &logicRequest, Node node); + void preRegisterTerm(TNode n); void check(Effort e); @@ -85,6 +90,25 @@ private: Statistics d_statistics; + + /** + * Return the uinterpreted function symbol corresponding to division-by-zero + * for this particular bit-width + * @param k should be UREM or UDIV + * @param width + * + * @return + */ + Node getBVDivByZero(Kind k, unsigned width); + + /** + * Maps from bit-vector width to divison-by-zero uninterpreted + * function symbols. + */ + __gnu_cxx::hash_map<unsigned, Node> d_BVDivByZero; + __gnu_cxx::hash_map<unsigned, Node> d_BVRemByZero; + + context::CDO<bool> d_lemmasAdded; // Are we in conflict? diff --git a/src/theory/ite_utilities.cpp b/src/theory/ite_utilities.cpp index 1b4e096f2..a4af4f26f 100644 --- a/src/theory/ite_utilities.cpp +++ b/src/theory/ite_utilities.cpp @@ -28,6 +28,7 @@ namespace CVC4 { namespace theory { namespace ite { + inline static bool isTermITE(TNode e) { return (e.getKind() == kind::ITE && !e.getType().isBoolean()); } @@ -77,9 +78,7 @@ struct CTIVStackElement { } /* CVC4::theory::ite */ - - -ITEUtilities::ITEUtilities(ContainsTermITEVistor* containsVisitor) +ITEUtilities::ITEUtilities(ContainsTermITEVisitor* containsVisitor) : d_containsVisitor(containsVisitor) , d_compressor(NULL) , d_simplifier(NULL) @@ -144,11 +143,11 @@ void ITEUtilities::clear(){ } /********************* */ -/* ContainsTermITEVistor +/* ContainsTermITEVisitor */ -ContainsTermITEVistor::ContainsTermITEVistor(): d_cache() {} -ContainsTermITEVistor::~ContainsTermITEVistor(){} -bool ContainsTermITEVistor::containsTermITE(TNode e){ +ContainsTermITEVisitor::ContainsTermITEVisitor(): d_cache() {} +ContainsTermITEVisitor::~ContainsTermITEVisitor(){} +bool ContainsTermITEVisitor::containsTermITE(TNode e){ /* throughout execution skip through NOT nodes. */ e = (e.getKind() == kind::NOT) ? e[0] : e; if(ite::triviallyContainsNoTermITEs(e)){ return false; } @@ -197,7 +196,7 @@ bool ContainsTermITEVistor::containsTermITE(TNode e){ } return foundTermIte; } -void ContainsTermITEVistor::garbageCollect() { +void ContainsTermITEVisitor::garbageCollect() { d_cache.clear(); } @@ -249,7 +248,7 @@ void IncomingArcCounter::clear() { /********************* */ /* ITECompressor */ -ITECompressor::ITECompressor(ContainsTermITEVistor* contains) +ITECompressor::ITECompressor(ContainsTermITEVisitor* contains) : d_contains(contains) , d_assertions(NULL) , d_incoming(true, true) @@ -547,7 +546,7 @@ uint32_t TermITEHeightCounter::termITEHeight(TNode e){ -ITESimplifier::ITESimplifier(ContainsTermITEVistor* contains) +ITESimplifier::ITESimplifier(ContainsTermITEVisitor* contains) : d_containsVisitor(contains) , d_termITEHeight() , d_constantLeaves() @@ -1608,6 +1607,5 @@ Node ITECareSimplifier::simplifyWithCare(TNode e) return substitute(e, substTable, cache); } - } /* namespace theory */ } /* namespace CVC4 */ diff --git a/src/theory/ite_utilities.h b/src/theory/ite_utilities.h index d9e6120aa..7f0986ecb 100644 --- a/src/theory/ite_utilities.h +++ b/src/theory/ite_utilities.h @@ -31,7 +31,7 @@ namespace CVC4 { namespace theory { -class ContainsTermITEVistor; +class ContainsTermITEVisitor; class IncomingArcCounter; class TermITEHeightCounter; class ITECompressor; @@ -40,7 +40,7 @@ class ITECareSimplifier; class ITEUtilities { public: - ITEUtilities(ContainsTermITEVistor* containsVisitor); + ITEUtilities(ContainsTermITEVisitor* containsVisitor); ~ITEUtilities(); Node simpITE(TNode assertion); @@ -55,7 +55,7 @@ public: void clear(); private: - ContainsTermITEVistor* d_containsVisitor; + ContainsTermITEVisitor* d_containsVisitor; ITECompressor* d_compressor; ITESimplifier* d_simplifier; ITECareSimplifier* d_careSimp; @@ -64,10 +64,10 @@ private: /** * A caching visitor that computes whether a node contains a term ite. */ -class ContainsTermITEVistor { +class ContainsTermITEVisitor { public: - ContainsTermITEVistor(); - ~ContainsTermITEVistor(); + ContainsTermITEVisitor(); + ~ContainsTermITEVisitor(); /** returns true if a node contains a term ite. */ bool containsTermITE(TNode n); @@ -140,7 +140,7 @@ private: */ class ITECompressor { public: - ITECompressor(ContainsTermITEVistor* contains); + ITECompressor(ContainsTermITEVisitor* contains); ~ITECompressor(); /* returns false if an assertion is discovered to be equal to false. */ @@ -153,7 +153,7 @@ private: Node d_true; /* Copy of true. */ Node d_false; /* Copy of false. */ - ContainsTermITEVistor* d_contains; + ContainsTermITEVisitor* d_contains; std::vector<Node>* d_assertions; IncomingArcCounter d_incoming; @@ -180,7 +180,7 @@ private: class ITESimplifier { public: - ITESimplifier(ContainsTermITEVistor* d_containsVisitor); + ITESimplifier(ContainsTermITEVisitor* d_containsVisitor); ~ITESimplifier(); Node simpITE(TNode assertion); @@ -192,7 +192,7 @@ private: Node d_true; Node d_false; - ContainsTermITEVistor* d_containsVisitor; + ContainsTermITEVisitor* d_containsVisitor; inline bool containsTermITE(TNode n) { return d_containsVisitor->containsTermITE(n); } diff --git a/src/theory/logic_info.h b/src/theory/logic_info.h index 2448898c0..a0777ae70 100644 --- a/src/theory/logic_info.h +++ b/src/theory/logic_info.h @@ -64,7 +64,6 @@ class CVC4_PUBLIC LogicInfo { case theory::THEORY_BUILTIN: case theory::THEORY_BOOL: case theory::THEORY_QUANTIFIERS: - case theory::THEORY_REWRITERULES: return false; default: return true; @@ -117,7 +116,7 @@ public: /** Is this a quantified logic? */ bool isQuantified() const { CheckArgument(d_locked, *this, "This LogicInfo isn't locked yet, and cannot be queried"); - return isTheoryEnabled(theory::THEORY_QUANTIFIERS) || isTheoryEnabled(theory::THEORY_REWRITERULES); + return isTheoryEnabled(theory::THEORY_QUANTIFIERS); } /** Is this the all-inclusive logic? */ @@ -214,7 +213,6 @@ public: */ void enableQuantifiers() { enableTheory(theory::THEORY_QUANTIFIERS); - enableTheory(theory::THEORY_REWRITERULES); } /** @@ -222,7 +220,6 @@ public: */ void disableQuantifiers() { disableTheory(theory::THEORY_QUANTIFIERS); - disableTheory(theory::THEORY_REWRITERULES); } // these are for arithmetic diff --git a/src/theory/quantifiers/bounded_integers.cpp b/src/theory/quantifiers/bounded_integers.cpp index 78f989807..bec8ea350 100644 --- a/src/theory/quantifiers/bounded_integers.cpp +++ b/src/theory/quantifiers/bounded_integers.cpp @@ -18,6 +18,7 @@ #include "theory/quantifiers/quant_util.h" #include "theory/quantifiers/first_order_model.h" #include "theory/quantifiers/model_engine.h" +#include "theory/quantifiers/term_database.h" using namespace CVC4; using namespace std; @@ -274,7 +275,7 @@ void BoundedIntegers::registerQuantifier( Node f ) { for( unsigned i=0; i<d_set[f].size(); i++) { Node v = d_set[f][i]; Node r = d_range[f][v]; - if( quantifiers::TermDb::hasBoundVarAttr(r) ){ + if( r.hasBoundVar() ){ //introduce a new bound Node new_range = NodeManager::currentNM()->mkSkolem( "bir_$$", r.getType(), "bound for term" ); d_nground_range[f][v] = d_range[f][v]; @@ -384,5 +385,5 @@ void BoundedIntegers::getBoundValues( Node f, Node v, RepSetIterator * rsi, Node } bool BoundedIntegers::isGroundRange(Node f, Node v) { - return isBoundVar(f,v) && !quantifiers::TermDb::hasBoundVarAttr(getLowerBound(f,v)) && !quantifiers::TermDb::hasBoundVarAttr(getUpperBound(f,v)); + return isBoundVar(f,v) && !getLowerBound(f,v).hasBoundVar() && !getUpperBound(f,v).hasBoundVar(); } diff --git a/src/theory/quantifiers/candidate_generator.cpp b/src/theory/quantifiers/candidate_generator.cpp index 1e89bb1ea..9b5e506ea 100644 --- a/src/theory/quantifiers/candidate_generator.cpp +++ b/src/theory/quantifiers/candidate_generator.cpp @@ -18,6 +18,7 @@ #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/inst_match.h" #include "theory/quantifiers_engine.h" +#include "theory/quantifiers/options.h" using namespace std; using namespace CVC4; @@ -62,7 +63,7 @@ CandidateGeneratorQE::CandidateGeneratorQE( QuantifiersEngine* qe, Node op ) : Assert( !d_op.isNull() ); } void CandidateGeneratorQE::resetInstantiationRound(){ - d_term_iter_limit = d_qe->getTermDatabase()->d_op_map[d_op].size(); + d_term_iter_limit = d_qe->getTermDatabase()->getNumGroundTerms( d_op ); } void CandidateGeneratorQE::reset( Node eqc ){ diff --git a/src/theory/quantifiers/first_order_model.cpp b/src/theory/quantifiers/first_order_model.cpp index 8c00c5af4..f3203da4b 100644 --- a/src/theory/quantifiers/first_order_model.cpp +++ b/src/theory/quantifiers/first_order_model.cpp @@ -18,6 +18,7 @@ #include "theory/quantifiers/quantifiers_attributes.h" #include "theory/quantifiers/full_model_check.h" #include "theory/quantifiers/qinterval_builder.h" +#include "theory/quantifiers/options.h" #define USE_INDEX_ORDERING diff --git a/src/theory/quantifiers/first_order_reasoning.cpp b/src/theory/quantifiers/first_order_reasoning.cpp index a696af3c2..1c87dad7b 100644 --- a/src/theory/quantifiers/first_order_reasoning.cpp +++ b/src/theory/quantifiers/first_order_reasoning.cpp @@ -19,10 +19,11 @@ #include "theory/rewriter.h" using namespace CVC4; -using namespace CVC4::kind; using namespace std; - -namespace CVC4 { +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; +using namespace CVC4::kind; +using namespace CVC4::context; void FirstOrderPropagation::collectLits( Node n, std::vector<Node> & lits ){ @@ -167,5 +168,3 @@ Node FirstOrderPropagation::simplify( Node n ) { return NodeManager::currentNM()->mkNode( n.getKind(), children ); } } - -} diff --git a/src/theory/quantifiers/first_order_reasoning.h b/src/theory/quantifiers/first_order_reasoning.h index 6bc5c21c1..92898eff5 100644 --- a/src/theory/quantifiers/first_order_reasoning.h +++ b/src/theory/quantifiers/first_order_reasoning.h @@ -25,6 +25,8 @@ #include "expr/type_node.h" namespace CVC4 { +namespace theory { +namespace quantifiers { class FirstOrderPropagation { private: @@ -41,5 +43,7 @@ public: }; } +} +} #endif diff --git a/src/theory/quantifiers/full_model_check.cpp b/src/theory/quantifiers/full_model_check.cpp index d542e878c..6c889781d 100644 --- a/src/theory/quantifiers/full_model_check.cpp +++ b/src/theory/quantifiers/full_model_check.cpp @@ -15,6 +15,7 @@ #include "theory/quantifiers/full_model_check.h" #include "theory/quantifiers/first_order_model.h" #include "theory/quantifiers/options.h" +#include "theory/quantifiers/term_database.h" using namespace std; using namespace CVC4; diff --git a/src/theory/quantifiers/inst_gen.cpp b/src/theory/quantifiers/inst_gen.cpp index 27b87e6a4..aaa65e630 100644 --- a/src/theory/quantifiers/inst_gen.cpp +++ b/src/theory/quantifiers/inst_gen.cpp @@ -16,6 +16,7 @@ #include "theory/quantifiers/model_engine.h" #include "theory/quantifiers/model_builder.h" #include "theory/quantifiers/first_order_model.h" +#include "theory/quantifiers/term_database.h" //#define CHILD_USE_CONSIDER diff --git a/src/theory/quantifiers/inst_match_generator.cpp b/src/theory/quantifiers/inst_match_generator.cpp index e1d301c09..13186c7cc 100644 --- a/src/theory/quantifiers/inst_match_generator.cpp +++ b/src/theory/quantifiers/inst_match_generator.cpp @@ -18,6 +18,7 @@ #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/candidate_generator.h" #include "theory/quantifiers_engine.h" +#include "theory/quantifiers/options.h" using namespace std; using namespace CVC4; diff --git a/src/theory/quantifiers/inst_strategy_e_matching.cpp b/src/theory/quantifiers/inst_strategy_e_matching.cpp index dee8192c1..0353b0b5f 100644 --- a/src/theory/quantifiers/inst_strategy_e_matching.cpp +++ b/src/theory/quantifiers/inst_strategy_e_matching.cpp @@ -97,22 +97,11 @@ void InstStrategyUserPatterns::addUserPattern( Node f, Node pat ){ //extend to literal matching d_quantEngine->getPhaseReqTerms( f, nodes ); //check match option - int matchOption = options::efficientEMatching() ? InstMatchGenerator::MATCH_GEN_EFFICIENT_E_MATCH : 0; + int matchOption = 0; d_user_gen[f].push_back( Trigger::mkTrigger( d_quantEngine, f, nodes, matchOption, true, Trigger::TR_MAKE_NEW, options::smartTriggers() ) ); } } -/* -InstStrategyUserPatterns::Statistics::Statistics(): - d_instantiations("InstStrategyUserPatterns::Instantiations", 0) -{ - StatisticsRegistry::registerStat(&d_instantiations); -} - -InstStrategyUserPatterns::Statistics::~Statistics(){ - StatisticsRegistry::unregisterStat(&d_instantiations); -} -*/ void InstStrategyAutoGenTriggers::processResetInstantiationRound( Theory::Effort effort ){ //reset triggers @@ -400,6 +389,14 @@ int InstStrategyFreeVariable::process( Node f, Theory::Effort effort, int e ){ }else{ Assert( index==(int)(childIndex.size())-1 ); unsigned nv = childIndex[index]+1; + if( options::cbqi() ){ + //skip inst constant nodes + while( nv<maxs[index] && nv<=max_i && + r==1 && quantifiers::TermDb::hasInstConstAttr( d_quantEngine->getTermDatabase()->d_type_map[f[0][index].getType()][nv] ) ){ + childIndex[index]++; + nv = childIndex[index]+1; + } + } if( nv<maxs[index] && nv<=max_i ){ childIndex[index]++; index++; diff --git a/src/theory/quantifiers/instantiation_engine.cpp b/src/theory/quantifiers/instantiation_engine.cpp index 99dc29bf8..e5705882a 100644 --- a/src/theory/quantifiers/instantiation_engine.cpp +++ b/src/theory/quantifiers/instantiation_engine.cpp @@ -209,7 +209,7 @@ void InstantiationEngine::check( Theory::Effort e ){ for( int i=0; i<(int)d_quantEngine->getModel()->getNumAssertedQuantifiers(); i++ ){ Node n = d_quantEngine->getModel()->getAssertedQuantifier( i ); //it is not active if we have found the skolemized negation is unsat - if( n.hasAttribute(QRewriteRuleAttribute()) ){ + if( TermDb::isRewriteRule( n ) ){ d_quant_active[n] = false; }else if( options::cbqi() && hasAddedCbqiLemma( n ) ){ Node cel = d_quantEngine->getTermDatabase()->getCounterexampleLiteral( n ); @@ -292,19 +292,23 @@ void InstantiationEngine::check( Theory::Effort e ){ } void InstantiationEngine::registerQuantifier( Node f ){ - //Notice() << "do cbqi " << f << " ? " << std::endl; - Node ceBody = d_quantEngine->getTermDatabase()->getInstConstantBody( f ); - if( !doCbqi( f ) ){ - d_quantEngine->addTermToDatabase( ceBody, true ); - } + if( !TermDb::isRewriteRule( f ) ){ + //Notice() << "do cbqi " << f << " ? " << std::endl; + if( options::cbqi() ){ + Node ceBody = d_quantEngine->getTermDatabase()->getInstConstantBody( f ); + if( !doCbqi( f ) ){ + d_quantEngine->addTermToDatabase( ceBody, true ); + } + } - //take into account user patterns - if( f.getNumChildren()==3 ){ - Node subsPat = d_quantEngine->getTermDatabase()->getInstConstantNode( f[2], f ); - //add patterns - for( int i=0; i<(int)subsPat.getNumChildren(); i++ ){ - //Notice() << "Add pattern " << subsPat[i] << " for " << f << std::endl; - addUserPattern( f, subsPat[i] ); + //take into account user patterns + if( f.getNumChildren()==3 ){ + Node subsPat = d_quantEngine->getTermDatabase()->getInstConstantNode( f[2], f ); + //add patterns + for( int i=0; i<(int)subsPat.getNumChildren(); i++ ){ + //Notice() << "Add pattern " << subsPat[i] << " for " << f << std::endl; + addUserPattern( f, subsPat[i] ); + } } } } diff --git a/src/theory/quantifiers/kinds b/src/theory/quantifiers/kinds index ab0e9d934..795bc1ab4 100644 --- a/src/theory/quantifiers/kinds +++ b/src/theory/quantifiers/kinds @@ -48,4 +48,25 @@ typerule BOUND_VAR_LIST ::CVC4::theory::quantifiers::QuantifierBoundVarListTypeR typerule INST_PATTERN ::CVC4::theory::quantifiers::QuantifierInstPatternTypeRule typerule INST_PATTERN_LIST ::CVC4::theory::quantifiers::QuantifierInstPatternListTypeRule +# for rewrite rules +# 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 "general 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::quantifiers::RewriteRuleTypeRule +typerule RR_REWRITE ::CVC4::theory::quantifiers::RRRewriteTypeRule +typerule RR_REDUCTION ::CVC4::theory::quantifiers::RRRedDedTypeRule +typerule RR_DEDUCTION ::CVC4::theory::quantifiers::RRRedDedTypeRule + endtheory diff --git a/src/theory/quantifiers/model_builder.cpp b/src/theory/quantifiers/model_builder.cpp index 502a34176..a12fc7ca2 100644 --- a/src/theory/quantifiers/model_builder.cpp +++ b/src/theory/quantifiers/model_builder.cpp @@ -24,6 +24,7 @@ #include "theory/quantifiers/quantifiers_attributes.h" #include "theory/quantifiers/inst_gen.h" #include "theory/quantifiers/trigger.h" +#include "theory/quantifiers/options.h" using namespace std; using namespace CVC4; @@ -39,7 +40,7 @@ TheoryEngineModelBuilder( qe->getTheoryEngine() ), d_curr_model( c, NULL ), d_qe } bool QModelBuilder::isQuantifierActive( Node f ) { - return !f.hasAttribute(QRewriteRuleAttribute()); + return !TermDb::isRewriteRule( f ); } @@ -381,7 +382,7 @@ QModelBuilderIG::Statistics::~Statistics(){ } bool QModelBuilderIG::isQuantifierActive( Node f ){ - return !f.hasAttribute(QRewriteRuleAttribute()) && + return !TermDb::isRewriteRule( f ) && ( d_considerAxioms || !f.getAttribute(AxiomAttribute()) ) && d_quant_sat.find( f )==d_quant_sat.end(); } @@ -1038,7 +1039,7 @@ int QModelBuilderInstGen::getSelectionFormulaScore( Node fn ){ if( fn.getKind()==APPLY_UF ){ Node op = fn.getOperator(); //return total number of terms - return d_qe->getTermDatabase()->d_op_count[op]; + return d_qe->getTermDatabase()->d_op_nonred_count[op]; }else{ int score = 0; for( size_t i=0; i<fn.getNumChildren(); i++ ){ diff --git a/src/theory/quantifiers/options b/src/theory/quantifiers/options index 6d333521b..0865f2c0b 100644 --- a/src/theory/quantifiers/options +++ b/src/theory/quantifiers/options @@ -42,8 +42,6 @@ option clauseSplit --clause-split bool :default false # forall x. P( x ) => f( S( x ) ) = x option preSkolemQuant --pre-skolem-quant bool :read-write :default false apply skolemization eagerly to bodies of quantified formulas -option iteRemoveQuant --ite-remove-quant bool :default false - apply ite removal to bodies of quantifiers # Whether to perform agressive miniscoping option aggressiveMiniscopeQuant --ag-miniscope-quant bool :default false perform aggressive miniscoping for quantifiers @@ -127,5 +125,9 @@ option qcfMode --quant-cf-mode=MODE CVC4::theory::quantifiers::QcfMode :default option qcfWhenMode --quant-cf-when=MODE CVC4::theory::quantifiers::QcfWhenMode :default CVC4::theory::quantifiers::QCF_WHEN_MODE_DEFAULT :include "theory/quantifiers/modes.h" :handler CVC4::theory::quantifiers::stringToQcfWhenMode :handler-include "theory/quantifiers/options_handlers.h" when to invoke conflict find mechanism for quantifiers +option quantRewriteRules --rewrite-rules bool :default true + use rewrite rules module +option rrOneInstPerRound --rr-one-inst-per-round bool :default false + add one instance of rewrite rule per round endmodule diff --git a/src/theory/quantifiers/options_handlers.h b/src/theory/quantifiers/options_handlers.h index 9892009a3..e9c754d4a 100644 --- a/src/theory/quantifiers/options_handlers.h +++ b/src/theory/quantifiers/options_handlers.h @@ -131,6 +131,9 @@ conflict \n\ partial \n\ + Apply QCF algorithm to instantiate heuristically as well. \n\ \n\ +partial \n\ ++ Apply QCF to instantiate heuristically as well. \n\ +\n\ mc \n\ + Apply QCF algorithm in a complete way, so that a model is ensured when it fails. \n\ \n\ diff --git a/src/theory/quantifiers/qinterval_builder.cpp b/src/theory/quantifiers/qinterval_builder.cpp index ece519d1c..285834e96 100755 --- a/src/theory/quantifiers/qinterval_builder.cpp +++ b/src/theory/quantifiers/qinterval_builder.cpp @@ -14,6 +14,8 @@ #include "theory/quantifiers/qinterval_builder.h"
+#include "theory/quantifiers/term_database.h"
+
using namespace std;
using namespace CVC4;
diff --git a/src/theory/quantifiers/quant_conflict_find.cpp b/src/theory/quantifiers/quant_conflict_find.cpp index ced4e1997..c0b318f23 100755 --- a/src/theory/quantifiers/quant_conflict_find.cpp +++ b/src/theory/quantifiers/quant_conflict_find.cpp @@ -18,6 +18,8 @@ #include "theory/quantifiers/quant_conflict_find.h"
#include "theory/quantifiers/quant_util.h"
#include "theory/theory_engine.h"
+#include "theory/quantifiers/options.h"
+#include "theory/quantifiers/term_database.h"
using namespace CVC4;
using namespace CVC4::kind;
@@ -93,7 +95,7 @@ void QuantInfo::initialize( Node q, Node qn ) { if( d_vars[j].getKind()!=BOUND_VARIABLE ){
bool beneathQuant = d_nbeneathQuant.find( d_vars[j] )==d_nbeneathQuant.end();
d_var_mg[j] = new MatchGen( this, d_vars[j], true, beneathQuant );
- if( !d_var_mg[j]->isValid() && options::qcfMode()<QCF_PARTIAL ){
+ if( !d_var_mg[j]->isValid() ){
d_mg->setInvalid();
break;
}else{
@@ -107,28 +109,17 @@ void QuantInfo::initialize( Node q, Node qn ) { std::vector< int > bvars;
d_mg->determineVariableOrder( this, bvars );
}
-
- //must also contain all variables?
- if( d_isPartial ){
- for( unsigned i=0; i<q[0].getNumChildren(); i++ ){
- if( d_has_var.find( q[0][i] )==d_has_var.end() ){
- d_isPartial = false;
- d_mg->setInvalid();
- break;
- }
- }
- }
}
- Trace("qcf-qregister-summary") << "QCF register : " << ( d_mg->isValid() ? ( isPartial() ? "PARTIAL " : "VALID " ) : "INVALID" ) << " : " << q << std::endl;
+ Trace("qcf-qregister-summary") << "QCF register : " << ( d_mg->isValid() ? "VALID " : "INVALID" ) << " : " << q << std::endl;
}
void QuantInfo::registerNode( Node n, bool hasPol, bool pol, bool beneathQuant ) {
Trace("qcf-qregister-debug2") << "Register : " << n << std::endl;
if( n.getKind()==FORALL ){
registerNode( n[1], hasPol, pol, true );
- }else{
- if( n.getKind()!=OR && n.getKind()!=AND && n.getKind()!=IFF && n.getKind()!=NOT ){
- if( quantifiers::TermDb::hasBoundVarAttr( n ) ){
+ }else{ + if( !MatchGen::isHandledBoolConnective( n ) ){
+ if( n.hasBoundVar() ){
//literals
if( n.getKind()==EQUAL ){
for( unsigned i=0; i<n.getNumChildren(); i++ ){
@@ -136,11 +127,9 @@ void QuantInfo::registerNode( Node n, bool hasPol, bool pol, bool beneathQuant ) }
}else if( MatchGen::isHandledUfTerm( n ) ){
flatten( n, beneathQuant );
- }else if( n.getKind()==ITE ){
- if( !n[1].getType().isBoolean() ){
- for( unsigned i=1; i<=2; i++ ){
- flatten( n[i], beneathQuant );
- }
+ }else if( n.getKind()==ITE ){ + for( unsigned i=1; i<=2; i++ ){
+ flatten( n[i], beneathQuant );
}
}
}
@@ -162,7 +151,7 @@ void QuantInfo::registerNode( Node n, bool hasPol, bool pol, bool beneathQuant ) void QuantInfo::flatten( Node n, bool beneathQuant ) {
Trace("qcf-qregister-debug2") << "Flatten : " << n << std::endl;
- if( quantifiers::TermDb::hasBoundVarAttr( n ) ){
+ if( n.hasBoundVar() ){
if( d_var_num.find( n )==d_var_num.end() ){
Trace("qcf-qregister-debug2") << "Add FLATTEN VAR : " << n << std::endl;
d_var_num[n] = d_vars.size();
@@ -220,6 +209,8 @@ void QuantInfo::reset_round( QuantConflictFind * p ) { for( std::map< int, MatchGen * >::iterator it = d_var_mg.begin(); it != d_var_mg.end(); ++it ){
it->second->reset_round( p );
}
+ //now, reset for matching
+ d_mg->reset( p, false, this );
}
int QuantInfo::getCurrentRepVar( int v ) {
@@ -249,6 +240,24 @@ TNode QuantInfo::getCurrentValue( TNode n ) { }
}
+TNode QuantInfo::getCurrentExpValue( TNode n ) {
+ int v = getVarNum( n );
+ if( v==-1 ){
+ return n;
+ }else{
+ if( d_match[v].isNull() ){
+ return n;
+ }else{
+ Assert( getVarNum( d_match[v] )!=v );
+ if( d_match_term[v].isNull() ){
+ return getCurrentValue( d_match[v] );
+ }else{
+ return d_match_term[v];
+ }
+ }
+ }
+}
+
bool QuantInfo::getCurrentCanBeEqual( QuantConflictFind * p, int v, TNode n, bool chDiseq ) {
//check disequalities
std::map< int, std::map< TNode, int > >::iterator itd = d_curr_var_deq.find( v );
@@ -453,109 +462,113 @@ bool QuantInfo::isMatchSpurious( QuantConflictFind * p ) { return false;
}
-bool QuantInfo::completeMatch( QuantConflictFind * p, std::vector< int >& assigned ) {
+bool QuantInfo::completeMatch( QuantConflictFind * p, std::vector< int >& assigned, bool doContinue ) {
//assign values for variables that were unassigned (usually not necessary, but handles corner cases)
- Trace("qcf-check") << std::endl;
- std::vector< int > unassigned[2];
- std::vector< TypeNode > unassigned_tn[2];
- for( int i=0; i<getNumVars(); i++ ){
- if( d_match[i].isNull() ){
- //Assert( i<(int)q[0].getNumChildren() );
- int rindex = d_var_mg.find( i )==d_var_mg.end() ? 1 : 0;
- unassigned[rindex].push_back( i );
- unassigned_tn[rindex].push_back( getVar( i ).getType() );
- assigned.push_back( i );
+ bool doFail = false;
+ bool success = true;
+ if( doContinue ){
+ doFail = true;
+ success = false;
+ }else{
+ d_unassigned.clear();
+ d_unassigned_tn.clear();
+ std::vector< int > unassigned[2];
+ std::vector< TypeNode > unassigned_tn[2];
+ for( int i=0; i<getNumVars(); i++ ){
+ if( d_match[i].isNull() ){
+ int rindex = d_var_mg.find( i )==d_var_mg.end() ? 1 : 0;
+ unassigned[rindex].push_back( i );
+ unassigned_tn[rindex].push_back( getVar( i ).getType() );
+ assigned.push_back( i );
+ }
}
+ d_unassigned_nvar = unassigned[0].size();
+ for( unsigned i=0; i<2; i++ ){
+ d_unassigned.insert( d_unassigned.end(), unassigned[i].begin(), unassigned[i].end() );
+ d_unassigned_tn.insert( d_unassigned_tn.end(), unassigned_tn[i].begin(), unassigned_tn[i].end() );
+ }
+ d_una_eqc_count.clear();
+ d_una_index = 0;
}
- bool success = true;
- for( unsigned r=0; r<2; r++ ){
- if( success && !unassigned[r].empty() ){
- Trace("qcf-check") << "Assign to unassigned, rep = " << r << "..." << std::endl;
- int index = 0;
- std::vector< int > eqc_count;
- bool doFail = false;
- do {
- if( doFail ){
- Trace("qcf-check-unassign") << "Failure, try again..." << std::endl;
- }
- bool invalidMatch = false;
- while( ( index>=0 && (int)index<(int)unassigned[r].size() ) || invalidMatch || doFail ){
- invalidMatch = false;
- if( !doFail && index==(int)eqc_count.size() ){
- //check if it has now been assigned
- if( r==0 ){
- if( !isConstrainedVar( unassigned[r][index] ) ){
- eqc_count.push_back( -1 );
- }else{
- d_var_mg[ unassigned[r][index] ]->reset( p, true, this );
- eqc_count.push_back( 0 );
- }
+ if( !d_unassigned.empty() ){
+ Trace("qcf-check") << "Assign to unassigned..." << std::endl;
+ do {
+ if( doFail ){
+ Trace("qcf-check-unassign") << "Failure, try again..." << std::endl;
+ }
+ bool invalidMatch = false;
+ while( ( d_una_index>=0 && (int)d_una_index<(int)d_unassigned.size() ) || invalidMatch || doFail ){
+ invalidMatch = false;
+ if( !doFail && d_una_index==(int)d_una_eqc_count.size() ){
+ //check if it has now been assigned
+ if( d_una_index<d_unassigned_nvar ){
+ if( !isConstrainedVar( d_unassigned[d_una_index] ) ){
+ d_una_eqc_count.push_back( -1 );
}else{
- eqc_count.push_back( 0 );
+ d_var_mg[ d_unassigned[d_una_index] ]->reset( p, true, this );
+ d_una_eqc_count.push_back( 0 );
}
}else{
- if( r==0 ){
- if( !doFail && !isConstrainedVar( unassigned[r][index] ) ){
- Trace("qcf-check-unassign") << "Succeeded, variable unconstrained at " << index << std::endl;
- index++;
- }else if( !doFail && d_var_mg[unassigned[r][index]]->getNextMatch( p, this ) ){
- Trace("qcf-check-unassign") << "Succeeded match with mg at " << index << std::endl;
- index++;
+ d_una_eqc_count.push_back( 0 );
+ }
+ }else{
+ bool failed = false;
+ if( !doFail ){
+ if( d_una_index<d_unassigned_nvar ){
+ if( !isConstrainedVar( d_unassigned[d_una_index] ) ){
+ Trace("qcf-check-unassign") << "Succeeded, variable unconstrained at " << d_una_index << std::endl;
+ d_una_index++;
+ }else if( d_var_mg[d_unassigned[d_una_index]]->getNextMatch( p, this ) ){
+ Trace("qcf-check-unassign") << "Succeeded match with mg at " << d_una_index << std::endl;
+ d_una_index++;
}else{
- Trace("qcf-check-unassign") << "Failed match with mg at " << index << std::endl;
- do{
- if( !doFail ){
- eqc_count.pop_back();
- }else{
- doFail = false;
- }
- index--;
- }while( index>=0 && eqc_count[index]==-1 );
+ failed = true;
+ Trace("qcf-check-unassign") << "Failed match with mg at " << d_una_index << std::endl;
}
}else{
- Assert( doFail || index==(int)eqc_count.size()-1 );
- if( !doFail && eqc_count[index]<(int)p->d_eqcs[unassigned_tn[r][index]].size() ){
- int currIndex = eqc_count[index];
- eqc_count[index]++;
- Trace("qcf-check-unassign") << unassigned[r][index] << "->" << p->d_eqcs[unassigned_tn[r][index]][currIndex] << std::endl;
- if( setMatch( p, unassigned[r][index], p->d_eqcs[unassigned_tn[r][index]][currIndex] ) ){
- //if( currIndex==0 ){
- // Assert( p->areEqual( p->d_model_basis[unassigned_tn[r][index]], p->d_eqcs[unassigned_tn[r][index]][currIndex] ) );
- // d_match_term[unassigned[r][index]] = p->d_model_basis[unassigned_tn[r][index]];
- //}else{
- d_match_term[unassigned[r][index]] = TNode::null();
- //}
- Trace("qcf-check-unassign") << "Succeeded match " << index << std::endl;
- index++;
+ Assert( doFail || d_una_index==(int)d_una_eqc_count.size()-1 );
+ if( d_una_eqc_count[d_una_index]<(int)p->d_eqcs[d_unassigned_tn[d_una_index]].size() ){
+ int currIndex = d_una_eqc_count[d_una_index];
+ d_una_eqc_count[d_una_index]++;
+ Trace("qcf-check-unassign") << d_unassigned[d_una_index] << "->" << p->d_eqcs[d_unassigned_tn[d_una_index]][currIndex] << std::endl;
+ if( setMatch( p, d_unassigned[d_una_index], p->d_eqcs[d_unassigned_tn[d_una_index]][currIndex] ) ){
+ d_match_term[d_unassigned[d_una_index]] = TNode::null();
+ Trace("qcf-check-unassign") << "Succeeded match " << d_una_index << std::endl;
+ d_una_index++;
}else{
- Trace("qcf-check-unassign") << "Failed match " << index << std::endl;
+ Trace("qcf-check-unassign") << "Failed match " << d_una_index << std::endl;
invalidMatch = true;
}
}else{
- Trace("qcf-check-unassign") << "No more matches " << index << std::endl;
- if( !doFail ){
- eqc_count.pop_back();
- }else{
- doFail = false;
- }
- index--;
+ failed = true;
+ Trace("qcf-check-unassign") << "No more matches " << d_una_index << std::endl;
}
}
}
+ if( doFail || failed ){
+ do{
+ if( !doFail ){
+ d_una_eqc_count.pop_back();
+ }else{
+ doFail = false;
+ }
+ d_una_index--;
+ }while( d_una_index>=0 && d_una_eqc_count[d_una_index]==-1 );
+ }
}
- success = index>=0;
- if( success ){
- doFail = true;
- Trace("qcf-check-unassign") << " Try: " << std::endl;
- for( unsigned i=0; i<unassigned[r].size(); i++ ){
- int ui = unassigned[r][i];
- if( !d_match[ui].isNull() ){
- Trace("qcf-check-unassign") << " Assigned #" << ui << " : " << d_vars[ui] << " -> " << d_match[ui] << std::endl;
- }
+ }
+ success = d_una_index>=0;
+ if( success ){
+ doFail = true;
+ Trace("qcf-check-unassign") << " Try: " << std::endl;
+ for( unsigned i=0; i<d_unassigned.size(); i++ ){
+ int ui = d_unassigned[i];
+ if( !d_match[ui].isNull() ){
+ Trace("qcf-check-unassign") << " Assigned #" << ui << " : " << d_vars[ui] << " -> " << d_match[ui] << std::endl;
}
}
- }while( success && isMatchSpurious( p ) );
- }
+ }
+ }while( success && isMatchSpurious( p ) );
}
if( success ){
return true;
@@ -568,6 +581,28 @@ bool QuantInfo::completeMatch( QuantConflictFind * p, std::vector< int >& assign }
}
+void QuantInfo::getMatch( std::vector< Node >& terms ){
+ for( unsigned i=0; i<d_q[0].getNumChildren(); i++ ){
+ //Node cv = qi->getCurrentValue( qi->d_match[i] );
+ int repVar = getCurrentRepVar( i );
+ Node cv;
+ //std::map< int, TNode >::iterator itmt = qi->d_match_term.find( repVar );
+ if( !d_match_term[repVar].isNull() ){
+ cv = d_match_term[repVar];
+ }else{
+ cv = d_match[repVar];
+ }
+ Debug("qcf-check-inst") << "INST : " << i << " -> " << cv << ", from " << d_match[i] << std::endl;
+ terms.push_back( cv );
+ }
+}
+
+void QuantInfo::revertMatch( std::vector< int >& assigned ) {
+ for( unsigned i=0; i<assigned.size(); i++ ){
+ d_match[ assigned[i] ] = TNode::null();
+ }
+}
+
void QuantInfo::debugPrintMatch( const char * c ) {
for( int i=0; i<getNumVars(); i++ ){
Trace(c) << " " << d_vars[i] << " -> ";
@@ -583,7 +618,10 @@ void QuantInfo::debugPrintMatch( const char * c ) { }
Trace(c) << "}";
}
- Trace(c) << std::endl;
+ if( !d_match_term[i].isNull() && d_match_term[i]!=d_match[i] ){
+ Trace(c) << ", EXP : " << d_match_term[i];
+ }
+ Trace(c) << std::endl;
}
}
@@ -614,9 +652,6 @@ MatchGen::MatchGen( QuantInfo * qi, Node n, bool isVar, bool beneathQuant ){ Trace("qcf-qregister-debug") << " is var #" << v << std::endl;
d_qni_var_num[d_qni_size] = v;
//qi->addFuncParent( v, f, j );
- if( nn.getKind()==BOUND_VARIABLE && !beneathQuant ){
- qi->d_has_var[nn] = true;
- }
}else{
Trace("qcf-qregister-debug") << " is gterm " << nn << std::endl;
d_qni_gterm[d_qni_size] = nn;
@@ -634,7 +669,7 @@ MatchGen::MatchGen( QuantInfo * qi, Node n, bool isVar, bool beneathQuant ){ Node nn = n.eqNode( n[i] );
d_children.push_back( MatchGen( qi, nn ) );
d_children[d_children.size()-1].d_qni_bound_except.push_back( 0 );
- if( !d_children[d_children.size()-1].isValid() && options::qcfMode()<QCF_PARTIAL ){
+ if( !d_children[d_children.size()-1].isValid() ){
setInvalid();
break;
}
@@ -647,7 +682,7 @@ MatchGen::MatchGen( QuantInfo * qi, Node n, bool isVar, bool beneathQuant ){ d_type = typ_invalid;
}
}else{
- if( quantifiers::TermDb::hasBoundVarAttr( n ) ){
+ if( n.hasBoundVar() ){
d_type_not = false;
d_n = n;
if( d_n.getKind()==NOT ){
@@ -655,14 +690,14 @@ MatchGen::MatchGen( QuantInfo * qi, Node n, bool isVar, bool beneathQuant ){ d_type_not = !d_type_not;
}
- if( d_n.getKind()==OR || d_n.getKind()==AND || d_n.getKind()==IFF || d_n.getKind()==ITE || d_n.getKind()==FORALL ){
+ if( isHandledBoolConnective( d_n ) ){
//non-literals
d_type = typ_formula;
bool nBeneathQuant = beneathQuant || d_n.getKind()==FORALL;
for( unsigned i=0; i<d_n.getNumChildren(); i++ ){
if( d_n.getKind()!=FORALL || i==1 ){
d_children.push_back( MatchGen( qi, d_n[i], false, nBeneathQuant ) );
- if( !d_children[d_children.size()-1].isValid() && options::qcfMode()<QCF_PARTIAL ){
+ if( !d_children[d_children.size()-1].isValid() ){
setInvalid();
break;
}
@@ -693,11 +728,9 @@ MatchGen::MatchGen( QuantInfo * qi, Node n, bool isVar, bool beneathQuant ){ //literals
if( d_n.getKind()==EQUAL ){
for( unsigned i=0; i<2; i++ ){
- if( quantifiers::TermDb::hasBoundVarAttr( d_n[i] ) ){
+ if( d_n[i].hasBoundVar() ){
if( !qi->isVar( d_n[i] ) ){
Trace("qcf-qregister-debug") << "ERROR : not var " << d_n[i] << std::endl;
- }else if( d_n[i].getKind()==BOUND_VARIABLE && !beneathQuant ){
- qi->d_has_var[d_n[i]] = true;
}
Assert( qi->isVar( d_n[i] ) );
}else{
@@ -708,6 +741,8 @@ MatchGen::MatchGen( QuantInfo * qi, Node n, bool isVar, bool beneathQuant ){ }else if( isHandledUfTerm( d_n ) ){
Assert( qi->isVar( d_n ) );
d_type = typ_pred;
+ }else if( d_n.getKind()==BOUND_VARIABLE ){
+ d_type = typ_bool_var;
}else{
Trace("qcf-invalid") << "Unhandled : " << d_n << std::endl;
}
@@ -737,9 +772,6 @@ MatchGen::MatchGen( QuantInfo * qi, Node n, bool isVar, bool beneathQuant ){ Trace("qcf-qregister-debug") << std::endl;
//Assert( d_children.size()==d_children_order.size() );
- if( !isValid() && options::qcfMode()>=QCF_PARTIAL ){
- qi->d_isPartial = true;
- }
}
void MatchGen::collectBoundVar( QuantInfo * qi, Node n, std::vector< int >& cbvars ) {
@@ -829,6 +861,7 @@ void MatchGen::determineVariableOrder( QuantInfo * qi, std::vector< int >& bvars void MatchGen::reset_round( QuantConflictFind * p ) {
+ d_wasSet = false;
for( unsigned i=0; i<d_children.size(); i++ ){
d_children[i].reset_round( p );
}
@@ -844,11 +877,14 @@ void MatchGen::reset_round( QuantConflictFind * p ) { }
}else if( d_type==typ_eq ){
for( unsigned i=0; i<d_n.getNumChildren(); i++ ){
- if( !quantifiers::TermDb::hasBoundVarAttr( d_n[i] ) ){
+ if( !d_n[i].hasBoundVar() ){
d_ground_eval[i] = p->evaluateTerm( d_n[i] );
}
}
}
+ d_qni_bound_cons.clear();
+ d_qni_bound_cons_var.clear();
+ d_qni_bound.clear();
}
void MatchGen::reset( QuantConflictFind * p, bool tgt, QuantInfo * qi ) {
@@ -860,16 +896,34 @@ void MatchGen::reset( QuantConflictFind * p, bool tgt, QuantInfo * qi ) { d_qni.clear();
d_qni_bound.clear();
d_child_counter = -1;
+ d_tgt_orig = d_tgt;
//set up processing matches
if( d_type==typ_invalid ){
- if( p->d_effort==QuantConflictFind::effort_partial ){
- d_child_counter = 0;
- }
+ //do nothing : return success once?
}else if( d_type==typ_ground ){
if( d_ground_eval[0]==( d_tgt ? p->d_true : p->d_false ) ){
d_child_counter = 0;
}
+ }else if( d_type==typ_bool_var ){
+ //get current value of the variable
+ TNode n = qi->getCurrentValue( d_n );
+ int vn = qi->getCurrentRepVar( qi->getVarNum( n ) );
+ if( vn==-1 ){
+ //evaluate the value, see if it is compatible
+ int e = p->evaluate( n );
+ if( ( e==1 && d_tgt ) || ( e==0 && !d_tgt ) ){
+ d_child_counter = 0;
+ }
+ }else{
+ //unassigned, set match to true/false
+ d_qni_bound[0] = vn;
+ qi->setMatch( p, vn, d_tgt ? p->d_true : p->d_false );
+ d_child_counter = 0;
+ }
+ if( d_child_counter==0 ){
+ d_qn.push_back( NULL );
+ }
}else if( d_type==typ_var ){
Assert( isHandledUfTerm( d_n ) );
Node f = getOperator( p, d_n );
@@ -904,6 +958,7 @@ void MatchGen::reset( QuantConflictFind * p, bool tgt, QuantInfo * qi ) { }
bool success;
if( vn[0]==-1 && vn[1]==-1 ){
+ //Trace("qcf-explain") << " reset : " << d_n << " check ground values " << nn[0] << " " << nn[1] << " (tgt=" << d_tgt << ")" << std::endl;
Debug("qcf-match-debug") << " reset: check ground values " << nn[0] << " " << nn[1] << " (" << d_tgt << ")" << std::endl;
//just compare values
if( d_tgt ){
@@ -931,6 +986,7 @@ void MatchGen::reset( QuantConflictFind * p, bool tgt, QuantInfo * qi ) { success = addc!=-1;
//if successful and non-redundant, store that we need to cleanup this
if( addc==1 ){
+ //Trace("qcf-explain") << " reset: " << d_n << " add constraint " << vn[0] << " -> " << nn[1] << " (vn=" << vn[1] << ")" << ", d_tgt = " << d_tgt << std::endl;
for( unsigned i=0; i<2; i++ ){
if( vn[i]!=-1 && std::find( d_qni_bound_except.begin(), d_qni_bound_except.end(), i )==d_qni_bound_except.end() ){
d_qni_bound[vn[i]] = vn[i];
@@ -950,10 +1006,7 @@ void MatchGen::reset( QuantConflictFind * p, bool tgt, QuantInfo * qi ) { d_qn.push_back( NULL );
}else{
if( d_tgt && d_n.getKind()==FORALL ){
- //return success once
- if( p->d_effort==QuantConflictFind::effort_partial ){
- d_child_counter = -2;
- }
+ //do nothing
}else{
//reset the first child to d_tgt
d_child_counter = 0;
@@ -962,6 +1015,7 @@ void MatchGen::reset( QuantConflictFind * p, bool tgt, QuantInfo * qi ) { }
}
d_binding = false;
+ d_wasSet = true;
Debug("qcf-match") << " reset: Finished reset for " << d_n << ", success = " << ( !d_qn.empty() || d_child_counter!=-1 ) << std::endl;
}
@@ -974,9 +1028,10 @@ bool MatchGen::getNextMatch( QuantConflictFind * p, QuantInfo * qi ) { d_child_counter = -1;
return true;
}else{
+ d_wasSet = false;
return false;
}
- }else if( d_type==typ_var || d_type==typ_eq || d_type==typ_pred ){
+ }else if( d_type==typ_var || d_type==typ_eq || d_type==typ_pred || d_type==typ_bool_var ){
bool success = false;
bool terminate = false;
do {
@@ -1020,8 +1075,8 @@ bool MatchGen::getNextMatch( QuantConflictFind * p, QuantInfo * qi ) { Debug("qcf-match-debug") << " failed." << std::endl;
success = false;
}else{
- Debug("qcf-match-debug") << " decrement..." << std::endl;
--d_binding_it;
+ Debug("qcf-match-debug") << " decrement..." << std::endl;
}
}while( success && ( d_binding_it->first==0 || qi->d_var_mg.find( d_binding_it->second )==qi->d_var_mg.end() ) );
doReset = false;
@@ -1056,6 +1111,7 @@ bool MatchGen::getNextMatch( QuantConflictFind * p, QuantInfo * qi ) { Debug("qcf-match") << " Clean up bound var " << it->first << (d_tgt ? "!" : "") << " = " << it->second << std::endl;
std::map< int, int >::iterator itb = d_qni_bound_cons_var.find( it->first );
int vn = itb!=d_qni_bound_cons_var.end() ? itb->second : -1;
+ //Trace("qcf-explain") << " cleanup: " << d_n << " remove constraint " << it->first << " -> " << it->second << " (vn=" << vn << ")" << ", d_tgt = " << d_tgt << std::endl;
qi->addConstraint( p, it->first, it->second, vn, d_tgt, true );
}
}
@@ -1085,6 +1141,7 @@ bool MatchGen::getNextMatch( QuantConflictFind * p, QuantInfo * qi ) { */
}
Debug("qcf-match") << " ...finished matching for " << d_n << ", success = " << success << std::endl;
+ d_wasSet = success;
return success;
}else if( d_type==typ_formula || d_type==typ_ite_var ){
bool success = false;
@@ -1096,7 +1153,7 @@ bool MatchGen::getNextMatch( QuantConflictFind * p, QuantInfo * qi ) { }else{
while( !success && d_child_counter>=0 ){
//transition system based on d_child_counter
- if( d_type==typ_top || d_n.getKind()==OR || d_n.getKind()==AND ){
+ if( d_n.getKind()==OR || d_n.getKind()==AND ){
if( (d_n.getKind()==AND)==d_tgt ){
//all children must match simultaneously
if( getChild( d_child_counter )->getNextMatch( p, qi ) ){
@@ -1181,6 +1238,7 @@ bool MatchGen::getNextMatch( QuantConflictFind * p, QuantInfo * qi ) { }
}
}
+ d_wasSet = success;
Debug("qcf-match") << " ...finished construct match for " << d_n << ", success = " << success << std::endl;
return success;
}
@@ -1189,6 +1247,84 @@ bool MatchGen::getNextMatch( QuantConflictFind * p, QuantInfo * qi ) { return false;
}
+bool MatchGen::getExplanation( QuantConflictFind * p, QuantInfo * qi, std::vector< Node >& exp ) {
+ if( d_type==typ_eq ){
+ Node n[2];
+ for( unsigned i=0; i<2; i++ ){
+ Trace("qcf-explain") << "Explain term " << d_n[i] << "..." << std::endl;
+ n[i] = getExplanationTerm( p, qi, d_n[i], exp );
+ }
+ Node eq = n[0].eqNode( n[1] );
+ if( !d_tgt_orig ){
+ eq = eq.negate();
+ }
+ exp.push_back( eq );
+ Trace("qcf-explain") << "Explanation for " << d_n << " (tgt=" << d_tgt_orig << ") is " << eq << ", set = " << d_wasSet << std::endl;
+ return true;
+ }else if( d_type==typ_pred ){
+ Trace("qcf-explain") << "Explain term " << d_n << "..." << std::endl;
+ Node n = getExplanationTerm( p, qi, d_n, exp );
+ if( !d_tgt_orig ){
+ n = n.negate();
+ }
+ exp.push_back( n );
+ Trace("qcf-explain") << "Explanation for " << d_n << " (tgt=" << d_tgt_orig << ") is " << n << ", set = " << d_wasSet << std::endl;
+ return true;
+ }else if( d_type==typ_formula ){
+ Trace("qcf-explain") << "Explanation get for " << d_n << ", counter = " << d_child_counter << ", tgt = " << d_tgt_orig << ", set = " << d_wasSet << std::endl;
+ if( d_n.getKind()==OR || d_n.getKind()==AND ){
+ if( (d_n.getKind()==AND)==d_tgt ){
+ for( unsigned i=0; i<getNumChildren(); i++ ){
+ if( !getChild( i )->getExplanation( p, qi, exp ) ){
+ return false;
+ }
+ }
+ }else{
+ return getChild( d_child_counter )->getExplanation( p, qi, exp );
+ }
+ }else if( d_n.getKind()==IFF ){
+ for( unsigned i=0; i<2; i++ ){
+ if( !getChild( i )->getExplanation( p, qi, exp ) ){
+ return false;
+ }
+ }
+ }else if( d_n.getKind()==ITE ){
+ for( unsigned i=0; i<3; i++ ){
+ bool isActive = ( ( i==0 && d_child_counter!=5 ) ||
+ ( i==1 && d_child_counter!=( d_tgt ? 3 : 1 ) ) ||
+ ( i==2 && d_child_counter!=( d_tgt ? 1 : 3 ) ) );
+ if( isActive ){
+ if( !getChild( i )->getExplanation( p, qi, exp ) ){
+ return false;
+ }
+ }
+ }
+ }else{
+ return false;
+ }
+ return true;
+ }else{
+ return false;
+ }
+}
+
+Node MatchGen::getExplanationTerm( QuantConflictFind * p, QuantInfo * qi, Node t, std::vector< Node >& exp ) {
+ Node v = qi->getCurrentExpValue( t );
+ if( isHandledUfTerm( t ) ){
+ for( unsigned i=0; i<t.getNumChildren(); i++ ){
+ Node vi = getExplanationTerm( p, qi, t[i], exp );
+ if( vi!=v[i] ){
+ Node eq = vi.eqNode( v[i] );
+ if( std::find( exp.begin(), exp.end(), eq )==exp.end() ){
+ Trace("qcf-explain") << " add : " << eq << "." << std::endl;
+ exp.push_back( eq );
+ }
+ }
+ }
+ }
+ return v;
+}
+
bool MatchGen::doMatching( QuantConflictFind * p, QuantInfo * qi ) {
if( !d_qn.empty() ){
if( d_qn[0]==NULL ){
@@ -1297,14 +1433,17 @@ bool MatchGen::doMatching( QuantConflictFind * p, QuantInfo * qi ) { Assert( !d_qni[d_qni.size()-1]->second.d_children.empty() );
TNode t = d_qni[d_qni.size()-1]->second.d_children.begin()->first;
Debug("qcf-match-debug") << " " << d_n << " matched " << t << std::endl;
+ qi->d_match_term[d_qni_var_num[0]] = t;
//set the match terms
for( std::map< int, int >::iterator it = d_qni_bound.begin(); it != d_qni_bound.end(); ++it ){
Debug("qcf-match-debug") << " position " << it->first << " bounded " << it->second << " / " << qi->d_q[0].getNumChildren() << std::endl;
- if( it->second<(int)qi->d_q[0].getNumChildren() && it->first>0 ){ //if it is an actual variable, we are interested in knowing the actual term
+ //if( it->second<(int)qi->d_q[0].getNumChildren() ){ //if it is an actual variable, we are interested in knowing the actual term
+ if( it->first>0 ){
Assert( !qi->d_match[ it->second ].isNull() );
Assert( p->areEqual( t[it->first-1], qi->d_match[ it->second ] ) );
qi->d_match_term[it->second] = t[it->first-1];
}
+ //}
}
}
}
@@ -1322,7 +1461,7 @@ void MatchGen::debugPrintType( const char * c, short typ, bool isTrace ) { case typ_formula: Trace(c) << "formula";break;
case typ_var: Trace(c) << "var";break;
case typ_ite_var: Trace(c) << "ite_var";break;
- case typ_top: Trace(c) << "top";break;
+ case typ_bool_var: Trace(c) << "bool_var";break;
}
}else{
switch( typ ){
@@ -1333,7 +1472,7 @@ void MatchGen::debugPrintType( const char * c, short typ, bool isTrace ) { case typ_formula: Debug(c) << "formula";break;
case typ_var: Debug(c) << "var";break;
case typ_ite_var: Debug(c) << "ite_var";break;
- case typ_top: Debug(c) << "top";break;
+ case typ_bool_var: Debug(c) << "bool_var";break;
}
}
}
@@ -1343,18 +1482,37 @@ void MatchGen::setInvalid() { d_children.clear();
}
+bool MatchGen::isHandledBoolConnective( TNode n ) {
+ return n.getType().isBoolean() && ( n.getKind()==OR || n.getKind()==AND || n.getKind()==IFF || n.getKind()==ITE || n.getKind()==FORALL || n.getKind()==NOT );
+}
+
bool MatchGen::isHandledUfTerm( TNode n ) {
- return n.getKind()==APPLY_UF;// || n.getKind()==GEQ;
+ return n.getKind()==APPLY_UF || n.getKind()==STORE || n.getKind()==SELECT ||
+ n.getKind()==APPLY_CONSTRUCTOR || n.getKind()==APPLY_SELECTOR || n.getKind()==APPLY_TESTER;// || n.getKind()==GEQ;
}
Node MatchGen::getOperator( QuantConflictFind * p, Node n ) {
if( isHandledUfTerm( n ) ){
- return n.getOperator();
+ return p->getQuantifiersEngine()->getTermDatabase()->getOperator( n );
}else{
return Node::null();
}
}
+bool MatchGen::isHandled( TNode n ) {
+ if( n.getKind()!=BOUND_VARIABLE && n.hasBoundVar() ){
+ if( !isHandledBoolConnective( n ) && !isHandledUfTerm( n ) && n.getKind()!=EQUAL && n.getKind()!=ITE ){
+ return false;
+ }
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ if( !isHandled( n[i] ) ){
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
QuantConflictFind::QuantConflictFind( QuantifiersEngine * qe, context::Context* c ) :
QuantifiersModule( qe ),
@@ -1377,30 +1535,32 @@ Node QuantConflictFind::mkEqNode( Node a, Node b ) { //-------------------------------------------------- registration
void QuantConflictFind::registerQuantifier( Node q ) {
- d_quants.push_back( q );
- d_quant_id[q] = d_quants.size();
- Trace("qcf-qregister") << "Register ";
- debugPrintQuant( "qcf-qregister", q );
- Trace("qcf-qregister") << " : " << q << std::endl;
- //make QcfNode structure
- Trace("qcf-qregister") << "- Get relevant equality/disequality pairs, calculate flattening..." << std::endl;
- d_qinfo[q].initialize( q, q[1] );
-
- //debug print
- Trace("qcf-qregister") << "- Flattened structure is :" << std::endl;
- Trace("qcf-qregister") << " ";
- debugPrintQuantBody( "qcf-qregister", q, q[1] );
- Trace("qcf-qregister") << std::endl;
- if( d_qinfo[q].d_vars.size()>q[0].getNumChildren() ){
- Trace("qcf-qregister") << " with additional constraints : " << std::endl;
- for( unsigned j=q[0].getNumChildren(); j<d_qinfo[q].d_vars.size(); j++ ){
- Trace("qcf-qregister") << " ?x" << j << " = ";
- debugPrintQuantBody( "qcf-qregister", q, d_qinfo[q].d_vars[j], false );
- Trace("qcf-qregister") << std::endl;
+ if( !TermDb::isRewriteRule( q ) ){
+ d_quants.push_back( q );
+ d_quant_id[q] = d_quants.size();
+ Trace("qcf-qregister") << "Register ";
+ debugPrintQuant( "qcf-qregister", q );
+ Trace("qcf-qregister") << " : " << q << std::endl;
+ //make QcfNode structure
+ Trace("qcf-qregister") << "- Get relevant equality/disequality pairs, calculate flattening..." << std::endl;
+ d_qinfo[q].initialize( q, q[1] );
+
+ //debug print
+ Trace("qcf-qregister") << "- Flattened structure is :" << std::endl;
+ Trace("qcf-qregister") << " ";
+ debugPrintQuantBody( "qcf-qregister", q, q[1] );
+ Trace("qcf-qregister") << std::endl;
+ if( d_qinfo[q].d_vars.size()>q[0].getNumChildren() ){
+ Trace("qcf-qregister") << " with additional constraints : " << std::endl;
+ for( unsigned j=q[0].getNumChildren(); j<d_qinfo[q].d_vars.size(); j++ ){
+ Trace("qcf-qregister") << " ?x" << j << " = ";
+ debugPrintQuantBody( "qcf-qregister", q, d_qinfo[q].d_vars[j], false );
+ Trace("qcf-qregister") << std::endl;
+ }
}
- }
- Trace("qcf-qregister") << "Done registering quantifier." << std::endl;
+ Trace("qcf-qregister") << "Done registering quantifier." << std::endl;
+ }
}
int QuantConflictFind::evaluate( Node n, bool pref, bool hasPref ) {
@@ -1481,6 +1641,18 @@ int QuantConflictFind::evaluate( Node n, bool pref, bool hasPref ) { return ret;
}
+short QuantConflictFind::getMaxQcfEffort() {
+ if( options::qcfMode()==QCF_CONFLICT_ONLY ){
+ return effort_conflict;
+ }else if( options::qcfMode()==QCF_PROP_EQ ){
+ return effort_prop_eq;
+ }else if( options::qcfMode()==QCF_MC ){
+ return effort_mc;
+ }else{
+ return 0;
+ }
+}
+
bool QuantConflictFind::areMatchEqual( TNode n1, TNode n2 ) {
//if( d_effort==QuantConflictFind::effort_mc ){
// return n1==n2 || !areDisequal( n1, n2 );
@@ -1500,14 +1672,16 @@ bool QuantConflictFind::areMatchDisequal( TNode n1, TNode n2 ) { //-------------------------------------------------- handling assertions / eqc
void QuantConflictFind::assertNode( Node q ) {
- Trace("qcf-proc") << "QCF : assertQuantifier : ";
- debugPrintQuant("qcf-proc", q);
- Trace("qcf-proc") << std::endl;
- d_qassert.push_back( q );
- //set the eqRegistries that this depends on to true
- //for( std::map< EqRegistry *, bool >::iterator it = d_qinfo[q].d_rel_eqr.begin(); it != d_qinfo[q].d_rel_eqr.end(); ++it ){
- // it->first->d_active.set( true );
- //}
+ if( !TermDb::isRewriteRule( q ) ){
+ Trace("qcf-proc") << "QCF : assertQuantifier : ";
+ debugPrintQuant("qcf-proc", q);
+ Trace("qcf-proc") << std::endl;
+ d_qassert.push_back( q );
+ //set the eqRegistries that this depends on to true
+ //for( std::map< EqRegistry *, bool >::iterator it = d_qinfo[q].d_rel_eqr.begin(); it != d_qinfo[q].d_rel_eqr.end(); ++it ){
+ // it->first->d_active.set( true );
+ //}
+ }
}
eq::EqualityEngine * QuantConflictFind::getEqualityEngine() {
@@ -1518,7 +1692,7 @@ bool QuantConflictFind::areEqual( Node n1, Node n2 ) { return getEqualityEngine()->hasTerm( n1 ) && getEqualityEngine()->hasTerm( n2 ) && getEqualityEngine()->areEqual( n1,n2 );
}
bool QuantConflictFind::areDisequal( Node n1, Node n2 ) {
- return getEqualityEngine()->hasTerm( n1 ) && getEqualityEngine()->hasTerm( n2 ) && getEqualityEngine()->areDisequal( n1,n2, false );
+ return n1!=n2 && getEqualityEngine()->hasTerm( n1 ) && getEqualityEngine()->hasTerm( n2 ) && getEqualityEngine()->areDisequal( n1,n2, false );
}
Node QuantConflictFind::getRepresentative( Node n ) {
if( getEqualityEngine()->hasTerm( n ) ){
@@ -1677,6 +1851,10 @@ void QuantConflictFind::assertDisequal( Node a, Node b ) { //-------------------------------------------------- check function
+void QuantConflictFind::reset_round( Theory::Effort level ) {
+ d_needs_computeRelEqr = true;
+}
+
/** check */
void QuantConflictFind::check( Theory::Effort level ) {
Trace("qcf-check") << "QCF : check : " << level << std::endl;
@@ -1695,7 +1873,6 @@ void QuantConflictFind::check( Theory::Effort level ) { clSet = double(clock())/double(CLOCKS_PER_SEC);
Trace("qcf-engine") << "---Conflict Find Engine Round, effort = " << level << "---" << std::endl;
}
- Trace("qcf-check") << "Compute relevant equalities..." << std::endl;
computeRelevantEqr();
//determine order for quantified formulas
@@ -1722,23 +1899,12 @@ void QuantConflictFind::check( Theory::Effort level ) { }
}
-
-
if( Trace.isOn("qcf-debug") ){
Trace("qcf-debug") << std::endl;
debugPrint("qcf-debug");
Trace("qcf-debug") << std::endl;
- }
- short end_e;
- if( options::qcfMode()==QCF_CONFLICT_ONLY ){
- end_e = effort_conflict;
- }else if( options::qcfMode()==QCF_PROP_EQ ){
- end_e = effort_prop_eq;
- }else if( options::qcfMode()==QCF_PARTIAL ){
- end_e = effort_partial;
- }else{
- end_e = effort_mc;
- }
+ } + short end_e = getMaxQcfEffort();
for( short e = effort_conflict; e<=end_e; e++ ){
d_effort = e;
Trace("qcf-check") << "Checking quantified formulas at effort " << e << "..." << std::endl;
@@ -1748,79 +1914,106 @@ void QuantConflictFind::check( Theory::Effort level ) { Assert( d_qinfo.find( q )!=d_qinfo.end() );
if( qi->d_mg->isValid() ){
- if( qi->isPartial()==(e==effort_partial) ){
- Trace("qcf-check") << "Check quantified formula ";
- debugPrintQuant("qcf-check", q);
- Trace("qcf-check") << " : " << q << "..." << std::endl;
-
- Trace("qcf-check-debug") << "Reset round..." << std::endl;
- qi->reset_round( this );
- //try to make a matches making the body false
- Trace("qcf-check-debug") << "Reset..." << std::endl;
- qi->d_mg->reset( this, false, qi );
- Trace("qcf-check-debug") << "Get next match..." << std::endl;
- while( qi->d_mg->getNextMatch( this, qi ) ){
-
- Trace("qcf-check") << "*** Produced match at effort " << e << " : " << std::endl;
- qi->debugPrintMatch("qcf-check");
- Trace("qcf-check") << std::endl;
-
- if( !qi->isMatchSpurious( this ) ){
- std::vector< int > assigned;
- if( qi->completeMatch( this, assigned ) ){
- std::vector< Node > terms;
- for( unsigned i=0; i<q[0].getNumChildren(); i++ ){
- //Node cv = qi->getCurrentValue( qi->d_match[i] );
- int repVar = qi->getCurrentRepVar( i );
- Node cv;
- //std::map< int, TNode >::iterator itmt = qi->d_match_term.find( repVar );
- if( !qi->d_match_term[repVar].isNull() ){
- cv = qi->d_match_term[repVar];
- }else{
- cv = qi->d_match[repVar];
+ Trace("qcf-check") << "Check quantified formula ";
+ debugPrintQuant("qcf-check", q);
+ Trace("qcf-check") << " : " << q << "..." << std::endl;
+
+ Trace("qcf-check-debug") << "Reset round..." << std::endl;
+ qi->reset_round( this );
+ //try to make a matches making the body false
+ Trace("qcf-check-debug") << "Get next match..." << std::endl;
+ while( qi->d_mg->getNextMatch( this, qi ) ){
+ Trace("qcf-check") << "*** Produced match at effort " << e << " : " << std::endl;
+ qi->debugPrintMatch("qcf-check");
+ Trace("qcf-check") << std::endl;
+ std::vector< int > assigned;
+ if( !qi->isMatchSpurious( this ) ){
+ if( qi->completeMatch( this, assigned ) ){
+ /*
+ if( options::qcfExp() && d_effort==effort_conflict ){
+ std::vector< Node > exp;
+ if( qi->d_mg->getExplanation( this, qi, exp ) ){
+ Trace("qcf-check-exp") << "Base explanation is : " << std::endl;
+ for( unsigned c=0; c<exp.size(); c++ ){
+ Trace("qcf-check-exp") << " " << exp[c] << std::endl;
}
- Debug("qcf-check-inst") << "INST : " << i << " -> " << cv << ", from " << qi->d_match[i] << std::endl;
- terms.push_back( cv );
- }
- if( Debug.isOn("qcf-check-inst") ){
- //if( e==effort_conflict ){
- Node inst = d_quantEngine->getInstantiation( q, terms );
- Debug("qcf-check-inst") << "Check instantiation " << inst << "..." << std::endl;
- Assert( evaluate( inst )!=1 );
- Assert( evaluate( inst )==-1 || e>effort_conflict );
- //}
- }
- if( d_quantEngine->addInstantiation( q, terms ) ){
- Trace("qcf-check") << " ... Added instantiation" << std::endl;
- ++addedLemmas;
- if( e==effort_conflict ){
- d_quant_order.insert( d_quant_order.begin(), q );
- d_conflict.set( true );
- ++(d_statistics.d_conflict_inst);
- break;
- }else if( e==effort_prop_eq ){
- ++(d_statistics.d_prop_inst);
- }else if( e==effort_partial ){
- ++(d_statistics.d_partial_inst);
+ std::vector< TNode > c_exp;
+ eq::EqualityEngine* ee = ((uf::TheoryUF*)d_quantEngine->getTheoryEngine()->theoryOf( THEORY_UF ))->getEqualityEngine() ;
+ for( unsigned c=0; c<exp.size(); c++ ){
+ bool pol = exp[c].getKind()!=NOT;
+ TNode lit = pol ? exp[c] : exp[c][0];
+ Trace("qcf-check-exp") << "Explain " << lit << ", polarity " << pol << std::endl;
+ if( lit.getKind()==EQUAL ){
+ if( !pol && !ee->areDisequal( lit[0], lit[1], true ) ){
+ exit( 98 );
+ }else if( pol && !ee->areEqual( lit[0], lit[1] ) ){
+ exit( 99 );
+ }
+ ee->explainEquality( lit[0], lit[1], pol, c_exp );
+ }else{
+ if( !ee->areEqual( lit, pol ? d_true : d_false ) ){
+ exit( pol ? 96 : 97 );
+ }
+ ee->explainPredicate( lit, pol, c_exp );
+ }
}
- }else{
- Trace("qcf-check") << " ... Failed to add instantiation" << std::endl;
- //Assert( false );
+ std::vector< Node > c_lem;
+ Trace("qcf-check-exp") << "Actual explanation is : " << std::endl;
+ for( unsigned c=0; c<c_exp.size(); c++ ){
+ Trace("qcf-check-exp") << " " << c_exp[c] << std::endl;
+ Node ccc = c_exp[c].negate();
+ if( std::find( c_lem.begin(), c_lem.end(), ccc )==c_lem.end() ){
+ c_lem.push_back( ccc );
+ }
+ }
+
+ c_lem.push_back( q.negate() );
+ Node conf = NodeManager::currentNM()->mkNode( OR, c_lem );
+ Trace("qcf-conflict") << "QCF conflict : " << conf << std::endl;
+ d_quantEngine->addLemma( conf, false );
+ d_conflict.set( true );
+ ++(d_statistics.d_conflict_inst);
+ ++addedLemmas;
+ break;
}
- //clean up assigned
- for( unsigned i=0; i<assigned.size(); i++ ){
- qi->d_match[ assigned[i] ] = TNode::null();
+ }
+ */
+ std::vector< Node > terms;
+ qi->getMatch( terms );
+ if( Debug.isOn("qcf-check-inst") ){
+ //if( e==effort_conflict ){
+ Node inst = d_quantEngine->getInstantiation( q, terms );
+ Debug("qcf-check-inst") << "Check instantiation " << inst << "..." << std::endl;
+ Assert( evaluate( inst )!=1 );
+ Assert( evaluate( inst )==-1 || e>effort_conflict );
+ //}
+ }
+ if( d_quantEngine->addInstantiation( q, terms ) ){
+ Trace("qcf-check") << " ... Added instantiation" << std::endl;
+ ++addedLemmas;
+ if( e==effort_conflict ){
+ d_quant_order.insert( d_quant_order.begin(), q );
+ d_conflict.set( true );
+ ++(d_statistics.d_conflict_inst);
+ break;
+ }else if( e==effort_prop_eq ){
+ ++(d_statistics.d_prop_inst);
}
}else{
- Trace("qcf-check") << " ... Spurious instantiation (cannot assign unassigned variables)" << std::endl;
+ Trace("qcf-check") << " ... Failed to add instantiation" << std::endl;
+ //Assert( false );
}
+ //clean up assigned
+ qi->revertMatch( assigned );
}else{
- Trace("qcf-check") << " ... Spurious instantiation (does not meet variable constraints)" << std::endl;
+ Trace("qcf-check") << " ... Spurious instantiation (cannot assign unassigned variables)" << std::endl;
}
- }
- if( d_conflict ){
- break;
- }
+ }else{
+ Trace("qcf-check") << " ... Spurious instantiation (match is inconsistent)" << std::endl;
+ } + }
+ if( d_conflict ){
+ break;
}
}
}
@@ -1833,7 +2026,7 @@ void QuantConflictFind::check( Theory::Effort level ) { double clSet2 = double(clock())/double(CLOCKS_PER_SEC);
Trace("qcf-engine") << "Finished conflict find engine, time = " << (clSet2-clSet);
if( addedLemmas>0 ){
- Trace("qcf-engine") << ", effort = " << ( d_effort==effort_conflict ? "conflict" : ( d_effort==effort_prop_eq ? "prop_eq" : (d_effort==effort_partial ? "partial" : "mc" ) ) );
+ Trace("qcf-engine") << ", effort = " << ( d_effort==effort_conflict ? "conflict" : ( d_effort==effort_prop_eq ? "prop_eq" : "mc" ) );
Trace("qcf-engine") << ", addedLemmas = " << addedLemmas;
}
Trace("qcf-engine") << std::endl;
@@ -1845,7 +2038,7 @@ void QuantConflictFind::check( Theory::Effort level ) { bool QuantConflictFind::needsCheck( Theory::Effort level ) {
d_performCheck = false;
- if( !d_conflict ){
+ if( options::quantConflictFind() && !d_conflict ){
if( level==Theory::EFFORT_LAST_CALL ){
d_performCheck = options::qcfWhenMode()==QCF_WHEN_MODE_LAST_CALL;
}else if( level==Theory::EFFORT_FULL ){
@@ -1858,131 +2051,135 @@ bool QuantConflictFind::needsCheck( Theory::Effort level ) { }
void QuantConflictFind::computeRelevantEqr() {
- d_uf_terms.clear();
- d_eqc_uf_terms.clear();
- d_eqcs.clear();
- d_model_basis.clear();
- d_arg_reps.clear();
- //double clSet = 0;
- //if( Trace.isOn("qcf-opt") ){
- // clSet = double(clock())/double(CLOCKS_PER_SEC);
- //}
-
- //long nTermst = 0;
- //long nTerms = 0;
- //long nEqc = 0;
+ if( d_needs_computeRelEqr ){
+ d_needs_computeRelEqr = false;
+ Trace("qcf-check") << "Compute relevant equalities..." << std::endl;
+ d_uf_terms.clear();
+ d_eqc_uf_terms.clear();
+ d_eqcs.clear();
+ d_model_basis.clear();
+ d_arg_reps.clear();
+ //double clSet = 0;
+ //if( Trace.isOn("qcf-opt") ){
+ // clSet = double(clock())/double(CLOCKS_PER_SEC);
+ //}
- //which nodes are irrelevant for disequality matches
- std::map< TNode, bool > irrelevant_dnode;
- //now, store matches
- eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( getEqualityEngine() );
- while( !eqcs_i.isFinished() ){
- //nEqc++;
- Node r = (*eqcs_i);
- TypeNode rtn = r.getType();
- if( options::qcfMode()==QCF_MC ){
- std::map< TypeNode, std::vector< TNode > >::iterator itt = d_eqcs.find( rtn );
- if( itt==d_eqcs.end() ){
- Node mb = getQuantifiersEngine()->getTermDatabase()->getModelBasisTerm( rtn );
- if( !getEqualityEngine()->hasTerm( mb ) ){
- Trace("qcf-warn") << "WARNING: Model basis term does not exist!" << std::endl;
- Assert( false );
- }
- Node mbr = getRepresentative( mb );
- if( mbr!=r ){
- d_eqcs[rtn].push_back( mbr );
+ //long nTermst = 0;
+ //long nTerms = 0;
+ //long nEqc = 0;
+
+ //which nodes are irrelevant for disequality matches
+ std::map< TNode, bool > irrelevant_dnode;
+ //now, store matches
+ eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( getEqualityEngine() );
+ while( !eqcs_i.isFinished() ){
+ //nEqc++;
+ Node r = (*eqcs_i);
+ TypeNode rtn = r.getType();
+ if( options::qcfMode()==QCF_MC ){
+ std::map< TypeNode, std::vector< TNode > >::iterator itt = d_eqcs.find( rtn );
+ if( itt==d_eqcs.end() ){
+ Node mb = getQuantifiersEngine()->getTermDatabase()->getModelBasisTerm( rtn );
+ if( !getEqualityEngine()->hasTerm( mb ) ){
+ Trace("qcf-warn") << "WARNING: Model basis term does not exist!" << std::endl;
+ Assert( false );
+ }
+ Node mbr = getRepresentative( mb );
+ if( mbr!=r ){
+ d_eqcs[rtn].push_back( mbr );
+ }
+ d_eqcs[rtn].push_back( r );
+ d_model_basis[rtn] = mb;
+ }else{
+ itt->second.push_back( r );
}
- d_eqcs[rtn].push_back( r );
- d_model_basis[rtn] = mb;
}else{
- itt->second.push_back( r );
- }
- }else{
- d_eqcs[rtn].push_back( r );
- }
- /*
- eq::EqClassIterator eqc_i = eq::EqClassIterator( r, getEqualityEngine() );
- while( !eqc_i.isFinished() ){
- TNode n = (*eqc_i);
- if( quantifiers::TermDb::hasBoundVarAttr( n ) ){
- std::cout << "BAD TERM IN DB : " << n << std::endl;
- exit( 199 );
- }
- ++eqc_i;
- }
- */
-
- //if( r.getType().isInteger() ){
- // Trace("qcf-mv") << "Model value for eqc(" << r << ") : " << d_quantEngine->getValuation().getModelValue( r ) << std::endl;
- //}
- //EqcInfo * eqcir = getEqcInfo( r, false );
- //get relevant nodes that we are disequal from
- /*
- std::vector< Node > deqc;
- if( eqcir ){
- for( NodeBoolMap::iterator it = eqcir->d_diseq.begin(); it != eqcir->d_diseq.end(); ++it ){
- if( (*it).second ){
- //Node rd = (*it).first;
- //if( rd!=getRepresentative( rd ) ){
- // std::cout << "Bad rep!" << std::endl;
- // exit( 0 );
- //}
- deqc.push_back( (*it).first );
+ d_eqcs[rtn].push_back( r );
+ } + /*
+ eq::EqClassIterator eqc_i = eq::EqClassIterator( r, getEqualityEngine() );
+ while( !eqc_i.isFinished() ){
+ TNode n = (*eqc_i);
+ if( n.hasBoundVar() ){
+ std::cout << "BAD TERM IN DB : " << n << std::endl;
+ exit( 199 );
+ }
+ ++eqc_i;
+ } +
*/
+ + //if( r.getType().isInteger() ){
+ // Trace("qcf-mv") << "Model value for eqc(" << r << ") : " << d_quantEngine->getValuation().getModelValue( r ) << std::endl;
+ //}
+ //EqcInfo * eqcir = getEqcInfo( r, false );
+ //get relevant nodes that we are disequal from
+ /*
+ std::vector< Node > deqc;
+ if( eqcir ){
+ for( NodeBoolMap::iterator it = eqcir->d_diseq.begin(); it != eqcir->d_diseq.end(); ++it ){
+ if( (*it).second ){
+ //Node rd = (*it).first;
+ //if( rd!=getRepresentative( rd ) ){
+ // std::cout << "Bad rep!" << std::endl;
+ // exit( 0 );
+ //}
+ deqc.push_back( (*it).first );
+ }
}
}
- }
- */
- //process disequalities
- /*
- eq::EqClassIterator eqc_i = eq::EqClassIterator( r, getEqualityEngine() );
- while( !eqc_i.isFinished() ){
- TNode n = (*eqc_i);
- if( n.getKind()!=EQUAL ){
- nTermst++;
- //node_to_rep[n] = r;
- //if( n.getNumChildren()>0 ){
- // if( n.getKind()!=APPLY_UF ){
- // std::cout << n.getKind() << " " << n.getOperator() << " " << n << std::endl;
- // }
- //}
- if( !quantifiers::TermDb::hasBoundVarAttr( n ) ){ //temporary
-
- bool isRedundant;
- std::map< TNode, std::vector< TNode > >::iterator it_na;
- TNode fn;
- if( MatchGen::isHandledUfTerm( n ) ){
- Node f = MatchGen::getOperator( this, n );
- computeArgReps( n );
- it_na = d_arg_reps.find( n );
- Assert( it_na!=d_arg_reps.end() );
- Node nadd = d_eqc_uf_terms[f].d_children[r].addTerm( n, d_arg_reps[n] );
- isRedundant = (nadd!=n);
- d_uf_terms[f].addTerm( n, d_arg_reps[n] );
+ */
+ //process disequalities
+ /*
+ eq::EqClassIterator eqc_i = eq::EqClassIterator( r, getEqualityEngine() );
+ while( !eqc_i.isFinished() ){
+ TNode n = (*eqc_i);
+ if( n.getKind()!=EQUAL ){
+ nTermst++;
+ //node_to_rep[n] = r;
+ //if( n.getNumChildren()>0 ){
+ // if( n.getKind()!=APPLY_UF ){
+ // std::cout << n.getKind() << " " << n.getOperator() << " " << n << std::endl;
+ // }
+ //}
+ if( !quantifiers::TermDb::hasBoundVarAttr( n ) ){ //temporary
+
+ bool isRedundant;
+ std::map< TNode, std::vector< TNode > >::iterator it_na;
+ TNode fn;
+ if( MatchGen::isHandledUfTerm( n ) ){
+ Node f = MatchGen::getOperator( this, n );
+ computeArgReps( n );
+ it_na = d_arg_reps.find( n );
+ Assert( it_na!=d_arg_reps.end() );
+ Node nadd = d_eqc_uf_terms[f].d_children[r].addTerm( n, d_arg_reps[n] );
+ isRedundant = (nadd!=n);
+ d_uf_terms[f].addTerm( n, d_arg_reps[n] );
+ }else{
+ isRedundant = false;
+ }
+ nTerms += isRedundant ? 0 : 1;
}else{
- isRedundant = false;
- }
- nTerms += isRedundant ? 0 : 1;
- }else{
- if( Debug.isOn("qcf-nground") ){
- Debug("qcf-nground") << "Non-ground term in eqc : " << n << std::endl;
- Assert( false );
+ if( Debug.isOn("qcf-nground") ){
+ Debug("qcf-nground") << "Non-ground term in eqc : " << n << std::endl;
+ Assert( false );
+ }
}
}
+ ++eqc_i;
}
- ++eqc_i;
+ */
+ ++eqcs_i;
+ }
+ /*
+ if( Trace.isOn("qcf-opt") ){
+ double clSet2 = double(clock())/double(CLOCKS_PER_SEC);
+ Trace("qcf-opt") << "Compute rel eqc : " << std::endl;
+ Trace("qcf-opt") << " " << nEqc << " equivalence classes. " << std::endl;
+ Trace("qcf-opt") << " " << nTerms << " / " << nTermst << " terms." << std::endl;
+ Trace("qcf-opt") << " Time : " << (clSet2-clSet) << std::endl;
}
*/
- ++eqcs_i;
- }
- /*
- if( Trace.isOn("qcf-opt") ){
- double clSet2 = double(clock())/double(CLOCKS_PER_SEC);
- Trace("qcf-opt") << "Compute rel eqc : " << std::endl;
- Trace("qcf-opt") << " " << nEqc << " equivalence classes. " << std::endl;
- Trace("qcf-opt") << " " << nTerms << " / " << nTermst << " terms." << std::endl;
- Trace("qcf-opt") << " Time : " << (clSet2-clSet) << std::endl;
}
- */
}
void QuantConflictFind::computeArgReps( TNode n ) {
@@ -1997,11 +2194,10 @@ void QuantConflictFind::computeArgReps( TNode n ) { void QuantConflictFind::computeUfTerms( TNode f ) {
if( d_uf_terms.find( f )==d_uf_terms.end() ){
d_uf_terms[f].clear();
- unsigned nt = d_quantEngine->getTermDatabase()->d_op_map[f].size();
+ unsigned nt = d_quantEngine->getTermDatabase()->getNumGroundTerms( f );
for( unsigned i=0; i<nt; i++ ){
Node n = d_quantEngine->getTermDatabase()->d_op_map[f][i];
- if( !n.getAttribute(NoMatchAttribute()) ){
- Assert( getEqualityEngine()->hasTerm( n ) );
+ if( getEqualityEngine()->hasTerm( n ) && !n.getAttribute(NoMatchAttribute()) ){
Node r = getRepresentative( n );
computeArgReps( n );
d_eqc_uf_terms[f].d_children[r].addTerm( n, d_arg_reps[n] );
@@ -2079,20 +2275,17 @@ void QuantConflictFind::debugPrintQuantBody( const char * c, Node q, Node n, boo QuantConflictFind::Statistics::Statistics():
d_inst_rounds("QuantConflictFind::Inst_Rounds", 0),
d_conflict_inst("QuantConflictFind::Instantiations_Conflict_Find", 0 ),
- d_prop_inst("QuantConflictFind::Instantiations_Prop", 0 ),
- d_partial_inst("QuantConflictFind::Instantiations_Partial", 0 )
+ d_prop_inst("QuantConflictFind::Instantiations_Prop", 0 )
{
StatisticsRegistry::registerStat(&d_inst_rounds);
StatisticsRegistry::registerStat(&d_conflict_inst);
StatisticsRegistry::registerStat(&d_prop_inst);
- StatisticsRegistry::registerStat(&d_partial_inst);
}
QuantConflictFind::Statistics::~Statistics(){
StatisticsRegistry::unregisterStat(&d_inst_rounds);
StatisticsRegistry::unregisterStat(&d_conflict_inst);
StatisticsRegistry::unregisterStat(&d_prop_inst);
- StatisticsRegistry::unregisterStat(&d_partial_inst);
}
-}
\ No newline at end of file +}
diff --git a/src/theory/quantifiers/quant_conflict_find.h b/src/theory/quantifiers/quant_conflict_find.h index 944cfa584..62bd347c7 100755 --- a/src/theory/quantifiers/quant_conflict_find.h +++ b/src/theory/quantifiers/quant_conflict_find.h @@ -83,13 +83,15 @@ public: typ_formula,
typ_var,
typ_ite_var,
- typ_top,
+ typ_bool_var,
};
void debugPrintType( const char * c, short typ, bool isTrace = false );
public:
MatchGen() : d_type( typ_invalid ){}
MatchGen( QuantInfo * qi, Node n, bool isVar = false, bool beneathQuant = false );
bool d_tgt;
+ bool d_tgt_orig;
+ bool d_wasSet;
Node d_n;
std::vector< MatchGen > d_children;
short d_type;
@@ -97,21 +99,32 @@ public: void reset_round( QuantConflictFind * p );
void reset( QuantConflictFind * p, bool tgt, QuantInfo * qi );
bool getNextMatch( QuantConflictFind * p, QuantInfo * qi );
+ bool getExplanation( QuantConflictFind * p, QuantInfo * qi, std::vector< Node >& exp );
+ Node getExplanationTerm( QuantConflictFind * p, QuantInfo * qi, Node t, std::vector< Node >& exp );
bool isValid() { return d_type!=typ_invalid; }
void setInvalid();
// is this term treated as UF application?
+ static bool isHandledBoolConnective( TNode n );
static bool isHandledUfTerm( TNode n );
static Node getOperator( QuantConflictFind * p, Node n );
+ //can this node be handled by the algorithm
+ static bool isHandled( TNode n );
};
//info for quantifiers
class QuantInfo {
private:
void registerNode( Node n, bool hasPol, bool pol, bool beneathQuant = false );
- void flatten( Node n, bool beneathQuant );
+ void flatten( Node n, bool beneathQuant ); +private: //for completing match
+ std::vector< int > d_unassigned;
+ std::vector< TypeNode > d_unassigned_tn;
+ int d_unassigned_nvar;
+ int d_una_index;
+ std::vector< int > d_una_eqc_count;
public:
- QuantInfo() : d_mg( NULL ), d_isPartial( false ) {}
+ QuantInfo() : d_mg( NULL ) {}
std::vector< TNode > d_vars;
std::map< TNode, int > d_var_num;
std::map< TNode, bool > d_nbeneathQuant;
@@ -120,6 +133,7 @@ public: bool isVar( TNode v ) { return d_var_num.find( v )!=d_var_num.end(); }
int getNumVars() { return (int)d_vars.size(); }
TNode getVar( int i ) { return d_vars[i]; }
+
MatchGen * d_mg;
Node d_q;
std::map< int, MatchGen * > d_var_mg;
@@ -133,20 +147,18 @@ public: std::map< int, std::map< TNode, int > > d_curr_var_deq;
int getCurrentRepVar( int v );
TNode getCurrentValue( TNode n );
+ TNode getCurrentExpValue( TNode n );
bool getCurrentCanBeEqual( QuantConflictFind * p, int v, TNode n, bool chDiseq = false );
int addConstraint( QuantConflictFind * p, int v, TNode n, bool polarity );
int addConstraint( QuantConflictFind * p, int v, TNode n, int vn, bool polarity, bool doRemove );
bool setMatch( QuantConflictFind * p, int v, TNode n );
bool isMatchSpurious( QuantConflictFind * p );
- bool completeMatch( QuantConflictFind * p, std::vector< int >& assigned );
+ bool completeMatch( QuantConflictFind * p, std::vector< int >& assigned, bool doContinue = false );
+ void revertMatch( std::vector< int >& assigned );
void debugPrintMatch( const char * c );
- bool isConstrainedVar( int v );
-public:
- // is partial
- bool d_isPartial;
- //the variables that this quantified formula has not beneath nested quantifiers
- std::map< TNode, bool > d_has_var;
- bool isPartial() { return d_isPartial; }
+ bool isConstrainedVar( int v ); +public: + void getMatch( std::vector< Node >& terms );
};
class QuantConflictFind : public QuantifiersModule
@@ -214,10 +226,11 @@ public: enum {
effort_conflict,
effort_prop_eq,
- effort_partial,
effort_mc,
};
short d_effort;
+ void setEffort( int e ) { d_effort = e; }
+ static short getMaxQcfEffort();
bool areMatchEqual( TNode n1, TNode n2 );
bool areMatchDisequal( TNode n1, TNode n2 );
public:
@@ -233,11 +246,15 @@ public: void merge( Node a, Node b );
/** assert disequal */
void assertDisequal( Node a, Node b );
+ /** reset round */
+ void reset_round( Theory::Effort level );
/** check */
void check( Theory::Effort level );
/** needs check */
bool needsCheck( Theory::Effort level );
private:
+ bool d_needs_computeRelEqr;
+public:
void computeRelevantEqr();
private:
void debugPrint( const char * c );
@@ -253,7 +270,6 @@ public: IntStat d_inst_rounds;
IntStat d_conflict_inst;
IntStat d_prop_inst;
- IntStat d_partial_inst;
Statistics();
~Statistics();
};
diff --git a/src/theory/quantifiers/quantifiers_rewriter.cpp b/src/theory/quantifiers/quantifiers_rewriter.cpp index ac847678d..a079dbaab 100644 --- a/src/theory/quantifiers/quantifiers_rewriter.cpp +++ b/src/theory/quantifiers/quantifiers_rewriter.cpp @@ -14,6 +14,7 @@ #include "theory/quantifiers/quantifiers_rewriter.h" #include "theory/quantifiers/options.h" +#include "theory/quantifiers/term_database.h" using namespace std; using namespace CVC4; @@ -90,19 +91,29 @@ void QuantifiersRewriter::addNodeToOrBuilder( Node n, NodeBuilder<>& t ){ } } -void QuantifiersRewriter::computeArgs( std::vector< Node >& args, std::vector< Node >& activeArgs, Node n ){ +void QuantifiersRewriter::computeArgs( std::vector< Node >& args, std::map< Node, bool >& activeMap, Node n ){ if( n.getKind()==BOUND_VARIABLE ){ - if( std::find( args.begin(), args.end(), n )!=args.end() && - std::find( activeArgs.begin(), activeArgs.end(), n )==activeArgs.end() ){ - activeArgs.push_back( n ); + if( std::find( args.begin(), args.end(), n )!=args.end() ){ + activeMap[ n ] = true; } }else{ for( int i=0; i<(int)n.getNumChildren(); i++ ){ - computeArgs( args, activeArgs, n[i] ); + computeArgs( args, activeMap, n[i] ); } } } +void QuantifiersRewriter::computeArgVec( std::vector< Node >& args, std::vector< Node >& activeArgs, Node n ) { + std::map< Node, bool > activeMap; + computeArgs( args, activeMap, n ); + for( unsigned i=0; i<args.size(); i++ ){ + if( activeMap[args[i]] ){ + activeArgs.push_back( args[i] ); + } + } +} + + bool QuantifiersRewriter::hasArg( std::vector< Node >& args, Node n ){ if( std::find( args.begin(), args.end(), n )!=args.end() ){ return true; @@ -135,7 +146,6 @@ void QuantifiersRewriter::setNestedQuantifiers2( Node n, Node q, std::vector< No } } - RewriteResponse QuantifiersRewriter::preRewrite(TNode in) { Trace("quantifiers-rewrite-debug") << "pre-rewriting " << in << " " << in.hasAttribute(NestedQuantAttribute()) << std::endl; if( in.getKind()==kind::EXISTS || in.getKind()==kind::FORALL ){ @@ -164,9 +174,7 @@ RewriteResponse QuantifiersRewriter::preRewrite(TNode in) { } Node n = NodeManager::currentNM()->mkNode( in.getKind(), children ); if( in!=n ){ - if( in.hasAttribute(NestedQuantAttribute()) ){ - setNestedQuantifiers( n, in.getAttribute(NestedQuantAttribute()) ); - } + setAttributes( in, n ); Trace("quantifiers-pre-rewrite") << "*** pre-rewrite " << in << std::endl; Trace("quantifiers-pre-rewrite") << " to " << std::endl; Trace("quantifiers-pre-rewrite") << n << std::endl; @@ -178,8 +186,9 @@ RewriteResponse QuantifiersRewriter::preRewrite(TNode in) { } RewriteResponse QuantifiersRewriter::postRewrite(TNode in) { - Trace("quantifiers-rewrite-debug") << "post-rewriting " << in << " " << in.hasAttribute(NestedQuantAttribute()) << std::endl; - if( in.getKind()==kind::EXISTS || in.getKind()==kind::FORALL ){ + Trace("quantifiers-rewrite-debug") << "post-rewriting " << in << std::endl; + Trace("quantifiers-rewrite-debug") << "Attributes : " << in.hasAttribute(NestedQuantAttribute()) << std::endl; + if( !options::quantRewriteRules() || !TermDb::isRewriteRule( in ) ){ RewriteStatus status = REWRITE_DONE; Node ret = in; //get the arguments @@ -217,12 +226,13 @@ RewriteResponse QuantifiersRewriter::postRewrite(TNode in) { } //print if changed if( in!=ret ){ - if( in.hasAttribute(NestedQuantAttribute()) ){ - setNestedQuantifiers( ret, in.getAttribute(NestedQuantAttribute()) ); - } + setAttributes( in, ret ); Trace("quantifiers-rewrite") << "*** rewrite " << in << std::endl; Trace("quantifiers-rewrite") << " to " << std::endl; Trace("quantifiers-rewrite") << ret << std::endl; + Trace("quantifiers-rewrite-debug") << "Attributes : " << ret.hasAttribute(NestedQuantAttribute()) << std::endl; + + } return RewriteResponse( status, ret ); } @@ -409,6 +419,15 @@ Node QuantifiersRewriter::computeClause( Node n ){ } } +void QuantifiersRewriter::setAttributes( Node in, Node n ) { + if( in.hasAttribute(NestedQuantAttribute()) ){ + setNestedQuantifiers( n, in.getAttribute(NestedQuantAttribute()) ); + } + //if( in.hasAttribute(QRewriteRuleAttribute()) ){ + // n.setAttribute(QRewriteRuleAttribute(), in.getAttribute(QRewriteRuleAttribute())); + //} +} + Node QuantifiersRewriter::computeCNF( Node n, std::vector< Node >& args, NodeBuilder<>& defs, bool forcePred ){ if( isLiteral( n ) ){ return n; @@ -432,7 +451,7 @@ Node QuantifiersRewriter::computeCNF( Node n, std::vector< Node >& args, NodeBui //compute the free variables Node nt = t; std::vector< Node > activeArgs; - computeArgs( args, activeArgs, nt ); + computeArgVec( args, activeArgs, nt ); std::vector< TypeNode > argTypes; for( int i=0; i<(int)activeArgs.size(); i++ ){ argTypes.push_back( activeArgs[i].getType() ); @@ -581,7 +600,7 @@ Node QuantifiersRewriter::computeSplit( Node f, Node body, std::vector< Node >& //get variables contained in the literal Node n = body[i]; std::vector<Node> lit_vars; - computeArgs( vars, lit_vars, n); + computeArgVec( vars, lit_vars, n); //collectVars( n, vars, lit_vars ); if (lit_vars.empty()) { lits.push_back(n); @@ -689,7 +708,7 @@ Node QuantifiersRewriter::computeOperation( Node f, int computeOption ){ n = computeElimSymbols( n ); }else if( computeOption==COMPUTE_MINISCOPING ){ //return directly - return computeMiniscoping( args, n, ipl, f.hasAttribute(NestedQuantAttribute()) ); + return computeMiniscoping( f, args, n, ipl, f.hasAttribute(NestedQuantAttribute()) ); }else if( computeOption==COMPUTE_AGGRESSIVE_MINISCOPING ){ return computeAggressiveMiniscoping( args, n, f.hasAttribute(NestedQuantAttribute()) ); }else if( computeOption==COMPUTE_NNF ){ @@ -735,7 +754,7 @@ Node QuantifiersRewriter::computeOperation( Node f, int computeOption ){ Node QuantifiersRewriter::mkForAll( std::vector< Node >& args, Node body, Node ipl ){ std::vector< Node > activeArgs; - computeArgs( args, activeArgs, body ); + computeArgVec( args, activeArgs, body ); if( activeArgs.empty() ){ return body; }else{ @@ -749,7 +768,7 @@ Node QuantifiersRewriter::mkForAll( std::vector< Node >& args, Node body, Node i } } -Node QuantifiersRewriter::computeMiniscoping( std::vector< Node >& args, Node body, Node ipl, bool isNested ){ +Node QuantifiersRewriter::computeMiniscoping( Node f, std::vector< Node >& args, Node body, Node ipl, bool isNested ){ //Notice() << "rewrite quant " << body << std::endl; if( body.getKind()==FORALL ){ //combine arguments @@ -763,21 +782,21 @@ Node QuantifiersRewriter::computeMiniscoping( std::vector< Node >& args, Node bo if( body.getKind()==NOT ){ //push not downwards if( body[0].getKind()==NOT ){ - return computeMiniscoping( args, body[0][0], ipl ); + return computeMiniscoping( f, 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 ); + return computeMiniscoping( f, args, t.constructNode(), ipl ); } }else if( body[0].getKind()==OR ){ if( doMiniscopingAnd() ){ NodeBuilder<> t(kind::AND); for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ Node trm = body[0][i].negate(); - t << computeMiniscoping( args, trm, ipl ); + t << computeMiniscoping( f, args, trm, ipl ); } return t.constructNode(); } @@ -787,7 +806,7 @@ Node QuantifiersRewriter::computeMiniscoping( std::vector< Node >& args, Node bo //break apart NodeBuilder<> t(kind::AND); for( int i=0; i<(int)body.getNumChildren(); i++ ){ - t << computeMiniscoping( args, body[i], ipl ); + t << computeMiniscoping( f, args, body[i], ipl ); } Node retVal = t; return retVal; @@ -815,7 +834,11 @@ Node QuantifiersRewriter::computeMiniscoping( std::vector< Node >& args, Node bo } } } + //if( body==f[1] ){ + // return f; + //}else{ return mkForAll( args, body, ipl ); + //} } Node QuantifiersRewriter::computeAggressiveMiniscoping( std::vector< Node >& args, Node body, bool isNested ){ @@ -826,7 +849,7 @@ Node QuantifiersRewriter::computeAggressiveMiniscoping( std::vector< Node >& arg Trace("ag-miniscope") << "compute aggressive miniscoping on " << body << std::endl; for( size_t i=0; i<body.getNumChildren(); i++ ){ std::vector< Node > activeArgs; - computeArgs( args, activeArgs, body[i] ); + computeArgVec( args, activeArgs, body[i] ); for (unsigned j=0; j<activeArgs.size(); j++ ){ varLits[activeArgs[j]].push_back( body[i] ); } @@ -964,3 +987,220 @@ bool QuantifiersRewriter::doOperation( Node f, bool isNested, int computeOption return false; } } + + + + +Node QuantifiersRewriter::rewriteRewriteRule( Node r ) { + Kind rrkind = r[2].getKind(); + + //guards, pattern, body + + // 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); + }; + + // Body/Remove_term/Guards/Triggers + Node body = r[2][1]; + TNode new_terms = r[2][1]; + std::vector<Node> guards; + std::vector<Node> pattern; + Node true_node = NodeManager::currentNM()->mkConst(true); + // shortcut + TNode head = r[2][0]; + switch(rrkind){ + case kind::RR_REWRITE: + // Equality + pattern.push_back( head ); + if( head.getType().isBoolean() ){ + body = head.iffNode(body); + }else{ + body = head.eqNode(body); + } + break; + case kind::RR_REDUCTION: + case kind::RR_DEDUCTION: + // Add head to guards and pattern + switch(head.getKind()){ + case kind::AND: + for( unsigned i = 0; i<head.getNumChildren(); i++ ){ + guards.push_back(head[i]); + pattern.push_back(head[i]); + } + break; + default: + if( head!=true_node ){ + guards.push_back(head); + pattern.push_back( head ); + } + break; + } + break; + default: + Unreachable("RewriteRules can be of only three kinds"); + break; + } + // Add the other guards + TNode g = r[1]; + switch(g.getKind()){ + case kind::AND: + for( unsigned i = 0; i<g.getNumChildren(); i++ ){ + guards.push_back(g[i]); + } + break; + default: + if( g != true_node ){ + guards.push_back( g ); + } + break; + } + // Add the other triggers + if( r[2].getNumChildren() >= 3 ){ + for( unsigned i=0; i<r[2][2][0].getNumChildren(); i++ ) { + pattern.push_back( r[2][2][0][i] ); + } + } + + Trace("rr-rewrite") << "Rule is " << r << std::endl; + Trace("rr-rewrite") << "Head is " << head << std::endl; + Trace("rr-rewrite") << "Patterns are "; + for( unsigned i=0; i<pattern.size(); i++ ){ + Trace("rr-rewrite") << pattern[i] << " "; + } + Trace("rr-rewrite") << std::endl; + + NodeBuilder<> forallB(kind::FORALL); + forallB << r[0]; + Node gg = guards.size()==0 ? true_node : ( guards.size()==1 ? guards[0] : NodeManager::currentNM()->mkNode( AND, guards ) ); + gg = NodeManager::currentNM()->mkNode( OR, gg.negate(), body ); + gg = Rewriter::rewrite( gg ); + forallB << gg; + NodeBuilder<> patternB(kind::INST_PATTERN); + patternB.append(pattern); + NodeBuilder<> patternListB(kind::INST_PATTERN_LIST); + //the entire rewrite rule is the first pattern + if( options::quantRewriteRules() ){ + patternListB << NodeManager::currentNM()->mkNode( INST_PATTERN, r ); + } + patternListB << static_cast<Node>(patternB); + forallB << static_cast<Node>(patternListB); + Node rn = (Node) forallB; + + return rn; +} + +struct ContainsQuantAttributeId {}; +typedef expr::Attribute<ContainsQuantAttributeId, uint64_t> ContainsQuantAttribute; + +// check if the given node contains a universal quantifier +bool QuantifiersRewriter::containsQuantifiers(Node n) { + if( n.hasAttribute(ContainsQuantAttribute()) ){ + return n.getAttribute(ContainsQuantAttribute())==1; + } else if(n.getKind() == kind::FORALL) { + return true; + } else { + bool cq = false; + for( unsigned i = 0; i < n.getNumChildren(); ++i ){ + if( containsQuantifiers(n[i]) ){ + cq = true; + break; + } + } + ContainsQuantAttribute cqa; + n.setAttribute(cqa, cq ? 1 : 0); + return cq; + } +} + +Node QuantifiersRewriter::preSkolemizeQuantifiers( Node n, bool polarity, std::vector< Node >& fvs ){ + Trace("pre-sk") << "Pre-skolem " << n << " " << polarity << " " << fvs.size() << endl; + if( n.getKind()==kind::NOT ){ + Node nn = preSkolemizeQuantifiers( n[0], !polarity, fvs ); + return nn.negate(); + }else if( n.getKind()==kind::FORALL ){ + if( polarity ){ + vector< Node > children; + children.push_back( n[0] ); + //add children to current scope + vector< Node > fvss; + fvss.insert( fvss.begin(), fvs.begin(), fvs.end() ); + for( int i=0; i<(int)n[0].getNumChildren(); i++ ){ + fvss.push_back( n[0][i] ); + } + //process body + children.push_back( preSkolemizeQuantifiers( n[1], polarity, fvss ) ); + if( n.getNumChildren()==3 ){ + children.push_back( n[2] ); + } + //return processed quantifier + return NodeManager::currentNM()->mkNode( kind::FORALL, children ); + }else{ + //process body + Node nn = preSkolemizeQuantifiers( n[1], polarity, fvs ); + //now, substitute skolems for the variables + vector< TypeNode > argTypes; + for( int i=0; i<(int)fvs.size(); i++ ){ + argTypes.push_back( fvs[i].getType() ); + } + //calculate the variables and substitution + vector< Node > vars; + vector< Node > subs; + for( int i=0; i<(int)n[0].getNumChildren(); i++ ){ + vars.push_back( n[0][i] ); + } + for( int i=0; i<(int)n[0].getNumChildren(); i++ ){ + //make the new function symbol + if( argTypes.empty() ){ + Node s = NodeManager::currentNM()->mkSkolem( "sk_$$", n[0][i].getType(), "created during pre-skolemization" ); + subs.push_back( s ); + }else{ + TypeNode typ = NodeManager::currentNM()->mkFunctionType( argTypes, n[0][i].getType() ); + Node op = NodeManager::currentNM()->mkSkolem( "skop_$$", typ, "op created during pre-skolemization" ); + //DOTHIS: set attribute on op, marking that it should not be selected as trigger + vector< Node > funcArgs; + funcArgs.push_back( op ); + funcArgs.insert( funcArgs.end(), fvs.begin(), fvs.end() ); + subs.push_back( NodeManager::currentNM()->mkNode( kind::APPLY_UF, funcArgs ) ); + } + } + //apply substitution + nn = nn.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + return nn; + } + }else{ + //check if it contains a quantifier as a subterm + //if so, we will write this node + if( containsQuantifiers( n ) ){ + if( n.getType().isBoolean() ){ + if( n.getKind()==kind::ITE || n.getKind()==kind::IFF || n.getKind()==kind::XOR || n.getKind()==kind::IMPLIES ){ + Node nn; + //must remove structure + if( n.getKind()==kind::ITE ){ + nn = NodeManager::currentNM()->mkNode( kind::AND, + NodeManager::currentNM()->mkNode( kind::OR, n[0].notNode(), n[1] ), + NodeManager::currentNM()->mkNode( kind::OR, n[0], n[2] ) ); + }else if( n.getKind()==kind::IFF || n.getKind()==kind::XOR ){ + nn = NodeManager::currentNM()->mkNode( kind::AND, + NodeManager::currentNM()->mkNode( kind::OR, n[0].notNode(), n.getKind()==kind::XOR ? n[1].notNode() : n[1] ), + NodeManager::currentNM()->mkNode( kind::OR, n[0], n.getKind()==kind::XOR ? n[1] : n[1].notNode() ) ); + }else if( n.getKind()==kind::IMPLIES ){ + nn = NodeManager::currentNM()->mkNode( kind::OR, n[0].notNode(), n[1] ); + } + return preSkolemizeQuantifiers( nn, polarity, fvs ); + }else if( n.getKind()==kind::AND || n.getKind()==kind::OR ){ + vector< Node > children; + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + children.push_back( preSkolemizeQuantifiers( n[i], polarity, fvs ) ); + } + return NodeManager::currentNM()->mkNode( n.getKind(), children ); + }else{ + //must pull ite's + } + } + } + return n; + } +} diff --git a/src/theory/quantifiers/quantifiers_rewriter.h b/src/theory/quantifiers/quantifiers_rewriter.h index e97d84701..4cbdb0cd1 100644 --- a/src/theory/quantifiers/quantifiers_rewriter.h +++ b/src/theory/quantifiers/quantifiers_rewriter.h @@ -38,14 +38,16 @@ public: 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 void computeArgs( std::vector< Node >& args, std::map< Node, bool >& activeMap, Node n ); + static void computeArgVec( 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 setNestedQuantifiers2( Node n, Node q, std::vector< Node >& processed ); static Node computeClause( Node n ); + static void setAttributes( Node in, Node n ); private: static Node computeElimSymbols( Node body ); - static Node computeMiniscoping( std::vector< Node >& args, Node body, Node ipl, bool isNested = false ); + static Node computeMiniscoping( Node f, std::vector< Node >& args, Node body, Node ipl, bool isNested = false ); static Node computeAggressiveMiniscoping( std::vector< Node >& args, Node body, bool isNested = false ); static Node computeNNF( Node body ); static Node computeSimpleIteLift( Node body ); @@ -79,6 +81,10 @@ private: static bool doMiniscopingAnd(); static bool doOperation( Node f, bool isNested, int computeOption ); public: + static Node rewriteRewriteRule( Node r ); + static bool containsQuantifiers(Node n); + static Node preSkolemizeQuantifiers(Node n, bool polarity, std::vector<Node>& fvs); +public: //static Node rewriteQuants( Node n, bool isNested = false ); //static Node rewriteQuant( Node n, bool isNested = false ); };/* class QuantifiersRewriter */ diff --git a/src/theory/quantifiers/relevant_domain.cpp b/src/theory/quantifiers/relevant_domain.cpp index 952f3409a..914141995 100644 --- a/src/theory/quantifiers/relevant_domain.cpp +++ b/src/theory/quantifiers/relevant_domain.cpp @@ -111,13 +111,15 @@ void RelevantDomain::compute(){ TermDb * db = d_qe->getTermDatabase(); for( std::map< Node, std::vector< Node > >::iterator it = db->d_op_map.begin(); it != db->d_op_map.end(); ++it ){ Node op = it->first; - for( unsigned i=0; i<it->second.size(); i++ ){ + unsigned sz = db->getNumGroundTerms( op ); + for( unsigned i=0; i<sz; i++ ){ Node n = it->second[i]; //if it is a non-redundant term if( !n.getAttribute(NoMatchAttribute()) ){ for( unsigned j=0; j<n.getNumChildren(); j++ ){ RDomain * rf = getRDomain( op, j ); rf->addTerm( n[j] ); + Trace("rel-dom-debug") << "...add ground term " << n[j] << " to rel dom " << op << "[" << j << "]" << std::endl; } } } @@ -188,6 +190,7 @@ void RelevantDomain::computeRelevantDomain( Node n, bool hasPol, bool pol ) { }else if( varCount==1 ){ int oCh = varCh==0 ? 1 : 0; bool ng = d_qe->getTermDatabase()->hasInstConstAttr( n[oCh] ); + Trace("rel-dom-debug") << "...add term " << n[oCh] << ", is ground = " << (!ng) << std::endl; //the negative occurrence adds the term to the domain if( !hasPol || !pol ){ rds[varCh]->addTerm( n[oCh], ng ); @@ -219,6 +222,7 @@ void RelevantDomain::computeRelevantDomainOpCh( RDomain * rf, Node n ) { rq->merge( rf ); } }else if( !d_qe->getTermDatabase()->hasInstConstAttr( n ) ){ + Trace("rel-dom-debug") << "...add ground term to rel dom " << n << std::endl; //term to add rf->addTerm( n ); } diff --git a/src/theory/quantifiers/rewrite_engine.cpp b/src/theory/quantifiers/rewrite_engine.cpp index 81de1e11d..59ba40ca7 100644 --- a/src/theory/quantifiers/rewrite_engine.cpp +++ b/src/theory/quantifiers/rewrite_engine.cpp @@ -21,6 +21,7 @@ #include "theory/quantifiers/options.h" #include "theory/quantifiers/inst_match_generator.h" #include "theory/theory_engine.h" +#include "theory/quantifiers/term_database.h" using namespace CVC4; using namespace std; @@ -37,14 +38,15 @@ struct PrioritySort { RewriteEngine::RewriteEngine( context::Context* c, QuantifiersEngine* qe ) : QuantifiersModule(qe) { - + d_true = NodeManager::currentNM()->mkConst( true ); } double RewriteEngine::getPriority( Node f ) { - Node rr = f.getAttribute(QRewriteRuleAttribute()); + Node rr = TermDb::getRewriteRule( f ); Node rrr = rr[2]; Trace("rr-priority") << "Get priority : " << rrr << " " << rrr.getKind() << std::endl; bool deterministic = rrr[1].getKind()!=OR; + if( rrr.getKind()==RR_REWRITE ){ return deterministic ? 0.0 : 3.0; }else if( rrr.getKind()==RR_DEDUCTION ){ @@ -54,26 +56,31 @@ double RewriteEngine::getPriority( Node f ) { }else{ return 6.0; } + + //return deterministic ? 0.0 : 1.0; } void RewriteEngine::check( Theory::Effort e ) { - if( e==Theory::EFFORT_LAST_CALL ){ - if( !d_quantEngine->getModel()->isModelSet() ){ - d_quantEngine->getTheoryEngine()->getModelBuilder()->buildModel( d_quantEngine->getModel(), true ); - } - if( d_true.isNull() ){ - d_true = NodeManager::currentNM()->mkConst( true ); - } - std::vector< Node > priority_order; - PrioritySort ps; - std::vector< int > indicies; - for( int i=0; i<(int)d_rr_quant.size(); i++ ){ - ps.d_priority.push_back( getPriority( d_rr_quant[i] ) ); - indicies.push_back( i ); - } - std::sort( indicies.begin(), indicies.end(), ps ); - for( unsigned i=0; i<indicies.size(); i++ ){ - priority_order.push_back( d_rr_quant[indicies[i]] ); + if( e==Theory::EFFORT_FULL ){ + Trace("rewrite-engine") << "---Rewrite Engine Round, effort = " << e << "---" << std::endl; + //if( e==Theory::EFFORT_LAST_CALL ){ + // if( !d_quantEngine->getModel()->isModelSet() ){ + // d_quantEngine->getTheoryEngine()->getModelBuilder()->buildModel( d_quantEngine->getModel(), true ); + // } + //} + if( d_needsSort ){ + d_priority_order.clear(); + PrioritySort ps; + std::vector< int > indicies; + for( int i=0; i<(int)d_rr_quant.size(); i++ ){ + ps.d_priority.push_back( getPriority( d_rr_quant[i] ) ); + indicies.push_back( i ); + } + std::sort( indicies.begin(), indicies.end(), ps ); + for( unsigned i=0; i<indicies.size(); i++ ){ + d_priority_order.push_back( d_rr_quant[indicies[i]] ); + } + d_needsSort = false; } //apply rewrite rules @@ -81,16 +88,17 @@ void RewriteEngine::check( Theory::Effort e ) { //per priority level int index = 0; bool success = true; - while( success && index<(int)priority_order.size() ) { - addedLemmas += checkRewriteRule( priority_order[index] ); + while( success && index<(int)d_priority_order.size() ) { + addedLemmas += checkRewriteRule( d_priority_order[index], e ); index++; - if( index<(int)priority_order.size() ){ - success = addedLemmas==0 || getPriority( priority_order[index] )==getPriority( priority_order[index-1] ); + if( index<(int)d_priority_order.size() ){ + success = addedLemmas==0 || getPriority( d_priority_order[index] )==getPriority( d_priority_order[index-1] ); } } - Trace("inst-engine") << "Rewrite engine added " << addedLemmas << " lemmas." << std::endl; + Trace("rewrite-engine") << "Finished rewrite engine, added " << addedLemmas << " lemmas." << std::endl; if (addedLemmas==0) { + }else{ //otherwise, the search will continue d_quantEngine->flushLemmas( &d_quantEngine->getOutputChannel() ); @@ -98,85 +106,185 @@ void RewriteEngine::check( Theory::Effort e ) { } } -int RewriteEngine::checkRewriteRule( Node f ) { - Trace("rewrite-engine-inst") << "Check " << f << ", priority = " << getPriority( f ) << "..." << std::endl; +int RewriteEngine::checkRewriteRule( Node f, Theory::Effort e ) { int addedLemmas = 0; - //reset triggers - Node rr = f.getAttribute(QRewriteRuleAttribute()); - if( d_rr_triggers.find(f)==d_rr_triggers.end() ){ - std::vector< inst::Trigger * > triggers; - if( f.getNumChildren()==3 ){ - for(unsigned i=0; i<f[2].getNumChildren(); i++ ){ - Node pat = f[2][i]; - std::vector< Node > nodes; - Trace("rewrite-engine-trigger") << "Trigger is : "; - for( int j=0; j<(int)pat.getNumChildren(); j++ ){ - Node p = d_quantEngine->getTermDatabase()->getInstConstantNode( pat[j], f ); - nodes.push_back( p ); - Trace("rewrite-engine-trigger") << p << " " << p.getKind() << " "; - } - Trace("rewrite-engine-trigger") << std::endl; - Assert( inst::Trigger::isUsableTrigger( nodes, f ) ); - inst::Trigger * tr = inst::Trigger::mkTrigger( d_quantEngine, f, nodes, 0, true, inst::Trigger::TR_MAKE_NEW, false ); - tr->getGenerator()->setActiveAdd(false); - triggers.push_back( tr ); - } - } - d_rr_triggers[f].insert( d_rr_triggers[f].end(), triggers.begin(), triggers.end() ); - } - for( unsigned i=0; i<d_rr_triggers[f].size(); i++ ){ - Trace("rewrite-engine-inst") << "Try trigger " << *d_rr_triggers[f][i] << std::endl; - d_rr_triggers[f][i]->resetInstantiationRound(); - d_rr_triggers[f][i]->reset( Node::null() ); - bool success; - do - { - InstMatch m( f ); - success = d_rr_triggers[f][i]->getNextMatch( f, m ); - if( success ){ - //see if instantiation is true in the model - Node rr = f.getAttribute(QRewriteRuleAttribute()); - Node rrg = rr[1]; - std::vector< Node > vars; - std::vector< Node > terms; - d_quantEngine->computeTermVector( f, m, vars, terms ); - Trace("rewrite-engine-inst-debug") << "Instantiation : " << m << std::endl; - Node inst = d_rr_guard[f]; - inst = inst.substitute( vars.begin(), vars.end(), terms.begin(), terms.end() ); - Trace("rewrite-engine-inst-debug") << "Try instantiation, guard : " << inst << std::endl; - FirstOrderModel * fm = d_quantEngine->getModel(); - Node v = fm->getValue( inst ); - Trace("rewrite-engine-inst-debug") << "Evaluated to " << v << std::endl; - if( v==d_true ){ - Trace("rewrite-engine-inst-debug") << "Add instantiation : " << m << std::endl; - if( d_quantEngine->addInstantiation( f, m ) ){ - addedLemmas++; - Trace("rewrite-engine-inst-debug") << "success" << std::endl; - //set the no-match attribute (the term is rewritten and can be ignored) - //Trace("rewrite-engine-inst-debug") << "We matched on : " << m.d_matched << std::endl; - //if( !m.d_matched.isNull() ){ - // NoMatchAttribute nma; - // m.d_matched.setAttribute(nma,true); + Trace("rewrite-engine-inst") << "Check " << d_qinfo_n[f] << ", priority = " << getPriority( f ) << ", effort = " << e << "..." << std::endl; + QuantConflictFind * qcf = d_quantEngine->getConflictFind(); + if( qcf ){ + //reset QCF module + qcf->computeRelevantEqr(); + qcf->setEffort( QuantConflictFind::effort_conflict ); + //get the proper quantifiers info + std::map< Node, QuantInfo >::iterator it = d_qinfo.find( f ); + if( it!=d_qinfo.end() ){ + QuantInfo * qi = &it->second; + if( qi->d_mg->isValid() ){ + Node rr = TermDb::getRewriteRule( f ); + Trace("rewrite-engine-inst-debug") << " Reset round..." << std::endl; + qi->reset_round( qcf ); + Trace("rewrite-engine-inst-debug") << " Get matches..." << std::endl; + while( qi->d_mg->getNextMatch( qcf, qi ) && ( addedLemmas==0 || !options::rrOneInstPerRound() ) ){ + Trace("rewrite-engine-inst-debug") << " Got match to complete..." << std::endl; + qi->debugPrintMatch( "rewrite-engine-inst-debug" ); + std::vector< int > assigned; + if( !qi->isMatchSpurious( qcf ) ){ + bool doContinue = false; + bool success = true; + int tempAddedLemmas = 0; + while( tempAddedLemmas==0 && success && ( addedLemmas==0 || !options::rrOneInstPerRound() ) ){ + success = qi->completeMatch( qcf, assigned, doContinue ); + doContinue = true; + if( success ){ + Trace("rewrite-engine-inst-debug") << " Construct match..." << std::endl; + std::vector< Node > inst; + qi->getMatch( inst ); + Trace("rewrite-engine-inst-debug") << " Add instantiation..." << std::endl; + for( unsigned i=0; i<f[0].getNumChildren(); i++ ){ + Trace("rewrite-engine-inst-debug") << " " << f[0][i] << " -> "; + if( i<inst.size() ){ + Trace("rewrite-engine-inst-debug") << inst[i] << std::endl; + }else{ + Trace("rewrite-engine-inst-debug") << "OUT_OF_RANGE" << std::endl; + Assert( false ); + } + } + //resize to remove auxiliary variables + if( inst.size()>f[0].getNumChildren() ){ + inst.resize( f[0].getNumChildren() ); + } + if( d_quantEngine->addInstantiation( f, inst ) ){ + addedLemmas++; + tempAddedLemmas++; + /* + //remove rewritten terms from consideration + std::vector< Node > to_remove; + switch( rr[2].getKind() ){ + case kind::RR_REWRITE: + to_remove.push_back( rr[2][0] ); + break; + case kind::RR_REDUCTION: + for( unsigned i=0; i<rr[2][0].getNumChildren(); i++ ){ + to_remove.push_back( rr[2][0][i] ); + } + break; + default: + break; + } + for( unsigned j=0; j<to_remove.size(); j++ ){ + Node ns = d_quantEngine->getSubstitute( to_remove[j], inst ); + Trace("rewrite-engine-inst-debug") << "Will remove : " << ns << std::endl; + ns.setAttribute(NoMatchAttribute(),true); + } + */ + }else{ + Trace("rewrite-engine-inst-debug") << " - failed." << std::endl; + } + Trace("rewrite-engine-inst-debug") << " Get next completion..." << std::endl; + } + } + //Trace("rewrite-engine-inst-debug") << " Reverted assigned variables : "; + //for( unsigned a=0; a<assigned.size(); a++ ) { + // Trace("rewrite-engine-inst-debug") << assigned[a] << " "; //} + //Trace("rewrite-engine-inst-debug") << std::endl; + //qi->revertMatch( assigned ); + //Assert( assigned.empty() ); + Trace("rewrite-engine-inst-debug") << " - failed to complete." << std::endl; }else{ - Trace("rewrite-engine-inst-debug") << "failure." << std::endl; + Trace("rewrite-engine-inst-debug") << " - match is spurious." << std::endl; } + Trace("rewrite-engine-inst-debug") << " Get next match..." << std::endl; } + }else{ + Trace("rewrite-engine-inst-debug") << "...Invalid qinfo." << std::endl; } - }while(success); + }else{ + Trace("rewrite-engine-inst-debug") << "...No qinfo." << std::endl; + } } - Trace("rewrite-engine-inst") << "Rule " << f << " generated " << addedLemmas << " lemmas." << std::endl; + Trace("rewrite-engine-inst") << "-> Generated " << addedLemmas << " lemmas." << std::endl; return addedLemmas; } void RewriteEngine::registerQuantifier( Node f ) { - if( f.hasAttribute(QRewriteRuleAttribute()) ){ - Trace("rewrite-engine") << "Register quantifier " << f << std::endl; - Node rr = f.getAttribute(QRewriteRuleAttribute()); - Trace("rewrite-engine") << " rewrite rule is : " << rr << std::endl; + Node rr = TermDb::getRewriteRule( f ); + if( !rr.isNull() ){ + Trace("rr-register") << "Register quantifier " << f << std::endl; + Trace("rr-register") << " rewrite rule is : " << rr << std::endl; d_rr_quant.push_back( f ); - d_rr_guard[f] = rr[1]; - Trace("rewrite-engine") << " guard is : " << d_rr_guard[f] << std::endl; + d_rr[f] = rr; + d_needsSort = true; + Trace("rr-register") << " guard is : " << d_rr[f][1] << std::endl; + + QuantConflictFind * qcf = d_quantEngine->getConflictFind(); + if( qcf ){ + std::vector< Node > qcfn_c; + + std::vector< Node > bvl; + for( unsigned i=0; i<f[0].getNumChildren(); i++ ){ + bvl.push_back( f[0][i] ); + } + + std::vector< Node > cc; + //Node head = rr[2][0]; + //if( head!=d_true ){ + //Node head_eq = head.getType().isBoolean() ? head.iffNode( head ) : head.eqNode( head ); + //head_eq = head_eq.negate(); + //cc.push_back( head_eq ); + //Trace("rr-register-debug") << " head eq is " << head_eq << std::endl; + //} + //add patterns + for( unsigned i=1; i<f[2].getNumChildren(); i++ ){ + std::vector< Node > nc; + for( unsigned j=0; j<f[2][i].getNumChildren(); j++ ){ + Node nn; + Node nbv = NodeManager::currentNM()->mkBoundVar( f[2][i][j].getType() ); + if( f[2][i][j].getType().isBoolean() ){ + if( f[2][i][j].getKind()!=APPLY_UF ){ + nn = f[2][i][j].negate(); + }else{ + nn = f[2][i][j].iffNode( nbv ).negate(); + bvl.push_back( nbv ); + } + //nn = f[2][i][j].negate(); + }else{ + nn = f[2][i][j].eqNode( nbv ).negate(); + bvl.push_back( nbv ); + } + nc.push_back( nn ); + } + if( !nc.empty() ){ + Node n = nc.size()==1 ? nc[0] : NodeManager::currentNM()->mkNode( AND, nc ); + Trace("rr-register-debug") << " pattern is " << n << std::endl; + if( std::find( cc.begin(), cc.end(), n )==cc.end() ){ + cc.push_back( n ); + } + } + } + qcfn_c.push_back( NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, bvl ) ); + + std::vector< Node > body_c; + //add the guards + if( d_rr[f][1].getKind()==AND ){ + for( unsigned j=0; j<d_rr[f][1].getNumChildren(); j++ ){ + if( MatchGen::isHandled( d_rr[f][1][j] ) ){ + body_c.push_back( d_rr[f][1][j].negate() ); + } + } + }else if( d_rr[f][1]!=d_true ){ + if( MatchGen::isHandled( d_rr[f][1] ) ){ + body_c.push_back( d_rr[f][1].negate() ); + } + } + //add the patterns to the body + body_c.push_back( cc.size()==1 ? cc[0] : NodeManager::currentNM()->mkNode( AND, cc ) ); + //make the body + qcfn_c.push_back( body_c.size()==1 ? body_c[0] : NodeManager::currentNM()->mkNode( OR, body_c ) ); + //make the quantified formula + d_qinfo_n[f] = NodeManager::currentNM()->mkNode( FORALL, qcfn_c ); + Trace("rr-register") << " qcf formula is : " << d_qinfo_n[f] << std::endl; + d_qinfo[f].initialize( d_qinfo_n[f], d_qinfo_n[f][1] ); + } } } @@ -184,3 +292,13 @@ void RewriteEngine::assertNode( Node n ) { } +Node RewriteEngine::getInstConstNode( Node n, Node q ) { + std::map< Node, Node >::iterator it = d_inst_const_node[q].find( n ); + if( it==d_inst_const_node[q].end() ){ + Node nn = d_quantEngine->getTermDatabase()->getInstConstantNode( n, q ); + d_inst_const_node[q][n] = nn; + return nn; + }else{ + return it->second; + } +}
\ No newline at end of file diff --git a/src/theory/quantifiers/rewrite_engine.h b/src/theory/quantifiers/rewrite_engine.h index a9a2f9b46..f85803617 100644 --- a/src/theory/quantifiers/rewrite_engine.h +++ b/src/theory/quantifiers/rewrite_engine.h @@ -29,19 +29,28 @@ namespace CVC4 { namespace theory { namespace quantifiers { +class QuantInfo; + class RewriteEngine : public QuantifiersModule { typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap; typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap; typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap; std::vector< Node > d_rr_quant; - std::map< Node, Node > d_rr_guard; + std::vector< Node > d_priority_order; + std::map< Node, Node > d_rr; Node d_true; /** explicitly provided patterns */ std::map< Node, std::vector< inst::Trigger* > > d_rr_triggers; + /** get the quantifer info object */ + std::map< Node, Node > d_qinfo_n; + std::map< Node, QuantInfo > d_qinfo; double getPriority( Node f ); + bool d_needsSort; + std::map< Node, std::map< Node, Node > > d_inst_const_node; + Node getInstConstNode( Node n, Node q ); private: - int checkRewriteRule( Node f ); + int checkRewriteRule( Node f, Theory::Effort e ); public: RewriteEngine( context::Context* c, QuantifiersEngine* qe ); diff --git a/src/theory/quantifiers/symmetry_breaking.cpp b/src/theory/quantifiers/symmetry_breaking.cpp index 0023b05bc..900db62d0 100644 --- a/src/theory/quantifiers/symmetry_breaking.cpp +++ b/src/theory/quantifiers/symmetry_breaking.cpp @@ -21,6 +21,7 @@ #include "theory/theory_engine.h" #include "util/sort_inference.h" #include "theory/uf/theory_uf_strong_solver.h" +#include "theory/uf/theory_uf.h" using namespace CVC4; using namespace CVC4::kind; diff --git a/src/theory/quantifiers/term_database.cpp b/src/theory/quantifiers/term_database.cpp index 39ba3e8af..ea1231e7a 100644 --- a/src/theory/quantifiers/term_database.cpp +++ b/src/theory/quantifiers/term_database.cpp @@ -18,7 +18,6 @@ #include "theory/theory_engine.h" #include "theory/quantifiers/first_order_model.h" #include "theory/quantifiers/options.h" -#include "theory/rewriterules/efficient_e_matching.h" #include "theory/quantifiers/theory_quantifiers.h" using namespace std; @@ -47,19 +46,20 @@ bool TermArgTrie::addTerm2( QuantifiersEngine* qe, Node n, int argIndex ){ } } -void TermDb::addTermEfficient( Node n, std::set< Node >& added){ - static AvailableInTermDb aitdi; - if (inst::Trigger::isAtomicTrigger( n ) && !n.getAttribute(aitdi)){ - //Already processed but new in this branch - n.setAttribute(aitdi,true); - added.insert( n ); - for( size_t i=0; i< n.getNumChildren(); i++ ){ - addTermEfficient(n[i],added); - } - } +TermDb::TermDb( context::Context* c, context::UserContext* u, QuantifiersEngine* qe ) : d_quantEngine( qe ), d_op_ccount( u ) { } +/** ground terms */ +unsigned TermDb::getNumGroundTerms( Node f ) { + std::map< Node, std::vector< Node > >::iterator it = d_op_map.find( f ); + if( it!=d_op_map.end() ){ + return it->second.size(); + }else{ + return 0; + } + //return d_op_ccount[f]; +} Node TermDb::getOperator( Node n ) { //return n.getOperator(); @@ -94,10 +94,8 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant ){ return; } if( d_processed.find( n )==d_processed.end() ){ - ++(d_quantEngine->d_statistics.d_term_in_termdb); d_processed.insert(n); d_type_map[ n.getType() ].push_back( n ); - n.setAttribute(AvailableInTermDb(),true); //if this is an atomic trigger, consider adding it //Call the children? if( inst::Trigger::isAtomicTrigger( n ) ){ @@ -105,27 +103,20 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant ){ Trace("term-db") << "register term in db " << n << std::endl; //std::cout << "register trigger term " << n << std::endl; Node op = getOperator( n ); + /* + int occ = d_op_ccount[op]; + if( occ<(int)d_op_map[op].size() ){ + d_op_map[op][occ] = n; + }else{ + d_op_map[op].push_back( n ); + } + d_op_ccount[op].set( occ + 1 ); + */ d_op_map[op].push_back( n ); added.insert( n ); for( size_t i=0; i<n.getNumChildren(); i++ ){ addTerm( n[i], added, withinQuant ); - if( options::efficientEMatching() ){ - EfficientEMatcher* eem = d_quantEngine->getEfficientEMatcher(); - if( d_parents[n[i]][op].empty() ){ - //must add parent to equivalence class info - Node nir = d_quantEngine->getEqualityQuery()->getRepresentative( n[i] ); - EqClassInfo* eci_nir = eem->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 ); - Assert(!getParents(n[i],op,i).empty()); - } - } if( options::eagerInstQuant() ){ if( !n.hasAttribute(InstLevelAttribute()) && n.getAttribute(InstLevelAttribute())==0 ){ int addedLemmas = 0; @@ -143,13 +134,6 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant ){ addTerm( n[i], added, withinQuant ); } } - }else{ - if( options::efficientEMatching() && !TermDb::hasInstConstAttr(n)){ - //Efficient e-matching must be notified - //The term in triggers are not important here - Debug("term-db") << "New in this branch term " << n << std::endl; - addTermEfficient(n,added); - } } } @@ -159,7 +143,7 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant ){ 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 ){ - d_op_count[ it->first ] = 0; + d_op_nonred_count[ it->first ] = 0; if( !it->second.empty() ){ if( it->second[0].getType().isBoolean() ){ d_pred_map_trie[ 0 ][ it->first ].d_data.clear(); @@ -177,7 +161,7 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant ){ congruentCount++; }else{ nonCongruentCount++; - d_op_count[ it->first ]++; + d_op_nonred_count[ it->first ]++; } }else{ congruentCount++; @@ -205,7 +189,7 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant ){ congruentCount++; }else{ nonCongruentCount++; - d_op_count[ op ]++; + d_op_nonred_count[ op ]++; } }else{ alreadyCongruentCount++; @@ -348,28 +332,6 @@ bool TermDb::hasInstConstAttr( Node n ) { return !getInstConstAttr(n).isNull(); } -bool TermDb::hasBoundVarAttr( Node n ) { - if( !n.getAttribute(HasBoundVarComputedAttribute()) ){ - bool hasBv = false; - if( n.getKind()==BOUND_VARIABLE ){ - hasBv = true; - }else{ - for (unsigned i=0; i<n.getNumChildren(); i++) { - if( hasBoundVarAttr(n[i]) ){ - hasBv = true; - break; - } - } - } - HasBoundVarAttribute hbva; - n.setAttribute(hbva, hasBv); - HasBoundVarComputedAttribute hbvca; - n.setAttribute(hbvca, true); - Trace("bva") << n << " has bva : " << n.getAttribute(HasBoundVarAttribute()) << std::endl; - } - return n.getAttribute(HasBoundVarAttribute()); -} - void TermDb::getBoundVars( Node n, std::vector< Node >& bvs) { if (n.getKind()==BOUND_VARIABLE ){ if ( std::find( bvs.begin(), bvs.end(), n)==bvs.end() ){ @@ -669,3 +631,15 @@ void TermDb::registerTrigger( theory::inst::Trigger* tr, Node op ){ d_op_triggers[op].push_back( tr ); } } + +bool TermDb::isRewriteRule( Node q ) { + return !getRewriteRule( q ).isNull(); +} + +Node TermDb::getRewriteRule( Node q ) { + if( q.getKind()==FORALL && q.getNumChildren()==3 && q[2].getNumChildren()>0 && q[2][0][0].getKind()==REWRITE_RULE ){ + return q[2][0][0]; + }else{ + return Node::null(); + } +} diff --git a/src/theory/quantifiers/term_database.h b/src/theory/quantifiers/term_database.h index 860f087dd..41108bc2a 100644 --- a/src/theory/quantifiers/term_database.h +++ b/src/theory/quantifiers/term_database.h @@ -44,15 +44,6 @@ typedef expr::Attribute<InstLevelAttributeId, uint64_t> InstLevelAttribute; struct InstVarNumAttributeId {}; typedef expr::Attribute<InstVarNumAttributeId, uint64_t> InstVarNumAttribute; -// Attribute that tell if a node have been asserted in this branch -struct AvailableInTermDbId {}; -/** use the special for boolean flag */ -typedef expr::Attribute<AvailableInTermDbId, - bool, - expr::attr::NullCleanupStrategy, - true // context dependent - > AvailableInTermDb; - struct ModelBasisAttributeId {}; typedef expr::Attribute<ModelBasisAttributeId, bool> ModelBasisAttribute; //for APPLY_UF terms, 1 : term has direct child with model basis attribute, @@ -60,15 +51,6 @@ typedef expr::Attribute<ModelBasisAttributeId, bool> ModelBasisAttribute; struct ModelBasisArgAttributeId {}; typedef expr::Attribute<ModelBasisArgAttributeId, uint64_t> ModelBasisArgAttribute; -struct HasBoundVarAttributeId {}; -typedef expr::Attribute<HasBoundVarAttributeId, bool> HasBoundVarAttribute; -struct HasBoundVarComputedAttributeId {}; -typedef expr::Attribute<HasBoundVarComputedAttributeId, bool> HasBoundVarComputedAttribute; - -//for rewrite rules -struct QRewriteRuleAttributeId {}; -typedef expr::Attribute<QRewriteRuleAttributeId, Node> QRewriteRuleAttribute; - //for bounded integers struct BoundIntLitAttributeId {}; typedef expr::Attribute<BoundIntLitAttributeId, uint64_t> BoundIntLitAttribute; @@ -106,6 +88,7 @@ class TermDb { friend class ::CVC4::theory::inst::Trigger; friend class ::CVC4::theory::rrinst::Trigger; friend class ::CVC4::theory::quantifiers::fmcheck::FullModelChecker; + typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap; private: /** reference to the quantifiers engine */ QuantifiersEngine* d_quantEngine; @@ -114,13 +97,17 @@ private: private: /** select op map */ std::map< Node, std::map< TypeNode, std::map< TypeNode, Node > > > d_par_op_map; + /** count number of ground terms per operator (user-context dependent) */ + NodeIntMap d_op_ccount; public: - TermDb( QuantifiersEngine* qe ) : d_quantEngine( qe ){} + TermDb( context::Context* c, context::UserContext* u, QuantifiersEngine* qe ); ~TermDb(){} + /** ground terms */ + unsigned getNumGroundTerms( Node f ); + /** count number of non-redundant ground terms per operator */ + std::map< Node, int > d_op_nonred_count; /** map from APPLY_UF operators to ground terms for that operator */ std::map< Node, std::vector< Node > > d_op_map; - /** count number of APPLY_UF terms per operator */ - std::map< Node, int > d_op_count; /** map from APPLY_UF functions to trie */ std::map< Node, TermArgTrie > d_func_map_trie; /** map from APPLY_UF predicates to trie */ @@ -133,9 +120,6 @@ public: void reset( Theory::Effort effort ); /** get operation */ Node getOperator( Node n ); -private: - /** for efficient e-matching */ - void addTermEfficient( Node n, std::set< Node >& added); public: /** parent structure (for efficient E-matching): n -> op -> index -> L @@ -210,8 +194,6 @@ public: static bool hasInstConstAttr( Node n ); //for bound variables public: - //does n have bound variables? - static bool hasBoundVarAttr( Node n ); //get bound variables in n static void getBoundVars( Node n, std::vector< Node >& bvs); //for skolem @@ -259,6 +241,11 @@ public: int isInstanceOf( Node n1, Node n2 ); /** filter all nodes that have instances */ void filterInstances( std::vector< Node >& nodes ); +public: + /** is quantifier treated as a rewrite rule? */ + static bool isRewriteRule( Node q ); + /** get the rewrite rule associated with the quanfied formula */ + static Node getRewriteRule( Node q ); };/* class TermDb */ }/* CVC4::theory::quantifiers namespace */ diff --git a/src/theory/quantifiers/theory_quantifiers_type_rules.h b/src/theory/quantifiers/theory_quantifiers_type_rules.h index b13d8601e..93bd2c012 100644 --- a/src/theory/quantifiers/theory_quantifiers_type_rules.h +++ b/src/theory/quantifiers/theory_quantifiers_type_rules.h @@ -104,6 +104,92 @@ struct QuantifierInstPatternListTypeRule { } };/* struct QuantifierInstPatternListTypeRule */ + +class RewriteRuleTypeRule { +public: + + /** + * Compute the type for (and optionally typecheck) a term belonging + * to the theory of rewriterules. + * + * @param nodeManager the NodeManager in use + * @param n the node to compute the type of + * @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[0], + "first argument of rewrite rule is not bound var list"); + } + if( n[ 1 ].getType(check)!=nodeManager->booleanType() ){ + throw TypeCheckingExceptionPrivate(n[1], + "guard of rewrite rule is not an actual guard"); + } + if( n[2].getType(check) != + TypeNode(nodeManager->mkTypeConst<TypeConstant>(RRHB_TYPE))){ + throw TypeCheckingExceptionPrivate(n[2], + "not a correct rewrite rule"); + } + } + return nodeManager->booleanType(); + } +};/* class RewriteRuleTypeRule */ + + +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::quantifiers namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/quantifiers_engine.cpp b/src/theory/quantifiers_engine.cpp index 41f53740a..7fa61477f 100644 --- a/src/theory/quantifiers_engine.cpp +++ b/src/theory/quantifiers_engine.cpp @@ -25,13 +25,14 @@ #include "theory/quantifiers/first_order_model.h" #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/trigger.h" -#include "theory/rewriterules/efficient_e_matching.h" -#include "theory/rewriterules/rr_trigger.h" +//#include "theory/rewriterules/efficient_e_matching.h" +//#include "theory/rewriterules/rr_trigger.h" #include "theory/quantifiers/bounded_integers.h" #include "theory/quantifiers/rewrite_engine.h" #include "theory/quantifiers/quant_conflict_find.h" #include "theory/quantifiers/relevant_domain.h" #include "theory/uf/options.h" +#include "theory/uf/theory_uf.h" using namespace std; using namespace CVC4; @@ -44,10 +45,10 @@ QuantifiersEngine::QuantifiersEngine(context::Context* c, context::UserContext* d_te( te ), d_lemmas_produced_c(u){ d_eq_query = new EqualityQueryQuantifiersEngine( this ); - d_term_db = new quantifiers::TermDb( this ); + d_term_db = new quantifiers::TermDb( c, u, this ); d_tr_trie = new inst::TriggerTrie; - d_rr_tr_trie = new rrinst::TriggerTrie; - d_eem = new EfficientEMatcher( this ); + //d_rr_tr_trie = new rrinst::TriggerTrie; + //d_eem = new EfficientEMatcher( this ); d_hasAddedLemma = false; Trace("quant-engine-debug") << "Initialize model, mbqi : " << options::mbqiMode() << std::endl; @@ -74,7 +75,7 @@ d_lemmas_produced_c(u){ } //add quantifiers modules - if( options::quantConflictFind() ){ + if( options::quantConflictFind() || options::quantRewriteRules() ){ d_qcf = new quantifiers::QuantConflictFind( this, c); d_modules.push_back( d_qcf ); }else{ @@ -100,7 +101,7 @@ d_lemmas_produced_c(u){ d_model_engine = NULL; d_bint = NULL; } - if( options::rewriteRulesAsAxioms() ){ + if( options::quantRewriteRules() ){ d_rr_engine = new quantifiers::RewriteEngine( c, this ); d_modules.push_back(d_rr_engine); }else{ @@ -129,10 +130,6 @@ EqualityQueryQuantifiersEngine* QuantifiersEngine::getEqualityQuery() { return d_eq_query; } -//Instantiator* QuantifiersEngine::getInstantiator( theory::TheoryId id ){ -// return d_te->theoryOf( id )->getInstantiator(); -//} - context::Context* QuantifiersEngine::getSatContext(){ return d_te->theoryOf( THEORY_QUANTIFIERS )->getSatContext(); } @@ -176,6 +173,9 @@ void QuantifiersEngine::check( Theory::Effort e ){ if( d_rel_dom ){ d_rel_dom->reset(); } + for( int i=0; i<(int)d_modules.size(); i++ ){ + d_modules[i]->reset_round( e ); + } if( e==Theory::EFFORT_LAST_CALL ){ //if effort is last call, try to minimize model first if( options::finiteModelFind() ){ @@ -216,7 +216,11 @@ void QuantifiersEngine::check( Theory::Effort e ){ void QuantifiersEngine::registerQuantifier( Node f ){ if( std::find( d_quants.begin(), d_quants.end(), f )==d_quants.end() ){ - Trace("quant") << "Register quantifier : " << f << std::endl; + Trace("quant") << "QuantifiersEngine : Register quantifier "; + if( d_term_db->isRewriteRule( f ) ){ + Trace("quant") << " (rewrite rule)"; + } + Trace("quant") << " : " << f << std::endl; d_quants.push_back( f ); ++(d_statistics.d_num_quant); @@ -277,9 +281,6 @@ Node QuantifiersEngine::getNextDecisionRequest(){ void QuantifiersEngine::addTermToDatabase( Node n, bool withinQuant ){ std::set< Node > added; getTermDatabase()->addTerm( n, added, withinQuant ); - if( options::efficientEMatching() ){ - d_eem->newTerms( added ); - } //added contains also the Node that just have been asserted in this branch if( d_quant_rel ){ for( std::set< Node >::iterator i=added.begin(), end=added.end(); i!=end; i++ ){ @@ -361,7 +362,7 @@ void QuantifiersEngine::setInstantiationLevelAttr( Node n, uint64_t level ){ } } -Node QuantifiersEngine::doSubstitute( Node n, std::vector< Node >& terms ){ +Node QuantifiersEngine::getSubstitute( Node n, std::vector< Node >& terms ){ if( n.getKind()==INST_CONSTANT ){ Debug("check-inst") << "Substitute inst constant : " << n << std::endl; return terms[n.getAttribute(InstVarNumAttribute())]; @@ -377,7 +378,7 @@ Node QuantifiersEngine::doSubstitute( Node n, std::vector< Node >& terms ){ } bool changed = false; for( unsigned i=0; i<n.getNumChildren(); i++ ){ - Node c = doSubstitute( n[i], terms ); + Node c = getSubstitute( n[i], terms ); cc.push_back( c ); changed = changed || c!=n[i]; } @@ -410,7 +411,7 @@ Node QuantifiersEngine::getInstantiation( Node f, std::vector< Node >& vars, std }else{ //do optimized version Node icb = d_term_db->getInstConstantBody( f ); - body = doSubstitute( icb, terms ); + body = getSubstitute( icb, terms ); if( Debug.isOn("check-inst") ){ Node body2 = f[ 1 ].substitute( vars.begin(), vars.end(), terms.begin(), terms.end() ); if( body!=body2 ){ @@ -612,15 +613,7 @@ QuantifiersEngine::Statistics::Statistics(): 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), - d_term_in_termdb("QuantifiersEngine::Term_in_TermDb", 0), - d_num_mono_candidates("QuantifiersEngine::NumMonoCandidates", 0), - d_num_mono_candidates_new_term("QuantifiersEngine::NumMonoCandidatesNewTerm", 0), - d_num_multi_candidates("QuantifiersEngine::NumMultiCandidates", 0), - d_mono_candidates_cache_hit("QuantifiersEngine::MonoCandidatesCacheHit", 0), - d_mono_candidates_cache_miss("QuantifiersEngine::MonoCandidatesCacheMiss", 0), - d_multi_candidates_cache_hit("QuantifiersEngine::MultiCandidatesCacheHit", 0), - d_multi_candidates_cache_miss("QuantifiersEngine::MultiCandidatesCacheMiss", 0) + d_multi_trigger_instantiations("QuantifiersEngine::Multi_Trigger_Instantiations", 0) { StatisticsRegistry::registerStat(&d_num_quant); StatisticsRegistry::registerStat(&d_instantiation_rounds); @@ -634,14 +627,6 @@ QuantifiersEngine::Statistics::Statistics(): StatisticsRegistry::registerStat(&d_simple_triggers); StatisticsRegistry::registerStat(&d_multi_triggers); StatisticsRegistry::registerStat(&d_multi_trigger_instantiations); - StatisticsRegistry::registerStat(&d_term_in_termdb); - StatisticsRegistry::registerStat(&d_num_mono_candidates); - StatisticsRegistry::registerStat(&d_num_mono_candidates_new_term); - StatisticsRegistry::registerStat(&d_num_multi_candidates); - StatisticsRegistry::registerStat(&d_mono_candidates_cache_hit); - StatisticsRegistry::registerStat(&d_mono_candidates_cache_miss); - StatisticsRegistry::registerStat(&d_multi_candidates_cache_hit); - StatisticsRegistry::registerStat(&d_multi_candidates_cache_miss); } QuantifiersEngine::Statistics::~Statistics(){ @@ -657,14 +642,6 @@ QuantifiersEngine::Statistics::~Statistics(){ StatisticsRegistry::unregisterStat(&d_simple_triggers); StatisticsRegistry::unregisterStat(&d_multi_triggers); StatisticsRegistry::unregisterStat(&d_multi_trigger_instantiations); - StatisticsRegistry::unregisterStat(&d_term_in_termdb); - StatisticsRegistry::unregisterStat(&d_num_mono_candidates); - StatisticsRegistry::unregisterStat(&d_num_mono_candidates_new_term); - StatisticsRegistry::unregisterStat(&d_num_multi_candidates); - StatisticsRegistry::unregisterStat(&d_mono_candidates_cache_hit); - StatisticsRegistry::unregisterStat(&d_mono_candidates_cache_miss); - StatisticsRegistry::unregisterStat(&d_multi_candidates_cache_hit); - StatisticsRegistry::unregisterStat(&d_multi_candidates_cache_miss); } eq::EqualityEngine* QuantifiersEngine::getMasterEqualityEngine(){ diff --git a/src/theory/quantifiers_engine.h b/src/theory/quantifiers_engine.h index fbc501aec..858093543 100644 --- a/src/theory/quantifiers_engine.h +++ b/src/theory/quantifiers_engine.h @@ -20,8 +20,8 @@ #include "theory/theory.h" #include "util/hash.h" #include "theory/quantifiers/inst_match.h" -#include "theory/rewriterules/rr_inst_match.h" #include "theory/quantifiers/quant_util.h" +#include "expr/attribute.h" #include "util/statistics_registry.h" @@ -49,6 +49,8 @@ public: virtual void finishInit() {} /* whether this module needs to check this round */ virtual bool needsCheck( Theory::Effort e ) { return e>=Theory::EFFORT_LAST_CALL; } + /* reset at a round */ + virtual void reset_round( Theory::Effort e ){} /* Call during quantifier engine's check */ virtual void check( Theory::Effort e ) = 0; /* Called for new quantifiers */ @@ -75,11 +77,7 @@ namespace inst { class TriggerTrie; }/* CVC4::theory::inst */ -namespace rrinst { - class TriggerTrie; -}/* CVC4::theory::inst */ - -class EfficientEMatcher; +//class EfficientEMatcher; class EqualityQueryQuantifiersEngine; class QuantifiersEngine { @@ -102,8 +100,6 @@ private: quantifiers::RelevantDomain* d_rel_dom; /** phase requirements for each quantifier for each instantiation literal */ std::map< Node, QuantPhaseReq* > d_phase_reqs; - /** efficient e-matcher */ - EfficientEMatcher* d_eem; /** instantiation engine */ quantifiers::InstantiationEngine* d_inst_engine; /** model engine */ @@ -131,8 +127,6 @@ private: quantifiers::TermDb* d_term_db; /** all triggers will be stored in this trie */ inst::TriggerTrie* d_tr_trie; - /** all triggers for rewrite rules will be stored in this trie */ - rrinst::TriggerTrie* d_rr_tr_trie; /** extended model object */ quantifiers::FirstOrderModel* d_model; /** statistics for debugging */ @@ -144,8 +138,6 @@ private: public: QuantifiersEngine(context::Context* c, context::UserContext* u, TheoryEngine* te); ~QuantifiersEngine(); - /** get instantiator for id */ - //Instantiator* getInstantiator( theory::TheoryId id ); /** get theory engine */ TheoryEngine* getTheoryEngine() { return d_te; } /** get equality query object for the given type. The default is the @@ -171,8 +163,6 @@ public: QuantPhaseReq* getPhaseRequirements( Node f ) { return d_phase_reqs.find( f )==d_phase_reqs.end() ? NULL : d_phase_reqs[f]; } /** get phase requirement terms */ void getPhaseReqTerms( Node f, std::vector< Node >& nodes ); - /** get efficient e-matching utility */ - EfficientEMatcher* getEfficientEMatcher() { return d_eem; } /** get bounded integers utility */ quantifiers::BoundedIntegers * getBoundedIntegers() { return d_bint; } /** Conflict find mechanism for quantifiers */ @@ -199,8 +189,6 @@ private: bool addInstantiation( Node f, std::vector< Node >& vars, std::vector< Node >& terms ); /** set instantiation level attr */ void setInstantiationLevelAttr( Node n, uint64_t level ); - /** do substitution */ - Node doSubstitute( Node n, std::vector< Node >& terms ); public: /** get instantiation */ Node getInstantiation( Node f, std::vector< Node >& vars, std::vector< Node >& terms ); @@ -208,6 +196,8 @@ public: Node getInstantiation( Node f, InstMatch& m ); /** get instantiation */ Node getInstantiation( Node f, std::vector< Node >& terms ); + /** do substitution */ + Node getSubstitute( Node n, std::vector< Node >& terms ); /** exist instantiation ? */ bool existsInstantiation( Node f, InstMatch& m, bool modEq = true, bool modInst = false ); /** add lemma lem */ @@ -238,8 +228,6 @@ public: quantifiers::TermDb* getTermDatabase() { return d_term_db; } /** get trigger database */ inst::TriggerTrie* getTriggerDatabase() { return d_tr_trie; } - /** get rewrite trigger database */ - rrinst::TriggerTrie* getRRTriggerDatabase() { return d_rr_tr_trie; } /** add term to database */ void addTermToDatabase( Node n, bool withinQuant = false ); /** get the master equality engine */ @@ -260,14 +248,6 @@ public: IntStat d_simple_triggers; IntStat d_multi_triggers; IntStat d_multi_trigger_instantiations; - IntStat d_term_in_termdb; - IntStat d_num_mono_candidates; - IntStat d_num_mono_candidates_new_term; - IntStat d_num_multi_candidates; - IntStat d_mono_candidates_cache_hit; - IntStat d_mono_candidates_cache_miss; - IntStat d_multi_candidates_cache_hit; - IntStat d_multi_candidates_cache_miss; Statistics(); ~Statistics(); };/* class QuantifiersEngine::Statistics */ diff --git a/src/theory/rewriterules/efficient_e_matching.cpp b/src/theory/rewriterules/efficient_e_matching.cpp deleted file mode 100644 index 05934fc8b..000000000 --- a/src/theory/rewriterules/efficient_e_matching.cpp +++ /dev/null @@ -1,680 +0,0 @@ -/********************* */ -/*! \file efficient_e_matching.cpp - ** \verbatim - ** Original author: Andrew Reynolds - ** Major contributors: Morgan Deters - ** Minor contributors (to current version): none - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief Implementation of theory uf instantiator class - **/ - -#include "theory/rewriterules/efficient_e_matching.h" -#include "theory/rewriterules/rr_candidate_generator.h" -#include "theory/quantifiers/candidate_generator.h" -#include "theory/quantifiers/options.h" -#include "theory/rewriterules/options.h" -#include "theory/quantifiers/term_database.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::inst; - -namespace CVC4 { -namespace theory { - -inline std::ostream& operator<<(std::ostream& out, const EfficientEMatcher::Ips& ips) { - return out; -}; - -EqClassInfo::EqClassInfo( context::Context* c ) : d_funs( c ), d_pfuns( c ), d_disequal( c ){ - -} - -//set member -void EqClassInfo::setMember( Node n, quantifiers::TermDb* db ){ - if( n.hasOperator() ){ - d_funs.insertAtContextLevelZero(n.getOperator(),true); - } - //add parent functions - for( std::hash_map< Node, std::hash_map< int, std::vector< Node > >, NodeHashFunction >::iterator it = db->d_parents[n].begin(); - it != db->d_parents[n].end(); ++it ){ - //TODO Is it true to do it at level 0? That depend when SetMember is called - // At worst it is a good overapproximation - d_pfuns.insertAtContextLevelZero( 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; - } -} - -inline void outputEqClassInfo( const char* c, const EqClassInfo* eci){ - Debug(c) << " funs:"; - for( EqClassInfo::BoolMap::iterator it = eci->d_funs.begin(); it != eci->d_funs.end(); it++ ) { - Debug(c) << (*it).first << ","; - } - Debug(c) << std::endl << "pfuns:"; - for( EqClassInfo::BoolMap::iterator it = eci->d_pfuns.begin(); it != eci->d_pfuns.end(); it++ ) { - Debug(c) << (*it).first << ","; - } - Debug(c) << std::endl; -} - - - -EfficientEMatcher::EfficientEMatcher( CVC4::theory::QuantifiersEngine* qe ) : d_quantEngine( qe ) -{ - -} - -eq::EqualityEngine* EfficientEMatcher::getEqualityEngine(){ - //return ((uf::TheoryUF*)d_quantEngine->getTheoryEngine()->theoryOf( THEORY_UF ))->getEqualityEngine(); - return d_quantEngine->getMasterEqualityEngine(); -} - -/** new node */ -void EfficientEMatcher::newEqClass( TNode n ){ - -} - -void EfficientEMatcher::newTerms(SetNode& s){ - static NoMatchAttribute rewrittenNodeAttribute; - /* op -> nodes (if the set is empty, the op is not interesting) */ - std::hash_map< TNode, SetNode, TNodeHashFunction > h; - /* types -> nodes (if the set is empty, the type is not interesting) */ - std::hash_map< TypeNode, SetNode, TypeNodeHashFunction > tyh; - for(SetNode::iterator i=s.begin(), end=s.end(); i != end; ++i){ - if (i->getAttribute(rewrittenNodeAttribute)) continue; /* skip it */ - if( !d_cand_gens.empty() ){ - // op - TNode op = i->getOperator(); - std::hash_map< TNode, SetNode, TNodeHashFunction >::iterator - is = h.find(op); - if(is == h.end()){ - std::pair<std::hash_map< TNode, SetNode, TNodeHashFunction >::iterator,bool> - p = h.insert(make_pair(op,SetNode())); - is = p.first; - if(d_cand_gens.find(op) != d_cand_gens.end()){ - is->second.insert(*i); - } /* else we have inserted an empty set */ - }else if(!is->second.empty()){ - is->second.insert(*i); - } - } - if( !d_cand_gen_types.empty() ){ - //type - TypeNode ty = i->getType(); - std::hash_map< TypeNode, SetNode, TypeNodeHashFunction >::iterator - is = tyh.find(ty); - if(is == tyh.end()){ - std::pair<std::hash_map< TypeNode, SetNode, TypeNodeHashFunction >::iterator,bool> - p = tyh.insert(make_pair(ty,SetNode())); - is = p.first; - if(d_cand_gen_types.find(ty) != d_cand_gen_types.end()){ - is->second.insert(*i); - } /* else we have inserted an empty set */ - }else if(!is->second.empty()){ - is->second.insert(*i); - } - } - } - //op - for(std::hash_map< TNode, SetNode, TNodeHashFunction >::iterator i=h.begin(), end=h.end(); - i != end; ++i){ - //new term, add n to candidate generators - if(i->second.empty()) continue; - std::map< Node, NodeNewTermDispatcher >::iterator - inpc = d_cand_gens.find(i->first); - //we know that this op exists - Assert(inpc != d_cand_gens.end()); - inpc->second.send(i->second); - } - //type - for(std::hash_map< TypeNode, SetNode, TypeNodeHashFunction >::iterator i=tyh.begin(), end=tyh.end(); - i != end; ++i){ - //new term, add n to candidate generators - if(i->second.empty()) continue; - std::map< TypeNode, NodeNewTermDispatcher >::iterator - inpc = d_cand_gen_types.find(i->first); - //we know that this op exists - Assert(inpc != d_cand_gen_types.end()); - inpc->second.send(i->second); - } - -} - - -/** merge */ -void EfficientEMatcher::merge( TNode a, TNode b ){ - if( options::efficientEMatching() ){ - //merge eqc_ops of b into a - EqClassInfo* eci_a = getOrCreateEquivalenceClassInfo( a ); - EqClassInfo* eci_b = getOrCreateEquivalenceClassInfo( b ); - - 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, eci_a, b, eci_b ); - computeCandidatesPcPairs( b, eci_b, a, eci_a ); - computeCandidatesPpPairs( a, eci_a, b, eci_b ); - computeCandidatesPpPairs( b, eci_b, a, eci_a ); - } - computeCandidatesConstants( a, eci_a, b, eci_b); - computeCandidatesConstants( b, eci_b, a, eci_a); - - eci_a->merge( eci_b ); - } -} - -/** assert terms are disequal */ -void EfficientEMatcher::assertDisequal( TNode a, TNode b, TNode reason ){ - -} - -EqClassInfo* EfficientEMatcher::getEquivalenceClassInfo( Node n ) { - return d_eqc_ops.find( n )==d_eqc_ops.end() ? NULL : d_eqc_ops[n]; -} -EqClassInfo* EfficientEMatcher::getOrCreateEquivalenceClassInfo( Node n ){ - Assert( n==getEqualityEngine()->getRepresentative( n ) ); - if( d_eqc_ops.find( n )==d_eqc_ops.end() ){ - EqClassInfo* eci = new EqClassInfo( d_quantEngine->getSatContext() ); - eci->setMember( n, d_quantEngine->getTermDatabase() ); - d_eqc_ops[n] = eci; - } - return d_eqc_ops[n]; -} - -void EfficientEMatcher::computeCandidatesPcPairs( Node a, EqClassInfo* eci_a, Node b, EqClassInfo* eci_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; - outputEqClassInfo("efficient-e-match",eci_a); - for( EqClassInfo::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::vector< std::pair< NodePcDispatcher*, Ips > > >::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 ) ){ - //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::vector< std::pair< NodePcDispatcher*, Ips > >::iterator cit = itf->second.begin(); - cit != itf->second.end(); ++cit ){ -#ifdef CVC4_DEBUG - Debug("efficient-e-match") << " Checking pattern " << cit->first->pat << std::endl; -#endif - Debug("efficient-e-match") << " Check inverted path string for pattern "; - outputIps( "efficient-e-match", cit->second ); - Debug("efficient-e-match") << std::endl; - - //collect all new relevant terms - SetNode terms; - terms.insert( b ); - collectTermsIps( cit->second, terms ); - if( terms.empty() ) continue; - Debug("efficient-e-match") << " -> Added terms (" << terms.size() << "): "; - for( SetNode::const_iterator t=terms.begin(), end=terms.end(); - t!=end; ++t ){ - Debug("efficient-e-match") << (*t) << " "; - } - Debug("efficient-e-match") << std::endl; - //add them as candidates to the candidate generator - cit->first->send(terms); - } - } - } - } -} - -void EfficientEMatcher::computeCandidatesPpPairs( Node a, EqClassInfo* eci_a, Node b, EqClassInfo* eci_b ){ - Debug("efficient-e-match") << "Compute candidates for pp pairs..." << std::endl; - for( std::map< Node, std::map< Node, std::vector< triple< NodePpDispatcher*, Ips, Ips > > > >::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::vector< triple<NodePpDispatcher*, Ips, Ips> > >::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::vector< triple<NodePpDispatcher*, Ips, Ips> > ::iterator cit = it2->second.begin(); - cit != it2->second.end(); ++cit ){ -#ifdef CVC4_DEBUG - Debug("efficient-e-match") << " Checking pattern " << cit->first->pat1 << " and " << cit->first->pat2 << std::endl; -#endif - Debug("efficient-e-match") << " Check inverted path string "; - outputIps( "efficient-e-match", cit->second ); - SetNode a_terms; - a_terms.insert( a ); - collectTermsIps( cit->second, a_terms ); - if( a_terms.empty() ) continue; - Debug("efficient-e-match") << " And check inverted path string "; - outputIps( "efficient-e-match", cit->third ); - SetNode b_terms; - b_terms.insert( b ); - collectTermsIps( cit->third, b_terms ); - if( b_terms.empty() ) continue; - //Start debug - Debug("efficient-e-match") << " -> Possibly Added termsA (" << a_terms.size() << "): "; - for( SetNode::const_iterator t=a_terms.begin(), end=a_terms.end(); - t!=end; ++t ){ - Debug("efficient-e-match") << (*t) << " "; - } - Debug("efficient-e-match") << std::endl; - Debug("efficient-e-match") << " -> Possibly Added termsB (" << b_terms.size() << "): "; - for( SetNode::const_iterator t=b_terms.begin(), end=b_terms.end(); - t!=end; ++t ){ - Debug("efficient-e-match") << (*t) << " "; - } - Debug("efficient-e-match") << std::endl; - //End debug - - cit->first->send(a_terms,b_terms); - } - } - } - } - } -} - - -void EfficientEMatcher::computeCandidatesConstants( Node a, EqClassInfo* eci_a, Node b, EqClassInfo* eci_b ){ - Debug("efficient-e-match") << "Compute candidates constants for cc pairs..." << std::endl; - Debug("efficient-e-match") << " Eq class = ["; - outputEqClass( "efficient-e-match", a); - Debug("efficient-e-match") << "]" << std::endl; - outputEqClassInfo("efficient-e-match",eci_a); - for( std::map< Node, std::map< Node, NodePcDispatcher* > >::iterator - it = d_cc_pairs.begin(), end = d_cc_pairs.end(); - it != end; ++it ) { - Debug("efficient-e-match") << " Checking application " << it->first << std::endl; - if( !eci_b->hasFunction(it->first) ) continue; - for( std::map< Node, NodePcDispatcher* >::iterator - itc = it->second.begin(), end = it->second.end(); - itc != end; ++itc ) { - //The constant - Debug("efficient-e-match") << " Checking constant " << a << std::endl; - if(getEqualityEngine()->getRepresentative(itc->first) != a) continue; - SetNode s; - eq::EqClassIterator eqc_iter( b, getEqualityEngine() ); - while( !eqc_iter.isFinished() ){ - Debug("efficient-e-match-debug") << "> look at " << (*eqc_iter) - << std::endl; - if( (*eqc_iter).hasOperator() && (*eqc_iter).getOperator() == it->first ) s.insert(*eqc_iter); - eqc_iter++; - } - - if( s.empty() ) continue; - Debug("efficient-e-match") << " -> Added terms (" << s.size() << "): "; - for( SetNode::const_iterator t=s.begin(), end=s.end(); - t!=end; ++t ){ - Debug("efficient-e-match") << (*t) << " "; - } - Debug("efficient-e-match") << std::endl; - itc->second->send(s); - } - } -} - -void EfficientEMatcher::collectTermsIps( Ips& ips, SetNode & terms ){ - Assert( ips.size() > 0); - return collectTermsIps( ips, terms, ips.size() - 1); -} - -void EfficientEMatcher::collectTermsIps( Ips& ips, SetNode& terms, int index ){ - if( !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!=0 ); // We want to keep the top symbol for the last - SetNode newTerms; - for( SetNode::const_iterator t=terms.begin(), end=terms.end(); - t!=end; ++t ){ - collectParentsTermsIps( *t, f, arg, newTerms, addRep ); - } - terms.swap(newTerms); - - Debug("efficient-e-match-debug") << "> Terms are now: "; - for( SetNode::const_iterator t=terms.begin(), end=terms.end(); - t!=end; ++t ){ - Debug("efficient-e-match-debug") << *t << " "; - } - Debug("efficient-e-match-debug") << std::endl; - - if(index!=0) collectTermsIps( ips, terms, index-1 ); - } -} - -bool EfficientEMatcher::collectParentsTermsIps( Node n, Node f, int arg, SetNode & terms, bool addRep, bool modEq ){ //modEq default true - bool addedTerm = false; - - if( modEq && getEqualityEngine()->hasTerm( n )){ - Assert( getEqualityEngine()->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( n, getEqualityEngine() ); - while( !eqc_iter.isFinished() ){ - Debug("efficient-e-match-debug") << "> look at " << (*eqc_iter) - << std::endl; - 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{ - quantifiers::TermDb* db = d_quantEngine->getTermDatabase(); - //see if parent f exists from argument arg - const std::vector<Node> & parents = db->getParents(n,f,arg); - for( size_t i=0; i<parents.size(); ++i ){ - TNode t = parents[i]; - if(!CandidateGenerator::isLegalCandidate(t)) continue; - if( addRep ) t = getEqualityEngine()->getRepresentative( t ); - terms.insert(t); - addedTerm = true; - } - } - return addedTerm; -} - -void EfficientEMatcher::registerPatternElementPairs2( Node pat, Ips& ips, PpIpsMap & pp_ips_map, NodePcDispatcher* npc ){ - Assert( pat.hasOperator() ); - //add information for possible pp-pair - ips.push_back( std::pair< Node, int >( pat.getOperator(), 0 ) ); //0 is just a dumb value - - for( int i=0; i<(int)pat.getNumChildren(); i++ ){ - if( pat[i].getKind()==INST_CONSTANT ){ - ips.back().second = i; - pp_ips_map[ pat[i] ].push_back( make_pair( pat.getOperator(), Ips( ips ) ) ); - } - } - - for( int i=0; i<(int)pat.getNumChildren(); i++ ){ - if( pat[i].getKind()==APPLY_UF ){ - ips.back().second = i; - registerPatternElementPairs2( pat[i], ips, pp_ips_map, npc ); - Debug("pattern-element-opt") << "Found pc-pair ( " << pat.getOperator() << ", " << pat[i].getOperator() << " )" << std::endl; - Debug("pattern-element-opt") << " Path = "; - outputIps( "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() ] - .push_back( make_pair(npc,Ips(ips)) ); - } - } - ips.pop_back(); -} - -void EfficientEMatcher::registerPatternElementPairs( Node pat, PpIpsMap & pp_ips_map, - NodePcDispatcher* npc, - NodePpDispatcher* npp){ - Ips ips; - registerPatternElementPairs2( pat, ips, pp_ips_map, npc ); - for( PpIpsMap::iterator it = pp_ips_map.begin(); it != pp_ips_map.end(); ++it ){ - // for each variable construct all the pp-pair - for( size_t j=0; j<it->second.size(); j++ ){ - for( size_t k=j+1; k<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 = "; - outputIps( "pattern-element-opt", it->second[j].second ); - Debug("pattern-element-opt") << " and "; - outputIps( "pattern-element-opt", it->second[k].second ); - Debug("pattern-element-opt") << std::endl; - d_pp_pairs[ it->second[j].first ][ it->second[k].first ] - .push_back( make_triple( npp, it->second[j].second, it->second[k].second )); - } - } - } -}; - -void findPpSite(Node pat, EfficientEMatcher::Ips& ips, EfficientEMatcher::PpIpsMap & pp_ips_map){ - Assert( pat.getKind()==APPLY_UF ); - //add information for possible pp-pair - - ips.push_back( make_pair( pat.getOperator(), 0) ); - for( size_t i=0; i<pat.getNumChildren(); i++ ){ - if( pat[i].getKind()==INST_CONSTANT ){ - ips.back().second = i; - pp_ips_map[ pat[i] ].push_back( make_pair( pat.getOperator(), EfficientEMatcher::Ips( ips ) ) ); - } - } - - for( size_t i=0; i<pat.getNumChildren(); i++ ){ - if( pat[i].getKind()==APPLY_UF ){ - ips.back().second = i; - findPpSite( pat[i], ips, pp_ips_map ); - } - } - ips.pop_back(); -} - -void EfficientEMatcher::combineMultiPpIpsMap(PpIpsMap & pp_ips_map, MultiPpIpsMap & multi_pp_ips_map, - EfficientHandler& eh, size_t index2,const std::vector<Node> & pats){ - hash_map<size_t,NodePpDispatcher*> npps; - for( PpIpsMap::iterator it = pp_ips_map.begin(); it != pp_ips_map.end(); ++it ){ - MultiPpIpsMap::iterator mit = multi_pp_ips_map.find(it->first); - if(mit == multi_pp_ips_map.end()) continue; - // for each variable construct all the pp-pair - // j the last pattern treated - for( std::vector< std::pair< Node, Ips > >::iterator j=it->second.begin(), jend = it->second.end() ; - j != jend; ++j){ - // k one of the previous one - for( std::vector< triple< size_t, Node, Ips > >::iterator k=mit->second.begin(), kend = mit->second.end() ; - k != kend; ++k){ - //found a pp-pair - Debug("pattern-element-opt") << "Found multi-pp-pair ( " << j->first - << ", " << k->second << " in "<< k->first - << " )" << std::endl; - Debug("pattern-element-opt") << " Paths = "; - outputIps( "pattern-element-opt", j->second ); - Debug("pattern-element-opt") << " and "; - outputIps( "pattern-element-opt", k->third ); - Debug("pattern-element-opt") << std::endl; - NodePpDispatcher* dispatcher; - hash_map<size_t,NodePpDispatcher*>::iterator inpp = npps.find(k->first); - if( inpp != npps.end() ) dispatcher = inpp->second; - else{ - dispatcher = new NodePpDispatcher(); -#ifdef CVC4_DEBUG - dispatcher->pat1 = pats[index2]; - dispatcher->pat2 = pats[k->first]; -#endif - dispatcher->addPpDispatcher(&eh,index2,k->first); - }; - d_pp_pairs[ j->first ][ k->second ].push_back( make_triple( dispatcher, j->second, k->third )); - } - } - } - - /** Put pp_ips_map to multi_pp_ips_map */ - for( PpIpsMap::iterator it = pp_ips_map.begin(); it != pp_ips_map.end(); ++it ){ - for( std::vector< std::pair< Node, Ips > >::iterator j=it->second.begin(), jend = it->second.end() ; - j != jend; ++j){ - multi_pp_ips_map[it->first].push_back(make_triple(index2, j->first, j->second)); - } - } - -} - - -void EfficientEMatcher::registerEfficientHandler( EfficientHandler& handler, - const std::vector< Node > & pats ){ - Assert(pats.size() > 0); - - MultiPpIpsMap multi_pp_ips_map; - PpIpsMap pp_ips_map; - //In a multi-pattern Pattern that is only a variable are specials, - //if the variable appears in another pattern, it can be discarded. - //Otherwise new term of this term can be candidate. So we stock them - //here before adding them. - std::vector< size_t > patVars; - - Debug("pattern-element-opt") << "Register patterns" << pats << std::endl; - for(size_t i = 0; i < pats.size(); ++i){ - if( pats[i].getKind() == kind::INST_CONSTANT){ - patVars.push_back(i); - continue; - } - //to complete - if( pats[i].getKind() == kind::NOT && pats[i][0].getKind() == kind::EQUAL){ - Node cst = NodeManager::currentNM()->mkConst<bool>(false); - TNode op = pats[i][0].getOperator(); - if(d_cc_pairs[op][cst] == NULL){ - d_cc_pairs[op][cst] = new NodePcDispatcher(); - } - d_cc_pairs[op][cst]->addPcDispatcher(&handler,i); - continue; - } - //end to complete - Debug("pattern-element-opt") << " Register candidate generator..." << pats[i] << std::endl; - /* Has the pattern already been seen */ - if( d_pat_cand_gens.find( pats[i] )==d_pat_cand_gens.end() ){ - NodePcDispatcher* npc = new NodePcDispatcher(); - NodePpDispatcher* npp = new NodePpDispatcher(); -#ifdef CVC4_DEBUG - npc->pat = pats[i]; - npp->pat1 = pats[i]; - npp->pat2 = pats[i]; -#endif - d_pat_cand_gens[pats[i]] = make_pair(npc,npp); - registerPatternElementPairs( pats[i], pp_ips_map, npc, npp ); - }else{ - Ips ips; - findPpSite(pats[i],ips,pp_ips_map); - } - //Has the top operator already been seen */ - TNode op = pats[i].getOperator(); - d_pat_cand_gens[pats[i]].first->addPcDispatcher(&handler,i); - d_pat_cand_gens[pats[i]].second->addPpDispatcher(&handler,i,i); - d_cand_gens[op].addNewTermDispatcher(&handler,i); - - combineMultiPpIpsMap(pp_ips_map,multi_pp_ips_map,handler,i,pats); - - pp_ips_map.clear(); - } - - for(size_t i = 0; i < patVars.size(); ++i){ - TNode var = pats[patVars[i]]; - Assert( var.getKind() == kind::INST_CONSTANT ); - if( multi_pp_ips_map.find(var) != multi_pp_ips_map.end() ){ - //The variable appear in another pattern, skip it - continue; - }; - d_cand_gen_types[var.getType()].addNewTermDispatcher(&handler,patVars[i]); - } - - //take all terms from the uf term db and add to candidate generator - if( pats[0].getKind() == kind::INST_CONSTANT ){ - TypeNode ty = pats[0].getType(); - rrinst::CandidateGenerator* cg = new GenericCandidateGeneratorClasses(d_quantEngine); - cg->reset(Node::null()); - TNode c; - SetNode ele; - while( !(c = cg->getNextCandidate()).isNull() ){ - if( c.getType() == ty ) ele.insert(c); - } - if( !ele.empty() ){ - if(Debug.isOn("efficient-e-match-stats")){ - Debug("efficient-e-match-stats") << "pattern " << pats << " initialized with " << ele.size() << " terms"<< std::endl; - } - handler.addMonoCandidate(ele, 0); - } - - } else if( pats[0].getKind() == kind::NOT && pats[0][0].getKind() == kind::EQUAL){ - Node cst = NodeManager::currentNM()->mkConst<bool>(false); - TNode op = pats[0][0].getOperator(); - cst = getEqualityEngine()->getRepresentative(cst); - SetNode ele; - eq::EqClassIterator eqc_iter( cst, getEqualityEngine() ); - while( !eqc_iter.isFinished() ){ - Debug("efficient-e-match-debug") << "> look at " << (*eqc_iter) - << std::endl; - if( (*eqc_iter).hasOperator() && (*eqc_iter).getOperator() == op ) ele.insert(*eqc_iter); - eqc_iter++; - } - if( !ele.empty() ){ - if(Debug.isOn("efficient-e-match-stats")){ - Debug("efficient-e-match-stats") << "pattern " << pats << " initialized with " << ele.size() << " terms"<< std::endl; - } - handler.addMonoCandidate(ele, 0); - } - - } else { - TermDb* db = d_quantEngine->getTermDatabase(); - Node op = db->getOperator( pats[0] ); - if(db->d_op_map[op].begin() != db->d_op_map[op].end()){ - SetNode ele; - ele.insert(db->d_op_map[op].begin(), db->d_op_map[op].end()); - if(Debug.isOn("efficient-e-match-stats")){ - Debug("efficient-e-match-stats") << "pattern " << pats << " initialized with " << ele.size() << " terms"<< std::endl; - } - handler.addMonoCandidate(ele, 0); - } - } - Debug("efficient-e-match") << "Done." << std::endl; -} - -void EfficientEMatcher::outputEqClass( const char* c, Node n ){ - if( getEqualityEngine()->hasTerm( n ) ){ - eq::EqClassIterator eqc_iter( getEqualityEngine()->getRepresentative( n ), - getEqualityEngine() ); - bool firstTime = true; - while( !eqc_iter.isFinished() ){ - if( !firstTime ){ Debug(c) << ", "; } - Debug(c) << (*eqc_iter); - firstTime = false; - eqc_iter++; - } - }else{ - Debug(c) << n; - } -} - -void EfficientEMatcher::outputIps( const char* c, Ips& ips ){ - for( int i=0; i<(int)ips.size(); i++ ){ - if( i>0 ){ Debug( c ) << "."; } - Debug( c ) << ips[i].first << "." << ips[i].second; - } -} - - -} /* namespace theory */ -} /* namespace cvc4 */ diff --git a/src/theory/rewriterules/efficient_e_matching.h b/src/theory/rewriterules/efficient_e_matching.h deleted file mode 100644 index b02d465fc..000000000 --- a/src/theory/rewriterules/efficient_e_matching.h +++ /dev/null @@ -1,450 +0,0 @@ -/********************* */ -/*! \file efficient_e_matching.h - ** \verbatim - ** Original author: Andrew Reynolds - ** Major contributors: Morgan Deters - ** Minor contributors (to current version): none - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief efficient e-matching - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__EFFICIENT_E_MATCHING_H -#define __CVC4__EFFICIENT_E_MATCHING_H - -#include "expr/node.h" -#include "context/context.h" -#include "context/context_mm.h" -#include "context/cdchunk_list.h" - -#include "util/statistics_registry.h" -#include "util/ntuple.h" -#include "context/cdqueue.h" -#include "context/cdo.h" - -#include "theory/uf/equality_engine.h" - -namespace CVC4 { -namespace theory { - -class QuantifiersEngine; - -namespace quantifiers{ - class TermDb; -} - -class EfficientEMatcher; -class HandlerPcDispatcher; -class HandlerPpDispatcher; - -typedef std::set<Node> SetNode; - -template<class T> -class CleanUpPointer{ -public: - inline void operator()(T** e){ - delete(*e); - }; -}; - -class EfficientHandler{ -public: - typedef std::pair< Node, size_t > MonoCandidate; - typedef std::pair< MonoCandidate, MonoCandidate > MultiCandidate; - typedef std::pair< SetNode, size_t > MonoCandidates; - typedef std::pair< MonoCandidates, MonoCandidates > MultiCandidates; -private: - /* Queue of candidates */ - typedef context::CDQueue< MonoCandidates *, CleanUpPointer<MonoCandidates> > MonoCandidatesQueue; - typedef context::CDQueue< MultiCandidates *, CleanUpPointer<MultiCandidates> > MultiCandidatesQueue; - MonoCandidatesQueue d_monoCandidates; - typedef SetNode::iterator SetNodeIter; - context::CDO<SetNodeIter> d_si; - context::CDO<bool> d_mono_not_first; - - MonoCandidatesQueue d_monoCandidatesNewTerm; - context::CDO<SetNodeIter> d_si_new_term; - context::CDO<bool> d_mono_not_first_new_term; - - - MultiCandidatesQueue d_multiCandidates; - context::CDO<SetNodeIter> d_si1; - context::CDO<SetNodeIter> d_si2; - context::CDO<bool> d_multi_not_first; - - - friend class EfficientEMatcher; - friend class HandlerPcDispatcher; - friend class HandlerPpDispatcher; - friend class HandlerNewTermDispatcher; -protected: - void addMonoCandidate(SetNode & s, size_t index){ - Assert(!s.empty()); - d_monoCandidates.push(new MonoCandidates(s,index)); - } - void addMonoCandidateNewTerm(SetNode & s, size_t index){ - Assert(!s.empty()); - d_monoCandidatesNewTerm.push(new MonoCandidates(s,index)); - } - void addMultiCandidate(SetNode & s1, size_t index1, SetNode & s2, size_t index2){ - Assert(!s1.empty() && !s2.empty()); - d_multiCandidates.push(new MultiCandidates(MonoCandidates(s1,index1), - MonoCandidates(s2,index2))); - } -public: - EfficientHandler(context::Context * c): - //false for d_mono_not_first beacause its the default constructor - d_monoCandidates(c), d_si(c), d_mono_not_first(c,false), - d_monoCandidatesNewTerm(c), d_si_new_term(c), - d_mono_not_first_new_term(c,false), - d_multiCandidates(c) , d_si1(c), d_si2(c), d_multi_not_first(c,false) {}; - - bool getNextMonoCandidate(MonoCandidate & candidate){ - if(d_monoCandidates.empty()) return false; - const MonoCandidates * front = d_monoCandidates.front(); - SetNodeIter si_tmp; - if(!d_mono_not_first){ - Assert(front->first.begin() != front->first.end()); - d_mono_not_first = true; - si_tmp=front->first.begin(); - }else{ - si_tmp = d_si; - ++si_tmp; - }; - if(si_tmp != front->first.end()){ - candidate.first = (*si_tmp); - candidate.second = front->second; - d_si = si_tmp; - Debug("efficienthandler") << "Mono produces " << candidate.first << " for " << candidate.second << std::endl; - return true; - }; - d_monoCandidates.pop(); - d_mono_not_first = false; - return getNextMonoCandidate(candidate); - }; - - bool getNextMonoCandidateNewTerm(MonoCandidate & candidate){ - if(d_monoCandidatesNewTerm.empty()) return false; - const MonoCandidates * front = d_monoCandidatesNewTerm.front(); - SetNodeIter si_tmp; - if(!d_mono_not_first_new_term){ - Assert(front->first.begin() != front->first.end()); - d_mono_not_first_new_term = true; - si_tmp=front->first.begin(); - }else{ - si_tmp = d_si_new_term; - ++si_tmp; - }; - if(si_tmp != front->first.end()){ - candidate.first = (*si_tmp); - candidate.second = front->second; - d_si_new_term = si_tmp; - Debug("efficienthandler") << "Mono produces " << candidate.first << " for " << candidate.second << std::endl; - return true; - }; - d_monoCandidatesNewTerm.pop(); - d_mono_not_first_new_term = false; - return getNextMonoCandidateNewTerm(candidate); - }; - - bool getNextMultiCandidate(MultiCandidate & candidate){ - if(d_multiCandidates.empty()) return false; - const MultiCandidates* front = d_multiCandidates.front(); - SetNodeIter si1_tmp; - SetNodeIter si2_tmp; - if(!d_multi_not_first){ - Assert(front->first.first.begin() != front->first.first.end()); - Assert(front->second.first.begin() != front->second.first.end()); - si1_tmp = front->first.first.begin(); - si2_tmp = front->second.first.begin(); - }else{ - si1_tmp = d_si1; - si2_tmp = d_si2; - ++si2_tmp; - }; - if(si2_tmp != front->second.first.end()){ - candidate.first.first = *si1_tmp; - candidate.first.second = front->first.second; - candidate.second.first = *si2_tmp; - candidate.second.second = front->second.second; - if(!d_multi_not_first){d_si1 = si1_tmp; d_multi_not_first = true; }; - d_si2 = si2_tmp; - Debug("efficienthandler") << "Multi1 produces " - << candidate.first.first << " for " - << candidate.first.second << " and " - << candidate.second.first << " for " - << candidate.second.second << " and " - << std::endl; - return true; - }; // end of the second set - si2_tmp = front->second.first.begin(); - ++si1_tmp; - if(si1_tmp != front->first.first.end()){ - candidate.first.first = *si1_tmp; - candidate.first.second = front->first.second; - candidate.second.first = *si2_tmp; - candidate.second.second = front->second.second; - d_si1 = si1_tmp; - d_si2 = si2_tmp; - Debug("efficienthandler") << "Multi2 produces " - << candidate.first.first << " for " - << candidate.first.second << " and " - << candidate.second.first << " for " - << candidate.second.second << " and " - << std::endl; - return true; - }; // end of the first set - d_multiCandidates.pop(); - d_multi_not_first = false; - return getNextMultiCandidate(candidate); - } -}; - -class PcDispatcher{ -public: - virtual ~PcDispatcher(){}; - /* Send the node to the dispatcher */ - virtual void send(SetNode & s) = 0; -}; - - -class HandlerPcDispatcher: public PcDispatcher{ - EfficientHandler* d_handler; - size_t d_index; -public: - HandlerPcDispatcher(EfficientHandler* handler, size_t index): - d_handler(handler), d_index(index) {}; - void send(SetNode & s){ - d_handler->addMonoCandidate(s,d_index); - } -}; - - -/** All the dispatcher that correspond to this node */ -class NodePcDispatcher: public PcDispatcher{ -#ifdef CVC4_DEBUG -public: - Node pat; -#endif/* CVC4_DEBUG*/ -private: - std::vector<HandlerPcDispatcher> d_dis; -public: - void send(SetNode & s){ - Assert(!s.empty()); - for(std::vector<HandlerPcDispatcher>::iterator i = d_dis.begin(), end = d_dis.end(); - i != end; ++i){ - (*i).send(s); - } - } - void addPcDispatcher(EfficientHandler* handler, size_t index){ - d_dis.push_back(HandlerPcDispatcher(handler,index)); - } -}; - - -class HandlerNewTermDispatcher: public PcDispatcher{ - EfficientHandler* d_handler; - size_t d_index; -public: - HandlerNewTermDispatcher(EfficientHandler* handler, size_t index): - d_handler(handler), d_index(index) {}; - void send(SetNode & s){ - d_handler->addMonoCandidateNewTerm(s,d_index); - } -}; - -/** All the dispatcher that correspond to this node */ -class NodeNewTermDispatcher: public PcDispatcher{ -#ifdef CVC4_DEBUG -public: - Node pat; -#endif/* CVC4_DEBUG*/ -private: - std::vector<HandlerNewTermDispatcher> d_dis; -public: - void send(SetNode & s){ - Assert(!s.empty()); - for(std::vector<HandlerNewTermDispatcher>::iterator i = d_dis.begin(), end = d_dis.end(); - i != end; ++i){ - (*i).send(s); - } - } - void addNewTermDispatcher(EfficientHandler* handler, size_t index){ - d_dis.push_back(HandlerNewTermDispatcher(handler,index)); - } -}; - -class PpDispatcher{ -public: - virtual ~PpDispatcher(){}; - /* Send the node to the dispatcher */ - virtual void send(SetNode & s1, SetNode & s2, SetNode & sinter) = 0; -}; - - -class HandlerPpDispatcher: public PpDispatcher{ - EfficientHandler* d_handler; - size_t d_index1; - size_t d_index2; -public: - HandlerPpDispatcher(EfficientHandler* handler, size_t index1, size_t index2): - d_handler(handler), d_index1(index1), d_index2(index2) {}; - void send(SetNode & s1, SetNode & s2, SetNode & sinter){ - if(d_index1 == d_index2){ - if(!sinter.empty()) - d_handler->addMonoCandidate(sinter,d_index1); - }else{ - d_handler->addMultiCandidate(s1,d_index1,s2,d_index2); - } - } -}; - - -/** All the dispatcher that correspond to this node */ -class NodePpDispatcher: public PpDispatcher{ -#ifdef CVC4_DEBUG -public: - Node pat1; - Node pat2; -#endif/* CVC4_DEBUG */ -private: - std::vector<HandlerPpDispatcher> d_dis; - void send(SetNode & s1, SetNode & s2, SetNode & inter){ - for(std::vector<HandlerPpDispatcher>::iterator i = d_dis.begin(), end = d_dis.end(); - i != end; ++i){ - (*i).send(s1,s2,inter); - } - } -public: - void send(SetNode & s1, SetNode & s2){ - // can be done in HandlerPpDispatcher lazily - Assert(!s1.empty() && !s2.empty()); - SetNode inter; - std::set_intersection( s1.begin(), s1.end(), s2.begin(), s2.end(), - std::inserter( inter, inter.begin() ) ); - send(s1,s2,inter); - } - void addPpDispatcher(EfficientHandler* handler, size_t index1, size_t index2){ - d_dis.push_back(HandlerPpDispatcher(handler,index1,index2)); - } -}; - -//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, quantifiers::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 EfficientEMatcher{ -protected: - /** reference to the quantifiers engine */ - QuantifiersEngine* d_quantEngine; -public: - EfficientEMatcher(CVC4::theory::QuantifiersEngine* qe); - ~EfficientEMatcher() { - for(std::map< Node, std::pair<NodePcDispatcher*, NodePpDispatcher*> >::iterator - i = d_pat_cand_gens.begin(), end = d_pat_cand_gens.end(); - i != end; i++){ - delete(i->second.first); - delete(i->second.second); - } - } - /** get equality engine we are using */ - eq::EqualityEngine* getEqualityEngine(); -private: - //information for each equivalence class - std::map< Node, EqClassInfo* > d_eqc_ops; -public: - /** 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 ); - typedef std::vector< std::pair< Node, int > > Ips; - typedef std::map< Node, std::vector< std::pair< Node, Ips > > > PpIpsMap; - typedef std::map< Node, std::vector< triple< size_t, Node, Ips > > > MultiPpIpsMap; -private: - /** 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::vector< std::pair< NodePcDispatcher*, Ips > > > > d_pc_pairs; - /** Parent/Parent Pairs (for efficient E-matching) */ - std::map< Node, std::map< Node, std::vector< triple< NodePpDispatcher*, Ips, Ips > > > > d_pp_pairs; - /** Constants/Child Pairs - So, for example, if we have the pattern f( x ) = c, then d_pc_pairs[f][c] = ..., pcdispatcher, ... - */ - //TODO constant in pattern can use the same thing just add an Ips - std::map< Node, std::map< Node, NodePcDispatcher* > > d_cc_pairs; - /** list of all candidate generators for each operator */ - std::map< Node, NodeNewTermDispatcher > d_cand_gens; - /** list of all candidate generators for each type */ - std::map< TypeNode, NodeNewTermDispatcher > d_cand_gen_types; - /** map from patterns to candidate generators */ - std::map< Node, std::pair<NodePcDispatcher*, NodePpDispatcher*> > d_pat_cand_gens; - /** helper functions */ - void registerPatternElementPairs2( Node pat, Ips& ips, - PpIpsMap & pp_ips_map, NodePcDispatcher* npc); - void registerPatternElementPairs( Node pat, PpIpsMap & pp_ips_map, - NodePcDispatcher* npc, NodePpDispatcher* npp); - /** find the pp-pair between pattern inside multi-pattern*/ - void combineMultiPpIpsMap(PpIpsMap & pp_ips_map, MultiPpIpsMap & multi_pp_ips_map, - EfficientHandler& eh, size_t index2, - const std::vector<Node> & pats); //pats for debug - /** compute candidates for pc pairs */ - void computeCandidatesPcPairs( Node a, EqClassInfo*, Node b, EqClassInfo* ); - /** compute candidates for pp pairs */ - void computeCandidatesPpPairs( Node a, EqClassInfo*, Node b, EqClassInfo* ); - /** compute candidates for cc pairs */ - void computeCandidatesConstants( Node a, EqClassInfo*, Node b, EqClassInfo* ); - /** collect terms based on inverted path string */ - void collectTermsIps( Ips& ips, SetNode& terms, int index); - bool collectParentsTermsIps( Node n, Node f, int arg, SetNode& terms, bool addRep, bool modEq = true ); -public: - void collectTermsIps( Ips& ips, SetNode& terms); -public: - void registerEfficientHandler( EfficientHandler& eh, const std::vector<Node> & pat ); -public: - void newTerms(SetNode& s); -public: - /** output eq class */ - void outputEqClass( const char* c, Node n ); - /** output inverted path string */ - void outputIps( const char* c, Ips& ips ); -};/* class EfficientEMatcher */ - - -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ - -#endif /* __CVC4__EFFICIENT_E_MATCHING_H */ diff --git a/src/theory/rewriterules/kinds b/src/theory/rewriterules/kinds deleted file mode 100644 index 490c8f100..000000000 --- a/src/theory/rewriterules/kinds +++ /dev/null @@ -1,37 +0,0 @@ -# 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 "general 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/options b/src/theory/rewriterules/options deleted file mode 100644 index 285e489be..000000000 --- a/src/theory/rewriterules/options +++ /dev/null @@ -1,14 +0,0 @@ -# -# Option specification file for CVC4 -# See src/options/base_options for a description of this file format -# - -module REWRITE_RULES "theory/rewriterules/options.h" Rewrite Rules - -option efficientEMatching --efficient-e-matching bool :default false - use efficient E-matching (only for rewrite rules) - -option rewriteRulesAsAxioms --rewrite-rules-as-axioms bool :default false - whether to convert rewrite rules to usual axioms (for debugging only) - -endmodule diff --git a/src/theory/rewriterules/rr_candidate_generator.cpp b/src/theory/rewriterules/rr_candidate_generator.cpp deleted file mode 100644 index 3ebb3547c..000000000 --- a/src/theory/rewriterules/rr_candidate_generator.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/********************* */ -/*! \file rr_candidate_generator.cpp - ** \verbatim - ** Original author: Morgan Deters - ** Major contributors: Andrew Reynolds - ** Minor contributors (to current version): none - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief Implementation of rr candidate generator class - **/ - -#include "theory/rewriterules/rr_candidate_generator.h" -#include "theory/theory_engine.h" -#include "theory/uf/theory_uf.h" -#include "theory/quantifiers/term_database.h" - -using namespace std; -using namespace CVC4; -using namespace CVC4::kind; -using namespace CVC4::context; -using namespace CVC4::theory; -using namespace CVC4::theory::rrinst; - -GenericCandidateGeneratorClasses::GenericCandidateGeneratorClasses(QuantifiersEngine * qe) : d_qe(qe){ - d_master_can_gen = new eq::rrinst::CandidateGeneratorTheoryEeClasses(d_qe->getMasterEqualityEngine()); -} - -GenericCandidateGeneratorClasses::~GenericCandidateGeneratorClasses(){ - delete d_master_can_gen; -} - -void GenericCandidateGeneratorClasses::resetInstantiationRound(){ - d_master_can_gen->resetInstantiationRound(); -} - -void GenericCandidateGeneratorClasses::reset(TNode eqc){ - d_master_can_gen->reset(eqc); -} - -TNode GenericCandidateGeneratorClasses::getNextCandidate(){ - return d_master_can_gen->getNextCandidate(); -} - - -GenericCandidateGeneratorClass::GenericCandidateGeneratorClass(QuantifiersEngine * qe): d_qe(qe) { - d_master_can_gen = new eq::rrinst::CandidateGeneratorTheoryEeClass(d_qe->getMasterEqualityEngine()); -} - -GenericCandidateGeneratorClass::~GenericCandidateGeneratorClass(){ - delete d_master_can_gen; -} - -void GenericCandidateGeneratorClass::resetInstantiationRound(){ - d_master_can_gen->resetInstantiationRound(); -} - -void GenericCandidateGeneratorClass::reset(TNode eqc){ - d_master_can_gen->reset(eqc); -} - -TNode GenericCandidateGeneratorClass::getNextCandidate(){ - return d_master_can_gen->getNextCandidate(); -} - diff --git a/src/theory/rewriterules/rr_candidate_generator.h b/src/theory/rewriterules/rr_candidate_generator.h deleted file mode 100644 index d00c8ab83..000000000 --- a/src/theory/rewriterules/rr_candidate_generator.h +++ /dev/null @@ -1,199 +0,0 @@ -/********************* */ -/*! \file rr_candidate_generator.h - ** \verbatim - ** Original author: Morgan Deters - ** Major contributors: Andrew Reynolds, Francois Bobot - ** Minor contributors (to current version): none - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief rr candidate generator - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__THEORY__REWRITERULES__RR_CANDIDATE_GENERATOR_H -#define __CVC4__THEORY__REWRITERULES__RR_CANDIDATE_GENERATOR_H - -#include "theory/quantifiers_engine.h" -#include "theory/quantifiers/term_database.h" -#include "theory/rewriterules/rr_inst_match.h" - -using namespace CVC4::theory::quantifiers; - -namespace CVC4 { -namespace theory { -namespace eq { - -namespace rrinst{ -typedef CVC4::theory::rrinst::CandidateGenerator CandidateGenerator; - -//New CandidateGenerator. They have a simpler semantic than the old one - -// Just iterate among the equivalence classes -// node::Null() must be given to reset -class CandidateGeneratorTheoryEeClasses : public CandidateGenerator{ -private: - //the equality classes iterator - eq::EqClassesIterator d_eq; - //equalityengine pointer - EqualityEngine* d_ee; -public: - CandidateGeneratorTheoryEeClasses( EqualityEngine * ee): d_ee( ee ){} - ~CandidateGeneratorTheoryEeClasses(){} - void resetInstantiationRound(){}; - void reset( TNode eqc ){ - Assert(eqc.isNull()); - d_eq = eq::EqClassesIterator( d_ee ); - }; //* the argument is not used - TNode getNextCandidate(){ - if( !d_eq.isFinished() ) return (*(d_eq++)); - else return Node::null(); - }; -}; - -// Just iterate among the equivalence class of the given node -// node::Null() *can't* be given to reset -class CandidateGeneratorTheoryEeClass : public CandidateGenerator{ -private: - //instantiator pointer - EqualityEngine* d_ee; - //the equality class iterator - eq::EqClassIterator d_eqc; - /* For the case where the given term doesn't exists in uf */ - Node d_retNode; -public: - CandidateGeneratorTheoryEeClass( EqualityEngine* ee): d_ee( ee ){} - ~CandidateGeneratorTheoryEeClass(){} - void resetInstantiationRound(){}; - void reset( TNode eqc ){ - Assert(!eqc.isNull()); - if( d_ee->hasTerm( eqc ) ){ - /* eqc is in uf */ - eqc = d_ee->getRepresentative( eqc ); - d_eqc = eq::EqClassIterator( eqc, d_ee ); - d_retNode = Node::null(); - }else{ - /* If eqc if not a term known by uf, it is the only one in its - equivalence class. So we will return only it */ - d_retNode = eqc; - d_eqc = eq::EqClassIterator(); - } - }; //* the argument is not used - TNode getNextCandidate(){ - if(d_retNode.isNull()){ - if( !d_eqc.isFinished() ) return (*(d_eqc++)); - else return Node::null(); - }else{ - /* the case where eqc not in uf */ - Node ret = d_retNode; - d_retNode = Node::null(); /* d_eqc.isFinished() must be true */ - return ret; - } - }; -}; - - -} /* namespace rrinst */ -} /* namespace eq */ - -namespace uf{ -namespace rrinst { - -typedef CVC4::theory::rrinst::CandidateGenerator CandidateGenerator; - -class CandidateGeneratorTheoryUfOp : public CandidateGenerator{ -private: - Node d_op; - //instantiator pointer - TermDb* d_tdb; - // Since new term can appears we restrict ourself to the one that - // exists at resetInstantiationRound - size_t d_term_iter_limit; - size_t d_term_iter; -public: - CandidateGeneratorTheoryUfOp(Node op, TermDb* tdb): d_op(op), d_tdb( tdb ){} - ~CandidateGeneratorTheoryUfOp(){} - void resetInstantiationRound(){ - d_term_iter_limit = d_tdb->d_op_map[d_op].size(); - }; - void reset( TNode eqc ){ - Assert(eqc.isNull()); - d_term_iter = 0; - }; //* the argument is not used - TNode getNextCandidate(){ - if( d_term_iter<d_term_iter_limit ){ - TNode n = d_tdb->d_op_map[d_op][d_term_iter]; - ++d_term_iter; - return n; - } else return Node::null(); - }; -}; - -class CandidateGeneratorTheoryUfType : public CandidateGenerator{ -private: - TypeNode d_type; - //instantiator pointer - TermDb* d_tdb; - // Since new term can appears we restrict ourself to the one that - // exists at resetInstantiationRound - size_t d_term_iter_limit; - size_t d_term_iter; -public: - CandidateGeneratorTheoryUfType(TypeNode type, TermDb* tdb): d_type(type), d_tdb( tdb ){} - ~CandidateGeneratorTheoryUfType(){} - void resetInstantiationRound(){ - d_term_iter_limit = d_tdb->d_type_map[d_type].size(); - }; - void reset( TNode eqc ){ - Assert(eqc.isNull()); - d_term_iter = 0; - }; //* the argument is not used - TNode getNextCandidate(){ - if( d_term_iter<d_term_iter_limit ){ - TNode n = d_tdb->d_type_map[d_type][d_term_iter]; - ++d_term_iter; - return n; - } else return Node::null(); - }; -}; - -} /* namespace rrinst */ -} /* namespace uf */ - -class GenericCandidateGeneratorClasses: public rrinst::CandidateGenerator{ - - /** The candidate generators */ - rrinst::CandidateGenerator* d_master_can_gen; - /** QuantifierEngine */ - QuantifiersEngine* d_qe; -public: - GenericCandidateGeneratorClasses(QuantifiersEngine * qe); - ~GenericCandidateGeneratorClasses(); - - void resetInstantiationRound(); - void reset(TNode eqc); - TNode getNextCandidate(); - void lookForNextTheory(); -}; - -class GenericCandidateGeneratorClass: public rrinst::CandidateGenerator{ - - /** The candidate generators */ - rrinst::CandidateGenerator* d_master_can_gen; - /** QuantifierEngine */ - QuantifiersEngine* d_qe; -public: - GenericCandidateGeneratorClass(QuantifiersEngine * qe); - ~GenericCandidateGeneratorClass(); - void resetInstantiationRound(); - void reset(TNode eqc); - TNode getNextCandidate(); -}; - -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ - -#endif /* __CVC4__THEORY__REWRITERULES__RR_CANDIDATE_GENERATOR_H */ diff --git a/src/theory/rewriterules/rr_inst_match.cpp b/src/theory/rewriterules/rr_inst_match.cpp deleted file mode 100644 index 2d7cf85fd..000000000 --- a/src/theory/rewriterules/rr_inst_match.cpp +++ /dev/null @@ -1,1596 +0,0 @@ -/********************* */ -/*! \file rr_inst_match.cpp - ** \verbatim - ** Original author: Morgan Deters - ** Major contributors: Francois Bobot - ** Minor contributors (to current version): Andrew Reynolds - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief Implementation of inst match class - **/ - -#include "theory/quantifiers/inst_match.h" -#include "theory/theory_engine.h" -#include "theory/quantifiers_engine.h" -#include "theory/uf/equality_engine.h" -#include "theory/arrays/theory_arrays.h" -#include "theory/datatypes/theory_datatypes.h" -#include "theory/rewriterules/rr_inst_match.h" -#include "theory/rewriterules/rr_trigger.h" -#include "theory/rewriterules/rr_inst_match_impl.h" -#include "theory/rewriterules/rr_candidate_generator.h" -#include "theory/quantifiers/candidate_generator.h" -#include "theory/rewriterules/efficient_e_matching.h" - -using namespace CVC4; -using namespace CVC4::kind; -using namespace CVC4::context; -using namespace CVC4::theory; -using namespace CVC4::theory::rrinst; -using namespace CVC4::theory::uf::rrinst; -using namespace CVC4::theory::eq::rrinst; - -namespace CVC4{ -namespace theory{ -namespace rrinst{ - - - - -InstMatch::InstMatch() { -} - -InstMatch::InstMatch( InstMatch* m ) { - d_map = m->d_map; -} - -bool InstMatch::setMatch( EqualityQuery* q, TNode v, TNode m, bool & set ){ - std::map< Node, Node >::iterator vn = d_map.find( v ); - if( !m.isNull() && !m.getType().isSubtypeOf( v.getType() ) ){ - set = false; - return false; - }else if( vn==d_map.end() || vn->second.isNull() ){ - set = true; - this->set(v,m); - Debug("matching-debug") << "Add partial " << v << "->" << m << std::endl; - return true; - }else{ - set = false; - return q->areEqual( vn->second, m ); - } -} - -bool InstMatch::setMatch( EqualityQuery* q, TNode v, TNode m ){ - bool set; - return setMatch(q,v,m,set); -} - -bool InstMatch::add( InstMatch& m ){ - for( std::map< Node, Node >::iterator it = m.d_map.begin(); it != m.d_map.end(); ++it ){ - if( !it->second.isNull() ){ - std::map< Node, Node >::iterator itf = d_map.find( it->first ); - if( itf==d_map.end() || itf->second.isNull() ){ - 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( !it->second.isNull() ){ - std::map< Node, Node >::iterator itf = d_map.find( it->first ); - if( itf==d_map.end() || itf->second.isNull() ){ - d_map[ it->first ] = it->second; - }else{ - if( !q->areEqual( it->second, itf->second ) ){ - 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( size_t i=0; i<f[0].getNumChildren(); i++ ){ - Node ic = qe->getTermDatabase()->getInstantiationConstant( f, i ); - if( d_map.find( ic )==d_map.end() ){ - d_map[ ic ] = qe->getTermDatabase()->getFreeVariableForInstConstant( ic ); - } - } -} - -//void InstMatch::makeInternalRepresentative( QuantifiersEngine* qe ){ -// EqualityQueryQuantifiersEngine* eqqe = (EqualityQueryQuantifiersEngine*)qe->getEqualityQuery(); -// for( std::map< Node, Node >::iterator it = d_map.begin(); it != d_map.end(); ++it ){ -// d_map[ it->first ] = eqqe->getInternalRepresentative( it->second ); -// } -//} - -void InstMatch::makeRepresentative( QuantifiersEngine* qe ){ - for( std::map< Node, Node >::iterator it = d_map.begin(); it != d_map.end(); ++it ){ - if( qe->getEqualityQuery()->getEngine()->hasTerm( it->second ) ){ - d_map[ it->first ] = qe->getEqualityQuery()->getEngine()->getRepresentative( it->second ); - } - } -} - -void InstMatch::applyRewrite(){ - for( std::map< Node, Node >::iterator it = d_map.begin(); it != d_map.end(); ++it ){ - it->second = Rewriter::rewrite(it->second); - } -} - -/** get value */ -Node InstMatch::getValue( Node var ) const{ - std::map< Node, Node >::const_iterator it = d_map.find( var ); - if( it!=d_map.end() ){ - return it->second; - }else{ - return Node::null(); - } -} - -Node InstMatch::get( QuantifiersEngine* qe, Node f, int i ) { - return get( qe->getTermDatabase()->getInstantiationConstant( f, i ) ); -} - -void InstMatch::set(TNode var, TNode n){ - Assert( !var.isNull() ); - if (Trace.isOn("inst-match-warn")) { - // For a strange use in inst_match.cpp InstMatchGeneratorSimple::addInstantiations - if( !n.isNull() && !n.getType().isSubtypeOf( var.getType() ) ){ - Trace("inst-match-warn") << quantifiers::TermDb::getInstConstAttr(var) << std::endl; - Trace("inst-match-warn") << var << " " << var.getType() << " " << n << " " << n.getType() << std::endl ; - } - } - Assert( n.isNull() || n.getType().isSubtypeOf( var.getType() ) ); - d_map[var] = n; -} - -void InstMatch::set( QuantifiersEngine* qe, Node f, int i, TNode n ) { - set( qe->getTermDatabase()->getInstantiationConstant( f, i ), n ); -} - - - - - - -typedef CVC4::theory::rrinst::InstMatch InstMatch; -typedef CVC4::theory::inst::CandidateGeneratorQueue CandidateGeneratorQueue; - -template<bool modEq> -class InstMatchTrie2Pairs -{ - typename std::vector< std::vector < typename InstMatchTrie2Gen<modEq>::Tree > > d_data; - InstMatchTrie2Gen<modEq> d_backtrack; -public: - InstMatchTrie2Pairs(context::Context* c, QuantifiersEngine* q, size_t n): - d_backtrack(c,q) { - // resize to a triangle - // - // | * - // | * * - // | * * * - // | -----> i - d_data.resize(n); - for(size_t i=0; i < n; ++i){ - d_data[i].resize(i+1,typename InstMatchTrie2Gen<modEq>::Tree(0)); - } - }; - InstMatchTrie2Pairs(const InstMatchTrie2Pairs &) CVC4_UNDEFINED; - const InstMatchTrie2Pairs & operator =(const InstMatchTrie2Pairs & e) CVC4_UNDEFINED; - /** add match m in the trie, - return true if it was never seen */ - inline bool addInstMatch( size_t i, size_t j, InstMatch& m){ - size_t k = std::min(i,j); - size_t l = std::max(i,j); - return d_backtrack.addInstMatch(m,&(d_data[l][k])); - }; - inline bool addInstMatch( size_t i, InstMatch& m){ - return d_backtrack.addInstMatch(m,&(d_data[i][i])); - }; - -}; - - -// 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 nonunifiable( TNode t0, TNode pat, const std::vector<Node> & vars){ - if(pat.isNull()) return true; - - typedef std::vector<std::pair<TNode,TNode> > tstack; - tstack stack(1,std::make_pair(t0,pat)); // 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; -}; - -/** New things */ -class DumbMatcher: public Matcher{ - void resetInstantiationRound( QuantifiersEngine* qe ){}; - bool reset( TNode n, InstMatch& m, QuantifiersEngine* qe ){ - return false; - } - bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ){ - return false; - } -}; - -class DumbPatMatcher: public PatMatcher{ - void resetInstantiationRound( QuantifiersEngine* qe ){}; - bool reset( InstMatch& m, QuantifiersEngine* qe ){ - return false; - } - bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ){ - return false; - } -}; - - -/* The order of the matching is: - reset arg1, nextMatch arg1, reset arg2, nextMatch arg2, ... */ -ApplyMatcher::ApplyMatcher( Node pat, QuantifiersEngine* qe): d_pattern(pat){ - - //set-up d_variables, d_constants, d_childrens - for( size_t i=0; i< d_pattern.getNumChildren(); ++i ){ - //EqualityQuery* q = qe->getEqualityQuery(d_pattern[i].getType()); - EqualityQuery* q = qe->getEqualityQuery(); - Assert( q != NULL ); - if( quantifiers::TermDb::hasInstConstAttr(d_pattern[i]) ){ - if( d_pattern[i].getKind()==INST_CONSTANT ){ - //It's a variable - d_variables.push_back(make_triple((TNode)d_pattern[i],i,q)); - }else{ - //It's neither a constant argument neither a variable - //we create the matcher for the subpattern - d_childrens.push_back(make_triple(mkMatcher((TNode)d_pattern[i], qe),i,q)); - }; - }else{ - // It's a constant - d_constants.push_back(make_triple((TNode)d_pattern[i],i,q)); - } - } -} - -void ApplyMatcher::resetInstantiationRound( QuantifiersEngine* qe ){ - for( size_t i=0; i< d_childrens.size(); i++ ){ - d_childrens[i].first->resetInstantiationRound( qe ); - } -} - -bool ApplyMatcher::reset(TNode t, InstMatch & m, QuantifiersEngine* qe){ - Debug("matching") << "Matching " << t << " against pattern " << d_pattern << " (" - << m.size() << ")" << std::endl; - - //if t is null - Assert( !t.isNull() ); - Assert( !quantifiers::TermDb::hasInstConstAttr(t) ); - Assert( t.getKind()==d_pattern.getKind() ); - Assert( (t.getKind()!=APPLY_UF && t.getKind()!=APPLY_CONSTRUCTOR) - || t.getOperator()==d_pattern.getOperator() ); - - typedef std::vector< triple<TNode,size_t,EqualityQuery*> >::iterator iterator; - for(iterator i = d_constants.begin(), end = d_constants.end(); - i != end; ++i){ - if( !i->third->areEqual( i->first, t[i->second] ) ){ - Debug("matching-fail") << "Match fail arg: " << i->first << " and " << t[i->second] << std::endl; - //setMatchFail( qe, d_pattern[i], t[i] ); - //ground arguments are not equal - return false; - } - } - - d_binded.clear(); - bool set; - for(iterator i = d_variables.begin(), end = d_variables.end(); - i != end; ++i){ - if( !m.setMatch( i->third, i->first, t[i->second], set) ){ - //match is in conflict - Debug("matching-debug") << "Match in conflict " << t[i->second] << " and " - << i->first << " because " - << m.get(i->first) - << std::endl; - Debug("matching-fail") << "Match fail: " << m.get(i->first) << " and " << t[i->second] << std::endl; - //setMatchFail( qe, partial[0].d_map[d_pattern[i]], t[i] ); - m.erase(d_binded.begin(), d_binded.end()); - return false; - }else{ - if(set){ //The variable has just been set - d_binded.push_back(i->first); - } - } - } - - //now, fit children into match - //we will be requesting candidates for matching terms for each child - d_reps.clear(); - for( size_t i=0; i< d_childrens.size(); i++ ){ - Debug("matching-debug") << "Take the representative of " << t[ d_childrens[i].second ] << std::endl; - Assert( d_childrens[i].third->hasTerm(t[ d_childrens[i].second ]) ); - Node rep = d_childrens[i].third->getRepresentative( t[ d_childrens[i].second ] ); - d_reps.push_back( rep ); - } - - if(d_childrens.size() == 0) return true; - else return getNextMatch(m, qe, true); -} - -bool ApplyMatcher::getNextMatch(InstMatch& m, QuantifiersEngine* qe, bool reset){ - Assert(d_childrens.size() > 0); - const size_t max = d_childrens.size() - 1; - size_t index = reset ? 0 : max; - Assert(d_childrens.size() == d_reps.size()); - while(true){ - if(reset ? - d_childrens[index].first->reset( d_reps[index], m, qe ) : - d_childrens[index].first->getNextMatch( m, qe )){ - if(index==max) return true; - ++index; - reset=true; - }else{ - if(index==0){ - m.erase(d_binded.begin(), d_binded.end()); - return false; - } - --index; - reset=false; - }; - } -} - -bool ApplyMatcher::getNextMatch(InstMatch& m, QuantifiersEngine* qe){ - if(d_childrens.size() == 0){ - m.erase(d_binded.begin(), d_binded.end()); - return false; - } else return getNextMatch(m, qe, false); -} - -/** Proxy that call the sub-matcher on the result return by the given candidate generator */ -template <class CG, class M> -class CandidateGeneratorMatcher: public Matcher{ - /** candidate generator */ - CG d_cg; - /** the sub-matcher */ - M d_m; -public: - CandidateGeneratorMatcher(CG cg, M m): d_cg(cg), d_m(m) - {/* last is Null */}; - void resetInstantiationRound( QuantifiersEngine* qe ){ - d_cg.resetInstantiationRound(); - d_m.resetInstantiationRound(qe); - }; - bool reset( TNode n, InstMatch& m, QuantifiersEngine* qe ){ - d_cg.reset(n); - return findMatch(m,qe); - } - bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ){ - // The sub-matcher has another match - return d_m.getNextMatch(m, qe) || findMatch(m,qe); - } -private: - bool findMatch( InstMatch& m, QuantifiersEngine* qe ){ - // Otherwise try to find a new candidate that has at least one match - while(true){ - TNode n = d_cg.getNextCandidate();//kept somewhere Term-db - Debug("matching") << "GenCand " << n << " (" << this << ")" << std::endl; - if(n.isNull()) return false; - if(d_m.reset(n,m,qe)) return true; - }; - } -}; - -/** Proxy that call the sub-matcher on the result return by the given candidate generator */ -template<class M> -class PatOfMatcher: public PatMatcher{ - M d_m; -public: - inline PatOfMatcher(M m): d_m(m){} - void resetInstantiationRound(QuantifiersEngine* qe){ - d_m.resetInstantiationRound(qe); - } - bool reset(InstMatch& m, QuantifiersEngine* qe){ - return d_m.reset(Node::null(),m,qe); - }; - bool getNextMatch(InstMatch& m, QuantifiersEngine* qe){ - return d_m.getNextMatch(m,qe); - }; -}; - -class ArithMatcher: public Matcher{ -private: - /** for arithmetic matching */ - std::map< Node, Node > d_arith_coeffs; - /** 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(). - */ - /** the variable that are set by this matcher */ - std::vector< TNode > d_binded; /* TNode because the variables are already in d_arith_coeffs */ - Node d_pattern; //for debugging -public: - ArithMatcher(Node pat, QuantifiersEngine* qe); - void resetInstantiationRound( QuantifiersEngine* qe ){}; - bool reset( TNode n, InstMatch& m, QuantifiersEngine* qe ); - bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ); -}; - -/** Match just a variable */ -class VarMatcher: public Matcher{ - Node d_var; - bool d_binded; /* True if the reset bind the variable to some value */ - EqualityQuery* d_q; -public: - VarMatcher(Node var, QuantifiersEngine* qe): d_var(var), d_binded(false){ - //d_q = qe->getEqualityQuery(var.getType()); - d_q = qe->getEqualityQuery(); - } - void resetInstantiationRound( QuantifiersEngine* qe ){}; - bool reset( TNode n, InstMatch& m, QuantifiersEngine* qe ){ - if(!m.setMatch( d_q, d_var, n, d_binded )){ - //match is in conflict - Debug("matching-fail") << "Match fail: " << m.get(d_var) - << " and " << n << std::endl; - return false; - } else return true; - }; - bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ){ - //match is in conflict - if (d_binded) m.erase(d_var); - return false; - } -}; - -template <class M, class Test > -class TestMatcher: public Matcher{ - M d_m; - Test d_test; -public: - inline TestMatcher(M m, Test test): d_m(m), d_test(test){} - inline void resetInstantiationRound(QuantifiersEngine* qe){ - d_m.resetInstantiationRound(qe); - } - inline bool reset(TNode n, InstMatch& m, QuantifiersEngine* qe){ - return d_test(n) && d_m.reset(n, m, qe); - } - inline bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ){ - return d_m.getNextMatch(m, qe); - } -}; - -class LegalOpTest/*: public unary_function<TNode,bool>*/ { - Node d_op; -public: - inline LegalOpTest(Node op): d_op(op){} - inline bool operator() (TNode n) { - return - CandidateGenerator::isLegalCandidate(n) && - // ( // n.getKind()==SELECT || n.getKind()==STORE || - // n.getKind()==APPLY_UF || n.getKind()==APPLY_CONSTRUCTOR) && - n.hasOperator() && - n.getOperator()==d_op; - }; -}; - -class LegalKindTest/* : public unary_function<TNode,bool>*/ { - Kind d_kind; -public: - inline LegalKindTest(Kind kind): d_kind(kind){} - inline bool operator() (TNode n) { - return - CandidateGenerator::isLegalCandidate(n) && - n.getKind()==d_kind; - }; -}; - -class LegalTypeTest/* : public unary_function<TNode,bool>*/ { - TypeNode d_type; -public: - inline LegalTypeTest(TypeNode type): d_type(type){} - inline bool operator() (TNode n) { - return - CandidateGenerator::isLegalCandidate(n) && - n.getType()==d_type; - }; -}; - -class LegalTest/* : public unary_function<TNode,bool>*/ { -public: - inline bool operator() (TNode n) { - return CandidateGenerator::isLegalCandidate(n); - }; -}; - -size_t numFreeVar(TNode t){ - size_t n = 0; - for( size_t i=0, size =t.getNumChildren(); i < size; ++i ){ - if( quantifiers::TermDb::hasInstConstAttr(t[i]) ){ - if( t[i].getKind()==INST_CONSTANT ){ - //variable - ++n; - }else{ - //neither variable nor constant - n += numFreeVar(t[i]); - } - } - } - return n; -} - -class OpMatcher: public Matcher{ - /* The matcher */ - typedef ApplyMatcher AuxMatcher3; - typedef TestMatcher< AuxMatcher3, LegalOpTest > AuxMatcher2; - typedef CandidateGeneratorMatcher< CandidateGeneratorTheoryEeClass, AuxMatcher2> AuxMatcher1; - AuxMatcher1 d_cgm; - static inline AuxMatcher1 createCgm(Node pat, QuantifiersEngine* qe){ - Assert( pat.getKind() == kind::APPLY_UF ); - /** In reverse order of matcher sequence */ - AuxMatcher3 am3(pat,qe); - /** Keep only the one that have the good operator */ - AuxMatcher2 am2(am3,LegalOpTest(pat.getOperator())); - /** Iter on the equivalence class of the given term */ - uf::TheoryUF* uf = static_cast<uf::TheoryUF *>(qe->getTheoryEngine()->theoryOf( theory::THEORY_UF )); - eq::EqualityEngine* ee = static_cast<eq::EqualityEngine*>(uf->getEqualityEngine()); - CandidateGeneratorTheoryEeClass cdtUfEq(ee); - /* Create a matcher from the candidate generator */ - AuxMatcher1 am1(cdtUfEq,am2); - return am1; - } - size_t d_num_var; - Node d_pat; -public: - OpMatcher( Node pat, QuantifiersEngine* qe ): - d_cgm(createCgm(pat, qe)),d_num_var(numFreeVar(pat)), - d_pat(pat) {} - - void resetInstantiationRound( QuantifiersEngine* qe ){ - d_cgm.resetInstantiationRound(qe); - }; - bool reset( TNode t, InstMatch& m, QuantifiersEngine* qe ){ - // size_t m_size = m.d_map.size(); - // if(m_size == d_num_var){ - // uf::EqualityEngine<uf::TheoryUF::NotifyClass>* ee = (static_cast<uf::TheoryUF*>(qe->getTheoryEngine()->theoryOf( theory::THEORY_UF )))->getEqualityEngine(); - // std::cout << "!"; - // return ee->areEqual(m.subst(d_pat),t); - // }else{ - // std::cout << m.d_map.size() << std::endl; - return d_cgm.reset(t, m, qe); - // } - } - bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ){ - return d_cgm.getNextMatch(m, qe); - } -}; - -class DatatypesMatcher: public Matcher{ - /* The matcher */ - typedef ApplyMatcher AuxMatcher3; - typedef TestMatcher< AuxMatcher3, LegalOpTest > AuxMatcher2; - typedef CandidateGeneratorMatcher< CandidateGeneratorTheoryEeClass, AuxMatcher2> AuxMatcher1; - AuxMatcher1 d_cgm; - static inline AuxMatcher1 createCgm(Node pat, QuantifiersEngine* qe){ - Assert( pat.getKind() == kind::APPLY_CONSTRUCTOR, - "For datatypes only constructor are accepted in pattern" ); - /** In reverse order of matcher sequence */ - AuxMatcher3 am3(pat,qe); - /** Keep only the one that have the good operator */ - AuxMatcher2 am2(am3,LegalOpTest(pat.getOperator())); - /** Iter on the equivalence class of the given term */ - datatypes::TheoryDatatypes* dt = static_cast<datatypes::TheoryDatatypes *>(qe->getTheoryEngine()->theoryOf( theory::THEORY_DATATYPES )); - eq::EqualityEngine* ee = static_cast<eq::EqualityEngine*>(dt->getEqualityEngine()); - CandidateGeneratorTheoryEeClass cdtDtEq(ee); - /* Create a matcher from the candidate generator */ - AuxMatcher1 am1(cdtDtEq,am2); - return am1; - } - Node d_pat; -public: - DatatypesMatcher( Node pat, QuantifiersEngine* qe ): - d_cgm(createCgm(pat, qe)), - d_pat(pat) {} - - void resetInstantiationRound( QuantifiersEngine* qe ){ - d_cgm.resetInstantiationRound(qe); - }; - bool reset( TNode t, InstMatch& m, QuantifiersEngine* qe ){ - Debug("matching") << "datatypes: " << t << " matches " << d_pat << std::endl; - return d_cgm.reset(t, m, qe); - } - bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ){ - return d_cgm.getNextMatch(m, qe); - } -}; - -class ArrayMatcher: public Matcher{ - /* The matcher */ - typedef ApplyMatcher AuxMatcher3; - typedef TestMatcher< AuxMatcher3, LegalKindTest > AuxMatcher2; - typedef CandidateGeneratorMatcher< CandidateGeneratorTheoryEeClass, AuxMatcher2> AuxMatcher1; - AuxMatcher1 d_cgm; - static inline AuxMatcher1 createCgm(Node pat, QuantifiersEngine* qe){ - Assert( pat.getKind() == kind::SELECT || pat.getKind() == kind::STORE ); - /** In reverse order of matcher sequence */ - AuxMatcher3 am3(pat,qe); - /** Keep only the one that have the good operator */ - AuxMatcher2 am2(am3, LegalKindTest(pat.getKind())); - /** Iter on the equivalence class of the given term */ - arrays::TheoryArrays* ar = static_cast<arrays::TheoryArrays *>(qe->getTheoryEngine()->theoryOf( theory::THEORY_ARRAY )); - eq::EqualityEngine* ee = - static_cast<eq::EqualityEngine*>(ar->getEqualityEngine()); - CandidateGeneratorTheoryEeClass cdtUfEq(ee); - /* Create a matcher from the candidate generator */ - AuxMatcher1 am1(cdtUfEq,am2); - return am1; - } - size_t d_num_var; - Node d_pat; -public: - ArrayMatcher( Node pat, QuantifiersEngine* qe ): - d_cgm(createCgm(pat, qe)),d_num_var(numFreeVar(pat)), - d_pat(pat) {} - - void resetInstantiationRound( QuantifiersEngine* qe ){ - d_cgm.resetInstantiationRound(qe); - }; - bool reset( TNode t, InstMatch& m, QuantifiersEngine* qe ){ - // size_t m_size = m.d_map.size(); - // if(m_size == d_num_var){ - // uf::EqualityEngine<uf::TheoryUF::NotifyClass>* ee = (static_cast<uf::TheoryUF*>(qe->getTheoryEngine()->theoryOf( theory::THEORY_UF )))->getEqualityEngine(); - // std::cout << "!"; - // return ee->areEqual(m.subst(d_pat),t); - // }else{ - // std::cout << m.d_map.size() << std::endl; - return d_cgm.reset(t, m, qe); - // } - } - bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ){ - return d_cgm.getNextMatch(m, qe); - } -}; - -class AllOpMatcher: public PatMatcher{ - /* The matcher */ - typedef ApplyMatcher AuxMatcher3; - typedef TestMatcher< AuxMatcher3, LegalTest > AuxMatcher2; - typedef CandidateGeneratorMatcher< CandidateGeneratorTheoryUfOp, AuxMatcher2> AuxMatcher1; - AuxMatcher1 d_cgm; - static inline AuxMatcher1 createCgm(Node pat, QuantifiersEngine* qe){ - Assert( pat.hasOperator() ); - /** In reverse order of matcher sequence */ - AuxMatcher3 am3(pat,qe); - /** Keep only the one that have the good operator */ - AuxMatcher2 am2(am3,LegalTest()); - /** Iter on the equivalence class of the given term */ - TermDb* tdb = qe->getTermDatabase(); - Node op = tdb->getOperator( pat ); - CandidateGeneratorTheoryUfOp cdtUfEq(op,tdb); - /* Create a matcher from the candidate generator */ - AuxMatcher1 am1(cdtUfEq,am2); - return am1; - } - size_t d_num_var; - Node d_pat; -public: - AllOpMatcher( TNode pat, QuantifiersEngine* qe ): - d_cgm(createCgm(pat, qe)), d_num_var(numFreeVar(pat)), - d_pat(pat) {} - - void resetInstantiationRound( QuantifiersEngine* qe ){ - d_cgm.resetInstantiationRound(qe); - }; - bool reset( InstMatch& m, QuantifiersEngine* qe ){ - // std::cout << m.d_map.size() << "/" << d_num_var << std::endl; - return d_cgm.reset(Node::null(), m, qe); - } - bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ){ - return d_cgm.getNextMatch(m, qe); - } -}; - -template <bool classes> /** true classes | false class */ -class GenericCandidateGeneratorClasses: public CandidateGenerator{ -private: - CandidateGenerator* d_cg; - QuantifiersEngine* d_qe; - -public: - void mkCandidateGenerator(){ - if(classes) - d_cg = new GenericCandidateGeneratorClasses(d_qe); - else - d_cg = new GenericCandidateGeneratorClass(d_qe); - } - - GenericCandidateGeneratorClasses(QuantifiersEngine* qe): - d_qe(qe) { - mkCandidateGenerator(); - } - ~GenericCandidateGeneratorClasses(){ - delete(d_cg); - } - const GenericCandidateGeneratorClasses & operator =(const GenericCandidateGeneratorClasses & m){ - mkCandidateGenerator(); - return m; - }; - GenericCandidateGeneratorClasses(const GenericCandidateGeneratorClasses & m): - d_qe(m.d_qe){ - mkCandidateGenerator(); - } - void resetInstantiationRound(){ - d_cg->resetInstantiationRound(); - }; - void reset( TNode eqc ){ - Assert( !classes || eqc.isNull() ); - d_cg->reset(eqc); - }; //* the argument is not used - TNode getNextCandidate(){ - return d_cg->getNextCandidate(); - }; -}; /* MetaCandidateGeneratorClasses */ - - -class GenericMatcher: public Matcher{ - /* The matcher */ - typedef ApplyMatcher AuxMatcher3; - typedef TestMatcher< AuxMatcher3, LegalOpTest > AuxMatcher2; - typedef CandidateGeneratorMatcher< GenericCandidateGeneratorClasses<false>, AuxMatcher2> AuxMatcher1; - AuxMatcher1 d_cgm; - static inline AuxMatcher1 createCgm(Node pat, QuantifiersEngine* qe){ - /** In reverse order of matcher sequence */ - AuxMatcher3 am3(pat,qe); - /** Keep only the one that have the good operator */ - AuxMatcher2 am2(am3,LegalOpTest(pat.getOperator())); - /** Iter on the equivalence class of the given term */ - GenericCandidateGeneratorClasses<false> cdtG(qe); - /* Create a matcher from the candidate generator */ - AuxMatcher1 am1(cdtG,am2); - return am1; - } - Node d_pat; -public: - GenericMatcher( Node pat, QuantifiersEngine* qe ): - d_cgm(createCgm(pat, qe)), - d_pat(pat) {} - - void resetInstantiationRound( QuantifiersEngine* qe ){ - d_cgm.resetInstantiationRound(qe); - }; - bool reset( TNode t, InstMatch& m, QuantifiersEngine* qe ){ - return d_cgm.reset(t, m, qe); - } - bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ){ - return d_cgm.getNextMatch(m, qe); - } -}; - - -class GenericPatMatcher: public PatMatcher{ - /* The matcher */ - typedef ApplyMatcher AuxMatcher3; - typedef TestMatcher< AuxMatcher3, LegalOpTest > AuxMatcher2; - typedef CandidateGeneratorMatcher< GenericCandidateGeneratorClasses<true>, AuxMatcher2> AuxMatcher1; - AuxMatcher1 d_cgm; - static inline AuxMatcher1 createCgm(Node pat, QuantifiersEngine* qe){ - /** In reverse order of matcher sequence */ - AuxMatcher3 am3(pat,qe); - /** Keep only the one that have the good operator */ - AuxMatcher2 am2(am3,LegalOpTest(pat.getOperator())); - /** Iter on the equivalence class of the given term */ - GenericCandidateGeneratorClasses<true> cdtG(qe); - /* Create a matcher from the candidate generator */ - AuxMatcher1 am1(cdtG,am2); - return am1; - } - Node d_pat; -public: - GenericPatMatcher( Node pat, QuantifiersEngine* qe ): - d_cgm(createCgm(pat, qe)), - d_pat(pat) {} - - void resetInstantiationRound( QuantifiersEngine* qe ){ - d_cgm.resetInstantiationRound(qe); - }; - bool reset( InstMatch& m, QuantifiersEngine* qe ){ - return d_cgm.reset(Node::null(), m, qe); - } - bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ){ - return d_cgm.getNextMatch(m, qe); - } -}; - -class MetaCandidateGeneratorClasses: public CandidateGenerator{ -private: - CandidateGenerator* d_cg; - TypeNode d_ty; - TheoryEngine* d_te; - -public: - CandidateGenerator* mkCandidateGenerator(TypeNode ty, TheoryEngine* te){ - Debug("inst-match-gen") << "MetaCandidateGenerator for type: " << ty - << " Theory : " << Theory::theoryOf(ty) << std::endl; - if( Theory::theoryOf(ty) == theory::THEORY_DATATYPES ){ - // datatypes::TheoryDatatypes* dt = static_cast<datatypes::TheoryDatatypes *>(te->theoryOf( theory::THEORY_DATATYPES )); - // return new datatypes::rrinst::CandidateGeneratorTheoryClasses(dt); - Unimplemented("MetaCandidateGeneratorClasses for THEORY_DATATYPES"); - }else if ( Theory::theoryOf(ty) == theory::THEORY_ARRAY ){ - arrays::TheoryArrays* ar = static_cast<arrays::TheoryArrays *>(te->theoryOf( theory::THEORY_ARRAY )); - eq::EqualityEngine* ee = - static_cast<eq::EqualityEngine*>(ar->getEqualityEngine()); - return new CandidateGeneratorTheoryEeClasses(ee); - } else { - uf::TheoryUF* uf = static_cast<uf::TheoryUF*>(te->theoryOf( theory::THEORY_UF )); - eq::EqualityEngine* ee = - static_cast<eq::EqualityEngine*>(uf->getEqualityEngine()); - return new CandidateGeneratorTheoryEeClasses(ee); - } - } - MetaCandidateGeneratorClasses(TypeNode ty, TheoryEngine* te): - d_ty(ty), d_te(te) { - d_cg = mkCandidateGenerator(ty,te); - } - ~MetaCandidateGeneratorClasses(){ - delete(d_cg); - } - const MetaCandidateGeneratorClasses & operator =(const MetaCandidateGeneratorClasses & m){ - d_cg = mkCandidateGenerator(m.d_ty, m.d_te); - return m; - }; - MetaCandidateGeneratorClasses(const MetaCandidateGeneratorClasses & m): - d_ty(m.d_ty), d_te(m.d_te){ - d_cg = mkCandidateGenerator(m.d_ty, m.d_te); - } - void resetInstantiationRound(){ - d_cg->resetInstantiationRound(); - }; - void reset( TNode eqc ){ - d_cg->reset(eqc); - }; //* the argument is not used - TNode getNextCandidate(){ - return d_cg->getNextCandidate(); - }; -}; /* MetaCandidateGeneratorClasses */ - -/** Match just a variable */ -class AllVarMatcher: public PatMatcher{ -private: - /* generator */ - typedef VarMatcher AuxMatcher3; - typedef TestMatcher< AuxMatcher3, LegalTypeTest > AuxMatcher2; - typedef CandidateGeneratorMatcher< MetaCandidateGeneratorClasses, AuxMatcher2 > AuxMatcher1; - AuxMatcher1 d_cgm; - static inline AuxMatcher1 createCgm(TNode pat, QuantifiersEngine* qe){ - Assert( pat.getKind()==INST_CONSTANT ); - TypeNode ty = pat.getType(); - Debug("inst-match-gen") << "create AllVarMatcher for type: " << ty << std::endl; - /** In reverse order of matcher sequence */ - /** Distribute it to all the pattern */ - AuxMatcher3 am3(pat,qe); - /** Keep only the one that have the good type */ - AuxMatcher2 am2(am3,LegalTypeTest(ty)); - /** Generate one term by eq classes */ - MetaCandidateGeneratorClasses mcdt(ty,qe->getTheoryEngine()); - /* Create a matcher from the candidate generator */ - AuxMatcher1 am1(mcdt,am2); - return am1; - } -public: - AllVarMatcher( TNode pat, QuantifiersEngine* qe ): - d_cgm(createCgm(pat, qe)){} - - void resetInstantiationRound( QuantifiersEngine* qe ){ - d_cgm.resetInstantiationRound(qe); - }; - bool reset( InstMatch& m, QuantifiersEngine* qe ){ - return d_cgm.reset(Node::null(), m, qe); //cdtUfEq doesn't use it's argument for reset - } - bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ){ - return d_cgm.getNextMatch(m, qe); - } -}; - -/** Match all the pattern with the same term */ -class SplitMatcher: public Matcher{ -private: - const size_t size; - ApplyMatcher d_m; /** Use ApplyMatcher by creating a fake application */ -public: - SplitMatcher(std::vector< Node > pats, QuantifiersEngine* qe): - size(pats.size()), - d_m(NodeManager::currentNM()->mkNode(kind::INST_PATTERN,pats), qe) {} - void resetInstantiationRound( QuantifiersEngine* qe ){ - d_m.resetInstantiationRound(qe); - }; - bool reset( TNode ex, InstMatch& m, QuantifiersEngine* qe ){ - NodeBuilder<> n(kind::INST_PATTERN); - for(size_t i = 0; i < size; ++i) n << ex; - Node nn = n; - return d_m.reset(nn,m,qe); - }; - bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ){ - return getNextMatch(m, qe); - } -}; - - -/** Match uf term in a fixed equivalence class */ -class UfCstEqMatcher: public PatMatcher{ -private: - /* equivalence class to match */ - Node d_cst; - /* generator */ - OpMatcher d_cgm; -public: - UfCstEqMatcher( Node pat, Node cst, QuantifiersEngine* qe ): - d_cst(cst), - d_cgm(OpMatcher(pat,qe)) {}; - void resetInstantiationRound( QuantifiersEngine* qe ){ - d_cgm.resetInstantiationRound(qe); - }; - bool reset( InstMatch& m, QuantifiersEngine* qe ){ - return d_cgm.reset(d_cst, m, qe); - } - bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ){ - return d_cgm.getNextMatch(m, qe); - } -}; - -/** Match equalities */ -class UfEqMatcher: public PatMatcher{ -private: - /* generator */ - typedef SplitMatcher AuxMatcher3; - typedef TestMatcher< AuxMatcher3, LegalTypeTest > AuxMatcher2; - typedef CandidateGeneratorMatcher< CandidateGeneratorTheoryEeClasses, AuxMatcher2 > AuxMatcher1; - AuxMatcher1 d_cgm; - static inline AuxMatcher1 createCgm(std::vector<Node> & pat, QuantifiersEngine* qe){ - Assert( pat.size() > 0); - TypeNode ty = pat[0].getType(); - for(size_t i = 1; i < pat.size(); ++i){ - Assert(pat[i].getType() == ty); - } - /** In reverse order of matcher sequence */ - /** Distribute it to all the pattern */ - AuxMatcher3 am3(pat,qe); - /** Keep only the one that have the good type */ - AuxMatcher2 am2(am3,LegalTypeTest(ty)); - /** Generate one term by eq classes */ - uf::TheoryUF* uf = static_cast<uf::TheoryUF*>(qe->getTheoryEngine()->theoryOf( theory::THEORY_UF )); - eq::EqualityEngine* ee = - static_cast<eq::EqualityEngine*>(uf->getEqualityEngine()); - CandidateGeneratorTheoryEeClasses cdtUfEq(ee); - /* Create a matcher from the candidate generator */ - AuxMatcher1 am1(cdtUfEq,am2); - return am1; - } -public: - UfEqMatcher( std::vector<Node> & pat, QuantifiersEngine* qe ): - d_cgm(createCgm(pat, qe)){} - - void resetInstantiationRound( QuantifiersEngine* qe ){ - d_cgm.resetInstantiationRound(qe); - }; - bool reset( InstMatch& m, QuantifiersEngine* qe ){ - return d_cgm.reset(Node::null(), m, qe); //cdtUfEq doesn't use it's argument for reset - } - bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ){ - return d_cgm.getNextMatch(m, qe); - } -}; - - -/** Match dis-equalities */ -class UfDeqMatcher: public PatMatcher{ -private: - /* generator */ - typedef ApplyMatcher AuxMatcher3; - - class EqTest/* : public unary_function<Node,bool>*/ { - TypeNode d_type; - public: - inline EqTest(TypeNode type): d_type(type){}; - inline bool operator() (Node n) { - return - CandidateGenerator::isLegalCandidate(n) && - n.getKind() == kind::EQUAL && - n[0].getType()==d_type; - }; - }; - typedef TestMatcher< AuxMatcher3, EqTest > AuxMatcher2; - typedef CandidateGeneratorMatcher< CandidateGeneratorTheoryEeClass, AuxMatcher2 > AuxMatcher1; - AuxMatcher1 d_cgm; - Node false_term; - static inline AuxMatcher1 createCgm(Node pat, QuantifiersEngine* qe){ - Assert( pat.getKind() == kind::NOT); - TNode eq = pat[0]; - Assert( eq.getKind() == kind::EQUAL); - TypeNode ty = eq[0].getType(); - /** In reverse order of matcher sequence */ - /** Distribute it to all the pattern */ - AuxMatcher3 am3(eq,qe); - /** Keep only the one that have the good type */ - AuxMatcher2 am2(am3,EqTest(ty)); - /** Will generate all the terms of the eq class of false */ - uf::TheoryUF* uf = static_cast<uf::TheoryUF*>(qe->getTheoryEngine()->theoryOf( theory::THEORY_UF )); - eq::EqualityEngine* ee = - static_cast<eq::EqualityEngine*>(uf->getEqualityEngine()); - CandidateGeneratorTheoryEeClass cdtUfEq(ee); - /* Create a matcher from the candidate generator */ - AuxMatcher1 am1(cdtUfEq,am2); - return am1; - } -public: - UfDeqMatcher( Node pat, QuantifiersEngine* qe ): - d_cgm(createCgm(pat, qe)), - false_term((static_cast<uf::TheoryUF*>(qe->getTheoryEngine()->theoryOf( theory::THEORY_UF )))->getEqualityEngine()-> - getRepresentative(NodeManager::currentNM()->mkConst<bool>(false) )){}; - void resetInstantiationRound( QuantifiersEngine* qe ){ - d_cgm.resetInstantiationRound(qe); - }; - bool reset( InstMatch& m, QuantifiersEngine* qe ){ - return d_cgm.reset(false_term, m, qe); - } - bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ){ - return d_cgm.getNextMatch(m, qe); - } -}; - -Matcher* mkMatcher( Node pat, QuantifiersEngine* qe ){ - Debug("inst-match-gen") << "mkMatcher: Pattern term is " << pat << std::endl; - - // if( pat.getKind() == kind::APPLY_UF){ - // return new OpMatcher(pat, qe); - // } else if( pat.getKind() == kind::APPLY_CONSTRUCTOR ){ - // return new DatatypesMatcher(pat, qe); - // } else if( pat.getKind() == kind::SELECT || pat.getKind() == kind::STORE ){ - // return new ArrayMatcher(pat, qe); - if( pat.getKind() == kind::APPLY_UF || - pat.getKind() == kind::APPLY_CONSTRUCTOR || - pat.getKind() == kind::SELECT || pat.getKind() == kind::STORE ){ - return new GenericMatcher(pat, qe); - } else { /* Arithmetic? */ - /** TODO: something simpler to see if the pattern is a good - arithmetic pattern */ - std::map< Node, Node > d_arith_coeffs; - if( !Trigger::getPatternArithmetic( quantifiers::TermDb::getInstConstAttr(pat), pat, d_arith_coeffs ) ){ - Message() << "(?) Unknown matching pattern is " << pat << std::endl; - Unimplemented("pattern not implemented"); - return new DumbMatcher(); - }else{ - Debug("matching-arith") << "Generated arithmetic pattern for " << pat << ": " << 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; - } - ArithMatcher am3 (pat, qe); - TestMatcher<ArithMatcher, LegalTypeTest> - am2(am3,LegalTypeTest(pat.getType())); - /* generator */ - uf::TheoryUF* uf = static_cast<uf::TheoryUF*>(qe->getTheoryEngine()->theoryOf( theory::THEORY_UF )); - eq::EqualityEngine* ee = - static_cast<eq::EqualityEngine*> (uf->getEqualityEngine()); - CandidateGeneratorTheoryEeClass cdtUfEq(ee); - return new CandidateGeneratorMatcher< CandidateGeneratorTheoryEeClass, - TestMatcher<ArithMatcher, LegalTypeTest> > (cdtUfEq,am2); - } - } -}; - -PatMatcher* mkPattern( Node pat, QuantifiersEngine* qe ){ - Debug("inst-match-gen") << "Pattern term is " << pat << std::endl; - Assert( quantifiers::TermDb::hasInstConstAttr(pat) ); - - if( pat.getKind()==kind::NOT && pat[0].getKind() == kind::EQUAL){ - /* Difference */ - return new UfDeqMatcher(pat, qe); - } else if (pat.getKind() == kind::EQUAL){ - if( !quantifiers::TermDb::hasInstConstAttr(pat[0])){ - Assert(quantifiers::TermDb::hasInstConstAttr(pat[1])); - return new UfCstEqMatcher(pat[1], pat[0], qe); - }else if( !quantifiers::TermDb::hasInstConstAttr(pat[1] )){ - Assert(quantifiers::TermDb::hasInstConstAttr(pat[0])); - return new UfCstEqMatcher(pat[0], pat[1], qe); - }else{ - std::vector< Node > pats(pat.begin(),pat.end()); - return new UfEqMatcher(pats,qe); - } - } else if( Trigger::isAtomicTrigger( pat ) ){ - return new AllOpMatcher(pat, qe); - // return new GenericPatMatcher(pat, qe); - } else if( pat.getKind()==INST_CONSTANT ){ - // just a variable - return new AllVarMatcher(pat, qe); - } else { /* Arithmetic? */ - /** TODO: something simpler to see if the pattern is a good - arithmetic pattern */ - std::map< Node, Node > d_arith_coeffs; - if( !Trigger::getPatternArithmetic( quantifiers::TermDb::getInstConstAttr(pat), pat, d_arith_coeffs ) ){ - Debug("inst-match-gen") << "(?) Unknown matching pattern is " << pat << std::endl; - Message() << "(?) Unknown matching pattern is " << pat << std::endl; - return new DumbPatMatcher(); - }else{ - Debug("matching-arith") << "Generated arithmetic pattern for " << pat << ": " << 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; - } - ArithMatcher am3 (pat, qe); - TestMatcher<ArithMatcher, LegalTest> - am2(am3,LegalTest()); - /* generator */ - TermDb* tdb = qe->getTermDatabase(); - CandidateGeneratorTheoryUfType cdtUfEq(pat.getType(),tdb); - typedef CandidateGeneratorMatcher< CandidateGeneratorTheoryUfType, - TestMatcher<ArithMatcher, LegalTest> > AuxMatcher1; - return new PatOfMatcher<AuxMatcher1>(AuxMatcher1(cdtUfEq,am2)); - } - } -}; - -ArithMatcher::ArithMatcher(Node pat, QuantifiersEngine* qe): d_pattern(pat){ - - if(Trigger::getPatternArithmetic(quantifiers::TermDb::getInstConstAttr(pat), pat, d_arith_coeffs ) ) - { - Debug("inst-match-gen") << "(?) Unknown matching pattern is " << d_pattern << std::endl; - Assert(false); - }else{ - Debug("matching-arith") << "Generated arithmetic pattern for " << d_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; - } - } - -}; - -bool ArithMatcher::reset( TNode t, InstMatch& m, QuantifiersEngine* qe ){ - Debug("matching-arith") << "Matching " << t << " " << d_pattern << std::endl; - d_binded.clear(); - 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.find( it->first )==m.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.get( it->first ) << std::endl; - Node tm = m.get( 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.set( ic, Rewriter::rewrite( tm )); - d_binded.push_back(ic); - //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.find( it->first )==m.end() ){ - m.set( it->first, NodeManager::currentNM()->mkConst( Rational(0) )); - d_binded.push_back(ic); - } - } - } - Debug("matching-arith") << "Setting " << ic << " to " << tm << std::endl; - return true; - }else{ - m.erase(d_binded.begin(), d_binded.end()); - return false; - } - }else{ - m.erase(d_binded.begin(), d_binded.end()); - return false; - } -}; - -bool ArithMatcher::getNextMatch( InstMatch& m, QuantifiersEngine* qe ){ - m.erase(d_binded.begin(), d_binded.end()); - return false; -}; - - -class MultiPatsMatcher: public PatsMatcher{ -private: - bool d_reset_done; - std::vector< PatMatcher* > d_patterns; - InstMatch d_im; - bool reset( QuantifiersEngine* qe ){ - d_im.clear(); - d_reset_done = true; - - return getNextMatch(qe,true); - }; - - bool getNextMatch(QuantifiersEngine* qe, bool reset){ - const size_t max = d_patterns.size() - 1; - size_t index = reset ? 0 : max; - while(true){ - Debug("matching") << "MultiPatsMatcher::index " << index << "/" - << max << (reset ? " reset_phase" : "") << std::endl; - if(reset ? - d_patterns[index]->reset( d_im, qe ) : - d_patterns[index]->getNextMatch( d_im, qe )){ - if(index==max) return true; - ++index; - reset=true; - }else{ - if(index==0) return false; - --index; - reset=false; - }; - } - } - -public: - MultiPatsMatcher(std::vector< Node > & pats, QuantifiersEngine* qe): - d_reset_done(false){ - Assert(pats.size() > 0); - for( size_t i=0; i< pats.size(); i++ ){ - d_patterns.push_back(mkPattern(pats[i],qe)); - }; - }; - void resetInstantiationRound( QuantifiersEngine* qe ){ - for( size_t i=0; i< d_patterns.size(); i++ ){ - d_patterns[i]->resetInstantiationRound( qe ); - }; - d_reset_done = false; - d_im.clear(); - }; - bool getNextMatch( QuantifiersEngine* qe ){ - Assert(d_patterns.size()>0); - if(d_reset_done) return getNextMatch(qe,false); - else return reset(qe); - } - const InstMatch& getInstMatch(){return d_im;}; - - int addInstantiations( InstMatch& baseMatch, Node quant, QuantifiersEngine* qe); -}; - -enum EffiStep{ - ES_STOP, - ES_GET_MONO_CANDIDATE, - ES_GET_MULTI_CANDIDATE, - ES_RESET1, - ES_RESET2, - ES_NEXT1, - ES_NEXT2, - ES_RESET_OTHER, - ES_NEXT_OTHER, -}; -static inline std::ostream& operator<<(std::ostream& out, const EffiStep& step) { - switch(step){ - case ES_STOP: out << "STOP"; break; - case ES_GET_MONO_CANDIDATE: out << "GET_MONO_CANDIDATE"; break; - case ES_GET_MULTI_CANDIDATE: out << "GET_MULTI_CANDIDATE"; break; - case ES_RESET1: out << "RESET1"; break; - case ES_RESET2: out << "RESET2"; break; - case ES_NEXT1: out << "NEXT1"; break; - case ES_NEXT2: out << "NEXT2"; break; - case ES_RESET_OTHER: out << "RESET_OTHER"; break; - case ES_NEXT_OTHER: out << "NEXT_OTHER"; break; - } - return out; -} - - -int MultiPatsMatcher::addInstantiations( InstMatch& baseMatch, Node quant, QuantifiersEngine* qe){ - //now, try to add instantiation for each match produced - int addedLemmas = 0; - resetInstantiationRound( qe ); - d_im.add( baseMatch ); - while( getNextMatch( qe ) ){ - InstMatch im_copy = getInstMatch(); - std::vector< Node > terms; - for( unsigned i=0; i<quant[0].getNumChildren(); i++ ){ - terms.push_back( im_copy.get( qe, quant, i ) ); - } - - //m.makeInternal( d_quantEngine->getEqualityQuery() ); - if( qe->addInstantiation( quant, terms ) ){ - addedLemmas++; - } - } - //return number of lemmas added - return addedLemmas; -} - -PatsMatcher* mkPatterns( std::vector< Node > pat, QuantifiersEngine* qe ){ - return new MultiPatsMatcher( pat, qe); -} - -class MultiEfficientPatsMatcher: public PatsMatcher{ -private: - bool d_phase_mono; - bool d_phase_new_term; - std::vector< PatMatcher* > d_patterns; - std::vector< Matcher* > d_direct_patterns; - InstMatch d_im; - EfficientHandler d_eh; - EfficientHandler::MultiCandidate d_mc; - InstMatchTrie2Pairs<true> d_cache; - std::vector<Node> d_pats; - // bool indexDone( size_t i){ - // return i == d_c.first.second || - // ( i == d_c.second.second && d_c.second.first.empty()); - // } - - - - static const EffiStep ES_START = ES_GET_MONO_CANDIDATE; - EffiStep d_step; - - //return true if it becomes bigger than d_patterns.size() - 1 - bool incrIndex(size_t & index){ - if(index == d_patterns.size() - 1) return true; - ++index; - if(index == d_mc.first.second - || (!d_phase_mono && index == d_mc.second.second)) - return incrIndex(index); - else return false; - } - - //return true if it becomes smaller than 0 - bool decrIndex(size_t & index){ - if(index == 0) return true; - --index; - if(index == d_mc.first.second - || (!d_phase_mono && index == d_mc.second.second)) - return decrIndex(index); - else return false; - } - - bool resetOther( QuantifiersEngine* qe ){ - return getNextMatchOther(qe,true); - }; - - - bool getNextMatchOther(QuantifiersEngine* qe, bool reset){ - size_t index = reset ? 0 : d_patterns.size(); - if(!reset && decrIndex(index)) return false; - if( reset && - (index == d_mc.first.second - || (!d_phase_mono && index == d_mc.second.second)) - && incrIndex(index)) return true; - while(true){ - Debug("matching") << "MultiEfficientPatsMatcher::index " << index << "/" - << d_patterns.size() - 1 << std::endl; - if(reset ? - d_patterns[index]->reset( d_im, qe ) : - d_patterns[index]->getNextMatch( d_im, qe )){ - if(incrIndex(index)) return true; - reset=true; - }else{ - if(decrIndex(index)) return false; - reset=false; - }; - } - } - - inline EffiStep TestMonoCache(QuantifiersEngine* qe){ - if( //!d_phase_new_term || - d_pats.size() == 1) return ES_RESET_OTHER; - if(d_cache.addInstMatch(d_mc.first.second,d_im)){ - Debug("inst-match::cache") << "Cache miss" << d_im << std::endl; - ++qe->d_statistics.d_mono_candidates_cache_miss; - return ES_RESET_OTHER; - } else { - Debug("inst-match::cache") << "Cache hit" << d_im << std::endl; - ++qe->d_statistics.d_mono_candidates_cache_hit; - return ES_NEXT1; - } - // ++qe->d_statistics.d_mono_candidates_cache_miss; - // return ES_RESET_OTHER; - } - - inline EffiStep TestMultiCache(QuantifiersEngine* qe){ - if(d_cache.addInstMatch(d_mc.first.second,d_mc.second.second,d_im)){ - ++qe->d_statistics.d_multi_candidates_cache_miss; - return ES_RESET_OTHER; - } else { - ++qe->d_statistics.d_multi_candidates_cache_hit; - return ES_NEXT2; - } - } - - -public: - - bool getNextMatch( QuantifiersEngine* qe ){ - Assert( d_step == ES_START || d_step == ES_NEXT_OTHER || d_step == ES_STOP ); - while(true){ - Debug("matching") << "d_step=" << d_step << " " - << "d_im=" << d_im << std::endl; - switch(d_step){ - case ES_GET_MONO_CANDIDATE: - Assert(d_im.empty()); - if(d_phase_new_term ? d_eh.getNextMonoCandidate(d_mc.first) : d_eh.getNextMonoCandidateNewTerm(d_mc.first)){ - if(d_phase_new_term) ++qe->d_statistics.d_num_mono_candidates_new_term; - else ++qe->d_statistics.d_num_mono_candidates; - d_phase_mono = true; - d_step = ES_RESET1; - } else if (!d_phase_new_term){ - d_phase_new_term = true; - d_step = ES_GET_MONO_CANDIDATE; - } else { - d_phase_new_term = false; - d_step = ES_GET_MULTI_CANDIDATE; - } - break; - case ES_GET_MULTI_CANDIDATE: - Assert(d_im.empty()); - if(d_eh.getNextMultiCandidate(d_mc)){ - ++qe->d_statistics.d_num_multi_candidates; - d_phase_mono = false; - d_step = ES_RESET1; - } else d_step = ES_STOP; - break; - case ES_RESET1: - if(d_direct_patterns[d_mc.first.second]->reset(d_mc.first.first,d_im,qe)) - d_step = d_phase_mono ? TestMonoCache(qe) : ES_RESET2; - else d_step = d_phase_mono ? ES_GET_MONO_CANDIDATE : ES_GET_MULTI_CANDIDATE; - break; - case ES_RESET2: - Assert(!d_phase_mono); - if(d_direct_patterns[d_mc.second.second]->reset(d_mc.second.first,d_im,qe)) - d_step = TestMultiCache(qe); - else d_step = ES_NEXT1; - break; - case ES_NEXT1: - if(d_direct_patterns[d_mc.first.second]->getNextMatch(d_im,qe)) - d_step = d_phase_mono ? TestMonoCache(qe) : ES_RESET2; - else d_step = d_phase_mono ? ES_GET_MONO_CANDIDATE : ES_GET_MULTI_CANDIDATE; - break; - case ES_NEXT2: - if(d_direct_patterns[d_mc.second.second]->getNextMatch(d_im,qe)) - d_step = TestMultiCache(qe); - else d_step = ES_NEXT1; - break; - case ES_RESET_OTHER: - if(resetOther(qe)){ - d_step = ES_NEXT_OTHER; - return true; - } else d_step = d_phase_mono ? ES_NEXT1 : ES_NEXT2; - break; - case ES_NEXT_OTHER: - { - if(!getNextMatchOther(qe,false)){ - d_step = d_phase_mono ? ES_NEXT1 : ES_NEXT2; - }else{ - d_step = ES_NEXT_OTHER; - return true; - } - } - break; - case ES_STOP: - Assert(d_im.empty()); - return false; - } - } - } - - MultiEfficientPatsMatcher(std::vector< Node > & pats, QuantifiersEngine* qe): - d_eh(qe->getTheoryEngine()->getSatContext()), - d_cache(qe->getTheoryEngine()->getSatContext(),qe,pats.size()), - d_pats(pats), d_step(ES_START) { - Assert(pats.size() > 0); - for( size_t i=0; i< pats.size(); i++ ){ - d_patterns.push_back(mkPattern(pats[i],qe)); - if(pats[i].getKind()==kind::INST_CONSTANT){ - d_direct_patterns.push_back(new VarMatcher(pats[i],qe)); - } else if( pats[i].getKind() == kind::NOT && pats[i][0].getKind() == kind::EQUAL){ - d_direct_patterns.push_back(new ApplyMatcher(pats[i][0],qe)); - } else { - d_direct_patterns.push_back(new ApplyMatcher(pats[i],qe)); - } - }; - EfficientEMatcher* eem = qe->getEfficientEMatcher(); - eem->registerEfficientHandler(d_eh, pats); - }; - void resetInstantiationRound( QuantifiersEngine* qe ){ - Assert(d_step == ES_START || d_step == ES_STOP); - for( size_t i=0; i< d_patterns.size(); i++ ){ - d_patterns[i]->resetInstantiationRound( qe ); - d_direct_patterns[i]->resetInstantiationRound( qe ); - }; - d_step = ES_START; - d_phase_new_term = false; - Assert(d_im.empty()); - }; - - const InstMatch& getInstMatch(){return d_im;}; - - int addInstantiations( InstMatch& baseMatch, Node quant, QuantifiersEngine* qe); -}; - -int MultiEfficientPatsMatcher::addInstantiations( InstMatch& baseMatch, Node quant, QuantifiersEngine* qe){ - //now, try to add instantiation for each match produced - int addedLemmas = 0; - Assert(baseMatch.empty()); - resetInstantiationRound( qe ); - while( getNextMatch( qe ) ){ - InstMatch im_copy = getInstMatch(); - std::vector< Node > terms; - for( unsigned i=0; i<quant[0].getNumChildren(); i++ ){ - terms.push_back( im_copy.get( qe, quant, i ) ); - } - - //m.makeInternal( d_quantEngine->getEqualityQuery() ); - if( qe->addInstantiation( quant, terms ) ){ - addedLemmas++; - } - } - //return number of lemmas added - return addedLemmas; -}; - -PatsMatcher* mkPatternsEfficient( std::vector< Node > pat, QuantifiersEngine* qe ){ - return new MultiEfficientPatsMatcher( pat, qe); -} - -} /* CVC4::theory::rrinst */ -} /* CVC4::theory */ -} /* CVC4 */ diff --git a/src/theory/rewriterules/rr_inst_match.h b/src/theory/rewriterules/rr_inst_match.h deleted file mode 100644 index c42dd8914..000000000 --- a/src/theory/rewriterules/rr_inst_match.h +++ /dev/null @@ -1,341 +0,0 @@ -/********************* */ -/*! \file rr_inst_match.h - ** \verbatim - ** Original author: Morgan Deters - ** Major contributors: Francois Bobot - ** Minor contributors (to current version): Andrew Reynolds - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief inst match class - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__THEORY__REWRITERULES__RR_INST_MATCH_H -#define __CVC4__THEORY__REWRITERULES__RR_INST_MATCH_H - -#include "theory/theory.h" -#include "util/hash.h" -#include "util/ntuple.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" - -#include "theory/quantifiers/term_database.h" -#include "expr/node_manager.h" -#include "expr/node_builder.h" - -#include "theory/quantifiers/options.h" -#include "theory/rewriterules/options.h" - -//#define USE_EFFICIENT_E_MATCHING - -namespace CVC4 { -namespace theory { - -class EqualityQuery; - -namespace rrinst{ - -/** basic class defining an instantiation */ -class InstMatch { - /* map from variable to ground terms */ - std::map< Node, Node > d_map; -public: - InstMatch(); - InstMatch( InstMatch* m ); - - /** set the match of v to m */ - bool setMatch( EqualityQuery* q, TNode v, TNode m ); - /* This version tell if the variable has been set */ - bool setMatch( EqualityQuery* q, TNode v, TNode m, bool & set); - /** 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 ); - /** is complete? */ - bool isComplete( Node f ) { return d_map.size()==f[0].getNumChildren(); } - /** make complete */ - void makeComplete( Node f, QuantifiersEngine* qe ); - /** make internal representative */ - //void makeInternalRepresentative( QuantifiersEngine* qe ); - /** make representative */ - void makeRepresentative( QuantifiersEngine* qe ); - /** get value */ - Node getValue( Node var ) const; - /** clear */ - void clear(){ d_map.clear(); } - /** is_empty */ - bool empty(){ return d_map.empty(); } - /** 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 << " )"; - } - - - //for rewrite rules - - /** apply rewrite */ - void applyRewrite(); - /** erase */ - template<class Iterator> - void erase(Iterator begin, Iterator end){ - for(Iterator i = begin; i != end; ++i){ - d_map.erase(*i); - }; - } - void erase(Node node){ d_map.erase(node); } - /** get */ - Node get( TNode var ) { return d_map[var]; } - Node get( QuantifiersEngine* qe, Node f, int i ); - /** set */ - void set(TNode var, TNode n); - void set( QuantifiersEngine* qe, Node f, int i, TNode n ); - /** size */ - size_t size(){ return d_map.size(); } - /* iterator */ - std::map< Node, Node >::iterator begin(){ return d_map.begin(); }; - std::map< Node, Node >::iterator end(){ return d_map.end(); }; - std::map< Node, Node >::iterator find(Node var){ return d_map.find(var); }; - /* Node used for matching the trigger only for mono-trigger (just for - efficiency because I need only that) */ - Node d_matched; -};/* class InstMatch */ - - - -class CandidateGenerator -{ -public: - CandidateGenerator(){} - virtual ~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( TNode eqc ) = 0; - virtual TNode getNextCandidate() = 0; - /** call this at the beginning of each instantiation round */ - virtual void resetInstantiationRound() = 0; -public: - /** legal candidate */ - static inline bool isLegalCandidate( TNode n ){ - return !n.getAttribute(NoMatchAttribute()) && - ( !options::cbqi() || !quantifiers::TermDb::hasInstConstAttr(n)) && - ( !options::efficientEMatching() || n.hasAttribute(AvailableInTermDb()) ); -} - -}; - - -inline std::ostream& operator<<(std::ostream& out, const InstMatch& m) { - m.toStream(out); - return out; -} - -template<bool modEq = false> class InstMatchTrie2; -template<bool modEq = false> class InstMatchTrie2Pairs; - -template<bool modEq = false> -class InstMatchTrie2Gen -{ - friend class InstMatchTrie2<modEq>; - friend class InstMatchTrie2Pairs<modEq>; - -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){ - Assert(t.e.empty()); Assert(e.empty()); - Assert(t.level == level); - return t; - } - Tree(size_t l): level(l) {}; - ~Tree(){ - for(typename MLevel::iterator i = e.begin(); i!=e.end(); ++i) - delete(i->second); - }; - }; - - - 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); - }; - }; - - EqualityQuery* d_eQ; - CandidateGenerator * d_cG; - - 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; - - /** add match m in the trie root - return true if it was never seen */ - bool addInstMatch( InstMatch& m, Tree * root); - -public: - InstMatchTrie2Gen(context::Context* c, QuantifiersEngine* q); - InstMatchTrie2Gen(const InstMatchTrie2Gen &) CVC4_UNDEFINED; - const InstMatchTrie2Gen & operator =(const InstMatchTrie2Gen & e) CVC4_UNDEFINED; -}; - -template<bool modEq> -class InstMatchTrie2 -{ - typename InstMatchTrie2Gen<modEq>::Tree d_data; - InstMatchTrie2Gen<modEq> d_backtrack; -public: - InstMatchTrie2(context::Context* c, QuantifiersEngine* q): d_data(0), - d_backtrack(c,q) {}; - InstMatchTrie2(const InstMatchTrie2 &) CVC4_UNDEFINED; - const InstMatchTrie2 & operator =(const InstMatchTrie2 & e) CVC4_UNDEFINED; - /** add match m in the trie, - return true if it was never seen */ - inline bool addInstMatch( InstMatch& m){ - return d_backtrack.addInstMatch(m,&d_data); - }; - -};/* class InstMatchTrie2 */ - -class Matcher -{ -public: - /** reset instantiation round (call this whenever equivalence classes have changed) */ - virtual void resetInstantiationRound( QuantifiersEngine* qe ) = 0; - /** reset the term to match, return false if there is no such term */ - virtual bool reset( TNode n, InstMatch& m, QuantifiersEngine* qe ) = 0; - /** get the next match. If it return false once you shouldn't call - getNextMatch again before doing a reset */ - virtual bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ) = 0; - /** If reset, or getNextMatch return false they remove from the - InstMatch the binding that they have previously created */ - - /** virtual Matcher in order to have defined behavior */ - virtual ~Matcher(){}; -}; - - -class ApplyMatcher: public Matcher{ -private: - /** What to check first: constant and variable */ - std::vector< triple< TNode,size_t,EqualityQuery* > > d_constants; - std::vector< triple< TNode,size_t,EqualityQuery* > > d_variables; - /** children generators, only the sub-pattern which are - neither a variable neither a constant appears */ - std::vector< triple< Matcher*, size_t, EqualityQuery* > > d_childrens; - /** the variable that have been set by this matcher (during its own reset) */ - std::vector< TNode > d_binded; /* TNode because the variable are already in d_pattern */ - /** the representative of the argument of the term given by the last reset */ - std::vector< Node > d_reps; -public: - /** The pattern we are producing matches for */ - Node d_pattern; -public: - /** constructors */ - ApplyMatcher( Node pat, QuantifiersEngine* qe); - /** destructor */ - ~ApplyMatcher(){/*TODO delete dandling pointers? */} - /** reset instantiation round (call this whenever equivalence classes have changed) */ - void resetInstantiationRound( QuantifiersEngine* qe ); - /** reset the term to match */ - bool reset( TNode n, InstMatch& m, QuantifiersEngine* qe ); - /** get the next match. */ - bool getNextMatch(InstMatch& m, QuantifiersEngine* qe); -private: - bool getNextMatch(InstMatch& m, QuantifiersEngine* qe, bool reset); -}; - - -/* Match literal so you don't choose the equivalence class( */ -class PatMatcher -{ -public: - /** reset instantiation round (call this whenever equivalence classes have changed) */ - virtual void resetInstantiationRound( QuantifiersEngine* qe ) = 0; - /** reset the matcher, return false if there is no such term */ - virtual bool reset( InstMatch& m, QuantifiersEngine* qe ) = 0; - /** get the next match. If it return false once you shouldn't call - getNextMatch again before doing a reset */ - virtual bool getNextMatch( InstMatch& m, QuantifiersEngine* qe ) = 0; - /** If reset, or getNextMatch return false they remove from the - InstMatch the binding that they have previously created */ -}; - -Matcher* mkMatcher( Node pat, QuantifiersEngine* qe ); -PatMatcher* mkPattern( Node pat, QuantifiersEngine* qe ); - -/* Match literal so you don't choose the equivalence class( */ -class PatsMatcher -{ -public: - /** reset instantiation round (call this whenever equivalence classes have changed) */ - virtual void resetInstantiationRound( QuantifiersEngine* qe ) = 0; - /** reset the matcher, return false if there is no such term */ - virtual bool getNextMatch( QuantifiersEngine* qe ) = 0; - virtual const InstMatch& getInstMatch() = 0; - /** Add directly the instantiation to quantifiers engine */ - virtual int addInstantiations( InstMatch& baseMatch, Node quant, QuantifiersEngine* qe) = 0; -}; - -PatsMatcher* mkPatterns( std::vector< Node > pat, QuantifiersEngine* qe ); -PatsMatcher* mkPatternsEfficient( std::vector< Node > pat, QuantifiersEngine* qe ); - -/** return true if whatever Node is substituted for the variables the - given Node can't match the pattern */ -bool nonunifiable( TNode t, TNode pat, const std::vector<Node> & vars); - -class InstMatchGenerator; - -}/* CVC4::theory rrinst */ - -}/* CVC4::theory namespace */ - -}/* CVC4 namespace */ - -#endif /* __CVC4__THEORY__REWRITERULES__RR_INST_MATCH_H */ diff --git a/src/theory/rewriterules/rr_inst_match_impl.h b/src/theory/rewriterules/rr_inst_match_impl.h deleted file mode 100644 index c0dea3ba2..000000000 --- a/src/theory/rewriterules/rr_inst_match_impl.h +++ /dev/null @@ -1,126 +0,0 @@ -/********************* */ -/*! \file rr_inst_match_impl.h - ** \verbatim - ** Original author: Morgan Deters - ** Major contributors: Francois Bobot - ** Minor contributors (to current version): Andrew Reynolds - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief inst match class - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__THEORY__REWRITERULES__RR_INST_MATCH_IMPL_H -#define __CVC4__THEORY__REWRITERULES__RR_INST_MATCH_IMPL_H - -#include "theory/rewriterules/rr_inst_match.h" -#include "theory/theory_engine.h" -#include "theory/quantifiers_engine.h" -#include "theory/rewriterules/rr_candidate_generator.h" -#include "theory/uf/equality_engine.h" - -namespace CVC4 { -namespace theory { -namespace rrinst { - -template<bool modEq> -InstMatchTrie2Gen<modEq>::InstMatchTrie2Gen(context::Context* c, QuantifiersEngine* qe): - d_context(c), d_mods(c) { - d_eQ = qe->getEqualityQuery(); - d_cG = new GenericCandidateGeneratorClass(qe); -}; - -/** add match m for quantifier f starting at index, take into account equalities q, return true if successful */ -template<bool modEq> -void InstMatchTrie2Gen<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(std::make_pair(current->second, root2)); - addSubTree(root2, ++current, end, currLevel ); -} - -/** exists match */ -template<bool modEq> -bool InstMatchTrie2Gen<modEq>::existsInstMatch(InstMatchTrie2Gen<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 InstMatchTrie2Gen<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_eQ->hasTerm( n ) ){ - //check modulo equality if any other instantiation match exists - d_cG->reset( d_eQ->getRepresentative( n ) ); - for(TNode en = d_cG->getNextCandidate() ; !en.isNull() ; - en = d_cG->getNextCandidate() ){ - if( en == n ) continue; // already tested - typename InstMatchTrie2Gen<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 InstMatchTrie2Gen<modEq>:: -addInstMatch( InstMatch& m, InstMatchTrie2Gen<modEq>::Tree* e ) { - d_cG->resetInstantiationRound(); - mapIter begin = m.begin(); - mapIter end = m.end(); - 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(std::make_pair(e,diverge->second)); - return true; - }else{ - return false; - } -} - -}/* CVC4::theory::rrinst namespace */ - -}/* CVC4::theory namespace */ - -}/* CVC4 namespace */ - -#endif /* __CVC4__THEORY__REWRITERULES__RR_INST_MATCH_IMPL_H */ diff --git a/src/theory/rewriterules/rr_trigger.cpp b/src/theory/rewriterules/rr_trigger.cpp deleted file mode 100644 index 13250cf1b..000000000 --- a/src/theory/rewriterules/rr_trigger.cpp +++ /dev/null @@ -1,385 +0,0 @@ -/********************* */ -/*! \file rr_trigger.cpp - ** \verbatim - ** Original author: Morgan Deters - ** Major contributors: Andrew Reynolds, Francois Bobot - ** Minor contributors (to current version): none - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief Implementation of trigger class - **/ - -#include "theory/rewriterules/rr_trigger.h" -#include "theory/theory_engine.h" -#include "theory/quantifiers_engine.h" -#include "theory/rewriterules/rr_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; -using namespace CVC4::theory::rrinst; - -/** 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() ); - Debug("trigger") << "Trigger for " << f << ": " << d_nodes << std::endl; - if(matchOption == MATCH_GEN_DEFAULT) d_mg = mkPatterns( d_nodes, qe ); - else d_mg = mkPatternsEfficient( d_nodes, qe ); - if( d_nodes.size()==1 ){ - if( isSimpleTrigger( d_nodes[0] ) ){ - ++(qe->d_statistics.d_triggers); - }else{ - ++(qe->d_statistics.d_simple_triggers); - } - }else{ - Debug("multi-trigger") << "Multi-trigger " << (*this) << std::endl; - //std::cout << "Multi-trigger for " << f << " : " << std::endl; - //std::cout << " " << (*this) << std::endl; - ++(qe->d_statistics.d_multi_triggers); - } -} - -void Trigger::resetInstantiationRound(){ - d_mg->resetInstantiationRound( d_quantEngine ); -} - - -bool Trigger::getNextMatch(){ - bool retVal = d_mg->getNextMatch( 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::addInstantiations( InstMatch& baseMatch ){ - int addedLemmas = d_mg->addInstantiations( baseMatch, - quantifiers::TermDb::getInstConstAttr(d_nodes[0]), - d_quantEngine); - 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; - qe->getTermDatabase()->computeVarContains( temp[i] ); - for( int j=0; j<(int)qe->getTermDatabase()->d_var_contains[ temp[i] ].size(); j++ ){ - Node v = qe->getTermDatabase()->d_var_contains[ temp[i] ][j]; - if( quantifiers::TermDb::getInstConstAttr(v)==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)qe->getTermDatabase()->d_var_contains[ temp[i] ].size(); j++ ){ - Node v = qe->getTermDatabase()->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)qe->getTermDatabase()->d_var_contains[ n ].size(); j++ ){ - Node v = qe->getTermDatabase()->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)qe->getTermDatabase()->d_var_contains[ n ].size(); j++ ){ - Node v = qe->getTermDatabase()->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 = qe->getTriggerDatabase()->getTrigger( trNodes ); - //if( t ){ - // trOld++; - //}else{ - // trNew++; - //} - //if( (trNew+trOld)%100==0 ){ - // std::cout << "Trigger new old = " << trNew << " " << trOld << std::endl; - //} - }else{ - Trigger* t = qe->getRRTriggerDatabase()->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 ); - qe->getRRTriggerDatabase()->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( quantifiers::TermDb::getInstConstAttr(n)==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::isSimpleTrigger( Node n ){ - if( isAtomicTrigger( n ) ){ - for( int i=0; i<(int)n.getNumChildren(); i++ ){ - if( n[i].getKind()!=INST_CONSTANT && quantifiers::TermDb::hasInstConstAttr(n[i]) ){ - return false; - } - } - return true; - }else{ - return false; - } -} - - -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() ); - qe->getTermDatabase()->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 ); - } - } -} - -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( quantifiers::TermDb::hasInstConstAttr(n[i]) ){ - if( n[i].getKind()==INST_CONSTANT ){ - if( quantifiers::TermDb::getInstConstAttr(n[i])==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 && quantifiers::TermDb::getInstConstAttr(n[0])==f ){ - Assert( !quantifiers::TermDb::hasInstConstAttr(n[1]) ); - coeffs[ n[0] ] = n[1]; - return true; - }else if( n[1].getKind()==INST_CONSTANT && quantifiers::TermDb::getInstConstAttr(n[1])==f ){ - Assert( !quantifiers::TermDb::hasInstConstAttr(n[0]) ); - coeffs[ n[1] ] = n[0]; - return true; - } - } - return false; -} - - - -Trigger* TriggerTrie::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 TriggerTrie::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 TriggerTrie; - } - d_children[n]->addTrigger2( nodes, t ); - } -} diff --git a/src/theory/rewriterules/rr_trigger.h b/src/theory/rewriterules/rr_trigger.h deleted file mode 100644 index f02f38d0e..000000000 --- a/src/theory/rewriterules/rr_trigger.h +++ /dev/null @@ -1,157 +0,0 @@ -/********************* */ -/*! \file rr_trigger.h - ** \verbatim - ** Original author: Morgan Deters - ** Major contributors: Andrew Reynolds, Francois Bobot - ** Minor contributors (to current version): Tianyi Liang - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief trigger class - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__THEORY__REWRITERULES__RR_TRIGGER_H -#define __CVC4__THEORY__REWRITERULES__RR_TRIGGER_H - -#include "theory/rewriterules/rr_inst_match.h" - -namespace CVC4 { -namespace theory { -namespace rrinst { - -//a collect of nodes representing a trigger -class Trigger { -private: - /** the quantifiers engine */ - QuantifiersEngine* d_quantEngine; - /** the quantifier this trigger is for */ - Node d_f; - /** match generators */ - PatsMatcher * d_mg; -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 ); - PatsMatcher* getGenerator() { return d_mg; } -public: - /** reset instantiation round (call this whenever equivalence classes have changed) */ - void resetInstantiationRound(); - /** get next match. must call reset( eqc ) once before this function. */ - bool getNextMatch(); - const InstMatch & getInstMatch(){return d_mg->getInstMatch();}; - /** 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); - /** 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 { - //options for producing matches - MATCH_GEN_DEFAULT = 0, - MATCH_GEN_EFFICIENT_E_MATCH, //generate matches via Efficient E - }; - 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 inline bool isUsableTrigger( TNode n, TNode f ){ - return quantifiers::TermDb::getInstConstAttr(n)==f && isAtomicTrigger( n ) && isUsable( n, f ); - } - static inline bool isAtomicTrigger( TNode n ){ - return - n.getKind()==kind::APPLY_UF || - n.getKind() == kind::APPLY_CONSTRUCTOR || - n.getKind()==kind::SELECT || - n.getKind()==kind::STORE; - } - static bool isUsableTrigger( std::vector< Node >& nodes, Node f ); - static bool isSimpleTrigger( Node n ); - /** 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; -} - -/** a trie of triggers */ -class TriggerTrie -{ -private: - Trigger* getTrigger2( std::vector< Node >& nodes ); - void addTrigger2( std::vector< Node >& nodes, Trigger* t ); -public: - TriggerTrie() : d_tr( NULL ){} - Trigger* d_tr; - std::map< Node, TriggerTrie* > 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 ); - } -}; - - -}/* CVC4::theory::rrinst namespace */ - -}/* CVC4::theory namespace */ - -}/* CVC4 namespace */ - -#endif /* __CVC4__THEORY__REWRITERULES__RR_TRIGGER_H */ diff --git a/src/theory/rewriterules/theory_rewriterules.cpp b/src/theory/rewriterules/theory_rewriterules.cpp deleted file mode 100644 index 7fe625da1..000000000 --- a/src/theory/rewriterules/theory_rewriterules.cpp +++ /dev/null @@ -1,640 +0,0 @@ -/********************* */ -/*! \file theory_rewriterules.cpp - ** \verbatim - ** Original author: Morgan Deters - ** Major contributors: Francois Bobot - ** Minor contributors (to current version): Andrew Reynolds - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** 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" -#include "theory/rewriterules/options.h" - - -using namespace std; -using namespace CVC4; -using namespace CVC4::kind; -using namespace CVC4::context; -using namespace CVC4::theory; -using namespace CVC4::theory::rewriterules; -using namespace CVC4::theory::rrinst; - - -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::guards") << g << "is true" << std::endl; - ++start; - continue; - case AFALSE: - Debug("rewriterules::guards") << g << "is false" << std::endl; - return -1; - case ADONTKNOW: - Debug("rewriterules::guards") << 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) : - Theory(THEORY_REWRITERULES, c, u, out, valuation, logicInfo), - d_rules(c), d_ruleinsts(c), d_guardeds(c), d_checkLevel(c,0), - d_explanations(c), d_ruleinsts_to_add(), d_ppAssert_on(false) -{ - d_true = NodeManager::currentNM()->mkConst<bool>(true); - d_false = NodeManager::currentNM()->mkConst<bool>(false); -} - -void TheoryRewriteRules::addMatchRuleTrigger(const RewriteRule * r, - rrinst::InstMatch & im, - bool delay){ - ++r->nb_matched; - ++d_statistics.d_match_found; - if(rewrite_instantiation) im.applyRewrite(); - if(representative_instantiation) - im.makeRepresentative( getQuantifiersEngine() ); - - if(!cache_match || !r->inCache(*this,im)){ - ++r->nb_applied; - ++d_statistics.d_cache_miss; - std::vector<Node> subst; - //AJR: replaced computeTermVec with this - for( size_t i=0; i<r->inst_vars.size(); i++ ){ - Node n = im.getValue( r->inst_vars[i] ); - Assert( !n.isNull() ); - subst.push_back( n ); - } - RuleInst * ri = new RuleInst(*this,r,subst, - r->directrr ? im.d_matched : Node::null()); - Debug("rewriterules::matching") << "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); - }; - }else{ - ++d_statistics.d_cache_hit; - }; -} - -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 - - //if we are here and ppAssert has not been done - // that means that ppAssert is off so we need to assert now - if(!d_ppAssert_on) addRewriteRule(get()); - else 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") << "RewriteRules::Check start " << 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::check") << "RewriteRules::Check rule: " << *r << std::endl; - Trigger & tr = r->trigger; - //reset instantiation round for trigger (set up match production) - tr.resetInstantiationRound(); - - /** Test the possible matching one by one */ - while(tr.getNextMatch()){ - rrinst::InstMatch im = tr.getInstMatch(); - addMatchRuleTrigger(r, im, true); - } - } - - const bool do_notification = d_checkLevel == 0 || level==EFFORT_FULL; - bool polldone = false; - - if(level==EFFORT_FULL) ++d_statistics.d_full_check; - else ++d_statistics.d_check; - - 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)) continue; - 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::guards") << "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 CVC4_UNUSED; - Assert(!getValuation().hasSatValue(g,value)); - Debug("rewriterules::check") << "RewriteRules::Check Narrowing on:" << g << std::endl; - /** Can split on already rewritten instrule... but... */ - getOutputChannel().split(g); - } - } - - Assert(d_ruleinsts_to_add.empty()); - Debug("rewriterules::check") << "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, - options::efficientEMatching()? - Trigger::MATCH_GEN_EFFICIENT_E_MATCH : - Trigger::MATCH_GEN_DEFAULT, - true, - Trigger::TR_MAKE_NEW, - false); - // options::smartMultiTriggers()); -}; - -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(size_t ig = 0; - ig != lpropa->size(); ++ig) { - (*lpropa)[ig].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){ - /** 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); - }; - }; - }; -}; - - -Node TheoryRewriteRules::normalizeConjunction(NodeBuilder<> & conjunction){ - Assert(conjunction.getKind() == kind::AND); - switch(conjunction.getNumChildren()){ - case 0: - return d_true; - case 1: - return conjunction[0]; - default: - return conjunction; - } - -} - -void explainInstantiation(const RuleInst *inst, TNode substHead, NodeBuilder<> & conjunction ){ - 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(!inst->d_matched.isNull()); - Assert( inst->d_matched.getKind() == kind::APPLY_UF); - Assert( substHead.getKind() == kind::APPLY_UF ); - Assert( inst->d_matched.getOperator() == substHead.getOperator() ); - Assert(conjunction.getKind() == kind::AND); - // replace the left hand side by the term really matched - NodeBuilder<2> nb; - 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] << substHead[i]; - conjunction << static_cast<Node>(nb); - } -} - -Node skolemizeBody( Node f ){ - /*TODO skolemize the subformula of s with constant or skolemize - directly in the body of the rewrite rule with an uninterpreted - function. - */ - if ( f.getKind()!=EXISTS ) return f; - std::vector< Node > vars; - std::vector< Node > csts; - for( int i=0; i<(int)f[0].getNumChildren(); i++ ){ - csts.push_back( NodeManager::currentNM()->mkSkolem( "skolem_$$", f[0][i].getType(), "is from skolemizing the body of a rewrite rule" ) ); - vars.push_back( f[0][i] ); - } - return f[ 1 ].substitute( vars.begin(), vars.end(), - csts.begin(), csts.end() ); -} - - -void TheoryRewriteRules::propagateRule(const RuleInst * inst, TCache cache){ - // Debug("rewriterules") << "A rewrite rules is verified. Add lemma:"; - Debug("rewriterules::propagate") << "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 = skolemizeBody(inst->substNode(*this,rule->body,cache)); - if(propagate_as_lemma){ - Node lemma = equality; - if(rule->directrr){ - NodeBuilder<> conjunction(kind::AND); - explainInstantiation(inst, - rule->guards.size() > 0? - inst->substNode(*this,rule->guards[0],cache) : equality[0], - conjunction); - Debug("rewriterules-directrr") << "lemma:" << lemma << " :: " << inst->d_matched; - //rewrite rule - TypeNode booleanType = NodeManager::currentNM()->booleanType(); - if(equality[1].getType(false) == booleanType) - equality = inst->d_matched.iffNode(equality[1]); - else equality = inst->d_matched.eqNode(equality[1]); - lemma = normalizeConjunction(conjunction).impNode(equality); - Debug("rewriterules-directrr") << " -> " << lemma << std::endl; - } - else if(rule->guards.size() > 0){ - // We can use implication for reduction rules since the head is known - // to be true - NodeBuilder<> conjunction(kind::AND); - substGuards(inst,cache,conjunction); - lemma = normalizeConjunction(conjunction).impNode(equality); - } - Debug("rewriterules::propagate") << "propagated " << lemma << std::endl; - getOutputChannel().lemma(lemma); - }else{ - Node lemma_lit = equality; - if(rule->directrr && rule->guards.size() == 0) - lemma_lit = inst->d_matched.eqNode(equality[1]); // rewrite rules - lemma_lit = getValuation().ensureLiteral(lemma_lit); - 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){ - NodeBuilder<> conflict(kind::AND); - - if(rule->directrr){ - explainInstantiation(inst, - rule->guards.size() > 0? - inst->substNode(*this,rule->guards[0],cache) : equality[0], - conflict); - if(rule->guards.size() > 0){ - //reduction rule - Assert(rule->guards.size() == 1); - conflict << inst->d_matched; //this one will be two times - } - } - substGuards(inst,cache,conflict); - conflict << lemma_lit; - getOutputChannel().conflict(normalizeConjunction(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); - // }; - for(size_t i = 0; i < rule->to_remove.size(); ++i){ - Node rewritten = inst->substNode(*this,rule->to_remove[i],cache); - Debug("rewriterules::simulateRewriting") << "tag " << rewritten << " as rewritten" << std::endl; - rewritten.setAttribute(rewrittenNodeAttribute,true); - }; - - }; - - if ( compute_opt && !rule->body_match.empty() ){ - - uf::TheoryUF* uf = static_cast<uf::TheoryUF *>(getQuantifiersEngine()->getTheoryEngine()->theoryOf( theory::THEORY_UF )); - eq::EqualityEngine* ee = - static_cast<eq::EqualityEngine*>(uf->getEqualityEngine()); - - //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; - // Use trigger2 since we can be in check - ApplyMatcher * tr = r->trigger_for_body_match; - Assert(tr != NULL); - tr->resetInstantiationRound(getQuantifiersEngine()); - rrinst::InstMatch im; - TNode m = inst->substNode(*this,(*p).first, cache); - Assert( m.getKind() == kind::APPLY_UF ); - ee->addTerm(m); - if( tr->reset(m,im,getQuantifiersEngine()) ){ - im.d_matched = m; - Debug("rewriterules::matching") << "SimulatedRewrite: " << std::endl; - addMatchRuleTrigger(r, im); - } - } - - } -}; - -void TheoryRewriteRules::substGuards(const RuleInst *inst, - TCache cache, - NodeBuilder<> & conjunction){ - const RewriteRule * r = inst->rule; - /** Guards */ /* TODO remove the duplicate with a set like in uf? */ - for(std::vector<Node>::const_iterator p = r->guards.begin(); - p != r->guards.end(); ++p) { - Assert(!p->isNull()); - conjunction << inst->substNode(*this,*p,cache); - }; -} - -Node TheoryRewriteRules::explain(TNode n){ - ExplanationMap::const_iterator p = d_explanations.find(n); - Assert(p!=d_explanations.end(),"I forget the explanation..."); - RuleInst inst = (*p).second; - const RewriteRule * rule = inst.rule; - TCache cache; - NodeBuilder<> explanation(kind::AND); - if(rule->directrr){ - explainInstantiation(&inst, - rule->guards.size() > 0? - inst.substNode(*this,rule->guards[0],cache): - inst.substNode(*this,rule->body[0] ,cache), - explanation); - if(rule->guards.size() > 0){ - //reduction rule - Assert(rule->guards.size() == 1); - explanation << inst.d_matched; //this one will be two times - } - }; - substGuards(&inst, cache ,explanation); - return normalizeConjunction(explanation); -} - -void TheoryRewriteRules::collectModelInfo( TheoryModel* m, bool fullModel ){ - -} - -Theory::PPAssertStatus TheoryRewriteRules::ppAssert(TNode in, SubstitutionMap& outSubstitutions) { - //TODO: here add only to the rewriterules database for ppRewrite, - //and not for the general one. Otherwise rewriting that occur latter - //on this rewriterules will be lost. But if the rewriting of the - //body is not done in "in", will it be done latter after - //substitution? Perhaps we should add the rewriterules to the - //database for ppRewrite also after the subtitution at the levvel of check - - // addRewriteRule(in); - // d_ppAssert_on = true; - return PP_ASSERT_STATUS_UNSOLVED; -} - -TheoryRewriteRules::Statistics::Statistics(): - d_num_rewriterules("TheoryRewriteRules::Num_RewriteRules", 0), - d_check("TheoryRewriteRules::Check", 0), - d_full_check("TheoryRewriteRules::FullCheck", 0), - d_poll("TheoryRewriteRules::Poll", 0), - d_match_found("TheoryRewriteRules::MatchFound", 0), - d_cache_hit("TheoryRewriteRules::CacheHit", 0), - d_cache_miss("TheoryRewriteRules::CacheMiss", 0) -{ - StatisticsRegistry::registerStat(&d_num_rewriterules); - StatisticsRegistry::registerStat(&d_check); - StatisticsRegistry::registerStat(&d_full_check); - StatisticsRegistry::registerStat(&d_poll); - StatisticsRegistry::registerStat(&d_match_found); - StatisticsRegistry::registerStat(&d_cache_hit); - StatisticsRegistry::registerStat(&d_cache_miss); -} - -TheoryRewriteRules::Statistics::~Statistics(){ - StatisticsRegistry::unregisterStat(&d_num_rewriterules); - StatisticsRegistry::unregisterStat(&d_check); - StatisticsRegistry::unregisterStat(&d_full_check); - StatisticsRegistry::unregisterStat(&d_poll); - StatisticsRegistry::unregisterStat(&d_match_found); - StatisticsRegistry::unregisterStat(&d_cache_hit); - StatisticsRegistry::unregisterStat(&d_cache_miss); -} - - -}/* 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 deleted file mode 100644 index 2cefe7f07..000000000 --- a/src/theory/rewriterules/theory_rewriterules.h +++ /dev/null @@ -1,288 +0,0 @@ -/********************* */ -/*! \file theory_rewriterules.h - ** \verbatim - ** Original author: Morgan Deters - ** Major contributors: none - ** Minor contributors (to current version): Andrew Reynolds, Francois Bobot - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** 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/rewriterules/rr_inst_match_impl.h" -#include "theory/rewriterules/rr_trigger.h" -#include "theory/rewriterules/rr_inst_match.h" -#include "util/statistics_registry.h" -#include "theory/rewriterules/theory_rewriterules_preprocess.h" - -namespace CVC4 { -namespace theory { -namespace rewriterules { -using namespace CVC4::theory::rrinst; -typedef CVC4::theory::rrinst::Trigger Trigger; - -typedef std::hash_map<TNode, TNode, TNodeHashFunction> TCache; - - enum Answer {ATRUE, AFALSE, ADONTKNOW}; - - class TheoryRewriteRules; /** forward */ - - class RewriteRule{ - public: - // constant - const size_t id; //for debugging - 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 ApplyMatcher * 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, ApplyMatcher * 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, rrinst::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; - bool d_ppAssert_on; //Indicate if a ppAssert have been done - - 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); - - /** Usual function for theories */ - void check(Theory::Effort e); - Node explain(TNode n); - void collectModelInfo( TheoryModel* m, bool fullModel ); - 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); - - /** Auxiliary 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); - - void substGuards(const RuleInst * inst, - TCache cache, - NodeBuilder<> & conjunction); - - void addRewriteRule(const Node r); - void computeMatchBody ( const RewriteRule * r, size_t start = 0); - void addMatchRuleTrigger(const RewriteRule* r, - rrinst::InstMatch & im, bool delay = true); - - Node normalizeConjunction(NodeBuilder<> & conjunction); - - /* 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); - - //create inst variable - std::vector<Node> createInstVariable( Node r, std::vector<Node> & vars ); - - /** statistics class */ - class Statistics { - public: - IntStat d_num_rewriterules; - IntStat d_check; - IntStat d_full_check; - IntStat d_poll; - IntStat d_match_found; - IntStat d_cache_hit; - IntStat d_cache_miss; - Statistics(); - ~Statistics(); - }; - Statistics d_statistics; - -};/* 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 deleted file mode 100644 index e20556ef3..000000000 --- a/src/theory/rewriterules/theory_rewriterules_params.h +++ /dev/null @@ -1,86 +0,0 @@ -/********************* */ -/*! \file theory_rewriterules_params.h - ** \verbatim - ** Original author: Morgan Deters - ** Major contributors: none - ** Minor contributors (to current version): Francois Bobot - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** 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 - **/ - -#include "cvc4_private.h" - -#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 = 0; - -/** - Use the current model to eliminate guard before asking for notification - */ -static const bool useCurrentModel = false; - -/** - Simulate rewriting by tagging rewritten terms. - */ -static const bool simulateRewritting = true; - -/** - 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 = false; - -}/* 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 deleted file mode 100644 index cc24357c6..000000000 --- a/src/theory/rewriterules/theory_rewriterules_preprocess.h +++ /dev/null @@ -1,176 +0,0 @@ -/********************* */ -/*! \file theory_rewriterules_preprocess.h - ** \verbatim - ** Original author: Morgan Deters - ** Major contributors: none - ** Minor contributors (to current version): Tim King - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief One utilitise for rewriterules definition - ** - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_PREPROCESS_H -#define __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_PREPROCESS_H - -#include <vector> -#include <ext/hash_set> -#include <ext/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 Step */ - - struct FinalStep : Step { - Node body; - TSubst vars; - - Node subst(Subst & subst){ - return body.substitute(vars.begin(), vars.end(), - subst.begin(), subst.end()); - } - - };/* struct FinalStep */ - - 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 Pattern */ - - - 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(); - } - };/* struct Args */ - -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); - } - -};/* class RRPpRewrite */ - - - -}/* CVC4::theory::rewriterules::rewriter namespace */ - -}/* CVC4::theory::rewriterules namespace */ -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ - -#endif /* __CVC4__THEORY__REWRITERULES__THEORY_REWRITERULES_PREPROCESS_H */ diff --git a/src/theory/rewriterules/theory_rewriterules_rewriter.h b/src/theory/rewriterules/theory_rewriterules_rewriter.h deleted file mode 100644 index 511ef3515..000000000 --- a/src/theory/rewriterules/theory_rewriterules_rewriter.h +++ /dev/null @@ -1,115 +0,0 @@ -/********************* */ -/*! \file theory_rewriterules_rewriter.h - ** \verbatim - ** Original author: Morgan Deters - ** Major contributors: none - ** Minor contributors (to current version): Tim King - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** \todo document this file - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__THEORY__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); - } - - /** - * 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() { } - - typedef expr::Attribute< RewriteCacheTag<true, THEORY_REWRITERULES>, Node> pre_rewrite; - typedef expr::Attribute< RewriteCacheTag<false, THEORY_REWRITERULES>, Node> post_rewrite; -}; - -}/* 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 deleted file mode 100644 index 38e22ed64..000000000 --- a/src/theory/rewriterules/theory_rewriterules_rules.cpp +++ /dev/null @@ -1,403 +0,0 @@ -/********************* */ -/*! \file theory_rewriterules_rules.cpp - ** \verbatim - ** Original author: Morgan Deters - ** Major contributors: Francois Bobot - ** Minor contributors (to current version): Andrew Reynolds - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** 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" -#include "theory/rewriterules/options.h" - -#include "theory/quantifiers/term_database.h" - -using namespace std; -using namespace CVC4; -using namespace CVC4::kind; -using namespace CVC4::context; -using namespace CVC4::theory; -using namespace CVC4::theory::rewriterules; -using namespace CVC4::theory::rrinst; - - -namespace CVC4 { -namespace theory { -namespace rewriterules { - -// TODO replace by a real dictionnary -// We should create a real substitution? slower more precise -// We don't do that often -bool nonunifiable( TNode t0, TNode pattern, const std::vector<Node> & vars){ - typedef std::vector<std::pair<TNode,TNode> > tstack; - tstack stack(1,std::make_pair(t0,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; -} - - -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 || r->trigger_for_body_match == NULL) continue; - //the split rules are not computed and the one with multi-pattern - if( !nonunifiable(t, r->trigger_for_body_match->d_pattern, 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( - options::rewriteRulesAsAxioms()? - static_cast<Node>(tri): - re.getQuantifiersEngine()->getTermDatabase()-> - 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); - Kind rrkind = r[2].getKind(); - /* 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 = createInstVariable(r,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]; - TypeNode booleanType = NodeManager::currentNM()->booleanType(); - switch(rrkind){ - case kind::RR_REWRITE: - /* Equality */ - to_remove.push_back(head); - addPattern(*this,head,pattern,vars,inst_constants,r); - if(head.getType(false) == booleanType) body = head.iffNode(body); - else body = head.eqNode(body); - break; - case kind::RR_REDUCTION: - /** Add head to remove */ - for(Node::iterator i = head.begin(); i != head.end(); ++i) { - to_remove.push_back(*i); - }; - 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 three 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][0].begin(); i != r[2][2][0].end(); ++i) { - // todo test during typing that its a good term (no not, atom, or term...) - addPattern(*this,*i,pattern,vars,inst_constants,r); - }; - // Assert(pattern.size() == 1, "currently only single pattern are supported"); - - - - - //If we convert to usual axioms - if(options::rewriteRulesAsAxioms()){ - NodeBuilder<> forallB(kind::FORALL); - forallB << r[0]; - NodeBuilder<> guardsB(kind::AND); - guardsB.append(guards); - forallB << normalizeConjunction(guardsB).impNode(body); - NodeBuilder<> patternB(kind::INST_PATTERN); - patternB.append(pattern); - NodeBuilder<> patternListB(kind::INST_PATTERN_LIST); - patternListB << static_cast<Node>(patternB); - forallB << static_cast<Node>(patternListB); - Node lem = (Node) forallB; - lem = Rewriter::rewrite(lem); - QRewriteRuleAttribute qra; - lem.setAttribute(qra,r); - getOutputChannel().lemma(lem); - return; - } - - //turn all to propagate - // if(true){ - // NodeBuilder<> guardsB(kind::AND); - // guardsB.append(guards); - // body = normalizeConjunction(guardsB).impNode(body); - // guards.clear(); - // rrkind = kind::RR_DEDUCTION; - // } - - - //Every variable must be seen in the pattern - if (!checkPatternVars(pattern,inst_constants)){ - Warning() << Node::setdepth(-1) << "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 && rrkind == kind::RR_REWRITE - && pattern.size() == 1){ - directrr = addRewritePattern(pattern[0],new_terms, inst_constants, vars); - } - - - // final construction - Trigger trigger = createTrigger(r,pattern); - ApplyMatcher * applymatcher = - pattern.size() == 1 && pattern[0].getKind() == kind::APPLY_UF? - new ApplyMatcher(pattern[0],getQuantifiersEngine()) : NULL; - RewriteRule * rr = new RewriteRule(*this, trigger, applymatcher, - 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::new") << "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 true; - case ITE: - return true; - case NOT: - return willDecide(node[0],!positive); - default: - return false; - } -} - -static size_t id_next = 0; -RewriteRule::RewriteRule(TheoryRewriteRules & re, - Trigger & tr, ApplyMatcher * applymatcher, - std::vector<Node> & g, Node b, TNode nt, - std::vector<Node> & fv,std::vector<Node> & iv, - std::vector<Node> & to_r, bool drr) : - id(++id_next), d_split(willDecide(b)), - trigger(tr), body(b), new_terms(nt), free_vars(), inst_vars(), - body_match(re.getSatContext()),trigger_for_body_match(applymatcher), - 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, rrinst::InstMatch & im)const{ - bool res = !d_cache.addInstMatch(im); - Debug("rewriterules::matching") << "rewriterules::cache " << im - << (res ? " HIT" : " MISS") << std::endl; - return res; -}; - -/** A rewrite rule */ -void RewriteRule::toStream(std::ostream& out) const{ - out << "[" << id << "] "; - 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->id << "]" << ","; - }; - out << "}" << (directrr?"*":"") << std::endl; -} - -RewriteRule::~RewriteRule(){ - Debug("rewriterule::stats") << *this - << " (" << nb_matched - << "," << nb_applied - << "," << nb_propagated - << ")" << std::endl; - delete(trigger_for_body_match); -} - -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()->theoryOf( THEORY_UF ))-> - registerPpRewrite(op,p); - } else p = i->second; - - return p->addRewritePattern(pattern,body,pvars,vars); - -} - -std::vector<Node> TheoryRewriteRules::createInstVariable( Node r, 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 ); - InstConstantAttribute ica; - ic.setAttribute(ica,r); - //also set the no-match attribute - NoMatchAttribute nma; - ic.setAttribute(nma,true); - }; - return inst_constant; -} - - -}/* 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 deleted file mode 100644 index 308194667..000000000 --- a/src/theory/rewriterules/theory_rewriterules_rules.h +++ /dev/null @@ -1,37 +0,0 @@ -/********************* */ -/*! \file theory_rewriterules_rules.h - ** \verbatim - ** Original author: Morgan Deters - ** Major contributors: none - ** Minor contributors (to current version): none - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** 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 deleted file mode 100644 index fa6bb2227..000000000 --- a/src/theory/rewriterules/theory_rewriterules_type_rules.h +++ /dev/null @@ -1,117 +0,0 @@ -/********************* */ -/*! \file theory_rewriterules_type_rules.h - ** \verbatim - ** Original author: Morgan Deters - ** Major contributors: none - ** Minor contributors (to current version): Francois Bobot - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2013 New York University and The University of Iowa - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** \todo document this file - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__THEORY__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 nodeManager the NodeManager in use - * @param n the node to compute the type of - * @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[0], - "first argument of rewrite rule is not bound var list"); - } - if( n[ 1 ].getType(check)!=nodeManager->booleanType() ){ - throw TypeCheckingExceptionPrivate(n[1], - "guard of rewrite rule is not an actual guard"); - } - if( n[2].getType(check) != - TypeNode(nodeManager->mkTypeConst<TypeConstant>(RRHB_TYPE))){ - throw TypeCheckingExceptionPrivate(n[2], - "not a correct rewrite rule"); - } - } - return nodeManager->booleanType(); - } -};/* class RewriteRuleTypeRule */ - - -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/strings/theory_strings.cpp b/src/theory/strings/theory_strings.cpp index fd9605e59..c44d2d334 100644 --- a/src/theory/strings/theory_strings.cpp +++ b/src/theory/strings/theory_strings.cpp @@ -42,15 +42,17 @@ TheoryStrings::TheoryStrings(context::Context* c, context::UserContext* u, Outpu d_infer_exp(c), d_nf_pairs(c), d_loop_antec(u), + d_length_intro_vars(u), + d_prereg_cached(u), + d_length_nodes(u), d_length_inst(u), - d_length_nodes(c), d_str_pos_ctn(c), d_str_neg_ctn(c), d_neg_ctn_eqlen(u), d_neg_ctn_ulen(u), d_pos_ctn_cached(u), d_neg_ctn_cached(u), - d_reg_exp_mem(c), + d_regexp_memberships(c), d_regexp_ucached(u), d_regexp_ccached(c), d_regexp_ant(c), @@ -400,9 +402,10 @@ void TheoryStrings::collectModelInfo( TheoryModel* m, bool fullModel ) { ///////////////////////////////////////////////////////////////////////////// void TheoryStrings::preRegisterTerm(TNode n) { - Debug("strings-prereg") << "TheoryStrings::preRegisterTerm() " << n << endl; - //collectTerms( n ); - switch (n.getKind()) { + if(d_prereg_cached.find(n) == d_prereg_cached.end()) { + Debug("strings-prereg") << "TheoryStrings::preRegisterTerm() " << n << endl; + //collectTerms( n ); + switch (n.getKind()) { case kind::EQUAL: d_equalityEngine.addTriggerEquality(n); break; @@ -432,16 +435,18 @@ void TheoryStrings::preRegisterTerm(TNode n) { } default: { if(n.getType().isString() && n.getKind()!=kind::STRING_CONCAT && n.getKind()!=kind::CONST_STRING ) { - Node n_len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, n); - Node n_len_eq_z = n.eqNode(d_emptyString); //n_len.eqNode( d_zero ); - n_len_eq_z = Rewriter::rewrite( n_len_eq_z ); - Node n_len_geq_zero = NodeManager::currentNM()->mkNode( kind::OR, n_len_eq_z, - NodeManager::currentNM()->mkNode( kind::GT, n_len, d_zero) ); - Trace("strings-lemma") << "Strings::Lemma LENGTH >= 0 : " << n_len_geq_zero << std::endl; - d_out->lemma(n_len_geq_zero); - d_out->requirePhase( n_len_eq_z, true ); + if( d_length_intro_vars.find(n)==d_length_intro_vars.end() ) { + Node n_len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, n); + Node n_len_eq_z = n_len.eqNode( d_zero ); + n_len_eq_z = Rewriter::rewrite( n_len_eq_z ); + Node n_len_geq_zero = NodeManager::currentNM()->mkNode( kind::OR, n_len_eq_z, + NodeManager::currentNM()->mkNode( kind::GT, n_len, d_zero) ); + Trace("strings-lemma") << "Strings::Lemma LENGTH >= 0 : " << n_len_geq_zero << std::endl; + d_out->lemma(n_len_geq_zero); + d_out->requirePhase( n_len_eq_z, true ); + } // FMF - if( n.getKind() == kind::VARIABLE ) {//options::stringFMF() && + if( n.getKind() == kind::VARIABLE && options::stringFMF() ) { d_input_vars.insert(n); } } @@ -453,9 +458,71 @@ void TheoryStrings::preRegisterTerm(TNode n) { d_equalityEngine.addTerm(n); } } + } + d_prereg_cached.insert(n); } } +Node TheoryStrings::expandDefinition(LogicRequest &logicRequest, Node node) { + switch (node.getKind()) { + case kind::STRING_CHARAT: { + if(d_ufSubstr.isNull()) { + std::vector< TypeNode > argTypes; + argTypes.push_back(NodeManager::currentNM()->stringType()); + argTypes.push_back(NodeManager::currentNM()->integerType()); + argTypes.push_back(NodeManager::currentNM()->integerType()); + d_ufSubstr = NodeManager::currentNM()->mkSkolem("__ufSS", + NodeManager::currentNM()->mkFunctionType( + argTypes, NodeManager::currentNM()->stringType()), + "uf substr", + NodeManager::SKOLEM_EXACT_NAME); + } + Node lenxgti = NodeManager::currentNM()->mkNode( kind::GT, + NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, node[0] ), node[1] ); + Node zero = NodeManager::currentNM()->mkConst( ::CVC4::Rational(0) ); + Node t1greq0 = NodeManager::currentNM()->mkNode( kind::GEQ, node[1], zero); + Node cond = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::AND, lenxgti, t1greq0 )); + Node one = NodeManager::currentNM()->mkConst( ::CVC4::Rational(1) ); + Node totalf = NodeManager::currentNM()->mkNode(kind::STRING_SUBSTR_TOTAL, node[0], node[1], one); + Node uf = NodeManager::currentNM()->mkNode(kind::APPLY_UF, d_ufSubstr, node[0], node[1], one); + return NodeManager::currentNM()->mkNode( kind::ITE, cond, totalf, uf ); + } + break; + + case kind::STRING_SUBSTR: { + if(d_ufSubstr.isNull()) { + std::vector< TypeNode > argTypes; + argTypes.push_back(NodeManager::currentNM()->stringType()); + argTypes.push_back(NodeManager::currentNM()->integerType()); + argTypes.push_back(NodeManager::currentNM()->integerType()); + d_ufSubstr = NodeManager::currentNM()->mkSkolem("__ufSS", + NodeManager::currentNM()->mkFunctionType( + argTypes, NodeManager::currentNM()->stringType()), + "uf substr", + NodeManager::SKOLEM_EXACT_NAME); + } + Node lenxgti = NodeManager::currentNM()->mkNode( kind::GEQ, + NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, node[0] ), + NodeManager::currentNM()->mkNode( kind::PLUS, node[1], node[2] ) ); + Node zero = NodeManager::currentNM()->mkConst( ::CVC4::Rational(0) ); + Node t1geq0 = NodeManager::currentNM()->mkNode(kind::GEQ, node[1], zero); + Node t2geq0 = NodeManager::currentNM()->mkNode(kind::GEQ, node[2], zero); + Node cond = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::AND, lenxgti, t1geq0, t2geq0 )); + Node totalf = NodeManager::currentNM()->mkNode(kind::STRING_SUBSTR_TOTAL, node[0], node[1], node[2]); + Node uf = NodeManager::currentNM()->mkNode(kind::APPLY_UF, d_ufSubstr, node[0], node[1], node[2]); + return NodeManager::currentNM()->mkNode( kind::ITE, cond, totalf, uf ); + } + break; + + default : + return node; + break; + } + + Unreachable(); +} + + void TheoryStrings::check(Effort e) { //Assert( d_pending.empty() ); @@ -485,7 +552,7 @@ void TheoryStrings::check(Effort e) { atom = polarity ? fact : fact[0]; //must record string in regular expressions if ( atom.getKind() == kind::STRING_IN_REGEXP ) { - d_reg_exp_mem.push_back( assertion ); + d_regexp_memberships.push_back( assertion ); //d_equalityEngine.assertPredicate(atom, polarity, fact); } else if (atom.getKind() == kind::STRING_STRCTN) { if(polarity) { @@ -1785,39 +1852,41 @@ bool TheoryStrings::checkSimple() { //then, add lemma if( n.getKind() == kind::CONST_STRING || n.getKind() == kind::STRING_CONCAT ) { if( d_length_nodes.find(n)==d_length_nodes.end() ) { - if( d_length_inst.find(n)==d_length_inst.end() ) { + Trace("strings-debug") << "get n: " << n << endl; + Node sk; + //if( d_length_inst.find(n)==d_length_inst.end() ) { //Node nr = d_equalityEngine.getRepresentative( n ); - //if( d_length_nodes.find(nr)==d_length_nodes.end() ) { - d_length_inst.insert(n); - Trace("strings-debug") << "get n: " << n << endl; - Node sk = NodeManager::currentNM()->mkSkolem( "lsym_$$", n.getType(), "created for length" ); - d_statistics.d_new_skolems += 1; - Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL, sk, n ); - eq = Rewriter::rewrite(eq); - Trace("strings-lemma") << "Strings::Lemma LENGTH Term : " << eq << std::endl; - d_out->lemma(eq); - Node skl = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk ); - Node lsum; - if( n.getKind() == kind::STRING_CONCAT ) { - //add lemma - std::vector<Node> node_vec; - for( unsigned i=0; i<n.getNumChildren(); i++ ) { - Node lni = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, n[i] ); - node_vec.push_back(lni); - } - lsum = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::PLUS, node_vec ) ); - } else if( n.getKind() == kind::CONST_STRING ) { - //add lemma - lsum = NodeManager::currentNM()->mkConst( ::CVC4::Rational( n.getConst<String>().size() ) ); - } - Node ceq = NodeManager::currentNM()->mkNode( kind::EQUAL, skl, lsum ); - ceq = Rewriter::rewrite(ceq); - Trace("strings-lemma") << "Strings::Lemma LENGTH : " << ceq << std::endl; - d_out->lemma(ceq); - addedLemma = true; - //} + sk = NodeManager::currentNM()->mkSkolem( "lsym_$$", n.getType(), "created for length" ); + d_statistics.d_new_skolems += 1; + d_length_intro_vars.insert( sk ); + Node eq = sk.eqNode(n); + eq = Rewriter::rewrite(eq); + Trace("strings-lemma") << "Strings::Lemma LENGTH Term : " << eq << std::endl; + d_out->lemma(eq); + //} else { + // sk = d_length_inst[n]; + //} + Node skl = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk ); + Node lsum; + if( n.getKind() == kind::STRING_CONCAT ) { + //add lemma + std::vector<Node> node_vec; + for( unsigned i=0; i<n.getNumChildren(); i++ ) { + Node lni = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, n[i] ); + node_vec.push_back(lni); + } + lsum = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::PLUS, node_vec ) ); + } else if( n.getKind() == kind::CONST_STRING ) { + //add lemma + lsum = NodeManager::currentNM()->mkConst( ::CVC4::Rational( n.getConst<String>().size() ) ); } - d_length_nodes[n] = true; + Node ceq = NodeManager::currentNM()->mkNode( kind::EQUAL, skl, lsum ); + ceq = Rewriter::rewrite(ceq); + Trace("strings-lemma") << "Strings::Lemma LENGTH : " << ceq << std::endl; + d_out->lemma(ceq); + addedLemma = true; + + d_length_nodes.insert(n); } } ++eqc_i; @@ -2229,9 +2298,9 @@ bool TheoryStrings::checkMemberships() { bool addedLemma = false; std::vector< Node > processed; std::vector< Node > cprocessed; - for( unsigned i=0; i<d_reg_exp_mem.size(); i++ ){ + for( unsigned i=0; i<d_regexp_memberships.size(); i++ ){ //check regular expression membership - Node assertion = d_reg_exp_mem[i]; + Node assertion = d_regexp_memberships[i]; if( d_regexp_ucached.find(assertion) == d_regexp_ucached.end() && d_regexp_ccached.find(assertion) == d_regexp_ccached.end() ) { Trace("strings-regexp") << "We have regular expression assertion : " << assertion << std::endl; @@ -2730,9 +2799,13 @@ void TheoryStrings::assertNode( Node lit ) { } Node TheoryStrings::mkSplitEq( const char * c, const char * info, Node lhs, Node rhs, bool lgtZero ) { - Node sk = NodeManager::currentNM()->mkSkolem( c, lhs.getType(), info ); + Node sk = NodeManager::currentNM()->mkSkolem( c, NodeManager::currentNM()->stringType(), info ); d_statistics.d_new_skolems += 1; - Node eq = lhs.eqNode( mkConcat( rhs, sk ) ); + Node cc = mkConcat( rhs, sk ); + //if(rhs.isConst()) { + // d_length_inst[cc] = lhs; + //} + Node eq = lhs.eqNode( cc ); eq = Rewriter::rewrite( eq ); if( lgtZero ) { Node sk_gt_zero = NodeManager::currentNM()->mkNode( kind::EQUAL, sk, d_emptyString).negate(); diff --git a/src/theory/strings/theory_strings.h b/src/theory/strings/theory_strings.h index cbfa481c3..e07c61a19 100644 --- a/src/theory/strings/theory_strings.h +++ b/src/theory/strings/theory_strings.h @@ -112,6 +112,18 @@ public: };/* class TheoryStrings::NotifyClass */ private: + /** + * Function symbol used to implement uninterpreted undefined string + * semantics. Needed to deal with partial charat/substr function. + */ + Node d_ufSubstr; + + /** + * Function symbol used to implement uninterpreted undefined string + * semantics. Needed to deal with partial str2int function. + */ + Node d_ufS2I; + // Constants Node d_emptyString; Node d_emptyRegexp; @@ -156,6 +168,9 @@ private: bool isNormalFormPair2( Node n1, Node n2 ); // loop ant NodeSet d_loop_antec; + NodeSet d_length_intro_vars; + // preReg cache + NodeSet d_prereg_cached; ///////////////////////////////////////////////////////////////////////////// // MODEL GENERATION @@ -193,8 +208,8 @@ private: std::map< Node, EqcInfo* > d_eqc_info; EqcInfo * getOrMakeEqcInfo( Node eqc, bool doMake = true ); //maintain which concat terms have the length lemma instantiated - NodeSet d_length_inst; - NodeBoolMap d_length_nodes; + NodeSet d_length_nodes; + NodeNodeMap d_length_inst; private: void mergeCstVec(std::vector< Node > &vec_strings); bool getNormalForms(Node &eqc, std::vector< Node > & visited, std::vector< Node > & nf, @@ -238,6 +253,7 @@ private: public: void preRegisterTerm(TNode n); + Node expandDefinition(LogicRequest &logicRequest, Node n); void check(Effort e); /** Conflict when merging two constants */ @@ -295,7 +311,7 @@ private: // Regular Expression private: // regular expression memberships - NodeList d_reg_exp_mem; + NodeList d_regexp_memberships; NodeSet d_regexp_ucached; NodeSet d_regexp_ccached; // antecedant for why regexp membership must be true diff --git a/src/theory/theory.h b/src/theory/theory.h index e8d53e539..ff648e1f2 100644 --- a/src/theory/theory.h +++ b/src/theory/theory.h @@ -22,6 +22,7 @@ #include "expr/node.h" //#include "expr/attribute.h" #include "expr/command.h" +#include "smt/logic_request.h" #include "theory/valuation.h" #include "theory/output_channel.h" #include "theory/logic_info.h" @@ -481,7 +482,23 @@ public: virtual void finishInit() { } /** - * Pre-register a term. Done one time for a Node, ever. + * Some theories have kinds that are effectively definitions and + * should be expanded before they are handled. Definitions allow + * a much wider range of actions than the normal forms given by the + * rewriter; they can enable other theories and create new terms. + * However no assumptions can be made about subterms having been + * expanded or rewritten. Where possible rewrite rules should be + * used, definitions should only be used when rewrites are not + * possible, for example in handling under-specified operations + * using partially defined functions. + */ + virtual Node expandDefinition(LogicRequest &logicRequest, Node node) { + // by default, do nothing + return node; + } + + /** + * Pre-register a term. Done one time for a Node per SAT context level. */ virtual void preRegisterTerm(TNode) { } diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp index c67a7c4bb..33ff18126 100644 --- a/src/theory/theory_engine.cpp +++ b/src/theory/theory_engine.cpp @@ -17,6 +17,8 @@ #include <vector> #include <list> +#include "theory/arith/arith_ite_utils.h" + #include "decision/decision_engine.h" #include "expr/attribute.h" @@ -50,7 +52,7 @@ #include "theory/uf/equality_engine.h" -#include "theory/rewriterules/efficient_e_matching.h" +//#include "theory/rewriterules/efficient_e_matching.h" #include "proof/proof_manager.h" @@ -93,7 +95,7 @@ void TheoryEngine::eqNotifyNewClass(TNode t){ void TheoryEngine::eqNotifyPreMerge(TNode t1, TNode t2){ //TODO: add notification to efficient E-matching if( d_logicInfo.isQuantified() ){ - d_quantEngine->getEfficientEMatcher()->merge( t1, t2 ); + //d_quantEngine->getEfficientEMatcher()->merge( t1, t2 ); if( options::quantConflictFind() ){ d_quantEngine->getConflictFind()->merge( t1, t2 ); } @@ -149,7 +151,8 @@ TheoryEngine::TheoryEngine(context::Context* context, d_preRegistrationVisitor(this, context), d_sharedTermsVisitor(d_sharedTerms), d_unconstrainedSimp(new UnconstrainedSimplifier(context, logicInfo)), - d_bvToBoolPreprocessor() + d_bvToBoolPreprocessor(), + d_arithSubstitutionsAdded("zzz::arith::substitutions", 0) { for(TheoryId theoryId = theory::THEORY_FIRST; theoryId != theory::THEORY_LAST; ++ theoryId) { d_theoryTable[theoryId] = NULL; @@ -167,6 +170,8 @@ TheoryEngine::TheoryEngine(context::Context* context, PROOF (ProofManager::currentPM()->initTheoryProof(); ); d_iteUtilities = new ITEUtilities(d_iteRemover.getContainsVisitor()); + + StatisticsRegistry::registerStat(&d_arithSubstitutionsAdded); } TheoryEngine::~TheoryEngine() { @@ -191,6 +196,8 @@ TheoryEngine::~TheoryEngine() { delete d_unconstrainedSimp; delete d_iteUtilities; + + StatisticsRegistry::unregisterStat(&d_arithSubstitutionsAdded); } void TheoryEngine::interrupt() throw(ModalException) { @@ -1461,7 +1468,8 @@ Node TheoryEngine::ppSimpITE(TNode assertion) bool TheoryEngine::donePPSimpITE(std::vector<Node>& assertions){ bool result = true; - if(d_iteUtilities->simpIteDidALotOfWorkHeuristic()){ + bool simpDidALotOfWork = d_iteUtilities->simpIteDidALotOfWorkHeuristic(); + if(simpDidALotOfWork){ if(options::compressItes()){ result = d_iteUtilities->compress(assertions); } @@ -1480,6 +1488,57 @@ bool TheoryEngine::donePPSimpITE(std::vector<Node>& assertions){ } } } + + // Do theory specific preprocessing passes + if(d_logicInfo.isTheoryEnabled(theory::THEORY_ARITH)){ + if(!simpDidALotOfWork){ + ContainsTermITEVisitor& contains = *d_iteRemover.getContainsVisitor(); + arith::ArithIteUtils aiteu(contains, d_userContext, getModel()); + bool anyItes = false; + for(size_t i = 0; i < assertions.size(); ++i){ + Node curr = assertions[i]; + if(contains.containsTermITE(curr)){ + anyItes = true; + Node res = aiteu.reduceVariablesInItes(curr); + Debug("arith::ite::red") << "@ " << i << " ... " << curr << endl << " ->" << res << endl; + if(curr != res){ + Node more = aiteu.reduceConstantIteByGCD(res); + Debug("arith::ite::red") << " gcd->" << more << endl; + assertions[i] = more; + } + } + } + if(!anyItes){ + unsigned prevSubCount = aiteu.getSubCount(); + aiteu.learnSubstitutions(assertions); + if(prevSubCount < aiteu.getSubCount()){ + d_arithSubstitutionsAdded += aiteu.getSubCount() - prevSubCount; + bool anySuccess = false; + for(size_t i = 0, N = assertions.size(); i < N; ++i){ + Node curr = assertions[i]; + Node next = Rewriter::rewrite(aiteu.applySubstitutions(curr)); + Node res = aiteu.reduceVariablesInItes(next); + Debug("arith::ite::red") << "@ " << i << " ... " << next << endl << " ->" << res << endl; + Node more = aiteu.reduceConstantIteByGCD(res); + Debug("arith::ite::red") << " gcd->" << more << endl; + if(more != next){ + anySuccess = true; + break; + } + } + for(size_t i = 0, N = assertions.size(); anySuccess && i < N; ++i){ + Node curr = assertions[i]; + Node next = Rewriter::rewrite(aiteu.applySubstitutions(curr)); + Node res = aiteu.reduceVariablesInItes(next); + Debug("arith::ite::red") << "@ " << i << " ... " << next << endl << " ->" << res << endl; + Node more = aiteu.reduceConstantIteByGCD(res); + Debug("arith::ite::red") << " gcd->" << more << endl; + assertions[i] = Rewriter::rewrite(more); + } + } + } + } + } return result; } @@ -1580,22 +1639,20 @@ void TheoryEngine::handleUserAttribute(const char* attr, Theory* t) { void TheoryEngine::checkTheoryAssertionsWithModel() { for(TheoryId theoryId = THEORY_FIRST; theoryId < THEORY_LAST; ++theoryId) { - if(theoryId != THEORY_REWRITERULES) { - Theory* theory = d_theoryTable[theoryId]; - if(theory && d_logicInfo.isTheoryEnabled(theoryId)) { - for(context::CDList<Assertion>::const_iterator it = theory->facts_begin(), - it_end = theory->facts_end(); - it != it_end; - ++it) { - Node assertion = (*it).assertion; - Node val = getModel()->getValue(assertion); - if(val != d_true) { - stringstream ss; - ss << theoryId << " has an asserted fact that the model doesn't satisfy." << endl - << "The fact: " << assertion << endl - << "Model value: " << val << endl; - InternalError(ss.str()); - } + Theory* theory = d_theoryTable[theoryId]; + if(theory && d_logicInfo.isTheoryEnabled(theoryId)) { + for(context::CDList<Assertion>::const_iterator it = theory->facts_begin(), + it_end = theory->facts_end(); + it != it_end; + ++it) { + Node assertion = (*it).assertion; + Node val = getModel()->getValue(assertion); + if(val != d_true) { + stringstream ss; + ss << theoryId << " has an asserted fact that the model doesn't satisfy." << endl + << "The fact: " << assertion << endl + << "Model value: " << val << endl; + InternalError(ss.str()); } } } diff --git a/src/theory/theory_engine.h b/src/theory/theory_engine.h index db31ef9b7..9a987c9d7 100644 --- a/src/theory/theory_engine.h +++ b/src/theory/theory_engine.h @@ -818,6 +818,9 @@ public: */ void checkTheoryAssertionsWithModel(); +private: + IntStat d_arithSubstitutionsAdded; + };/* class TheoryEngine */ }/* CVC4 namespace */ diff --git a/src/theory/uf/equality_engine.cpp b/src/theory/uf/equality_engine.cpp index df1d2ebde..0fb42231f 100644 --- a/src/theory/uf/equality_engine.cpp +++ b/src/theory/uf/equality_engine.cpp @@ -3,7 +3,7 @@ ** \verbatim ** Original author: Dejan Jovanovic ** Major contributors: none - ** Minor contributors (to current version): Dejan Jovanovic, Tim King, Francois Bobot, Morgan Deters + ** Minor contributors (to current version): Dejan Jovanovic, Tim King, Francois Bobot, Morgan Deters, Andrew Reynolds ** This file is part of the CVC4 project. ** Copyright (c) 2009-2013 New York University and The University of Iowa ** See the file COPYING in the top-level source directory for licensing @@ -364,9 +364,9 @@ const EqualityNode& EqualityEngine::getEqualityNode(EqualityNodeId nodeId) const return d_equalityNodes[nodeId]; } -void EqualityEngine::assertEqualityInternal(TNode t1, TNode t2, TNode reason) { +void EqualityEngine::assertEqualityInternal(TNode t1, TNode t2, TNode reason, MergeReasonType pid) { - Debug("equality") << d_name << "::eq::addEqualityInternal(" << t1 << "," << t2 << ")" << std::endl; + Debug("equality") << d_name << "::eq::addEqualityInternal(" << t1 << "," << t2 << "), pid = " << pid << std::endl; if (d_done) { return; @@ -379,13 +379,13 @@ void EqualityEngine::assertEqualityInternal(TNode t1, TNode t2, TNode reason) { // Add to the queue and propagate EqualityNodeId t1Id = getNodeId(t1); EqualityNodeId t2Id = getNodeId(t2); - enqueue(MergeCandidate(t1Id, t2Id, MERGED_THROUGH_EQUALITY, reason)); + enqueue(MergeCandidate(t1Id, t2Id, pid, reason)); } -void EqualityEngine::assertPredicate(TNode t, bool polarity, TNode reason) { +void EqualityEngine::assertPredicate(TNode t, bool polarity, TNode reason, MergeReasonType pid) { Debug("equality") << d_name << "::eq::addPredicate(" << t << "," << (polarity ? "true" : "false") << ")" << std::endl; Assert(t.getKind() != kind::EQUAL, "Use assertEquality instead"); - assertEqualityInternal(t, polarity ? d_true : d_false, reason); + assertEqualityInternal(t, polarity ? d_true : d_false, reason, pid); propagate(); } @@ -395,7 +395,7 @@ void EqualityEngine::mergePredicates(TNode p, TNode q, TNode reason) { propagate(); } -void EqualityEngine::assertEquality(TNode eq, bool polarity, TNode reason) { +void EqualityEngine::assertEquality(TNode eq, bool polarity, TNode reason, MergeReasonType pid) { Debug("equality") << d_name << "::eq::addEquality(" << eq << "," << (polarity ? "true" : "false") << ")" << std::endl; if (polarity) { // If two terms are already equal, don't assert anything @@ -403,7 +403,7 @@ void EqualityEngine::assertEquality(TNode eq, bool polarity, TNode reason) { return; } // Add equality between terms - assertEqualityInternal(eq[0], eq[1], reason); + assertEqualityInternal(eq[0], eq[1], reason, pid); propagate(); } else { // If two terms are already dis-equal, don't assert anything @@ -418,7 +418,7 @@ void EqualityEngine::assertEquality(TNode eq, bool polarity, TNode reason) { Debug("equality::trigger") << d_name << "::eq::addEquality(" << eq << "," << (polarity ? "true" : "false") << ")" << std::endl; - assertEqualityInternal(eq, d_false, reason); + assertEqualityInternal(eq, d_false, reason, pid); propagate(); if (d_done) { @@ -1028,14 +1028,6 @@ void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, st Debug("equality") << pop; break; } - case MERGED_THROUGH_EQUALITY: - // Construct the equality - Debug("equality") << d_name << "::eq::getExplanation(): adding: " << d_equalityEdges[currentEdge].getReason() << std::endl; - if( eqpc ){ - eqpc->d_node = d_equalityEdges[currentEdge].getReason(); - } - equalities.push_back(d_equalityEdges[currentEdge].getReason()); - break; case MERGED_THROUGH_REFLEXIVITY: { // x1 == x1 Debug("equality") << d_name << "::eq::getExplanation(): due to reflexivity, going deeper" << std::endl; @@ -1080,8 +1072,21 @@ void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, st break; } - default: - Unreachable(); + default: { + // Construct the equality + Debug("equality") << d_name << "::eq::getExplanation(): adding: " << d_equalityEdges[currentEdge].getReason() << std::endl; + if( eqpc ){ + if( reasonType==MERGED_THROUGH_EQUALITY ){ + eqpc->d_node = d_equalityEdges[currentEdge].getReason(); + }else{ + //theory-specific proof rule : TODO + eqpc->d_id = reasonType; + //eqpc->d_node = d_equalityEdges[currentEdge].getNodeId(); + } + } + equalities.push_back(d_equalityEdges[currentEdge].getReason()); + break; + } } // Go to the previous @@ -2054,7 +2059,7 @@ void EqProof::debug_print( const char * c, unsigned tb ){ } for( unsigned i=0; i<d_children.size(); i++ ){ if( i>0 || !d_node.isNull() ) Debug( c ) << ","; - std::cout << std::endl; + Debug( c ) << std::endl; d_children[i]->debug_print( c, tb+1 ); } } diff --git a/src/theory/uf/equality_engine.h b/src/theory/uf/equality_engine.h index 8bb849496..2961b510a 100644 --- a/src/theory/uf/equality_engine.h +++ b/src/theory/uf/equality_engine.h @@ -521,7 +521,7 @@ private: /** * Adds an equality of terms t1 and t2 to the database. */ - void assertEqualityInternal(TNode t1, TNode t2, TNode reason); + void assertEqualityInternal(TNode t1, TNode t2, TNode reason, MergeReasonType pid = MERGED_THROUGH_EQUALITY); /** * Adds a trigger equality to the database with the trigger node and polarity for notification. @@ -751,7 +751,7 @@ public: * asserting the negated predicate * @param reason the reason to keep for building explanations */ - void assertPredicate(TNode p, bool polarity, TNode reason); + void assertPredicate(TNode p, bool polarity, TNode reason, MergeReasonType pid = MERGED_THROUGH_EQUALITY); /** * Adds predicate p and q and makes them equal. @@ -766,7 +766,7 @@ public: * asserting the negated equality * @param reason the reason to keep for building explanations */ - void assertEquality(TNode eq, bool polarity, TNode reason); + void assertEquality(TNode eq, bool polarity, TNode reason, MergeReasonType pid = MERGED_THROUGH_EQUALITY); /** * Returns the current representative of the term t. diff --git a/src/theory/uf/equality_engine_types.h b/src/theory/uf/equality_engine_types.h index 435a1ece5..0ee50c74d 100644 --- a/src/theory/uf/equality_engine_types.h +++ b/src/theory/uf/equality_engine_types.h @@ -70,6 +70,10 @@ enum MergeReasonType { /** (for proofs only) Equality was merged due to transitivity */ MERGED_THROUGH_TRANS, + + /** Theory specific proof rules */ + MERGED_ARRAYS_ROW, + MERGED_ARRAYS_ROW1, }; inline std::ostream& operator << (std::ostream& out, MergeReasonType reason) { @@ -91,7 +95,8 @@ inline std::ostream& operator << (std::ostream& out, MergeReasonType reason) { out << "transitivity"; break; default: - Unreachable(); + out << "[theory]"; + break; } return out; } diff --git a/src/util/dump.h b/src/util/dump.h index 0bde68d76..a85062af1 100644 --- a/src/util/dump.h +++ b/src/util/dump.h @@ -14,7 +14,7 @@ ** Dump utility classes and functions. **/ -#include "cvc4_public.h" +#include "cvc4_private.h" #ifndef __CVC4__DUMP_H #define __CVC4__DUMP_H diff --git a/src/util/ite_removal.cpp b/src/util/ite_removal.cpp index 0dfea4399..1b29f9ef8 100644 --- a/src/util/ite_removal.cpp +++ b/src/util/ite_removal.cpp @@ -18,7 +18,6 @@ #include "util/ite_removal.h" #include "expr/command.h" -#include "theory/quantifiers/options.h" #include "theory/ite_utilities.h" using namespace CVC4; @@ -29,7 +28,7 @@ namespace CVC4 { RemoveITE::RemoveITE(context::UserContext* u) : d_iteCache(u) { - d_containsVisitor = new theory::ContainsTermITEVistor(); + d_containsVisitor = new theory::ContainsTermITEVisitor(); } RemoveITE::~RemoveITE(){ @@ -40,7 +39,7 @@ void RemoveITE::garbageCollect(){ d_containsVisitor->garbageCollect(); } -theory::ContainsTermITEVistor* RemoveITE::getContainsVisitor(){ +theory::ContainsTermITEVisitor* RemoveITE::getContainsVisitor() { return d_containsVisitor; } @@ -51,22 +50,20 @@ size_t RemoveITE::collectedCacheSizes() const{ void RemoveITE::run(std::vector<Node>& output, IteSkolemMap& iteSkolemMap) { for (unsigned i = 0, i_end = output.size(); i < i_end; ++ i) { - std::vector<Node> quantVar; // Do this in two steps to avoid Node problems(?) // Appears related to bug 512, splitting this into two lines // fixes the bug on clang on Mac OS - Node itesRemoved = run(output[i], output, iteSkolemMap, quantVar); + Node itesRemoved = run(output[i], output, iteSkolemMap, false); output[i] = itesRemoved; } } -bool RemoveITE::containsTermITE(TNode e){ +bool RemoveITE::containsTermITE(TNode e) const { return d_containsVisitor->containsTermITE(e); } Node RemoveITE::run(TNode node, std::vector<Node>& output, - IteSkolemMap& iteSkolemMap, - std::vector<Node>& quantVar) { + IteSkolemMap& iteSkolemMap, bool inQuant) { // Current node Debug("ite") << "removeITEs(" << node << ")" << endl; @@ -76,35 +73,27 @@ Node RemoveITE::run(TNode node, std::vector<Node>& output, } // The result may be cached already + std::pair<Node, bool> cacheKey(node, inQuant); NodeManager *nodeManager = NodeManager::currentNM(); - ITECache::const_iterator i = d_iteCache.find(node); + ITECache::const_iterator i = d_iteCache.find(cacheKey); if(i != d_iteCache.end()) { Node cached = (*i).second; Debug("ite") << "removeITEs: in-cache: " << cached << endl; return cached.isNull() ? Node(node) : cached; } + // Remember that we're inside a quantifier + if(node.getKind() == kind::FORALL || node.getKind() == kind::EXISTS) { + inQuant = true; + } + // If an ITE replace it if(node.getKind() == kind::ITE) { TypeNode nodeType = node.getType(); - if(!nodeType.isBoolean()) { + if(!nodeType.isBoolean() && (!inQuant || !node.hasBoundVar())) { Node skolem; // Make the skolem to represent the ITE - if( quantVar.empty() ){ - skolem = nodeManager->mkSkolem("termITE_$$", nodeType, "a variable introduced due to term-level ITE removal"); - }else{ - //if in the scope of free variables, make a skolem operator - vector< TypeNode > argTypes; - for( size_t i=0; i<quantVar.size(); i++ ){ - argTypes.push_back( quantVar[i].getType() ); - } - TypeNode typ = NodeManager::currentNM()->mkFunctionType( argTypes, nodeType ); - Node op = NodeManager::currentNM()->mkSkolem( "termITEop_$$", typ, "a function variable introduced due to term-level ITE removal" ); - vector< Node > funcArgs; - funcArgs.push_back( op ); - funcArgs.insert( funcArgs.end(), quantVar.begin(), quantVar.end() ); - skolem = NodeManager::currentNM()->mkNode( kind::APPLY_UF, funcArgs ); - } + skolem = nodeManager->mkSkolem("termITE_$$", nodeType, "a variable introduced due to term-level ITE removal"); // The new assertion Node newAssertion = @@ -113,18 +102,10 @@ Node RemoveITE::run(TNode node, std::vector<Node>& output, Debug("ite") << "removeITEs(" << node << ") => " << newAssertion << endl; // Attach the skolem - d_iteCache.insert(node, skolem); + d_iteCache.insert(cacheKey, skolem); // Remove ITEs from the new assertion, rewrite it and push it to the output - newAssertion = run(newAssertion, output, iteSkolemMap, quantVar); - - if( !quantVar.empty() ){ - //if in the scope of free variables, it is a quantified assertion - vector< Node > children; - children.push_back( NodeManager::currentNM()->mkNode( kind::BOUND_VAR_LIST, quantVar ) ); - children.push_back( newAssertion ); - newAssertion = NodeManager::currentNM()->mkNode( kind::FORALL, children ); - } + newAssertion = run(newAssertion, output, iteSkolemMap, inQuant); iteSkolemMap[skolem] = output.size(); output.push_back(newAssertion); @@ -135,42 +116,66 @@ Node RemoveITE::run(TNode node, std::vector<Node>& output, } // If not an ITE, go deep - if( ( node.getKind() != kind::FORALL || options::iteRemoveQuant() ) && - node.getKind() != kind::EXISTS && - node.getKind() != kind::REWRITE_RULE ) { - std::vector< Node > newQuantVar; - newQuantVar.insert( newQuantVar.end(), quantVar.begin(), quantVar.end() ); - if( node.getKind()==kind::FORALL ){ - for( size_t i=0; i<node[0].getNumChildren(); i++ ){ - newQuantVar.push_back( node[0][i] ); - } - } - 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, newQuantVar); - somethingChanged |= (newChild != *it); - newChildren.push_back(newChild); - } + 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, inQuant); + somethingChanged |= (newChild != *it); + newChildren.push_back(newChild); + } - // If changes, we rewrite - if(somethingChanged) { - Node cached = nodeManager->mkNode(node.getKind(), newChildren); - d_iteCache.insert(node, cached); - return cached; - } else { - d_iteCache.insert(node, Node::null()); - return node; - } + // If changes, we rewrite + if(somethingChanged) { + Node cached = nodeManager->mkNode(node.getKind(), newChildren); + d_iteCache.insert(cacheKey, cached); + return cached; } else { - d_iteCache.insert(node, Node::null()); + d_iteCache.insert(cacheKey, Node::null()); return node; } } +Node RemoveITE::replace(TNode node, bool inQuant) const { + if(node.isVar() || node.isConst() || + (options::biasedITERemoval() && !containsTermITE(node))){ + return Node(node); + } + + // Check the cache + NodeManager *nodeManager = NodeManager::currentNM(); + ITECache::const_iterator i = d_iteCache.find(make_pair(node, inQuant)); + if(i != d_iteCache.end()) { + Node cached = (*i).second; + return cached.isNull() ? Node(node) : cached; + } + + // Remember that we're inside a quantifier + if(node.getKind() == kind::FORALL || node.getKind() == kind::EXISTS) { + inQuant = true; + } + + vector<Node> newChildren; + bool somethingChanged = false; + if(node.getMetaKind() == kind::metakind::PARAMETERIZED) { + newChildren.push_back(node.getOperator()); + } + // Replace in children + for(TNode::const_iterator it = node.begin(), end = node.end(); it != end; ++it) { + Node newChild = replace(*it, inQuant); + somethingChanged |= (newChild != *it); + newChildren.push_back(newChild); + } + + // If changes, we rewrite + if(somethingChanged) { + return nodeManager->mkNode(node.getKind(), newChildren); + } else { + return node; + } +} }/* CVC4 namespace */ diff --git a/src/util/ite_removal.h b/src/util/ite_removal.h index c2464636e..de5f83f27 100644 --- a/src/util/ite_removal.h +++ b/src/util/ite_removal.h @@ -23,17 +23,19 @@ #include "util/dump.h" #include "context/context.h" #include "context/cdinsert_hashmap.h" +#include "util/hash.h" +#include "util/bool.h" namespace CVC4 { namespace theory { -class ContainsTermITEVistor; -} + class ContainsTermITEVisitor; +}/* CVC4::theory namespace */ typedef std::hash_map<Node, unsigned, NodeHashFunction> IteSkolemMap; class RemoveITE { - typedef context::CDInsertHashMap<Node, Node, NodeHashFunction> ITECache; + typedef context::CDInsertHashMap< std::pair<Node, bool>, Node, PairHashFunction<Node, bool, NodeHashFunction, BoolHashFunction> > ITECache; ITECache d_iteCache; @@ -59,22 +61,28 @@ public: * ite created in conjunction with that skolem variable. */ Node run(TNode node, std::vector<Node>& additionalAssertions, - IteSkolemMap& iteSkolemMap, std::vector<Node>& quantVar); + IteSkolemMap& iteSkolemMap, bool inQuant); - /** Returns true if e contains a term ite.*/ - bool containsTermITE(TNode e); + /** + * Substitute under node using pre-existing cache. Do not remove + * any ITEs not seen during previous runs. + */ + Node replace(TNode node, bool inQuant = false) const; + + /** Returns true if e contains a term ite. */ + bool containsTermITE(TNode e) const; - /** Returns the collected size of the caches.*/ + /** Returns the collected size of the caches. */ size_t collectedCacheSizes() const; - /** Garbage collects non-context dependent data-structures.*/ + /** Garbage collects non-context dependent data-structures. */ void garbageCollect(); - /** Return the RemoveITE's containsVisitor.*/ - theory::ContainsTermITEVistor* getContainsVisitor(); + /** Return the RemoveITE's containsVisitor. */ + theory::ContainsTermITEVisitor* getContainsVisitor(); private: - theory::ContainsTermITEVistor* d_containsVisitor; + theory::ContainsTermITEVisitor* d_containsVisitor; };/* class RemoveTTE */ diff --git a/src/util/rational.i b/src/util/rational.i index a65c78327..a9e3e23f8 100644 --- a/src/util/rational.i +++ b/src/util/rational.i @@ -2,6 +2,8 @@ #include "util/rational.h" %} +%ignore CVC4::RationalFromDoubleException::RationalFromDoubleException(double); + %ignore CVC4::Rational::Rational(int); %ignore CVC4::Rational::Rational(unsigned int); %ignore CVC4::Rational::Rational(int, int); diff --git a/src/util/rational_cln_imp.cpp b/src/util/rational_cln_imp.cpp index 2b29ece22..f674481de 100644 --- a/src/util/rational_cln_imp.cpp +++ b/src/util/rational_cln_imp.cpp @@ -17,6 +17,7 @@ #include "cvc4autoconfig.h" #include "util/rational.h" #include <string> +#include <sstream> #ifndef CVC4_CLN_IMP # error "This source should only ever be built if CVC4_CLN_IMP is on !" @@ -50,3 +51,56 @@ std::ostream& CVC4::operator<<(std::ostream& os, const Rational& q){ return os << q.toString(); } + + +/** Equivalent to calling (this->abs()).cmp(b.abs()) */ +int Rational::absCmp(const Rational& q) const{ + const Rational& r = *this; + int rsgn = r.sgn(); + int qsgn = q.sgn(); + if(rsgn == 0){ + return (qsgn == 0) ? 0 : -1; + }else if(qsgn == 0){ + Assert(rsgn != 0); + return 1; + }else if((rsgn > 0) && (qsgn > 0)){ + return r.cmp(q); + }else if((rsgn < 0) && (qsgn < 0)){ + // if r < q < 0, q.cmp(r) = +1, (r.abs()).cmp(q.abs()) = +1 + // if q < r < 0, q.cmp(r) = -1, (r.abs()).cmp(q.abs()) = -1 + // if q = r < 0, q.cmp(r) = 0, (r.abs()).cmp(q.abs()) = 0 + return q.cmp(r); + }else if((rsgn < 0) && (qsgn > 0)){ + Rational rpos = -r; + return rpos.cmp(q); + }else { + Assert(rsgn > 0 && (qsgn < 0)); + Rational qpos = -q; + return r.cmp(qpos); + } +} + +Rational Rational::fromDouble(double d) throw(RationalFromDoubleException){ + try{ + cln::cl_DF fromD = d; + Rational q; + q.d_value = cln::rationalize(fromD); + return q; + }catch(cln::floating_point_underflow_exception& fpue){ + throw RationalFromDoubleException(d); + }catch(cln::floating_point_nan_exception& fpne){ + throw RationalFromDoubleException(d); + }catch(cln::floating_point_overflow_exception& fpoe){ + throw RationalFromDoubleException(d); + } +} + +RationalFromDoubleException::RationalFromDoubleException(double d) throw() + : Exception() +{ + std::stringstream ss; + ss << "RationalFromDoubleException("; + ss << d; + ss << ")"; + setMessage(ss.str()); +} diff --git a/src/util/rational_cln_imp.h b/src/util/rational_cln_imp.h index da2af6c1f..b144ab419 100644 --- a/src/util/rational_cln_imp.h +++ b/src/util/rational_cln_imp.h @@ -38,6 +38,11 @@ namespace CVC4 { +class CVC4_PUBLIC RationalFromDoubleException : public Exception { +public: + RationalFromDoubleException(double d) throw(); +}; + /** ** A multi-precision rational constant. ** This stores the rational as a pair of multi-precision integers, @@ -189,12 +194,7 @@ public: } /** Return an exact rational for a double d. */ - static Rational fromDouble(double d){ - cln::cl_DF fromD = d; - Rational q; - q.d_value = cln::rationalize(fromD); - return q; - } + static Rational fromDouble(double d) throw(RationalFromDoubleException); /** * Get a double representation of this Rational, which is @@ -259,6 +259,10 @@ public: return Integer(cln::ceiling1(d_value)); } + Rational floor_frac() const { + return (*this) - Rational(floor()); + } + Rational& operator=(const Rational& x){ if(this == &x) return *this; d_value = x.d_value; @@ -349,6 +353,9 @@ public: return getNumerator().length() + getDenominator().length(); } + /** Equivalent to calling (this->abs()).cmp(b.abs()) */ + int absCmp(const Rational& q) const; + };/* class Rational */ struct RationalHashFunction { diff --git a/src/util/rational_gmp_imp.cpp b/src/util/rational_gmp_imp.cpp index d496803dc..25c7dab59 100644 --- a/src/util/rational_gmp_imp.cpp +++ b/src/util/rational_gmp_imp.cpp @@ -17,6 +17,8 @@ #include "cvc4autoconfig.h" #include "util/rational.h" #include <string> +#include <sstream> +#include <cmath> #ifndef CVC4_GMP_IMP # error "This source should only ever be built if CVC4_GMP_IMP is on !" @@ -50,3 +52,52 @@ std::ostream& CVC4::operator<<(std::ostream& os, const Rational& q){ return os << q.toString(); } + +/** Equivalent to calling (this->abs()).cmp(b.abs()) */ +int Rational::absCmp(const Rational& q) const{ + const Rational& r = *this; + int rsgn = r.sgn(); + int qsgn = q.sgn(); + if(rsgn == 0){ + return (qsgn == 0) ? 0 : -1; + }else if(qsgn == 0){ + Assert(rsgn != 0); + return 1; + }else if((rsgn > 0) && (qsgn > 0)){ + return r.cmp(q); + }else if((rsgn < 0) && (qsgn < 0)){ + // if r < q < 0, q.cmp(r) = +1, (r.abs()).cmp(q.abs()) = +1 + // if q < r < 0, q.cmp(r) = -1, (r.abs()).cmp(q.abs()) = -1 + // if q = r < 0, q.cmp(r) = 0, (r.abs()).cmp(q.abs()) = 0 + return q.cmp(r); + }else if((rsgn < 0) && (qsgn > 0)){ + Rational rpos = -r; + return rpos.cmp(q); + }else { + Assert(rsgn > 0 && (qsgn < 0)); + Rational qpos = -q; + return r.cmp(qpos); + } +} + + +/** Return an exact rational for a double d. */ +Rational Rational::fromDouble(double d) throw(RationalFromDoubleException){ + if(std::isfinite(d)){ + Rational q; + mpq_set_d(q.d_value.get_mpq_t(), d); + return q; + } + + throw RationalFromDoubleException(d); +} + +RationalFromDoubleException::RationalFromDoubleException(double d) throw() + : Exception() +{ + std::stringstream ss; + ss << "RationalFromDoubleException("; + ss << d; + ss << ")"; + setMessage(ss.str()); +} diff --git a/src/util/rational_gmp_imp.h b/src/util/rational_gmp_imp.h index 02ccc273c..273b3072d 100644 --- a/src/util/rational_gmp_imp.h +++ b/src/util/rational_gmp_imp.h @@ -28,6 +28,11 @@ namespace CVC4 { +class CVC4_PUBLIC RationalFromDoubleException : public Exception { +public: + RationalFromDoubleException(double d) throw(); +}; + /** ** A multi-precision rational constant. ** This stores the rational as a pair of multi-precision integers, @@ -172,12 +177,7 @@ public: return Integer(d_value.get_den()); } - /** Return an exact rational for a double d. */ - static Rational fromDouble(double d){ - Rational q; - mpq_set_d(q.d_value.get_mpq_t(), d); - return q; - } + static Rational fromDouble(double d) throw(RationalFromDoubleException); /** * Get a double representation of this Rational, which is @@ -234,6 +234,10 @@ public: return Integer(q); } + Rational floor_frac() const { + return (*this) - Rational(floor()); + } + Rational& operator=(const Rational& x){ if(this == &x) return *this; d_value = x.d_value; @@ -326,6 +330,10 @@ public: uint32_t denLen = getDenominator().length(); return numLen + denLen; } + + /** Equivalent to calling (this->abs()).cmp(b.abs()) */ + int absCmp(const Rational& q) const; + };/* class Rational */ struct RationalHashFunction { |