diff options
Diffstat (limited to 'src')
316 files changed, 16512 insertions, 8055 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 64d95deed..e6d5f80ed 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -110,7 +110,7 @@ svn_versioninfo.cpp: svninfo # This .tmp business is to keep from having to re-compile options.cpp # (and then re-link the libraries) if nothing has changed. svninfo: svninfo.tmp - $(AM_V_GEN)diff -q svninfo.tmp svninfo &>/dev/null || mv svninfo.tmp svninfo || true + $(AM_V_GEN)if diff -q svninfo.tmp svninfo &>/dev/null; then rm -f svninfo.tmp; else mv svninfo.tmp svninfo; fi # .PHONY ensures the .tmp version is always rebuilt (to check for any changes) .PHONY: svninfo.tmp svninfo.tmp: @@ -138,7 +138,7 @@ git_versioninfo.cpp: gitinfo # This .tmp business is to keep from having to re-compile options.cpp # (and then re-link the libraries) if nothing has changed. gitinfo: gitinfo.tmp - $(AM_V_GEN)diff -q gitinfo.tmp gitinfo &>/dev/null || mv gitinfo.tmp gitinfo || true + $(AM_V_GEN)if diff -q gitinfo.tmp gitinfo &>/dev/null; then rm -f gitinfo.tmp; else mv gitinfo.tmp gitinfo; fi || true # .PHONY ensures the .tmp version is always rebuilt (to check for any changes) .PHONY: gitinfo.tmp gitinfo.tmp: @@ -163,15 +163,18 @@ install-data-local: $(mkinstalldirs) "$$(dirname "$(DESTDIR)$(includedir)/cvc4/$$d")"; \ if [ -e "$$f" ]; then \ path="$$f"; \ - fixpath="$$f.fix"; \ else \ path="$(srcdir)/$$f"; \ - fixpath="$(builddir)/$$f.fix"; \ - $(MKDIR_P) "`dirname "$$fixpath"`"; \ fi; \ + fixpath="$(top_builddir)/header_install.fix"; \ sed 's,^\([ \t]*#[ \t]*include[ \t*]\)"\(.*\)"\([ \t]*\)$$,\1<cvc4/\2>\3,' "$$path" > "$$fixpath" || exit 1; \ echo $(INSTALL_DATA) "$$fixpath" "$(DESTDIR)$(includedir)/cvc4/$$d"; \ - $(INSTALL_DATA) "$$fixpath" "$(DESTDIR)$(includedir)/cvc4/$$d" || exit 1; \ + if $(INSTALL_DATA) "$$fixpath" "$(DESTDIR)$(includedir)/cvc4/$$d"; then \ + rm -f "$$fixpath"; \ + else \ + rm -f "$$fixpath"; \ + exit 1; \ + fi; \ done uninstall-local: @@ -196,34 +199,3 @@ uninstall-local: -rmdir "$(DESTDIR)$(includedir)/cvc4/bindings" -rmdir "$(DESTDIR)$(includedir)/cvc4" -rmdir "$(DESTDIR)$(libdir)/ocaml/cvc4" - -# clean up the .fix files -mostlyclean-local: - (echo include/cvc4.h; \ - echo include/cvc4_public.h; \ - echo include/cvc4parser_public.h; \ - echo util/tls.h; \ - echo util/integer.h; \ - echo util/rational.h; \ - find * -name '*.h.fix' | \ - xargs grep -l '^# *include *<cvc4/cvc4.*_public\.h>' | \ - sed 's,\.fix$$,,'; \ - (cd "$(srcdir)" && find * -name '*.h' | \ - xargs grep -l '^# *include *"cvc4.*_public\.h"')) | \ - while read f; do \ - if expr "$$f" : ".*_\(template\|private\|private_library\|test_utils\)\.h$$" &>/dev/null; then \ - continue; \ - fi; \ - d="$$(echo "$$f" | sed 's,^include/,,')"; \ - if [ -e "$$f" ]; then \ - path="$$f"; \ - fixpath="$$f.fix"; \ - else \ - path="$(srcdir)/$$f"; \ - fixpath="$(builddir)/$$f.fix"; \ - $(MKDIR_P) "`dirname "$$fixpath"`"; \ - fi; \ - echo rm -f "$$fixpath"; \ - rm -f "$$fixpath"; \ - done - diff --git a/src/bindings/Makefile.am b/src/bindings/Makefile.am index cc2a7c53f..80e65d180 100644 --- a/src/bindings/Makefile.am +++ b/src/bindings/Makefile.am @@ -142,7 +142,7 @@ endif endif nodist_java_libcvc4jni_la_SOURCES = java.cpp -java_libcvc4jni_la_CXXFLAGS = @FNO_STRICT_ALIASING@ @WNO_UNUSED_VARIABLE@ @WNO_UNINITIALIZED@ +java_libcvc4jni_la_CXXFLAGS = -Wno-all @FNO_STRICT_ALIASING@ @WNO_UNUSED_VARIABLE@ @WNO_UNINITIALIZED@ nodist_csharp_CVC4_la_SOURCES = csharp.cpp nodist_perl_CVC4_la_SOURCES = perl.cpp nodist_php_CVC4_la_SOURCES = php.cpp @@ -171,7 +171,9 @@ MOSTLYCLEANFILES = \ $(patsubst %,%.d,$(filter-out c c++,$(CVC4_LANGUAGE_BINDINGS))) \ CVC4.jar -java_libcvc4jni_la-java.lo java.lo: java.cpp +java_libcvc4jni_la-java.lo: java.cpp + $(AM_V_CXX)$(LTCXXCOMPILE) -c $(JAVA_CPPFLAGS) $(java_libcvc4jni_la_CXXFLAGS) -o $@ $< +java.lo: java.cpp $(AM_V_CXX)$(LTCXXCOMPILE) -c $(JAVA_CPPFLAGS) $(java_libcvc4jni_la_CXXFLAGS) -o $@ $< CVC4.jar: java.cpp $(AM_V_GEN) \ diff --git a/src/bindings/compat/java/include/cvc3/JniUtils.h b/src/bindings/compat/java/include/cvc3/JniUtils.h index 567c691fe..404774c62 100644 --- a/src/bindings/compat/java/include/cvc3/JniUtils.h +++ b/src/bindings/compat/java/include/cvc3/JniUtils.h @@ -109,7 +109,7 @@ namespace Java_cvc3_JniUtils { // embeds a c++ object into a jobject, // and takes over the responsibility to deallocate it template <class T> jobject embed_own(JNIEnv* env, T* cobj) { - DebugAssert(&cobj != NULL, "JniUtils::embed_own: null object given"); + DebugAssert(cobj != NULL, "JniUtils::embed_own: null object given"); return embed<T>(env, cobj, typeid(cobj), &DeleteEmbedded<T>::deleteEmbedded); } diff --git a/src/compat/cvc3_compat.cpp b/src/compat/cvc3_compat.cpp index 0ecc6b5c7..2b552684a 100644 --- a/src/compat/cvc3_compat.cpp +++ b/src/compat/cvc3_compat.cpp @@ -25,6 +25,7 @@ #include "util/hash.h" #include "util/subrange_bound.h" #include "util/predicate.h" +#include "util/output.h" #include "parser/parser.h" #include "parser/parser_builder.h" @@ -1065,8 +1066,8 @@ Type ValidityChecker::intType() { } Type ValidityChecker::subrangeType(const Expr& l, const Expr& r) { - bool noLowerBound = l.getType().isString() && l.getConst<string>() == "_NEGINF"; - bool noUpperBound = r.getType().isString() && r.getConst<string>() == "_POSINF"; + bool noLowerBound = l.getType().isString() && l.getConst<CVC4::String>() == "_NEGINF"; + bool noUpperBound = r.getType().isString() && r.getConst<CVC4::String>() == "_POSINF"; CVC4::CheckArgument(noLowerBound || (l.getKind() == CVC4::kind::CONST_RATIONAL && l.getConst<Rational>().isIntegral()), l); CVC4::CheckArgument(noUpperBound || (r.getKind() == CVC4::kind::CONST_RATIONAL && r.getConst<Rational>().isIntegral()), r); CVC4::SubrangeBound bl = noLowerBound ? CVC4::SubrangeBound() : CVC4::SubrangeBound(l.getConst<Rational>().getNumerator()); @@ -1196,7 +1197,7 @@ void ValidityChecker::dataType(const std::vector<std::string>& names, CVC4::CheckArgument(selectors[i][j].size() == types[i][j].size(), types, "expected sub-vectors in selectors and types vectors to match in size"); for(unsigned k = 0; k < selectors[i][j].size(); ++k) { if(types[i][j][k].getType().isString()) { - ctor.addArg(selectors[i][j][k], CVC4::DatatypeUnresolvedType(types[i][j][k].getConst<string>())); + ctor.addArg(selectors[i][j][k], CVC4::DatatypeUnresolvedType(types[i][j][k].getConst<CVC4::String>().toString())); } else { ctor.addArg(selectors[i][j][k], exprToType(types[i][j][k])); } @@ -1306,12 +1307,12 @@ Expr ValidityChecker::getTypePred(const Type&t, const Expr& e) { } Expr ValidityChecker::stringExpr(const std::string& str) { - return d_em->mkConst(str); + return d_em->mkConst(CVC4::String(str)); } Expr ValidityChecker::idExpr(const std::string& name) { // represent as a string expr, CVC4 doesn't have id exprs - return d_em->mkConst(name); + return d_em->mkConst(CVC4::String(name)); } Expr ValidityChecker::listExpr(const std::vector<Expr>& kids) { @@ -1332,21 +1333,21 @@ Expr ValidityChecker::listExpr(const Expr& e1, const Expr& e2, const Expr& e3) { Expr ValidityChecker::listExpr(const std::string& op, const std::vector<Expr>& kids) { - return d_em->mkExpr(CVC4::kind::SEXPR, d_em->mkConst(op), vector<CVC4::Expr>(kids.begin(), kids.end())); + return d_em->mkExpr(CVC4::kind::SEXPR, d_em->mkConst(CVC4::String(op)), vector<CVC4::Expr>(kids.begin(), kids.end())); } Expr ValidityChecker::listExpr(const std::string& op, const Expr& e1) { - return d_em->mkExpr(CVC4::kind::SEXPR, d_em->mkConst(op), e1); + return d_em->mkExpr(CVC4::kind::SEXPR, d_em->mkConst(CVC4::String(op)), e1); } Expr ValidityChecker::listExpr(const std::string& op, const Expr& e1, const Expr& e2) { - return d_em->mkExpr(CVC4::kind::SEXPR, d_em->mkConst(op), e1, e2); + return d_em->mkExpr(CVC4::kind::SEXPR, d_em->mkConst(CVC4::String(op)), e1, e2); } Expr ValidityChecker::listExpr(const std::string& op, const Expr& e1, const Expr& e2, const Expr& e3) { - return d_em->mkExpr(CVC4::kind::SEXPR, d_em->mkConst(op), e1, e2, e3); + return d_em->mkExpr(CVC4::kind::SEXPR, d_em->mkConst(CVC4::String(op)), e1, e2, e3); } void ValidityChecker::printExpr(const Expr& e) { @@ -2273,7 +2274,7 @@ void ValidityChecker::popto(int stackLevel) { } int ValidityChecker::scopeLevel() { - return d_parserContext->getDeclarationLevel(); + return d_parserContext->scopeLevel(); } void ValidityChecker::pushScope() { @@ -2287,12 +2288,12 @@ void ValidityChecker::popScope() { void ValidityChecker::poptoScope(int scopeLevel) { CVC4::CheckArgument(scopeLevel >= 0, scopeLevel, "Cannot pop to a negative scope level %d", scopeLevel); - CVC4::CheckArgument(unsigned(scopeLevel) <= d_parserContext->getDeclarationLevel(), + CVC4::CheckArgument(unsigned(scopeLevel) <= d_parserContext->scopeLevel(), scopeLevel, "Cannot pop to a scope level higher than the current one! " "At scope level %u, user requested scope level %d", - d_parserContext->getDeclarationLevel(), scopeLevel); - while(unsigned(scopeLevel) < d_parserContext->getDeclarationLevel()) { + d_parserContext->scopeLevel(), scopeLevel); + while(unsigned(scopeLevel) < d_parserContext->scopeLevel()) { popScope(); } } diff --git a/src/cvc4.i b/src/cvc4.i index 965452b84..6e9380146 100644 --- a/src/cvc4.i +++ b/src/cvc4.i @@ -54,9 +54,11 @@ using namespace CVC4; #include "expr/expr.h" #include "util/datatype.h" #include "expr/command.h" -#include "bindings/java_stream_adapters.h" +#ifdef SWIGJAVA +#include "bindings/java_stream_adapters.h" std::set<JavaInputStreamAdapter*> CVC4::JavaInputStreamAdapter::s_adapters; +#endif %} %template(vectorCommandPtr) std::vector< CVC4::Command* >; @@ -248,7 +250,6 @@ std::set<JavaInputStreamAdapter*> CVC4::JavaInputStreamAdapter::s_adapters; %include "util/bool.i" %include "util/sexpr.i" %include "util/statistics.i" -%include "util/output.i" %include "util/result.i" %include "util/configuration.i" %include "util/bitvector.i" diff --git a/src/decision/Makefile.am b/src/decision/Makefile.am index 6d49c6301..f75a17a69 100644 --- a/src/decision/Makefile.am +++ b/src/decision/Makefile.am @@ -12,9 +12,7 @@ libdecision_la_SOURCES = \ decision_engine.cpp \ decision_strategy.h \ justification_heuristic.h \ - justification_heuristic.cpp \ - relevancy.h \ - relevancy.cpp + justification_heuristic.cpp EXTRA_DIST = \ options_handlers.h diff --git a/src/decision/decision_engine.cpp b/src/decision/decision_engine.cpp index 687515ff0..073a3ff6b 100644 --- a/src/decision/decision_engine.cpp +++ b/src/decision/decision_engine.cpp @@ -16,7 +16,6 @@ #include "decision/decision_engine.h" #include "decision/justification_heuristic.h" -#include "decision/relevancy.h" #include "expr/node.h" #include "decision/options.h" @@ -62,18 +61,6 @@ void DecisionEngine::init() enableStrategy(ds); d_needIteSkolemMap.push_back(ds); } - if(options::decisionMode() == decision::DECISION_STRATEGY_RELEVANCY) { - if(options::incrementalSolving()) { - Warning() << "Relevancy decision heuristic and incremental not supported together" - << std::endl; - return; // Currently not supported with incremental - } - RelevancyStrategy* ds = - new decision::Relevancy(this, d_satContext); - enableStrategy(ds); - d_needIteSkolemMap.push_back(ds); - d_relevancyStrategy = ds; - } } @@ -82,6 +69,13 @@ void DecisionEngine::enableStrategy(DecisionStrategy* ds) d_enabledStrategies.push_back(ds); } +void DecisionEngine::clearStrategies(){ + for(unsigned i = 0; i < d_enabledStrategies.size(); ++i){ + delete d_enabledStrategies[i]; + } + d_enabledStrategies.clear(); + d_needIteSkolemMap.clear(); +} bool DecisionEngine::isRelevant(SatVariable var) { @@ -105,13 +99,6 @@ SatValue DecisionEngine::getPolarity(SatVariable var) } } - - - - - - - void DecisionEngine::addAssertions(const vector<Node> &assertions) { Assert(false); // doing this so that we revisit what to do @@ -139,15 +126,4 @@ void DecisionEngine::addAssertions(const vector<Node> &assertions, addAssertions(assertions, assertionsEnd, iteSkolemMap); } -// void DecisionEngine::addAssertion(Node n) -// { -// d_result = SAT_VALUE_UNKNOWN; -// if(needIteSkolemMap()) { -// d_assertions.push_back(n); -// } -// for(unsigned i = 0; i < d_needIteSkolemMap.size(); ++i) -// d_needIteSkolemMap[i]->notifyAssertionsAvailable(); -// } - - }/* CVC4 namespace */ diff --git a/src/decision/decision_engine.h b/src/decision/decision_engine.h index bc071f7f6..f28b13774 100644 --- a/src/decision/decision_engine.h +++ b/src/decision/decision_engine.h @@ -96,6 +96,10 @@ public: /* enables decision stragies based on options */ void init(); + /* clears all of the strategies */ + void clearStrategies(); + + /** * This is called by SmtEngine, at shutdown time, just before * destruction. It is important because there are destruction @@ -106,8 +110,7 @@ public: d_engineState = 2; Trace("decision") << "Shutting down decision engine" << std::endl; - for(unsigned i = 0; i < d_enabledStrategies.size(); ++i) - delete d_enabledStrategies[i]; + clearStrategies(); } // Interface for External World to use our services diff --git a/src/decision/justification_heuristic.cpp b/src/decision/justification_heuristic.cpp index de49f6e0a..78de1a74e 100644 --- a/src/decision/justification_heuristic.cpp +++ b/src/decision/justification_heuristic.cpp @@ -96,7 +96,8 @@ CVC4::prop::SatLiteral JustificationHeuristic::getNextThresh(bool &stopSearch, D Debug("decision") << "---" << std::endl << d_assertions[i] << std::endl; // Sanity check: if it was false, aren't we inconsistent? - Assert( tryGetSatValue(d_assertions[i]) != SAT_VALUE_FALSE); + // Commenting out. See bug 374. In short, to do with how CNF stream works. + // Assert( tryGetSatValue(d_assertions[i]) != SAT_VALUE_FALSE); SatValue desiredVal = SAT_VALUE_TRUE; SatLiteral litDecision; @@ -105,6 +106,7 @@ CVC4::prop::SatLiteral JustificationHeuristic::getNextThresh(bool &stopSearch, D if(litDecision != undefSatLiteral) { setPrvsIndex(i); + Trace("decision") << "jh: spliting on " << litDecision << std::endl; return litDecision; } } @@ -441,8 +443,9 @@ JustificationHeuristic::findSplitterRec(TNode node, SatValue desiredVal) Assert(desiredVal != SAT_VALUE_UNKNOWN, "expected known value"); /* Good luck, hope you can get what you want */ - Assert(litVal == desiredVal || litVal == SAT_VALUE_UNKNOWN, - "invariant violated"); + // See bug 374 + // Assert(litVal == desiredVal || litVal == SAT_VALUE_UNKNOWN, + // "invariant violated"); /* What type of node is this */ Kind k = node.getKind(); @@ -458,12 +461,12 @@ JustificationHeuristic::findSplitterRec(TNode node, SatValue desiredVal) * If not in theory of booleans, check if this is something to split-on. */ if(tId != theory::THEORY_BOOL) { - // if node has embedded ites, resolve that first if(handleEmbeddedITEs(node) == FOUND_SPLITTER) return FOUND_SPLITTER; if(litVal != SAT_VALUE_UNKNOWN) { + Assert(litVal == desiredVal); setJustified(node); return NO_SPLITTER; } diff --git a/src/decision/options b/src/decision/options index b86577e7b..1f0b137cb 100644 --- a/src/decision/options +++ b/src/decision/options @@ -6,17 +6,9 @@ module DECISION "decision/options.h" Decision heuristics # When/whether to use any decision strategies -option decisionMode --decision=MODE decision::DecisionMode :handler CVC4::decision::stringToDecisionMode :default decision::DECISION_STRATEGY_INTERNAL :read-write :include "decision/decision_mode.h" :handler-include "decision/options_handlers.h" +option decisionMode decision-mode --decision=MODE decision::DecisionMode :handler CVC4::decision::stringToDecisionMode :default decision::DECISION_STRATEGY_INTERNAL :read-write :include "decision/decision_mode.h" :handler-include "decision/options_handlers.h" choose decision mode, see --decision=help -option decisionRelevancyLeaves bool -# permille = part per thousand -option decisionMaxRelTimeAsPermille --decision-budget=N "unsigned short" :read-write :predicate less_equal(1000L) :predicate CVC4::decision::checkDecisionBudget :predicate-include "decision/options_handlers.h" - impose a budget for relevancy heuristic which increases linearly with each decision. N between 0 and 1000. (default: 1000, no budget) -# if false, do justification stuff using relevancy.h -option decisionComputeRelevancy bool -# use the must be relevant -option decisionMustRelevancy bool # only use DE to determine when to stop, not to make decisions option decisionStopOnly bool diff --git a/src/decision/options_handlers.h b/src/decision/options_handlers.h index 05d975ef1..44a623970 100644 --- a/src/decision/options_handlers.h +++ b/src/decision/options_handlers.h @@ -37,31 +37,8 @@ justification\n\ justification-stoponly\n\ + Use the justification heuristic only to stop early, not for decisions\n\ "; -/** Under-development options, commenting out from help for the release */ -/* -\n\ -relevancy\n\ -+ Under development may-relevancy\n\ -\n\ -relevancy-leaves\n\ -+ May-relevancy, but decide only on leaves\n\ -\n\ -Developer modes:\n\ -\n\ -justification-rel\n\ -+ Use the relevancy code to do the justification stuff\n\ -+ (This should do exact same thing as justification)\n\ -\n\ -justification-must\n\ -+ Start deciding on literals close to root instead of those\n\ -+ near leaves (don't expect it to work well) [Unimplemented]\n\ -";*/ inline DecisionMode stringToDecisionMode(std::string option, std::string optarg, SmtEngine* smt) throw(OptionException) { - options::decisionRelevancyLeaves.set(false); - options::decisionMaxRelTimeAsPermille.set(1000); - options::decisionComputeRelevancy.set(true); - options::decisionMustRelevancy.set(false); options::decisionStopOnly.set(false); if(optarg == "internal") { @@ -71,25 +48,6 @@ inline DecisionMode stringToDecisionMode(std::string option, std::string optarg, } else if(optarg == "justification-stoponly") { options::decisionStopOnly.set(true); return DECISION_STRATEGY_JUSTIFICATION; - } else if(optarg == "relevancy") { - options::decisionRelevancyLeaves.set(false); - return DECISION_STRATEGY_RELEVANCY; - } else if(optarg == "relevancy-leaves") { - options::decisionRelevancyLeaves.set(true); - Trace("options") << "version is " << options::version() << std::endl; - return DECISION_STRATEGY_RELEVANCY; - } else if(optarg == "justification-rel") { - // relevancyLeaves : irrelevant - // maxRelTimeAsPermille : irrelevant - options::decisionComputeRelevancy.set(false); - options::decisionMustRelevancy.set(false); - return DECISION_STRATEGY_RELEVANCY; - } else if(optarg == "justification-must") { - // relevancyLeaves : irrelevant - // maxRelTimeAsPermille : irrelevant - options::decisionComputeRelevancy.set(false); - options::decisionMustRelevancy.set(true); - return DECISION_STRATEGY_RELEVANCY; } else if(optarg == "help") { puts(decisionModeHelp.c_str()); exit(1); @@ -99,14 +57,6 @@ inline DecisionMode stringToDecisionMode(std::string option, std::string optarg, } } -inline void checkDecisionBudget(std::string option, unsigned short budget, SmtEngine* smt) throw(OptionException) { - if(budget == 0) { - Warning() << "Decision budget is 0. Consider using internal decision heuristic and " - << std::endl << " removing this option." << std::endl; - - } -} - inline DecisionWeightInternal stringToDecisionWeightInternal(std::string option, std::string optarg, SmtEngine* smt) throw(OptionException) { if(optarg == "off") return DECISION_WEIGHT_INTERNAL_OFF; diff --git a/src/decision/relevancy.cpp b/src/decision/relevancy.cpp deleted file mode 100644 index f83e238d4..000000000 --- a/src/decision/relevancy.cpp +++ /dev/null @@ -1,379 +0,0 @@ -/********************* */ -/*! \file relevancy.cpp - ** \verbatim - ** Original author: Kshitij Bansal - ** Major contributors: none - ** Minor contributors (to current version): Dejan Jovanovic, Morgan Deters - ** 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 Justification heuristic for decision making - ** - ** A ATGP-inspired justification-based decision heuristic. See - ** [insert reference] for more details. This code is, or not, based - ** on the CVC3 implementation of the same heuristic -- note below. - ** - ** It needs access to the simplified but non-clausal formula. - **/ -/*****************************************************************************/ - -#include "relevancy.h" - -#include "expr/node_manager.h" -#include "expr/kind.h" -#include "theory/rewriter.h" -#include "util/ite_removal.h" - -// Relevancy stuff - -const double Relevancy::secondsPerDecision = 0.001; -const double Relevancy::secondsPerExpense = 1e-7; -const double Relevancy::EPS = 1e-9; - - -void Relevancy::setJustified(TNode n) -{ - Debug("decision") << " marking [" << n.getId() << "]"<< n << "as justified" << std::endl; - d_justified.insert(n); - if(options::decisionComputeRelevancy()) { - d_relevancy[n] = d_maxRelevancy[n]; - updateRelevancy(n); - } -} - -bool Relevancy::checkJustified(TNode n) -{ - return d_justified.find(n) != d_justified.end(); -} - -SatValue Relevancy::tryGetSatValue(Node n) -{ - Debug("decision") << " " << n << " has sat value " << " "; - if(d_decisionEngine->hasSatLiteral(n) ) { - Debug("decision") << d_decisionEngine->getSatValue(n) << std::endl; - return d_decisionEngine->getSatValue(n); - } else { - Debug("decision") << "NO SAT LITERAL" << std::endl; - return SAT_VALUE_UNKNOWN; - }//end of else -} - -void Relevancy::computeITEs(TNode n, IteList &l) -{ - Debug("jh-ite") << " computeITEs( " << n << ", &l)\n"; - for(unsigned i=0; i<n.getNumChildren(); ++i) { - SkolemMap::iterator it2 = d_iteAssertions.find(n[i]); - if(it2 != d_iteAssertions.end()) - l.push_back(it2->second); - computeITEs(n[i], l); - } -} - -const Relevancy::IteList& Relevancy::getITEs(TNode n) -{ - IteCache::iterator it = d_iteCache.find(n); - if(it != d_iteCache.end()) { - return it->second; - } else { - // Compute the list of ITEs - // TODO: optimize by avoiding multiple lookup for d_iteCache[n] - d_iteCache[n] = vector<TNode>(); - computeITEs(n, d_iteCache[n]); - return d_iteCache[n]; - } -} - -bool Relevancy::findSplitterRec(TNode node, - SatValue desiredVal) -{ - Trace("decision") - << "findSplitterRec([" << node.getId() << "]" << node << ", " - << desiredVal << ", .. )" << std::endl; - - ++d_expense; - - /* Handle NOT as a special case */ - while (node.getKind() == kind::NOT) { - desiredVal = invertValue(desiredVal); - node = node[0]; - } - - /* Avoid infinite loops */ - if(d_visited.find(node) != d_visited.end()) { - Debug("decision") << " node repeated. kind was " << node.getKind() << std::endl; - Assert(false); - Assert(node.getKind() == kind::ITE); - return false; - } - - /* Base case */ - if(checkJustified(node)) { - return false; - } - - /** - * If we have already explored the subtree for some desiredVal, we - * skip this and continue exploring the rest - */ - if(d_polarityCache.find(node) == d_polarityCache.end()) { - d_polarityCache[node] = desiredVal; - } else { - Assert(d_multipleBacktrace || options::decisionComputeRelevancy()); - return true; - } - - d_visited.insert(node); - -#if defined CVC4_ASSERTIONS || defined CVC4_TRACING - // We don't always have a sat literal, so remember that. Will need - // it for some assertions we make. - bool litPresent = d_decisionEngine->hasSatLiteral(node); - if(Trace.isOn("decision")) { - if(!litPresent) { - Trace("decision") << "no sat literal for this node" << std::endl; - } - } - //Assert(litPresent); -- fails -#endif - - // Get value of sat literal for the node, if there is one - SatValue litVal = tryGetSatValue(node); - - /* You'd better know what you want */ - Assert(desiredVal != SAT_VALUE_UNKNOWN, "expected known value"); - - /* Good luck, hope you can get what you want */ - Assert(litVal == desiredVal || litVal == SAT_VALUE_UNKNOWN, - "invariant violated"); - - /* What type of node is this */ - Kind k = node.getKind(); - theory::TheoryId tId = theory::kindToTheoryId(k); - - /* Some debugging stuff */ - Trace("jh-findSplitterRec") << "kind = " << k << std::endl; - Trace("jh-findSplitterRec") << "theoryId = " << tId << std::endl; - Trace("jh-findSplitterRec") << "node = " << node << std::endl; - Trace("jh-findSplitterRec") << "litVal = " << litVal << std::endl; - - /** - * If not in theory of booleans, and not a "boolean" EQUAL (IFF), - * then check if this is something to split-on. - */ - if(tId != theory::THEORY_BOOL - // && !(k == kind::EQUAL && node[0].getType().isBoolean()) - ) { - - // if node has embedded ites -- which currently happens iff it got - // replaced during ite removal -- then try to resolve that first - const IteList& l = getITEs(node); - Debug("jh-ite") << " ite size = " << l.size() << std::endl; - for(unsigned i = 0; i < l.size(); ++i) { - Assert(l[i].getKind() == kind::ITE, "Expected ITE"); - Debug("jh-ite") << " i = " << i - << " l[i] = " << l[i] << std::endl; - if(d_visited.find(node) != d_visited.end() ) continue; - if(findSplitterRec(l[i], SAT_VALUE_TRUE)) { - d_visited.erase(node); - return true; - } - } - Debug("jh-ite") << " ite done " << l.size() << std::endl; - - if(litVal != SAT_VALUE_UNKNOWN) { - d_visited.erase(node); - setJustified(node); - return false; - } else { - /* if(not d_decisionEngine->hasSatLiteral(node)) - throw GiveUpException(); */ - Assert(d_decisionEngine->hasSatLiteral(node)); - SatVariable v = d_decisionEngine->getSatLiteral(node).getSatVariable(); - *d_curDecision = SatLiteral(v, desiredVal != SAT_VALUE_TRUE ); - Trace("decision") << "decision " << *d_curDecision << std::endl; - Trace("decision") << "Found something to split. Glad to be able to serve you." << std::endl; - d_visited.erase(node); - return true; - } - } - - bool ret = false; - SatValue flipCaseVal = SAT_VALUE_FALSE; - switch (k) { - - case kind::CONST_BOOLEAN: - Assert(node.getConst<bool>() == false || desiredVal == SAT_VALUE_TRUE); - Assert(node.getConst<bool>() == true || desiredVal == SAT_VALUE_FALSE); - break; - - case kind::AND: - if (desiredVal == SAT_VALUE_FALSE) ret = handleOrTrue(node, SAT_VALUE_FALSE); - else ret = handleOrFalse(node, SAT_VALUE_TRUE); - break; - - case kind::OR: - if (desiredVal == SAT_VALUE_FALSE) ret = handleOrFalse(node, SAT_VALUE_FALSE); - else ret = handleOrTrue(node, SAT_VALUE_TRUE); - break; - - case kind::IMPLIES: - Assert(node.getNumChildren() == 2, "Expected 2 fanins"); - if (desiredVal == SAT_VALUE_FALSE) { - ret = findSplitterRec(node[0], SAT_VALUE_TRUE); - if(ret) break; - ret = findSplitterRec(node[1], SAT_VALUE_FALSE); - break; - } - else { - if (tryGetSatValue(node[0]) != SAT_VALUE_TRUE) { - ret = findSplitterRec(node[0], SAT_VALUE_FALSE); - //if(!ret || !d_multipleBacktrace) - break; - } - if (tryGetSatValue(node[1]) != SAT_VALUE_FALSE) { - ret = findSplitterRec(node[1], SAT_VALUE_TRUE); - break; - } - Assert(d_multipleBacktrace, "No controlling input found (3)"); - } - break; - - case kind::XOR: - flipCaseVal = SAT_VALUE_TRUE; - case kind::IFF: { - SatValue val = tryGetSatValue(node[0]); - if (val != SAT_VALUE_UNKNOWN) { - ret = findSplitterRec(node[0], val); - if (ret) break; - if (desiredVal == flipCaseVal) val = invertValue(val); - ret = findSplitterRec(node[1], val); - } - else { - val = tryGetSatValue(node[1]); - if (val == SAT_VALUE_UNKNOWN) val = SAT_VALUE_FALSE; - if (desiredVal == flipCaseVal) val = invertValue(val); - ret = findSplitterRec(node[0], val); - if(ret) break; - Assert(false, "Unable to find controlling input (4)"); - } - break; - } - - case kind::ITE: - ret = handleITE(node, desiredVal); - break; - - default: - Assert(false, "Unexpected Boolean operator"); - break; - }//end of switch(k) - - d_visited.erase(node); - if(ret == false) { - Assert(litPresent == false || litVal == desiredVal, - "Output should be justified"); - setJustified(node); - } - return ret; - - Unreachable(); -}/* findRecSplit method */ - -bool Relevancy::handleOrFalse(TNode node, SatValue desiredVal) { - Debug("jh-findSplitterRec") << " handleOrFalse (" << node << ", " - << desiredVal << std::endl; - - Assert( (node.getKind() == kind::AND and desiredVal == SAT_VALUE_TRUE) or - (node.getKind() == kind::OR and desiredVal == SAT_VALUE_FALSE) ); - - int n = node.getNumChildren(); - bool ret = false; - for(int i = 0; i < n; ++i) { - if (findSplitterRec(node[i], desiredVal)) { - if(!options::decisionComputeRelevancy()) - return true; - else - ret = true; - } - } - return ret; -} - -bool Relevancy::handleOrTrue(TNode node, SatValue desiredVal) { - Debug("jh-findSplitterRec") << " handleOrTrue (" << node << ", " - << desiredVal << std::endl; - - Assert( (node.getKind() == kind::AND and desiredVal == SAT_VALUE_FALSE) or - (node.getKind() == kind::OR and desiredVal == SAT_VALUE_TRUE) ); - - int n = node.getNumChildren(); - SatValue desiredValInverted = invertValue(desiredVal); - for(int i = 0; i < n; ++i) { - if ( tryGetSatValue(node[i]) != desiredValInverted ) { - // PRE RELEVANCY return findSplitterRec(node[i], desiredVal); - bool ret = findSplitterRec(node[i], desiredVal); - if(ret) { - // Splitter could be found... so can't justify this node - if(!d_multipleBacktrace) - return true; - } - else { - // Splitter couldn't be found... should be justified - return false; - } - } - } - // Multiple backtrace is on, so must have reached here because - // everything had splitter - if(d_multipleBacktrace) return true; - - if(Debug.isOn("jh-findSplitterRec")) { - Debug("jh-findSplitterRec") << " * ** " << std::endl; - Debug("jh-findSplitterRec") << node.getKind() << " " - << node << std::endl; - for(unsigned i = 0; i < node.getNumChildren(); ++i) - Debug("jh-findSplitterRec") << "child: " << tryGetSatValue(node[i]) - << std::endl; - Debug("jh-findSplitterRec") << "node: " << tryGetSatValue(node) - << std::endl; - } - Assert(false, "No controlling input found"); - return false; -} - -bool Relevancy::handleITE(TNode node, SatValue desiredVal) -{ - Debug("jh-findSplitterRec") << " handleITE (" << node << ", " - << desiredVal << std::endl; - - //[0]: if, [1]: then, [2]: else - SatValue ifVal = tryGetSatValue(node[0]); - if (ifVal == SAT_VALUE_UNKNOWN) { - - // are we better off trying false? if not, try true - SatValue ifDesiredVal = - (tryGetSatValue(node[2]) == desiredVal || - tryGetSatValue(node[1]) == invertValue(desiredVal)) - ? SAT_VALUE_FALSE : SAT_VALUE_TRUE; - - if(findSplitterRec(node[0], ifDesiredVal)) return true; - - Assert(false, "No controlling input found (6)"); - } else { - - // Try to justify 'if' - if(findSplitterRec(node[0], ifVal)) return true; - - // If that was successful, we need to go into only one of 'then' - // or 'else' - int ch = (ifVal == SAT_VALUE_TRUE) ? 1 : 2; - int chVal = tryGetSatValue(node[ch]); - if( (chVal == SAT_VALUE_UNKNOWN || chVal == desiredVal) && - findSplitterRec(node[ch], desiredVal) ) { - return true; - } - }// else (...ifVal...) - return false; -}//handleITE diff --git a/src/decision/relevancy.h b/src/decision/relevancy.h deleted file mode 100644 index fd16eb0cc..000000000 --- a/src/decision/relevancy.h +++ /dev/null @@ -1,421 +0,0 @@ -/********************* */ -/*! \file relevancy.h - ** \verbatim - ** Original author: Kshitij Bansal - ** Major contributors: none - ** Minor contributors (to current version): Tim King, Dejan Jovanovic, Morgan Deters - ** 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 Justification heuristic for decision making - ** - ** A ATGP-inspired justification-based decision heuristic. See - ** [insert reference] for more details. This code is, or not, based - ** on the CVC3 implementation of the same heuristic. - ** - ** It needs access to the simplified but non-clausal formula. - ** - ** Relevancy - ** --------- - ** - ** This class also currently computes the may-relevancy of a node - ** - ** A node is may-relevant if there is a path from the root of the - ** formula to the node such that no node on the path is justified. As - ** contrapositive, a node is not relevant (with the may-notion) if all - ** path from the root to the node go through a justified node. - **/ - -#include "cvc4_private.h" - -#ifndef __CVC4__DECISION__RELEVANCY -#define __CVC4__DECISION__RELEVANCY - -#include "decision_engine.h" -#include "decision_strategy.h" -#include "decision/options.h" - -#include "context/cdhashset.h" -#include "context/cdhashmap.h" -#include "expr/node.h" -#include "prop/sat_solver_types.h" - -namespace CVC4 { - -namespace decision { - -class RelGiveUpException : public Exception { -public: - RelGiveUpException() : - Exception("relevancy: giving up") { - } -};/* class GiveUpException */ - -class Relevancy : public RelevancyStrategy { - typedef std::vector<TNode> IteList; - typedef hash_map<TNode,IteList,TNodeHashFunction> IteCache; - typedef hash_map<TNode,TNode,TNodeHashFunction> SkolemMap; - typedef hash_map<TNode,SatValue,TNodeHashFunction> PolarityCache; - - // being 'justified' is monotonic with respect to decisions - context::CDHashSet<Node, NodeHashFunction> d_justified; - context::CDO<unsigned> d_prvsIndex; - - IntStat d_helfulness; - IntStat d_polqueries; - IntStat d_polhelp; - IntStat d_giveup; - IntStat d_expense; /* Total number of nodes considered over - all calls to the findSplitter function */ - TimerStat d_timestat; - - /** - * A copy of the assertions that need to be justified - * directly. Doesn't have ones introduced during during ITE-removal. - */ - std::vector<TNode> d_assertions; - //TNode is fine since decisionEngine has them too - - /** map from ite-rewrite skolem to a boolean-ite assertion */ - SkolemMap d_iteAssertions; - // 'key' being TNode is fine since if a skolem didn't exist anywhere, - // we won't look it up. as for 'value', same reason as d_assertions - - /** Cache for ITE skolems present in a atomic formula */ - IteCache d_iteCache; - - /** - * This is used to prevent infinite loop when trying to find a - * splitter. Can happen when exploring assertion corresponding to a - * term-ITE. - */ - hash_set<TNode,TNodeHashFunction> d_visited; - - /** - * May-relevancy of a node is an integer. For a node n, it becomes - * irrelevant when d_maxRelevancy[n] = d_relevancy[n] - */ - hash_map<TNode,int,TNodeHashFunction> d_maxRelevancy; - context::CDHashMap<TNode,int,TNodeHashFunction> d_relevancy; - PolarityCache d_polarityCache; - - /** **** * * *** * * OPTIONS *** * * ** * * **** */ - - /** - * do we do "multiple-backtrace" to try to justify a node - */ - bool d_multipleBacktrace; - //bool d_computeRelevancy; // are we in a mode where we compute relevancy? - - static const double secondsPerDecision; - static const double secondsPerExpense; - static const double EPS; - - /** Maximum time this algorithm should spent as part of whole algorithm */ - double d_maxTimeAsPercentageOfTotalTime; - - /** current decision for the recursive call */ - SatLiteral* d_curDecision; -public: - Relevancy(CVC4::DecisionEngine* de, context::Context *c): - RelevancyStrategy(de, c), - d_justified(c), - d_prvsIndex(c, 0), - d_helfulness("decision::rel::preventedDecisions", 0), - d_polqueries("decision::rel::polarityQueries", 0), - d_polhelp("decision::rel::polarityHelp", 0), - d_giveup("decision::rel::giveup", 0), - d_expense("decision::rel::expense", 0), - d_timestat("decision::rel::time"), - d_relevancy(c), - d_multipleBacktrace(options::decisionComputeRelevancy()), - // d_computeRelevancy(decOpt.computeRelevancy), - d_maxTimeAsPercentageOfTotalTime(options::decisionMaxRelTimeAsPermille()*1.0/10.0), - d_curDecision(NULL) - { - Warning() << "There are known bugs in this Relevancy code which we know" - << "how to fix (but haven't yet)." << std::endl - << "Please bug kshitij if you wish to use." << std::endl; - - StatisticsRegistry::registerStat(&d_helfulness); - StatisticsRegistry::registerStat(&d_polqueries); - StatisticsRegistry::registerStat(&d_polhelp); - StatisticsRegistry::registerStat(&d_giveup); - StatisticsRegistry::registerStat(&d_expense); - StatisticsRegistry::registerStat(&d_timestat); - Trace("decision") << "relevancy enabled with" << std::endl; - Trace("decision") << " * computeRelevancy: " << (options::decisionComputeRelevancy() ? "on" : "off") - << std::endl; - Trace("decision") << " * relevancyLeaves: " << (options::decisionRelevancyLeaves() ? "on" : "off") - << std::endl; - Trace("decision") << " * mustRelevancy [unimplemented]: " << (options::decisionMustRelevancy() ? "on" : "off") - << std::endl; - } - ~Relevancy() { - StatisticsRegistry::unregisterStat(&d_helfulness); - StatisticsRegistry::unregisterStat(&d_polqueries); - StatisticsRegistry::unregisterStat(&d_polhelp); - StatisticsRegistry::unregisterStat(&d_giveup); - StatisticsRegistry::unregisterStat(&d_expense); - StatisticsRegistry::unregisterStat(&d_timestat); - } - prop::SatLiteral getNext(bool &stopSearch) { - Debug("decision") << std::endl; - Trace("decision") << "Relevancy::getNext()" << std::endl; - TimerStat::CodeTimer codeTimer(d_timestat); - - if( d_maxTimeAsPercentageOfTotalTime < 100.0 - EPS && - double(d_polqueries.getData())*secondsPerDecision < - d_maxTimeAsPercentageOfTotalTime*double(d_expense.getData())*secondsPerExpense - ) { - ++d_giveup; - return undefSatLiteral; - } - - d_visited.clear(); - d_polarityCache.clear(); - - for(unsigned i = d_prvsIndex; i < d_assertions.size(); ++i) { - Trace("decision") << d_assertions[i] << std::endl; - - // Sanity check: if it was false, aren't we inconsistent? - Assert( tryGetSatValue(d_assertions[i]) != SAT_VALUE_FALSE); - - SatValue desiredVal = SAT_VALUE_TRUE; - SatLiteral litDecision = findSplitter(d_assertions[i], desiredVal); - - if(!options::decisionComputeRelevancy() && litDecision != undefSatLiteral) { - d_prvsIndex = i; - return litDecision; - } - } - - if(options::decisionComputeRelevancy()) return undefSatLiteral; - - Trace("decision") << "jh: Nothing to split on " << std::endl; - -#if defined CVC4_ASSERTIONS || defined CVC4_DEBUG - bool alljustified = true; - for(unsigned i = 0 ; i < d_assertions.size() && alljustified ; ++i) { - TNode curass = d_assertions[i]; - while(curass.getKind() == kind::NOT) - curass = curass[0]; - alljustified &= checkJustified(curass); - - if(Debug.isOn("decision")) { - if(!checkJustified(curass)) - Debug("decision") << "****** Not justified [i="<<i<<"]: " - << d_assertions[i] << std::endl; - } - } - Assert(alljustified); -#endif - - // SAT solver can stop... - stopSearch = true; - d_decisionEngine->setResult(SAT_VALUE_TRUE); - return prop::undefSatLiteral; - } - - void addAssertions(const std::vector<Node> &assertions, - unsigned assertionsEnd, - IteSkolemMap iteSkolemMap) { - Trace("decision") - << "Relevancy::addAssertions()" - << " size = " << assertions.size() - << " assertionsEnd = " << assertionsEnd - << std::endl; - - // Save mapping between ite skolems and ite assertions - for(IteSkolemMap::iterator i = iteSkolemMap.begin(); - i != iteSkolemMap.end(); ++i) { - Assert(i->second >= assertionsEnd && i->second < assertions.size()); - Debug("jh-ite") << " jh-ite: " << (i->first) << " maps to " - << assertions[(i->second)] << std::endl; - d_iteAssertions[i->first] = assertions[i->second]; - } - - // Save the 'real' assertions locally - for(unsigned i = 0; i < assertionsEnd; ++i) { - d_assertions.push_back(assertions[i]); - d_visited.clear(); - if(options::decisionComputeRelevancy()) increaseMaxRelevancy(assertions[i]); - d_visited.clear(); - } - - } - - bool isRelevant(TNode n) { - Trace("decision") << "isRelevant(" << n << "): "; - - if(Debug.isOn("decision")) { - if(d_maxRelevancy.find(n) == d_maxRelevancy.end()) { - // we are becuase of not getting information about literals - // created using newLiteral command... need to figure how to - // handle that - Message() << "isRelevant: WARNING: didn't find node when we should had" << std::endl; - } - } - - if(d_maxRelevancy.find(n) == d_maxRelevancy.end()) { - Trace("decision") << "yes [not found in db]" << std::endl; - return true; - } - - if(options::decisionRelevancyLeaves() && !isAtomicFormula(n)) { - Trace("decision") << "no [not a leaf]" << std::endl; - return false; - } - - Trace("decision") << (d_maxRelevancy[n] == d_relevancy[n] ? "no":"yes") - << std::endl; - - Debug("decision") << " maxRel: " << (d_maxRelevancy[n] ) - << " rel: " << d_relevancy[n].get() << std::endl; - // Assert(d_maxRelevancy.find(n) != d_maxRelevancy.end()); - Assert(0 <= d_relevancy[n] && d_relevancy[n] <= d_maxRelevancy[n]); - - if(d_maxRelevancy[n] == d_relevancy[n]) { - ++d_helfulness; // preventedDecisions - return false; - } else { - return true; - } - } - - SatValue getPolarity(TNode n) { - Trace("decision") << "getPolarity([" << n.getId() << "]" << n << "): "; - Assert(n.getKind() != kind::NOT); - ++d_polqueries; - PolarityCache::iterator it = d_polarityCache.find(n); - if(it == d_polarityCache.end()) { - Trace("decision") << "don't know" << std::endl; - return SAT_VALUE_UNKNOWN; - } else { - ++d_polhelp; - Trace("decision") << it->second << std::endl; - return it->second; - } - } - - /* Compute justified */ - /*bool computeJustified() { - - }*/ -private: - SatLiteral findSplitter(TNode node, SatValue desiredVal) - { - bool ret; - SatLiteral litDecision; - try { - // init - d_curDecision = &litDecision; - - // solve - ret = findSplitterRec(node, desiredVal); - - // post - d_curDecision = NULL; - }catch(RelGiveUpException &e) { - return prop::undefSatLiteral; - } - if(ret == true) { - Trace("decision") << "Yippee!!" << std::endl; - return litDecision; - } else { - return undefSatLiteral; - } - } - - /** - * Do all the hardwork. - * Return 'true' if the node cannot be justfied, 'false' it it can be. - * Sets 'd_curDecision' if unable to justify (depending on the mode) - * If 'd_computeRelevancy' is on does multiple backtrace - */ - bool findSplitterRec(TNode node, SatValue value); - // Functions to make findSplitterRec modular... - bool handleOrFalse(TNode node, SatValue desiredVal); - bool handleOrTrue(TNode node, SatValue desiredVal); - bool handleITE(TNode node, SatValue desiredVal); - - /* Helper functions */ - void setJustified(TNode); - bool checkJustified(TNode); - - /* If literal exists corresponding to the node return - that. Otherwise an UNKNOWN */ - SatValue tryGetSatValue(Node n); - - /* Get list of all term-ITEs for the atomic formula v */ - const Relevancy::IteList& getITEs(TNode n); - - /* Compute all term-ITEs in a node recursively */ - void computeITEs(TNode n, IteList &l); - - /* Checks whether something is an atomic formula or not */ - bool isAtomicFormula(TNode n) { - return theory::kindToTheoryId(n.getKind()) != theory::THEORY_BOOL; - } - - /** Recursively increase relevancies */ - void updateRelevancy(TNode n) { - if(d_visited.find(n) != d_visited.end()) return; - - Assert(d_relevancy[n] <= d_maxRelevancy[n]); - - if(d_relevancy[n] != d_maxRelevancy[n]) - return; - - d_visited.insert(n); - if(isAtomicFormula(n)) { - const IteList& l = getITEs(n); - for(unsigned i = 0; i < l.size(); ++i) { - if(d_visited.find(l[i]) == d_visited.end()) continue; - d_relevancy[l[i]] = d_relevancy[l[i]] + 1; - updateRelevancy(l[i]); - } - } else { - for(unsigned i = 0; i < n.getNumChildren(); ++i) { - if(d_visited.find(n[i]) == d_visited.end()) continue; - d_relevancy[n[i]] = d_relevancy[n[i]] + 1; - updateRelevancy(n[i]); - } - } - d_visited.erase(n); - } - - /* */ - void increaseMaxRelevancy(TNode n) { - - Debug("decision") - << "increaseMaxRelevancy([" << n.getId() << "]" << n << ")" - << std::endl; - - d_maxRelevancy[n]++; - - // don't update children multiple times - if(d_visited.find(n) != d_visited.end()) return; - - d_visited.insert(n); - // Part to make the recursive call - if(isAtomicFormula(n)) { - const IteList& l = getITEs(n); - for(unsigned i = 0; i < l.size(); ++i) - if(d_visited.find(l[i]) == d_visited.end()) - increaseMaxRelevancy(l[i]); - } else { - for(unsigned i = 0; i < n.getNumChildren(); ++i) - increaseMaxRelevancy(n[i]); - } //else (...atomic formula...) - } - -};/* class Relevancy */ - -}/* namespace decision */ - -}/* namespace CVC4 */ - -#endif /* __CVC4__DECISION__RELEVANCY */ diff --git a/src/expr/attribute.h b/src/expr/attribute.h index 70f04be85..721a09403 100644 --- a/src/expr/attribute.h +++ b/src/expr/attribute.h @@ -446,7 +446,7 @@ struct HasAttribute<false, AttrKind> { static inline bool hasAttribute(const AttributeManager* am, NodeValue* nv) { typedef typename AttrKind::value_type value_type; - typedef KindValueToTableValueMapping<value_type> mapping; + //typedef KindValueToTableValueMapping<value_type> mapping; typedef typename getTable<value_type, AttrKind::context_dependent>:: table_type table_type; @@ -522,14 +522,14 @@ AttributeManager::setAttribute(NodeValue* nv, template <class T> inline void AttributeManager::deleteFromTable(AttrHash<T>& table, NodeValue* nv) { - for(uint64_t id = 0; id < attr::LastAttributeId<T, false>::s_id; ++id) { + for(uint64_t id = 0; id < attr::LastAttributeId<T, false>::getId(); ++id) { typedef AttributeTraits<T, false> traits_t; typedef AttrHash<T> hash_t; std::pair<uint64_t, NodeValue*> pr = std::make_pair(id, nv); - if(traits_t::cleanup[id] != NULL) { + if(traits_t::getCleanup()[id] != NULL) { typename hash_t::iterator i = table.find(pr); if(i != table.end()) { - traits_t::cleanup[id]((*i).second); + traits_t::getCleanup()[id]((*i).second); table.erase(pr); } } else { @@ -544,7 +544,7 @@ inline void AttributeManager::deleteFromTable(AttrHash<T>& table, template <class T> inline void AttributeManager::deleteFromTable(CDAttrHash<T>& table, NodeValue* nv) { - for(unsigned id = 0; id < attr::LastAttributeId<T, true>::s_id; ++id) { + for(unsigned id = 0; id < attr::LastAttributeId<T, true>::getId(); ++id) { table.obliterate(std::make_pair(id, nv)); } } @@ -558,8 +558,8 @@ inline void AttributeManager::deleteAllFromTable(AttrHash<T>& table) { bool anyRequireClearing = false; typedef AttributeTraits<T, false> traits_t; typedef AttrHash<T> hash_t; - for(uint64_t id = 0; id < attr::LastAttributeId<T, false>::s_id; ++id) { - if(traits_t::cleanup[id] != NULL) { + for(uint64_t id = 0; id < attr::LastAttributeId<T, false>::getId(); ++id) { + if(traits_t::getCleanup()[id] != NULL) { anyRequireClearing = true; } } @@ -575,8 +575,8 @@ inline void AttributeManager::deleteAllFromTable(AttrHash<T>& table) { << " node_value: " << ((*it).first.second) << std::endl; */ - if(traits_t::cleanup[id] != NULL) { - traits_t::cleanup[id]((*it).second); + if(traits_t::getCleanup()[id] != NULL) { + traits_t::getCleanup()[id]((*it).second); } ++it; } diff --git a/src/expr/attribute_internals.h b/src/expr/attribute_internals.h index 4893075c3..a0086440b 100644 --- a/src/expr/attribute_internals.h +++ b/src/expr/attribute_internals.h @@ -679,13 +679,12 @@ namespace attr { */ template <class T, bool context_dep> struct LastAttributeId { - static uint64_t s_id; + static uint64_t& getId() { + static uint64_t s_id = 0; + return s_id; + } }; -/** Initially zero. */ -template <class T, bool context_dep> -uint64_t LastAttributeId<T, context_dep>::s_id = 0; - }/* CVC4::expr::attr namespace */ // ATTRIBUTE TRAITS ============================================================ @@ -699,13 +698,12 @@ namespace attr { template <class T, bool context_dep> struct AttributeTraits { typedef void (*cleanup_t)(T); - static std::vector<cleanup_t> cleanup; + static std::vector<cleanup_t>& getCleanup() { + static std::vector<cleanup_t> cleanup; + return cleanup; + } }; -template <class T, bool context_dep> -std::vector<typename AttributeTraits<T, context_dep>::cleanup_t> - AttributeTraits<T, context_dep>::cleanup; - }/* CVC4::expr::attr namespace */ // ATTRIBUTE DEFINITION ======================================================== @@ -765,9 +763,9 @@ public: typedef typename attr::KindValueToTableValueMapping<value_t>:: table_value_type table_value_type; typedef attr::AttributeTraits<table_value_type, context_dep> traits; - uint64_t id = attr::LastAttributeId<table_value_type, context_dep>::s_id++; - //Assert(traits::cleanup.size() == id);// sanity check - traits::cleanup.push_back(attr::getCleanupStrategy<value_t, + uint64_t id = attr::LastAttributeId<table_value_type, context_dep>::getId()++; + Assert(traits::getCleanup().size() == id);// sanity check + traits::getCleanup().push_back(attr::getCleanupStrategy<value_t, CleanupStrategy>::fn); return id; } @@ -827,7 +825,7 @@ public: * return the id. */ static inline uint64_t registerAttribute() { - uint64_t id = attr::LastAttributeId<bool, context_dep>::s_id++; + uint64_t id = attr::LastAttributeId<bool, context_dep>::getId()++; AlwaysAssert( id <= 63, "Too many boolean node attributes registered " "during initialization !" ); @@ -876,7 +874,7 @@ template <class T, class value_t, class CleanupStrategy, bool context_dep> const uint64_t Attribute<T, value_t, CleanupStrategy, context_dep>::s_id = ( attr::AttributeTraits<typename attr::KindValueToTableValueMapping<value_t>:: table_value_type, - context_dep>::cleanup.size(), + context_dep>::getCleanup().size(), Attribute<T, value_t, CleanupStrategy, context_dep>::registerAttribute() ); /** Assign unique IDs to attributes at load time. */ diff --git a/src/expr/command.cpp b/src/expr/command.cpp index 3a88a6cba..8ae448657 100644 --- a/src/expr/command.cpp +++ b/src/expr/command.cpp @@ -100,7 +100,7 @@ bool Command::fail() const throw() { void Command::invoke(SmtEngine* smtEngine, std::ostream& out) throw() { invoke(smtEngine); if(!(isMuted() && ok())) { - printResult(out); + printResult(out, smtEngine->getOption("command-verbosity:" + getCommandName()).getIntegerValue().toUnsignedInt()); } } @@ -119,9 +119,11 @@ void CommandStatus::toStream(std::ostream& out, OutputLanguage language) const t Printer::getPrinter(language)->toStream(out, this); } -void Command::printResult(std::ostream& out) const throw() { +void Command::printResult(std::ostream& out, uint32_t verbosity) const throw() { if(d_commandStatus != NULL) { - out << *d_commandStatus; + if((!ok() && verbosity >= 1) || verbosity >= 2) { + out << *d_commandStatus; + } } } @@ -148,6 +150,10 @@ Command* EmptyCommand::clone() const { return new EmptyCommand(d_name); } +std::string EmptyCommand::getCommandName() const throw() { + return "empty"; +} + /* class EchoCommand */ EchoCommand::EchoCommand(std::string output) throw() : @@ -166,7 +172,7 @@ void EchoCommand::invoke(SmtEngine* smtEngine) throw() { void EchoCommand::invoke(SmtEngine* smtEngine, std::ostream& out) throw() { out << d_output << std::endl; d_commandStatus = CommandSuccess::instance(); - printResult(out); + printResult(out, smtEngine->getOption("command-verbosity:" + getCommandName()).getIntegerValue().toUnsignedInt()); } Command* EchoCommand::exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap) { @@ -177,6 +183,10 @@ Command* EchoCommand::clone() const { return new EchoCommand(d_output); } +std::string EchoCommand::getCommandName() const throw() { + return "echo"; +} + /* class AssertCommand */ AssertCommand::AssertCommand(const Expr& e) throw() : @@ -204,6 +214,11 @@ Command* AssertCommand::clone() const { return new AssertCommand(d_expr); } +std::string AssertCommand::getCommandName() const throw() { + return "assert"; +} + + /* class PushCommand */ void PushCommand::invoke(SmtEngine* smtEngine) throw() { @@ -223,6 +238,10 @@ Command* PushCommand::clone() const { return new PushCommand(); } +std::string PushCommand::getCommandName() const throw() { + return "push"; +} + /* class PopCommand */ void PopCommand::invoke(SmtEngine* smtEngine) throw() { @@ -242,6 +261,10 @@ Command* PopCommand::clone() const { return new PopCommand(); } +std::string PopCommand::getCommandName() const throw() { + return "pop"; +} + /* class CheckSatCommand */ CheckSatCommand::CheckSatCommand() throw() : @@ -269,9 +292,9 @@ Result CheckSatCommand::getResult() const throw() { return d_result; } -void CheckSatCommand::printResult(std::ostream& out) const throw() { +void CheckSatCommand::printResult(std::ostream& out, uint32_t verbosity) const throw() { if(! ok()) { - this->Command::printResult(out); + this->Command::printResult(out, verbosity); } else { out << d_result << endl; } @@ -289,6 +312,10 @@ Command* CheckSatCommand::clone() const { return c; } +std::string CheckSatCommand::getCommandName() const throw() { + return "check-sat"; +} + /* class QueryCommand */ QueryCommand::QueryCommand(const Expr& e) throw() : @@ -312,9 +339,9 @@ Result QueryCommand::getResult() const throw() { return d_result; } -void QueryCommand::printResult(std::ostream& out) const throw() { +void QueryCommand::printResult(std::ostream& out, uint32_t verbosity) const throw() { if(! ok()) { - this->Command::printResult(out); + this->Command::printResult(out, verbosity); } else { out << d_result << endl; } @@ -332,6 +359,10 @@ Command* QueryCommand::clone() const { return c; } +std::string QueryCommand::getCommandName() const throw() { + return "query"; +} + /* class QuitCommand */ QuitCommand::QuitCommand() throw() { @@ -350,6 +381,10 @@ Command* QuitCommand::clone() const { return new QuitCommand(); } +std::string QuitCommand::getCommandName() const throw() { + return "exit"; +} + /* class CommentCommand */ CommentCommand::CommentCommand(std::string comment) throw() : d_comment(comment) { @@ -372,6 +407,10 @@ Command* CommentCommand::clone() const { return new CommentCommand(d_comment); } +std::string CommentCommand::getCommandName() const throw() { + return "comment"; +} + /* class CommandSequence */ CommandSequence::CommandSequence() throw() : @@ -422,16 +461,13 @@ void CommandSequence::invoke(SmtEngine* smtEngine, std::ostream& out) throw() { d_commandStatus = CommandSuccess::instance(); } -CommandSequence::const_iterator CommandSequence::begin() const throw() { - return d_commandSequence.begin(); -} - Command* CommandSequence::exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap) { CommandSequence* seq = new CommandSequence(); for(iterator i = begin(); i != end(); ++i) { Command* cmd_to_export = *i; Command* cmd = cmd_to_export->exportTo(exprManager, variableMap); seq->addCommand(cmd); + Debug("export") << "[export] so far coverted: " << seq << endl; } seq->d_index = d_index; return seq; @@ -446,6 +482,10 @@ Command* CommandSequence::clone() const { return seq; } +CommandSequence::const_iterator CommandSequence::begin() const throw() { + return d_commandSequence.begin(); +} + CommandSequence::const_iterator CommandSequence::end() const throw() { return d_commandSequence.end(); } @@ -458,6 +498,10 @@ CommandSequence::iterator CommandSequence::end() throw() { return d_commandSequence.end(); } +std::string CommandSequence::getCommandName() const throw() { + return "sequence"; +} + /* class DeclarationSequenceCommand */ /* class DeclarationDefinitionCommand */ @@ -500,6 +544,10 @@ Command* DeclareFunctionCommand::clone() const { return new DeclareFunctionCommand(d_symbol, d_func, d_type); } +std::string DeclareFunctionCommand::getCommandName() const throw() { + return "declare-fun"; +} + /* class DeclareTypeCommand */ DeclareTypeCommand::DeclareTypeCommand(const std::string& id, size_t arity, Type t) throw() : @@ -530,6 +578,10 @@ Command* DeclareTypeCommand::clone() const { return new DeclareTypeCommand(d_symbol, d_arity, d_type); } +std::string DeclareTypeCommand::getCommandName() const throw() { + return "declare-sort"; +} + /* class DefineTypeCommand */ DefineTypeCommand::DefineTypeCommand(const std::string& id, @@ -571,6 +623,10 @@ Command* DefineTypeCommand::clone() const { return new DefineTypeCommand(d_symbol, d_params, d_type); } +std::string DefineTypeCommand::getCommandName() const throw() { + return "define-sort"; +} + /* class DefineFunctionCommand */ DefineFunctionCommand::DefineFunctionCommand(const std::string& id, @@ -616,7 +672,7 @@ void DefineFunctionCommand::invoke(SmtEngine* smtEngine) throw() { } Command* DefineFunctionCommand::exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap) { - Expr func = d_func.exportTo(exprManager, variableMap); + Expr func = d_func.exportTo(exprManager, variableMap, /* flags = */ ExprManager::VAR_FLAG_DEFINED); vector<Expr> formals; transform(d_formals.begin(), d_formals.end(), back_inserter(formals), ExportTransformer(exprManager, variableMap)); @@ -628,6 +684,10 @@ Command* DefineFunctionCommand::clone() const { return new DefineFunctionCommand(d_symbol, d_func, d_formals, d_formula); } +std::string DefineFunctionCommand::getCommandName() const throw() { + return "define-fun"; +} + /* class DefineNamedFunctionCommand */ DefineNamedFunctionCommand::DefineNamedFunctionCommand(const std::string& id, @@ -695,6 +755,10 @@ Command* SetUserAttributeCommand::clone() const{ return new SetUserAttributeCommand( d_attr, d_expr ); } +std::string SetUserAttributeCommand::getCommandName() const throw() { + return "set-user-attribute"; +} + /* class SimplifyCommand */ SimplifyCommand::SimplifyCommand(Expr term) throw() : @@ -718,9 +782,9 @@ Expr SimplifyCommand::getResult() const throw() { return d_result; } -void SimplifyCommand::printResult(std::ostream& out) const throw() { +void SimplifyCommand::printResult(std::ostream& out, uint32_t verbosity) const throw() { if(! ok()) { - this->Command::printResult(out); + this->Command::printResult(out, verbosity); } else { out << d_result << endl; } @@ -738,6 +802,10 @@ Command* SimplifyCommand::clone() const { return c; } +std::string SimplifyCommand::getCommandName() const throw() { + return "simplify"; +} + /* class ExpandDefinitionsCommand */ ExpandDefinitionsCommand::ExpandDefinitionsCommand(Expr term) throw() : @@ -757,9 +825,9 @@ Expr ExpandDefinitionsCommand::getResult() const throw() { return d_result; } -void ExpandDefinitionsCommand::printResult(std::ostream& out) const throw() { +void ExpandDefinitionsCommand::printResult(std::ostream& out, uint32_t verbosity) const throw() { if(! ok()) { - this->Command::printResult(out); + this->Command::printResult(out, verbosity); } else { out << d_result << endl; } @@ -777,6 +845,10 @@ Command* ExpandDefinitionsCommand::clone() const { return c; } +std::string ExpandDefinitionsCommand::getCommandName() const throw() { + return "expand-definitions"; +} + /* class GetValueCommand */ GetValueCommand::GetValueCommand(Expr term) throw() : @@ -795,17 +867,17 @@ const std::vector<Expr>& GetValueCommand::getTerms() const throw() { void GetValueCommand::invoke(SmtEngine* smtEngine) throw() { try { - vector<Node> result; - NodeManager* nm = NodeManager::fromExprManager(smtEngine->getExprManager()); + vector<Expr> result; + ExprManager* em = smtEngine->getExprManager(); + NodeManager* nm = NodeManager::fromExprManager(em); for(std::vector<Expr>::const_iterator i = d_terms.begin(); i != d_terms.end(); ++i) { Assert(nm == NodeManager::fromExprManager((*i).getExprManager())); smt::SmtScope scope(smtEngine); Node request = Node::fromExpr(options::expandDefinitions() ? smtEngine->expandDefinitions(*i) : *i); Node value = Node::fromExpr(smtEngine->getValue(*i)); - result.push_back(nm->mkNode(kind::SEXPR, request, value)); + result.push_back(nm->mkNode(kind::SEXPR, request, value).toExpr()); } - Node n = nm->mkNode(kind::SEXPR, result); - d_result = nm->toExpr(n); + d_result = em->mkExpr(kind::SEXPR, result); d_commandStatus = CommandSuccess::instance(); } catch(exception& e) { d_commandStatus = new CommandFailure(e.what()); @@ -816,9 +888,9 @@ Expr GetValueCommand::getResult() const throw() { return d_result; } -void GetValueCommand::printResult(std::ostream& out) const throw() { +void GetValueCommand::printResult(std::ostream& out, uint32_t verbosity) const throw() { if(! ok()) { - this->Command::printResult(out); + this->Command::printResult(out, verbosity); } else { Expr::dag::Scope scope(out, false); out << d_result << endl; @@ -841,6 +913,10 @@ Command* GetValueCommand::clone() const { return c; } +std::string GetValueCommand::getCommandName() const throw() { + return "get-value"; +} + /* class GetAssignmentCommand */ GetAssignmentCommand::GetAssignmentCommand() throw() { @@ -859,9 +935,9 @@ SExpr GetAssignmentCommand::getResult() const throw() { return d_result; } -void GetAssignmentCommand::printResult(std::ostream& out) const throw() { +void GetAssignmentCommand::printResult(std::ostream& out, uint32_t verbosity) const throw() { if(! ok()) { - this->Command::printResult(out); + this->Command::printResult(out, verbosity); } else { out << d_result << endl; } @@ -879,6 +955,10 @@ Command* GetAssignmentCommand::clone() const { return c; } +std::string GetAssignmentCommand::getCommandName() const throw() { + return "get-assignment"; +} + /* class GetModelCommand */ GetModelCommand::GetModelCommand() throw() { @@ -900,9 +980,9 @@ Model* GetModelCommand::getResult() const throw() { } */ -void GetModelCommand::printResult(std::ostream& out) const throw() { +void GetModelCommand::printResult(std::ostream& out, uint32_t verbosity) const throw() { if(! ok()) { - this->Command::printResult(out); + this->Command::printResult(out, verbosity); } else { out << *d_result; } @@ -922,6 +1002,10 @@ Command* GetModelCommand::clone() const { return c; } +std::string GetModelCommand::getCommandName() const throw() { + return "get-model"; +} + /* class GetProofCommand */ GetProofCommand::GetProofCommand() throw() { @@ -940,9 +1024,9 @@ Proof* GetProofCommand::getResult() const throw() { return d_result; } -void GetProofCommand::printResult(std::ostream& out) const throw() { +void GetProofCommand::printResult(std::ostream& out, uint32_t verbosity) const throw() { if(! ok()) { - this->Command::printResult(out); + this->Command::printResult(out, verbosity); } else { d_result->toStream(out); } @@ -960,6 +1044,10 @@ Command* GetProofCommand::clone() const { return c; } +std::string GetProofCommand::getCommandName() const throw() { + return "get-proof"; +} + /* class GetUnsatCoreCommand */ GetUnsatCoreCommand::GetUnsatCoreCommand() throw() { @@ -977,9 +1065,9 @@ void GetUnsatCoreCommand::invoke(SmtEngine* smtEngine) throw() { d_commandStatus = new CommandUnsupported(); } -void GetUnsatCoreCommand::printResult(std::ostream& out) const throw() { +void GetUnsatCoreCommand::printResult(std::ostream& out, uint32_t verbosity) const throw() { if(! ok()) { - this->Command::printResult(out); + this->Command::printResult(out, verbosity); } else { //do nothing -- unsat cores not yet supported // d_result->toStream(out); @@ -998,6 +1086,10 @@ Command* GetUnsatCoreCommand::clone() const { return c; } +std::string GetUnsatCoreCommand::getCommandName() const throw() { + return "get-unsat-core"; +} + /* class GetAssertionsCommand */ GetAssertionsCommand::GetAssertionsCommand() throw() { @@ -1021,9 +1113,9 @@ std::string GetAssertionsCommand::getResult() const throw() { return d_result; } -void GetAssertionsCommand::printResult(std::ostream& out) const throw() { +void GetAssertionsCommand::printResult(std::ostream& out, uint32_t verbosity) const throw() { if(! ok()) { - this->Command::printResult(out); + this->Command::printResult(out, verbosity); } else { out << d_result; } @@ -1041,6 +1133,10 @@ Command* GetAssertionsCommand::clone() const { return c; } +std::string GetAssertionsCommand::getCommandName() const throw() { + return "get-assertions"; +} + /* class SetBenchmarkStatusCommand */ SetBenchmarkStatusCommand::SetBenchmarkStatusCommand(BenchmarkStatus status) throw() : @@ -1071,6 +1167,10 @@ Command* SetBenchmarkStatusCommand::clone() const { return new SetBenchmarkStatusCommand(d_status); } +std::string SetBenchmarkStatusCommand::getCommandName() const throw() { + return "set-info"; +} + /* class SetBenchmarkLogicCommand */ SetBenchmarkLogicCommand::SetBenchmarkLogicCommand(std::string logic) throw() : @@ -1098,6 +1198,10 @@ Command* SetBenchmarkLogicCommand::clone() const { return new SetBenchmarkLogicCommand(d_logic); } +std::string SetBenchmarkLogicCommand::getCommandName() const throw() { + return "set-logic"; +} + /* class SetInfoCommand */ SetInfoCommand::SetInfoCommand(std::string flag, const SExpr& sexpr) throw() : @@ -1118,7 +1222,8 @@ void SetInfoCommand::invoke(SmtEngine* smtEngine) throw() { smtEngine->setInfo(d_flag, d_sexpr); d_commandStatus = CommandSuccess::instance(); } catch(UnrecognizedOptionException&) { - d_commandStatus = new CommandUnsupported(); + // As per SMT-LIB spec, silently accept unknown set-info keys + d_commandStatus = CommandSuccess::instance(); } catch(exception& e) { d_commandStatus = new CommandFailure(e.what()); } @@ -1132,6 +1237,10 @@ Command* SetInfoCommand::clone() const { return new SetInfoCommand(d_flag, d_sexpr); } +std::string SetInfoCommand::getCommandName() const throw() { + return "set-info"; +} + /* class GetInfoCommand */ GetInfoCommand::GetInfoCommand(std::string flag) throw() : @@ -1162,9 +1271,9 @@ std::string GetInfoCommand::getResult() const throw() { return d_result; } -void GetInfoCommand::printResult(std::ostream& out) const throw() { +void GetInfoCommand::printResult(std::ostream& out, uint32_t verbosity) const throw() { if(! ok()) { - this->Command::printResult(out); + this->Command::printResult(out, verbosity); } else if(d_result != "") { out << d_result << endl; } @@ -1182,6 +1291,10 @@ Command* GetInfoCommand::clone() const { return c; } +std::string GetInfoCommand::getCommandName() const throw() { + return "get-info"; +} + /* class SetOptionCommand */ SetOptionCommand::SetOptionCommand(std::string flag, const SExpr& sexpr) throw() : @@ -1216,6 +1329,10 @@ Command* SetOptionCommand::clone() const { return new SetOptionCommand(d_flag, d_sexpr); } +std::string SetOptionCommand::getCommandName() const throw() { + return "set-option"; +} + /* class GetOptionCommand */ GetOptionCommand::GetOptionCommand(std::string flag) throw() : @@ -1228,11 +1345,9 @@ std::string GetOptionCommand::getFlag() const throw() { void GetOptionCommand::invoke(SmtEngine* smtEngine) throw() { try { - vector<SExpr> v; - v.push_back(SExpr(SExpr::Keyword(string(":") + d_flag))); - v.push_back(smtEngine->getOption(d_flag)); + SExpr res = smtEngine->getOption(d_flag); stringstream ss; - ss << SExpr(v); + ss << res; d_result = ss.str(); d_commandStatus = CommandSuccess::instance(); } catch(UnrecognizedOptionException&) { @@ -1246,9 +1361,9 @@ std::string GetOptionCommand::getResult() const throw() { return d_result; } -void GetOptionCommand::printResult(std::ostream& out) const throw() { +void GetOptionCommand::printResult(std::ostream& out, uint32_t verbosity) const throw() { if(! ok()) { - this->Command::printResult(out); + this->Command::printResult(out, verbosity); } else if(d_result != "") { out << d_result << endl; } @@ -1266,6 +1381,10 @@ Command* GetOptionCommand::clone() const { return c; } +std::string GetOptionCommand::getCommandName() const throw() { + return "get-option"; +} + /* class DatatypeDeclarationCommand */ DatatypeDeclarationCommand::DatatypeDeclarationCommand(const DatatypeType& datatype) throw() : @@ -1295,6 +1414,10 @@ Command* DatatypeDeclarationCommand::clone() const { return new DatatypeDeclarationCommand(d_datatypes); } +std::string DatatypeDeclarationCommand::getCommandName() const throw() { + return "declare-datatypes"; +} + /* class RewriteRuleCommand */ RewriteRuleCommand::RewriteRuleCommand(const std::vector<Expr>& vars, @@ -1395,6 +1518,10 @@ Command* RewriteRuleCommand::clone() const { return new RewriteRuleCommand(d_vars, d_guards, d_head, d_body, d_triggers); } +std::string RewriteRuleCommand::getCommandName() const throw() { + return "rewrite-rule"; +} + /* class PropagateRuleCommand */ PropagateRuleCommand::PropagateRuleCommand(const std::vector<Expr>& vars, @@ -1512,6 +1639,10 @@ Command* PropagateRuleCommand::clone() const { return new PropagateRuleCommand(d_vars, d_guards, d_heads, d_body, d_triggers); } +std::string PropagateRuleCommand::getCommandName() const throw() { + return "propagate-rule"; +} + /* output stream insertion operator for benchmark statuses */ std::ostream& operator<<(std::ostream& out, BenchmarkStatus status) throw() { diff --git a/src/expr/command.h b/src/expr/command.h index 38a8b1efa..a3d58e5dd 100644 --- a/src/expr/command.h +++ b/src/expr/command.h @@ -215,6 +215,8 @@ public: std::string toString() const throw(); + virtual std::string getCommandName() const throw() = 0; + /** * If false, instruct this Command not to print a success message. */ @@ -240,7 +242,7 @@ public: /** Get the command status (it's NULL if we haven't run yet). */ const CommandStatus* getCommandStatus() const throw() { return d_commandStatus; } - virtual void printResult(std::ostream& out) const throw(); + virtual void printResult(std::ostream& out, uint32_t verbosity = 2) const throw(); /** * Maps this Command into one for a different ExprManager, using @@ -287,6 +289,7 @@ public: void invoke(SmtEngine* smtEngine) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class EmptyCommand */ class CVC4_PUBLIC EchoCommand : public Command { @@ -300,6 +303,7 @@ public: void invoke(SmtEngine* smtEngine, std::ostream& out) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class EchoCommand */ class CVC4_PUBLIC AssertCommand : public Command { @@ -312,6 +316,7 @@ public: void invoke(SmtEngine* smtEngine) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class AssertCommand */ class CVC4_PUBLIC PushCommand : public Command { @@ -320,6 +325,7 @@ public: void invoke(SmtEngine* smtEngine) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class PushCommand */ class CVC4_PUBLIC PopCommand : public Command { @@ -328,6 +334,7 @@ public: void invoke(SmtEngine* smtEngine) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class PopCommand */ class CVC4_PUBLIC DeclarationDefinitionCommand : public Command { @@ -352,6 +359,7 @@ public: void invoke(SmtEngine* smtEngine) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class DeclareFunctionCommand */ class CVC4_PUBLIC DeclareTypeCommand : public DeclarationDefinitionCommand { @@ -366,6 +374,7 @@ public: void invoke(SmtEngine* smtEngine) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class DeclareTypeCommand */ class CVC4_PUBLIC DefineTypeCommand : public DeclarationDefinitionCommand { @@ -381,6 +390,7 @@ public: void invoke(SmtEngine* smtEngine) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class DefineTypeCommand */ class CVC4_PUBLIC DefineFunctionCommand : public DeclarationDefinitionCommand { @@ -399,6 +409,7 @@ public: void invoke(SmtEngine* smtEngine) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class DefineFunctionCommand */ /** @@ -433,6 +444,7 @@ public: void invoke(SmtEngine* smtEngine) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class SetUserAttributeCommand */ @@ -447,9 +459,10 @@ public: Expr getExpr() const throw(); void invoke(SmtEngine* smtEngine) throw(); Result getResult() const throw(); - void printResult(std::ostream& out) const throw(); + void printResult(std::ostream& out, uint32_t verbosity = 2) const throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class CheckSatCommand */ class CVC4_PUBLIC QueryCommand : public Command { @@ -462,9 +475,10 @@ public: Expr getExpr() const throw(); void invoke(SmtEngine* smtEngine) throw(); Result getResult() const throw(); - void printResult(std::ostream& out) const throw(); + void printResult(std::ostream& out, uint32_t verbosity = 2) const throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class QueryCommand */ // this is TRANSFORM in the CVC presentation language @@ -478,9 +492,10 @@ public: Expr getTerm() const throw(); void invoke(SmtEngine* smtEngine) throw(); Expr getResult() const throw(); - void printResult(std::ostream& out) const throw(); + void printResult(std::ostream& out, uint32_t verbosity = 2) const throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class SimplifyCommand */ class CVC4_PUBLIC ExpandDefinitionsCommand : public Command { @@ -493,9 +508,10 @@ public: Expr getTerm() const throw(); void invoke(SmtEngine* smtEngine) throw(); Expr getResult() const throw(); - void printResult(std::ostream& out) const throw(); + void printResult(std::ostream& out, uint32_t verbosity = 2) const throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class ExpandDefinitionsCommand */ class CVC4_PUBLIC GetValueCommand : public Command { @@ -509,9 +525,10 @@ public: const std::vector<Expr>& getTerms() const throw(); void invoke(SmtEngine* smtEngine) throw(); Expr getResult() const throw(); - void printResult(std::ostream& out) const throw(); + void printResult(std::ostream& out, uint32_t verbosity = 2) const throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class GetValueCommand */ class CVC4_PUBLIC GetAssignmentCommand : public Command { @@ -522,9 +539,10 @@ public: ~GetAssignmentCommand() throw() {} void invoke(SmtEngine* smtEngine) throw(); SExpr getResult() const throw(); - void printResult(std::ostream& out) const throw(); + void printResult(std::ostream& out, uint32_t verbosity = 2) const throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class GetAssignmentCommand */ class CVC4_PUBLIC GetModelCommand : public Command { @@ -537,9 +555,10 @@ public: void invoke(SmtEngine* smtEngine) throw(); // Model is private to the library -- for now //Model* getResult() const throw(); - void printResult(std::ostream& out) const throw(); + void printResult(std::ostream& out, uint32_t verbosity = 2) const throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class GetModelCommand */ class CVC4_PUBLIC GetProofCommand : public Command { @@ -550,9 +569,10 @@ public: ~GetProofCommand() throw() {} void invoke(SmtEngine* smtEngine) throw(); Proof* getResult() const throw(); - void printResult(std::ostream& out) const throw(); + void printResult(std::ostream& out, uint32_t verbosity = 2) const throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class GetProofCommand */ class CVC4_PUBLIC GetUnsatCoreCommand : public Command { @@ -562,9 +582,10 @@ public: GetUnsatCoreCommand() throw(); ~GetUnsatCoreCommand() throw() {} void invoke(SmtEngine* smtEngine) throw(); - void printResult(std::ostream& out) const throw(); + void printResult(std::ostream& out, uint32_t verbosity = 2) const throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class GetUnsatCoreCommand */ class CVC4_PUBLIC GetAssertionsCommand : public Command { @@ -575,9 +596,10 @@ public: ~GetAssertionsCommand() throw() {} void invoke(SmtEngine* smtEngine) throw(); std::string getResult() const throw(); - void printResult(std::ostream& out) const throw(); + void printResult(std::ostream& out, uint32_t verbosity = 2) const throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class GetAssertionsCommand */ class CVC4_PUBLIC SetBenchmarkStatusCommand : public Command { @@ -590,6 +612,7 @@ public: void invoke(SmtEngine* smtEngine) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class SetBenchmarkStatusCommand */ class CVC4_PUBLIC SetBenchmarkLogicCommand : public Command { @@ -602,6 +625,7 @@ public: void invoke(SmtEngine* smtEngine) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class SetBenchmarkLogicCommand */ class CVC4_PUBLIC SetInfoCommand : public Command { @@ -616,6 +640,7 @@ public: void invoke(SmtEngine* smtEngine) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class SetInfoCommand */ class CVC4_PUBLIC GetInfoCommand : public Command { @@ -628,9 +653,10 @@ public: std::string getFlag() const throw(); void invoke(SmtEngine* smtEngine) throw(); std::string getResult() const throw(); - void printResult(std::ostream& out) const throw(); + void printResult(std::ostream& out, uint32_t verbosity = 2) const throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class GetInfoCommand */ class CVC4_PUBLIC SetOptionCommand : public Command { @@ -645,6 +671,7 @@ public: void invoke(SmtEngine* smtEngine) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class SetOptionCommand */ class CVC4_PUBLIC GetOptionCommand : public Command { @@ -657,9 +684,10 @@ public: std::string getFlag() const throw(); void invoke(SmtEngine* smtEngine) throw(); std::string getResult() const throw(); - void printResult(std::ostream& out) const throw(); + void printResult(std::ostream& out, uint32_t verbosity = 2) const throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class GetOptionCommand */ class CVC4_PUBLIC DatatypeDeclarationCommand : public Command { @@ -673,6 +701,7 @@ public: void invoke(SmtEngine* smtEngine) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class DatatypeDeclarationCommand */ class CVC4_PUBLIC RewriteRuleCommand : public Command { @@ -703,6 +732,7 @@ public: void invoke(SmtEngine* smtEngine) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class RewriteRuleCommand */ class CVC4_PUBLIC PropagateRuleCommand : public Command { @@ -738,6 +768,7 @@ public: void invoke(SmtEngine* smtEngine) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class PropagateRuleCommand */ @@ -748,6 +779,7 @@ public: void invoke(SmtEngine* smtEngine) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class QuitCommand */ class CVC4_PUBLIC CommentCommand : public Command { @@ -759,6 +791,7 @@ public: void invoke(SmtEngine* smtEngine) throw(); Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class CommentCommand */ class CVC4_PUBLIC CommandSequence : public Command { @@ -788,6 +821,7 @@ public: Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap); Command* clone() const; + std::string getCommandName() const throw(); };/* class CommandSequence */ class CVC4_PUBLIC DeclarationSequence : public CommandSequence { diff --git a/src/expr/expr.i b/src/expr/expr.i index 977345a63..c649a5ebb 100644 --- a/src/expr/expr.i +++ b/src/expr/expr.i @@ -9,7 +9,11 @@ #endif /* SWIGJAVA */ %} +#ifdef SWIGPYTHON +%rename(doApply) CVC4::ExprHashFunction::operator()(CVC4::Expr) const; +#else /* SWIGPYTHON */ %rename(apply) CVC4::ExprHashFunction::operator()(CVC4::Expr) const; +#endif /* SWIGPYTHON */ %ignore CVC4::operator<<(std::ostream&, const Expr&); %ignore CVC4::operator<<(std::ostream&, const TypeCheckingException&); diff --git a/src/expr/expr_manager.i b/src/expr/expr_manager.i index a386af5ee..a2ff0337a 100644 --- a/src/expr/expr_manager.i +++ b/src/expr/expr_manager.i @@ -34,6 +34,10 @@ %template(mkConst) CVC4::ExprManager::mkConst<CVC4::UninterpretedConstant>; %template(mkConst) CVC4::ExprManager::mkConst<CVC4::kind::Kind_t>; %template(mkConst) CVC4::ExprManager::mkConst<CVC4::Datatype>; +%template(mkConst) CVC4::ExprManager::mkConst<CVC4::TupleSelect>; +%template(mkConst) CVC4::ExprManager::mkConst<CVC4::TupleUpdate>; +%template(mkConst) CVC4::ExprManager::mkConst<CVC4::RecordSelect>; +%template(mkConst) CVC4::ExprManager::mkConst<CVC4::RecordUpdate>; %template(mkConst) CVC4::ExprManager::mkConst<CVC4::Rational>; %template(mkConst) CVC4::ExprManager::mkConst<CVC4::BitVector>; %template(mkConst) CVC4::ExprManager::mkConst<CVC4::Predicate>; diff --git a/src/expr/expr_manager_template.cpp b/src/expr/expr_manager_template.cpp index 524bc2095..d87c498e6 100644 --- a/src/expr/expr_manager_template.cpp +++ b/src/expr/expr_manager_template.cpp @@ -304,8 +304,8 @@ Expr ExprManager::mkExpr(Kind kind, Expr child1, const std::vector<Expr>& otherC Expr ExprManager::mkExpr(Expr opExpr) { const unsigned n = 0; - Kind kind = kind::operatorKindToKind(opExpr.getKind()); - CheckArgument(kind::metaKindOf(kind) == kind::metakind::PARAMETERIZED, opExpr, + Kind kind = NodeManager::operatorToKind(opExpr.getNode()); + CheckArgument(opExpr.getKind() == kind::BUILTIN || kind::metaKindOf(kind) == kind::metakind::PARAMETERIZED, opExpr, "This Expr constructor is for parameterized kinds only"); CheckArgument(n >= minArity(kind) && n <= maxArity(kind), kind, "Exprs with kind %s must have at least %u children and " @@ -323,8 +323,8 @@ Expr ExprManager::mkExpr(Expr opExpr) { Expr ExprManager::mkExpr(Expr opExpr, Expr child1) { const unsigned n = 1; - Kind kind = kind::operatorKindToKind(opExpr.getKind()); - CheckArgument(kind::metaKindOf(kind) == kind::metakind::PARAMETERIZED, opExpr, + Kind kind = NodeManager::operatorToKind(opExpr.getNode()); + CheckArgument(opExpr.getKind() == kind::BUILTIN || kind::metaKindOf(kind) == kind::metakind::PARAMETERIZED, opExpr, "This Expr constructor is for parameterized kinds only"); CheckArgument(n >= minArity(kind) && n <= maxArity(kind), kind, "Exprs with kind %s must have at least %u children and " @@ -342,8 +342,8 @@ Expr ExprManager::mkExpr(Expr opExpr, Expr child1) { Expr ExprManager::mkExpr(Expr opExpr, Expr child1, Expr child2) { const unsigned n = 2; - Kind kind = kind::operatorKindToKind(opExpr.getKind()); - CheckArgument(kind::metaKindOf(kind) == kind::metakind::PARAMETERIZED, opExpr, + Kind kind = NodeManager::operatorToKind(opExpr.getNode()); + CheckArgument(opExpr.getKind() == kind::BUILTIN || kind::metaKindOf(kind) == kind::metakind::PARAMETERIZED, opExpr, "This Expr constructor is for parameterized kinds only"); CheckArgument(n >= minArity(kind) && n <= maxArity(kind), kind, "Exprs with kind %s must have at least %u children and " @@ -363,8 +363,8 @@ Expr ExprManager::mkExpr(Expr opExpr, Expr child1, Expr child2) { Expr ExprManager::mkExpr(Expr opExpr, Expr child1, Expr child2, Expr child3) { const unsigned n = 3; - Kind kind = kind::operatorKindToKind(opExpr.getKind()); - CheckArgument(kind::metaKindOf(kind) == kind::metakind::PARAMETERIZED, opExpr, + Kind kind = NodeManager::operatorToKind(opExpr.getNode()); + CheckArgument(opExpr.getKind() == kind::BUILTIN || kind::metaKindOf(kind) == kind::metakind::PARAMETERIZED, opExpr, "This Expr constructor is for parameterized kinds only"); CheckArgument(n >= minArity(kind) && n <= maxArity(kind), kind, "Exprs with kind %s must have at least %u children and " @@ -386,8 +386,8 @@ Expr ExprManager::mkExpr(Expr opExpr, Expr child1, Expr child2, Expr child3) { Expr ExprManager::mkExpr(Expr opExpr, Expr child1, Expr child2, Expr child3, Expr child4) { const unsigned n = 4; - Kind kind = kind::operatorKindToKind(opExpr.getKind()); - CheckArgument(kind::metaKindOf(kind) == kind::metakind::PARAMETERIZED, opExpr, + Kind kind = NodeManager::operatorToKind(opExpr.getNode()); + CheckArgument(opExpr.getKind() == kind::BUILTIN || kind::metaKindOf(kind) == kind::metakind::PARAMETERIZED, opExpr, "This Expr constructor is for parameterized kinds only"); CheckArgument(n >= minArity(kind) && n <= maxArity(kind), kind, "Exprs with kind %s must have at least %u children and " @@ -410,8 +410,8 @@ Expr ExprManager::mkExpr(Expr opExpr, Expr child1, Expr child2, Expr child3, Expr ExprManager::mkExpr(Expr opExpr, Expr child1, Expr child2, Expr child3, Expr child4, Expr child5) { const unsigned n = 5; - Kind kind = kind::operatorKindToKind(opExpr.getKind()); - CheckArgument(kind::metaKindOf(kind) == kind::metakind::PARAMETERIZED, opExpr, + Kind kind = NodeManager::operatorToKind(opExpr.getNode()); + CheckArgument(opExpr.getKind() == kind::BUILTIN || kind::metaKindOf(kind) == kind::metakind::PARAMETERIZED, opExpr, "This Expr constructor is for parameterized kinds only"); CheckArgument(n >= minArity(kind) && n <= maxArity(kind), kind, "Exprs with kind %s must have at least %u children and " @@ -434,8 +434,8 @@ Expr ExprManager::mkExpr(Expr opExpr, Expr child1, Expr child2, Expr child3, Expr ExprManager::mkExpr(Expr opExpr, const std::vector<Expr>& children) { const unsigned n = children.size(); - Kind kind = kind::operatorKindToKind(opExpr.getKind()); - CheckArgument(kind::metaKindOf(kind) == kind::metakind::PARAMETERIZED, opExpr, + Kind kind = NodeManager::operatorToKind(opExpr.getNode()); + CheckArgument(opExpr.getKind() == kind::BUILTIN || kind::metaKindOf(kind) == kind::metakind::PARAMETERIZED, opExpr, "This Expr constructor is for parameterized kinds only"); CheckArgument(n >= minArity(kind) && n <= maxArity(kind), kind, "Exprs with kind %s must have at least %u children and " @@ -509,7 +509,6 @@ FunctionType ExprManager::mkPredicateType(const std::vector<Type>& sorts) { TupleType ExprManager::mkTupleType(const std::vector<Type>& types) { NodeManagerScope nms(d_nodeManager); - Assert( types.size() >= 1 ); std::vector<TypeNode> typeNodes; for (unsigned i = 0, i_end = types.size(); i < i_end; ++ i) { typeNodes.push_back(*types[i].d_typeNode); @@ -719,9 +718,9 @@ TesterType ExprManager::mkTesterType(Type domain) const { return Type(d_nodeManager, new TypeNode(d_nodeManager->mkTesterType(*domain.d_typeNode))); } -SortType ExprManager::mkSort(const std::string& name) const { +SortType ExprManager::mkSort(const std::string& name, uint32_t flags) const { NodeManagerScope nms(d_nodeManager); - return SortType(Type(d_nodeManager, new TypeNode(d_nodeManager->mkSort(name)))); + return SortType(Type(d_nodeManager, new TypeNode(d_nodeManager->mkSort(name, flags)))); } SortConstructorType ExprManager::mkSortConstructor(const std::string& name, @@ -805,20 +804,20 @@ Type ExprManager::getType(Expr e, bool check) throw (TypeCheckingException) { return t; } -Expr ExprManager::mkVar(const std::string& name, Type type, bool isGlobal) { +Expr ExprManager::mkVar(const std::string& name, Type type, uint32_t flags) { Assert(NodeManager::currentNM() == NULL, "ExprManager::mkVar() should only be called externally, not from within CVC4 code. Please use mkSkolem()."); NodeManagerScope nms(d_nodeManager); - Node* n = d_nodeManager->mkVarPtr(name, *type.d_typeNode, isGlobal); + Node* n = d_nodeManager->mkVarPtr(name, *type.d_typeNode, flags); Debug("nm") << "set " << name << " on " << *n << std::endl; INC_STAT_VAR(type, false); return Expr(this, n); } -Expr ExprManager::mkVar(Type type, bool isGlobal) { +Expr ExprManager::mkVar(Type type, uint32_t flags) { Assert(NodeManager::currentNM() == NULL, "ExprManager::mkVar() should only be called externally, not from within CVC4 code. Please use mkSkolem()."); NodeManagerScope nms(d_nodeManager); INC_STAT_VAR(type, false); - return Expr(this, d_nodeManager->mkVarPtr(*type.d_typeNode, isGlobal)); + return Expr(this, d_nodeManager->mkVarPtr(*type.d_typeNode, flags)); } Expr ExprManager::mkBoundVar(const std::string& name, Type type) { diff --git a/src/expr/expr_manager_template.h b/src/expr/expr_manager_template.h index fd81c9bf8..3f0ec2f9c 100644 --- a/src/expr/expr_manager_template.h +++ b/src/expr/expr_manager_template.h @@ -430,8 +430,14 @@ public: /** Make a type representing a tester with the given parameterization. */ TesterType mkTesterType(Type domain) const; + /** Bits for use in mkSort() flags. */ + enum { + SORT_FLAG_NONE = 0, + SORT_FLAG_PLACEHOLDER = 1 + };/* enum */ + /** Make a new sort with the given name. */ - SortType mkSort(const std::string& name) const; + SortType mkSort(const std::string& name, uint32_t flags = SORT_FLAG_NONE) const; /** Make a sort constructor from a name and arity. */ SortConstructorType mkSortConstructor(const std::string& name, @@ -468,10 +474,82 @@ public: Type getType(Expr e, bool check = false) throw(TypeCheckingException); - // variables are special, because duplicates are permitted - Expr mkVar(const std::string& name, Type type, bool isGlobal = false); - Expr mkVar(Type type, bool isGlobal = false); + /** Bits for use in mkVar() flags. */ + enum { + VAR_FLAG_NONE = 0, + VAR_FLAG_GLOBAL = 1, + VAR_FLAG_DEFINED = 2 + };/* enum */ + + /** + * Create a new, fresh variable. This variable is guaranteed to be + * distinct from every variable thus far in the ExprManager, even + * if it shares a name with another; this is to support any kind of + * scoping policy on top of ExprManager. The SymbolTable class + * can be used to store and lookup symbols by name, if desired. + * + * @param name a name to associate to the fresh new variable + * @param type the type for the new variable + * @param flags - VAR_FLAG_NONE - no flags; + * VAR_FLAG_GLOBAL - whether this variable is to be + * considered "global" or not. Note that this information isn't + * used by the ExprManager, but is passed on to the ExprManager's + * event subscribers like the model-building service; if isGlobal + * is true, this newly-created variable will still available in + * models generated after an intervening pop. + * VAR_FLAG_DEFINED - if this is to be a "defined" symbol, e.g., for + * use with SmtEngine::defineFunction(). This keeps a declaration + * from being emitted in API dumps (since a subsequent definition is + * expected to be dumped instead). + */ + Expr mkVar(const std::string& name, Type type, uint32_t flags = VAR_FLAG_NONE); + + /** + * Create a (nameless) new, fresh variable. This variable is guaranteed + * to be distinct from every variable thus far in the ExprManager. + * + * @param type the type for the new variable + * @param flags - VAR_FLAG_GLOBAL - whether this variable is to be considered "global" + * or not. Note that this information isn't used by the ExprManager, + * but is passed on to the ExprManager's event subscribers like the + * model-building service; if isGlobal is true, this newly-created + * variable will still available in models generated after an + * intervening pop. + */ + Expr mkVar(Type type, uint32_t flags = VAR_FLAG_NONE); + + /** + * Create a new, fresh variable for use in a binder expression + * (the BOUND_VAR_LIST of a FORALL, EXISTS, or LAMBDA). It is + * an error for this bound variable to exist outside of a binder, + * and it should also only be used in a single binder expression. + * That is, two distinct FORALL expressions should use entirely + * disjoint sets of bound variables (however, a single FORALL + * expression can be used in multiple places in a formula without + * a problem). This newly-created bound variable is guaranteed to + * be distinct from every variable thus far in the ExprManager, even + * if it shares a name with another; this is to support any kind of + * scoping policy on top of ExprManager. The SymbolTable class + * can be used to store and lookup symbols by name, if desired. + * + * @param name a name to associate to the fresh new bound variable + * @param type the type for the new bound variable + */ Expr mkBoundVar(const std::string& name, Type type); + + /** + * Create a (nameless) new, fresh variable for use in a binder + * expression (the BOUND_VAR_LIST of a FORALL, EXISTS, or LAMBDA). + * It is an error for this bound variable to exist outside of a + * binder, and it should also only be used in a single binder + * expression. That is, two distinct FORALL expressions should use + * entirely disjoint sets of bound variables (however, a single FORALL + * expression can be used in multiple places in a formula without + * a problem). This newly-created bound variable is guaranteed to + * be distinct from every variable thus far in the ExprManager. + * + * @param type the type for the new bound variable + */ Expr mkBoundVar(Type type); /** Get a reference to the statistics registry for this ExprManager */ diff --git a/src/expr/expr_template.cpp b/src/expr/expr_template.cpp index 7e809ed62..ad9ec49ac 100644 --- a/src/expr/expr_template.cpp +++ b/src/expr/expr_template.cpp @@ -115,7 +115,7 @@ namespace expr { static Node exportConstant(TNode n, NodeManager* to); -Node exportInternal(TNode n, ExprManager* from, ExprManager* to, ExprManagerMapCollection& vmap) { +Node exportInternal(TNode n, ExprManager* from, ExprManager* to, ExprManagerMapCollection& vmap, uint32_t flags) { if(n.isNull()) return Node::null(); if(theory::kindToTheoryId(n.getKind()) == theory::THEORY_DATATYPES) { throw ExportUnsupportedException @@ -146,7 +146,7 @@ Node exportInternal(TNode n, ExprManager* from, ExprManager* to, ExprManagerMapC bool isGlobal; Node::fromExpr(from_e).getAttribute(GlobalVarAttr(), isGlobal); NodeManagerScope nullScope(NULL); - to_e = to->mkVar(name, type, isGlobal);// FIXME thread safety + to_e = to->mkVar(name, type, isGlobal ? ExprManager::VAR_FLAG_GLOBAL : flags);// FIXME thread safety } else if(n.getKind() == kind::SKOLEM) { // skolems are only available at the Node level (not the Expr level) TypeNode typeNode = TypeNode::fromType(type); @@ -178,13 +178,13 @@ Node exportInternal(TNode n, ExprManager* from, ExprManager* to, ExprManagerMapC if(n.getMetaKind() == kind::metakind::PARAMETERIZED) { Debug("export") << "+ parameterized, op is " << n.getOperator() << std::endl; children.reserve(n.getNumChildren() + 1); - children.push_back(exportInternal(n.getOperator(), from, to, vmap)); + children.push_back(exportInternal(n.getOperator(), from, to, vmap, flags)); } else { children.reserve(n.getNumChildren()); } for(TNode::iterator i = n.begin(), i_end = n.end(); i != i_end; ++i) { Debug("export") << "+ child: " << *i << std::endl; - children.push_back(exportInternal(*i, from, to, vmap)); + children.push_back(exportInternal(*i, from, to, vmap, flags)); } if(Debug.isOn("export")) { ExprManagerScope ems(*to); @@ -199,11 +199,12 @@ Node exportInternal(TNode n, ExprManager* from, ExprManager* to, ExprManagerMapC }/* CVC4::expr namespace */ -Expr Expr::exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap) const { +Expr Expr::exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap, + uint32_t flags /* = 0 */) const { Assert(d_exprManager != exprManager, "No sense in cloning an Expr in the same ExprManager"); ExprManagerScope ems(*this); - return Expr(exprManager, new Node(expr::exportInternal(*d_node, d_exprManager, exprManager, variableMap))); + return Expr(exprManager, new Node(expr::exportInternal(*d_node, d_exprManager, exprManager, variableMap, flags))); } Expr& Expr::operator=(const Expr& e) { diff --git a/src/expr/expr_template.h b/src/expr/expr_template.h index 7772de81e..e262fada8 100644 --- a/src/expr/expr_template.h +++ b/src/expr/expr_template.h @@ -81,7 +81,7 @@ namespace expr { class CVC4_PUBLIC ExprDag; class CVC4_PUBLIC ExprSetLanguage; - NodeTemplate<true> exportInternal(NodeTemplate<false> n, ExprManager* from, ExprManager* to, ExprManagerMapCollection& vmap); + NodeTemplate<true> exportInternal(NodeTemplate<false> n, ExprManager* from, ExprManager* to, ExprManagerMapCollection& vmap, uint32_t flags); }/* CVC4::expr namespace */ /** @@ -510,7 +510,7 @@ public: * variableMap for the translation and extending it with any new * mappings. */ - Expr exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap) const; + Expr exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap, uint32_t flags = 0) const; /** * IOStream manipulator to set the maximum depth of Exprs when @@ -591,7 +591,7 @@ private: friend class TypeCheckingException; friend class expr::pickle::Pickler; friend class prop::TheoryProxy; - friend NodeTemplate<true> expr::exportInternal(NodeTemplate<false> n, ExprManager* from, ExprManager* to, ExprManagerMapCollection& vmap); + friend NodeTemplate<true> expr::exportInternal(NodeTemplate<false> n, ExprManager* from, ExprManager* to, ExprManagerMapCollection& vmap, uint32_t flags); friend std::ostream& CVC4::operator<<(std::ostream& out, const Expr& e); template <bool ref_count> friend class NodeTemplate; diff --git a/src/expr/metakind_template.h b/src/expr/metakind_template.h index 22d7baac3..93cebe00a 100644 --- a/src/expr/metakind_template.h +++ b/src/expr/metakind_template.h @@ -126,18 +126,6 @@ ${metakind_kinds} return metaKinds[k + 1]; }/* metaKindOf(k) */ -/** - * Map a kind of the operator to the kind of the enclosing expression. For - * example, since the kind of functions is just VARIABLE, it should map - * VARIABLE to APPLY_UF. - */ -static inline Kind operatorKindToKind(Kind k) { - switch (k) { -${metakind_operatorKinds} - default: - return kind::UNDEFINED_KIND; /* LAST_KIND */ - }; -} }/* CVC4::kind namespace */ namespace expr { @@ -282,6 +270,13 @@ inline void NodeValueConstPrinter::toStream(std::ostream& out, TNode n) { toStream(out, n.d_nv); } +// The reinterpret_cast of d_children to various constant payload types +// in deleteNodeValueConstant(), below, can flag a "strict aliasing" +// warning; it should actually be okay, because we never access the +// embedded constant as a NodeValue* child, and never access an embedded +// NodeValue* child as a constant. +#pragma GCC diagnostic ignored "-Wstrict-aliasing" + /** * Cleanup to be performed when a NodeValue zombie is collected, and * it has CONSTANT metakind. This calls the destructor for the underlying @@ -301,6 +296,9 @@ ${metakind_constDeleters} } } +// re-enable the strict-aliasing warning +# pragma GCC diagnostic warning "-Wstrict-aliasing" + inline unsigned getLowerBoundForKind(::CVC4::Kind k) { static const unsigned lbs[] = { 0, /* NULL_EXPR */ @@ -324,9 +322,30 @@ ${metakind_ubchildren} } }/* CVC4::kind::metakind namespace */ + +/** + * Map a kind of the operator to the kind of the enclosing expression. For + * example, since the kind of functions is just VARIABLE, it should map + * VARIABLE to APPLY_UF. + */ +static inline Kind operatorToKind(::CVC4::expr::NodeValue* nv) { + if(nv->getKind() == kind::BUILTIN) { + return nv->getConst<Kind>(); + } else if(nv->getKind() == kind::LAMBDA) { + return kind::APPLY_UF; + } + + switch(Kind k CVC4_UNUSED = nv->getKind()) { +${metakind_operatorKinds} + + default: + return kind::UNDEFINED_KIND; /* LAST_KIND */ + }; +} + }/* CVC4::kind namespace */ -#line 330 "${template}" +#line 349 "${template}" namespace theory { diff --git a/src/expr/mkmetakind b/src/expr/mkmetakind index d8192e432..7ffe0230b 100755 --- a/src/expr/mkmetakind +++ b/src/expr/mkmetakind @@ -303,8 +303,9 @@ struct ConstantMapReverse< ::CVC4::kind::$1 > { function registerOperatorToKind { operatorKind=$1 applyKind=$2 - metakind_operatorKinds="${metakind_operatorKinds} case kind::$applyKind: return kind::$operatorKind; -"; + metakind_operatorKinds="${metakind_operatorKinds} +#line $lineno \"$kf\" + case kind::$applyKind: return kind::$operatorKind;"; } function register_metakind { @@ -381,8 +382,8 @@ while [ $# -gt 0 ]; do /* from $b */ " metakind_operatorKinds="${metakind_operatorKinds} - /* from $b */ -" + + /* from $b */" source "$kf" if ! $seen_theory; then echo "$kf: error: no theory content found in file!" >&2 diff --git a/src/expr/node.h b/src/expr/node.h index 7003aae87..e6a163a8b 100644 --- a/src/expr/node.h +++ b/src/expr/node.h @@ -182,7 +182,7 @@ class NodeTemplate { friend class expr::NodeValue; friend class expr::pickle::PicklerPrivate; - friend Node expr::exportInternal(TNode n, ExprManager* from, ExprManager* to, ExprManagerMapCollection& vmap); + friend Node expr::exportInternal(TNode n, ExprManager* from, ExprManager* to, ExprManagerMapCollection& vmap, uint32_t flags); /** A convenient null-valued encapsulated pointer */ static NodeTemplate s_null; @@ -1094,7 +1094,7 @@ NodeTemplate<ref_count>& NodeTemplate<ref_count>:: operator=(const NodeTemplate& e) { Assert(d_nv != NULL, "Expecting a non-NULL expression value!"); Assert(e.d_nv != NULL, "Expecting a non-NULL expression value on RHS!"); - if(EXPECT_TRUE( d_nv != e.d_nv )) { + if(__builtin_expect( ( d_nv != e.d_nv ), true )) { if(ref_count) { // shouldn't ever fail Assert(d_nv->d_rc > 0, @@ -1118,7 +1118,7 @@ NodeTemplate<ref_count>& NodeTemplate<ref_count>:: operator=(const NodeTemplate<!ref_count>& e) { Assert(d_nv != NULL, "Expecting a non-NULL expression value!"); Assert(e.d_nv != NULL, "Expecting a non-NULL expression value on RHS!"); - if(EXPECT_TRUE( d_nv != e.d_nv )) { + if(__builtin_expect( ( d_nv != e.d_nv ), true )) { if(ref_count) { // shouldn't ever fail Assert(d_nv->d_rc > 0, "Node reference count would be negative"); @@ -1337,7 +1337,11 @@ NodeTemplate<ref_count>::substitute(TNode node, TNode replacement, NodeBuilder<> nb(getKind()); if(getMetaKind() == kind::metakind::PARAMETERIZED) { // push the operator - nb << getOperator(); + if(getOperator() == node) { + nb << replacement; + } else { + nb << getOperator().substitute(node, replacement, cache); + } } for(const_iterator i = begin(), iend = end(); diff --git a/src/expr/node_builder.h b/src/expr/node_builder.h index 773828ccb..64080c275 100644 --- a/src/expr/node_builder.h +++ b/src/expr/node_builder.h @@ -228,7 +228,7 @@ class NodeBuilder { * Internally, this state is represented by d_nv pointing to NULL. */ inline bool isUsed() const { - return EXPECT_FALSE( d_nv == NULL ); + return __builtin_expect( ( d_nv == NULL ), false ); } /** @@ -259,7 +259,7 @@ class NodeBuilder { * heap-allocated by this class). */ inline bool nvIsAllocated() const { - return EXPECT_FALSE( d_nv != &d_inlineNv ) && EXPECT_TRUE( d_nv != NULL ); + return __builtin_expect( ( d_nv != &d_inlineNv ), false ) && __builtin_expect(( d_nv != NULL ), true ); } /** @@ -267,7 +267,7 @@ class NodeBuilder { * first. */ inline bool nvNeedsToBeAllocated() const { - return EXPECT_FALSE( d_nv->d_nchildren == d_nvMaxChildren ); + return __builtin_expect( ( d_nv->d_nchildren == d_nvMaxChildren ), false ); } /** @@ -279,7 +279,7 @@ class NodeBuilder { inline void realloc() { size_t newSize = 2 * size_t(d_nvMaxChildren); size_t hardLimit = (1lu << __CVC4__EXPR__NODE_VALUE__NBITS__NCHILDREN) - 1; - realloc(EXPECT_FALSE( newSize > hardLimit ) ? hardLimit : newSize); + realloc(__builtin_expect( ( newSize > hardLimit ), false ) ? hardLimit : newSize); } /** @@ -297,7 +297,7 @@ class NodeBuilder { * double-decremented on destruction/clear. Otherwise, do nothing. */ inline void allocateNvIfNecessaryForAppend() { - if(EXPECT_FALSE( nvNeedsToBeAllocated() )) { + if(__builtin_expect( ( nvNeedsToBeAllocated() ), false )) { realloc(); } } @@ -331,8 +331,8 @@ class NodeBuilder { * @throws bad_alloc if the reallocation fails */ void crop() { - if(EXPECT_FALSE( nvIsAllocated() ) && - EXPECT_TRUE( d_nvMaxChildren > d_nv->d_nchildren )) { + if(__builtin_expect( ( nvIsAllocated() ), false ) && + __builtin_expect( ( d_nvMaxChildren > d_nv->d_nchildren ), true )) { // Ensure d_nv is not modified on allocation failure expr::NodeValue* newBlock = (expr::NodeValue*) std::realloc(d_nv, @@ -419,9 +419,9 @@ public: } inline ~NodeBuilder() { - if(EXPECT_FALSE( nvIsAllocated() )) { + if(__builtin_expect( ( nvIsAllocated() ), false )) { dealloc(); - } else if(EXPECT_FALSE( !isUsed() )) { + } else if(__builtin_expect( ( !isUsed() ), false )) { decrRefCounts(); } } @@ -578,7 +578,7 @@ public: // NodeBuilder construction or at the last clear()), but we do // now. That means we appended a Kind with operator<<(Kind), // which now (lazily) we'll collapse. - if(EXPECT_FALSE( d_nv->d_id == 0 && getKind() != kind::UNDEFINED_KIND )) { + if(__builtin_expect( ( d_nv->d_id == 0 && getKind() != kind::UNDEFINED_KIND ), false )) { Node n2 = operator Node(); clear(); append(n2); @@ -600,7 +600,7 @@ public: // NodeBuilder construction or at the last clear()), but we do // now. That means we appended a Kind with operator<<(Kind), // which now (lazily) we'll collapse. - if(EXPECT_FALSE( d_nv->d_id == 0 && getKind() != kind::UNDEFINED_KIND )) { + if(__builtin_expect( ( d_nv->d_id == 0 && getKind() != kind::UNDEFINED_KIND ), false )) { Node n2 = operator Node(); clear(); append(n2); @@ -619,7 +619,7 @@ public: // NodeBuilder construction or at the last clear()), but we do // now. That means we appended a Kind with operator<<(Kind), // which now (lazily) we'll collapse. - if(EXPECT_FALSE( d_nv->d_id == 0 && getKind() != kind::UNDEFINED_KIND )) { + if(__builtin_expect( ( d_nv->d_id == 0 && getKind() != kind::UNDEFINED_KIND ), false )) { Node n2 = operator Node(); clear(); append(n2); @@ -660,6 +660,9 @@ public: Assert(!isUsed(), "NodeBuilder is one-shot only; " "attempt to access it after conversion"); Assert(!n.isNull(), "Cannot use NULL Node as a child of a Node"); + if(n.getKind() == kind::BUILTIN) { + return *this << NodeManager::operatorToKind(n); + } allocateNvIfNecessaryForAppend(); expr::NodeValue* nv = n.d_nv; nv->inc(); @@ -756,9 +759,9 @@ template <unsigned nchild_thresh> void NodeBuilder<nchild_thresh>::clear(Kind k) { Assert(k != kind::NULL_EXPR, "illegal Node-building clear kind"); - if(EXPECT_FALSE( nvIsAllocated() )) { + if(__builtin_expect( ( nvIsAllocated() ), false )) { dealloc(); - } else if(EXPECT_FALSE( !isUsed() )) { + } else if(__builtin_expect( ( !isUsed() ), false )) { decrRefCounts(); } else { setUnused(); @@ -783,7 +786,7 @@ void NodeBuilder<nchild_thresh>::realloc(size_t toSize) { "attempt to realloc() a NodeBuilder to size %u (beyond hard limit of %u)", toSize, (1lu << __CVC4__EXPR__NODE_VALUE__NBITS__NCHILDREN) - 1 ); - if(EXPECT_FALSE( nvIsAllocated() )) { + if(__builtin_expect( ( nvIsAllocated() ), false )) { // Ensure d_nv is not modified on allocation failure expr::NodeValue* newBlock = (expr::NodeValue*) std::realloc(d_nv, sizeof(expr::NodeValue) + @@ -980,7 +983,7 @@ expr::NodeValue* NodeBuilder<nchild_thresh>::constructNV() { #if 0 // if the kind is PARAMETERIZED, check that the operator is correctly-kinded Assert(kind::metaKindOf(getKind()) != kind::metakind::PARAMETERIZED || - kind::operatorKindToKind(getOperator().getKind()) == getKind(), + NodeManager::operatorToKind(getOperator()) == getKind(), "Attempted to construct a parameterized kind `%s' with " "incorrectly-kinded operator `%s'", kind::kindToString(getKind()).c_str(), @@ -992,7 +995,7 @@ expr::NodeValue* NodeBuilder<nchild_thresh>::constructNV() { // NodeManager pool of Nodes. See implementation notes at the top // of this file. - if(EXPECT_TRUE( ! nvIsAllocated() )) { + if(__builtin_expect( ( ! nvIsAllocated() ), true )) { /** Case 1. d_nv points to d_inlineNv: it is the backing store ** allocated "inline" in this NodeBuilder. **/ @@ -1165,7 +1168,7 @@ expr::NodeValue* NodeBuilder<nchild_thresh>::constructNV() const { #if 0 // if the kind is PARAMETERIZED, check that the operator is correctly-kinded Assert(kind::metaKindOf(getKind()) != kind::metakind::PARAMETERIZED || - kind::operatorKindToKind(getOperator().getKind()) == getKind(), + NodeManager::operatorToKind(getOperator()) == getKind(), "Attempted to construct a parameterized kind `%s' with " "incorrectly-kinded operator `%s'", kind::kindToString(getKind()).c_str(), @@ -1177,7 +1180,7 @@ expr::NodeValue* NodeBuilder<nchild_thresh>::constructNV() const { // NodeManager pool of Nodes. See implementation notes at the top // of this file. - if(EXPECT_TRUE( ! nvIsAllocated() )) { + if(__builtin_expect( ( ! nvIsAllocated() ), true )) { /** Case 1. d_nv points to d_inlineNv: it is the backing store ** allocated "inline" in this NodeBuilder. **/ diff --git a/src/expr/node_manager.h b/src/expr/node_manager.h index 0c62d31c5..31f6d75d9 100644 --- a/src/expr/node_manager.h +++ b/src/expr/node_manager.h @@ -75,12 +75,12 @@ typedef expr::Attribute<expr::attr::DatatypeRecordTag, TypeNode> DatatypeRecordA class NodeManagerListener { public: virtual ~NodeManagerListener() { } - virtual void nmNotifyNewSort(TypeNode tn) { } + virtual void nmNotifyNewSort(TypeNode tn, uint32_t flags) { } virtual void nmNotifyNewSortConstructor(TypeNode tn) { } - virtual void nmNotifyInstantiateSortConstructor(TypeNode ctor, TypeNode sort) { } + virtual void nmNotifyInstantiateSortConstructor(TypeNode ctor, TypeNode sort, uint32_t flags) { } virtual void nmNotifyNewDatatypes(const std::vector<DatatypeType>& datatypes) { } - virtual void nmNotifyNewVar(TNode n, bool isGlobal) { } - virtual void nmNotifyNewSkolem(TNode n, const std::string& comment, bool isGlobal) { } + virtual void nmNotifyNewVar(TNode n, uint32_t flags) { } + virtual void nmNotifyNewSkolem(TNode n, const std::string& comment, uint32_t flags) { } };/* class NodeManagerListener */ class NodeManager { @@ -90,8 +90,8 @@ class NodeManager { friend class expr::TypeChecker; // friends so they can access mkVar() here, which is private - friend Expr ExprManager::mkVar(const std::string&, Type, bool isGlobal); - friend Expr ExprManager::mkVar(Type, bool isGlobal); + friend Expr ExprManager::mkVar(const std::string&, Type, uint32_t flags); + friend Expr ExprManager::mkVar(Type, uint32_t flags); // friend so it can access NodeManager's d_listeners and notify clients friend std::vector<DatatypeType> ExprManager::mkMutualDatatypeTypes(const std::vector<Datatype>&, const std::set<Type>&); @@ -309,12 +309,12 @@ class NodeManager { * version of this is private to avoid internal uses of mkVar() from * within CVC4. Such uses should employ mkSkolem() instead. */ - Node mkVar(const std::string& name, const TypeNode& type, bool isGlobal = false); - Node* mkVarPtr(const std::string& name, const TypeNode& type, bool isGlobal = false); + Node mkVar(const std::string& name, const TypeNode& type, uint32_t flags = ExprManager::VAR_FLAG_NONE); + Node* mkVarPtr(const std::string& name, const TypeNode& type, uint32_t flags = ExprManager::VAR_FLAG_NONE); /** Create a variable with the given type. */ - Node mkVar(const TypeNode& type, bool isGlobal = false); - Node* mkVarPtr(const TypeNode& type, bool isGlobal = false); + Node mkVar(const TypeNode& type, uint32_t flags = ExprManager::VAR_FLAG_NONE); + Node* mkVarPtr(const TypeNode& type, uint32_t flags = ExprManager::VAR_FLAG_NONE); public: @@ -353,6 +353,9 @@ public: d_listeners.erase(elt); } + /** Get a Kind from an operator expression */ + static inline Kind operatorToKind(TNode n); + // general expression-builders /** Create a node with one child. */ @@ -680,6 +683,9 @@ public: /** Get the (singleton) type for strings. */ inline TypeNode stringType(); + /** Get the (singleton) type for RegExp. */ + inline TypeNode regexpType(); + /** Get the bound var list type. */ inline TypeNode boundVarListType(); @@ -775,14 +781,15 @@ public: inline TypeNode mkTesterType(TypeNode domain); /** Make a new (anonymous) sort of arity 0. */ - inline TypeNode mkSort(); + inline TypeNode mkSort(uint32_t flags = ExprManager::SORT_FLAG_NONE); /** Make a new sort with the given name of arity 0. */ - inline TypeNode mkSort(const std::string& name); + inline TypeNode mkSort(const std::string& name, uint32_t flags = ExprManager::SORT_FLAG_NONE); /** Make a new sort by parameterizing the given sort constructor. */ inline TypeNode mkSort(TypeNode constructor, - const std::vector<TypeNode>& children); + const std::vector<TypeNode>& children, + uint32_t flags = ExprManager::SORT_FLAG_NONE); /** Make a new sort with the given name and arity. */ inline TypeNode mkSortConstructor(const std::string& name, size_t arity); @@ -1026,6 +1033,11 @@ inline TypeNode NodeManager::stringType() { return TypeNode(mkTypeConst<TypeConstant>(STRING_TYPE)); } +/** Get the (singleton) type for regexps. */ +inline TypeNode NodeManager::regexpType() { + return TypeNode(mkTypeConst<TypeConstant>(REGEXP_TYPE)); +} + /** Get the bound var list type. */ inline TypeNode NodeManager::boundVarListType() { return TypeNode(mkTypeConst<TypeConstant>(BOUND_VAR_LIST_TYPE)); @@ -1087,7 +1099,6 @@ NodeManager::mkPredicateType(const std::vector<TypeNode>& sorts) { } inline TypeNode NodeManager::mkTupleType(const std::vector<TypeNode>& types) { - Assert(types.size() >= 1); std::vector<TypeNode> typeNodes; for (unsigned i = 0; i < types.size(); ++ i) { CheckArgument(!types[i].isFunctionLike(), types, @@ -1218,31 +1229,32 @@ inline bool NodeManager::hasOperator(Kind k) { } } -inline TypeNode NodeManager::mkSort() { +inline TypeNode NodeManager::mkSort(uint32_t flags) { NodeBuilder<1> nb(this, kind::SORT_TYPE); Node sortTag = NodeBuilder<0>(this, kind::SORT_TAG); nb << sortTag; TypeNode tn = nb.constructTypeNode(); for(std::vector<NodeManagerListener*>::iterator i = d_listeners.begin(); i != d_listeners.end(); ++i) { - (*i)->nmNotifyNewSort(tn); + (*i)->nmNotifyNewSort(tn, flags); } return tn; } -inline TypeNode NodeManager::mkSort(const std::string& name) { +inline TypeNode NodeManager::mkSort(const std::string& name, uint32_t flags) { NodeBuilder<1> nb(this, kind::SORT_TYPE); Node sortTag = NodeBuilder<0>(this, kind::SORT_TAG); nb << sortTag; TypeNode tn = nb.constructTypeNode(); setAttribute(tn, expr::VarNameAttr(), name); for(std::vector<NodeManagerListener*>::iterator i = d_listeners.begin(); i != d_listeners.end(); ++i) { - (*i)->nmNotifyNewSort(tn); + (*i)->nmNotifyNewSort(tn, flags); } return tn; } inline TypeNode NodeManager::mkSort(TypeNode constructor, - const std::vector<TypeNode>& children) { + const std::vector<TypeNode>& children, + uint32_t flags) { Assert(constructor.getKind() == kind::SORT_TYPE && constructor.getNumChildren() == 0, "expected a sort constructor"); @@ -1260,7 +1272,7 @@ inline TypeNode NodeManager::mkSort(TypeNode constructor, TypeNode type = nb.constructTypeNode(); setAttribute(type, expr::VarNameAttr(), name); for(std::vector<NodeManagerListener*>::iterator i = d_listeners.begin(); i != d_listeners.end(); ++i) { - (*i)->nmNotifyInstantiateSortConstructor(constructor, type); + (*i)->nmNotifyInstantiateSortConstructor(constructor, type, flags); } return type; } @@ -1280,6 +1292,10 @@ inline TypeNode NodeManager::mkSortConstructor(const std::string& name, return type; } +inline Kind NodeManager::operatorToKind(TNode n) { + return kind::operatorToKind(n.d_nv); +} + inline Node NodeManager::mkNode(Kind kind, TNode child1) { NodeBuilder<1> nb(this, kind); nb << child1; @@ -1367,80 +1383,114 @@ inline Node* NodeManager::mkNodePtr(Kind kind, // for operators inline Node NodeManager::mkNode(TNode opNode) { - NodeBuilder<1> nb(this, kind::operatorKindToKind(opNode.getKind())); - nb << opNode; + NodeBuilder<1> nb(this, operatorToKind(opNode)); + if(opNode.getKind() != kind::BUILTIN) { + nb << opNode; + } return nb.constructNode(); } inline Node* NodeManager::mkNodePtr(TNode opNode) { - NodeBuilder<1> nb(this, kind::operatorKindToKind(opNode.getKind())); - nb << opNode; + NodeBuilder<1> nb(this, operatorToKind(opNode)); + if(opNode.getKind() != kind::BUILTIN) { + nb << opNode; + } return nb.constructNodePtr(); } inline Node NodeManager::mkNode(TNode opNode, TNode child1) { - NodeBuilder<2> nb(this, kind::operatorKindToKind(opNode.getKind())); - nb << opNode << child1; + NodeBuilder<2> nb(this, operatorToKind(opNode)); + if(opNode.getKind() != kind::BUILTIN) { + nb << opNode; + } + nb << child1; return nb.constructNode(); } inline Node* NodeManager::mkNodePtr(TNode opNode, TNode child1) { - NodeBuilder<2> nb(this, kind::operatorKindToKind(opNode.getKind())); - nb << opNode << child1; + NodeBuilder<2> nb(this, operatorToKind(opNode)); + if(opNode.getKind() != kind::BUILTIN) { + nb << opNode; + } + nb << child1; return nb.constructNodePtr(); } inline Node NodeManager::mkNode(TNode opNode, TNode child1, TNode child2) { - NodeBuilder<3> nb(this, kind::operatorKindToKind(opNode.getKind())); - nb << opNode << child1 << child2; + NodeBuilder<3> nb(this, operatorToKind(opNode)); + if(opNode.getKind() != kind::BUILTIN) { + nb << opNode; + } + nb << child1 << child2; return nb.constructNode(); } inline Node* NodeManager::mkNodePtr(TNode opNode, TNode child1, TNode child2) { - NodeBuilder<3> nb(this, kind::operatorKindToKind(opNode.getKind())); - nb << opNode << child1 << child2; + NodeBuilder<3> nb(this, operatorToKind(opNode)); + if(opNode.getKind() != kind::BUILTIN) { + nb << opNode; + } + nb << child1 << child2; return nb.constructNodePtr(); } inline Node NodeManager::mkNode(TNode opNode, TNode child1, TNode child2, TNode child3) { - NodeBuilder<4> nb(this, kind::operatorKindToKind(opNode.getKind())); - nb << opNode << child1 << child2 << child3; + NodeBuilder<4> nb(this, operatorToKind(opNode)); + if(opNode.getKind() != kind::BUILTIN) { + nb << opNode; + } + nb << child1 << child2 << child3; return nb.constructNode(); } inline Node* NodeManager::mkNodePtr(TNode opNode, TNode child1, TNode child2, TNode child3) { - NodeBuilder<4> nb(this, kind::operatorKindToKind(opNode.getKind())); - nb << opNode << child1 << child2 << child3; + NodeBuilder<4> nb(this, operatorToKind(opNode)); + if(opNode.getKind() != kind::BUILTIN) { + nb << opNode; + } + nb << child1 << child2 << child3; return nb.constructNodePtr(); } inline Node NodeManager::mkNode(TNode opNode, TNode child1, TNode child2, TNode child3, TNode child4) { - NodeBuilder<5> nb(this, kind::operatorKindToKind(opNode.getKind())); - nb << opNode << child1 << child2 << child3 << child4; + NodeBuilder<5> nb(this, operatorToKind(opNode)); + if(opNode.getKind() != kind::BUILTIN) { + nb << opNode; + } + nb << child1 << child2 << child3 << child4; return nb.constructNode(); } inline Node* NodeManager::mkNodePtr(TNode opNode, TNode child1, TNode child2, TNode child3, TNode child4) { - NodeBuilder<5> nb(this, kind::operatorKindToKind(opNode.getKind())); - nb << opNode << child1 << child2 << child3 << child4; + NodeBuilder<5> nb(this, operatorToKind(opNode)); + if(opNode.getKind() != kind::BUILTIN) { + nb << opNode; + } + nb << child1 << child2 << child3 << child4; return nb.constructNodePtr(); } inline Node NodeManager::mkNode(TNode opNode, TNode child1, TNode child2, TNode child3, TNode child4, TNode child5) { - NodeBuilder<6> nb(this, kind::operatorKindToKind(opNode.getKind())); - nb << opNode << child1 << child2 << child3 << child4 << child5; + NodeBuilder<6> nb(this, operatorToKind(opNode)); + if(opNode.getKind() != kind::BUILTIN) { + nb << opNode; + } + nb << child1 << child2 << child3 << child4 << child5; return nb.constructNode(); } inline Node* NodeManager::mkNodePtr(TNode opNode, TNode child1, TNode child2, TNode child3, TNode child4, TNode child5) { - NodeBuilder<6> nb(this, kind::operatorKindToKind(opNode.getKind())); - nb << opNode << child1 << child2 << child3 << child4 << child5; + NodeBuilder<6> nb(this, operatorToKind(opNode)); + if(opNode.getKind() != kind::BUILTIN) { + nb << opNode; + } + nb << child1 << child2 << child3 << child4 << child5; return nb.constructNodePtr(); } @@ -1449,8 +1499,10 @@ template <bool ref_count> inline Node NodeManager::mkNode(TNode opNode, const std::vector<NodeTemplate<ref_count> >& children) { - NodeBuilder<> nb(this, kind::operatorKindToKind(opNode.getKind())); - nb << opNode; + NodeBuilder<> nb(this, operatorToKind(opNode)); + if(opNode.getKind() != kind::BUILTIN) { + nb << opNode; + } nb.append(children); return nb.constructNode(); } @@ -1459,8 +1511,10 @@ template <bool ref_count> inline Node* NodeManager::mkNodePtr(TNode opNode, const std::vector<NodeTemplate<ref_count> >& children) { - NodeBuilder<> nb(this, kind::operatorKindToKind(opNode.getKind())); - nb << opNode; + NodeBuilder<> nb(this, operatorToKind(opNode)); + if(opNode.getKind() != kind::BUILTIN) { + nb << opNode; + } nb.append(children); return nb.constructNodePtr(); } @@ -1487,27 +1541,27 @@ inline TypeNode NodeManager::mkTypeNode(Kind kind, } -inline Node NodeManager::mkVar(const std::string& name, const TypeNode& type, bool isGlobal) { +inline Node NodeManager::mkVar(const std::string& name, const TypeNode& type, uint32_t flags) { Node n = NodeBuilder<0>(this, kind::VARIABLE); setAttribute(n, TypeAttr(), type); setAttribute(n, TypeCheckedAttr(), true); setAttribute(n, expr::VarNameAttr(), name); - setAttribute(n, expr::GlobalVarAttr(), isGlobal); + setAttribute(n, expr::GlobalVarAttr(), flags & ExprManager::VAR_FLAG_GLOBAL); for(std::vector<NodeManagerListener*>::iterator i = d_listeners.begin(); i != d_listeners.end(); ++i) { - (*i)->nmNotifyNewVar(n, isGlobal); + (*i)->nmNotifyNewVar(n, flags); } return n; } inline Node* NodeManager::mkVarPtr(const std::string& name, - const TypeNode& type, bool isGlobal) { + const TypeNode& type, uint32_t flags) { Node* n = NodeBuilder<0>(this, kind::VARIABLE).constructNodePtr(); setAttribute(*n, TypeAttr(), type); setAttribute(*n, TypeCheckedAttr(), true); setAttribute(*n, expr::VarNameAttr(), name); - setAttribute(*n, expr::GlobalVarAttr(), isGlobal); + setAttribute(*n, expr::GlobalVarAttr(), flags & ExprManager::VAR_FLAG_GLOBAL); for(std::vector<NodeManagerListener*>::iterator i = d_listeners.begin(); i != d_listeners.end(); ++i) { - (*i)->nmNotifyNewVar(*n, isGlobal); + (*i)->nmNotifyNewVar(*n, flags); } return n; } @@ -1525,24 +1579,24 @@ inline Node* NodeManager::mkBoundVarPtr(const std::string& name, return n; } -inline Node NodeManager::mkVar(const TypeNode& type, bool isGlobal) { +inline Node NodeManager::mkVar(const TypeNode& type, uint32_t flags) { Node n = NodeBuilder<0>(this, kind::VARIABLE); setAttribute(n, TypeAttr(), type); setAttribute(n, TypeCheckedAttr(), true); - setAttribute(n, expr::GlobalVarAttr(), isGlobal); + setAttribute(n, expr::GlobalVarAttr(), flags & ExprManager::VAR_FLAG_GLOBAL); for(std::vector<NodeManagerListener*>::iterator i = d_listeners.begin(); i != d_listeners.end(); ++i) { - (*i)->nmNotifyNewVar(n, isGlobal); + (*i)->nmNotifyNewVar(n, flags); } return n; } -inline Node* NodeManager::mkVarPtr(const TypeNode& type, bool isGlobal) { +inline Node* NodeManager::mkVarPtr(const TypeNode& type, uint32_t flags) { Node* n = NodeBuilder<0>(this, kind::VARIABLE).constructNodePtr(); setAttribute(*n, TypeAttr(), type); setAttribute(*n, TypeCheckedAttr(), true); - setAttribute(*n, expr::GlobalVarAttr(), isGlobal); + setAttribute(*n, expr::GlobalVarAttr(), flags & ExprManager::VAR_FLAG_GLOBAL); for(std::vector<NodeManagerListener*>::iterator i = d_listeners.begin(); i != d_listeners.end(); ++i) { - (*i)->nmNotifyNewVar(*n, isGlobal); + (*i)->nmNotifyNewVar(*n, flags); } return n; } diff --git a/src/expr/node_value.h b/src/expr/node_value.h index 13fdc6a7f..56ac70c1e 100644 --- a/src/expr/node_value.h +++ b/src/expr/node_value.h @@ -299,7 +299,7 @@ private: RefCountGuard(const NodeValue* nv) : d_nv(const_cast<NodeValue*>(nv)) { // inc() - if(EXPECT_TRUE( d_nv->d_rc < MAX_RC )) { + if(__builtin_expect( ( d_nv->d_rc < MAX_RC ), true )) { ++d_nv->d_rc; } } @@ -309,7 +309,7 @@ private: // E.g., this can happen when debugging code calls the print // routines below. As RefCountGuards are scoped on the stack, // this should be fine---but not in multithreaded contexts! - if(EXPECT_TRUE( d_nv->d_rc < MAX_RC )) { + if(__builtin_expect( ( d_nv->d_rc < MAX_RC ), true )) { --d_nv->d_rc; } } @@ -400,16 +400,16 @@ inline void NodeValue::inc() { "NodeValue is currently being deleted " "and increment is being called on it. Don't Do That!"); // FIXME multithreading - if(EXPECT_TRUE( d_rc < MAX_RC )) { + if(__builtin_expect( ( d_rc < MAX_RC ), true )) { ++d_rc; } } inline void NodeValue::dec() { // FIXME multithreading - if(EXPECT_TRUE( d_rc < MAX_RC )) { + if(__builtin_expect( ( d_rc < MAX_RC ), true )) { --d_rc; - if(EXPECT_FALSE( d_rc == 0 )) { + if(__builtin_expect( ( d_rc == 0 ), false )) { Assert(NodeManager::currentNM() != NULL, "No current NodeManager on destruction of NodeValue: " "maybe a public CVC4 interface function is missing a NodeManagerScope ?"); diff --git a/src/expr/type.cpp b/src/expr/type.cpp index 71c25bd50..f3cf992ba 100644 --- a/src/expr/type.cpp +++ b/src/expr/type.cpp @@ -317,6 +317,10 @@ bool Type::isSubrange() const { return d_typeNode->isSubrange(); } +size_t FunctionType::getArity() const { + return d_typeNode->getNumChildren() - 1; +} + vector<Type> FunctionType::getArgTypes() const { NodeManagerScope nms(d_nodeManager); vector<Type> args; diff --git a/src/expr/type.h b/src/expr/type.h index 5e4e86264..3c772d461 100644 --- a/src/expr/type.h +++ b/src/expr/type.h @@ -415,6 +415,9 @@ public: /** Construct from the base type */ FunctionType(const Type& type = Type()) throw(IllegalArgumentException); + /** Get the arity of the function type */ + size_t getArity() const; + /** Get the argument types */ std::vector<Type> getArgTypes() const; diff --git a/src/expr/type.i b/src/expr/type.i index e227cca23..6394dda67 100644 --- a/src/expr/type.i +++ b/src/expr/type.i @@ -2,7 +2,11 @@ #include "expr/type.h" %} +#ifdef SWIGPYTHON +%rename(doApply) CVC4::TypeHashFunction::operator()(const CVC4::Type&) const; +#else /* SWIGPYTHON */ %rename(apply) CVC4::TypeHashFunction::operator()(const CVC4::Type&) const; +#endif /* SWIGPYTHON */ %ignore CVC4::operator<<(std::ostream&, const Type&); diff --git a/src/expr/type_node.cpp b/src/expr/type_node.cpp index e654b5d71..2fc380224 100644 --- a/src/expr/type_node.cpp +++ b/src/expr/type_node.cpp @@ -123,6 +123,15 @@ bool TypeNode::isSubtypeOf(TypeNode t) const { } return true; } + if(isFunction()) { + // A function is a subtype of another if the args are the same type, and + // the return type is a subtype of the other's. This is enough for now + // (and it's necessary for model generation, since a Real-valued function + // might return a constant Int and thus the model value is typed differently). + return t.isFunction() && + getArgTypes() == t.getArgTypes() && + getRangeType().isSubtypeOf(t.getRangeType()); + } if(isPredicateSubtype()) { return getSubtypeParentType().isSubtypeOf(t); } @@ -291,7 +300,7 @@ TypeNode TypeNode::leastCommonTypeNode(TypeNode t0, TypeNode t1){ Assert(!t0.isNull()); Assert(!t1.isNull()); - if(EXPECT_TRUE(t0 == t1)) { + if(__builtin_expect( (t0 == t1), true )) { return t0; } else { // t0 != t1 if(t0.getKind()== kind::TYPE_CONSTANT) { diff --git a/src/expr/type_node.h b/src/expr/type_node.h index 35d630a91..145ca2aba 100644 --- a/src/expr/type_node.h +++ b/src/expr/type_node.h @@ -554,6 +554,9 @@ public: /** Get the constituent types of a symbolic expression type */ std::vector<TypeNode> getSExprTypes() const; + /** Is this a regexp type */ + bool isRegExp() const; + /** Is this a bit-vector type */ bool isBitVector() const; @@ -619,7 +622,7 @@ public: * If this is \top, i.e. there is no inhabited type that contains both, * a TypeNode such that isNull() is true is returned. * - * For more information see: http://church.cims.nyu.edu/wiki/Cvc4_Type_Lattice + * For more information see: http://cvc4.cs.nyu.edu/wiki/Cvc4_Type_Lattice */ static TypeNode leastCommonTypeNode(TypeNode t0, TypeNode t1); @@ -770,7 +773,7 @@ inline TypeNode& TypeNode::operator=(const TypeNode& typeNode) { Assert(d_nv != NULL, "Expecting a non-NULL expression value!"); Assert(typeNode.d_nv != NULL, "Expecting a non-NULL expression value on RHS!"); - if(EXPECT_TRUE( d_nv != typeNode.d_nv )) { + if(__builtin_expect( ( d_nv != typeNode.d_nv ), true )) { d_nv->dec(); d_nv = typeNode.d_nv; d_nv->inc(); @@ -842,6 +845,12 @@ inline bool TypeNode::isString() const { getConst<TypeConstant>() == STRING_TYPE; } +/** Is this a regexp type */ +inline bool TypeNode::isRegExp() const { + return getKind() == kind::TYPE_CONSTANT && + getConst<TypeConstant>() == REGEXP_TYPE; +} + inline bool TypeNode::isArray() const { return getKind() == kind::ARRAY_TYPE; } diff --git a/src/include/cvc4_private_library.h b/src/include/cvc4_private_library.h index b04160a81..f7fd1b607 100644 --- a/src/include/cvc4_private_library.h +++ b/src/include/cvc4_private_library.h @@ -19,7 +19,7 @@ #ifndef __CVC4_PRIVATE_LIBRARY_H #define __CVC4_PRIVATE_LIBRARY_H -#if ! (defined(__BUILDING_CVC4LIB) || defined(__BUILDING_CVC4LIB_UNIT_TEST) || defined(__BUILDING_CVC4PARSERLIB) || defined(__BUILDING_CVC4PARSERLIB_UNIT_TEST) || defined(__BUILDING_CVC4DRIVER)) +#if ! (defined(__BUILDING_CVC4LIB) || defined(__BUILDING_CVC4LIB_UNIT_TEST) || defined(__BUILDING_CVC4PARSERLIB) || defined(__BUILDING_CVC4PARSERLIB_UNIT_TEST) || defined(__BUILDING_CVC4COMPATLIB) || defined(__BUILDING_CVC4DRIVER)) # warning A "private library" CVC4 header was included when not building the library, driver, or private unit test code. #endif /* ! (__BUILDING_CVC4LIB || __BUILDING_CVC4LIB_UNIT_TEST || __BUILDING_CVC4PARSERLIB || __BUILDING_CVC4PARSERLIB_UNIT_TEST || __BUILDING_CVC4DRIVER) */ diff --git a/src/include/cvc4_public.h b/src/include/cvc4_public.h index b8c30aeef..b7431ce1c 100644 --- a/src/include/cvc4_public.h +++ b/src/include/cvc4_public.h @@ -68,7 +68,4 @@ # define CVC4_WARN_UNUSED_RESULT #endif /* __GNUC__ */ -#define EXPECT_TRUE(x) __builtin_expect( (x), true ) -#define EXPECT_FALSE(x) __builtin_expect( (x), false ) - #endif /* __CVC4_PUBLIC_H */ diff --git a/src/lib/clock_gettime.h b/src/lib/clock_gettime.h index 2d3455aed..43c3395a4 100644 --- a/src/lib/clock_gettime.h +++ b/src/lib/clock_gettime.h @@ -30,7 +30,7 @@ /* otherwise, we have to define it */ -#ifdef __WIN32__ +#if defined(__WIN32__) && !defined(__WIN64__) #ifdef __cplusplus extern "C" { @@ -45,12 +45,12 @@ struct timespec { }/* extern "C" */ #endif /* __cplusplus */ -#else /* ! __WIN32__ */ +#else /* !__WIN32__ || __WIN64__ */ /* get timespec from <time.h> */ #include <time.h> -#endif /* __WIN32__ */ +#endif /* __WIN32__ && !__WIN64__ */ #ifdef __cplusplus extern "C" { diff --git a/src/main/command_executor.cpp b/src/main/command_executor.cpp index f1742b549..9ee896107 100644 --- a/src/main/command_executor.cpp +++ b/src/main/command_executor.cpp @@ -19,14 +19,17 @@ #include "main/main.h" +#include "smt/options.h" + namespace CVC4 { namespace main { -CommandExecutor::CommandExecutor(ExprManager &exprMgr, Options &options): +CommandExecutor::CommandExecutor(ExprManager &exprMgr, Options &options) : d_exprMgr(exprMgr), d_smtEngine(SmtEngine(&exprMgr)), d_options(options), - d_stats("driver") { + d_stats("driver"), + d_result() { } bool CommandExecutor::doCommand(Command* cmd) @@ -56,7 +59,7 @@ bool CommandExecutor::doCommand(Command* cmd) } } -bool CommandExecutor::doCommandSingleton(Command *cmd) +bool CommandExecutor::doCommandSingleton(Command* cmd) { bool status = true; if(d_options[options::verbosity] >= -1) { @@ -64,14 +67,27 @@ bool CommandExecutor::doCommandSingleton(Command *cmd) } else { status = smtEngineInvoke(&d_smtEngine, cmd, NULL); } + Result res; + CheckSatCommand* cs = dynamic_cast<CheckSatCommand*>(cmd); + if(cs != NULL) { + d_result = res = cs->getResult(); + } + QueryCommand* q = dynamic_cast<QueryCommand*>(cmd); + if(q != NULL) { + d_result = res = q->getResult(); + } + // dump the model if option is set + if( status && + d_options[options::produceModels] && + d_options[options::dumpModels] && + ( res.asSatisfiabilityResult() == Result::SAT || + (res.isUnknown() && res.whyUnknown() == Result::INCOMPLETE) ) ) { + Command* gm = new GetModelCommand(); + status = doCommandSingleton(gm); + } return status; } -std::string CommandExecutor::getSmtEngineStatus() -{ - return d_smtEngine.getInfo("status").getValue(); -} - bool smtEngineInvoke(SmtEngine* smt, Command* cmd, std::ostream *out) { if(out == NULL) { diff --git a/src/main/command_executor.h b/src/main/command_executor.h index f1b8d8f2f..cbc71b075 100644 --- a/src/main/command_executor.h +++ b/src/main/command_executor.h @@ -34,6 +34,7 @@ protected: SmtEngine d_smtEngine; Options& d_options; StatisticsRegistry d_stats; + Result d_result; public: CommandExecutor(ExprManager &exprMgr, Options &options); @@ -47,7 +48,7 @@ public: */ bool doCommand(CVC4::Command* cmd); - virtual std::string getSmtEngineStatus(); + Result getResult() const { return d_result; } StatisticsRegistry& getStatisticsRegistry() { return d_stats; diff --git a/src/main/command_executor_portfolio.cpp b/src/main/command_executor_portfolio.cpp index 63f689d48..e58df5699 100644 --- a/src/main/command_executor_portfolio.cpp +++ b/src/main/command_executor_portfolio.cpp @@ -184,7 +184,7 @@ bool CommandExecutorPortfolio::doCommandSingleton(Command* cmd) // command if(dynamic_cast<CheckSatCommand*>(cmd) != NULL || - dynamic_cast<QueryCommand*>(cmd) != NULL) { + dynamic_cast<QueryCommand*>(cmd) != NULL) { mode = 1; } else if(dynamic_cast<GetValueCommand*>(cmd) != NULL || dynamic_cast<GetAssignmentCommand*>(cmd) != NULL || @@ -198,6 +198,10 @@ bool CommandExecutorPortfolio::doCommandSingleton(Command* cmd) mode = 2; } + Debug("portfolio::outputmode") << "Mode is " << mode + << "lastWinner is " << d_lastWinner + << "d_seq is " << d_seq << std::endl; + if(mode == 0) { d_seq->addCommand(cmd->clone()); Command* cmdExported = @@ -214,7 +218,7 @@ bool CommandExecutorPortfolio::doCommandSingleton(Command* cmd) // We currently don't support changing number of threads for each // command, but things have been architected in a way so that this - // can be acheived with not a lot of work + // can be achieved without a lot of work. Command *seqs[d_numThreads]; if(d_lastWinner == 0) @@ -238,7 +242,7 @@ bool CommandExecutorPortfolio::doCommandSingleton(Command* cmd) int(i) == d_lastWinner ? cmd->exportTo(d_exprMgrs[i], *(d_vmaps[i])) : d_seq->exportTo(d_exprMgrs[i], *(d_vmaps[i]) ); - }catch(ExportUnsupportedException& e){ + } catch(ExportUnsupportedException& e) { if(d_options[options::fallbackSequential]) { Notice() << "Unsupported theory encountered, switching to sequential mode."; return CommandExecutor::doCommandSingleton(cmd); @@ -292,11 +296,11 @@ bool CommandExecutorPortfolio::doCommandSingleton(Command* cmd) runPortfolio(d_numThreads, smFn, fns, d_options[options::waitToJoin]); - d_seq = NULL; delete d_seq; d_seq = new CommandSequence(); d_lastWinner = portfolioReturn.first; + d_result = d_smts[d_lastWinner]->getStatusOfLastCommand(); if(d_ostringstreams.size() != 0) { assert(d_numThreads == d_options[options::threads]); @@ -340,11 +344,6 @@ bool CommandExecutorPortfolio::doCommandSingleton(Command* cmd) }/* CommandExecutorPortfolio::doCommandSingleton() */ -std::string CommandExecutorPortfolio::getSmtEngineStatus() -{ - return d_smts[d_lastWinner]->getInfo("status").getValue(); -} - void CommandExecutorPortfolio::flushStatistics(std::ostream& out) const { assert(d_numThreads == d_exprMgrs.size() && d_exprMgrs.size() == d_smts.size()); for(size_t i = 0; i < d_numThreads; ++i) { diff --git a/src/main/driver_unified.cpp b/src/main/driver_unified.cpp index f57d4f2d7..d42f389c1 100644 --- a/src/main/driver_unified.cpp +++ b/src/main/driver_unified.cpp @@ -123,7 +123,7 @@ int runCvc4(int argc, char* argv[], Options& opts) { exit(0); } - segvNoSpin = opts[options::segvNoSpin]; + segvSpin = opts[options::segvSpin]; // If in competition mode, set output stream option to flush immediately #ifdef CVC4_COMPETITION_MODE @@ -297,13 +297,13 @@ int runCvc4(int argc, char* argv[], Options& opts) { opts.set(options::replayStream, NULL); } - string result = "unknown"; + Result result; if(status) { - result = pExecutor->getSmtEngineStatus(); + result = pExecutor->getResult(); - if(result == "sat") { + if(result.asSatisfiabilityResult() == Result::SAT) { returnValue = 10; - } else if(result == "unsat") { + } else if(result.asSatisfiabilityResult() == Result::UNSAT) { returnValue = 20; } else { returnValue = 0; diff --git a/src/main/interactive_shell.cpp b/src/main/interactive_shell.cpp index 3376e9d0b..a8099ca30 100644 --- a/src/main/interactive_shell.cpp +++ b/src/main/interactive_shell.cpp @@ -33,6 +33,7 @@ #include "parser/parser_builder.h" #include "options/options.h" #include "util/language.h" +#include "util/output.h" #include <string.h> #include <cassert> @@ -313,7 +314,11 @@ restart: line += "\n"; goto restart; } catch(ParserException& pe) { - d_out << pe << endl; + if(d_options[options::outputLanguage] == output::LANG_SMTLIB_V2) { + d_out << "(error \"" << pe << "\")" << endl; + } else { + d_out << pe << endl; + } // We can't really clear out the sequence and abort the current line, // because the parse error might be for the second command on the // line. The first ones haven't yet been executed by the SmtEngine, diff --git a/src/main/main.cpp b/src/main/main.cpp index 7b61b48aa..a4c4b9c0a 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -37,10 +37,12 @@ #include "util/output.h" #include "util/result.h" #include "util/statistics.h" +#include "util/language.h" using namespace std; using namespace CVC4; using namespace CVC4::main; +using namespace CVC4::language; /** * CVC4's main() routine is just an exception-safe wrapper around CVC4. @@ -64,7 +66,11 @@ int main(int argc, char* argv[]) { #ifdef CVC4_COMPETITION_MODE *opts[options::out] << "unknown" << endl; #endif - *opts[options::err] << "CVC4 Error:" << endl << e << endl; + if(opts[options::outputLanguage] == output::LANG_SMTLIB_V2) { + *opts[options::err] << "(error \"" << e << "\")" << endl; + } else { + *opts[options::err] << "CVC4 Error:" << endl << e << endl; + } if(opts[options::statistics] && pExecutor != NULL) { pTotalTime->stop(); pExecutor->flushStatistics(*opts[options::err]); diff --git a/src/main/main.h b/src/main/main.h index 0180c34d2..154919aa9 100644 --- a/src/main/main.h +++ b/src/main/main.h @@ -51,7 +51,7 @@ extern CVC4::TimerStat* pTotalTime; * Useful for nightly regressions, noninteractive performance runs * etc. See util.cpp. */ -extern bool segvNoSpin; +extern bool segvSpin; /** A pointer to the options in play */ extern CVC4_THREADLOCAL(Options*) pOptions; diff --git a/src/main/options b/src/main/options index 14a7a9f3f..ba36e43ab 100644 --- a/src/main/options +++ b/src/main/options @@ -38,6 +38,10 @@ option fallbackSequential --fallback-sequential bool :default false option incrementalParallel --incremental-parallel bool :default false :link --incremental Use parallel solver even in incremental mode (may print 'unknown's at times) +option segvSpin --segv-spin bool :default false + spin on segfault/other crash waiting for gdb +undocumented-alias --segv-nospin = --no-segv-spin + expert-option waitToJoin --wait-to-join bool :default true wait for other threads to join before quitting diff --git a/src/main/portfolio.cpp b/src/main/portfolio.cpp index 263458247..cf8bba1ba 100644 --- a/src/main/portfolio.cpp +++ b/src/main/portfolio.cpp @@ -38,6 +38,8 @@ int global_winner; template<typename S> void runThread(int thread_id, boost::function<S()> threadFn, S& returnValue) { + /* Uncommment line to delay first thread, useful to unearth errors/debug */ + // if(thread_id == 0) { sleep(1); } returnValue = threadFn(); if( mutex_done.try_lock() ) { diff --git a/src/main/portfolio_util.h b/src/main/portfolio_util.h index 6f6e2f03b..d22ca07d9 100644 --- a/src/main/portfolio_util.h +++ b/src/main/portfolio_util.h @@ -21,6 +21,7 @@ #include "util/channel.h" #include "util/lemma_input_channel.h" #include "util/lemma_output_channel.h" +#include "util/output.h" #include "main/options.h" namespace CVC4 { diff --git a/src/main/util.cpp b/src/main/util.cpp index 5bd0c9bd4..14ee82613 100644 --- a/src/main/util.cpp +++ b/src/main/util.cpp @@ -54,7 +54,7 @@ namespace main { * Useful for nightly regressions, noninteractive performance runs * etc. */ -bool segvNoSpin = false; +bool segvSpin = false; #ifndef __WIN32__ @@ -98,8 +98,7 @@ void segv_handler(int sig, siginfo_t* info, void* c) { cerr << "Looks like a NULL pointer was dereferenced." << endl; } - if(segvNoSpin) { - fprintf(stderr, "No-spin requested, aborting...\n"); + if(!segvSpin) { if((*pOptions)[options::statistics] && pExecutor != NULL) { pTotalTime->stop(); pExecutor->flushStatistics(cerr); @@ -133,8 +132,7 @@ void segv_handler(int sig, siginfo_t* info, void* c) { void ill_handler(int sig, siginfo_t* info, void*) { #ifdef CVC4_DEBUG fprintf(stderr, "CVC4 executed an illegal instruction in DEBUG mode.\n"); - if(segvNoSpin) { - fprintf(stderr, "No-spin requested, aborting...\n"); + if(!segvSpin) { if((*pOptions)[options::statistics] && pExecutor != NULL) { pTotalTime->stop(); pExecutor->flushStatistics(cerr); @@ -174,8 +172,7 @@ void cvc4unexpected() { fprintf(stderr, "The exception is:\n%s\n\n", static_cast<const char*>(CVC4::s_debugLastException)); } - if(segvNoSpin) { - fprintf(stderr, "No-spin requested.\n"); + if(!segvSpin) { if((*pOptions)[options::statistics] && pExecutor != NULL) { pTotalTime->stop(); pExecutor->flushStatistics(cerr); diff --git a/src/options/Makefile.am b/src/options/Makefile.am index 21988df56..5b0894680 100644 --- a/src/options/Makefile.am +++ b/src/options/Makefile.am @@ -23,6 +23,8 @@ OPTIONS_FILES_SRCS = \ ../theory/quantifiers/options.h \ ../theory/rewriterules/options.cpp \ ../theory/rewriterules/options.h \ + ../theory/strings/options.cpp \ + ../theory/strings/options.h \ ../prop/options.cpp \ ../prop/options.h \ ../proof/options.cpp \ @@ -36,7 +38,9 @@ OPTIONS_FILES_SRCS = \ ../main/options.cpp \ ../main/options.h \ ../parser/options.cpp \ - ../parser/options.h + ../parser/options.h \ + ../theory/idl/options.cpp \ + ../theory/idl/options.h OPTIONS_FILES = \ $(patsubst %.cpp,%,$(filter %.cpp,$(OPTIONS_FILES_SRCS))) @@ -80,6 +84,8 @@ nodist_liboptions_la_SOURCES = \ ../theory/quantifiers/options.h \ ../theory/rewriterules/options.cpp \ ../theory/rewriterules/options.h \ + ../theory/strings/options.cpp \ + ../theory/strings/options.h \ ../prop/options.cpp \ ../prop/options.h \ ../proof/options.cpp \ @@ -93,7 +99,9 @@ nodist_liboptions_la_SOURCES = \ ../main/options.cpp \ ../main/options.h \ ../parser/options.cpp \ - ../parser/options.h + ../parser/options.h \ + ../theory/idl/options.cpp \ + ../theory/idl/options.h BUILT_SOURCES = \ exprs-builts \ diff --git a/src/options/base_options b/src/options/base_options index 71754cca5..a6f24c7f3 100644 --- a/src/options/base_options +++ b/src/options/base_options @@ -103,7 +103,7 @@ common-option - -v --verbose void :handler CVC4::options::increaseVerbosity common-option - -q --quiet void :handler CVC4::options::decreaseVerbosity decrease verbosity (may be repeated) -common-option statistics statistics --stats bool +common-option statistics statistics --stats bool :predicate CVC4::smt::statsEnabledBuild :predicate-include "smt/options_handlers.h" give statistics on exit undocumented-alias --statistics = --stats undocumented-alias --no-statistics = --no-stats @@ -114,9 +114,6 @@ option parseOnly parse-only --parse-only bool :read-write option preprocessOnly preprocess-only --preprocess-only bool exit after preprocessing input -option segvNoSpin --segv-nospin bool - don't spin on segfault waiting for gdb - option - trace -t --trace=TAG argument :handler CVC4::options::addTraceTag trace something (e.g. -t pushpop), can repeat option - debug -d --debug=TAG argument :handler CVC4::options::addDebugTag diff --git a/src/options/mkoptions b/src/options/mkoptions index fa6c4c260..d856c7293 100755 --- a/src/options/mkoptions +++ b/src/options/mkoptions @@ -73,7 +73,9 @@ options_cpp_template="$1"; shift all_modules_defaults= all_modules_short_options= all_modules_long_options= +all_modules_smt_options= all_modules_option_handlers= +all_modules_get_options= smt_getoption_handlers= smt_setoption_handlers= include_all_option_headers= @@ -665,14 +667,64 @@ template <> options::${internal}__option_t::type runHandlerAndPredicates(options fi if [ -n "$smtname" ]; then + all_modules_smt_options="${all_modules_smt_options:+$all_modules_smt_options,} +#line $lineno \"$kf\" + \"$smtname\"" if [ "$internal" != - ]; then - smt_getoption_handlers="${smt_getoption_handlers} + case "$type" in + bool) + all_modules_get_options="${all_modules_get_options:+$all_modules_get_options +#line $lineno \"$kf\" + }{ std::vector<SExpr> v; v.push_back(\"$smtname\"); v.push_back(SExpr::Keyword(d_holder->$internal ? \"true\" : \"false\")); opts.push_back(v); }" + smt_getoption_handlers="${smt_getoption_handlers} +#line $lineno \"$kf\" + if(key == \"$smtname\") { +#line $lineno \"$kf\" + return SExprKeyword(options::$internal() ? \"true\" : \"false\"); + }";; + int|unsigned|int*_t|uint*_t|unsigned\ long|long|CVC4::Integer) + all_modules_get_options="${all_modules_get_options:+$all_modules_get_options +#line $lineno \"$kf\" + }{ std::vector<SExpr> v; v.push_back(\"$smtname\"); v.push_back(d_holder->$internal); opts.push_back(v); }" + smt_getoption_handlers="${smt_getoption_handlers} +#line $lineno \"$kf\" + if(key == \"$smtname\") { +#line $lineno \"$kf\" + return SExpr(Integer(options::$internal())); + }";; + float|double) + all_modules_get_options="${all_modules_get_options:+$all_modules_get_options +#line $lineno \"$kf\" + }{ std::vector<SExpr> v; v.push_back(\"$smtname\"); v.push_back(Rational::fromDouble(d_holder->$internal)); opts.push_back(v); }" + smt_getoption_handlers="${smt_getoption_handlers} +#line $lineno \"$kf\" + if(key == \"$smtname\") { +#line $lineno \"$kf\" + stringstream ss; ss << std::fixed << options::$internal(); + return SExpr(Rational::fromDecimal(ss.str())); + }";; + CVC4::Rational) + all_modules_get_options="${all_modules_get_options:+$all_modules_get_options +#line $lineno \"$kf\" + }{ std::vector<SExpr> v; v.push_back(\"$smtname\"); v.push_back(d_holder->$internal); opts.push_back(v); }" + smt_getoption_handlers="${smt_getoption_handlers} +#line $lineno \"$kf\" + if(key == \"$smtname\") { +#line $lineno \"$kf\" + return SExpr(options::$internal()); + }";; + *) + all_modules_get_options="${all_modules_get_options:+$all_modules_get_options +#line $lineno \"$kf\" + }{ std::stringstream ss; ss << d_holder->$internal; std::vector<SExpr> v; v.push_back(\"$smtname\"); v.push_back(ss.str()); opts.push_back(v); }" + smt_getoption_handlers="${smt_getoption_handlers} #line $lineno \"$kf\" if(key == \"$smtname\") { #line $lineno \"$kf\" stringstream ss; ss << options::$internal(); return SExpr(ss.str()); - }" + }";; + esac fi if [ "$type" = bool ]; then @@ -731,6 +783,36 @@ template <> options::${internal}__option_t::type runHandlerAndPredicates(options return; }" fi + elif [ -n "$long_option" -o "$long_option_alternate" ] && [ "$internal" != - ]; then + case "$type" in + bool) + getoption_name="$long_option" + inv= + # case where we have a --disable but no corresponding --enable + if [ -z "$getoption_name" ]; then + getoption_name="$long_option_alternate" + inv='!' + fi + all_modules_get_options="${all_modules_get_options:+$all_modules_get_options +#line $lineno \"$kf\" + }{ std::vector<SExpr> v; v.push_back(\"$getoption_name\"); v.push_back(SExpr::Keyword((${inv}d_holder->$internal) ? \"true\" : \"false\")); opts.push_back(v); }";; + int|unsigned|int*_t|uint*_t|unsigned\ long|long|CVC4::Integer) + all_modules_get_options="${all_modules_get_options:+$all_modules_get_options +#line $lineno \"$kf\" + }{ std::vector<SExpr> v; v.push_back(\"$long_option\"); v.push_back(d_holder->$internal); opts.push_back(v); }";; + float|double) + all_modules_get_options="${all_modules_get_options:+$all_modules_get_options +#line $lineno \"$kf\" + }{ std::vector<SExpr> v; v.push_back(\"$long_option\"); v.push_back(Rational::fromDouble(d_holder->$internal)); opts.push_back(v); }";; + CVC4::Rational) + all_modules_get_options="${all_modules_get_options:+$all_modules_get_options +#line $lineno \"$kf\" + }{ std::vector<SExpr> v; v.push_back(\"$long_option\"); v.push_back(d_holder->$internal); opts.push_back(v); }";; + *) + all_modules_get_options="${all_modules_get_options:+$all_modules_get_options +#line $lineno \"$kf\" + }{ std::stringstream ss; ss << d_holder->$internal; std::vector<SExpr> v; v.push_back(\"$long_option\"); v.push_back(ss.str()); opts.push_back(v); }";; + esac fi if [ "$type" = bool ]; then @@ -1446,7 +1528,9 @@ for var in \ all_modules_defaults \ all_modules_short_options \ all_modules_long_options \ + all_modules_smt_options \ all_modules_option_handlers \ + all_modules_get_options \ include_all_option_headers \ all_modules_contributions \ all_custom_handlers \ diff --git a/src/options/options.h b/src/options/options.h index 2be0e7b51..eaafade93 100644 --- a/src/options/options.h +++ b/src/options/options.h @@ -27,6 +27,7 @@ #include "options/option_exception.h" #include "util/language.h" #include "util/tls.h" +#include "util/sexpr.h" namespace CVC4 { @@ -118,12 +119,33 @@ public: static void printLanguageHelp(std::ostream& out); /** + * Look up long command-line option names that bear some similarity to + * the given name. Don't include the initial "--". This might be + * useful in case of typos. Can return an empty vector if there are + * no suggestions. + */ + static std::vector<std::string> suggestCommandLineOptions(const std::string& optionName) throw(); + + /** + * Look up SMT option names that bear some similarity to + * the given name. Don't include the initial ":". This might be + * useful in case of typos. Can return an empty vector if there are + * no suggestions. + */ + static std::vector<std::string> suggestSmtOptions(const std::string& optionName) throw(); + + /** * Initialize the options based on the given command-line arguments. * The return value is what's left of the command line (that is, the * non-option arguments). */ std::vector<std::string> parseOptions(int argc, char* argv[]) throw(OptionException); + /** + * Get the setting for all options. + */ + SExpr getOptions() const throw(); + };/* class Options */ }/* CVC4 namespace */ diff --git a/src/options/options_template.cpp b/src/options/options_template.cpp index 81ffe1b27..d97d11364 100644 --- a/src/options/options_template.cpp +++ b/src/options/options_template.cpp @@ -14,6 +14,26 @@ ** Contains code for handling command-line options **/ +#if !defined(_BSD_SOURCE) && defined(__MINGW32__) && !defined(__MINGW64__) +// force use of optreset; mingw32 croaks on argv-switching otherwise +# include "cvc4autoconfig.h" +# define _BSD_SOURCE +# undef HAVE_DECL_OPTRESET +# define HAVE_DECL_OPTRESET 1 +# define CVC4_IS_NOT_REALLY_BSD +#endif /* !_BSD_SOURCE && __MINGW32__ && !__MINGW64__ */ + +#ifdef __MINGW64__ +extern int optreset; +#endif /* __MINGW64__ */ + +#include <getopt.h> + +// clean up +#ifdef CVC4_IS_NOT_REALLY_BSD +# undef _BSD_SOURCE +#endif /* CVC4_IS_NOT_REALLY_BSD */ + #include <cstdio> #include <cstdlib> #include <new> @@ -25,8 +45,6 @@ #include <stdint.h> #include <time.h> -#include <getopt.h> - #include "expr/expr.h" #include "util/configuration.h" #include "util/exception.h" @@ -35,7 +53,7 @@ ${include_all_option_headers} -#line 39 "${template}" +#line 57 "${template}" #include "util/output.h" #include "options/options_holder.h" @@ -44,7 +62,7 @@ ${include_all_option_headers} ${option_handler_includes} -#line 48 "${template}" +#line 66 "${template}" using namespace CVC4; using namespace CVC4::options; @@ -181,7 +199,7 @@ void runBoolPredicates(T, std::string option, bool b, SmtEngine* smt) { ${all_custom_handlers} -#line 185 "${template}" +#line 203 "${template}" #ifdef CVC4_DEBUG # define USE_EARLY_TYPE_CHECKING_BY_DEFAULT true @@ -211,18 +229,18 @@ options::OptionsHolder::OptionsHolder() : ${all_modules_defaults} { } -#line 215 "${template}" +#line 233 "${template}" static const std::string mostCommonOptionsDescription = "\ Most commonly-used CVC4 options:${common_documentation}"; -#line 220 "${template}" +#line 238 "${template}" static const std::string optionsDescription = mostCommonOptionsDescription + "\n\ \n\ Additional CVC4 options:${remaining_documentation}"; -#line 226 "${template}" +#line 244 "${template}" static const std::string optionsFootnote = "\n\ [*] Each of these options has a --no-OPTIONNAME variant, which reverses the\n\ @@ -293,7 +311,7 @@ static struct option cmdlineOptions[] = {${all_modules_long_options} { NULL, no_argument, NULL, '\0' } };/* cmdlineOptions */ -#line 297 "${template}" +#line 315 "${template}" static void preemptGetopt(int& argc, char**& argv, const char* opt) { const size_t maxoptlen = 128; @@ -352,6 +370,8 @@ std::vector<std::string> Options::parseOptions(int argc, char* main_argv[]) thro const char *progName = main_argv[0]; SmtEngine* const smt = NULL; + Debug("options") << "main_argv == " << main_argv << std::endl; + // Reset getopt(), in the case of multiple calls to parseOptions(). // This can be = 1 in newer GNU getopt, but older (< 2007) require = 0. optind = 0; @@ -443,10 +463,33 @@ std::vector<std::string> Options::parseOptions(int argc, char* main_argv[]) thro } while(main_optind < argc && main_argv[main_optind][0] != '-'); continue; } + Debug("options") << "[ before, optind == " << optind << " ]" << std::endl; +#if defined(__MINGW32__) || defined(__MINGW64__) + if(optreset == 1 && optind > 1) { + // on mingw, optreset will reset the optind, so we have to + // manually advance argc, argv + main_argv[optind - 1] = main_argv[0]; + argv = main_argv += optind - 1; + argc -= optind - 1; + old_optind = optind = main_optind = 1; + if(argc > 0) { + Debug("options") << "looking at : " << argv[0] << std::endl; + } + /*c = getopt_long(argc, main_argv, + "+:${all_modules_short_options}", + cmdlineOptions, NULL); + Debug("options") << "pre-emptory c is " << c << " (" << char(c) << ")" << std::endl; + Debug("options") << "optind was reset to " << optind << std::endl; + optind = main_optind; + Debug("options") << "I restored optind to " << optind << std::endl;*/ + } +#endif /* __MINGW32__ || __MINGW64__ */ + Debug("options") << "[ argc == " << argc << ", main_argv == " << main_argv << " ]" << std::endl; c = getopt_long(argc, main_argv, "+:${all_modules_short_options}", cmdlineOptions, NULL); main_optind = optind; + Debug("options") << "[ got " << int(c) << " (" << char(c) << ") ]" << std::endl; Debug("options") << "[ next option will be at pos: " << optind << " ]" << std::endl; if(c == -1) { Debug("options") << "done with option parsing" << std::endl; @@ -461,7 +504,7 @@ std::vector<std::string> Options::parseOptions(int argc, char* main_argv[]) thro switch(c) { ${all_modules_option_handlers} -#line 465 "${template}" +#line 508 "${template}" case ':': // This can be a long or short option, and the way to get at the @@ -518,6 +561,48 @@ ${all_modules_option_handlers} return nonOptions; } +std::vector<std::string> Options::suggestCommandLineOptions(const std::string& optionName) throw() { + std::vector<std::string> suggestions; + + const char* opt; + for(size_t i = 0; (opt = cmdlineOptions[i].name) != NULL; ++i) { + if(std::strstr(opt, optionName.c_str()) != NULL) { + suggestions.push_back(opt); + } + } + + return suggestions; +} + +static const char* smtOptions[] = { + ${all_modules_smt_options}, +#line 580 "${template}" + NULL +};/* smtOptions[] */ + +std::vector<std::string> Options::suggestSmtOptions(const std::string& optionName) throw() { + std::vector<std::string> suggestions; + + const char* opt; + for(size_t i = 0; (opt = smtOptions[i]) != NULL; ++i) { + if(std::strstr(opt, optionName.c_str()) != NULL) { + suggestions.push_back(opt); + } + } + + return suggestions; +} + +SExpr Options::getOptions() const throw() { + std::vector<SExpr> opts; + + ${all_modules_get_options} + +#line 602 "${template}" + + return SExpr(opts); +} + #undef USE_EARLY_TYPE_CHECKING_BY_DEFAULT #undef DO_SEMANTIC_CHECKS_BY_DEFAULT diff --git a/src/parser/antlr_input.cpp b/src/parser/antlr_input.cpp index cd622b8a6..d498d3c54 100644 --- a/src/parser/antlr_input.cpp +++ b/src/parser/antlr_input.cpp @@ -46,9 +46,10 @@ namespace parser { AntlrInputStream::AntlrInputStream(std::string name, pANTLR3_INPUT_STREAM input, bool fileIsTemporary) : - InputStream(name,fileIsTemporary), + InputStream(name, fileIsTemporary), d_input(input) { assert( input != NULL ); + input->fileName = input->strFactory->newStr8(input->strFactory, (pANTLR3_UINT8)name.c_str()); } AntlrInputStream::~AntlrInputStream() { @@ -286,16 +287,18 @@ void AntlrInput::warning(const std::string& message) { void AntlrInput::parseError(const std::string& message, bool eofException) throw (ParserException) { Debug("parser") << "Throwing exception: " - << getInputStream()->getName() << ":" + << (const char*)d_lexer->rec->state->tokSource->fileName->chars << ":" << d_lexer->getLine(d_lexer) << "." << d_lexer->getCharPositionInLine(d_lexer) << ": " << message << endl; if(eofException) { - throw ParserEndOfFileException(message, getInputStream()->getName(), + throw ParserEndOfFileException(message, + (const char*)d_lexer->rec->state->tokSource->fileName->chars, d_lexer->getLine(d_lexer), d_lexer->getCharPositionInLine(d_lexer)); } else { - throw ParserException(message, getInputStream()->getName(), + throw ParserException(message, + (const char*)d_lexer->rec->state->tokSource->fileName->chars, d_lexer->getLine(d_lexer), d_lexer->getCharPositionInLine(d_lexer)); } diff --git a/src/parser/antlr_input.h b/src/parser/antlr_input.h index 3b7d53be8..8763e8451 100644 --- a/src/parser/antlr_input.h +++ b/src/parser/antlr_input.h @@ -14,12 +14,21 @@ ** Base for ANTLR parser classes. **/ +#include <antlr3.h> + +// ANTLR3 headers define these in our space :( +// undef them so that we don't get multiple-definition warnings +#undef PACKAGE_BUGREPORT +#undef PACKAGE_NAME +#undef PACKAGE_STRING +#undef PACKAGE_TARNAME +#undef PACKAGE_VERSION + #include "cvc4parser_private.h" #ifndef __CVC4__PARSER__ANTLR_INPUT_H #define __CVC4__PARSER__ANTLR_INPUT_H -#include <antlr3.h> #include <iostream> #include <sstream> #include <stdexcept> @@ -34,6 +43,7 @@ #include "util/bitvector.h" #include "util/integer.h" #include "util/rational.h" +#include "util/output.h" namespace CVC4 { diff --git a/src/parser/antlr_line_buffered_input.cpp b/src/parser/antlr_line_buffered_input.cpp index c2f73d988..a59fb3531 100644 --- a/src/parser/antlr_line_buffered_input.cpp +++ b/src/parser/antlr_line_buffered_input.cpp @@ -16,6 +16,15 @@ **/ #include <antlr3.h> + +// ANTLR3 headers define these in our space :( +// undef them so that we don't get multiple-definition warnings +#undef PACKAGE_BUGREPORT +#undef PACKAGE_NAME +#undef PACKAGE_STRING +#undef PACKAGE_TARNAME +#undef PACKAGE_VERSION + #include <iostream> #include <string> #include <cassert> diff --git a/src/parser/bounded_token_buffer.cpp b/src/parser/bounded_token_buffer.cpp index 88a7c6463..112d9b0ed 100644 --- a/src/parser/bounded_token_buffer.cpp +++ b/src/parser/bounded_token_buffer.cpp @@ -241,7 +241,7 @@ static pANTLR3_COMMON_TOKEN tokLT(pANTLR3_TOKEN_STREAM ts, ANTLR3_INT32 k) { } /* Initialize the buffer on our first call. */ - if( EXPECT_FALSE(buffer->empty == ANTLR3_TRUE) ) { + if( __builtin_expect( (buffer->empty == ANTLR3_TRUE), false ) ) { assert( buffer->tokenBuffer != NULL ); buffer->tokenBuffer[ 0 ] = nextToken(buffer); buffer->maxIndex = 0; diff --git a/src/parser/cvc/Cvc.g b/src/parser/cvc/Cvc.g index 217e7ab97..03d1e7a8a 100644 --- a/src/parser/cvc/Cvc.g +++ b/src/parser/cvc/Cvc.g @@ -150,6 +150,7 @@ tokens { MOD_TOK = 'MOD'; INTDIV_TOK = 'DIV'; FLOOR_TOK = 'FLOOR'; + DISTINCT_TOK = 'DISTINCT'; // Bitvectors @@ -270,8 +271,7 @@ int getOperatorPrecedence(int type) { case LEQ_TOK: case LT_TOK: case GEQ_TOK: - case GT_TOK: - case FLOOR_TOK: return 25; + case GT_TOK: return 25; case EQUAL_TOK: case DISEQUAL_TOK: return 26; case NOT_TOK: return 27; @@ -953,7 +953,7 @@ declareVariables[CVC4::Command*& cmd, CVC4::Type& t, const std::vector<std::stri } else { Debug("parser") << " " << *i << " not declared" << std::endl; if(topLevel) { - Expr func = PARSER_STATE->mkVar(*i, t, true); + Expr func = PARSER_STATE->mkVar(*i, t, ExprManager::VAR_FLAG_GLOBAL); Command* decl = new DeclareFunctionCommand(*i, func, t); seq->addCommand(decl); } else { @@ -968,13 +968,14 @@ declareVariables[CVC4::Command*& cmd, CVC4::Type& t, const std::vector<std::stri // like e.g. FORALL(x:INT = 4): [...] PARSER_STATE->parseError("cannot construct a definition here; maybe you want a LET"); } - Debug("parser") << "made " << idList.front() << " = " << f << std::endl; + assert(!idList.empty()); for(std::vector<std::string>::const_iterator i = idList.begin(), i_end = idList.end(); i != i_end; ++i) { + Debug("parser") << "making " << *i << " : " << t << " = " << f << std::endl; PARSER_STATE->checkDeclaration(*i, CHECK_UNDECLARED, SYM_VARIABLE); - Expr func = EXPR_MANAGER->mkVar(*i, t, true); + Expr func = EXPR_MANAGER->mkVar(*i, t, ExprManager::VAR_FLAG_GLOBAL | ExprManager::VAR_FLAG_DEFINED); PARSER_STATE->defineFunction(*i, f); Command* decl = new DefineFunctionCommand(*i, func, f); seq->addCommand(decl); @@ -1162,15 +1163,11 @@ restrictedTypePossiblyFunctionLHS[CVC4::Type& t, } /* tuple types / old-style function types */ - | LBRACKET type[t,check] { types.push_back(t); } - ( COMMA type[t,check] { types.push_back(t); } )* RBRACKET - { if(types.size() == 1) { - if(types.front().isFunction()) { - // old style function syntax [ T -> U ] - PARSER_STATE->parseError("old-style function type syntax not supported anymore; please use the new syntax"); - } else { - PARSER_STATE->parseError("I'm not sure what you're trying to do here; tuples must have > 1 type"); - } + | LBRACKET ( type[t,check] { types.push_back(t); } + ( COMMA type[t,check] { types.push_back(t); } )* )? RBRACKET + { if(types.size() == 1 && types.front().isFunction()) { + // old style function syntax [ T -> U ] + PARSER_STATE->parseError("old-style function type syntax not supported anymore; please use the new syntax"); } else { // tuple type [ T, U, V... ] t = EXPR_MANAGER->mkTupleType(types); @@ -1178,13 +1175,17 @@ restrictedTypePossiblyFunctionLHS[CVC4::Type& t, } /* record types */ - | SQHASH identifier[id,CHECK_NONE,SYM_SORT] COLON type[t,check] { typeIds.push_back(std::make_pair(id, t)); } - ( COMMA identifier[id,CHECK_NONE,SYM_SORT] COLON type[t,check] { typeIds.push_back(std::make_pair(id, t)); } )* HASHSQ + | SQHASH ( identifier[id,CHECK_NONE,SYM_SORT] COLON type[t,check] { typeIds.push_back(std::make_pair(id, t)); } + ( COMMA identifier[id,CHECK_NONE,SYM_SORT] COLON type[t,check] { typeIds.push_back(std::make_pair(id, t)); } )* )? HASHSQ { t = EXPR_MANAGER->mkRecordType(typeIds); } /* bitvector types */ | BITVECTOR_TOK LPAREN k=numeral RPAREN - { t = EXPR_MANAGER->mkBitVectorType(k); } + { if(k == 0) { + PARSER_STATE->parseError("Illegal bitvector size: 0"); + } + t = EXPR_MANAGER->mkBitVectorType(k); + } /* basic types */ | BOOLEAN_TOK { t = EXPR_MANAGER->booleanType(); } @@ -1326,7 +1327,7 @@ prefixFormula[CVC4::Expr& f] { PARSER_STATE->popScope(); Type t = EXPR_MANAGER->mkFunctionType(types, f.getType()); std::string name = "lambda"; - Expr func = PARSER_STATE->mkAnonymousFunction(name, t); + Expr func = PARSER_STATE->mkAnonymousFunction(name, t, ExprManager::VAR_FLAG_DEFINED); Command* cmd = new DefineFunctionCommand(name, func, terms, f); PARSER_STATE->preemptCommand(cmd); f = func; @@ -1337,7 +1338,7 @@ prefixFormula[CVC4::Expr& f] boundVarDecl[ids,t] RPAREN COLON formula[f] { PARSER_STATE->popScope(); UNSUPPORTED("array literals not supported yet"); - f = EXPR_MANAGER->mkVar(EXPR_MANAGER->mkArrayType(t, f.getType()), true); + f = EXPR_MANAGER->mkVar(EXPR_MANAGER->mkArrayType(t, f.getType()), ExprManager::VAR_FLAG_GLOBAL); } ; @@ -1368,7 +1369,7 @@ letDecl : identifier[name,CHECK_NONE,SYM_VARIABLE] EQUAL_TOK formula[e] { Debug("parser") << Expr::setlanguage(language::output::LANG_CVC4) << e.getType() << std::endl; PARSER_STATE->defineVar(name, e); - Debug("parser") << "LET[" << PARSER_STATE->getDeclarationLevel() << "]: " + Debug("parser") << "LET[" << PARSER_STATE->scopeLevel() << "]: " << name << std::endl << " ==>" << " " << e << std::endl; } @@ -1578,7 +1579,7 @@ postfixTerm[CVC4::Expr& f] std::string id; Type t; } - : bvTerm[f] + : ( bvTerm[f] ( /* array select / bitvector extract */ LBRACKET ( formula[f2] { extract = false; } @@ -1659,6 +1660,13 @@ postfixTerm[CVC4::Expr& f] } ) )* + | FLOOR_TOK LPAREN formula[f] RPAREN + { f = MK_EXPR(CVC4::kind::TO_INTEGER, f); } + | DISTINCT_TOK LPAREN + formula[f] { args.push_back(f); } + ( COMMA formula[f] { args.push_back(f); } )* RPAREN + { f = (args.size() == 1) ? MK_CONST(bool(true)) : MK_EXPR(CVC4::kind::DISTINCT, args); } + ) ( typeAscription[f, t] { if(f.getKind() == CVC4::kind::APPLY_CONSTRUCTOR && t.isDatatype()) { std::vector<CVC4::Expr> v; @@ -1834,6 +1842,10 @@ simpleTerm[CVC4::Expr& f] } } + /* empty tuple literal */ + | LPAREN RPAREN + { f = MK_EXPR(kind::TUPLE, std::vector<Expr>()); } + /* boolean literals */ | TRUE_TOK { f = MK_CONST(bool(true)); } | FALSE_TOK { f = MK_CONST(bool(false)); } @@ -1939,15 +1951,15 @@ datatypeDef[std::vector<CVC4::Datatype>& datatypes] * below. */ : identifier[id,CHECK_NONE,SYM_SORT] { PARSER_STATE->pushScope(); } ( LBRACKET identifier[id2,CHECK_UNDECLARED,SYM_SORT] { - t = PARSER_STATE->mkSort(id2); + t = PARSER_STATE->mkSort(id2, ExprManager::SORT_FLAG_PLACEHOLDER); params.push_back( t ); } ( COMMA identifier[id2,CHECK_UNDECLARED,SYM_SORT] { - t = PARSER_STATE->mkSort(id2); + t = PARSER_STATE->mkSort(id2, ExprManager::SORT_FLAG_PLACEHOLDER); params.push_back( t ); } )* RBRACKET )? - { datatypes.push_back(Datatype(id,params)); + { datatypes.push_back(Datatype(id, params)); if(!PARSER_STATE->isUnresolvedType(id)) { // if not unresolved, must be undeclared PARSER_STATE->checkDeclaration(id, CHECK_UNDECLARED, SYM_SORT); diff --git a/src/parser/cvc/Makefile.am b/src/parser/cvc/Makefile.am index 7c5d48c1c..b7066dd7e 100644 --- a/src/parser/cvc/Makefile.am +++ b/src/parser/cvc/Makefile.am @@ -1,7 +1,7 @@ AM_CPPFLAGS = \ -D__BUILDING_CVC4PARSERLIB \ -I@builddir@/../.. -I@srcdir@/../../include -I@srcdir@/../.. $(ANTLR_INCLUDES) -AM_CXXFLAGS = -Wall -Wno-unknown-pragmas $(FLAG_VISIBILITY_HIDDEN) $(WNO_PARENTHESES) $(WNO_TAUTOLOGICAL_COMPARE) -Wno-unused-function -Wno-unused-variable $(WNO_CONVERSION_NULL) +AM_CXXFLAGS = -Wall -Wno-unknown-pragmas $(FLAG_VISIBILITY_HIDDEN) $(WNO_PARENTHESES) $(WNO_TAUTOLOGICAL_COMPARE) -Wno-unused-function -Wno-unused-variable $(WNO_UNINITIALIZED) $(WNO_CONVERSION_NULL) # Compile generated C files using C++ compiler CC=$(CXX) diff --git a/src/parser/options b/src/parser/options index beae09823..f277b231d 100644 --- a/src/parser/options +++ b/src/parser/options @@ -14,4 +14,8 @@ option memoryMap --mmap bool option semanticChecks /--no-checking bool :default DO_SEMANTIC_CHECKS_BY_DEFAULT :link /--no-type-checking disable ALL semantic checks, including type checks +# this is just to support security in the online version +# (--no-include-file disables filesystem access in TPTP and SMT2 parsers) +undocumented-option canIncludeFile /--no-include-file bool :default true + endmodule diff --git a/src/parser/parser.cpp b/src/parser/parser.cpp index 198d1cc31..5d9b6c7ae 100644 --- a/src/parser/parser.cpp +++ b/src/parser/parser.cpp @@ -42,11 +42,13 @@ Parser::Parser(ExprManager* exprManager, Input* input, bool strictMode, bool par d_input(input), d_symtabAllocated(), d_symtab(&d_symtabAllocated), + d_assertionLevel(0), d_anonymousFunctionCount(0), d_done(false), d_checksEnabled(true), d_strictMode(strictMode), - d_parseOnly(parseOnly) { + d_parseOnly(parseOnly), + d_canIncludeFile(true) { d_input->setParser(*this); } @@ -137,11 +139,10 @@ bool Parser::isPredicate(const std::string& name) { } Expr -Parser::mkVar(const std::string& name, const Type& type, - bool levelZero) { - Debug("parser") << "mkVar(" << name << ", " << type << ", " << levelZero << ")" << std::endl; - Expr expr = d_exprManager->mkVar(name, type, levelZero); - defineVar(name, expr, levelZero); +Parser::mkVar(const std::string& name, const Type& type, uint32_t flags) { + Debug("parser") << "mkVar(" << name << ", " << type << ")" << std::endl; + Expr expr = d_exprManager->mkVar(name, type, flags); + defineVar(name, expr, flags & ExprManager::VAR_FLAG_GLOBAL); return expr; } @@ -154,35 +155,31 @@ Parser::mkBoundVar(const std::string& name, const Type& type) { } Expr -Parser::mkFunction(const std::string& name, const Type& type, - bool levelZero) { +Parser::mkFunction(const std::string& name, const Type& type, uint32_t flags) { Debug("parser") << "mkVar(" << name << ", " << type << ")" << std::endl; - Expr expr = d_exprManager->mkVar(name, type, levelZero); - defineFunction(name, expr, levelZero); + Expr expr = d_exprManager->mkVar(name, type, flags); + defineFunction(name, expr, flags & ExprManager::VAR_FLAG_GLOBAL); return expr; } Expr -Parser::mkAnonymousFunction(const std::string& prefix, const Type& type) { +Parser::mkAnonymousFunction(const std::string& prefix, const Type& type, uint32_t flags) { stringstream name; name << prefix << "_anon_" << ++d_anonymousFunctionCount; - return mkFunction(name.str(), type); + return d_exprManager->mkVar(name.str(), type, flags); } std::vector<Expr> -Parser::mkVars(const std::vector<std::string> names, - const Type& type, - bool levelZero) { +Parser::mkVars(const std::vector<std::string> names, const Type& type, uint32_t flags) { std::vector<Expr> vars; for(unsigned i = 0; i < names.size(); ++i) { - vars.push_back(mkVar(names[i], type, levelZero)); + vars.push_back(mkVar(names[i], type, flags)); } return vars; } std::vector<Expr> -Parser::mkBoundVars(const std::vector<std::string> names, - const Type& type) { +Parser::mkBoundVars(const std::vector<std::string> names, const Type& type) { std::vector<Expr> vars; for(unsigned i = 0; i < names.size(); ++i) { vars.push_back(mkBoundVar(names[i], type)); @@ -191,16 +188,14 @@ Parser::mkBoundVars(const std::vector<std::string> names, } void -Parser::defineVar(const std::string& name, const Expr& val, - bool levelZero) { - Debug("parser") << "defineVar( " << name << " := " << val << " , " << levelZero << ")" << std::endl;; +Parser::defineVar(const std::string& name, const Expr& val, bool levelZero) { + Debug("parser") << "defineVar( " << name << " := " << val << ")" << std::endl;; d_symtab->bind(name, val, levelZero); assert( isDeclared(name) ); } void -Parser::defineFunction(const std::string& name, const Expr& val, - bool levelZero) { +Parser::defineFunction(const std::string& name, const Expr& val, bool levelZero) { d_symtab->bindDefinedFunction(name, val, levelZero); assert( isDeclared(name) ); } @@ -236,9 +231,9 @@ Parser::defineParameterizedType(const std::string& name, } SortType -Parser::mkSort(const std::string& name) { +Parser::mkSort(const std::string& name, uint32_t flags) { Debug("parser") << "newSort(" << name << ")" << std::endl; - Type type = d_exprManager->mkSort(name); + Type type = d_exprManager->mkSort(name, flags); defineType(name, type); return type; } @@ -253,7 +248,7 @@ Parser::mkSortConstructor(const std::string& name, size_t arity) { } SortType Parser::mkUnresolvedType(const std::string& name) { - SortType unresolved = mkSort(name); + SortType unresolved = mkSort(name, ExprManager::SORT_FLAG_PLACEHOLDER); d_unresolved.insert(unresolved); return unresolved; } @@ -357,7 +352,7 @@ Parser::mkMutualDatatypeTypes(const std::vector<Datatype>& datatypes) { bool Parser::isDeclared(const std::string& name, SymbolType type) { switch(type) { case SYM_VARIABLE: - return d_symtab->isBound(name); + return d_reservedSymbols.find(name) != d_reservedSymbols.end() || d_symtab->isBound(name); case SYM_SORT: return d_symtab->isBoundType(name); } @@ -365,9 +360,15 @@ bool Parser::isDeclared(const std::string& name, SymbolType type) { return false; } +void Parser::reserveSymbolAtAssertionLevel(const std::string& varName) { + checkDeclaration(varName, CHECK_UNDECLARED, SYM_VARIABLE); + d_reservedSymbols.insert(varName); +} + void Parser::checkDeclaration(const std::string& varName, DeclarationCheck check, - SymbolType type) + SymbolType type, + std::string notes) throw(ParserException) { if(!d_checksEnabled) { return; @@ -377,14 +378,16 @@ void Parser::checkDeclaration(const std::string& varName, case CHECK_DECLARED: if( !isDeclared(varName, type) ) { parseError("Symbol " + varName + " not declared as a " + - (type == SYM_VARIABLE ? "variable" : "type")); + (type == SYM_VARIABLE ? "variable" : "type") + + (notes.size() == 0 ? notes : "\n" + notes)); } break; case CHECK_UNDECLARED: if( isDeclared(varName, type) ) { parseError("Symbol " + varName + " previously declared as a " + - (type == SYM_VARIABLE ? "variable" : "type")); + (type == SYM_VARIABLE ? "variable" : "type") + + (notes.size() == 0 ? notes : "\n" + notes)); } break; diff --git a/src/parser/parser.h b/src/parser/parser.h index 1ca56dc06..b6ba482b7 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -126,6 +126,23 @@ class CVC4_PUBLIC Parser { */ SymbolTable* d_symtab; + /** + * The level of the assertions in the declaration scope. Things declared + * after this level are bindings from e.g. a let, a quantifier, or a + * lambda. + */ + size_t d_assertionLevel; + + /** + * Maintains a list of reserved symbols at the assertion level that might + * not occur in our symbol table. This is necessary to e.g. support the + * proper behavior of the :named annotation in SMT-LIBv2 when used under + * a let or a quantifier, since inside a let/quant body the declaration + * scope is that of the let/quant body, but the defined name should be + * reserved at the assertion level. + */ + std::set<std::string> d_reservedSymbols; + /** How many anonymous functions we've created. */ size_t d_anonymousFunctionCount; @@ -141,6 +158,12 @@ class CVC4_PUBLIC Parser { /** Are we only parsing? */ bool d_parseOnly; + /** + * Can we include files? (Set to false for security purposes in + * e.g. the online version.) + */ + bool d_canIncludeFile; + /** The set of operators available in the current logic. */ std::set<Kind> d_logicOperators; @@ -235,6 +258,10 @@ public: bool strictModeEnabled() { return d_strictMode; } + void allowIncludeFile() { d_canIncludeFile = true; } + void disallowIncludeFile() { d_canIncludeFile = false; } + bool canIncludeFile() const { return d_canIncludeFile; } + /** * Returns a variable, given a name. * @@ -285,7 +312,13 @@ public: * @throws ParserException if checks are enabled and the check fails */ void checkDeclaration(const std::string& name, DeclarationCheck check, - SymbolType type = SYM_VARIABLE) throw(ParserException); + SymbolType type = SYM_VARIABLE, + std::string notes = "") throw(ParserException); + + /** + * Reserve a symbol at the assertion level. + */ + void reserveSymbolAtAssertionLevel(const std::string& name); /** * Checks whether the given name is bound to a function. @@ -326,14 +359,14 @@ public: /** Create a new CVC4 variable expression of the given type. */ Expr mkVar(const std::string& name, const Type& type, - bool levelZero = false); + uint32_t flags = ExprManager::VAR_FLAG_NONE); /** * Create a set of new CVC4 variable expressions of the given type. */ std::vector<Expr> mkVars(const std::vector<std::string> names, const Type& type, - bool levelZero = false); + uint32_t flags = ExprManager::VAR_FLAG_NONE); /** Create a new CVC4 bound variable expression of the given type. */ Expr mkBoundVar(const std::string& name, const Type& type); @@ -345,18 +378,19 @@ public: /** Create a new CVC4 function expression of the given type. */ Expr mkFunction(const std::string& name, const Type& type, - bool levelZero = false); + uint32_t flags = ExprManager::VAR_FLAG_NONE); /** * Create a new CVC4 function expression of the given type, * appending a unique index to its name. (That's the ONLY * difference between mkAnonymousFunction() and mkFunction()). */ - Expr mkAnonymousFunction(const std::string& prefix, const Type& type); + Expr mkAnonymousFunction(const std::string& prefix, const Type& type, + uint32_t flags = ExprManager::VAR_FLAG_NONE); /** Create a new variable definition (e.g., from a let binding). */ void defineVar(const std::string& name, const Expr& val, - bool levelZero = false); + bool levelZero = false); /** Create a new function definition (e.g., from a define-fun). */ void defineFunction(const std::string& name, const Expr& val, @@ -377,7 +411,8 @@ public: /** * Creates a new sort with the given name. */ - SortType mkSort(const std::string& name); + SortType mkSort(const std::string& name, + uint32_t flags = ExprManager::SORT_FLAG_NONE); /** * Creates a new sort constructor with the given name and arity. @@ -462,6 +497,11 @@ public: d_input->parseError(msg); } + /** Unexpectedly encountered an EOF */ + inline void unexpectedEOF(const std::string& msg) throw(ParserException) { + d_input->parseError(msg, true); + } + /** * If we are parsing only, don't raise an exception; if we are not, * raise a parse error with the given message. There is no actual @@ -482,8 +522,25 @@ public: } } - inline void pushScope() { d_symtab->pushScope(); } - inline void popScope() { d_symtab->popScope(); } + /** + * Gets the current declaration level. + */ + inline size_t scopeLevel() const { return d_symtab->getLevel(); } + + inline void pushScope(bool bindingLevel = false) { + d_symtab->pushScope(); + if(!bindingLevel) { + d_assertionLevel = scopeLevel(); + } + } + + inline void popScope() { + d_symtab->popScope(); + if(scopeLevel() < d_assertionLevel) { + d_assertionLevel = scopeLevel(); + d_reservedSymbols.clear(); + } + } /** * Set the current symbol table used by this parser. @@ -527,13 +584,6 @@ public: } /** - * Gets the current declaration level. - */ - inline size_t getDeclarationLevel() const throw() { - return d_symtab->getLevel(); - } - - /** * An expression stream interface for a parser. This stream simply * pulls expressions from the given Parser object. * diff --git a/src/parser/parser_builder.cpp b/src/parser/parser_builder.cpp index 721337c9e..cb8c0d4f6 100644 --- a/src/parser/parser_builder.cpp +++ b/src/parser/parser_builder.cpp @@ -54,6 +54,7 @@ void ParserBuilder::init(ExprManager* exprManager, d_exprManager = exprManager; d_checksEnabled = true; d_strictMode = false; + d_canIncludeFile = true; d_mmap = false; d_parseOnly = false; } @@ -102,6 +103,12 @@ Parser* ParserBuilder::build() parser->disableChecks(); } + if( d_canIncludeFile ) { + parser->allowIncludeFile(); + } else { + parser->disallowIncludeFile(); + } + return parser; } @@ -146,7 +153,8 @@ ParserBuilder& ParserBuilder::withOptions(const Options& options) { .withMmap(options[options::memoryMap]) .withChecks(options[options::semanticChecks]) .withStrictMode(options[options::strictParsing]) - .withParseOnly(options[options::parseOnly]); + .withParseOnly(options[options::parseOnly]) + .withIncludeFile(options[options::canIncludeFile]); } ParserBuilder& ParserBuilder::withStrictMode(bool flag) { @@ -154,6 +162,11 @@ ParserBuilder& ParserBuilder::withStrictMode(bool flag) { return *this; } +ParserBuilder& ParserBuilder::withIncludeFile(bool flag) { + d_canIncludeFile = flag; + return *this; +} + ParserBuilder& ParserBuilder::withStreamInput(std::istream& input) { d_inputType = STREAM_INPUT; d_streamInput = &input; diff --git a/src/parser/parser_builder.h b/src/parser/parser_builder.h index 75e2b4fbe..b6e15b2ff 100644 --- a/src/parser/parser_builder.h +++ b/src/parser/parser_builder.h @@ -71,6 +71,9 @@ class CVC4_PUBLIC ParserBuilder { /** Should we parse in strict mode? */ bool d_strictMode; + /** Should we allow include-file commands? */ + bool d_canIncludeFile; + /** Should we memory-map a file input? */ bool d_mmap; @@ -146,6 +149,13 @@ public: */ ParserBuilder& withStrictMode(bool flag = true); + /** + * Should the include-file commands be enabled? + * + * (Default: yes) + */ + ParserBuilder& withIncludeFile(bool flag = true); + /** Set the parser to use the given stream for its input. */ ParserBuilder& withStreamInput(std::istream& input); diff --git a/src/parser/smt1/smt1.cpp b/src/parser/smt1/smt1.cpp index 41b0523bd..c9bbd3860 100644 --- a/src/parser/smt1/smt1.cpp +++ b/src/parser/smt1/smt1.cpp @@ -3,7 +3,7 @@ ** \verbatim ** Original author: Morgan Deters ** Major contributors: Christopher L. Conway - ** Minor contributors (to current version): Tim King, Dejan Jovanovic, Clark Barrett + ** Minor contributors (to current version): Tim King, Dejan Jovanovic, Clark Barrett, 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 @@ -39,6 +39,7 @@ std::hash_map<const std::string, Smt1::Logic, CVC4::StringHashFunction> Smt1::ne logicMap["QF_NIA"] = QF_NIA; logicMap["QF_NRA"] = QF_NRA; logicMap["QF_RDL"] = QF_RDL; + logicMap["QF_S"] = QF_S; logicMap["QF_SAT"] = QF_SAT; logicMap["QF_UF"] = QF_UF; logicMap["QF_UFIDL"] = QF_UFIDL; @@ -180,6 +181,10 @@ void Smt1::setLogic(const std::string& name) { d_logic = toLogic(name); switch(d_logic) { + case QF_S: + throw ParserException("Strings theory unsupported in SMT-LIBv1 front-end; try SMT-LIBv2."); + break; + case QF_AX: addTheory(THEORY_ARRAYS_EX); break; diff --git a/src/parser/smt1/smt1.h b/src/parser/smt1/smt1.h index d6961371a..f96a4e810 100644 --- a/src/parser/smt1/smt1.h +++ b/src/parser/smt1/smt1.h @@ -3,7 +3,7 @@ ** \verbatim ** Original author: Morgan Deters ** Major contributors: Christopher L. Conway - ** Minor contributors (to current version): Clark Barrett + ** Minor contributors (to current version): Clark Barrett, 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 @@ -52,6 +52,7 @@ public: QF_NIA, QF_NRA, QF_RDL, + QF_S, // nonstandard (for string theory) QF_SAT, QF_UF, QF_UFIDL, @@ -82,6 +83,7 @@ public: THEORY_INT_INT_REAL_ARRAY_ARRAYS_EX, THEORY_REALS, THEORY_REALS_INTS, + THEORY_STRINGS, THEORY_QUANTIFIERS }; diff --git a/src/parser/smt2/Smt2.g b/src/parser/smt2/Smt2.g index 8dcde2483..c84046570 100644 --- a/src/parser/smt2/Smt2.g +++ b/src/parser/smt2/Smt2.g @@ -3,7 +3,7 @@ ** \verbatim ** Original author: Christopher L. Conway ** Major contributors: Morgan Deters - ** Minor contributors (to current version): Dejan Jovanovic, Andrew Reynolds, Francois Bobot + ** Minor contributors (to current version): Dejan Jovanovic, Andrew Reynolds, Francois Bobot, 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 @@ -133,6 +133,41 @@ using namespace CVC4::parser; #define MK_CONST EXPR_MANAGER->mkConst #define UNSUPPORTED PARSER_STATE->unimplementedFeature +static bool isClosed(Expr e, std::set<Expr>& free, std::hash_set<Expr, ExprHashFunction>& closedCache) { + if(closedCache.find(e) != closedCache.end()) { + return true; + } + + if(e.getKind() == kind::FORALL || e.getKind() == kind::EXISTS || e.getKind() == kind::LAMBDA) { + isClosed(e[1], free, closedCache); + for(Expr::const_iterator i = e[0].begin(); i != e[0].end(); ++i) { + free.erase((*i)[0]); + } + } else if(e.getKind() == kind::BOUND_VARIABLE) { + free.insert(e); + return false; + } else { + if(e.hasOperator()) { + isClosed(e.getOperator(), free, closedCache); + } + for(Expr::const_iterator i = e.begin(); i != e.end(); ++i) { + isClosed(*i, free, closedCache); + } + } + + if(free.empty()) { + closedCache.insert(e); + return true; + } else { + return false; + } +} + +static inline bool isClosed(Expr e, std::set<Expr>& free) { + std::hash_set<Expr, ExprHashFunction> cache; + return isClosed(e, free, cache); +} + }/* parser::postinclude */ /** @@ -153,7 +188,26 @@ parseExpr returns [CVC4::parser::smt2::myExpr expr] * @return the parsed command, or NULL if we've reached the end of the input */ parseCommand returns [CVC4::Command* cmd = NULL] +@declarations { + std::string name; +} : LPAREN_TOK c = command RPAREN_TOK { $cmd = c; } + + /* This extended command has to be in the outermost production so that + * the RPAREN_TOK is properly eaten and we are in a good state to read + * the included file's tokens. */ + | LPAREN_TOK INCLUDE_TOK str[name] RPAREN_TOK + { if(!PARSER_STATE->canIncludeFile()) { + PARSER_STATE->parseError("include-file feature was disabled for this run."); + } + if(PARSER_STATE->strictModeEnabled()) { + PARSER_STATE->parseError("Extended commands are not permitted while operating in strict compliance mode."); + } + PARSER_STATE->includeFile(name); + // The command of the included file will be produced at the next parseCommand() call + cmd = new EmptyCommand("include::" + name); + } + | EOF { $cmd = 0; } ; @@ -188,9 +242,8 @@ command returns [CVC4::Command* cmd = NULL] GET_INFO_TOK KEYWORD { cmd = new GetInfoCommand(AntlrInput::tokenText($KEYWORD).c_str() + 1); } | /* set-option */ - SET_OPTION_TOK KEYWORD symbolicExpr[sexpr] - { name = AntlrInput::tokenText($KEYWORD); - PARSER_STATE->setOption(name.c_str() + 1, sexpr); + SET_OPTION_TOK keyword[name] symbolicExpr[sexpr] + { PARSER_STATE->setOption(name.c_str() + 1, sexpr); cmd = new SetOptionCommand(name.c_str() + 1, sexpr); } | /* get-option */ GET_OPTION_TOK KEYWORD @@ -216,7 +269,7 @@ command returns [CVC4::Command* cmd = NULL] symbol[name,CHECK_UNDECLARED,SYM_SORT] { PARSER_STATE->checkUserSymbol(name); } LPAREN_TOK symbolList[names,CHECK_NONE,SYM_SORT] RPAREN_TOK - { PARSER_STATE->pushScope(); + { PARSER_STATE->pushScope(true); for(std::vector<std::string>::const_iterator i = names.begin(), iend = names.end(); i != iend; @@ -262,7 +315,7 @@ command returns [CVC4::Command* cmd = NULL] } t = EXPR_MANAGER->mkFunctionType(sorts, t); } - PARSER_STATE->pushScope(); + PARSER_STATE->pushScope(true); for(std::vector<std::pair<std::string, CVC4::Type> >::const_iterator i = sortedVarNames.begin(), iend = sortedVarNames.end(); i != iend; @@ -275,7 +328,7 @@ command returns [CVC4::Command* cmd = NULL] // declare the name down here (while parsing term, signature // must not be extended with the name itself; no recursion // permitted) - Expr func = PARSER_STATE->mkFunction(name, t); + Expr func = PARSER_STATE->mkFunction(name, t, ExprManager::VAR_FLAG_DEFINED); $cmd = new DefineFunctionCommand(name, func, terms, expr); } | /* value query */ @@ -284,23 +337,29 @@ command returns [CVC4::Command* cmd = NULL] { $cmd = new GetValueCommand(terms); } | /* get-assignment */ GET_ASSIGNMENT_TOK { PARSER_STATE->checkThatLogicIsSet(); } - { cmd = new GetAssignmentCommand; } + { cmd = new GetAssignmentCommand(); } | /* assertion */ ASSERT_TOK { PARSER_STATE->checkThatLogicIsSet(); } term[expr, expr2] { cmd = new AssertCommand(expr); } - | /* checksat */ + | /* check-sat */ CHECKSAT_TOK { PARSER_STATE->checkThatLogicIsSet(); } - { cmd = new CheckSatCommand(MK_CONST(bool(true))); } + ( term[expr, expr2] + { if(PARSER_STATE->strictModeEnabled()) { + PARSER_STATE->parseError("Extended commands (such as check-sat with an argument) are not permitted while operating in strict compliance mode."); + } + } + | { expr = MK_CONST(bool(true)); } ) + { cmd = new CheckSatCommand(expr); } | /* get-assertions */ GET_ASSERTIONS_TOK { PARSER_STATE->checkThatLogicIsSet(); } - { cmd = new GetAssertionsCommand; } + { cmd = new GetAssertionsCommand(); } | /* get-proof */ GET_PROOF_TOK { PARSER_STATE->checkThatLogicIsSet(); } - { cmd = new GetProofCommand; } + { cmd = new GetProofCommand(); } | /* get-unsat-core */ GET_UNSAT_CORE_TOK { PARSER_STATE->checkThatLogicIsSet(); } - { cmd = new GetUnsatCoreCommand; } + { cmd = new GetUnsatCoreCommand(); } | /* push */ PUSH_TOK { PARSER_STATE->checkThatLogicIsSet(); } ( k=INTEGER_LITERAL @@ -314,7 +373,9 @@ command returns [CVC4::Command* cmd = NULL] CommandSequence* seq = new CommandSequence(); do { PARSER_STATE->pushScope(); - seq->addCommand(new PushCommand()); + Command* c = new PushCommand(); + c->setMuted(n > 1); + seq->addCommand(c); } while(--n > 0); cmd = seq; } @@ -322,12 +383,15 @@ command returns [CVC4::Command* cmd = NULL] | { if(PARSER_STATE->strictModeEnabled()) { PARSER_STATE->parseError("Strict compliance mode demands an integer to be provided to PUSH. Maybe you want (push 1)?"); } else { - cmd = new PushCommand; + cmd = new PushCommand(); } } ) | POP_TOK { PARSER_STATE->checkThatLogicIsSet(); } ( k=INTEGER_LITERAL { unsigned n = AntlrInput::tokenToUnsigned(k); + if(n > PARSER_STATE->scopeLevel()) { + PARSER_STATE->parseError("Attempted to pop above the top stack frame."); + } if(n == 0) { cmd = new EmptyCommand(); } else if(n == 1) { @@ -337,7 +401,9 @@ command returns [CVC4::Command* cmd = NULL] CommandSequence* seq = new CommandSequence(); do { PARSER_STATE->popScope(); - seq->addCommand(new PopCommand()); + Command* c = new PopCommand(); + c->setMuted(n > 1); + seq->addCommand(c); } while(--n > 0); cmd = seq; } @@ -345,11 +411,11 @@ command returns [CVC4::Command* cmd = NULL] | { if(PARSER_STATE->strictModeEnabled()) { PARSER_STATE->parseError("Strict compliance mode demands an integer to be provided to POP. Maybe you want (pop 1)?"); } else { - cmd = new PopCommand; + cmd = new PopCommand(); } } ) | EXIT_TOK - { cmd = new QuitCommand; } + { cmd = new QuitCommand(); } /* CVC4-extended SMT-LIB commands */ | extendedCommand[cmd] @@ -385,8 +451,8 @@ extendedCommand[CVC4::Command*& cmd] * --smtlib2 compliance mode. */ : DECLARE_DATATYPES_TOK { PARSER_STATE->checkThatLogicIsSet(); } { /* open a scope to keep the UnresolvedTypes contained */ - PARSER_STATE->pushScope(); } - LPAREN_TOK /* parametric sorts */ + PARSER_STATE->pushScope(true); } + LPAREN_TOK /* parametric sorts */ ( symbol[name,CHECK_UNDECLARED,SYM_SORT] { sorts.push_back( PARSER_STATE->mkSort(name) ); } )* @@ -396,7 +462,7 @@ extendedCommand[CVC4::Command*& cmd] cmd = new DatatypeDeclarationCommand(PARSER_STATE->mkMutualDatatypeTypes(dts)); } | /* get model */ GET_MODEL_TOK { PARSER_STATE->checkThatLogicIsSet(); } - { cmd = new GetModelCommand; } + { cmd = new GetModelCommand(); } | ECHO_TOK ( simpleSymbolicExpr[sexpr] { std::stringstream ss; @@ -465,7 +531,7 @@ extendedCommand[CVC4::Command*& cmd] ( symbol[name,CHECK_UNDECLARED,SYM_VARIABLE] { PARSER_STATE->checkUserSymbol(name); } term[e,e2] - { Expr func = PARSER_STATE->mkFunction(name, e.getType()); + { Expr func = PARSER_STATE->mkFunction(name, e.getType(), ExprManager::VAR_FLAG_DEFINED); $cmd = new DefineFunctionCommand(name, func, e); } | LPAREN_TOK @@ -474,7 +540,7 @@ extendedCommand[CVC4::Command*& cmd] sortedVarList[sortedVarNames] RPAREN_TOK { /* add variables to parser state before parsing term */ Debug("parser") << "define fun: '" << name << "'" << std::endl; - PARSER_STATE->pushScope(); + PARSER_STATE->pushScope(true); for(std::vector<std::pair<std::string, CVC4::Type> >::const_iterator i = sortedVarNames.begin(), iend = sortedVarNames.end(); i != iend; @@ -499,7 +565,7 @@ extendedCommand[CVC4::Command*& cmd] } t = EXPR_MANAGER->mkFunctionType(sorts, t); } - Expr func = PARSER_STATE->mkFunction(name, t); + Expr func = PARSER_STATE->mkFunction(name, t, ExprManager::VAR_FLAG_DEFINED); $cmd = new DefineFunctionCommand(name, func, terms, e); } ) @@ -521,7 +587,7 @@ rewriterulesCommand[CVC4::Command*& cmd] LPAREN_TOK sortedVarList[sortedVarNames] RPAREN_TOK { kind = CVC4::kind::RR_REWRITE; - PARSER_STATE->pushScope(); + PARSER_STATE->pushScope(true); for(std::vector<std::pair<std::string, CVC4::Type> >::const_iterator i = sortedVarNames.begin(), iend = sortedVarNames.end(); i != iend; @@ -562,7 +628,7 @@ rewriterulesCommand[CVC4::Command*& cmd] | rewritePropaKind[kind] LPAREN_TOK sortedVarList[sortedVarNames] RPAREN_TOK { - PARSER_STATE->pushScope(); + PARSER_STATE->pushScope(true); for(std::vector<std::pair<std::string, CVC4::Type> >::const_iterator i = sortedVarNames.begin(), iend = sortedVarNames.end(); i != iend; @@ -631,6 +697,7 @@ simpleSymbolicExprNoKeyword[CVC4::SExpr& sexpr] @declarations { CVC4::Kind k; std::string s; + std::vector<unsigned int> s_vec; } : INTEGER_LITERAL { sexpr = SExpr(Integer(AntlrInput::tokenText($INTEGER_LITERAL))); } @@ -638,8 +705,17 @@ simpleSymbolicExprNoKeyword[CVC4::SExpr& sexpr] { sexpr = SExpr(AntlrInput::tokenToRational($DECIMAL_LITERAL)); } | str[s] { sexpr = SExpr(s); } +// | LPAREN_TOK STRCST_TOK +// ( INTEGER_LITERAL { +// s_vec.push_back( atoi( AntlrInput::tokenText($INTEGER_LITERAL) ) + 65 ); +// } )* RPAREN_TOK +// { +// sexpr = SExpr( MK_CONST( ::CVC4::String(s_vec) ) ); +// } | symbol[s,CHECK_NONE,SYM_SORT] { sexpr = SExpr(SExpr::Keyword(s)); } + | tok=(ASSERT_TOK | CHECKSAT_TOK | DECLARE_FUN_TOK | DECLARE_SORT_TOK | DEFINE_FUN_TOK | DEFINE_SORT_TOK | GET_VALUE_TOK | GET_ASSIGNMENT_TOK | GET_ASSERTIONS_TOK | GET_PROOF_TOK | GET_UNSAT_CORE_TOK | EXIT_TOK | SET_LOGIC_TOK | SET_INFO_TOK | GET_INFO_TOK | SET_OPTION_TOK | GET_OPTION_TOK | PUSH_TOK | POP_TOK | DECLARE_DATATYPES_TOK | GET_MODEL_TOK | ECHO_TOK | REWRITE_RULE_TOK | REDUCTION_RULE_TOK | PROPAGATION_RULE_TOK | SIMPLIFY_TOK) + { sexpr = SExpr(SExpr::Keyword(AntlrInput::tokenText($tok))); } | builtinOp[k] { std::stringstream ss; ss << Expr::setlanguage(CVC4::language::output::LANG_SMTLIB_V2) << EXPR_MANAGER->mkConst(k); @@ -647,6 +723,11 @@ simpleSymbolicExprNoKeyword[CVC4::SExpr& sexpr] } ; +keyword[std::string& s] + : KEYWORD + { s = AntlrInput::tokenText($KEYWORD); } + ; + simpleSymbolicExpr[CVC4::SExpr& sexpr] : simpleSymbolicExprNoKeyword[sexpr] | KEYWORD @@ -671,7 +752,7 @@ symbolicExpr[CVC4::SExpr& sexpr] term[CVC4::Expr& expr, CVC4::Expr& expr2] @init { Debug("parser") << "term: " << AntlrInput::tokenText(LT(1)) << std::endl; - Kind kind; + Kind kind = kind::NULL_EXPR; Expr op; std::string name; std::vector<Expr> args; @@ -684,6 +765,7 @@ term[CVC4::Expr& expr, CVC4::Expr& expr2] std::hash_set<std::string, StringHashFunction> names; std::vector< std::pair<std::string, Expr> > binders; Type type; + std::string s; } : /* a built-in operator application */ LPAREN_TOK builtinOp[kind] termList[args,expr] RPAREN_TOK @@ -699,19 +781,38 @@ term[CVC4::Expr& expr, CVC4::Expr& expr2] (kind == CVC4::kind::AND || kind == CVC4::kind::OR) && args.size() == 1) { /* Unary AND/OR can be replaced with the argument. - It just so happens expr should already by the only argument. */ + * It just so happens expr should already be the only argument. */ assert( expr == args[0] ); } else if( CVC4::kind::isAssociative(kind) && args.size() > EXPR_MANAGER->maxArity(kind) ) { /* Special treatment for associative operators with lots of children */ - expr = EXPR_MANAGER->mkAssociative(kind,args); + expr = EXPR_MANAGER->mkAssociative(kind, args); } else if( kind == CVC4::kind::MINUS && args.size() == 1 ) { expr = MK_EXPR(CVC4::kind::UMINUS, args[0]); + } else if( ( kind == CVC4::kind::XOR || kind == CVC4::kind::MINUS ) && + args.size() > 2 ) { + /* left-associative, but CVC4 internally only supports 2 args */ + expr = args[0]; + for(size_t i = 1; i < args.size(); ++i) { + expr = MK_EXPR(kind, expr, args[i]); + } + } else if( kind == CVC4::kind::IMPLIES && args.size() > 2 ) { + /* right-associative, but CVC4 internally only supports 2 args */ + expr = args[args.size() - 1]; + for(size_t i = args.size() - 1; i > 0;) { + expr = MK_EXPR(kind, args[--i], expr); + } } else if( ( kind == CVC4::kind::IFF || kind == CVC4::kind::EQUAL || kind == CVC4::kind::LT || kind == CVC4::kind::GT || kind == CVC4::kind::LEQ || kind == CVC4::kind::GEQ ) && args.size() > 2 ) { - expr = MK_EXPR(CVC4::kind::CHAIN, MK_CONST(kind), args); + /* "chainable", but CVC4 internally only supports 2 args */ + expr = MK_EXPR(MK_CONST(Chain(kind)), args); + } else if( PARSER_STATE->strictModeEnabled() && kind == CVC4::kind::ABS && + args.size() == 1 && !args[0].getType().isInteger() ) { + /* first, check that ABS is even defined in this logic */ + PARSER_STATE->checkOperator(kind, args.size()); + PARSER_STATE->parseError("abs can only be applied to Int, not Real, while in strict SMT-LIB compliance mode"); } else { PARSER_STATE->checkOperator(kind, args.size()); expr = MK_EXPR(kind, args); @@ -726,7 +827,7 @@ term[CVC4::Expr& expr, CVC4::Expr& expr2] v.push_back(MK_EXPR( CVC4::kind::APPLY_TYPE_ASCRIPTION, MK_CONST(AscriptionType(dtc.getSpecializedConstructorType(type))), f.getOperator() )); v.insert(v.end(), f.begin(), f.end()); - f = MK_EXPR(CVC4::kind::APPLY_CONSTRUCTOR, v); + expr = MK_EXPR(CVC4::kind::APPLY_CONSTRUCTOR, v); } else { if(f.getType() != type) { PARSER_STATE->parseError("Type ascription not satisfied."); @@ -736,7 +837,7 @@ term[CVC4::Expr& expr, CVC4::Expr& expr2] | LPAREN_TOK quantOp[kind] LPAREN_TOK sortedVarList[sortedVarNames] RPAREN_TOK { - PARSER_STATE->pushScope(); + PARSER_STATE->pushScope(true); for(std::vector<std::pair<std::string, CVC4::Type> >::const_iterator i = sortedVarNames.begin(), iend = sortedVarNames.end(); i != iend; @@ -769,6 +870,10 @@ term[CVC4::Expr& expr, CVC4::Expr& expr2] expr = MK_EXPR(kind, args); } } + //| /* substring */ + //LPAREN_TOK STRSUB_TOK n1=INTEGER_LITERAL n2=INTEGER_LITERAL RPAREN_TOK + //{ + //} | /* A non-built-in function application */ LPAREN_TOK functionName[name, CHECK_DECLARED] @@ -801,11 +906,12 @@ term[CVC4::Expr& expr, CVC4::Expr& expr2] | /* An indexed function application */ LPAREN_TOK indexedFunctionName[op] termList[args,expr] RPAREN_TOK - { expr = MK_EXPR(op, args); } - + { expr = MK_EXPR(op, args); + PARSER_STATE->checkOperator(expr.getKind(), args.size()); + } | /* a let binding */ LPAREN_TOK LET_TOK LPAREN_TOK - { PARSER_STATE->pushScope(); } + { PARSER_STATE->pushScope(true); } ( LPAREN_TOK symbol[name,CHECK_NONE,SYM_VARIABLE] term[expr, f2] RPAREN_TOK // this is a parallel let, so we have to save up all the contributions // of the let and define them only later on @@ -918,6 +1024,9 @@ term[CVC4::Expr& expr, CVC4::Expr& expr2] std::string binString = AntlrInput::tokenTextSubstr($BINARY_LITERAL, 2); expr = MK_CONST( BitVector(binString, 2) ); } + | str[s] + { expr = MK_CONST( ::CVC4::String(s) ); } + // NOTE: Theory constants go here ; @@ -971,10 +1080,24 @@ attribute[CVC4::Expr& expr,CVC4::Expr& retExpr, std::string& attr] | ATTRIBUTE_NAMED_TOK symbolicExpr[sexpr] { attr = std::string(":named"); + if(!sexpr.isKeyword()) { + PARSER_STATE->parseError("improperly formed :named annotation"); + } std::string name = sexpr.getValue(); - // FIXME ensure expr is a closed subterm - // check that sexpr is a fresh function symbol - PARSER_STATE->checkDeclaration(name, CHECK_UNDECLARED, SYM_VARIABLE); + PARSER_STATE->checkUserSymbol(name); + // ensure expr is a closed subterm + std::set<Expr> freeVars; + if(!isClosed(expr, freeVars)) { + assert(!freeVars.empty()); + std::stringstream ss; + ss << ":named annotations can only name terms that are closed; this one contains free variables:"; + for(std::set<Expr>::const_iterator i = freeVars.begin(); i != freeVars.end(); ++i) { + ss << " " << *i; + } + PARSER_STATE->parseError(ss.str()); + } + // check that sexpr is a fresh function symbol, and reserve it + PARSER_STATE->reserveSymbolAtAssertionLevel(name); // define it Expr func = PARSER_STATE->mkFunction(name, expr.getType()); // bind name to expr with define-fun @@ -1003,6 +1126,13 @@ indexedFunctionName[CVC4::Expr& op] { op = MK_CONST(BitVectorRotateLeft(AntlrInput::tokenToUnsigned($n))); } | 'rotate_right' n=INTEGER_LITERAL { op = MK_CONST(BitVectorRotateRight(AntlrInput::tokenToUnsigned($n))); } + | DIVISIBLE_TOK n=INTEGER_LITERAL + { op = MK_CONST(Divisible(AntlrInput::tokenToUnsigned($n))); } + | INT2BV_TOK n=INTEGER_LITERAL + { op = MK_CONST(IntToBitVector(AntlrInput::tokenToUnsigned($n))); + if(PARSER_STATE->strictModeEnabled()) { + PARSER_STATE->parseError("bv2nat and int2bv are not part of SMT-LIB, and aren't available in SMT-LIB strict compliance mode"); + } } | badIndexedFunctionName ) RPAREN_TOK @@ -1075,6 +1205,10 @@ builtinOp[CVC4::Kind& kind] | DIV_TOK { $kind = CVC4::kind::DIVISION; } | INTS_DIV_TOK { $kind = CVC4::kind::INTS_DIVISION; } | INTS_MOD_TOK { $kind = CVC4::kind::INTS_MODULUS; } + | ABS_TOK { $kind = CVC4::kind::ABS; } + | IS_INT_TOK { $kind = CVC4::kind::IS_INTEGER; } + | TO_INT_TOK { $kind = CVC4::kind::TO_INTEGER; } + | TO_REAL_TOK { $kind = CVC4::kind::TO_REAL; } | SELECT_TOK { $kind = CVC4::kind::SELECT; } | STORE_TOK { $kind = CVC4::kind::STORE; } @@ -1109,6 +1243,22 @@ builtinOp[CVC4::Kind& kind] | BVSGT_TOK { $kind = CVC4::kind::BITVECTOR_SGT; } | BVSGE_TOK { $kind = CVC4::kind::BITVECTOR_SGE; } + | BV2NAT_TOK { $kind = CVC4::kind::BITVECTOR_TO_NAT; + if(PARSER_STATE->strictModeEnabled()) { + PARSER_STATE->parseError("bv2nat and int2bv are not part of SMT-LIB, and aren't available in SMT-LIB strict compliance mode"); + } } + + | STRCON_TOK { $kind = CVC4::kind::STRING_CONCAT; } + | STRLEN_TOK { $kind = CVC4::kind::STRING_LENGTH; } + | STRINRE_TOK { $kind = CVC4::kind::STRING_IN_REGEXP; } + | STRTORE_TOK { $kind = CVC4::kind::STRING_TO_REGEXP; } + | RECON_TOK { $kind = CVC4::kind::REGEXP_CONCAT; } + | REOR_TOK { $kind = CVC4::kind::REGEXP_OR; } + | REINTER_TOK { $kind = CVC4::kind::REGEXP_INTER; } + | RESTAR_TOK { $kind = CVC4::kind::REGEXP_STAR; } + | REPLUS_TOK { $kind = CVC4::kind::REGEXP_PLUS; } + | REOPT_TOK { $kind = CVC4::kind::REGEXP_OPT; } + // NOTE: Theory operators go here ; @@ -1200,6 +1350,9 @@ sortSymbol[CVC4::Type& t, CVC4::parser::DeclarationCheck check] if( numerals.size() != 1 ) { PARSER_STATE->parseError("Illegal bitvector type."); } + if(numerals.front() == 0) { + PARSER_STATE->parseError("Illegal bitvector size: 0"); + } t = EXPR_MANAGER->mkBitVectorType(numerals.front()); } else { std::stringstream ss; @@ -1209,21 +1362,20 @@ sortSymbol[CVC4::Type& t, CVC4::parser::DeclarationCheck check] } | LPAREN_TOK symbol[name,CHECK_NONE,SYM_SORT] sortList[args] RPAREN_TOK { - if( check == CHECK_DECLARED || PARSER_STATE->isDeclared(name, SYM_SORT)) { - if( name == "Array" ) { - if( args.size() != 2 ) { - PARSER_STATE->parseError("Illegal array type."); - } - t = EXPR_MANAGER->mkArrayType( args[0], args[1] ); - } else { - t = PARSER_STATE->getSort(name, args); + if(name == "Array") { + if(args.size() != 2) { + PARSER_STATE->parseError("Illegal array type."); } - }else{ - //make unresolved type + t = EXPR_MANAGER->mkArrayType( args[0], args[1] ); + } else if(check == CHECK_DECLARED || + PARSER_STATE->isDeclared(name, SYM_SORT)) { + t = PARSER_STATE->getSort(name, args); + } else { + // make unresolved type if(args.empty()) { t = PARSER_STATE->mkUnresolvedType(name); Debug("parser-param") << "param: make unres type " << name << std::endl; - }else{ + } else { t = PARSER_STATE->mkUnresolvedTypeConstructor(name,args); t = SortConstructorType(t).instantiate( args ); Debug("parser-param") << "param: make unres param type " << name << " " << args.size() << " " @@ -1271,6 +1423,8 @@ symbol[std::string& id, PARSER_STATE->checkDeclaration(id, check, type); } } + | UNTERMINATED_QUOTED_SYMBOL EOF + { PARSER_STATE->unexpectedEOF("unterminated |quoted| symbol"); } ; /** @@ -1294,7 +1448,7 @@ datatypeDef[std::vector<CVC4::Datatype>& datatypes, std::vector< CVC4::Type >& p * datatypes won't work, because this type will already be * "defined" as an unresolved type; don't worry, we check * below. */ - : symbol[id,CHECK_NONE,SYM_SORT] { PARSER_STATE->pushScope(); } + : symbol[id,CHECK_NONE,SYM_SORT] { PARSER_STATE->pushScope(true); } /* ( '[' symbol[id2,CHECK_UNDECLARED,SYM_SORT] { t = PARSER_STATE->mkSort(id2); params.push_back( t ); @@ -1344,7 +1498,8 @@ selector[CVC4::DatatypeConstructor& ctor] } : symbol[id,CHECK_UNDECLARED,SYM_SORT] sortSymbol[t,CHECK_NONE] { ctor.addArg(id, t); - Debug("parser-idt") << "selector: " << id.c_str() << std::endl; + Debug("parser-idt") << "selector: " << id.c_str() + << " of type " << t << std::endl; } ; @@ -1389,6 +1544,7 @@ DECLARE_PREDS_TOK : 'declare-preds'; DEFINE_TOK : 'define'; DECLARE_CONST_TOK : 'declare-const'; SIMPLIFY_TOK : 'simplify'; +INCLUDE_TOK : 'include'; // attributes ATTRIBUTE_PATTERN_TOK : ':pattern'; @@ -1407,6 +1563,7 @@ FORALL_TOK : 'forall'; GREATER_THAN_TOK : '>'; GREATER_THAN_EQUAL_TOK : '>='; IMPLIES_TOK : '=>'; +IS_INT_TOK : 'is_int'; LESS_THAN_TOK : '<'; LESS_THAN_EQUAL_TOK : '<='; MINUS_TOK : '-'; @@ -1419,10 +1576,15 @@ SELECT_TOK : 'select'; STAR_TOK : '*'; STORE_TOK : 'store'; // TILDE_TOK : '~'; +TO_INT_TOK : 'to_int'; +TO_REAL_TOK : 'to_real'; XOR_TOK : 'xor'; INTS_DIV_TOK : 'div'; INTS_MOD_TOK : 'mod'; +ABS_TOK : 'abs'; + +DIVISIBLE_TOK : 'divisible'; CONCAT_TOK : 'concat'; BVNOT_TOK : 'bvnot'; @@ -1453,6 +1615,22 @@ BVSLT_TOK : 'bvslt'; BVSLE_TOK : 'bvsle'; BVSGT_TOK : 'bvsgt'; BVSGE_TOK : 'bvsge'; +BV2NAT_TOK : 'bv2nat'; +INT2BV_TOK : 'int2bv'; + +//STRING +//STRCST_TOK : 'str.cst'; +STRCON_TOK : 'str.++'; +STRLEN_TOK : 'str.len'; +//STRSUB_TOK : 'str.sub' ; +STRINRE_TOK : 'str.in.re'; +STRTORE_TOK : 'str.to.re'; +RECON_TOK : 're.++'; +REOR_TOK : 're.or'; +REINTER_TOK : 're.itr'; +RESTAR_TOK : 're.*'; +REPLUS_TOK : 're.+'; +REOPT_TOK : 're.opt'; /** * A sequence of printable ASCII characters (except backslash) that starts @@ -1464,6 +1642,9 @@ BVSGE_TOK : 'bvsge'; QUOTED_SYMBOL : '|' ~('|' | '\\')* '|' ; +UNTERMINATED_QUOTED_SYMBOL + : '|' ~('|' | '\\')* + ; /** * Matches a keyword from the input. A keyword is a simple symbol prefixed diff --git a/src/parser/smt2/smt2.cpp b/src/parser/smt2/smt2.cpp index a7f7796cd..884502cf2 100644 --- a/src/parser/smt2/smt2.cpp +++ b/src/parser/smt2/smt2.cpp @@ -19,6 +19,11 @@ #include "parser/parser.h" #include "parser/smt1/smt1.h" #include "parser/smt2/smt2.h" +#include "parser/antlr_input.h" + +// ANTLR defines these, which is really bad! +#undef true +#undef false namespace CVC4 { namespace parser { @@ -36,7 +41,6 @@ void Smt2::addArithmeticOperators() { addOperator(kind::MINUS); addOperator(kind::UMINUS); addOperator(kind::MULT); - addOperator(kind::DIVISION); addOperator(kind::LT); addOperator(kind::LEQ); addOperator(kind::GT); @@ -80,6 +84,15 @@ void Smt2::addBitvectorOperators() { addOperator(kind::BITVECTOR_SIGN_EXTEND); addOperator(kind::BITVECTOR_ROTATE_LEFT); addOperator(kind::BITVECTOR_ROTATE_RIGHT); + + addOperator(kind::INT_TO_BITVECTOR); + addOperator(kind::BITVECTOR_TO_NAT); +} + +void Smt2::addStringOperators() { + addOperator(kind::STRING_CONCAT); + addOperator(kind::STRING_LENGTH); + //addOperator(kind::STRING_IN_REGEXP); } void Smt2::addTheory(Theory theory) { @@ -106,22 +119,36 @@ void Smt2::addTheory(Theory theory) { case THEORY_REALS_INTS: defineType("Real", getExprManager()->realType()); - // falling-through on purpose, to add Ints part of RealsInts + addOperator(kind::DIVISION); + addOperator(kind::TO_INTEGER); + addOperator(kind::IS_INTEGER); + addOperator(kind::TO_REAL); + // falling through on purpose, to add Ints part of Reals_Ints case THEORY_INTS: defineType("Int", getExprManager()->integerType()); addArithmeticOperators(); + addOperator(kind::INTS_DIVISION); + addOperator(kind::INTS_MODULUS); + addOperator(kind::ABS); + addOperator(kind::DIVISIBLE); break; case THEORY_REALS: defineType("Real", getExprManager()->realType()); addArithmeticOperators(); + addOperator(kind::DIVISION); break; case THEORY_BITVECTORS: addBitvectorOperators(); break; + case THEORY_STRINGS: + defineType("String", getExprManager()->stringType()); + addStringOperators(); + break; + case THEORY_QUANTIFIERS: break; @@ -138,135 +165,41 @@ bool Smt2::logicIsSet() { void Smt2::setLogic(const std::string& name) { d_logicSet = true; - d_logic = Smt1::toLogic(name); + d_logic = name; // Core theory belongs to every logic addTheory(THEORY_CORE); - switch(d_logic) { - case Smt1::QF_SAT: - /* No extra symbols necessary */ - break; - - case Smt1::QF_AX: - addTheory(THEORY_ARRAYS); - break; - - case Smt1::QF_IDL: - case Smt1::QF_LIA: - case Smt1::QF_NIA: - addTheory(THEORY_INTS); - break; - - case Smt1::QF_RDL: - case Smt1::QF_LRA: - case Smt1::QF_NRA: - addTheory(THEORY_REALS); - break; - - case Smt1::QF_UF: - addOperator(kind::APPLY_UF); - break; - - case Smt1::QF_UFIDL: - case Smt1::QF_UFLIA: - case Smt1::QF_UFNIA:// nonstandard logic - addTheory(THEORY_INTS); - addOperator(kind::APPLY_UF); - break; - - case Smt1::QF_UFLRA: - case Smt1::QF_UFNRA: - addTheory(THEORY_REALS); + if(d_logic.isTheoryEnabled(theory::THEORY_UF)) { addOperator(kind::APPLY_UF); - break; - - case Smt1::QF_UFLIRA:// nonstandard logic - case Smt1::QF_UFNIRA:// nonstandard logic - addOperator(kind::APPLY_UF); - addTheory(THEORY_INTS); - addTheory(THEORY_REALS); - break; - - case Smt1::QF_BV: - addTheory(THEORY_BITVECTORS); - break; - - case Smt1::QF_ABV: - addTheory(THEORY_ARRAYS); - addTheory(THEORY_BITVECTORS); - break; - - case Smt1::QF_UFBV: - addOperator(kind::APPLY_UF); - addTheory(THEORY_BITVECTORS); - break; + } - case Smt1::QF_AUFBV: - addOperator(kind::APPLY_UF); - addTheory(THEORY_ARRAYS); - addTheory(THEORY_BITVECTORS); - break; + if(d_logic.isTheoryEnabled(theory::THEORY_ARITH)) { + if(d_logic.areIntegersUsed()) { + if(d_logic.areRealsUsed()) { + addTheory(THEORY_REALS_INTS); + } else { + addTheory(THEORY_INTS); + } + } else if(d_logic.areRealsUsed()) { + addTheory(THEORY_REALS); + } + } - case Smt1::QF_AUFBVLIA: - addOperator(kind::APPLY_UF); + if(d_logic.isTheoryEnabled(theory::THEORY_ARRAY)) { addTheory(THEORY_ARRAYS); - addTheory(THEORY_BITVECTORS); - addTheory(THEORY_INTS); - break; + } - case Smt1::QF_AUFBVLRA: - addOperator(kind::APPLY_UF); - addTheory(THEORY_ARRAYS); + if(d_logic.isTheoryEnabled(theory::THEORY_BV)) { addTheory(THEORY_BITVECTORS); - addTheory(THEORY_REALS); - break; - - case Smt1::QF_AUFLIA: - addTheory(THEORY_ARRAYS); - addOperator(kind::APPLY_UF); - addTheory(THEORY_INTS); - break; - - case Smt1::QF_AUFLIRA: - addTheory(THEORY_ARRAYS); - addOperator(kind::APPLY_UF); - addTheory(THEORY_INTS); - addTheory(THEORY_REALS); - break; + } - case Smt1::ALL_SUPPORTED: - addTheory(THEORY_QUANTIFIERS); - /* fall through */ - case Smt1::QF_ALL_SUPPORTED: - addTheory(THEORY_ARRAYS); - addOperator(kind::APPLY_UF); - addTheory(THEORY_INTS); - addTheory(THEORY_REALS); - addTheory(THEORY_BITVECTORS); - break; + if(d_logic.isTheoryEnabled(theory::THEORY_STRINGS)) { + addTheory(THEORY_STRINGS); + } - case Smt1::AUFLIA: - case Smt1::AUFLIRA: - case Smt1::AUFNIRA: - case Smt1::LRA: - case Smt1::UFNIA: - case Smt1::UFNIRA: - case Smt1::UFLRA: - if(d_logic != Smt1::AUFLIA && d_logic != Smt1::UFNIA) { - addTheory(THEORY_REALS); - } - if(d_logic != Smt1::LRA) { - addOperator(kind::APPLY_UF); - if(d_logic != Smt1::UFLRA) { - addTheory(THEORY_INTS); - if(d_logic != Smt1::UFNIA && d_logic != Smt1::UFNIRA) { - addTheory(THEORY_ARRAYS); - } - } - } + if(d_logic.isQuantified()) { addTheory(THEORY_QUANTIFIERS); - break; } }/* Smt2::setLogic() */ @@ -297,5 +230,65 @@ void Smt2::checkThatLogicIsSet() { } } +/* The include are managed in the lexer but called in the parser */ +// Inspired by http://www.antlr3.org/api/C/interop.html + +static bool newInputStream(const std::string& filename, pANTLR3_LEXER lexer) { + Debug("parser") << "Including " << filename << std::endl; + // Create a new input stream and take advantage of built in stream stacking + // in C target runtime. + // + pANTLR3_INPUT_STREAM in; +#ifdef CVC4_ANTLR3_OLD_INPUT_STREAM + in = antlr3AsciiFileStreamNew((pANTLR3_UINT8) filename.c_str()); +#else /* CVC4_ANTLR3_OLD_INPUT_STREAM */ + in = antlr3FileStreamNew((pANTLR3_UINT8) filename.c_str(), ANTLR3_ENC_8BIT); +#endif /* CVC4_ANTLR3_OLD_INPUT_STREAM */ + if( in == NULL ) { + Debug("parser") << "Can't open " << filename << std::endl; + return false; + } + // Same thing as the predefined PUSHSTREAM(in); + lexer->pushCharStream(lexer, in); + // restart it + //lexer->rec->state->tokenStartCharIndex = -10; + //lexer->emit(lexer); + + // Note that the input stream is not closed when it EOFs, I don't bother + // to do it here, but it is up to you to track streams created like this + // and destroy them when the whole parse session is complete. Remember that you + // don't want to do this until all tokens have been manipulated all the way through + // your tree parsers etc as the token does not store the text it just refers + // back to the input stream and trying to get the text for it will abort if you + // close the input stream too early. + + //TODO what said before + return true; +} + +void Smt2::includeFile(const std::string& filename) { + // security for online version + if(!canIncludeFile()) { + parseError("include-file feature was disabled for this run."); + } + + // Get the lexer + AntlrInput* ai = static_cast<AntlrInput*>(getInput()); + pANTLR3_LEXER lexer = ai->getAntlr3Lexer(); + // get the name of the current stream "Does it work inside an include?" + const std::string inputName = ai->getInputStreamName(); + + // Find the directory of the current input file + std::string path; + size_t pos = inputName.rfind('/'); + if(pos != std::string::npos) { + path = std::string(inputName, 0, pos + 1); + } + path.append(filename); + if(!newInputStream(path, lexer)) { + parseError("Couldn't open include file `" + path + "'"); + } +} + }/* CVC4::parser namespace */ }/* CVC4 namespace */ diff --git a/src/parser/smt2/smt2.h b/src/parser/smt2/smt2.h index 5762ff5f9..cc46efe07 100644 --- a/src/parser/smt2/smt2.h +++ b/src/parser/smt2/smt2.h @@ -21,6 +21,7 @@ #include "parser/parser.h" #include "parser/smt1/smt1.h" +#include "theory/logic_info.h" #include "util/abstract_value.h" #include <sstream> @@ -43,11 +44,12 @@ public: THEORY_REALS, THEORY_REALS_INTS, THEORY_QUANTIFIERS, + THEORY_STRINGS }; private: bool d_logicSet; - Smt1::Logic d_logic; + LogicInfo d_logic; protected: Smt2(ExprManager* exprManager, Input* input, bool strictMode = false, bool parseOnly = false); @@ -84,6 +86,8 @@ public: } } + void includeFile(const std::string& filename); + bool isAbstractValue(const std::string& name) { return name.length() >= 2 && name[0] == '@' && name[1] != '0' && name.find_first_not_of("0123456789", 1) == std::string::npos; @@ -94,12 +98,38 @@ public: return getExprManager()->mkConst(AbstractValue(Integer(name.substr(1)))); } + /** + * Smt2 parser provides its own checkDeclaration, which does the + * same as the base, but with some more helpful errors. + */ + void checkDeclaration(const std::string& name, DeclarationCheck check, + SymbolType type = SYM_VARIABLE, + std::string notes = "") throw(ParserException) { + // if the symbol is something like "-1", we'll give the user a helpful + // syntax hint. (-1 is a valid identifier in SMT-LIB, NOT unary minus.) + if( check != CHECK_DECLARED || + name[0] != '-' || + name.find_first_not_of("0123456789", 1) != std::string::npos ) { + this->Parser::checkDeclaration(name, check, type, notes); + return; + } + + std::stringstream ss; + ss << notes + << "You may have intended to apply unary minus: `(- " + << name.substr(1) + << ")'\n"; + this->Parser::checkDeclaration(name, check, type, ss.str()); + } + private: void addArithmeticOperators(); void addBitvectorOperators(); + void addStringOperators(); + };/* class Smt2 */ }/* CVC4::parser namespace */ diff --git a/src/parser/tptp/Tptp.g b/src/parser/tptp/Tptp.g index 9e814b358..61e0999e9 100644 --- a/src/parser/tptp/Tptp.g +++ b/src/parser/tptp/Tptp.g @@ -27,7 +27,7 @@ options { // Only lookahead of <= k requested (disable for LL* parsing) // Note that CVC4's BoundedTokenBuffer requires a fixed k ! // If you change this k, change it also in tptp_input.cpp ! - k = 1; + k = 2; }/* options */ @header { @@ -102,6 +102,8 @@ using namespace CVC4::parser; #include "util/output.h" #include "util/rational.h" #include <vector> +#include <iterator> +#include <algorithm> using namespace CVC4; using namespace CVC4::parser; @@ -114,12 +116,12 @@ using namespace CVC4::parser; #define EXPR_MANAGER PARSER_STATE->getExprManager() #undef MK_EXPR #define MK_EXPR EXPR_MANAGER->mkExpr +#undef MK_EXPR_ASSOCIATIVE +#define MK_EXPR_ASSOCIATIVE EXPR_MANAGER->mkAssociative #undef MK_CONST #define MK_CONST EXPR_MANAGER->mkConst #define UNSUPPORTED PARSER_STATE->unimplementedFeature - - }/* parser::postinclude */ /** @@ -139,46 +141,81 @@ parseCommand returns [CVC4::Command* cmd = NULL] @declarations { Expr expr; Tptp::FormulaRole fr; - std::string name; - std::string incl_args; + std::string name, inclSymbol; } // : LPAREN_TOK c = command RPAREN_TOK { $cmd = c; } : CNF_TOK LPAREN_TOK nameN[name] COMMA_TOK formulaRole[fr] COMMA_TOK - { PARSER_STATE->cnf=true; PARSER_STATE->pushScope(); } + { PARSER_STATE->cnf = true; PARSER_STATE->fof = false; PARSER_STATE->pushScope(); } cnfFormula[expr] { PARSER_STATE->popScope(); std::vector<Expr> bvl = PARSER_STATE->getFreeVar(); - if(!bvl.empty()){ + if(!bvl.empty()) { expr = MK_EXPR(kind::FORALL,MK_EXPR(kind::BOUND_VAR_LIST,bvl),expr); }; } (COMMA_TOK anything*)? RPAREN_TOK DOT_TOK { - cmd = PARSER_STATE->makeCommand(fr,expr); + cmd = PARSER_STATE->makeCommand(fr, expr, /* cnf == */ true); } | FOF_TOK LPAREN_TOK nameN[name] COMMA_TOK formulaRole[fr] COMMA_TOK - { PARSER_STATE->cnf=false; } + { PARSER_STATE->cnf = false; PARSER_STATE->fof = true; } fofFormula[expr] (COMMA_TOK anything*)? RPAREN_TOK DOT_TOK { - cmd = PARSER_STATE->makeCommand(fr,expr); + cmd = PARSER_STATE->makeCommand(fr, expr, /* cnf == */ false); } + | TFF_TOK LPAREN_TOK nameN[name] COMMA_TOK + ( TYPE_TOK COMMA_TOK tffTypedAtom[cmd] + | formulaRole[fr] COMMA_TOK + { PARSER_STATE->cnf = false; PARSER_STATE->fof = false; } + tffFormula[expr] (COMMA_TOK anything*)? + { + cmd = PARSER_STATE->makeCommand(fr, expr, /* cnf == */ false); + } + ) RPAREN_TOK DOT_TOK | INCLUDE_TOK LPAREN_TOK unquotedFileName[name] - ( COMMA_TOK LBRACK_TOK nameN[incl_args] ( COMMA_TOK nameN[incl_args] )* RBRACK_TOK )? + ( COMMA_TOK LBRACK_TOK nameN[inclSymbol] + ( COMMA_TOK nameN[inclSymbol] )* RBRACK_TOK )? RPAREN_TOK DOT_TOK - { - PARSER_STATE->includeFile(name); - // The command of the included file will be produce at the new parseCommand call + { /* TODO - implement symbol filtering for file inclusion. + * the following removes duplicates and "all", just need to pass it + * through to includeFile() and implement it there. + std::sort(inclArgs.begin(), inclArgs.end()); + std::vector<std::string>::iterator it = + std::unique(inclArgs.begin(), inclArgs.end()); + inclArgs.resize(std::distance(inclArgs.begin(), it)); + it = std::lower_bound(inclArgs.begin(), inclArgs.end(), std::string("all")); + if(it != inclArgs.end() && *it == "all") { + inclArgs.erase(it); + } + */ + PARSER_STATE->includeFile(name /* , inclArgs */ ); + // The command of the included file will be produced at the next parseCommand() call cmd = new EmptyCommand("include::" + name); } | EOF { - PARSER_STATE->preemptCommand(new CheckSatCommand(MK_CONST(bool(true)))); + std::string filename = PARSER_STATE->getInput()->getInputStreamName(); + size_t i = filename.find_last_of('/'); + if(i != std::string::npos) { + filename = filename.substr(i + 1); + } + if(filename.substr(filename.length() - 2) == ".p") { + filename = filename.substr(0, filename.length() - 2); + } + CommandSequence* seq = new CommandSequence(); + seq->addCommand(new SetInfoCommand("name", filename)); + if(PARSER_STATE->hasConjecture()) { + seq->addCommand(new QueryCommand(MK_CONST(bool(false)))); + } else { + seq->addCommand(new CheckSatCommand()); + } + PARSER_STATE->preemptCommand(seq); cmd = NULL; } ; /* Parse a formula Role */ -formulaRole[CVC4::parser::Tptp::FormulaRole & role] +formulaRole[CVC4::parser::Tptp::FormulaRole& role] : LOWER_WORD { std::string r = AntlrInput::tokenText($LOWER_WORD); @@ -204,33 +241,30 @@ formulaRole[CVC4::parser::Tptp::FormulaRole & role] /* CNF */ /* It can parse a little more than the cnf grammar: false and true can appear. - Normally only false can appear and only at top level -*/ + * Normally only false can appear and only at top level. */ cnfFormula[CVC4::Expr& expr] - : - LPAREN_TOK disjunction[expr] RPAREN_TOK -| disjunction[expr] + : LPAREN_TOK cnfDisjunction[expr] RPAREN_TOK + | cnfDisjunction[expr] //| FALSE_TOK { expr = MK_CONST(bool(false)); } ; -disjunction[CVC4::Expr& expr] +cnfDisjunction[CVC4::Expr& expr] @declarations { std::vector<Expr> args; } - : - literal[expr] {args.push_back(expr); } ( OR_TOK literal[expr] {args.push_back(expr); } )* - { - if(args.size() > 1){ - expr = MK_EXPR(kind::OR,args); + : cnfLiteral[expr] { args.push_back(expr); } + ( OR_TOK cnfLiteral[expr] { args.push_back(expr); } )* + { if(args.size() > 1) { + expr = MK_EXPR_ASSOCIATIVE(kind::OR, args); } // else its already in the expr } ; -literal[CVC4::Expr& expr] +cnfLiteral[CVC4::Expr& expr] : atomicFormula[expr] - | NOT_TOK atomicFormula[expr] { expr = MK_EXPR(kind::NOT,expr); } -// | folInfixUnary[expr] + | NOT_TOK atomicFormula[expr] { expr = MK_EXPR(kind::NOT, expr); } +//| folInfixUnary[expr] ; atomicFormula[CVC4::Expr& expr] @@ -241,28 +275,32 @@ atomicFormula[CVC4::Expr& expr] bool equal; } : atomicWord[name] (LPAREN_TOK arguments[args] RPAREN_TOK)? - ( ( equalOp[equal] //equality/disequality between terms - { - PARSER_STATE->makeApplication(expr,name,args,true); - } - term[expr2] - { - expr = MK_EXPR(kind::EQUAL, expr, expr2); - if(!equal) expr = MK_EXPR(kind::NOT,expr); - } - ) - | //predicate - { - PARSER_STATE->makeApplication(expr,name,args,false); + ( equalOp[equal] term[expr2] + { // equality/disequality between terms + PARSER_STATE->makeApplication(expr, name, args, true); + expr = MK_EXPR(kind::EQUAL, expr, expr2); + if(!equal) expr = MK_EXPR(kind::NOT, expr); } - ) - | simpleTerm[expr] equalOp[equal] term[expr2] - { + | { // predicate + PARSER_STATE->makeApplication(expr, name, args, false); + } + ) + | definedFun[expr] LPAREN_TOK arguments[args] RPAREN_TOK + equalOp[equal] term[expr2] + { expr = EXPR_MANAGER->mkExpr(expr, args); + expr = MK_EXPR(kind::EQUAL, expr, expr2); + if(!equal) expr = MK_EXPR(kind::NOT, expr); + } + | (simpleTerm[expr] | letTerm[expr] | conditionalTerm[expr]) + equalOp[equal] term[expr2] + { // equality/disequality between terms expr = MK_EXPR(kind::EQUAL, expr, expr2); - if(!equal) expr = MK_EXPR(kind::NOT,expr); + if(!equal) expr = MK_EXPR(kind::NOT, expr); } + | definedPred[expr] LPAREN_TOK arguments[args] RPAREN_TOK + { expr = EXPR_MANAGER->mkExpr(expr, args); } | definedProp[expr] -; + ; //%----Using <plain_term> removes a reduce/reduce ambiguity in lex/yacc. //%----Note: "defined" means a word starting with one $ and "system" means $$. @@ -270,35 +308,170 @@ definedProp[CVC4::Expr& expr] : TRUE_TOK { expr = MK_CONST(bool(true)); } | FALSE_TOK { expr = MK_CONST(bool(false)); } ; + +definedPred[CVC4::Expr& expr] + : '$less' { expr = EXPR_MANAGER->operatorOf(CVC4::kind::LT); } + | '$lesseq' { expr = EXPR_MANAGER->operatorOf(CVC4::kind::LEQ); } + | '$greater' { expr = EXPR_MANAGER->operatorOf(CVC4::kind::GT); } + | '$greatereq' { expr = EXPR_MANAGER->operatorOf(CVC4::kind::GEQ); } + | '$is_rat' // all "real" are actually "rat" in CVC4 + { Expr n = EXPR_MANAGER->mkBoundVar("N", EXPR_MANAGER->realType()); + n = MK_EXPR(CVC4::kind::BOUND_VAR_LIST, n); + expr = MK_EXPR(CVC4::kind::LAMBDA, n, MK_CONST(bool(true))); + } + | '$is_int' { expr = EXPR_MANAGER->operatorOf(CVC4::kind::IS_INTEGER); } + | '$distinct' { expr = EXPR_MANAGER->operatorOf(CVC4::kind::DISTINCT); } + ; + +definedFun[CVC4::Expr& expr] +@declarations { + bool remainder = false; +} + : '$uminus' { expr = EXPR_MANAGER->operatorOf(CVC4::kind::UMINUS); } + | '$sum' { expr = EXPR_MANAGER->operatorOf(CVC4::kind::PLUS); } + | '$difference' { expr = EXPR_MANAGER->operatorOf(CVC4::kind::MINUS); } + | '$product' { expr = EXPR_MANAGER->operatorOf(CVC4::kind::MULT); } + | '$quotient' { expr = EXPR_MANAGER->operatorOf(CVC4::kind::DIVISION_TOTAL); } + | ( '$quotient_e' { remainder = false; } + | '$remainder_e' { remainder = true; } + ) + { Expr n = EXPR_MANAGER->mkBoundVar("N", EXPR_MANAGER->realType()); + Expr d = EXPR_MANAGER->mkBoundVar("D", EXPR_MANAGER->realType()); + Expr formals = MK_EXPR(CVC4::kind::BOUND_VAR_LIST, n, d); + expr = MK_EXPR(CVC4::kind::DIVISION_TOTAL, n, d); + expr = MK_EXPR(CVC4::kind::ITE, MK_EXPR(CVC4::kind::GEQ, d, MK_CONST(Rational(0))), + MK_EXPR(CVC4::kind::TO_INTEGER, expr), + MK_EXPR(CVC4::kind::UMINUS, MK_EXPR(CVC4::kind::TO_INTEGER, MK_EXPR(CVC4::kind::UMINUS, expr)))); + if(remainder) { + expr = MK_EXPR(CVC4::kind::MINUS, n, MK_EXPR(CVC4::kind::MULT, expr, d)); + } + expr = MK_EXPR(CVC4::kind::LAMBDA, formals, expr); + } + | ( '$quotient_t' { remainder = false; } + | '$remainder_t' { remainder = true; } + ) + { Expr n = EXPR_MANAGER->mkBoundVar("N", EXPR_MANAGER->realType()); + Expr d = EXPR_MANAGER->mkBoundVar("D", EXPR_MANAGER->realType()); + Expr formals = MK_EXPR(CVC4::kind::BOUND_VAR_LIST, n, d); + expr = MK_EXPR(CVC4::kind::DIVISION_TOTAL, n, d); + expr = MK_EXPR(CVC4::kind::ITE, MK_EXPR(CVC4::kind::GEQ, expr, MK_CONST(Rational(0))), + MK_EXPR(CVC4::kind::TO_INTEGER, expr), + MK_EXPR(CVC4::kind::UMINUS, MK_EXPR(CVC4::kind::TO_INTEGER, MK_EXPR(CVC4::kind::UMINUS, expr)))); + if(remainder) { + expr = MK_EXPR(CVC4::kind::MINUS, n, MK_EXPR(CVC4::kind::MULT, expr, d)); + } + expr = MK_EXPR(CVC4::kind::LAMBDA, formals, expr); + } + | ( '$quotient_f' { remainder = false; } + | '$remainder_f' { remainder = true; } + ) + { Expr n = EXPR_MANAGER->mkBoundVar("N", EXPR_MANAGER->realType()); + Expr d = EXPR_MANAGER->mkBoundVar("D", EXPR_MANAGER->realType()); + Expr formals = MK_EXPR(CVC4::kind::BOUND_VAR_LIST, n, d); + expr = MK_EXPR(CVC4::kind::DIVISION_TOTAL, n, d); + expr = MK_EXPR(CVC4::kind::TO_INTEGER, expr); + if(remainder) { + expr = MK_EXPR(CVC4::kind::MINUS, n, MK_EXPR(CVC4::kind::MULT, expr, d)); + } + expr = MK_EXPR(CVC4::kind::LAMBDA, formals, expr); + } + | '$floor' { expr = EXPR_MANAGER->operatorOf(CVC4::kind::TO_INTEGER); } + | '$ceiling' + { Expr n = EXPR_MANAGER->mkBoundVar("N", EXPR_MANAGER->realType()); + Expr formals = MK_EXPR(CVC4::kind::BOUND_VAR_LIST, n); + expr = MK_EXPR(CVC4::kind::UMINUS, MK_EXPR(CVC4::kind::TO_INTEGER, MK_EXPR(CVC4::kind::UMINUS, n))); + expr = MK_EXPR(CVC4::kind::LAMBDA, formals, expr); + } + | '$truncate' + { Expr n = EXPR_MANAGER->mkBoundVar("N", EXPR_MANAGER->realType()); + Expr formals = MK_EXPR(CVC4::kind::BOUND_VAR_LIST, n); + expr = MK_EXPR(CVC4::kind::ITE, MK_EXPR(CVC4::kind::GEQ, n, MK_CONST(Rational(0))), + MK_EXPR(CVC4::kind::TO_INTEGER, n), + MK_EXPR(CVC4::kind::UMINUS, MK_EXPR(CVC4::kind::TO_INTEGER, MK_EXPR(CVC4::kind::UMINUS, n)))); + expr = MK_EXPR(CVC4::kind::LAMBDA, formals, expr); + } + | '$round' + { Expr n = EXPR_MANAGER->mkBoundVar("N", EXPR_MANAGER->realType()); + Expr formals = MK_EXPR(CVC4::kind::BOUND_VAR_LIST, n); + Expr decPart = MK_EXPR(CVC4::kind::MINUS, n, MK_EXPR(CVC4::kind::TO_INTEGER, n)); + expr = MK_EXPR(CVC4::kind::ITE, MK_EXPR(CVC4::kind::LT, decPart, MK_CONST(Rational(1, 2))), + // if decPart < 0.5, round down + MK_EXPR(CVC4::kind::TO_INTEGER, n), + MK_EXPR(CVC4::kind::ITE, MK_EXPR(CVC4::kind::GT, decPart, MK_CONST(Rational(1, 2))), + // if decPart > 0.5, round up + MK_EXPR(CVC4::kind::TO_INTEGER, MK_EXPR(CVC4::kind::PLUS, n, MK_CONST(Rational(1)))), + // if decPart == 0.5, round to nearest even integer: + // result is: to_int(n/2 + .5) * 2 + MK_EXPR(CVC4::kind::MULT, MK_EXPR(CVC4::kind::TO_INTEGER, MK_EXPR(CVC4::kind::PLUS, MK_EXPR(CVC4::kind::DIVISION_TOTAL, n, MK_CONST(Rational(2))), MK_CONST(Rational(1, 2)))), MK_CONST(Rational(2))))); + expr = MK_EXPR(CVC4::kind::LAMBDA, formals, expr); + } + | '$to_int' { expr = EXPR_MANAGER->operatorOf(CVC4::kind::TO_INTEGER); } + | '$to_rat' { expr = EXPR_MANAGER->operatorOf(CVC4::kind::TO_REAL); } + | '$to_real' { expr = EXPR_MANAGER->operatorOf(CVC4::kind::TO_REAL); } + ; + //%----Pure CNF should not use $true or $false in problems, and use $false only //%----at the roots of a refutation. -equalOp[bool & equal] - : EQUAL_TOK {equal = true;} - | DISEQUAL_TOK {equal = false;} +equalOp[bool& equal] + : EQUAL_TOK { equal = true; } + | DISEQUAL_TOK { equal = false; } ; term[CVC4::Expr& expr] - : functionTerm[expr] + : functionTerm[expr] + | conditionalTerm[expr] | simpleTerm[expr] -// | conditionalTerm -// | let_term + | letTerm[expr] + ; + +letTerm[CVC4::Expr& expr] +@declarations { + CVC4::Expr lhs, rhs; +} + : '$let_ft' LPAREN_TOK { PARSER_STATE->pushScope(); } + tffLetFormulaDefn[lhs, rhs] COMMA_TOK + term[expr] + { PARSER_STATE->popScope(); + expr = expr.substitute(lhs, rhs); + } + RPAREN_TOK + | '$let_tt' LPAREN_TOK { PARSER_STATE->pushScope(); } + tffLetTermDefn[lhs, rhs] COMMA_TOK + term[expr] + { PARSER_STATE->popScope(); + expr = expr.substitute(lhs, rhs); + } + RPAREN_TOK ; /* Not an application */ simpleTerm[CVC4::Expr& expr] : variable[expr] | NUMBER { expr = PARSER_STATE->d_tmp_expr; } - | DISTINCT_OBJECT { expr = PARSER_STATE->convertStrToUnsorted( - AntlrInput::tokenText($DISTINCT_OBJECT)); } -; + | DISTINCT_OBJECT { expr = PARSER_STATE->convertStrToUnsorted(AntlrInput::tokenText($DISTINCT_OBJECT)); } + ; functionTerm[CVC4::Expr& expr] - : plainTerm[expr] // | <defined_term> | <system_term> +@declarations { + std::vector<CVC4::Expr> args; +} + : plainTerm[expr] + | definedFun[expr] LPAREN_TOK arguments[args] RPAREN_TOK + { expr = EXPR_MANAGER->mkExpr(expr, args); } +// | <system_term> + ; + +conditionalTerm[CVC4::Expr& expr] +@declarations { + CVC4::Expr expr2, expr3; +} + : '$ite_t' LPAREN_TOK tffLogicFormula[expr] COMMA_TOK term[expr2] COMMA_TOK term[expr3] RPAREN_TOK + { expr = EXPR_MANAGER->mkExpr(CVC4::kind::ITE, expr, expr2, expr3); } ; plainTerm[CVC4::Expr& expr] -@declarations{ +@declarations { std::string name; std::vector<Expr> args; } @@ -308,19 +481,19 @@ plainTerm[CVC4::Expr& expr] } ; -arguments[std::vector<CVC4::Expr> & args] -@declarations{ +arguments[std::vector<CVC4::Expr>& args] +@declarations { Expr expr; } : term[expr] { args.push_back(expr); } ( COMMA_TOK term[expr] { args.push_back(expr); } )* ; -variable[CVC4::Expr & expr] +variable[CVC4::Expr& expr] : UPPER_WORD { std::string name = AntlrInput::tokenText($UPPER_WORD); - if(!PARSER_STATE->cnf || PARSER_STATE->isDeclared(name)){ + if(!PARSER_STATE->cnf || PARSER_STATE->isDeclared(name)) { expr = PARSER_STATE->getVariable(name); } else { expr = PARSER_STATE->mkBoundVar(name, PARSER_STATE->d_unsorted); @@ -331,18 +504,18 @@ variable[CVC4::Expr & expr] /*******/ /* FOF */ -fofFormula[CVC4::Expr & expr] : fofLogicFormula[expr] ; +fofFormula[CVC4::Expr& expr] : fofLogicFormula[expr] ; -fofLogicFormula[CVC4::Expr & expr] -@declarations{ +fofLogicFormula[CVC4::Expr& expr] +@declarations { tptp::NonAssoc na; std::vector< Expr > args; Expr expr2; } : fofUnitaryFormula[expr] - ( //Non Assoc <=> <~> ~& ~| + ( // Non-associative: <=> <~> ~& ~| ( fofBinaryNonAssoc[na] fofUnitaryFormula[expr2] - { switch(na){ + { switch(na) { case tptp::NA_IFF: expr = MK_EXPR(kind::IFF,expr,expr2); break; @@ -364,21 +537,21 @@ fofLogicFormula[CVC4::Expr & expr] } } ) - | //And & + | // N-ary and & ( { args.push_back(expr); } ( AND_TOK fofUnitaryFormula[expr] { args.push_back(expr); } )+ - { expr = MK_EXPR(kind::AND,args); } + { expr = MK_EXPR_ASSOCIATIVE(kind::AND, args); } ) - | //Or | + | // N-ary or | ( { args.push_back(expr); } ( OR_TOK fofUnitaryFormula[expr] { args.push_back(expr); } )+ - { expr = MK_EXPR(kind::OR,args); } + { expr = MK_EXPR_ASSOCIATIVE(kind::OR, args); } ) - )? + )? ; -fofUnitaryFormula[CVC4::Expr & expr] -@declarations{ +fofUnitaryFormula[CVC4::Expr& expr] +@declarations { Kind kind; std::vector< Expr > bv; } @@ -389,20 +562,20 @@ fofUnitaryFormula[CVC4::Expr & expr] folQuantifier[kind] LBRACK_TOK {PARSER_STATE->pushScope();} ( bindvariable[expr] { bv.push_back(expr); } ( COMMA_TOK bindvariable[expr] { bv.push_back(expr); } )* ) RBRACK_TOK - COLON_TOK fofUnitaryFormula[expr] - { PARSER_STATE->popScope(); - expr = MK_EXPR(kind, MK_EXPR(kind::BOUND_VAR_LIST, bv), expr); - } ; + COLON_TOK fofUnitaryFormula[expr] + { PARSER_STATE->popScope(); + expr = MK_EXPR(kind, MK_EXPR(kind::BOUND_VAR_LIST, bv), expr); + } + ; -bindvariable[CVC4::Expr & expr] +bindvariable[CVC4::Expr& expr] : UPPER_WORD - { - std::string name = AntlrInput::tokenText($UPPER_WORD); + { std::string name = AntlrInput::tokenText($UPPER_WORD); expr = PARSER_STATE->mkBoundVar(name, PARSER_STATE->d_unsorted); } - ; + ; -fofBinaryNonAssoc[CVC4::parser::tptp::NonAssoc & na] +fofBinaryNonAssoc[CVC4::parser::tptp::NonAssoc& na] : IFF_TOK { na = tptp::NA_IFF; } | REVIFF_TOK { na = tptp::NA_REVIFF; } | REVOR_TOK { na = tptp::NA_REVOR; } @@ -411,11 +584,229 @@ fofBinaryNonAssoc[CVC4::parser::tptp::NonAssoc & na] | REVIMPLIES_TOK { na = tptp::NA_REVIMPLIES; } ; -folQuantifier[CVC4::Kind & kind] +folQuantifier[CVC4::Kind& kind] : BANG_TOK { kind = kind::FORALL; } | MARK_TOK { kind = kind::EXISTS; } ; +/*******/ +/* TFF */ +tffFormula[CVC4::Expr& expr] : tffLogicFormula[expr]; + +tffTypedAtom[CVC4::Command*& cmd] +@declarations { + CVC4::Expr expr; + CVC4::Type type; + std::string name; +} + : LPAREN_TOK tffTypedAtom[cmd] RPAREN_TOK + | nameN[name] COLON_TOK + ( '$tType' + { if(PARSER_STATE->isDeclared(name, SYM_SORT)) { + // duplicate declaration is fine, they're compatible + cmd = new EmptyCommand("compatible redeclaration of sort " + name); + } else if(PARSER_STATE->isDeclared(name, SYM_VARIABLE)) { + // error: cannot be both sort and constant + PARSER_STATE->parseError("Symbol `" + name + "' previously declared as a constant; cannot also be a sort"); + } else { + // as yet, it's undeclared + Type type = PARSER_STATE->mkSort(name); + cmd = new DeclareTypeCommand(name, 0, type); + } + } + | parseType[type] + { if(PARSER_STATE->isDeclared(name, SYM_SORT)) { + // error: cannot be both sort and constant + PARSER_STATE->parseError("Symbol `" + name + "' previously declared as a sort"); + cmd = new EmptyCommand("compatible redeclaration of sort " + name); + } else if(PARSER_STATE->isDeclared(name, SYM_VARIABLE)) { + if(type == PARSER_STATE->getVariable(name).getType()) { + // duplicate declaration is fine, they're compatible + cmd = new EmptyCommand("compatible redeclaration of constant " + name); + } else { + // error: sorts incompatible + PARSER_STATE->parseError("Symbol `" + name + "' declared previously with a different sort"); + } + } else { + // as yet, it's undeclared + CVC4::Expr expr; + if(type.isFunction()) { + expr = PARSER_STATE->mkFunction(name, type); + } else { + expr = PARSER_STATE->mkVar(name, type); + } + cmd = new DeclareFunctionCommand(name, expr, type); + } + } + ) + ; + +tffLogicFormula[CVC4::Expr& expr] +@declarations { + tptp::NonAssoc na; + std::vector< Expr > args; + Expr expr2; +} + : tffUnitaryFormula[expr] + ( // Non Assoc <=> <~> ~& ~| + ( fofBinaryNonAssoc[na] tffUnitaryFormula[expr2] + { switch(na) { + case tptp::NA_IFF: + expr = MK_EXPR(kind::IFF,expr,expr2); + break; + case tptp::NA_REVIFF: + expr = MK_EXPR(kind::XOR,expr,expr2); + break; + case tptp::NA_IMPLIES: + expr = MK_EXPR(kind::IMPLIES,expr,expr2); + break; + case tptp::NA_REVIMPLIES: + expr = MK_EXPR(kind::IMPLIES,expr2,expr); + break; + case tptp::NA_REVOR: + expr = MK_EXPR(kind::NOT,MK_EXPR(kind::OR,expr,expr2)); + break; + case tptp::NA_REVAND: + expr = MK_EXPR(kind::NOT,MK_EXPR(kind::AND,expr,expr2)); + break; + } + } + ) + | // And & + ( { args.push_back(expr); } + ( AND_TOK tffUnitaryFormula[expr] { args.push_back(expr); } )+ + { expr = MK_EXPR(kind::AND,args); } + ) + | // Or | + ( { args.push_back(expr); } + ( OR_TOK tffUnitaryFormula[expr] { args.push_back(expr); } )+ + { expr = MK_EXPR(kind::OR,args); } + ) + )? + ; + +tffUnitaryFormula[CVC4::Expr& expr] +@declarations { + Kind kind; + std::vector< Expr > bv; + Expr lhs, rhs; +} + : atomicFormula[expr] + | LPAREN_TOK tffLogicFormula[expr] RPAREN_TOK + | NOT_TOK tffUnitaryFormula[expr] { expr = MK_EXPR(kind::NOT,expr); } + | // Quantified + folQuantifier[kind] LBRACK_TOK {PARSER_STATE->pushScope();} + ( tffbindvariable[expr] { bv.push_back(expr); } + ( COMMA_TOK tffbindvariable[expr] { bv.push_back(expr); } )* ) RBRACK_TOK + COLON_TOK tffUnitaryFormula[expr] + { PARSER_STATE->popScope(); + expr = MK_EXPR(kind, MK_EXPR(kind::BOUND_VAR_LIST, bv), expr); + } + | '$ite_f' LPAREN_TOK tffLogicFormula[expr] COMMA_TOK tffLogicFormula[lhs] COMMA_TOK tffLogicFormula[rhs] RPAREN_TOK + { expr = EXPR_MANAGER->mkExpr(CVC4::kind::ITE, expr, lhs, rhs); } + | '$let_tf' LPAREN_TOK { PARSER_STATE->pushScope(); } + tffLetTermDefn[lhs, rhs] COMMA_TOK + tffFormula[expr] + { PARSER_STATE->popScope(); + expr = expr.substitute(lhs, rhs); + } + RPAREN_TOK + | '$let_ff' LPAREN_TOK { PARSER_STATE->pushScope(); } + tffLetFormulaDefn[lhs, rhs] COMMA_TOK + tffFormula[expr] + { PARSER_STATE->popScope(); + expr = expr.substitute(lhs, rhs); + } + RPAREN_TOK + ; + +tffLetTermDefn[CVC4::Expr& lhs, CVC4::Expr& rhs] +@declarations { + std::vector<CVC4::Expr> bvlist; +} + : (BANG_TOK LBRACK_TOK tffVariableList[bvlist] RBRACK_TOK COLON_TOK)* + tffLetTermBinding[bvlist, lhs, rhs] + ; + +tffLetTermBinding[std::vector<CVC4::Expr>& bvlist, CVC4::Expr& lhs, CVC4::Expr& rhs] + : term[lhs] EQUAL_TOK term[rhs] + { PARSER_STATE->checkLetBinding(bvlist, lhs, rhs, false); + rhs = MK_EXPR(CVC4::kind::LAMBDA, MK_EXPR(CVC4::kind::BOUND_VAR_LIST, lhs.getChildren()), rhs); + lhs = lhs.getOperator(); + } + | LPAREN_TOK tffLetTermBinding[bvlist, lhs, rhs] RPAREN_TOK + ; + +tffLetFormulaDefn[CVC4::Expr& lhs, CVC4::Expr& rhs] +@declarations { + std::vector<CVC4::Expr> bvlist; +} + : (BANG_TOK LBRACK_TOK tffVariableList[bvlist] RBRACK_TOK COLON_TOK)* + tffLetFormulaBinding[bvlist, lhs, rhs] + ; + +tffLetFormulaBinding[std::vector<CVC4::Expr>& bvlist, CVC4::Expr& lhs, CVC4::Expr& rhs] + : atomicFormula[lhs] IFF_TOK tffUnitaryFormula[rhs] + { PARSER_STATE->checkLetBinding(bvlist, lhs, rhs, true); + rhs = MK_EXPR(CVC4::kind::LAMBDA, MK_EXPR(CVC4::kind::BOUND_VAR_LIST, lhs.getChildren()), rhs); + lhs = lhs.getOperator(); + } + | LPAREN_TOK tffLetFormulaBinding[bvlist, lhs, rhs] RPAREN_TOK + ; + +tffbindvariable[CVC4::Expr& expr] +@declarations { + CVC4::Type type = PARSER_STATE->d_unsorted; +} + : UPPER_WORD + ( COLON_TOK parseType[type] )? + { std::string name = AntlrInput::tokenText($UPPER_WORD); + expr = PARSER_STATE->mkBoundVar(name, type); + } + ; + +// bvlist is accumulative; it can already contain elements +// on the way in, which are left undisturbed +tffVariableList[std::vector<CVC4::Expr>& bvlist] +@declarations { + CVC4::Expr e; +} + : tffbindvariable[e] { bvlist.push_back(e); } + ( COMMA_TOK tffbindvariable[e] { bvlist.push_back(e); } )* + ; + +parseType[CVC4::Type& type] +@declarations { + std::vector<CVC4::Type> v; +} + : simpleType[type] + | ( simpleType[type] { v.push_back(type); } + | LPAREN_TOK simpleType[type] { v.push_back(type); } + ( TIMES_TOK simpleType[type] { v.push_back(type); } )+ + RPAREN_TOK + ) + GREATER_TOK simpleType[type] + { v.push_back(type); + type = EXPR_MANAGER->mkFunctionType(v); + } + ; + +// non-function types +simpleType[CVC4::Type& type] + : DEFINED_SYMBOL + { std::string s = AntlrInput::tokenText($DEFINED_SYMBOL); + if(s == "\$i") type = PARSER_STATE->d_unsorted; + else if(s == "\$o") type = EXPR_MANAGER->booleanType(); + else if(s == "\$int") type = EXPR_MANAGER->integerType(); + else if(s == "\$rat") type = EXPR_MANAGER->realType(); + else if(s == "\$real") type = EXPR_MANAGER->realType(); + else if(s == "\$tType") PARSER_STATE->parseError("Type of types `\$tType' cannot be used here"); + else PARSER_STATE->parseError("unknown defined type `" + s + "'"); + } + | LOWER_WORD + { type = PARSER_STATE->getSort(AntlrInput::tokenText($LOWER_WORD)); } + ; + /***********************************************/ /* Anything well parenthesis */ @@ -447,6 +838,7 @@ anything | FOF_TOK | THF_TOK | TFF_TOK + | TYPE_TOK | INCLUDE_TOK | DISTINCT_OBJECT | UPPER_WORD @@ -467,6 +859,8 @@ LBRACK_TOK : '['; RBRACK_TOK : ']'; COLON_TOK : ':'; +GREATER_TOK : '>'; + //operator OR_TOK : '|'; NOT_TOK : '~'; @@ -494,6 +888,7 @@ CNF_TOK : 'cnf'; FOF_TOK : 'fof'; THF_TOK : 'thf'; TFF_TOK : 'tff'; +TYPE_TOK : 'type'; INCLUDE_TOK : 'include'; //Other defined symbols, must be defined after all the other @@ -533,30 +928,30 @@ UPPER_WORD : UPPER_ALPHA ALPHA_NUMERIC*; LOWER_WORD : LOWER_ALPHA ALPHA_NUMERIC*; /* filename */ -unquotedFileName[std::string & name] /* Beware fileName identifier is used by the backend ... */ +unquotedFileName[std::string& name] /* Beware fileName identifier is used by the backend ... */ : (s=LOWER_WORD_SINGLE_QUOTED | s=SINGLE_QUOTED) { name = AntlrInput::tokenText($s); name = name.substr(1, name.size() - 2 ); }; /* axiom name */ -nameN[std::string & name] +nameN[std::string& name] : atomicWord[name] | NUMBER { name = AntlrInput::tokenText($NUMBER); } ; /* atomic word everything (fof, cnf, thf, tff, include must not be keyword at this position ) */ -atomicWord[std::string & name] +atomicWord[std::string& name] : FOF_TOK { name = "fof"; } | CNF_TOK { name = "cnf"; } | THF_TOK { name = "thf"; } | TFF_TOK { name = "tff"; } + | TYPE_TOK { name = "type"; } | INCLUDE_TOK { name = "include"; } | LOWER_WORD { name = AntlrInput::tokenText($LOWER_WORD); } - | LOWER_WORD_SINGLE_QUOTED //the lower word single quoted are considered without quote - { - /* strip off the quotes */ - name = AntlrInput::tokenTextSubstr($LOWER_WORD_SINGLE_QUOTED,1, + | LOWER_WORD_SINGLE_QUOTED // the lower word single quoted are considered without quote + { /* strip off the quotes */ + name = AntlrInput::tokenTextSubstr($LOWER_WORD_SINGLE_QUOTED, 1 , ($LOWER_WORD_SINGLE_QUOTED->stop - $LOWER_WORD_SINGLE_QUOTED->start) - 1); } | SINGLE_QUOTED {name = AntlrInput::tokenText($SINGLE_QUOTED); }; //for the others the quote remains @@ -565,35 +960,34 @@ atomicWord[std::string & name] /* Rational */ fragment DOT : '.'; -fragment EXPONENT : 'E'|'e'; +fragment EXPONENT : 'E' | 'e'; fragment SLASH : '/'; fragment DECIMAL : NUMERIC+; -/* Currently we put all in the rationnal type */ -fragment SIGN[bool & pos] : PLUS_TOK | MINUS_TOK { pos = false; } ; - +/* Currently we put all in the rational type */ +fragment SIGN[bool& pos] + : PLUS_TOK { pos = true; } + | MINUS_TOK { pos = false; } + ; NUMBER -@declarations{ +@declarations { bool pos = true; bool posE = true; } - : - ( SIGN[pos]? num=DECIMAL - { - Integer i (AntlrInput::tokenText($num)); - Rational r = ( pos ? i : -i ); - PARSER_STATE->d_tmp_expr = MK_CONST(r); - } - | SIGN[pos]? num=DECIMAL DOT den=DECIMAL (EXPONENT SIGN[posE]? e=DECIMAL)? - { - std::string snum = AntlrInput::tokenText($num); + : ( SIGN[pos]? num=DECIMAL + { Integer i(AntlrInput::tokenText($num)); + Rational r = pos ? i : -i; + PARSER_STATE->d_tmp_expr = MK_CONST(r); + } + | SIGN[pos]? num=DECIMAL DOT den=DECIMAL (EXPONENT SIGN[posE]? e=DECIMAL)? + { std::string snum = AntlrInput::tokenText($num); std::string sden = AntlrInput::tokenText($den); /* compute the numerator */ - Integer inum( snum + sden ); + Integer inum(snum + sden); // The sign - inum = pos ? inum : - inum; + inum = pos ? inum : -inum; // The exponent size_t exp = ($e == NULL ? 0 : AntlrInput::tokenToUnsigned($e)); // Decimal part @@ -601,26 +995,25 @@ NUMBER /* multiply it by 10 raised to the exponent reduced by the * number of decimal place in den (dec) */ Rational r; - if( !posE ) r = Rational(inum, Integer(10).pow(exp + dec)); - else if( exp == dec ) r = Rational(inum); - else if( exp > dec ) r = Rational(inum * Integer(10).pow(exp - dec)); + if(!posE) r = Rational(inum, Integer(10).pow(exp + dec)); + else if(exp == dec) r = Rational(inum); + else if(exp > dec) r = Rational(inum * Integer(10).pow(exp - dec)); else r = Rational(inum, Integer(10).pow(dec - exp)); - PARSER_STATE->d_tmp_expr = MK_CONST( r ); + PARSER_STATE->d_tmp_expr = MK_CONST(r); + } + | SIGN[pos]? num=DECIMAL SLASH den=DECIMAL + { Integer inum(AntlrInput::tokenText($num)); + Integer iden(AntlrInput::tokenText($den)); + PARSER_STATE->d_tmp_expr = MK_CONST(Rational(pos ? inum : -inum, iden)); + } + ) + { if(PARSER_STATE->cnf || PARSER_STATE->fof) { + // We're in an unsorted context, so put a conversion around it + PARSER_STATE->d_tmp_expr = PARSER_STATE->convertRatToUnsorted( PARSER_STATE->d_tmp_expr ); } - | SIGN[pos]? num=DECIMAL SLASH den=DECIMAL - { - Integer inum(AntlrInput::tokenText($num)); - Integer iden(AntlrInput::tokenText($den)); - PARSER_STATE->d_tmp_expr = MK_CONST( - Rational(pos ? inum : -inum, iden)); - } - ) { - //Put a convertion around it - PARSER_STATE->d_tmp_expr = PARSER_STATE->convertRatToUnsorted( PARSER_STATE->d_tmp_expr ); } ; - /** * Matches the comments and ignores them */ @@ -629,5 +1022,3 @@ COMMENT | '/*' ( options {greedy=false;} : . )* '*/' { SKIP(); } //comment block ; - - diff --git a/src/parser/tptp/tptp.cpp b/src/parser/tptp/tptp.cpp index ee7ee4c61..edffaa01f 100644 --- a/src/parser/tptp/tptp.cpp +++ b/src/parser/tptp/tptp.cpp @@ -33,25 +33,26 @@ Tptp::Tptp(ExprManager* exprManager, Input* input, bool strictMode, bool parseOn /* Try to find TPTP dir */ // From tptp4x FileUtilities //----Try the TPTP directory, and TPTP variations - char* home = getenv("TPTP"); - if (home == NULL) { - home = getenv("TPTP_HOME"); + char* home = getenv("TPTP"); + if(home == NULL) { + home = getenv("TPTP_HOME"); // //----If no TPTP_HOME, try the tptp user (aaargh) -// if (home == NULL && (PasswdEntry = getpwnam("tptp")) != NULL) { -// home = PasswdEntry->pw_dir; +// if(home == NULL && (PasswdEntry = getpwnam("tptp")) != NULL) { +// home = PasswdEntry->pw_dir; // } //----Now look in the TPTP directory from there - if (home != NULL) { - d_tptpDir = home; - d_tptpDir.append("/TPTP/"); - } - } else { + if(home != NULL) { d_tptpDir = home; - //add trailing "/" - if( d_tptpDir[d_tptpDir.size() - 1] != '/'){ - d_tptpDir.append("/"); - } + d_tptpDir.append("/TPTP/"); } + } else { + d_tptpDir = home; + //add trailing "/" + if(d_tptpDir[d_tptpDir.size() - 1] != '/') { + d_tptpDir.append("/"); + } + } + d_hasConjecture = false; } void Tptp::addTheory(Theory theory) { @@ -91,7 +92,7 @@ void Tptp::addTheory(Theory theory) { /* The include are managed in the lexer but called in the parser */ // Inspired by http://www.antlr3.org/api/C/interop.html -bool newInputStream(std::string fileName, pANTLR3_LEXER lexer){ +bool newInputStream(std::string fileName, pANTLR3_LEXER lexer) { Debug("parser") << "Including " << fileName << std::endl; // Create a new input stream and take advantage of built in stream stacking // in C target runtime. @@ -102,11 +103,11 @@ bool newInputStream(std::string fileName, pANTLR3_LEXER lexer){ #else /* CVC4_ANTLR3_OLD_INPUT_STREAM */ in = antlr3FileStreamNew((pANTLR3_UINT8) fileName.c_str(), ANTLR3_ENC_8BIT); #endif /* CVC4_ANTLR3_OLD_INPUT_STREAM */ - if( in == NULL ) { + if(in == NULL) { Debug("parser") << "Can't open " << fileName << std::endl; return false; } - // Samething than the predefined PUSHSTREAM(in); + // Same thing as the predefined PUSHSTREAM(in); lexer->pushCharStream(lexer,in); // restart it //lexer->rec->state->tokenStartCharIndex = -10; @@ -125,41 +126,95 @@ bool newInputStream(std::string fileName, pANTLR3_LEXER lexer){ return true; } +/* overridden popCharStream for the lexer - necessary if we had symbol + * filtering in file inclusion. +void Tptp::myPopCharStream(pANTLR3_LEXER lexer) { + ((Tptp*)lexer->super)->d_oldPopCharStream(lexer); + ((Tptp*)lexer->super)->popScope(); +} +*/ + +void Tptp::includeFile(std::string fileName) { + // security for online version + if(!canIncludeFile()) { + parseError("include-file feature was disabled for this run."); + } -void Tptp::includeFile(std::string fileName){ // Get the lexer AntlrInput * ai = static_cast<AntlrInput *>(getInput()); pANTLR3_LEXER lexer = ai->getAntlr3Lexer(); + + // set up popCharStream - would be necessary for handling symbol + // filtering in inclusions + /* + if(d_oldPopCharStream == NULL) { + d_oldPopCharStream = lexer->popCharStream; + lexer->popCharStream = myPopCharStream; + } + */ + + // push the inclusion scope; will be popped by our special popCharStream + // would be necessary for handling symbol filtering in inclusions + //pushScope(); + // get the name of the current stream "Does it work inside an include?" const std::string inputName = ai->getInputStreamName(); // Test in the directory of the actual parsed file std::string currentDirFileName; - if( inputName != "<stdin>"){ - // TODO: Use dirname ot Boost::filesystem? + if(inputName != "<stdin>") { + // TODO: Use dirname or Boost::filesystem? size_t pos = inputName.rfind('/'); - if( pos != std::string::npos){ + if(pos != std::string::npos) { currentDirFileName = std::string(inputName, 0, pos + 1); } currentDirFileName.append(fileName); - if( newInputStream(currentDirFileName,lexer) ){ + if(newInputStream(currentDirFileName,lexer)) { return; } } else { currentDirFileName = "<unknown current directory for stdin>"; } - if( d_tptpDir.empty() ){ + if(d_tptpDir.empty()) { parseError("Couldn't open included file: " + fileName + " at " + currentDirFileName + " and the TPTP directory is not specified (environment variable TPTP)"); }; std::string tptpDirFileName = d_tptpDir + fileName; - if( !newInputStream(tptpDirFileName,lexer) ){ + if(! newInputStream(tptpDirFileName,lexer)) { parseError("Couldn't open included file: " + fileName + " at " + currentDirFileName + " or " + tptpDirFileName); } } +void Tptp::checkLetBinding(std::vector<Expr>& bvlist, Expr lhs, Expr rhs, bool formula) { + if(lhs.getKind() != CVC4::kind::APPLY_UF) { + parseError("malformed let: LHS must be a flat function application"); + } + std::vector<CVC4::Expr> v = lhs.getChildren(); + if(formula && !lhs.getType().isBoolean()) { + parseError("malformed let: LHS must be formula"); + } + for(size_t i = 0; i < v.size(); ++i) { + if(v[i].hasOperator()) { + parseError("malformed let: LHS must be flat, illegal child: " + v[i].toString()); + } + } + std::sort(v.begin(), v.end()); + std::sort(bvlist.begin(), bvlist.end()); + // ensure all let-bound variables appear on the LHS, and appear only once + for(size_t i = 0; i < bvlist.size(); ++i) { + std::vector<CVC4::Expr>::const_iterator found = std::lower_bound(v.begin(), v.end(), bvlist[i]); + if(found == v.end() || *found != bvlist[i]) { + parseError("malformed let: LHS must make use of all quantified variables, missing `" + bvlist[i].toString() + "'"); + } + std::vector<CVC4::Expr>::const_iterator found2 = found + 1; + if(found2 != v.end() && *found2 == *found) { + parseError("malformed let: LHS cannot use same bound variable twice: " + (*found).toString()); + } + } +} + }/* CVC4::parser namespace */ }/* CVC4 namespace */ diff --git a/src/parser/tptp/tptp.h b/src/parser/tptp/tptp.h index 6b7adbbf7..e180d1524 100644 --- a/src/parser/tptp/tptp.h +++ b/src/parser/tptp/tptp.h @@ -24,6 +24,8 @@ #include "util/hash.h" #include <ext/hash_set> #include <cassert> +#include "parser/options.h" +#include "parser/antlr_input.h" namespace CVC4 { @@ -45,46 +47,57 @@ class Tptp : public Parser { std::hash_set<Expr, ExprHashFunction> d_r_converted; std::hash_map<std::string, Expr, StringHashFunction> d_distinct_objects; - //TPTP directory where to find includes + // TPTP directory where to find includes; // empty if none could be determined std::string d_tptpDir; + // hack to make output SZS ontology-compliant + bool d_hasConjecture; + + static void myPopCharStream(pANTLR3_LEXER lexer); + void (*d_oldPopCharStream)(pANTLR3_LEXER); + public: - bool cnf; //in a cnf formula - void addFreeVar(Expr var){assert(cnf); d_freeVar.push_back(var); }; - std::vector< Expr > getFreeVar(){ + bool cnf; // in a cnf formula + bool fof; // in an fof formula + + void addFreeVar(Expr var) { + assert(cnf); + d_freeVar.push_back(var); + } + std::vector< Expr > getFreeVar() { assert(cnf); std::vector< Expr > r; r.swap(d_freeVar); return r; } - inline Expr convertRatToUnsorted(Expr expr){ + inline Expr convertRatToUnsorted(Expr expr) { ExprManager * em = getExprManager(); // Create the conversion function If they doesn't exists - if(d_rtu_op.isNull()){ + if(d_rtu_op.isNull()) { Type t; - //Conversion from rational to unsorted + // Conversion from rational to unsorted t = em->mkFunctionType(em->realType(), d_unsorted); d_rtu_op = em->mkVar("$$rtu",t); preemptCommand(new DeclareFunctionCommand("$$rtu", d_rtu_op, t)); - //Conversion from unsorted to rational + // Conversion from unsorted to rational t = em->mkFunctionType(d_unsorted, em->realType()); d_utr_op = em->mkVar("$$utr",t); - preemptCommand(new DeclareFunctionCommand("$$utur", d_utr_op, t)); + preemptCommand(new DeclareFunctionCommand("$$utr", d_utr_op, t)); } // Add the inverse in order to show that over the elements that // appear in the problem there is a bijection between unsorted and // rational Expr ret = em->mkExpr(kind::APPLY_UF,d_rtu_op,expr); - if ( d_r_converted.find(expr) == d_r_converted.end() ){ + if(d_r_converted.find(expr) == d_r_converted.end()) { d_r_converted.insert(expr); - Expr eq = em->mkExpr(kind::EQUAL,expr, - em->mkExpr(kind::APPLY_UF,d_utr_op,ret)); + Expr eq = em->mkExpr(kind::EQUAL, expr, + em->mkExpr(kind::APPLY_UF, d_utr_op, ret)); preemptCommand(new AssertCommand(eq)); - }; + } return ret; } @@ -98,12 +111,13 @@ public: public: - //TPTP (CNF and FOF) is unsorted so we define this common type + // CNF and FOF are unsorted so we define this common type. + // This is also the Type of $i in TFF. Type d_unsorted; enum Theory { THEORY_CORE, - }; + };/* enum Theory */ enum FormulaRole { FR_AXIOM, @@ -120,8 +134,9 @@ public: FR_FI_FUNCTORS, FR_FI_PREDICATES, FR_TYPE, - }; + };/* enum FormulaRole */ + bool hasConjecture() const { return d_hasConjecture; } protected: Tptp(ExprManager* exprManager, Input* input, bool strictMode = false, bool parseOnly = false); @@ -134,10 +149,10 @@ public: */ void addTheory(Theory theory); - inline void makeApplication(Expr & expr, std::string & name, - std::vector<Expr> & args, bool term); + inline void makeApplication(Expr& expr, std::string& name, + std::vector<Expr>& args, bool term); - inline Command* makeCommand(FormulaRole fr, Expr & expr); + inline Command* makeCommand(FormulaRole fr, Expr& expr, bool cnf); /** Ugly hack because I don't know how to return an expression from a token */ @@ -147,41 +162,53 @@ public: is reused */ void includeFile(std::string fileName); + /** Check a TPTP let binding for well-formedness. */ + void checkLetBinding(std::vector<Expr>& bvlist, Expr lhs, Expr rhs, bool formula); + private: void addArithmeticOperators(); };/* class Tptp */ -inline void Tptp::makeApplication(Expr & expr, std::string & name, - std::vector<Expr> & args, bool term){ - // We distinguish the symbols according to their arities - std::stringstream ss; - ss << name << "_" << args.size(); - name = ss.str(); - if(args.empty()){ // Its a constant - if(isDeclared(name)){ //already appeared +inline void Tptp::makeApplication(Expr& expr, std::string& name, + std::vector<Expr>& args, bool term) { + if(args.empty()) { // Its a constant + if(isDeclared(name)) { // already appeared expr = getVariable(name); } else { Type t = term ? d_unsorted : getExprManager()->booleanType(); - expr = mkVar(name,t,true); //levelZero + expr = mkVar(name, t, ExprManager::VAR_FLAG_GLOBAL); // levelZero preemptCommand(new DeclareFunctionCommand(name, expr, t)); } } else { // Its an application - if(isDeclared(name)){ //already appeared + if(isDeclared(name)) { // already appeared expr = getVariable(name); } else { std::vector<Type> sorts(args.size(), d_unsorted); Type t = term ? d_unsorted : getExprManager()->booleanType(); t = getExprManager()->mkFunctionType(sorts, t); - expr = mkVar(name,t,true); //levelZero + expr = mkVar(name, t, ExprManager::VAR_FLAG_GLOBAL); // levelZero preemptCommand(new DeclareFunctionCommand(name, expr, t)); } + // args might be rationals, in which case we need to create + // distinct constants of the "unsorted" sort to represent them + for(size_t i = 0; i < args.size(); ++i) { + if(args[i].getType().isReal() && FunctionType(expr.getType()).getArgTypes()[i] == d_unsorted) { + args[i] = convertRatToUnsorted(args[i]); + } + } expr = getExprManager()->mkExpr(kind::APPLY_UF, expr, args); } -}; +} -inline Command* Tptp::makeCommand(FormulaRole fr, Expr & expr){ - switch(fr){ +inline Command* Tptp::makeCommand(FormulaRole fr, Expr& expr, bool cnf) { + // For SZS ontology compliance. + // if we're in cnf() though, conjectures don't result in "Theorem" or + // "CounterSatisfiable". + if(!cnf && (fr == FR_NEGATED_CONJECTURE || fr == FR_CONJECTURE)) { + d_hasConjecture = true; + } + switch(fr) { case FR_AXIOM: case FR_HYPOTHESIS: case FR_DEFINITION: diff --git a/src/parser/tptp/tptp_input.cpp b/src/parser/tptp/tptp_input.cpp index 34a620187..bfaeb07c9 100644 --- a/src/parser/tptp/tptp_input.cpp +++ b/src/parser/tptp/tptp_input.cpp @@ -28,9 +28,9 @@ namespace CVC4 { namespace parser { -/* Use lookahead=1 */ +/* Use lookahead=2 */ TptpInput::TptpInput(AntlrInputStream& inputStream) : - AntlrInput(inputStream, 1) { + AntlrInput(inputStream, 2) { pANTLR3_INPUT_STREAM input = inputStream.getAntlr3InputStream(); assert( input != NULL ); diff --git a/src/parser/tptp/tptp_input.h b/src/parser/tptp/tptp_input.h index 19e928e7e..cb2bcd3a3 100644 --- a/src/parser/tptp/tptp_input.h +++ b/src/parser/tptp/tptp_input.h @@ -64,7 +64,7 @@ public: /** Get the language that this Input is reading. */ InputLanguage getLanguage() const throw() { - return language::input::LANG_SMTLIB_V2; + return language::input::LANG_TPTP; } protected: diff --git a/src/printer/Makefile.am b/src/printer/Makefile.am index fd48b8352..cd938088e 100644 --- a/src/printer/Makefile.am +++ b/src/printer/Makefile.am @@ -19,7 +19,9 @@ libprinter_la_SOURCES = \ smt2/smt2_printer.h \ smt2/smt2_printer.cpp \ cvc/cvc_printer.h \ - cvc/cvc_printer.cpp + cvc/cvc_printer.cpp \ + tptp/tptp_printer.h \ + tptp/tptp_printer.cpp EXTRA_DIST = \ options_handlers.h diff --git a/src/printer/ast/ast_printer.cpp b/src/printer/ast/ast_printer.cpp index 57462c9f7..88c769f26 100644 --- a/src/printer/ast/ast_printer.cpp +++ b/src/printer/ast/ast_printer.cpp @@ -185,11 +185,11 @@ void AstPrinter::toStream(std::ostream& out, const CommandStatus* s) const throw }/* AstPrinter::toStream(CommandStatus*) */ -void AstPrinter::toStream(std::ostream& out, Model& m) const throw() { +void AstPrinter::toStream(std::ostream& out, const Model& m) const throw() { out << "Model()"; } -void AstPrinter::toStream(std::ostream& out, Model& m, const Command* c) const throw() { +void AstPrinter::toStream(std::ostream& out, const Model& m, const Command* c) const throw() { // shouldn't be called; only the non-Command* version above should be Unreachable(); } diff --git a/src/printer/ast/ast_printer.h b/src/printer/ast/ast_printer.h index c9ee6071d..f09de9d00 100644 --- a/src/printer/ast/ast_printer.h +++ b/src/printer/ast/ast_printer.h @@ -29,13 +29,13 @@ namespace ast { class AstPrinter : public CVC4::Printer { void toStream(std::ostream& out, TNode n, int toDepth, bool types) const throw(); - void toStream(std::ostream& out, Model& m, const Command* c) const throw(); + void toStream(std::ostream& out, const Model& m, const Command* c) const throw(); public: using CVC4::Printer::toStream; void toStream(std::ostream& out, TNode n, int toDepth, bool types, size_t dag) const throw(); void toStream(std::ostream& out, const Command* c, int toDepth, bool types, size_t dag) const throw(); void toStream(std::ostream& out, const CommandStatus* s) const throw(); - void toStream(std::ostream& out, Model& m) const throw(); + void toStream(std::ostream& out, const Model& m) const throw(); };/* class AstPrinter */ }/* CVC4::printer::ast namespace */ diff --git a/src/printer/cvc/cvc_printer.cpp b/src/printer/cvc/cvc_printer.cpp index 23c4727f3..513ff7276 100644 --- a/src/printer/cvc/cvc_printer.cpp +++ b/src/printer/cvc/cvc_printer.cpp @@ -813,8 +813,8 @@ void CvcPrinter::toStream(std::ostream& out, const CommandStatus* s) const throw }/* CvcPrinter::toStream(CommandStatus*) */ -void CvcPrinter::toStream(std::ostream& out, Model& m, const Command* c) const throw() { - theory::TheoryModel& tm = (theory::TheoryModel&) m; +void CvcPrinter::toStream(std::ostream& out, const Model& m, const Command* c) const throw() { + const theory::TheoryModel& tm = (const theory::TheoryModel&) m; if(dynamic_cast<const DeclareTypeCommand*>(c) != NULL) { TypeNode tn = TypeNode::fromType( ((const DeclareTypeCommand*)c)->getType() ); if( options::modelUninterpDtEnum() && tn.isSort() && diff --git a/src/printer/cvc/cvc_printer.h b/src/printer/cvc/cvc_printer.h index 49b70b012..15b04488d 100644 --- a/src/printer/cvc/cvc_printer.h +++ b/src/printer/cvc/cvc_printer.h @@ -29,7 +29,7 @@ namespace cvc { class CvcPrinter : public CVC4::Printer { void toStream(std::ostream& out, TNode n, int toDepth, bool types, bool bracket) const throw(); - void toStream(std::ostream& out, Model& m, const Command* c) const throw(); + void toStream(std::ostream& out, const Model& m, const Command* c) const throw(); public: using CVC4::Printer::toStream; void toStream(std::ostream& out, TNode n, int toDepth, bool types, size_t dag) const throw(); diff --git a/src/printer/printer.cpp b/src/printer/printer.cpp index 2a10ae451..f9d7c2a38 100644 --- a/src/printer/printer.cpp +++ b/src/printer/printer.cpp @@ -20,6 +20,7 @@ #include "printer/smt1/smt1_printer.h" #include "printer/smt2/smt2_printer.h" +#include "printer/tptp/tptp_printer.h" #include "printer/cvc/cvc_printer.h" #include "printer/ast/ast_printer.h" @@ -41,8 +42,8 @@ Printer* Printer::makePrinter(OutputLanguage lang) throw() { case LANG_SMTLIB_V2: return new printer::smt2::Smt2Printer(); - case LANG_TPTP: //TODO the printer - return new printer::smt2::Smt2Printer(); + case LANG_TPTP: + return new printer::tptp::TptpPrinter(); case LANG_CVC4: return new printer::cvc::CvcPrinter(); @@ -125,7 +126,7 @@ void Printer::toStream(std::ostream& out, const SExpr& sexpr) const throw() { } }/* Printer::toStream(SExpr) */ -void Printer::toStream(std::ostream& out, Model& m) const throw() { +void Printer::toStream(std::ostream& out, const Model& m) const throw() { for(size_t i = 0; i < m.getNumCommands(); ++i) { toStream(out, m, m.getCommand(i)); } diff --git a/src/printer/printer.h b/src/printer/printer.h index 48a041d6a..d3a9201ee 100644 --- a/src/printer/printer.h +++ b/src/printer/printer.h @@ -43,7 +43,12 @@ protected: Printer() throw() {} /** write model response to command */ - virtual void toStream(std::ostream& out, Model& m, const Command* c) const throw() = 0; + virtual void toStream(std::ostream& out, const Model& m, const Command* c) const throw() = 0; + + /** write model response to command using another language printer */ + void toStreamUsing(OutputLanguage lang, std::ostream& out, const Model& m, const Command* c) const throw() { + getPrinter(lang)->toStream(out, m, c); + } public: /** Get the Printer for a given OutputLanguage */ @@ -79,7 +84,7 @@ public: virtual void toStream(std::ostream& out, const Result& r) const throw(); /** Write a Model out to a stream with this Printer. */ - virtual void toStream(std::ostream& out, Model& m) const throw(); + virtual void toStream(std::ostream& out, const Model& m) const throw(); };/* class Printer */ diff --git a/src/printer/smt1/smt1_printer.cpp b/src/printer/smt1/smt1_printer.cpp index f62709988..c5c491cc0 100644 --- a/src/printer/smt1/smt1_printer.cpp +++ b/src/printer/smt1/smt1_printer.cpp @@ -49,11 +49,11 @@ void Smt1Printer::toStream(std::ostream& out, const SExpr& sexpr) const throw() Printer::getPrinter(language::output::LANG_SMTLIB_V2)->toStream(out, sexpr); }/* Smt1Printer::toStream() */ -void Smt1Printer::toStream(std::ostream& out, Model& m) const throw() { +void Smt1Printer::toStream(std::ostream& out, const Model& m) const throw() { Printer::getPrinter(language::output::LANG_SMTLIB_V2)->toStream(out, m); } -void Smt1Printer::toStream(std::ostream& out, Model& m, const Command* c) const throw() { +void Smt1Printer::toStream(std::ostream& out, const Model& m, const Command* c) const throw() { // shouldn't be called; only the non-Command* version above should be Unreachable(); } diff --git a/src/printer/smt1/smt1_printer.h b/src/printer/smt1/smt1_printer.h index 185d23699..9faf76cc0 100644 --- a/src/printer/smt1/smt1_printer.h +++ b/src/printer/smt1/smt1_printer.h @@ -28,14 +28,14 @@ namespace printer { namespace smt1 { class Smt1Printer : public CVC4::Printer { - void toStream(std::ostream& out, Model& m, const Command* c) const throw(); + void toStream(std::ostream& out, const Model& m, const Command* c) const throw(); public: using CVC4::Printer::toStream; void toStream(std::ostream& out, TNode n, int toDepth, bool types, size_t dag) const throw(); void toStream(std::ostream& out, const Command* c, int toDepth, bool types, size_t dag) const throw(); void toStream(std::ostream& out, const CommandStatus* s) const throw(); void toStream(std::ostream& out, const SExpr& sexpr) const throw(); - void toStream(std::ostream& out, Model& m) const throw(); + void toStream(std::ostream& out, const Model& m) const throw(); };/* class Smt1Printer */ }/* CVC4::printer::smt1 namespace */ diff --git a/src/printer/smt2/smt2_printer.cpp b/src/printer/smt2/smt2_printer.cpp index c59df6269..756e521a6 100644 --- a/src/printer/smt2/smt2_printer.cpp +++ b/src/printer/smt2/smt2_printer.cpp @@ -172,6 +172,12 @@ void Smt2Printer::toStream(std::ostream& out, TNode n, break; } + case kind::STORE_ALL: { + ArrayStoreAll asa = n.getConst<ArrayStoreAll>(); + out << "(__array_store_all__ " << asa.getType() << " " << asa.getExpr() << ")"; + break; + } + case kind::SUBRANGE_TYPE: { const SubrangeBounds& bounds = n.getConst<SubrangeBounds>(); // No way to represent subranges in SMT-LIBv2; this is inspired @@ -214,7 +220,7 @@ void Smt2Printer::toStream(std::ostream& out, TNode n, bool stillNeedToPrintParams = true; // operator - if(n.getNumChildren() != 0) out << '('; + if(n.getNumChildren() != 0 && n.getKind()!=kind::INST_PATTERN_LIST) out << '('; switch(Kind k = n.getKind()) { // builtin theory case kind::APPLY: break; @@ -241,11 +247,25 @@ void Smt2Printer::toStream(std::ostream& out, TNode n, case kind::MULT: case kind::MINUS: case kind::UMINUS: - case kind::DIVISION: case kind::LT: case kind::LEQ: case kind::GT: - case kind::GEQ: out << smtKindString(k) << " "; break; + case kind::GEQ: + case kind::DIVISION: + case kind::DIVISION_TOTAL: + case kind::INTS_DIVISION: + case kind::INTS_DIVISION_TOTAL: + case kind::INTS_MODULUS: + case kind::INTS_MODULUS_TOTAL: + case kind::ABS: + case kind::IS_INTEGER: + case kind::TO_INTEGER: + case kind::TO_REAL: out << smtKindString(k) << " "; break; + + case kind::DIVISIBLE: + out << "(_ divisible " << n.getOperator().getConst<Divisible>().k << ")"; + stillNeedToPrintParams = false; + break; // arrays theory case kind::SELECT: @@ -284,6 +304,7 @@ void Smt2Printer::toStream(std::ostream& out, TNode n, case kind::BITVECTOR_SLE: out << "bvsle "; break; case kind::BITVECTOR_SGT: out << "bvsgt "; break; case kind::BITVECTOR_SGE: out << "bvsge "; break; + case kind::BITVECTOR_TO_NAT: out << "bv2nat "; break; case kind::BITVECTOR_EXTRACT: case kind::BITVECTOR_REPEAT: @@ -291,6 +312,7 @@ void Smt2Printer::toStream(std::ostream& out, TNode n, case kind::BITVECTOR_SIGN_EXTEND: case kind::BITVECTOR_ROTATE_LEFT: case kind::BITVECTOR_ROTATE_RIGHT: + case kind::INT_TO_BITVECTOR: printBvParameterizedOp(out, n); out << ' '; stillNeedToPrintParams = false; @@ -312,12 +334,29 @@ void Smt2Printer::toStream(std::ostream& out, TNode n, break; // quantifiers - case kind::FORALL: out << "forall "; break; - case kind::EXISTS: out << "exists "; break; + case kind::FORALL: + case kind::EXISTS: + if( k==kind::FORALL ){ + out << "forall "; + }else{ + out << "exists "; + } + for( unsigned i=0; i<2; i++) { + out << n[i] << " "; + if( i==0 && n.getNumChildren()==3 ){ + out << "(! "; + } + } + if( n.getNumChildren()==3 ){ + out << n[2]; + out << ")"; + } + out << ")"; + return; + break; case kind::BOUND_VAR_LIST: // the left parenthesis is already printed (before the switch) - for(TNode::iterator i = n.begin(), - iend = n.end(); + for(TNode::iterator i = n.begin(), iend = n.end(); i != iend; ) { out << '('; toStream(out, (*i), toDepth < 0 ? toDepth : toDepth - 1, types); @@ -334,8 +373,13 @@ void Smt2Printer::toStream(std::ostream& out, TNode n, out << ')'; return; case kind::INST_PATTERN: + break; case kind::INST_PATTERN_LIST: // TODO user patterns + for(unsigned i=0; i<n.getNumChildren(); i++) { + out << ":pattern " << n[i]; + } + return; break; default: @@ -399,15 +443,25 @@ static string smtKindString(Kind k) throw() { case kind::MULT: return "*"; case kind::MINUS: return "-"; case kind::UMINUS: return "-"; - case kind::DIVISION: return "/"; case kind::LT: return "<"; case kind::LEQ: return "<="; case kind::GT: return ">"; case kind::GEQ: return ">="; + case kind::DIVISION: + case kind::DIVISION_TOTAL: return "/"; + case kind::INTS_DIVISION: return "div"; + case kind::INTS_DIVISION_TOTAL: return "INTS_DIVISION_TOTAL"; + case kind::INTS_MODULUS: return "mod"; + case kind::INTS_MODULUS_TOTAL: return "INTS_MODULUS_TOTAL"; + case kind::ABS: return "abs"; + case kind::IS_INTEGER: return "is_int"; + case kind::TO_INTEGER: return "to_int"; + case kind::TO_REAL: return "to_real"; // arrays theory case kind::SELECT: return "select"; case kind::STORE: return "store"; + case kind::STORE_ALL: return "__array_store_all__"; case kind::ARRAY_TYPE: return "Array"; // bv theory @@ -483,6 +537,10 @@ static void printBvParameterizedOp(std::ostream& out, TNode n) throw() { out << "rotate_right " << n.getOperator().getConst<BitVectorRotateRight>().rotateRightAmount; break; + case kind::INT_TO_BITVECTOR: + out << "int2bv " + << n.getOperator().getConst<IntToBitVector>().size; + break; default: out << n.getKind(); } @@ -504,6 +562,7 @@ void Smt2Printer::toStream(std::ostream& out, const Command* c, tryToStream<CheckSatCommand>(out, c) || tryToStream<QueryCommand>(out, c) || tryToStream<QuitCommand>(out, c) || + tryToStream<DeclarationSequence>(out, c) || tryToStream<CommandSequence>(out, c) || tryToStream<DeclareFunctionCommand>(out, c) || tryToStream<DeclareTypeCommand>(out, c) || @@ -554,15 +613,15 @@ void Smt2Printer::toStream(std::ostream& out, const CommandStatus* s) const thro }/* Smt2Printer::toStream(CommandStatus*) */ -void Smt2Printer::toStream(std::ostream& out, Model& m) const throw() { +void Smt2Printer::toStream(std::ostream& out, const Model& m) const throw() { out << "(model" << std::endl; this->Printer::toStream(out, m); out << ")" << std::endl; } -void Smt2Printer::toStream(std::ostream& out, Model& m, const Command* c) const throw() { - theory::TheoryModel& tm = (theory::TheoryModel&) m; +void Smt2Printer::toStream(std::ostream& out, const Model& m, const Command* c) const throw() { + const theory::TheoryModel& tm = (const theory::TheoryModel&) m; if(dynamic_cast<const DeclareTypeCommand*>(c) != NULL) { TypeNode tn = TypeNode::fromType( ((const DeclareTypeCommand*)c)->getType() ); if( options::modelUninterpDtEnum() && tn.isSort() && @@ -644,7 +703,7 @@ void Smt2Printer::toStream(std::ostream& out, Model& m, const Command* c) const } void Smt2Printer::toStream(std::ostream& out, const Result& r) const throw() { - if (r.getType() == Result::TYPE_SAT && r.isSat() == Result::SAT_UNKNOWN) { + if(r.getType() == Result::TYPE_SAT && r.isSat() == Result::SAT_UNKNOWN) { out << "unknown"; } else { Printer::toStream(out, r); @@ -718,15 +777,26 @@ static void toStream(std::ostream& out, const DeclareFunctionCommand* c) throw() static void toStream(std::ostream& out, const DefineFunctionCommand* c) throw() { Expr func = c->getFunction(); - const vector<Expr>& formals = c->getFormals(); + const vector<Expr>* formals = &c->getFormals(); out << "(define-fun " << func << " ("; Type type = func.getType(); + Expr formula = c->getFormula(); if(type.isFunction()) { - vector<Expr>::const_iterator i = formals.begin(); + vector<Expr> f; + if(formals->empty()) { + const vector<Type>& params = FunctionType(type).getArgTypes(); + for(vector<Type>::const_iterator j = params.begin(); j != params.end(); ++j) { + f.push_back(NodeManager::currentNM()->mkSkolem("a", TypeNode::fromType(*j), "", + NodeManager::SKOLEM_NO_NOTIFY).toExpr()); + } + formula = NodeManager::currentNM()->toExprManager()->mkExpr(kind::APPLY_UF, formula, f); + formals = &f; + } + vector<Expr>::const_iterator i = formals->begin(); for(;;) { out << "(" << (*i) << " " << (*i).getType() << ")"; ++i; - if(i != formals.end()) { + if(i != formals->end()) { out << " "; } else { break; @@ -734,7 +804,6 @@ static void toStream(std::ostream& out, const DefineFunctionCommand* c) throw() } type = FunctionType(type).getRangeType(); } - Expr formula = c->getFormula(); out << ") " << type << " " << formula << ")"; } diff --git a/src/printer/smt2/smt2_printer.h b/src/printer/smt2/smt2_printer.h index 76ec39258..871b3823a 100644 --- a/src/printer/smt2/smt2_printer.h +++ b/src/printer/smt2/smt2_printer.h @@ -29,8 +29,8 @@ namespace smt2 { class Smt2Printer : public CVC4::Printer { void toStream(std::ostream& out, TNode n, int toDepth, bool types) const throw(); - void toStream(std::ostream& out, Model& m, const Command* c) const throw(); - void toStream(std::ostream& out, Model& m) const throw(); + void toStream(std::ostream& out, const Model& m, const Command* c) const throw(); + void toStream(std::ostream& out, const Model& m) const throw(); public: using CVC4::Printer::toStream; void toStream(std::ostream& out, TNode n, int toDepth, bool types, size_t dag) const throw(); diff --git a/src/printer/tptp/tptp_printer.cpp b/src/printer/tptp/tptp_printer.cpp new file mode 100644 index 000000000..ec2a8758b --- /dev/null +++ b/src/printer/tptp/tptp_printer.cpp @@ -0,0 +1,83 @@ +/********************* */ +/*! \file tptp_printer.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-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief The pretty-printer interface for the TPTP output language + ** + ** The pretty-printer interface for the TPTP output language. + **/ + +#include "printer/tptp/tptp_printer.h" +#include "expr/expr.h" // for ExprSetDepth etc.. +#include "util/language.h" // for LANG_AST +#include "expr/node_manager.h" // for VarNameAttr +#include "expr/command.h" + +#include <iostream> +#include <vector> +#include <string> +#include <typeinfo> + +using namespace std; + +namespace CVC4 { +namespace printer { +namespace tptp { + +void TptpPrinter::toStream(std::ostream& out, TNode n, + int toDepth, bool types, size_t dag) const throw() { + n.toStream(out, toDepth, types, dag, language::output::LANG_SMTLIB_V2); +}/* TptpPrinter::toStream() */ + +void TptpPrinter::toStream(std::ostream& out, const Command* c, + int toDepth, bool types, size_t dag) const throw() { + c->toStream(out, toDepth, types, dag, language::output::LANG_SMTLIB_V2); +}/* TptpPrinter::toStream() */ + +void TptpPrinter::toStream(std::ostream& out, const CommandStatus* s) const throw() { + s->toStream(out, language::output::LANG_SMTLIB_V2); +}/* TptpPrinter::toStream() */ + +void TptpPrinter::toStream(std::ostream& out, const SExpr& sexpr) const throw() { + Printer::getPrinter(language::output::LANG_SMTLIB_V2)->toStream(out, sexpr); +}/* TptpPrinter::toStream() */ + +void TptpPrinter::toStream(std::ostream& out, const Model& m) const throw() { + out << "% SZS output start FiniteModel for " << m.getInputName() << endl; + for(size_t i = 0; i < m.getNumCommands(); ++i) { + this->Printer::toStreamUsing(language::output::LANG_SMTLIB_V2, out, m, m.getCommand(i)); + } + out << "% SZS output end FiniteModel for " << m.getInputName() << endl; +} + +void TptpPrinter::toStream(std::ostream& out, const Model& m, const Command* c) const throw() { + // shouldn't be called; only the non-Command* version above should be + Unreachable(); +} + +void TptpPrinter::toStream(std::ostream& out, const Result& r) const throw() { + out << "% SZS status "; + if(r.isSat() == Result::SAT) { + out << "Satisfiable"; + } else if(r.isSat() == Result::UNSAT) { + out << "Unsatisfiable"; + } else if(r.isValid() == Result::VALID) { + out << "Theorem"; + } else if(r.isValid() == Result::INVALID) { + out << "CounterSatisfiable"; + } else { + out << "GaveUp"; + } + out << " for " << r.getInputName(); +} + +}/* CVC4::printer::tptp namespace */ +}/* CVC4::printer namespace */ +}/* CVC4 namespace */ diff --git a/src/printer/tptp/tptp_printer.h b/src/printer/tptp/tptp_printer.h new file mode 100644 index 000000000..a0f3de62b --- /dev/null +++ b/src/printer/tptp/tptp_printer.h @@ -0,0 +1,46 @@ +/********************* */ +/*! \file tptp_printer.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 The pretty-printer interface for the TPTP output language + ** + ** The pretty-printer interface for the TPTP output language. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__PRINTER__TPTP_PRINTER_H +#define __CVC4__PRINTER__TPTP_PRINTER_H + +#include <iostream> + +#include "printer/printer.h" + +namespace CVC4 { +namespace printer { +namespace tptp { + +class TptpPrinter : public CVC4::Printer { + void toStream(std::ostream& out, const Model& m, const Command* c) const throw(); +public: + using CVC4::Printer::toStream; + void toStream(std::ostream& out, TNode n, int toDepth, bool types, size_t dag) const throw(); + void toStream(std::ostream& out, const Command* c, int toDepth, bool types, size_t dag) const throw(); + void toStream(std::ostream& out, const CommandStatus* s) const throw(); + void toStream(std::ostream& out, const SExpr& sexpr) const throw(); + void toStream(std::ostream& out, const Model& m) const throw(); + void toStream(std::ostream& out, const Result& r) const throw(); +};/* class TptpPrinter */ + +}/* CVC4::printer::tptp namespace */ +}/* CVC4::printer namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__PRINTER__TPTP_PRINTER_H */ diff --git a/src/proof/cnf_proof.cpp b/src/proof/cnf_proof.cpp index d6e493c8d..5f03ef5cf 100644 --- a/src/proof/cnf_proof.cpp +++ b/src/proof/cnf_proof.cpp @@ -19,8 +19,8 @@ using namespace CVC4::prop; namespace CVC4 { + CnfProof::CnfProof(CnfStream* stream) : d_cnfStream(stream) {} - } /* CVC4 namespace */ diff --git a/src/proof/cnf_proof.h b/src/proof/cnf_proof.h index c265e46df..984dc76c6 100644 --- a/src/proof/cnf_proof.h +++ b/src/proof/cnf_proof.h @@ -17,7 +17,7 @@ **/ #include "cvc4_private.h" - +#include "util/proof.h" #ifndef __CVC4__CNF_PROOF_H #define __CVC4__CNF_PROOF_H diff --git a/src/proof/sat_proof.cpp b/src/proof/sat_proof.cpp index df695c2d1..d9b57f87e 100644 --- a/src/proof/sat_proof.cpp +++ b/src/proof/sat_proof.cpp @@ -173,7 +173,10 @@ SatProof::SatProof(Minisat::Solver* solver, bool checkRes) : d_emptyClauseId(-1), d_nullId(-2), d_temp_clauseId(), - d_temp_idClause() + d_temp_idClause(), + d_unitConflictId(), + d_storedUnitConflict(false), + d_atomToVar() { d_proxy = new ProofProxy(this); } @@ -353,6 +356,7 @@ ClauseId SatProof::registerClause(::Minisat::CRef clause, bool isInput) { d_inputClauses.insert(newId); } } + Debug("proof:sat:detailed") <<"registerClause " << d_clauseId[clause] << " " <<isInput << "\n"; return d_clauseId[clause]; } @@ -367,6 +371,7 @@ ClauseId SatProof::registerUnitClause(::Minisat::Lit lit, bool isInput) { d_inputClauses.insert(newId); } } + Debug("proof:sat:detailed") <<"registerUnitClause " << d_unitId[toInt(lit)] << " " << isInput <<"\n"; return d_unitId[toInt(lit)]; } @@ -527,10 +532,25 @@ void SatProof::toStream(std::ostream& out) { Unimplemented("native proof printing not supported yet"); } +void SatProof::storeUnitConflict(::Minisat::Lit conflict_lit) { + Assert (!d_storedUnitConflict); + d_unitConflictId = registerUnitClause(conflict_lit); + d_storedUnitConflict = true; + Debug("proof:sat:detailed") <<"storeUnitConflict " << d_unitConflictId << "\n"; +} + void SatProof::finalizeProof(::Minisat::CRef conflict_ref) { Assert(d_resStack.size() == 0); - //ClauseId conflict_id = getClauseId(conflict_ref); - ClauseId conflict_id = registerClause(conflict_ref); //FIXME + Assert (conflict_ref != ::Minisat::CRef_Undef); + ClauseId conflict_id; + if (conflict_ref == ::Minisat::CRef_Lazy) { + Assert (d_storedUnitConflict); + conflict_id = d_unitConflictId; + } else { + Assert (!d_storedUnitConflict); + conflict_id = registerClause(conflict_ref); //FIXME + } + Debug("proof:sat") << "proof::finalizeProof Final Conflict "; print(conflict_id); @@ -573,6 +593,14 @@ void SatProof::markDeleted(CRef clause) { } } +/// store mapping from theory atoms to new variables +void SatProof::storeAtom(::Minisat::Lit literal, Expr atom) { + Assert(d_atomToVar.find(atom) == d_atomToVar.end()); + d_atomToVar[atom] = literal; +} + + + /// LFSCSatProof class std::string LFSCSatProof::varName(::Minisat::Lit lit) { @@ -613,6 +641,7 @@ void LFSCSatProof::collectLemmas(ClauseId id) { d_seenLemmas.insert(id); } + Assert (d_resChains.find(id) != d_resChains.end()); ResChain* res = d_resChains[id]; ClauseId start = res->getStart(); collectLemmas(start); @@ -658,6 +687,14 @@ void LFSCSatProof::printResolution(ClauseId id) { void LFSCSatProof::printInputClause(ClauseId id) { + if (isUnit(id)) { + ::Minisat::Lit lit = getUnit(id); + d_clauseSS << "(% " << clauseName(id) << " (holds (clc "; + d_clauseSS << varName(lit) << "cln ))"; + d_paren << ")"; + return; + } + ostringstream os; CRef ref = getClauseRef(id); Assert (ref != CRef_Undef); @@ -692,6 +729,7 @@ void LFSCSatProof::printVariables() { void LFSCSatProof::flush(std::ostream& out) { + out << d_atomsSS.str(); out << "(check \n"; d_paren <<")"; out << d_varSS.str(); @@ -705,7 +743,7 @@ void LFSCSatProof::flush(std::ostream& out) { void LFSCSatProof::toStream(std::ostream& out) { Debug("proof:sat") << " LFSCSatProof::printProof \n"; - + // first collect lemmas to print in reverse order collectLemmas(d_emptyClauseId); for(IdSet::iterator it = d_seenLemmas.begin(); it!= d_seenLemmas.end(); ++it) { @@ -713,13 +751,22 @@ void LFSCSatProof::toStream(std::ostream& out) { printResolution(*it); } } + printAtoms(); // last resolution to be printed is the empty clause printResolution(d_emptyClauseId); - + printClauses(); printVariables(); flush(out); } +void LFSCSatProof::printAtoms() { + d_atomsSS << "; Mapping between boolean variables and theory atoms \n"; + for (AtomToVar::iterator it = d_atomToVar.begin(); it != d_atomToVar.end(); ++it) { + d_atomsSS << "; " << it->first << " => v" << var(it->second) << "\n"; + } +} + + } /* CVC4 namespace */ diff --git a/src/proof/sat_proof.h b/src/proof/sat_proof.h index d497a4eba..fb8966400 100644 --- a/src/proof/sat_proof.h +++ b/src/proof/sat_proof.h @@ -26,6 +26,8 @@ #include <ext/hash_map> #include <ext/hash_set> #include <sstream> +#include "expr/expr.h" + namespace Minisat { class Solver; @@ -90,6 +92,8 @@ typedef std::vector < ResChain* > ResStack; typedef std::hash_set < int > VarSet; typedef std::set < ClauseId > IdSet; typedef std::vector < ::Minisat::Lit > LitVector; +typedef __gnu_cxx::hash_map<Expr, ::Minisat::Lit, ExprHashFunction > AtomToVar; + class SatProof; class ProofProxy : public ProofProxyAbstract { @@ -124,7 +128,14 @@ protected: // temporary map for updating CRefs ClauseIdMap d_temp_clauseId; - IdClauseMap d_temp_idClause; + IdClauseMap d_temp_idClause; + + // unit conflict + ClauseId d_unitConflictId; + bool d_storedUnitConflict; + + // atom mapping + AtomToVar d_atomToVar; public: SatProof(::Minisat::Solver* solver, bool checkRes = false); protected: @@ -197,6 +208,9 @@ public: /// clause registration methods ClauseId registerClause(const ::Minisat::CRef clause, bool isInput = false); ClauseId registerUnitClause(const ::Minisat::Lit lit, bool isInput = false); + + void storeUnitConflict(::Minisat::Lit lit); + /** * Marks the deleted clauses as deleted. Note we may still use them in the final * resolution. @@ -216,12 +230,20 @@ public: */ void storeUnitResolution(::Minisat::Lit lit); - ProofProxy* getProxy() {return d_proxy; } + ProofProxy* getProxy() {return d_proxy; } + /** + * At mapping between literal and theory-atom it represents + * + * @param literal + * @param atom + */ + void storeAtom(::Minisat::Lit literal, Expr atom); };/* class SatProof */ class LFSCSatProof: public SatProof { private: - VarSet d_seenVars; + VarSet d_seenVars; + std::ostringstream d_atomsSS; std::ostringstream d_varSS; std::ostringstream d_lemmaSS; std::ostringstream d_clauseSS; @@ -239,11 +261,12 @@ private: void printVariables(); void printClauses(); void flush(std::ostream& out); - + void printAtoms(); public: LFSCSatProof(::Minisat::Solver* solver, bool checkRes = false): SatProof(solver, checkRes), d_seenVars(), + d_atomsSS(), d_varSS(), d_lemmaSS(), d_paren(), diff --git a/src/prop/cnf_stream.cpp b/src/prop/cnf_stream.cpp index 4be58bdef..8ebb461e5 100644 --- a/src/prop/cnf_stream.cpp +++ b/src/prop/cnf_stream.cpp @@ -27,7 +27,9 @@ #include "expr/expr.h" #include "prop/theory_proxy.h" #include "theory/bv/options.h" - +#include "proof/proof_manager.h" +#include "proof/sat_proof.h" +#include "prop/minisat/minisat.h" #include <queue> using namespace std; @@ -236,7 +238,7 @@ SatLiteral CnfStream::convertAtom(TNode node) { // Make a new literal (variables are not considered theory literals) SatLiteral lit = newLiteral(node, theoryLiteral, preRegister, canEliminate); - + PROOF (ProofManager::getSatProof()->storeAtom(MinisatSatSolver::toMinisatLit(lit), node.toExpr()); ); // Return the resulting literal return lit; } diff --git a/src/prop/minisat/core/Solver.cc b/src/prop/minisat/core/Solver.cc index 6196ca357..36e196821 100644 --- a/src/prop/minisat/core/Solver.cc +++ b/src/prop/minisat/core/Solver.cc @@ -135,6 +135,8 @@ Solver::Solver(CVC4::prop::TheoryProxy* proxy, CVC4::context::Context* context, // Assert the constants uncheckedEnqueue(mkLit(varTrue, false)); uncheckedEnqueue(mkLit(varFalse, true)); + PROOF( ProofManager::getSatProof()->registerUnitClause(mkLit(varTrue, false), true); ) + PROOF( ProofManager::getSatProof()->registerUnitClause(mkLit(varFalse, true), true); ) } @@ -229,7 +231,7 @@ CRef Solver::reason(Var x) { int i, j; Lit prev = lit_Undef; for (i = 0, j = 0; i < explanation.size(); ++ i) { - // This clause is valid theory propagation, so it's level is the level of the top literal + // This clause is valid theory propagation, so its level is the level of the top literal explLevel = std::max(explLevel, intro_level(var(explanation[i]))); Assert(value(explanation[i]) != l_Undef); @@ -261,6 +263,7 @@ CRef Solver::reason(Var x) { // Construct the reason CRef real_reason = ca.alloc(explLevel, explanation, true); + PROOF (ProofManager::getSatProof()->registerClause(real_reason, true); ); vardata[x] = VarData(real_reason, level(x), user_level(x), intro_level(x), trail_index(x)); clauses_removable.push(real_reason); attachClause(real_reason); @@ -297,9 +300,9 @@ bool Solver::addClause_(vec<Lit>& ps, bool removable) if (ps[i] == p) { continue; } - // If a literals is false at 0 level (both sat and user level) we also ignore it + // If a literal is false at 0 level (both sat and user level) we also ignore it if (value(ps[i]) == l_False) { - if (level(var(ps[i])) == 0 && user_level(var(ps[i])) == 0) { + if (!PROOF_ON() && level(var(ps[i])) == 0 && user_level(var(ps[i])) == 0) { continue; } else { // If we decide to keep it, we count it into the false literals @@ -345,7 +348,7 @@ bool Solver::addClause_(vec<Lit>& ps, bool removable) assert(assigns[var(ps[0])] != l_False); uncheckedEnqueue(ps[0], cr); PROOF( if (ps.size() == 1) { ProofManager::getSatProof()->registerUnitClause(ps[0], true); } ) - return ok = (propagate(CHECK_WITHOUTH_THEORY) == CRef_Undef); + return ok = (propagate(CHECK_WITHOUT_THEORY) == CRef_Undef); } else return ok; } } @@ -789,7 +792,8 @@ CRef Solver::propagate(TheoryCheckType type) // If there are lemmas (or conflicts) update them if (lemmas.size() > 0) { recheck = true; - return updateLemmas(); + confl = updateLemmas(); + return confl; } else { recheck = proxy->theoryNeedCheck(); return confl; @@ -801,9 +805,8 @@ CRef Solver::propagate(TheoryCheckType type) do { // Propagate on the clauses confl = propagateBool(); - // If no conflict, do the theory check - if (confl == CRef_Undef && type != CHECK_WITHOUTH_THEORY) { + if (confl == CRef_Undef && type != CHECK_WITHOUT_THEORY) { // Do the theory check if (type == CHECK_FINAL_FAKE) { theoryCheck(CVC4::theory::Theory::EFFORT_FULL); @@ -836,7 +839,6 @@ CRef Solver::propagate(TheoryCheckType type) } } } while (confl == CRef_Undef && qhead < trail.size()); - return confl; } @@ -1017,8 +1019,8 @@ void Solver::removeClausesAboveLevel(vec<CRef>& cs, int level) for (i = j = 0; i < cs.size(); i++){ Clause& c = ca[cs[i]]; if (c.level() > level) { - assert(!locked(c)); - removeClause(cs[i]); + assert(!locked(c)); + removeClause(cs[i]); } else { cs[j++] = cs[i]; } @@ -1048,7 +1050,7 @@ bool Solver::simplify() { assert(decisionLevel() == 0); - if (!ok || propagate(CHECK_WITHOUTH_THEORY) != CRef_Undef) + if (!ok || propagate(CHECK_WITHOUT_THEORY) != CRef_Undef) return ok = false; if (nAssigns() == simpDB_assigns || (simpDB_props > 0)) @@ -1210,7 +1212,7 @@ lbool Solver::search(int nof_conflicts) if (next == lit_Undef) { // We need to do a full theory check to confirm - Debug("minisat::search") << "Doing a full theoy check..." + Debug("minisat::search") << "Doing a full theory check..." << std::endl; check_type = CHECK_FINAL; continue; @@ -1490,7 +1492,7 @@ void Solver::pop() Debug("minisat") << "== unassigning " << trail.last() << std::endl; Var x = var(trail.last()); if (user_level(x) > assertionLevel) { - assigns [x] = l_Undef; + assigns[x] = l_Undef; vardata[x] = VarData(CRef_Undef, -1, -1, intro_level(x), -1); if(phase_saving >= 1 && (polarity[x] & 0x2) == 0) polarity[x] = sign(trail.last()); @@ -1503,7 +1505,7 @@ void Solver::pop() // The head should be at the trail top qhead = trail.size(); - // Remove the clause + // Remove the clauses removeClausesAboveLevel(clauses_persistent, assertionLevel); removeClausesAboveLevel(clauses_removable, assertionLevel); @@ -1579,6 +1581,7 @@ CRef Solver::updateLemmas() { vec<Lit>& lemma = lemmas[i]; // If it's an empty lemma, we have a conflict at zero level if (lemma.size() == 0) { + Assert (! PROOF_ON()); conflict = CRef_Lazy; backtrackLevel = 0; Debug("minisat::lemmas") << "Solver::updateLemmas(): found empty clause" << std::endl; @@ -1628,6 +1631,7 @@ CRef Solver::updateLemmas() { } lemma_ref = ca.alloc(clauseLevel, lemma, removable); + PROOF (ProofManager::getSatProof()->registerClause(lemma_ref, true); ); if (removable) { clauses_removable.push(lemma_ref); } else { @@ -1647,6 +1651,7 @@ CRef Solver::updateLemmas() { } else { Debug("minisat::lemmas") << "Solver::updateLemmas(): unit conflict or empty clause" << std::endl; conflict = CRef_Lazy; + PROOF(ProofManager::getSatProof()->storeUnitConflict(lemma[0]);); } } else { Debug("minisat::lemmas") << "lemma size is " << lemma.size() << std::endl; diff --git a/src/prop/minisat/core/Solver.h b/src/prop/minisat/core/Solver.h index 55780479a..30d72ac75 100644 --- a/src/prop/minisat/core/Solver.h +++ b/src/prop/minisat/core/Solver.h @@ -266,7 +266,7 @@ protected: int level; // User level when the literal was added to the trail int user_level; - // Use level at which this literal was introduced + // User level at which this literal was introduced int intro_level; // The index in the trail int trail_index; @@ -335,7 +335,7 @@ protected: enum TheoryCheckType { // Quick check, but don't perform theory reasoning - CHECK_WITHOUTH_THEORY, + CHECK_WITHOUT_THEORY, // Check and perform theory reasoning CHECK_WITH_THEORY, // The SAT abstraction of the problem is satisfiable, perform a full theory check diff --git a/src/prop/minisat/minisat.h b/src/prop/minisat/minisat.h index 37e471846..ec49b5f71 100644 --- a/src/prop/minisat/minisat.h +++ b/src/prop/minisat/minisat.h @@ -30,16 +30,15 @@ class MinisatSatSolver : public DPLLSatSolverInterface { /** The SatSolver used */ Minisat::SimpSolver* d_minisat; - /** The SatSolver uses this to communicate with the theories */ TheoryProxy* d_theoryProxy; - /** Context we will be using to synchronzie the sat solver */ + /** Context we will be using to synchronize the sat solver */ context::Context* d_context; public: - MinisatSatSolver (); + MinisatSatSolver(); ~MinisatSatSolver(); static SatVariable toSatVariable(Minisat::Var var); diff --git a/src/prop/minisat/mtl/Vec.h b/src/prop/minisat/mtl/Vec.h index 5d8c2850e..5f85f6fcd 100644 --- a/src/prop/minisat/mtl/Vec.h +++ b/src/prop/minisat/mtl/Vec.h @@ -86,7 +86,7 @@ public: const T& operator [] (int index) const { return data[index]; } T& operator [] (int index) { return data[index]; } - // Duplicatation (preferred instead): + // Duplication (preferred instead): void copyTo(vec<T>& copy) const { copy.clear(); copy.growTo(sz); for (int i = 0; i < sz; i++) copy[i] = data[i]; } void moveTo(vec<T>& dest) { dest.clear(true); dest.data = data; dest.sz = sz; dest.cap = cap; data = NULL; sz = 0; cap = 0; } }; diff --git a/src/prop/minisat/simp/SimpSolver.cc b/src/prop/minisat/simp/SimpSolver.cc index 0e0e5d3ae..6dcdb76c7 100644 --- a/src/prop/minisat/simp/SimpSolver.cc +++ b/src/prop/minisat/simp/SimpSolver.cc @@ -67,7 +67,7 @@ SimpSolver::SimpSolver(CVC4::prop::TheoryProxy* proxy, CVC4::context::Context* c if(options::minisatUseElim() && options::minisatUseElim.wasSetByUser() && enableIncremental) { - WarningOnce() << "Incremental mode incompatible with --minisat-elimination" << std::endl; + WarningOnce() << "Incremental mode incompatible with --minisat-elim" << std::endl; } vec<Lit> dummy(1,lit_Undef); @@ -239,7 +239,7 @@ bool SimpSolver::strengthenClause(CRef cr, Lit l) updateElimHeap(var(l)); } - return c.size() == 1 ? enqueue(c[0]) && propagate(CHECK_WITHOUTH_THEORY) == CRef_Undef : true; + return c.size() == 1 ? enqueue(c[0]) && propagate(CHECK_WITHOUT_THEORY) == CRef_Undef : true; } @@ -346,7 +346,7 @@ bool SimpSolver::implied(const vec<Lit>& c) uncheckedEnqueue(~c[i]); } - bool result = propagate(CHECK_WITHOUTH_THEORY) != CRef_Undef; + bool result = propagate(CHECK_WITHOUT_THEORY) != CRef_Undef; cancelUntil(0); return result; } @@ -435,7 +435,7 @@ bool SimpSolver::asymm(Var v, CRef cr) else l = c[i]; - if (propagate(CHECK_WITHOUTH_THEORY) != CRef_Undef){ + if (propagate(CHECK_WITHOUT_THEORY) != CRef_Undef){ cancelUntil(0); asymm_lits++; if (!strengthenClause(cr, l)) diff --git a/src/prop/options b/src/prop/options index e3a0f814a..b300c3fb6 100644 --- a/src/prop/options +++ b/src/prop/options @@ -22,8 +22,8 @@ option satRestartFirst --restart-int-base=N unsigned :default 25 option satRestartInc --restart-int-inc=F double :default 3.0 :predicate greater_equal(0.0) sets the restart interval increase factor for the sat solver (F=3.0 by default) -option sat_refine_conflicts --refine-conflicts bool - refine theory conflict clauses +option sat_refine_conflicts --refine-conflicts bool :default false + refine theory conflict clauses (default false) option minisatUseElim --minisat-elimination bool :default true :read-write use Minisat elimination diff --git a/src/prop/prop_engine.cpp b/src/prop/prop_engine.cpp index c465ed97a..a169d31e6 100644 --- a/src/prop/prop_engine.cpp +++ b/src/prop/prop_engine.cpp @@ -83,7 +83,8 @@ PropEngine::PropEngine(TheoryEngine* te, DecisionEngine *de, Context* satContext userContext, // fullLitToNode Map = options::threads() > 1 || - options::decisionMode() == decision::DECISION_STRATEGY_RELEVANCY); + options::decisionMode() == decision::DECISION_STRATEGY_RELEVANCY + ); d_satSolver->initialize(d_context, new TheoryProxy(this, d_theoryEngine, d_decisionEngine, d_context, d_cnfStream)); @@ -280,6 +281,7 @@ void PropEngine::interrupt() throw(ModalException) { void PropEngine::spendResource() throw() { // TODO implement me + checkTime(); } bool PropEngine::properExplanation(TNode node, TNode expl) const { diff --git a/src/smt/boolean_terms.cpp b/src/smt/boolean_terms.cpp index 0063035ff..9f1b9c1a6 100644 --- a/src/smt/boolean_terms.cpp +++ b/src/smt/boolean_terms.cpp @@ -288,7 +288,7 @@ TypeNode BooleanTermConverter::convertType(TypeNode type, bool datatypesContext) } return newRec; } - if(type.getNumChildren() > 0) { + if(!type.isSort() && type.getNumChildren() > 0) { Debug("boolean-terms") << "here at A for " << type << ":" << type.getId() << endl; // This should handle tuples and arrays ok. // Might handle function types too, but they can't go @@ -487,7 +487,39 @@ Node BooleanTermConverter::rewriteBooleanTermsRec(TNode top, theory::TheoryId pa } else if(t.isArray()) { TypeNode indexType = convertType(t.getArrayIndexType(), false); TypeNode constituentType = convertType(t.getArrayConstituentType(), false); - if(indexType != t.getArrayIndexType() || constituentType != t.getArrayConstituentType()) { + if(indexType != t.getArrayIndexType() && constituentType == t.getArrayConstituentType()) { + TypeNode newType = nm->mkArrayType(indexType, constituentType); + Node n = nm->mkSkolem(top.getAttribute(expr::VarNameAttr()) + "'", + newType, "an array variable introduced by Boolean-term conversion", + NodeManager::SKOLEM_EXACT_NAME); + top.setAttribute(BooleanTermAttr(), n); + Debug("boolean-terms") << "constructed: " << n << " of type " << newType << endl; + Node n_ff = nm->mkNode(kind::SELECT, n, d_ff); + Node n_tt = nm->mkNode(kind::SELECT, n, d_tt); + Node base = nm->mkConst(ArrayStoreAll(ArrayType(top.getType().toType()), (*TypeEnumerator(n_ff.getType())).toExpr())); + Node repl = nm->mkNode(kind::STORE, + nm->mkNode(kind::STORE, base, nm->mkConst(true), + n_tt), + nm->mkConst(false), n_ff); + Debug("boolean-terms") << "array replacement: " << top << " => " << repl << endl; + d_smt.d_theoryEngine->getModel()->addSubstitution(top, repl); + d_termCache[make_pair(top, parentTheory)] = n; + result.top() << n; + worklist.pop(); + goto next_worklist; + } else if(indexType == t.getArrayIndexType() && constituentType != t.getArrayConstituentType()) { + TypeNode newType = nm->mkArrayType(indexType, constituentType); + Node n = nm->mkSkolem(top.getAttribute(expr::VarNameAttr()) + "'", + newType, "an array variable introduced by Boolean-term conversion", + NodeManager::SKOLEM_EXACT_NAME); + top.setAttribute(BooleanTermAttr(), n); + Debug("boolean-terms") << "constructed: " << n << " of type " << newType << endl; + d_smt.d_theoryEngine->getModel()->addSubstitution(top, n); + d_termCache[make_pair(top, parentTheory)] = n; + result.top() << n; + worklist.pop(); + goto next_worklist; + } else if(indexType != t.getArrayIndexType() && constituentType != t.getArrayConstituentType()) { TypeNode newType = nm->mkArrayType(indexType, constituentType); Node n = nm->mkSkolem(top.getAttribute(expr::VarNameAttr()) + "'", newType, "an array variable introduced by Boolean-term conversion", @@ -594,7 +626,7 @@ Node BooleanTermConverter::rewriteBooleanTermsRec(TNode top, theory::TheoryId pa worklist.pop(); goto next_worklist; } - } else if(t.getNumChildren() > 0) { + } else if(!t.isSort() && t.getNumChildren() > 0) { for(TypeNode::iterator i = t.begin(); i != t.end(); ++i) { if((*i).isBoolean()) { vector<TypeNode> argTypes(t.begin(), t.end()); @@ -617,9 +649,6 @@ Node BooleanTermConverter::rewriteBooleanTermsRec(TNode top, theory::TheoryId pa goto next_worklist; } switch(k) { - case kind::LAMBDA: - Unreachable("not expecting a LAMBDA in boolean-term conversion: %s", top.toString().c_str()); - case kind::BOUND_VAR_LIST: result.top() << top; worklist.pop(); @@ -704,7 +733,8 @@ Node BooleanTermConverter::rewriteBooleanTermsRec(TNode top, theory::TheoryId pa k != kind::TUPLE_SELECT && k != kind::TUPLE_UPDATE && k != kind::RECORD_SELECT && - k != kind::RECORD_UPDATE) { + k != kind::RECORD_UPDATE && + k != kind::DIVISIBLE) { Debug("bt") << "rewriting: " << top.getOperator() << endl; result.top() << rewriteBooleanTermsRec(top.getOperator(), theory::THEORY_BUILTIN, quantBoolVars); Debug("bt") << "got: " << result.top().getOperator() << endl; @@ -715,7 +745,7 @@ Node BooleanTermConverter::rewriteBooleanTermsRec(TNode top, theory::TheoryId pa // push children for(int i = top.getNumChildren() - 1; i >= 0; --i) { Debug("bt") << "rewriting: " << top[i] << endl; - worklist.push(triple<TNode, theory::TheoryId, bool>(top[i], isBoolean(top, i) ? theory::THEORY_BOOL : (top.getKind() == kind::APPLY_CONSTRUCTOR ? theory::THEORY_DATATYPES : theory::THEORY_BUILTIN), false)); + worklist.push(triple<TNode, theory::TheoryId, bool>(top[i], top.getKind() == kind::CHAIN ? parentTheory : (isBoolean(top, i) ? theory::THEORY_BOOL : (top.getKind() == kind::APPLY_CONSTRUCTOR ? theory::THEORY_DATATYPES : theory::THEORY_BUILTIN)), false)); //b << rewriteBooleanTermsRec(top[i], isBoolean(top, i) ? , quantBoolVars); //Debug("bt") << "got: " << b[b.getNumChildren() - 1] << endl; } diff --git a/src/smt/options b/src/smt/options index 2680f4105..7a72881b4 100644 --- a/src/smt/options +++ b/src/smt/options @@ -24,6 +24,8 @@ common-option produceModels produce-models -m --produce-models bool :default fal support the get-value and get-model commands option checkModels check-models --check-models bool :predicate CVC4::smt::beforeSearch :predicate-include "smt/options_handlers.h" after SAT/INVALID/UNKNOWN, check that the generated model satisfies user assertions +option dumpModels --dump-models bool :default false + output models after every SAT/INVALID/UNKNOWN response option proof produce-proofs --proof bool :default false :predicate CVC4::smt::proofEnabledBuild CVC4::smt::beforeSearch :predicate-include "smt/options_handlers.h" turn on proof generation # this is just a placeholder for later; it doesn't show up in command-line options listings @@ -46,7 +48,7 @@ option unconstrainedSimp --unconstrained-simp bool :default false :read-write option repeatSimp --repeat-simp bool :read-write make multiple passes with nonclausal simplifier -option sortInference --sort-inference bool :default false +option sortInference --sort-inference bool :read-write :default false apply sort inference to input problem common-option incrementalSolving incremental -i --incremental bool @@ -67,7 +69,7 @@ common-option cumulativeMillisecondLimit tlimit --tlimit=MS "unsigned long" common-option perCallMillisecondLimit tlimit-per --tlimit-per=MS "unsigned long" enable time limiting per query (give milliseconds) common-option cumulativeResourceLimit rlimit --rlimit=N "unsigned long" - enable resource limiting + enable resource limiting (currently, roughly the number of SAT conflicts) common-option perCallResourceLimit rlimit-per --rlimit-per=N "unsigned long" enable resource limiting per query diff --git a/src/smt/options_handlers.h b/src/smt/options_handlers.h index 6b8d94c08..c631b8c84 100644 --- a/src/smt/options_handlers.h +++ b/src/smt/options_handlers.h @@ -186,6 +186,11 @@ inline void dumpMode(std::string option, std::string optarg, SmtEngine* smt) { } else if(!strcmp(p, "ite-removal")) { } else if(!strcmp(p, "repeat-simplify")) { } else if(!strcmp(p, "theory-preprocessing")) { + } else if(!strcmp(p, "nonclausal")) { + } else if(!strcmp(p, "theorypp")) { + } else if(!strcmp(p, "itesimp")) { + } else if(!strcmp(p, "unconstrained")) { + } else if(!strcmp(p, "repeatsimp")) { } else { throw OptionException(std::string("don't know how to dump `") + optargPtr + "'. Please consult --dump help."); @@ -409,6 +414,17 @@ inline std::ostream* checkReplayLogFilename(std::string option, std::string opta #endif /* CVC4_REPLAY */ } +// ensure we are a stats-enabled build of CVC4 +inline void statsEnabledBuild(std::string option, bool value, SmtEngine* smt) throw(OptionException) { +#ifndef CVC4_STATISTICS_ON + if(value) { + std::stringstream ss; + ss << "option `" << option << "' requires a statistics-enabled build of CVC4; this binary was not built with statistics support"; + throw OptionException(ss.str()); + } +#endif /* CVC4_STATISTICS_ON */ +} + }/* CVC4::smt namespace */ }/* CVC4 namespace */ diff --git a/src/smt/smt_engine.cpp b/src/smt/smt_engine.cpp index 0d473a1a1..39ccc70c4 100644 --- a/src/smt/smt_engine.cpp +++ b/src/smt/smt_engine.cpp @@ -74,6 +74,8 @@ #include "util/sort_inference.h" #include "theory/quantifiers/macros.h" #include "theory/datatypes/options.h" +#include "theory/quantifiers/first_order_reasoning.h" +#include "theory/strings/theory_strings_preprocess.h" using namespace std; using namespace CVC4; @@ -245,6 +247,15 @@ class SmtEnginePrivate : public NodeManagerListener { /** Assertions to push to sat */ vector<Node> d_assertionsToCheck; + /** Whether any assertions have been processed */ + CDO<bool> d_assertionsProcessed; + + /** Index for where to store substitutions */ + CDO<unsigned> d_substitutionsIndex; + + // Cached true value + Node d_true; + /** * A context that never pushes/pops, for use by CD structures (like * SubstitutionMaps) that should be "global". @@ -292,14 +303,13 @@ class SmtEnginePrivate : public NodeManagerListener { */ Node d_modZero; +public: /** * Map from skolem variables to index in d_assertionsToCheck containing * corresponding introduced Boolean ite */ IteSkolemMap d_iteSkolemMap; -public: - /** Instance of the ITE remover */ RemoveITE d_iteRemover; @@ -307,18 +317,6 @@ private: /** The top level substitutions */ SubstitutionMap d_topLevelSubstitutions; - /** - * The last substitution that the SAT layer was told about. - * In incremental settings, substitutions cannot be performed - * "backward," only forward. So SAT needs to be told of all - * substitutions that are going to be done. This iterator - * holds the last substitution from d_topLevelSubstitutions - * that was pushed out to SAT. - * If d_lastSubstitutionPos == d_topLevelSubstitutions.end(), - * then nothing has been pushed out yet. - */ - context::CDO<SubstitutionMap::iterator> d_lastSubstitutionPos; - static const bool d_doConstantProp = true; /** @@ -350,8 +348,8 @@ private: bool checkForBadSkolems(TNode n, TNode skolem, hash_map<Node, bool, NodeHashFunction>& cache); // Lift bit-vectors of size 1 to booleans - void bvToBool(); - + void bvToBool(); + // Simplify ITE structure void simpITE(); @@ -394,6 +392,8 @@ public: d_propagator(d_nonClausalLearnedLiterals, true, true), d_propagatorNeedsFinish(false), d_assertionsToCheck(), + d_assertionsProcessed(smt.d_userContext, false), + d_substitutionsIndex(smt.d_userContext, 0), d_fakeContext(), d_abstractValueMap(&d_fakeContext), d_abstractValues(), @@ -402,9 +402,10 @@ public: d_modZero(), d_iteSkolemMap(), d_iteRemover(smt.d_userContext), - d_topLevelSubstitutions(smt.d_userContext), - d_lastSubstitutionPos(smt.d_userContext, d_topLevelSubstitutions.end()) { + d_topLevelSubstitutions(smt.d_userContext) + { d_smt.d_nodeManager->subscribeEvents(this); + d_true = NodeManager::currentNM()->mkConst(true); } ~SmtEnginePrivate() { @@ -419,11 +420,13 @@ public: d_smt.d_nodeManager->unsubscribeEvents(this); } - void nmNotifyNewSort(TypeNode tn) { + void nmNotifyNewSort(TypeNode tn, uint32_t flags) { DeclareTypeCommand c(tn.getAttribute(expr::VarNameAttr()), 0, tn.toType()); - d_smt.addToModelCommandAndDump(c); + if((flags & ExprManager::SORT_FLAG_PLACEHOLDER) == 0) { + d_smt.addToModelCommandAndDump(c, flags); + } } void nmNotifyNewSortConstructor(TypeNode tn) { @@ -438,17 +441,19 @@ public: d_smt.addToModelCommandAndDump(c); } - void nmNotifyNewVar(TNode n, bool isGlobal) { + void nmNotifyNewVar(TNode n, uint32_t flags) { DeclareFunctionCommand c(n.getAttribute(expr::VarNameAttr()), n.toExpr(), n.getType().toType()); - d_smt.addToModelCommandAndDump(c, isGlobal); + if((flags & ExprManager::VAR_FLAG_DEFINED) == 0) { + d_smt.addToModelCommandAndDump(c, flags); + } if(n.getType().isBoolean() && !options::incrementalSolving()) { d_boolVars.push_back(n); } } - void nmNotifyNewSkolem(TNode n, const std::string& comment, bool isGlobal) { + void nmNotifyNewSkolem(TNode n, const std::string& comment, uint32_t flags) { string id = n.getAttribute(expr::VarNameAttr()); DeclareFunctionCommand c(id, n.toExpr(), @@ -456,7 +461,9 @@ public: if(Dump.isOn("skolems") && comment != "") { Dump("skolems") << CommentCommand(id + " is " + comment); } - d_smt.addToModelCommandAndDump(c, isGlobal, false, "skolems"); + if((flags & ExprManager::VAR_FLAG_DEFINED) == 0) { + d_smt.addToModelCommandAndDump(c, flags, false, "skolems"); + } if(n.getType().isBoolean() && !options::incrementalSolving()) { d_boolVars.push_back(n); } @@ -678,7 +685,7 @@ void SmtEngine::finalOptionsAreSet() { return; } - if (options::bitvectorEagerBitblast()) { + if(options::bitvectorEagerBitblast()) { // Eager solver should use the internal decision strategy options::decisionMode.set(DECISION_STRATEGY_INTERNAL); } @@ -792,21 +799,24 @@ SmtEngine::~SmtEngine() throw() { void SmtEngine::setLogic(const LogicInfo& logic) throw(ModalException) { SmtScope smts(this); - + if(d_fullyInited) { + throw ModalException("Cannot set logic in SmtEngine after the engine has finished initializing"); + } d_logic = logic; setLogicInternal(); } -void SmtEngine::setLogic(const std::string& s) throw(ModalException) { +void SmtEngine::setLogic(const std::string& s) throw(ModalException, LogicException) { SmtScope smts(this); - - setLogic(LogicInfo(s)); + try { + setLogic(LogicInfo(s)); + } catch(IllegalArgumentException& e) { + throw LogicException(e.what()); + } } -void SmtEngine::setLogic(const char* logic) throw(ModalException){ - SmtScope smts(this); - - setLogic(LogicInfo(string(logic))); +void SmtEngine::setLogic(const char* logic) throw(ModalException, LogicException) { + setLogic(string(logic)); } LogicInfo SmtEngine::getLogicInfo() const { @@ -823,24 +833,12 @@ void SmtEngine::setLogicInternal() throw() { d_logic.lock(); - // may need to force uninterpreted functions to be on for non-linear - if(((d_logic.isTheoryEnabled(THEORY_ARITH) && !d_logic.isLinear()) || - d_logic.isTheoryEnabled(THEORY_BV)) && - !d_logic.isTheoryEnabled(THEORY_UF)){ - d_logic = d_logic.getUnlockedCopy(); - d_logic.enableTheory(THEORY_UF); - d_logic.lock(); - } - // Set the options for the theoryOf if(!options::theoryOfMode.wasSetByUser()) { if(d_logic.isSharingEnabled() && !d_logic.isTheoryEnabled(THEORY_BV)) { - Theory::setTheoryOfMode(THEORY_OF_TERM_BASED); - } else { - Theory::setTheoryOfMode(THEORY_OF_TYPE_BASED); + Trace("smt") << "setting theoryof-mode to term-based" << endl; + options::theoryOfMode.set(THEORY_OF_TERM_BASED); } - } else { - Theory::setTheoryOfMode(options::theoryOfMode()); } // by default, symmetry breaker is on only for QF_UF @@ -849,11 +847,10 @@ void SmtEngine::setLogicInternal() throw() { Trace("smt") << "setting uf symmetry breaker to " << qf_uf << endl; options::ufSymmetryBreaker.set(qf_uf); } - // by default, nonclausal simplification is off for QF_SAT and for quantifiers + // by default, nonclausal simplification is off for QF_SAT if(! options::simplificationMode.wasSetByUser()) { bool qf_sat = d_logic.isPure(THEORY_BOOL) && !d_logic.isQuantified(); - bool quantifiers = d_logic.isQuantified(); - Trace("smt") << "setting simplification mode to <" << d_logic.getLogicString() << "> " << (!qf_sat && !quantifiers) << endl; + Trace("smt") << "setting simplification mode to <" << d_logic.getLogicString() << "> " << (!qf_sat) << endl; //simplification=none works better for SMT LIB benchmarks with quantifiers, not others //options::simplificationMode.set(qf_sat || quantifiers ? SIMPLIFICATION_MODE_NONE : SIMPLIFICATION_MODE_BATCH); options::simplificationMode.set(qf_sat ? SIMPLIFICATION_MODE_NONE : SIMPLIFICATION_MODE_BATCH); @@ -990,14 +987,16 @@ void SmtEngine::setLogicInternal() throw() { d_logic.isPure(THEORY_ARITH) && d_logic.isLinear() && !d_logic.isDifferenceLogic() && !d_logic.areIntegersUsed() ) || // Quantifiers - d_logic.isQuantified() + d_logic.isQuantified() || + // Strings + d_logic.isTheoryEnabled(THEORY_STRINGS) ? decision::DECISION_STRATEGY_JUSTIFICATION : decision::DECISION_STRATEGY_INTERNAL ); bool stoponly = // ALL_SUPPORTED - d_logic.hasEverything() ? false : + d_logic.hasEverything() || d_logic.isTheoryEnabled(THEORY_STRINGS) ? false : ( // QF_AUFLIA (not d_logic.isQuantified() && d_logic.isTheoryEnabled(THEORY_ARRAY) && @@ -1020,11 +1019,21 @@ void SmtEngine::setLogicInternal() throw() { //for finite model finding if( ! options::instWhenMode.wasSetByUser()){ + //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 ); } } + if ( ! options::fmfInstGen.wasSetByUser()) { + //if full model checking is on, disable inst-gen techniques + if( options::fmfFullModelCheck() ){ + options::fmfInstGen.set( false ); + } + } + if( options::ufssSymBreak() ){ + options::sortInference.set( true ); + } //until bugs 371,431 are fixed if( ! options::minisatUseElim.wasSetByUser()){ @@ -1108,13 +1117,15 @@ void SmtEngine::setInfo(const std::string& key, const CVC4::SExpr& value) } // Check for standard info keys (SMT-LIB v1, SMT-LIB v2, ...) - if(key == "name" || - key == "source" || + if(key == "source" || key == "category" || key == "difficulty" || key == "notes") { // ignore these return; + } else if(key == "name") { + d_filename = value.getValue(); + return; } else if(key == "smt-lib-version") { if( (value.isInteger() && value.getIntegerValue() == Integer(2)) || (value.isRational() && value.getRationalValue() == Rational(2)) || @@ -1133,7 +1144,7 @@ void SmtEngine::setInfo(const std::string& key, const CVC4::SExpr& value) throw OptionException("argument to (set-info :status ..) must be " "`sat' or `unsat' or `unknown'"); } - d_status = Result(s); + d_status = Result(s, d_filename); return; } throw UnrecognizedOptionException(); @@ -1166,7 +1177,7 @@ CVC4::SExpr SmtEngine::getInfo(const std::string& key) const return stats; } else if(key == "error-behavior") { // immediate-exit | continued-execution - return SExpr::Keyword("immediate-exit"); + return SExpr::Keyword("continued-execution"); } else if(key == "name") { return Configuration::getName(); } else if(key == "version") { @@ -1194,6 +1205,9 @@ CVC4::SExpr SmtEngine::getInfo(const std::string& key) const throw ModalException("Can't get-info :reason-unknown when the " "last result wasn't unknown!"); } + } else if(key == "all-options") { + // get the options, like all-statistics + return Options::current().getOptions(); } else { throw UnrecognizedOptionException(); } @@ -1213,13 +1227,13 @@ void SmtEngine::defineFunction(Expr func, throw TypeCheckingException(func, ss.str()); } } - if(Dump.isOn("declarations")) { - stringstream ss; - ss << Expr::setlanguage(Expr::setlanguage::getLanguage(Dump.getStream())) - << func; - DefineFunctionCommand c(ss.str(), func, formals, formula); - addToModelCommandAndDump(c, false, true, "declarations"); - } + + stringstream ss; + ss << Expr::setlanguage(Expr::setlanguage::getLanguage(Dump.getStream())) + << func; + DefineFunctionCommand c(ss.str(), func, formals, formula); + addToModelCommandAndDump(c, ExprManager::VAR_FLAG_NONE, true, "declarations"); + SmtScope smts(this); // Substitute out any abstract values in formula @@ -1316,6 +1330,11 @@ Node SmtEnginePrivate::expandBVDivByZero(TNode n) { 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; } @@ -1323,168 +1342,209 @@ Node SmtEnginePrivate::expandBVDivByZero(TNode n) { Node SmtEnginePrivate::expandDefinitions(TNode n, hash_map<Node, Node, NodeHashFunction>& cache) throw(TypeCheckingException, LogicException) { - Kind k = n.getKind(); + stack< triple<Node, Node, bool> > worklist; + stack<Node> result; + worklist.push(make_triple(Node(n), Node(n), false)); + + do { + n = worklist.top().first; + Node node = worklist.top().second; + bool childrenPushed = worklist.top().third; + worklist.pop(); - 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) { - return d_smt.d_nodeManager->mkNode(kind::LAMBDA, d_smt.d_nodeManager->mkNode(kind::BOUND_VAR_LIST, (*i).second.getFormals()), (*i).second.getFormula()); + if(!childrenPushed) { + Kind k = n.getKind(); + + 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; } - // don't bother putting in the cache - return (*i).second.getFormula(); - } - // don't bother putting in the cache - return n; - } - // maybe it's in the cache - hash_map<Node, Node, NodeHashFunction>::iterator cacheHit = cache.find(n); - if(cacheHit != cache.end()) { - TNode ret = (*cacheHit).second; - return ret.isNull() ? n : ret; - } + // maybe it's in the cache + hash_map<Node, Node, NodeHashFunction>::iterator cacheHit = cache.find(n); + if(cacheHit != cache.end()) { + TNode ret = (*cacheHit).second; + result.push(ret.isNull() ? n : ret); + continue; + } - // otherwise expand it + // otherwise expand it - Node node = n; - 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::DIVISION: { - // partial function: division - if(d_smt.d_logic.isLinear()) { - node = n; - break; - } - if(d_divByZero.isNull()) { - d_divByZero = nm->mkSkolem("divByZero", nm->mkFunctionType(nm->realType(), nm->realType()), - "partial real division", NodeManager::SKOLEM_EXACT_NAME); - } - 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_smt.d_logic.isLinear()) { - node = n; - break; - } - if(d_intDivByZero.isNull()) { - d_intDivByZero = nm->mkSkolem("intDivByZero", nm->mkFunctionType(nm->integerType(), nm->integerType()), - "partial integer division", NodeManager::SKOLEM_EXACT_NAME); - } - 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_smt.d_logic.isLinear()) { - node = n; - break; - } - if(d_modZero.isNull()) { - d_modZero = nm->mkSkolem("modZero", nm->mkFunctionType(nm->integerType(), nm->integerType()), - "partial modulus", NodeManager::SKOLEM_EXACT_NAME); - } - 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::APPLY: { - // application of a user-defined symbol - TNode func = n.getOperator(); - SmtEngine::DefinedFunctionMap::const_iterator i = - d_smt.d_definedFunctions->find(func); - DefinedFunction def = (*i).second; - vector<Node> formals = def.getFormals(); - - if(Debug.isOn("expand")) { - Debug("expand") << "found: " << n << endl; - Debug("expand") << " func: " << func << endl; - string name = func.getAttribute(expr::VarNameAttr()); - Debug("expand") << " : \"" << name << "\"" << endl; - } - if(i == d_smt.d_definedFunctions->end()) { - throw TypeCheckingException(n.toExpr(), string("Undefined function: `") + func.toString() + "'"); - } - if(Debug.isOn("expand")) { - Debug("expand") << " defn: " << def.getFunction() << endl - << " ["; - if(formals.size() > 0) { - copy( formals.begin(), formals.end() - 1, - ostream_iterator<Node>(Debug("expand"), ", ") ); - Debug("expand") << formals.back(); + 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::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; } - Debug("expand") << "]" << endl - << " " << def.getFunction().getType() << endl - << " " << def.getFormula() << endl; - } - TNode fm = def.getFormula(); - Node instance = fm.substitute(formals.begin(), formals.end(), - n.begin(), n.end()); - Debug("expand") << "made : " << instance << endl; + 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; + } - Node expanded = expandDefinitions(instance, cache); - cache[n] = (n == expanded ? Node::null() : expanded); - return expanded; - } + 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; + } - default: - // unknown kind for expansion, just iterate over the children - node = n; - } + 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; + } - // there should be children here, otherwise we short-circuited a return, above - Assert(node.getNumChildren() > 0); + case kind::APPLY: { + // application of a user-defined symbol + TNode func = n.getOperator(); + SmtEngine::DefinedFunctionMap::const_iterator i = + d_smt.d_definedFunctions->find(func); + DefinedFunction def = (*i).second; + vector<Node> formals = def.getFormals(); + + if(Debug.isOn("expand")) { + Debug("expand") << "found: " << n << endl; + Debug("expand") << " func: " << func << endl; + string name = func.getAttribute(expr::VarNameAttr()); + Debug("expand") << " : \"" << name << "\"" << endl; + } + if(i == d_smt.d_definedFunctions->end()) { + throw TypeCheckingException(n.toExpr(), string("Undefined function: `") + func.toString() + "'"); + } + if(Debug.isOn("expand")) { + Debug("expand") << " defn: " << def.getFunction() << endl + << " ["; + if(formals.size() > 0) { + copy( formals.begin(), formals.end() - 1, + ostream_iterator<Node>(Debug("expand"), ", ") ); + Debug("expand") << formals.back(); + } + Debug("expand") << "]" << endl + << " " << def.getFunction().getType() << endl + << " " << def.getFormula() << endl; + } - // the partial functions can fall through, in which case we still - // consider their children - Debug("expand") << "cons : " << node << endl; - NodeBuilder<> nb(node.getKind()); - if(node.getMetaKind() == kind::metakind::PARAMETERIZED) { - Debug("expand") << "op : " << node.getOperator() << endl; - nb << node.getOperator(); - } - for(Node::iterator i = node.begin(), - iend = node.end(); - i != iend; - ++i) { - Node expanded = expandDefinitions(*i, cache); - Debug("expand") << "exchld: " << expanded << endl; - nb << expanded; - } - node = nb; - cache[n] = n == node ? Node::null() : node; - return node; + TNode fm = def.getFormula(); + Node instance = fm.substitute(formals.begin(), formals.end(), + n.begin(), n.end()); + Debug("expand") << "made : " << instance << endl; + + Node expanded = expandDefinitions(instance, cache); + cache[n] = (n == expanded ? Node::null() : expanded); + result.push(expanded); + continue; + } + + default: + // unknown kind for expansion, just iterate over the children + node = n; + } + + // there should be children here, otherwise we short-circuited a result-push/continue, above + 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)); + + for(size_t i = 0; i < node.getNumChildren(); ++i) { + worklist.push(make_triple(node[i], node[i], false)); + } + + } else { + + Debug("expand") << "cons : " << node << endl; + //cout << "cons : " << node << endl; + NodeBuilder<> nb(node.getKind()); + if(node.getMetaKind() == kind::metakind::PARAMETERIZED) { + Debug("expand") << "op : " << node.getOperator() << endl; + //cout << "op : " << node.getOperator() << endl; + nb << node.getOperator(); + } + for(size_t i = 0; i < node.getNumChildren(); ++i) { + Assert(!result.empty()); + Node expanded = result.top(); + result.pop(); + //cout << "exchld : " << expanded << endl; + Debug("expand") << "exchld : " << expanded << endl; + nb << expanded; + } + node = nb; + cache[n] = n == node ? Node::null() : node; + result.push(node); + } + } while(!worklist.empty()); + + AlwaysAssert(result.size() == 1); + + return result.top(); } @@ -1666,6 +1726,10 @@ bool SmtEnginePrivate::nonClausalSimplify() { << "asserting to propagator" << endl; for (unsigned i = 0; i < d_assertionsToPreprocess.size(); ++ i) { Assert(Rewriter::rewrite(d_assertionsToPreprocess[i]) == d_assertionsToPreprocess[i]); + // Don't reprocess substitutions + if (d_substitutionsIndex > 0 && i == d_substitutionsIndex) { + continue; + } Trace("simplify") << "SmtEnginePrivate::nonClausalSimplify(): asserting " << d_assertionsToPreprocess[i] << endl; d_propagator.assertTrue(d_assertionsToPreprocess[i]); } @@ -1684,12 +1748,15 @@ bool SmtEnginePrivate::nonClausalSimplify() { // No, conflict, go through the literals and solve them SubstitutionMap constantPropagations(d_smt.d_context); + SubstitutionMap newSubstitutions(d_smt.d_context); + SubstitutionMap::iterator pos; unsigned j = 0; for(unsigned i = 0, i_end = d_nonClausalLearnedLiterals.size(); i < i_end; ++ i) { // Simplify the literal we learned wrt previous substitutions Node learnedLiteral = d_nonClausalLearnedLiterals[i]; Assert(Rewriter::rewrite(learnedLiteral) == learnedLiteral); - Node learnedLiteralNew = d_topLevelSubstitutions.apply(learnedLiteral); + Assert(d_topLevelSubstitutions.apply(learnedLiteral) == learnedLiteral); + Node learnedLiteralNew = newSubstitutions.apply(learnedLiteral); if (learnedLiteral != learnedLiteralNew) { learnedLiteral = Rewriter::rewrite(learnedLiteralNew); } @@ -1717,18 +1784,21 @@ bool SmtEnginePrivate::nonClausalSimplify() { return false; } } - // Solve it with the corresponding theory + + // Solve it with the corresponding theory, possibly adding new + // substitutions to newSubstitutions Trace("simplify") << "SmtEnginePrivate::nonClausalSimplify(): " << "solving " << learnedLiteral << endl; + Theory::PPAssertStatus solveStatus = - d_smt.d_theoryEngine->solve(learnedLiteral, d_topLevelSubstitutions); + d_smt.d_theoryEngine->solve(learnedLiteral, newSubstitutions); switch (solveStatus) { case Theory::PP_ASSERT_STATUS_SOLVED: { // The literal should rewrite to true Trace("simplify") << "SmtEnginePrivate::nonClausalSimplify(): " << "solved " << learnedLiteral << endl; - Assert(Rewriter::rewrite(d_topLevelSubstitutions.apply(learnedLiteral)).isConst()); + Assert(Rewriter::rewrite(newSubstitutions.apply(learnedLiteral)).isConst()); // vector<pair<Node, Node> > equations; // constantPropagations.simplifyLHS(d_topLevelSubstitutions, equations, true); // if (equations.empty()) { @@ -1763,6 +1833,7 @@ bool SmtEnginePrivate::nonClausalSimplify() { Assert(!t.isConst()); Assert(constantPropagations.apply(t) == t); Assert(d_topLevelSubstitutions.apply(t) == t); + Assert(newSubstitutions.apply(t) == t); constantPropagations.addSubstitution(t, c); // vector<pair<Node,Node> > equations;a // constantPropagations.simplifyLHS(t, c, equations, true); @@ -1788,10 +1859,11 @@ bool SmtEnginePrivate::nonClausalSimplify() { // 3. if l -> r is a constant propagation and l is a subterm of l' with l' -> r' another constant propagation, then l'[l/r] -> r' should be a // constant propagation too // 4. each lhs of constantPropagations is different from each rhs - SubstitutionMap::iterator pos = d_topLevelSubstitutions.begin(); - for (; pos != d_topLevelSubstitutions.end(); ++pos) { + for (pos = newSubstitutions.begin(); pos != newSubstitutions.end(); ++pos) { Assert((*pos).first.isVar()); - // Assert(d_topLevelSubstitutions.apply((*pos).second) == (*pos).second); + Assert(d_topLevelSubstitutions.apply((*pos).first) == (*pos).first); + Assert(d_topLevelSubstitutions.apply((*pos).second) == (*pos).second); + Assert(newSubstitutions.apply(newSubstitutions.apply((*pos).second)) == newSubstitutions.apply((*pos).second)); } for (pos = constantPropagations.begin(); pos != constantPropagations.end(); ++pos) { Assert((*pos).second.isConst()); @@ -1815,27 +1887,16 @@ bool SmtEnginePrivate::nonClausalSimplify() { // Resize the learnt d_nonClausalLearnedLiterals.resize(j); - //must add substitutions to model - TheoryModel* m = d_smt.d_theoryEngine->getModel(); - if(m != NULL) { - for( SubstitutionMap::iterator pos = d_topLevelSubstitutions.begin(); pos != d_topLevelSubstitutions.end(); ++pos) { - Node n = (*pos).first; - Node v = (*pos).second; - Trace("model") << "Add substitution : " << n << " " << v << endl; - m->addSubstitution( n, v ); - } - } - hash_set<TNode, TNodeHashFunction> s; - Trace("debugging") << "NonClausal simplify pre-preprocess\n"; + Trace("debugging") << "NonClausal simplify pre-preprocess\n"; for (unsigned i = 0; i < d_assertionsToPreprocess.size(); ++ i) { Node assertion = d_assertionsToPreprocess[i]; - Node assertionNew = d_topLevelSubstitutions.apply(assertion); + Node assertionNew = newSubstitutions.apply(assertion); Trace("debugging") << "assertion = " << assertion << endl; - Trace("debugging") << "assertionNew = " << assertionNew << endl; + Trace("debugging") << "assertionNew = " << assertionNew << endl; if (assertion != assertionNew) { assertion = Rewriter::rewrite(assertionNew); - Trace("debugging") << "rewrite(assertion) = " << assertion << endl; + Trace("debugging") << "rewrite(assertion) = " << assertion << endl; } Assert(Rewriter::rewrite(assertion) == assertion); for (;;) { @@ -1844,11 +1905,11 @@ bool SmtEnginePrivate::nonClausalSimplify() { break; } ++d_smt.d_stats->d_numConstantProps; - Trace("debugging") << "assertionNew = " << assertionNew << endl; + Trace("debugging") << "assertionNew = " << assertionNew << endl; assertion = Rewriter::rewrite(assertionNew); - Trace("debugging") << "assertionNew = " << assertionNew << endl; + Trace("debugging") << "assertionNew = " << assertionNew << endl; } - Trace("debugging") << "\n"; + Trace("debugging") << "\n"; s.insert(assertion); d_assertionsToCheck.push_back(assertion); Trace("simplify") << "SmtEnginePrivate::nonClausalSimplify(): " @@ -1857,34 +1918,44 @@ bool SmtEnginePrivate::nonClausalSimplify() { } d_assertionsToPreprocess.clear(); - NodeBuilder<> learnedBuilder(kind::AND); - Assert(d_realAssertionsEnd <= d_assertionsToCheck.size()); - learnedBuilder << d_assertionsToCheck[d_realAssertionsEnd - 1]; - - if( options::incrementalSolving() || - options::simplificationMode() == SIMPLIFICATION_MODE_INCREMENTAL ) { - // Keep substitutions - SubstitutionMap::iterator pos = d_lastSubstitutionPos; - if(pos == d_topLevelSubstitutions.end()) { - pos = d_topLevelSubstitutions.begin(); - } else { - ++pos; - } - - while(pos != d_topLevelSubstitutions.end()) { - // Push out this substitution - TNode lhs = (*pos).first, rhs = (*pos).second; + // If in incremental mode, add substitutions to the list of assertions + if (d_substitutionsIndex > 0) { + NodeBuilder<> substitutionsBuilder(kind::AND); + substitutionsBuilder << d_assertionsToCheck[d_substitutionsIndex]; + pos = newSubstitutions.begin(); + for (; pos != newSubstitutions.end(); ++pos) { + // Add back this substitution as an assertion + TNode lhs = (*pos).first, rhs = newSubstitutions.apply((*pos).second); Node n = NodeManager::currentNM()->mkNode(lhs.getType().isBoolean() ? kind::IFF : kind::EQUAL, lhs, rhs); - learnedBuilder << n; + substitutionsBuilder << n; Trace("simplify") << "SmtEnginePrivate::nonClausalSimplify(): will notify SAT layer of substitution: " << n << endl; - d_lastSubstitutionPos = pos; - ++pos; + } + if (substitutionsBuilder.getNumChildren() > 1) { + d_assertionsToCheck[d_substitutionsIndex] = + Rewriter::rewrite(Node(substitutionsBuilder)); + } + } + else { + // If not in incremental mode, must add substitutions to model + TheoryModel* m = d_smt.d_theoryEngine->getModel(); + if(m != NULL) { + for(pos = newSubstitutions.begin(); pos != newSubstitutions.end(); ++pos) { + Node n = (*pos).first; + Node v = newSubstitutions.apply((*pos).second); + Trace("model") << "Add substitution : " << n << " " << v << endl; + m->addSubstitution( n, v ); + } } } + NodeBuilder<> learnedBuilder(kind::AND); + Assert(d_realAssertionsEnd <= d_assertionsToCheck.size()); + learnedBuilder << d_assertionsToCheck[d_realAssertionsEnd - 1]; + for (unsigned i = 0; i < d_nonClausalLearnedLiterals.size(); ++ i) { Node learned = d_nonClausalLearnedLiterals[i]; - Node learnedNew = d_topLevelSubstitutions.apply(learned); + Assert(d_topLevelSubstitutions.apply(learned) == learned); + Node learnedNew = newSubstitutions.apply(learned); if (learned != learnedNew) { learned = Rewriter::rewrite(learnedNew); } @@ -1908,10 +1979,11 @@ bool SmtEnginePrivate::nonClausalSimplify() { } d_nonClausalLearnedLiterals.clear(); - SubstitutionMap::iterator pos = constantPropagations.begin(); - for (; pos != constantPropagations.end(); ++pos) { + + for (pos = constantPropagations.begin(); pos != constantPropagations.end(); ++pos) { Node cProp = (*pos).first.eqNode((*pos).second); - Node cPropNew = d_topLevelSubstitutions.apply(cProp); + Assert(d_topLevelSubstitutions.apply(cProp) == cProp); + Node cPropNew = newSubstitutions.apply(cProp); if (cProp != cPropNew) { cProp = Rewriter::rewrite(cPropNew); Assert(Rewriter::rewrite(cProp) == cProp); @@ -1926,6 +1998,11 @@ bool SmtEnginePrivate::nonClausalSimplify() { << cProp << endl; } + // Add new substitutions to topLevelSubstitutions + // Note that we don't have to keep rhs's in full solved form + // because SubstitutionMap::apply does a fixed-point iteration when substituting + d_topLevelSubstitutions.addSubstitutions(newSubstitutions); + if(learnedBuilder.getNumChildren() > 1) { d_assertionsToCheck[d_realAssertionsEnd - 1] = Rewriter::rewrite(Node(learnedBuilder)); @@ -1939,7 +2016,7 @@ bool SmtEnginePrivate::nonClausalSimplify() { void SmtEnginePrivate::bvToBool() { Trace("bv-to-bool") << "SmtEnginePrivate::bvToBool()" << endl; std::vector<Node> new_assertions; - d_smt.d_theoryEngine->ppBvToBool(d_assertionsToCheck, new_assertions); + d_smt.d_theoryEngine->ppBvToBool(d_assertionsToCheck, new_assertions); for (unsigned i = 0; i < d_assertionsToCheck.size(); ++ i) { d_assertionsToCheck[i] = Rewriter::rewrite(new_assertions[i]); } @@ -1950,8 +2027,7 @@ void SmtEnginePrivate::simpITE() { Trace("simplify") << "SmtEnginePrivate::simpITE()" << endl; - for (unsigned i = 0; i < d_assertionsToCheck.size(); ++ i) { - + for (unsigned i = 0; i < d_assertionsToCheck.size(); ++i) { d_assertionsToCheck[i] = d_smt.d_theoryEngine->ppSimpITE(d_assertionsToCheck[i]); } } @@ -2039,7 +2115,6 @@ void SmtEnginePrivate::traceBackToAssertions(const std::vector<Node>& nodes, std size_t SmtEnginePrivate::removeFromConjunction(Node& n, const std::hash_set<unsigned>& toRemove) { Assert(n.getKind() == kind::AND); - Node trueNode = NodeManager::currentNM()->mkConst(true); size_t removals = 0; for(Node::iterator j = n.begin(); j != n.end(); ++j) { size_t subremovals = 0; @@ -2070,7 +2145,7 @@ size_t SmtEnginePrivate::removeFromConjunction(Node& n, const std::hash_set<unsi } } if(b.getNumChildren() == 0) { - n = trueNode; + n = d_true; b.clear(); } else if(b.getNumChildren() == 1) { n = b[0]; @@ -2383,11 +2458,10 @@ void SmtEnginePrivate::doMiplibTrick() { } if(!removeAssertions.empty()) { Debug("miplib") << "SmtEnginePrivate::simplify(): scrubbing miplib encoding..." << endl; - Node trueNode = nm->mkConst(true); for(size_t i = 0; i < d_realAssertionsEnd; ++i) { if(removeAssertions.find(d_assertionsToCheck[i].getId()) != removeAssertions.end()) { Debug("miplib") << "SmtEnginePrivate::simplify(): - removing " << d_assertionsToCheck[i] << endl; - d_assertionsToCheck[i] = trueNode; + d_assertionsToCheck[i] = d_true; ++d_smt.d_stats->d_numMiplibAssertionsRemoved; } else if(d_assertionsToCheck[i].getKind() == kind::AND) { size_t removals = removeFromConjunction(d_assertionsToCheck[i], removeAssertions); @@ -2453,6 +2527,7 @@ bool SmtEnginePrivate::simplifyAssertions() d_assertionsToCheck.swap(d_assertionsToPreprocess); } + dumpAssertions("post-nonclausal", d_assertionsToCheck); Trace("smt") << "POST nonClausalSimplify" << endl; Debug("smt") << " d_assertionsToPreprocess: " << d_assertionsToPreprocess.size() << endl; Debug("smt") << " d_assertionsToCheck : " << d_assertionsToCheck.size() << endl; @@ -2470,6 +2545,7 @@ bool SmtEnginePrivate::simplifyAssertions() } } + dumpAssertions("post-theorypp", d_assertionsToCheck); Trace("smt") << "POST theoryPP" << endl; Debug("smt") << " d_assertionsToPreprocess: " << d_assertionsToPreprocess.size() << endl; Debug("smt") << " d_assertionsToCheck : " << d_assertionsToCheck.size() << endl; @@ -2480,6 +2556,7 @@ bool SmtEnginePrivate::simplifyAssertions() simpITE(); } + dumpAssertions("post-itesimp", d_assertionsToCheck); Trace("smt") << "POST iteSimp" << endl; Debug("smt") << " d_assertionsToPreprocess: " << d_assertionsToPreprocess.size() << endl; Debug("smt") << " d_assertionsToCheck : " << d_assertionsToCheck.size() << endl; @@ -2490,6 +2567,7 @@ bool SmtEnginePrivate::simplifyAssertions() unconstrainedSimp(); } + dumpAssertions("post-unconstrained", d_assertionsToCheck); Trace("smt") << "POST unconstrainedSimp" << endl; Debug("smt") << " d_assertionsToPreprocess: " << d_assertionsToPreprocess.size() << endl; Debug("smt") << " d_assertionsToCheck : " << d_assertionsToCheck.size() << endl; @@ -2506,6 +2584,7 @@ bool SmtEnginePrivate::simplifyAssertions() } } + dumpAssertions("post-repeatsimp", d_assertionsToCheck); Trace("smt") << "POST repeatSimp" << endl; Debug("smt") << " d_assertionsToPreprocess: " << d_assertionsToPreprocess.size() << endl; Debug("smt") << " d_assertionsToCheck : " << d_assertionsToCheck.size() << endl; @@ -2534,11 +2613,26 @@ Result SmtEngine::check() { Trace("smt") << "SmtEngine::check(): processing assertions" << endl; d_private->processAssertions(); + // Turn off stop only for QF_LRA + // TODO: Bring up in a meeting where to put this + if(options::decisionStopOnly() && !options::decisionMode.wasSetByUser() ){ + if( // QF_LRA + (not d_logic.isQuantified() && + d_logic.isPure(THEORY_ARITH) && d_logic.isLinear() && !d_logic.isDifferenceLogic() && !d_logic.areIntegersUsed() + )){ + if(d_private->d_iteSkolemMap.empty()){ + options::decisionStopOnly.set(false); + d_decisionEngine->clearStrategies(); + Trace("smt") << "SmtEngine::check(): turning off stop only" << endl; + } + } + } + unsigned long millis = 0; if(d_timeBudgetCumulative != 0) { millis = getTimeRemaining(); if(millis == 0) { - return Result(Result::VALIDITY_UNKNOWN, Result::TIMEOUT); + return Result(Result::VALIDITY_UNKNOWN, Result::TIMEOUT, d_filename); } } if(d_timeBudgetPerCall != 0 && (millis == 0 || d_timeBudgetPerCall < millis)) { @@ -2549,7 +2643,7 @@ Result SmtEngine::check() { if(d_resourceBudgetCumulative != 0) { resource = getResourceRemaining(); if(resource == 0) { - return Result(Result::VALIDITY_UNKNOWN, Result::RESOURCEOUT); + return Result(Result::VALIDITY_UNKNOWN, Result::RESOURCEOUT, d_filename); } } if(d_resourceBudgetPerCall != 0 && (resource == 0 || d_resourceBudgetPerCall < resource)) { @@ -2570,13 +2664,13 @@ Result SmtEngine::check() { Trace("limit") << "SmtEngine::check(): cumulative millis " << d_cumulativeTimeUsed << ", conflicts " << d_cumulativeResourceUsed << endl; - return result; + return Result(result, d_filename); } Result SmtEngine::quickCheck() { Assert(d_fullyInited); Trace("smt") << "SMT quickCheck()" << endl; - return Result(Result::VALIDITY_UNKNOWN, Result::REQUIRES_FULL_CHECK); + return Result(Result::VALIDITY_UNKNOWN, Result::REQUIRES_FULL_CHECK, d_filename); } @@ -2655,14 +2749,26 @@ void SmtEnginePrivate::processAssertions() { Assert(d_assertionsToCheck.size() == 0); - // any assertions added beyond realAssertionsEnd must NOT affect the - // equisatisfiability - d_realAssertionsEnd = d_assertionsToPreprocess.size(); - if(d_realAssertionsEnd == 0) { + if (d_assertionsToPreprocess.size() == 0) { // nothing to do return; } + if (d_assertionsProcessed && + ( options::incrementalSolving() || + options::simplificationMode() == SIMPLIFICATION_MODE_INCREMENTAL )) { + // Placeholder for storing substitutions + d_substitutionsIndex = d_assertionsToPreprocess.size(); + d_assertionsToPreprocess.push_back(NodeManager::currentNM()->mkConst<bool>(true)); + } + + // Add dummy assertion in last position - to be used as a + // placeholder for any new assertions to get added + d_assertionsToPreprocess.push_back(NodeManager::currentNM()->mkConst<bool>(true)); + // any assertions added beyond realAssertionsEnd must NOT affect the + // equisatisfiability + d_realAssertionsEnd = d_assertionsToPreprocess.size(); + // Assertions are NOT guaranteed to be rewritten by this point dumpAssertions("pre-definition-expansion", d_assertionsToPreprocess); @@ -2671,7 +2777,7 @@ void SmtEnginePrivate::processAssertions() { Trace("simplify") << "SmtEnginePrivate::simplify(): expanding definitions" << endl; TimerStat::CodeTimer codeTimer(d_smt.d_stats->d_definitionExpansionTime); hash_map<Node, Node, NodeHashFunction> cache; - for (unsigned i = 0; i < d_assertionsToPreprocess.size(); ++ i) { + for(unsigned i = 0; i < d_assertionsToPreprocess.size(); ++ i) { d_assertionsToPreprocess[i] = expandDefinitions(d_assertionsToPreprocess[i], cache); } @@ -2740,6 +2846,7 @@ void SmtEnginePrivate::processAssertions() { Debug("smt") << " d_assertionsToCheck : " << d_assertionsToCheck.size() << endl; dumpAssertions("pre-substitution", d_assertionsToPreprocess); + // Apply the substitutions we already have, and normalize Chat() << "applying substitutions..." << endl; Trace("simplify") << "SmtEnginePrivate::nonClausalSimplify(): " @@ -2754,6 +2861,14 @@ void SmtEnginePrivate::processAssertions() { // Assertions ARE guaranteed to be rewritten by this point + if( d_smt.d_logic.isTheoryEnabled(THEORY_STRINGS) ) { + CVC4::theory::strings::StringsPreprocess sp; + sp.simplify( d_assertionsToPreprocess ); + for (unsigned i = 0; i < d_assertionsToPreprocess.size(); ++ i) { + d_assertionsToPreprocess[i] = Rewriter::rewrite( d_assertionsToPreprocess[i] ); + } + } + dumpAssertions("pre-skolem-quant", d_assertionsToPreprocess); if( options::preSkolemQuant() ){ //apply pre-skolemization to existential quantifiers @@ -2778,6 +2893,12 @@ void SmtEnginePrivate::processAssertions() { }while( success ); } + Trace("fo-rsn-enable") << std::endl; + if( options::foPropQuant() ){ + FirstOrderPropagation fop; + fop.simplify( d_assertionsToPreprocess ); + } + if( options::sortInference() ){ //sort inference technique d_smt.d_theoryEngine->getSortInference()->simplify( d_assertionsToPreprocess ); @@ -2798,7 +2919,7 @@ void SmtEnginePrivate::processAssertions() { } dumpAssertions("post-static-learning", d_assertionsToCheck); - // Lift bit-vectors of size 1 to bool + // Lift bit-vectors of size 1 to bool if(options::bvToBool()) { Chat() << "...doing bvToBool..." << endl; bvToBool(); @@ -2808,7 +2929,7 @@ void SmtEnginePrivate::processAssertions() { Debug("smt") << " d_assertionsToPreprocess: " << d_assertionsToPreprocess.size() << endl; Debug("smt") << " d_assertionsToCheck : " << d_assertionsToCheck.size() << endl; - + dumpAssertions("pre-ite-removal", d_assertionsToCheck); { Chat() << "removing term ITEs..." << endl; @@ -2883,7 +3004,7 @@ void SmtEnginePrivate::processAssertions() { Rewriter::rewrite(Node(builder)); } // For some reason this is needed for some benchmarks, such as - // http://church.cims.nyu.edu/benchmarks/smtlib2/QF_AUFBV/dwp_formulas/try5_small_difret_functions_dwp_tac.re_node_set_remove_at.il.dwp.smt2 + // http://cvc4.cs.nyu.edu/benchmarks/smtlib2/QF_AUFBV/dwp_formulas/try5_small_difret_functions_dwp_tac.re_node_set_remove_at.il.dwp.smt2 // Figure it out later removeITEs(); // Assert(iteRewriteAssertionsEnd == d_assertionsToCheck.size()); @@ -2938,6 +3059,8 @@ void SmtEnginePrivate::processAssertions() { } } + d_assertionsProcessed = true; + d_assertionsToCheck.clear(); d_iteSkolemMap.clear(); } @@ -2945,6 +3068,11 @@ void SmtEnginePrivate::processAssertions() { void SmtEnginePrivate::addFormula(TNode n) throw(TypeCheckingException, LogicException) { + if (n == d_true) { + // nothing to do + return; + } + Trace("smt") << "SmtEnginePrivate::addFormula(" << n << ")" << endl; // Add the normalized formula to the queue @@ -3130,7 +3258,7 @@ Result SmtEngine::assertFormula(const Expr& ex) throw(TypeCheckingException, Log return quickCheck().asValidityResult(); } -Node SmtEngine::postprocess(TNode node, TypeNode expectedType) { +Node SmtEngine::postprocess(TNode node, TypeNode expectedType) const { ModelPostprocessor mpost; NodeVisitor<ModelPostprocessor> visitor; Node value = visitor.run(mpost, node); @@ -3185,7 +3313,7 @@ Expr SmtEngine::expandDefinitions(const Expr& ex) throw(TypeCheckingException, L return n.toExpr(); } -Expr SmtEngine::getValue(const Expr& ex) throw(ModalException, TypeCheckingException, LogicException) { +Expr SmtEngine::getValue(const Expr& ex) const throw(ModalException, TypeCheckingException, LogicException) { Assert(ex.getExprManager() == d_exprManager); SmtScope smts(this); @@ -3300,7 +3428,7 @@ CVC4::SExpr SmtEngine::getAssignment() throw(ModalException) { } if(d_assignments == NULL) { - return SExpr(); + return SExpr(vector<SExpr>()); } vector<SExpr> sexprs; @@ -3340,7 +3468,7 @@ CVC4::SExpr SmtEngine::getAssignment() throw(ModalException) { return SExpr(sexprs); } -void SmtEngine::addToModelCommandAndDump(const Command& c, bool isGlobal, bool userVisible, const char* dumpTag) { +void SmtEngine::addToModelCommandAndDump(const Command& c, uint32_t flags, bool userVisible, const char* dumpTag) { Trace("smt") << "SMT addToModelCommandAndDump(" << c << ")" << endl; SmtScope smts(this); // If we aren't yet fully inited, the user might still turn on @@ -3353,7 +3481,7 @@ void SmtEngine::addToModelCommandAndDump(const Command& c, bool isGlobal, bool u // and expects to find their cardinalities in the model. if(/* userVisible && */ (!d_fullyInited || options::produceModels())) { doPendingPops(); - if(isGlobal) { + if(flags & ExprManager::VAR_FLAG_GLOBAL) { d_modelGlobalCommands.push_back(c.clone()); } else { d_modelCommands->push_back(c.clone()); @@ -3391,7 +3519,9 @@ Model* SmtEngine::getModel() throw(ModalException) { "Cannot get model when produce-models options is off."; throw ModalException(msg); } - return d_theoryEngine->getModel(); + TheoryModel* m = d_theoryEngine->getModel(); + m->d_inputName = d_filename; + return m; } void SmtEngine::checkModel(bool hardFailure) { @@ -3412,13 +3542,8 @@ void SmtEngine::checkModel(bool hardFailure) { // Check individual theory assertions d_theoryEngine->checkTheoryAssertionsWithModel(); - if(Notice.isOn()) { - // This operator<< routine is non-const (i.e., takes a non-const Model&). - // This confuses the Notice() output routines, so extract the ostream - // from it and output it "manually." Should be fixed by making Model - // accessors const. - Notice.getStream() << *m; - } + // Output the model + Notice() << *m; // We have a "fake context" for the substitution map (we don't need it // to be context-dependent) @@ -3506,6 +3631,12 @@ void SmtEngine::checkModel(bool hardFailure) { Debug("boolean-terms") << "++ got " << n << endl; Notice() << "SmtEngine::checkModel(): -- substitutes to " << n << endl; + if(Theory::theoryOf(n) != THEORY_REWRITERULES) { + // 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; @@ -3527,6 +3658,7 @@ void SmtEngine::checkModel(bool hardFailure) { // but don't show up in our substitution map above. n = m->getValue(n); Notice() << "SmtEngine::checkModel(): -- model-substitutes to " << n << endl; + AlwaysAssert(!hardFailure || n.isConst() || n.getKind() == kind::LAMBDA); // The result should be == true. if(n != NodeManager::currentNM()->mkConst(true)) { @@ -3612,6 +3744,11 @@ void SmtEngine::push() throw(ModalException, LogicException) { d_needPostsolve = false; } + // The problem isn't really "extended" yet, but this disallows + // get-model after a push, simplifying our lives somewhat and + // staying symmtric with pop. + d_problemExtended = true; + d_userLevels.push_back(d_userContext->getLevel()); internalPush(); Trace("userpushpop") << "SmtEngine: pushed to level " @@ -3638,6 +3775,14 @@ void SmtEngine::pop() throw(ModalException) { d_needPostsolve = false; } + // The problem isn't really "extended" yet, but this disallows + // get-model after a pop, simplifying our lives somewhat. It might + // not be strictly necessary to do so, since the pops occur lazily, + // but also it would be weird to have a legally-executed (get-model) + // that only returns a subset of the assignment (because the rest + // is no longer in scope!). + d_problemExtended = true; + AlwaysAssert(d_userContext->getLevel() > 0); AlwaysAssert(d_userLevels.back() < d_userContext->getLevel()); while (d_userLevels.back() < d_userContext->getLevel()) { diff --git a/src/smt/smt_engine.h b/src/smt/smt_engine.h index a22e34c21..9655297b3 100644 --- a/src/smt/smt_engine.h +++ b/src/smt/smt_engine.h @@ -189,9 +189,9 @@ class CVC4_PUBLIC SmtEngine { bool d_fullyInited; /** - * Whether or not we have added any assertions/declarations/definitions - * since the last checkSat/query (and therefore we're not responsible - * for an assignment). + * Whether or not we have added any assertions/declarations/definitions, + * or done push/pop, since the last checkSat/query, and therefore we're + * not responsible for models or proofs. */ bool d_problemExtended; @@ -234,6 +234,16 @@ class CVC4_PUBLIC SmtEngine { Result d_status; /** + * The name of the input (if any). + */ + std::string d_filename; + + /** + * Verbosity of various commands. + */ + std::map<std::string, Integer> d_commandVerbosity; + + /** * A private utility class to SmtEngine. */ smt::SmtEnginePrivate* d_private; @@ -249,7 +259,7 @@ class CVC4_PUBLIC SmtEngine { * like turning datatypes back into tuples, length-1-bitvectors back * into booleans, etc. */ - Node postprocess(TNode n, TypeNode expectedType); + Node postprocess(TNode n, TypeNode expectedType) const; /** * This is something of an "init" procedure, but is idempotent; call @@ -326,7 +336,7 @@ class CVC4_PUBLIC SmtEngine { * Add to Model command. This is used for recording a command * that should be reported during a get-model call. */ - void addToModelCommandAndDump(const Command& c, bool isGlobal = false, bool userVisible = true, const char* dumpTag = "declarations"); + void addToModelCommandAndDump(const Command& c, uint32_t flags = 0, bool userVisible = true, const char* dumpTag = "declarations"); /** * Get the model (only if immediately preceded by a SAT @@ -350,12 +360,12 @@ public: /** * Set the logic of the script. */ - void setLogic(const std::string& logic) throw(ModalException); + void setLogic(const std::string& logic) throw(ModalException, LogicException); /** * Set the logic of the script. */ - void setLogic(const char* logic) throw(ModalException); + void setLogic(const char* logic) throw(ModalException, LogicException); /** * Set the logic of the script. @@ -444,7 +454,7 @@ public: * by a SAT or INVALID query). Only permitted if the SmtEngine is * set to operate interactively and produce-models is on. */ - Expr getValue(const Expr& e) throw(ModalException, TypeCheckingException, LogicException); + Expr getValue(const Expr& e) const throw(ModalException, TypeCheckingException, LogicException); /** * Add a function to the set of expressions whose value is to be @@ -497,9 +507,11 @@ public: /** * Set a resource limit for SmtEngine operations. This is like a time * limit, but it's deterministic so that reproducible results can be - * obtained. However, please note that it may not be deterministic - * between different versions of CVC4, or even the same version on - * different platforms. + * obtained. Currently, it's based on the number of conflicts. + * However, please note that the definition may change between different + * versions of CVC4 (as may the number of conflicts required, anyway), + * and it might even be different between instances of the same version + * of CVC4 on different platforms. * * A cumulative and non-cumulative (per-call) resource limit can be * set at the same time. A call to setResourceLimit() with diff --git a/src/smt/smt_options_template.cpp b/src/smt/smt_options_template.cpp index 638cf2f83..4edd91a8d 100644 --- a/src/smt/smt_options_template.cpp +++ b/src/smt/smt_options_template.cpp @@ -44,11 +44,29 @@ void SmtEngine::setOption(const std::string& key, const CVC4::SExpr& value) Dump("benchmark") << SetOptionCommand(key, value); } + if(key == "command-verbosity") { + if(!value.isAtom()) { + const vector<SExpr>& cs = value.getChildren(); + if(cs.size() == 2 && + (cs[0].isKeyword() || cs[0].isString()) && + cs[1].isInteger()) { + string c = cs[0].getValue(); + const Integer& v = cs[1].getIntegerValue(); + if(v < 0 || v > 2) { + throw OptionException("command-verbosity must be 0, 1, or 2"); + } + d_commandVerbosity[c] = v; + return; + } + } + throw OptionException("command-verbosity value must be a tuple (command-name, integer)"); + } + string optionarg = value.getValue(); ${smt_setoption_handlers} -#line 52 "${template}" +#line 70 "${template}" throw UnrecognizedOptionException(key); } @@ -59,13 +77,56 @@ CVC4::SExpr SmtEngine::getOption(const std::string& key) const NodeManagerScope nms(d_nodeManager); Trace("smt") << "SMT getOption(" << key << ")" << endl; + + if(key.length() >= 18 && + key.compare(0, 18, "command-verbosity:") == 0) { + map<string, Integer>::const_iterator i = d_commandVerbosity.find(key.c_str() + 18); + if(i != d_commandVerbosity.end()) { + return (*i).second; + } + i = d_commandVerbosity.find("*"); + if(i != d_commandVerbosity.end()) { + return (*i).second; + } + return Integer(2); + } + if(Dump.isOn("benchmark")) { Dump("benchmark") << GetOptionCommand(key); } + if(key == "command-verbosity") { + vector<SExpr> result; + SExpr defaultVerbosity; + for(map<string, Integer>::const_iterator i = d_commandVerbosity.begin(); + i != d_commandVerbosity.end(); + ++i) { + vector<SExpr> v; + v.push_back((*i).first); + v.push_back((*i).second); + if((*i).first == "*") { + // put the default at the end of the SExpr + defaultVerbosity = v; + } else { + result.push_back(v); + } + } + // put the default at the end of the SExpr + if(!defaultVerbosity.isAtom()) { + result.push_back(defaultVerbosity); + } else { + // ensure the default is always listed + vector<SExpr> v; + v.push_back("*"); + v.push_back(Integer(2)); + result.push_back(v); + } + return result; + } + ${smt_getoption_handlers} -#line 69 "${template}" +#line 130 "${template}" throw UnrecognizedOptionException(key); } diff --git a/src/theory/Makefile.am b/src/theory/Makefile.am index bd7b881e1..860075aa8 100644 --- a/src/theory/Makefile.am +++ b/src/theory/Makefile.am @@ -3,7 +3,7 @@ AM_CPPFLAGS = \ -I@builddir@/.. -I@srcdir@/../include -I@srcdir@/.. AM_CXXFLAGS = -Wall -Wno-unknown-pragmas $(FLAG_VISIBILITY_HIDDEN) -SUBDIRS = builtin booleans uf arith bv arrays datatypes quantifiers rewriterules +SUBDIRS = builtin booleans uf arith bv arrays datatypes quantifiers rewriterules idl strings DIST_SUBDIRS = $(SUBDIRS) example noinst_LTLIBRARIES = libtheory.la @@ -42,7 +42,9 @@ libtheory_la_SOURCES = \ model.h \ model.cpp \ rep_set.h \ - rep_set.cpp + rep_set.cpp \ + atom_requests.h \ + atom_requests.cpp nodist_libtheory_la_SOURCES = \ rewriter_tables.h \ @@ -58,7 +60,9 @@ libtheory_la_LIBADD = \ @builddir@/bv/libbv.la \ @builddir@/datatypes/libdatatypes.la \ @builddir@/quantifiers/libquantifiers.la \ - @builddir@/rewriterules/librewriterules.la + @builddir@/rewriterules/librewriterules.la \ + @builddir@/idl/libidl.la \ + @builddir@/strings/libstrings.la EXTRA_DIST = \ logic_info.i \ diff --git a/src/theory/arith/Makefile.am b/src/theory/arith/Makefile.am index 3c664d806..620b8a121 100644 --- a/src/theory/arith/Makefile.am +++ b/src/theory/arith/Makefile.am @@ -51,8 +51,8 @@ libarith_la_SOURCES = \ soi_simplex.cpp \ approx_simplex.h \ approx_simplex.cpp \ - pure_update_simplex.h \ - pure_update_simplex.cpp \ + attempt_solution_simplex.h \ + attempt_solution_simplex.cpp \ theory_arith.h \ theory_arith.cpp \ theory_arith_private_forward.h \ diff --git a/src/theory/arith/approx_simplex.cpp b/src/theory/arith/approx_simplex.cpp index d6be9f657..0f5a0fd4e 100644 --- a/src/theory/arith/approx_simplex.cpp +++ b/src/theory/arith/approx_simplex.cpp @@ -2,6 +2,7 @@ #include "theory/arith/approx_simplex.h" #include "theory/arith/normal_form.h" +#include "theory/arith/constraint.h" #include <math.h> #include <cmath> @@ -94,6 +95,10 @@ public: return Solution(); } + virtual ArithRatPairVec heuristicOptCoeffs() const{ + return ArithRatPairVec(); + } + virtual ApproxResult solveMIP(){ return ApproxError; } @@ -111,8 +116,14 @@ public: /* Begin the declaration of GLPK specific code. */ #ifdef CVC4_USE_GLPK extern "C" { -#include <glpk.h> -} +/* Sometimes the header is in a subdirectory glpk/, sometimes not. + * The configure script figures it out. */ +#ifdef HAVE_GLPK_GLPK_H +# include <glpk/glpk.h> +#else /* HAVE_GLPK_GLPK_H */ +# include <glpk.h> +#endif /* HAVE_GLPK_GLPK_H */ +}/* extern "C" */ namespace CVC4 { namespace theory { @@ -132,6 +143,8 @@ private: bool d_solvedRelaxation; bool d_solvedMIP; + static int s_verbosity; + public: ApproxGLPK(const ArithVariables& vars); ~ApproxGLPK(); @@ -141,16 +154,22 @@ public: return extractSolution(false); } + virtual ArithRatPairVec heuristicOptCoeffs() const; + virtual ApproxResult solveMIP(); virtual Solution extractMIP() const{ return extractSolution(true); } virtual void setOptCoeffs(const ArithRatPairVec& ref); + static void printGLPKStatus(int status, std::ostream& out); private: Solution extractSolution(bool mip) const; + int guessDir(ArithVar v) const; }; +int ApproxGLPK::s_verbosity = 0; + }/* CVC4::theory::arith namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ @@ -220,8 +239,10 @@ 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; - //cout << v << " "; - //d_vars.printModel(v, cout); + if(s_verbosity >= 2){ + Message() << v << " "; + d_vars.printModel(v, Message()); + } int type; double lb = 0.0; @@ -301,6 +322,189 @@ ApproxGLPK::ApproxGLPK(const ArithVariables& avars) : delete[] ja; delete[] ar; } +int ApproxGLPK::guessDir(ArithVar v) const{ + if(d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){ + return -1; + }else if(!d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){ + return 1; + }else if(!d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){ + return 0; + }else{ + int ubSgn = d_vars.getUpperBound(v).sgn(); + int lbSgn = d_vars.getLowerBound(v).sgn(); + + if(ubSgn != 0 && lbSgn == 0){ + return -1; + }else if(ubSgn == 0 && lbSgn != 0){ + return 1; + } + + return 1; + } +} + +ArithRatPairVec ApproxGLPK::heuristicOptCoeffs() const{ + ArithRatPairVec ret; + + // Strategies are guess: + // 1 simple shared "ceiling" variable: danoint, pk1 + // x1 >= c, x1 >= tmp1, x1 >= tmp2, ... + // 1 large row: fixnet, vpm2, pp08a + // (+ .......... ) <= c + // Not yet supported: + // 1 complex shared "ceiling" variable: opt1217 + // x1 >= c, x1 >= (+ ..... ), x1 >= (+ ..... ) + // and all of the ... are the same sign + + + // Candidates: + // 1) Upper and lower bounds are not equal. + // 2) The variable is not integer + // 3a) For columns look for a ceiling variable + // 3B) For rows look for a large row with + + DenseMap<BoundCounts> d_colCandidates; + DenseMap<uint32_t> d_rowCandidates; + + double sumRowLength = 0.0; + uint32_t maxRowLength = 0; + 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()); + } + + int type; + if(d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){ + if(d_vars.boundsAreEqual(v)){ + type = GLP_FX; + }else{ + type = GLP_DB; + } + }else if(d_vars.hasUpperBound(v) && !d_vars.hasLowerBound(v)){ + type = GLP_UP; + }else if(!d_vars.hasUpperBound(v) && d_vars.hasLowerBound(v)){ + type = GLP_LO; + }else{ + type = GLP_FR; + } + + if(type != GLP_FX && type != GLP_FR){ + + if(d_vars.isSlack(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); + }else if(!d_vars.isInteger(v)){ + d_colCandidates.set(v, BoundCounts()); + } + } + } + + uint32_t maxCount = 0; + for(DenseMap<int>::const_iterator i = d_rowIndices.begin(), i_end = d_rowIndices.end(); i != i_end; ++i){ + ArithVar v = *i; + + bool lbCap = d_vars.hasLowerBound(v) && !d_vars.hasUpperBound(v); + bool ubCap = !d_vars.hasLowerBound(v) && d_vars.hasUpperBound(v); + + if(lbCap || ubCap){ + Constraint b = lbCap ? d_vars.getLowerBoundConstraint(v) + : d_vars.getUpperBoundConstraint(v); + + if(!(b->getValue()).noninfinitesimalIsZero()){ continue; } + + Polynomial poly = Polynomial::parsePolynomial(d_vars.asNode(v)); + if(poly.size() != 2) { continue; } + + Polynomial::iterator j = poly.begin(); + Monomial first = *j; + ++j; + Monomial second = *j; + + bool firstIsPos = first.constantIsPositive(); + bool secondIsPos = second.constantIsPositive(); + + if(firstIsPos == secondIsPos){ continue; } + + Monomial pos = firstIsPos == lbCap ? first : second; + Monomial neg = firstIsPos != lbCap ? first : second; + // pos >= neg + VarList p = pos.getVarList(); + VarList n = neg.getVarList(); + if(d_vars.hasArithVar(p.getNode())){ + ArithVar ap = d_vars.asArithVar(p.getNode()); + if( d_colCandidates.isKey(ap)){ + BoundCounts bc = d_colCandidates.get(ap); + bc = BoundCounts(bc.lowerBoundCount(), bc.upperBoundCount()+1); + maxCount = std::max(maxCount, bc.upperBoundCount()); + d_colCandidates.set(ap, bc); + } + } + if(d_vars.hasArithVar(n.getNode())){ + ArithVar an = d_vars.asArithVar(n.getNode()); + if( d_colCandidates.isKey(an)){ + BoundCounts bc = d_colCandidates.get(an); + bc = BoundCounts(bc.lowerBoundCount()+1, bc.upperBoundCount()); + maxCount = std::max(maxCount, bc.lowerBoundCount()); + d_colCandidates.set(an, bc); + } + } + } + } + + // Attempt row + double avgRowLength = d_rowCandidates.size() >= 1 ? + ( sumRowLength / d_rowCandidates.size() ) : 0.0; + + // There is a large row among the candidates + bool guessARowCandidate = maxRowLength >= (10.0 * avgRowLength); + + double rowLengthReq = (maxRowLength * .9); + + if(guessARowCandidate){ + for(DenseMap<uint32_t>::const_iterator i = d_rowCandidates.begin(), iend =d_rowCandidates.end(); i != iend; ++i ){ + ArithVar r = *i; + uint32_t len = d_rowCandidates[r]; + + int dir = guessDir(r); + if(len >= rowLengthReq){ + if(s_verbosity >= 1){ + Message() << "high row " << r << " " << len << " " << avgRowLength << " " << dir<< endl; + d_vars.printModel(r, Message()); + } + ret.push_back(ArithRatPair(r, Rational(dir))); + } + } + } + + // Attempt columns + bool guessAColCandidate = maxCount >= 4; + if(guessAColCandidate){ + for(DenseMap<BoundCounts>::const_iterator i = d_colCandidates.begin(), iend = d_colCandidates.end(); i != iend; ++i ){ + ArithVar c = *i; + BoundCounts bc = d_colCandidates[c]; + + int dir = guessDir(c); + double ubScore = double(bc.upperBoundCount()) / maxCount; + double lbScore = double(bc.lowerBoundCount()) / maxCount; + if(ubScore >= .9 || lbScore >= .9){ + if(s_verbosity >= 1){ + Message() << "high col " << c << " " << bc << " " << ubScore << " " << lbScore << " " << dir << endl; + d_vars.printModel(c, Message()); + } + ret.push_back(ArithRatPair(c, Rational(c))); + } + } + } + + + return ret; +} void ApproxGLPK::setOptCoeffs(const ArithRatPairVec& ref){ DenseMap<double> nbCoeffs; @@ -346,6 +550,7 @@ void ApproxGLPK::setOptCoeffs(const ArithRatPairVec& ref){ } } + /* * rough strategy: * real relaxation @@ -361,7 +566,7 @@ void ApproxGLPK::setOptCoeffs(const ArithRatPairVec& ref){ * check with FCSimplex */ -static void printGLPKStatus(int status, std::ostream& out){ +void ApproxGLPK::printGLPKStatus(int status, std::ostream& out){ switch(status){ case GLP_OPT: out << "GLP_OPT" << endl; @@ -406,77 +611,90 @@ ApproximateSimplex::Solution ApproxGLPK::extractSolution(bool mip) const{ int glpk_index = isSlack ? 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); - //cout << "assignment " << vi << endl; + if(s_verbosity >= 2){ + Message() << "assignment " << vi << endl; + } + + bool useDefaultAssignment = false; switch(status){ case GLP_BS: - //cout << "basic" << endl; + //Message() << "basic" << endl; newBasis.add(vi); + useDefaultAssignment = true; break; case GLP_NL: case GLP_NS: if(!mip){ - //cout << "non-basic lb" << endl; + if(s_verbosity >= 2){ Message() << "non-basic lb" << endl; } newValues.set(vi, d_vars.getLowerBound(vi)); - break; - }// intentionally fall through otherwise + }else{// intentionally fall through otherwise + useDefaultAssignment = true; + } + break; case GLP_NU: if(!mip){ - // cout << "non-basic ub" << endl; + if(s_verbosity >= 2){ Message() << "non-basic ub" << endl; } newValues.set(vi, d_vars.getUpperBound(vi)); - break; - }// intentionally fall through otherwise + }else {// intentionally fall through otherwise + useDefaultAssignment = true; + } + break; default: { - // cout << "non-basic other" << endl; + useDefaultAssignment = true; + } + break; + } + + 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)); + const DeltaRational& oldAssign = d_vars.getAssignment(vi); - 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)); - const DeltaRational& oldAssign = d_vars.getAssignment(vi); + if(d_vars.hasLowerBound(vi) && + roughlyEqual(newAssign, d_vars.getLowerBound(vi).approx(SMALL_FIXED_DELTA))){ + //Message() << " to lb" << endl; - if(d_vars.hasLowerBound(vi) && - roughlyEqual(newAssign, d_vars.getLowerBound(vi).approx(SMALL_FIXED_DELTA))){ - //cout << " to lb" << endl; + newValues.set(vi, d_vars.getLowerBound(vi)); + }else if(d_vars.hasUpperBound(vi) && + roughlyEqual(newAssign, d_vars.getUpperBound(vi).approx(SMALL_FIXED_DELTA))){ + newValues.set(vi, d_vars.getUpperBound(vi)); + // Message() << " to ub" << endl; + }else{ - newValues.set(vi, d_vars.getLowerBound(vi)); - }else if(d_vars.hasUpperBound(vi) && - roughlyEqual(newAssign, d_vars.getUpperBound(vi).approx(SMALL_FIXED_DELTA))){ - newValues.set(vi, d_vars.getUpperBound(vi)); - // cout << " to ub" << endl; + double rounded = round(newAssign); + if(roughlyEqual(newAssign, rounded)){ + // Message() << "roughly equal " << rounded << " " << newAssign << " " << oldAssign << endl; + newAssign = rounded; }else{ + // Message() << "not roughly equal " << rounded << " " << newAssign << " " << oldAssign << endl; + } - double rounded = round(newAssign); - if(roughlyEqual(newAssign, rounded)){ - // cout << "roughly equal " << rounded << " " << newAssign << " " << oldAssign << endl; - newAssign = rounded; - }else{ - // cout << "not roughly equal " << rounded << " " << newAssign << " " << oldAssign << endl; - } - - DeltaRational proposal = estimateWithCFE(newAssign); - - - if(roughlyEqual(newAssign, oldAssign.approx(SMALL_FIXED_DELTA))){ - // cout << " to prev value" << newAssign << " " << oldAssign << endl; - proposal = d_vars.getAssignment(vi); - } - - - if(d_vars.strictlyLessThanLowerBound(vi, proposal)){ - //cout << " round to lb " << d_vars.getLowerBound(vi) << endl; - proposal = d_vars.getLowerBound(vi); - }else if(d_vars.strictlyGreaterThanUpperBound(vi, proposal)){ - //cout << " round to ub " << d_vars.getUpperBound(vi) << endl; - proposal = d_vars.getUpperBound(vi); - }else{ - //cout << " use proposal" << proposal << " " << oldAssign << endl; - } - newValues.set(vi, proposal); + DeltaRational proposal = estimateWithCFE(newAssign); + + + if(roughlyEqual(newAssign, oldAssign.approx(SMALL_FIXED_DELTA))){ + // Message() << " to prev value" << newAssign << " " << oldAssign << endl; + proposal = d_vars.getAssignment(vi); } - break; + + + if(d_vars.strictlyLessThanLowerBound(vi, proposal)){ + //Message() << " round to lb " << d_vars.getLowerBound(vi) << endl; + proposal = d_vars.getLowerBound(vi); + }else if(d_vars.strictlyGreaterThanUpperBound(vi, proposal)){ + //Message() << " round to ub " << d_vars.getUpperBound(vi) << endl; + proposal = d_vars.getUpperBound(vi); + }else{ + //Message() << " use proposal" << proposal << " " << oldAssign << endl; + } + newValues.set(vi, proposal); } } } @@ -492,8 +710,10 @@ ApproximateSimplex::ApproxResult ApproxGLPK::solveRelaxation(){ parm.meth = GLP_PRIMAL; parm.pricing = GLP_PT_PSE; parm.it_lim = d_pivotLimit; - //parm.msg_lev = GLP_MSG_ALL; parm.msg_lev = GLP_MSG_OFF; + if(s_verbosity >= 1){ + parm.msg_lev = GLP_MSG_ALL; + } int res = glp_simplex(d_prob, &parm); switch(res){ @@ -550,8 +770,10 @@ ApproximateSimplex::ApproxResult ApproxGLPK::solveMIP(){ parm.cov_cuts = GLP_ON; parm.cb_func = stopAtBingoOrPivotLimit; parm.cb_info = &d_pivotLimit; - //parm.msg_lev = GLP_MSG_ALL; parm.msg_lev = GLP_MSG_OFF; + if(s_verbosity >= 1){ + parm.msg_lev = GLP_MSG_ALL; + } int res = glp_intopt(d_prob, &parm); switch(res){ diff --git a/src/theory/arith/approx_simplex.h b/src/theory/arith/approx_simplex.h index a2f3cde24..a34c8981d 100644 --- a/src/theory/arith/approx_simplex.h +++ b/src/theory/arith/approx_simplex.h @@ -24,7 +24,7 @@ public: /** * If glpk is enabled, return a subclass that can do something. - * If glpk is disabled, return a sublass that does nothing. + * If glpk is disabled, return a subclass that does nothing. */ static ApproximateSimplex* mkApproximateSimplexSolver(const ArithVariables& vars); ApproximateSimplex(); @@ -44,18 +44,15 @@ public: /** 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 ApproxResult solveMIP() = 0; virtual Solution extractMIP() const = 0; - static void applySolution(LinearEqualityModule& linEq, const Solution& sol){ - linEq.forceNewBasis(sol.newBasis); - linEq.updateMany(sol.newValues); - } - - /** UTILIES FOR DEALING WITH ESTIMATES */ + /** UTILITIES FOR DEALING WITH ESTIMATES */ static const double SMALL_FIXED_DELTA; static const double TOLERENCE; diff --git a/src/theory/arith/arith_rewriter.cpp b/src/theory/arith/arith_rewriter.cpp index aa5049ed4..247c09294 100644 --- a/src/theory/arith/arith_rewriter.cpp +++ b/src/theory/arith/arith_rewriter.cpp @@ -29,7 +29,8 @@ namespace theory { namespace arith { bool ArithRewriter::isAtom(TNode n) { - return arith::isRelationOperator(n.getKind()); + Kind k = n.getKind(); + return arith::isRelationOperator(k) || k == kind::IS_INTEGER || k == kind::DIVISIBLE; } RewriteResponse ArithRewriter::rewriteConstant(TNode t){ @@ -98,11 +99,28 @@ RewriteResponse ArithRewriter::preRewriteTerm(TNode t){ return preRewritePlus(t); case kind::MULT: return preRewriteMult(t); - //case kind::INTS_DIVISION: - //case kind::INTS_MODULUS: + case kind::INTS_DIVISION: + case kind::INTS_MODULUS: + return RewriteResponse(REWRITE_DONE, t); case kind::INTS_DIVISION_TOTAL: case kind::INTS_MODULUS_TOTAL: return rewriteIntsDivModTotal(t,true); + case kind::ABS: + if(t[0].isConst()) { + const Rational& rat = t[0].getConst<Rational>(); + if(rat >= 0) { + return RewriteResponse(REWRITE_DONE, t[0]); + } else { + return RewriteResponse(REWRITE_DONE, + NodeManager::currentNM()->mkConst(-rat)); + } + } + return RewriteResponse(REWRITE_DONE, t); + case kind::IS_INTEGER: + case kind::TO_INTEGER: + return RewriteResponse(REWRITE_DONE, t); + case kind::TO_REAL: + return RewriteResponse(REWRITE_DONE, t[0]); default: Unhandled(k); } @@ -126,11 +144,44 @@ RewriteResponse ArithRewriter::postRewriteTerm(TNode t){ return postRewritePlus(t); case kind::MULT: return postRewriteMult(t); - //case kind::INTS_DIVISION: - //case kind::INTS_MODULUS: + case kind::INTS_DIVISION: + case kind::INTS_MODULUS: + return RewriteResponse(REWRITE_DONE, t); case kind::INTS_DIVISION_TOTAL: case kind::INTS_MODULUS_TOTAL: return rewriteIntsDivModTotal(t, false); + case kind::ABS: + if(t[0].isConst()) { + const Rational& rat = t[0].getConst<Rational>(); + if(rat >= 0) { + return RewriteResponse(REWRITE_DONE, t[0]); + } else { + return RewriteResponse(REWRITE_DONE, + NodeManager::currentNM()->mkConst(-rat)); + } + } + case kind::TO_REAL: + return RewriteResponse(REWRITE_DONE, t[0]); + case kind::TO_INTEGER: + if(t[0].isConst()) { + return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkConst(Rational(t[0].getConst<Rational>().floor()))); + } + if(t[0].getType().isInteger()) { + return RewriteResponse(REWRITE_DONE, t[0]); + } + //Unimplemented("TO_INTEGER, nonconstant"); + //return rewriteToInteger(t); + return RewriteResponse(REWRITE_DONE, t); + case kind::IS_INTEGER: + if(t[0].isConst()) { + return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkConst(t[0].getConst<Rational>().getDenominator() == 1)); + } + if(t[0].getType().isInteger()) { + return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkConst(true)); + } + //Unimplemented("IS_INTEGER, nonconstant"); + //return rewriteIsInteger(t); + return RewriteResponse(REWRITE_DONE, t); default: Unreachable(); } @@ -190,6 +241,25 @@ RewriteResponse ArithRewriter::postRewriteMult(TNode t){ } RewriteResponse ArithRewriter::postRewriteAtom(TNode atom){ + if(atom.getKind() == kind::IS_INTEGER) { + if(atom[0].isConst()) { + return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkConst(atom[0].getConst<Rational>().isIntegral())); + } + if(atom[0].getType().isInteger()) { + return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkConst(true)); + } + // not supported, but this isn't the right place to complain + return RewriteResponse(REWRITE_DONE, atom); + } else if(atom.getKind() == kind::DIVISIBLE) { + if(atom[0].isConst()) { + return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkConst(bool((atom[0].getConst<Rational>() / atom.getOperator().getConst<Divisible>().k).isIntegral()))); + } + if(atom.getOperator().getConst<Divisible>().k.isOne()) { + return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkConst(true)); + } + return RewriteResponse(REWRITE_AGAIN, NodeManager::currentNM()->mkNode(kind::EQUAL, NodeManager::currentNM()->mkNode(kind::INTS_MODULUS_TOTAL, atom[0], NodeManager::currentNM()->mkConst(Rational(atom.getOperator().getConst<Divisible>().k))), NodeManager::currentNM()->mkConst(Rational(0)))); + } + // left |><| right TNode left = atom[0]; TNode right = atom[1]; @@ -217,6 +287,14 @@ RewriteResponse ArithRewriter::preRewriteAtom(TNode atom){ }else if(atom.getKind() == kind::LT){ Node geq = currNM->mkNode(kind::GEQ, atom[0], atom[1]); return RewriteResponse(REWRITE_DONE, currNM->mkNode(kind::NOT, geq)); + }else if(atom.getKind() == kind::IS_INTEGER){ + if(atom[0].getType().isInteger()){ + return RewriteResponse(REWRITE_DONE, currNM->mkConst(true)); + } + }else if(atom.getKind() == kind::DIVISIBLE){ + if(atom.getOperator().getConst<Divisible>().k.isOne()){ + return RewriteResponse(REWRITE_DONE, currNM->mkConst(true)); + } } return RewriteResponse(REWRITE_DONE, atom); @@ -329,6 +407,13 @@ RewriteResponse ArithRewriter::rewriteIntsDivModTotal(TNode t, bool pre){ Assert(k == kind::INTS_DIVISION || k == kind::INTS_DIVISION_TOTAL); return RewriteResponse(REWRITE_AGAIN, n); } + }else if(dIsConstant && d.getConst<Rational>().isNegativeOne()){ + if(k == kind::INTS_MODULUS || k == kind::INTS_MODULUS_TOTAL){ + return RewriteResponse(REWRITE_DONE, mkRationalNode(0)); + }else{ + Assert(k == kind::INTS_DIVISION || k == kind::INTS_DIVISION_TOTAL); + return RewriteResponse(REWRITE_AGAIN, NodeManager::currentNM()->mkNode(kind::UMINUS, n)); + } }else if(dIsConstant && n.getKind() == kind::CONST_RATIONAL){ Assert(d.getConst<Rational>().isIntegral()); Assert(n.getConst<Rational>().isIntegral()); diff --git a/src/theory/arith/arith_static_learner.h b/src/theory/arith/arith_static_learner.h index c4bf92a16..d8407eeba 100644 --- a/src/theory/arith/arith_static_learner.h +++ b/src/theory/arith/arith_static_learner.h @@ -23,7 +23,6 @@ #include "util/statistics_registry.h" #include "theory/arith/arith_utilities.h" -#include "theory/substitutions.h" #include "context/context.h" #include "context/cdlist.h" diff --git a/src/theory/arith/attempt_solution_simplex.cpp b/src/theory/arith/attempt_solution_simplex.cpp new file mode 100644 index 000000000..f0cecc24b --- /dev/null +++ b/src/theory/arith/attempt_solution_simplex.cpp @@ -0,0 +1,135 @@ + +#include "theory/arith/attempt_solution_simplex.h" +#include "theory/arith/options.h" +#include "theory/arith/constraint.h" + +using namespace std; + +namespace CVC4 { +namespace theory { +namespace arith { + +AttemptSolutionSDP::AttemptSolutionSDP(LinearEqualityModule& linEq, ErrorSet& errors, RaiseConflict conflictChannel, TempVarMalloc tvmalloc) + : SimplexDecisionProcedure(linEq, errors, conflictChannel, tvmalloc) + , d_statistics() +{ } + +AttemptSolutionSDP::Statistics::Statistics(): + d_searchTime("theory::arith::attempt::searchTime"), + d_queueTime("theory::arith::attempt::queueTime"), + d_conflicts("theory::arith::attempt::conflicts", 0) +{ + StatisticsRegistry::registerStat(&d_searchTime); + StatisticsRegistry::registerStat(&d_queueTime); + StatisticsRegistry::registerStat(&d_conflicts); +} + +AttemptSolutionSDP::Statistics::~Statistics(){ + StatisticsRegistry::unregisterStat(&d_searchTime); + StatisticsRegistry::unregisterStat(&d_queueTime); + StatisticsRegistry::unregisterStat(&d_conflicts); +} + +bool AttemptSolutionSDP::matchesNewValue(const DenseMap<DeltaRational>& nv, ArithVar v) const{ + return nv[v] == d_variables.getAssignment(v); +} + +Result::Sat AttemptSolutionSDP::attempt(const ApproximateSimplex::Solution& sol){ + const DenseSet& newBasis = sol.newBasis; + const DenseMap<DeltaRational>& newValues = sol.newValues; + + DenseSet needsToBeAdded; + for(DenseSet::const_iterator i = newBasis.begin(), i_end = newBasis.end(); i != i_end; ++i){ + ArithVar b = *i; + if(!d_tableau.isBasic(b)){ + needsToBeAdded.add(b); + } + } + DenseMap<DeltaRational>::const_iterator nvi = newValues.begin(), nvi_end = newValues.end(); + for(; nvi != nvi_end; ++nvi){ + ArithVar currentlyNb = *nvi; + if(!d_tableau.isBasic(currentlyNb)){ + if(!matchesNewValue(newValues, currentlyNb)){ + const DeltaRational& newValue = newValues[currentlyNb]; + Trace("arith::updateMany") + << "updateMany:" << currentlyNb << " " + << d_variables.getAssignment(currentlyNb) << " to "<< newValue << endl; + d_linEq.update(currentlyNb, newValue); + Assert(d_variables.assignmentIsConsistent(currentlyNb)); + } + } + } + d_errorSet.reduceToSignals(); + d_errorSet.setSelectionRule(VAR_ORDER); + + static int instance = 0; + ++instance; + + if(processSignals()){ + Debug("arith::findModel") << "attemptSolution("<< instance <<") early conflict" << endl; + d_conflictVariables.purge(); + return Result::UNSAT; + }else if(d_errorSet.errorEmpty()){ + Debug("arith::findModel") << "attemptSolution("<< instance <<") fixed itself" << endl; + return Result::SAT; + } + + while(!needsToBeAdded.empty() && !d_errorSet.errorEmpty()){ + ArithVar toRemove = ARITHVAR_SENTINEL; + ArithVar toAdd = ARITHVAR_SENTINEL; + DenseSet::const_iterator i = needsToBeAdded.begin(), i_end = needsToBeAdded.end(); + for(; toAdd == ARITHVAR_SENTINEL && i != i_end; ++i){ + ArithVar v = *i; + + Tableau::ColIterator colIter = d_tableau.colIterator(v); + for(; !colIter.atEnd(); ++colIter){ + const Tableau::Entry& entry = *colIter; + Assert(entry.getColVar() == v); + ArithVar b = d_tableau.rowIndexToBasic(entry.getRowIndex()); + if(!newBasis.isMember(b)){ + toAdd = v; + + bool favorBOverToRemove = + (toRemove == ARITHVAR_SENTINEL) || + (matchesNewValue(newValues, toRemove) && !matchesNewValue(newValues, b)) || + (d_tableau.basicRowLength(toRemove) > d_tableau.basicRowLength(b)); + + if(favorBOverToRemove){ + toRemove = b; + } + } + } + } + Assert(toRemove != ARITHVAR_SENTINEL); + Assert(toAdd != ARITHVAR_SENTINEL); + + Trace("arith::forceNewBasis") << toRemove << " " << toAdd << endl; + //Message() << toRemove << " " << toAdd << endl; + + d_linEq.pivotAndUpdate(toRemove, toAdd, newValues[toRemove]); + + Trace("arith::forceNewBasis") << needsToBeAdded.size() << "to go" << endl; + //Message() << needsToBeAdded.size() << "to go" << endl; + needsToBeAdded.remove(toAdd); + + bool conflict = processSignals(); + if(conflict){ + d_errorSet.reduceToSignals(); + d_conflictVariables.purge(); + + return Result::UNSAT; + } + } + Assert( d_conflictVariables.empty() ); + + if(d_errorSet.errorEmpty()){ + return Result::SAT; + }else{ + d_errorSet.reduceToSignals(); + return Result::SAT_UNKNOWN; + } +} + +}/* CVC4::theory::arith namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/arith/pure_update_simplex.h b/src/theory/arith/attempt_solution_simplex.h index 50b751d7b..5bcdc6aab 100644 --- a/src/theory/arith/pure_update_simplex.h +++ b/src/theory/arith/attempt_solution_simplex.h @@ -53,64 +53,43 @@ #pragma once -#include "theory/arith/simplex.h" -#include "util/dense_map.h" #include "util/statistics_registry.h" -#include <stdint.h> -#include "theory/arith/arithvar.h" -#include "theory/arith/delta_rational.h" +#include "theory/arith/simplex.h" +#include "theory/arith/approx_simplex.h" namespace CVC4 { namespace theory { namespace arith { -class PureUpdateSimplexDecisionProcedure : public SimplexDecisionProcedure{ +class AttemptSolutionSDP : public SimplexDecisionProcedure { public: - PureUpdateSimplexDecisionProcedure(LinearEqualityModule& linEq, ErrorSet& errors, RaiseConflict conflictChannel, TempVarMalloc tvmalloc); + AttemptSolutionSDP(LinearEqualityModule& linEq, ErrorSet& errors, RaiseConflict conflictChannel, TempVarMalloc tvmalloc); - Result::Sat findModel(bool exactResult); + Result::Sat attempt(const ApproximateSimplex::Solution& sol); -private: - ArithVar d_focusErrorVar; - - bool attemptPureUpdates(); + Result::Sat findModel(bool exactResult){ + Unreachable(); + } - /** - * This is the main simplex for DPLL(T) loop. - * It runs for at most maxIterations. - * - * Returns true iff it has found a conflict. - * d_conflictVariable will be set and the conflict for this row is reported. - */ - bool searchForFeasibleSolution(uint32_t maxIterations); +private: + bool matchesNewValue(const DenseMap<DeltaRational>& nv, ArithVar v) const; bool processSignals(){ - TimerStat &timer = d_statistics.d_processSignalsTime; - IntStat& conflictStat = d_statistics.d_foundConflicts; + TimerStat &timer = d_statistics.d_queueTime; + IntStat& conflictStat = d_statistics.d_conflicts; return standardProcessSignals(timer, conflictStat); } - /** These fields are designed to be accessible to TheoryArith methods. */ class Statistics { public: - IntStat d_pureUpdateFoundUnsat; - IntStat d_pureUpdateFoundSat; - IntStat d_pureUpdateMissed; - IntStat d_pureUpdates; - IntStat d_pureUpdateDropped; - IntStat d_pureUpdateConflicts; - - IntStat d_foundConflicts; - - TimerStat d_attemptPureUpdatesTimer; - TimerStat d_processSignalsTime; - - TimerStat d_constructionTimer; + TimerStat d_searchTime; + TimerStat d_queueTime; + IntStat d_conflicts; Statistics(); ~Statistics(); } d_statistics; -};/* class PureUpdateSimplexDecisionProcedure */ +};/* class AttemptSolutionSDP */ }/* CVC4::theory::arith namespace */ }/* CVC4::theory namespace */ diff --git a/src/theory/arith/bound_counts.h b/src/theory/arith/bound_counts.h index 954cc056a..49c1a94ce 100644 --- a/src/theory/arith/bound_counts.h +++ b/src/theory/arith/bound_counts.h @@ -1,3 +1,20 @@ +/********************* */ +/*! \file bound_counts.h + ** \verbatim + ** Original author: Tim King + ** 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 [[ Add one-line brief description here ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + #include "cvc4_private.h" #pragma once @@ -10,62 +27,92 @@ namespace CVC4 { namespace theory { namespace arith { -/** - * x = \sum_{a < 0} a_i i + \sum_{b > 0} b_j j - * - * AtUpperBound = {assignment(i) = lb(i)} \cup {assignment(j) = ub(j)} - * AtLowerBound = {assignment(i) = ub(i)} \cup {assignment(j) = lb(j)} - */ class BoundCounts { private: - uint32_t d_atLowerBounds; - uint32_t d_atUpperBounds; + uint32_t d_lowerBoundCount; + uint32_t d_upperBoundCount; public: - BoundCounts() : d_atLowerBounds(0), d_atUpperBounds(0) {} + BoundCounts() : d_lowerBoundCount(0), d_upperBoundCount(0) {} BoundCounts(uint32_t lbs, uint32_t ubs) - : d_atLowerBounds(lbs), d_atUpperBounds(ubs) {} + : d_lowerBoundCount(lbs), d_upperBoundCount(ubs) {} bool operator==(BoundCounts bc) const { - return d_atLowerBounds == bc.d_atLowerBounds - && d_atUpperBounds == bc.d_atUpperBounds; + return d_lowerBoundCount == bc.d_lowerBoundCount + && d_upperBoundCount == bc.d_upperBoundCount; } bool operator!=(BoundCounts bc) const { - return d_atLowerBounds != bc.d_atLowerBounds - || d_atUpperBounds != bc.d_atUpperBounds; + return d_lowerBoundCount != bc.d_lowerBoundCount + || d_upperBoundCount != bc.d_upperBoundCount; + } + /** This is not a total order! */ + bool operator>=(BoundCounts bc) const { + return d_lowerBoundCount >= bc.d_lowerBoundCount && + d_upperBoundCount >= bc.d_upperBoundCount; } - inline bool isZero() const{ return d_atLowerBounds == 0 && d_atUpperBounds == 0; } - inline uint32_t atLowerBounds() const{ - return d_atLowerBounds; + + inline bool isZero() const{ return d_lowerBoundCount == 0 && d_upperBoundCount == 0; } + inline uint32_t lowerBoundCount() const{ + return d_lowerBoundCount; } - inline uint32_t atUpperBounds() const{ - return d_atUpperBounds; + inline uint32_t upperBoundCount() const{ + return d_upperBoundCount; } inline BoundCounts operator+(BoundCounts bc) const{ - return BoundCounts(d_atLowerBounds + bc.d_atLowerBounds, - d_atUpperBounds + bc.d_atUpperBounds); + return BoundCounts(d_lowerBoundCount + bc.d_lowerBoundCount, + d_upperBoundCount + bc.d_upperBoundCount); } inline BoundCounts operator-(BoundCounts bc) const { - Assert(d_atLowerBounds >= bc.d_atLowerBounds); - Assert(d_atUpperBounds >= bc.d_atUpperBounds); - return BoundCounts(d_atLowerBounds - bc.d_atLowerBounds, - d_atUpperBounds - bc.d_atUpperBounds); + Assert( *this >= bc ); + return BoundCounts(d_lowerBoundCount - bc.d_lowerBoundCount, + d_upperBoundCount - bc.d_upperBoundCount); + } + + + inline BoundCounts& operator+=(BoundCounts bc) { + d_upperBoundCount += bc.d_upperBoundCount; + d_lowerBoundCount += bc.d_lowerBoundCount; + return *this; + } + + inline BoundCounts& operator-=(BoundCounts bc) { + Assert(d_lowerBoundCount >= bc.d_lowerBoundCount); + Assert(d_upperBoundCount >= bc.d_upperBoundCount); + d_upperBoundCount -= bc.d_upperBoundCount; + d_lowerBoundCount -= bc.d_lowerBoundCount; + + return *this; + } + + /** Based on the sign coefficient a variable is multiplied by, + * the effects the bound counts either has no effect (sgn == 0), + * the lower bounds and upper bounds flip (sgn < 0), or nothing (sgn >0). + */ + inline BoundCounts multiplyBySgn(int sgn) const{ + if(sgn > 0){ + return *this; + }else if(sgn == 0){ + return BoundCounts(0,0); + }else{ + return BoundCounts(d_upperBoundCount, d_lowerBoundCount); + } } inline void addInChange(int sgn, BoundCounts before, BoundCounts after){ - Assert(before != after); - if(sgn < 0){ - Assert(d_atUpperBounds >= before.d_atLowerBounds); - Assert(d_atLowerBounds >= before.d_atUpperBounds); - d_atUpperBounds += after.d_atLowerBounds - before.d_atLowerBounds; - d_atLowerBounds += after.d_atUpperBounds - before.d_atUpperBounds; + if(before == after){ + return; + }else if(sgn < 0){ + Assert(d_upperBoundCount >= before.d_lowerBoundCount); + Assert(d_lowerBoundCount >= before.d_upperBoundCount); + d_upperBoundCount += after.d_lowerBoundCount - before.d_lowerBoundCount; + d_lowerBoundCount += after.d_upperBoundCount - before.d_upperBoundCount; }else if(sgn > 0){ - Assert(d_atUpperBounds >= before.d_atUpperBounds); - Assert(d_atLowerBounds >= before.d_atLowerBounds); - d_atUpperBounds += after.d_atUpperBounds - before.d_atUpperBounds; - d_atLowerBounds += after.d_atLowerBounds - before.d_atLowerBounds; + Assert(d_upperBoundCount >= before.d_upperBoundCount); + Assert(d_lowerBoundCount >= before.d_lowerBoundCount); + d_upperBoundCount += after.d_upperBoundCount - before.d_upperBoundCount; + d_lowerBoundCount += after.d_lowerBoundCount - before.d_lowerBoundCount; } } @@ -74,69 +121,112 @@ public: Assert(!bc.isZero()); if(before < 0){ - d_atUpperBounds -= bc.d_atLowerBounds; - d_atLowerBounds -= bc.d_atUpperBounds; + d_upperBoundCount -= bc.d_lowerBoundCount; + d_lowerBoundCount -= bc.d_upperBoundCount; }else if(before > 0){ - d_atUpperBounds -= bc.d_atUpperBounds; - d_atLowerBounds -= bc.d_atLowerBounds; + d_upperBoundCount -= bc.d_upperBoundCount; + d_lowerBoundCount -= bc.d_lowerBoundCount; } if(after < 0){ - d_atUpperBounds += bc.d_atLowerBounds; - d_atLowerBounds += bc.d_atUpperBounds; + d_upperBoundCount += bc.d_lowerBoundCount; + d_lowerBoundCount += bc.d_upperBoundCount; }else if(after > 0){ - d_atUpperBounds += bc.d_atUpperBounds; - d_atLowerBounds += bc.d_atLowerBounds; + d_upperBoundCount += bc.d_upperBoundCount; + d_lowerBoundCount += bc.d_lowerBoundCount; } } +}; - inline BoundCounts& operator+=(BoundCounts bc) { - d_atUpperBounds += bc.d_atUpperBounds; - d_atLowerBounds += bc.d_atLowerBounds; - return *this; - } +class BoundsInfo { +private: - inline BoundCounts& operator-=(BoundCounts bc) { - Assert(d_atLowerBounds >= bc.d_atLowerBounds); - Assert(d_atUpperBounds >= bc.d_atUpperBounds); - d_atUpperBounds -= bc.d_atUpperBounds; - d_atLowerBounds -= bc.d_atLowerBounds; + /** + * x = \sum_{a < 0} a_i i + \sum_{b > 0} b_j j + * + * AtUpperBound = {assignment(i) = lb(i)} \cup {assignment(j) = ub(j)} + * AtLowerBound = {assignment(i) = ub(i)} \cup {assignment(j) = lb(j)} + */ + BoundCounts d_atBounds; - return *this; + /** This is for counting how many upper and lower bounds a row has. */ + BoundCounts d_hasBounds; + +public: + BoundsInfo() : d_atBounds(), d_hasBounds() {} + BoundsInfo(BoundCounts atBounds, BoundCounts hasBounds) + : d_atBounds(atBounds), d_hasBounds(hasBounds) {} + + BoundCounts atBounds() const { return d_atBounds; } + BoundCounts hasBounds() const { return d_hasBounds; } + + /** This corresponds to adding in another variable to the row. */ + inline BoundsInfo operator+(const BoundsInfo& bc) const{ + return BoundsInfo(d_atBounds + bc.d_atBounds, + d_hasBounds + bc.d_hasBounds); + } + /** This corresponds to removing a variable from the row. */ + inline BoundsInfo operator-(const BoundsInfo& bc) const { + Assert(*this >= bc); + return BoundsInfo(d_atBounds - bc.d_atBounds, + d_hasBounds - bc.d_hasBounds); } - inline BoundCounts multiplyBySgn(int sgn) const{ - if(sgn > 0){ - return *this; - }else if(sgn == 0){ - return BoundCounts(0,0); - }else{ - return BoundCounts(d_atUpperBounds, d_atLowerBounds); - } + inline BoundsInfo& operator+=(const BoundsInfo& bc) { + d_atBounds += bc.d_atBounds; + d_hasBounds += bc.d_hasBounds; + return (*this); } -}; -typedef DenseMap<BoundCounts> BoundCountingVector; + /** Based on the sign coefficient a variable is multiplied by, + * the effects the bound counts either has no effect (sgn == 0), + * the lower bounds and upper bounds flip (sgn < 0), or nothing (sgn >0). + */ + inline BoundsInfo multiplyBySgn(int sgn) const{ + return BoundsInfo(d_atBounds.multiplyBySgn(sgn), d_hasBounds.multiplyBySgn(sgn)); + } -class BoundCountingLookup { -private: - BoundCountingVector* d_bc; -public: - BoundCountingLookup(BoundCountingVector* bc) : d_bc(bc) {} - BoundCounts boundCounts(ArithVar v) const { - Assert(d_bc->isKey(v)); - return (*d_bc)[v]; + bool operator==(const BoundsInfo& other) const{ + return d_atBounds == other.d_atBounds && d_hasBounds == other.d_hasBounds; + } + bool operator!=(const BoundsInfo& other) const{ + return !(*this == other); + } + /** This is not a total order! */ + bool operator>=(const BoundsInfo& other) const{ + return d_atBounds >= other.d_atBounds && d_hasBounds >= other.d_hasBounds; + } + void addInChange(int sgn, const BoundsInfo& before, const BoundsInfo& after){ + addInAtBoundChange(sgn, before.d_atBounds, after.d_atBounds); + addInHasBoundChange(sgn, before.d_hasBounds, after.d_hasBounds); + } + void addInAtBoundChange(int sgn, BoundCounts before, BoundCounts after){ + d_atBounds.addInChange(sgn, before, after); + } + void addInHasBoundChange(int sgn, BoundCounts before, BoundCounts after){ + d_hasBounds.addInChange(sgn, before, after); + } + + inline void addInSgn(const BoundsInfo& bc, int before, int after){ + if(!bc.d_atBounds.isZero()){ d_atBounds.addInSgn(bc.d_atBounds, before, after);} + if(!bc.d_hasBounds.isZero()){ d_hasBounds.addInSgn(bc.d_hasBounds, before, after);} } }; +/** This is intended to map each row to its relevant bound information. */ +typedef DenseMap<BoundsInfo> BoundInfoMap; + inline std::ostream& operator<<(std::ostream& os, const BoundCounts& bc){ - os << "[bc " << bc.atLowerBounds() << ", " - << bc.atUpperBounds() << "]"; + os << "[bc " << bc.lowerBoundCount() << ", " << bc.upperBoundCount() << "]"; return os; } -class BoundCountsCallback { +inline std::ostream& operator<<(std::ostream& os, const BoundsInfo& inf){ + os << "[bi : @ " << inf.atBounds() << ", " << inf.hasBounds() << "]"; + return os; +} +class BoundUpdateCallback { public: - virtual void operator()(ArithVar v, BoundCounts bc) = 0; + virtual void operator()(ArithVar v, const BoundsInfo& up) = 0; }; }/* CVC4::theory::arith namespace */ diff --git a/src/theory/arith/callbacks.cpp b/src/theory/arith/callbacks.cpp index 6b6170b20..1e827d316 100644 --- a/src/theory/arith/callbacks.cpp +++ b/src/theory/arith/callbacks.cpp @@ -32,6 +32,10 @@ void RaiseConflict::operator()(Node n){ d_ta.raiseConflict(n); } +const BoundsInfo& BoundCountingLookup::boundsInfo(ArithVar basic) const{ + return d_ta.boundsInfo(basic); +} + }/* CVC4::theory::arith namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/arith/callbacks.h b/src/theory/arith/callbacks.h index 0d754d159..718799e9f 100644 --- a/src/theory/arith/callbacks.h +++ b/src/theory/arith/callbacks.h @@ -3,10 +3,10 @@ #include "expr/node.h" #include "util/rational.h" -#include "context/cdlist.h" #include "theory/arith/theory_arith_private_forward.h" #include "theory/arith/arithvar.h" +#include "theory/arith/bound_counts.h" namespace CVC4 { namespace theory { @@ -87,6 +87,20 @@ public: void operator()(Node n); }; +class BoundCountingLookup { +private: + TheoryArithPrivate& d_ta; +public: + BoundCountingLookup(TheoryArithPrivate& ta) : d_ta(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(); + } +}; + }/* CVC4::theory::arith namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/arith/constraint.cpp b/src/theory/arith/constraint.cpp index e26687bf1..78b9d3494 100644 --- a/src/theory/arith/constraint.cpp +++ b/src/theory/arith/constraint.cpp @@ -922,6 +922,28 @@ bool ConstraintValue::proofIsEmpty() const{ return result; } +Node ConstraintValue::makeImplication(const std::vector<Constraint>& b) const{ + Node antecedent = makeConjunction(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); + } + if(nb.getNumChildren() >= 2){ + return nb; + }else if(nb.getNumChildren() == 1){ + return nb[0]; + }else{ + return mkBoolNode(true); + } +} + void ConstraintValue::explainBefore(NodeBuilder<>& nb, AssertionOrder order) const{ Assert(hasProof()); Assert(!isSelfExplaining() || assertedToTheTheory()); diff --git a/src/theory/arith/constraint.h b/src/theory/arith/constraint.h index a5d64a652..4966115d2 100644 --- a/src/theory/arith/constraint.h +++ b/src/theory/arith/constraint.h @@ -598,6 +598,8 @@ public: void impliedBy(Constraint a, Constraint b); void impliedBy(const std::vector<Constraint>& b); + Node makeImplication(const std::vector<Constraint>& b) const; + static Node makeConjunction(const std::vector<Constraint>& b); /** The node must have a proof already and be eligible for propagation! */ void propagate(); diff --git a/src/theory/arith/dual_simplex.cpp b/src/theory/arith/dual_simplex.cpp index 7caee6708..a9304ae76 100644 --- a/src/theory/arith/dual_simplex.cpp +++ b/src/theory/arith/dual_simplex.cpp @@ -196,19 +196,19 @@ bool DualSimplexDecisionProcedure::searchForFeasibleSolution(uint32_t remainingI //DeltaRational beta_i = d_variables.getAssignment(x_i); ArithVar x_j = ARITHVAR_SENTINEL; - int32_t prevErrorSize = d_errorSet.errorSize(); + int32_t prevErrorSize CVC4_UNUSED = d_errorSet.errorSize(); if(d_variables.cmpAssignmentLowerBound(x_i) < 0 ){ x_j = d_linEq.selectSlackUpperBound(x_i, pf); if(x_j == ARITHVAR_SENTINEL ){ Unreachable(); - ++(d_statistics.d_statUpdateConflicts); - reportConflict(x_i); - ++(d_statistics.d_simplexConflicts); + // ++(d_statistics.d_statUpdateConflicts); + // reportConflict(x_i); + // ++(d_statistics.d_simplexConflicts); // Node conflict = d_linEq.generateConflictBelowLowerBound(x_i); //unsat // d_conflictVariable = x_i; // reportConflict(conflict); - return true; + // return true; }else{ const DeltaRational& l_i = d_variables.getLowerBound(x_i); d_linEq.pivotAndUpdate(x_i, x_j, l_i); @@ -217,13 +217,13 @@ bool DualSimplexDecisionProcedure::searchForFeasibleSolution(uint32_t remainingI x_j = d_linEq.selectSlackLowerBound(x_i, pf); if(x_j == ARITHVAR_SENTINEL ){ Unreachable(); - ++(d_statistics.d_statUpdateConflicts); - reportConflict(x_i); - ++(d_statistics.d_simplexConflicts); + // ++(d_statistics.d_statUpdateConflicts); + // reportConflict(x_i); + // ++(d_statistics.d_simplexConflicts); // Node conflict = d_linEq.generateConflictAboveUpperBound(x_i); //unsat // d_conflictVariable = x_i; // reportConflict(conflict); - return true; + // return true; }else{ const DeltaRational& u_i = d_variables.getUpperBound(x_i); d_linEq.pivotAndUpdate(x_i, x_j, u_i); @@ -232,16 +232,19 @@ bool DualSimplexDecisionProcedure::searchForFeasibleSolution(uint32_t remainingI Assert(x_j != ARITHVAR_SENTINEL); bool conflict = processSignals(); - int32_t currErrorSize = d_errorSet.errorSize(); + int32_t currErrorSize CVC4_UNUSED = d_errorSet.errorSize(); d_pivots++; - // cout << "#" << d_pivots - // << " c" << conflict - // << " d" << (prevErrorSize - currErrorSize) - // << " f" << d_errorSet.inError(x_j) - // << " h" << d_conflictVariables.isMember(x_j) - // << " " << x_i << "->" << x_j - // << endl; + if(Debug.isOn("arith::dual")){ + Debug("arith::dual") + << "#" << d_pivots + << " c" << conflict + << " d" << (prevErrorSize - currErrorSize) + << " f" << d_errorSet.inError(x_j) + << " h" << d_conflictVariables.isMember(x_j) + << " " << x_i << "->" << x_j + << endl; + } if(conflict){ return true; diff --git a/src/theory/arith/error_set.cpp b/src/theory/arith/error_set.cpp index ee72d1949..dea78acf7 100644 --- a/src/theory/arith/error_set.cpp +++ b/src/theory/arith/error_set.cpp @@ -1,11 +1,11 @@ /********************* */ -/*! \file arith_priority_queue.cpp +/*! \file error_set.cpp ** \verbatim - ** Original author: taking - ** Major contributors: mdeters - ** Minor contributors (to current version): none - ** This file is part of the CVC4 prototype. - ** Copyright (c) 2009-2012 New York University and The University of Iowa + ** Original author: Tim King + ** Major contributors: none + ** Minor contributors (to current version): Morgan Deters + ** 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 ** diff --git a/src/theory/arith/error_set.h b/src/theory/arith/error_set.h index 91d7e49ea..d1b692cb4 100644 --- a/src/theory/arith/error_set.h +++ b/src/theory/arith/error_set.h @@ -1,11 +1,11 @@ /********************* */ -/*! \file arith_priority_queue.h +/*! \file error_set.h ** \verbatim - ** Original author: taking + ** Original author: Tim King ** Major contributors: none - ** Minor contributors (to current version): mdeters - ** This file is part of the CVC4 prototype. - ** Copyright (c) 2009-2012 New York University and The University of Iowa + ** Minor contributors (to current version): Morgan Deters + ** 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 ** @@ -26,9 +26,9 @@ #include "theory/arith/partial_model.h" #include "theory/arith/arith_heuristic_pivot_rule.h" #include "theory/arith/tableau_sizes.h" +#include "theory/arith/callbacks.h" #include "util/statistics_registry.h" -//#include <boost/heap/d_ary_heap.hpp> #if CVC4_GCC_HAS_PB_DS_BUG // Unfortunate bug in some older GCCs (e.g., v4.2): @@ -377,8 +377,8 @@ public: uint32_t sumMetric(ArithVar a) const{ Assert(inError(a)); - BoundCounts bcs = d_boundLookup.boundCounts(a); - uint32_t count = getSgn(a) > 0 ? bcs.atUpperBounds() : bcs.atLowerBounds(); + BoundCounts bcs = d_boundLookup.atBounds(a); + uint32_t count = getSgn(a) > 0 ? bcs.upperBoundCount() : bcs.lowerBoundCount(); uint32_t length = d_tableauSizes.getRowLength(a); diff --git a/src/theory/arith/fc_simplex.cpp b/src/theory/arith/fc_simplex.cpp index ac4625ba3..e99e62505 100644 --- a/src/theory/arith/fc_simplex.cpp +++ b/src/theory/arith/fc_simplex.cpp @@ -289,7 +289,7 @@ UpdateInfo FCSimplexDecisionProcedure::selectPrimalUpdate(ArithVar basic, Linear Debug("arith::selectPrimalUpdate") << "selectPrimalUpdate " << instance << endl << basic << " " << d_tableau.basicRowLength(basic) - << " " << d_linEq._countBounds(basic) << endl; + << " " << d_linEq.debugBasicAtBoundCount(basic) << endl; static const int s_maxCandidatesAfterImprove = 3; bool isFocus = basic == d_focusErrorVar; @@ -358,18 +358,9 @@ UpdateInfo FCSimplexDecisionProcedure::selectPrimalUpdate(ArithVar basic, Linear ArithVar curr = cand.d_nb; const Rational& coeff = *cand.d_coeff; -#warning "Who is using computeSafeUpdate?" LinearEqualityModule::UpdatePreferenceFunction leavingPrefFunc = selectLeavingFunction(curr); UpdateInfo currProposal = d_linEq.speculativeUpdate(curr, coeff, leavingPrefFunc); - //int curr_movement = cand.d_sgn; - // if(isFocus){ - // currProposal = d_linEq.speculativeUpdate(curr, coeff, upf); - // }else{ - // currProposal = UpdateInfo(curr, curr_movement); - // d_linEq.computeSafeUpdate(currProposal, bpf); - // } - Debug("arith::selectPrimalUpdate") << "selected " << selected << endl << "currProp " << currProposal << endl @@ -505,7 +496,7 @@ void FCSimplexDecisionProcedure::debugPrintSignal(ArithVar updated) const{ int dir = !d_variables.assignmentIsConsistent(updated) ? d_errorSet.getSgn(updated) : 0; Debug("updateAndSignal") << " dir " << dir; - Debug("updateAndSignal") << " _countBounds " << d_linEq._countBounds(updated) << endl; + Debug("updateAndSignal") << " debugBasicAtBoundCount " << d_linEq.debugBasicAtBoundCount(updated) << endl; } bool debugUpdatedBasic(const UpdateInfo& selected, ArithVar updated){ @@ -530,7 +521,7 @@ void FCSimplexDecisionProcedure::updateAndSignal(const UpdateInfo& selected, Wit ArithVar leaving = selected.leaving(); ss << "leaving " << leaving << " " << d_tableau.basicRowLength(leaving) - << " " << d_linEq._countBounds(leaving) + << " " << d_linEq.debugBasicAtBoundCount(leaving) << endl; } if(degenerate(w) && selected.describesPivot()){ @@ -539,7 +530,7 @@ void FCSimplexDecisionProcedure::updateAndSignal(const UpdateInfo& selected, Wit << "degenerate " << leaving << ", atBounds " << d_linEq.basicsAtBounds(selected) << ", len " << d_tableau.basicRowLength(leaving) - << ", bc " << d_linEq._countBounds(leaving) + << ", bc " << d_linEq.debugBasicAtBoundCount(leaving) << endl; } } diff --git a/src/theory/arith/kinds b/src/theory/arith/kinds index 07cfcc9e5..a8a4047ca 100644 --- a/src/theory/arith/kinds +++ b/src/theory/arith/kinds @@ -23,8 +23,16 @@ operator INTS_DIVISION 2 "ints division (user symbol)" operator INTS_DIVISION_TOTAL 2 "ints division with interpreted division by 0 (internal symbol)" operator INTS_MODULUS 2 "ints modulus (user symbol)" operator INTS_MODULUS_TOTAL 2 "ints modulus with interpreted division by 0 (internal symbol)" +operator ABS 1 "absolute value" +parameterized DIVISIBLE DIVISIBLE_OP 1 "divisibility-by-k predicate" operator POW 2 "arithmetic power" +constant DIVISIBLE_OP \ + ::CVC4::Divisible \ + ::CVC4::DivisibleHashFunction \ + "util/divisible.h" \ + "operator for the divisibility-by-k predicate" + sort REAL_TYPE \ Cardinality::REALS \ well-founded \ @@ -72,6 +80,10 @@ operator LEQ 2 "less than or equal, x <= y" operator GT 2 "greater than, x > y" operator GEQ 2 "greater than or equal, x >= y" +operator IS_INTEGER 1 "term is integer" +operator TO_INTEGER 1 "cast term to integer" +operator TO_REAL 1 "cast term to real" + typerule PLUS ::CVC4::theory::arith::ArithOperatorTypeRule typerule MULT ::CVC4::theory::arith::ArithOperatorTypeRule typerule MINUS ::CVC4::theory::arith::ArithOperatorTypeRule @@ -86,11 +98,17 @@ typerule LEQ ::CVC4::theory::arith::ArithPredicateTypeRule typerule GT ::CVC4::theory::arith::ArithPredicateTypeRule typerule GEQ ::CVC4::theory::arith::ArithPredicateTypeRule -typerule INTS_DIVISION ::CVC4::theory::arith::ArithOperatorTypeRule -typerule INTS_MODULUS ::CVC4::theory::arith::ArithOperatorTypeRule +typerule TO_REAL ::CVC4::theory::arith::ArithOperatorTypeRule +typerule TO_INTEGER ::CVC4::theory::arith::ArithOperatorTypeRule +typerule IS_INTEGER ::CVC4::theory::arith::ArithUnaryPredicateTypeRule + +typerule ABS ::CVC4::theory::arith::IntOperatorTypeRule +typerule INTS_DIVISION ::CVC4::theory::arith::IntOperatorTypeRule +typerule INTS_MODULUS ::CVC4::theory::arith::IntOperatorTypeRule +typerule DIVISIBLE ::CVC4::theory::arith::IntUnaryPredicateTypeRule typerule DIVISION_TOTAL ::CVC4::theory::arith::ArithOperatorTypeRule -typerule INTS_DIVISION_TOTAL ::CVC4::theory::arith::ArithOperatorTypeRule -typerule INTS_MODULUS_TOTAL ::CVC4::theory::arith::ArithOperatorTypeRule +typerule INTS_DIVISION_TOTAL ::CVC4::theory::arith::IntOperatorTypeRule +typerule INTS_MODULUS_TOTAL ::CVC4::theory::arith::IntOperatorTypeRule endtheory diff --git a/src/theory/arith/linear_equality.cpp b/src/theory/arith/linear_equality.cpp index 42d8b41f8..5817a3629 100644 --- a/src/theory/arith/linear_equality.cpp +++ b/src/theory/arith/linear_equality.cpp @@ -25,18 +25,10 @@ namespace theory { namespace arith { /* Explicitly instatiate these functions. */ -template void LinearEqualityModule::propagateNonbasics<true>(ArithVar basic, Constraint c); -template void LinearEqualityModule::propagateNonbasics<false>(ArithVar basic, Constraint c); template ArithVar LinearEqualityModule::selectSlack<true>(ArithVar x_i, VarPreferenceFunction pf) const; template ArithVar LinearEqualityModule::selectSlack<false>(ArithVar x_i, VarPreferenceFunction pf) const; -// template bool LinearEqualityModule::preferNonDegenerate<true>(const UpdateInfo& a, const UpdateInfo& b) const; -// template bool LinearEqualityModule::preferNonDegenerate<false>(const UpdateInfo& a, const UpdateInfo& b) const; - -// template bool LinearEqualityModule::preferErrorsFixed<true>(const UpdateInfo& a, const UpdateInfo& b) const; -// template bool LinearEqualityModule::preferErrorsFixed<false>(const UpdateInfo& a, const UpdateInfo& b) const; - template bool LinearEqualityModule::preferWitness<true>(const UpdateInfo& a, const UpdateInfo& b) const; template bool LinearEqualityModule::preferWitness<false>(const UpdateInfo& a, const UpdateInfo& b) const; @@ -57,14 +49,13 @@ void Border::output(std::ostream& out) const{ << "}"; } -LinearEqualityModule::LinearEqualityModule(ArithVariables& vars, Tableau& t, BoundCountingVector& boundTracking, BasicVarModelUpdateCallBack f): +LinearEqualityModule::LinearEqualityModule(ArithVariables& vars, Tableau& t, BoundInfoMap& boundsTracking, BasicVarModelUpdateCallBack f): d_variables(vars), d_tableau(t), d_basicVariableUpdates(f), d_increasing(1), d_decreasing(-1), - d_relevantErrorBuffer(), - d_boundTracking(boundTracking), + d_btracking(boundsTracking), d_areTracking(false), d_trackCallback(this) {} @@ -77,7 +68,8 @@ LinearEqualityModule::Statistics::Statistics(): d_weakeningAttempts("theory::arith::weakening::attempts",0), d_weakeningSuccesses("theory::arith::weakening::success",0), d_weakenings("theory::arith::weakening::total",0), - d_weakenTime("theory::arith::weakening::time") + d_weakenTime("theory::arith::weakening::time"), + d_forceTime("theory::arith::forcing::time") { StatisticsRegistry::registerStat(&d_statPivots); StatisticsRegistry::registerStat(&d_statUpdates); @@ -89,6 +81,7 @@ LinearEqualityModule::Statistics::Statistics(): StatisticsRegistry::registerStat(&d_weakeningSuccesses); StatisticsRegistry::registerStat(&d_weakenings); StatisticsRegistry::registerStat(&d_weakenTime); + StatisticsRegistry::registerStat(&d_forceTime); } LinearEqualityModule::Statistics::~Statistics(){ @@ -102,50 +95,56 @@ LinearEqualityModule::Statistics::~Statistics(){ StatisticsRegistry::unregisterStat(&d_weakeningSuccesses); StatisticsRegistry::unregisterStat(&d_weakenings); StatisticsRegistry::unregisterStat(&d_weakenTime); + StatisticsRegistry::unregisterStat(&d_forceTime); } -void LinearEqualityModule::includeBoundCountChange(ArithVar nb, BoundCounts prev){ - if(d_tableau.isBasic(nb)){ - return; - } - Assert(!d_tableau.isBasic(nb)); + +void LinearEqualityModule::includeBoundUpdate(ArithVar v, const BoundsInfo& prev){ Assert(!d_areTracking); - BoundCounts curr = d_variables.boundCounts(nb); + BoundsInfo curr = d_variables.boundsInfo(v); Assert(prev != curr); - Tableau::ColIterator basicIter = d_tableau.colIterator(nb); + Tableau::ColIterator basicIter = d_tableau.colIterator(v); for(; !basicIter.atEnd(); ++basicIter){ const Tableau::Entry& entry = *basicIter; - Assert(entry.getColVar() == nb); + Assert(entry.getColVar() == v); int a_ijSgn = entry.getCoefficient().sgn(); - ArithVar basic = d_tableau.rowIndexToBasic(entry.getRowIndex()); - - BoundCounts& counts = d_boundTracking.get(basic); - Debug("includeBoundCountChange") << basic << " " << counts << " to " ; - counts -= prev.multiplyBySgn(a_ijSgn); - counts += curr.multiplyBySgn(a_ijSgn); - Debug("includeBoundCountChange") << counts << " " << a_ijSgn << std::endl; + RowIndex ridx = entry.getRowIndex(); + BoundsInfo& counts = d_btracking.get(ridx); + Debug("includeBoundUpdate") << d_tableau.rowIndexToBasic(ridx) << " " << counts << " to " ; + counts.addInChange(a_ijSgn, prev, curr); + Debug("includeBoundUpdate") << counts << " " << a_ijSgn << std::endl; } - d_boundTracking.set(nb, curr); } void LinearEqualityModule::updateMany(const DenseMap<DeltaRational>& many){ for(DenseMap<DeltaRational>::const_iterator i = many.begin(), i_end = many.end(); i != i_end; ++i){ ArithVar nb = *i; - Assert(!d_tableau.isBasic(nb)); - const DeltaRational& newValue = many[nb]; - if(newValue != d_variables.getAssignment(nb)){ - Trace("arith::updateMany") - << "updateMany:" << nb << " " - << d_variables.getAssignment(nb) << " to "<< newValue << endl; - update(nb, newValue); + if(!d_tableau.isBasic(nb)){ + Assert(!d_tableau.isBasic(nb)); + const DeltaRational& newValue = many[nb]; + if(newValue != d_variables.getAssignment(nb)){ + Trace("arith::updateMany") + << "updateMany:" << nb << " " + << d_variables.getAssignment(nb) << " to "<< newValue << endl; + update(nb, newValue); + } } } } + + +void LinearEqualityModule::applySolution(const DenseSet& newBasis, const DenseMap<DeltaRational>& newValues){ + forceNewBasis(newBasis); + updateMany(newValues); +} + void LinearEqualityModule::forceNewBasis(const DenseSet& newBasis){ + TimerStat::CodeTimer codeTimer(d_statistics.d_forceTime); + cout << "force begin" << endl; DenseSet needsToBeAdded; for(DenseSet::const_iterator i = newBasis.begin(), i_end = newBasis.end(); i != i_end; ++i){ ArithVar b = *i; @@ -179,10 +178,12 @@ void LinearEqualityModule::forceNewBasis(const DenseSet& newBasis){ Assert(toAdd != ARITHVAR_SENTINEL); Trace("arith::forceNewBasis") << toRemove << " " << toAdd << endl; + Message() << toRemove << " " << toAdd << endl; d_tableau.pivot(toRemove, toAdd, d_trackCallback); d_basicVariableUpdates(toAdd); Trace("arith::forceNewBasis") << needsToBeAdded.size() << "to go" << endl; + Message() << needsToBeAdded.size() << "to go" << endl; needsToBeAdded.remove(toAdd); } } @@ -231,9 +232,9 @@ void LinearEqualityModule::updateTracked(ArithVar x_i, const DeltaRational& v){ << d_variables.getAssignment(x_i) << "|-> " << v << endl; - BoundCounts before = d_variables.boundCounts(x_i); + BoundCounts before = d_variables.atBoundCounts(x_i); d_variables.setAssignment(x_i, v); - BoundCounts after = d_variables.boundCounts(x_i); + BoundCounts after = d_variables.atBoundCounts(x_i); bool anyChange = before != after; @@ -242,17 +243,24 @@ void LinearEqualityModule::updateTracked(ArithVar x_i, const DeltaRational& v){ const Tableau::Entry& entry = *colIter; Assert(entry.getColVar() == x_i); - ArithVar x_j = d_tableau.rowIndexToBasic(entry.getRowIndex()); + RowIndex ridx = entry.getRowIndex(); + ArithVar x_j = d_tableau.rowIndexToBasic(ridx); const Rational& a_ji = entry.getCoefficient(); const DeltaRational& assignment = d_variables.getAssignment(x_j); DeltaRational nAssignment = assignment+(diff * a_ji); Debug("update") << x_j << " " << a_ji << assignment << " -> " << nAssignment << endl; + BoundCounts xjBefore = d_variables.atBoundCounts(x_j); d_variables.setAssignment(x_j, nAssignment); + BoundCounts xjAfter = d_variables.atBoundCounts(x_j); - if(anyChange && basicIsTracked(x_j)){ - BoundCounts& next_bc_k = d_boundTracking.get(x_j); - next_bc_k.addInChange(a_ji.sgn(), before, after); + Assert(rowIndexIsTracked(ridx)); + BoundsInfo& next_bc_k = d_btracking.get(ridx); + if(anyChange){ + next_bc_k.addInAtBoundChange(a_ji.sgn(), before, after); + } + if(xjBefore != xjAfter){ + next_bc_k.addInAtBoundChange(-1, xjBefore, xjAfter); } d_basicVariableUpdates(x_j); @@ -332,7 +340,7 @@ void LinearEqualityModule::debugCheckTracking(){ ArithVar var = entry.getColVar(); const Rational& coeff = entry.getCoefficient(); DeltaRational beta = d_variables.getAssignment(var); - Debug("arith::tracking") << var << " " << d_variables.boundCounts(var) + Debug("arith::tracking") << var << " " << d_variables.boundsInfo(var) << " " << beta << coeff; if(d_variables.hasLowerBound(var)){ Debug("arith::tracking") << "(lb " << d_variables.getLowerBound(var) << ")"; @@ -345,11 +353,12 @@ void LinearEqualityModule::debugCheckTracking(){ Debug("arith::tracking") << "end row"<< endl; if(basicIsTracked(basic)){ - BoundCounts computed = computeBoundCounts(basic); + RowIndex ridx = d_tableau.basicToRowIndex(basic); + BoundsInfo computed = computeRowBoundInfo(ridx, false); Debug("arith::tracking") << "computed " << computed - << " tracking " << d_boundTracking[basic] << endl; - Assert(computed == d_boundTracking[basic]); + << " tracking " << d_btracking[ridx] << endl; + Assert(computed == d_btracking[ridx]); } } @@ -426,19 +435,19 @@ bool LinearEqualityModule::debugEntireLinEqIsConsistent(const string& s){ return result; } -DeltaRational LinearEqualityModule::computeBound(ArithVar basic, bool upperBound){ +DeltaRational LinearEqualityModule::computeRowBound(RowIndex ridx, bool rowUb, ArithVar skip) const { DeltaRational sum(0,0); - for(Tableau::RowIterator i = d_tableau.basicRowIterator(basic); !i.atEnd(); ++i){ + for(Tableau::RowIterator i = d_tableau.ridRowIterator(ridx); !i.atEnd(); ++i){ const Tableau::Entry& entry = (*i); - ArithVar nonbasic = entry.getColVar(); - if(nonbasic == basic) continue; + ArithVar v = entry.getColVar(); + if(v == skip){ continue; } + const Rational& coeff = entry.getCoefficient(); - int sgn = coeff.sgn(); - bool ub = upperBound ? (sgn > 0) : (sgn < 0); + bool vUb = (rowUb == (coeff.sgn() > 0)); - const DeltaRational& bound = ub ? - d_variables.getUpperBound(nonbasic): - d_variables.getLowerBound(nonbasic); + const DeltaRational& bound = vUb ? + d_variables.getUpperBound(v): + d_variables.getLowerBound(v); DeltaRational diff = bound * coeff; sum = sum + diff; @@ -449,7 +458,7 @@ DeltaRational LinearEqualityModule::computeBound(ArithVar basic, bool upperBound /** * Computes the value of a basic variable using the current assignment. */ -DeltaRational LinearEqualityModule::computeRowValue(ArithVar x, bool useSafe){ +DeltaRational LinearEqualityModule::computeRowValue(ArithVar x, bool useSafe) const{ Assert(d_tableau.isBasic(x)); DeltaRational sum(0); @@ -465,76 +474,71 @@ DeltaRational LinearEqualityModule::computeRowValue(ArithVar x, bool useSafe){ return sum; } -bool LinearEqualityModule::hasBounds(ArithVar basic, bool upperBound){ - for(Tableau::RowIterator iter = d_tableau.basicRowIterator(basic); !iter.atEnd(); ++iter){ +const Tableau::Entry* LinearEqualityModule::rowLacksBound(RowIndex ridx, bool rowUb, ArithVar skip){ + Tableau::RowIterator iter = d_tableau.ridRowIterator(ridx); + for(; !iter.atEnd(); ++iter){ const Tableau::Entry& entry = *iter; ArithVar var = entry.getColVar(); - if(var == basic) continue; + if(var == skip) { continue; } + int sgn = entry.getCoefficient().sgn(); - if(upperBound){ - if( (sgn < 0 && !d_variables.hasLowerBound(var)) || - (sgn > 0 && !d_variables.hasUpperBound(var))){ - return false; - } - }else{ - if( (sgn < 0 && !d_variables.hasUpperBound(var)) || - (sgn > 0 && !d_variables.hasLowerBound(var))){ - return false; - } + bool selectUb = (rowUb == (sgn > 0)); + bool hasBound = selectUb ? + d_variables.hasUpperBound(var): + d_variables.hasLowerBound(var); + if(!hasBound){ + return &entry; } } - return true; + return NULL; } -template <bool upperBound> -void LinearEqualityModule::propagateNonbasics(ArithVar basic, Constraint c){ - Assert(d_tableau.isBasic(basic)); - Assert(c->getVariable() == basic); +void LinearEqualityModule::propagateBasicFromRow(Constraint c){ + Assert(c != NullConstraint); + Assert(c->isUpperBound() || c->isLowerBound()); Assert(!c->assertedToTheTheory()); - Assert(!upperBound || c->isUpperBound()); // upperbound implies c is an upperbound - Assert(upperBound || c->isLowerBound()); // !upperbound implies c is a lowerbound - //Assert(c->canBePropagated()); Assert(!c->hasProof()); - Debug("arith::explainNonbasics") << "LinearEqualityModule::explainNonbasics(" - << basic <<") start" << endl; + bool upperBound = c->isUpperBound(); + ArithVar basic = c->getVariable(); + RowIndex ridx = d_tableau.basicToRowIndex(basic); vector<Constraint> bounds; + propagateRow(bounds, ridx, upperBound, c); + c->impliedBy(bounds); +} + +void LinearEqualityModule::propagateRow(vector<Constraint>& into, RowIndex ridx, bool rowUp, Constraint c){ + Assert(!c->assertedToTheTheory()); + Assert(c->canBePropagated()); + Assert(!c->hasProof()); + + ArithVar v = c->getVariable(); + Debug("arith::explainNonbasics") << "LinearEqualityModule::explainNonbasics(" + << v <<") start" << endl; - Tableau::RowIterator iter = d_tableau.basicRowIterator(basic); + Tableau::RowIterator iter = d_tableau.ridRowIterator(ridx); for(; !iter.atEnd(); ++iter){ const Tableau::Entry& entry = *iter; ArithVar nonbasic = entry.getColVar(); - if(nonbasic == basic) continue; + if(nonbasic == v){ continue; } const Rational& a_ij = entry.getCoefficient(); int sgn = a_ij.sgn(); Assert(sgn != 0); - Constraint bound = NullConstraint; - if(upperBound){ - if(sgn < 0){ - bound = d_variables.getLowerBoundConstraint(nonbasic); - }else{ - Assert(sgn > 0); - bound = d_variables.getUpperBoundConstraint(nonbasic); - } - }else{ - if(sgn < 0){ - bound = d_variables.getUpperBoundConstraint(nonbasic); - }else{ - Assert(sgn > 0); - bound = d_variables.getLowerBoundConstraint(nonbasic); - } - } + bool selectUb = rowUp ? (sgn > 0) : (sgn < 0); + Constraint bound = selectUb + ? d_variables.getUpperBoundConstraint(nonbasic) + : d_variables.getLowerBoundConstraint(nonbasic); + Assert(bound != NullConstraint); Debug("arith::explainNonbasics") << "explainNonbasics" << bound << " for " << c << endl; - bounds.push_back(bound); + into.push_back(bound); } - c->impliedBy(bounds); Debug("arith::explainNonbasics") << "LinearEqualityModule::explainNonbasics(" - << basic << ") done" << endl; + << v << ") done" << endl; } Constraint LinearEqualityModule::weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic) const { @@ -745,60 +749,35 @@ void LinearEqualityModule::stopTrackingBoundCounts(){ } -void LinearEqualityModule::trackVariable(ArithVar x_i){ - Assert(!basicIsTracked(x_i)); - BoundCounts counts(0,0); - - for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){ - const Tableau::Entry& entry = *iter; - ArithVar nonbasic = entry.getColVar(); - if(nonbasic == x_i) continue; - - const Rational& a_ij = entry.getCoefficient(); - counts += (d_variables.oldBoundCounts(nonbasic)).multiplyBySgn(a_ij.sgn()); - } - d_boundTracking.set(x_i, counts); +void LinearEqualityModule::trackRowIndex(RowIndex ridx){ + Assert(!rowIndexIsTracked(ridx)); + BoundsInfo bi = computeRowBoundInfo(ridx, true); + d_btracking.set(ridx, bi); } -BoundCounts LinearEqualityModule::computeBoundCounts(ArithVar x_i) const{ - BoundCounts counts(0,0); +BoundsInfo LinearEqualityModule::computeRowBoundInfo(RowIndex ridx, bool inQueue) const{ + BoundsInfo bi; - for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){ + Tableau::RowIterator iter = d_tableau.ridRowIterator(ridx); + for(; !iter.atEnd(); ++iter){ const Tableau::Entry& entry = *iter; - ArithVar nonbasic = entry.getColVar(); - if(nonbasic == x_i) continue; - + ArithVar v = entry.getColVar(); const Rational& a_ij = entry.getCoefficient(); - counts += (d_variables.boundCounts(nonbasic)).multiplyBySgn(a_ij.sgn()); + bi += (d_variables.selectBoundsInfo(v, inQueue)).multiplyBySgn(a_ij.sgn()); } - - return counts; + return bi; } -// BoundCounts LinearEqualityModule::cachingCountBounds(ArithVar x_i) const{ -// if(d_boundTracking.isKey(x_i)){ -// return d_boundTracking[x_i]; -// }else{ -// return computeBoundCounts(x_i); -// } -// } -BoundCounts LinearEqualityModule::_countBounds(ArithVar x_i) const { - Assert(d_boundTracking.isKey(x_i)); - return d_boundTracking[x_i]; +BoundCounts LinearEqualityModule::debugBasicAtBoundCount(ArithVar x_i) const { + return d_btracking[d_tableau.basicToRowIndex(x_i)].atBounds(); } -// BoundCounts LinearEqualityModule::countBounds(ArithVar x_i){ -// if(d_boundTracking.isKey(x_i)){ -// return d_boundTracking[x_i]; -// }else{ -// BoundCounts bc = computeBoundCounts(x_i); -// if(d_areTracking){ -// d_boundTracking.set(x_i,bc); -// } -// return bc; -// } -// } - +/** + * If the pivot described in u were performed, + * then the row would qualify as being either at the minimum/maximum + * to the non-basics being at their bounds. + * The minimum/maximum is determined by the direction the non-basic is changing. + */ bool LinearEqualityModule::basicsAtBounds(const UpdateInfo& u) const { Assert(u.describesPivot()); @@ -814,289 +793,78 @@ bool LinearEqualityModule::basicsAtBounds(const UpdateInfo& u) const { int toLB = (c->getType() == LowerBound || c->getType() == Equality) ? 1 : 0; + RowIndex ridx = d_tableau.basicToRowIndex(basic); - BoundCounts bcs = d_boundTracking[basic]; + BoundCounts bcs = d_btracking[ridx].atBounds(); // x = c*n + \sum d*m - // n = 1/c * x + -1/c * (\sum d*m) - BoundCounts nonb = bcs - d_variables.boundCounts(nonbasic).multiplyBySgn(coeffSgn); + // 0 = -x + c*n + \sum d*m + // n = 1/c * x + -1/c * (\sum d*m) + BoundCounts nonb = bcs - d_variables.atBoundCounts(nonbasic).multiplyBySgn(coeffSgn); + nonb.addInChange(-1, d_variables.atBoundCounts(basic), BoundCounts(toLB, toUB)); nonb = nonb.multiplyBySgn(-coeffSgn); - nonb += BoundCounts(toLB, toUB).multiplyBySgn(coeffSgn); uint32_t length = d_tableau.basicRowLength(basic); Debug("basicsAtBounds") << "bcs " << bcs << "nonb " << nonb << "length " << length << endl; - + // nonb has nb excluded. if(nbdir < 0){ - return bcs.atLowerBounds() + 1 == length; + return nonb.lowerBoundCount() + 1 == length; }else{ Assert(nbdir > 0); - return bcs.atUpperBounds() + 1 == length; + return nonb.upperBoundCount() + 1 == length; } } bool LinearEqualityModule::nonbasicsAtLowerBounds(ArithVar basic) const { Assert(basicIsTracked(basic)); - BoundCounts bcs = d_boundTracking[basic]; + RowIndex ridx = d_tableau.basicToRowIndex(basic); + + BoundCounts bcs = d_btracking[ridx].atBounds(); uint32_t length = d_tableau.basicRowLength(basic); - return bcs.atLowerBounds() + 1 == length; + // return true if excluding the basic is every element is at its "lowerbound" + // The psuedo code is: + // bcs -= basic.count(basic, basic's sgn) + // return bcs.lowerBoundCount() + 1 == length + // As basic's sign is always -1, we can pull out the pieces of the count: + // bcs.lowerBoundCount() - basic.atUpperBoundInd() + 1 == length + // basic.atUpperBoundInd() is either 0 or 1 + uint32_t lbc = bcs.lowerBoundCount(); + return (lbc == length) || + (lbc + 1 == length && d_variables.cmpAssignmentUpperBound(basic) != 0); } bool LinearEqualityModule::nonbasicsAtUpperBounds(ArithVar basic) const { Assert(basicIsTracked(basic)); - BoundCounts bcs = d_boundTracking[basic]; + RowIndex ridx = d_tableau.basicToRowIndex(basic); + BoundCounts bcs = d_btracking[ridx].atBounds(); uint32_t length = d_tableau.basicRowLength(basic); + uint32_t ubc = bcs.upperBoundCount(); + // See the comment for nonbasicsAtLowerBounds() - return bcs.atUpperBounds() + 1 == length; -} - -void LinearEqualityModule::trackingSwap(ArithVar basic, ArithVar nb, int nbSgn) { - Assert(basicIsTracked(basic)); - - // z = a*x + \sum b*y - // x = (1/a) z + \sum (-1/a)*b*y - // basicCount(z) = bc(a*x) + bc(\sum b y) - // basicCount(x) = bc(z/a) + bc(\sum -b/a * y) - - // sgn(1/a) = sgn(a) - // bc(a*x) = bc(x).multiply(sgn(a)) - // bc(z/a) = bc(z).multiply(sgn(a)) - // bc(\sum -b/a * y) = bc(\sum b y).multiplyBySgn(-sgn(a)) - // bc(\sum b y) = basicCount(z) - bc(a*x) - // basicCount(x) = - // = bc(z).multiply(sgn(a)) + (basicCount(z) - bc(a*x)).multiplyBySgn(-sgn(a)) - - BoundCounts bc = d_boundTracking[basic]; - bc -= (d_variables.boundCounts(nb)).multiplyBySgn(nbSgn); - bc = bc.multiplyBySgn(-nbSgn); - bc += d_variables.boundCounts(basic).multiplyBySgn(nbSgn); - d_boundTracking.set(nb, bc); - d_boundTracking.remove(basic); -} - -void LinearEqualityModule::trackingCoefficientChange(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn){ - Assert(oldSgn != currSgn); - BoundCounts nb_bc = d_variables.boundCounts(nb); - - if(!nb_bc.isZero()){ - ArithVar basic = d_tableau.rowIndexToBasic(ridx); - Assert(basicIsTracked(basic)); - - BoundCounts& basic_bc = d_boundTracking.get(basic); - basic_bc.addInSgn(nb_bc, oldSgn, currSgn); - } + return (ubc == length) || + (ubc + 1 == length && d_variables.cmpAssignmentLowerBound(basic) != 0); } -void LinearEqualityModule::computeSafeUpdate(UpdateInfo& inf, VarPreferenceFunction pref){ - ArithVar nb = inf.nonbasic(); - int sgn = inf.nonbasicDirection(); +void LinearEqualityModule::trackingMultiplyRow(RowIndex ridx, int sgn) { + Assert(rowIndexIsTracked(ridx)); Assert(sgn != 0); - Assert(!d_tableau.isBasic(nb)); - - //inf.setErrorsChange(0); - //inf.setlimiting = NullConstraint; - - - // Error variables moving in the correct direction - Assert(d_relevantErrorBuffer.empty()); - - // phases : - enum ComputeSafePhase { - NoBoundSelected, - NbsBoundSelected, - BasicBoundSelected, - DegenerateBoundSelected - } phase; - - phase = NoBoundSelected; - - static int instance = 0; - Debug("computeSafeUpdate") << "computeSafeUpdate " << (++instance) << endl; - - if(sgn > 0 && d_variables.hasUpperBound(nb)){ - Constraint ub = d_variables.getUpperBoundConstraint(nb); - inf.updatePureFocus(ub->getValue() - d_variables.getAssignment(nb), ub); - - Assert(inf.nonbasicDelta().sgn() == sgn); - Debug("computeSafeUpdate") << "computeSafeUpdate " << inf.limiting() << endl; - phase = NbsBoundSelected; - }else if(sgn < 0 && d_variables.hasLowerBound(nb)){ - Constraint lb = d_variables.getLowerBoundConstraint(nb); - inf.updatePureFocus(lb->getValue() - d_variables.getAssignment(nb), lb); - - Assert(inf.nonbasicDelta().sgn() == sgn); - - Debug("computeSafeUpdate") << "computeSafeUpdate " << inf.limiting() << endl; - phase = NbsBoundSelected; - } - - Tableau::ColIterator basicIter = d_tableau.colIterator(nb); - for(; !basicIter.atEnd(); ++basicIter){ - const Tableau::Entry& entry = *basicIter; - Assert(entry.getColVar() == nb); - - ArithVar basic = d_tableau.rowIndexToBasic(entry.getRowIndex()); - const Rational& a_ji = entry.getCoefficient(); - int basic_movement = sgn * a_ji.sgn(); - - Debug("computeSafeUpdate") - << "computeSafeUpdate: " - << basic << ", " - << basic_movement << ", " - << d_variables.cmpAssignmentUpperBound(basic) << ", " - << d_variables.cmpAssignmentLowerBound(basic) << ", " - << a_ji << ", " - << d_variables.getAssignment(basic) << endl; - - Constraint proposal = NullConstraint; - - if(basic_movement > 0){ - if(d_variables.cmpAssignmentLowerBound(basic) < 0){ - d_relevantErrorBuffer.push_back(&entry); - } - if(d_variables.hasUpperBound(basic) && - d_variables.cmpAssignmentUpperBound(basic) <= 0){ - proposal = d_variables.getUpperBoundConstraint(basic); - } - }else if(basic_movement < 0){ - if(d_variables.cmpAssignmentUpperBound(basic) > 0){ - d_relevantErrorBuffer.push_back(&entry); - } - if(d_variables.hasLowerBound(basic) && - d_variables.cmpAssignmentLowerBound(basic) >= 0){ - proposal = d_variables.getLowerBoundConstraint(basic); - } - } - if(proposal != NullConstraint){ - const Rational& coeff = entry.getCoefficient(); - DeltaRational diff = proposal->getValue() - d_variables.getAssignment(basic); - diff /= coeff; - int cmp = phase == NoBoundSelected ? 0 : diff.cmp(inf.nonbasicDelta()); - Assert(diff.sgn() == sgn || diff.sgn() == 0); - bool prefer = false; - switch(phase){ - case NoBoundSelected: - prefer = true; - break; - case NbsBoundSelected: - prefer = (sgn > 0 && cmp < 0 ) || (sgn < 0 && cmp > 0); - break; - case BasicBoundSelected: - prefer = - (sgn > 0 && cmp < 0 ) || - (sgn < 0 && cmp > 0) || - (cmp == 0 && basic == (this->*pref)(basic, inf.leaving())); - break; - case DegenerateBoundSelected: - prefer = cmp == 0 && basic == (this->*pref)(basic, inf.leaving()); - break; - } - if(prefer){ - inf.updatePivot(diff, coeff, proposal); - - phase = (diff.sgn() != 0) ? BasicBoundSelected : DegenerateBoundSelected; - } - } + if(sgn < 0){ + BoundsInfo& bi = d_btracking.get(ridx); + bi = bi.multiplyBySgn(sgn); } - - if(phase == DegenerateBoundSelected){ - inf.setErrorsChange(0); - }else{ - computedFixed(inf); - } - inf.determineFocusDirection(); - - d_relevantErrorBuffer.clear(); } -void LinearEqualityModule::computedFixed(UpdateInfo& proposal){ - Assert(proposal.nonbasicDirection() != 0); - Assert(!d_tableau.isBasic(proposal.nonbasic())); - - //bool unconstrained = (proposal.d_limiting == NullConstraint); - - Assert(!proposal.unbounded() || !d_relevantErrorBuffer.empty()); - - Assert(proposal.unbounded() || - proposal.nonbasicDelta().sgn() == proposal.nonbasicDirection()); - - // proposal.d_value is the max - - UpdateInfo max; - int dropped = 0; - //Constraint maxFix = NullConstraint; - //DeltaRational maxAmount; - - EntryPointerVector::const_iterator i = d_relevantErrorBuffer.begin(); - EntryPointerVector::const_iterator i_end = d_relevantErrorBuffer.end(); - for(; i != i_end; ++i){ - const Tableau::Entry& entry = *(*i); - Assert(entry.getColVar() == proposal.nonbasic()); - - ArithVar basic = d_tableau.rowIndexToBasic(entry.getRowIndex()); - const Rational& a_ji = entry.getCoefficient(); - int basic_movement = proposal.nonbasicDirection() * a_ji.sgn(); - - DeltaRational theta; - DeltaRational proposedValue; - if(!proposal.unbounded()){ - theta = proposal.nonbasicDelta() * a_ji; - proposedValue = theta + d_variables.getAssignment(basic); - } - - Constraint fixed = NullConstraint; - - if(basic_movement < 0){ - Assert(d_variables.cmpAssignmentUpperBound(basic) > 0); - - if(proposal.unbounded() || d_variables.cmpToUpperBound(basic, proposedValue) <= 0){ - --dropped; - fixed = d_variables.getUpperBoundConstraint(basic); - } - }else if(basic_movement > 0){ - Assert(d_variables.cmpAssignmentLowerBound(basic) < 0); +void LinearEqualityModule::trackingCoefficientChange(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn){ + Assert(oldSgn != currSgn); + BoundsInfo nb_inf = d_variables.boundsInfo(nb); - if(proposal.unbounded() || d_variables.cmpToLowerBound(basic, proposedValue) >= 0){ - --dropped; - fixed = d_variables.getLowerBoundConstraint(basic); - } - } - if(fixed != NullConstraint){ - DeltaRational amount = fixed->getValue() - d_variables.getAssignment(basic); - amount /= a_ji; - Assert(amount.sgn() == proposal.nonbasicDirection()); - - if(max.uninitialized()){ - max = UpdateInfo(proposal.nonbasic(), proposal.nonbasicDirection()); - max.updatePivot(amount, a_ji, fixed, dropped); - }else{ - int cmp = amount.cmp(max.nonbasicDelta()); - bool prefer = - (proposal.nonbasicDirection() < 0 && cmp < 0) || - (proposal.nonbasicDirection() > 0 && cmp > 0) || - (cmp == 0 && fixed->getVariable() < max.limiting()->getVariable()); - - if(prefer){ - max.updatePivot(amount, a_ji, fixed, dropped); - }else{ - max.setErrorsChange(dropped); - } - } - } - } - Assert(dropped < 0 || !proposal.unbounded()); + Assert(rowIndexIsTracked(ridx)); - if(dropped < 0){ - proposal = max; - }else{ - Assert(dropped == 0); - Assert(proposal.nonbasicDelta().sgn() != 0); - Assert(proposal.nonbasicDirection() != 0); - proposal.setErrorsChange(0); - } - Assert(proposal.errorsChange() == dropped); + BoundsInfo& row_bi = d_btracking.get(ridx); + row_bi.addInSgn(nb_inf, oldSgn, currSgn); } ArithVar LinearEqualityModule::minBy(const ArithVarVec& vec, VarPreferenceFunction pf) const{ @@ -1188,8 +956,9 @@ bool LinearEqualityModule::willBeInConflictAfterPivot(const Tableau::Entry& entr // Assume past this point, nb will be in error if this pivot is done ArithVar nb = entry.getColVar(); - ArithVar basic = d_tableau.rowIndexToBasic(entry.getRowIndex()); - Assert(basicIsTracked(basic)); + RowIndex ridx = entry.getRowIndex(); + ArithVar basic = d_tableau.rowIndexToBasic(ridx); + Assert(rowIndexIsTracked(ridx)); int coeffSgn = entry.getCoefficient().sgn(); @@ -1201,12 +970,11 @@ bool LinearEqualityModule::willBeInConflictAfterPivot(const Tableau::Entry& entr // 2) -a * x = -y + \sum b * z // 3) x = (-1/a) * ( -y + \sum b * z) - Assert(basicIsTracked(basic)); - BoundCounts bc = d_boundTracking[basic]; + BoundCounts bc = d_btracking[ridx].atBounds(); // 1) y = a * x + \sum b * z // Get bc(\sum b * z) - BoundCounts sumOnly = bc - d_variables.boundCounts(nb).multiplyBySgn(coeffSgn); + BoundCounts sumOnly = bc - d_variables.atBoundCounts(nb).multiplyBySgn(coeffSgn); // y's bounds in the proposed model int yWillBeAtUb = (bToUB || d_variables.boundsAreEqual(basic)) ? 1 : 0; @@ -1215,19 +983,19 @@ bool LinearEqualityModule::willBeInConflictAfterPivot(const Tableau::Entry& entr // 2) -a * x = -y + \sum b * z // Get bc(-y + \sum b * z) - BoundCounts withNegY = sumOnly + ysBounds.multiplyBySgn(-1); + sumOnly.addInChange(-1, d_variables.atBoundCounts(basic), ysBounds); // 3) x = (-1/a) * ( -y + \sum b * z) // Get bc((-1/a) * ( -y + \sum b * z)) - BoundCounts xsBoundsAfterPivot = withNegY.multiplyBySgn(-coeffSgn); + BoundCounts xsBoundsAfterPivot = sumOnly.multiplyBySgn(-coeffSgn); uint32_t length = d_tableau.basicRowLength(basic); if(nbSgn > 0){ // Only check for the upper bound being violated - return xsBoundsAfterPivot.atLowerBounds() + 1 == length; + return xsBoundsAfterPivot.lowerBoundCount() + 1 == length; }else{ // Only check for the lower bound being violated - return xsBoundsAfterPivot.atUpperBounds() + 1 == length; + return xsBoundsAfterPivot.upperBoundCount() + 1 == length; } } diff --git a/src/theory/arith/linear_equality.h b/src/theory/arith/linear_equality.h index 8b9b888f2..293a0ddad 100644 --- a/src/theory/arith/linear_equality.h +++ b/src/theory/arith/linear_equality.h @@ -192,9 +192,6 @@ public: }; - - - class LinearEqualityModule { public: typedef ArithVar (LinearEqualityModule::*VarPreferenceFunction)(ArithVar, ArithVar) const; @@ -226,14 +223,12 @@ public: * Initializes a LinearEqualityModule with a partial model, a tableau, * and a callback function for when basic variables update their values. */ - LinearEqualityModule(ArithVariables& vars, Tableau& t, BoundCountingVector& boundTracking, BasicVarModelUpdateCallBack f); + LinearEqualityModule(ArithVariables& vars, Tableau& t, BoundInfoMap& boundTracking, BasicVarModelUpdateCallBack f); /** * Updates the assignment of a nonbasic variable x_i to v. * Also updates the assignment of basic variables accordingly. */ - void updateUntracked(ArithVar x_i, const DeltaRational& v); - void updateTracked(ArithVar x_i, const DeltaRational& v); void update(ArithVar x_i, const DeltaRational& v){ if(d_areTracking){ updateTracked(x_i,v); @@ -241,7 +236,13 @@ public: updateUntracked(x_i,v); } } - void updateMany(const DenseMap<DeltaRational>& many); + + /** Specialization of update if the module is not tracking yet (for Assert*). */ + void updateUntracked(ArithVar x_i, const DeltaRational& v); + + /** Specialization of update if the module is not tracking yet (for Simplex). */ + void updateTracked(ArithVar x_i, const DeltaRational& v); + /** * Updates the value of a basic variable x_i to v, @@ -249,28 +250,34 @@ public: * Updates the assignment of the other basic variables accordingly. */ void pivotAndUpdate(ArithVar x_i, ArithVar x_j, const DeltaRational& v); - //void pivotAndUpdateAdj(ArithVar x_i, ArithVar x_j, const DeltaRational& v); ArithVariables& getVariables() const{ return d_variables; } Tableau& getTableau() const{ return d_tableau; } + /** + * Updates every non-basic to reflect the assignment in many. + * For use with ApproximateSimplex. + */ + void updateMany(const DenseMap<DeltaRational>& many); void forceNewBasis(const DenseSet& newBasis); + void applySolution(const DenseSet& newBasis, const DenseMap<DeltaRational>& newValues); + + + /** + * Returns a pointer to the first Tableau entry on the row ridx that does not + * have an either a lower bound/upper bound for proving a bound on skip. + * The variable skip is always excluded. Returns NULL if there is no such element. + * + * If skip == ARITHVAR_SENTINEL, this is equivalent to considering the whole row. + */ + const Tableau::Entry* rowLacksBound(RowIndex ridx, bool upperBound, ArithVar skip); - bool hasBounds(ArithVar basic, bool upperBound); - bool hasLowerBounds(ArithVar basic){ - return hasBounds(basic, false); - } - bool hasUpperBounds(ArithVar basic){ - return hasBounds(basic, true); - } void startTrackingBoundCounts(); void stopTrackingBoundCounts(); - void includeBoundCountChange(ArithVar nb, BoundCounts prev); - - void computeSafeUpdate(UpdateInfo& inf, VarPreferenceFunction basic); + void includeBoundUpdate(ArithVar nb, const BoundsInfo& prev); uint32_t updateProduct(const UpdateInfo& inf) const; @@ -334,40 +341,6 @@ public: } } - // template<bool heuristic> - // bool preferNonDegenerate(const UpdateInfo& a, const UpdateInfo& b) const{ - // if(a.focusDirection() == b.focusDirection()){ - // if(heuristic){ - // return preferNeitherBound(a,b); - // }else{ - // return minNonBasicVarOrder(a,b); - // } - // }else{ - // return a.focusDirection() < b.focusDirection(); - // } - // } - - // template <bool heuristic> - // bool preferErrorsFixed(const UpdateInfo& a, const UpdateInfo& b) const{ - // if( a.errorsChange() == b.errorsChange() ){ - // return preferNonDegenerate<heuristic>(a,b); - // }else{ - // return a.errorsChange() > b.errorsChange(); - // } - // } - - // template <bool heuristic> - // bool preferConflictFound(const UpdateInfo& a, const UpdateInfo& b) const{ - // if(a.d_foundConflict && b.d_foundConflict){ - // // if both are true, determinize the preference - // return minNonBasicVarOrder(a,b); - // }else if( a.d_foundConflict || b.d_foundConflict ){ - // return b.d_foundConflict; - // }else{ - // return preferErrorsFixed<heuristic>(a,b); - // } - // } - bool modifiedBlands(const UpdateInfo& a, const UpdateInfo& b) const { Assert(a.focusDirection() == 0 && b.focusDirection() == 0); Assert(a.describesPivot()); @@ -427,31 +400,27 @@ public: } private: - typedef std::vector<const Tableau::Entry*> EntryPointerVector; - EntryPointerVector d_relevantErrorBuffer; - - //uint32_t computeUnconstrainedUpdate(ArithVar nb, int sgn, DeltaRational& am); - //uint32_t computedFixed(ArithVar nb, int sgn, const DeltaRational& am); - void computedFixed(UpdateInfo&); - // RowIndex -> BoundCount - BoundCountingVector& d_boundTracking; + /** + * This maps each row index to its relevant bounds info. + * This tracks the count for how many variables on a row have bounds + * and how many are assigned at their bounds. + */ + BoundInfoMap& d_btracking; bool d_areTracking; +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); + /** * Exports either the explanation of an upperbound or a lower bound * of the basic variable basic, using the non-basic variables in the row. */ - template <bool upperBound> - void propagateNonbasics(ArithVar basic, Constraint c); - -public: - void propagateNonbasicsLowerBound(ArithVar basic, Constraint c){ - propagateNonbasics<false>(basic, c); - } - void propagateNonbasicsUpperBound(ArithVar basic, Constraint c){ - propagateNonbasics<true>(basic, c); - } + void propagateRow(std::vector<Constraint>& into, RowIndex ridx, bool rowUp, Constraint c); /** * Computes the value of a basic variable using the assignments @@ -460,14 +429,7 @@ public: * - the the current assignment (useSafe=false) or * - the safe assignment (useSafe = true). */ - DeltaRational computeRowValue(ArithVar x, bool useSafe); - - inline DeltaRational computeLowerBound(ArithVar basic){ - return computeBound(basic, false); - } - inline DeltaRational computeUpperBound(ArithVar basic){ - return computeBound(basic, true); - } + DeltaRational computeRowValue(ArithVar x, bool useSafe) const; /** * A PreferenceFunction takes a const ref to the SimplexDecisionProcedure, @@ -548,41 +510,64 @@ public: const Tableau::Entry* selectSlackEntry(ArithVar x_i, bool above) const; + inline bool rowIndexIsTracked(RowIndex ridx) const { + return d_btracking.isKey(ridx); + } inline bool basicIsTracked(ArithVar v) const { - return d_boundTracking.isKey(v); + return rowIndexIsTracked(d_tableau.basicToRowIndex(v)); } - void trackVariable(ArithVar x_i); - - void maybeRemoveTracking(ArithVar v){ - Assert(!d_tableau.isBasic(v)); - if(d_boundTracking.isKey(v)){ - d_boundTracking.remove(v); - } + void trackRowIndex(RowIndex ridx); + void stopTrackingRowIndex(RowIndex ridx){ + Assert(rowIndexIsTracked(ridx)); + d_btracking.remove(ridx); } - // void trackVariable(ArithVar x_i){ - // Assert(!basicIsTracked(x_i)); - // d_boundTracking.set(x_i,computeBoundCounts(x_i)); - // } + /** + * If the pivot described in u were performed, + * then the row would qualify as being either at the minimum/maximum + * to the non-basics being at their bounds. + * The minimum/maximum is determined by the direction the non-basic is changing. + */ bool basicsAtBounds(const UpdateInfo& u) const; + private: - BoundCounts computeBoundCounts(ArithVar x_i) const; + + /** + * Recomputes the bound info for a row using either the information + * in the bounds queue or the current information. + * O(row length of ridx) + */ + BoundsInfo computeRowBoundInfo(RowIndex ridx, bool inQueue) const; + public: - //BoundCounts cachingCountBounds(ArithVar x_i) const; - BoundCounts _countBounds(ArithVar x_i) const; + /** Debug only routine. */ + BoundCounts debugBasicAtBoundCount(ArithVar x_i) const; + + /** Track the effect of the change of coefficient for bound counting. */ void trackingCoefficientChange(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn); - void trackingSwap(ArithVar basic, ArithVar nb, int sgn); + /** Track the effect of multiplying a row by a sign for bound counting. */ + void trackingMultiplyRow(RowIndex ridx, int sgn); + + /** Count for how many on a row have *an* upper/lower bounds. */ + BoundCounts hasBoundCount(RowIndex ri) const { + Assert(d_variables.boundsQueueEmpty()); + return d_btracking[ri].hasBounds(); + } + /** + * Are there any non-basics on x_i's row that are not at + * their respective lower bounds (mod sgns). + * O(1) time due to the atBound() count. + */ bool nonbasicsAtLowerBounds(ArithVar x_i) const; - bool nonbasicsAtUpperBounds(ArithVar x_i) const; - ArithVar _anySlackLowerBound(ArithVar x_i) const { - return selectSlack<true>(x_i, &LinearEqualityModule::noPreference); - } - ArithVar _anySlackUpperBound(ArithVar x_i) const { - return selectSlack<false>(x_i, &LinearEqualityModule::noPreference); - } + /** + * Are there any non-basics on x_i's row that are not at + * their respective upper bounds (mod sgns). + * O(1) time due to the atBound() count. + */ + bool nonbasicsAtUpperBounds(ArithVar x_i) const; private: class TrackingCallback : public CoefficientChangeCallback { @@ -593,8 +578,8 @@ private: void update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn){ d_linEq->trackingCoefficientChange(ridx, nb, oldSgn, currSgn); } - void swap(ArithVar basic, ArithVar nb, int oldNbSgn){ - d_linEq->trackingSwap(basic, nb, oldNbSgn); + void multiplyRow(RowIndex ridx, int sgn){ + d_linEq->trackingMultiplyRow(ridx, sgn); } bool canUseRow(RowIndex ridx) const { ArithVar basic = d_linEq->getTableau().rowIndexToBasic(ridx); @@ -629,8 +614,11 @@ public: return minimallyWeakConflict(false, conflictVar); } -private: - DeltaRational computeBound(ArithVar basic, bool upperBound); + /** + * Computes the sum of the upper/lower bound of row. + * The variable skip is not included in the sum. + */ + DeltaRational computeRowBound(RowIndex ridx, bool rowUb, ArithVar skip) const; public: void substitutePlusTimesConstant(ArithVar to, ArithVar from, const Rational& mult); @@ -703,6 +691,7 @@ private: IntStat d_weakeningAttempts, d_weakeningSuccesses, d_weakenings; TimerStat d_weakenTime; + TimerStat d_forceTime; Statistics(); ~Statistics(); @@ -737,13 +726,13 @@ public: } }; -class UpdateTrackingCallback : public BoundCountsCallback { +class UpdateTrackingCallback : public BoundUpdateCallback { private: LinearEqualityModule* d_mod; public: UpdateTrackingCallback(LinearEqualityModule* mod): d_mod(mod){} - void operator()(ArithVar v, BoundCounts bc){ - d_mod->includeBoundCountChange(v, bc); + void operator()(ArithVar v, const BoundsInfo& bi){ + d_mod->includeBoundUpdate(v, bi); } }; diff --git a/src/theory/arith/matrix.cpp b/src/theory/arith/matrix.cpp index 7136c3fa8..b8bd68488 100644 --- a/src/theory/arith/matrix.cpp +++ b/src/theory/arith/matrix.cpp @@ -24,7 +24,7 @@ namespace theory { namespace arith { void NoEffectCCCB::update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) {} -void NoEffectCCCB::swap(ArithVar basic, ArithVar nb, int nbSgn){} +void NoEffectCCCB::multiplyRow(RowIndex ridx, int sgn){} bool NoEffectCCCB::canUseRow(RowIndex ridx) const { return false; } }/* CVC4::theory::arith namespace */ diff --git a/src/theory/arith/matrix.h b/src/theory/arith/matrix.h index 100f999e0..d93b6986e 100644 --- a/src/theory/arith/matrix.h +++ b/src/theory/arith/matrix.h @@ -39,15 +39,15 @@ const RowIndex ROW_INDEX_SENTINEL = std::numeric_limits<RowIndex>::max(); class CoefficientChangeCallback { public: - virtual void update(RowIndex basic, ArithVar nb, int oldSgn, int currSgn) = 0; - virtual void swap(ArithVar basic, ArithVar nb, int nbSgn) = 0; + virtual void update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn) = 0; + virtual void multiplyRow(RowIndex ridx, int Sgn) = 0; virtual bool canUseRow(RowIndex ridx) const = 0; }; class NoEffectCCCB : public CoefficientChangeCallback { public: void update(RowIndex ridx, ArithVar nb, int oldSgn, int currSgn); - void swap(ArithVar basic, ArithVar nb, int nbSgn); + void multiplyRow(RowIndex ridx, int Sgn); bool canUseRow(RowIndex ridx) const; }; diff --git a/src/theory/arith/normal_form.cpp b/src/theory/arith/normal_form.cpp index 8454ca210..7cd202e53 100644 --- a/src/theory/arith/normal_form.cpp +++ b/src/theory/arith/normal_form.cpp @@ -22,7 +22,7 @@ using namespace std; namespace CVC4 { -namespace theory{ +namespace theory { namespace arith { bool Variable::isDivMember(Node n){ @@ -745,9 +745,8 @@ bool Comparison::isNormalGEQ() const { return false; }else{ if(left.isIntegral()){ - return left.denominatorLCMIsOne() && left.numeratorGCDIsOne(); + return left.signNormalizedReducedSum(); }else{ - Debug("nf::tmp") << "imme sdfhkdjfh "<< left.leadingCoefficientIsAbsOne() << endl; return left.leadingCoefficientIsAbsOne(); } } @@ -768,7 +767,7 @@ bool Comparison::isNormalLT() const { return false; }else{ if(left.isIntegral()){ - return left.denominatorLCMIsOne() && left.numeratorGCDIsOne(); + return left.signNormalizedReducedSum(); }else{ return left.leadingCoefficientIsAbsOne(); } @@ -889,6 +888,7 @@ Node Comparison::mkIntInequality(Kind k, const Polynomial& p){ Polynomial left = sp.getPolynomial(); Rational right = - (sp.getConstant().getValue()); + Monomial m = left.getHead(); Assert(!m.isConstant()); @@ -899,16 +899,31 @@ Node Comparison::mkIntInequality(Kind k, const Polynomial& p){ Polynomial newLeft = left * mult; Rational rightMult = right * mult; + bool negateResult = false; + if(!newLeft.leadingCoefficientIsPositive()){ + // multiply by -1 + // a: left >= right or b: left > right + // becomes + // a: -left <= -right or b: -left < -right + // a: not (-left > -right) or b: (not -left >= -right) + newLeft = -newLeft; + rightMult = -rightMult; + k = (kind::GT == k) ? kind::GEQ : kind::GT; + negateResult = true; + // the later stages handle: + // a: not (-left >= -right + 1) or b: (not -left >= -right) + } + Node result = Node::null(); if(rightMult.isIntegral()){ if(k == kind::GT){ // (> p z) // (>= p (+ z 1)) Constant rightMultPlusOne = Constant::mkConstant(rightMult + 1); - return toNode(kind::GEQ, newLeft, rightMultPlusOne); + result = toNode(kind::GEQ, newLeft, rightMultPlusOne); }else{ Constant newRight = Constant::mkConstant(rightMult); - return toNode(kind::GEQ, newLeft, newRight); + result = toNode(kind::GEQ, newLeft, newRight); } }else{ //(>= l (/ n d)) @@ -916,7 +931,13 @@ Node Comparison::mkIntInequality(Kind k, const Polynomial& p){ //This also hold for GT as (ceil (/ n d)) > (/ n d) Integer ceilr = rightMult.ceiling(); Constant ceilRight = Constant::mkConstant(ceilr); - return toNode(kind::GEQ, newLeft, ceilRight); + result = toNode(kind::GEQ, newLeft, ceilRight); + } + Assert(!result.isNull()); + if(negateResult){ + return result.notNode(); + }else{ + return result; } } diff --git a/src/theory/arith/normal_form.h b/src/theory/arith/normal_form.h index bcf9cbfa4..1dddb5a5b 100644 --- a/src/theory/arith/normal_form.h +++ b/src/theory/arith/normal_form.h @@ -76,12 +76,13 @@ namespace arith { * (exists realMonomial (monomialList qpolynomial)) * abs(monomialCoefficient (head (monomialList qpolynomial))) == 1 * - * integer_cmp := (<= zpolynomial constant) + * integer_cmp := (>= zpolynomial constant) * where * not (exists constantMonomial (monomialList zpolynomial)) * (forall integerMonomial (monomialList zpolynomial)) * the gcd of all numerators of coefficients is 1 * the denominator of all coefficients and the constant is 1 + * the leading coefficient is positive * * rational_eq := (= qvarlist qpolynomial) * where @@ -240,6 +241,11 @@ public: case kind::INTS_MODULUS_TOTAL: case kind::DIVISION_TOTAL: return isDivMember(n); + case kind::ABS: + case kind::TO_INTEGER: + // Treat to_int as a variable; it is replaced in early preprocessing + // by a variable. + return true; default: return (!isRelationOperator(k)) && (Theory::isLeafOf(n, theory::THEORY_ARITH)); @@ -939,6 +945,10 @@ public: bool denominatorLCMIsOne() const; bool numeratorGCDIsOne() const; + bool signNormalizedReducedSum() const { + return leadingCoefficientIsPositive() && denominatorLCMIsOne() && numeratorGCDIsOne(); + } + /** * Returns the Least Common Multiple of the denominators of the coefficients * of the monomials. @@ -1265,7 +1275,7 @@ private: * Creates a comparison equivalent to (k l 0). * k is either GT or GEQ. * It is not the case that all variables in l are integral. - */ + */ static Node mkRatInequality(Kind k, const Polynomial& l); public: diff --git a/src/theory/arith/options b/src/theory/arith/options index 977d6cb32..43b582bc8 100644 --- a/src/theory/arith/options +++ b/src/theory/arith/options @@ -56,7 +56,7 @@ option arithMLTrick miplib-trick --enable-miplib-trick/--disable-miplib-trick bo turns on the preprocessing step of attempting to infer bounds on miplib problems /turns off the preprocessing step of attempting to infer bounds on miplib problems -option arithMLTrickSubstitutions miplib-trick-subs --miplib-trick-subs unsigned :default 1 +option arithMLTrickSubstitutions miplib-trick-subs --miplib-trick-subs=N unsigned :default 1 do substitution for miplib 'tmp' vars if defined in <= N eliminated vars option doCutAllBounded --cut-all-bounded bool :default false :read-write @@ -67,11 +67,11 @@ option maxCutsInContext --maxCutsInContext unsigned :default 65535 maximum cuts in a given context before signalling a restart option revertArithModels --revert-arith-models-on-unsat bool :default false - Revert the arithmetic model to a known safe model on unsat if one is cached + revert the arithmetic model to a known safe model on unsat if one is cached option havePenalties --fc-penalties bool :default false :read-write turns on degenerate pivot penalties -/ turns off degenerate pivot penalties +/turns off degenerate pivot penalties option useFC --use-fcsimplex bool :default false :read-write use focusing and converging simplex (FMCAD 2013 submission) @@ -80,15 +80,27 @@ option useSOI --use-soi bool :default false :read-write use sum of infeasibility simplex (FMCAD 2013 submission) option restrictedPivots --restrict-pivots bool :default true :read-write - have a pivot cap for simplex at effort levels below fullEffort. + have a pivot cap for simplex at effort levels below fullEffort 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. + tuning how final check works for really hard problems option exportDioDecompositions --dio-decomps bool :default false :read-write - Let skolem variables for integer divisibility constraints leak from the dio solver. + let skolem variables for integer divisibility constraints leak from the dio solver + +option newProp --new-prop bool :default false :read-write + use the new row propagation system + +option arithPropAsLemmaLength --arith-prop-clauses uint16_t :default 8 :read-write + rows shorter than this are propagated as clauses + +option soiQuickExplain --soi-qe bool :default false :read-write + use quick explain to minimize the sum of infeasibility conflicts + +option rewriteDivk rewrite-divk --rewrite-divk bool :default false + rewrite division and mod when by a constant into linear terms endmodule diff --git a/src/theory/arith/partial_model.cpp b/src/theory/arith/partial_model.cpp index 695d9df25..3fae3751c 100644 --- a/src/theory/arith/partial_model.cpp +++ b/src/theory/arith/partial_model.cpp @@ -35,12 +35,13 @@ ArithVariables::ArithVariables(context::Context* c, DeltaComputeCallback deltaCo d_released(), d_releasedIterator(d_released.begin()), d_nodeToArithVarMap(), + d_boundsQueue(), + d_enqueueingBoundCounts(true), d_lbRevertHistory(c, true, LowerBoundCleanUp(this)), d_ubRevertHistory(c, true, UpperBoundCleanUp(this)), d_deltaIsSafe(false), d_delta(-1,1), - d_deltaComputingFunc(deltaComputingFunc), - d_enqueueingBoundCounts(true) + d_deltaComputingFunc(deltaComputingFunc) { } ArithVariables::VarInfo::VarInfo() @@ -55,6 +56,10 @@ ArithVariables::VarInfo::VarInfo() d_slack(false) { } +bool ArithVariables::VarInfo::initialized() const { + return d_var != ARITHVAR_SENTINEL; +} + void ArithVariables::VarInfo::initialize(ArithVar v, Node n, bool slack){ Assert(!initialized()); Assert(d_lb == NullConstraint); @@ -82,7 +87,7 @@ void ArithVariables::VarInfo::uninitialize(){ d_node = Node::null(); } -bool ArithVariables::VarInfo::setAssignment(const DeltaRational& a, BoundCounts & prev){ +bool ArithVariables::VarInfo::setAssignment(const DeltaRational& a, BoundsInfo& prev){ Assert(initialized()); d_assignment = a; int cmpUB = (d_ub == NullConstraint) ? -1 : @@ -97,7 +102,7 @@ bool ArithVariables::VarInfo::setAssignment(const DeltaRational& a, BoundCounts (cmpUB == 0 || d_cmpAssignmentUB == 0); if(lbChanged || ubChanged){ - prev = boundCounts(); + prev = boundsInfo(); } d_cmpAssignmentUB = cmpUB; @@ -119,33 +124,59 @@ void ArithVariables::releaseArithVar(ArithVar v){ } } -bool ArithVariables::VarInfo::setUpperBound(Constraint ub, BoundCounts& prev){ +bool ArithVariables::VarInfo::setUpperBound(Constraint ub, BoundsInfo& prev){ Assert(initialized()); - d_ub = ub; - int cmpUB = (d_ub == NullConstraint) ? -1 : d_assignment.cmp(d_ub->getValue()); - bool ubChanged = cmpUB != d_cmpAssignmentUB && - (cmpUB == 0 || d_cmpAssignmentUB == 0); + bool wasNull = d_ub == NullConstraint; + bool isNull = ub == NullConstraint; + + int cmpUB = isNull ? -1 : d_assignment.cmp(ub->getValue()); + bool ubChanged = (wasNull != isNull) || + (cmpUB != d_cmpAssignmentUB && (cmpUB == 0 || d_cmpAssignmentUB == 0)); if(ubChanged){ - prev = boundCounts(); + prev = boundsInfo(); } + d_ub = ub; d_cmpAssignmentUB = cmpUB; return ubChanged; } -bool ArithVariables::VarInfo::setLowerBound(Constraint lb, BoundCounts& prev){ +bool ArithVariables::VarInfo::setLowerBound(Constraint lb, BoundsInfo& prev){ Assert(initialized()); - d_lb = lb; - int cmpLB = (d_lb == NullConstraint) ? 1 : d_assignment.cmp(d_lb->getValue()); + bool wasNull = d_lb == NullConstraint; + bool isNull = lb == NullConstraint; - bool lbChanged = cmpLB != d_cmpAssignmentLB && - (cmpLB == 0 || d_cmpAssignmentLB == 0); + int cmpLB = isNull ? 1 : d_assignment.cmp(lb->getValue()); + + bool lbChanged = (wasNull != isNull) || + (cmpLB != d_cmpAssignmentLB && (cmpLB == 0 || d_cmpAssignmentLB == 0)); if(lbChanged){ - prev = boundCounts(); + prev = boundsInfo(); } + d_lb = lb; d_cmpAssignmentLB = cmpLB; return lbChanged; } +BoundCounts ArithVariables::VarInfo::atBoundCounts() const { + uint32_t lbIndc = (d_cmpAssignmentLB == 0) ? 1 : 0; + uint32_t ubIndc = (d_cmpAssignmentUB == 0) ? 1 : 0; + return BoundCounts(lbIndc, ubIndc); +} + +BoundCounts ArithVariables::VarInfo::hasBoundCounts() const { + uint32_t lbIndc = (d_lb != NullConstraint) ? 1 : 0; + uint32_t ubIndc = (d_ub != NullConstraint) ? 1 : 0; + return BoundCounts(lbIndc, ubIndc); +} + +BoundsInfo ArithVariables::VarInfo::boundsInfo() const{ + return BoundsInfo(atBoundCounts(), hasBoundCounts()); +} + +bool ArithVariables::VarInfo::canBeReclaimed() const{ + return d_pushCount == 0; +} + void ArithVariables::attemptToReclaimReleased(){ std::list<ArithVar>::iterator i_end = d_released.end(); for(int iter = 0; iter < 20 && d_releasedIterator != i_end; ++d_releasedIterator){ @@ -170,7 +201,7 @@ ArithVar ArithVariables::allocateVariable(){ attemptToReclaimReleased(); } bool reclaim = !d_pool.empty(); - + ArithVar varX; if(reclaim){ varX = d_pool.back(); @@ -210,7 +241,7 @@ void ArithVariables::setAssignment(ArithVar x, const DeltaRational& r){ } invalidateDelta(); - BoundCounts prev; + BoundsInfo prev; if(vi.setAssignment(r, prev)){ addToBoundQueue(x, prev); } @@ -229,7 +260,7 @@ void ArithVariables::setAssignment(ArithVar x, const DeltaRational& safe, const invalidateDelta(); VarInfo& vi = d_vars.get(x); - BoundCounts prev; + BoundsInfo prev; if(vi.setAssignment(r, prev)){ addToBoundQueue(x, prev); } @@ -314,7 +345,7 @@ void ArithVariables::setLowerBoundConstraint(Constraint c){ invalidateDelta(); VarInfo& vi = d_vars.get(x); pushLowerBound(vi); - BoundCounts prev; + BoundsInfo prev; if(vi.setLowerBound(c, prev)){ addToBoundQueue(x, prev); } @@ -333,37 +364,12 @@ void ArithVariables::setUpperBoundConstraint(Constraint c){ invalidateDelta(); VarInfo& vi = d_vars.get(x); pushUpperBound(vi); - BoundCounts prev; + BoundsInfo prev; if(vi.setUpperBound(c, prev)){ addToBoundQueue(x, prev); } } -// void ArithVariables::forceRelaxLowerBound(ArithVar v){ -// AssertArgument(inMaps(v), "Calling forceRelaxLowerBound on a variable that is not properly setup."); -// AssertArgument(hasLowerBound(v), "Calling forceRelaxLowerBound on a variable without a lowerbound."); - -// Debug("partial_model") << "forceRelaxLowerBound(" << v << ") dropping :" << getLowerBoundConstraint(v) << endl; - -// invalidateDelta(); -// VarInfo& vi = d_vars.get(v); -// pushLowerBound(vi); -// vi.setLowerBound(NullConstraint); -// } - - -// void ArithVariables::forceRelaxUpperBound(ArithVar v){ -// AssertArgument(inMaps(v), "Calling forceRelaxUpperBound on a variable that is not properly setup."); -// AssertArgument(hasUpperBound(v), "Calling forceRelaxUpperBound on a variable without an upper bound."); - -// Debug("partial_model") << "forceRelaxUpperBound(" << v << ") dropping :" << getUpperBoundConstraint(v) << endl; - -// invalidateDelta(); -// VarInfo& vi = d_vars.get(v); -// pushUpperBound(vi); -// vi.setUpperBound(NullConstraint); -// } - int ArithVariables::cmpToLowerBound(ArithVar x, const DeltaRational& c) const{ if(!hasLowerBound(x)){ // l = -\intfy @@ -405,30 +411,16 @@ bool ArithVariables::hasEitherBound(ArithVar x) const{ bool ArithVariables::strictlyBelowUpperBound(ArithVar x) const{ return d_vars[x].d_cmpAssignmentUB < 0; - // if(!hasUpperBound(x)){ // u = \infty - // return true; - // }else{ - // return d_assignment[x] < getUpperBound(x); - // } } bool ArithVariables::strictlyAboveLowerBound(ArithVar x) const{ return d_vars[x].d_cmpAssignmentLB > 0; - // if(!hasLowerBound(x)){ // l = -\infty - // return true; - // }else{ - // return getLowerBound(x) < d_assignment[x]; - // } } bool ArithVariables::assignmentIsConsistent(ArithVar x) const{ return d_vars[x].d_cmpAssignmentLB >= 0 && d_vars[x].d_cmpAssignmentUB <= 0; - // const DeltaRational& beta = getAssignment(x); - - // //l_i <= beta(x_i) <= u_i - // return greaterThanLowerBound(x,beta) && lessThanUpperBound(x,beta); } @@ -442,7 +434,7 @@ void ArithVariables::clearSafeAssignments(bool revert){ ArithVar atBack = d_safeAssignment.back(); if(revert){ VarInfo& vi = d_vars.get(atBack); - BoundCounts prev; + BoundsInfo prev; if(vi.setAssignment(d_safeAssignment[atBack], prev)){ addToBoundQueue(atBack, prev); } @@ -489,33 +481,6 @@ void ArithVariables::printModel(ArithVar x) const{ printModel(x, Debug("model")); } -// BoundRelationship ArithVariables::boundRelationship(Constraint lb, const DeltaRational& d, Constraint ub){ -// if(lb == NullConstraint && ub == NullConstraint){ -// return BetweenBounds; -// }else if(lb == NullConstraint){ -// int cmp = d.cmp(ub->getValue()); -// return (cmp < 0) ? BetweenBounds : -// (cmp == 0 ? AtUpperBound : AboveUpperBound); -// }else if(ub == NullConstraint){ -// int cmp = d.cmp(lb->getValue()); -// return (cmp > 0) ? BetweenBounds : -// (cmp == 0 ? AtLowerBound : BelowLowerBound); -// }else{ -// Assert(lb->getValue() <= ub->getValue()); -// int cmpToLB = d.cmp(lb->getValue()); -// if(cmpToLB < 0){ -// return BelowLowerBound; -// }else if(cmpToLB == 0){ -// return (d == ub->getValue()) ? AtBothBounds : AtLowerBound; -// }else{ -// // d > 0 -// int cmpToUB = d.cmp(ub->getValue()); -// return (cmpToUB > 0) ? BetweenBounds : -// (cmpToUB == 0 ? AtLowerBound : BelowLowerBound); -// } -// } -// } - void ArithVariables::pushUpperBound(VarInfo& vi){ ++vi.d_pushCount; d_ubRevertHistory.push_back(make_pair(vi.d_var, vi.d_ub)); @@ -528,7 +493,7 @@ void ArithVariables::pushLowerBound(VarInfo& vi){ void ArithVariables::popUpperBound(AVCPair* c){ ArithVar x = c->first; VarInfo& vi = d_vars.get(x); - BoundCounts prev; + BoundsInfo prev; if(vi.setUpperBound(c->second, prev)){ addToBoundQueue(x, prev); } @@ -538,62 +503,42 @@ void ArithVariables::popUpperBound(AVCPair* c){ void ArithVariables::popLowerBound(AVCPair* c){ ArithVar x = c->first; VarInfo& vi = d_vars.get(x); - BoundCounts prev; + BoundsInfo prev; if(vi.setLowerBound(c->second, prev)){ addToBoundQueue(x, prev); } --vi.d_pushCount; } -/* To ensure that the final deallocation stuff works, - * we need to ensure that we need to not reference any of the other vectors - */ -// void ArithVariables::relaxUpperBound(Constraint curr, Constraint afterPop){ -// BoundRelation next = Undefined; -// switch(d_boundRel[x]){ -// case BelowLowerBound: -// case BetweenBounds: -// case AtLowerBound: -// return; // do nothing -// case AtUpperBound: -// if(afterPop != NullConstraint -// && curr->getValue() == afterPop->getValue()){ -// next = AtUpperBound; -// }else{ -// next = BetweenBounds; -// } -// break; -// case AtBothBounds: -// if(afterPop != NullConstraint -// && curr->getValue() == afterPop->getValue()){ -// next = AtUpperBound; -// }else{ -// next = AtLowerBound; -// } -// break; -// case AboveUpperBound: -// if(afterPop == NullConstraint){ -// next = BetweenBounds; -// }else{ -// int cmp = d_assignment[x].cmp(afterPop->getValue()); -// next = (cmp < 0) ? BetweenBounds : -// (cmp == 0) ? AtUpperBound : AboveUpperBound; -// } -// break; -// default: -// Unreachable(); -// } -// d_boundRel[x] = next; -// } +void ArithVariables::addToBoundQueue(ArithVar v, const BoundsInfo& prev){ + if(d_enqueueingBoundCounts && !d_boundsQueue.isKey(v)){ + d_boundsQueue.set(v, prev); + } +} +BoundsInfo ArithVariables::selectBoundsInfo(ArithVar v, bool old) const { + if(old && d_boundsQueue.isKey(v)){ + return d_boundsQueue[v]; + }else{ + return boundsInfo(v); + } +} +bool ArithVariables::boundsQueueEmpty() const { + return d_boundsQueue.empty(); +} -// void ArithVariables::relaxLowerBound(Constraint curr, Constraint afterPop){ -// // TODO this can be optimized using the automata induced by d_boundRel and -// // the knowledge that lb <= ub -// ArithVar x = curr->getVariable(); -// d_boundRel[x] = boundRelationship(afterPop, d_assignment[x], d_ubc[x]); -// } +void ArithVariables::processBoundsQueue(BoundUpdateCallback& changed){ + while(!boundsQueueEmpty()){ + ArithVar v = d_boundsQueue.back(); + BoundsInfo prev = d_boundsQueue[v]; + d_boundsQueue.pop_back(); + BoundsInfo curr = boundsInfo(v); + if(prev != curr){ + changed(v, prev); + } + } +} void ArithVariables::LowerBoundCleanUp::operator()(AVCPair* p){ d_pm->popLowerBound(p); diff --git a/src/theory/arith/partial_model.h b/src/theory/arith/partial_model.h index dcfe97079..deafb559a 100644 --- a/src/theory/arith/partial_model.h +++ b/src/theory/arith/partial_model.h @@ -44,6 +44,7 @@ namespace arith { class ArithVariables { private: + class VarInfo { friend class ArithVariables; ArithVar d_var; @@ -62,29 +63,36 @@ private: public: VarInfo(); - bool setAssignment(const DeltaRational& r, BoundCounts& prev); - bool setLowerBound(Constraint c, BoundCounts& prev); - bool setUpperBound(Constraint c, BoundCounts& prev); + bool setAssignment(const DeltaRational& r, BoundsInfo& prev); + bool setLowerBound(Constraint c, BoundsInfo& prev); + bool setUpperBound(Constraint c, BoundsInfo& prev); - bool initialized() const { - return d_var != ARITHVAR_SENTINEL; - } + /** 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. + */ void initialize(ArithVar v, Node n, bool slack); + /** Uninitializes the VarInfo. */ void uninitialize(); - bool canBeReclaimed() const{ - return d_pushCount == 0; - } + bool canBeReclaimed() const; - BoundCounts boundCounts() const { - uint32_t lbIndc = (d_cmpAssignmentLB == 0) ? 1 : 0; - uint32_t ubIndc = (d_cmpAssignmentUB == 0) ? 1 : 0; - return BoundCounts(lbIndc, ubIndc); - } + /** 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. */ + BoundCounts hasBoundCounts() const; + + /** Stores both atBoundCounts() and hasBoundCounts(). */ + BoundsInfo boundsInfo() const; }; - //Maps from ArithVar -> VarInfo + /**Maps from ArithVar -> VarInfo */ typedef DenseMap<VarInfo> VarInfoVec; + /** This maps an ArithVar to its Variable information.*/ VarInfoVec d_vars; // Partial Map from Arithvar -> PreviousAssignment @@ -104,7 +112,15 @@ private: // Inverse of d_vars[x].d_node NodeToArithVarMap d_nodeToArithVarMap; - DenseMap<BoundCounts> d_atBoundsQueue; + + /** The queue of constraints where the assignment is at the bound.*/ + DenseMap<BoundsInfo> d_boundsQueue; + + /** + * 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. + */ + bool d_enqueueingBoundCounts; public: @@ -188,6 +204,11 @@ private: bool isSlack(ArithVar x) const { return d_vars[x].d_slack; } + + bool integralAssignment(ArithVar x) const { + return getAssignment(x).isIntegral(); + } + private: typedef std::pair<ArithVar, Constraint> AVCPair; @@ -225,7 +246,6 @@ private: // Function to call if the value of delta needs to be recomputed. DeltaComputeCallback d_deltaComputingFunc; - bool d_enqueueingBoundCounts; public: @@ -252,22 +272,7 @@ public: return d_vars[x].d_lb; } - /** - * This forces the lower bound for a variable to be relaxed in the current context. - * This is done by forcing the lower bound to be NullConstraint. - * This is an expert only operation! (See primal simplex for an example.) - */ - //void forceRelaxLowerBound(ArithVar x); - - /** - * This forces the upper bound for a variable to be relaxed in the current context. - * This is done by forcing the upper bound to be NullConstraint. - * This is an expert only operation! (See primal simplex for an example.) - */ - //void forceRelaxUpperBound(ArithVar x); - /* Initializes a variable to a safe value.*/ - //void initialize(ArithVar x, const DeltaRational& r); void initialize(ArithVar x, Node n, bool slack); ArithVar allocate(Node n, bool slack = false); @@ -360,8 +365,14 @@ public: return d_vars[x].d_cmpAssignmentUB; } - inline BoundCounts boundCounts(ArithVar x) const { - return d_vars[x].boundCounts(); + inline BoundCounts atBoundCounts(ArithVar x) const { + return d_vars[x].atBoundCounts(); + } + inline BoundCounts hasBoundCounts(ArithVar x) const { + return d_vars[x].hasBoundCounts(); + } + inline BoundsInfo boundsInfo(ArithVar x) const{ + return d_vars[x].boundsInfo(); } bool strictlyBelowUpperBound(ArithVar x) const; @@ -391,38 +402,14 @@ public: d_deltaIsSafe = true; } - // inline bool initialized(ArithVar x) const { - // return d_vars[x].initialized(); - // } - - void addToBoundQueue(ArithVar v, BoundCounts prev){ - if(d_enqueueingBoundCounts && !d_atBoundsQueue.isKey(v)){ - d_atBoundsQueue.set(v, prev); - } - } - - BoundCounts oldBoundCounts(ArithVar v) const { - if(d_atBoundsQueue.isKey(v)){ - return d_atBoundsQueue[v]; - }else{ - return boundCounts(v); - } - } + void startQueueingBoundCounts(){ d_enqueueingBoundCounts = true; } + void stopQueueingBoundCounts(){ d_enqueueingBoundCounts = false; } + void addToBoundQueue(ArithVar v, const BoundsInfo& prev); - void startQueueingAtBoundQueue(){ d_enqueueingBoundCounts = true; } - void stopQueueingAtBoundQueue(){ d_enqueueingBoundCounts = false; } + BoundsInfo selectBoundsInfo(ArithVar v, bool old) const; - void processAtBoundQueue(BoundCountsCallback& changed){ - while(!d_atBoundsQueue.empty()){ - ArithVar v = d_atBoundsQueue.back(); - BoundCounts prev = d_atBoundsQueue[v]; - d_atBoundsQueue.pop_back(); - BoundCounts curr = boundCounts(v); - if(prev != curr){ - changed(v, prev); - } - } - } + bool boundsQueueEmpty() const; + void processBoundsQueue(BoundUpdateCallback& changed); void printEntireModel(std::ostream& out) const; diff --git a/src/theory/arith/pure_update_simplex.cpp b/src/theory/arith/pure_update_simplex.cpp deleted file mode 100644 index 9b3edfa6f..000000000 --- a/src/theory/arith/pure_update_simplex.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/********************* */ -/*! \file simplex.cpp - ** \verbatim - ** Original author: taking - ** Major contributors: none - ** Minor contributors (to current version): mdeters - ** This file is part of the CVC4 prototype. - ** Copyright (c) 2009-2012 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 "theory/arith/pure_update_simplex.h" -#include "theory/arith/options.h" -#include "theory/arith/constraint.h" - -using namespace std; - -namespace CVC4 { -namespace theory { -namespace arith { - -PureUpdateSimplexDecisionProcedure::PureUpdateSimplexDecisionProcedure(LinearEqualityModule& linEq, ErrorSet& errors, RaiseConflict conflictChannel, TempVarMalloc tvmalloc) - : SimplexDecisionProcedure(linEq, errors, conflictChannel, tvmalloc) -{ } - -Result::Sat PureUpdateSimplexDecisionProcedure::findModel(bool exactResult){ - Assert(d_conflictVariables.empty()); - - static CVC4_THREADLOCAL(unsigned int) instance = 0; - instance = instance + 1; - - if(d_errorSet.errorEmpty() && !d_errorSet.moreSignals()){ - Debug("arith::findModel") << "puFindModel("<< instance <<") " - << "trivial" << endl; - return Result::SAT; - } - - // We need to reduce this because of - d_errorSet.reduceToSignals(); - d_errorSet.setSelectionRule(VAR_ORDER); - - if(processSignals()){ - d_conflictVariables.purge(); - - Debug("arith::findModel") << "puFindModel("<< instance <<") " - << "early conflict" << endl; - return Result::UNSAT; - }else if(d_errorSet.errorEmpty()){ - Debug("arith::findModel") << "puFindModel("<< instance <<") " - << "fixed itself" << endl; - Assert(!d_errorSet.moreSignals()); - return Result::SAT; - } - - Debug("arith::findModel") << "puFindModel(" << instance <<") " - << "start non-trivial" << endl; - - static const bool verbose = false; - Result::Sat result = Result::SAT_UNKNOWN; - - if(result == Result::SAT_UNKNOWN){ - if(attemptPureUpdates()){ - result = Result::UNSAT; - } - if(result == Result::UNSAT){ - ++(d_statistics.d_pureUpdateFoundUnsat); - if(verbose){ Message() << "pure updates found unsat" << endl; } - }else if(d_errorSet.errorEmpty()){ - ++(d_statistics.d_pureUpdateFoundSat); - if(verbose){ Message() << "pure updates found model" << endl; } - }else{ - ++(d_statistics.d_pureUpdateMissed); - if(verbose){ Message() << "pure updates missed" << endl; } - } - } - - Assert(!d_errorSet.moreSignals()); - if(result == Result::SAT_UNKNOWN && d_errorSet.errorEmpty()){ - result = Result::SAT; - } - - // ensure that the conflict variable is still in the queue. - d_conflictVariables.purge(); - - Assert(d_focusErrorVar == ARITHVAR_SENTINEL); - Debug("arith::findModel") << "end findModel() " << instance << " " - << result << endl; - - return result; -} - - - -PureUpdateSimplexDecisionProcedure::Statistics::Statistics(): - d_pureUpdateFoundUnsat("theory::arith::PureUpdate::FoundUnsat", 0), - d_pureUpdateFoundSat("theory::arith::PureUpdate::FoundSat", 0), - d_pureUpdateMissed("theory::arith::PureUpdate::Missed", 0), - d_pureUpdates("theory::arith::PureUpdate::updates", 0), - d_pureUpdateDropped("theory::arith::PureUpdate::dropped", 0), - d_pureUpdateConflicts("theory::arith::PureUpdate::conflicts", 0), - d_foundConflicts("theory::arith::PureUpdate::foundConflicts", 0), - d_attemptPureUpdatesTimer("theory::arith::PureUpdate::timer"), - d_processSignalsTime("theory::arith::PureUpdate::process::timer"), - d_constructionTimer("theory::arith::PureUpdate::construction::timer") -{ - StatisticsRegistry::registerStat(&d_pureUpdateFoundUnsat); - StatisticsRegistry::registerStat(&d_pureUpdateFoundSat); - StatisticsRegistry::registerStat(&d_pureUpdateMissed); - StatisticsRegistry::registerStat(&d_pureUpdates); - StatisticsRegistry::registerStat(&d_pureUpdateDropped); - StatisticsRegistry::registerStat(&d_pureUpdateConflicts); - - StatisticsRegistry::registerStat(&d_foundConflicts); - - StatisticsRegistry::registerStat(&d_attemptPureUpdatesTimer); - StatisticsRegistry::registerStat(&d_processSignalsTime); - StatisticsRegistry::registerStat(&d_constructionTimer); -} - -PureUpdateSimplexDecisionProcedure::Statistics::~Statistics(){ - StatisticsRegistry::unregisterStat(&d_pureUpdateFoundUnsat); - StatisticsRegistry::unregisterStat(&d_pureUpdateFoundSat); - StatisticsRegistry::unregisterStat(&d_pureUpdateMissed); - StatisticsRegistry::unregisterStat(&d_pureUpdates); - StatisticsRegistry::unregisterStat(&d_pureUpdateDropped); - StatisticsRegistry::unregisterStat(&d_pureUpdateConflicts); - - StatisticsRegistry::unregisterStat(&d_foundConflicts); - - StatisticsRegistry::unregisterStat(&d_attemptPureUpdatesTimer); - StatisticsRegistry::unregisterStat(&d_processSignalsTime); - StatisticsRegistry::unregisterStat(&d_constructionTimer); -} - -bool PureUpdateSimplexDecisionProcedure::attemptPureUpdates(){ - TimerStat::CodeTimer codeTimer(d_statistics.d_attemptPureUpdatesTimer); - - Assert(!d_errorSet.focusEmpty()); - Assert(d_errorSet.noSignals()); - - d_focusErrorVar = constructInfeasiblityFunction(d_statistics.d_constructionTimer); - - UpdateInfo proposal; - int boundImprovements = 0; - int dropped = 0; - int computations = 0; - - for( Tableau::RowIterator ri = d_tableau.basicRowIterator(d_focusErrorVar); !ri.atEnd(); ++ri){ - const Tableau::Entry& e = *ri; - - ArithVar curr = e.getColVar(); - if(curr == d_focusErrorVar){ continue; } - - int dir = e.getCoefficient().sgn(); - Assert(dir != 0); - - bool worthwhile = false; - - if( (dir > 0 && d_variables.cmpAssignmentUpperBound(curr) < 0) || - (dir < 0 && d_variables.cmpAssignmentLowerBound(curr) > 0) ){ - - ++computations; - proposal = UpdateInfo(curr, dir); - d_linEq.computeSafeUpdate(proposal, &LinearEqualityModule::noPreference); - - Assert(proposal.errorsChange() <= 0); - Assert(proposal.focusDirection() >= 0); - - worthwhile = proposal.errorsChange() < 0 || - (proposal.focusDirection() > 0 && - d_variables.boundCounts(curr).isZero() && - !proposal.describesPivot()); - - Debug("pu::refined") - << "pure update proposal " - << curr << " " - << worthwhile << " " - << proposal - << endl; - } - if(worthwhile){ - Debug("pu") << d_variables.getAssignment(d_focusErrorVar) << endl; - - BoundCounts before = d_variables.boundCounts(curr); - DeltaRational newAssignment = - d_variables.getAssignment(curr) + proposal.nonbasicDelta(); - d_linEq.updateTracked(curr, newAssignment); - BoundCounts after = d_variables.boundCounts(curr); - - ++d_statistics.d_pureUpdates; - ++boundImprovements; - Debug("pu") << boundImprovements << ": " << curr - << " before: " << before - << " after: " << after - << e.getCoefficient() - << d_variables.getAssignment(d_focusErrorVar) << endl; - - uint32_t prevSize = d_errorSet.errorSize(); - Assert(d_errorSet.moreSignals()); - if(Debug.isOn("pu")){ d_errorSet.debugPrint(Debug("pu")); } - while(d_errorSet.moreSignals()){ - ArithVar updated = d_errorSet.topSignal(); - bool wasInError = d_errorSet.inError(updated); - d_errorSet.popSignal(); - if(updated == curr){ continue; } - Assert(d_tableau.isBasic(updated)); - if(!d_linEq.basicIsTracked(updated)){continue;} - - - Assert(d_linEq.basicIsTracked(updated)); - Assert(wasInError || d_variables.assignmentIsConsistent(updated)); - - if(!d_variables.assignmentIsConsistent(updated) - && checkBasicForConflict(updated)){ - Assert(!d_conflictVariables.isMember(updated) ); - Debug("pu") - << "It worked? " - << d_statistics.d_pureUpdateConflicts.getData() - << " " << curr - << " " << checkBasicForConflict(updated) << endl; - reportConflict(updated); - ++(d_statistics.d_foundConflicts); - ++(d_statistics.d_pureUpdateConflicts); - } - } - if(d_conflictVariables.empty()){ - if(Debug.isOn("pu")){ d_errorSet.debugPrint(Debug("pu")); } - uint32_t currSize = d_errorSet.errorSize(); - Assert(currSize <= prevSize); - if(currSize < prevSize){ - dropped+= prevSize - currSize; - if(currSize == 0){ - break; - } - } - }else{ - break; - } - } - } - - tearDownInfeasiblityFunction(d_statistics.d_constructionTimer, d_focusErrorVar); - d_focusErrorVar = ARITHVAR_SENTINEL; - - (d_statistics.d_pureUpdateDropped) += dropped; - - Assert(d_errorSet.noSignals()); - return !d_conflictVariables.empty(); -} - - -}/* CVC4::theory::arith namespace */ -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ diff --git a/src/theory/arith/simplex-converge.cpp b/src/theory/arith/simplex-converge.cpp deleted file mode 100644 index decce3882..000000000 --- a/src/theory/arith/simplex-converge.cpp +++ /dev/null @@ -1,1674 +0,0 @@ -/********************* */ -/*! \file simplex.cpp - ** \verbatim - ** Original author: taking - ** Major contributors: none - ** Minor contributors (to current version): none - ** This file is part of the CVC4 prototype. - ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) - ** Courant Institute of Mathematical Sciences - ** New York University - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** \todo document this file - **/ - - -#include "theory/arith/simplex.h" -#include "theory/arith/options.h" - -using namespace std; - -using namespace CVC4; -using namespace CVC4::kind; - -using namespace CVC4::theory; -using namespace CVC4::theory::arith; - -static const bool CHECK_AFTER_PIVOT = true; - -SimplexDecisionProcedure::SimplexDecisionProcedure(LinearEqualityModule& linEq, NodeCallBack& conflictChannel, ArithVarMalloc& malloc, ConstraintDatabase& cd) : - d_conflictVariable(ARITHVAR_SENTINEL), - d_linEq(linEq), - d_partialModel(d_linEq.getPartialModel()), - d_tableau(d_linEq.getTableau()), - d_queue(d_partialModel, d_tableau), - d_numVariables(0), - d_conflictChannel(conflictChannel), - d_pivotsInRound(), - d_DELTA_ZERO(0,0), - d_arithVarMalloc(malloc), - d_constraintDatabase(cd), - d_optRow(ARITHVAR_SENTINEL), - d_negOptConstant(d_DELTA_ZERO) -{ - switch(ArithHeuristicPivotRule rule = options::arithHeuristicPivotRule()) { - case MINIMUM: - d_queue.setPivotRule(ArithPriorityQueue::MINIMUM); - break; - case BREAK_TIES: - d_queue.setPivotRule(ArithPriorityQueue::BREAK_TIES); - break; - case MAXIMUM: - d_queue.setPivotRule(ArithPriorityQueue::MAXIMUM); - break; - default: - Unhandled(rule); - } - - srand(62047); -} - -SimplexDecisionProcedure::Statistics::Statistics(): - d_statUpdateConflicts("theory::arith::UpdateConflicts", 0), - d_findConflictOnTheQueueTime("theory::arith::findConflictOnTheQueueTime"), - d_attemptBeforeDiffSearch("theory::arith::qi::BeforeDiffSearch::attempt",0), - d_successBeforeDiffSearch("theory::arith::qi::BeforeDiffSearch::success",0), - d_attemptAfterDiffSearch("theory::arith::qi::AfterDiffSearch::attempt",0), - d_successAfterDiffSearch("theory::arith::qi::AfterDiffSearch::success",0), - d_attemptDuringDiffSearch("theory::arith::qi::DuringDiffSearch::attempt",0), - d_successDuringDiffSearch("theory::arith::qi::DuringDiffSearch::success",0), - d_attemptDuringVarOrderSearch("theory::arith::qi::DuringVarOrderSearch::attempt",0), - d_successDuringVarOrderSearch("theory::arith::qi::DuringVarOrderSearch::success",0), - d_attemptAfterVarOrderSearch("theory::arith::qi::AfterVarOrderSearch::attempt",0), - d_successAfterVarOrderSearch("theory::arith::qi::AfterVarOrderSearch::success",0), - d_weakeningAttempts("theory::arith::weakening::attempts",0), - d_weakeningSuccesses("theory::arith::weakening::success",0), - d_weakenings("theory::arith::weakening::total",0), - d_weakenTime("theory::arith::weakening::time"), - d_simplexConflicts("theory::arith::simplexConflicts",0), - // primal - d_primalTimer("theory::arith::primal::overall::timer"), - d_internalTimer("theory::arith::primal::internal::timer"), - d_primalCalls("theory::arith::primal::calls",0), - d_primalSatCalls("theory::arith::primal::calls::sat",0), - d_primalUnsatCalls("theory::arith::primal::calls::unsat",0), - d_primalPivots("theory::arith::primal::pivots",0), - d_primalImprovingPivots("theory::arith::primal::pivots::improving",0), - d_primalThresholdReachedPivot("theory::arith::primal::thresholds",0), - d_primalThresholdReachedPivot_dropped("theory::arith::primal::thresholds::dropped",0), - d_primalReachedMaxPivots("theory::arith::primal::maxpivots",0), - d_primalReachedMaxPivots_contractMadeProgress("theory::arith::primal::maxpivots::contract",0), - d_primalReachedMaxPivots_checkForConflictWorked("theory::arith::primal::maxpivots::checkworked",0), - d_primalGlobalMinimum("theory::arith::primal::minimum",0), - d_primalGlobalMinimum_rowConflictWorked("theory::arith::primal::minimum::checkworked",0), - d_primalGlobalMinimum_firstHalfWasSat("theory::arith::primal::minimum::firsthalf::sat",0), - d_primalGlobalMinimum_firstHalfWasUnsat("theory::arith::primal::minimum::firsthalf::unsat",0), - d_primalGlobalMinimum_contractMadeProgress("theory::arith::primal::minimum::progress",0), - d_unboundedFound("theory::arith::primal::unbounded",0), - d_unboundedFound_drive("theory::arith::primal::unbounded::drive",0), - d_unboundedFound_dropped("theory::arith::primal::unbounded::dropped",0) -{ - StatisticsRegistry::registerStat(&d_statUpdateConflicts); - - StatisticsRegistry::registerStat(&d_findConflictOnTheQueueTime); - - StatisticsRegistry::registerStat(&d_attemptBeforeDiffSearch); - StatisticsRegistry::registerStat(&d_successBeforeDiffSearch); - StatisticsRegistry::registerStat(&d_attemptAfterDiffSearch); - StatisticsRegistry::registerStat(&d_successAfterDiffSearch); - StatisticsRegistry::registerStat(&d_attemptDuringDiffSearch); - StatisticsRegistry::registerStat(&d_successDuringDiffSearch); - StatisticsRegistry::registerStat(&d_attemptDuringVarOrderSearch); - StatisticsRegistry::registerStat(&d_successDuringVarOrderSearch); - StatisticsRegistry::registerStat(&d_attemptAfterVarOrderSearch); - StatisticsRegistry::registerStat(&d_successAfterVarOrderSearch); - - StatisticsRegistry::registerStat(&d_weakeningAttempts); - StatisticsRegistry::registerStat(&d_weakeningSuccesses); - StatisticsRegistry::registerStat(&d_weakenings); - StatisticsRegistry::registerStat(&d_weakenTime); - - StatisticsRegistry::registerStat(&d_simplexConflicts); - - //primal - StatisticsRegistry::registerStat(&d_primalTimer); - StatisticsRegistry::registerStat(&d_internalTimer); - - StatisticsRegistry::registerStat(&d_primalCalls); - StatisticsRegistry::registerStat(&d_primalSatCalls); - StatisticsRegistry::registerStat(&d_primalUnsatCalls); - - StatisticsRegistry::registerStat(&d_primalPivots); - StatisticsRegistry::registerStat(&d_primalImprovingPivots); - - StatisticsRegistry::registerStat(&d_primalThresholdReachedPivot); - StatisticsRegistry::registerStat(&d_primalThresholdReachedPivot_dropped); - - StatisticsRegistry::registerStat(&d_primalReachedMaxPivots); - StatisticsRegistry::registerStat(&d_primalReachedMaxPivots_contractMadeProgress); - StatisticsRegistry::registerStat(&d_primalReachedMaxPivots_checkForConflictWorked); - - - StatisticsRegistry::registerStat(&d_primalGlobalMinimum); - StatisticsRegistry::registerStat(&d_primalGlobalMinimum_rowConflictWorked); - StatisticsRegistry::registerStat(&d_primalGlobalMinimum_firstHalfWasSat); - StatisticsRegistry::registerStat(&d_primalGlobalMinimum_firstHalfWasUnsat); - StatisticsRegistry::registerStat(&d_primalGlobalMinimum_contractMadeProgress); - - - StatisticsRegistry::registerStat(&d_unboundedFound); - StatisticsRegistry::registerStat(&d_unboundedFound_drive); - StatisticsRegistry::registerStat(&d_unboundedFound_dropped); - -} - -SimplexDecisionProcedure::Statistics::~Statistics(){ - StatisticsRegistry::unregisterStat(&d_statUpdateConflicts); - - StatisticsRegistry::unregisterStat(&d_findConflictOnTheQueueTime); - - StatisticsRegistry::unregisterStat(&d_attemptBeforeDiffSearch); - StatisticsRegistry::unregisterStat(&d_successBeforeDiffSearch); - StatisticsRegistry::unregisterStat(&d_attemptAfterDiffSearch); - StatisticsRegistry::unregisterStat(&d_successAfterDiffSearch); - StatisticsRegistry::unregisterStat(&d_attemptDuringDiffSearch); - StatisticsRegistry::unregisterStat(&d_successDuringDiffSearch); - StatisticsRegistry::unregisterStat(&d_attemptDuringVarOrderSearch); - StatisticsRegistry::unregisterStat(&d_successDuringVarOrderSearch); - StatisticsRegistry::unregisterStat(&d_attemptAfterVarOrderSearch); - StatisticsRegistry::unregisterStat(&d_successAfterVarOrderSearch); - - StatisticsRegistry::unregisterStat(&d_weakeningAttempts); - StatisticsRegistry::unregisterStat(&d_weakeningSuccesses); - StatisticsRegistry::unregisterStat(&d_weakenings); - StatisticsRegistry::unregisterStat(&d_weakenTime); - - StatisticsRegistry::unregisterStat(&d_simplexConflicts); - - //primal - StatisticsRegistry::unregisterStat(&d_primalTimer); - StatisticsRegistry::unregisterStat(&d_internalTimer); - - StatisticsRegistry::unregisterStat(&d_primalCalls); - StatisticsRegistry::unregisterStat(&d_primalSatCalls); - StatisticsRegistry::unregisterStat(&d_primalUnsatCalls); - - StatisticsRegistry::unregisterStat(&d_primalPivots); - StatisticsRegistry::unregisterStat(&d_primalImprovingPivots); - - StatisticsRegistry::unregisterStat(&d_primalThresholdReachedPivot); - StatisticsRegistry::unregisterStat(&d_primalThresholdReachedPivot_dropped); - - StatisticsRegistry::unregisterStat(&d_primalReachedMaxPivots); - StatisticsRegistry::unregisterStat(&d_primalReachedMaxPivots_contractMadeProgress); - StatisticsRegistry::unregisterStat(&d_primalReachedMaxPivots_checkForConflictWorked); - - - StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum); - StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum_rowConflictWorked); - StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum_firstHalfWasSat); - StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum_firstHalfWasUnsat); - StatisticsRegistry::unregisterStat(&d_primalGlobalMinimum_contractMadeProgress); - - StatisticsRegistry::unregisterStat(&d_unboundedFound); - StatisticsRegistry::unregisterStat(&d_unboundedFound_drive); - StatisticsRegistry::unregisterStat(&d_unboundedFound_dropped); -} - - - - -ArithVar SimplexDecisionProcedure::minVarOrder(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y){ - Assert(x != ARITHVAR_SENTINEL); - Assert(y != ARITHVAR_SENTINEL); - // Assert(!simp.d_tableau.isBasic(x)); - // Assert(!simp.d_tableau.isBasic(y)); - if(x <= y){ - return x; - } else { - return y; - } -} - -ArithVar SimplexDecisionProcedure::minColLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y){ - Assert(x != ARITHVAR_SENTINEL); - Assert(y != ARITHVAR_SENTINEL); - Assert(!simp.d_tableau.isBasic(x)); - Assert(!simp.d_tableau.isBasic(y)); - uint32_t xLen = simp.d_tableau.getColLength(x); - uint32_t yLen = simp.d_tableau.getColLength(y); - if( xLen > yLen){ - return y; - } else if( xLen== yLen ){ - return minVarOrder(simp,x,y); - }else{ - return x; - } -} - -ArithVar SimplexDecisionProcedure::minRowLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y){ - Assert(x != ARITHVAR_SENTINEL); - Assert(y != ARITHVAR_SENTINEL); - Assert(simp.d_tableau.isBasic(x)); - Assert(simp.d_tableau.isBasic(y)); - uint32_t xLen = simp.d_tableau.getRowLength(simp.d_tableau.basicToRowIndex(x)); - uint32_t yLen = simp.d_tableau.getRowLength(simp.d_tableau.basicToRowIndex(y)); - if( xLen > yLen){ - return y; - } else if( xLen == yLen ){ - return minVarOrder(simp,x,y); - }else{ - return x; - } -} -ArithVar SimplexDecisionProcedure::minBoundAndRowCount(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y){ - Assert(x != ARITHVAR_SENTINEL); - Assert(y != ARITHVAR_SENTINEL); - Assert(!simp.d_tableau.isBasic(x)); - Assert(!simp.d_tableau.isBasic(y)); - if(simp.d_partialModel.hasEitherBound(x) && !simp.d_partialModel.hasEitherBound(y)){ - return y; - }else if(!simp.d_partialModel.hasEitherBound(x) && simp.d_partialModel.hasEitherBound(y)){ - return x; - }else { - return minColLength(simp, x, y); - } -} - -template <bool above> -ArithVar SimplexDecisionProcedure::selectSlack(ArithVar x_i, SimplexDecisionProcedure::PreferenceFunction pref){ - ArithVar slack = ARITHVAR_SENTINEL; - - for(Tableau::RowIterator iter = d_tableau.basicRowIterator(x_i); !iter.atEnd(); ++iter){ - const Tableau::Entry& entry = *iter; - ArithVar nonbasic = entry.getColVar(); - if(nonbasic == x_i) continue; - - const Rational& a_ij = entry.getCoefficient(); - int sgn = a_ij.sgn(); - if(isAcceptableSlack<above>(sgn, nonbasic)){ - //If one of the above conditions is met, we have found an acceptable - //nonbasic variable to pivot x_i with. We can now choose which one we - //prefer the most. - slack = (slack == ARITHVAR_SENTINEL) ? nonbasic : pref(*this, slack, nonbasic); - } - } - - return slack; -} - -Node betterConflict(TNode x, TNode y){ - if(x.isNull()) return y; - else if(y.isNull()) return x; - else if(x.getNumChildren() <= y.getNumChildren()) return x; - else return y; -} - -bool SimplexDecisionProcedure::findConflictOnTheQueue(SearchPeriod type) { - TimerStat::CodeTimer codeTimer(d_statistics.d_findConflictOnTheQueueTime); - Assert(d_successes.empty()); - - switch(type){ - case BeforeDiffSearch: ++(d_statistics.d_attemptBeforeDiffSearch); break; - case DuringDiffSearch: ++(d_statistics.d_attemptDuringDiffSearch); break; - case AfterDiffSearch: ++(d_statistics.d_attemptAfterDiffSearch); break; - case DuringVarOrderSearch: ++(d_statistics.d_attemptDuringVarOrderSearch); break; - case AfterVarOrderSearch: ++(d_statistics.d_attemptAfterVarOrderSearch); break; - } - - ArithPriorityQueue::const_iterator i = d_queue.begin(); - ArithPriorityQueue::const_iterator end = d_queue.end(); - for(; i != end; ++i){ - ArithVar x_i = *i; - - if(x_i != d_conflictVariable && d_tableau.isBasic(x_i) && !d_successes.isMember(x_i)){ - Node possibleConflict = checkBasicForConflict(x_i); - if(!possibleConflict.isNull()){ - d_successes.add(x_i); - reportConflict(possibleConflict); - } - } - } - if(!d_successes.empty()){ - switch(type){ - case BeforeDiffSearch: ++(d_statistics.d_successBeforeDiffSearch); break; - case DuringDiffSearch: ++(d_statistics.d_successDuringDiffSearch); break; - case AfterDiffSearch: ++(d_statistics.d_successAfterDiffSearch); break; - case DuringVarOrderSearch: ++(d_statistics.d_successDuringVarOrderSearch); break; - case AfterVarOrderSearch: ++(d_statistics.d_successAfterVarOrderSearch); break; - } - d_successes.purge(); - return true; - }else{ - return false; - } -} - -Result::Sat SimplexDecisionProcedure::dualFindModel(bool exactResult){ - Assert(d_conflictVariable == ARITHVAR_SENTINEL); - Assert(d_queue.inCollectionMode()); - - if(d_queue.empty()){ - return Result::SAT; - } - static CVC4_THREADLOCAL(unsigned int) instance = 0; - instance = instance + 1; - Debug("arith::findModel") << "begin findModel()" << instance << endl; - - d_queue.transitionToDifferenceMode(); - - Result::Sat result = Result::SAT_UNKNOWN; - - if(d_queue.empty()){ - result = Result::SAT; - }else if(d_queue.size() > 1){ - if(findConflictOnTheQueue(BeforeDiffSearch)){ - result = Result::UNSAT; - } - } - static const bool verbose = false; - exactResult |= options::arithStandardCheckVarOrderPivots() < 0; - const uint32_t inexactResultsVarOrderPivots = exactResult ? 0 : options::arithStandardCheckVarOrderPivots(); - - uint32_t checkPeriod = options::arithSimplexCheckPeriod(); - if(result == Result::SAT_UNKNOWN){ - uint32_t numDifferencePivots = options::arithHeuristicPivots() < 0 ? - d_numVariables + 1 : options::arithHeuristicPivots(); - // The signed to unsigned conversion is safe. - uint32_t pivotsRemaining = numDifferencePivots; - while(!d_queue.empty() && - result != Result::UNSAT && - pivotsRemaining > 0){ - uint32_t pivotsToDo = min(checkPeriod, pivotsRemaining); - pivotsRemaining -= pivotsToDo; - if(searchForFeasibleSolution(pivotsToDo)){ - result = Result::UNSAT; - }//Once every CHECK_PERIOD examine the entire queue for conflicts - if(result != Result::UNSAT){ - if(findConflictOnTheQueue(DuringDiffSearch)) { result = Result::UNSAT; } - }else{ - findConflictOnTheQueue(AfterDiffSearch); // already unsat - } - } - - if(verbose && numDifferencePivots > 0){ - if(result == Result::UNSAT){ - Message() << "diff order found unsat" << endl; - }else if(d_queue.empty()){ - Message() << "diff order found model" << endl; - }else{ - Message() << "diff order missed" << endl; - } - } - } - - if(!d_queue.empty() && result != Result::UNSAT){ - if(exactResult){ - d_queue.transitionToVariableOrderMode(); - - while(!d_queue.empty() && result != Result::UNSAT){ - if(searchForFeasibleSolution(checkPeriod)){ - result = Result::UNSAT; - } - - //Once every CHECK_PERIOD examine the entire queue for conflicts - if(result != Result::UNSAT){ - if(findConflictOnTheQueue(DuringVarOrderSearch)){ - result = Result::UNSAT; - } - } else{ - findConflictOnTheQueue(AfterVarOrderSearch); - } - } - if(verbose){ - if(result == Result::UNSAT){ - Message() << "bland found unsat" << endl; - }else if(d_queue.empty()){ - Message() << "bland found model" << endl; - }else{ - Message() << "bland order missed" << endl; - } - } - }else{ - d_queue.transitionToVariableOrderMode(); - - if(searchForFeasibleSolution(inexactResultsVarOrderPivots)){ - result = Result::UNSAT; - findConflictOnTheQueue(AfterVarOrderSearch); // already unsat - }else{ - if(findConflictOnTheQueue(AfterVarOrderSearch)) { result = Result::UNSAT; } - } - - if(verbose){ - if(result == Result::UNSAT){ - Message() << "restricted var order found unsat" << endl; - }else if(d_queue.empty()){ - Message() << "restricted var order found model" << endl; - }else{ - Message() << "restricted var order missed" << endl; - } - } - } - } - - if(result == Result::SAT_UNKNOWN && d_queue.empty()){ - result = Result::SAT; - } - - - - d_pivotsInRound.purge(); - // ensure that the conflict variable is still in the queue. - if(d_conflictVariable != ARITHVAR_SENTINEL){ - d_queue.enqueueIfInconsistent(d_conflictVariable); - } - d_conflictVariable = ARITHVAR_SENTINEL; - - d_queue.transitionToCollectionMode(); - Assert(d_queue.inCollectionMode()); - Debug("arith::findModel") << "end findModel() " << instance << " " << result << endl; - return result; - - - // Assert(foundConflict || d_queue.empty()); - - // // Curiously the invariant that we always do a full check - // // means that the assignment we can always empty these queues. - // d_queue.clear(); - // d_pivotsInRound.purge(); - // d_conflictVariable = ARITHVAR_SENTINEL; - - // Assert(!d_queue.inCollectionMode()); - // d_queue.transitionToCollectionMode(); - - - // Assert(d_queue.inCollectionMode()); - - // Debug("arith::findModel") << "end findModel() " << instance << endl; - - // return foundConflict; -} - - - -Node SimplexDecisionProcedure::checkBasicForConflict(ArithVar basic){ - - Assert(d_tableau.isBasic(basic)); - const DeltaRational& beta = d_partialModel.getAssignment(basic); - - if(d_partialModel.strictlyLessThanLowerBound(basic, beta)){ - ArithVar x_j = selectSlackUpperBound(basic); - if(x_j == ARITHVAR_SENTINEL ){ - return generateConflictBelowLowerBound(basic); - } - }else if(d_partialModel.strictlyGreaterThanUpperBound(basic, beta)){ - ArithVar x_j = selectSlackLowerBound(basic); - if(x_j == ARITHVAR_SENTINEL ){ - return generateConflictAboveUpperBound(basic); - } - } - return Node::null(); -} - -//corresponds to Check() in dM06 -//template <SimplexDecisionProcedure::PreferenceFunction pf> -bool SimplexDecisionProcedure::searchForFeasibleSolution(uint32_t remainingIterations){ - Debug("arith") << "searchForFeasibleSolution" << endl; - Assert(remainingIterations > 0); - - while(remainingIterations > 0){ - if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); } - - ArithVar x_i = d_queue.dequeueInconsistentBasicVariable(); - Debug("arith::update::select") << "selectSmallestInconsistentVar()=" << x_i << endl; - if(x_i == ARITHVAR_SENTINEL){ - Debug("arith_update") << "No inconsistent variables" << endl; - return false; //sat - } - - --remainingIterations; - - bool useVarOrderPivot = d_pivotsInRound.count(x_i) >= options::arithPivotThreshold(); - if(!useVarOrderPivot){ - d_pivotsInRound.add(x_i); - } - - - Debug("playground") << "pivots in rounds: " << d_pivotsInRound.count(x_i) - << " use " << useVarOrderPivot - << " threshold " << options::arithPivotThreshold() - << endl; - - PreferenceFunction pf = useVarOrderPivot ? minVarOrder : minBoundAndRowCount; - - DeltaRational beta_i = d_partialModel.getAssignment(x_i); - ArithVar x_j = ARITHVAR_SENTINEL; - - if(d_partialModel.strictlyLessThanLowerBound(x_i, beta_i)){ - x_j = selectSlackUpperBound(x_i, pf); - if(x_j == ARITHVAR_SENTINEL ){ - ++(d_statistics.d_statUpdateConflicts); - Node conflict = generateConflictBelowLowerBound(x_i); //unsat - d_conflictVariable = x_i; - reportConflict(conflict); - return true; - } - DeltaRational l_i = d_partialModel.getLowerBound(x_i); - d_linEq.pivotAndUpdate(x_i, x_j, l_i); - - }else if(d_partialModel.strictlyGreaterThanUpperBound(x_i, beta_i)){ - x_j = selectSlackLowerBound(x_i, pf); - if(x_j == ARITHVAR_SENTINEL ){ - ++(d_statistics.d_statUpdateConflicts); - Node conflict = generateConflictAboveUpperBound(x_i); //unsat - - d_conflictVariable = x_i; - reportConflict(conflict); - return true; - } - DeltaRational u_i = d_partialModel.getUpperBound(x_i); - d_linEq.pivotAndUpdate(x_i, x_j, u_i); - } - Assert(x_j != ARITHVAR_SENTINEL); - - //Check to see if we already have a conflict with x_j to prevent wasteful work - if(CHECK_AFTER_PIVOT){ - Node possibleConflict = checkBasicForConflict(x_j); - if(!possibleConflict.isNull()){ - d_conflictVariable = x_j; - reportConflict(possibleConflict); - return true; // unsat - } - } - } - Assert(remainingIterations == 0); - - return false; -} - - - -Constraint SimplexDecisionProcedure::weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic){ - - int sgn = coeff.sgn(); - bool ub = aboveUpper?(sgn < 0) : (sgn > 0); - - Constraint c = ub ? - d_partialModel.getUpperBoundConstraint(v) : - d_partialModel.getLowerBoundConstraint(v); - -// #warning "revisit" -// Node exp = ub ? -// d_partialModel.explainUpperBound(v) : -// d_partialModel.explainLowerBound(v); - - bool weakened; - do{ - const DeltaRational& bound = c->getValue(); - - weakened = false; - - Constraint weaker = ub? - c->getStrictlyWeakerUpperBound(true, true): - c->getStrictlyWeakerLowerBound(true, true); - - // Node weaker = ub? - // d_propManager.strictlyWeakerAssertedUpperBound(v, bound): - // d_propManager.strictlyWeakerAssertedLowerBound(v, bound); - - if(weaker != NullConstraint){ - //if(!weaker.isNull()){ - const DeltaRational& weakerBound = weaker->getValue(); - //DeltaRational weakerBound = asDeltaRational(weaker); - - DeltaRational diff = aboveUpper ? bound - weakerBound : weakerBound - bound; - //if var == basic, - // if aboveUpper, weakerBound > bound, multiply by -1 - // if !aboveUpper, weakerBound < bound, multiply by -1 - diff = diff * coeff; - if(surplus > diff){ - ++d_statistics.d_weakenings; - weakened = true; - anyWeakening = true; - surplus = surplus - diff; - - Debug("weak") << "found:" << endl; - if(v == basic){ - Debug("weak") << " basic: "; - } - Debug("weak") << " " << surplus << " "<< diff << endl - << " " << bound << c << endl - << " " << weakerBound << weaker << endl; - - Assert(diff > d_DELTA_ZERO); - c = weaker; - } - } - }while(weakened); - - return c; -} - -Node SimplexDecisionProcedure::weakenConflict(bool aboveUpper, ArithVar basicVar){ - TimerStat::CodeTimer codeTimer(d_statistics.d_weakenTime); - - const DeltaRational& assignment = d_partialModel.getAssignment(basicVar); - DeltaRational surplus; - if(aboveUpper){ - Assert(d_partialModel.hasUpperBound(basicVar)); - Assert(assignment > d_partialModel.getUpperBound(basicVar)); - surplus = assignment - d_partialModel.getUpperBound(basicVar); - }else{ - Assert(d_partialModel.hasLowerBound(basicVar)); - Assert(assignment < d_partialModel.getLowerBound(basicVar)); - surplus = d_partialModel.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); - Debug("weak") << "weak : " << weakening << " " - << c->assertedToTheTheory() << " " - << d_partialModel.getAssignment(v) << " " - << c << endl - << c->explainForConflict() << endl; - anyWeakenings = anyWeakenings || weakening; - - Debug("weak") << "weak : " << c->explainForConflict() << endl; - c->explainForConflict(conflict); - } - ++d_statistics.d_weakeningAttempts; - if(anyWeakenings){ - ++d_statistics.d_weakeningSuccesses; - } - return conflict; -} - - -Node SimplexDecisionProcedure::generateConflictAboveUpperBound(ArithVar conflictVar){ - return weakenConflict(true, conflictVar); -} - -Node SimplexDecisionProcedure::generateConflictBelowLowerBound(ArithVar conflictVar){ - return weakenConflict(false, conflictVar); -} - - -// responses -// unbounded below(arithvar) -// reached threshold -// reached maxpivots -// reached GlobalMinimumd -// -SimplexDecisionProcedure::PrimalResponse SimplexDecisionProcedure::primal(uint32_t maxIterations) -{ - Assert(d_optRow != ARITHVAR_SENTINEL); - - for(uint32_t iteration = 0; iteration < maxIterations; iteration++){ - if(belowThreshold()){ - return ReachedThresholdValue; - } - - PrimalResponse res = primalCheck(); - switch(res){ - case GlobalMinimum: - case FoundUnboundedVariable: - return res; - case NoProgressOnLeaving: - ++d_statistics.d_primalPivots; - ++d_pivotsSinceOptProgress; - ++d_pivotsSinceLastCheck; - ++d_pivotsSinceErrorProgress; - - d_linEq.pivotAndUpdate(d_primalCarry.d_entering, d_primalCarry.d_leaving, d_partialModel.getAssignment(d_primalCarry.d_entering)); - - if(Debug.isOn("primal::tableau")){ - d_linEq.debugCheckTableau(); - } - if(Debug.isOn("primal::consistent")){ Assert(d_linEq.debugEntireLinEqIsConsistent("MakeProgressOnLeaving")); } - - break; - case MakeProgressOnLeaving: - { - ++d_statistics.d_primalPivots; - ++d_statistics.d_primalImprovingPivots; - - d_pivotsSinceOptProgress = 0; - ++d_pivotsSinceErrorProgress; - ++d_pivotsSinceLastCheck; - - d_linEq.pivotAndUpdate(d_primalCarry.d_entering, d_primalCarry.d_leaving, d_primalCarry.d_nextEnteringValue); - - static int helpful = 0; - static int hurtful = 0; - static int penguin = 0; - if(d_currentErrorVariables.isKey(d_primalCarry.d_entering)){ - cout << "entering is error " << d_primalCarry.d_entering; - if(d_currentErrorVariables[d_primalCarry.d_entering].errorIsLeqZero(d_partialModel)){ - cout << " now below threshold (" << (++helpful) << ") " << d_pivotsSinceErrorProgress << endl; - }else{ - cout << "ouch (" << (++hurtful) << ")"<< d_pivotsSinceErrorProgress << endl; - } - }else if(d_currentErrorVariables.isKey(d_primalCarry.d_leaving)){ - cout << "leaving is error " << d_primalCarry.d_leaving; - if(d_currentErrorVariables[d_primalCarry.d_leaving].errorIsLeqZero(d_partialModel)){ - cout << " now below threshold(" << (++helpful) << ")" << d_pivotsSinceErrorProgress << endl; - }else{ - cout << "ouch (" << (++hurtful) << ")" << d_pivotsSinceErrorProgress<< endl; - } - }else{ - cout << " penguin (" << (++penguin) << ")" << d_pivotsSinceErrorProgress<< endl; - } - - if(Debug.isOn("primal::tableau")){ - d_linEq.debugCheckTableau(); - } - if(Debug.isOn("primal::consistent")){ Assert(d_linEq.debugEntireLinEqIsConsistent("MakeProgressOnLeaving")); } - } - break; - default: - Unreachable(); - } - } - return UsedMaxPivots; -} - - -/** - * Error set - * ErrorVariable |-> {ErrorVariable, InputVariable, InputConstraint} - */ - -/** - * Returns SAT if it was able to satisfy all of the constraints in the error set - * Returns UNSAT if it was able to able to find an error - */ -Result::Sat SimplexDecisionProcedure::primalConverge(int depth){ - d_pivotsSinceLastCheck = 0; - - while(!d_currentErrorVariables.empty()){ - PrimalResponse res = primal(MAX_ITERATIONS - d_pivotsSinceLastCheck); - - switch(res){ - case FoundUnboundedVariable: - - // Drive the variable to at least 0 - // TODO This variable should be driven to a value that makes all of the error functions including it 0 - // It'll or another unbounded will be selected in the next round anyways so ignore for now. - ++d_statistics.d_unboundedFound; - if( !belowThreshold() ){ - driveOptToZero(d_primalCarry.d_unbounded); - d_linEq.debugCheckTableau(); - if(Debug.isOn("primal::consistent")){ Assert(d_linEq.debugEntireLinEqIsConsistent("primalConverge")); } - - ++d_statistics.d_unboundedFound_drive; - } - Assert(belowThreshold()); - { - uint32_t dropped = contractErrorVariables(true); - Debug("primal::converge") << "primalConverge -> FoundUnboundedVariable -> dropped " << dropped << " to " << d_currentErrorVariables.size() << endl; - d_statistics.d_unboundedFound_dropped += dropped; - } - break; - case ReachedThresholdValue: - ++d_statistics.d_primalThresholdReachedPivot; - - Assert(belowThreshold()); - { - uint32_t dropped = contractErrorVariables(true); - Debug("primal::converge") << "primalConverge -> ReachedThresholdValue -> dropped " << dropped << " to " << d_currentErrorVariables.size() << endl; - d_statistics.d_primalThresholdReachedPivot_dropped += dropped; - } - break; - case UsedMaxPivots: - { - d_pivotsSinceLastCheck = 0; - ++d_statistics.d_primalReachedMaxPivots; - - // periodically attempt to do the following : - // contract the error variable - // check for a conflict on an error variable - uint32_t dropped = contractErrorVariables(false); - - if( checkForRowConflicts() ){ // try to periodically look for a row - Debug("primal::converge") << "primalConverge -> UsedMaxPivots -> unsat " << endl; - - ++d_statistics.d_primalReachedMaxPivots_checkForConflictWorked; - return Result::UNSAT; // row conflicts are minimal so stop. - } - - if(dropped > 0){ - Debug("primal::converge") << "primalConverge -> UsedMaxPivots -> dropped " << dropped << " to " << d_currentErrorVariables.size() << endl; - ++d_statistics.d_primalReachedMaxPivots_contractMadeProgress; - }else{ - Debug("primal::converge") << "primalConverge -> UsedMaxPivots -> nothing " << endl; - } - } - break; - case GlobalMinimum: - ++d_statistics.d_primalGlobalMinimum; - - // If the minimum is positive, this is unsat. - // However, the optimization row is not necessarily a minimal conflict - if(!belowThreshold()){ - - if(d_currentErrorVariables.size() == 1){ - // The optimization function is exactly the same as the last remaining variable - // The conflict for the row is the same as the conflict for the optimization function. - bool foundConflict = checkForRowConflicts(); - Assert(foundConflict); - Debug("primal::converge") << "primalConverge -> GlobalMinimum -> one variable" << endl; - - return Result::UNSAT; - }else{ - // There are at least 2 error variables. - // Look for a minimal conflict - - - if(checkForRowConflicts() ){ - Debug("primal::converge") << "primalConverge -> GlobalMinimum -> postitive -> rowconflict " << endl; - - ++d_statistics.d_primalGlobalMinimum_rowConflictWorked; - return Result::UNSAT; - } - - uint32_t dropped = contractErrorVariables(false); - - Debug("primal::converge") << "primalConverge -> GlobalMinimum -> postitive -> dropped " << dropped << " to " << d_currentErrorVariables.size() << endl; - if(dropped > 0){ - ++d_statistics.d_primalGlobalMinimum_contractMadeProgress; - } - - ErrorMap half; - d_currentErrorVariables.splitInto(half); - - Debug("primal::converge") << "primalConverge -> GlobalMinimum -> recursion " << depth << endl; - - - reconstructOptimizationFunction(); - Result::Sat resultOnRemaining = primalConverge(depth + 1); - - if(resultOnRemaining == Result::UNSAT){ - Debug("primal::converge") << "primalConverge -> GlobalMinimum -> recursion " << depth << " was unsat " << endl; - ++d_statistics.d_primalGlobalMinimum_firstHalfWasUnsat; - restoreErrorVariables(half); - return Result::UNSAT; - }else{ - ++d_statistics.d_primalGlobalMinimum_firstHalfWasSat; - Debug("primal::converge") << "primalConverge -> GlobalMinimum -> recursion " << depth << " was sat " << endl; - - Assert(resultOnRemaining == Result::SAT); - Assert(d_currentErrorVariables.empty()); - d_currentErrorVariables.addAll(half); - reconstructOptimizationFunction(); - return primalConverge(depth + 1); - } - } - - }else{ - - // if the optimum is <= 0 - // drop all of the satisfied variables and continue; - uint32_t dropped = contractErrorVariables(true); - Debug("primal::converge") << "primalConverge -> GlobalMinimum -> negative -> dropped "<< dropped << " to " << d_currentErrorVariables.size() << endl; - - ++d_statistics.d_primalGlobalMinimum_contractMadeProgress; - } - break; - default: - Unreachable(); - } - } - - return Result::SAT; -} - - -Result::Sat SimplexDecisionProcedure::primalFindModel(){ - Assert(d_primalCarry.isClear()); - - // Reduce the queue to only contain violations - reduceQueue(); - - if(d_queue.empty()){ - return Result::SAT; - } - TimerStat::CodeTimer codeTimer(d_statistics.d_primalTimer); - - ++d_statistics.d_primalCalls; - - Debug("primalFindModel") << "primalFindModel() begin" << endl; - - const int PAUSE_RATE = 100; - if(Debug.isOn("primal::pause") && d_statistics.d_primalCalls.getData() % PAUSE_RATE == 0){ - Debug("primal::pause") << "waiting for input: "; - std::string dummy; - std::getline(std::cin, dummy); - } - - // TODO restore the tableau by ejecting variables - // Tableau copy(d_tableau); - - Result::Sat answer; - { - TimerStat::CodeTimer codeTimer(d_statistics.d_internalTimer); - - // This is needed because of the fiddling with the partial model - //context::Context::ScopedPush speculativePush(satContext); - - constructErrorVariables(); - constructOptimizationFunction(); - if(Debug.isOn("primal::tableau")){ d_linEq.debugCheckTableau(); } - if(Debug.isOn("primal::consistent")){ d_linEq.debugEntireLinEqIsConsistent("primalFindModel 1"); } - answer = primalConverge(0); - } - removeOptimizationFunction(); - - - // exit - uint32_t nc = d_tableau.getNumColumns(); - //d_tableau = copy; - while(d_tableau.getNumColumns() < nc){ - d_tableau.increaseSize(); - } - restoreErrorVariables(d_currentErrorVariables); - - reduceQueue(); - - if(Debug.isOn("primal::tableau")){ d_linEq.debugCheckTableau(); } - - if(Debug.isOn("primal::consistent")){ d_linEq.debugEntireLinEqIsConsistent("primalFindModel2"); } - Debug("primalFindModel") << "primalFindModel() end " << answer << endl; - - // The set of variables in conflict with their bounds will still be a subset of the - // variables that are in conflict with their bounds in the beginning. - // The basic variables are the same because of the copy. - // Thus it is safe to not try to not recompute the queue of violating variables - - if(answer == Result::UNSAT){ - // This needs to be done in a different context level than the push - ++d_statistics.d_primalUnsatCalls; - }else{ - ++d_statistics.d_primalSatCalls; - } - - d_primalCarry.clear(); - - return answer; -} - -/** Clears the ErrorMap and relase the resources associated with it. - * There are a couple of error maps around - */ -void SimplexDecisionProcedure::restoreErrorVariables(SimplexDecisionProcedure::ErrorMap& es){ - while(!es.empty()){ - ArithVar e = es.back(); - - reassertErrorConstraint(e, es, false, false); - es.pop_back(); - } -} - -void SimplexDecisionProcedure::constructErrorVariables(){ - Assert(d_currentErrorVariables.empty()); - Assert(!d_queue.empty()); - - for(ArithPriorityQueue::const_iterator iter = d_queue.begin(), end = d_queue.end(); iter != end; ++iter){ - ArithVar input = *iter; - - Assert(d_tableau.isBasic(input)); - Assert(!d_partialModel.assignmentIsConsistent(input)); - - Assert(!d_currentErrorVariables.isKey(input)); - - bool ub = d_partialModel.strictlyGreaterThanUpperBound(input, d_partialModel.getAssignment(input)); - - Constraint original = ub ? d_partialModel.getUpperBoundConstraint(input) - : d_partialModel.getLowerBoundConstraint(input); - - d_currentErrorVariables.set(input, ErrorInfo(input, ub, original)); - - if(ub){ - d_partialModel.forceRelaxUpperBound(input); - }else{ - d_partialModel.forceRelaxLowerBound(input); - } - Debug("primal") << "adding error variable (" << input << ", " << ", " << original <<") "; - Debug("primal") << "ub "<< ub << " " << d_partialModel.getAssignment(input) << " " << original->getValue() <<")" << endl; - d_currentErrorVariables.set(input, ErrorInfo(input, ub, original)); - - // Constraint boundIsValue = d_constraintDatabase.getConstraint(bound, Equality, original->getValue()); - // boundIsValue->setPsuedoConstraint(); - - // d_partialModel.setAssignment(bound, boundIsValue->getValue()); - // d_partialModel.setUpperBoundConstraint(boundIsValue); - // d_partialModel.setLowerBoundConstraint(boundIsValue); - - // // if ub - // // then error = x - boundIsValue - // // else error = boundIsValue - x - - // ArithVar error = requestVariable(); - - // DeltaRational diff = ub ? - // d_partialModel.getAssignment(input) - boundIsValue->getValue() : - // boundIsValue->getValue() - d_partialModel.getAssignment(input); - - // d_partialModel.setAssignment(error, diff); - - // vector<Rational> coeffs; - // vector<ArithVar> variables; - // variables.push_back(input); - // coeffs.push_back(ub ? Rational(1) : Rational(-1)); - // variables.push_back(bound); - // coeffs.push_back(ub ? Rational(-1) : Rational(1)); - - // d_tableau.addRow(error, coeffs, variables); - - - } - - if(Debug.isOn("primal::tableau")){ d_linEq.debugCheckTableau(); } - if(Debug.isOn("primal::consistent")){ d_linEq.debugEntireLinEqIsConsistent("constructErrorVariables");} - Assert(!d_currentErrorVariables.empty()); -} - - - -/** Returns true if it has found a row conflict for any of the error variables. */ -bool SimplexDecisionProcedure::checkForRowConflicts(){ - vector<ArithVar> inConflict; - for(ErrorMap::const_iterator iter = d_currentErrorVariables.begin(), end = d_currentErrorVariables.end(); iter != end; ++iter){ - ArithVar error = *iter; - const ErrorInfo& info = d_currentErrorVariables[error]; - if(d_tableau.isBasic(error) && !info.errorIsLeqZero(d_partialModel)){ - - ArithVar x_j = info.isUpperbound() ? - selectSlackLowerBound(error) : - selectSlackUpperBound(error); - - if(x_j == ARITHVAR_SENTINEL ){ - inConflict.push_back(error); - } - } - } - - if(!inConflict.empty()){ - while(!inConflict.empty()){ - ArithVar error = inConflict.back(); - inConflict.pop_back(); - - reassertErrorConstraint(error, d_currentErrorVariables, false, true); - - Node conflict = d_currentErrorVariables[error].isUpperbound() ? - generateConflictAboveUpperBound(error) : - generateConflictBelowLowerBound(error); - Assert(!conflict.isNull()); - - d_currentErrorVariables.remove(error); - - reportConflict(conflict); - } - reconstructOptimizationFunction(); - return true; - }else{ - return false; - } -} - -void SimplexDecisionProcedure::reassertErrorConstraint(ArithVar e, SimplexDecisionProcedure::ErrorMap& es, bool definitelyTrue, bool definitelyFalse){ - Assert(es.isKey(e)); - const ErrorInfo& info = es.get(e); - Constraint original = info.getConstraint(); - - if(info.isUpperbound()){ - d_partialModel.setUpperBoundConstraint(original); - }else if(original->isLowerBound()){ - d_partialModel.setLowerBoundConstraint(original); - } - - Assert(!definitelyTrue || d_partialModel.assignmentIsConsistent(e)); - Assert(!definitelyFalse || !d_partialModel.assignmentIsConsistent(e)); -} - -uint32_t SimplexDecisionProcedure::contractErrorVariables(bool guaranteedSuccess){ - uint32_t entrySize = d_currentErrorVariables.size(); - Debug("primal::contract") << "contractErrorVariables() begin : " << d_currentErrorVariables.size() << endl; - - std::vector<ArithVar> toRemove; - for(ErrorMap::const_iterator iter = d_currentErrorVariables.begin(), end = d_currentErrorVariables.end(); iter != end; ++iter){ - ArithVar e = *iter; - if(d_currentErrorVariables[e].errorIsLeqZero(d_partialModel)){ - toRemove.push_back(e); - } - } - - Assert(!guaranteedSuccess || !toRemove.empty()); - - if(!toRemove.empty()){ - while(!toRemove.empty()){ - ArithVar e = toRemove.back(); - toRemove.pop_back(); - reassertErrorConstraint(e, d_currentErrorVariables, true, false); - d_currentErrorVariables.remove(e); - } - - reconstructOptimizationFunction(); - } - - Debug("primal::contract") << "contractErrorVariables() end : " << d_currentErrorVariables.size() << endl; - - uint32_t exitSize = d_currentErrorVariables.size(); - - Assert(exitSize <= entrySize); - Assert(!guaranteedSuccess|| exitSize < entrySize); - return entrySize - exitSize; -} - -void SimplexDecisionProcedure::removeOptimizationFunction(){ - Assert(d_optRow != ARITHVAR_SENTINEL); - Assert(d_tableau.isBasic(d_optRow)); - - d_tableau.removeBasicRow(d_optRow); - releaseVariable(d_optRow); - - d_optRow = ARITHVAR_SENTINEL; - d_negOptConstant = d_DELTA_ZERO; - - Assert(d_optRow == ARITHVAR_SENTINEL); -} - -void SimplexDecisionProcedure::constructOptimizationFunction(){ - Assert(d_optRow == ARITHVAR_SENTINEL); - Assert(d_negOptConstant == d_DELTA_ZERO); - - d_optRow = requestVariable(); - - - std::vector<Rational> coeffs; - std::vector<ArithVar> variables; - for(ErrorMap::const_iterator iter = d_currentErrorVariables.begin(), end = d_currentErrorVariables.end(); iter != end; ++iter){ - ArithVar e = *iter; - - if(d_currentErrorVariables[e].isUpperbound()){ - coeffs.push_back(Rational(1)); - variables.push_back(e); - d_negOptConstant = d_negOptConstant + (d_currentErrorVariables[e].getConstraint()->getValue()); - }else{ - coeffs.push_back(Rational(-1)); - variables.push_back(e); - d_negOptConstant = d_negOptConstant - (d_currentErrorVariables[e].getConstraint()->getValue()); - } - } - d_tableau.addRow(d_optRow, coeffs, variables); - - DeltaRational newAssignment = d_linEq.computeRowValue(d_optRow, false); - d_partialModel.setAssignment(d_optRow, newAssignment); - - if(Debug.isOn("primal::tableau")){ d_linEq.debugCheckTableau(); } - - - if(Debug.isOn("primal::consistent")){ - d_linEq.debugEntireLinEqIsConsistent("constructOptimizationFunction"); - } - - d_pivotsSinceOptProgress = 0; - d_pivotsSinceErrorProgress = 0; - - Assert(d_optRow != ARITHVAR_SENTINEL); -} - -void SimplexDecisionProcedure::reconstructOptimizationFunction(){ - removeOptimizationFunction(); - constructOptimizationFunction(); -} - - - -/* TODO: - * Very naive implementation. Recomputes everything every time. - * Currently looks for the variable that can decrease the optimization function the most. - * - */ -SimplexDecisionProcedure::PrimalResponse SimplexDecisionProcedure::primalCheck() -{ - Debug("primal") << "primalCheck() begin" << endl; - - ArithVar leaving = ARITHVAR_SENTINEL; - ArithVar entering = ARITHVAR_SENTINEL; - DeltaRational leavingShift = d_DELTA_ZERO; // The amount the leaving variable can change by without making the tableau inconsistent - DeltaRational leavingDelta = d_DELTA_ZERO; // The amount the optimization function changes by selecting leaving - - Assert(d_improvementCandidates.empty()); - - for( Tableau::RowIterator ri = d_tableau.basicRowIterator(d_optRow); !ri.atEnd(); ++ri){ - const Tableau::Entry& e = *ri; - - ArithVar curr = e.getColVar(); - if(curr == d_optRow){ continue; } - - - int sgn = e.getCoefficient().sgn(); - Assert(sgn != 0); - if( (sgn < 0 && d_partialModel.strictlyBelowUpperBound(curr)) || - (sgn > 0 && d_partialModel.strictlyAboveLowerBound(curr)) ){ - - d_improvementCandidates.push_back(&e); - } - } - - if(d_improvementCandidates.empty()){ - Debug("primal") << "primalCheck() end : global" << endl; - return GlobalMinimum; // No variable in the optimization function can be improved - } - - DeltaRational minimumShift; - DeltaRational currShift; - for(EntryVector::const_iterator ci = d_improvementCandidates.begin(), end_ci = d_improvementCandidates.end(); ci != end_ci; ++ci){ - const Tableau::Entry& e = *(*ci); - ArithVar curr = e.getColVar(); - - ArithVar currEntering; - bool progress; - - minimumShift = (leaving == ARITHVAR_SENTINEL ) ? leavingDelta/(e.getCoefficient().abs()) : d_DELTA_ZERO; - int sgn = e.getCoefficient().sgn(); - computeShift(curr, sgn < 0, progress, currEntering, currShift, minimumShift); - - if(currEntering == ARITHVAR_SENTINEL){ - d_improvementCandidates.clear(); - - Debug("primal") << "primalCheck() end : unbounded" << endl; - d_primalCarry.d_unbounded = curr; - return FoundUnboundedVariable; - }else if(progress) { - leaving = curr; - leavingShift = currShift; - leavingDelta = currShift * e.getCoefficient(); - entering = currEntering; - - Assert(leavingDelta < d_DELTA_ZERO); - - const int RECHECK_PERIOD = 10; - if(d_pivotsSinceErrorProgress % RECHECK_PERIOD != 0){ - // we can make progress, stop - break; - } - } - } - - if(leaving == ARITHVAR_SENTINEL){ - cout << "Nothing can make progress " << endl; - - const uint32_t THRESHOLD = 20; - if(d_pivotsSinceOptProgress <= THRESHOLD){ - - int index = rand() % d_improvementCandidates.size(); - leaving = (*d_improvementCandidates[index]).getColVar(); - entering = selectFirstValid(leaving, (*d_improvementCandidates[index]).getCoefficient().sgn() < 0); - }else{ // Bland's rule - bool increasing; - for(EntryVector::const_iterator ci = d_improvementCandidates.begin(), end_ci = d_improvementCandidates.end(); ci != end_ci; ++ci){ - const Tableau::Entry& e = *(*ci); - ArithVar curr = e.getColVar(); - leaving = (leaving == ARITHVAR_SENTINEL) ? curr : minVarOrder(*this, curr, leaving); - if(leaving == curr){ - increasing = (e.getCoefficient().sgn() < 0); - } - } - - entering = selectMinimumValid(leaving, increasing); - } - Assert(leaving != ARITHVAR_SENTINEL); - Assert(entering != ARITHVAR_SENTINEL); - - d_primalCarry.d_leaving = leaving; - d_primalCarry.d_entering = entering; - - d_primalCarry.d_nextEnteringValue = d_partialModel.getAssignment(entering); - - Debug("primal") << "primalCheck() end : no progress made " << leaving << " to " << entering << " (" << d_pivotsSinceOptProgress << ")"<< endl; - d_improvementCandidates.clear(); - return NoProgressOnLeaving; - }else{ - const Tableau::Entry& enterLeavingEntry = d_tableau.findEntry(d_tableau.basicToRowIndex(entering), leaving); - Assert(!enterLeavingEntry.blank()); - - d_primalCarry.d_leaving = leaving; - d_primalCarry.d_entering = entering; - d_primalCarry.d_nextEnteringValue = d_partialModel.getAssignment(entering) - + leavingShift * enterLeavingEntry.getCoefficient(); - - Debug("primal") << "primalCheck() end : progress" << endl - << leaving << " to " << entering << " ~ " - << d_partialModel.getAssignment(leaving) << " ~ " << leavingShift - << " ~ " << enterLeavingEntry.getCoefficient() - << " ~ " << d_primalCarry.d_nextEnteringValue << endl; - - d_improvementCandidates.clear(); - return MakeProgressOnLeaving; - } - - // anyProgress = true; - - // DeltaRational currDelta = currShift * e.getCoefficient(); - - // int cmp = currDelta.cmp(leavingDelta); - - // // Cases: - // // 1) No candidate yet, - // // 2) there is a candidate with a strictly better update, or - // // 3) there is a candidate with the same update value that has a smaller value in the variable ordering. - // // - // // Case 3 covers Bland's rule. - // if(entering == ARITHVAR_SENTINEL || cmp < 0){ - // leaving = curr; - // }else if( cmp == 0 ){ - // leaving = minVarOrder(*this, curr, leaving); - // } - - // if(leaving == curr){ - // leavingShift = currShift; - // leavingDelta = currDelta; - // entering = currEntering; - // } - // } - // } - - // if(leaving == ARITHVAR_SENTINEL){ - // Debug("primal") << "primalCheck() end : global" << endl; - // return GlobalMinimum; // No variable in the optimization function can be improved - // }else{ - // const Tableau::Entry& enterLeavingEntry = d_tableau.findEntry(d_tableau.basicToRowIndex(entering), leaving); - // Assert(!enterLeavingEntry.blank()); - - // d_primalCarry.d_leaving = leaving; - // d_primalCarry.d_entering = entering; - // d_primalCarry.d_nextEnteringValue = d_partialModel.getAssignment(entering) - // + leavingShift * enterLeavingEntry.getCoefficient(); - - // Debug("primal") << "primalCheck() end : progress" << endl - // << leaving << " to " << entering << " ~ " - // << d_partialModel.getAssignment(leaving) << " ~ " << leavingShift - // << " ~ " << enterLeavingEntry.getCoefficient() - // << " ~ " << d_primalCarry.d_nextEnteringValue << endl; - // return MakeProgressOnLeaving; - // } -} - -ArithVar SimplexDecisionProcedure::selectMinimumValid(ArithVar v, bool increasing){ - ArithVar minimum = ARITHVAR_SENTINEL; - for(Tableau::ColIterator colIter = d_tableau.colIterator(v);!colIter.atEnd(); ++colIter){ - const Tableau::Entry& e = *colIter; - ArithVar basic = d_tableau.rowIndexToBasic(e.getRowIndex()); - if(basic == d_optRow) continue; - - - int esgn = e.getCoefficient().sgn(); - bool basicInc = (increasing == (esgn > 0)); - - if(!(basicInc ? d_partialModel.strictlyBelowUpperBound(basic) : - d_partialModel.strictlyAboveLowerBound(basic))){ - if(minimum == ARITHVAR_SENTINEL){ - minimum = basic; - }else{ - minimum = minVarOrder(*this, basic, minimum); - } - } - } - return minimum; -} - -ArithVar SimplexDecisionProcedure::selectFirstValid(ArithVar v, bool increasing){ - ArithVar minimum = ARITHVAR_SENTINEL; - - for(Tableau::ColIterator colIter = d_tableau.colIterator(v);!colIter.atEnd(); ++colIter){ - const Tableau::Entry& e = *colIter; - ArithVar basic = d_tableau.rowIndexToBasic(e.getRowIndex()); - if(basic == d_optRow) continue; - - int esgn = e.getCoefficient().sgn(); - bool basicInc = (increasing == (esgn > 0)); - - if(!(basicInc ? d_partialModel.strictlyBelowUpperBound(basic) : - d_partialModel.strictlyAboveLowerBound(basic))){ - if(minimum == ARITHVAR_SENTINEL){ - minimum = basic; - }else{ - minimum = minRowLength(*this, basic, minimum); - } - } - } - return minimum; -} - - - -void SimplexDecisionProcedure::computeShift(ArithVar leaving, bool increasing, bool& progress, ArithVar& entering, DeltaRational& shift, const DeltaRational& minimumShift){ - Assert(increasing ? (minimumShift >= d_DELTA_ZERO) : (minimumShift <= d_DELTA_ZERO) ); - - static int instance = 0; - Debug("primal") << "computeshift " << ++instance << " " << leaving << endl; - - // The selection for the leaving variable - entering = ARITHVAR_SENTINEL; - - // no progress is initially made - progress = false; - - bool bounded = false; - - if(increasing ? d_partialModel.hasUpperBound(leaving) : d_partialModel.hasLowerBound(leaving)){ - const DeltaRational& assignment = d_partialModel.getAssignment(leaving); - - bounded = true; - - DeltaRational diff = increasing ? d_partialModel.getUpperBound(leaving) - assignment : d_partialModel.getLowerBound(leaving) - assignment; - Assert(increasing ? diff.sgn() >=0 : diff.sgn() <= 0); - if((increasing) ? (diff < minimumShift) : ( diff > minimumShift)){ - Assert(!progress); - entering = leaving; // My my my, what an ugly hack - return; // no progress is possible stop - } - } - - // shift has a meaningful value once entering has a meaningful value - // if increasing, - // then shift > minimumShift >= 0 - // else shift < minimumShift <= 0 - // - // Maintain the following invariant: - // - // if increasing, - // if e_ij > 0, diff >= shift > minimumShift >= 0 - // if e_ij < 0, diff >= shift > minimumShift >= 0 - // if !increasing, - // if e_ij > 0, diff <= shift < minimumShift <= 0 - // if e_ij < 0, diff <= shift < minimumShift <= 0 - // if increasing == (e_ij > 0), diff = (d_partialModel.getUpperBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient() - // if increasing != (e_ij > 0), diff = (d_partialModel.getLowerBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient() - - for(Tableau::ColIterator colIter = d_tableau.colIterator(leaving);!colIter.atEnd(); ++colIter){ - const Tableau::Entry& e = *colIter; - - ArithVar basic = d_tableau.rowIndexToBasic(e.getRowIndex()); - if(basic == d_optRow) continue; - - int esgn = e.getCoefficient().sgn(); - bool basicInc = (increasing == (esgn > 0)); - // If both are true, increasing the variable entering increases the basic variable - // If both are false, the entering variable is decreasing, but the coefficient is negative and the basic variable is increasing - // If exactly one is false, the basic variable is decreasing. - - Debug("primal::shift") << basic << " " << d_partialModel.hasUpperBound(basic) << " " - << d_partialModel.hasLowerBound(basic) << " " - << e.getCoefficient() << endl; - - if( (basicInc && d_partialModel.hasUpperBound(basic))|| - (!basicInc && d_partialModel.hasLowerBound(basic))){ - - if(!(basicInc ? d_partialModel.strictlyBelowUpperBound(basic) : - d_partialModel.strictlyAboveLowerBound(basic))){ - // diff == 0, as diff > minimumShift >= 0 or diff < minimumShift <= 0 - Assert(!progress); - entering = basic; - return; - }else{ - DeltaRational diff = basicInc ? - (d_partialModel.getUpperBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient() : - (d_partialModel.getLowerBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient(); - - if( entering == ARITHVAR_SENTINEL ){ - if(increasing ? (diff <= minimumShift) : (diff >= minimumShift)){ - Assert(!progress); - entering = basic; - return; - }else{ - Assert(increasing ? (diff > minimumShift) : (diff < minimumShift)); - shift = diff; - entering = basic; - bounded = true; - } - }else{ - if( increasing ? (diff < shift) : diff > shift){ - // a new min for increasing - // a new max for decreasing - - if(increasing ? (diff <= minimumShift) : (diff >= minimumShift)){ - Assert(!progress); - entering = basic; - return; - }else{ - Assert(increasing ? (diff > minimumShift) : (diff < minimumShift)); - shift = diff; - entering = basic; - } - } - } - } - } - } - - if(!bounded){ - // A totally unbounded variable - Assert(entering == ARITHVAR_SENTINEL); - progress = true; - return; - }else if(entering == ARITHVAR_SENTINEL){ - // We have a variable that is bounded only by its maximum - for(Tableau::ColIterator colIter = d_tableau.colIterator(leaving);!colIter.atEnd(); ++colIter){ - const Tableau::Entry& e = *colIter; - - ArithVar basic = d_tableau.rowIndexToBasic(e.getRowIndex()); - if(basic == d_optRow){ - continue; - } else{ - entering = basic; - break; - } - } - Assert(entering != ARITHVAR_SENTINEL); - - Assert(increasing ? d_partialModel.hasUpperBound(leaving) : d_partialModel.hasLowerBound(leaving)); - - const DeltaRational& assignment = d_partialModel.getAssignment(leaving); - DeltaRational diff = increasing ? d_partialModel.getUpperBound(leaving) - assignment : d_partialModel.getLowerBound(leaving) - assignment; - - shift = diff; - - Assert(increasing ? shift.sgn() >=0 : shift.sgn() <= 0); - Assert(increasing ? shift > minimumShift : shift < minimumShift); - - progress = true; - return; - }else{ - Assert(bounded); - progress = true; - - if((increasing ? d_partialModel.hasUpperBound(leaving) : d_partialModel.hasLowerBound(leaving) )){ - Assert(entering != ARITHVAR_SENTINEL); - const DeltaRational& assignment = d_partialModel.getAssignment(leaving); - DeltaRational diff = increasing ? d_partialModel.getUpperBound(leaving) - assignment : d_partialModel.getLowerBound(leaving) - assignment; - if((increasing) ? (diff < shift) : ( diff > shift)){ - shift = diff; - } - } - - Assert(increasing ? shift.sgn() >=0 : shift.sgn() <= 0); - Assert(increasing ? shift > minimumShift : shift < minimumShift); - return; - } - - - // if(! bounded || - // (increasing && diff < shift) || // a new min for increasing - // (!increasing && diff > shift)){ // a new max for decreasing - // bounded = true; - // shift = diff; - // entering = basic; - // } - // } - - // if(notAtTheBound && !blandMode){ - // DeltaRational diff = basicInc ? - // (d_partialModel.getUpperBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient() : - // (d_partialModel.getLowerBound(basic) - d_partialModel.getAssignment(basic)) / e.getCoefficient(); - - // if(! bounded || - // (increasing && diff < shift) || // a new min for increasing - // (!increasing && diff > shift)){ // a new max for decreasing - // bounded = true; - // shift = diff; - // entering = basic; - // } - // }else if (!notAtTheBound) { // basic is already exactly at the bound - // if(!blandMode){ // Enter into using Bland's rule - // blandMode = true; - // bounded = true; - // shift = d_DELTA_ZERO; - // entering = basic; - // }else{ - // entering = minVarOrder(*this, entering, basic); // Bland's rule. - // } - // } - // else this basic variable cannot be violated by increasing/decreasing entering - - - - - // if(!blandMode && (increasing ? d_partialModel.hasUpperBound(leaving) : d_partialModel.hasLowerBound(leaving) )){ - // Assert(entering != ARITHVAR_SENTINEL); - // bounded = true; - // DeltaRational diff = increasing ? d_partialModel.getUpperBound(leaving) - assignment : d_partialModel.getLowerBound(leaving) - assignment; - // if((increasing) ? (diff < shift) : ( diff > shift)){ - // shift = diff; - // } - // } - - // Assert(increasing ? shift.sgn() >=0 : shift.sgn() <= 0); - - // return shift; -} - - -/** - * Given an variable on the optimization row that can be used to decrease the value of the optimization function - * arbitrarily and an optimization function that is strictly positive in the current model, - * driveOptToZero updates the value of unbounded s.t. the value of d_opt is exactly 0. - */ -void SimplexDecisionProcedure::driveOptToZero(ArithVar unbounded){ - Assert(!belowThreshold()); - - const Tableau::Entry& e = d_tableau.findEntry(d_tableau.basicToRowIndex(d_optRow), unbounded); - Assert(!e.blank()); - - DeltaRational theta = (d_negOptConstant-d_partialModel.getAssignment(d_optRow))/ (e.getCoefficient()); - Assert((e.getCoefficient().sgn() > 0) ? (theta.sgn() < 0) : (theta.sgn() > 0)); - - DeltaRational newAssignment = d_partialModel.getAssignment(unbounded) + theta; - d_linEq.update(unbounded, newAssignment); - - if(Debug.isOn("primal::consistent")){ Assert(d_linEq.debugEntireLinEqIsConsistent("driveOptToZero")); } - - Assert(belowThreshold()); -} diff --git a/src/theory/arith/simplex-converge.h b/src/theory/arith/simplex-converge.h deleted file mode 100644 index dac3a9b49..000000000 --- a/src/theory/arith/simplex-converge.h +++ /dev/null @@ -1,531 +0,0 @@ -/********************* */ -/*! \file simplex.h - ** \verbatim - ** Original author: taking - ** Major contributors: none - ** Minor contributors (to current version): none - ** This file is part of the CVC4 prototype. - ** Copyright (c) 2009, 2010, 2011 The Analysis of Computer Systems Group (ACSys) - ** Courant Institute of Mathematical Sciences - ** New York University - ** See the file COPYING in the top-level source directory for licensing - ** information.\endverbatim - ** - ** \brief This is an implementation of the Simplex Module for the Simplex for DPLL(T) decision procedure. - ** - ** This implements the Simplex module for the Simpelx for DPLL(T) decision procedure. - ** See the Simplex for DPLL(T) technical report for more background.(citation?) - ** This shares with the theory a Tableau, and a PartialModel that: - ** - satisfies the equalities in the Tableau, and - ** - the assignment for the non-basic variables satisfies their bounds. - ** This is required to either produce a conflict or satisifying PartialModel. - ** Further, we require being told when a basic variable updates its value. - ** - ** During the Simplex search we maintain a queue of variables. - ** The queue is required to contain all of the basic variables that voilate their bounds. - ** As elimination from the queue is more efficient to be done lazily, - ** we do not maintain that the queue of variables needs to be only basic variables or only variables that satisfy their bounds. - ** - ** The simplex procedure roughly follows Alberto's thesis. (citation?) - ** There is one round of selecting using a heuristic pivoting rule. (See PreferenceFunction Documentation for the available options.) - ** The non-basic variable is the one that appears in the fewest pivots. (Bruno says that Leonardo invented this first.) - ** After this, Bland's pivot rule is invoked. - ** - ** During this proccess, we periodically inspect the queue of variables to - ** 1) remove now extraneous extries, - ** 2) detect conflicts that are "waiting" on the queue but may not be detected by the current queue heuristics, and - ** 3) detect multiple conflicts. - ** - ** Conflicts are greedily slackened to use the weakest bounds that still produce the conflict. - ** - ** Extra things tracked atm: (Subject to change at Tim's whims) - ** - A superset of all of the newly pivoted variables. - ** - A queue of additional conflicts that were discovered by Simplex. - ** These are theory valid and are currently turned into lemmas - **/ - - -#include "cvc4_private.h" - -#ifndef __CVC4__THEORY__ARITH__SIMPLEX_H -#define __CVC4__THEORY__ARITH__SIMPLEX_H - -#include "theory/arith/arithvar.h" -#include "theory/arith/arith_priority_queue.h" -#include "theory/arith/delta_rational.h" -#include "theory/arith/matrix.h" -#include "theory/arith/partial_model.h" -#include "theory/arith/linear_equality.h" - -#include "context/cdlist.h" - -#include "util/dense_map.h" -#include "options/options.h" -#include "util/stats.h" -#include "util/result.h" - -#include <queue> - -namespace CVC4 { -namespace theory { -namespace arith { - -class SimplexDecisionProcedure { -private: - ArithVar d_conflictVariable; - DenseSet d_successes; - - /** Linear equality module. */ - LinearEqualityModule& d_linEq; - - /** - * Manages information about the assignment and upper and lower bounds on - * variables. - * Partial model matches that in LinearEqualityModule. - */ - ArithPartialModel& d_partialModel; - - /** - * Stores the linear equalities used by Simplex. - * Tableau from the LinearEquality module. - */ - Tableau& d_tableau; - - /** Contains a superset of the basic variables in violation of their bounds. */ - ArithPriorityQueue d_queue; - - /** Number of variables in the system. This is used for tuning heuristics. */ - ArithVar d_numVariables; - - /** This is the call back channel for Simplex to report conflicts. */ - NodeCallBack& d_conflictChannel; - - /** Maps a variable to how many times they have been used as a pivot in the simplex search. */ - DenseMultiset d_pivotsInRound; - - /** Stores to the DeltaRational constant 0. */ - DeltaRational d_DELTA_ZERO; - - //TODO make an option - const static uint32_t MAX_ITERATIONS = 20; - - - /** Used for requesting d_opt, bound and error variables for primal.*/ - ArithVarMalloc& d_arithVarMalloc; - - /** Used for constructing temporary variables, bound and error variables for primal.*/ - ConstraintDatabase& d_constraintDatabase; - -public: - SimplexDecisionProcedure(LinearEqualityModule& linEq, - NodeCallBack& conflictChannel, - ArithVarMalloc& variables, - ConstraintDatabase& constraintDatabase); - - /** - * This must be called when the value of a basic variable may now voilate one - * of its bounds. - */ - void updateBasic(ArithVar x){ - d_queue.enqueueIfInconsistent(x); - } - - /** - * Tries to update the assignments of variables such that all of the - * assignments are consistent with their bounds. - * This is done by a simplex search through the possible bases of the tableau. - * - * If all of the variables can be made consistent with their bounds - * SAT is returned. Otherwise UNSAT is returned, and at least 1 conflict - * was reported on the conflictCallback passed to the Module. - * - * Tableau pivoting is performed so variables may switch from being basic to - * nonbasic and vice versa. - * - * Corresponds to the "check()" procedure in [Cav06]. - */ - Result::Sat dualFindModel(bool exactResult); - - - /** - * Tries to update the assignments of the variables s.t. all of the assignments - * are consistent with their bounds. - * - * This is a REALLY heavy hammer consider calling dualFindModel(false) first. - * - * !!!!!!!!!!!!!IMPORTANT!!!!!!!!!!!!!! - * This procedure needs to temporarily relax contraints to contruct a satisfiable system. - * To do this, it is going to do a sat push. - */ - Result::Sat primalFindModel(); - -private: - - - /** - * A PreferenceFunction takes a const ref to the SimplexDecisionProcedure, - * and 2 ArithVar variables and returns one of the ArithVar variables potentially - * using the internals of the SimplexDecisionProcedure. - * - * Both ArithVar must be non-basic in d_tableau. - */ - typedef ArithVar (*PreferenceFunction)(const SimplexDecisionProcedure&, ArithVar, ArithVar); - - /** - * minVarOrder is a PreferenceFunction for selecting the smaller of the 2 ArithVars. - * This PreferenceFunction is used during the VarOrder stage of - * findModel. - */ - static ArithVar minVarOrder(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y); - - /** - * minRowCount is a PreferenceFunction for selecting the variable with the smaller - * row count in the tableau. - * - * This is a heuristic rule and should not be used - * during the VarOrder stage of findModel. - */ - static ArithVar minColLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y); - static ArithVar minRowLength(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y); - - /** - * minBoundAndRowCount is a PreferenceFunction for preferring a variable - * without an asserted bound over variables with an asserted bound. - * If both have bounds or both do not have bounds, - * the rule falls back to minRowCount(...). - * - * This is a heuristic rule and should not be used - * during the VarOrder stage of findModel. - */ - static ArithVar minBoundAndRowCount(const SimplexDecisionProcedure& simp, ArithVar x, ArithVar y); - - - /** - * This is the main simplex for DPLL(T) loop. - * It runs for at most maxIterations. - * - * Returns true iff it has found a conflict. - * d_conflictVariable will be set and the conflict for this row is reported. - */ - bool searchForFeasibleSolution(uint32_t maxIterations); - - enum SearchPeriod {BeforeDiffSearch, DuringDiffSearch, AfterDiffSearch, DuringVarOrderSearch, AfterVarOrderSearch}; - - bool findConflictOnTheQueue(SearchPeriod period); - - - /** - * Given the basic variable x_i, - * this function finds the smallest nonbasic variable x_j in the row of x_i - * in the tableau that can "take up the slack" to let x_i satisfy its bounds. - * This returns ARITHVAR_SENTINEL if none exists. - * - * More formally one of the following conditions must be satisfied: - * - lowerBound && a_ij < 0 && assignment(x_j) < upperbound(x_j) - * - lowerBound && a_ij > 0 && assignment(x_j) > lowerbound(x_j) - * - !lowerBound && a_ij > 0 && assignment(x_j) < upperbound(x_j) - * - !lowerBound && a_ij < 0 && assignment(x_j) > lowerbound(x_j) - * - */ - template <bool lowerBound> ArithVar selectSlack(ArithVar x_i, PreferenceFunction pf); - ArithVar selectSlackLowerBound(ArithVar x_i, PreferenceFunction pf = minVarOrder) { - return selectSlack<true>(x_i, pf); - } - ArithVar selectSlackUpperBound(ArithVar x_i, PreferenceFunction pf = minVarOrder) { - return selectSlack<false>(x_i, pf); - } - /** - * Returns the smallest basic variable whose assignment is not consistent - * with its upper and lower bounds. - */ - ArithVar selectSmallestInconsistentVar(); - - /** - * Given a non-basic variable that is know to not be updatable - * to a consistent value, construct and return a conflict. - * Follows section 4.2 in the CAV06 paper. - */ - Node generateConflictAboveUpperBound(ArithVar conflictVar); - Node generateConflictBelowLowerBound(ArithVar conflictVar); - -public: - void increaseMax() {d_numVariables++;} - - - void clearQueue() { - d_queue.clear(); - } - - - bool debugIsInCollectionQueue(ArithVar var) const{ - Assert(d_queue.inCollectionMode()); - return d_queue.collectionModeContains(var); - } - - void reduceQueue(){ - d_queue.reduce(); - } - - ArithPriorityQueue::const_iterator queueBegin() const{ - return d_queue.begin(); - } - - ArithPriorityQueue::const_iterator queueEnd() const{ - return d_queue.end(); - } - -private: - - /** Reports a conflict to on the output channel. */ - void reportConflict(Node conflict){ - d_conflictChannel(conflict); - ++(d_statistics.d_simplexConflicts); - } - - template <bool above> - inline bool isAcceptableSlack(int sgn, ArithVar nonbasic){ - return - ( above && sgn < 0 && d_partialModel.strictlyBelowUpperBound(nonbasic)) || - ( above && sgn > 0 && d_partialModel.strictlyAboveLowerBound(nonbasic)) || - (!above && sgn > 0 && d_partialModel.strictlyBelowUpperBound(nonbasic)) || - (!above && sgn < 0 && d_partialModel.strictlyAboveLowerBound(nonbasic)); - } - - /** - * Checks a basic variable, b, to see if it is in conflict. - * If a conflict is discovered a node summarizing the conflict is returned. - * Otherwise, Node::null() is returned. - */ - Node checkBasicForConflict(ArithVar b); - - Node weakenConflict(bool aboveUpper, ArithVar basicVar); - Constraint weakestExplanation(bool aboveUpper, DeltaRational& surplus, ArithVar v, const Rational& coeff, bool& anyWeakening, ArithVar basic); - - /** Gets a fresh variable from TheoryArith. */ - ArithVar requestVariable(){ - return d_arithVarMalloc.request(); - } - - /** Releases a requested variable from TheoryArith.*/ - void releaseVariable(ArithVar v){ - d_arithVarMalloc.release(v); - } - - - /** An error info keeps the information associated with the construction of an ErrorVariable. */ - class ErrorInfo { - private: - /** The variable for which the error variable was constructed.*/ - ArithVar d_variable; - - // If false -> lowerbound - bool d_upperbound; - - /** The violated constraint this was constructed to try to satisfy.*/ - Constraint d_violated; - - public: - ErrorInfo(ArithVar error, bool ub, const Constraint original) - : d_variable(error), d_upperbound(ub), d_violated(original) {} - - ErrorInfo() : - d_variable(ARITHVAR_SENTINEL), d_upperbound(false), d_violated(NullConstraint){} - - inline ArithVar getVariable() const { - return d_variable; - } - - inline bool isUpperbound() const { - return d_upperbound; - } - - inline bool errorIsLeqZero(const ArithPartialModel& d_pm) const{ - return isUpperbound() ? - (d_pm.getAssignment(d_variable) <= d_violated->getValue()) : - (d_pm.getAssignment(d_variable) >= d_violated->getValue()); - } - - inline Constraint getConstraint() const { - return d_violated; - } - - inline DeltaRational getErrorAmount(const ArithPartialModel& d_pm) const { - return isUpperbound() ? - (d_pm.getAssignment(d_variable) - d_violated->getValue()) : - (d_violated->getValue() - d_pm.getAssignment(d_variable)); - } - }; - - typedef DenseMap<ErrorInfo> ErrorMap; - - /** A map from the error variables to the associated ErrorInfo.*/ - ErrorMap d_currentErrorVariables; - - /** The optimization function is implicitly defined as - * f_i = d_optRow - d_negOptConstant - * - * d_optRow is a basic variable in the tableau. - * The tableau maintains that it is equal to the sum of -1^{!ub} * the error variables in - * d_currentErrorVariables. - * - * d_negOptConstant is explicitly the negation of the sum of the bounds that were violated - * - * assignment(f_i) <= 0 iff assignment(d_optRow) <= d_negOptConstant - */ - /** The variable for the variable part of the optimization function.*/ - ArithVar d_optRow; - - /** The constant part of the optimization function.*/ - DeltaRational d_negOptConstant; - - inline bool belowThreshold() const { - return d_partialModel.getAssignment(d_optRow) <= d_negOptConstant; - } - - /** - * A PrimalResponse represents the state that the primal simplex solver is in. - */ - enum PrimalResponse { - // The optimization can decrease arbitrarily on some variable in the function - FoundUnboundedVariable, - - // The optimization function has reached a threshold value and is checking back in - ReachedThresholdValue, - - // Simplex has used up its pivot bound and is checking back in with its caller - UsedMaxPivots, - - //Simplex can make progress on the pair of entering and leaving variables - MakeProgressOnLeaving, - - //Simplex is not at a minimum but no leaving variable can be changed to help - NoProgressOnLeaving, - - // Simplex has reached a minimum for its optimization function - GlobalMinimum - }; - - /** - * These fields are for sticking information for passing information back with the PrimalRespones. - * These fields should be ignored as unsafe/unknown unless you have a PrimalResponse that states - * the field makes sense. - */ - struct PrimalPassBack { - public: - /** - * A variable s.t. its value can be increased/decreased arbitrarily to change the optimization function - * arbitrarily low. - */ - ArithVar d_unbounded; - - /** The leaving variable selection for primal simplex. */ - ArithVar d_leaving; - - /** The entering variable selection for primal simplex. */ - ArithVar d_entering; - - /** The new value for the leaving variable value for primal simplex.*/ - DeltaRational d_nextEnteringValue; - - PrimalPassBack() { clear(); } - void clear(){ - d_unbounded = (d_leaving = (d_entering = ARITHVAR_SENTINEL)); - d_nextEnteringValue = DeltaRational(); - } - - bool isClear() const { - return d_unbounded == ARITHVAR_SENTINEL && - d_leaving == ARITHVAR_SENTINEL && - d_entering == ARITHVAR_SENTINEL && - d_nextEnteringValue.sgn() == 0; - } - } d_primalCarry; - - uint32_t d_pivotsSinceErrorProgress; - uint32_t d_pivotsSinceOptProgress; - uint32_t d_pivotsSinceLastCheck; - - typedef std::vector< const Tableau::Entry* > EntryVector; - EntryVector d_improvementCandidates; - - PrimalResponse primal(uint32_t maxIterations); - PrimalResponse primalCheck(); - Result::Sat primalConverge(int depth); - void driveOptToZero(ArithVar unbounded); - uint32_t contractErrorVariables(bool guaranteedSuccess); - bool checkForRowConflicts(); - void restoreErrorVariables(ErrorMap& es); - void constructErrorVariables(); - void constructOptimizationFunction(); - void removeOptimizationFunction(); - void reconstructOptimizationFunction(); - ArithVar selectMinimumValid(ArithVar v, bool increasing); - ArithVar selectFirstValid(ArithVar v, bool increasing); - - void reassertErrorConstraint(ArithVar e, ErrorMap& es, bool definitelyTrue, bool definitelyFalse); - void computeShift(ArithVar leaving, bool increasing, bool& progress, ArithVar& entering, DeltaRational& shift, const DeltaRational& minimumShift); - - /** These fields are designed to be accessible to TheoryArith methods. */ - class Statistics { - public: - IntStat d_statUpdateConflicts; - - TimerStat d_findConflictOnTheQueueTime; - - IntStat d_attemptBeforeDiffSearch, d_successBeforeDiffSearch; - IntStat d_attemptAfterDiffSearch, d_successAfterDiffSearch; - IntStat d_attemptDuringDiffSearch, d_successDuringDiffSearch; - IntStat d_attemptDuringVarOrderSearch, d_successDuringVarOrderSearch; - IntStat d_attemptAfterVarOrderSearch, d_successAfterVarOrderSearch; - - IntStat d_weakeningAttempts, d_weakeningSuccesses, d_weakenings; - TimerStat d_weakenTime; - - - IntStat d_simplexConflicts; - - // Primal stuffs - TimerStat d_primalTimer; - TimerStat d_internalTimer; - - IntStat d_primalCalls; - IntStat d_primalSatCalls; - IntStat d_primalUnsatCalls; - - IntStat d_primalPivots; - IntStat d_primalImprovingPivots; - - IntStat d_primalThresholdReachedPivot; - IntStat d_primalThresholdReachedPivot_dropped; - - IntStat d_primalReachedMaxPivots; - IntStat d_primalReachedMaxPivots_contractMadeProgress; - IntStat d_primalReachedMaxPivots_checkForConflictWorked; - - - IntStat d_primalGlobalMinimum; - IntStat d_primalGlobalMinimum_rowConflictWorked; - IntStat d_primalGlobalMinimum_firstHalfWasSat; - IntStat d_primalGlobalMinimum_firstHalfWasUnsat; - IntStat d_primalGlobalMinimum_contractMadeProgress; - - - IntStat d_unboundedFound; - IntStat d_unboundedFound_drive; - IntStat d_unboundedFound_dropped; - - - Statistics(); - ~Statistics(); - }; - - Statistics d_statistics; - -};/* class SimplexDecisionProcedure */ - -}/* CVC4::theory::arith namespace */ -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ - -#endif /* __CVC4__THEORY__ARITH__SIMPLEX_H */ - diff --git a/src/theory/arith/simplex.cpp b/src/theory/arith/simplex.cpp index 02e49258c..a160f4fe2 100644 --- a/src/theory/arith/simplex.cpp +++ b/src/theory/arith/simplex.cpp @@ -130,6 +130,8 @@ void SimplexDecisionProcedure::tearDownInfeasiblityFunction(TimerStat& timer, Ar Assert(tmp != ARITHVAR_SENTINEL); Assert(d_tableau.isBasic(tmp)); + RowIndex ri = d_tableau.basicToRowIndex(tmp); + d_linEq.stopTrackingRowIndex(ri); d_tableau.removeBasicRow(tmp); releaseVariable(tmp); } @@ -168,6 +170,12 @@ void SimplexDecisionProcedure::addToInfeasFunc(TimerStat& timer, ArithVar inf, A adjustInfeasFunc(timer, inf, justE); } +void SimplexDecisionProcedure::removeFromInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e){ + AVIntPairVec justE; + int opSgn = -d_errorSet.getSgn(e); + justE.push_back(make_pair(e, opSgn)); + adjustInfeasFunc(timer, inf, justE); +} ArithVar SimplexDecisionProcedure::constructInfeasiblityFunction(TimerStat& timer, const ArithVarVec& set){ TimerStat::CodeTimer codeTimer(timer); @@ -193,9 +201,10 @@ ArithVar SimplexDecisionProcedure::constructInfeasiblityFunction(TimerStat& time DeltaRational newAssignment = d_linEq.computeRowValue(inf, false); d_variables.setAssignment(inf, newAssignment); - d_linEq.trackVariable(inf); + //d_linEq.trackVariable(inf); + d_linEq.trackRowIndex(d_tableau.basicToRowIndex(inf)); - Debug("Inf") << inf << " " << newAssignment << endl; + Debug("constructInfeasiblityFunction") << inf << " " << newAssignment << endl; return inf; } @@ -226,7 +235,7 @@ void SimplexDecisionProcedure::addRowSgns(sgn_table& sgns, ArithVar basic, int n } } -ArithVar SimplexDecisionProcedure::find_basic_outside(const sgn_table& sgns, ArithVar col, int sgn, const DenseSet& m){ +ArithVar SimplexDecisionProcedure::find_basic_in_sgns(const sgn_table& sgns, ArithVar col, int sgn, const DenseSet& m, bool inside){ pair<ArithVar, int> p = make_pair(col, determinizeSgn(sgn)); sgn_table::const_iterator i = sgns.find(p); @@ -234,7 +243,7 @@ ArithVar SimplexDecisionProcedure::find_basic_outside(const sgn_table& sgns, Ari const ArithVarVec& vec = (*i).second; for(ArithVarVec::const_iterator viter = vec.begin(), vend = vec.end(); viter != vend; ++viter){ ArithVar curr = *viter; - if(!m.isMember(curr)){ + if(inside == m.isMember(curr)){ return curr; } } diff --git a/src/theory/arith/simplex.h b/src/theory/arith/simplex.h index bc47a128a..d646ca889 100644 --- a/src/theory/arith/simplex.h +++ b/src/theory/arith/simplex.h @@ -121,6 +121,7 @@ protected: void tearDownInfeasiblityFunction(TimerStat& timer, ArithVar inf); void adjustInfeasFunc(TimerStat& timer, ArithVar inf, const AVIntPairVec& focusChanges); void addToInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e); + void removeFromInfeasFunc(TimerStat& timer, ArithVar inf, ArithVar e); void shrinkInfeasFunc(TimerStat& timer, ArithVar inf, const ArithVarVec& dropped); public: @@ -197,7 +198,8 @@ protected: void addSgn(sgn_table& sgns, ArithVar col, int sgn, ArithVar basic); void addRowSgns(sgn_table& sgns, ArithVar basic, int norm); - ArithVar find_basic_outside(const sgn_table& sgns, ArithVar col, int sgn, const DenseSet& m); + ArithVar find_basic_in_sgns(const sgn_table& sgns, ArithVar col, int sgn, const DenseSet& m, bool inside); + sgn_table::const_iterator find_sgns(const sgn_table& sgns, ArithVar col, int sgn); };/* class SimplexDecisionProcedure */ diff --git a/src/theory/arith/simplex_update.h b/src/theory/arith/simplex_update.h index 516586568..64aa193dd 100644 --- a/src/theory/arith/simplex_update.h +++ b/src/theory/arith/simplex_update.h @@ -318,7 +318,7 @@ private: } /** - * Determines the appropraite WitnessImprovement for the update. + * Determines the appropriate WitnessImprovement for the update. * useBlands breaks ties for degenerate pivots. * * This is safe if: diff --git a/src/theory/arith/soi_simplex.cpp b/src/theory/arith/soi_simplex.cpp index 7255d92ef..c0ee7ad20 100644 --- a/src/theory/arith/soi_simplex.cpp +++ b/src/theory/arith/soi_simplex.cpp @@ -22,6 +22,8 @@ #include "util/statistics_registry.h" +#include <algorithm> + using namespace std; namespace CVC4 { @@ -237,7 +239,7 @@ UpdateInfo SumOfInfeasibilitiesSPD::selectUpdate(LinearEqualityModule::UpdatePre Debug("soi::selectPrimalUpdate") << "selectPrimalUpdate " << instance << endl << d_soiVar << " " << d_tableau.basicRowLength(d_soiVar) - << " " << d_linEq._countBounds(d_soiVar) << endl; + << " " << d_linEq.debugBasicAtBoundCount(d_soiVar) << endl; typedef std::vector<Cand> CandVector; CandVector candidates; @@ -284,7 +286,6 @@ UpdateInfo SumOfInfeasibilitiesSPD::selectUpdate(LinearEqualityModule::UpdatePre ArithVar curr = cand.d_nb; const Rational& coeff = *cand.d_coeff; -#warning "Who is using computeSafeUpdate?" LinearEqualityModule::UpdatePreferenceFunction leavingPrefFunc = selectLeavingFunction(curr); UpdateInfo currProposal = d_linEq.speculativeUpdate(curr, coeff, leavingPrefFunc); @@ -349,7 +350,7 @@ void SumOfInfeasibilitiesSPD::debugPrintSignal(ArithVar updated) const{ int dir = !d_variables.assignmentIsConsistent(updated) ? d_errorSet.getSgn(updated) : 0; Debug("updateAndSignal") << " dir " << dir; - Debug("updateAndSignal") << " _countBounds " << d_linEq._countBounds(updated) << endl; + Debug("updateAndSignal") << " debugBasicAtBoundCount " << d_linEq.debugBasicAtBoundCount(updated) << endl; } @@ -367,7 +368,7 @@ void SumOfInfeasibilitiesSPD::updateAndSignal(const UpdateInfo& selected, Witnes ArithVar leaving = selected.leaving(); ss << "leaving " << leaving << " " << d_tableau.basicRowLength(leaving) - << " " << d_linEq._countBounds(leaving) + << " " << d_linEq.debugBasicAtBoundCount(leaving) << endl; } if(degenerate(w) && selected.describesPivot()){ @@ -376,7 +377,7 @@ void SumOfInfeasibilitiesSPD::updateAndSignal(const UpdateInfo& selected, Witnes << "degenerate " << leaving << ", atBounds " << d_linEq.basicsAtBounds(selected) << ", len " << d_tableau.basicRowLength(leaving) - << ", bc " << d_linEq._countBounds(leaving) + << ", bc " << d_linEq.debugBasicAtBoundCount(leaving) << endl; } } @@ -433,6 +434,192 @@ void SumOfInfeasibilitiesSPD::updateAndSignal(const UpdateInfo& selected, Witnes adjustFocusAndError(selected, focusChanges); } +void SumOfInfeasibilitiesSPD::qeAddRange(uint32_t begin, uint32_t end){ + Assert(!d_qeInSoi.empty()); + for(uint32_t i = begin; i != end; ++i){ + ArithVar v = d_qeConflict[i]; + addToInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, v); + d_qeInSoi.add(v); + } +} + +void SumOfInfeasibilitiesSPD::qeRemoveRange(uint32_t begin, uint32_t end){ + for(uint32_t i = begin; i != end; ++i){ + ArithVar v = d_qeConflict[i]; + removeFromInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, v); + d_qeInSoi.remove(v); + } + Assert(!d_qeInSoi.empty()); +} + +void SumOfInfeasibilitiesSPD::qeSwapRange(uint32_t N, uint32_t r, uint32_t s){ + for(uint32_t i = 0; i < N; ++i){ + std::swap(d_qeConflict[r+i], d_qeConflict[s+i]); + } +} + +/** + * Region notation: + * A region is either + * - A single element X@i with the name X at the position i + * - A sequence of indices X@[i,j) with the name X and the elements between i [inclusive] and j exclusive + * - A concatenation of regions R1 and R2, R1;R2 + * + * Given the fixed assumptions C @ [0,cEnd) and a set of candidate minimizations U@[cEnd, uEnd) + * s.t. C \cup U is known to be in conflict ([0,uEnd) has a conflict), find a minimal + * subset of U, Delta, s.t. C \cup Delta is in conflict. + * + * Pre: + * [0, uEnd) is a set and is in conflict. + * uEnd <= assumptions.size() + * [0, cEnd) is in d_inSoi. + * + * Invariants: [0,cEnd) is never modified + * + * Post: + * [0, cEnd); [cEnd, deltaEnd) is in conflict + * [0, deltaEnd) is a set + * [0, deltaEnd) is in d_inSoi + */ +uint32_t SumOfInfeasibilitiesSPD::quickExplainRec(uint32_t cEnd, uint32_t uEnd){ + Assert(cEnd <= uEnd); + Assert(d_qeInUAndNotInSoi.empty()); + Assert(d_qeGreedyOrder.empty()); + + const Tableau::Entry* spoiler = NULL; + + if(d_soiVar != ARITHVAR_SENTINEL && d_linEq.selectSlackEntry(d_soiVar, false) == NULL){ + // already in conflict + return cEnd; + } + + Assert(cEnd < uEnd); + + // Phase 1 : Construct the conflict greedily + + for(uint32_t i = cEnd; i < uEnd; ++i){ + d_qeInUAndNotInSoi.add(d_qeConflict[i]); + } + if(d_soiVar == ARITHVAR_SENTINEL){ // special case for d_soiVar being empty + ArithVar first = d_qeConflict[cEnd]; + d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, first); + d_qeInSoi.add(first); + d_qeInUAndNotInSoi.remove(first); + d_qeGreedyOrder.push_back(first); + } + while((spoiler = d_linEq.selectSlackEntry(d_soiVar, false)) != NULL){ + Assert(!d_qeInUAndNotInSoi.empty()); + + ArithVar nb = spoiler->getColVar(); + int oppositeSgn = -(spoiler->getCoefficient().sgn()); + Assert(oppositeSgn != 0); + + ArithVar basicWithOp = find_basic_in_sgns(d_qeSgns, nb, oppositeSgn, d_qeInUAndNotInSoi, true); + Assert(basicWithOp != ARITHVAR_SENTINEL); + + addToInfeasFunc(d_statistics.d_soiConflictMinimization, d_soiVar, basicWithOp); + d_qeInSoi.add(basicWithOp); + d_qeInUAndNotInSoi.remove(basicWithOp); + d_qeGreedyOrder.push_back(basicWithOp); + } + Assert(spoiler == NULL); + + // Compact the set u + uint32_t newEnd = cEnd + d_qeGreedyOrder.size(); + std::copy(d_qeGreedyOrder.begin(), d_qeGreedyOrder.end(), d_qeConflict.begin()+cEnd); + + d_qeInUAndNotInSoi.purge(); + d_qeGreedyOrder.clear(); + + // Phase 2 : Recursively determine the minimal set of rows + + uint32_t xPos = cEnd; + std::swap(d_qeGreedyOrder[xPos], d_qeGreedyOrder[newEnd - 1]); + uint32_t uBegin = xPos + 1; + uint32_t split = (newEnd - uBegin)/2 + uBegin; + + //assumptions : C @ [0, cEnd); X @ xPos; U1 @ [u1Begin, split); U2 @ [split, newEnd) + // [0, newEnd) == d_inSoi + + uint32_t compactU2; + if(split == newEnd){ // U2 is empty + compactU2 = newEnd; + }else{ + // Remove U2 from Soi + qeRemoveRange(split, newEnd); + // [0, split) == d_inSoi + + // pre assumptions: C + X + U1 @ [0,split); U2 [split, newEnd) + compactU2 = quickExplainRec(split, newEnd); + // post: + // assumptions: C + X + U1 @ [0, split); delta2 @ [split - compactU2) + // d_inSoi = [0, compactU2) + } + uint32_t deltaSize = compactU2 - split; + qeSwapRange(deltaSize, uBegin, split); + uint32_t d2End = uBegin+deltaSize; + // assumptions : C @ [0, cEnd); X @ xPos; delta2 @ [uBegin, d2End); U1 @ [d2End, compactU2) + // d_inSoi == [0, compactU2) + + uint32_t d1End; + if(d2End == compactU2){ // U1 is empty + d1End = d2End; + }else{ + qeRemoveRange(d2End, compactU2); + + //pre assumptions : C + X + delta2 @ [0, d2End); U1 @ [d2End, compactU2); + d1End = quickExplainRec(d2End, compactU2); + //post: + // assumptions : C + X + delta2 @ [0, d2End); delta1 @ [d2End, d1End); + // d_inSoi = [0, d1End) + } + //After both: + // d_inSoi == [0, d1End), C @ [0, cEnd); X + delta2 + delta 1 @ [xPos, d1End); + + Assert(d_qeInUAndNotInSoi.empty()); + Assert(d_qeGreedyOrder.empty()); + return d1End; +} + +void SumOfInfeasibilitiesSPD::quickExplain(){ + Assert(d_qeInSoi.empty()); + Assert(d_qeInUAndNotInSoi.empty()); + Assert(d_qeGreedyOrder.empty()); + Assert(d_soiVar == ARITHVAR_SENTINEL); + Assert(d_qeSgns.empty()); + + d_qeConflict.clear(); + d_errorSet.pushFocusInto(d_qeConflict); + + //cout << d_qeConflict.size() << " "; + uint32_t size = d_qeConflict.size(); + + if(size > 2){ + for(ErrorSet::focus_iterator iter = d_errorSet.focusBegin(), end = d_errorSet.focusEnd(); iter != end; ++iter){ + ArithVar e = *iter; + addRowSgns(d_qeSgns, e, d_errorSet.getSgn(e)); + } + uint32_t end = quickExplainRec(0u, size); + Assert(end <= d_qeConflict.size()); + Assert(d_soiVar != ARITHVAR_SENTINEL); + Assert(!d_qeInSoi.empty()); + + d_qeConflict.resize(end); + tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar); + d_soiVar = ARITHVAR_SENTINEL; + d_qeInSoi.purge(); + d_qeSgns.clear(); + } + + //cout << d_qeConflict.size() << endl; + + Assert(d_qeInSoi.empty()); + Assert(d_qeInUAndNotInSoi.empty()); + Assert(d_qeGreedyOrder.empty()); + Assert(d_soiVar == ARITHVAR_SENTINEL); + Assert(d_qeSgns.empty()); +} + unsigned SumOfInfeasibilitiesSPD::trySet(const ArithVarVec& set){ Assert(d_soiVar == ARITHVAR_SENTINEL); bool success = false; @@ -446,30 +633,6 @@ unsigned SumOfInfeasibilitiesSPD::trySet(const ArithVarVec& set){ return success ? set.size() : std::numeric_limits<int>::max(); } -unsigned SumOfInfeasibilitiesSPD::tryAllSubsets(const ArithVarVec& set, unsigned depth, ArithVarVec& tmp) { - if(depth < set.size()){ - unsigned resWithout = tryAllSubsets(set, depth+1, tmp); - if(resWithout == tmp.size() && resWithout < set.size()){ - for(unsigned i = 0; i < tmp.size(); ++i){ - cout << tmp[i] << " "; - } - cout << endl; - } - tmp.push_back(set[depth]); - unsigned resWith = tryAllSubsets(set, depth+1, tmp); - if(resWith == tmp.size() && resWith < set.size()){ - for(unsigned i = 0; i < tmp.size(); ++i){ - cout << tmp[i] << " "; - } - cout << endl; - } - tmp.pop_back(); - return std::min(resWith, resWithout); - }else{ - return trySet(tmp); - } -} - std::vector< ArithVarVec > SumOfInfeasibilitiesSPD::greedyConflictSubsets(){ std::vector< ArithVarVec > subsets; Assert(d_soiVar == ARITHVAR_SENTINEL); @@ -547,8 +710,6 @@ std::vector< ArithVarVec > SumOfInfeasibilitiesSPD::greedyConflictSubsets(){ underConstruction.push_back(v); d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization, v); - bool uniqueChoices = true; - //cout << "trying " << v << endl; const Tableau::Entry* spoiler = NULL; @@ -559,7 +720,7 @@ std::vector< ArithVarVec > SumOfInfeasibilitiesSPD::greedyConflictSubsets(){ //cout << "looking for " << nb << " " << oppositeSgn << endl; - ArithVar basicWithOp = find_basic_outside(sgns, nb, oppositeSgn, hasParticipated); + ArithVar basicWithOp = find_basic_in_sgns(sgns, nb, oppositeSgn, hasParticipated, false); if(basicWithOp == ARITHVAR_SENTINEL){ //cout << "search did not work for " << nb << endl; @@ -648,17 +809,26 @@ WitnessImprovement SumOfInfeasibilitiesSPD::SOIConflict(){ tearDownInfeasiblityFunction(d_statistics.d_soiConflictMinimization, d_soiVar); d_soiVar = ARITHVAR_SENTINEL; - vector<ArithVarVec> subsets = greedyConflictSubsets(); - Assert( d_soiVar == ARITHVAR_SENTINEL); - 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); + if(options::soiQuickExplain()){ + quickExplain(); + Node conflict = generateSOIConflict(d_qeConflict); //cout << conflict << endl; - - //reportConflict(conf); do not do this. We need a custom explanations! d_conflictChannel(conflict); + }else{ + + vector<ArithVarVec> subsets = greedyConflictSubsets(); + Assert( d_soiVar == ARITHVAR_SENTINEL); + + 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); + //cout << conflict << endl; + + //reportConflict(conf); do not do this. We need a custom explanations! + d_conflictChannel(conflict); + } } Assert( d_soiVar == ARITHVAR_SENTINEL); d_soiVar = constructInfeasiblityFunction(d_statistics.d_soiConflictMinimization); @@ -703,7 +873,7 @@ WitnessImprovement SumOfInfeasibilitiesSPD::soiRound() { } bool SumOfInfeasibilitiesSPD::debugSOI(WitnessImprovement w, ostream& out, int instance) const{ -#warning "Redo SOI" +//#warning "Redo SOI" return true; // out << "DLV("<<instance<<") "; // switch(w){ diff --git a/src/theory/arith/soi_simplex.h b/src/theory/arith/soi_simplex.h index 006839a55..de565df64 100644 --- a/src/theory/arith/soi_simplex.h +++ b/src/theory/arith/soi_simplex.h @@ -195,6 +195,19 @@ private: IntStat& conflictStat = d_statistics.d_initialConflicts; return standardProcessSignals(timer, conflictStat); } + + void quickExplain(); + DenseSet d_qeInSoi; + DenseSet d_qeInUAndNotInSoi; + ArithVarVec d_qeConflict; + ArithVarVec d_qeGreedyOrder; + sgn_table d_qeSgns; + + uint32_t quickExplainRec(uint32_t cEnd, uint32_t uEnd); + void qeAddRange(uint32_t begin, uint32_t end); + void qeRemoveRange(uint32_t begin, uint32_t end); + void qeSwapRange(uint32_t N, uint32_t r, uint32_t s); + unsigned trySet(const ArithVarVec& set); unsigned tryAllSubsets(const ArithVarVec& set, unsigned depth, ArithVarVec& tmp); diff --git a/src/theory/arith/tableau.cpp b/src/theory/arith/tableau.cpp index c54b0857a..9d06fadc4 100644 --- a/src/theory/arith/tableau.cpp +++ b/src/theory/arith/tableau.cpp @@ -75,7 +75,7 @@ void Tableau::rowPivot(ArithVar basicOld, ArithVar basicNew, CoefficientChangeCa d_basic2RowIndex.set(basicNew, rid); d_rowIndex2basic.set(rid, basicNew); - cb.swap(basicOld, basicNew, a_rs_sgn); + cb.multiplyRow(rid, -a_rs_sgn); } diff --git a/src/theory/arith/tableau.h b/src/theory/arith/tableau.h index 8b6ef1df6..deed7e7be 100644 --- a/src/theory/arith/tableau.h +++ b/src/theory/arith/tableau.h @@ -72,8 +72,12 @@ public: return getColumn(x).begin(); } + RowIterator ridRowIterator(RowIndex rid) const { + return getRow(rid).begin(); + } + RowIterator basicRowIterator(ArithVar basic) const { - return getRow(basicToRowIndex(basic)).begin(); + return ridRowIterator(basicToRowIndex(basic)); } const Entry& basicFindEntry(ArithVar basic, ArithVar col) const { diff --git a/src/theory/arith/theory_arith.cpp b/src/theory/arith/theory_arith.cpp index c0442da90..6c7f622ec 100644 --- a/src/theory/arith/theory_arith.cpp +++ b/src/theory/arith/theory_arith.cpp @@ -47,6 +47,7 @@ void TheoryArith::addSharedTerm(TNode n){ } Node TheoryArith::ppRewrite(TNode atom) { + CodeTimer timer(d_ppRewriteTimer); return d_internal->ppRewrite(atom); } @@ -86,6 +87,10 @@ EqualityStatus TheoryArith::getEqualityStatus(TNode a, TNode b) { return d_internal->getEqualityStatus(a,b); } +Node TheoryArith::getModelValue(TNode var) { + return d_internal->getModelValue( var ); +} + }/* CVC4::theory::arith namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/arith/theory_arith.h b/src/theory/arith/theory_arith.h index 10c79b293..451f1e8ff 100644 --- a/src/theory/arith/theory_arith.h +++ b/src/theory/arith/theory_arith.h @@ -42,6 +42,7 @@ private: TheoryArithPrivate* d_internal; + KEEP_STATISTIC(TimerStat, d_ppRewriteTimer, "theory::arith::ppRewriteTimer"); public: TheoryArith(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe); @@ -73,6 +74,8 @@ public: EqualityStatus getEqualityStatus(TNode a, TNode b); void addSharedTerm(TNode n); + + Node getModelValue(TNode var); };/* class TheoryArith */ }/* CVC4::theory::arith namespace */ diff --git a/src/theory/arith/theory_arith_private.cpp b/src/theory/arith/theory_arith_private.cpp index 263f9536b..9d13dccb7 100644 --- a/src/theory/arith/theory_arith_private.cpp +++ b/src/theory/arith/theory_arith_private.cpp @@ -67,6 +67,8 @@ #include "theory/arith/options.h" +#include "theory/quantifiers/bounded_integers.h" + #include <stdint.h> #include <vector> @@ -83,12 +85,13 @@ namespace arith { TheoryArithPrivate::TheoryArithPrivate(TheoryArith& containing, context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) : d_containing(containing), d_nlIncomplete( false), - d_boundTracking(), + d_rowTracking(), d_constraintDatabase(c, u, d_partialModel, d_congruenceManager, RaiseConflict(*this)), d_qflraStatus(Result::SAT_UNKNOWN), d_unknownsInARow(0), d_hasDoneWorkSinceCut(false), d_learner(u), + d_quantEngine(qe), d_assertionsThatDoNotMatchTheirLiterals(c), d_nextIntegerCheckVar(0), d_constantIntegerVariables(c), @@ -96,9 +99,9 @@ TheoryArithPrivate::TheoryArithPrivate(TheoryArith& containing, context::Context d_currentPropagationList(), d_learnedBounds(c), d_partialModel(c, DeltaComputeCallback(*this)), - d_errorSet(d_partialModel, TableauSizes(&d_tableau), BoundCountingLookup(&d_boundTracking)), + d_errorSet(d_partialModel, TableauSizes(&d_tableau), BoundCountingLookup(*this)), d_tableau(), - d_linEq(d_partialModel, d_tableau, d_boundTracking, BasicVarModelUpdateCallBack(*this)), + d_linEq(d_partialModel, d_tableau, d_rowTracking, BasicVarModelUpdateCallBack(*this)), d_diosolver(c), d_restartsCounter(0), d_tableauSizeHasBeenModified(false), @@ -107,15 +110,19 @@ TheoryArithPrivate::TheoryArithPrivate(TheoryArith& containing, context::Context 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_pureUpdate(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_DELTA_ZERO(0), d_fullCheckCounter(0), d_cutCount(c, 0), d_cutInContext(c), + d_likelyIntegerInfeasible(c, false), + d_guessedCoeffSet(c, false), + d_guessedCoeffs(), d_statistics() { + srand(79); } TheoryArithPrivate::~TheoryArithPrivate(){ } @@ -489,9 +496,7 @@ bool TheoryArithPrivate::AssertUpper(Constraint constraint){ }else if(!lb->negationHasProof()){ Constraint negLb = lb->getNegation(); negLb->impliedBy(constraint, diseq); - //if(!negLb->canBePropagated()){ d_learnedBounds.push_back(negLb); - //}//otherwise let this be propagated/asserted later } } } @@ -732,19 +737,155 @@ void TheoryArithPrivate::addSharedTerm(TNode n){ } } +Node TheoryArithPrivate::getModelValue(TNode term) { + try{ + DeltaRational drv = getDeltaValue(term); + const Rational& delta = d_partialModel.getDelta(); + Rational qmodel = drv.substituteDelta( delta ); + return mkRationalNode( qmodel ); + } catch (DeltaRationalException& dr) { + return Node::null(); + } catch (ModelException& me) { + return Node::null(); + } +} + +namespace attr { + struct ToIntegerTag { }; + struct LinearIntDivTag { }; +}/* CVC4::theory::arith::attr namespace */ + +/** + * This attribute maps the child of a to_int / is_int to the + * corresponding integer skolem. + */ +typedef expr::Attribute<attr::ToIntegerTag, Node> ToIntegerAttr; + +/** + * This attribute maps division-by-constant-k terms to a variable + * used to eliminate them. + */ +typedef expr::Attribute<attr::LinearIntDivTag, Node> LinearIntDivAttr; + +Node TheoryArithPrivate::ppRewriteTerms(TNode n) { + if(Theory::theoryOf(n) != THEORY_ARITH) { + return n; + } + + NodeManager* nm = NodeManager::currentNM(); + + switch(Kind k = n.getKind()) { + + case kind::TO_INTEGER: + case kind::IS_INTEGER: { + Node intVar; + if(!n[0].getAttribute(ToIntegerAttr(), intVar)) { + intVar = nm->mkSkolem("toInt", nm->integerType(), "a conversion of a Real term to its Integer part"); + n[0].setAttribute(ToIntegerAttr(), intVar); + d_containing.d_out->lemma(nm->mkNode(kind::AND, nm->mkNode(kind::LT, nm->mkNode(kind::MINUS, n[0], nm->mkConst(Rational(1))), intVar), nm->mkNode(kind::LEQ, intVar, n[0]))); + } + if(n.getKind() == kind::TO_INTEGER) { + Node node = intVar; + return node; + } else { + Node node = nm->mkNode(kind::EQUAL, n[0], intVar); + return node; + } + Unreachable(); + } + + case kind::INTS_DIVISION: + case kind::INTS_DIVISION_TOTAL: { + if(!options::rewriteDivk()) { + return n; + } + Node num = Rewriter::rewrite(n[0]); + Node den = Rewriter::rewrite(n[1]); + if(den.isConst()) { + const Rational& rat = den.getConst<Rational>(); + Assert(!num.isConst()); + if(rat != 0) { + Node intVar; + Node rw = nm->mkNode(k, num, den); + if(!rw.getAttribute(LinearIntDivAttr(), intVar)) { + intVar = nm->mkSkolem("linearIntDiv", nm->integerType(), "the result of an intdiv-by-k term"); + rw.setAttribute(LinearIntDivAttr(), intVar); + if(rat > 0) { + d_containing.d_out->lemma(nm->mkNode(kind::AND, nm->mkNode(kind::LEQ, nm->mkNode(kind::MULT, den, intVar), num), nm->mkNode(kind::LT, num, nm->mkNode(kind::MULT, den, nm->mkNode(kind::PLUS, intVar, nm->mkConst(Rational(1))))))); + } else { + d_containing.d_out->lemma(nm->mkNode(kind::AND, nm->mkNode(kind::LEQ, nm->mkNode(kind::MULT, den, intVar), num), nm->mkNode(kind::LT, num, nm->mkNode(kind::MULT, den, nm->mkNode(kind::PLUS, intVar, nm->mkConst(Rational(-1))))))); + } + } + return intVar; + } + } + break; + } + + case kind::INTS_MODULUS: + case kind::INTS_MODULUS_TOTAL: { + if(!options::rewriteDivk()) { + return n; + } + Node num = Rewriter::rewrite(n[0]); + Node den = Rewriter::rewrite(n[1]); + if(den.isConst()) { + const Rational& rat = den.getConst<Rational>(); + Assert(!num.isConst()); + if(rat != 0) { + Node intVar; + Node rw = nm->mkNode(k, num, den); + if(!rw.getAttribute(LinearIntDivAttr(), intVar)) { + intVar = nm->mkSkolem("linearIntDiv", nm->integerType(), "the result of an intdiv-by-k term"); + rw.setAttribute(LinearIntDivAttr(), intVar); + if(rat > 0) { + d_containing.d_out->lemma(nm->mkNode(kind::AND, nm->mkNode(kind::LEQ, nm->mkNode(kind::MULT, den, intVar), num), nm->mkNode(kind::LT, num, nm->mkNode(kind::MULT, den, nm->mkNode(kind::PLUS, intVar, nm->mkConst(Rational(1))))))); + } else { + d_containing.d_out->lemma(nm->mkNode(kind::AND, nm->mkNode(kind::LEQ, nm->mkNode(kind::MULT, den, intVar), num), nm->mkNode(kind::LT, num, nm->mkNode(kind::MULT, den, nm->mkNode(kind::PLUS, intVar, nm->mkConst(Rational(-1))))))); + } + } + Node node = nm->mkNode(kind::MINUS, num, nm->mkNode(kind::MULT, den, intVar)); + return node; + } + } + break; + } + + default: + ; + } + + for(TNode::const_iterator i = n.begin(); i != n.end(); ++i) { + Node rewritten = ppRewriteTerms(*i); + if(rewritten != *i) { + NodeBuilder<> b(n.getKind()); + b.append(n.begin(), i); + b << rewritten; + for(++i; i != n.end(); ++i) { + b << ppRewriteTerms(*i); + } + rewritten = b; + return rewritten; + } + } + + return n; +} + Node TheoryArithPrivate::ppRewrite(TNode atom) { Debug("arith::preprocess") << "arith::preprocess() : " << atom << endl; - if (atom.getKind() == kind::EQUAL && options::arithRewriteEq()) { Node leq = NodeBuilder<2>(kind::LEQ) << atom[0] << atom[1]; Node geq = NodeBuilder<2>(kind::GEQ) << atom[0] << atom[1]; + leq = ppRewriteTerms(leq); + geq = ppRewriteTerms(geq); Node rewritten = Rewriter::rewrite(leq.andNode(geq)); Debug("arith::preprocess") << "arith::preprocess() : returning " << rewritten << endl; return rewritten; } else { - return atom; + return ppRewriteTerms(atom); } } @@ -897,7 +1038,7 @@ void TheoryArithPrivate::setupVariableList(const VarList& vl){ // vl is the product of at least 2 variables // vl : (* v1 v2 ...) if(getLogicInfo().isLinear()){ - throw LogicException("Non-linear term was asserted to arithmetic in a linear logic."); + throw LogicException("A non-linear fact was asserted to arithmetic in a linear logic."); } setIncomplete(); @@ -934,7 +1075,7 @@ void TheoryArithPrivate::setupDivLike(const Variable& v){ Assert(v.isDivLike()); if(getLogicInfo().isLinear()){ - throw LogicException("Non-linear term was asserted to arithmetic in a linear logic."); + throw LogicException("A non-linear fact (involving div/mod/divisibility) was asserted to arithmetic in a linear logic;\nif you only use division (or modulus) by a constant value, or if you only use the divisibility-by-k predicate, try using the --rewrite-divk option."); } Node vnode = v.getNode(); @@ -1100,7 +1241,7 @@ void TheoryArithPrivate::setupPolynomial(const Polynomial& poly) { ArithVar varSlack = requestArithVar(polyNode, true); d_tableau.addRow(varSlack, coefficients, variables); setupBasicValue(varSlack); - d_linEq.trackVariable(varSlack); + d_linEq.trackRowIndex(d_tableau.basicToRowIndex(varSlack)); //Add differences to the difference manager Polynomial::iterator i = poly.begin(), end = poly.end(); @@ -1156,16 +1297,22 @@ void TheoryArithPrivate::setupAtom(TNode atom) { void TheoryArithPrivate::preRegisterTerm(TNode n) { Debug("arith::preregister") <<"begin arith::preRegisterTerm("<< n <<")"<< endl; - if(isRelationOperator(n.getKind())){ - if(!isSetup(n)){ - setupAtom(n); + try { + if(isRelationOperator(n.getKind())){ + if(!isSetup(n)){ + setupAtom(n); + } + Constraint c = d_constraintDatabase.lookup(n); + Assert(c != NullConstraint); + + Debug("arith::preregister") << "setup constraint" << c << endl; + Assert(!c->canBePropagated()); + c->setPreregistered(); } - Constraint c = d_constraintDatabase.lookup(n); - Assert(c != NullConstraint); - - Debug("arith::preregister") << "setup constraint" << c << endl; - Assert(!c->canBePropagated()); - c->setPreregistered(); + } catch(LogicException& le) { + std::stringstream ss; + ss << le.getMessage() << endl << "The fact in question: " << n << endl; + throw LogicException(ss.str()); } Debug("arith::preregister") << "end arith::preRegisterTerm("<< n <<")" << endl; @@ -1176,82 +1323,33 @@ void TheoryArithPrivate::releaseArithVar(ArithVar v){ d_constraintDatabase.removeVariable(v); d_partialModel.releaseArithVar(v); - d_linEq.maybeRemoveTracking(v); } ArithVar TheoryArithPrivate::requestArithVar(TNode x, bool slack){ //TODO : The VarList trick is good enough? Assert(isLeaf(x) || VarList::isMember(x) || x.getKind() == PLUS); if(getLogicInfo().isLinear() && Variable::isDivMember(x)){ - throw LogicException("Non-linear term was asserted to arithmetic in a linear logic."); + stringstream ss; + ss << "A non-linear fact (involving div/mod/divisibility) was asserted to arithmetic in a linear logic: " << x << endl + << "if you only use division (or modulus) by a constant value, or if you only use the divisibility-by-k predicate, try using the --rewrite-divk option."; + throw LogicException(ss.str()); } Assert(!d_partialModel.hasArithVar(x)); Assert(x.getType().isReal()); // real or integer - // ArithVar varX = d_variables.size(); - // d_variables.push_back(Node(x)); - ArithVar max = d_partialModel.getNumberOfVariables(); ArithVar varX = d_partialModel.allocate(x, slack); bool reclaim = max >= d_partialModel.getNumberOfVariables();; - if(reclaim){ - // varX = d_pool.back(); - // d_pool.pop_back(); - - // d_partialModel.setAssignment(varX, d_DELTA_ZERO, d_DELTA_ZERO); - }else{ - // varX = d_numberOfVariables; - // ++d_numberOfVariables; - - // d_slackVars.push_back(true); - // d_variableTypes.push_back(ATReal); - + if(!reclaim){ d_dualSimplex.increaseMax(); d_tableau.increaseSize(); d_tableauSizeHasBeenModified = true; - - //d_partialModel.initialize(varX, d_DELTA_ZERO); } - - // ArithType type; - // if(slack){ - // //The type computation is not quite accurate for Rationals that are integral. - // //We'll use the isIntegral check from the polynomial package instead. - // Polynomial p = Polynomial::parsePolynomial(x); - // type = p.isIntegral() ? ATInteger : ATReal; - // }else{ - // type = nodeToArithType(x); - // } - // d_variableTypes[varX] = type; - // d_slackVars[varX] = slack; - d_constraintDatabase.addVariable(varX); - //d_partialModel.setArithVar(x,varX); - - // Debug("integers") << "isInteger[[" << x << "]]: " << x.getType().isInteger() << endl; - - // if(slack){ - // //The type computation is not quite accurate for Rationals that are integral. - // //We'll use the isIntegral check from the polynomial package instead. - // Polynomial p = Polynomial::parsePolynomial(x); - // d_variableTypes.push_back(p.isIntegral() ? ATInteger : ATReal); - // }else{ - // d_variableTypes.push_back(nodeToArithType(x)); - // } - - // d_slackVars.push_back(slack); - - // d_simplex.increaseMax(); - - // d_tableau.increaseSize(); - // d_tableauSizeHasBeenModified = true; - - // d_constraintDatabase.addVariable(varX); - Debug("arith::arithvar") << x << " |-> " << varX << endl; Assert(!d_partialModel.hasUpperBound(varX)); @@ -1423,6 +1521,10 @@ Constraint TheoryArithPrivate::constraintFromFactQueue(){ Assert(!done()); TNode assertion = get(); + if( options::finiteModelFind() && d_quantEngine && d_quantEngine->getBoundedIntegers() ){ + d_quantEngine->getBoundedIntegers()->assertNode(assertion); + } + Kind simpleKind = Comparison::comparisonKind(assertion); Constraint constraint = d_constraintDatabase.lookup(assertion); if(constraint == NullConstraint){ @@ -1454,20 +1556,6 @@ Constraint TheoryArithPrivate::constraintFromFactQueue(){ } } - // Kind simpleKind = Comparison::comparisonKind(assertion); - // Assert(simpleKind != UNDEFINED_KIND); - // Assert(constraint != NullConstraint || - // simpleKind == EQUAL || - // simpleKind == DISTINCT ); - // if(simpleKind == EQUAL || simpleKind == DISTINCT){ - // Node eq = (simpleKind == DISTINCT) ? assertion[0] : assertion; - - // if(!isSetup(eq)){ - // //The previous code was equivalent to: - // setupAtom(eq); - // constraint = d_constraintDatabase.lookup(assertion); - // } - // } Assert(constraint != NullConstraint); if(constraint->negationHasProof()){ @@ -1609,9 +1697,9 @@ void TheoryArithPrivate::branchVector(const std::vector<ArithVar>& lemmas){ bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){ Assert(d_qflraStatus != Result::SAT); - d_partialModel.stopQueueingAtBoundQueue(); + d_partialModel.stopQueueingBoundCounts(); UpdateTrackingCallback utcb(&d_linEq); - d_partialModel.processAtBoundQueue(utcb); + d_partialModel.processBoundsQueue(utcb); d_linEq.startTrackingBoundCounts(); bool noPivotLimit = Theory::fullEffort(effortLevel) || @@ -1641,13 +1729,23 @@ bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){ 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_qflraStatus == Result::SAT && !hasIntegerModel() && !d_likelyIntegerInfeasible)){ ApproximateSimplex* approxSolver = ApproximateSimplex::mkApproximateSimplexSolver(d_partialModel); approxSolver->setPivotLimit(relaxationLimit); + if(!d_guessedCoeffSet){ + d_guessedCoeffs = approxSolver->heuristicOptCoeffs(); + d_guessedCoeffSet = true; + } + if(!d_guessedCoeffs.empty()){ + approxSolver->setOptCoeffs(d_guessedCoeffs); + } + ApproximateSimplex::ApproxResult relaxRes, mipRes; ApproximateSimplex::Solution relaxSolution, mipSolution; relaxRes = approxSolver->solveRelaxation(); @@ -1655,30 +1753,37 @@ bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){ case ApproximateSimplex::ApproxSat: { relaxSolution = approxSolver->extractRelaxation(); - approxSolver->setPivotLimit(mipLimit); - mipRes = approxSolver->solveMIP(); - d_errorSet.reduceToSignals(); - if(mipRes == ApproximateSimplex::ApproxSat){ - mipSolution = approxSolver->extractMIP(); - ApproximateSimplex::applySolution(d_linEq, mipSolution); + + if(d_likelyIntegerInfeasible){ + d_qflraStatus = d_attemptSolSimplex.attempt(relaxSolution); }else{ - ApproximateSimplex::applySolution(d_linEq, relaxSolution); - // if(d_qflraStatus != UNSAT){ - // d_likelyIntegerUnsat = true; - // } + 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); + }else{ + if(mipRes == ApproximateSimplex::ApproxUnsat){ + d_likelyIntegerInfeasible = true; + } + d_qflraStatus = d_attemptSolSimplex.attempt(relaxSolution); + } } options::arithStandardCheckVarOrderPivots.set(pass2Limit); - d_qflraStatus = simplex.findModel(false); + if(d_qflraStatus != Result::UNSAT){ d_qflraStatus = simplex.findModel(false); } + //Message() << "done" << endl; } break; case ApproximateSimplex::ApproxUnsat: { ApproximateSimplex::Solution sol = approxSolver->extractRelaxation(); - d_errorSet.reduceToSignals(); - ApproximateSimplex::applySolution(d_linEq, sol); - options::arithStandardCheckVarOrderPivots.set(100); - d_qflraStatus = simplex.findModel(false); + d_qflraStatus = d_attemptSolSimplex.attempt(sol); + options::arithStandardCheckVarOrderPivots.set(pass2Limit); + + if(d_qflraStatus != Result::UNSAT){ d_qflraStatus = simplex.findModel(false); } } break; default: @@ -1688,11 +1793,14 @@ bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){ } 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); } } @@ -1701,7 +1809,7 @@ bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){ // TODO Save zeroes with no conflicts d_linEq.stopTrackingBoundCounts(); - d_partialModel.startQueueingAtBoundQueue(); + d_partialModel.startQueueingBoundCounts(); return emmittedConflictOrSplit; } @@ -1765,7 +1873,7 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ if(Debug.isOn("arith::print_assertions")) { - debugPrintAssertions(); + debugPrintAssertions(Debug("arith::print_assertions")); } bool emmittedConflictOrSplit = false; @@ -1911,7 +2019,6 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel)){ emmittedConflictOrSplit = splitDisequalities(); } - emmittedConflictOrSplit = false; if(!emmittedConflictOrSplit && Theory::fullEffort(effortLevel) && !hasIntegerModel()){ Node possibleConflict = Node::null(); @@ -1967,7 +2074,9 @@ void TheoryArithPrivate::check(Theory::Effort effortLevel){ } if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); } - if(Debug.isOn("arith::print_model")) { debugPrintModel(); } + if(Debug.isOn("arith::print_model")) { + debugPrintModel(Debug("arith::print_model")); + } Debug("arith") << "TheoryArithPrivate::check end" << std::endl; } @@ -2088,37 +2197,38 @@ bool TheoryArithPrivate::splitDisequalities(){ * Should be guarded by at least Debug.isOn("arith::print_assertions"). * Prints to Debug("arith::print_assertions") */ -void TheoryArithPrivate::debugPrintAssertions() { - Debug("arith::print_assertions") << "Assertions:" << endl; +void TheoryArithPrivate::debugPrintAssertions(std::ostream& out) const { + out << "Assertions:" << endl; 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); - Debug("arith::print_assertions") << lConstr << endl; + out << lConstr << endl; } if (d_partialModel.hasUpperBound(i)) { Constraint uConstr = d_partialModel.getUpperBoundConstraint(i); - Debug("arith::print_assertions") << uConstr << endl; + out << uConstr << endl; } } context::CDQueue<Constraint>::const_iterator it = d_diseqQueue.begin(); context::CDQueue<Constraint>::const_iterator it_end = d_diseqQueue.end(); for(; it != it_end; ++ it) { - Debug("arith::print_assertions") << *it << endl; + out << *it << endl; } } -void TheoryArithPrivate::debugPrintModel(){ - Debug("arith::print_model") << "Model:" << endl; +void TheoryArithPrivate::debugPrintModel(std::ostream& out) const{ + out << "Model:" << endl; for (var_iterator vi = var_begin(), vend = var_end(); vi != vend; ++vi){ ArithVar i = *vi; if(d_partialModel.hasNode(i)){ - Debug("arith::print_model") << d_partialModel.asNode(i) << " : " << + out << d_partialModel.asNode(i) << " : " << d_partialModel.getAssignment(i); - if(d_tableau.isBasic(i)) - Debug("arith::print_model") << " (basic)"; - Debug("arith::print_model") << endl; + if(d_tableau.isBasic(i)){ + out << " (basic)"; + } + out << endl; } } } @@ -2161,7 +2271,11 @@ void TheoryArithPrivate::propagate(Theory::Effort e) { (options::arithPropagationMode() == BOUND_INFERENCE_PROP || options::arithPropagationMode() == BOTH_PROP) && hasAnyUpdates()){ - propagateCandidates(); + if(options::newProp()){ + propagateCandidatesNew(); + }else{ + propagateCandidates(); + } }else{ clearUpdates(); } @@ -2432,37 +2546,6 @@ void TheoryArithPrivate::notifyRestart(){ if(Debug.isOn("paranoid:check_tableau")){ d_linEq.debugCheckTableau(); } ++d_restartsCounter; -#warning "removing restart" - // return; - - // uint32_t currSize = d_tableau.size(); - // uint32_t copySize = d_smallTableauCopy.size(); - - // Debug("arith::reset") << "resetting" << d_restartsCounter << endl; - // Debug("arith::reset") << "curr " << currSize << " copy " << copySize << endl; - // Debug("arith::reset") << "tableauSizeHasBeenModified " << d_tableauSizeHasBeenModified << endl; - - // if(d_tableauSizeHasBeenModified){ - // Debug("arith::reset") << "row has been added must copy " << d_restartsCounter << endl; - // d_smallTableauCopy = d_tableau; - // d_tableauSizeHasBeenModified = false; - // }else if( d_restartsCounter >= RESET_START){ - // if(copySize >= currSize * 1.1 ){ - // Debug("arith::reset") << "size has shrunk " << d_restartsCounter << endl; - // ++d_statistics.d_smallerSetToCurr; - // d_smallTableauCopy = d_tableau; - // }else if(d_tableauResetDensity * copySize <= currSize){ - // d_errorSet.popAllSignals(); - // if(safeToReset()){ - // Debug("arith::reset") << "resetting " << d_restartsCounter << endl; - // ++d_statistics.d_currSetToSmaller; - // d_tableau = d_smallTableauCopy; - // }else{ - // Debug("arith::reset") << "not safe to reset at the moment " << d_restartsCounter << endl; - // } - // } - // } - // Assert(unenqueuedVariablesAreConsistent()); } bool TheoryArithPrivate::entireStateIsConsistent(const string& s){ @@ -2478,6 +2561,14 @@ bool TheoryArithPrivate::entireStateIsConsistent(const string& s){ } Warning() << endl; result = false; + }else if(d_partialModel.isInteger(var) && !d_partialModel.integralAssignment(var)){ + d_partialModel.printModel(var); + Warning() << s << ":" << "Assignment is not integer for integer variable " << var << d_partialModel.asNode(var); + if(d_tableau.isBasic(var)){ + Warning() << " (basic)"; + } + Warning() << endl; + result = false; } } return result; @@ -2570,9 +2661,8 @@ EqualityStatus TheoryArithPrivate::getEqualityStatus(TNode a, TNode b) { bool TheoryArithPrivate::propagateCandidateBound(ArithVar basic, bool upperBound){ ++d_statistics.d_boundComputations; - DeltaRational bound = upperBound ? - d_linEq.computeUpperBound(basic): - d_linEq.computeLowerBound(basic); + RowIndex ridx = d_tableau.basicToRowIndex(basic); + DeltaRational bound = d_linEq.computeRowBound(ridx, upperBound, basic); if((upperBound && d_partialModel.strictlyLessThanUpperBound(basic, bound)) || (!upperBound && d_partialModel.strictlyGreaterThanLowerBound(basic, bound))){ @@ -2620,28 +2710,44 @@ bool TheoryArithPrivate::propagateCandidateBound(ArithVar basic, bool upperBound } if(!assertedToTheTheory && canBePropagated && !hasProof ){ - if(upperBound){ - Assert(bestImplied != d_partialModel.getUpperBoundConstraint(basic)); - d_linEq.propagateNonbasicsUpperBound(basic, bestImplied); - }else{ - Assert(bestImplied != d_partialModel.getLowerBoundConstraint(basic)); - d_linEq.propagateNonbasicsLowerBound(basic, bestImplied); - } + d_linEq.propagateBasicFromRow(bestImplied); // I think this can be skipped if canBePropagated is true //d_learnedBounds.push(bestImplied); + if(Debug.isOn("arith::prop")){ + Debug("arith::prop") << "success " << bestImplied << endl; + d_partialModel.printModel(basic, Debug("arith::prop")); + } return true; } + if(Debug.isOn("arith::prop")){ + Debug("arith::prop") << "failed " << basic << " " << bound << assertedToTheTheory << " " << + canBePropagated << " " << hasProof << endl; + d_partialModel.printModel(basic, Debug("arith::prop")); + } } + }else if(Debug.isOn("arith::prop")){ + Debug("arith::prop") << "false " << bound << " "; + d_partialModel.printModel(basic, Debug("arith::prop")); } return false; } void TheoryArithPrivate::propagateCandidate(ArithVar basic){ bool success = false; - if(d_partialModel.strictlyAboveLowerBound(basic) && d_linEq.hasLowerBounds(basic)){ + RowIndex ridx = d_tableau.basicToRowIndex(basic); + + bool tryLowerBound = + d_partialModel.strictlyAboveLowerBound(basic) && + d_linEq.rowLacksBound(ridx, false, basic) == NULL; + + bool tryUpperBound = + d_partialModel.strictlyBelowUpperBound(basic) && + d_linEq.rowLacksBound(ridx, true, basic) == NULL; + + if(tryLowerBound){ success |= propagateCandidateLowerBound(basic); } - if(d_partialModel.strictlyBelowUpperBound(basic) && d_linEq.hasUpperBounds(basic)){ + if(tryUpperBound){ success |= propagateCandidateUpperBound(basic); } if(success){ @@ -2652,6 +2758,8 @@ void TheoryArithPrivate::propagateCandidate(ArithVar basic){ void TheoryArithPrivate::propagateCandidates(){ TimerStat::CodeTimer codeTimer(d_statistics.d_boundComputationTime); + Debug("arith::prop") << "propagateCandidates begin" << endl; + Assert(d_candidateBasics.empty()); if(d_updatedBounds.empty()){ return; } @@ -2685,6 +2793,292 @@ void TheoryArithPrivate::propagateCandidates(){ Assert(d_tableau.isBasic(candidate)); propagateCandidate(candidate); } + Debug("arith::prop") << "propagateCandidates end" << endl << endl << endl; +} + +void TheoryArithPrivate::propagateCandidatesNew(){ + /* Four criteria must be met for progagation on a variable to happen using a row: + * 0: A new bound has to have been added to the row. + * 1: The hasBoundsCount for the row must be "full" or be full minus one variable + * (This is O(1) to check, but requires book keeping.) + * 2: The current assignment must be strictly smaller/greater than the current bound. + * assign(x) < upper(x) + * (This is O(1) to compute.) + * 3: There is a bound that is strictly smaller/greater than the current assignment. + * assign(x) < c for some x <= c literal + * (This is O(log n) to compute.) + * 4: The implied bound on x is strictly smaller/greater than the current bound. + * (This is O(n) to compute.) + */ + + TimerStat::CodeTimer codeTimer(d_statistics.d_boundComputationTime); + Debug("arith::prop") << "propagateCandidatesNew begin" << endl; + + Assert(d_qflraStatus == Result::SAT); + if(d_updatedBounds.empty()){ return; } + dumpUpdatedBoundsToRows(); + Assert(d_updatedBounds.empty()); + + if(!d_candidateRows.empty()){ + UpdateTrackingCallback utcb(&d_linEq); + d_partialModel.processBoundsQueue(utcb); + } + + while(!d_candidateRows.empty()){ + RowIndex candidate = d_candidateRows.back(); + d_candidateRows.pop_back(); + propagateCandidateRow(candidate); + } + Debug("arith::prop") << "propagateCandidatesNew end" << endl << endl << endl; +} + +bool TheoryArithPrivate::propagateMightSucceed(ArithVar v, bool ub) const{ + int cmp = ub ? d_partialModel.cmpAssignmentUpperBound(v) + : d_partialModel.cmpAssignmentLowerBound(v); + bool hasSlack = ub ? cmp < 0 : cmp > 0; + if(hasSlack){ + ConstraintType t = ub ? UpperBound : LowerBound; + const DeltaRational& a = d_partialModel.getAssignment(v); + + if(isInteger(v) && !a.isIntegral()){ + return true; + } + + Constraint strongestPossible = d_constraintDatabase.getBestImpliedBound(v, t, a); + if(strongestPossible == NullConstraint){ + return false; + }else{ + bool assertedToTheTheory = strongestPossible->assertedToTheTheory(); + bool canBePropagated = strongestPossible->canBePropagated(); + bool hasProof = strongestPossible->hasProof(); + + return !assertedToTheTheory && canBePropagated && !hasProof; + } + }else{ + return false; + } +} + +bool TheoryArithPrivate::attemptSingleton(RowIndex ridx, bool rowUp){ + Debug("arith::prop") << " attemptSingleton" << ridx; + + const Tableau::Entry* ep; + ep = d_linEq.rowLacksBound(ridx, rowUp, ARITHVAR_SENTINEL); + Assert(ep != NULL); + + ArithVar v = ep->getColVar(); + const Rational& coeff = ep->getCoefficient(); + + // 0 = c * v + \sum rest + // Suppose rowUp + // - c * v = \sum rest \leq D + // if c > 0, v \geq -D/c so !vUp + // if c < 0, v \leq -D/c so vUp + // Suppose not rowUp + // - c * v = \sum rest \geq D + // if c > 0, v \leq -D/c so vUp + // if c < 0, v \geq -D/c so !vUp + bool vUp = (rowUp == ( coeff.sgn() < 0)); + + Debug("arith::prop") << " " << rowUp << " " << v << " " << coeff << " " << vUp << endl; + Debug("arith::prop") << " " << propagateMightSucceed(v, vUp) << endl; + + if(propagateMightSucceed(v, vUp)){ + DeltaRational dr = d_linEq.computeRowBound(ridx, rowUp, v); + DeltaRational bound = dr / (- coeff); + return tryToPropagate(ridx, rowUp, v, vUp, bound); + } + return false; +} + +bool TheoryArithPrivate::attemptFull(RowIndex ridx, bool rowUp){ + Debug("arith::prop") << " attemptFull" << ridx << endl; + + vector<const Tableau::Entry*> candidates; + + for(Tableau::RowIterator i = d_tableau.ridRowIterator(ridx); !i.atEnd(); ++i){ + const Tableau::Entry& e =*i; + const Rational& c = e.getCoefficient(); + ArithVar v = e.getColVar(); + bool vUp = (rowUp == (c.sgn() < 0)); + if(propagateMightSucceed(v, vUp)){ + candidates.push_back(&e); + } + } + if(candidates.empty()){ return false; } + + const DeltaRational slack = + d_linEq.computeRowBound(ridx, rowUp, ARITHVAR_SENTINEL); + bool any = false; + vector<const Tableau::Entry*>::const_iterator i, iend; + for(i = candidates.begin(), iend = candidates.end(); i != iend; ++i){ + const Tableau::Entry* ep = *i; + const Rational& c = ep->getCoefficient(); + ArithVar v = ep->getColVar(); + + // See the comment for attemptSingleton() + bool activeUp = (rowUp == (c.sgn() > 0)); + bool vUb = (rowUp == (c.sgn() < 0)); + + const DeltaRational& activeBound = activeUp ? + d_partialModel.getUpperBound(v): + d_partialModel.getLowerBound(v); + + DeltaRational contribution = activeBound * c; + DeltaRational impliedBound = (slack - contribution)/(-c); + + bool success = tryToPropagate(ridx, rowUp, v, vUb, impliedBound); + any |= success; + } + return any; +} + +bool TheoryArithPrivate::tryToPropagate(RowIndex ridx, bool rowUp, ArithVar v, bool vUb, const DeltaRational& bound){ + + bool weaker = vUb ? d_partialModel.strictlyLessThanUpperBound(v, bound): + d_partialModel.strictlyGreaterThanLowerBound(v, bound); + if(weaker){ + ConstraintType t = vUb ? UpperBound : LowerBound; + + if(isInteger(v)){ + //cout << "maybe" << endl; + //cout << bound << endl; + } + Constraint implied = d_constraintDatabase.getBestImpliedBound(v, t, bound); + if(implied != NullConstraint){ + return rowImplicationCanBeApplied(ridx, rowUp, implied); + } + } + return false; +} + +Node flattenImplication(Node imp){ + NodeBuilder<> nb(kind::OR); + Node left = imp[0]; + Node right = imp[1]; + + if(left.getKind() == kind::AND){ + for(Node::iterator i = left.begin(), iend = left.end(); i != iend; ++i) { + nb << (*i).negate(); + } + }else{ + nb << left.negate(); + } + + if(right.getKind() == kind::OR){ + for(Node::iterator i = right.begin(), iend = right.end(); i != iend; ++i) { + nb << *i; + } + }else{ + nb << right; + } + + return nb; +} + +bool TheoryArithPrivate::rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, Constraint implied){ + Assert(implied != NullConstraint); + ArithVar v = implied->getVariable(); + + bool assertedToTheTheory = implied->assertedToTheTheory(); + bool canBePropagated = implied->canBePropagated(); + bool hasProof = implied->hasProof(); + + Debug("arith::prop") << "arith::prop" << v + << " " << assertedToTheTheory + << " " << canBePropagated + << " " << hasProof + << endl; + + if(implied->negationHasProof()){ + Warning() << "the negation of " << implied << " : " << endl + << "has proof " << implied->getNegation() << endl + << implied->getNegation()->explainForConflict() << endl; + } + + if(!assertedToTheTheory && canBePropagated && !hasProof ){ + vector<Constraint> explain; + d_linEq.propagateRow(explain, ridx, rowUp, implied); + if(d_tableau.getRowLength(ridx) <= options::arithPropAsLemmaLength()){ + Node implication = implied->makeImplication(explain); + Node clause = flattenImplication(implication); + outputLemma(clause); + }else{ + implied->impliedBy(explain); + } + return true; + } + + if(Debug.isOn("arith::prop")){ + Debug("arith::prop") + << "failed " << v << " " << assertedToTheTheory << " " + << canBePropagated << " " << hasProof << " " << implied << endl; + d_partialModel.printModel(v, Debug("arith::prop")); + } + return false; +} + +double fRand(double fMin, double fMax) +{ + double f = (double)rand() / RAND_MAX; + return fMin + f * (fMax - fMin); +} + +bool TheoryArithPrivate::propagateCandidateRow(RowIndex ridx){ + BoundCounts hasCount = d_linEq.hasBoundCount(ridx); + uint32_t rowLength = d_tableau.getRowLength(ridx); + + bool success = false; + static int instance = 0; + ++instance; + + Debug("arith::prop") + << "propagateCandidateRow " << instance << " attempt " << rowLength << " " << hasCount << endl; + + if(rowLength >= options::arithPropagateMaxLength()){ + if(fRand(0.0,1.0) >= double(options::arithPropagateMaxLength())/rowLength){ + return false; + } + } + + if(hasCount.lowerBoundCount() == rowLength){ + success |= attemptFull(ridx, false); + }else if(hasCount.lowerBoundCount() + 1 == rowLength){ + success |= attemptSingleton(ridx, false); + } + + if(hasCount.upperBoundCount() == rowLength){ + success |= attemptFull(ridx, true); + }else if(hasCount.upperBoundCount() + 1 == rowLength){ + success |= attemptSingleton(ridx, true); + } + return success; +} + +void TheoryArithPrivate::dumpUpdatedBoundsToRows(){ + Assert(d_candidateRows.empty()); + DenseSet::const_iterator i = d_updatedBounds.begin(); + DenseSet::const_iterator end = d_updatedBounds.end(); + for(; i != end; ++i){ + ArithVar var = *i; + if(d_tableau.isBasic(var)){ + RowIndex ridx = d_tableau.basicToRowIndex(var); + d_candidateRows.softAdd(ridx); + }else{ + Tableau::ColIterator basicIter = d_tableau.colIterator(var); + for(; !basicIter.atEnd(); ++basicIter){ + const Tableau::Entry& entry = *basicIter; + RowIndex ridx = entry.getRowIndex(); + d_candidateRows.softAdd(ridx); + } + } + } + d_updatedBounds.purge(); +} + +const BoundsInfo& TheoryArithPrivate::boundsInfo(ArithVar basic) const{ + RowIndex ridx = d_tableau.basicToRowIndex(basic); + return d_rowTracking[ridx]; } }/* CVC4::theory::arith namespace */ diff --git a/src/theory/arith/theory_arith_private.h b/src/theory/arith/theory_arith_private.h index 7b37a813f..22fc8d4a7 100644 --- a/src/theory/arith/theory_arith_private.h +++ b/src/theory/arith/theory_arith_private.h @@ -58,7 +58,7 @@ #include "theory/arith/dual_simplex.h" #include "theory/arith/fc_simplex.h" #include "theory/arith/soi_simplex.h" -#include "theory/arith/pure_update_simplex.h" +#include "theory/arith/attempt_solution_simplex.h" #include "theory/arith/constraint.h" @@ -105,7 +105,7 @@ private: // TODO A better would be: //context::CDO<bool> d_nlIncomplete; - BoundCountingVector d_boundTracking; + BoundInfoMap d_rowTracking; /** * The constraint database associated with the theory. @@ -132,7 +132,8 @@ private: /** Static learner. */ ArithStaticLearner d_learner; - + /** quantifiers engine */ + QuantifiersEngine * d_quantEngine; //std::vector<ArithVar> d_pool; public: void releaseArithVar(ArithVar v); @@ -316,9 +317,9 @@ private: /** This implements the Simplex decision procedure. */ DualSimplexDecisionProcedure d_dualSimplex; - PureUpdateSimplexDecisionProcedure d_pureUpdate; FCSimplexDecisionProcedure d_fcSimplex; SumOfInfeasibilitiesSPD d_soiSimplex; + AttemptSolutionSDP d_attemptSolSimplex; bool solveRealRelaxation(Theory::Effort effortLevel); @@ -345,7 +346,8 @@ private: Node axiomIteForTotalDivision(Node div_tot); Node axiomIteForTotalIntDivision(Node int_div_like); - + // handle linear /, div, mod, and also is_int, to_int + Node ppRewriteTerms(TNode atom); public: TheoryArithPrivate(TheoryArith& containing, context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe); @@ -381,6 +383,8 @@ public: void addSharedTerm(TNode n); + Node getModelValue(TNode var); + private: /** The constant zero. */ @@ -430,6 +434,10 @@ public: */ ArithVar requestArithVar(TNode x, bool slack); +public: + const BoundsInfo& boundsInfo(ArithVar basic) const; + + private: /** Initial (not context dependent) sets up for a variable.*/ void setupBasicValue(ArithVar x); @@ -463,12 +471,25 @@ private: /** Tracks the basic variables where propagation might be possible. */ DenseSet d_candidateBasics; + DenseSet d_candidateRows; bool hasAnyUpdates() { return !d_updatedBounds.empty(); } void clearUpdates(); void revertOutOfConflict(); + void propagateCandidatesNew(); + void dumpUpdatedBoundsToRows(); + bool propagateCandidateRow(RowIndex rid); + bool propagateMightSucceed(ArithVar v, bool ub) const; + /** Attempt to perform a row propagation where there is at most 1 possible variable.*/ + bool attemptSingleton(RowIndex ridx, bool rowUp); + /** 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); + + void propagateCandidates(); void propagateCandidate(ArithVar basic); bool propagateCandidateBound(ArithVar basic, bool upperBound); @@ -517,9 +538,9 @@ private: std::vector<ArithVar>& variables); /** Routine for debugging. Print the assertions the theory is aware of. */ - void debugPrintAssertions(); + void debugPrintAssertions(std::ostream& out) const; /** Debugging only routine. Prints the model. */ - void debugPrintModel(); + void debugPrintModel(std::ostream& out) const; inline LogicInfo getLogicInfo() const { return d_containing.getLogicInfo(); } inline bool done() const { return d_containing.done(); } @@ -553,6 +574,12 @@ private: context::CDO<unsigned> d_cutCount; context::CDHashSet<ArithVar, std::hash<ArithVar> > d_cutInContext; + context::CDO<bool> d_likelyIntegerInfeasible; + + + context::CDO<bool> d_guessedCoeffSet; + ArithRatPairVec d_guessedCoeffs; + /** These fields are designed to be accessible to TheoryArith methods. */ class Statistics { public: diff --git a/src/theory/arith/theory_arith_type_rules.h b/src/theory/arith/theory_arith_type_rules.h index cc8451f8b..45e18fe0d 100644 --- a/src/theory/arith/theory_arith_type_rules.h +++ b/src/theory/arith/theory_arith_type_rules.h @@ -62,12 +62,37 @@ public: } } } - Kind k = n.getKind(); - bool isDivision = k == kind::DIVISION || k == kind::DIVISION_TOTAL; - return (isInteger && !isDivision ? integerType : realType); + switch(Kind k = n.getKind()) { + case kind::TO_REAL: + return realType; + case kind::TO_INTEGER: + return integerType; + default: { + bool isDivision = k == kind::DIVISION || k == kind::DIVISION_TOTAL; + return (isInteger && !isDivision ? integerType : realType); + } + } } };/* class ArithOperatorTypeRule */ +class IntOperatorTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + TNode::iterator child_it = n.begin(); + TNode::iterator child_it_end = n.end(); + if(check) { + for(; child_it != child_it_end; ++child_it) { + TypeNode childType = (*child_it).getType(check); + if (!childType.isInteger()) { + throw TypeCheckingExceptionPrivate(n, "expecting an integer subterm"); + } + } + } + return nodeManager->integerType(); + } +};/* class IntOperatorTypeRule */ + class ArithPredicateTypeRule { public: inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) @@ -87,6 +112,34 @@ public: } };/* class ArithPredicateTypeRule */ +class ArithUnaryPredicateTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + if( check ) { + TypeNode t = n[0].getType(check); + if (!t.isReal()) { + throw TypeCheckingExceptionPrivate(n, "expecting an arithmetic term"); + } + } + return nodeManager->booleanType(); + } +};/* class ArithUnaryPredicateTypeRule */ + +class IntUnaryPredicateTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + if( check ) { + TypeNode t = n[0].getType(check); + if (!t.isInteger()) { + throw TypeCheckingExceptionPrivate(n, "expecting an integer term"); + } + } + return nodeManager->booleanType(); + } +};/* class IntUnaryPredicateTypeRule */ + class SubrangeProperties { public: inline static Cardinality computeCardinality(TypeNode type) { diff --git a/src/theory/arrays/Makefile.am b/src/theory/arrays/Makefile.am index ec834522f..77f102cf8 100644 --- a/src/theory/arrays/Makefile.am +++ b/src/theory/arrays/Makefile.am @@ -16,9 +16,7 @@ libarrays_la_SOURCES = \ array_info.h \ array_info.cpp \ static_fact_manager.h \ - static_fact_manager.cpp \ - theory_arrays_model.h \ - theory_arrays_model.cpp + static_fact_manager.cpp EXTRA_DIST = \ kinds diff --git a/src/theory/arrays/theory_arrays.cpp b/src/theory/arrays/theory_arrays.cpp index 801893107..98346d0e3 100644 --- a/src/theory/arrays/theory_arrays.cpp +++ b/src/theory/arrays/theory_arrays.cpp @@ -21,7 +21,6 @@ #include <map> #include "theory/rewriter.h" #include "expr/command.h" -#include "theory/arrays/theory_arrays_model.h" #include "theory/model.h" #include "theory/arrays/options.h" #include "smt/logic_exception.h" @@ -464,7 +463,9 @@ void TheoryArrays::preRegisterTermInternal(TNode node) } case kind::STORE: { // Invariant: array terms should be preregistered before being added to the equality engine - Assert(!d_equalityEngine.hasTerm(node)); + if (d_equalityEngine.hasTerm(node)) { + break; + } d_equalityEngine.addTriggerTerm(node, THEORY_ARRAY); TNode a = d_equalityEngine.getRepresentative(node[0]); @@ -493,7 +494,7 @@ void TheoryArrays::preRegisterTermInternal(TNode node) } case kind::STORE_ALL: { throw LogicException("Array theory solver does not yet support assertions using constant array value"); - } + } default: // Variables etc if (node.getType().isArray()) { @@ -588,7 +589,10 @@ void TheoryArrays::computeCareGraph() } } } - if (options::arraysModelBased()) return; + if (options::arraysModelBased()) { + checkModel(EFFORT_COMBINATION); + return; + } if (d_sharedTerms) { vector< pair<TNode, TNode> > currentPairs; @@ -1009,7 +1013,7 @@ void TheoryArrays::checkModel(Effort e) Assert(d_skolemAssertions.empty()); Assert(d_lemmas.empty()); - if (fullEffort(e)) { + if (combination(e)) { // Add constraints for shared terms context::CDList<TNode>::const_iterator shared_it = shared_terms_begin(), shared_it_end = shared_terms_end(), shared_it2; Node modelVal, modelVal2, d; @@ -1061,9 +1065,10 @@ void TheoryArrays::checkModel(Effort e) unsigned constraintIdx; Node assertion, assertionToCheck; vector<TNode> assumptions; - // int numrestarts = 0; - while (true) { - // ++numrestarts; + int numrestarts = 0; + while (true || numrestarts < 1 || fullEffort(e) || combination(e)) { + ++numrestarts; + d_out->safePoint(); int level = getSatContext()->getLevel(); d_getModelValCache.clear(); for (constraintIdx = 0; constraintIdx < d_modelConstraints.size(); ++constraintIdx) { @@ -1076,7 +1081,7 @@ void TheoryArrays::checkModel(Effort e) if (constraintIdx == d_modelConstraints.size()) { break; } - + if (assertion.getKind() == kind::EQUAL && assertion[0].getType().isArray()) { assertionToCheck = solveWrite(expandStores(assertion[0], assumptions).eqNode(expandStores(assertion[1], assumptions)), true, true, false); if (assertionToCheck.getKind() == kind::AND && @@ -1202,20 +1207,20 @@ void TheoryArrays::checkModel(Effort e) } { // generate lemma - // if (all.size() == 0) { - // d_lemmas.push_back(decision.negate()); - // } - // else { - // NodeBuilder<> disjunction(kind::OR); - // std::set<TNode>::const_iterator it = all.begin(); - // std::set<TNode>::const_iterator it_end = all.end(); - // while (it != it_end) { - // disjunction << (*it).negate(); - // ++it; - // } - // disjunction << decision.negate(); - // d_lemmas.push_back(disjunction); - // } + if (all.size() == 0) { + d_lemmas.push_back(decision.negate()); + } + else { + NodeBuilder<> disjunction(kind::OR); + std::set<TNode>::const_iterator it = all.begin(); + std::set<TNode>::const_iterator it_end = all.end(); + while (it != it_end) { + disjunction << (*it).negate(); + ++it; + } + disjunction << decision.negate(); + d_lemmas.push_back(disjunction); + } } d_equalityEngine.assertEquality(decision, eq, explanation); if (!eq) decision = decision.notNode(); @@ -1268,6 +1273,13 @@ void TheoryArrays::checkModel(Effort e) } d_skolemIndex = d_skolemIndex + 1; } + // Reregister stores + if (assertionToCheck != assertion && + assertionToCheck.getKind() == kind::AND && + assertionToCheck[assertionToCheck.getNumChildren()-1].getKind() == kind::EQUAL) { + TNode s = assertionToCheck[assertionToCheck.getNumChildren()-1][0]; + preRegisterStores(s); + } } if (d_conflict) { break; @@ -1294,10 +1306,10 @@ void TheoryArrays::checkModel(Effort e) d_skolemIndex = 0; while (!d_lemmas.empty()) { Debug("arrays-model-based") << "Sending lemma: " << d_lemmas.back() << endl; - d_out->lemma(d_lemmas.back()); + d_out->splitLemma(d_lemmas.back()); #ifdef CVC4_ASSERTIONS - Assert(d_lemmasSaved.find(d_lemmas.back()) == d_lemmasSaved.end()); - d_lemmasSaved.insert(d_lemmas.back()); + // Assert(d_lemmasSaved.find(d_lemmas.back()) == d_lemmasSaved.end()); + // d_lemmasSaved.insert(d_lemmas.back()); #endif d_lemmas.pop_back(); } @@ -1416,7 +1428,7 @@ Node TheoryArrays::getModelValRec(TNode node) } ++d_numGetModelValConflicts; getSatContext()->pop(); - } + } ++te; if (te.isFinished()) { Assert(false); @@ -1453,7 +1465,7 @@ bool TheoryArrays::hasLoop(TNode node, TNode target) return true; } } - + return false; } @@ -1633,9 +1645,14 @@ bool TheoryArrays::setModelVal(TNode node, TNode val, bool invert, bool explain, return true; } } - getSatContext()->push(); Node d = node.eqNode(val); - d_decisions.push_back(invert ? d.notNode() : d); + Node r = Rewriter::rewrite(d); + if (r.isConst()) { + d_equalityEngine.assertEquality(d, r == d_true, d_true); + return ((r == d_true) == (!invert)); + } + getSatContext()->push(); + d_decisions.push_back(invert ? d.negate() : d); d_equalityEngine.assertEquality(d, !invert, d_decisions.back()); Debug("arrays-model-based") << "Asserting " << d_decisions.back() << " with explanation " << d_decisions.back() << endl; ++d_numSetModelValSplits; @@ -1667,7 +1684,7 @@ bool TheoryArrays::setModelVal(TNode node, TNode val, bool invert, bool explain, d_decisions.pop_back(); d_permRef.push_back(explanation); d = d.negate(); - Debug("arrays-model-based") << "Asserting learned literal " << d << " with explanation " << explanation << endl; + Debug("arrays-model-based") << "Asserting learned literal2 " << d << " with explanation " << explanation << endl; bool eq = true; if (d.getKind() == kind::NOT) { d = d[0]; diff --git a/src/theory/arrays/theory_arrays_model.cpp b/src/theory/arrays/theory_arrays_model.cpp deleted file mode 100644 index b5c81ef69..000000000 --- a/src/theory/arrays/theory_arrays_model.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/********************* */ -/*! \file theory_arrays_model.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_arrays_model class - **/ - -#include "theory/theory_engine.h" -#include "theory/arrays/theory_arrays_model.h" -#include "theory/model.h" - -using namespace std; -using namespace CVC4; -using namespace CVC4::kind; -using namespace CVC4::context; -using namespace CVC4::theory; -using namespace CVC4::theory::arrays; - -ArrayModel::ArrayModel( Node arr, TheoryModel* m ) : d_arr( arr ){ - d_base_arr = arr; - while( d_base_arr.getKind()==STORE ){ - Node ri = m->getRepresentative( d_base_arr[1] ); - if( d_values.find( ri )==d_values.end() ){ - d_values[ ri ] = m->getRepresentative( d_base_arr[2] ); - } - d_base_arr = d_base_arr[0]; - } -} - -Node ArrayModel::getValue( TheoryModel* m, Node i ){ - i = m->getRepresentative( i ); - std::map< Node, Node >::iterator it = d_values.find( i ); - if( it!=d_values.end() ){ - return it->second; - }else{ - return NodeManager::currentNM()->mkNode( SELECT, getArrayValue(), i ); - //return d_default_value; //TODO: guarantee I can return this here - } -} - -void ArrayModel::setValue( TheoryModel* m, Node i, Node e ){ - Node ri = m->getRepresentative( i ); - if( d_values.find( ri )==d_values.end() ){ - d_values[ ri ] = m->getRepresentative( e ); - } -} - -void ArrayModel::setDefaultArray( Node arr ){ - d_base_arr = arr; -} - -Node ArrayModel::getArrayValue(){ - Node curr = d_base_arr; - for( std::map< Node, Node >::iterator it = d_values.begin(); it != d_values.end(); ++it ){ - curr = NodeManager::currentNM()->mkNode( STORE, curr, it->first, it->second ); - } - return curr; -} diff --git a/src/theory/arrays/theory_arrays_model.h b/src/theory/arrays/theory_arrays_model.h deleted file mode 100644 index 66dc80568..000000000 --- a/src/theory/arrays/theory_arrays_model.h +++ /dev/null @@ -1,58 +0,0 @@ -/********************* */ -/*! \file theory_arrays_model.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 MODEL for theory of arrays - **/ - - -#include "cvc4_private.h" - -#ifndef __CVC4__THEORY_ARRAYS_MODEL_H -#define __CVC4__THEORY_ARRAYS_MODEL_H - -#include "theory/quantifiers_engine.h" - -namespace CVC4 { -namespace theory { - -class TheoryModel; - -namespace arrays { - -class ArrayModel{ -protected: - /** the array this model is for */ - Node d_arr; -public: - ArrayModel(){} - ArrayModel( Node arr, TheoryModel* m ); - ~ArrayModel() {} -public: - /** pre-defined values */ - std::map< Node, Node > d_values; - /** base array */ - Node d_base_arr; - /** get value, return arguments that the value depends on */ - Node getValue( TheoryModel* m, Node i ); - /** set value */ - void setValue( TheoryModel* m, Node i, Node e ); - /** set default */ - void setDefaultArray( Node arr ); -public: - /** get array value */ - Node getArrayValue(); -};/* class ArrayModel */ - -} -} -} - -#endif
\ No newline at end of file diff --git a/src/theory/arrays/theory_arrays_rewriter.h b/src/theory/arrays/theory_arrays_rewriter.h index 18bbef8cf..5df06bda8 100644 --- a/src/theory/arrays/theory_arrays_rewriter.h +++ b/src/theory/arrays/theory_arrays_rewriter.h @@ -36,6 +36,10 @@ namespace attr { typedef expr::Attribute<attr::ArrayConstantMostFrequentValueCountTag, uint64_t> ArrayConstantMostFrequentValueCountAttr; typedef expr::Attribute<attr::ArrayConstantMostFrequentValueTag, Node> ArrayConstantMostFrequentValueAttr; +static inline Node mkEqNode(Node a, Node b) { + return a.getType().isBoolean() ? a.iffNode(b) : a.eqNode(b); +} + class TheoryArraysRewriter { static Node normalizeConstant(TNode node) { return normalizeConstant(node, node[1].getType().getCardinality()); @@ -244,7 +248,7 @@ public: val = false; } else { - n = Rewriter::rewrite(store[1].eqNode(index)); + n = Rewriter::rewrite(mkEqNode(store[1], index)); if (n.getKind() != kind::CONST_BOOLEAN) { break; } @@ -301,7 +305,7 @@ public: val = false; } else { - Node eqRewritten = Rewriter::rewrite(store[1].eqNode(index)); + Node eqRewritten = Rewriter::rewrite(mkEqNode(store[1], index)); if (eqRewritten.getKind() != kind::CONST_BOOLEAN) { Trace("arrays-postrewrite") << "Arrays::postRewrite returning " << node << std::endl; return RewriteResponse(REWRITE_DONE, node); @@ -340,7 +344,7 @@ public: val = false; } else { - n = Rewriter::rewrite(store[1].eqNode(index)); + n = Rewriter::rewrite(mkEqNode(store[1], index)); if (n.getKind() != kind::CONST_BOOLEAN) { break; } @@ -416,7 +420,7 @@ public: val = false; } else { - n = Rewriter::rewrite(store[1].eqNode(index)); + n = Rewriter::rewrite(mkEqNode(store[1], index)); if (n.getKind() != kind::CONST_BOOLEAN) { break; } @@ -466,7 +470,7 @@ public: val = false; } else { - Node eqRewritten = Rewriter::rewrite(store[1].eqNode(index)); + Node eqRewritten = Rewriter::rewrite(mkEqNode(store[1], index)); if (eqRewritten.getKind() != kind::CONST_BOOLEAN) { break; } diff --git a/src/theory/atom_requests.cpp b/src/theory/atom_requests.cpp new file mode 100644 index 000000000..3d111f9f8 --- /dev/null +++ b/src/theory/atom_requests.cpp @@ -0,0 +1,62 @@ +#include "theory/atom_requests.h" + +using namespace CVC4; + +AtomRequests::AtomRequests(context::Context* context) +: d_allRequests(context) +, d_requests(context) +, d_triggerToRequestMap(context) +{} + +AtomRequests::element_index AtomRequests::getList(TNode trigger) const { + trigger_to_list_map::const_iterator find = d_triggerToRequestMap.find(trigger); + if (find == d_triggerToRequestMap.end()) { + return null_index; + } else { + return (*find).second; + } +} + +bool AtomRequests::isTrigger(TNode atom) const { + return getList(atom) != null_index; +} + +AtomRequests::atom_iterator AtomRequests::getAtomIterator(TNode trigger) const { + return atom_iterator(*this, getList(trigger)); +} + +void AtomRequests::add(TNode triggerAtom, TNode atomToSend, theory::TheoryId toTheory) { + + Debug("theory::atoms") << "AtomRequests::add(" << triggerAtom << ", " << atomToSend << ", " << toTheory << ")" << std::endl; + + Request request(atomToSend, toTheory); + + if (d_allRequests.find(request) != d_allRequests.end()) { + // Have it already + Debug("theory::atoms") << "AtomRequests::add(" << triggerAtom << ", " << atomToSend << ", " << toTheory << "): already there" << std::endl; + return; + } + Debug("theory::atoms") << "AtomRequests::add(" << triggerAtom << ", " << atomToSend << ", " << toTheory << "): adding" << std::endl; + + /// Mark the new request + d_allRequests.insert(request); + + // Index of the new request in the list of trigger + element_index index = d_requests.size(); + element_index previous = getList(triggerAtom); + d_requests.push_back(Element(request, previous)); + d_triggerToRequestMap[triggerAtom] = index; +} + +bool AtomRequests::atom_iterator::done() const { + return index == null_index; +} + +void AtomRequests::atom_iterator::next() { + index = requests.d_requests[index].previous; +} + +const AtomRequests::Request& AtomRequests::atom_iterator::get() const { + return requests.d_requests[index].request; +} + diff --git a/src/theory/atom_requests.h b/src/theory/atom_requests.h new file mode 100644 index 000000000..99878125a --- /dev/null +++ b/src/theory/atom_requests.h @@ -0,0 +1,107 @@ +#pragma once + +#include "expr/node.h" +#include "theory/theory.h" +#include "context/cdlist.h" +#include "context/cdhashset.h" +#include "context/cdhashmap.h" + +namespace CVC4 { + +class AtomRequests { + +public: + + /** Which atom and where to send it */ + struct Request { + /** Atom */ + Node atom; + /** Where to send it */ + theory::TheoryId toTheory; + + Request(TNode atom, theory::TheoryId toTheory) + : atom(atom), toTheory(toTheory) {} + Request() + : toTheory(theory::THEORY_LAST) + {} + + bool operator == (const Request& other) const { + return atom == other.atom && toTheory == other.toTheory; + } + + size_t hash() const { + return atom.getId(); + } + + }; + + AtomRequests(context::Context* context); + + /** Mark the atom to be sent to a theory, when the trigger atom gets assigned */ + void add(TNode triggerAtom, TNode atomToSend, theory::TheoryId toTheory); + + /** Returns true if the node is a trigger and has a list of atoms to send */ + bool isTrigger(TNode atom) const; + + /** Indices in lists */ + typedef size_t element_index; + + class atom_iterator { + const AtomRequests& requests; + element_index index; + friend class AtomRequests; + atom_iterator(const AtomRequests& requests, element_index start) + : requests(requests), index(start) {} + public: + /** Is this iterator done */ + bool done() const; + /** Go to the next element */ + void next(); + /** Get the actual request */ + const Request& get() const; + }; + + atom_iterator getAtomIterator(TNode trigger) const; + +private: + + struct RequestHashFunction { + size_t operator () (const Request& r) const { + return r.hash(); + } + }; + + /** Set of all requests so we don't add twice */ + context::CDHashSet<Request, RequestHashFunction> d_allRequests; + + static const element_index null_index = -1; + + struct Element { + /** Current request */ + Request request; + /** Previous request */ + element_index previous; + + Element(const Request& request, element_index previous) + : request(request), previous(previous) + {} + }; + + /** We index the requests in this vector, it's a list */ + context::CDList<Element> d_requests; + + typedef context::CDHashMap<Node, element_index, NodeHashFunction> trigger_to_list_map; + + /** Map from triggers, to the list of elements they trigger */ + trigger_to_list_map d_triggerToRequestMap; + + /** Get the list index of the trigger */ + element_index getList(TNode trigger) const; + +}; + +} + + + + diff --git a/src/theory/booleans/theory_bool.cpp b/src/theory/booleans/theory_bool.cpp index 3e75dd258..895f4a279 100644 --- a/src/theory/booleans/theory_bool.cpp +++ b/src/theory/booleans/theory_bool.cpp @@ -19,6 +19,7 @@ #include "theory/booleans/circuit_propagator.h" #include "theory/valuation.h" #include "util/boolean_simplification.h" +#include "theory/substitutions.h" #include <vector> #include <stack> diff --git a/src/theory/builtin/kinds b/src/theory/builtin/kinds index fca79aff0..b51feea6d 100644 --- a/src/theory/builtin/kinds +++ b/src/theory/builtin/kinds @@ -300,7 +300,12 @@ operator SEXPR 0: "a symbolic expression" operator LAMBDA 2 "lambda" -parameterized CHAIN BUILTIN 2: "chain operator" +parameterized CHAIN CHAIN_OP 2: "chained operator" +constant CHAIN_OP \ + ::CVC4::Chain \ + ::CVC4::ChainHashFunction \ + "util/chain.h" \ + "the chained operator" constant TYPE_CONSTANT \ ::CVC4::TypeConstant \ @@ -321,29 +326,13 @@ well-founded SEXPR_TYPE \ "::CVC4::theory::builtin::SExprProperties::mkGroundTerm(%TYPE%)" \ "theory/builtin/theory_builtin_type_rules.h" -# These will eventually move to a theory of strings. -# -# For now these are unbounded strings over a fixed, finite alphabet -# (this may change). -sort STRING_TYPE \ - Cardinality::INTEGERS \ - well-founded \ - "NodeManager::currentNM()->mkConst(::std::string())" \ - "string" \ - "String type" -constant CONST_STRING \ - ::std::string \ - ::CVC4::StringHashFunction \ - "util/hash.h" \ - "a string of characters" -typerule CONST_STRING ::CVC4::theory::builtin::StringConstantTypeRule - typerule APPLY ::CVC4::theory::builtin::ApplyTypeRule typerule EQUAL ::CVC4::theory::builtin::EqualityTypeRule typerule DISTINCT ::CVC4::theory::builtin::DistinctTypeRule typerule SEXPR ::CVC4::theory::builtin::SExprTypeRule typerule LAMBDA ::CVC4::theory::builtin::LambdaTypeRule typerule CHAIN ::CVC4::theory::builtin::ChainTypeRule +typerule CHAIN_OP ::CVC4::theory::builtin::ChainedOperatorTypeRule constant SUBTYPE_TYPE \ ::CVC4::Predicate \ diff --git a/src/theory/builtin/theory_builtin_rewriter.cpp b/src/theory/builtin/theory_builtin_rewriter.cpp index 4d62ce511..392e146ba 100644 --- a/src/theory/builtin/theory_builtin_rewriter.cpp +++ b/src/theory/builtin/theory_builtin_rewriter.cpp @@ -16,6 +16,7 @@ **/ #include "theory/builtin/theory_builtin_rewriter.h" +#include "util/chain.h" using namespace std; @@ -53,7 +54,7 @@ Node TheoryBuiltinRewriter::blastChain(TNode in) { Assert(in.getKind() == kind::CHAIN); - Kind chainedOp = in.getOperator().getConst<Kind>(); + Kind chainedOp = in.getOperator().getConst<Chain>().getOperator(); if(in.getNumChildren() == 2) { // if this is the case exactly 1 pair will be generated so the diff --git a/src/theory/builtin/theory_builtin_type_rules.h b/src/theory/builtin/theory_builtin_type_rules.h index 2a4e07528..c7143bdeb 100644 --- a/src/theory/builtin/theory_builtin_type_rules.h +++ b/src/theory/builtin/theory_builtin_type_rules.h @@ -146,14 +146,6 @@ public: } };/* class AbstractValueTypeRule */ -class StringConstantTypeRule { -public: - inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) { - Assert(n.getKind() == kind::CONST_STRING); - return nodeManager->stringType(); - } -};/* class StringConstantTypeRule */ - class LambdaTypeRule { public: inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) { @@ -220,6 +212,14 @@ public: } };/* class ChainTypeRule */ +class ChainedOperatorTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) { + Assert(n.getKind() == kind::CHAIN_OP); + return nodeManager->getType(nodeManager->operatorOf(n.getConst<Chain>().getOperator()), check); + } +};/* class ChainedOperatorTypeRule */ + class SortProperties { public: inline static bool isWellFounded(TypeNode type) { diff --git a/src/theory/bv/bitblaster.cpp b/src/theory/bv/bitblaster.cpp index 8579012ab..d17dd588f 100644 --- a/src/theory/bv/bitblaster.cpp +++ b/src/theory/bv/bitblaster.cpp @@ -12,7 +12,7 @@ ** \brief [[ Add one-line brief description here ]] ** ** [[ Add lengthier description here ]] - ** + ** **/ #include "bitblaster.h" @@ -29,7 +29,7 @@ using namespace std; using namespace CVC4::theory::bv::utils; -using namespace CVC4::context; +using namespace CVC4::context; using namespace CVC4::prop; namespace CVC4 { @@ -37,20 +37,20 @@ namespace theory { namespace bv{ std::string toString(Bits& bits) { - ostringstream os; + ostringstream os; for (int i = bits.size() - 1; i >= 0; --i) { TNode bit = bits[i]; if (bit.getKind() == kind::CONST_BOOLEAN) { os << (bit.getConst<bool>() ? "1" : "0"); } else { - os << bit<< " "; + os << bit<< " "; } } os <<"\n"; - - return os.str(); + + return os.str(); } -/////// Bitblaster +/////// Bitblaster Bitblaster::Bitblaster(context::Context* c, bv::TheoryBV* bv) : d_bv(bv), @@ -64,38 +64,41 @@ Bitblaster::Bitblaster(context::Context* c, bv::TheoryBV* bv) : d_cnfStream = new TseitinCnfStream(d_satSolver, new NullRegistrar(), new Context()); MinisatNotify* notify = new MinisatNotify(d_cnfStream, bv); - d_satSolver->setNotify(notify); + d_satSolver->setNotify(notify); // initializing the bit-blasting strategies - initAtomBBStrategies(); - initTermBBStrategies(); + initAtomBBStrategies(); + initTermBBStrategies(); } Bitblaster::~Bitblaster() { delete d_cnfStream; - delete d_satSolver; + delete d_satSolver; } -/** +/** * Bitblasts the atom, assigns it a marker literal, adding it to the SAT solver * NOTE: duplicate clauses are not detected because of marker literal * @param node the atom to be bitblasted - * + * */ void Bitblaster::bbAtom(TNode node) { node = node.getKind() == kind::NOT? node[0] : node; - + if (hasBBAtom(node)) { - return; + return; } // make sure it is marked as an atom - addAtom(node); + addAtom(node); - Debug("bitvector-bitblast") << "Bitblasting node " << node <<"\n"; + Debug("bitvector-bitblast") << "Bitblasting node " << node <<"\n"; ++d_statistics.d_numAtoms; // the bitblasted definition of the atom - Node atom_bb = Rewriter::rewrite(d_atomBBStrategies[node.getKind()](node, this)); + Node normalized = Rewriter::rewrite(node); + Node atom_bb = normalized.getKind() != kind::CONST_BOOLEAN ? + Rewriter::rewrite(d_atomBBStrategies[normalized.getKind()](normalized, this)) : + normalized; // asserting that the atom is true iff the definition holds Node atom_definition = mkNode(kind::IFF, node, atom_bb); @@ -123,14 +126,14 @@ void Bitblaster::bbTerm(TNode node, Bits& bits) { return; } - Debug("bitvector-bitblast") << "Bitblasting node " << node <<"\n"; + Debug("bitvector-bitblast") << "Bitblasting node " << node <<"\n"; ++d_statistics.d_numTerms; d_termBBStrategies[node.getKind()] (node, bits,this); - + Assert (bits.size() == utils::getSize(node)); - cacheTermDef(node, bits); + cacheTermDef(node, bits); } Node Bitblaster::bbOptimize(TNode node) { @@ -139,21 +142,21 @@ Node Bitblaster::bbOptimize(TNode node) { if (node.getKind() == kind::BITVECTOR_PLUS) { if (RewriteRule<BBPlusNeg>::applies(node)) { Node res = RewriteRule<BBPlusNeg>::run<false>(node); - return res; + return res; } // if (RewriteRule<BBFactorOut>::applies(node)) { // Node res = RewriteRule<BBFactorOut>::run<false>(node); - // return res; - // } + // return res; + // } } else if (node.getKind() == kind::BITVECTOR_MULT) { if (RewriteRule<MultPow2>::applies(node)) { Node res = RewriteRule<MultPow2>::run<false>(node); - return res; + return res; } } - - return node; + + return node; } /// Public methods @@ -170,31 +173,31 @@ void Bitblaster::explain(TNode atom, std::vector<TNode>& explanation) { std::vector<SatLiteral> literal_explanation; d_satSolver->explain(d_cnfStream->getLiteral(atom), literal_explanation); for (unsigned i = 0; i < literal_explanation.size(); ++i) { - explanation.push_back(d_cnfStream->getNode(literal_explanation[i])); + explanation.push_back(d_cnfStream->getNode(literal_explanation[i])); } } -/** +/* * Asserts the clauses corresponding to the atom to the Sat Solver * by turning on the marker literal (i.e. setting it to false) * @param node the atom to be asserted - * + * */ - + bool Bitblaster::propagate() { return d_satSolver->propagate() == prop::SAT_VALUE_TRUE; } bool Bitblaster::assertToSat(TNode lit, bool propagate) { // strip the not - TNode atom; + TNode atom; if (lit.getKind() == kind::NOT) { - atom = lit[0]; + atom = lit[0]; } else { - atom = lit; + atom = lit; } - + Assert (hasBBAtom(atom)); SatLiteral markerLit = d_cnfStream->getLiteral(atom); @@ -202,9 +205,9 @@ bool Bitblaster::assertToSat(TNode lit, bool propagate) { if(lit.getKind() == kind::NOT) { markerLit = ~markerLit; } - + Debug("bitvector-bb") << "TheoryBV::Bitblaster::assertToSat asserting node: " << atom <<"\n"; - Debug("bitvector-bb") << "TheoryBV::Bitblaster::assertToSat with literal: " << markerLit << "\n"; + Debug("bitvector-bb") << "TheoryBV::Bitblaster::assertToSat with literal: " << markerLit << "\n"; SatValue ret = d_satSolver->assertAssumption(markerLit, propagate); @@ -214,13 +217,13 @@ bool Bitblaster::assertToSat(TNode lit, bool propagate) { return ret == prop::SAT_VALUE_TRUE; } -/** - * Calls the solve method for the Sat Solver. +/** + * Calls the solve method for the Sat Solver. * passing it the marker literals to be asserted - * + * * @return true for sat, and false for unsat */ - + bool Bitblaster::solve(bool quick_solve) { if (Trace.isOn("bitvector")) { Trace("bitvector") << "Bitblaster::solve() asserted atoms "; @@ -229,24 +232,24 @@ bool Bitblaster::solve(bool quick_solve) { Trace("bitvector") << " " << d_cnfStream->getNode(*it) << "\n"; } } - Debug("bitvector") << "Bitblaster::solve() asserted atoms " << d_assertedAtoms.size() <<"\n"; - return SAT_VALUE_TRUE == d_satSolver->solve(); + Debug("bitvector") << "Bitblaster::solve() asserted atoms " << d_assertedAtoms.size() <<"\n"; + return SAT_VALUE_TRUE == d_satSolver->solve(); } void Bitblaster::getConflict(std::vector<TNode>& conflict) { SatClause conflictClause; d_satSolver->getUnsatCore(conflictClause); - + for (unsigned i = 0; i < conflictClause.size(); i++) { - SatLiteral lit = conflictClause[i]; + SatLiteral lit = conflictClause[i]; TNode atom = d_cnfStream->getNode(lit); - Node not_atom; + Node not_atom; if (atom.getKind() == kind::NOT) { not_atom = atom[0]; } else { - not_atom = NodeManager::currentNM()->mkNode(kind::NOT, atom); + not_atom = NodeManager::currentNM()->mkNode(kind::NOT, atom); } - conflict.push_back(not_atom); + conflict.push_back(not_atom); } } @@ -256,9 +259,9 @@ void Bitblaster::getConflict(std::vector<TNode>& conflict) { void Bitblaster::initAtomBBStrategies() { for (int i = 0 ; i < kind::LAST_KIND; ++i ) { - d_atomBBStrategies[i] = UndefinedAtomBBStrategy; + d_atomBBStrategies[i] = UndefinedAtomBBStrategy; } - + /// setting default bb strategies for atoms d_atomBBStrategies [ kind::EQUAL ] = DefaultEqBB; d_atomBBStrategies [ kind::BITVECTOR_ULT ] = DefaultUltBB; @@ -269,7 +272,7 @@ void Bitblaster::initAtomBBStrategies() { d_atomBBStrategies [ kind::BITVECTOR_SLE ] = DefaultSleBB; d_atomBBStrategies [ kind::BITVECTOR_SGT ] = DefaultSgtBB; d_atomBBStrategies [ kind::BITVECTOR_SGE ] = DefaultSgeBB; - + } void Bitblaster::initTermBBStrategies() { @@ -278,7 +281,7 @@ void Bitblaster::initTermBBStrategies() { for (int i = 0 ; i < kind::LAST_KIND; ++i ) { d_termBBStrategies[i] = DefaultVarBB; } - + /// setting default bb strategies for terms: // d_termBBStrategies [ kind::VARIABLE ] = DefaultVarBB; d_termBBStrategies [ kind::CONST_BITVECTOR ] = DefaultConstBB; @@ -295,13 +298,13 @@ void Bitblaster::initTermBBStrategies() { d_termBBStrategies [ kind::BITVECTOR_PLUS ] = DefaultPlusBB; d_termBBStrategies [ kind::BITVECTOR_SUB ] = DefaultSubBB; d_termBBStrategies [ kind::BITVECTOR_NEG ] = DefaultNegBB; - d_termBBStrategies [ kind::BITVECTOR_UDIV ] = UndefinedTermBBStrategy; - d_termBBStrategies [ kind::BITVECTOR_UREM ] = UndefinedTermBBStrategy; + d_termBBStrategies [ kind::BITVECTOR_UDIV ] = UndefinedTermBBStrategy; + d_termBBStrategies [ kind::BITVECTOR_UREM ] = UndefinedTermBBStrategy; d_termBBStrategies [ kind::BITVECTOR_UDIV_TOTAL ] = DefaultUdivBB; d_termBBStrategies [ kind::BITVECTOR_UREM_TOTAL ] = DefaultUremBB; - d_termBBStrategies [ kind::BITVECTOR_SDIV ] = UndefinedTermBBStrategy; - d_termBBStrategies [ kind::BITVECTOR_SREM ] = UndefinedTermBBStrategy; - d_termBBStrategies [ kind::BITVECTOR_SMOD ] = UndefinedTermBBStrategy; + d_termBBStrategies [ kind::BITVECTOR_SDIV ] = UndefinedTermBBStrategy; + d_termBBStrategies [ kind::BITVECTOR_SREM ] = UndefinedTermBBStrategy; + d_termBBStrategies [ kind::BITVECTOR_SMOD ] = UndefinedTermBBStrategy; d_termBBStrategies [ kind::BITVECTOR_SHL ] = DefaultShlBB; d_termBBStrategies [ kind::BITVECTOR_LSHR ] = DefaultLshrBB; d_termBBStrategies [ kind::BITVECTOR_ASHR ] = DefaultAshrBB; @@ -313,22 +316,22 @@ void Bitblaster::initTermBBStrategies() { d_termBBStrategies [ kind::BITVECTOR_ROTATE_LEFT ] = DefaultRotateLeftBB; } - + bool Bitblaster::hasBBAtom(TNode atom) const { return d_bitblastedAtoms.find(atom) != d_bitblastedAtoms.end(); } void Bitblaster::cacheTermDef(TNode term, Bits def) { Assert (d_termCache.find(term) == d_termCache.end()); - d_termCache[term] = def; + d_termCache[term] = def; } bool Bitblaster::hasBBTerm(TNode node) const { - return d_termCache.find(node) != d_termCache.end(); + return d_termCache.find(node) != d_termCache.end(); } void Bitblaster::getBBTerm(TNode node, Bits& bits) const { - Assert (hasBBTerm(node)); + Assert (hasBBTerm(node)); // copy? bits = d_termCache.find(node)->second; } @@ -337,7 +340,7 @@ Bitblaster::Statistics::Statistics() : d_numTermClauses("theory::bv::NumberOfTermSatClauses", 0), d_numAtomClauses("theory::bv::NumberOfAtomSatClauses", 0), d_numTerms("theory::bv::NumberOfBitblastedTerms", 0), - d_numAtoms("theory::bv::NumberOfBitblastedAtoms", 0), + d_numAtoms("theory::bv::NumberOfBitblastedAtoms", 0), d_bitblastTimer("theory::bv::BitblastTimer") { StatisticsRegistry::registerStat(&d_numTermClauses); @@ -374,7 +377,7 @@ void Bitblaster::MinisatNotify::notify(prop::SatClause& clause) { }; void Bitblaster::MinisatNotify::safePoint() { - d_bv->d_out->safePoint(); + d_bv->d_out->safePoint(); } EqualityStatus Bitblaster::getEqualityStatus(TNode a, TNode b) { @@ -417,70 +420,77 @@ EqualityStatus Bitblaster::getEqualityStatus(TNode a, TNode b) { bool Bitblaster::isSharedTerm(TNode node) { - return d_bv->d_sharedTermsSet.find(node) != d_bv->d_sharedTermsSet.end(); + return d_bv->d_sharedTermsSet.find(node) != d_bv->d_sharedTermsSet.end(); } bool Bitblaster::hasValue(TNode a) { - Assert (d_termCache.find(a) != d_termCache.end()); + Assert (d_termCache.find(a) != d_termCache.end()); Bits bits = d_termCache[a]; for (int i = bits.size() -1; i >= 0; --i) { - SatValue bit_value; - if (d_cnfStream->hasLiteral(bits[i])) { + SatValue bit_value; + if (d_cnfStream->hasLiteral(bits[i])) { SatLiteral bit = d_cnfStream->getLiteral(bits[i]); bit_value = d_satSolver->value(bit); if (bit_value == SAT_VALUE_UNKNOWN) - return false; + return false; } else { - return false; + return false; } } - return true; + return true; } -/** +/** * Returns the value a is currently assigned to in the SAT solver - * or null if the value is completely unassigned. - * - * @param a - * - * @return + * or null if the value is completely unassigned. + * + * @param a + * @param fullModel whether to create a "full model," i.e., add + * constants to equivalence classes that don't already have them + * + * @return */ -Node Bitblaster::getVarValue(TNode a) { +Node Bitblaster::getVarValue(TNode a, bool fullModel) { if (d_termCache.find(a) == d_termCache.end()) { Assert(isSharedTerm(a)); - return Node(); + return Node(); } Bits bits = d_termCache[a]; - Integer value(0); + Integer value(0); for (int i = bits.size() -1; i >= 0; --i) { SatValue bit_value; - if (d_cnfStream->hasLiteral(bits[i])) { + if (d_cnfStream->hasLiteral(bits[i])) { SatLiteral bit = d_cnfStream->getLiteral(bits[i]); bit_value = d_satSolver->value(bit); - Assert (bit_value != SAT_VALUE_UNKNOWN); + Assert (bit_value != SAT_VALUE_UNKNOWN); } else { - // the bit is unconstrainted so we can give it an arbitrary value + //TODO: return Node() if fullModel=false? + // the bit is unconstrainted so we can give it an arbitrary value bit_value = SAT_VALUE_FALSE; } - Integer bit_int = bit_value == SAT_VALUE_TRUE ? Integer(1) : Integer(0); - value = value * 2 + bit_int; + Integer bit_int = bit_value == SAT_VALUE_TRUE ? Integer(1) : Integer(0); + value = value * 2 + bit_int; } - return utils::mkConst(BitVector(bits.size(), value)); + return utils::mkConst(BitVector(bits.size(), value)); } -void Bitblaster::collectModelInfo(TheoryModel* m) { +void Bitblaster::collectModelInfo(TheoryModel* m, bool fullModel) { __gnu_cxx::hash_set<TNode, TNodeHashFunction>::iterator it = d_variables.begin(); for (; it!= d_variables.end(); ++it) { TNode var = *it; if (Theory::theoryOf(var) == theory::THEORY_BV || isSharedTerm(var)) { - Node const_value = getVarValue(var); + Node const_value = getVarValue(var, fullModel); if(const_value == Node()) { - // if the value is unassigned just set it to zero - const_value = utils::mkConst(BitVector(utils::getSize(var), 0u)); + if( fullModel ){ + // if the value is unassigned just set it to zero + const_value = utils::mkConst(BitVector(utils::getSize(var), 0u)); + } + } + if(const_value != Node()) { + Debug("bitvector-model") << "Bitblaster::collectModelInfo (assert (= " + << var << " " + << const_value << "))\n"; + m->assertEquality(var, const_value, true); } - Debug("bitvector-model") << "Bitblaster::collectModelInfo (assert (= " - << var << " " - << const_value << "))\n"; - m->assertEquality(var, const_value, true); } } } diff --git a/src/theory/bv/bitblaster.h b/src/theory/bv/bitblaster.h index c122c407d..6fab0369c 100644 --- a/src/theory/bv/bitblaster.h +++ b/src/theory/bv/bitblaster.h @@ -55,14 +55,14 @@ namespace bv { typedef std::vector<Node> Bits; -std::string toString (Bits& bits); +std::string toString (Bits& bits); class TheoryBV; -/** - * The Bitblaster that manages the mapping between Nodes - * and their bitwise definition - * +/** + * The Bitblaster that manages the mapping between Nodes + * and their bitwise definition + * */ class Bitblaster { @@ -79,26 +79,26 @@ class Bitblaster { void notify(prop::SatClause& clause); void safePoint(); }; - - + + typedef __gnu_cxx::hash_map <Node, Bits, TNodeHashFunction > TermDefMap; typedef __gnu_cxx::hash_set<TNode, TNodeHashFunction> AtomSet; - typedef __gnu_cxx::hash_set<TNode, TNodeHashFunction> VarSet; - - typedef void (*TermBBStrategy) (TNode, Bits&, Bitblaster*); - typedef Node (*AtomBBStrategy) (TNode, Bitblaster*); + typedef __gnu_cxx::hash_set<TNode, TNodeHashFunction> VarSet; + + typedef void (*TermBBStrategy) (TNode, Bits&, Bitblaster*); + typedef Node (*AtomBBStrategy) (TNode, Bitblaster*); TheoryBV *d_bv; - + // sat solver used for bitblasting and associated CnfStream theory::OutputChannel* d_bvOutput; - prop::BVSatSolverInterface* d_satSolver; + prop::BVSatSolverInterface* d_satSolver; prop::CnfStream* d_cnfStream; // caches and mappings TermDefMap d_termCache; AtomSet d_bitblastedAtoms; - VarSet d_variables; + VarSet d_variables; context::CDList<prop::SatLiteral> d_assertedAtoms; /**< context dependent list storing the atoms currently asserted by the DPLL SAT solver. */ @@ -111,79 +111,80 @@ class Bitblaster { /// function tables for the various bitblasting strategies indexed by node kind TermBBStrategy d_termBBStrategies[kind::LAST_KIND]; - AtomBBStrategy d_atomBBStrategies[kind::LAST_KIND]; + AtomBBStrategy d_atomBBStrategies[kind::LAST_KIND]; // helper methods to initialize function tables void initAtomBBStrategies(); - void initTermBBStrategies(); + void initTermBBStrategies(); // returns a node that might be easier to bitblast - Node bbOptimize(TNode node); - - void addAtom(TNode atom); + Node bbOptimize(TNode node); + + void addAtom(TNode atom); // division is bitblasted in terms of constraints // so it needs to use private bitblaster interface void bbUdiv(TNode node, Bits& bits); void bbUrem(TNode node, Bits& bits); - bool hasValue(TNode a); + bool hasValue(TNode a); public: void cacheTermDef(TNode node, Bits def); // public so we can cache remainder for division void bbTerm(TNode node, Bits& bits); void bbAtom(TNode node); - - Bitblaster(context::Context* c, bv::TheoryBV* bv); + + Bitblaster(context::Context* c, bv::TheoryBV* bv); ~Bitblaster(); bool assertToSat(TNode node, bool propagate = true); bool propagate(); bool solve(bool quick_solve = false); - void getConflict(std::vector<TNode>& conflict); + void getConflict(std::vector<TNode>& conflict); void explain(TNode atom, std::vector<TNode>& explanation); EqualityStatus getEqualityStatus(TNode a, TNode b); - /** + /** * Return a constant Node representing the value of a variable - * in the current model. - * @param a - * - * @return + * in the current model. + * @param a + * + * @return */ - Node getVarValue(TNode a); - /** - * Adds a constant value for each bit-blasted variable in the model. - * - * @param m the model + Node getVarValue(TNode a, bool fullModel=true); + /** + * Adds a constant value for each bit-blasted variable in the model. + * + * @param m the model + * @param fullModel whether to create a "full model," i.e., add + * constants to equivalence classes that don't already have them */ - void collectModelInfo(TheoryModel* m); - /** - * Stores the variable (or non-bv term) and its corresponding bits. - * - * @param var - * @param bits + void collectModelInfo(TheoryModel* m, bool fullModel); + /** + * Stores the variable (or non-bv term) and its corresponding bits. + * + * @param var */ void storeVariable(TNode var) { - d_variables.insert(var); + d_variables.insert(var); } bool isSharedTerm(TNode node); uint64_t computeAtomWeight(TNode node); private: - + class Statistics { public: IntStat d_numTermClauses, d_numAtomClauses; - IntStat d_numTerms, d_numAtoms; + IntStat d_numTerms, d_numAtoms; TimerStat d_bitblastTimer; Statistics(); - ~Statistics(); - }; - + ~Statistics(); + }; + Statistics d_statistics; }; -} /* bv namespace */ +} /* bv namespace */ } /* theory namespace */ diff --git a/src/theory/bv/bv_subtheory.h b/src/theory/bv/bv_subtheory.h index 8374a3f75..0b0551283 100644 --- a/src/theory/bv/bv_subtheory.h +++ b/src/theory/bv/bv_subtheory.h @@ -9,7 +9,7 @@ ** See the file COPYING in the top-level source directory for licensing ** information.\endverbatim ** - ** \brief Algebraic solver. + ** \brief Algebraic solver. ** ** Algebraic solver. **/ @@ -46,7 +46,7 @@ inline std::ostream& operator << (std::ostream& out, SubTheory subtheory) { out << "BV_CORE_SUBTHEORY"; break; case SUB_INEQUALITY: - out << "BV_INEQUALITY_SUBTHEORY"; + out << "BV_INEQUALITY_SUBTHEORY"; default: Unreachable(); break; @@ -55,13 +55,10 @@ inline std::ostream& operator << (std::ostream& out, SubTheory subtheory) { } -const bool d_useEqualityEngine = true; -const bool d_useSatPropagation = true; +// forward declaration +class TheoryBV; -// forward declaration -class TheoryBV; - -typedef context::CDQueue<Node> AssertionQueue; +typedef context::CDQueue<Node> AssertionQueue; /** * Abstract base class for bit-vector subtheory solvers * @@ -78,7 +75,7 @@ protected: AssertionQueue d_assertionQueue; context::CDO<uint32_t> d_assertionIndex; public: - + SubtheorySolver(context::Context* c, TheoryBV* bv) : d_context(c), d_bv(bv), @@ -86,24 +83,24 @@ public: d_assertionIndex(c, 0) {} virtual ~SubtheorySolver() {} - virtual bool check(Theory::Effort e) = 0; + virtual bool check(Theory::Effort e) = 0; virtual void explain(TNode literal, std::vector<TNode>& assumptions) = 0; virtual void preRegister(TNode node) {} virtual void propagate(Theory::Effort e) {} - virtual void collectModelInfo(TheoryModel* m) = 0; - virtual Node getModelValue(TNode var) = 0; + virtual void collectModelInfo(TheoryModel* m, bool fullModel) = 0; + virtual Node getModelValue(TNode var) = 0; virtual bool isComplete() = 0; virtual EqualityStatus getEqualityStatus(TNode a, TNode b) = 0; - virtual void addSharedTerm(TNode node) {} + virtual void addSharedTerm(TNode node) {} bool done() { return d_assertionQueue.size() == d_assertionIndex; } TNode get() { - Assert (!done()); + Assert (!done()); TNode res = d_assertionQueue[d_assertionIndex]; d_assertionIndex = d_assertionIndex + 1; - return res; + return res; } virtual void assertFact(TNode fact) { d_assertionQueue.push_back(fact); } -}; +}; } } diff --git a/src/theory/bv/bv_subtheory_bitblast.cpp b/src/theory/bv/bv_subtheory_bitblast.cpp index 2308f36a3..5a0c17134 100644 --- a/src/theory/bv/bv_subtheory_bitblast.cpp +++ b/src/theory/bv/bv_subtheory_bitblast.cpp @@ -34,7 +34,8 @@ BitblastSolver::BitblastSolver(context::Context* c, TheoryBV* bv) d_bitblaster(new Bitblaster(c, bv)), d_bitblastQueue(c), d_statistics(), - d_validModelCache(c, true) + d_validModelCache(c, true), + d_useSatPropagation(options::bvPropagate()) {} BitblastSolver::~BitblastSolver() { @@ -83,20 +84,20 @@ void BitblastSolver::bitblastQueue() { } bool BitblastSolver::check(Theory::Effort e) { - Debug("bv-bitblast") << "BitblastSolver::check (" << e << ")\n"; + Debug("bv-bitblast") << "BitblastSolver::check (" << e << ")\n"; Assert(!options::bitvectorEagerBitblast()); - ++(d_statistics.d_numCallstoCheck); + ++(d_statistics.d_numCallstoCheck); //// Lazy bit-blasting // bit-blast enqueued nodes bitblastQueue(); - // Processing assertions + // Processing assertions while (!done()) { TNode fact = get(); d_validModelCache = false; - Debug("bv-bitblast") << " fact " << fact << ")\n"; + Debug("bv-bitblast") << " fact " << fact << ")\n"; if (!d_bv->inConflict() && (!d_bv->wasPropagatedBySubtheory(fact) || d_bv->getPropagatingSubtheory(fact) != SUB_BITBLAST)) { // Some atoms have not been bit-blasted yet d_bitblaster->bbAtom(fact); @@ -143,8 +144,8 @@ EqualityStatus BitblastSolver::getEqualityStatus(TNode a, TNode b) { return d_bitblaster->getEqualityStatus(a, b); } -void BitblastSolver::collectModelInfo(TheoryModel* m) { - return d_bitblaster->collectModelInfo(m); +void BitblastSolver::collectModelInfo(TheoryModel* m, bool fullModel) { + return d_bitblaster->collectModelInfo(m, fullModel); } Node BitblastSolver::getModelValue(TNode node) @@ -188,5 +189,5 @@ Node BitblastSolver::getModelValueRec(TNode node) Assert(val.isConst()); d_modelCache[node] = val; Debug("bitvector-model") << node << " => " << val <<"\n"; - return val; + return val; } diff --git a/src/theory/bv/bv_subtheory_bitblast.h b/src/theory/bv/bv_subtheory_bitblast.h index 819b3d62c..f1204dbdf 100644 --- a/src/theory/bv/bv_subtheory_bitblast.h +++ b/src/theory/bv/bv_subtheory_bitblast.h @@ -19,7 +19,6 @@ #pragma once #include "theory/bv/bv_subtheory.h" -#include "theory/substitutions.h" namespace CVC4 { namespace theory { namespace bv { @@ -33,20 +32,21 @@ class BitblastSolver : public SubtheorySolver { struct Statistics { IntStat d_numCallstoCheck; Statistics(); - ~Statistics(); - }; + ~Statistics(); + }; /** Bitblaster */ Bitblaster* d_bitblaster; /** Nodes that still need to be bit-blasted */ context::CDQueue<TNode> d_bitblastQueue; - Statistics d_statistics; + Statistics d_statistics; typedef std::hash_map<Node, Node, NodeHashFunction> NodeMap; NodeMap d_modelCache; context::CDO<bool> d_validModelCache; Node getModelValueRec(TNode node); + bool d_useSatPropagation; public: BitblastSolver(context::Context* c, TheoryBV* bv); ~BitblastSolver(); @@ -55,7 +55,7 @@ public: bool check(Theory::Effort e); void explain(TNode literal, std::vector<TNode>& assumptions); EqualityStatus getEqualityStatus(TNode a, TNode b); - void collectModelInfo(TheoryModel* m); + void collectModelInfo(TheoryModel* m, bool fullModel); Node getModelValue(TNode node); bool isComplete() { return true; } void bitblastQueue(); diff --git a/src/theory/bv/bv_subtheory_core.cpp b/src/theory/bv/bv_subtheory_core.cpp index c0546f892..45946b8c8 100644 --- a/src/theory/bv/bv_subtheory_core.cpp +++ b/src/theory/bv/bv_subtheory_core.cpp @@ -37,53 +37,48 @@ CoreSolver::CoreSolver(context::Context* c, TheoryBV* bv) d_isCoreTheory(c, true), d_reasons(c) { - if (d_useEqualityEngine) { - - // The kinds we are treating as function application in congruence - d_equalityEngine.addFunctionKind(kind::BITVECTOR_CONCAT, true); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_AND); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_OR); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_XOR); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_NOT); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_NAND); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_NOR); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_XNOR); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_COMP); - d_equalityEngine.addFunctionKind(kind::BITVECTOR_MULT, true); - d_equalityEngine.addFunctionKind(kind::BITVECTOR_PLUS, true); - d_equalityEngine.addFunctionKind(kind::BITVECTOR_EXTRACT, true); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SUB); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_NEG); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_UDIV); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_UREM); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SDIV); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SREM); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SMOD); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SHL); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_LSHR); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_ASHR); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_ULT); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_ULE); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_UGT); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_UGE); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SLT); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SLE); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SGT); - // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SGE); - } + + // The kinds we are treating as function application in congruence + d_equalityEngine.addFunctionKind(kind::BITVECTOR_CONCAT, true); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_AND); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_OR); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_XOR); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_NOT); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_NAND); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_NOR); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_XNOR); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_COMP); + d_equalityEngine.addFunctionKind(kind::BITVECTOR_MULT, true); + d_equalityEngine.addFunctionKind(kind::BITVECTOR_PLUS, true); + d_equalityEngine.addFunctionKind(kind::BITVECTOR_EXTRACT, true); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SUB); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_NEG); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_UDIV); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_UREM); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SDIV); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SREM); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SMOD); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SHL); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_LSHR); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_ASHR); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_ULT); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_ULE); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_UGT); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_UGE); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SLT); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SLE); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SGT); + // d_equalityEngine.addFunctionKind(kind::BITVECTOR_SGE); } CoreSolver::~CoreSolver() { - delete d_slicer; + delete d_slicer; } void CoreSolver::setMasterEqualityEngine(eq::EqualityEngine* eq) { d_equalityEngine.setMasterEqualityEngine(eq); } void CoreSolver::preRegister(TNode node) { - if (!d_useEqualityEngine) - return; - if (node.getKind() == kind::EQUAL) { d_equalityEngine.addTriggerEquality(node); if (options::bitvectorCoreSolver()) { @@ -109,51 +104,51 @@ Node CoreSolver::getBaseDecomposition(TNode a) { std::vector<Node> a_decomp; d_slicer->getBaseDecomposition(a, a_decomp); Node new_a = utils::mkConcat(a_decomp); - Debug("bv-slicer") << "CoreSolver::getBaseDecomposition " << a <<" => " << new_a << "\n"; - return new_a; + Debug("bv-slicer") << "CoreSolver::getBaseDecomposition " << a <<" => " << new_a << "\n"; + return new_a; } bool CoreSolver::decomposeFact(TNode fact) { - Debug("bv-slicer") << "CoreSolver::decomposeFact fact=" << fact << endl; + Debug("bv-slicer") << "CoreSolver::decomposeFact fact=" << fact << endl; // assert decompositions since the equality engine does not know the semantics of // concat: // a == a_1 concat ... concat a_k // b == b_1 concat ... concat b_k - Debug("bv-slicer") << "CoreSolver::decomposeFact fact=" << fact << endl; - // FIXME: are this the right things to assert? + Debug("bv-slicer") << "CoreSolver::decomposeFact fact=" << fact << endl; + // FIXME: are this the right things to assert? // assert decompositions since the equality engine does not know the semantics of // concat: // a == a_1 concat ... concat a_k // b == b_1 concat ... concat b_k - TNode eq = fact.getKind() == kind::NOT? fact[0] : fact; + TNode eq = fact.getKind() == kind::NOT? fact[0] : fact; TNode a = eq[0]; TNode b = eq[1]; Node new_a = getBaseDecomposition(a); - Node new_b = getBaseDecomposition(b); - + Node new_b = getBaseDecomposition(b); + Assert (utils::getSize(new_a) == utils::getSize(new_b) && - utils::getSize(new_a) == utils::getSize(a)); - + utils::getSize(new_a) == utils::getSize(a)); + NodeManager* nm = NodeManager::currentNM(); Node a_eq_new_a = nm->mkNode(kind::EQUAL, a, new_a); Node b_eq_new_b = nm->mkNode(kind::EQUAL, b, new_b); bool ok = true; ok = assertFactToEqualityEngine(a_eq_new_a, utils::mkTrue()); - if (!ok) return false; + if (!ok) return false; ok = assertFactToEqualityEngine(b_eq_new_b, utils::mkTrue()); - if (!ok) return false; + if (!ok) return false; ok = assertFactToEqualityEngine(fact, fact); if (!ok) return false; - + if (fact.getKind() == kind::EQUAL) { // assert the individual equalities as well // a_i == b_i if (new_a.getKind() == kind::BITVECTOR_CONCAT && new_b.getKind() == kind::BITVECTOR_CONCAT) { - - Assert (new_a.getNumChildren() == new_b.getNumChildren()); + + Assert (new_a.getNumChildren() == new_b.getNumChildren()); for (unsigned i = 0; i < new_a.getNumChildren(); ++i) { Node eq_i = nm->mkNode(kind::EQUAL, new_a[i], new_b[i]); ok = assertFactToEqualityEngine(eq_i, fact); @@ -161,23 +156,23 @@ bool CoreSolver::decomposeFact(TNode fact) { } } } - return true; + return true; } bool CoreSolver::check(Theory::Effort e) { Trace("bitvector::core") << "CoreSolver::check \n"; Assert (!d_bv->inConflict()); - ++(d_statistics.d_numCallstoCheck); - bool ok = true; + ++(d_statistics.d_numCallstoCheck); + bool ok = true; std::vector<Node> core_eqs; while (! done()) { - TNode fact = get(); - + TNode fact = get(); + // update whether we are in the core fragment if (d_isCoreTheory && !d_slicer->isCoreTerm(fact)) { - d_isCoreTheory = false; + d_isCoreTheory = false; } - + // only reason about equalities if (fact.getKind() == kind::EQUAL || (fact.getKind() == kind::NOT && fact[0].getKind() == kind::EQUAL)) { if (options::bitvectorCoreSolver()) { @@ -186,31 +181,31 @@ bool CoreSolver::check(Theory::Effort e) { ok = assertFactToEqualityEngine(fact, fact); } } else { - ok = assertFactToEqualityEngine(fact, fact); + ok = assertFactToEqualityEngine(fact, fact); } if (!ok) - return false; + return false; } - + if (Theory::fullEffort(e) && isComplete()) { buildModel(); } - + return true; } void CoreSolver::buildModel() { if (options::bitvectorCoreSolver()) { // FIXME - Unreachable(); - return; + Unreachable(); + return; } - Debug("bv-core") << "CoreSolver::buildModel() \n"; - d_modelValues.clear(); + Debug("bv-core") << "CoreSolver::buildModel() \n"; + d_modelValues.clear(); TNodeSet constants; - TNodeSet constants_in_eq_engine; + TNodeSet constants_in_eq_engine; // collect constants in equality engine - eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(&d_equalityEngine); + eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(&d_equalityEngine); while (!eqcs_i.isFinished()) { TNode repr = *eqcs_i; if (repr.getKind() == kind::CONST_BITVECTOR) { @@ -218,39 +213,39 @@ void CoreSolver::buildModel() { eq::EqClassIterator it(repr, &d_equalityEngine); if (!(++it).isFinished() || true) { constants.insert(repr); - constants_in_eq_engine.insert(repr); + constants_in_eq_engine.insert(repr); } } - ++eqcs_i; + ++eqcs_i; } // build repr to value map - + eqcs_i = eq::EqClassesIterator(&d_equalityEngine); while (!eqcs_i.isFinished()) { TNode repr = *eqcs_i; ++eqcs_i; - + if (repr.getKind() != kind::VARIABLE && repr.getKind() != kind::SKOLEM && repr.getKind() != kind::CONST_BITVECTOR && !d_bv->isSharedTerm(repr)) { - continue; + continue; } - - TypeNode type = repr.getType(); + + TypeNode type = repr.getType(); if (type.isBitVector() && repr.getKind()!= kind::CONST_BITVECTOR) { - Debug("bv-core-model") << " processing " << repr <<"\n"; + Debug("bv-core-model") << " processing " << repr <<"\n"; // we need to assign a value for it TypeEnumerator te(type); - Node val; + Node val; do { - val = *te; + val = *te; ++te; // Debug("bv-core-model") << " trying value " << val << "\n"; // Debug("bv-core-model") << " is in set? " << constants.count(val) << "\n"; - // Debug("bv-core-model") << " enumerator done? " << te.isFinished() << "\n"; + // Debug("bv-core-model") << " enumerator done? " << te.isFinished() << "\n"; } while (constants.count(val) != 0 && !(te.isFinished())); - + if (te.isFinished() && constants.count(val) != 0) { // if we cannot enumerate anymore values we just return the lemma stating that // at least two of the representatives are equal. @@ -259,15 +254,15 @@ void CoreSolver::buildModel() { for (TNodeSet::const_iterator it = constants_in_eq_engine.begin(); it != constants_in_eq_engine.end(); ++it) { - TNode constant = *it; + TNode constant = *it; if (utils::getSize(constant) == utils::getSize(repr)) { - representatives.push_back(constant); + representatives.push_back(constant); } } for (ModelValue::const_iterator it = d_modelValues.begin(); it != d_modelValues.end(); ++it) { representatives.push_back(it->first); } - std::vector<Node> equalities; + std::vector<Node> equalities; for (unsigned i = 0; i < representatives.size(); ++i) { for (unsigned j = i + 1; j < representatives.size(); ++j) { TNode a = representatives[i]; @@ -279,19 +274,19 @@ void CoreSolver::buildModel() { } Node lemma = utils::mkOr(equalities); d_bv->lemma(lemma); - Debug("bv-core") << " lemma: " << lemma << "\n"; - return; + Debug("bv-core") << " lemma: " << lemma << "\n"; + return; } Debug("bv-core-model") << " " << repr << " => " << val <<"\n" ; constants.insert(val); - d_modelValues[repr] = val; + d_modelValues[repr] = val; } } } bool CoreSolver::assertFactToEqualityEngine(TNode fact, TNode reason) { - // Notify the equality engine - if (d_useEqualityEngine && !d_bv->inConflict() && (!d_bv->wasPropagatedBySubtheory(fact) || !d_bv->getPropagatingSubtheory(fact) == SUB_CORE)) { + // Notify the equality engine + if (!d_bv->inConflict() && (!d_bv->wasPropagatedBySubtheory(fact) || !d_bv->getPropagatingSubtheory(fact) == SUB_CORE)) { Debug("bv-slicer-eq") << "CoreSolver::assertFactToEqualityEngine fact=" << fact << endl; // Debug("bv-slicer-eq") << " reason=" << reason << endl; bool negated = fact.getKind() == kind::NOT; @@ -315,8 +310,8 @@ bool CoreSolver::assertFactToEqualityEngine(TNode fact, TNode reason) { // checking for a conflict if (d_bv->inConflict()) { return false; - } - return true; + } + return true; } bool CoreSolver::NotifyClass::eqNotifyTriggerEquality(TNode equality, bool value) { @@ -361,10 +356,10 @@ void CoreSolver::conflict(TNode a, TNode b) { d_bv->setConflict(conflict); } -void CoreSolver::collectModelInfo(TheoryModel* m) { +void CoreSolver::collectModelInfo(TheoryModel* m, bool fullModel) { if (options::bitvectorCoreSolver()) { Unreachable(); - return; + return; } if (Debug.isOn("bitvector-model")) { context::CDQueue<Node>::const_iterator it = d_assertionQueue.begin(); @@ -377,11 +372,11 @@ void CoreSolver::collectModelInfo(TheoryModel* m) { d_bv->computeRelevantTerms(termSet); m->assertEqualityEngine(&d_equalityEngine, &termSet); if (isComplete()) { - Debug("bitvector-model") << "CoreSolver::collectModelInfo complete."; + Debug("bitvector-model") << "CoreSolver::collectModelInfo complete."; for (ModelValue::const_iterator it = d_modelValues.begin(); it != d_modelValues.end(); ++it) { Node a = it->first; Node b = it->second; - m->assertEquality(a, b, true); + m->assertEquality(a, b, true); } } } @@ -390,23 +385,23 @@ Node CoreSolver::getModelValue(TNode var) { // we don't need to evaluate bv expressions and only look at variable values // because this only gets called when the core theory is complete (i.e. no other bv // function symbols are currently asserted) - Assert (d_slicer->isCoreTerm(var)); - - Debug("bitvector-model") << "CoreSolver::getModelValue (" << var <<")"; + Assert (d_slicer->isCoreTerm(var)); + + Debug("bitvector-model") << "CoreSolver::getModelValue (" << var <<")"; Assert (isComplete()); TNode repr = d_equalityEngine.getRepresentative(var); - Node result = Node(); + Node result = Node(); if (repr.getKind() == kind::CONST_BITVECTOR) { - result = repr; + result = repr; } else if (d_modelValues.find(repr) == d_modelValues.end()) { // it may be a shared term that never gets asserted // result is just Null Assert(d_bv->isSharedTerm(var)); } else { - result = d_modelValues[repr]; + result = d_modelValues[repr]; } - Debug("bitvector-model") << " => " << result <<"\n"; - return result; + Debug("bitvector-model") << " => " << result <<"\n"; + return result; } CoreSolver::Statistics::Statistics() diff --git a/src/theory/bv/bv_subtheory_core.h b/src/theory/bv/bv_subtheory_core.h index 423408a7c..b886bbdd5 100644 --- a/src/theory/bv/bv_subtheory_core.h +++ b/src/theory/bv/bv_subtheory_core.h @@ -25,21 +25,21 @@ namespace CVC4 { namespace theory { namespace bv { -class Slicer; -class Base; +class Slicer; +class Base; /** * Bitvector equality solver */ class CoreSolver : public SubtheorySolver { typedef __gnu_cxx::hash_map<TNode, Node, TNodeHashFunction> ModelValue; - typedef __gnu_cxx::hash_set<TNode, TNodeHashFunction> TNodeSet; + typedef __gnu_cxx::hash_set<TNode, TNodeHashFunction> TNodeSet; struct Statistics { IntStat d_numCallstoCheck; Statistics(); - ~Statistics(); - }; - + ~Statistics(); + }; + // NotifyClass: handles call-back from congruence closure module class NotifyClass : public eq::EqualityEngineNotify { CoreSolver& d_solver; @@ -59,13 +59,13 @@ class CoreSolver : public SubtheorySolver { /** The notify class for d_equalityEngine */ NotifyClass d_notify; - + /** Equality engine */ eq::EqualityEngine d_equalityEngine; /** Store a propagation to the bv solver */ bool storePropagation(TNode literal); - + /** Store a conflict from merging two constants */ void conflict(TNode a, TNode b); @@ -74,12 +74,12 @@ class CoreSolver : public SubtheorySolver { /** To make sure we keep the explanations */ context::CDHashSet<Node, NodeHashFunction> d_reasons; ModelValue d_modelValues; - void buildModel(); - bool assertFactToEqualityEngine(TNode fact, TNode reason); + void buildModel(); + bool assertFactToEqualityEngine(TNode fact, TNode reason); bool decomposeFact(TNode fact); Node getBaseDecomposition(TNode a); - Statistics d_statistics; -public: + Statistics d_statistics; +public: CoreSolver(context::Context* c, TheoryBV* bv); ~CoreSolver(); bool isComplete() { return d_isCoreTheory; } @@ -87,8 +87,8 @@ public: void preRegister(TNode node); bool check(Theory::Effort e); void explain(TNode literal, std::vector<TNode>& assumptions); - void collectModelInfo(TheoryModel* m); - Node getModelValue(TNode var); + void collectModelInfo(TheoryModel* m, bool fullModel); + Node getModelValue(TNode var); void addSharedTerm(TNode t) { d_equalityEngine.addTriggerTerm(t, THEORY_BV); } diff --git a/src/theory/bv/bv_subtheory_inequality.cpp b/src/theory/bv/bv_subtheory_inequality.cpp index f45250f5b..a3970c9e3 100644 --- a/src/theory/bv/bv_subtheory_inequality.cpp +++ b/src/theory/bv/bv_subtheory_inequality.cpp @@ -29,17 +29,17 @@ using namespace CVC4::theory::bv::utils; bool InequalitySolver::check(Theory::Effort e) { Debug("bv-subtheory-inequality") << "InequalitySolveR::check("<< e <<")\n"; ++(d_statistics.d_numCallstoCheck); - - bool ok = true; + + bool ok = true; while (!done() && ok) { TNode fact = get(); - Debug("bv-subtheory-inequality") << " "<< fact <<"\n"; + Debug("bv-subtheory-inequality") << " "<< fact <<"\n"; if (fact.getKind() == kind::EQUAL) { TNode a = fact[0]; TNode b = fact[1]; ok = d_inequalityGraph.addInequality(a, b, false, fact); if (ok) - ok = d_inequalityGraph.addInequality(b, a, false, fact); + ok = d_inequalityGraph.addInequality(b, a, false, fact); } else if (fact.getKind() == kind::NOT && fact[0].getKind() == kind::EQUAL) { TNode a = fact[0][0]; TNode b = fact[0][1]; @@ -47,40 +47,40 @@ bool InequalitySolver::check(Theory::Effort e) { } if (fact.getKind() == kind::NOT && fact[0].getKind() == kind::BITVECTOR_ULE) { TNode a = fact[0][1]; - TNode b = fact[0][0]; + TNode b = fact[0][0]; ok = d_inequalityGraph.addInequality(a, b, true, fact); // propagate // if (d_bv->isSharedTerm(a) && d_bv->isSharedTerm(b)) { - // Node neq = utils::mkNode(kind::NOT, utils::mkNode(kind::EQUAL, a, b)); + // Node neq = utils::mkNode(kind::NOT, utils::mkNode(kind::EQUAL, a, b)); // d_bv->storePropagation(neq, SUB_INEQUALITY); // d_explanations[neq] = fact; // } } else if (fact.getKind() == kind::NOT && fact[0].getKind() == kind::BITVECTOR_ULT) { TNode a = fact[0][1]; - TNode b = fact[0][0]; + TNode b = fact[0][0]; ok = d_inequalityGraph.addInequality(a, b, false, fact); } else if (fact.getKind() == kind::BITVECTOR_ULT) { TNode a = fact[0]; - TNode b = fact[1]; + TNode b = fact[1]; ok = d_inequalityGraph.addInequality(a, b, true, fact); // propagate // if (d_bv->isSharedTerm(a) && d_bv->isSharedTerm(b)) { - // Node neq = utils::mkNode(kind::NOT, utils::mkNode(kind::EQUAL, a, b)); + // Node neq = utils::mkNode(kind::NOT, utils::mkNode(kind::EQUAL, a, b)); // d_bv->storePropagation(neq, SUB_INEQUALITY); // d_explanations[neq] = fact; // } } else if (fact.getKind() == kind::BITVECTOR_ULE) { TNode a = fact[0]; - TNode b = fact[1]; + TNode b = fact[1]; ok = d_inequalityGraph.addInequality(a, b, false, fact); } } - + if (!ok) { std::vector<TNode> conflict; - d_inequalityGraph.getConflict(conflict); + d_inequalityGraph.getConflict(conflict); d_bv->setConflict(utils::flattenAnd(conflict)); - return false; + return false; } if (isComplete() && Theory::fullEffort(e)) { @@ -89,36 +89,39 @@ bool InequalitySolver::check(Theory::Effort e) { std::vector<Node> lemmas; d_inequalityGraph.checkDisequalities(lemmas); for(unsigned i = 0; i < lemmas.size(); ++i) { - d_bv->lemma(lemmas[i]); + d_bv->lemma(lemmas[i]); } } - return true; + return true; } EqualityStatus InequalitySolver::getEqualityStatus(TNode a, TNode b) { + if (!isComplete()) + return EQUALITY_UNKNOWN; + Node a_lt_b = utils::mkNode(kind::BITVECTOR_ULT, a, b); Node b_lt_a = utils::mkNode(kind::BITVECTOR_ULT, b, a); // if an inequality containing the terms has been asserted then we know // the equality is false if (d_assertionSet.contains(a_lt_b) || d_assertionSet.contains(b_lt_a)) { - return EQUALITY_FALSE; + return EQUALITY_FALSE; } - + if (!d_inequalityGraph.hasValueInModel(a) || !d_inequalityGraph.hasValueInModel(b)) { - return EQUALITY_UNKNOWN; + return EQUALITY_UNKNOWN; } // TODO: check if this disequality is entailed by inequalities via transitivity - + BitVector a_val = d_inequalityGraph.getValueInModel(a); BitVector b_val = d_inequalityGraph.getValueInModel(b); - + if (a_val == b_val) { - return EQUALITY_TRUE_IN_MODEL; + return EQUALITY_TRUE_IN_MODEL; } else { - return EQUALITY_FALSE_IN_MODEL; + return EQUALITY_FALSE_IN_MODEL; } } @@ -126,19 +129,19 @@ void InequalitySolver::assertFact(TNode fact) { d_assertionQueue.push_back(fact); d_assertionSet.insert(fact); if (!isInequalityOnly(fact)) { - d_isComplete = false; + d_isComplete = false; } } bool InequalitySolver::isInequalityOnly(TNode node) { if (d_ineqTermCache.find(node) != d_ineqTermCache.end()) { - return d_ineqTermCache[node]; + return d_ineqTermCache[node]; } - + if (node.getKind() == kind::NOT) { - node = node[0]; + node = node[0]; } - + if (node.getKind() != kind::EQUAL && node.getKind() != kind::BITVECTOR_ULT && node.getKind() != kind::BITVECTOR_ULE && @@ -146,50 +149,50 @@ bool InequalitySolver::isInequalityOnly(TNode node) { node.getKind() != kind::SELECT && node.getKind() != kind::STORE && node.getMetaKind() != kind::metakind::VARIABLE) { - return false; + return false; } - bool res = true; + bool res = true; for (unsigned i = 0; i < node.getNumChildren(); ++i) { res = res && isInequalityOnly(node[i]); } - d_ineqTermCache[node] = res; - return res; + d_ineqTermCache[node] = res; + return res; } void InequalitySolver::explain(TNode literal, std::vector<TNode>& assumptions) { Assert (d_explanations.find(literal) != d_explanations.end()); TNode explanation = d_explanations[literal]; assumptions.push_back(explanation); - Debug("bv-inequality-explain") << "InequalitySolver::explain " << literal << " with " << explanation <<"\n"; + Debug("bv-inequality-explain") << "InequalitySolver::explain " << literal << " with " << explanation <<"\n"; } void InequalitySolver::propagate(Theory::Effort e) { - Assert (false); + Assert (false); } -void InequalitySolver::collectModelInfo(TheoryModel* m) { - Debug("bitvector-model") << "InequalitySolver::collectModelInfo \n"; +void InequalitySolver::collectModelInfo(TheoryModel* m, bool fullModel) { + Debug("bitvector-model") << "InequalitySolver::collectModelInfo \n"; std::vector<Node> model; d_inequalityGraph.getAllValuesInModel(model); for (unsigned i = 0; i < model.size(); ++i) { - Assert (model[i].getKind() == kind::EQUAL); - m->assertEquality(model[i][0], model[i][1], true); + Assert (model[i].getKind() == kind::EQUAL); + m->assertEquality(model[i][0], model[i][1], true); } } Node InequalitySolver::getModelValue(TNode var) { - Assert (isInequalityOnly(var)); - Debug("bitvector-model") << "InequalitySolver::getModelValue (" << var <<")"; + Assert (isInequalityOnly(var)); + Debug("bitvector-model") << "InequalitySolver::getModelValue (" << var <<")"; Assert (isComplete()); - Node result = Node(); + Node result = Node(); if (!d_inequalityGraph.hasValueInModel(var)) { Assert (d_bv->isSharedTerm(var)); } else { BitVector val = d_inequalityGraph.getValueInModel(var); result = utils::mkConst(val); } - Debug("bitvector-model") << " => " << result <<"\n"; - return result; + Debug("bitvector-model") << " => " << result <<"\n"; + return result; } InequalitySolver::Statistics::Statistics() diff --git a/src/theory/bv/bv_subtheory_inequality.h b/src/theory/bv/bv_subtheory_inequality.h index 390a329ff..6e0139e09 100644 --- a/src/theory/bv/bv_subtheory_inequality.h +++ b/src/theory/bv/bv_subtheory_inequality.h @@ -9,7 +9,7 @@ ** See the file COPYING in the top-level source directory for licensing ** information.\endverbatim ** - ** \brief Algebraic solver. + ** \brief Algebraic solver. ** ** Algebraic solver. **/ @@ -31,16 +31,16 @@ class InequalitySolver: public SubtheorySolver { struct Statistics { IntStat d_numCallstoCheck; Statistics(); - ~Statistics(); - }; + ~Statistics(); + }; - context::CDHashSet<Node, NodeHashFunction> d_assertionSet; + context::CDHashSet<Node, NodeHashFunction> d_assertionSet; InequalityGraph d_inequalityGraph; - context::CDHashMap<Node, TNode, NodeHashFunction> d_explanations; + context::CDHashMap<Node, TNode, NodeHashFunction> d_explanations; context::CDO<bool> d_isComplete; - __gnu_cxx::hash_map<TNode, bool, TNodeHashFunction> d_ineqTermCache; - bool isInequalityOnly(TNode node); - Statistics d_statistics; + __gnu_cxx::hash_map<TNode, bool, TNodeHashFunction> d_ineqTermCache; + bool isInequalityOnly(TNode node); + Statistics d_statistics; public: InequalitySolver(context::Context* c, TheoryBV* bv) : SubtheorySolver(c, bv), @@ -51,19 +51,19 @@ public: d_ineqTermCache(), d_statistics() {} - + bool check(Theory::Effort e); - void propagate(Theory::Effort e); + void propagate(Theory::Effort e); void explain(TNode literal, std::vector<TNode>& assumptions); bool isComplete() { return d_isComplete; } - void collectModelInfo(TheoryModel* m); - Node getModelValue(TNode var); + void collectModelInfo(TheoryModel* m, bool fullModel); + Node getModelValue(TNode var); EqualityStatus getEqualityStatus(TNode a, TNode b); - void assertFact(TNode fact); -}; + void assertFact(TNode fact); +}; } } } -#endif /* __CVC4__THEORY__BV__BV_SUBTHEORY__INEQUALITY_H */ +#endif /* __CVC4__THEORY__BV__BV_SUBTHEORY__INEQUALITY_H */ diff --git a/src/theory/bv/kinds b/src/theory/bv/kinds index 052e477ea..aeae1073e 100644 --- a/src/theory/bv/kinds +++ b/src/theory/bv/kinds @@ -120,6 +120,14 @@ parameterized BITVECTOR_SIGN_EXTEND BITVECTOR_SIGN_EXTEND_OP 1 "bit-vector sign- parameterized BITVECTOR_ROTATE_LEFT BITVECTOR_ROTATE_LEFT_OP 1 "bit-vector rotate left" parameterized BITVECTOR_ROTATE_RIGHT BITVECTOR_ROTATE_RIGHT_OP 1 "bit-vector rotate right" +constant INT_TO_BITVECTOR_OP \ + ::CVC4::IntToBitVector \ + "::CVC4::UnsignedHashFunction< ::CVC4::IntToBitVector >" \ + "util/bitvector.h" \ + "operator for the integer conversion to bit-vector" +parameterized INT_TO_BITVECTOR INT_TO_BITVECTOR_OP 1 "integer conversion to bit-vector" +operator BITVECTOR_TO_NAT 1 "bit-vector conversion to (nonnegative) integer" + typerule CONST_BITVECTOR ::CVC4::theory::bv::BitVectorConstantTypeRule typerule BITVECTOR_AND ::CVC4::theory::bv::BitVectorFixedWidthTypeRule @@ -166,4 +174,7 @@ typerule BITVECTOR_REPEAT ::CVC4::theory::bv::BitVectorRepeatTypeRule typerule BITVECTOR_ZERO_EXTEND ::CVC4::theory::bv::BitVectorExtendTypeRule typerule BITVECTOR_SIGN_EXTEND ::CVC4::theory::bv::BitVectorExtendTypeRule +typerule BITVECTOR_TO_NAT ::CVC4::theory::bv::BitVectorConversionTypeRule +typerule INT_TO_BITVECTOR ::CVC4::theory::bv::BitVectorConversionTypeRule + endtheory diff --git a/src/theory/bv/options b/src/theory/bv/options index 7b87baa21..077299d1f 100644 --- a/src/theory/bv/options +++ b/src/theory/bv/options @@ -22,5 +22,11 @@ option bitvectorCoreSolver --bv-core-solver bool option bvToBool --bv-to-bool bool lift bit-vectors of size 1 to booleans when possible + +option bvPropagate --bv-propagate bool :default true + use bit-vector propagation in the bit-blaster + +option bvEquality --bv-eq bool :default true + use the equality engine for the bit-vector theory endmodule diff --git a/src/theory/bv/theory_bv.cpp b/src/theory/bv/theory_bv.cpp index cb68a0f65..a2de951aa 100644 --- a/src/theory/bv/theory_bv.cpp +++ b/src/theory/bv/theory_bv.cpp @@ -49,29 +49,32 @@ TheoryBV::TheoryBV(context::Context* c, context::UserContext* u, OutputChannel& d_literalsToPropagateIndex(c, 0), d_propagatedBy(c) { - SubtheorySolver* core_solver = new CoreSolver(c, this); - d_subtheories.push_back(core_solver); - d_subtheoryMap[SUB_CORE] = core_solver; - + if (options::bvEquality()) { + SubtheorySolver* core_solver = new CoreSolver(c, this); + d_subtheories.push_back(core_solver); + d_subtheoryMap[SUB_CORE] = core_solver; + } if (options::bitvectorInequalitySolver()) { - SubtheorySolver* ineq_solver = new InequalitySolver(c, this); + SubtheorySolver* ineq_solver = new InequalitySolver(c, this); d_subtheories.push_back(ineq_solver); d_subtheoryMap[SUB_INEQUALITY] = ineq_solver; } - - SubtheorySolver* bb_solver = new BitblastSolver(c, this); + + SubtheorySolver* bb_solver = new BitblastSolver(c, this); d_subtheories.push_back(bb_solver); d_subtheoryMap[SUB_BITBLAST] = bb_solver; } TheoryBV::~TheoryBV() { for (unsigned i = 0; i < d_subtheories.size(); ++i) { - delete d_subtheories[i]; + delete d_subtheories[i]; } } void TheoryBV::setMasterEqualityEngine(eq::EqualityEngine* eq) { - dynamic_cast<CoreSolver*>(d_subtheoryMap[SUB_CORE])->setMasterEqualityEngine(eq); + if (options::bvEquality()) { + dynamic_cast<CoreSolver*>(d_subtheoryMap[SUB_CORE])->setMasterEqualityEngine(eq); + } } TheoryBV::Statistics::Statistics(): @@ -110,7 +113,7 @@ void TheoryBV::preRegisterTerm(TNode node) { return; } for (unsigned i = 0; i < d_subtheories.size(); ++i) { - d_subtheories[i]->preRegister(node); + d_subtheories[i]->preRegister(node); } } @@ -131,22 +134,22 @@ void TheoryBV::checkForLemma(TNode fact) { if (fact[0].getKind() == kind::BITVECTOR_UREM_TOTAL) { TNode urem = fact[0]; TNode result = fact[1]; - TNode divisor = urem[1]; + TNode divisor = urem[1]; Node result_ult_div = mkNode(kind::BITVECTOR_ULT, result, divisor); Node divisor_eq_0 = mkNode(kind::EQUAL, divisor, - mkConst(BitVector(getSize(divisor), 0u))); + mkConst(BitVector(getSize(divisor), 0u))); Node split = utils::mkNode(kind::OR, divisor_eq_0, mkNode(kind::NOT, fact), result_ult_div); lemma(split); } if (fact[1].getKind() == kind::BITVECTOR_UREM_TOTAL) { TNode urem = fact[1]; TNode result = fact[0]; - TNode divisor = urem[1]; + TNode divisor = urem[1]; Node result_ult_div = mkNode(kind::BITVECTOR_ULT, result, divisor); Node divisor_eq_0 = mkNode(kind::EQUAL, divisor, - mkConst(BitVector(getSize(divisor), 0u))); + mkConst(BitVector(getSize(divisor), 0u))); Node split = utils::mkNode(kind::OR, divisor_eq_0, mkNode(kind::NOT, fact), result_ult_div); lemma(split); } @@ -162,9 +165,9 @@ void TheoryBV::check(Effort e) } if (Theory::fullEffort(e)) { - ++(d_statistics.d_numCallsToCheckFullEffort); + ++(d_statistics.d_numCallsToCheckFullEffort); } else { - ++(d_statistics.d_numCallsToCheckStandardEffort); + ++(d_statistics.d_numCallsToCheckStandardEffort); } // if we are already in conflict just return the conflict if (inConflict()) { @@ -174,28 +177,29 @@ void TheoryBV::check(Effort e) while (!done()) { TNode fact = get().assertion; - checkForLemma(fact); + checkForLemma(fact); + for (unsigned i = 0; i < d_subtheories.size(); ++i) { - d_subtheories[i]->assertFact(fact); + d_subtheories[i]->assertFact(fact); } } bool ok = true; bool complete = false; for (unsigned i = 0; i < d_subtheories.size(); ++i) { - Assert (!inConflict()); + Assert (!inConflict()); ok = d_subtheories[i]->check(e); - complete = d_subtheories[i]->isComplete(); + complete = d_subtheories[i]->isComplete(); if (!ok) { // if we are in a conflict no need to check with other theories Assert (inConflict()); sendConflict(); - return; + return; } if (complete) { // if the last subtheory was complete we stop - return; + return; } } } @@ -205,8 +209,8 @@ void TheoryBV::collectModelInfo( TheoryModel* m, bool fullModel ){ // Assert (fullModel); // can only query full model for (unsigned i = 0; i < d_subtheories.size(); ++i) { if (d_subtheories[i]->isComplete()) { - d_subtheories[i]->collectModelInfo(m); - return; + d_subtheories[i]->collectModelInfo(m, fullModel); + return; } } } @@ -215,10 +219,10 @@ Node TheoryBV::getModelValue(TNode var) { Assert(!inConflict()); for (unsigned i = 0; i < d_subtheories.size(); ++i) { if (d_subtheories[i]->isComplete()) { - return d_subtheories[i]->getModelValue(var); + return d_subtheories[i]->getModelValue(var); } } - Unreachable(); + Unreachable(); } void TheoryBV::propagate(Effort e) { @@ -236,7 +240,7 @@ void TheoryBV::propagate(Effort e) { bool ok = true; for (; d_literalsToPropagateIndex < d_literalsToPropagate.size() && ok; d_literalsToPropagateIndex = d_literalsToPropagateIndex + 1) { TNode literal = d_literalsToPropagate[d_literalsToPropagateIndex]; - // temporary fix for incremental bit-blasting + // temporary fix for incremental bit-blasting if (d_valuation.isSatLiteral(literal)) { Debug("bitvector::propagate") << "TheoryBV:: propagating " << literal <<"\n"; ok = d_out->propagate(literal); @@ -286,14 +290,14 @@ Node TheoryBV::ppRewrite(TNode t) if (options::bitvectorCoreSolver() && t.getKind() == kind::EQUAL) { std::vector<Node> equalities; Slicer::splitEqualities(t, equalities); - return utils::mkAnd(equalities); + return utils::mkAnd(equalities); } - + return t; } void TheoryBV::presolve() { - Debug("bitvector") << "TheoryBV::presolve" << endl; + Debug("bitvector") << "TheoryBV::presolve" << endl; } bool TheoryBV::storePropagation(TNode literal, SubTheory subtheory) @@ -318,7 +322,7 @@ bool TheoryBV::storePropagation(TNode literal, SubTheory subtheory) // Safe to ignore this one, subtheory should produce a conflict return true; } - + d_propagatedBy[literal] = subtheory; } @@ -366,7 +370,7 @@ Node TheoryBV::explain(TNode node) { void TheoryBV::addSharedTerm(TNode t) { Debug("bitvector::sharing") << indent() << "TheoryBV::addSharedTerm(" << t << ")" << std::endl; d_sharedTermsSet.insert(t); - if (!options::bitvectorEagerBitblast() && d_useEqualityEngine) { + if (!options::bitvectorEagerBitblast() && options::bvEquality()) { for (unsigned i = 0; i < d_subtheories.size(); ++i) { d_subtheories[i]->addSharedTerm(t); } @@ -379,11 +383,11 @@ EqualityStatus TheoryBV::getEqualityStatus(TNode a, TNode b) if (options::bitvectorEagerBitblast()) { return EQUALITY_UNKNOWN; } - + for (unsigned i = 0; i < d_subtheories.size(); ++i) { EqualityStatus status = d_subtheories[i]->getEqualityStatus(a, b); if (status != EQUALITY_UNKNOWN) { - return status; + return status; } } return EQUALITY_UNKNOWN; ; diff --git a/src/theory/bv/theory_bv_rewrite_rules.h b/src/theory/bv/theory_bv_rewrite_rules.h index baaf7e133..8426afb59 100644 --- a/src/theory/bv/theory_bv_rewrite_rules.h +++ b/src/theory/bv/theory_bv_rewrite_rules.h @@ -66,6 +66,9 @@ enum RewriteRuleId { SremEliminate, ZeroExtendEliminate, SignExtendEliminate, + BVToNatEliminate, + IntToBVEliminate, + /// ground term evaluation EvalEquals, EvalConcat, @@ -173,13 +176,15 @@ inline std::ostream& operator << (std::ostream& out, RewriteRuleId ruleId) { case FailEq: out << "FailEq"; return out; case SimplifyEq: out << "SimplifyEq"; return out; case ReflexivityEq: out << "ReflexivityEq"; return out; - case UgtEliminate: out << "UgtEliminate"; return out; - case SgtEliminate: out << "SgtEliminate"; return out; - case UgeEliminate: out << "UgeEliminate"; return out; - case SgeEliminate: out << "SgeEliminate"; return out; + case UgtEliminate: out << "UgtEliminate"; return out; + case SgtEliminate: out << "SgtEliminate"; return out; + case UgeEliminate: out << "UgeEliminate"; return out; + case SgeEliminate: out << "SgeEliminate"; return out; case RepeatEliminate: out << "RepeatEliminate"; return out; case RotateLeftEliminate: out << "RotateLeftEliminate"; return out; case RotateRightEliminate:out << "RotateRightEliminate";return out; + case BVToNatEliminate: out << "BVToNatEliminate"; return out; + case IntToBVEliminate: out << "IntToBVEliminate"; return out; case NandEliminate: out << "NandEliminate"; return out; case NorEliminate : out << "NorEliminate"; return out; case SdivEliminate : out << "SdivEliminate"; return out; @@ -214,7 +219,7 @@ inline std::ostream& operator << (std::ostream& out, RewriteRuleId ruleId) { case ExtractBitwise : out << "ExtractBitwise"; return out; case ExtractNot : out << "ExtractNot"; return out; case ExtractArith : out << "ExtractArith"; return out; - case ExtractArith2 : out << "ExtractArith2"; return out; + case ExtractArith2 : out << "ExtractArith2"; return out; case DoubleNeg : out << "DoubleNeg"; return out; case NotConcat : out << "NotConcat"; return out; case NotAnd : out << "NotAnd"; return out; @@ -226,7 +231,7 @@ inline std::ostream& operator << (std::ostream& out, RewriteRuleId ruleId) { case BitwiseNotOr : out << "BitwiseNotOr"; return out; case XorNot : out << "XorNot"; return out; case LtSelf : out << "LtSelf"; return out; - case LteSelf : out << "LteSelf"; return out; + case LteSelf : out << "LteSelf"; return out; case UltZero : out << "UltZero"; return out; case UleZero : out << "UleZero"; return out; case ZeroUle : out << "ZeroUle"; return out; @@ -491,7 +496,8 @@ struct AllRewriteRules { RewriteRule<BitwiseEq> rule113; RewriteRule<UltOne> rule114; RewriteRule<SltZero> rule115; - + RewriteRule<BVToNatEliminate> rule116; + RewriteRule<IntToBVEliminate> rule117; }; template<> inline diff --git a/src/theory/bv/theory_bv_rewrite_rules_constant_evaluation.h b/src/theory/bv/theory_bv_rewrite_rules_constant_evaluation.h index a5368d2c9..9f3d12415 100644 --- a/src/theory/bv/theory_bv_rewrite_rules_constant_evaluation.h +++ b/src/theory/bv/theory_bv_rewrite_rules_constant_evaluation.h @@ -198,8 +198,9 @@ Node RewriteRule<EvalNeg>::apply(TNode node) { } template<> inline bool RewriteRule<EvalUdiv>::applies(TNode node) { - return (node.getKind() == kind::BITVECTOR_UDIV_TOTAL && - utils::isBVGroundTerm(node)); + return (utils::isBVGroundTerm(node) && + (node.getKind() == kind::BITVECTOR_UDIV_TOTAL || + (node.getKind() == kind::BITVECTOR_UDIV && node[1].isConst()))); } template<> inline @@ -213,8 +214,9 @@ Node RewriteRule<EvalUdiv>::apply(TNode node) { } template<> inline bool RewriteRule<EvalUrem>::applies(TNode node) { - return (node.getKind() == kind::BITVECTOR_UREM_TOTAL && - utils::isBVGroundTerm(node)); + return (utils::isBVGroundTerm(node) && + (node.getKind() == kind::BITVECTOR_UREM_TOTAL || + (node.getKind() == kind::BITVECTOR_UREM && node[1].isConst()))); } template<> inline diff --git a/src/theory/bv/theory_bv_rewrite_rules_operator_elimination.h b/src/theory/bv/theory_bv_rewrite_rules_operator_elimination.h index cf36633fa..b513dbf90 100644 --- a/src/theory/bv/theory_bv_rewrite_rules_operator_elimination.h +++ b/src/theory/bv/theory_bv_rewrite_rules_operator_elimination.h @@ -231,6 +231,57 @@ Node RewriteRule<RotateRightEliminate>::apply(TNode node) { } template<> +bool RewriteRule<BVToNatEliminate>::applies(TNode node) { + return (node.getKind() == kind::BITVECTOR_TO_NAT); +} + +template<> +Node RewriteRule<BVToNatEliminate>::apply(TNode node) { + Debug("bv-rewrite") << "RewriteRule<BVToNatEliminate>(" << node << ")" << std::endl; + + const unsigned size = utils::getSize(node[0]); + NodeManager* const nm = NodeManager::currentNM(); + const Node z = nm->mkConst(Rational(0)); + const Node bvone = nm->mkConst(BitVector(1u, 1u)); + + NodeBuilder<> result(kind::PLUS); + Integer i = 1; + for(unsigned bit = 0; bit < size; ++bit, i *= 2) { + Node cond = nm->mkNode(kind::EQUAL, nm->mkNode(nm->mkConst(BitVectorExtract(bit, bit)), node[0]), bvone); + result << nm->mkNode(kind::ITE, cond, nm->mkConst(Rational(i)), z); + } + + return Node(result); +} + +template<> +bool RewriteRule<IntToBVEliminate>::applies(TNode node) { + return (node.getKind() == kind::INT_TO_BITVECTOR); +} + +template<> +Node RewriteRule<IntToBVEliminate>::apply(TNode node) { + Debug("bv-rewrite") << "RewriteRule<IntToBVEliminate>(" << node << ")" << std::endl; + + const unsigned size = node.getOperator().getConst<IntToBitVector>().size; + NodeManager* const nm = NodeManager::currentNM(); + const Node bvzero = nm->mkConst(BitVector(1u, 0u)); + const Node bvone = nm->mkConst(BitVector(1u, 1u)); + + std::vector<Node> v; + Integer i = 2; + while(v.size() < size) { + Node cond = nm->mkNode(kind::GEQ, nm->mkNode(kind::INTS_MODULUS_TOTAL, node[0], nm->mkConst(Rational(i))), nm->mkConst(Rational(i, 2))); + v.push_back(nm->mkNode(kind::ITE, cond, bvone, bvzero)); + i *= 2; + } + + NodeBuilder<> result(kind::BITVECTOR_CONCAT); + result.append(v.rbegin(), v.rend()); + return Node(result); +} + +template<> bool RewriteRule<NandEliminate>::applies(TNode node) { return (node.getKind() == kind::BITVECTOR_NAND && node.getNumChildren() == 2); diff --git a/src/theory/bv/theory_bv_rewrite_rules_simplification.h b/src/theory/bv/theory_bv_rewrite_rules_simplification.h index d660dde29..ff7d67cb0 100644 --- a/src/theory/bv/theory_bv_rewrite_rules_simplification.h +++ b/src/theory/bv/theory_bv_rewrite_rules_simplification.h @@ -45,7 +45,9 @@ template<> inline Node RewriteRule<ShlByConst>::apply(TNode node) { Debug("bv-rewrite") << "RewriteRule<ShlByConst>(" << node << ")" << std::endl; Integer amount = node[1].getConst<BitVector>().toInteger(); - + if (amount == 0) { + return node[0]; + } Node a = node[0]; uint32_t size = utils::getSize(a); @@ -59,6 +61,7 @@ Node RewriteRule<ShlByConst>::apply(TNode node) { Assert(amount < Integer(1).multiplyByPow2(32)); uint32_t uint32_amount = amount.toUnsignedInt(); + Node left = utils::mkExtract(a, size - 1 - uint32_amount, 0); Node right = utils::mkConst(BitVector(uint32_amount, Integer(0))); return utils::mkConcat(left, right); @@ -81,6 +84,9 @@ template<> inline Node RewriteRule<LshrByConst>::apply(TNode node) { Debug("bv-rewrite") << "RewriteRule<LshrByConst>(" << node << ")" << std::endl; Integer amount = node[1].getConst<BitVector>().toInteger(); + if (amount == 0) { + return node[0]; + } Node a = node[0]; uint32_t size = utils::getSize(a); @@ -117,7 +123,10 @@ template<> inline Node RewriteRule<AshrByConst>::apply(TNode node) { Debug("bv-rewrite") << "RewriteRule<AshrByConst>(" << node << ")" << std::endl; Integer amount = node[1].getConst<BitVector>().toInteger(); - + if (amount == 0) { + return node[0]; + } + Node a = node[0]; uint32_t size = utils::getSize(a); Node sign_bit = utils::mkExtract(a, size-1, size-1); @@ -812,7 +821,9 @@ Node RewriteRule<UdivPow2>::apply(TNode node) { Debug("bv-rewrite") << "RewriteRule<UdivPow2>(" << node << ")" << std::endl; Node a = node[0]; unsigned power = utils::isPow2Const(node[1]) -1; - + if (power == 0) { + return a; + } Node extract = utils::mkExtract(a, utils::getSize(node) - 1, power); Node zeros = utils::mkConst(power, 0); diff --git a/src/theory/bv/theory_bv_rewriter.cpp b/src/theory/bv/theory_bv_rewriter.cpp index 7844e5b92..2467ae3f1 100644 --- a/src/theory/bv/theory_bv_rewriter.cpp +++ b/src/theory/bv/theory_bv_rewriter.cpp @@ -70,7 +70,7 @@ RewriteResponse TheoryBVRewriter::postRewrite(TNode node) { return res; } -RewriteResponse TheoryBVRewriter::RewriteUlt(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteUlt(TNode node, bool prerewrite) { // reduce common subexpressions on both sides Node resultNode = LinearRewriteStrategy < RewriteRule<EvalUlt>, @@ -82,7 +82,7 @@ RewriteResponse TheoryBVRewriter::RewriteUlt(TNode node, bool preregister) { return RewriteResponse(REWRITE_DONE, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteSlt(TNode node, bool preregister){ +RewriteResponse TheoryBVRewriter::RewriteSlt(TNode node, bool prerewrite){ Node resultNode = LinearRewriteStrategy < RewriteRule < EvalSlt > >::apply(node); @@ -97,7 +97,7 @@ RewriteResponse TheoryBVRewriter::RewriteSlt(TNode node, bool preregister){ // return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteUle(TNode node, bool preregister){ +RewriteResponse TheoryBVRewriter::RewriteUle(TNode node, bool prerewrite){ Node resultNode = LinearRewriteStrategy < RewriteRule<EvalUle>, RewriteRule<UleMax>, @@ -109,7 +109,7 @@ RewriteResponse TheoryBVRewriter::RewriteUle(TNode node, bool preregister){ return RewriteResponse(resultNode == node ? REWRITE_DONE : REWRITE_AGAIN, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteSle(TNode node, bool preregister){ +RewriteResponse TheoryBVRewriter::RewriteSle(TNode node, bool prerewrite){ Node resultNode = LinearRewriteStrategy < RewriteRule <EvalSle>, RewriteRule <SleEliminate> @@ -117,7 +117,7 @@ RewriteResponse TheoryBVRewriter::RewriteSle(TNode node, bool preregister){ return RewriteResponse(resultNode == node? REWRITE_DONE : REWRITE_AGAIN, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteUgt(TNode node, bool preregister){ +RewriteResponse TheoryBVRewriter::RewriteUgt(TNode node, bool prerewrite){ Node resultNode = LinearRewriteStrategy < RewriteRule<UgtEliminate> >::apply(node); @@ -125,7 +125,7 @@ RewriteResponse TheoryBVRewriter::RewriteUgt(TNode node, bool preregister){ return RewriteResponse(REWRITE_AGAIN, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteSgt(TNode node, bool preregister){ +RewriteResponse TheoryBVRewriter::RewriteSgt(TNode node, bool prerewrite){ Node resultNode = LinearRewriteStrategy < RewriteRule<SgtEliminate> //RewriteRule<SltEliminate> @@ -134,7 +134,7 @@ RewriteResponse TheoryBVRewriter::RewriteSgt(TNode node, bool preregister){ return RewriteResponse(REWRITE_AGAIN, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteUge(TNode node, bool preregister){ +RewriteResponse TheoryBVRewriter::RewriteUge(TNode node, bool prerewrite){ Node resultNode = LinearRewriteStrategy < RewriteRule<UgeEliminate> >::apply(node); @@ -142,7 +142,7 @@ RewriteResponse TheoryBVRewriter::RewriteUge(TNode node, bool preregister){ return RewriteResponse(REWRITE_AGAIN, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteSge(TNode node, bool preregister){ +RewriteResponse TheoryBVRewriter::RewriteSge(TNode node, bool prerewrite){ Node resultNode = LinearRewriteStrategy < RewriteRule<SgeEliminate> // RewriteRule<SleEliminate> @@ -151,7 +151,7 @@ RewriteResponse TheoryBVRewriter::RewriteSge(TNode node, bool preregister){ return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteNot(TNode node, bool preregister){ +RewriteResponse TheoryBVRewriter::RewriteNot(TNode node, bool prerewrite){ Node resultNode = node; if(RewriteRule<NotXor>::applies(node)) { @@ -166,7 +166,7 @@ RewriteResponse TheoryBVRewriter::RewriteNot(TNode node, bool preregister){ return RewriteResponse(REWRITE_DONE, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteExtract(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteExtract(TNode node, bool prerewrite) { Node resultNode = node; if (RewriteRule<ExtractConcat>::applies(node)) { @@ -206,7 +206,7 @@ RewriteResponse TheoryBVRewriter::RewriteExtract(TNode node, bool preregister) { } -RewriteResponse TheoryBVRewriter::RewriteConcat(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteConcat(TNode node, bool prerewrite) { Node resultNode = LinearRewriteStrategy < RewriteRule<ConcatFlatten>, @@ -220,63 +220,70 @@ RewriteResponse TheoryBVRewriter::RewriteConcat(TNode node, bool preregister) { return RewriteResponse(REWRITE_DONE, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteAnd(TNode node, bool preregister){ +RewriteResponse TheoryBVRewriter::RewriteAnd(TNode node, bool prerewrite) { Node resultNode = node; - resultNode = LinearRewriteStrategy < RewriteRule<FlattenAssocCommut>, - RewriteRule<AndSimplify>, - RewriteRule<BitwiseSlicing> + RewriteRule<AndSimplify> >::apply(node); - if (resultNode.getKind() != node.getKind()) { - return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); + if (!prerewrite) { + resultNode = LinearRewriteStrategy + < RewriteRule<BitwiseSlicing> + >::apply(resultNode); + + if (resultNode.getKind() != node.getKind()) { + return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); + } } return RewriteResponse(REWRITE_DONE, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteOr(TNode node, bool preregister){ +RewriteResponse TheoryBVRewriter::RewriteOr(TNode node, bool prerewrite){ Node resultNode = node; - resultNode = LinearRewriteStrategy < RewriteRule<FlattenAssocCommut>, - RewriteRule<OrSimplify>, - RewriteRule<BitwiseSlicing> - >::apply(node); + RewriteRule<OrSimplify> + >::apply(node); - if (resultNode.getKind() != node.getKind()) { - return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); + if (!prerewrite) { + resultNode = LinearRewriteStrategy + < RewriteRule<BitwiseSlicing> + >::apply(resultNode); + + if (resultNode.getKind() != node.getKind()) { + return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); + } } return RewriteResponse(REWRITE_DONE, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteXor(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteXor(TNode node, bool prerewrite) { Node resultNode = node; - resultNode = LinearRewriteStrategy < RewriteRule<FlattenAssocCommut>, // flatten the expression RewriteRule<XorSimplify>, // simplify duplicates and constants RewriteRule<XorZero>, // checks if the constant part is zero and eliminates it RewriteRule<BitwiseSlicing> - >::apply(node); - - // this simplification introduces new terms and might require further - // rewriting - if (RewriteRule<XorOne>::applies(resultNode)) { - resultNode = RewriteRule<XorOne>::run<false> (resultNode); - return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); - } + >::apply(node); - if (resultNode.getKind() != node.getKind()) { - return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); + if (!prerewrite) { + resultNode = LinearRewriteStrategy + < RewriteRule<XorOne>, + RewriteRule <BitwiseSlicing> + >::apply(resultNode); + + if (resultNode.getKind() != node.getKind()) { + return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); + } } return RewriteResponse(REWRITE_DONE, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteXnor(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteXnor(TNode node, bool prerewrite) { Node resultNode = LinearRewriteStrategy < RewriteRule<XnorEliminate> >::apply(node); @@ -284,7 +291,7 @@ RewriteResponse TheoryBVRewriter::RewriteXnor(TNode node, bool preregister) { return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteNand(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteNand(TNode node, bool prerewrite) { Node resultNode = LinearRewriteStrategy < RewriteRule<NandEliminate> >::apply(node); @@ -292,7 +299,7 @@ RewriteResponse TheoryBVRewriter::RewriteNand(TNode node, bool preregister) { return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteNor(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteNor(TNode node, bool prerewrite) { Node resultNode = LinearRewriteStrategy < RewriteRule<NorEliminate> >::apply(node); @@ -300,7 +307,7 @@ RewriteResponse TheoryBVRewriter::RewriteNor(TNode node, bool preregister) { return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteComp(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteComp(TNode node, bool prerewrite) { Node resultNode = LinearRewriteStrategy < RewriteRule<CompEliminate> >::apply(node); @@ -308,7 +315,7 @@ RewriteResponse TheoryBVRewriter::RewriteComp(TNode node, bool preregister) { return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteMult(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteMult(TNode node, bool prerewrite) { Node resultNode = node; resultNode = LinearRewriteStrategy @@ -318,7 +325,7 @@ RewriteResponse TheoryBVRewriter::RewriteMult(TNode node, bool preregister) { >::apply(node); // only apply if every subterm was already rewritten - if (!preregister) { + if (!prerewrite) { // distributes multiplication by constant over +, - and unary - if(RewriteRule<MultDistribConst>::applies(resultNode)) { resultNode = RewriteRule<MultDistribConst>::run<false>(resultNode); @@ -333,23 +340,28 @@ RewriteResponse TheoryBVRewriter::RewriteMult(TNode node, bool preregister) { return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); } -RewriteResponse TheoryBVRewriter::RewritePlus(TNode node, bool preregister) { - if (preregister) { - return RewriteResponse(REWRITE_DONE, node); +RewriteResponse TheoryBVRewriter::RewritePlus(TNode node, bool prerewrite) { + Node resultNode = node; + if (prerewrite) { + resultNode = LinearRewriteStrategy + < RewriteRule<FlattenAssocCommut> + >::apply(node); + return RewriteResponse(REWRITE_DONE, resultNode); } - Node resultNode = LinearRewriteStrategy - < RewriteRule<FlattenAssocCommut>, + + resultNode = LinearRewriteStrategy + < RewriteRule<FlattenAssocCommut>, RewriteRule<PlusCombineLikeTerms> - // RewriteRule<PlusLiftConcat> >::apply(node); - if (resultNode == node) { - return RewriteResponse(REWRITE_DONE, resultNode); - } else { - return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); + + if (node != resultNode) { + return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); } + + return RewriteResponse(REWRITE_DONE, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteSub(TNode node, bool preregister){ +RewriteResponse TheoryBVRewriter::RewriteSub(TNode node, bool prerewrite){ // return RewriteResponse(REWRITE_DONE, node); Node resultNode = LinearRewriteStrategy < RewriteRule<SubEliminate> @@ -358,7 +370,7 @@ RewriteResponse TheoryBVRewriter::RewriteSub(TNode node, bool preregister){ return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteNeg(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteNeg(TNode node, bool prerewrite) { Node resultNode = node; resultNode = LinearRewriteStrategy @@ -372,7 +384,7 @@ RewriteResponse TheoryBVRewriter::RewriteNeg(TNode node, bool preregister) { return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); } - if(!preregister) { + if(!prerewrite) { if (RewriteRule<NegMult>::applies(node)) { resultNode = RewriteRule<NegMult>::run<false>(node); return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); @@ -382,8 +394,27 @@ RewriteResponse TheoryBVRewriter::RewriteNeg(TNode node, bool preregister) { return RewriteResponse(REWRITE_DONE, resultNode); } +RewriteResponse TheoryBVRewriter::RewriteUdiv(TNode node, bool prerewrite){ + Node resultNode = node; + + if(node[1].isConst() && node[1].getConst<BitVector>().getValue() != 0) { + return RewriteUdivTotal(node, prerewrite); + } -RewriteResponse TheoryBVRewriter::RewriteUdivTotal(TNode node, bool preregister){ + return RewriteResponse(REWRITE_DONE, resultNode); +} + +RewriteResponse TheoryBVRewriter::RewriteUrem(TNode node, bool prerewrite){ + Node resultNode = node; + + if(node[1].isConst() && node[1].getConst<BitVector>().getValue() != 0) { + return RewriteUremTotal(node, prerewrite); + } + + return RewriteResponse(REWRITE_DONE, resultNode); +} + +RewriteResponse TheoryBVRewriter::RewriteUdivTotal(TNode node, bool prerewrite){ Node resultNode = node; if(RewriteRule<UdivPow2>::applies(node)) { @@ -400,7 +431,7 @@ RewriteResponse TheoryBVRewriter::RewriteUdivTotal(TNode node, bool preregister) return RewriteResponse(REWRITE_DONE, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteUremTotal(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteUremTotal(TNode node, bool prerewrite) { Node resultNode = node; if(RewriteRule<UremPow2>::applies(node)) { @@ -416,7 +447,7 @@ RewriteResponse TheoryBVRewriter::RewriteUremTotal(TNode node, bool preregister) return RewriteResponse(REWRITE_DONE, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteSmod(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteSmod(TNode node, bool prerewrite) { Node resultNode = LinearRewriteStrategy < RewriteRule<SmodEliminate> >::apply(node); @@ -424,7 +455,7 @@ RewriteResponse TheoryBVRewriter::RewriteSmod(TNode node, bool preregister) { return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteSdiv(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteSdiv(TNode node, bool prerewrite) { Node resultNode = LinearRewriteStrategy < RewriteRule<SdivEliminate> >::apply(node); @@ -432,14 +463,14 @@ RewriteResponse TheoryBVRewriter::RewriteSdiv(TNode node, bool preregister) { return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteSrem(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteSrem(TNode node, bool prerewrite) { Node resultNode = LinearRewriteStrategy < RewriteRule<SremEliminate> >::apply(node); return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteShl(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteShl(TNode node, bool prerewrite) { Node resultNode = node; if(RewriteRule<ShlByConst>::applies(node)) { resultNode = RewriteRule<ShlByConst>::run <false> (node); @@ -454,7 +485,7 @@ RewriteResponse TheoryBVRewriter::RewriteShl(TNode node, bool preregister) { return RewriteResponse(REWRITE_DONE, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteLshr(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteLshr(TNode node, bool prerewrite) { Node resultNode = node; if(RewriteRule<LshrByConst>::applies(node)) { resultNode = RewriteRule<LshrByConst>::run <false> (node); @@ -469,7 +500,7 @@ RewriteResponse TheoryBVRewriter::RewriteLshr(TNode node, bool preregister) { return RewriteResponse(REWRITE_DONE, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteAshr(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteAshr(TNode node, bool prerewrite) { Node resultNode = node; if(RewriteRule<AshrByConst>::applies(node)) { resultNode = RewriteRule<AshrByConst>::run <false> (node); @@ -485,7 +516,7 @@ RewriteResponse TheoryBVRewriter::RewriteAshr(TNode node, bool preregister) { } -RewriteResponse TheoryBVRewriter::RewriteRepeat(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteRepeat(TNode node, bool prerewrite) { Node resultNode = LinearRewriteStrategy < RewriteRule<RepeatEliminate > >::apply(node); @@ -493,7 +524,7 @@ RewriteResponse TheoryBVRewriter::RewriteRepeat(TNode node, bool preregister) { return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteZeroExtend(TNode node, bool preregister){ +RewriteResponse TheoryBVRewriter::RewriteZeroExtend(TNode node, bool prerewrite){ Node resultNode = LinearRewriteStrategy < RewriteRule<ZeroExtendEliminate > >::apply(node); @@ -501,7 +532,7 @@ RewriteResponse TheoryBVRewriter::RewriteZeroExtend(TNode node, bool preregister return RewriteResponse(REWRITE_AGAIN, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteSignExtend(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteSignExtend(TNode node, bool prerewrite) { Node resultNode = LinearRewriteStrategy < RewriteRule<EvalSignExtend> >::apply(node); @@ -511,7 +542,7 @@ RewriteResponse TheoryBVRewriter::RewriteSignExtend(TNode node, bool preregister } -RewriteResponse TheoryBVRewriter::RewriteRotateRight(TNode node, bool preregister) { +RewriteResponse TheoryBVRewriter::RewriteRotateRight(TNode node, bool prerewrite) { Node resultNode = LinearRewriteStrategy < RewriteRule<RotateRightEliminate > >::apply(node); @@ -519,16 +550,32 @@ RewriteResponse TheoryBVRewriter::RewriteRotateRight(TNode node, bool preregiste return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteRotateLeft(TNode node, bool preregister){ +RewriteResponse TheoryBVRewriter::RewriteRotateLeft(TNode node, bool prerewrite){ Node resultNode = LinearRewriteStrategy < RewriteRule<RotateLeftEliminate > - >::apply(node); + >::apply(node); return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); } -RewriteResponse TheoryBVRewriter::RewriteEqual(TNode node, bool preregister) { - if (preregister) { +RewriteResponse TheoryBVRewriter::RewriteBVToNat(TNode node, bool prerewrite) { + Node resultNode = LinearRewriteStrategy + < RewriteRule<BVToNatEliminate> + >::apply(node); + + return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); +} + +RewriteResponse TheoryBVRewriter::RewriteIntToBV(TNode node, bool prerewrite) { + Node resultNode = LinearRewriteStrategy + < RewriteRule<IntToBVEliminate> + >::apply(node); + + return RewriteResponse(REWRITE_AGAIN_FULL, resultNode); +} + +RewriteResponse TheoryBVRewriter::RewriteEqual(TNode node, bool prerewrite) { + if (prerewrite) { Node resultNode = LinearRewriteStrategy < RewriteRule<FailEq>, RewriteRule<SimplifyEq>, @@ -593,8 +640,8 @@ void TheoryBVRewriter::initializeRewrites() { d_rewriteTable [ kind::BITVECTOR_PLUS ] = RewritePlus; d_rewriteTable [ kind::BITVECTOR_SUB ] = RewriteSub; d_rewriteTable [ kind::BITVECTOR_NEG ] = RewriteNeg; - // d_rewriteTable [ kind::BITVECTOR_UDIV ] = RewriteUdiv; - // d_rewriteTable [ kind::BITVECTOR_UREM ] = RewriteUrem; + d_rewriteTable [ kind::BITVECTOR_UDIV ] = RewriteUdiv; + d_rewriteTable [ kind::BITVECTOR_UREM ] = RewriteUrem; d_rewriteTable [ kind::BITVECTOR_UDIV_TOTAL ] = RewriteUdivTotal; d_rewriteTable [ kind::BITVECTOR_UREM_TOTAL ] = RewriteUremTotal; d_rewriteTable [ kind::BITVECTOR_SMOD ] = RewriteSmod; @@ -609,6 +656,9 @@ void TheoryBVRewriter::initializeRewrites() { d_rewriteTable [ kind::BITVECTOR_SIGN_EXTEND ] = RewriteSignExtend; d_rewriteTable [ kind::BITVECTOR_ROTATE_RIGHT ] = RewriteRotateRight; d_rewriteTable [ kind::BITVECTOR_ROTATE_LEFT ] = RewriteRotateLeft; + + d_rewriteTable [ kind::BITVECTOR_TO_NAT ] = RewriteBVToNat; + d_rewriteTable [ kind::INT_TO_BITVECTOR ] = RewriteIntToBV; } Node TheoryBVRewriter::eliminateBVSDiv(TNode node) { diff --git a/src/theory/bv/theory_bv_rewriter.h b/src/theory/bv/theory_bv_rewriter.h index 183b5e8d5..def8e24fe 100644 --- a/src/theory/bv/theory_bv_rewriter.h +++ b/src/theory/bv/theory_bv_rewriter.h @@ -78,6 +78,9 @@ class TheoryBVRewriter { static RewriteResponse RewriteRotateRight(TNode node, bool prerewrite = false); static RewriteResponse RewriteRotateLeft(TNode node, bool prerewrite = false); + static RewriteResponse RewriteBVToNat(TNode node, bool prerewrite = false); + static RewriteResponse RewriteIntToBV(TNode node, bool prerewrite = false); + public: static RewriteResponse postRewrite(TNode node); diff --git a/src/theory/bv/theory_bv_type_rules.h b/src/theory/bv/theory_bv_type_rules.h index 00284e7ae..67dae0cfa 100644 --- a/src/theory/bv/theory_bv_type_rules.h +++ b/src/theory/bv/theory_bv_type_rules.h @@ -29,6 +29,11 @@ class BitVectorConstantTypeRule { public: inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) throw (TypeCheckingExceptionPrivate, AssertionException) { + if (check) { + if (n.getConst<BitVector>().getSize() == 0) { + throw TypeCheckingExceptionPrivate(n, "constant of size 0"); + } + } return nodeManager->mkBitVectorType(n.getConst<BitVector>().getSize()); } }; @@ -190,6 +195,29 @@ public: } }; +class BitVectorConversionTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + if(n.getKind() == kind::BITVECTOR_TO_NAT) { + if(check && !n[0].getType(check).isBitVector()) { + throw TypeCheckingExceptionPrivate(n, "expecting bit-vector term"); + } + return nodeManager->integerType(); + } + + if(n.getKind() == kind::INT_TO_BITVECTOR) { + size_t bvSize = n.getOperator().getConst<IntToBitVector>(); + if(check && !n[0].getType(check).isInteger()) { + throw TypeCheckingExceptionPrivate(n, "expecting integer term"); + } + return nodeManager->mkBitVectorType(bvSize); + } + + InternalError("bv-conversion typerule invoked for non-bv-conversion kind"); + } +}; + class CardinalityComputer { public: inline static Cardinality computeCardinality(TypeNode type) { diff --git a/src/theory/bv/theory_bv_utils.h b/src/theory/bv/theory_bv_utils.h index 5847bac3e..ab6a615a2 100644 --- a/src/theory/bv/theory_bv_utils.h +++ b/src/theory/bv/theory_bv_utils.h @@ -357,7 +357,7 @@ inline Node flattenAnd(std::vector<TNode>& queue) { } -// neeed a better name, this is not technically a ground term +// need a better name, this is not technically a ground term inline bool isBVGroundTerm(TNode node) { if (node.getNumChildren() == 0) { return node.isConst(); diff --git a/src/theory/datatypes/datatypes_rewriter.h b/src/theory/datatypes/datatypes_rewriter.h index bc6d1f839..186444e0a 100644 --- a/src/theory/datatypes/datatypes_rewriter.h +++ b/src/theory/datatypes/datatypes_rewriter.h @@ -21,6 +21,7 @@ #include "theory/rewriter.h" #include "theory/datatypes/options.h" +#include "theory/type_enumerator.h" namespace CVC4 { namespace theory { @@ -137,10 +138,16 @@ public: return RewriteResponse(REWRITE_DONE, in[0][selectorIndex]); }else{ if( options::dtRewriteErrorSel() ){ - Node gt = in.getType().mkGroundTerm(); + Node gt; + if( in.getType().isSort() ){ + TypeEnumerator te(in.getType()); + gt = *te; + }else{ + gt = in.getType().mkGroundTerm(); + } TypeNode gtt = gt.getType(); //Assert( gtt.isDatatype() || gtt.isParametricDatatype() ); - if( !gtt.isInstantiatedDatatype() ){ + if( gtt.isDatatype() && !gtt.isInstantiatedDatatype() ){ gt = NodeManager::currentNM()->mkNode(kind::APPLY_TYPE_ASCRIPTION, NodeManager::currentNM()->mkConst(AscriptionType(in.getType().toType())), gt); } diff --git a/src/theory/datatypes/kinds b/src/theory/datatypes/kinds index 2e58677df..81ef32b32 100644 --- a/src/theory/datatypes/kinds +++ b/src/theory/datatypes/kinds @@ -88,7 +88,7 @@ typerule APPLY_TYPE_ASCRIPTION ::CVC4::theory::datatypes::DatatypeAscriptionType # constructor applications are constant if they are applied only to constants construle APPLY_CONSTRUCTOR ::CVC4::theory::datatypes::DatatypeConstructorTypeRule -operator TUPLE_TYPE 1: "tuple type" +operator TUPLE_TYPE 0: "tuple type" cardinality TUPLE_TYPE \ "::CVC4::theory::datatypes::TupleProperties::computeCardinality(%TYPE%)" \ "theory/datatypes/theory_datatypes_type_rules.h" @@ -100,7 +100,7 @@ enumerator TUPLE_TYPE \ "::CVC4::theory::datatypes::TupleEnumerator" \ "theory/datatypes/type_enumerator.h" -operator TUPLE 1: "a tuple" +operator TUPLE 0: "a tuple" typerule TUPLE ::CVC4::theory::datatypes::TupleTypeRule construle TUPLE ::CVC4::theory::datatypes::TupleProperties diff --git a/src/theory/datatypes/theory_datatypes.cpp b/src/theory/datatypes/theory_datatypes.cpp index 7f96232d6..a0651efb4 100644 --- a/src/theory/datatypes/theory_datatypes.cpp +++ b/src/theory/datatypes/theory_datatypes.cpp @@ -91,7 +91,7 @@ TheoryDatatypes::EqcInfo* TheoryDatatypes::getOrMakeEqcInfo( Node n, bool doMake } void TheoryDatatypes::check(Effort e) { - + Trace("datatypes-debug") << "Check effort " << e << std::endl; while(!done() && !d_conflict) { // Get all the assertions Assertion assertion = get(); @@ -117,80 +117,95 @@ void TheoryDatatypes::check(Effort e) { if( e == EFFORT_FULL ) { Debug("datatypes-split") << "Check for splits " << e << endl; - eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine ); - while( !eqcs_i.isFinished() ){ - Node n = (*eqcs_i); - if( DatatypesRewriter::isTermDatatype( n ) ){ - EqcInfo* eqc = getOrMakeEqcInfo( n, true ); - //if there are more than 1 possible constructors for eqc - if( eqc->d_constructor.get().isNull() && !hasLabel( eqc, n ) ) { - const Datatype& dt = ((DatatypeType)(n.getType()).toType()).getDatatype(); - //if only one constructor, then this term must be this constructor - if( dt.getNumConstructors()==1 ){ - Node t = NodeManager::currentNM()->mkNode( APPLY_TESTER, Node::fromExpr( dt[0].getTester() ), n ); - d_pending.push_back( t ); - d_pending_exp[ t ] = NodeManager::currentNM()->mkConst( true ); - Trace("datatypes-infer") << "DtInfer : " << t << ", trivial" << std::endl; - d_infer.push_back( t ); - }else{ - std::vector< bool > pcons; - getPossibleCons( eqc, n, pcons ); - //std::cout << "pcons " << n << " = "; - //for( int i=0; i<(int)pcons.size(); i++ ){ //std::cout << pcons[i] << " "; } - //std::cout << std::endl; - //check if we do not need to resolve the constructor type for this equivalence class. - // this is if there are no selectors for this equivalence class, its possible values are infinite, - // and we are not producing a model, then do not split. - int consIndex = -1; - bool needSplit = true; - for( unsigned int j=0; j<pcons.size(); j++ ) { - if( pcons[j] ) { - if( consIndex==-1 ){ - consIndex = j; + bool addedFact = false; + do { + eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine ); + while( !eqcs_i.isFinished() ){ + Node n = (*eqcs_i); + if( DatatypesRewriter::isTermDatatype( n ) ){ + Trace("datatypes-debug") << "Process equivalence class " << n << std::endl; + EqcInfo* eqc = getOrMakeEqcInfo( n, true ); + //if there are more than 1 possible constructors for eqc + if( eqc->d_constructor.get().isNull() && !hasLabel( eqc, n ) ) { + Trace("datatypes-debug") << "No constructor..." << std::endl; + const Datatype& dt = ((DatatypeType)(n.getType()).toType()).getDatatype(); + //if only one constructor, then this term must be this constructor + if( dt.getNumConstructors()==1 ){ + Node t = NodeManager::currentNM()->mkNode( APPLY_TESTER, Node::fromExpr( dt[0].getTester() ), n ); + d_pending.push_back( t ); + d_pending_exp[ t ] = NodeManager::currentNM()->mkConst( true ); + Trace("datatypes-infer") << "DtInfer : " << t << ", trivial" << std::endl; + d_infer.push_back( t ); + }else{ + std::vector< bool > pcons; + getPossibleCons( eqc, n, pcons ); + //std::cout << "pcons " << n << " = "; + //for( int i=0; i<(int)pcons.size(); i++ ){ //std::cout << pcons[i] << " "; } + //std::cout << std::endl; + //check if we do not need to resolve the constructor type for this equivalence class. + // this is if there are no selectors for this equivalence class, its possible values are infinite, + // and we are not producing a model, then do not split. + int consIndex = -1; + bool needSplit = true; + for( unsigned int j=0; j<pcons.size(); j++ ) { + if( pcons[j] ) { + if( consIndex==-1 ){ + consIndex = j; + } + if( !dt[ j ].isFinite() && !eqc->d_selectors ) { + needSplit = false; + } } - if( !dt[ j ].isFinite() && !eqc->d_selectors ) { - needSplit = false; + } + if( !needSplit && mustSpecifyAssignment() ){ + //for the sake of termination, we must choose the constructor of a ground term + //NEED GUARENTEE: groundTerm should not contain any subterms of the same type + //** TODO: this is probably not good enough, actually need fair enumeration strategy + Node groundTerm = n.getType().mkGroundTerm(); + int index = Datatype::indexOf( groundTerm.getOperator().toExpr() ); + if( pcons[index] ){ + consIndex = index; } + needSplit = true; } - } - if( !needSplit && mustSpecifyAssignment() ){ - //for the sake of termination, we must choose the constructor of a ground term - //NEED GUARENTEE: groundTerm should not contain any subterms of the same type - //** TODO: this is probably not good enough, actually need fair enumeration strategy - Node groundTerm = n.getType().mkGroundTerm(); - int index = Datatype::indexOf( groundTerm.getOperator().toExpr() ); - if( pcons[index] ){ - consIndex = index; + if( needSplit && consIndex!=-1 ) { + Node test = NodeManager::currentNM()->mkNode( APPLY_TESTER, Node::fromExpr( dt[consIndex].getTester() ), n ); + Trace("dt-split") << "*************Split for possible constructor " << dt[consIndex] << " for " << n << endl; + test = Rewriter::rewrite( test ); + NodeBuilder<> nb(kind::OR); + nb << test << test.notNode(); + Node lemma = nb; + d_out->lemma( lemma ); + d_out->requirePhase( test, true ); + return; + }else{ + Trace("dt-split") << "Do not split constructor for " << n << std::endl; } - needSplit = true; - } - if( needSplit && consIndex!=-1 ) { - Node test = NodeManager::currentNM()->mkNode( APPLY_TESTER, Node::fromExpr( dt[consIndex].getTester() ), n ); - Trace("dt-split") << "*************Split for possible constructor " << dt[consIndex] << " for " << n << endl; - test = Rewriter::rewrite( test ); - NodeBuilder<> nb(kind::OR); - nb << test << test.notNode(); - Node lemma = nb; - d_out->lemma( lemma ); - d_out->requirePhase( test, true ); - return; - }else{ - Trace("dt-split") << "Do not split constructor for " << n << std::endl; } + }else{ + Trace("datatypes-debug") << "Has constructor " << eqc->d_constructor.get() << std::endl; } } + ++eqcs_i; } - ++eqcs_i; - } - flushPendingFacts(); - if( !d_conflict ){ - if( options::dtRewriteErrorSel() ){ - collapseSelectors(); - flushPendingFacts(); + Trace("datatypes-debug") << "Flush pending facts..." << std::endl; + addedFact = !d_pending.empty() || !d_pending_merge.empty(); + flushPendingFacts(); + if( !d_conflict ){ + if( options::dtRewriteErrorSel() ){ + bool innerAddedFact = false; + do { + collapseSelectors(); + innerAddedFact = !d_pending.empty() || !d_pending_merge.empty(); + flushPendingFacts(); + }while( !d_conflict && innerAddedFact ); + } } - } + }while( !d_conflict && addedFact ); + Trace("datatypes-debug") << "Finished. " << d_conflict << std::endl; if( !d_conflict ){ - // printModelDebug(); + Trace("dt-model-test") << std::endl; + printModelDebug("dt-model-test"); } } @@ -1003,8 +1018,8 @@ bool TheoryDatatypes::mustCommunicateFact( Node n, Node exp ){ // (3) Instantiate : is_C( t ) => t = C( sel_1( t ) ... sel_n( t ) ) //We may need to communicate (3) outwards if the conclusions involve other theories Trace("dt-lemma-debug") << "Compute for " << exp << " => " << n << std::endl; + bool addLemma = false; if( ( n.getKind()==EQUAL || n.getKind()==IFF) && n[1].getKind()==APPLY_CONSTRUCTOR && exp.getKind()!=EQUAL ){ - bool addLemma = false; #if 1 const Datatype& dt = ((DatatypeType)(n[1].getType()).toType()).getDatatype(); addLemma = dt.involvesExternalType(); @@ -1028,6 +1043,11 @@ bool TheoryDatatypes::mustCommunicateFact( Node n, Node exp ){ } } } + //else if( exp.getKind()==APPLY_TESTER ){ + //if( n.getKind()==EQUAL && !DatatypesRewriter::isTermDatatype( n[0] ) ){ + // return true; + //} + //} Trace("dt-lemma-debug") << "Do not need to communicate " << n << std::endl; return false; } @@ -1066,6 +1086,10 @@ Node TheoryDatatypes::getRepresentative( Node a ){ void TheoryDatatypes::printModelDebug( const char* c ){ + if(! (Trace.isOn(c))) { + return; + } + Trace( c ) << "Datatypes model : " << std::endl; eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine ); while( !eqcs_i.isFinished() ){ diff --git a/src/theory/idl/Makefile b/src/theory/idl/Makefile new file mode 100644 index 000000000..75ae33c7e --- /dev/null +++ b/src/theory/idl/Makefile @@ -0,0 +1,4 @@ +topdir = ../../.. +srcdir = src/theory/idl + +include $(topdir)/Makefile.subdir diff --git a/src/theory/idl/Makefile.am b/src/theory/idl/Makefile.am new file mode 100644 index 000000000..4297e3bdb --- /dev/null +++ b/src/theory/idl/Makefile.am @@ -0,0 +1,19 @@ +AM_CPPFLAGS = \ + -D__BUILDING_CVC4LIB \ + -I@builddir@/../.. -I@srcdir@/../../include -I@srcdir@/../.. +AM_CXXFLAGS = -Wall -Wno-unknown-pragmas $(FLAG_VISIBILITY_HIDDEN) + +noinst_LTLIBRARIES = libidl.la + +libidl_la_SOURCES = \ + idl_model.h \ + idl_model.cpp \ + idl_assertion.h \ + idl_assertion.cpp \ + idl_assertion_db.h \ + idl_assertion_db.cpp \ + theory_idl.h \ + theory_idl.cpp + +EXTRA_DIST = \ + kinds diff --git a/src/theory/idl/idl_assertion.cpp b/src/theory/idl/idl_assertion.cpp new file mode 100644 index 000000000..1e725932b --- /dev/null +++ b/src/theory/idl/idl_assertion.cpp @@ -0,0 +1,213 @@ +/********************* */ +/*! \file idl_assertion.cpp + ** \verbatim + ** Original author: Dejan Jovanovic + ** 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 [[ Add one-line brief description here ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#include "theory/idl/idl_assertion.h" + +using namespace CVC4; +using namespace theory; +using namespace idl; + +IDLAssertion::IDLAssertion() +: d_op(kind::LAST_KIND) +{} + +IDLAssertion::IDLAssertion(TNode node) { + bool ok = parse(node, 1, false); + if (!ok) { + d_x = d_y = TNode::null(); + } else { + if (d_op == kind::GT) { + // Turn GT into LT x - y > c is the same as y - x < -c + std::swap(d_x, d_y); + d_c = -d_c; + d_op = kind::LT; + } + if (d_op == kind::GEQ) { + // Turn GT into LT x - y >= c is the same as y - x <= -c + std::swap(d_x, d_y); + d_c = -d_c; + d_op = kind::LEQ; + } + if (d_op == kind::LT) { + // Turn strict into non-strict x - y < c is the same as x - y <= c-1 + d_c = d_c - 1; + d_op = kind::LEQ; + } + } + d_original = node; +} + +IDLAssertion::IDLAssertion(const IDLAssertion& other) +: d_x(other.d_x) +, d_y(other.d_y) +, d_op(other.d_op) +, d_c(other.d_c) +, d_original(other.d_original) +{} + +bool IDLAssertion::propagate(IDLModel& model) const { + Debug("theory::idl::model") << model << std::endl; + Assert(ok()); + // Should be d_x - d_y <= d_c, or d_x - d_c <= d_y + Integer x_value = model.getValue(d_x); + Integer y_value = model.getValue(d_y); + if (x_value - y_value > d_c) { + model.setValue(d_y, x_value - d_c, IDLReason(d_x, d_original)); + Debug("theory::idl::model") << model << std::endl; + return true; + } else { + return false; + } +} + +void IDLAssertion::toStream(std::ostream& out) const { + out << "IDL[" << d_x << " - " << d_y << " " << d_op << " " << d_c << "]"; +} + +/** Negates the given arithmetic kind */ +static Kind negateOp(Kind op) { + switch (op) { + case kind::LT: + // not (a < b) = (a >= b) + return kind::GEQ; + case kind::LEQ: + // not (a <= b) = (a > b) + return kind::GT; + case kind::GT: + // not (a > b) = (a <= b) + return kind::LEQ; + case kind::GEQ: + // not (a >= b) = (a < b) + return kind::LT; + case kind::EQUAL: + // not (a = b) = (a != b) + return kind::DISTINCT; + case kind::DISTINCT: + // not (a != b) = (a = b) + return kind::EQUAL; + default: + Unreachable(); + break; + } + return kind::LAST_KIND; +} + +bool IDLAssertion::parse(TNode node, int c, bool negated) { + + // Only unit coefficients allowed + if (c != 1 && c != -1) { + return false; + } + + // Assume we're ok + bool ok = true; + + // The kind of the node + switch(node.getKind()) { + + case kind::NOT: + // We parse the negation + ok = parse(node[0], c, true); + // Setup the kind + if (ok) { + d_op = negateOp(d_op); + } + break; + + case kind::EQUAL: + case kind::LT: + case kind::LEQ: + case kind::GT: + case kind::GEQ: { + // All relation operators are parsed on both sides + d_op = node.getKind(); + ok = parse(node[0], c, negated); + if (ok) { + ok = parse(node[1],-c, negated); + } + break; + } + + case kind::CONST_RATIONAL: { + // Constants + Rational m = node.getConst<Rational>(); + if (m.isIntegral()) { + d_c += m.getNumerator() * (-c); + } else { + ok = false; + } + break; + } + case kind::MULT: { + // Only unit multiplication of variables + if (node.getNumChildren() == 2 && node[0].isConst()) { + Rational a = node[0].getConst<Rational>(); + if (a == 1 || a == -1) { + ok = parse(node[1], c * a.sgn(), negated); + } else { + ok = false; + } + } else { + ok = false; + } + break; + } + + case kind::PLUS: { + for(unsigned i = 0; i < node.getNumChildren(); ++i) { + ok = parse(node[i], c, negated); + if(!ok) { + break; + } + } + break; + } + + case kind::MINUS: { + ok = parse(node[0], c, negated); + if (ok) { + ok = parse(node[1], -c, negated); + } + break; + } + + case kind::UMINUS: { + ok = parse(node[0], -c, negated); + break; + } + + default: { + if (c > 0) { + if (d_x.isNull()) { + d_x = node; + } else { + ok = false; + } + } else { + if (d_y.isNull()) { + d_y = node; + } else { + ok = false; + } + } + break; + } + } // End case + + // Difference logic OK + return ok; +} diff --git a/src/theory/idl/idl_assertion.h b/src/theory/idl/idl_assertion.h new file mode 100644 index 000000000..8ce0e93b2 --- /dev/null +++ b/src/theory/idl/idl_assertion.h @@ -0,0 +1,91 @@ +/********************* */ +/*! \file idl_assertion.h + ** \verbatim + ** Original author: Dejan Jovanovic + ** 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 [[ Add one-line brief description here ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#pragma once + +#include "theory/idl/idl_model.h" + +namespace CVC4 { +namespace theory { +namespace idl { + +/** + * An internal representation of the IDL assertions. Each IDL assertions is + * of the form (x - y op c) where op is one of (<=, =, !=). IDL assertion + * can be constructed from an expression. + */ +class IDLAssertion { + + /** The positive variable */ + TNode d_x; + /** The negative variable */ + TNode d_y; + /** The relation */ + Kind d_op; + /** The RHS constant */ + Integer d_c; + + /** Original assertion we got this one from */ + TNode d_original; + + /** Parses the given node into an assertion, and return true if OK. */ + bool parse(TNode node, int c = 1, bool negated = false); + +public: + + /** Null assertion */ + IDLAssertion(); + /** Create the assertion from given node */ + IDLAssertion(TNode node); + /** Copy constructor */ + IDLAssertion(const IDLAssertion& other); + + TNode getX() const { return d_x; } + TNode getY() const { return d_y; } + Kind getOp() const { return d_op;} + Integer getC() const { return d_c; } + + /** + * Propagate the constraint using the model. For example, if the constraint + * is of the form x - y <= -1, and the value of x in the model is 0, then + * + * (x - y <= -1) and (x = 0) implies y >= x + 1 = 1 + * + * If the value of y is less then 1, is is set to 1 and true is returned. If + * the value of y is 1 or more, than false is return. + * + * @return true if value of y was updated + */ + bool propagate(IDLModel& model) const; + + /** Is this constraint proper */ + bool ok() const { + return !d_x.isNull() || !d_y.isNull(); + } + + /** Output to the stream */ + void toStream(std::ostream& out) const; +}; + +inline std::ostream& operator << (std::ostream& out, const IDLAssertion& assertion) { + assertion.toStream(out); + return out; +} + +} +} +} diff --git a/src/theory/idl/idl_assertion_db.cpp b/src/theory/idl/idl_assertion_db.cpp new file mode 100644 index 000000000..697c70c02 --- /dev/null +++ b/src/theory/idl/idl_assertion_db.cpp @@ -0,0 +1,59 @@ +/********************* */ +/*! \file idl_assertion_db.cpp + ** \verbatim + ** Original author: Dejan Jovanovic + ** 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 [[ Add one-line brief description here ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#include "theory/idl/idl_assertion_db.h" + +using namespace CVC4; +using namespace theory; +using namespace idl; + +IDLAssertionDB::IDLAssertionDB(context::Context* c) +: d_assertions(c) +, d_variableLists(c) +{} + +void IDLAssertionDB::add(const IDLAssertion& assertion, TNode var) { + // Is there a list for the variable already? + unsigned previous = -1; + var_to_unsigned_map::iterator find = d_variableLists.find(var); + if (find != d_variableLists.end()) { + previous = (*find).second; + } + // Add to the DB + d_variableLists[var] = d_assertions.size(); + d_assertions.push_back(IDLAssertionListElement(assertion, previous)); +} + +IDLAssertionDB::iterator::iterator(IDLAssertionDB& db, TNode var) +: d_db(db) +, d_current(-1) +{ + var_to_unsigned_map::const_iterator find = d_db.d_variableLists.find(var); + if (find != d_db.d_variableLists.end()) { + d_current = (*find).second; + } +} + +void IDLAssertionDB::iterator::next() { + if (d_current != (unsigned)(-1)) { + d_current = d_db.d_assertions[d_current].d_previous; + } +} + +IDLAssertion IDLAssertionDB::iterator::get() const { + return d_db.d_assertions[d_current].d_assertion; +} diff --git a/src/theory/idl/idl_assertion_db.h b/src/theory/idl/idl_assertion_db.h new file mode 100644 index 000000000..0501bc6bf --- /dev/null +++ b/src/theory/idl/idl_assertion_db.h @@ -0,0 +1,86 @@ +/********************* */ +/*! \file idl_assertion_db.h + ** \verbatim + ** Original author: Dejan Jovanovic + ** 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 [[ Add one-line brief description here ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#pragma once + +#include "theory/idl/idl_assertion.h" +#include "context/cdlist.h" + +namespace CVC4 { +namespace theory { +namespace idl { + +/** + * Context-dependent database assertions, organized by variable. Each variable + * can be associated a list of IDL assertions. The list of assertions can + * be iterated over using the provided iterator class. + */ +class IDLAssertionDB { + + /** Elements of the assertion lists */ + struct IDLAssertionListElement { + /** The assertion itself */ + IDLAssertion d_assertion; + /** The inndex of the previous element (-1 for null) */ + unsigned d_previous; + + IDLAssertionListElement(const IDLAssertion& assertion, unsigned previous) + : d_assertion(assertion), d_previous(previous) + {} + }; + + /** All assertions in a context dependent stack */ + context::CDList<IDLAssertionListElement> d_assertions; + + typedef context::CDHashMap<TNode, unsigned, TNodeHashFunction> var_to_unsigned_map; + + /** Map from variables to the first element of their list */ + var_to_unsigned_map d_variableLists; + +public: + + /** Create a new assertion database */ + IDLAssertionDB(context::Context* c); + + /** Add a new assertion, attach to the list of the given variable */ + void add(const IDLAssertion& assertion, TNode var); + + /** Iteration over the constraints of a variable */ + class iterator { + /** The database */ + const IDLAssertionDB& d_db; + /** Index of the current constraint */ + unsigned d_current; + public: + /** Construct the iterator for the variable */ + iterator(IDLAssertionDB& db, TNode var); + /** Is this iterator done */ + bool done() const { return d_current == (unsigned)(-1); } + /** Next element */ + void next(); + /** Get the assertion */ + IDLAssertion get() const; + }; +}; + +} +} +} + + + + diff --git a/src/theory/idl/idl_model.cpp b/src/theory/idl/idl_model.cpp new file mode 100644 index 000000000..75f4834ea --- /dev/null +++ b/src/theory/idl/idl_model.cpp @@ -0,0 +1,64 @@ +/********************* */ +/*! \file idl_model.cpp + ** \verbatim + ** Original author: Dejan Jovanovic + ** 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 [[ Add one-line brief description here ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#include "theory/idl/idl_model.h" + +using namespace CVC4; +using namespace theory; +using namespace idl; + +IDLModel::IDLModel(context::Context* context) +: d_model(context) +, d_reason(context) +{} + +Integer IDLModel::getValue(TNode var) const { + model_value_map::const_iterator find = d_model.find(var); + if (find != d_model.end()) { + return (*find).second; + } else { + return 0; + } +} + +void IDLModel::setValue(TNode var, Integer value, IDLReason reason) { + Assert(!reason.constraint.isNull()); + d_model[var] = value; + d_reason[var] = reason; +} + +void IDLModel::getReasonCycle(TNode var, std::vector<TNode>& reasons) { + TNode current = var; + do { + Debug("theory::idl::model") << "processing: " << var << std::endl; + Assert(d_reason.find(current) != d_reason.end()); + IDLReason reason = d_reason[current]; + Debug("theory::idl::model") << "adding reason: " << reason.constraint << std::endl; + reasons.push_back(reason.constraint); + current = reason.x; + } while (current != var); +} + +void IDLModel::toStream(std::ostream& out) const { + model_value_map::const_iterator it = d_model.begin(); + model_value_map::const_iterator it_end = d_model.end(); + out << "Model[" << std::endl; + for (; it != it_end; ++ it) { + out << (*it).first << " -> " << (*it).second << std::endl; + } + out << "]"; +} diff --git a/src/theory/idl/idl_model.h b/src/theory/idl/idl_model.h new file mode 100644 index 000000000..64407684b --- /dev/null +++ b/src/theory/idl/idl_model.h @@ -0,0 +1,84 @@ +/********************* */ +/*! \file idl_model.h + ** \verbatim + ** Original author: Dejan Jovanovic + ** 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 [[ Add one-line brief description here ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#pragma once + +#include "expr/node.h" +#include "context/cdhashmap.h" + +namespace CVC4 { +namespace theory { +namespace idl { + +/** + * A reason for a value of a variable in the model is a constraint that implies + * this value by means of the value of another variable. For example, if the + * value of x is 0, then the variable x and the constraint (y > 0) are a reason + * for the y taking the value 1. + */ +struct IDLReason { + /** The variable of the reason */ + TNode x; + /** The constraint of the reaason */ + TNode constraint; + + IDLReason(TNode x, TNode constraint) + : x(x), constraint(constraint) {} + IDLReason() {} +}; + +/** + * A model maps variables to integer values and backs them up with reasons. + * Default values (if not set with setValue) for all variables are 0. + */ +class IDLModel { + + typedef context::CDHashMap<TNode, Integer, TNodeHashFunction> model_value_map; + typedef context::CDHashMap<TNode, IDLReason, TNodeHashFunction> model_reason_map; + + /** Values assigned to individual variables */ + model_value_map d_model; + + /** Reasons constraining the individual variables */ + model_reason_map d_reason; + +public: + + IDLModel(context::Context* context); + + /** Get the model value of the variable */ + Integer getValue(TNode var) const; + + /** Set the value of the variable */ + void setValue(TNode var, Integer value, IDLReason reason); + + /** Get the cycle of reasons behind the variable var */ + void getReasonCycle(TNode var, std::vector<TNode>& reasons); + + /** Output to the given stream */ + void toStream(std::ostream& out) const; + +}; + +inline std::ostream& operator << (std::ostream& out, const IDLModel& model) { + model.toStream(out); + return out; +} + +} +} +} diff --git a/src/theory/idl/kinds b/src/theory/idl/kinds new file mode 100644 index 000000000..6bf0218b0 --- /dev/null +++ b/src/theory/idl/kinds @@ -0,0 +1,8 @@ +# kinds -*- sh -*- +# +# For documentation on this file format, please refer to +# src/theory/builtin/kinds. +# + +alternate THEORY_ARITH "idl" ::CVC4::theory::idl::TheoryIdl "theory/idl/theory_idl.h" + diff --git a/src/theory/idl/options b/src/theory/idl/options new file mode 100644 index 000000000..c1c9edcef --- /dev/null +++ b/src/theory/idl/options @@ -0,0 +1,12 @@ +# +# Option specification file for CVC4 +# See src/options/base_options for a description of this file format +# + +module IDL "theory/idl/options.h" Idl + +option idlRewriteEq --enable-idl-rewrite-equalities/--disable-idl-rewrite-equalities bool :default false :read-write + enable rewriting equalities into two inequalities in IDL solver (default is disabled) +/disable rewriting equalities into two inequalities in IDL solver (default is disabled) + +endmodule diff --git a/src/theory/idl/theory_idl.cpp b/src/theory/idl/theory_idl.cpp new file mode 100644 index 000000000..e5100fc71 --- /dev/null +++ b/src/theory/idl/theory_idl.cpp @@ -0,0 +1,143 @@ +/********************* */ +/*! \file theory_idl.cpp + ** \verbatim + ** Original author: Dejan Jovanovic + ** 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 [[ Add one-line brief description here ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#include "theory/idl/theory_idl.h" +#include "theory/idl/options.h" +#include "theory/rewriter.h" + +#include <set> +#include <queue> + +using namespace std; + +using namespace CVC4; +using namespace theory; +using namespace idl; + +TheoryIdl::TheoryIdl(context::Context* c, context::UserContext* u, OutputChannel& out, + Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) +: Theory(THEORY_ARITH, c, u, out, valuation, logicInfo, qe) +, d_model(c) +, d_assertionsDB(c) +{} + +Node TheoryIdl::ppRewrite(TNode atom) { + if (atom.getKind() == kind::EQUAL && options::idlRewriteEq()) { + // If the option is turned on, each equality into two inequalities. This in + // effect removes equalities, and theorefore dis-equalities too. + Node leq = NodeBuilder<2>(kind::LEQ) << atom[0] << atom[1]; + Node geq = NodeBuilder<2>(kind::GEQ) << atom[0] << atom[1]; + Node rewritten = Rewriter::rewrite(leq.andNode(geq)); + return rewritten; + } else { + return atom; + } +} + +void TheoryIdl::check(Effort level) { + + while(!done()) { + + // Get the next assertion + Assertion assertion = get(); + Debug("theory::idl") << "TheoryIdl::check(): processing " << assertion.assertion << std::endl; + + // Convert the assertion into the internal representation + IDLAssertion idlAssertion(assertion.assertion); + Debug("theory::idl") << "TheoryIdl::check(): got " << idlAssertion << std::endl; + + if (idlAssertion.ok()) { + if (idlAssertion.getOp() == kind::DISTINCT) { + // We don't handle dis-equalities + d_out->setIncomplete(); + } else { + // Process the convex assertions immediately + bool ok = processAssertion(idlAssertion); + if (!ok) { + // In conflict, we're done + return; + } + } + } else { + // Not an IDL assertion, set incomplete + d_out->setIncomplete(); + } + } + +} + +bool TheoryIdl::processAssertion(const IDLAssertion& assertion) { + + Debug("theory::idl") << "TheoryIdl::processAssertion(" << assertion << ")" << std::endl; + + // Add the constraint (x - y op c) to the list assertions of x + d_assertionsDB.add(assertion, assertion.getX()); + + // Update the model, if forced by the assertion + bool y_updated = assertion.propagate(d_model); + + // If the value of y was updated, we might need to update further + if (y_updated) { + + std::queue<TNode> queue; // Queue of variables to consider + std::set<TNode> inQueue; // Current elements of the queue + + // Add the first updated variable to the queue + queue.push(assertion.getY()); + inQueue.insert(assertion.getY()); + + while (!queue.empty()) { + // Pop a new variable x off the queue + TNode x = queue.front(); + queue.pop(); + inQueue.erase(x); + + // Go through the constraint (x - y op c), and update values of y + IDLAssertionDB::iterator it(d_assertionsDB, x); + while (!it.done()) { + // Get the assertion and update y + IDLAssertion x_y_assertion = it.get(); + y_updated = x_y_assertion.propagate(d_model); + // If updated add to the queue + if (y_updated) { + // If the variable that we updated is the same as the first + // variable that we updated, it's a cycle of updates => conflict + if (x_y_assertion.getY() == assertion.getX()) { + std::vector<TNode> reasons; + d_model.getReasonCycle(x_y_assertion.getY(), reasons); + // Construct the reason of the conflict + Node conflict = NodeManager::currentNM()->mkNode(kind::AND, reasons); + d_out->conflict(conflict); + return false; + } else { + // No cycle, just a model update, so we add to the queue + TNode y = x_y_assertion.getY(); + if (inQueue.count(y) == 0) { + queue.push(y); + inQueue.insert(x_y_assertion.getY()); + } + } + } + // Go to the next constraint + it.next(); + } + } + } + + // Everything fine, no conflict + return true; +} diff --git a/src/theory/idl/theory_idl.h b/src/theory/idl/theory_idl.h new file mode 100644 index 000000000..c629ad2b0 --- /dev/null +++ b/src/theory/idl/theory_idl.h @@ -0,0 +1,63 @@ +/********************* */ +/*! \file theory_idl.h + ** \verbatim + ** Original author: Dejan Jovanovic + ** 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 [[ Add one-line brief description here ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#pragma once + +#include "cvc4_private.h" + +#include "theory/theory.h" +#include "theory/idl/idl_model.h" +#include "theory/idl/idl_assertion_db.h" + +namespace CVC4 { +namespace theory { +namespace idl { + +/** + * Handles integer difference logic (IDL) constraints. + */ +class TheoryIdl : public Theory { + + /** The current model */ + IDLModel d_model; + + /** The asserted constraints, organized by variable */ + IDLAssertionDB d_assertionsDB; + + /** Process a new assertion, returns false if in conflict */ + bool processAssertion(const IDLAssertion& assertion); + +public: + + /** Theory constructor. */ + TheoryIdl(context::Context* c, context::UserContext* u, OutputChannel& out, + Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe); + + /** Pre-processing of input atoms */ + Node ppRewrite(TNode atom); + + /** Check the assertions for satisfiability */ + void check(Effort effort); + + /** Identity string */ + std::string identify() const { return "THEORY_IDL"; } + +};/* class TheoryIdl */ + +}/* CVC4::theory::idl namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/ite_simplifier.cpp b/src/theory/ite_simplifier.cpp index ec9eb27d4..463a9c41a 100644 --- a/src/theory/ite_simplifier.cpp +++ b/src/theory/ite_simplifier.cpp @@ -33,7 +33,7 @@ bool ITESimplifier::containsTermITE(TNode e) } hash_map<Node, bool, NodeHashFunction>::iterator it; - it = d_containsTermITECache.find(e); + it = d_containsTermITECache.find(e); if (it != d_containsTermITECache.end()) { return (*it).second; } @@ -60,7 +60,7 @@ bool ITESimplifier::leavesAreConst(TNode e, TheoryId tid) } hash_map<Node, bool, NodeHashFunction>::iterator it; - it = d_leavesConstCache.find(e); + it = d_leavesConstCache.find(e); if (it != d_leavesConstCache.end()) { return (*it).second; } diff --git a/src/theory/ite_simplifier.h b/src/theory/ite_simplifier.h index 0f648f91d..07fa0dedb 100644 --- a/src/theory/ite_simplifier.h +++ b/src/theory/ite_simplifier.h @@ -31,9 +31,7 @@ #include "prop/prop_engine.h" #include "context/cdhashset.h" #include "theory/theory.h" -#include "theory/substitutions.h" #include "theory/rewriter.h" -#include "theory/substitutions.h" #include "theory/shared_terms_database.h" #include "theory/term_registration_visitor.h" #include "theory/valuation.h" @@ -43,6 +41,7 @@ #include "util/ite_removal.h" namespace CVC4 { +namespace theory { class ITESimplifier { Node d_true; @@ -160,6 +159,7 @@ public: }; -} +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ #endif diff --git a/src/theory/logic_info.cpp b/src/theory/logic_info.cpp index dc9de8662..d74f36069 100644 --- a/src/theory/logic_info.cpp +++ b/src/theory/logic_info.cpp @@ -3,7 +3,7 @@ ** \verbatim ** Original author: Morgan Deters ** Major contributors: none - ** Minor contributors (to current version): Dejan Jovanovic + ** Minor contributors (to current version): Dejan Jovanovic, 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 @@ -105,6 +105,10 @@ std::string LogicInfo::getLogicString() const { ss << "DT"; ++seen; } + if(d_theories[THEORY_STRINGS]) { + ss << "S"; + ++seen; + } if(d_theories[THEORY_ARITH]) { if(isDifferenceLogic()) { ss << (areIntegersUsed() ? "I" : ""); @@ -177,10 +181,21 @@ void LogicInfo::setLogicString(std::string logicString) throw(IllegalArgumentExc enableTheory(THEORY_ARRAY); ++p; } + if(*p == 'S') { + // Strings requires arith for length constraints, + // and UF for equality (?) + enableTheory(THEORY_STRINGS); + enableTheory(THEORY_UF); + enableTheory(THEORY_ARITH); + enableIntegers(); + arithOnlyLinear(); + ++p; + } if(!strncmp(p, "UF", 2)) { enableTheory(THEORY_UF); p += 2; } + // allow BV or DT in either order if(!strncmp(p, "BV", 2)) { enableTheory(THEORY_BV); p += 2; @@ -189,6 +204,10 @@ void LogicInfo::setLogicString(std::string logicString) throw(IllegalArgumentExc enableTheory(THEORY_DATATYPES); p += 2; } + if(!d_theories[THEORY_BV] && !strncmp(p, "BV", 2)) { + enableTheory(THEORY_BV); + p += 2; + } if(!strncmp(p, "IDL", 3)) { enableIntegers(); disableReals(); @@ -241,7 +260,12 @@ void LogicInfo::setLogicString(std::string logicString) throw(IllegalArgumentExc } if(*p != '\0') { stringstream err; - err << "LogicInfo::setLogicString(): junk (\"" << p << "\") at end of logic string: " << logicString; + err << "LogicInfo::setLogicString(): "; + if(p == logicString) { + err << "cannot parse logic string: " << logicString; + } else { + err << "junk (\"" << p << "\") at end of logic string: " << logicString; + } IllegalArgument(logicString, err.str().c_str()); } diff --git a/src/theory/logic_info.h b/src/theory/logic_info.h index c7b5c58f9..2448898c0 100644 --- a/src/theory/logic_info.h +++ b/src/theory/logic_info.h @@ -155,21 +155,25 @@ public: /** Are integers in this logic? */ bool areIntegersUsed() const { CheckArgument(d_locked, *this, "This LogicInfo isn't locked yet, and cannot be queried"); + CheckArgument(isTheoryEnabled(theory::THEORY_ARITH), *this, "Arithmetic not used in this LogicInfo; cannot ask whether integers are used"); return d_integers; } /** Are reals in this logic? */ bool areRealsUsed() const { CheckArgument(d_locked, *this, "This LogicInfo isn't locked yet, and cannot be queried"); + CheckArgument(isTheoryEnabled(theory::THEORY_ARITH), *this, "Arithmetic not used in this LogicInfo; cannot ask whether reals are used"); return d_reals; } /** Does this logic only linear arithmetic? */ bool isLinear() const { CheckArgument(d_locked, *this, "This LogicInfo isn't locked yet, and cannot be queried"); + CheckArgument(isTheoryEnabled(theory::THEORY_ARITH), *this, "Arithmetic not used in this LogicInfo; cannot ask whether it's linear"); return d_linear || d_differenceLogic; } /** Does this logic only permit difference reasoning? (implies linear) */ bool isDifferenceLogic() const { CheckArgument(d_locked, *this, "This LogicInfo isn't locked yet, and cannot be queried"); + CheckArgument(isTheoryEnabled(theory::THEORY_ARITH), *this, "Arithmetic not used in this LogicInfo; cannot ask whether it's difference logic"); return d_differenceLogic; } diff --git a/src/theory/model.cpp b/src/theory/model.cpp index 1c511be30..840c8bc3a 100644 --- a/src/theory/model.cpp +++ b/src/theory/model.cpp @@ -19,6 +19,7 @@ #include "smt/options.h" #include "smt/smt_engine.h" #include "theory/uf/theory_uf_model.h" +#include "theory/uf/options.h" using namespace std; using namespace CVC4; @@ -27,7 +28,7 @@ using namespace CVC4::context; using namespace CVC4::theory; TheoryModel::TheoryModel( context::Context* c, std::string name, bool enableFuncModels) : - d_substitutions(c), d_equalityEngine(c, name), d_modelBuilt(c, false), d_enableFuncModels(enableFuncModels) + d_substitutions(c, false), d_equalityEngine(c, name), d_modelBuilt(c, false), d_enableFuncModels(enableFuncModels) { d_true = NodeManager::currentNM()->mkConst( true ); d_false = NodeManager::currentNM()->mkConst( false ); @@ -47,12 +48,15 @@ void TheoryModel::reset(){ d_uf_models.clear(); } -Node TheoryModel::getValue( TNode n ) const{ +Node TheoryModel::getValue(TNode n) const { //apply substitutions - Node nn = d_substitutions.apply( n ); + Node nn = d_substitutions.apply(n); //get value in model - nn = getModelValue( nn ); - Assert(nn.isConst() || nn.getKind() == kind::LAMBDA); + nn = getModelValue(nn); + if(options::condenseFunctionValues() || nn.getKind() != kind::LAMBDA) { + //normalize + nn = Rewriter::rewrite(nn); + } return nn; } @@ -94,16 +98,15 @@ Node TheoryModel::getModelValue(TNode n, bool hasBoundVars) const // no good. Instead, return the quantifier itself. If we're in // checkModel(), and the quantifier actually matters, we'll get an // assert-fail since the quantifier isn't a constant. - if(!d_equalityEngine.hasTerm(n)) { + if(!d_equalityEngine.hasTerm(Rewriter::rewrite(n))) { return n; + } else { + n = Rewriter::rewrite(n); } } else { if(n.getKind() == kind::LAMBDA) { NodeManager* nm = NodeManager::currentNM(); Node body = getModelValue(n[1], true); - // This is a bit ugly, but cache inside simplifier can change, so can't be const - // The ite simplifier is needed to get rid of artifacts created by Boolean terms - body = const_cast<ITESimplifier*>(&d_iteSimp)->simpITE(body); body = Rewriter::rewrite(body); return nm->mkNode(kind::LAMBDA, n[0], body); } @@ -430,6 +433,7 @@ void TheoryEngineModelBuilder::checkTerms(TNode n, TheoryModel* tm, NodeSet& cac void TheoryEngineModelBuilder::buildModel(Model* m, bool fullModel) { + Trace("model-builder") << "TheoryEngineModelBuilder: buildModel, fullModel = " << fullModel << std::endl; TheoryModel* tm = (TheoryModel*)m; // buildModel with fullModel = true should only be called once in any context @@ -718,6 +722,7 @@ void TheoryEngineModelBuilder::buildModel(Model* m, bool fullModel) } if (!fullModel) { + Trace("model-builder") << "Make sure ECs have reps..." << std::endl; // Make sure every EC has a rep for (itMap = assertedReps.begin(); itMap != assertedReps.end(); ++itMap ) { tm->d_reps[itMap->first] = itMap->second; @@ -851,8 +856,10 @@ void TheoryEngineModelBuilder::processBuildModel(TheoryModel* m, bool fullModel) default_v = (*te); } ufmt.setDefaultValue( m, default_v ); - ufmt.simplify(); - Node val = ufmt.getFunctionValue( "_ufmt_" ); + if(options::condenseFunctionValues()) { + ufmt.simplify(); + } + Node val = ufmt.getFunctionValue( "_ufmt_", options::condenseFunctionValues() ); Trace("model-builder") << " Assigning (" << n << ") to (" << val << ")" << endl; m->d_uf_models[n] = val; //ufmt.debugPrint( std::cout, m ); diff --git a/src/theory/options b/src/theory/options index 5d752fca1..9944264c8 100644 --- a/src/theory/options +++ b/src/theory/options @@ -5,8 +5,8 @@ module THEORY "theory/options.h" Theory layer -expert-option theoryOfMode --theoryof-mode=MODE CVC4::theory::TheoryOfMode :handler CVC4::theory::stringToTheoryOfMode :handler-include "theory/options_handlers.h" :default CVC4::theory::THEORY_OF_TYPE_BASED :include "theory/theoryof_mode.h" - mode for theoryof +expert-option theoryOfMode theoryof-mode --theoryof-mode=MODE CVC4::theory::TheoryOfMode :handler CVC4::theory::stringToTheoryOfMode :handler-include "theory/options_handlers.h" :default CVC4::theory::THEORY_OF_TYPE_BASED :include "theory/theoryof_mode.h" :read-write + mode for Theory::theoryof() option - use-theory --use-theory=NAME argument :handler CVC4::theory::useTheory :handler-include "theory/options_handlers.h" use alternate theory implementation NAME (--use-theory=help for a list) diff --git a/src/theory/output_channel.h b/src/theory/output_channel.h index af3065404..44b89e8cb 100644 --- a/src/theory/output_channel.h +++ b/src/theory/output_channel.h @@ -127,9 +127,12 @@ public: */ LemmaStatus split(TNode n) throw(TypeCheckingExceptionPrivate, AssertionException) { - return lemma(n.orNode(n.notNode())); + return splitLemma(n.orNode(n.notNode())); } + virtual LemmaStatus splitLemma(TNode n, bool removable = false) + throw(TypeCheckingExceptionPrivate, AssertionException) = 0; + /** * If a decision is made on n, it must be in the phase specified. * Note that this is enforced *globally*, i.e., it is completely @@ -219,7 +222,7 @@ public: /** Demands that the search restart from sat search level 0. * Using this leads to non-termination issues. - * It is appropraite for prototyping for theories. + * It is appropriate for prototyping for theories. */ virtual void demandRestart() throw(TypeCheckingExceptionPrivate, AssertionException) {} diff --git a/src/theory/quantifiers/Makefile.am b/src/theory/quantifiers/Makefile.am index 7fea8cf3a..be24d6c67 100644 --- a/src/theory/quantifiers/Makefile.am +++ b/src/theory/quantifiers/Makefile.am @@ -23,8 +23,6 @@ libquantifiers_la_SOURCES = \ model_engine.cpp \ modes.cpp \ modes.h \ - relevant_domain.h \ - relevant_domain.cpp \ term_database.h \ term_database.cpp \ first_order_model.h \ @@ -44,7 +42,20 @@ libquantifiers_la_SOURCES = \ inst_strategy_e_matching.h \ inst_strategy_e_matching.cpp \ inst_strategy_cbqi.h \ - inst_strategy_cbqi.cpp + inst_strategy_cbqi.cpp \ + full_model_check.h \ + full_model_check.cpp \ + bounded_integers.h \ + bounded_integers.cpp \ + first_order_reasoning.h \ + first_order_reasoning.cpp \ + rewrite_engine.h \ + rewrite_engine.cpp \ + relevant_domain.h \ + relevant_domain.cpp \ + symmetry_breaking.h \ + symmetry_breaking.cpp + EXTRA_DIST = \ kinds \ diff --git a/src/theory/quantifiers/bounded_integers.cpp b/src/theory/quantifiers/bounded_integers.cpp new file mode 100644 index 000000000..30ff5242b --- /dev/null +++ b/src/theory/quantifiers/bounded_integers.cpp @@ -0,0 +1,372 @@ +/********************* */ +/*! \file bounded_integers.cpp + ** \verbatim + ** Original author: 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 Bounded integers module + ** + ** This class manages integer bounds for quantifiers + **/ + +#include "theory/quantifiers/bounded_integers.h" +#include "theory/quantifiers/quant_util.h" +#include "theory/quantifiers/first_order_model.h" +#include "theory/quantifiers/model_engine.h" + +using namespace CVC4; +using namespace std; +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; +using namespace CVC4::kind; + +void BoundedIntegers::RangeModel::initialize() { + //add initial split lemma + Node ltr = NodeManager::currentNM()->mkNode( LT, d_range, NodeManager::currentNM()->mkConst( Rational(0) ) ); + ltr = Rewriter::rewrite( ltr ); + Trace("bound-int-lemma") << " *** bound int: initial split on " << ltr << std::endl; + d_bi->getQuantifiersEngine()->getOutputChannel().split( ltr ); + Node ltr_lit = ltr.getKind()==NOT ? ltr[0] : ltr; + d_range_literal[-1] = ltr_lit; + d_lit_to_range[ltr_lit] = -1; + d_lit_to_pol[ltr_lit] = ltr.getKind()!=NOT; + //register with bounded integers + Trace("bound-int-debug") << "Literal " << ltr_lit << " is literal for " << d_range << std::endl; + d_bi->addLiteralFromRange(ltr_lit, d_range); +} + +void BoundedIntegers::RangeModel::assertNode(Node n) { + bool pol = n.getKind()!=NOT; + Node nlit = n.getKind()==NOT ? n[0] : n; + if( d_lit_to_range.find( nlit )!=d_lit_to_range.end() ){ + Trace("bound-int-assert") << "With polarity = " << pol << " (req "<< d_lit_to_pol[nlit] << ")"; + Trace("bound-int-assert") << ", found literal " << nlit; + Trace("bound-int-assert") << ", it is bound literal " << d_lit_to_range[nlit] << " for " << d_range << std::endl; + d_range_assertions[nlit] = (pol==d_lit_to_pol[nlit]); + if( pol!=d_lit_to_pol[nlit] ){ + //check if we need a new split? + if( !d_has_range ){ + bool needsRange = true; + for( std::map< Node, int >::iterator it = d_lit_to_range.begin(); it != d_lit_to_range.end(); ++it ){ + if( d_range_assertions.find( it->first )==d_range_assertions.end() ){ + needsRange = false; + break; + } + } + if( needsRange ){ + allocateRange(); + } + } + }else{ + if (!d_has_range || d_lit_to_range[nlit]<d_curr_range ){ + Trace("bound-int-bound") << "Successfully bound " << d_range << " to " << d_lit_to_range[nlit] << std::endl; + d_curr_range = d_lit_to_range[nlit]; + } + //set the range + d_has_range = true; + } + }else{ + Message() << "Could not find literal " << nlit << " for range " << d_range << std::endl; + exit(0); + } +} + +void BoundedIntegers::RangeModel::allocateRange() { + d_curr_max++; + int newBound = d_curr_max; + Trace("bound-int-proc") << "Allocate range bound " << newBound << " for " << d_range << std::endl; + //TODO: newBound should be chosen in a smarter way + Node ltr = NodeManager::currentNM()->mkNode( LEQ, d_range, NodeManager::currentNM()->mkConst( Rational(newBound) ) ); + ltr = Rewriter::rewrite( ltr ); + Trace("bound-int-lemma") << " *** bound int: split on " << ltr << std::endl; + d_bi->getQuantifiersEngine()->getOutputChannel().split( ltr ); + Node ltr_lit = ltr.getKind()==NOT ? ltr[0] : ltr; + d_range_literal[newBound] = ltr_lit; + d_lit_to_range[ltr_lit] = newBound; + d_lit_to_pol[ltr_lit] = ltr.getKind()!=NOT; + //register with bounded integers + d_bi->addLiteralFromRange(ltr_lit, d_range); +} + +Node BoundedIntegers::RangeModel::getNextDecisionRequest() { + //request the current cardinality as a decision literal, if not already asserted + for( std::map< Node, int >::iterator it = d_lit_to_range.begin(); it != d_lit_to_range.end(); ++it ){ + int i = it->second; + if( !d_has_range || i<d_curr_range ){ + Node rn = it->first; + Assert( !rn.isNull() ); + if( d_range_assertions.find( rn )==d_range_assertions.end() ){ + if (!d_lit_to_pol[it->first]) { + rn = rn.negate(); + } + Trace("bound-int-dec") << "For " << d_range << ", make decision " << rn << " to make range " << i << std::endl; + return rn; + } + } + } + return Node::null(); +} + + +BoundedIntegers::BoundedIntegers(context::Context* c, QuantifiersEngine* qe) : +QuantifiersModule(qe), d_assertions(c){ + +} + +bool BoundedIntegers::isBound( Node f, Node v ) { + return std::find( d_set[f].begin(), d_set[f].end(), v )!=d_set[f].end(); +} + +bool BoundedIntegers::hasNonBoundVar( Node f, Node b ) { + if( b.getKind()==BOUND_VARIABLE ){ + if( !isBound( f, b ) ){ + return true; + } + }else{ + for( unsigned i=0; i<b.getNumChildren(); i++ ){ + if( hasNonBoundVar( f, b[i] ) ){ + return true; + } + } + } + return false; +} + +void BoundedIntegers::processLiteral( Node f, Node lit, bool pol, + std::map< int, std::map< Node, Node > >& bound_lit_map, + std::map< int, std::map< Node, bool > >& bound_lit_pol_map ) { + if( lit.getKind()==GEQ && lit[0].getType().isInteger() ){ + std::map< Node, Node > msum; + if (QuantArith::getMonomialSumLit( lit, msum )){ + Trace("bound-int-debug") << "Literal (polarity = " << pol << ") " << lit << " is monomial sum : " << std::endl; + for(std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){ + Trace("bound-int-debug") << " "; + if( !it->second.isNull() ){ + Trace("bound-int-debug") << it->second; + if( !it->first.isNull() ){ + Trace("bound-int-debug") << " * "; + } + } + if( !it->first.isNull() ){ + Trace("bound-int-debug") << it->first; + } + Trace("bound-int-debug") << std::endl; + } + Trace("bound-int-debug") << std::endl; + for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){ + if ( !it->first.isNull() && it->first.getKind()==BOUND_VARIABLE && !isBound( f, it->first ) ){ + Node veq; + if( QuantArith::isolate( it->first, msum, veq, GEQ ) ){ + Node n1 = veq[0]; + Node n2 = veq[1]; + if(pol){ + //flip + n1 = veq[1]; + n2 = veq[0]; + if( n1.getKind()==BOUND_VARIABLE ){ + n2 = QuantArith::offset( n2, 1 ); + }else{ + n1 = QuantArith::offset( n1, -1 ); + } + veq = NodeManager::currentNM()->mkNode( GEQ, n1, n2 ); + } + Trace("bound-int-debug") << "Isolated for " << it->first << " : (" << n1 << " >= " << n2 << ")" << std::endl; + Node bv = n1.getKind()==BOUND_VARIABLE ? n1 : n2; + if( !isBound( f, bv ) ){ + if( !hasNonBoundVar( f, n1.getKind()==BOUND_VARIABLE ? n2 : n1 ) ) { + Trace("bound-int-debug") << "The bound is relevant." << std::endl; + int loru = n1.getKind()==BOUND_VARIABLE ? 0 : 1; + d_bounds[loru][f][bv] = (n1.getKind()==BOUND_VARIABLE ? n2 : n1); + bound_lit_map[loru][bv] = lit; + bound_lit_pol_map[loru][bv] = pol; + } + } + } + } + } + } + }else if( lit.getKind()==LEQ || lit.getKind()==LT || lit.getKind()==GT ) { + Message() << "BoundedIntegers : Bad kind for literal : " << lit << std::endl; + exit(0); + } +} + +void BoundedIntegers::process( Node f, Node n, bool pol, + std::map< int, std::map< Node, Node > >& bound_lit_map, + std::map< int, std::map< Node, bool > >& bound_lit_pol_map ){ + if( (( n.getKind()==IMPLIES || n.getKind()==OR) && pol) || (n.getKind()==AND && !pol) ){ + for( unsigned i=0; i<n.getNumChildren(); i++) { + bool newPol = n.getKind()==IMPLIES && i==0 ? !pol : pol; + process( f, n[i], newPol, bound_lit_map, bound_lit_pol_map ); + } + }else if( n.getKind()==NOT ){ + process( f, n[0], !pol, bound_lit_map, bound_lit_pol_map ); + }else { + processLiteral( f, n, pol, bound_lit_map, bound_lit_pol_map ); + } +} + +void BoundedIntegers::check( Theory::Effort e ) { + +} + + +void BoundedIntegers::addLiteralFromRange( Node lit, Node r ) { + d_lit_to_ranges[lit].push_back(r); + //check if it is already asserted? + if(d_assertions.find(lit)!=d_assertions.end()){ + d_rms[r]->assertNode( d_assertions[lit] ? lit : lit.negate() ); + } +} + +void BoundedIntegers::registerQuantifier( Node f ) { + Trace("bound-int") << "Register quantifier " << f << std::endl; + bool hasIntType = false; + int finiteTypes = 0; + std::map< Node, int > numMap; + for( unsigned i=0; i<f[0].getNumChildren(); i++) { + numMap[f[0][i]] = i; + if( f[0][i].getType().isInteger() ){ + hasIntType = true; + } + else if( f[0][i].getType().isSort() ){ + finiteTypes++; + } + } + if( hasIntType ){ + bool success; + do{ + std::map< int, std::map< Node, Node > > bound_lit_map; + std::map< int, std::map< Node, bool > > bound_lit_pol_map; + success = false; + process( f, f[1], true, bound_lit_map, bound_lit_pol_map ); + for( std::map< Node, Node >::iterator it = d_bounds[0][f].begin(); it != d_bounds[0][f].end(); ++it ){ + Node v = it->first; + if( !isBound(f,v) ){ + if( d_bounds[1][f].find(v)!=d_bounds[1][f].end() ){ + d_set[f].push_back(v); + d_set_nums[f].push_back(numMap[v]); + success = true; + //set Attributes on literals + for( unsigned b=0; b<2; b++ ){ + Assert( bound_lit_map[b].find( v )!=bound_lit_map[b].end() ); + Assert( bound_lit_pol_map[b].find( v )!=bound_lit_pol_map[b].end() ); + BoundIntLitAttribute bila; + bound_lit_map[b][v].setAttribute( bila, bound_lit_pol_map[b][v] ? 1 : 0 ); + } + Trace("bound-int") << "Variable " << v << " is bound because of literals " << bound_lit_map[0][v] << " and " << bound_lit_map[1][v] << std::endl; + } + } + } + }while( success ); + Trace("bound-int") << "Bounds are : " << std::endl; + for( unsigned i=0; i<d_set[f].size(); i++) { + Node v = d_set[f][i]; + Node r = NodeManager::currentNM()->mkNode( MINUS, d_bounds[1][f][v], d_bounds[0][f][v] ); + d_range[f][v] = Rewriter::rewrite( r ); + Trace("bound-int") << " " << d_bounds[0][f][v] << " <= " << v << " <= " << d_bounds[1][f][v] << " (range is " << d_range[f][v] << ")" << std::endl; + } + if( d_set[f].size()==(f[0].getNumChildren()-finiteTypes) ){ + d_bound_quants.push_back( 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) ){ + //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]; + d_range[f][v] = new_range; + r = new_range; + } + if( r.getKind()!=CONST_RATIONAL ){ + if( std::find(d_ranges.begin(), d_ranges.end(), r)==d_ranges.end() ){ + Trace("bound-int") << "For " << v << ", bounded Integer Module will try to minimize : " << r << " " << r.getKind() << std::endl; + d_ranges.push_back( r ); + d_rms[r] = new RangeModel(this, r, d_quantEngine->getSatContext() ); + d_rms[r]->initialize(); + } + } + } + }else{ + Trace("bound-int-warn") << "Warning : Bounded Integers : Could not find bounds for " << f << std::endl; + } + } +} + +void BoundedIntegers::assertNode( Node n ) { + Trace("bound-int-assert") << "Assert " << n << std::endl; + Node nlit = n.getKind()==NOT ? n[0] : n; + if( d_lit_to_ranges.find(nlit)!=d_lit_to_ranges.end() ){ + Trace("bound-int-assert") << "This is the bounding literal for " << d_lit_to_ranges[nlit].size() << " ranges." << std::endl; + for( unsigned i=0; i<d_lit_to_ranges[nlit].size(); i++) { + Node r = d_lit_to_ranges[nlit][i]; + Trace("bound-int-assert") << " ...this is a bounding literal for " << r << std::endl; + d_rms[r]->assertNode( n ); + } + } + d_assertions[nlit] = n.getKind()!=NOT; +} + +Node BoundedIntegers::getNextDecisionRequest() { + Trace("bound-int-dec") << "bi: Get next decision request?" << std::endl; + for( unsigned i=0; i<d_ranges.size(); i++) { + Node d = d_rms[d_ranges[i]]->getNextDecisionRequest(); + if (!d.isNull()) { + return d; + } + } + return Node::null(); +} + +void BoundedIntegers::getBounds( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ) { + l = d_bounds[0][f][v]; + u = d_bounds[1][f][v]; + if( d_nground_range[f].find(v)!=d_nground_range[f].end() ){ + //must create substitution + std::vector< Node > vars; + std::vector< Node > subs; + Trace("bound-int-rsi") << "Get bound value in model of variable " << v << std::endl; + for( unsigned i=0; i<d_set[f].size(); i++) { + if( d_set[f][i]!=v ){ + Trace("bound-int-rsi") << "Look up the value for " << d_set[f][i] << " " << rsi->d_var_order[d_set_nums[f][i]] << std::endl; + Trace("bound-int-rsi") << "term : " << rsi->getTerm(rsi->d_var_order[d_set_nums[f][i]]) << std::endl; + vars.push_back(d_set[f][i]); + subs.push_back(rsi->getTerm(rsi->d_var_order[d_set_nums[f][i]])); + }else{ + break; + } + } + Trace("bound-int-rsi") << "Do substitution..." << std::endl; + //check if it has been instantiated + if (!vars.empty() && !d_bnd_it[f][v].hasInstantiated(subs)){ + //must add the lemma + Node nn = d_nground_range[f][v]; + nn = nn.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + Node lem = NodeManager::currentNM()->mkNode( LEQ, nn, d_range[f][v] ); + Trace("bound-int-lemma") << "*** Add lemma to minimize instantiated non-ground term " << lem << std::endl; + d_quantEngine->getOutputChannel().lemma(lem); + l = Node::null(); + u = Node::null(); + return; + }else{ + u = u.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + l = l.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + } + } +} + +void BoundedIntegers::getBoundValues( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ) { + getBounds( f, v, rsi, l, u ); + Trace("bound-int-rsi") << "Get value in model for..." << l << " and " << u << std::endl; + l = d_quantEngine->getModel()->getCurrentModelValue( l ); + u = d_quantEngine->getModel()->getCurrentModelValue( u ); + Trace("bound-int-rsi") << "Value is " << l << " ... " << u << std::endl; + return; +} + +bool BoundedIntegers::isGroundRange(Node f, Node v) { + return isBoundVar(f,v) && !quantifiers::TermDb::hasBoundVarAttr(getLowerBound(f,v)) && !quantifiers::TermDb::hasBoundVarAttr(getUpperBound(f,v)); +} diff --git a/src/theory/quantifiers/bounded_integers.h b/src/theory/quantifiers/bounded_integers.h new file mode 100644 index 000000000..3da938d31 --- /dev/null +++ b/src/theory/quantifiers/bounded_integers.h @@ -0,0 +1,127 @@ +/********************* */ +/*! \file bounded_integers.h +** \verbatim +** Original author: 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 This class manages integer bounds for quantifiers +**/ + +#include "cvc4_private.h" + +#ifndef __CVC4__BOUNDED_INTEGERS_H +#define __CVC4__BOUNDED_INTEGERS_H + + +#include "theory/quantifiers_engine.h" + +#include "context/context.h" +#include "context/context_mm.h" +#include "context/cdchunk_list.h" + +namespace CVC4 { +namespace theory { + +class RepSetIterator; + +namespace quantifiers { + + +class BoundedIntegers : public QuantifiersModule +{ + typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap; + typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap; + typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap; +private: + //for determining bounds + bool isBound( Node f, Node v ); + bool hasNonBoundVar( Node f, Node b ); + std::map< Node, std::map< Node, Node > > d_bounds[2]; + std::map< Node, std::vector< Node > > d_set; + std::map< Node, std::vector< int > > d_set_nums; + std::map< Node, std::map< Node, Node > > d_range; + std::map< Node, std::map< Node, Node > > d_nground_range; + void hasFreeVar( Node f, Node n ); + void process( Node f, Node n, bool pol, + std::map< int, std::map< Node, Node > >& bound_lit_map, + std::map< int, std::map< Node, bool > >& bound_lit_pol_map ); + void processLiteral( Node f, Node lit, bool pol, + std::map< int, std::map< Node, Node > >& bound_lit_map, + std::map< int, std::map< Node, bool > >& bound_lit_pol_map ); + std::vector< Node > d_bound_quants; +private: + class RangeModel { + private: + BoundedIntegers * d_bi; + void allocateRange(); + public: + RangeModel(BoundedIntegers * bi, Node r, context::Context* c) : d_bi(bi), + d_range(r), d_curr_max(-1), d_range_assertions(c), d_has_range(c,false), d_curr_range(c,-1) {} + Node d_range; + int d_curr_max; + std::map< int, Node > d_range_literal; + std::map< Node, bool > d_lit_to_pol; + std::map< Node, int > d_lit_to_range; + NodeBoolMap d_range_assertions; + context::CDO< bool > d_has_range; + context::CDO< int > d_curr_range; + void initialize(); + void assertNode(Node n); + Node getNextDecisionRequest(); + }; +private: + //information for minimizing ranges + std::vector< Node > d_ranges; + //map to range model objects + std::map< Node, RangeModel * > d_rms; + //literal to range + std::map< Node, std::vector< Node > > d_lit_to_ranges; + //list of currently asserted arithmetic literals + NodeBoolMap d_assertions; +private: + //class to store whether bounding lemmas have been added + class BoundInstTrie + { + public: + std::map< Node, BoundInstTrie > d_children; + bool hasInstantiated( std::vector< Node > & vals, int index = 0, bool madeNew = false ){ + if( index>=(int)vals.size() ){ + return !madeNew; + }else{ + Node n = vals[index]; + if( d_children.find(n)==d_children.end() ){ + madeNew = true; + } + return d_children[n].hasInstantiated(vals,index+1,madeNew); + } + } + }; + std::map< Node, std::map< Node, BoundInstTrie > > d_bnd_it; +private: + void addLiteralFromRange( Node lit, Node r ); +public: + BoundedIntegers( context::Context* c, QuantifiersEngine* qe ); + + void check( Theory::Effort e ); + void registerQuantifier( Node f ); + void assertNode( Node n ); + Node getNextDecisionRequest(); + bool isBoundVar( Node f, Node v ) { return std::find( d_set[f].begin(), d_set[f].end(), v )!=d_set[f].end(); } + unsigned getNumBoundVars( Node f ) { return d_set[f].size(); } + Node getBoundVar( Node f, int i ) { return d_set[f][i]; } + int getBoundVarNum( Node f, int i ) { return d_set_nums[f][i]; } + Node getLowerBound( Node f, Node v ){ return d_bounds[0][f][v]; } + Node getUpperBound( Node f, Node v ){ return d_bounds[1][f][v]; } + void getBounds( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ); + void getBoundValues( Node f, Node v, RepSetIterator * rsi, Node & l, Node & u ); + bool isGroundRange(Node f, Node v); +}; + +} +} +} + +#endif diff --git a/src/theory/quantifiers/candidate_generator.cpp b/src/theory/quantifiers/candidate_generator.cpp index 0c423de19..42b49cf01 100644 --- a/src/theory/quantifiers/candidate_generator.cpp +++ b/src/theory/quantifiers/candidate_generator.cpp @@ -27,7 +27,7 @@ using namespace CVC4::theory; using namespace CVC4::theory::inst; bool CandidateGenerator::isLegalCandidate( Node n ){ - return ( !n.getAttribute(NoMatchAttribute()) && ( !options::cbqi() || !n.hasAttribute(InstConstantAttribute()) ) ); + return ( !n.getAttribute(NoMatchAttribute()) && ( !options::cbqi() || !quantifiers::TermDb::hasInstConstAttr(n) ) ); } void CandidateGeneratorQueue::addCandidate( Node n ) { @@ -149,7 +149,7 @@ void CandidateGeneratorQELitEq::reset( Node eqc ){ d_eq = eq::EqClassesIterator( d_qe->getEqualityQuery()->getEngine() ); } Node CandidateGeneratorQELitEq::getNextCandidate(){ - while( d_eq.isFinished() ){ + while( !d_eq.isFinished() ){ Node n = (*d_eq); ++d_eq; if( n.getType()==d_match_pattern[0].getType() ){ @@ -186,3 +186,29 @@ Node CandidateGeneratorQELitDeq::getNextCandidate(){ } return Node::null(); } + + +CandidateGeneratorQEAll::CandidateGeneratorQEAll( QuantifiersEngine* qe, Node mpat ) : + d_match_pattern( mpat ), d_qe( qe ){ + +} + +void CandidateGeneratorQEAll::resetInstantiationRound() { + +} + +void CandidateGeneratorQEAll::reset( Node eqc ) { + d_eq = eq::EqClassesIterator( d_qe->getEqualityQuery()->getEngine() ); +} + +Node CandidateGeneratorQEAll::getNextCandidate() { + while( !d_eq.isFinished() ){ + Node n = (*d_eq); + ++d_eq; + if( n.getType()==d_match_pattern.getType() ){ + //an equivalence class with the same type as the pattern, return it + return n; + } + } + return Node::null(); +} diff --git a/src/theory/quantifiers/candidate_generator.h b/src/theory/quantifiers/candidate_generator.h index 81b98ce0a..402a29848 100644 --- a/src/theory/quantifiers/candidate_generator.h +++ b/src/theory/quantifiers/candidate_generator.h @@ -69,8 +69,7 @@ public: Node getNextCandidate(); };/* class CandidateGeneratorQueue */ -class CandidateGeneratorQEDisequal; - +//the default generator class CandidateGeneratorQE : public CandidateGenerator { friend class CandidateGeneratorQEDisequal; @@ -93,27 +92,6 @@ public: Node getNextCandidate(); }; - -//class CandidateGeneratorQEDisequal : public CandidateGenerator -//{ -//private: -// //equivalence class -// Node d_eq_class; -// //equivalence class info -// EqClassInfo* d_eci; -// //equivalence class iterator -// EqClassInfo::BoolMap::const_iterator d_eqci_iter; -// //instantiator pointer -// QuantifiersEngine* d_qe; -//public: -// CandidateGeneratorQEDisequal( QuantifiersEngine* qe, Node eqc ); -// ~CandidateGeneratorQEDisequal(){} -// -// void resetInstantiationRound(); -// void reset( Node eqc ); //should be what you want to be disequal from -// Node getNextCandidate(); -//}; - class CandidateGeneratorQELitEq : public CandidateGenerator { private: @@ -150,6 +128,24 @@ public: Node getNextCandidate(); }; +class CandidateGeneratorQEAll : public CandidateGenerator +{ +private: + //the equality classes iterator + eq::EqClassesIterator d_eq; + //equality you are trying to match equalities for + Node d_match_pattern; + //einstantiator pointer + QuantifiersEngine* d_qe; +public: + CandidateGeneratorQEAll( QuantifiersEngine* qe, Node mpat ); + ~CandidateGeneratorQEAll(){} + + void resetInstantiationRound(); + void reset( Node eqc ); + Node getNextCandidate(); +}; + }/* CVC4::theory::inst namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/quantifiers/first_order_model.cpp b/src/theory/quantifiers/first_order_model.cpp index bba9c0163..63cac9c15 100644 --- a/src/theory/quantifiers/first_order_model.cpp +++ b/src/theory/quantifiers/first_order_model.cpp @@ -25,6 +25,7 @@ using namespace CVC4::kind; using namespace CVC4::context; using namespace CVC4::theory; using namespace CVC4::theory::quantifiers; +using namespace CVC4::theory::quantifiers::fmcheck; FirstOrderModel::FirstOrderModel( context::Context* c, std::string name ) : TheoryModel( c, name, true ), d_axiom_asserted( c, false ), d_forall_asserts( c ), d_isModelSet( c, false ){ @@ -38,15 +39,34 @@ void FirstOrderModel::assertQuantifier( Node n ){ } } -void FirstOrderModel::reset(){ - TheoryModel::reset(); +Node FirstOrderModel::getCurrentModelValue( Node n, bool partial ) { + std::vector< Node > children; + if( n.getNumChildren()>0 ){ + if( n.getKind()!=APPLY_UF && n.getMetaKind() == kind::metakind::PARAMETERIZED ){ + children.push_back( n.getOperator() ); + } + for (unsigned i=0; i<n.getNumChildren(); i++) { + Node nc = getCurrentModelValue( n[i], partial ); + if (nc.isNull()) { + return Node::null(); + }else{ + children.push_back( nc ); + } + } + if( n.getKind()==APPLY_UF ){ + return getCurrentUfModelValue( n, children, partial ); + }else{ + Node nn = NodeManager::currentNM()->mkNode( n.getKind(), children ); + nn = Rewriter::rewrite( nn ); + return nn; + } + }else{ + return getRepresentative(n); + } } -void FirstOrderModel::initialize( bool considerAxioms ){ - //rebuild models - d_uf_model_tree.clear(); - d_uf_model_gen.clear(); - d_array_model.clear(); +void FirstOrderModel::initialize( bool considerAxioms ) { + processInitialize(); //this is called after representatives have been chosen and the equality engine has been built //for each quantifier, collect all operators we care about for( int i=0; i<getNumAssertedQuantifiers(); i++ ){ @@ -59,6 +79,23 @@ void FirstOrderModel::initialize( bool considerAxioms ){ } void FirstOrderModel::initializeModelForTerm( Node n ){ + processInitializeModelForTerm( n ); + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + initializeModelForTerm( n[i] ); + } +} + +FirstOrderModelIG::FirstOrderModelIG(context::Context* c, std::string name) : FirstOrderModel(c,name) { + +} + +void FirstOrderModelIG::processInitialize(){ + //rebuild models + d_uf_model_tree.clear(); + d_uf_model_gen.clear(); +} + +void FirstOrderModelIG::processInitializeModelForTerm( Node n ){ if( n.getKind()==APPLY_UF ){ Node op = n.getOperator(); if( d_uf_model_tree.find( op )==d_uf_model_tree.end() ){ @@ -82,14 +119,11 @@ void FirstOrderModel::initializeModelForTerm( Node n ){ } } */ - for( int i=0; i<(int)n.getNumChildren(); i++ ){ - initializeModelForTerm( n[i] ); - } } //for evaluation of quantifier bodies -void FirstOrderModel::resetEvaluate(){ +void FirstOrderModelIG::resetEvaluate(){ d_eval_uf_use_default.clear(); d_eval_uf_model.clear(); d_eval_term_index_order.clear(); @@ -107,7 +141,7 @@ void FirstOrderModel::resetEvaluate(){ // if eVal = 0, then n' cannot be proven to be equal to phaseReq // if eVal is not 0, then // each n{ri->d_index[0]/x_0...ri->d_index[depIndex]/x_depIndex, */x_(depIndex+1) ... */x_n } is equivalent in the current model -int FirstOrderModel::evaluate( Node n, int& depIndex, RepSetIterator* ri ){ +int FirstOrderModelIG::evaluate( Node n, int& depIndex, RepSetIterator* ri ){ ++d_eval_formulas; //Debug("fmf-eval-debug") << "Evaluate " << n << " " << phaseReq << std::endl; //Notice() << "Eval " << n << std::endl; @@ -226,7 +260,7 @@ int FirstOrderModel::evaluate( Node n, int& depIndex, RepSetIterator* ri ){ } } -Node FirstOrderModel::evaluateTerm( Node n, int& depIndex, RepSetIterator* ri ){ +Node FirstOrderModelIG::evaluateTerm( Node n, int& depIndex, RepSetIterator* ri ){ //Message() << "Eval term " << n << std::endl; Node val; depIndex = ri->getNumTerms()-1; @@ -342,7 +376,7 @@ Node FirstOrderModel::evaluateTerm( Node n, int& depIndex, RepSetIterator* ri ){ return val; } -Node FirstOrderModel::evaluateTermDefault( Node n, int& depIndex, std::vector< int >& childDepIndex, RepSetIterator* ri ){ +Node FirstOrderModelIG::evaluateTermDefault( Node n, int& depIndex, std::vector< int >& childDepIndex, RepSetIterator* ri ){ depIndex = -1; if( n.getNumChildren()==0 ){ return n; @@ -372,14 +406,14 @@ Node FirstOrderModel::evaluateTermDefault( Node n, int& depIndex, std::vector< i } } -void FirstOrderModel::clearEvalFailed( int index ){ +void FirstOrderModelIG::clearEvalFailed( int index ){ for( int i=0; i<(int)d_eval_failed_lits[index].size(); i++ ){ d_eval_failed[ d_eval_failed_lits[index][i] ] = false; } d_eval_failed_lits[index].clear(); } -void FirstOrderModel::makeEvalUfModel( Node n ){ +void FirstOrderModelIG::makeEvalUfModel( Node n ){ if( d_eval_uf_model.find( n )==d_eval_uf_model.end() ){ makeEvalUfIndexOrder( n ); if( !d_eval_uf_use_default[n] ){ @@ -397,7 +431,7 @@ struct sortGetMaxVariableNum { int computeMaxVariableNum( Node n ){ if( n.getKind()==INST_CONSTANT ){ return n.getAttribute(InstVarNumAttribute()); - }else if( n.hasAttribute(InstConstantAttribute()) ){ + }else if( TermDb::hasInstConstAttr(n) ){ int maxVal = -1; for( int i=0; i<(int)n.getNumChildren(); i++ ){ int val = getMaxVariableNum( n[i] ); @@ -423,7 +457,7 @@ struct sortGetMaxVariableNum { bool operator() (Node i,Node j) { return (getMaxVariableNum(i)<getMaxVariableNum(j));} }; -void FirstOrderModel::makeEvalUfIndexOrder( Node n ){ +void FirstOrderModelIG::makeEvalUfIndexOrder( Node n ){ if( d_eval_term_index_order.find( n )==d_eval_term_index_order.end() ){ #ifdef USE_INDEX_ORDERING //sort arguments in order of least significant vs. most significant variable in default ordering @@ -460,3 +494,187 @@ void FirstOrderModel::makeEvalUfIndexOrder( Node n ){ #endif } } + +Node FirstOrderModelIG::getCurrentUfModelValue( Node n, std::vector< Node > & args, bool partial ) { + std::vector< Node > children; + children.push_back(n.getOperator()); + children.insert(children.end(), args.begin(), args.end()); + Node nv = NodeManager::currentNM()->mkNode(APPLY_UF, children); + //make the term model specifically for nv + makeEvalUfModel( nv ); + int argDepIndex; + if( d_eval_uf_use_default[nv] ){ + return d_uf_model_tree[ n.getOperator() ].getValue( this, nv, argDepIndex ); + }else{ + return d_eval_uf_model[ nv ].getValue( this, nv, argDepIndex ); + } +} + + + + + + +FirstOrderModelFmc::FirstOrderModelFmc(QuantifiersEngine * qe, context::Context* c, std::string name) : +FirstOrderModel(c, name), d_qe(qe){ + +} + +Node FirstOrderModelFmc::getUsedRepresentative(Node n, bool strict) { + //Assert( fm->hasTerm(n) ); + TypeNode tn = n.getType(); + if( tn.isBoolean() ){ + return areEqual(n, d_true) ? d_true : d_false; + }else{ + if( !hasTerm(n) ){ + if( strict ){ + return Node::null(); + }else{ + Trace("fmc-warn") << "WARNING : no representative for " << n << std::endl; + } + } + Node r = getRepresentative(n); + if( d_model_basis_rep.find(tn)!=d_model_basis_rep.end() ){ + if (r==d_model_basis_rep[tn]) { + r = d_qe->getTermDatabase()->getModelBasisTerm(tn); + } + } + return r; + } +} + +Node FirstOrderModelFmc::getCurrentUfModelValue( Node n, std::vector< Node > & args, bool partial ) { + Trace("fmc-uf-model") << "Get model value for " << n << " " << n.getKind() << std::endl; + for(unsigned i=0; i<args.size(); i++) { + args[i] = getUsedRepresentative(args[i]); + } + Assert( n.getKind()==APPLY_UF ); + return d_models[n.getOperator()]->evaluate(this, args); +} + +void FirstOrderModelFmc::processInitialize() { + if( options::fmfFmcInterval() && intervalOp.isNull() ){ + std::vector< TypeNode > types; + for(unsigned i=0; i<2; i++){ + types.push_back(NodeManager::currentNM()->integerType()); + } + TypeNode typ = NodeManager::currentNM()->mkFunctionType( types, NodeManager::currentNM()->integerType() ); + intervalOp = NodeManager::currentNM()->mkSkolem( "interval_$$", typ, "op representing interval" ); + } + for( std::map<Node, Def * >::iterator it = d_models.begin(); it != d_models.end(); ++it ){ + it->second->reset(); + } + d_model_basis_rep.clear(); +} + +void FirstOrderModelFmc::processInitializeModelForTerm(Node n) { + if( n.getKind()==APPLY_UF ){ + if( d_models.find(n.getOperator())==d_models.end()) { + d_models[n.getOperator()] = new Def; + } + } +} + +Node FirstOrderModelFmc::getSomeDomainElement(TypeNode tn){ + //check if there is even any domain elements at all + if (!d_rep_set.hasType(tn)) { + Trace("fmc-model-debug") << "Must create domain element for " << tn << "..." << std::endl; + Node mbt = d_qe->getTermDatabase()->getModelBasisTerm(tn); + d_rep_set.d_type_reps[tn].push_back(mbt); + }else if( d_rep_set.d_type_reps[tn].size()==0 ){ + Message() << "empty reps" << std::endl; + exit(0); + } + return d_rep_set.d_type_reps[tn][0]; +} + + +bool FirstOrderModelFmc::isStar(Node n) { + return n==getStar(n.getType()); +} + +Node FirstOrderModelFmc::getStar(TypeNode tn) { + if( d_type_star.find(tn)==d_type_star.end() ){ + Node st = NodeManager::currentNM()->mkSkolem( "star_$$", tn, "skolem created for full-model checking" ); + d_type_star[tn] = st; + } + return d_type_star[tn]; +} + +Node FirstOrderModelFmc::getStarElement(TypeNode tn) { + Node st = getStar(tn); + if( options::fmfFmcInterval() && tn.isInteger() ){ + st = getInterval( st, st ); + } + return st; +} + +bool FirstOrderModelFmc::isModelBasisTerm(Node n) { + return n==getModelBasisTerm(n.getType()); +} + +Node FirstOrderModelFmc::getModelBasisTerm(TypeNode tn) { + return d_qe->getTermDatabase()->getModelBasisTerm(tn); +} + +Node FirstOrderModelFmc::getFunctionValue(Node op, const char* argPrefix ) { + Trace("fmc-model") << "Get function value for " << op << std::endl; + TypeNode type = op.getType(); + std::vector< Node > vars; + for( size_t i=0; i<type.getNumChildren()-1; i++ ){ + std::stringstream ss; + ss << argPrefix << (i+1); + Node b = NodeManager::currentNM()->mkBoundVar( ss.str(), type[i] ); + vars.push_back( b ); + } + Node boundVarList = NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, vars); + Node curr; + for( int i=(d_models[op]->d_cond.size()-1); i>=0; i--) { + Node v = getRepresentative( d_models[op]->d_value[i] ); + if( curr.isNull() ){ + curr = v; + }else{ + //make the condition + Node cond = d_models[op]->d_cond[i]; + std::vector< Node > children; + for( unsigned j=0; j<cond.getNumChildren(); j++) { + if (isInterval(cond[j])){ + if( !isStar(cond[j][0]) ){ + children.push_back( NodeManager::currentNM()->mkNode( GEQ, vars[j], cond[j][0] ) ); + } + if( !isStar(cond[j][1]) ){ + children.push_back( NodeManager::currentNM()->mkNode( LT, vars[j], cond[j][1] ) ); + } + }else if (!isStar(cond[j])){ + Node c = getUsedRepresentative( cond[j] ); + children.push_back( NodeManager::currentNM()->mkNode( EQUAL, vars[j], c ) ); + } + } + Assert( !children.empty() ); + Node cc = children.size()==1 ? children[0] : NodeManager::currentNM()->mkNode( AND, children ); + curr = NodeManager::currentNM()->mkNode( ITE, cc, v, curr ); + } + } + curr = Rewriter::rewrite( curr ); + return NodeManager::currentNM()->mkNode(kind::LAMBDA, boundVarList, curr); +} + +bool FirstOrderModelFmc::isInterval(Node n) { + return n.getKind()==APPLY_UF && n.getOperator()==intervalOp; +} + +Node FirstOrderModelFmc::getInterval( Node lb, Node ub ){ + return NodeManager::currentNM()->mkNode( APPLY_UF, intervalOp, lb, ub ); +} + +bool FirstOrderModelFmc::isInRange( Node v, Node i ) { + for( unsigned b=0; b<2; b++ ){ + if( !isStar( i[b] ) ){ + if( ( b==0 && i[b].getConst<Rational>() > v.getConst<Rational>() ) || + ( b==1 && i[b].getConst<Rational>() <= v.getConst<Rational>() ) ){ + return false; + } + } + } + return true; +} diff --git a/src/theory/quantifiers/first_order_model.h b/src/theory/quantifiers/first_order_model.h index 76f21e19c..f6e012660 100644 --- a/src/theory/quantifiers/first_order_model.h +++ b/src/theory/quantifiers/first_order_model.h @@ -19,7 +19,6 @@ #include "theory/model.h" #include "theory/uf/theory_uf_model.h" -#include "theory/arrays/theory_arrays_model.h" namespace CVC4 { namespace theory { @@ -30,33 +29,22 @@ namespace quantifiers{ class TermDb; +class FirstOrderModelIG; +namespace fmcheck { + class FirstOrderModelFmc; +} + class FirstOrderModel : public TheoryModel { private: - //for initialize model - void initializeModelForTerm( Node n ); /** whether an axiom is asserted */ context::CDO< bool > d_axiom_asserted; /** list of quantifiers asserted in the current context */ context::CDList<Node> d_forall_asserts; /** is model set */ context::CDO< bool > d_isModelSet; -public: //for Theory UF: - //models for each UF operator - std::map< Node, uf::UfModelTree > d_uf_model_tree; - //model generators - std::map< Node, uf::UfModelTreeGenerator > d_uf_model_gen; -private: - //map from terms to the models used to calculate their value - std::map< Node, bool > d_eval_uf_use_default; - std::map< Node, uf::UfModelTree > d_eval_uf_model; - void makeEvalUfModel( Node n ); - //index ordering to use for each term - std::map< Node, std::vector< int > > d_eval_term_index_order; - void makeEvalUfIndexOrder( Node n ); -public: //for Theory Arrays: - //default value for each non-store array - std::map< Node, arrays::ArrayModel > d_array_model; + /** get current model value */ + virtual Node getCurrentUfModelValue( Node n, std::vector< Node > & args, bool partial ) = 0; public: //for Theory Quantifiers: /** assert quantifier */ void assertQuantifier( Node n ); @@ -66,19 +54,51 @@ public: //for Theory Quantifiers: Node getAssertedQuantifier( int i ) { return d_forall_asserts[i]; } /** bool axiom asserted */ bool isAxiomAsserted() { return d_axiom_asserted; } + /** initialize model for term */ + void initializeModelForTerm( Node n ); + virtual void processInitializeModelForTerm( Node n ) = 0; public: FirstOrderModel( context::Context* c, std::string name ); virtual ~FirstOrderModel(){} - // reset the model - void reset(); + virtual FirstOrderModelIG * asFirstOrderModelIG() { return NULL; } + virtual fmcheck::FirstOrderModelFmc * asFirstOrderModelFmc() { return NULL; } // initialize the model void initialize( bool considerAxioms = true ); + virtual void processInitialize() = 0; /** mark model set */ void markModelSet() { d_isModelSet = true; } /** is model set */ bool isModelSet() { return d_isModelSet; } + /** get current model value */ + Node getCurrentModelValue( Node n, bool partial = false ); +};/* class FirstOrderModel */ + + +class FirstOrderModelIG : public FirstOrderModel +{ +public: //for Theory UF: + //models for each UF operator + std::map< Node, uf::UfModelTree > d_uf_model_tree; + //model generators + std::map< Node, uf::UfModelTreeGenerator > d_uf_model_gen; +private: + //map from terms to the models used to calculate their value + std::map< Node, bool > d_eval_uf_use_default; + std::map< Node, uf::UfModelTree > d_eval_uf_model; + void makeEvalUfModel( Node n ); + //index ordering to use for each term + std::map< Node, std::vector< int > > d_eval_term_index_order; + void makeEvalUfIndexOrder( Node n ); + /** get current model value */ + Node getCurrentUfModelValue( Node n, std::vector< Node > & args, bool partial ); //the following functions are for evaluating quantifier bodies public: + FirstOrderModelIG(context::Context* c, std::string name); + FirstOrderModelIG * asFirstOrderModelIG() { return this; } + // initialize the model + void processInitialize(); + //for initialize model + void processInitializeModelForTerm( Node n ); /** reset evaluation */ void resetEvaluate(); /** evaluate functions */ @@ -97,7 +117,49 @@ private: void clearEvalFailed( int index ); std::map< Node, bool > d_eval_failed; std::map< int, std::vector< Node > > d_eval_failed_lits; -};/* class FirstOrderModel */ +}; + + +namespace fmcheck { + +class Def; + +class FirstOrderModelFmc : public FirstOrderModel +{ + friend class FullModelChecker; +private: + /** quant engine */ + QuantifiersEngine * d_qe; + /** models for UF */ + std::map<Node, Def * > d_models; + std::map<TypeNode, Node > d_model_basis_rep; + std::map<TypeNode, Node > d_type_star; + Node intervalOp; + Node getUsedRepresentative(Node n, bool strict = false); + /** get current model value */ + Node getCurrentUfModelValue( Node n, std::vector< Node > & args, bool partial ); + void processInitializeModelForTerm(Node n); +public: + FirstOrderModelFmc(QuantifiersEngine * qe, context::Context* c, std::string name); + FirstOrderModelFmc * asFirstOrderModelFmc() { return this; } + // initialize the model + void processInitialize(); + + Node getFunctionValue(Node op, const char* argPrefix ); + + bool isStar(Node n); + Node getStar(TypeNode tn); + Node getStarElement(TypeNode tn); + bool isModelBasisTerm(Node n); + Node getModelBasisTerm(TypeNode tn); + Node getSomeDomainElement(TypeNode tn); + bool isInterval(Node n); + Node getInterval( Node lb, Node ub ); + bool isInRange( Node v, Node i ); +}; + +} + }/* CVC4::theory::quantifiers namespace */ }/* CVC4::theory namespace */ diff --git a/src/theory/quantifiers/first_order_reasoning.cpp b/src/theory/quantifiers/first_order_reasoning.cpp new file mode 100644 index 000000000..ebfb55f08 --- /dev/null +++ b/src/theory/quantifiers/first_order_reasoning.cpp @@ -0,0 +1,171 @@ +/********************* */ +/*! \file first_order_reasoning.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009-2012 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief first order reasoning module + ** + **/ + +#include <vector> + +#include "theory/quantifiers/first_order_reasoning.h" +#include "theory/rewriter.h" + +using namespace CVC4; +using namespace CVC4::kind; +using namespace std; + +namespace CVC4 { + + +void FirstOrderPropagation::collectLits( Node n, std::vector<Node> & lits ){ + if( n.getKind()==FORALL ){ + collectLits( n[1], lits ); + }else if( n.getKind()==OR ){ + for(unsigned j=0; j<n.getNumChildren(); j++) { + collectLits(n[j], lits ); + } + }else{ + lits.push_back( n ); + } +} + +void FirstOrderPropagation::simplify( std::vector< Node >& assertions ){ + for( unsigned i=0; i<assertions.size(); i++) { + Trace("fo-rsn") << "Assert : " << assertions[i] << std::endl; + } + + //process all assertions + int num_processed; + int num_true = 0; + int num_rounds = 0; + do { + num_processed = 0; + for( unsigned i=0; i<assertions.size(); i++ ){ + if( d_assertion_true.find(assertions[i])==d_assertion_true.end() ){ + std::vector< Node > fo_lits; + collectLits( assertions[i], fo_lits ); + Node unitLit = process( assertions[i], fo_lits ); + if( !unitLit.isNull() ){ + Trace("fo-rsn-debug") << "...possible unit literal : " << unitLit << " from " << assertions[i] << std::endl; + bool pol = unitLit.getKind()!=NOT; + unitLit = unitLit.getKind()==NOT ? unitLit[0] : unitLit; + if( unitLit.getKind()==EQUAL ){ + + }else if( unitLit.getKind()==APPLY_UF ){ + //make sure all are unique vars; + bool success = true; + std::vector< Node > unique_vars; + for( unsigned j=0; j<unitLit.getNumChildren(); j++) { + if( unitLit[j].getKind()==BOUND_VARIABLE ){ + if( std::find(unique_vars.begin(), unique_vars.end(), unitLit[j])==unique_vars.end() ){ + unique_vars.push_back( unitLit[j] ); + }else{ + success = false; + break; + } + }else{ + success = false; + break; + } + } + if( success ){ + d_const_def[unitLit.getOperator()] = NodeManager::currentNM()->mkConst(pol); + Trace("fo-rsn") << "Propagate : " << unitLit.getOperator() << " == " << pol << std::endl; + Trace("fo-rsn") << " from : " << assertions[i] << std::endl; + d_assertion_true[assertions[i]] = true; + num_processed++; + } + }else if( unitLit.getKind()==VARIABLE ){ + d_const_def[unitLit] = NodeManager::currentNM()->mkConst(pol); + Trace("fo-rsn") << "Propagate variable : " << unitLit << " == " << pol << std::endl; + Trace("fo-rsn") << " from : " << assertions[i] << std::endl; + d_assertion_true[assertions[i]] = true; + num_processed++; + } + } + if( d_assertion_true.find(assertions[i])!=d_assertion_true.end() ){ + num_true++; + } + } + } + num_rounds++; + }while( num_processed>0 ); + Trace("fo-rsn-sum") << "Simplified " << num_true << " / " << assertions.size() << " in " << num_rounds << " rounds." << std::endl; + for( unsigned i=0; i<assertions.size(); i++ ){ + assertions[i] = theory::Rewriter::rewrite( simplify( assertions[i] ) ); + } +} + +Node FirstOrderPropagation::process(Node a, std::vector< Node > & lits) { + int index = -1; + for( unsigned i=0; i<lits.size(); i++) { + bool pol = lits[i].getKind()!=NOT; + Node n = lits[i].getKind()==NOT ? lits[i][0] : lits[i]; + Node litDef; + if( n.getKind()==APPLY_UF ){ + if( d_const_def.find(n.getOperator())!=d_const_def.end() ){ + litDef = d_const_def[n.getOperator()]; + } + }else if( n.getKind()==VARIABLE ){ + if( d_const_def.find(n)!=d_const_def.end() ){ + litDef = d_const_def[n]; + } + } + if( !litDef.isNull() ){ + Node poln = NodeManager::currentNM()->mkConst( pol ); + if( litDef==poln ){ + Trace("fo-rsn-debug") << "Assertion " << a << " is true because of " << lits[i] << std::endl; + d_assertion_true[a] = true; + return Node::null(); + } + } + if( litDef.isNull() ){ + if( index==-1 ){ + //store undefined index + index = i; + }else{ + //two undefined, return null + return Node::null(); + } + } + } + if( index!=-1 ){ + return lits[index]; + }else{ + return Node::null(); + } +} + +Node FirstOrderPropagation::simplify( Node n ) { + if( n.getKind()==VARIABLE ){ + if( d_const_def.find(n)!=d_const_def.end() ){ + return d_const_def[n]; + } + }else if( n.getKind()==APPLY_UF ){ + if( d_const_def.find(n.getOperator())!=d_const_def.end() ){ + return d_const_def[n.getOperator()]; + } + } + if( n.getNumChildren()==0 ){ + return n; + }else{ + std::vector< Node > children; + if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ + children.push_back( n.getOperator() ); + } + for(unsigned i=0; i<n.getNumChildren(); i++) { + children.push_back( simplify(n[i]) ); + } + 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 new file mode 100644 index 000000000..5f217050c --- /dev/null +++ b/src/theory/quantifiers/first_order_reasoning.h @@ -0,0 +1,45 @@ +/********************* */ +/*! \file first_order_reasoning.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009-2012 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Pre-process step for first-order reasoning + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__FIRST_ORDER_REASONING_H +#define __CVC4__FIRST_ORDER_REASONING_H + +#include <iostream> +#include <string> +#include <vector> +#include <map> +#include "expr/node.h" +#include "expr/type_node.h" + +namespace CVC4 { + +class FirstOrderPropagation { +private: + std::map< Node, Node > d_const_def; + std::map< Node, bool > d_assertion_true; + Node process(Node a, std::vector< Node > & lits); + void collectLits( Node n, std::vector<Node> & lits ); + Node simplify( Node n ); +public: + FirstOrderPropagation(){} + ~FirstOrderPropagation(){} + + void simplify( std::vector< Node >& assertions ); +}; + +} + +#endif diff --git a/src/theory/quantifiers/full_model_check.cpp b/src/theory/quantifiers/full_model_check.cpp new file mode 100644 index 000000000..bf10369e6 --- /dev/null +++ b/src/theory/quantifiers/full_model_check.cpp @@ -0,0 +1,1409 @@ +/********************* */ +/*! \file full_model_check.cpp + ** \verbatim + ** Original author: 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 full model check class + **/ + +#include "theory/quantifiers/full_model_check.h" +#include "theory/quantifiers/first_order_model.h" +#include "theory/quantifiers/options.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::context; +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; +using namespace CVC4::theory::inst; +using namespace CVC4::theory::quantifiers::fmcheck; + +struct ModelBasisArgSort +{ + std::vector< Node > d_terms; + bool operator() (int i,int j) { + return (d_terms[i].getAttribute(ModelBasisArgAttribute()) < + d_terms[j].getAttribute(ModelBasisArgAttribute()) ); + } +}; + + +struct ConstRationalSort +{ + std::vector< Node > d_terms; + bool operator() (int i, int j) { + return (d_terms[i].getConst<Rational>() < d_terms[j].getConst<Rational>() ); + } +}; + + +bool EntryTrie::hasGeneralization( FirstOrderModelFmc * m, Node c, int index ) { + if (index==(int)c.getNumChildren()) { + return d_data!=-1; + }else{ + TypeNode tn = c[index].getType(); + Node st = m->getStar(tn); + if(d_child.find(st)!=d_child.end()) { + if( d_child[st].hasGeneralization(m, c, index+1) ){ + return true; + } + } + if( c[index]!=st && d_child.find( c[index] )!=d_child.end() ){ + if( d_child[ c[index] ].hasGeneralization(m, c, index+1) ){ + return true; + } + } + if( !options::fmfFmcInterval() || !c[index].getType().isInteger() ){ + //for star: check if all children are defined and have generalizations + if( options::fmfFmcCoverSimplify() && c[index]==st ){ + //check if all children exist and are complete + int num_child_def = d_child.size() - (d_child.find(st)!=d_child.end() ? 1 : 0); + if( num_child_def==m->d_rep_set.getNumRepresentatives(tn) ){ + bool complete = true; + for ( std::map<Node,EntryTrie>::iterator it = d_child.begin(); it != d_child.end(); ++it ){ + if( !m->isStar(it->first) ){ + if( !it->second.hasGeneralization(m, c, index+1) ){ + complete = false; + break; + } + } + } + if( complete ){ + return true; + } + } + } + } + + return false; + } +} + +int EntryTrie::getGeneralizationIndex( FirstOrderModelFmc * m, std::vector<Node> & inst, int index ) { + Debug("fmc-entry-trie") << "Get generalization index " << inst.size() << " " << index << std::endl; + if (index==(int)inst.size()) { + return d_data; + }else{ + int minIndex = -1; + if( options::fmfFmcInterval() && inst[index].getType().isInteger() ){ + for( std::map<Node,EntryTrie>::iterator it = d_child.begin(); it != d_child.end(); ++it ){ + if( !m->isInterval( it->first ) ){ + std::cout << "Not an interval during getGenIndex " << it->first << std::endl; + exit( 11 ); + } + //check if it is in the range + if( m->isInRange(inst[index], it->first ) ){ + int gindex = it->second.getGeneralizationIndex(m, inst, index+1); + if( minIndex==-1 || (gindex!=-1 && gindex<minIndex) ){ + minIndex = gindex; + } + } + } + }else{ + Node st = m->getStar(inst[index].getType()); + if(d_child.find(st)!=d_child.end()) { + minIndex = d_child[st].getGeneralizationIndex(m, inst, index+1); + } + Node cc = inst[index]; + if( cc!=st && d_child.find( cc )!=d_child.end() ){ + int gindex = d_child[ cc ].getGeneralizationIndex(m, inst, index+1); + if (minIndex==-1 || (gindex!=-1 && gindex<minIndex) ){ + minIndex = gindex; + } + } + } + return minIndex; + } +} + +void EntryTrie::addEntry( FirstOrderModelFmc * m, Node c, Node v, int data, int index ) { + if (index==(int)c.getNumChildren()) { + if(d_data==-1) { + d_data = data; + } + } + else { + d_child[ c[index] ].addEntry(m,c,v,data,index+1); + if( d_complete==0 ){ + d_complete = -1; + } + } +} + +void EntryTrie::getEntries( FirstOrderModelFmc * m, Node c, std::vector<int> & compat, std::vector<int> & gen, int index, bool is_gen ) { + if (index==(int)c.getNumChildren()) { + if( d_data!=-1) { + if( is_gen ){ + gen.push_back(d_data); + } + compat.push_back(d_data); + } + }else{ + if (m->isStar(c[index])) { + for ( std::map<Node,EntryTrie>::iterator it = d_child.begin(); it != d_child.end(); ++it ){ + it->second.getEntries(m, c, compat, gen, index+1, is_gen ); + } + }else{ + Node st = m->getStar(c[index].getType()); + if(d_child.find(st)!=d_child.end()) { + d_child[st].getEntries(m, c, compat, gen, index+1, false); + } + if( d_child.find( c[index] )!=d_child.end() ){ + d_child[ c[index] ].getEntries(m, c, compat, gen, index+1, is_gen); + } + } + + } +} + +void EntryTrie::collectIndices(Node c, int index, std::vector< int >& indices ) { + if (index==(int)c.getNumChildren()) { + if( d_data!=-1 ){ + indices.push_back( d_data ); + } + }else{ + for ( std::map<Node,EntryTrie>::iterator it = d_child.begin(); it != d_child.end(); ++it ){ + it->second.collectIndices(c, index+1, indices ); + } + } +} + +bool EntryTrie::isComplete(FirstOrderModelFmc * m, Node c, int index) { + if( d_complete==-1 ){ + d_complete = 1; + if (index<(int)c.getNumChildren()) { + Node st = m->getStar(c[index].getType()); + if(d_child.find(st)!=d_child.end()) { + if (!d_child[st].isComplete(m, c, index+1)) { + d_complete = 0; + } + }else{ + d_complete = 0; + } + } + } + return d_complete==1; +} + +bool Def::addEntry( FirstOrderModelFmc * m, Node c, Node v) { + if (d_et.hasGeneralization(m, c)) { + Trace("fmc-debug") << "Already has generalization, skip." << std::endl; + return false; + } + int newIndex = (int)d_cond.size(); + if (!d_has_simplified) { + std::vector<int> compat; + std::vector<int> gen; + d_et.getEntries(m, c, compat, gen); + for( unsigned i=0; i<compat.size(); i++) { + if( d_status[compat[i]]==status_unk ){ + if( d_value[compat[i]]!=v ){ + d_status[compat[i]] = status_non_redundant; + } + } + } + for( unsigned i=0; i<gen.size(); i++) { + if( d_status[gen[i]]==status_unk ){ + if( d_value[gen[i]]==v ){ + d_status[gen[i]] = status_redundant; + } + } + } + d_status.push_back( status_unk ); + } + d_et.addEntry(m, c, v, newIndex); + d_cond.push_back(c); + d_value.push_back(v); + return true; +} + +Node Def::evaluate( FirstOrderModelFmc * m, std::vector<Node>& inst ) { + int gindex = d_et.getGeneralizationIndex(m, inst); + if (gindex!=-1) { + return d_value[gindex]; + }else{ + Trace("fmc-warn") << "Warning : evaluation came up null!" << std::endl; + return Node::null(); + } +} + +int Def::getGeneralizationIndex( FirstOrderModelFmc * m, std::vector<Node>& inst ) { + return d_et.getGeneralizationIndex(m, inst); +} + +void Def::basic_simplify( FirstOrderModelFmc * m ) { + d_has_simplified = true; + std::vector< Node > cond; + cond.insert( cond.end(), d_cond.begin(), d_cond.end() ); + d_cond.clear(); + std::vector< Node > value; + value.insert( value.end(), d_value.begin(), d_value.end() ); + d_value.clear(); + d_et.reset(); + for (unsigned i=0; i<d_status.size(); i++) { + if( d_status[i]!=status_redundant ){ + addEntry(m, cond[i], value[i]); + } + } + d_status.clear(); +} + +void Def::simplify(FullModelChecker * mc, FirstOrderModelFmc * m) { + basic_simplify( m ); + + //check if the last entry is not all star, if so, we can make the last entry all stars + if( !d_cond.empty() ){ + bool last_all_stars = true; + Node cc = d_cond[d_cond.size()-1]; + for( unsigned i=0; i<cc.getNumChildren(); i++ ){ + if( !m->isInterval(cc[i]) && !m->isStar(cc[i]) ){ + last_all_stars = false; + break; + } + } + if( !last_all_stars ){ + Trace("fmc-cover-simplify") << "Need to modify last entry to be all stars." << std::endl; + Trace("fmc-cover-simplify") << "Before: " << std::endl; + debugPrint("fmc-cover-simplify",Node::null(), mc); + Trace("fmc-cover-simplify") << std::endl; + std::vector< Node > cond; + cond.insert( cond.end(), d_cond.begin(), d_cond.end() ); + d_cond.clear(); + std::vector< Node > value; + value.insert( value.end(), d_value.begin(), d_value.end() ); + d_value.clear(); + d_et.reset(); + d_has_simplified = false; + //change the last to all star + std::vector< Node > nc; + nc.push_back( cc.getOperator() ); + for( unsigned j=0; j< cc.getNumChildren(); j++){ + nc.push_back(m->getStarElement(cc[j].getType())); + } + cond[cond.size()-1] = NodeManager::currentNM()->mkNode( APPLY_UF, nc ); + //re-add the entries + for (unsigned i=0; i<cond.size(); i++) { + addEntry(m, cond[i], value[i]); + } + Trace("fmc-cover-simplify") << "Finished re-adding entries." << std::endl; + basic_simplify( m ); + Trace("fmc-cover-simplify") << "After: " << std::endl; + debugPrint("fmc-cover-simplify",Node::null(), mc); + Trace("fmc-cover-simplify") << std::endl; + } + } +} + +void Def::debugPrint(const char * tr, Node op, FullModelChecker * m) { + if (!op.isNull()) { + Trace(tr) << "Model for " << op << " : " << std::endl; + } + for( unsigned i=0; i<d_cond.size(); i++) { + //print the condition + if (!op.isNull()) { + Trace(tr) << op; + } + m->debugPrintCond(tr, d_cond[i], true); + Trace(tr) << " -> "; + m->debugPrint(tr, d_value[i]); + Trace(tr) << std::endl; + } +} + + +FullModelChecker::FullModelChecker(context::Context* c, QuantifiersEngine* qe) : +QModelBuilder( c, qe ){ + d_true = NodeManager::currentNM()->mkConst(true); + d_false = NodeManager::currentNM()->mkConst(false); +} + +bool FullModelChecker::optBuildAtFullModel() { + //need to build after full model has taken effect if we are constructing interval models + // this is because we need to have a constant in all integer equivalence classes + return options::fmfFmcInterval(); +} + +void FullModelChecker::processBuildModel(TheoryModel* m, bool fullModel){ + FirstOrderModelFmc * fm = ((FirstOrderModelFmc*)m)->asFirstOrderModelFmc(); + if( fullModel==optBuildAtFullModel() ){ + Trace("fmc") << "---Full Model Check reset() " << std::endl; + fm->initialize( d_considerAxioms ); + d_quant_models.clear(); + d_rep_ids.clear(); + d_star_insts.clear(); + //process representatives + for( std::map< TypeNode, std::vector< Node > >::iterator it = fm->d_rep_set.d_type_reps.begin(); + it != fm->d_rep_set.d_type_reps.end(); ++it ){ + if( it->first.isSort() ){ + Trace("fmc") << "Cardinality( " << it->first << " )" << " = " << it->second.size() << std::endl; + Node mbt = d_qe->getTermDatabase()->getModelBasisTerm(it->first); + Node rmbt = fm->getUsedRepresentative( mbt); + int mbt_index = -1; + Trace("fmc") << " Model basis term : " << mbt << std::endl; + for( size_t a=0; a<it->second.size(); a++ ){ + Node r = fm->getUsedRepresentative( it->second[a] ); + std::vector< Node > eqc; + ((EqualityQueryQuantifiersEngine*)d_qe->getEqualityQuery())->getEquivalenceClass( r, eqc ); + Trace("fmc-model-debug") << " " << (it->second[a]==r) << (r==mbt); + Trace("fmc-model-debug") << " : " << it->second[a] << " : " << r << " : "; + //Trace("fmc-model-debug") << r2 << " : " << ir << " : "; + Trace("fmc-model-debug") << " {"; + //find best selection for representative + for( size_t i=0; i<eqc.size(); i++ ){ + Trace("fmc-model-debug") << eqc[i] << ", "; + } + Trace("fmc-model-debug") << "}" << std::endl; + + //if this is the model basis eqc, replace with actual model basis term + if (r==rmbt || (mbt_index==-1 && a==(it->second.size()-1))) { + fm->d_model_basis_rep[it->first] = r; + r = mbt; + mbt_index = a; + } + d_rep_ids[it->first][r] = (int)a; + } + Trace("fmc-model-debug") << std::endl; + + if (mbt_index==-1) { + std::cout << " WARNING: model basis term is not a representative!" << std::endl; + exit(0); + }else{ + Trace("fmc") << "Star index for " << it->first << " is " << mbt_index << std::endl; + } + } + } + //also process non-rep set sorts + for( std::map<Node, Def * >::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ) { + Node op = it->first; + TypeNode tno = op.getType(); + for( unsigned i=0; i<tno.getNumChildren(); i++) { + initializeType( fm, tno[i] ); + } + } + //now, make models + for( std::map<Node, Def * >::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ) { + Node op = it->first; + //reset the model + fm->d_models[op]->reset(); + + Trace("fmc-model-debug") << fm->d_uf_terms[op].size() << " model values for " << op << " ... " << std::endl; + for( size_t i=0; i<fm->d_uf_terms[op].size(); i++ ){ + Node r = fm->getUsedRepresentative(fm->d_uf_terms[op][i]); + Trace("fmc-model-debug") << fm->d_uf_terms[op][i] << " -> " << r << std::endl; + } + Trace("fmc-model-debug") << std::endl; + + + std::vector< Node > add_conds; + std::vector< Node > add_values; + bool needsDefault = true; + for( size_t i=0; i<fm->d_uf_terms[op].size(); i++ ){ + Node n = fm->d_uf_terms[op][i]; + if( !n.getAttribute(NoMatchAttribute()) ){ + add_conds.push_back( n ); + add_values.push_back( n ); + } + } + //possibly get default + if( needsDefault ){ + Node nmb = d_qe->getTermDatabase()->getModelBasisOpTerm(op); + //add default value if necessary + if( fm->hasTerm( nmb ) ){ + Trace("fmc-model-debug") << "Add default " << nmb << std::endl; + add_conds.push_back( nmb ); + add_values.push_back( nmb ); + }else{ + Node vmb = getSomeDomainElement(fm, nmb.getType()); + Trace("fmc-model-debug") << "Add default to default representative " << nmb << " "; + Trace("fmc-model-debug") << fm->d_rep_set.d_type_reps[nmb.getType()].size() << std::endl; + add_conds.push_back( nmb ); + add_values.push_back( vmb ); + } + } + + std::vector< Node > conds; + std::vector< Node > values; + std::vector< Node > entry_conds; + //get the entries for the mdoel + for( size_t i=0; i<add_conds.size(); i++ ){ + Node c = add_conds[i]; + Node v = add_values[i]; + std::vector< Node > children; + std::vector< Node > entry_children; + children.push_back(op); + entry_children.push_back(op); + bool hasNonStar = false; + for( unsigned i=0; i<c.getNumChildren(); i++) { + Node ri = fm->getUsedRepresentative( c[i]); + if( !ri.getType().isSort() && !ri.isConst() ){ + Trace("fmc-warn") << "Warning : model has non-constant argument in model " << ri << std::endl; + } + children.push_back(ri); + if( !options::fmfFmcInterval() || !ri.getType().isInteger() ){ + if (fm->isModelBasisTerm(ri) ) { + ri = fm->getStar( ri.getType() ); + }else{ + hasNonStar = true; + } + } + entry_children.push_back(ri); + } + Node n = NodeManager::currentNM()->mkNode( APPLY_UF, children ); + Node nv = fm->getUsedRepresentative( v ); + if( !nv.getType().isSort() && !nv.isConst() ){ + Trace("fmc-warn") << "Warning : model has non-constant value in model " << nv << std::endl; + } + Node en = (useSimpleModels() && hasNonStar) ? n : NodeManager::currentNM()->mkNode( APPLY_UF, entry_children ); + if( std::find(conds.begin(), conds.end(), n )==conds.end() ){ + Trace("fmc-model-debug") << "- add " << n << " -> " << nv << " (entry is " << en << ")" << std::endl; + conds.push_back(n); + values.push_back(nv); + entry_conds.push_back(en); + } + else { + Trace("fmc-model-debug") << "Already have entry for : " << n << " -> " << nv << " (entry is " << en << ")" << std::endl; + } + } + + + //sort based on # default arguments + std::vector< int > indices; + ModelBasisArgSort mbas; + for (int i=0; i<(int)conds.size(); i++) { + d_qe->getTermDatabase()->computeModelBasisArgAttribute( conds[i] ); + mbas.d_terms.push_back(conds[i]); + indices.push_back(i); + } + std::sort( indices.begin(), indices.end(), mbas ); + + for (int i=0; i<(int)indices.size(); i++) { + fm->d_models[op]->addEntry(fm, entry_conds[indices[i]], values[indices[i]]); + } + + + if( options::fmfFmcInterval() ){ + Trace("fmc-interval-model") << "Changing to interval model, Before : " << std::endl; + fm->d_models[op]->debugPrint("fmc-interval-model", op, this); + Trace("fmc-interval-model") << std::endl; + std::vector< int > indices; + for( int i=0; i<(int)fm->d_models[op]->d_cond.size(); i++ ){ + indices.push_back( i ); + } + std::map< int, std::map< int, Node > > changed_vals; + makeIntervalModel( fm, op, indices, 0, changed_vals ); + + std::vector< Node > conds; + std::vector< Node > values; + for( unsigned i=0; i<fm->d_models[op]->d_cond.size(); i++ ){ + if( changed_vals.find(i)==changed_vals.end() ){ + conds.push_back( fm->d_models[op]->d_cond[i] ); + }else{ + std::vector< Node > children; + children.push_back( op ); + for( unsigned j=0; j<fm->d_models[op]->d_cond[i].getNumChildren(); j++ ){ + if( changed_vals[i].find(j)==changed_vals[i].end() ){ + children.push_back( fm->d_models[op]->d_cond[i][j] ); + }else{ + children.push_back( changed_vals[i][j] ); + } + } + Node nc = NodeManager::currentNM()->mkNode( APPLY_UF, children ); + conds.push_back( nc ); + Trace("fmc-interval-model") << "Interval : Entry #" << i << " changed to "; + debugPrintCond("fmc-interval-model", nc, true ); + Trace("fmc-interval-model") << std::endl; + } + values.push_back( fm->d_models[op]->d_value[i] ); + } + fm->d_models[op]->reset(); + for( unsigned i=0; i<conds.size(); i++ ){ + fm->d_models[op]->addEntry(fm, conds[i], values[i] ); + } + } + + Trace("fmc-model-simplify") << "Before simplification : " << std::endl; + fm->d_models[op]->debugPrint("fmc-model-simplify", op, this); + Trace("fmc-model-simplify") << std::endl; + + Trace("fmc-model-simplify") << "Simplifying " << op << "..." << std::endl; + fm->d_models[op]->simplify( this, fm ); + + fm->d_models[op]->debugPrint("fmc-model", op, this); + Trace("fmc-model") << std::endl; + } + } + if( fullModel ){ + //make function values + for( std::map<Node, Def * >::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ){ + m->d_uf_models[ it->first ] = getFunctionValue( fm, it->first, "$x" ); + } + TheoryEngineModelBuilder::processBuildModel( m, fullModel ); + //mark that the model has been set + fm->markModelSet(); + //debug the model + debugModel( fm ); + } +} + +void FullModelChecker::initializeType( FirstOrderModelFmc * fm, TypeNode tn ){ + if( fm->d_model_basis_rep.find( tn )==fm->d_model_basis_rep.end() ){ + Trace("fmc") << "Initialize type " << tn << " hasType = " << fm->d_rep_set.hasType(tn) << std::endl; + Node mbn; + if (!fm->d_rep_set.hasType(tn)) { + mbn = fm->getSomeDomainElement(tn); + }else{ + mbn = d_qe->getTermDatabase()->getModelBasisTerm(tn); + } + Node mbnr = fm->getUsedRepresentative( mbn ); + fm->d_model_basis_rep[tn] = mbnr; + Trace("fmc") << "Add model basis for type " << tn << " : " << mbn << " " << mbnr << std::endl; + } +} + +void FullModelChecker::debugPrintCond(const char * tr, Node n, bool dispStar) { + Trace(tr) << "("; + for( unsigned j=0; j<n.getNumChildren(); j++) { + if( j>0 ) Trace(tr) << ", "; + debugPrint(tr, n[j], dispStar); + } + Trace(tr) << ")"; +} + +void FullModelChecker::debugPrint(const char * tr, Node n, bool dispStar) { + FirstOrderModelFmc * fm = (FirstOrderModelFmc *)d_qe->getModel(); + if( n.isNull() ){ + Trace(tr) << "null"; + } + else if(fm->isStar(n) && dispStar) { + Trace(tr) << "*"; + } + else if(fm->isInterval(n)) { + debugPrint(tr, n[0], dispStar ); + Trace(tr) << "..."; + debugPrint(tr, n[1], dispStar ); + }else{ + TypeNode tn = n.getType(); + if( d_rep_ids.find(tn)!=d_rep_ids.end() ){ + if (d_rep_ids[tn].find(n)!=d_rep_ids[tn].end()) { + Trace(tr) << d_rep_ids[tn][n]; + }else{ + Trace(tr) << n; + } + }else{ + Trace(tr) << n; + } + } +} + + +bool FullModelChecker::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ) { + Trace("fmc") << "Full model check " << f << ", effort = " << effort << "..." << std::endl; + if( optUseModel() ){ + FirstOrderModelFmc * fmfmc = fm->asFirstOrderModelFmc(); + if (effort==0) { + //register the quantifier + if (d_quant_cond.find(f)==d_quant_cond.end()) { + std::vector< TypeNode > types; + for(unsigned i=0; i<f[0].getNumChildren(); i++){ + types.push_back(f[0][i].getType()); + d_quant_var_id[f][f[0][i]] = i; + } + TypeNode typ = NodeManager::currentNM()->mkFunctionType( types, NodeManager::currentNM()->booleanType() ); + Node op = NodeManager::currentNM()->mkSkolem( "fmc_$$", typ, "op created for full-model checking" ); + d_quant_cond[f] = op; + } + //make sure all types are set + for( unsigned i=0; i<f[0].getNumChildren(); i++ ){ + initializeType( fmfmc, f[0][i].getType() ); + } + + //model check the quantifier + doCheck(fmfmc, f, d_quant_models[f], f[1]); + Trace("fmc") << "Definition for quantifier " << f << " is : " << std::endl; + d_quant_models[f].debugPrint("fmc", Node::null(), this); + Trace("fmc") << std::endl; + + //consider all entries going to non-true + for (unsigned i=0; i<d_quant_models[f].d_cond.size(); i++) { + if( d_quant_models[f].d_value[i]!=d_true) { + Trace("fmc-inst") << "Instantiate based on " << d_quant_models[f].d_cond[i] << "..." << std::endl; + bool hasStar = false; + std::vector< Node > inst; + for (unsigned j=0; j<d_quant_models[f].d_cond[i].getNumChildren(); j++) { + if (fmfmc->isStar(d_quant_models[f].d_cond[i][j])) { + hasStar = true; + inst.push_back(fmfmc->getModelBasisTerm(d_quant_models[f].d_cond[i][j].getType())); + }else if( fmfmc->isInterval(d_quant_models[f].d_cond[i][j])){ + hasStar = true; + //if interval, find a sample point + if( fmfmc->isStar(d_quant_models[f].d_cond[i][j][0]) ){ + if( fmfmc->isStar(d_quant_models[f].d_cond[i][j][1]) ){ + inst.push_back(fmfmc->getModelBasisTerm(d_quant_models[f].d_cond[i][j][1].getType())); + }else{ + Node nn = NodeManager::currentNM()->mkNode( MINUS, d_quant_models[f].d_cond[i][j][1], + NodeManager::currentNM()->mkConst( Rational(1) ) ); + nn = Rewriter::rewrite( nn ); + inst.push_back( nn ); + } + }else{ + inst.push_back(d_quant_models[f].d_cond[i][j][0]); + } + }else{ + inst.push_back(d_quant_models[f].d_cond[i][j]); + } + } + bool addInst = true; + if( hasStar ){ + //try obvious (specified by inst) + Node ev = d_quant_models[f].evaluate(fmfmc, inst); + if (ev==d_true) { + addInst = false; + } + }else{ + //for debugging + if (Trace.isOn("fmc-test-inst")) { + Node ev = d_quant_models[f].evaluate(fmfmc, inst); + if( ev==d_true ){ + std::cout << "WARNING: instantiation was true! " << f << " " << d_quant_models[f].d_cond[i] << std::endl; + exit(0); + }else{ + Trace("fmc-test-inst") << "...instantiation evaluated to false." << std::endl; + } + } + } + if( addInst ){ + InstMatch m; + for( unsigned j=0; j<inst.size(); j++) { + m.set( d_qe, f, j, inst[j] ); + } + d_triedLemmas++; + if( d_qe->addInstantiation( f, m ) ){ + Trace("fmc-debug-inst") << "** Added instantiation." << std::endl; + d_addedLemmas++; + }else{ + Trace("fmc-debug-inst") << "** Instantiation was duplicate." << std::endl; + //this can happen if evaluation is unknown + //might try it next effort level + d_star_insts[f].push_back(i); + } + }else{ + Trace("fmc-debug-inst") << "** Instantiation was already true." << std::endl; + //might try it next effort level + d_star_insts[f].push_back(i); + } + } + } + }else{ + if (!d_star_insts[f].empty()) { + Trace("fmc-exh") << "Exhaustive instantiate " << f << std::endl; + Trace("fmc-exh") << "Definition was : " << std::endl; + d_quant_models[f].debugPrint("fmc-exh", Node::null(), this); + Trace("fmc-exh") << std::endl; + Def temp; + //simplify the exceptions? + for( int i=(d_star_insts[f].size()-1); i>=0; i--) { + //get witness for d_star_insts[f][i] + int j = d_star_insts[f][i]; + if( temp.addEntry(fmfmc, d_quant_models[f].d_cond[j], d_quant_models[f].d_value[j] ) ){ + if( !exhaustiveInstantiate(fmfmc, f, d_quant_models[f].d_cond[j], j ) ){ + //something went wrong, resort to exhaustive instantiation + return false; + } + } + } + } + } + return true; + }else{ + return false; + } +} + +bool FullModelChecker::exhaustiveInstantiate(FirstOrderModelFmc * fm, Node f, Node c, int c_index) { + RepSetIterator riter( d_qe, &(fm->d_rep_set) ); + Trace("fmc-exh") << "Exhaustive instantiate based on index " << c_index << " : " << c << " "; + debugPrintCond("fmc-exh", c, true); + Trace("fmc-exh")<< std::endl; + Trace("fmc-exh-debug") << "Set interval domains..." << std::endl; + //set the bounds on the iterator based on intervals + for( unsigned i=0; i<c.getNumChildren(); i++ ){ + if( c[i].getType().isInteger() ){ + if( fm->isInterval(c[i]) ){ + for( unsigned b=0; b<2; b++ ){ + if( !fm->isStar(c[i][b]) ){ + riter.d_bounds[b][i] = c[i][b]; + } + } + }else if( !fm->isStar(c[i]) ){ + riter.d_bounds[0][i] = c[i]; + riter.d_bounds[1][i] = QuantArith::offset( c[i], 1 ); + } + } + } + Trace("fmc-exh-debug") << "Set quantifier..." << std::endl; + //initialize + if( riter.setQuantifier( f ) ){ + Trace("fmc-exh-debug") << "Set element domains..." << std::endl; + //set the domains based on the entry + for (unsigned i=0; i<c.getNumChildren(); i++) { + if (riter.d_enum_type[i]==RepSetIterator::ENUM_DOMAIN_ELEMENTS) { + TypeNode tn = c[i].getType(); + if( d_rep_ids.find(tn)!=d_rep_ids.end() ){ + if( fm->isInterval(c[i]) || fm->isStar(c[i]) ){ + //add the full range + }else{ + if (d_rep_ids[tn].find(c[i])!=d_rep_ids[tn].end()) { + riter.d_domain[i].clear(); + riter.d_domain[i].push_back(d_rep_ids[tn][c[i]]); + }else{ + return false; + } + } + }else{ + return false; + } + } + } + int addedLemmas = 0; + //now do full iteration + while( !riter.isFinished() ){ + d_triedLemmas++; + Trace("fmc-exh-debug") << "Inst : "; + std::vector< Node > inst; + for( int i=0; i<riter.getNumTerms(); i++ ){ + //m.set( d_quantEngine, f, riter.d_index_order[i], riter.getTerm( i ) ); + Node r = fm->getUsedRepresentative( riter.getTerm( i ) ); + debugPrint("fmc-exh-debug", r); + Trace("fmc-exh-debug") << " "; + inst.push_back(r); + } + int ev_index = d_quant_models[f].getGeneralizationIndex(fm, inst); + Trace("fmc-exh-debug") << ", index = " << ev_index << " / " << d_quant_models[f].d_value.size(); + Node ev = ev_index==-1 ? Node::null() : d_quant_models[f].d_value[ev_index]; + if (ev!=d_true) { + InstMatch m; + for( int i=0; i<riter.getNumTerms(); i++ ){ + m.set( d_qe, f, i, riter.getTerm( i ) ); + } + Trace("fmc-exh-debug") << ", add!"; + //add as instantiation + if( d_qe->addInstantiation( f, m ) ){ + Trace("fmc-exh-debug") << " ...success."; + addedLemmas++; + } + }else{ + Trace("fmc-exh-debug") << ", already true"; + } + Trace("fmc-exh-debug") << std::endl; + int index = riter.increment(); + Trace("fmc-exh-debug") << "Incremented index " << index << std::endl; + if (index>=0 && riter.d_index[index]>0 && addedLemmas>0 && riter.d_enum_type[index]==RepSetIterator::ENUM_RANGE) { + Trace("fmc-exh-debug") << "Since this is a range enumeration, skip to the next..." << std::endl; + riter.increment2( index-1 ); + } + } + d_addedLemmas += addedLemmas; + return true; + }else{ + return false; + } +} + +void FullModelChecker::doCheck(FirstOrderModelFmc * fm, Node f, Def & d, Node n ) { + Trace("fmc-debug") << "Check " << n << " " << n.getKind() << std::endl; + //first check if it is a bounding literal + if( n.hasAttribute(BoundIntLitAttribute()) ){ + Trace("fmc-debug") << "It is a bounding literal, polarity = " << n.getAttribute(BoundIntLitAttribute()) << std::endl; + d.addEntry(fm, mkCondDefault(fm, f), n.getAttribute(BoundIntLitAttribute())==1 ? d_false : d_true ); + }else if( n.getKind() == kind::BOUND_VARIABLE ){ + Trace("fmc-debug") << "Add default entry..." << std::endl; + d.addEntry(fm, mkCondDefault(fm, f), n); + } + else if( n.getKind() == kind::NOT ){ + //just do directly + doCheck( fm, f, d, n[0] ); + doNegate( d ); + } + else if( n.getKind() == kind::FORALL ){ + d.addEntry(fm, mkCondDefault(fm, f), Node::null()); + } + else if( n.getType().isArray() ){ + //make the definition + Node r = fm->getRepresentative(n); + Trace("fmc-debug") << "Representative for array is " << r << std::endl; + while( r.getKind() == kind::STORE ){ + Node i = fm->getUsedRepresentative( r[1] ); + Node e = fm->getUsedRepresentative( r[2] ); + d.addEntry(fm, mkArrayCond(i), e ); + r = fm->getRepresentative( r[0] ); + } + Node defC = mkArrayCond(fm->getStar(n.getType().getArrayIndexType())); + bool success = false; + Node odefaultValue; + if( r.getKind() == kind::STORE_ALL ){ + ArrayStoreAll storeAll = r.getConst<ArrayStoreAll>(); + odefaultValue = Node::fromExpr(storeAll.getExpr()); + Node defaultValue = fm->getUsedRepresentative( odefaultValue, true ); + if( !defaultValue.isNull() ){ + d.addEntry(fm, defC, defaultValue); + success = true; + } + } + if( !success ){ + Trace("fmc-warn") << "WARNING : ARRAYS : Can't process base array " << r << std::endl; + Trace("fmc-warn") << " Default value was : " << odefaultValue << std::endl; + Trace("fmc-debug") << "Can't process base array " << r << std::endl; + //can't process this array + d.reset(); + d.addEntry(fm, defC, Node::null()); + } + } + else if( n.getNumChildren()==0 ){ + Node r = n; + if( !n.isConst() ){ + if( !fm->hasTerm(n) ){ + r = getSomeDomainElement(fm, n.getType() ); + } + r = fm->getUsedRepresentative( r ); + } + Trace("fmc-debug") << "Add constant entry..." << std::endl; + d.addEntry(fm, mkCondDefault(fm, f), r); + } + else{ + std::vector< int > var_ch; + std::vector< Def > children; + for( int i=0; i<(int)n.getNumChildren(); i++) { + Def dc; + doCheck(fm, f, dc, n[i]); + children.push_back(dc); + if( n[i].getKind() == kind::BOUND_VARIABLE ){ + var_ch.push_back(i); + } + } + + if( n.getKind()==APPLY_UF ){ + Trace("fmc-debug") << "Do uninterpreted compose " << n << std::endl; + //uninterpreted compose + doUninterpretedCompose( fm, f, d, n.getOperator(), children ); + } else if( n.getKind()==SELECT ){ + Trace("fmc-debug") << "Do select compose " << n << std::endl; + std::vector< Def > children2; + children2.push_back( children[1] ); + std::vector< Node > cond; + mkCondDefaultVec(fm, f, cond); + std::vector< Node > val; + doUninterpretedCompose(fm, f, d, children[0], children2, 0, cond, val ); + } else { + if( !var_ch.empty() ){ + if( n.getKind()==EQUAL ){ + if( var_ch.size()==2 ){ + Trace("fmc-debug") << "Do variable equality " << n << std::endl; + doVariableEquality( fm, f, d, n ); + }else{ + Trace("fmc-debug") << "Do variable relation " << n << std::endl; + doVariableRelation( fm, f, d, var_ch[0]==0 ? children[1] : children[0], var_ch[0]==0 ? n[0] : n[1] ); + } + }else{ + Trace("fmc-warn") << "Don't know how to check " << n << std::endl; + d.addEntry(fm, mkCondDefault(fm, f), Node::null()); + } + }else{ + Trace("fmc-debug") << "Do interpreted compose " << n << std::endl; + std::vector< Node > cond; + mkCondDefaultVec(fm, f, cond); + std::vector< Node > val; + //interpreted compose + doInterpretedCompose( fm, f, d, n, children, 0, cond, val ); + } + } + Trace("fmc-debug") << "Simplify the definition..." << std::endl; + d.debugPrint("fmc-debug", Node::null(), this); + d.simplify(this, fm); + Trace("fmc-debug") << "Done simplifying" << std::endl; + } + Trace("fmc-debug") << "Definition for " << n << " is : " << std::endl; + d.debugPrint("fmc-debug", Node::null(), this); + Trace("fmc-debug") << std::endl; +} + +void FullModelChecker::doNegate( Def & dc ) { + for (unsigned i=0; i<dc.d_cond.size(); i++) { + if (!dc.d_value[i].isNull()) { + dc.d_value[i] = dc.d_value[i]==d_true ? d_false : d_true; + } + } +} + +void FullModelChecker::doVariableEquality( FirstOrderModelFmc * fm, Node f, Def & d, Node eq ) { + std::vector<Node> cond; + mkCondDefaultVec(fm, f, cond); + if (eq[0]==eq[1]){ + d.addEntry(fm, mkCond(cond), d_true); + }else{ + TypeNode tn = eq[0].getType(); + if( tn.isSort() ){ + int j = getVariableId(f, eq[0]); + int k = getVariableId(f, eq[1]); + if( !fm->d_rep_set.hasType( tn ) ){ + getSomeDomainElement( fm, tn ); //to verify the type is initialized + } + for (unsigned i=0; i<fm->d_rep_set.d_type_reps[tn].size(); i++) { + Node r = fm->getUsedRepresentative( fm->d_rep_set.d_type_reps[tn][i] ); + cond[j+1] = r; + cond[k+1] = r; + d.addEntry( fm, mkCond(cond), d_true); + } + d.addEntry( fm, mkCondDefault(fm, f), d_false); + }else{ + d.addEntry( fm, mkCondDefault(fm, f), Node::null()); + } + } +} + +void FullModelChecker::doVariableRelation( FirstOrderModelFmc * fm, Node f, Def & d, Def & dc, Node v) { + int j = getVariableId(f, v); + for (unsigned i=0; i<dc.d_cond.size(); i++) { + Node val = dc.d_value[i]; + if( val.isNull() ){ + d.addEntry( fm, dc.d_cond[i], val); + }else{ + if( dc.d_cond[i][j]!=val ){ + if (fm->isStar(dc.d_cond[i][j])) { + std::vector<Node> cond; + mkCondVec(dc.d_cond[i],cond); + cond[j+1] = val; + d.addEntry(fm, mkCond(cond), d_true); + cond[j+1] = fm->getStar(val.getType()); + d.addEntry(fm, mkCond(cond), d_false); + }else{ + d.addEntry( fm, dc.d_cond[i], d_false); + } + }else{ + d.addEntry( fm, dc.d_cond[i], d_true); + } + } + } +} + +void FullModelChecker::doUninterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, Node op, std::vector< Def > & dc ) { + Trace("fmc-uf-debug") << "Definition : " << std::endl; + fm->d_models[op]->debugPrint("fmc-uf-debug", op, this); + Trace("fmc-uf-debug") << std::endl; + + std::vector< Node > cond; + mkCondDefaultVec(fm, f, cond); + std::vector< Node > val; + doUninterpretedCompose( fm, f, d, *fm->d_models[op], dc, 0, cond, val); +} + +void FullModelChecker::doUninterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, + Def & df, std::vector< Def > & dc, int index, + std::vector< Node > & cond, std::vector<Node> & val ) { + Trace("fmc-uf-process") << "process at " << index << std::endl; + for( unsigned i=1; i<cond.size(); i++) { + debugPrint("fmc-uf-process", cond[i], true); + Trace("fmc-uf-process") << " "; + } + Trace("fmc-uf-process") << std::endl; + if (index==(int)dc.size()) { + //we have an entry, now do actual compose + std::map< int, Node > entries; + doUninterpretedCompose2( fm, f, entries, 0, cond, val, df.d_et); + if( entries.empty() ){ + d.addEntry(fm, mkCond(cond), Node::null()); + }else{ + //add them to the definition + for( unsigned e=0; e<df.d_cond.size(); e++ ){ + if ( entries.find(e)!=entries.end() ){ + Trace("fmf-uf-process-debug") << "Add entry..." << std::endl; + d.addEntry(fm, entries[e], df.d_value[e] ); + Trace("fmf-uf-process-debug") << "Done add entry." << std::endl; + } + } + } + }else{ + for (unsigned i=0; i<dc[index].d_cond.size(); i++) { + if (isCompat(fm, cond, dc[index].d_cond[i])!=0) { + std::vector< Node > new_cond; + new_cond.insert(new_cond.end(), cond.begin(), cond.end()); + if( doMeet(fm, new_cond, dc[index].d_cond[i]) ){ + Trace("fmc-uf-process") << "index " << i << " succeeded meet." << std::endl; + val.push_back(dc[index].d_value[i]); + doUninterpretedCompose(fm, f, d, df, dc, index+1, new_cond, val); + val.pop_back(); + }else{ + Trace("fmc-uf-process") << "index " << i << " failed meet." << std::endl; + } + } + } + } +} + +void FullModelChecker::doUninterpretedCompose2( FirstOrderModelFmc * fm, Node f, + std::map< int, Node > & entries, int index, + std::vector< Node > & cond, std::vector< Node > & val, + EntryTrie & curr ) { + Trace("fmc-uf-process") << "compose " << index << std::endl; + for( unsigned i=1; i<cond.size(); i++) { + debugPrint("fmc-uf-process", cond[i], true); + Trace("fmc-uf-process") << " "; + } + Trace("fmc-uf-process") << std::endl; + if (index==(int)val.size()) { + Node c = mkCond(cond); + Trace("fmc-uf-entry") << "Entry : " << c << " -> index[" << curr.d_data << "]" << std::endl; + entries[curr.d_data] = c; + }else{ + Node v = val[index]; + bool bind_var = false; + if( !v.isNull() && v.getKind()==kind::BOUND_VARIABLE ){ + int j = getVariableId(f, v); + Trace("fmc-uf-process") << v << " is variable #" << j << std::endl; + if (!fm->isStar(cond[j+1]) && !fm->isInterval(cond[j+1])) { + v = cond[j+1]; + }else{ + bind_var = true; + } + } + if (bind_var) { + Trace("fmc-uf-process") << "bind variable..." << std::endl; + int j = getVariableId(f, v); + if( fm->isStar(cond[j+1]) ){ + for (std::map<Node, EntryTrie>::iterator it = curr.d_child.begin(); it != curr.d_child.end(); ++it) { + cond[j+1] = it->first; + doUninterpretedCompose2(fm, f, entries, index+1, cond, val, it->second); + } + cond[j+1] = fm->getStar(v.getType()); + }else{ + Node orig = cond[j+1]; + for (std::map<Node, EntryTrie>::iterator it = curr.d_child.begin(); it != curr.d_child.end(); ++it) { + Node nw = doIntervalMeet( fm, it->first, orig ); + if( !nw.isNull() ){ + cond[j+1] = nw; + doUninterpretedCompose2(fm, f, entries, index+1, cond, val, it->second); + } + } + cond[j+1] = orig; + } + }else{ + if( !v.isNull() ){ + if( options::fmfFmcInterval() && v.getType().isInteger() ){ + for (std::map<Node, EntryTrie>::iterator it = curr.d_child.begin(); it != curr.d_child.end(); ++it) { + if( fm->isInRange( v, it->first ) ){ + doUninterpretedCompose2(fm, f, entries, index+1, cond, val, it->second); + } + } + }else{ + if (curr.d_child.find(v)!=curr.d_child.end()) { + Trace("fmc-uf-process") << "follow value..." << std::endl; + doUninterpretedCompose2(fm, f, entries, index+1, cond, val, curr.d_child[v]); + } + Node st = fm->getStarElement(v.getType()); + if (curr.d_child.find(st)!=curr.d_child.end()) { + Trace("fmc-uf-process") << "follow star..." << std::endl; + doUninterpretedCompose2(fm, f, entries, index+1, cond, val, curr.d_child[st]); + } + } + } + } + } +} + +void FullModelChecker::doInterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, Node n, + std::vector< Def > & dc, int index, + std::vector< Node > & cond, std::vector<Node> & val ) { + if ( index==(int)dc.size() ){ + Node c = mkCond(cond); + Node v = evaluateInterpreted(n, val); + d.addEntry(fm, c, v); + } + else { + TypeNode vtn = n.getType(); + for (unsigned i=0; i<dc[index].d_cond.size(); i++) { + if (isCompat(fm, cond, dc[index].d_cond[i])!=0) { + std::vector< Node > new_cond; + new_cond.insert(new_cond.end(), cond.begin(), cond.end()); + if( doMeet(fm, new_cond, dc[index].d_cond[i]) ){ + bool process = true; + if (vtn.isBoolean()) { + //short circuit + if( (n.getKind()==OR && dc[index].d_value[i]==d_true) || + (n.getKind()==AND && dc[index].d_value[i]==d_false) ){ + Node c = mkCond(new_cond); + d.addEntry(fm, c, dc[index].d_value[i]); + process = false; + } + } + if (process) { + val.push_back(dc[index].d_value[i]); + doInterpretedCompose(fm, f, d, n, dc, index+1, new_cond, val); + val.pop_back(); + } + } + } + } + } +} + +int FullModelChecker::isCompat( FirstOrderModelFmc * fm, std::vector< Node > & cond, Node c ) { + Trace("fmc-debug3") << "isCompat " << c << std::endl; + Assert(cond.size()==c.getNumChildren()+1); + for (unsigned i=1; i<cond.size(); i++) { + if( options::fmfFmcInterval() && cond[i].getType().isInteger() ){ + Node iv = doIntervalMeet( fm, cond[i], c[i-1], false ); + if( iv.isNull() ){ + return 0; + } + }else{ + if( cond[i]!=c[i-1] && !fm->isStar(cond[i]) && !fm->isStar(c[i-1]) ) { + return 0; + } + } + } + return 1; +} + +bool FullModelChecker::doMeet( FirstOrderModelFmc * fm, std::vector< Node > & cond, Node c ) { + Trace("fmc-debug3") << "doMeet " << c << std::endl; + Assert(cond.size()==c.getNumChildren()+1); + for (unsigned i=1; i<cond.size(); i++) { + if( cond[i]!=c[i-1] ) { + if( options::fmfFmcInterval() && cond[i].getType().isInteger() ){ + Node iv = doIntervalMeet( fm, cond[i], c[i-1] ); + if( !iv.isNull() ){ + cond[i] = iv; + }else{ + return false; + } + }else{ + if( fm->isStar(cond[i]) ){ + cond[i] = c[i-1]; + }else if( !fm->isStar(c[i-1]) ){ + return false; + } + } + } + } + return true; +} + +Node FullModelChecker::doIntervalMeet( FirstOrderModelFmc * fm, Node i1, Node i2, bool mk ) { + if( fm->isStar( i1 ) ){ + return i2; + }else if( fm->isStar( i2 ) ){ + return i1; + }else{ + if( !fm->isInterval( i1 ) || !fm->isInterval( i2 ) ){ + std::cout << "Not interval during meet! " << i1 << " " << i2 << std::endl; + exit( 0 ); + } + Node b[2]; + for( unsigned j=0; j<2; j++ ){ + Node b1 = i1[j]; + Node b2 = i2[j]; + if( fm->isStar( b1 ) ){ + b[j] = b2; + }else if( fm->isStar( b2 ) ){ + b[j] = b1; + }else if( b1.getConst<Rational>() < b2.getConst<Rational>() ){ + b[j] = j==0 ? b2 : b1; + }else{ + b[j] = j==0 ? b1 : b2; + } + } + if( fm->isStar( b[0] ) || fm->isStar( b[1] ) || b[0].getConst<Rational>() < b[1].getConst<Rational>() ){ + return mk ? fm->getInterval( b[0], b[1] ) : i1; + }else{ + return Node::null(); + } + } +} + +Node FullModelChecker::mkCond( std::vector< Node > & cond ) { + return NodeManager::currentNM()->mkNode(APPLY_UF, cond); +} + +Node FullModelChecker::mkCondDefault( FirstOrderModelFmc * fm, Node f) { + std::vector< Node > cond; + mkCondDefaultVec(fm, f, cond); + return mkCond(cond); +} + +void FullModelChecker::mkCondDefaultVec( FirstOrderModelFmc * fm, Node f, std::vector< Node > & cond ) { + Trace("fmc-debug") << "Make default vec, intervals = " << options::fmfFmcInterval() << std::endl; + //get function symbol for f + cond.push_back(d_quant_cond[f]); + for (unsigned i=0; i<f[0].getNumChildren(); i++) { + Node ts = fm->getStarElement( f[0][i].getType() ); + cond.push_back(ts); + } +} + +void FullModelChecker::mkCondVec( Node n, std::vector< Node > & cond ) { + cond.push_back(n.getOperator()); + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + cond.push_back( n[i] ); + } +} + +Node FullModelChecker::mkArrayCond( Node a ) { + if( d_array_term_cond.find(a)==d_array_term_cond.end() ){ + if( d_array_cond.find(a.getType())==d_array_cond.end() ){ + TypeNode typ = NodeManager::currentNM()->mkFunctionType( a.getType(), NodeManager::currentNM()->booleanType() ); + Node op = NodeManager::currentNM()->mkSkolem( "fmc_$$", typ, "op created for full-model checking" ); + d_array_cond[a.getType()] = op; + } + std::vector< Node > cond; + cond.push_back(d_array_cond[a.getType()]); + cond.push_back(a); + d_array_term_cond[a] = NodeManager::currentNM()->mkNode(APPLY_UF, cond ); + } + return d_array_term_cond[a]; +} + +Node FullModelChecker::evaluateInterpreted( Node n, std::vector< Node > & vals ) { + if( n.getKind()==EQUAL ){ + if (!vals[0].isNull() && !vals[1].isNull()) { + return vals[0]==vals[1] ? d_true : d_false; + }else{ + return Node::null(); + } + }else if( n.getKind()==ITE ){ + if( vals[0]==d_true ){ + return vals[1]; + }else if( vals[0]==d_false ){ + return vals[2]; + }else{ + return vals[1]==vals[2] ? vals[1] : Node::null(); + } + }else if( n.getKind()==AND || n.getKind()==OR ){ + bool isNull = false; + for (unsigned i=0; i<vals.size(); i++) { + if((vals[i]==d_true && n.getKind()==OR) || (vals[i]==d_false && n.getKind()==AND)) { + return vals[i]; + }else if( vals[i].isNull() ){ + isNull = true; + } + } + return isNull ? Node::null() : vals[0]; + }else{ + std::vector<Node> children; + if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ + children.push_back( n.getOperator() ); + } + for (unsigned i=0; i<vals.size(); i++) { + if( vals[i].isNull() ){ + return Node::null(); + }else{ + children.push_back( vals[i] ); + } + } + Node nc = NodeManager::currentNM()->mkNode(n.getKind(), children); + Trace("fmc-eval") << "Evaluate " << nc << " to "; + nc = Rewriter::rewrite(nc); + Trace("fmc-eval") << nc << std::endl; + return nc; + } +} + +Node FullModelChecker::getSomeDomainElement( FirstOrderModelFmc * fm, TypeNode tn ) { + bool addRepId = !fm->d_rep_set.hasType( tn ); + Node de = fm->getSomeDomainElement(tn); + if( addRepId ){ + d_rep_ids[tn][de] = 0; + } + return de; +} + +Node FullModelChecker::getFunctionValue(FirstOrderModelFmc * fm, Node op, const char* argPrefix ) { + return fm->getFunctionValue(op, argPrefix); +} + + +bool FullModelChecker::useSimpleModels() { + return options::fmfFmcSimple(); +} + +void FullModelChecker::makeIntervalModel( FirstOrderModelFmc * fm, Node op, std::vector< int > & indices, int index, + std::map< int, std::map< int, Node > >& changed_vals ) { + if( index==(int)fm->d_models[op]->d_cond[0].getNumChildren() ){ + makeIntervalModel2( fm, op, indices, 0, changed_vals ); + }else{ + TypeNode tn = fm->d_models[op]->d_cond[0][index].getType(); + if( tn.isInteger() ){ + makeIntervalModel(fm,op,indices,index+1, changed_vals ); + }else{ + std::map< Node, std::vector< int > > new_indices; + for( unsigned i=0; i<indices.size(); i++ ){ + Node v = fm->d_models[op]->d_cond[indices[i]][index]; + new_indices[v].push_back( indices[i] ); + } + + for( std::map< Node, std::vector< int > >::iterator it = new_indices.begin(); it != new_indices.end(); ++it ){ + makeIntervalModel( fm, op, it->second, index+1, changed_vals ); + } + } + } +} + +void FullModelChecker::makeIntervalModel2( FirstOrderModelFmc * fm, Node op, std::vector< int > & indices, int index, + std::map< int, std::map< int, Node > >& changed_vals ) { + Debug("fmc-interval-model-debug") << "Process " << index << " with indicies : "; + for( unsigned i=0; i<indices.size(); i++ ){ + Debug("fmc-interval-model-debug") << indices[i] << " "; + } + Debug("fmc-interval-model-debug") << std::endl; + + if( index<(int)fm->d_models[op]->d_cond[0].getNumChildren() ){ + TypeNode tn = fm->d_models[op]->d_cond[0][index].getType(); + if( tn.isInteger() ){ + std::map< Node, std::vector< int > > new_indices; + for( unsigned i=0; i<indices.size(); i++ ){ + Node v = fm->d_models[op]->d_cond[indices[i]][index]; + new_indices[v].push_back( indices[i] ); + if( !v.isConst() ){ + Trace("fmc-warn") << "WARNING: for interval, model has non-constant : " << v << std::endl; + Trace("fmc-warn") << "From condition : " << fm->d_models[op]->d_cond[indices[i]] << std::endl; + } + } + + std::vector< Node > values; + for( std::map< Node, std::vector< int > >::iterator it = new_indices.begin(); it != new_indices.end(); ++it ){ + makeIntervalModel2( fm, op, it->second, index+1, changed_vals ); + values.push_back( it->first ); + } + + if( tn.isInteger() ){ + //sort values by size + ConstRationalSort crs; + std::vector< int > sindices; + for( unsigned i=0; i<values.size(); i++ ){ + sindices.push_back( (int)i ); + crs.d_terms.push_back( values[i] ); + } + std::sort( sindices.begin(), sindices.end(), crs ); + + Node ub = fm->getStar( tn ); + for( int i=(int)(sindices.size()-1); i>=0; i-- ){ + Node lb = fm->getStar( tn ); + if( i>0 ){ + lb = values[sindices[i]]; + } + Node interval = fm->getInterval( lb, ub ); + for( unsigned j=0; j<new_indices[values[sindices[i]]].size(); j++ ){ + Debug("fmc-interval-model-debug") << "Change " << new_indices[values[sindices[i]]][j] << ", " << index << " to " << interval << std::endl; + changed_vals[new_indices[values[sindices[i]]][j]][index] = interval; + } + ub = lb; + } + } + }else{ + makeIntervalModel2( fm, op, indices, index+1, changed_vals ); + } + } +} diff --git a/src/theory/quantifiers/full_model_check.h b/src/theory/quantifiers/full_model_check.h new file mode 100644 index 000000000..6bb375c34 --- /dev/null +++ b/src/theory/quantifiers/full_model_check.h @@ -0,0 +1,160 @@ +/********************* */ +/*! \file full_model_check.h + ** \verbatim + ** Original author: 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 Full model check class + **/ + +#include "cvc4_private.h" + +#ifndef FULL_MODEL_CHECK +#define FULL_MODEL_CHECK + +#include "theory/quantifiers/model_builder.h" +#include "theory/quantifiers/first_order_model.h" + +namespace CVC4 { +namespace theory { +namespace quantifiers { +namespace fmcheck { + + +class FirstOrderModelFmc; +class FullModelChecker; + +class EntryTrie +{ +private: + int d_complete; +public: + EntryTrie() : d_complete(-1), d_data(-1){} + std::map<Node,EntryTrie> d_child; + int d_data; + void reset() { d_data = -1; d_child.clear(); d_complete = -1; } + void addEntry( FirstOrderModelFmc * m, Node c, Node v, int data, int index = 0 ); + bool hasGeneralization( FirstOrderModelFmc * m, Node c, int index = 0 ); + int getGeneralizationIndex( FirstOrderModelFmc * m, std::vector<Node> & inst, int index = 0 ); + void getEntries( FirstOrderModelFmc * m, Node c, std::vector<int> & compat, std::vector<int> & gen, int index = 0, bool is_gen = true ); + + void collectIndices(Node c, int index, std::vector< int >& indices ); + bool isComplete(FirstOrderModelFmc * m, Node c, int index); +}; + + +class Def +{ +public: + EntryTrie d_et; + //cond is APPLY_UF whose arguments are returned by FullModelChecker::getRepresentative + std::vector< Node > d_cond; + //value is returned by FullModelChecker::getRepresentative + std::vector< Node > d_value; + void basic_simplify( FirstOrderModelFmc * m ); +private: + enum { + status_unk, + status_redundant, + status_non_redundant + }; + std::vector< int > d_status; + bool d_has_simplified; +public: + Def() : d_has_simplified(false){} + void reset() { + d_et.reset(); + d_cond.clear(); + d_value.clear(); + d_status.clear(); + d_has_simplified = false; + } + bool addEntry( FirstOrderModelFmc * m, Node c, Node v); + Node evaluate( FirstOrderModelFmc * m, std::vector<Node>& inst ); + int getGeneralizationIndex( FirstOrderModelFmc * m, std::vector<Node>& inst ); + void simplify( FullModelChecker * mc, FirstOrderModelFmc * m ); + void debugPrint(const char * tr, Node op, FullModelChecker * m); +}; + + +class FullModelChecker : public QModelBuilder +{ +protected: + Node d_true; + Node d_false; + std::map<TypeNode, std::map< Node, int > > d_rep_ids; + std::map<Node, Def > d_quant_models; + std::map<Node, Node > d_quant_cond; + std::map< TypeNode, Node > d_array_cond; + std::map< Node, Node > d_array_term_cond; + std::map<Node, std::map< Node, int > > d_quant_var_id; + std::map<Node, std::vector< int > > d_star_insts; + void initializeType( FirstOrderModelFmc * fm, TypeNode tn ); + Node normalizeArgReps(FirstOrderModelFmc * fm, Node op, Node n); + bool exhaustiveInstantiate(FirstOrderModelFmc * fm, Node f, Node c, int c_index); +protected: + void makeIntervalModel2( FirstOrderModelFmc * fm, Node op, std::vector< int > & indices, int index, + std::map< int, std::map< int, Node > >& changed_vals ); + void makeIntervalModel( FirstOrderModelFmc * fm, Node op, std::vector< int > & indices, int index, + std::map< int, std::map< int, Node > >& changed_vals ); +private: + void doCheck(FirstOrderModelFmc * fm, Node f, Def & d, Node n ); + + void doNegate( Def & dc ); + void doVariableEquality( FirstOrderModelFmc * fm, Node f, Def & d, Node eq ); + void doVariableRelation( FirstOrderModelFmc * fm, Node f, Def & d, Def & dc, Node v); + void doUninterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, Node n, std::vector< Def > & dc ); + + void doUninterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, + Def & df, std::vector< Def > & dc, int index, + std::vector< Node > & cond, std::vector<Node> & val ); + void doUninterpretedCompose2( FirstOrderModelFmc * fm, Node f, + std::map< int, Node > & entries, int index, + std::vector< Node > & cond, std::vector< Node > & val, + EntryTrie & curr); + + void doInterpretedCompose( FirstOrderModelFmc * fm, Node f, Def & d, Node n, + std::vector< Def > & dc, int index, + std::vector< Node > & cond, std::vector<Node> & val ); + int isCompat( FirstOrderModelFmc * fm, std::vector< Node > & cond, Node c ); + Node doIntervalMeet( FirstOrderModelFmc * fm, Node i1, Node i2, bool mk = true ); + bool doMeet( FirstOrderModelFmc * fm, std::vector< Node > & cond, Node c ); + Node mkCond( std::vector< Node > & cond ); + Node mkCondDefault( FirstOrderModelFmc * fm, Node f ); + void mkCondDefaultVec( FirstOrderModelFmc * fm, Node f, std::vector< Node > & cond ); + void mkCondVec( Node n, std::vector< Node > & cond ); + Node mkArrayCond( Node a ); + Node evaluateInterpreted( Node n, std::vector< Node > & vals ); + Node getSomeDomainElement( FirstOrderModelFmc * fm, TypeNode tn ); +public: + FullModelChecker( context::Context* c, QuantifiersEngine* qe ); + ~FullModelChecker(){} + + bool optBuildAtFullModel(); + + int getVariableId(Node f, Node n) { return d_quant_var_id[f][n]; } + + void debugPrintCond(const char * tr, Node n, bool dispStar = false); + void debugPrint(const char * tr, Node n, bool dispStar = false); + + bool doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ); + + Node getFunctionValue(FirstOrderModelFmc * fm, Node op, const char* argPrefix ); + + /** process build model */ + void processBuildModel(TheoryModel* m, bool fullModel); + /** get current model value */ + Node getCurrentUfModelValue( FirstOrderModelFmc* fm, Node n, std::vector< Node > & args, bool partial ); + + bool useSimpleModels(); +}; + +} +} +} +} + +#endif diff --git a/src/theory/quantifiers/inst_gen.cpp b/src/theory/quantifiers/inst_gen.cpp index e495b39c0..157861670 100644 --- a/src/theory/quantifiers/inst_gen.cpp +++ b/src/theory/quantifiers/inst_gen.cpp @@ -29,10 +29,10 @@ using namespace CVC4::theory::quantifiers; InstGenProcess::InstGenProcess( Node n ) : d_node( n ){ - Assert( n.hasAttribute(InstConstantAttribute()) ); + Assert( TermDb::hasInstConstAttr(n) ); int count = 0; for( size_t i=0; i<n.getNumChildren(); i++ ){ - if( n[i].getKind()!=INST_CONSTANT && n[i].hasAttribute(InstConstantAttribute()) ){ + if( n[i].getKind()!=INST_CONSTANT && TermDb::hasInstConstAttr(n[i]) ){ d_children.push_back( InstGenProcess( n[i] ) ); d_children_index.push_back( i ); d_children_map[ i ] = count; @@ -47,7 +47,7 @@ void InstGenProcess::addMatchValue( QuantifiersEngine* qe, Node f, Node val, Ins if( d_inst_trie[val].addInstMatch( qe, f, m, true ) ){ d_match_values.push_back( val ); d_matches.push_back( InstMatch( &m ) ); - qe->getModelEngine()->getModelBuilder()->d_instGenMatches++; + ((QModelBuilderIG*)qe->getModelEngine()->getModelBuilder())->d_instGenMatches++; } } } @@ -92,7 +92,7 @@ void InstGenProcess::calculateMatches( QuantifiersEngine* qe, Node f, std::vecto //for each term we consider, calculate a current match for( size_t i=0; i<considerTerms.size(); i++ ){ Node n = considerTerms[i]; - bool isSelected = qe->getModelEngine()->getModelBuilder()->isTermSelected( n ); + bool isSelected = ((QModelBuilderIG*)qe->getModelEngine()->getModelBuilder())->isTermSelected( n ); bool hadSuccess CVC4_UNUSED = false; for( int t=(isSelected ? 0 : 1); t<2; t++ ){ if( t==0 || !n.getAttribute(NoMatchAttribute()) ){ @@ -193,7 +193,7 @@ void InstGenProcess::calculateMatches( QuantifiersEngine* qe, Node f, std::vecto //process all values for( size_t i=0; i<considerTerms.size(); i++ ){ Node n = considerTerms[i]; - bool isSelected = qe->getModelEngine()->getModelBuilder()->isTermSelected( n ); + bool isSelected = ((QModelBuilderIG*)qe->getModelEngine()->getModelBuilder())->isTermSelected( n ); for( int t=(isSelected ? 0 : 1); t<2; t++ ){ //do not consider ground case if it is already congruent to another ground term if( t==0 || !n.getAttribute(NoMatchAttribute()) ){ diff --git a/src/theory/quantifiers/inst_match.cpp b/src/theory/quantifiers/inst_match.cpp index f6a0dad11..d55f72a88 100644 --- a/src/theory/quantifiers/inst_match.cpp +++ b/src/theory/quantifiers/inst_match.cpp @@ -134,18 +134,27 @@ Node InstMatch::getValue( Node var ) const{ } } +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( !n.isNull() &&// For a strange use in inst_match.cpp InstMatchGeneratorSimple::addInstantiations - //var.getType() == n.getType() - !n.getType().isSubtypeOf( var.getType() ) ){ - Trace("inst-match-warn") << var.getAttribute(InstConstantAttribute()) << std::endl; - Trace("inst-match-warn") << var << " " << var.getType() << " " << n << " " << n.getType() << std::endl ; - Assert(false); + 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 ); +} + /** add match m for quantifier f starting at index, take into account equalities q, return true if successful */ void InstMatchTrie::addInstMatch2( QuantifiersEngine* qe, Node f, InstMatch& m, int index, ImtIndexOrder* imtio ){ if( long(index)<long(f[0].getNumChildren()) && ( !imtio || long(index)<long(imtio->d_order.size()) ) ){ diff --git a/src/theory/quantifiers/inst_match.h b/src/theory/quantifiers/inst_match.h index 127f83c60..72447fd66 100644 --- a/src/theory/quantifiers/inst_match.h +++ b/src/theory/quantifiers/inst_match.h @@ -92,8 +92,11 @@ public: 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(); }; diff --git a/src/theory/quantifiers/inst_match_generator.cpp b/src/theory/quantifiers/inst_match_generator.cpp index de7f2f373..bf4bb15a6 100644 --- a/src/theory/quantifiers/inst_match_generator.cpp +++ b/src/theory/quantifiers/inst_match_generator.cpp @@ -32,16 +32,16 @@ namespace inst { InstMatchGenerator::InstMatchGenerator( Node pat, int matchPolicy ) : d_matchPolicy( matchPolicy ){ d_active_add = false; - Assert( pat.hasAttribute(InstConstantAttribute()) ); + Assert( quantifiers::TermDb::hasInstConstAttr(pat) ); d_pattern = pat; d_match_pattern = pat; d_next = NULL; } -void InstMatchGenerator::setActiveAdd(){ - d_active_add = true; +void InstMatchGenerator::setActiveAdd(bool val){ + d_active_add = val; if( d_next!=NULL ){ - d_next->setActiveAdd(); + d_next->setActiveAdd(val); } } @@ -52,28 +52,43 @@ void InstMatchGenerator::initialize( QuantifiersEngine* qe, std::vector< InstMat //we want to add the children of the NOT d_match_pattern = d_pattern[0]; } - if( d_match_pattern.getKind()==IFF || d_match_pattern.getKind()==EQUAL ){ - if( !d_match_pattern[0].hasAttribute(InstConstantAttribute()) ){ - Assert( d_match_pattern[1].hasAttribute(InstConstantAttribute()) ); - //swap sides - Node pat = d_pattern; - d_pattern = NodeManager::currentNM()->mkNode( d_match_pattern.getKind(), d_match_pattern[1], d_match_pattern[0] ); - d_pattern = pat.getKind()==NOT ? d_pattern.notNode() : d_pattern; - if( pat.getKind()!=NOT ){ //TEMPORARY until we do better implementation of disequality matching - d_match_pattern = d_match_pattern[1]; - }else{ - d_match_pattern = d_pattern[0][0]; + if( d_match_pattern.getKind()==IFF || d_match_pattern.getKind()==EQUAL || d_match_pattern.getKind()==GEQ ){ + //make sure the matching portion of the equality is on the LHS of d_pattern + // and record what d_match_pattern is + if( !quantifiers::TermDb::hasInstConstAttr(d_match_pattern[0]) || + d_match_pattern[0].getKind()==INST_CONSTANT ){ + if( d_match_pattern[1].getKind()!=INST_CONSTANT ){ + Assert( quantifiers::TermDb::hasInstConstAttr(d_match_pattern[1]) ); + Node mp = d_match_pattern[1]; + //swap sides + Node pat = d_pattern; + if(d_match_pattern.getKind()==GEQ){ + d_pattern = NodeManager::currentNM()->mkNode( kind::GT, d_match_pattern[1], d_match_pattern[0] ); + d_pattern = d_pattern.negate(); + }else{ + d_pattern = NodeManager::currentNM()->mkNode( d_match_pattern.getKind(), d_match_pattern[1], d_match_pattern[0] ); + } + d_pattern = pat.getKind()==NOT ? d_pattern.negate() : d_pattern; + d_match_pattern = mp; } - }else if( !d_match_pattern[1].hasAttribute(InstConstantAttribute()) ){ - Assert( d_match_pattern[0].hasAttribute(InstConstantAttribute()) ); - if( d_pattern.getKind()!=NOT ){ //TEMPORARY until we do better implementation of disequality matching - d_match_pattern = d_match_pattern[0]; + }else if( !quantifiers::TermDb::hasInstConstAttr(d_match_pattern[1]) || + d_match_pattern[1].getKind()==INST_CONSTANT ){ + if( d_match_pattern[0].getKind()!=INST_CONSTANT ){ + Assert( quantifiers::TermDb::hasInstConstAttr(d_match_pattern[0]) ); + if( d_pattern.getKind()!=NOT ){ //TEMPORARY until we do better implementation of disequality matching + d_match_pattern = d_match_pattern[0]; + }else if( d_match_pattern[1].getKind()==INST_CONSTANT ){ + d_match_pattern = d_match_pattern[0]; + } } } } + Trace("inst-match-gen") << "Pattern is " << d_pattern << ", match pattern is " << d_match_pattern << std::endl; + + //now, collect children of d_match_pattern int childMatchPolicy = MATCH_GEN_DEFAULT; for( int i=0; i<(int)d_match_pattern.getNumChildren(); i++ ){ - if( d_match_pattern[i].hasAttribute(InstConstantAttribute()) ){ + if( quantifiers::TermDb::hasInstConstAttr(d_match_pattern[i]) ){ if( d_match_pattern[i].getKind()!=INST_CONSTANT && !Trigger::isBooleanTermTrigger( d_match_pattern[i] ) ){ InstMatchGenerator * cimg = new InstMatchGenerator( d_match_pattern[i], childMatchPolicy ); d_children.push_back( cimg ); @@ -83,10 +98,11 @@ void InstMatchGenerator::initialize( QuantifiersEngine* qe, std::vector< InstMat } } - Debug("inst-match-gen") << "Pattern is " << d_pattern << ", match pattern is " << d_match_pattern << std::endl; - //create candidate generator - if( d_match_pattern.getKind()==EQUAL || d_match_pattern.getKind()==IFF ){ + if( d_match_pattern.getKind()==INST_CONSTANT ){ + d_cg = new CandidateGeneratorQEAll( qe, d_match_pattern ); + } + else if( d_match_pattern.getKind()==EQUAL || d_match_pattern.getKind()==IFF ){ Assert( d_matchPolicy==MATCH_GEN_DEFAULT ); //we will be producing candidates via literal matching heuristics if( d_pattern.getKind()!=NOT ){ @@ -96,50 +112,40 @@ void InstMatchGenerator::initialize( QuantifiersEngine* qe, std::vector< InstMat //candidates will be all disequalities d_cg = new inst::CandidateGeneratorQELitDeq( qe, d_match_pattern ); } - }else if( d_pattern.getKind()==EQUAL || d_pattern.getKind()==IFF || d_pattern.getKind()==NOT ){ + }else if( d_pattern.getKind()==EQUAL || d_pattern.getKind()==IFF || + d_pattern.getKind()==GEQ || d_pattern.getKind()==GT || d_pattern.getKind()==NOT ){ Assert( d_matchPolicy==MATCH_GEN_DEFAULT ); if( d_pattern.getKind()==NOT ){ - Unimplemented("Disequal generator unimplemented"); + if (d_pattern[0][1].getKind()!=INST_CONSTANT) { + Unimplemented("Disequal generator unimplemented"); + }else{ + d_eq_class = d_pattern[0][1]; + } }else{ - Assert( Trigger::isAtomicTrigger( d_match_pattern ) ); - //we are matching only in a particular equivalence class - d_cg = new inst::CandidateGeneratorQE( qe, d_match_pattern.getOperator() ); //store the equivalence class that we will call d_cg->reset( ... ) on d_eq_class = d_pattern[1]; } + Assert( Trigger::isAtomicTrigger( d_match_pattern ) ); + //we are matching only in a particular equivalence class + d_cg = new inst::CandidateGeneratorQE( qe, d_match_pattern.getOperator() ); }else if( Trigger::isAtomicTrigger( d_match_pattern ) ){ - //if( d_matchPolicy==MATCH_GEN_EFFICIENT_E_MATCH ){ - //Warning() << "Currently efficient e matching is not taken into account for quantifiers: " << d_pattern << std::endl; - //} //we will be scanning lists trying to find d_match_pattern.getOperator() d_cg = new inst::CandidateGeneratorQE( qe, d_match_pattern.getOperator() ); }else{ d_cg = new CandidateGeneratorQueue; - if( !Trigger::isArithmeticTrigger( d_match_pattern.getAttribute(InstConstantAttribute()), d_match_pattern, d_arith_coeffs ) ){ - Debug("inst-match-gen") << "(?) Unknown matching pattern is " << d_match_pattern << std::endl; - //Warning() << "(?) Unknown matching pattern is " << d_match_pattern << std::endl; - d_matchPolicy = MATCH_GEN_INTERNAL_ERROR; - }else{ - Debug("matching-arith") << "Generated arithmetic pattern for " << d_match_pattern << ": " << std::endl; - for( std::map< Node, Node >::iterator it = d_arith_coeffs.begin(); it != d_arith_coeffs.end(); ++it ){ - Debug("matching-arith") << " " << it->first << " -> " << it->second << std::endl; - } - //we will treat this as match gen internal arithmetic - d_matchPolicy = MATCH_GEN_INTERNAL_ARITHMETIC; - } + Trace("inst-match-gen-warn") << "(?) Unknown matching pattern is " << d_match_pattern << std::endl; + d_matchPolicy = MATCH_GEN_INTERNAL_ERROR; } } } /** get match (not modulo equality) */ bool InstMatchGenerator::getMatch( Node f, Node t, InstMatch& m, QuantifiersEngine* qe ){ - Debug("matching") << "Matching " << t << " against pattern " << d_match_pattern << " (" - << m << ")" << ", " << d_children.size() << std::endl; + Trace("matching") << "Matching " << t << " against pattern " << d_match_pattern << " (" + << m << ")" << ", " << d_children.size() << ", pattern is " << d_pattern << std::endl; Assert( !d_match_pattern.isNull() ); if( qe->d_optMatchIgnoreModelBasis && t.getAttribute(ModelBasisAttribute()) ){ return true; - }else if( d_matchPolicy==MATCH_GEN_INTERNAL_ARITHMETIC ){ - return getMatchArithmetic( t, m, qe ); }else if( d_matchPolicy==MATCH_GEN_INTERNAL_ERROR ){ return false; }else{ @@ -149,12 +155,12 @@ bool InstMatchGenerator::getMatch( Node f, Node t, InstMatch& m, QuantifiersEngi InstMatch prev( &m ); //if t is null Assert( !t.isNull() ); - Assert( !t.hasAttribute(InstConstantAttribute()) ); + Assert( !quantifiers::TermDb::hasInstConstAttr(t) ); Assert( t.getKind()==d_match_pattern.getKind() ); Assert( !Trigger::isAtomicTrigger( d_match_pattern ) || t.getOperator()==d_match_pattern.getOperator() ); //first, check if ground arguments are not equal, or a match is in conflict for( int i=0; i<(int)d_match_pattern.getNumChildren(); i++ ){ - if( d_match_pattern[i].hasAttribute(InstConstantAttribute()) ){ + if( quantifiers::TermDb::hasInstConstAttr(d_match_pattern[i]) ){ if( d_match_pattern[i].getKind()==INST_CONSTANT || Trigger::isBooleanTermTrigger( d_match_pattern[i] ) ){ Node vv = d_match_pattern[i]; Node tt = t[i]; @@ -164,24 +170,54 @@ bool InstMatchGenerator::getMatch( Node f, Node t, InstMatch& m, QuantifiersEngi } if( !m.setMatch( q, vv, tt ) ){ //match is in conflict - Debug("matching-debug") << "Match in conflict " << tt << " and " + Trace("matching-debug") << "Match in conflict " << tt << " and " << vv << " because " << m.get(vv) << std::endl; - Debug("matching-fail") << "Match fail: " << m.get(vv) << " and " << tt << std::endl; + Trace("matching-fail") << "Match fail: " << m.get(vv) << " and " << tt << std::endl; success = false; break; } } }else{ if( !q->areEqual( d_match_pattern[i], t[i] ) ){ - Debug("matching-fail") << "Match fail arg: " << d_match_pattern[i] << " and " << t[i] << std::endl; + Trace("matching-fail") << "Match fail arg: " << d_match_pattern[i] << " and " << t[i] << std::endl; //ground arguments are not equal success = false; break; } } } + //for relational matching + if( !d_eq_class.isNull() && d_eq_class.getKind()==INST_CONSTANT ){ + //also must fit match to equivalence class + bool pol = d_pattern.getKind()!=NOT; + Node pat = d_pattern.getKind()==NOT ? d_pattern[0] : d_pattern; + Node t_match; + if( pol ){ + if (pat.getKind()==GT) { + Node r = NodeManager::currentNM()->mkConst( Rational(-1) ); + t_match = NodeManager::currentNM()->mkNode(PLUS, t, r); + }else{ + t_match = t; + } + }else{ + if(pat.getKind()==EQUAL) { + Node r = NodeManager::currentNM()->mkConst( Rational(1) ); + t_match = NodeManager::currentNM()->mkNode(PLUS, t, r); + }else if( pat.getKind()==IFF ){ + t_match = NodeManager::currentNM()->mkConst( !q->areEqual( NodeManager::currentNM()->mkConst(true), t ) ); + }else if( pat.getKind()==GEQ ){ + Node r = NodeManager::currentNM()->mkConst( Rational(1) ); + t_match = NodeManager::currentNM()->mkNode(PLUS, t, r); + }else if( pat.getKind()==GT ){ + t_match = t; + } + } + if( !t_match.isNull() && !m.setMatch( q, d_eq_class, t_match ) ){ + success = false; + } + } if( success ){ //now, fit children into match //we will be requesting candidates for matching terms for each child @@ -208,95 +244,45 @@ bool InstMatchGenerator::getMatch( Node f, Node t, InstMatch& m, QuantifiersEngi } } -bool InstMatchGenerator::getMatchArithmetic( Node t, InstMatch& m, QuantifiersEngine* qe ){ - Debug("matching-arith") << "Matching " << t << " " << d_match_pattern << std::endl; - if( !d_arith_coeffs.empty() ){ - NodeBuilder<> tb(kind::PLUS); - Node ic = Node::null(); - for( std::map< Node, Node >::iterator it = d_arith_coeffs.begin(); it != d_arith_coeffs.end(); ++it ){ - Debug("matching-arith") << it->first << " -> " << it->second << std::endl; - if( !it->first.isNull() ){ - if( m.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 )); - //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) ) ); - } - } - } - Debug("matching-arith") << "Setting " << ic << " to " << tm << std::endl; - return true; - }else{ - return false; - } - }else{ - return false; - } -} - - /** reset instantiation round */ void InstMatchGenerator::resetInstantiationRound( QuantifiersEngine* qe ){ - if( d_match_pattern.isNull() ){ - for( int i=0; i<(int)d_children.size(); i++ ){ - d_children[i]->resetInstantiationRound( qe ); - } - }else{ + if( !d_match_pattern.isNull() ){ + Trace("matching-debug2") << this << " reset instantiation round." << std::endl; + d_needsReset = true; if( d_cg ){ d_cg->resetInstantiationRound(); } } + if( d_next ){ + d_next->resetInstantiationRound( qe ); + } } void InstMatchGenerator::reset( Node eqc, QuantifiersEngine* qe ){ + Trace("matching-debug2") << this << " reset " << eqc << "." << std::endl; if( !eqc.isNull() ){ d_eq_class = eqc; } //we have a specific equivalence class in mind //we are producing matches for f(E) ~ t, where E is a non-ground vector of terms, and t is a ground term //just look in equivalence class of the RHS - d_cg->reset( d_eq_class ); + d_cg->reset( d_eq_class.getKind()==INST_CONSTANT ? Node::null() : d_eq_class ); + d_needsReset = false; } bool InstMatchGenerator::getNextMatch( Node f, InstMatch& m, QuantifiersEngine* qe ){ + if( d_needsReset ){ + Trace("matching") << "Reset not done yet, must do the reset..." << std::endl; + reset( d_eq_class.getKind()==INST_CONSTANT ? Node::null() : d_eq_class, qe ); + } m.d_matched = Node::null(); - //Debug("matching") << this << " " << d_pattern << " get next match 2 " << m << " in eq class " << d_eq_class << std::endl; + Trace("matching") << this << " " << d_match_pattern << " get next match " << m << " in eq class " << d_eq_class << std::endl; bool success = false; Node t; do{ //get the next candidate term t t = d_cg->getNextCandidate(); + Trace("matching-debug2") << "Matching candidate : " << t << std::endl; //if t not null, try to fit it into match m if( !t.isNull() && t.getType()==d_match_pattern.getType() ){ success = getMatch( f, t, m, qe ); @@ -304,9 +290,9 @@ bool InstMatchGenerator::getNextMatch( Node f, InstMatch& m, QuantifiersEngine* }while( !success && !t.isNull() ); m.d_matched = t; if( !success ){ - //Debug("matching") << this << " failed, reset " << d_eq_class << std::endl; + Trace("matching") << this << " failed, reset " << d_eq_class << std::endl; //we failed, must reset - reset( d_eq_class, qe ); + reset( d_eq_class.getKind()==INST_CONSTANT ? Node::null() : d_eq_class, qe ); } return success; } diff --git a/src/theory/quantifiers/inst_match_generator.h b/src/theory/quantifiers/inst_match_generator.h index 4c954fa81..5d2128922 100644 --- a/src/theory/quantifiers/inst_match_generator.h +++ b/src/theory/quantifiers/inst_match_generator.h @@ -44,13 +44,14 @@ public: /** add ground term t, called when t is added to term db */ virtual int addTerm( Node f, Node t, QuantifiersEngine* qe ) = 0; /** set active add */ - virtual void setActiveAdd() {} + virtual void setActiveAdd( bool val ) {} };/* class IMGenerator */ class CandidateGenerator; class InstMatchGenerator : public IMGenerator { private: + bool d_needsReset; /** candidate generator */ CandidateGenerator* d_cg; /** policy to use for matching */ @@ -72,12 +73,8 @@ public: MATCH_GEN_DEFAULT = 0, MATCH_GEN_EFFICIENT_E_MATCH, //generate matches via Efficient E-matching for SMT solvers //others (internally used) - MATCH_GEN_INTERNAL_ARITHMETIC, MATCH_GEN_INTERNAL_ERROR, }; -private: - /** for arithmetic */ - bool getMatchArithmetic( Node t, InstMatch& m, QuantifiersEngine* qe ); public: /** get the match against ground term or formula t. d_match_pattern and t should have the same shape. @@ -108,7 +105,7 @@ public: int addTerm( Node f, Node t, QuantifiersEngine* qe ); bool d_active_add; - void setActiveAdd(); + void setActiveAdd( bool val ); static InstMatchGenerator* mkInstMatchGenerator( Node pat, QuantifiersEngine* qe ); static InstMatchGenerator* mkInstMatchGenerator( std::vector< Node >& pats, QuantifiersEngine* qe ); diff --git a/src/theory/quantifiers/inst_strategy_cbqi.cpp b/src/theory/quantifiers/inst_strategy_cbqi.cpp index dbdf95613..4fe4072a3 100644 --- a/src/theory/quantifiers/inst_strategy_cbqi.cpp +++ b/src/theory/quantifiers/inst_strategy_cbqi.cpp @@ -41,6 +41,19 @@ bool InstStrategySimplex::calculateShouldProcess( Node f ){ return false; } +void getInstantiationConstants( Node n, std::vector< Node >& ics ){ + if( n.getKind()==INST_CONSTANT ){ + if( std::find( ics.begin(), ics.end(), n )==ics.end() ){ + ics.push_back( n ); + } + }else{ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + getInstantiationConstants( n[i], ics ); + } + } +} + + void InstStrategySimplex::processResetInstantiationRound( Theory::Effort effort ){ Debug("quant-arith") << "Setting up simplex for instantiator... " << std::endl; d_instRows.clear(); @@ -54,37 +67,69 @@ void InstStrategySimplex::processResetInstantiationRound( Theory::Effort effort ArithVar x = *vi; if( d_th->d_internal->d_partialModel.hasEitherBound( x ) ){ Node n = avnm.asNode(x); - Node f; - NodeBuilder<> t(kind::PLUS); - if( n.getKind()==PLUS ){ - for( int i=0; i<(int)n.getNumChildren(); i++ ){ - addTermToRow( x, n[i], f, t ); + + //collect instantiation constants + std::vector< Node > ics; + getInstantiationConstants( n, ics ); + for( unsigned i=0; i<ics.size(); i++ ){ + + NodeBuilder<> t(kind::PLUS); + if( n.getKind()==PLUS ){ + for( int j=0; j<(int)n.getNumChildren(); j++ ){ + addTermToRow( ics[i], x, n[j], t ); + } + }else{ + addTermToRow( ics[i], x, n, t ); } - }else{ - addTermToRow( x, n, f, t ); - } - if( f!=Node::null() ){ - d_instRows[f].push_back( x ); + d_instRows[ics[i]].push_back( x ); //this theory has constraints from f + Node f = TermDb::getInstConstAttr(ics[i]); Debug("quant-arith") << "Has constraints from " << f << std::endl; //set that we should process it d_quantActive[ f ] = true; //set tableaux term if( t.getNumChildren()==0 ){ - d_tableaux_term[x] = NodeManager::currentNM()->mkConst( Rational(0) ); + d_tableaux_term[ics[i]][x] = NodeManager::currentNM()->mkConst( Rational(0) ); }else if( t.getNumChildren()==1 ){ - d_tableaux_term[x] = t.getChild( 0 ); + d_tableaux_term[ics[i]][x] = t.getChild( 0 ); }else{ - d_tableaux_term[x] = t; + d_tableaux_term[ics[i]][x] = t; } } } } //print debug + Debug("quant-arith-debug") << std::endl; debugPrint( "quant-arith-debug" ); d_counter++; } +void InstStrategySimplex::addTermToRow( Node i, ArithVar x, Node n, NodeBuilder<>& t ){ + if( n.getKind()==MULT ){ + if( TermDb::hasInstConstAttr(n[1]) ){ + if( n[1]==i ){ + d_ceTableaux[i][x][ n[1] ] = n[0]; + }else{ + d_tableaux_ce_term[i][x][ n[1] ] = n[0]; + } + }else{ + d_tableaux[i][x][ n[1] ] = n[0]; + t << n; + } + }else{ + if( TermDb::hasInstConstAttr(n) ){ + if( n==i ){ + d_ceTableaux[i][x][ n ] = Node::null(); + }else{ + d_tableaux_ce_term[i][x][ n ] = NodeManager::currentNM()->mkConst( Rational(1) ); + } + }else{ + d_tableaux[i][x][ n ] = NodeManager::currentNM()->mkConst( Rational(1) ); + t << n; + } + } +} + int InstStrategySimplex::process( Node f, Theory::Effort effort, int e ){ if( e<2 ){ return STATUS_UNFINISHED; @@ -92,48 +137,51 @@ int InstStrategySimplex::process( Node f, Theory::Effort effort, int e ){ //Notice() << f << std::endl; //Notice() << "Num inst rows = " << d_th->d_instRows[f].size() << std::endl; //Notice() << "Num inst constants = " << d_quantEngine->getNumInstantiationConstants( f ) << std::endl; - Debug("quant-arith-simplex") << "InstStrategySimplex check " << f << ", rows = " << d_instRows[f].size() << std::endl; - for( int j=0; j<(int)d_instRows[f].size(); j++ ){ - ArithVar x = d_instRows[f][j]; - if( !d_ceTableaux[x].empty() ){ - Debug("quant-arith-simplex") << "Check row " << x << std::endl; - //instantiation row will be A*e + B*t = beta, - // where e is a vector of terms , and t is vector of ground terms. - // Say one term in A*e is coeff*e_i, where e_i is an instantiation constant - // We will construct the term ( beta - B*t)/coeff to use for e_i. - InstMatch m; - //By default, choose the first instantiation constant to be e_i. - Node var = d_ceTableaux[x].begin()->first; - if( var.getType().isInteger() ){ - std::map< Node, Node >::iterator it = d_ceTableaux[x].begin(); - //try to find coefficent that is +/- 1 - while( !var.isNull() && !d_ceTableaux[x][var].isNull() && d_ceTableaux[x][var]!=d_negOne ){ - ++it; - if( it==d_ceTableaux[x].end() ){ - var = Node::null(); - }else{ - var = it->first; + for( int i=0; i<d_quantEngine->getTermDatabase()->getNumInstantiationConstants( f ); i++ ){ + Node ic = d_quantEngine->getTermDatabase()->getInstantiationConstant( f, i ); + Debug("quant-arith-simplex") << "InstStrategySimplex check " << ic << ", rows = " << d_instRows[ic].size() << std::endl; + for( int j=0; j<(int)d_instRows[ic].size(); j++ ){ + ArithVar x = d_instRows[ic][j]; + if( !d_ceTableaux[ic][x].empty() ){ + Debug("quant-arith-simplex") << "Check row " << ic << " " << x << std::endl; + //instantiation row will be A*e + B*t = beta, + // where e is a vector of terms , and t is vector of ground terms. + // Say one term in A*e is coeff*e_i, where e_i is an instantiation constant + // We will construct the term ( beta - B*t)/coeff to use for e_i. + InstMatch m; + //By default, choose the first instantiation constant to be e_i. + Node var = d_ceTableaux[ic][x].begin()->first; + if( var.getType().isInteger() ){ + std::map< Node, Node >::iterator it = d_ceTableaux[ic][x].begin(); + //try to find coefficent that is +/- 1 + while( !var.isNull() && !d_ceTableaux[ic][x][var].isNull() && d_ceTableaux[ic][x][var]!=d_negOne ){ + ++it; + if( it==d_ceTableaux[ic][x].end() ){ + var = Node::null(); + }else{ + var = it->first; + } } + //otherwise, try one that divides all ground term coefficients? DO_THIS } - //otherwise, try one that divides all ground term coefficients? DO_THIS - } - if( !var.isNull() ){ - Debug("quant-arith-simplex") << "Instantiate with var " << var << std::endl; - doInstantiation( f, d_tableaux_term[x], x, m, var ); - }else{ - Debug("quant-arith-simplex") << "Could not find var." << std::endl; + if( !var.isNull() ){ + Debug("quant-arith-simplex") << "Instantiate with var " << var << std::endl; + doInstantiation( f, ic, d_tableaux_term[ic][x], x, m, var ); + }else{ + Debug("quant-arith-simplex") << "Could not find var." << std::endl; + } + ////choose a new variable based on alternation strategy + //int index = d_counter%(int)d_th->d_ceTableaux[x].size(); + //Node var; + //for( std::map< Node, Node >::iterator it = d_th->d_ceTableaux[x].begin(); it != d_th->d_ceTableaux[x].end(); ++it ){ + // if( index==0 ){ + // var = it->first; + // break; + // } + // index--; + //} + //d_th->doInstantiation( f, d_th->d_tableaux_term[x], x, &m, var ); } - ////choose a new variable based on alternation strategy - //int index = d_counter%(int)d_th->d_ceTableaux[x].size(); - //Node var; - //for( std::map< Node, Node >::iterator it = d_th->d_ceTableaux[x].begin(); it != d_th->d_ceTableaux[x].end(); ++it ){ - // if( index==0 ){ - // var = it->first; - // break; - // } - // index--; - //} - //d_th->doInstantiation( f, d_th->d_tableaux_term[x], x, &m, var ); } } } @@ -141,34 +189,6 @@ int InstStrategySimplex::process( Node f, Theory::Effort effort, int e ){ } -void InstStrategySimplex::addTermToRow( ArithVar x, Node n, Node& f, NodeBuilder<>& t ){ - if( n.getKind()==MULT ){ - if( n[1].hasAttribute(InstConstantAttribute()) ){ - f = n[1].getAttribute(InstConstantAttribute()); - if( n[1].getKind()==INST_CONSTANT ){ - d_ceTableaux[x][ n[1] ] = n[0]; - }else{ - d_tableaux_ce_term[x][ n[1] ] = n[0]; - } - }else{ - d_tableaux[x][ n[1] ] = n[0]; - t << n; - } - }else{ - if( n.hasAttribute(InstConstantAttribute()) ){ - f = n.getAttribute(InstConstantAttribute()); - if( n.getKind()==INST_CONSTANT ){ - d_ceTableaux[x][ n ] = Node::null(); - }else{ - d_tableaux_ce_term[x][ n ] = NodeManager::currentNM()->mkConst( Rational(1) ); - } - }else{ - d_tableaux[x][ n ] = NodeManager::currentNM()->mkConst( Rational(1) ); - t << n; - } - } -} - void InstStrategySimplex::debugPrint( const char* c ){ ArithVariables& avnm = d_th->d_internal->d_partialModel; ArithVariables::var_iterator vi, vend; @@ -218,14 +238,17 @@ void InstStrategySimplex::debugPrint( const char* c ){ Debug( c ) << d_quantEngine->getTermDatabase()->getInstantiationConstant( f, i ); } Debug(c) << std::endl; - Debug(c) << " Instantiation rows: "; - for( int i=0; i<(int)d_instRows[f].size(); i++ ){ - if( i>0 ){ - Debug(c) << ", "; + for( int j=0; j<d_quantEngine->getTermDatabase()->getNumInstantiationConstants( f ); j++ ){ + Node ic = d_quantEngine->getTermDatabase()->getInstantiationConstant( f, j ); + Debug(c) << " Instantiation rows for " << ic << " : "; + for( int i=0; i<(int)d_instRows[ic].size(); i++ ){ + if( i>0 ){ + Debug(c) << ", "; + } + Debug(c) << d_instRows[ic][i]; } - Debug(c) << d_instRows[f][i]; + Debug(c) << std::endl; } - Debug(c) << std::endl; } } @@ -234,15 +257,15 @@ void InstStrategySimplex::debugPrint( const char* c ){ // t[e] is a vector of terms containing instantiation constants from f, // and term is a ground term (c1*t1 + ... + cn*tn). // We construct the term ( beta - term )/coeff to use as an instantiation for var. -bool InstStrategySimplex::doInstantiation( Node f, Node term, ArithVar x, InstMatch& m, Node var ){ +bool InstStrategySimplex::doInstantiation( Node f, Node ic, Node term, ArithVar x, InstMatch& m, Node var ){ //first try +delta - if( doInstantiation2( f, term, x, m, var ) ){ + if( doInstantiation2( f, ic, term, x, m, var ) ){ ++(d_quantEngine->getInstantiationEngine()->d_statistics.d_instantiations_cbqi_arith); return true; }else{ #ifdef ARITH_INSTANTIATOR_USE_MINUS_DELTA //otherwise try -delta - if( doInstantiation2( f, term, x, m, var, true ) ){ + if( doInstantiation2( f, ic, term, x, m, var, true ) ){ ++(d_quantEngine->getInstantiationEngine()->d_statistics.d_instantiations_cbqi_arith_minus); return true; }else{ @@ -254,16 +277,16 @@ bool InstStrategySimplex::doInstantiation( Node f, Node term, ArithVar x, InstMa } } -bool InstStrategySimplex::doInstantiation2( Node f, Node term, ArithVar x, InstMatch& m, Node var, bool minus_delta ){ +bool InstStrategySimplex::doInstantiation2( Node f, Node ic, Node term, ArithVar x, InstMatch& m, Node var, bool minus_delta ){ // make term ( beta - term )/coeff Node beta = getTableauxValue( x, minus_delta ); Node instVal = NodeManager::currentNM()->mkNode( MINUS, beta, term ); - if( !d_ceTableaux[x][var].isNull() ){ + if( !d_ceTableaux[ic][x][var].isNull() ){ if( var.getType().isInteger() ){ - Assert( d_ceTableaux[x][var]==NodeManager::currentNM()->mkConst( Rational(-1) ) ); - instVal = NodeManager::currentNM()->mkNode( MULT, d_ceTableaux[x][var], instVal ); + Assert( d_ceTableaux[ic][x][var]==NodeManager::currentNM()->mkConst( Rational(-1) ) ); + instVal = NodeManager::currentNM()->mkNode( MULT, d_ceTableaux[ic][x][var], instVal ); }else{ - Node coeff = NodeManager::currentNM()->mkConst( Rational(1) / d_ceTableaux[x][var].getConst<Rational>() ); + Node coeff = NodeManager::currentNM()->mkConst( Rational(1) / d_ceTableaux[ic][x][var].getConst<Rational>() ); instVal = NodeManager::currentNM()->mkNode( MULT, coeff, instVal ); } } @@ -327,81 +350,9 @@ int InstStrategyDatatypesValue::process( Node f, Theory::Effort effort, int e ){ Node InstStrategyDatatypesValue::getValueFor( Node n ){ //simply get the ground value for n in the current model, if it exists, // or return an arbitrary ground term otherwise - if( !n.hasAttribute(InstConstantAttribute()) ){ + if( !TermDb::hasInstConstAttr(n) ){ return n; }else{ return n; } - /* FIXME - - Debug("quant-datatypes-debug") << "get value for " << n << std::endl; - if( !n.hasAttribute(InstConstantAttribute()) ){ - return n; - }else{ - Assert( n.getType().isDatatype() ); - //check if in equivalence class with ground term - Node rep = getRepresentative( n ); - Debug("quant-datatypes-debug") << "Rep is " << rep << std::endl; - if( !rep.hasAttribute(InstConstantAttribute()) ){ - return rep; - }else{ - if( !n.getType().isDatatype() ){ - return n.getType().mkGroundTerm(); - }else{ - if( n.getKind()==APPLY_CONSTRUCTOR ){ - std::vector< Node > children; - children.push_back( n.getOperator() ); - for( int i=0; i<(int)n.getNumChildren(); i++ ){ - children.push_back( getValueFor( n[i] ) ); - } - return NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, children ); - }else{ - const Datatype& dt = ((DatatypeType)(n.getType()).toType()).getDatatype(); - TheoryDatatypes::EqLists* labels = &((TheoryDatatypes*)d_th)->d_labels; - //otherwise, use which constructor the inst constant is current chosen to be - if( labels->find( n )!=labels->end() ){ - TheoryDatatypes::EqList* lbl = (*labels->find( n )).second; - int tIndex = -1; - if( !lbl->empty() && (*lbl)[ lbl->size()-1 ].getKind()==APPLY_TESTER ){ - Debug("quant-datatypes-debug") << n << " tester is " << (*lbl)[ lbl->size()-1 ] << std::endl; - tIndex = Datatype::indexOf((*lbl)[ lbl->size()-1 ].getOperator().toExpr()); - }else{ - Debug("quant-datatypes-debug") << "find possible tester choice" << std::endl; - //must find a possible choice - vector< bool > possibleCons; - possibleCons.resize( dt.getNumConstructors(), true ); - for( TheoryDatatypes::EqList::const_iterator j = lbl->begin(); j != lbl->end(); j++ ) { - Node leqn = (*j); - possibleCons[ Datatype::indexOf( leqn[0].getOperator().toExpr() ) ] = false; - } - for( unsigned int j=0; j<possibleCons.size(); j++ ) { - if( possibleCons[j] ){ - tIndex = j; - break; - } - } - } - Assert( tIndex!=-1 ); - Node cons = Node::fromExpr( dt[ tIndex ].getConstructor() ); - Debug("quant-datatypes-debug") << n << " cons is " << cons << std::endl; - std::vector< Node > children; - children.push_back( cons ); - for( int i=0; i<(int)dt[ tIndex ].getNumArgs(); i++ ) { - Node sn = NodeManager::currentNM()->mkNode( APPLY_SELECTOR, Node::fromExpr( dt[tIndex][i].getSelector() ), n ); - if( n.hasAttribute(InstConstantAttribute()) ){ - InstConstantAttribute ica; - sn.setAttribute(ica,n.getAttribute(InstConstantAttribute()) ); - } - Node snn = getValueFor( sn ); - children.push_back( snn ); - } - return NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, children ); - }else{ - return n.getType().mkGroundTerm(); - } - } - } - } - } - */ } diff --git a/src/theory/quantifiers/inst_strategy_cbqi.h b/src/theory/quantifiers/inst_strategy_cbqi.h index a45318489..821beeae0 100644 --- a/src/theory/quantifiers/inst_strategy_cbqi.h +++ b/src/theory/quantifiers/inst_strategy_cbqi.h @@ -49,19 +49,19 @@ private: /** for each quantifier, simplex rows */ std::map< Node, std::vector< arith::ArithVar > > d_instRows; /** tableaux */ - std::map< arith::ArithVar, Node > d_tableaux_term; - std::map< arith::ArithVar, std::map< Node, Node > > d_tableaux_ce_term; - std::map< arith::ArithVar, std::map< Node, Node > > d_tableaux; + std::map< Node, std::map< arith::ArithVar, Node > > d_tableaux_term; + std::map< Node, std::map< arith::ArithVar, std::map< Node, Node > > > d_tableaux_ce_term; + std::map< Node, std::map< arith::ArithVar, std::map< Node, Node > > > d_tableaux; /** ce tableaux */ - std::map< arith::ArithVar, std::map< Node, Node > > d_ceTableaux; + std::map< Node, std::map< arith::ArithVar, std::map< Node, Node > > > d_ceTableaux; /** get value */ Node getTableauxValue( Node n, bool minus_delta = false ); Node getTableauxValue( arith::ArithVar v, bool minus_delta = false ); /** do instantiation */ - bool doInstantiation( Node f, Node term, arith::ArithVar x, InstMatch& m, Node var ); - bool doInstantiation2( Node f, Node term, arith::ArithVar x, InstMatch& m, Node var, bool minus_delta = false ); + bool doInstantiation( Node f, Node ic, Node term, arith::ArithVar x, InstMatch& m, Node var ); + bool doInstantiation2( Node f, Node ic, Node term, arith::ArithVar x, InstMatch& m, Node var, bool minus_delta = false ); /** add term to row */ - void addTermToRow( arith::ArithVar x, Node n, Node& f, NodeBuilder<>& t ); + void addTermToRow( Node ic, arith::ArithVar x, Node n, NodeBuilder<>& t ); /** print debug */ void debugPrint( const char* c ); private: diff --git a/src/theory/quantifiers/inst_strategy_e_matching.cpp b/src/theory/quantifiers/inst_strategy_e_matching.cpp index 0e1266e0d..ef81d55a1 100644 --- a/src/theory/quantifiers/inst_strategy_e_matching.cpp +++ b/src/theory/quantifiers/inst_strategy_e_matching.cpp @@ -144,7 +144,11 @@ int InstStrategyAutoGenTriggers::process( Node f, Theory::Effort effort, int e ) } if( gen ){ generateTriggers( f, effort, e, status ); + if( d_auto_gen_trigger[f].empty() && f.getNumChildren()==2 ){ + Trace("no-trigger") << "Could not find trigger for " << f << std::endl; + } } + //if( e==4 ){ // d_processed_trigger.clear(); // d_quantEngine->getEqualityQuery()->setLiberal( true ); diff --git a/src/theory/quantifiers/instantiation_engine.cpp b/src/theory/quantifiers/instantiation_engine.cpp index 77df69456..628f8b14a 100644 --- a/src/theory/quantifiers/instantiation_engine.cpp +++ b/src/theory/quantifiers/instantiation_engine.cpp @@ -92,7 +92,7 @@ bool InstantiationEngine::doInstantiationRound( Theory::Effort effort ){ NodeBuilder<> nb(kind::OR); nb << f << ceLit; Node lem = nb; - Debug("cbqi-debug") << "Counterexample lemma : " << lem << std::endl; + Trace("cbqi") << "Counterexample lemma : " << lem << std::endl; d_quantEngine->getOutputChannel().lemma( lem ); addedLemma = true; } @@ -197,7 +197,10 @@ void InstantiationEngine::check( Theory::Effort e ){ << d_quantEngine->getModel()->getNumAssertedQuantifiers() << std::endl; for( int i=0; i<(int)d_quantEngine->getModel()->getNumAssertedQuantifiers(); i++ ){ Node n = d_quantEngine->getModel()->getAssertedQuantifier( i ); - if( options::cbqi() && hasAddedCbqiLemma( n ) ){ + //it is not active if we have found the skolemized negation is unsat + if( n.hasAttribute(QRewriteRuleAttribute()) ){ + d_quant_active[n] = false; + }else if( options::cbqi() && hasAddedCbqiLemma( n ) ){ Node cel = d_quantEngine->getTermDatabase()->getCounterexampleLiteral( n ); bool active, value; bool ceValue = false; @@ -210,7 +213,9 @@ void InstantiationEngine::check( Theory::Effort e ){ d_quant_active[n] = active; if( active ){ Debug("quantifiers") << " Active : " << n; - quantActive = true; + if( !TermDb::hasInstConstAttr(n) ){ + quantActive = true; + } }else{ Debug("quantifiers") << " NOT active : " << n; if( d_quantEngine->getValuation().isDecision( cel ) ){ @@ -226,14 +231,18 @@ void InstantiationEngine::check( Theory::Effort e ){ Debug("quantifiers") << ", ce is asserted"; } Debug("quantifiers") << std::endl; + //it is not active if it corresponds to a rewrite rule: we will process in rewrite engine }else{ d_quant_active[n] = true; - quantActive = true; + if( !TermDb::hasInstConstAttr(n) ){ + quantActive = true; + } Debug("quantifiers") << " Active : " << n << ", no ce assigned." << std::endl; } Debug("quantifiers-relevance") << "Quantifier : " << n << std::endl; Debug("quantifiers-relevance") << " Relevance : " << d_quantEngine->getQuantifierRelevance()->getRelevance( n ) << std::endl; Debug("quantifiers") << " Relevance : " << d_quantEngine->getQuantifierRelevance()->getRelevance( n ) << std::endl; + Trace("inst-engine-debug") << "Process : " << n << " " << d_quant_active[n] << std::endl; } if( quantActive ){ bool addedLemmas = doInstantiationRound( e ); diff --git a/src/theory/quantifiers/model_builder.cpp b/src/theory/quantifiers/model_builder.cpp index 0b74cfc5e..5edf2de96 100644 --- a/src/theory/quantifiers/model_builder.cpp +++ b/src/theory/quantifiers/model_builder.cpp @@ -18,7 +18,6 @@ #include "theory/uf/theory_uf.h" #include "theory/uf/theory_uf_model.h" #include "theory/uf/theory_uf_strong_solver.h" -#include "theory/arrays/theory_arrays_model.h" #include "theory/quantifiers/first_order_model.h" #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/model_builder.h" @@ -33,6 +32,65 @@ using namespace CVC4::context; using namespace CVC4::theory; using namespace CVC4::theory::quantifiers; + +QModelBuilder::QModelBuilder( context::Context* c, QuantifiersEngine* qe ) : +TheoryEngineModelBuilder( qe->getTheoryEngine() ), d_curr_model( c, NULL ), d_qe( qe ){ + d_considerAxioms = true; +} + +bool QModelBuilder::isQuantifierActive( Node f ) { + return !f.hasAttribute(QRewriteRuleAttribute()); +} + + +bool QModelBuilder::optUseModel() { + return options::fmfModelBasedInst(); +} + +void QModelBuilder::debugModel( FirstOrderModel* fm ){ + //debug the model: cycle through all instantiations for all quantifiers, report ones that are not true + if( Trace.isOn("quant-model-warn") ){ + Trace("quant-model-warn") << "Testing quantifier instantiations..." << std::endl; + int tests = 0; + int bad = 0; + for( int i=0; i<fm->getNumAssertedQuantifiers(); i++ ){ + Node f = fm->getAssertedQuantifier( i ); + std::vector< Node > vars; + for( int j=0; j<(int)f[0].getNumChildren(); j++ ){ + vars.push_back( f[0][j] ); + } + RepSetIterator riter( d_qe, &(fm->d_rep_set) ); + if( riter.setQuantifier( f ) ){ + while( !riter.isFinished() ){ + tests++; + std::vector< Node > terms; + for( int i=0; i<riter.getNumTerms(); i++ ){ + terms.push_back( riter.getTerm( i ) ); + } + Node n = d_qe->getInstantiation( f, vars, terms ); + Node val = fm->getValue( n ); + if( val!=fm->d_true ){ + Trace("quant-model-warn") << "******* Instantiation " << n << " for " << std::endl; + Trace("quant-model-warn") << " " << f << std::endl; + Trace("quant-model-warn") << " Evaluates to " << val << std::endl; + bad++; + } + riter.increment(); + } + Trace("quant-model-warn") << "Tested " << tests << " instantiations"; + if( bad>0 ){ + Trace("quant-model-warn") << ", " << bad << " failed" << std::endl; + } + Trace("quant-model-warn") << "." << std::endl; + }else{ + Trace("quant-model-warn") << "Warning: Could not test quantifier " << f << std::endl; + } + } + } +} + + + bool TermArgBasisTrie::addTerm2( FirstOrderModel* fm, Node n, int argIndex ){ if( argIndex<(int)n.getNumChildren() ){ Node r; @@ -53,49 +111,25 @@ bool TermArgBasisTrie::addTerm2( FirstOrderModel* fm, Node n, int argIndex ){ } } -ModelEngineBuilder::ModelEngineBuilder( context::Context* c, QuantifiersEngine* qe ) : -TheoryEngineModelBuilder( qe->getTheoryEngine() ), -d_qe( qe ), d_curr_model( c, NULL ){ - d_considerAxioms = true; + +QModelBuilderIG::QModelBuilderIG( context::Context* c, QuantifiersEngine* qe ) : +QModelBuilder( c, qe ) { + } -void ModelEngineBuilder::debugModel( FirstOrderModel* fm ){ - //debug the model: cycle through all instantiations for all quantifiers, report ones that are not true - if( Trace.isOn("quant-model-warn") ){ - for( int i=0; i<fm->getNumAssertedQuantifiers(); i++ ){ - Node f = fm->getAssertedQuantifier( i ); - std::vector< Node > vars; - for( int j=0; j<(int)f[0].getNumChildren(); j++ ){ - vars.push_back( f[0][j] ); - } - RepSetIterator riter( &(fm->d_rep_set) ); - riter.setQuantifier( f ); - while( !riter.isFinished() ){ - std::vector< Node > terms; - for( int i=0; i<riter.getNumTerms(); i++ ){ - terms.push_back( riter.getTerm( i ) ); - } - Node n = d_qe->getInstantiation( f, vars, terms ); - Node val = fm->getValue( n ); - if( val!=fm->d_true ){ - Trace("quant-model-warn") << "******* Instantiation " << n << " for " << std::endl; - Trace("quant-model-warn") << " " << f << std::endl; - Trace("quant-model-warn") << " Evaluates to " << val << std::endl; - } - riter.increment(); - } - } - } +Node QModelBuilderIG::getCurrentUfModelValue( FirstOrderModel* fm, Node n, std::vector< Node > & args, bool partial ) { + return n; } -void ModelEngineBuilder::processBuildModel( TheoryModel* m, bool fullModel ) { - FirstOrderModel* fm = (FirstOrderModel*)m; +void QModelBuilderIG::processBuildModel( TheoryModel* m, bool fullModel ) { + FirstOrderModel* f = (FirstOrderModel*)m; + FirstOrderModelIG* fm = f->asFirstOrderModelIG(); if( fullModel ){ Assert( d_curr_model==fm ); //update models for( std::map< Node, uf::UfModelTree >::iterator it = fm->d_uf_model_tree.begin(); it != fm->d_uf_model_tree.end(); ++it ){ it->second.update( fm ); - Trace("model-func") << "ModelEngineBuilder: Make function value from tree " << it->first << std::endl; + Trace("model-func") << "QModelBuilder: Make function value from tree " << it->first << std::endl; //construct function values fm->d_uf_models[ it->first ] = it->second.getFunctionValue( "$x" ); } @@ -106,7 +140,6 @@ void ModelEngineBuilder::processBuildModel( TheoryModel* m, bool fullModel ) { debugModel( fm ); }else{ d_curr_model = fm; - d_addedLemmas = 0; d_didInstGen = false; //reset the internal information reset( fm ); @@ -186,12 +219,13 @@ void ModelEngineBuilder::processBuildModel( TheoryModel* m, bool fullModel ) { } } //construct the model if necessary - if( d_addedLemmas==0 || optExhInstNonInstGenQuant() ){ + if( d_addedLemmas==0 ){ //if no immediate exceptions, build the model // this model will be an approximation that will need to be tested via exhaustive instantiation Trace("model-engine-debug") << "Building model..." << std::endl; //build model for UF for( std::map< Node, uf::UfModelTree >::iterator it = fm->d_uf_model_tree.begin(); it != fm->d_uf_model_tree.end(); ++it ){ + Trace("model-engine-debug-uf") << "Building model for " << it->first << "..." << std::endl; constructModelUf( fm, it->first ); } /* @@ -211,7 +245,7 @@ void ModelEngineBuilder::processBuildModel( TheoryModel* m, bool fullModel ) { } } -int ModelEngineBuilder::initializeQuantifier( Node f, Node fp ){ +int QModelBuilderIG::initializeQuantifier( Node f, Node fp ){ if( d_quant_basis_match_added.find( f )==d_quant_basis_match_added.end() ){ //create the basis match if necessary if( d_quant_basis_match.find( f )==d_quant_basis_match.end() ){ @@ -254,17 +288,18 @@ int ModelEngineBuilder::initializeQuantifier( Node f, Node fp ){ return 0; } -void ModelEngineBuilder::analyzeModel( FirstOrderModel* fm ){ +void QModelBuilderIG::analyzeModel( FirstOrderModel* fm ){ + FirstOrderModelIG* fmig = fm->asFirstOrderModelIG(); d_uf_model_constructed.clear(); //determine if any functions are constant - for( std::map< Node, uf::UfModelTree >::iterator it = fm->d_uf_model_tree.begin(); it != fm->d_uf_model_tree.end(); ++it ){ + for( std::map< Node, uf::UfModelTree >::iterator it = fmig->d_uf_model_tree.begin(); it != fmig->d_uf_model_tree.end(); ++it ){ Node op = it->first; TermArgBasisTrie tabt; - for( size_t i=0; i<fm->d_uf_terms[op].size(); i++ ){ - Node n = fm->d_uf_terms[op][i]; + for( size_t i=0; i<fmig->d_uf_terms[op].size(); i++ ){ + Node n = fmig->d_uf_terms[op][i]; //for calculating if op is constant if( !n.getAttribute(NoMatchAttribute()) ){ - Node v = fm->getRepresentative( n ); + Node v = fmig->getRepresentative( n ); if( i==0 ){ d_uf_prefs[op].d_const_val = v; }else if( v!=d_uf_prefs[op].d_const_val ){ @@ -273,10 +308,10 @@ void ModelEngineBuilder::analyzeModel( FirstOrderModel* fm ){ } } //for calculating terms that we don't need to consider - if( !n.getAttribute(NoMatchAttribute()) || n.getAttribute(ModelBasisArgAttribute())==1 ){ + if( !n.getAttribute(NoMatchAttribute()) || n.getAttribute(ModelBasisArgAttribute())!=0 ){ if( !n.getAttribute(BasisNoMatchAttribute()) ){ //need to consider if it is not congruent modulo model basis - if( !tabt.addTerm( fm, n ) ){ + if( !tabt.addTerm( fmig, n ) ){ BasisNoMatchAttribute bnma; n.setAttribute(bnma,true); } @@ -284,10 +319,10 @@ void ModelEngineBuilder::analyzeModel( FirstOrderModel* fm ){ } } if( !d_uf_prefs[op].d_const_val.isNull() ){ - fm->d_uf_model_gen[op].setDefaultValue( d_uf_prefs[op].d_const_val ); - fm->d_uf_model_gen[op].makeModel( fm, it->second ); + fmig->d_uf_model_gen[op].setDefaultValue( d_uf_prefs[op].d_const_val ); + fmig->d_uf_model_gen[op].makeModel( fmig, it->second ); Debug("fmf-model-cons") << "Function " << op << " is the constant function "; - fm->printRepresentativeDebug( "fmf-model-cons", d_uf_prefs[op].d_const_val ); + fmig->printRepresentativeDebug( "fmf-model-cons", d_uf_prefs[op].d_const_val ); Debug("fmf-model-cons") << std::endl; d_uf_model_constructed[op] = true; }else{ @@ -296,7 +331,7 @@ void ModelEngineBuilder::analyzeModel( FirstOrderModel* fm ){ } } -bool ModelEngineBuilder::hasConstantDefinition( Node n ){ +bool QModelBuilderIG::hasConstantDefinition( Node n ){ Node lit = n.getKind()==NOT ? n[0] : n; if( lit.getKind()==APPLY_UF ){ Node op = lit.getOperator(); @@ -307,60 +342,141 @@ bool ModelEngineBuilder::hasConstantDefinition( Node n ){ return false; } -bool ModelEngineBuilder::optUseModel() { - return options::fmfModelBasedInst(); -} - -bool ModelEngineBuilder::optInstGen(){ +bool QModelBuilderIG::optInstGen(){ return options::fmfInstGen(); } -bool ModelEngineBuilder::optOneQuantPerRoundInstGen(){ +bool QModelBuilderIG::optOneQuantPerRoundInstGen(){ return options::fmfInstGenOneQuantPerRound(); } -bool ModelEngineBuilder::optExhInstNonInstGenQuant(){ - return options::fmfNewInstGen(); -} - -void ModelEngineBuilder::setEffort( int effort ){ - d_considerAxioms = effort>=1; -} - -ModelEngineBuilder::Statistics::Statistics(): - d_num_quants_init("ModelEngineBuilder::Number_Quantifiers", 0), - d_num_partial_quants_init("ModelEngineBuilder::Number_Partial_Quantifiers", 0), - d_init_inst_gen_lemmas("ModelEngineBuilder::Initialize_Inst_Gen_Lemmas", 0 ), - d_inst_gen_lemmas("ModelEngineBuilder::Inst_Gen_Lemmas", 0 ) +QModelBuilderIG::Statistics::Statistics(): + d_num_quants_init("QModelBuilderIG::Number_Quantifiers", 0), + d_num_partial_quants_init("QModelBuilderIG::Number_Partial_Quantifiers", 0), + d_init_inst_gen_lemmas("QModelBuilderIG::Initialize_Inst_Gen_Lemmas", 0 ), + d_inst_gen_lemmas("QModelBuilderIG::Inst_Gen_Lemmas", 0 ), + d_eval_formulas("QModelBuilderIG::Eval_Formulas", 0 ), + d_eval_uf_terms("QModelBuilderIG::Eval_Uf_Terms", 0 ), + d_eval_lits("QModelBuilderIG::Eval_Lits", 0 ), + d_eval_lits_unknown("QModelBuilderIG::Eval_Lits_Unknown", 0 ) { StatisticsRegistry::registerStat(&d_num_quants_init); StatisticsRegistry::registerStat(&d_num_partial_quants_init); StatisticsRegistry::registerStat(&d_init_inst_gen_lemmas); StatisticsRegistry::registerStat(&d_inst_gen_lemmas); + StatisticsRegistry::registerStat(&d_eval_formulas); + StatisticsRegistry::registerStat(&d_eval_uf_terms); + StatisticsRegistry::registerStat(&d_eval_lits); + StatisticsRegistry::registerStat(&d_eval_lits_unknown); } -ModelEngineBuilder::Statistics::~Statistics(){ +QModelBuilderIG::Statistics::~Statistics(){ StatisticsRegistry::unregisterStat(&d_num_quants_init); StatisticsRegistry::unregisterStat(&d_num_partial_quants_init); StatisticsRegistry::unregisterStat(&d_init_inst_gen_lemmas); StatisticsRegistry::unregisterStat(&d_inst_gen_lemmas); + StatisticsRegistry::unregisterStat(&d_eval_formulas); + StatisticsRegistry::unregisterStat(&d_eval_uf_terms); + StatisticsRegistry::unregisterStat(&d_eval_lits); + StatisticsRegistry::unregisterStat(&d_eval_lits_unknown); } -bool ModelEngineBuilder::isQuantifierActive( Node f ){ - return ( d_considerAxioms || !f.getAttribute(AxiomAttribute()) ) && d_quant_sat.find( f )==d_quant_sat.end(); +bool QModelBuilderIG::isQuantifierActive( Node f ){ + return !f.hasAttribute(QRewriteRuleAttribute()) && + ( d_considerAxioms || !f.getAttribute(AxiomAttribute()) ) && d_quant_sat.find( f )==d_quant_sat.end(); } -bool ModelEngineBuilder::isTermActive( Node n ){ +bool QModelBuilderIG::isTermActive( Node n ){ return !n.getAttribute(NoMatchAttribute()) || //it is not congruent to another active term - ( n.getAttribute(ModelBasisArgAttribute())==1 && !n.getAttribute(BasisNoMatchAttribute()) ); //or it has model basis arguments + ( n.getAttribute(ModelBasisArgAttribute())!=0 && !n.getAttribute(BasisNoMatchAttribute()) ); //or it has model basis arguments //and is not congruent modulo model basis //to another active term } +//do exhaustive instantiation +bool QModelBuilderIG::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ) { + if( optUseModel() ){ + + RepSetIterator riter( d_qe, &(d_qe->getModel()->d_rep_set) ); + if( riter.setQuantifier( f ) ){ + FirstOrderModelIG * fmig = (FirstOrderModelIG*)d_qe->getModel(); + Debug("inst-fmf-ei") << "Reset evaluate..." << std::endl; + fmig->resetEvaluate(); + Debug("inst-fmf-ei") << "Begin instantiation..." << std::endl; + while( !riter.isFinished() && ( d_addedLemmas==0 || !options::fmfOneInstPerRound() ) ){ + d_triedLemmas++; + for( int i=0; i<(int)riter.d_index.size(); i++ ){ + Trace("try") << i << " : " << riter.d_index[i] << " : " << riter.getTerm( i ) << std::endl; + } + int eval = 0; + int depIndex; + //see if instantiation is already true in current model + Debug("fmf-model-eval") << "Evaluating "; + riter.debugPrintSmall("fmf-model-eval"); + Debug("fmf-model-eval") << "Done calculating terms." << std::endl; + //if evaluate(...)==1, then the instantiation is already true in the model + // depIndex is the index of the least significant variable that this evaluation relies upon + depIndex = riter.getNumTerms()-1; + eval = fmig->evaluate( d_qe->getTermDatabase()->getInstConstantBody( f ), depIndex, &riter ); + if( eval==1 ){ + Debug("fmf-model-eval") << " Returned success with depIndex = " << depIndex << std::endl; + }else{ + Debug("fmf-model-eval") << " Returned " << (eval==-1 ? "failure" : "unknown") << ", depIndex = " << depIndex << std::endl; + } + if( eval==1 ){ + //instantiation is already true -> skip + riter.increment2( depIndex ); + }else{ + //instantiation was not shown to be true, construct the match + InstMatch m; + for( int i=0; i<riter.getNumTerms(); i++ ){ + m.set( d_qe, f, riter.d_index_order[i], riter.getTerm( i ) ); + } + Debug("fmf-model-eval") << "* Add instantiation " << m << std::endl; + //add as instantiation + if( d_qe->addInstantiation( f, m ) ){ + d_addedLemmas++; + //if the instantiation is show to be false, and we wish to skip multiple instantiations at once + if( eval==-1 ){ + riter.increment2( depIndex ); + }else{ + riter.increment(); + } + }else{ + Debug("fmf-model-eval") << "* Failed Add instantiation " << m << std::endl; + riter.increment(); + } + } + } + //print debugging information + if( fmig ){ + d_statistics.d_eval_formulas += fmig->d_eval_formulas; + d_statistics.d_eval_uf_terms += fmig->d_eval_uf_terms; + d_statistics.d_eval_lits += fmig->d_eval_lits; + d_statistics.d_eval_lits_unknown += fmig->d_eval_lits_unknown; + } + Trace("inst-fmf-ei") << "Finished: " << std::endl; + Trace("inst-fmf-ei") << " Inst Tried: " << d_triedLemmas << std::endl; + Trace("inst-fmf-ei") << " Inst Added: " << d_addedLemmas << std::endl; + if( d_addedLemmas>1000 ){ + Trace("model-engine-warn") << "WARNING: many instantiations produced for " << f << ": " << std::endl; + Trace("model-engine-warn") << " Inst Tried: " << d_triedLemmas << std::endl; + Trace("model-engine-warn") << " Inst Added: " << d_addedLemmas << std::endl; + Trace("model-engine-warn") << std::endl; + } + } + //if the iterator is incomplete, we will return unknown instead of sat if no instantiations are added this round + d_incomplete_check = riter.d_incomplete; + return true; + }else{ + return false; + } +} + -void ModelEngineBuilderDefault::reset( FirstOrderModel* fm ){ +void QModelBuilderDefault::reset( FirstOrderModel* fm ){ d_quant_selection_lit.clear(); d_quant_selection_lit_candidates.clear(); d_quant_selection_lit_terms.clear(); @@ -369,7 +485,7 @@ void ModelEngineBuilderDefault::reset( FirstOrderModel* fm ){ } -int ModelEngineBuilderDefault::getSelectionScore( std::vector< Node >& uf_terms ) { +int QModelBuilderDefault::getSelectionScore( std::vector< Node >& uf_terms ) { /* size_t maxChildren = 0; for( size_t i=0; i<uf_terms.size(); i++ ){ @@ -383,7 +499,8 @@ int ModelEngineBuilderDefault::getSelectionScore( std::vector< Node >& uf_terms return 0; } -void ModelEngineBuilderDefault::analyzeQuantifier( FirstOrderModel* fm, Node f ){ +void QModelBuilderDefault::analyzeQuantifier( FirstOrderModel* fm, Node f ){ + FirstOrderModelIG* fmig = fm->asFirstOrderModelIG(); Debug("fmf-model-prefs") << "Analyze quantifier " << f << std::endl; //the pro/con preferences for this quantifier std::vector< Node > pro_con[2]; @@ -408,7 +525,7 @@ void ModelEngineBuilderDefault::analyzeQuantifier( FirstOrderModel* fm, Node f ) // constant definitions. bool isConst = true; std::vector< Node > uf_terms; - if( n.hasAttribute(InstConstantAttribute()) ){ + if( TermDb::hasInstConstAttr(n) ){ isConst = false; if( gn.getKind()==APPLY_UF ){ uf_terms.push_back( gn ); @@ -416,9 +533,9 @@ void ModelEngineBuilderDefault::analyzeQuantifier( FirstOrderModel* fm, Node f ) }else if( gn.getKind()==EQUAL ){ isConst = true; for( int j=0; j<2; j++ ){ - if( n[j].hasAttribute(InstConstantAttribute()) ){ + if( TermDb::hasInstConstAttr(n[j]) ){ if( n[j].getKind()==APPLY_UF && - fm->d_uf_model_tree.find( gn[j].getOperator() )!=fm->d_uf_model_tree.end() ){ + fmig->d_uf_model_tree.find( gn[j].getOperator() )!=fmig->d_uf_model_tree.end() ){ uf_terms.push_back( gn[j] ); isConst = isConst && hasConstantDefinition( gn[j] ); }else{ @@ -506,14 +623,14 @@ void ModelEngineBuilderDefault::analyzeQuantifier( FirstOrderModel* fm, Node f ) for( int k=0; k<2; k++ ){ for( int j=0; j<(int)pro_con[k].size(); j++ ){ Node op = pro_con[k][j].getOperator(); - Node r = fm->getRepresentative( pro_con[k][j] ); + Node r = fmig->getRepresentative( pro_con[k][j] ); d_uf_prefs[op].setValuePreference( f, pro_con[k][j], r, k==0 ); } } } } -int ModelEngineBuilderDefault::doInstGen( FirstOrderModel* fm, Node f ){ +int QModelBuilderDefault::doInstGen( FirstOrderModel* fm, Node f ){ int addedLemmas = 0; //we wish to add all known exceptions to our selection literal for f. this will help to refine our current model. //This step is advantageous over exhaustive instantiation, since we are adding instantiations that involve model basis terms, @@ -523,17 +640,16 @@ int ModelEngineBuilderDefault::doInstGen( FirstOrderModel* fm, Node f ){ for( size_t i=0; i<d_quant_selection_lit_candidates[f].size(); i++ ){ bool phase = d_quant_selection_lit_candidates[f][i].getKind()!=NOT; Node lit = d_quant_selection_lit_candidates[f][i].getKind()==NOT ? d_quant_selection_lit_candidates[f][i][0] : d_quant_selection_lit_candidates[f][i]; - Assert( lit.hasAttribute(InstConstantAttribute()) ); + Assert( TermDb::hasInstConstAttr(lit) ); std::vector< Node > tr_terms; if( lit.getKind()==APPLY_UF ){ //only match predicates that are contrary to this one, use literal matching Node eq = NodeManager::currentNM()->mkNode( IFF, lit, !phase ? fm->d_true : fm->d_false ); - d_qe->getTermDatabase()->setInstantiationConstantAttr( eq, f ); tr_terms.push_back( eq ); }else if( lit.getKind()==EQUAL ){ //collect trigger terms for( int j=0; j<2; j++ ){ - if( lit[j].hasAttribute(InstConstantAttribute()) ){ + if( TermDb::hasInstConstAttr(lit[j]) ){ if( lit[j].getKind()==APPLY_UF ){ tr_terms.push_back( lit[j] ); }else{ @@ -564,7 +680,8 @@ int ModelEngineBuilderDefault::doInstGen( FirstOrderModel* fm, Node f ){ return addedLemmas; } -void ModelEngineBuilderDefault::constructModelUf( FirstOrderModel* fm, Node op ){ +void QModelBuilderDefault::constructModelUf( FirstOrderModel* fm, Node op ){ + FirstOrderModelIG* fmig = fm->asFirstOrderModelIG(); if( optReconsiderFuncConstants() ){ //reconsider constant functions that weren't necessary if( d_uf_model_constructed[op] ){ @@ -573,8 +690,8 @@ void ModelEngineBuilderDefault::constructModelUf( FirstOrderModel* fm, Node op ) Node v = d_uf_prefs[op].d_const_val; if( d_uf_prefs[op].d_value_pro_con[0][v].empty() ){ Debug("fmf-model-cons-debug") << "Consider changing the default value for " << op << std::endl; - fm->d_uf_model_tree[op].clear(); - fm->d_uf_model_gen[op].clear(); + fmig->d_uf_model_tree[op].clear(); + fmig->d_uf_model_gen[op].clear(); d_uf_model_constructed[op] = false; } } @@ -586,20 +703,20 @@ void ModelEngineBuilderDefault::constructModelUf( FirstOrderModel* fm, Node op ) Node defaultTerm = d_qe->getTermDatabase()->getModelBasisOpTerm( op ); Trace("fmf-model-cons") << "Construct model for " << op << "..." << std::endl; //set the values in the model - for( size_t i=0; i<fm->d_uf_terms[op].size(); i++ ){ - Node n = fm->d_uf_terms[op][i]; + for( size_t i=0; i<fmig->d_uf_terms[op].size(); i++ ){ + Node n = fmig->d_uf_terms[op][i]; if( isTermActive( n ) ){ - Node v = fm->getRepresentative( n ); - Trace("fmf-model-cons") << "Set term " << n << " : " << fm->d_rep_set.getIndexFor( v ) << " " << v << std::endl; + Node v = fmig->getRepresentative( n ); + Trace("fmf-model-cons") << "Set term " << n << " : " << fmig->d_rep_set.getIndexFor( v ) << " " << v << std::endl; //if this assertion did not help the model, just consider it ground //set n = v in the model tree //set it as ground value - fm->d_uf_model_gen[op].setValue( fm, n, v ); - if( fm->d_uf_model_gen[op].optUsePartialDefaults() ){ + fmig->d_uf_model_gen[op].setValue( fm, n, v ); + if( fmig->d_uf_model_gen[op].optUsePartialDefaults() ){ //also set as default value if necessary - if( n.hasAttribute(ModelBasisArgAttribute()) && n.getAttribute(ModelBasisArgAttribute())==1 ){ + if( n.hasAttribute(ModelBasisArgAttribute()) && n.getAttribute(ModelBasisArgAttribute())!=0 ){ Trace("fmf-model-cons") << " Set as default." << std::endl; - fm->d_uf_model_gen[op].setValue( fm, n, v, false ); + fmig->d_uf_model_gen[op].setValue( fm, n, v, false ); if( n==defaultTerm ){ //incidentally already set, we will not need to find a default value setDefaultVal = false; @@ -607,7 +724,7 @@ void ModelEngineBuilderDefault::constructModelUf( FirstOrderModel* fm, Node op ) } }else{ if( n==defaultTerm ){ - fm->d_uf_model_gen[op].setValue( fm, n, v, false ); + fmig->d_uf_model_gen[op].setValue( fm, n, v, false ); //incidentally already set, we will not need to find a default value setDefaultVal = false; } @@ -619,12 +736,19 @@ void ModelEngineBuilderDefault::constructModelUf( FirstOrderModel* fm, Node op ) Trace("fmf-model-cons") << " Choose default value..." << std::endl; //chose defaultVal based on heuristic, currently the best ratio of "pro" responses Node defaultVal = d_uf_prefs[op].getBestDefaultValue( defaultTerm, fm ); + if( defaultVal.isNull() ){ + if (!fmig->d_rep_set.hasType(defaultTerm.getType())) { + Node mbt = d_qe->getTermDatabase()->getModelBasisTerm(defaultTerm.getType()); + fmig->d_rep_set.d_type_reps[defaultTerm.getType()].push_back(mbt); + } + defaultVal = fmig->d_rep_set.d_type_reps[defaultTerm.getType()][0]; + } Assert( !defaultVal.isNull() ); - Trace("fmf-model-cons") << "Set default term : " << fm->d_rep_set.getIndexFor( defaultVal ) << std::endl; - fm->d_uf_model_gen[op].setValue( fm, defaultTerm, defaultVal, false ); + Trace("fmf-model-cons") << "Set default term : " << fmig->d_rep_set.getIndexFor( defaultVal ) << std::endl; + fmig->d_uf_model_gen[op].setValue( fm, defaultTerm, defaultVal, false ); } Debug("fmf-model-cons") << " Making model..."; - fm->d_uf_model_gen[op].makeModel( fm, fm->d_uf_model_tree[op] ); + fmig->d_uf_model_gen[op].makeModel( fm, fmig->d_uf_model_tree[op] ); d_uf_model_constructed[op] = true; Debug("fmf-model-cons") << " Finished constructing model for " << op << "." << std::endl; } @@ -635,7 +759,7 @@ void ModelEngineBuilderDefault::constructModelUf( FirstOrderModel* fm, Node op ) ////////////////////// Inst-Gen style Model Builder /////////// -void ModelEngineBuilderInstGen::reset( FirstOrderModel* fm ){ +void QModelBuilderInstGen::reset( FirstOrderModel* fm ){ //for new inst gen d_quant_selection_formula.clear(); d_term_selected.clear(); @@ -643,15 +767,15 @@ void ModelEngineBuilderInstGen::reset( FirstOrderModel* fm ){ //d_sub_quant_inst_trie.clear();//* } -int ModelEngineBuilderInstGen::initializeQuantifier( Node f, Node fp ){ - int addedLemmas = ModelEngineBuilder::initializeQuantifier( f, fp ); +int QModelBuilderInstGen::initializeQuantifier( Node f, Node fp ){ + int addedLemmas = QModelBuilderIG::initializeQuantifier( f, fp ); for( size_t i=0; i<d_sub_quants[f].size(); i++ ){ addedLemmas += initializeQuantifier( d_sub_quants[f][i], fp ); } return addedLemmas; } -void ModelEngineBuilderInstGen::analyzeQuantifier( FirstOrderModel* fm, Node f ){ +void QModelBuilderInstGen::analyzeQuantifier( FirstOrderModel* fm, Node f ){ //Node fp = getParentQuantifier( f );//* //bool quantRedundant = ( f!=fp && d_sub_quant_inst_trie[fp].addInstMatch( d_qe, fp, d_sub_quant_inst[ f ], true ) ); //if( f==fp || d_sub_quant_inst_trie[fp].addInstMatch( d_qe, fp, d_sub_quant_inst[ f ], true ) ){//* @@ -662,7 +786,6 @@ void ModelEngineBuilderInstGen::analyzeQuantifier( FirstOrderModel* fm, Node f ) //if( !s.isNull() ){ // s = Rewriter::rewrite( s ); //} - d_qe->getTermDatabase()->setInstantiationConstantAttr( s, f ); Trace("sel-form-debug") << "Selection formula " << f << std::endl; Trace("sel-form-debug") << " " << s << std::endl; if( !s.isNull() ){ @@ -685,7 +808,7 @@ void ModelEngineBuilderInstGen::analyzeQuantifier( FirstOrderModel* fm, Node f ) } -int ModelEngineBuilderInstGen::doInstGen( FirstOrderModel* fm, Node f ){ +int QModelBuilderInstGen::doInstGen( FirstOrderModel* fm, Node f ){ int addedLemmas = 0; if( d_quant_sat.find( f )==d_quant_sat.end() ){ Node fp = d_sub_quant_parent.find( f )==d_sub_quant_parent.end() ? f : d_sub_quant_parent[f]; @@ -802,7 +925,7 @@ Node mkAndSelectionFormula( Node n1, Node n2 ){ //if possible, returns a formula n' such that n' => ( n <=> polarity ), and n' is true in the current context, // and NULL otherwise -Node ModelEngineBuilderInstGen::getSelectionFormula( Node fn, Node n, bool polarity, int useOption ){ +Node QModelBuilderInstGen::getSelectionFormula( Node fn, Node n, bool polarity, int useOption ){ Trace("sel-form-debug") << "Looking for selection formula " << n << " " << polarity << std::endl; Node ret; if( n.getKind()==NOT ){ @@ -911,7 +1034,7 @@ Node ModelEngineBuilderInstGen::getSelectionFormula( Node fn, Node n, bool polar return ret; } -int ModelEngineBuilderInstGen::getSelectionFormulaScore( Node fn ){ +int QModelBuilderInstGen::getSelectionFormulaScore( Node fn ){ if( fn.getType().isBoolean() ){ if( fn.getKind()==APPLY_UF ){ Node op = fn.getOperator(); @@ -929,13 +1052,13 @@ int ModelEngineBuilderInstGen::getSelectionFormulaScore( Node fn ){ } } -void ModelEngineBuilderInstGen::setSelectedTerms( Node s ){ +void QModelBuilderInstGen::setSelectedTerms( Node s ){ //if it is apply uf and has model basis arguments, then mark term as being "selected" if( s.getKind()==APPLY_UF ){ Assert( s.hasAttribute(ModelBasisArgAttribute()) ); if( !s.hasAttribute(ModelBasisArgAttribute()) ) std::cout << "no mba!! " << s << std::endl; - if( s.getAttribute(ModelBasisArgAttribute())==1 ){ + if( s.getAttribute(ModelBasisArgAttribute())!=0 ){ d_term_selected[ s ] = true; Trace("sel-form-term") << " " << s << " is a selected term." << std::endl; } @@ -945,7 +1068,7 @@ void ModelEngineBuilderInstGen::setSelectedTerms( Node s ){ } } -bool ModelEngineBuilderInstGen::isUsableSelectionLiteral( Node n, int useOption ){ +bool QModelBuilderInstGen::isUsableSelectionLiteral( Node n, int useOption ){ if( n.getKind()==FORALL ){ return false; }else if( n.getKind()!=APPLY_UF ){ @@ -964,7 +1087,7 @@ bool ModelEngineBuilderInstGen::isUsableSelectionLiteral( Node n, int useOption return true; } -void ModelEngineBuilderInstGen::getParentQuantifierMatch( InstMatch& mp, Node fp, InstMatch& m, Node f ){ +void QModelBuilderInstGen::getParentQuantifierMatch( InstMatch& mp, Node fp, InstMatch& m, Node f ){ if( f!=fp ){ //std::cout << "gpqm " << fp << " " << f << " " << m << std::endl; //std::cout << " " << fp[0].getNumChildren() << " " << f[0].getNumChildren() << std::endl; @@ -988,20 +1111,21 @@ void ModelEngineBuilderInstGen::getParentQuantifierMatch( InstMatch& mp, Node fp } } -void ModelEngineBuilderInstGen::constructModelUf( FirstOrderModel* fm, Node op ){ +void QModelBuilderInstGen::constructModelUf( FirstOrderModel* fm, Node op ){ + FirstOrderModelIG* fmig = fm->asFirstOrderModelIG(); bool setDefaultVal = true; Node defaultTerm = d_qe->getTermDatabase()->getModelBasisOpTerm( op ); //set the values in the model - for( size_t i=0; i<fm->d_uf_terms[op].size(); i++ ){ - Node n = fm->d_uf_terms[op][i]; + for( size_t i=0; i<fmig->d_uf_terms[op].size(); i++ ){ + Node n = fmig->d_uf_terms[op][i]; if( isTermActive( n ) ){ - Node v = fm->getRepresentative( n ); - fm->d_uf_model_gen[op].setValue( fm, n, v ); + Node v = fmig->getRepresentative( n ); + fmig->d_uf_model_gen[op].setValue( fm, n, v ); } //also possible set as default if( d_term_selected.find( n )!=d_term_selected.end() || n==defaultTerm ){ - Node v = fm->getRepresentative( n ); - fm->d_uf_model_gen[op].setValue( fm, n, v, false ); + Node v = fmig->getRepresentative( n ); + fmig->d_uf_model_gen[op].setValue( fm, n, v, false ); if( n==defaultTerm ){ setDefaultVal = false; } @@ -1010,12 +1134,12 @@ void ModelEngineBuilderInstGen::constructModelUf( FirstOrderModel* fm, Node op ) //set the overall default value if not set already (is this necessary??) if( setDefaultVal ){ Node defaultVal = d_uf_prefs[op].getBestDefaultValue( defaultTerm, fm ); - fm->d_uf_model_gen[op].setValue( fm, defaultTerm, defaultVal, false ); + fmig->d_uf_model_gen[op].setValue( fm, defaultTerm, defaultVal, false ); } - fm->d_uf_model_gen[op].makeModel( fm, fm->d_uf_model_tree[op] ); + fmig->d_uf_model_gen[op].makeModel( fm, fmig->d_uf_model_tree[op] ); d_uf_model_constructed[op] = true; } -bool ModelEngineBuilderInstGen::existsInstantiation( Node f, InstMatch& m, bool modEq, bool modInst ){ +bool QModelBuilderInstGen::existsInstantiation( Node f, InstMatch& m, bool modEq, bool modInst ){ return d_child_sub_quant_inst_trie[f].existsInstMatch( d_qe, f, m, modEq, true ); -}
\ No newline at end of file +} diff --git a/src/theory/quantifiers/model_builder.h b/src/theory/quantifiers/model_builder.h index 31448acee..b96c58767 100644 --- a/src/theory/quantifiers/model_builder.h +++ b/src/theory/quantifiers/model_builder.h @@ -25,6 +25,42 @@ namespace CVC4 { namespace theory { namespace quantifiers { + +class QModelBuilder : public TheoryEngineModelBuilder +{ +protected: + //the model we are working with + context::CDO< FirstOrderModel* > d_curr_model; + //quantifiers engine + QuantifiersEngine* d_qe; +public: + QModelBuilder( context::Context* c, QuantifiersEngine* qe ); + virtual ~QModelBuilder(){} + // is quantifier active? + virtual bool isQuantifierActive( Node f ); + //do exhaustive instantiation + virtual bool doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ) { return false; } + //whether to construct model + virtual bool optUseModel(); + //whether to construct model at fullModel = true + virtual bool optBuildAtFullModel() { return false; } + //consider axioms + bool d_considerAxioms; + /** number of lemmas generated while building model */ + //is the exhaustive instantiation incomplete? + bool d_incomplete_check; + int d_addedLemmas; + int d_triedLemmas; + /** exist instantiation ? */ + virtual bool existsInstantiation( Node f, InstMatch& m, bool modEq = true, bool modInst = false ) { return false; } + //debug model + void debugModel( FirstOrderModel* fm ); +}; + + + + + /** Attribute true for nodes that should not be used when considered for inst-gen basis */ struct BasisNoMatchAttributeId {}; /** use the special for boolean flag */ @@ -47,17 +83,13 @@ public: /** model builder class * This class is capable of building candidate models based on the current quantified formulas * that are asserted. Use: - * (1) call ModelEngineBuilder::buildModel( m, false );, where m is a FirstOrderModel + * (1) call QModelBuilder::buildModel( m, false );, where m is a FirstOrderModel * (2) if candidate model is determined to be a real model, - then call ModelEngineBuilder::buildModel( m, true ); + then call QModelBuilder::buildModel( m, true ); */ -class ModelEngineBuilder : public TheoryEngineModelBuilder +class QModelBuilderIG : public QModelBuilder { protected: - //quantifiers engine - QuantifiersEngine* d_qe; - //the model we are working with - context::CDO< FirstOrderModel* > d_curr_model; //map from operators to model preference data std::map< Node, uf::UfModelPreferenceData > d_uf_prefs; //built model uf @@ -66,6 +98,8 @@ protected: bool d_didInstGen; /** process build model */ virtual void processBuildModel( TheoryModel* m, bool fullModel ); + /** get current model value */ + Node getCurrentUfModelValue( FirstOrderModel* fm, Node n, std::vector< Node > & args, bool partial ); protected: //reset virtual void reset( FirstOrderModel* fm ) = 0; @@ -90,25 +124,13 @@ protected: //helper functions /** term has constant definition */ bool hasConstantDefinition( Node n ); public: - ModelEngineBuilder( context::Context* c, QuantifiersEngine* qe ); - virtual ~ModelEngineBuilder(){} - /** number of lemmas generated while building model */ - int d_addedLemmas; - //consider axioms - bool d_considerAxioms; - // set effort - void setEffort( int effort ); - //debug model - void debugModel( FirstOrderModel* fm ); + QModelBuilderIG( context::Context* c, QuantifiersEngine* qe ); + virtual ~QModelBuilderIG(){} public: - //whether to construct model - virtual bool optUseModel(); //whether to add inst-gen lemmas virtual bool optInstGen(); //whether to only consider only quantifier per round of inst-gen virtual bool optOneQuantPerRoundInstGen(); - //whether we should exhaustively instantiate quantifiers where inst-gen is not working - virtual bool optExhInstNonInstGenQuant(); /** statistics class */ class Statistics { public: @@ -116,22 +138,26 @@ public: IntStat d_num_partial_quants_init; IntStat d_init_inst_gen_lemmas; IntStat d_inst_gen_lemmas; + IntStat d_eval_formulas; + IntStat d_eval_uf_terms; + IntStat d_eval_lits; + IntStat d_eval_lits_unknown; Statistics(); ~Statistics(); }; Statistics d_statistics; - // is quantifier active? - bool isQuantifierActive( Node f ); // is term active bool isTermActive( Node n ); // is term selected virtual bool isTermSelected( Node n ) { return false; } - /** exist instantiation ? */ - virtual bool existsInstantiation( Node f, InstMatch& m, bool modEq = true, bool modInst = false ) { return false; } /** quantifier has inst-gen definition */ virtual bool hasInstGen( Node f ) = 0; /** did inst gen this round? */ bool didInstGen() { return d_didInstGen; } + // is quantifier active? + bool isQuantifierActive( Node f ); + //do exhaustive instantiation + bool doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ); //temporary stats int d_numQuantSat; @@ -140,10 +166,10 @@ public: int d_numQuantNoSelForm; //temporary stat int d_instGenMatches; -};/* class ModelEngineBuilder */ +};/* class QModelBuilder */ -class ModelEngineBuilderDefault : public ModelEngineBuilder +class QModelBuilderDefault : public QModelBuilderIG { private: ///information for (old) InstGen //map from quantifiers to their selection literals @@ -167,15 +193,15 @@ protected: //theory-specific build models void constructModelUf( FirstOrderModel* fm, Node op ); public: - ModelEngineBuilderDefault( context::Context* c, QuantifiersEngine* qe ) : ModelEngineBuilder( c, qe ){} - ~ModelEngineBuilderDefault(){} + QModelBuilderDefault( context::Context* c, QuantifiersEngine* qe ) : QModelBuilderIG( c, qe ){} + ~QModelBuilderDefault(){} //options bool optReconsiderFuncConstants() { return true; } //has inst gen bool hasInstGen( Node f ) { return !d_quant_selection_lit[f].isNull(); } }; -class ModelEngineBuilderInstGen : public ModelEngineBuilder +class QModelBuilderInstGen : public QModelBuilderIG { private: ///information for (new) InstGen //map from quantifiers to their selection formulas @@ -217,8 +243,8 @@ private: //get parent quantifier Node getParentQuantifier( Node f ) { return d_sub_quant_parent.find( f )==d_sub_quant_parent.end() ? f : d_sub_quant_parent[f]; } public: - ModelEngineBuilderInstGen( context::Context* c, QuantifiersEngine* qe ) : ModelEngineBuilder( c, qe ){} - ~ModelEngineBuilderInstGen(){} + QModelBuilderInstGen( context::Context* c, QuantifiersEngine* qe ) : QModelBuilderIG( c, qe ){} + ~QModelBuilderInstGen(){} // is term selected bool isTermSelected( Node n ) { return d_term_selected.find( n )!=d_term_selected.end(); } /** exist instantiation ? */ diff --git a/src/theory/quantifiers/model_engine.cpp b/src/theory/quantifiers/model_engine.cpp index a69b278c0..cb8cb8154 100644 --- a/src/theory/quantifiers/model_engine.cpp +++ b/src/theory/quantifiers/model_engine.cpp @@ -18,13 +18,10 @@ #include "theory/uf/theory_uf.h" #include "theory/uf/theory_uf_strong_solver.h" #include "theory/quantifiers/options.h" -#include "theory/arrays/theory_arrays_model.h" #include "theory/quantifiers/first_order_model.h" #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/quantifiers_attributes.h" -#define EVAL_FAIL_SKIP_MULTIPLE - using namespace std; using namespace CVC4; using namespace CVC4::kind; @@ -35,15 +32,21 @@ using namespace CVC4::theory::inst; //Model Engine constructor ModelEngine::ModelEngine( context::Context* c, QuantifiersEngine* qe ) : -QuantifiersModule( qe ), -d_rel_domain( qe, qe->getModel() ){ +QuantifiersModule( qe ){ - if( options::fmfNewInstGen() ){ - d_builder = new ModelEngineBuilderInstGen( c, qe ); + if( options::fmfFullModelCheck() ){ + d_builder = new fmcheck::FullModelChecker( c, qe ); + }else if( options::fmfNewInstGen() ){ + d_builder = new QModelBuilderInstGen( c, qe ); }else{ - d_builder = new ModelEngineBuilderDefault( c, qe ); + d_builder = new QModelBuilderDefault( c, qe ); } + if( options::fmfRelevantDomain() ){ + d_rel_dom = new RelevantDomain( qe, qe->getModel() ); + }else{ + d_rel_dom = NULL; + } } void ModelEngine::check( Theory::Effort e ){ @@ -57,6 +60,7 @@ void ModelEngine::check( Theory::Effort e ){ clSet = double(clock())/double(CLOCKS_PER_SEC); } ++(d_statistics.d_inst_rounds); + bool buildAtFullModel = d_builder->optBuildAtFullModel(); //two effort levels: first try exhaustive instantiation without axioms, then with. int startEffort = ( !fm->isAxiomAsserted() || options::axiomInstMode()==AXIOM_INST_MODE_DEFAULT ) ? 1 : 0; for( int effort=startEffort; effort<2; effort++ ){ @@ -66,8 +70,9 @@ void ModelEngine::check( Theory::Effort e ){ Trace("model-engine") << "---Model Engine Round---" << std::endl; //initialize the model Trace("model-engine-debug") << "Build model..." << std::endl; - d_builder->setEffort( effort ); - d_builder->buildModel( fm, false ); + d_builder->d_considerAxioms = effort>=1; + d_builder->d_addedLemmas = 0; + d_builder->buildModel( fm, buildAtFullModel ); addedLemmas += (int)d_builder->d_addedLemmas; //if builder has lemmas, add and return if( addedLemmas==0 ){ @@ -81,11 +86,7 @@ void ModelEngine::check( Theory::Effort e ){ Debug("fmf-model-complete") << std::endl; debugPrint("fmf-model-complete"); //successfully built an acceptable model, now check it - addedLemmas += checkModel( check_model_full ); - }else if( d_builder->didInstGen() && d_builder->optExhInstNonInstGenQuant() ){ - Trace("model-engine-debug") << "Check model for non-inst gen quantifiers..." << std::endl; - //check quantifiers that inst-gen didn't apply to - addedLemmas += checkModel( check_model_no_inst_gen ); + addedLemmas += checkModel(); } } if( addedLemmas==0 ){ @@ -108,7 +109,7 @@ void ModelEngine::check( Theory::Effort e ){ //CVC4 will answer SAT or unknown Trace("fmf-consistent") << std::endl; debugPrint("fmf-consistent"); - if( options::produceModels() ){ + if( options::produceModels() && !buildAtFullModel ){ // finish building the model d_builder->buildModel( fm, true ); } @@ -131,28 +132,12 @@ void ModelEngine::assertNode( Node f ){ } -bool ModelEngine::optOneInstPerQuantRound(){ - return options::fmfOneInstPerRound(); -} - -bool ModelEngine::optUseRelevantDomain(){ - return options::fmfRelevantDomain(); -} - bool ModelEngine::optOneQuantPerRound(){ return options::fmfOneQuantPerRound(); } -bool ModelEngine::optExhInstEvalSkipMultiple(){ -#ifdef EVAL_FAIL_SKIP_MULTIPLE - return true; -#else - return false; -#endif -} -int ModelEngine::checkModel( int checkOption ){ - int addedLemmas = 0; +int ModelEngine::checkModel(){ FirstOrderModel* fm = d_quantEngine->getModel(); //for debugging if( Trace.isOn("model-engine") || Trace.isOn("model-engine-debug") ){ @@ -161,27 +146,28 @@ int ModelEngine::checkModel( int checkOption ){ if( it->first.isSort() ){ Trace("model-engine") << "Cardinality( " << it->first << " )" << " = " << it->second.size() << std::endl; Trace("model-engine-debug") << " "; + Node mbt = d_quantEngine->getTermDatabase()->getModelBasisTerm(it->first); for( size_t i=0; i<it->second.size(); i++ ){ //Trace("model-engine-debug") << it->second[i] << " "; Node r = ((EqualityQueryQuantifiersEngine*)d_quantEngine->getEqualityQuery())->getRepresentative( it->second[i] ); Trace("model-engine-debug") << r << " "; } Trace("model-engine-debug") << std::endl; + Trace("model-engine-debug") << " Model basis term : " << mbt << std::endl; } } } - //compute the relevant domain if necessary - if( optUseRelevantDomain() ){ - d_rel_domain.compute(); + //relevant domain? + if( d_rel_dom ){ + d_rel_dom->compute(); } + d_triedLemmas = 0; - d_testLemmas = 0; - d_relevantLemmas = 0; + d_addedLemmas = 0; d_totalLemmas = 0; - Trace("model-engine-debug") << "Do exhaustive instantiation..." << std::endl; + //for statistics for( int i=0; i<fm->getNumAssertedQuantifiers(); i++ ){ Node f = fm->getAssertedQuantifier( i ); - //keep track of total instantiations for statistics int totalInst = 1; for( size_t i=0; i<f[0].getNumChildren(); i++ ){ TypeNode tn = f[0][i].getType(); @@ -190,133 +176,85 @@ int ModelEngine::checkModel( int checkOption ){ } } d_totalLemmas += totalInst; - //determine if we should check this quantifiers - bool checkQuant = false; - if( checkOption==check_model_full ){ - checkQuant = d_builder->isQuantifierActive( f ); - }else if( checkOption==check_model_no_inst_gen ){ - checkQuant = !d_builder->hasInstGen( f ); - } - //if we need to consider this quantifier on this iteration - if( checkQuant ){ - addedLemmas += exhaustiveInstantiate( f, optUseRelevantDomain() ); - if( Trace.isOn("model-engine-warn") ){ - if( addedLemmas>10000 ){ - Debug("fmf-exit") << std::endl; - debugPrint("fmf-exit"); - exit( 0 ); + } + + Trace("model-engine-debug") << "Do exhaustive instantiation..." << std::endl; + int e_max = options::fmfFullModelCheck() && options::fmfModelBasedInst() ? 2 : 1; + for( int e=0; e<e_max; e++) { + if (d_addedLemmas==0) { + for( int i=0; i<fm->getNumAssertedQuantifiers(); i++ ){ + Node f = fm->getAssertedQuantifier( i ); + //determine if we should check this quantifier + if( d_builder->isQuantifierActive( f ) ){ + exhaustiveInstantiate( f, e ); + if( Trace.isOn("model-engine-warn") ){ + if( d_addedLemmas>10000 ){ + Debug("fmf-exit") << std::endl; + debugPrint("fmf-exit"); + exit( 0 ); + } + } + if( optOneQuantPerRound() && d_addedLemmas>0 ){ + break; + } } } - if( optOneQuantPerRound() && addedLemmas>0 ){ - break; - } } } //print debug information if( Trace.isOn("model-engine") ){ Trace("model-engine") << "Instantiate axioms : " << ( d_builder->d_considerAxioms ? "yes" : "no" ) << std::endl; - Trace("model-engine") << "Added Lemmas = " << addedLemmas << " / " << d_triedLemmas << " / "; - Trace("model-engine") << d_testLemmas << " / " << d_relevantLemmas << " / " << d_totalLemmas << std::endl; + Trace("model-engine") << "Added Lemmas = " << d_addedLemmas << " / " << d_triedLemmas << " / "; + Trace("model-engine") << d_totalLemmas << std::endl; } - d_statistics.d_exh_inst_lemmas += addedLemmas; - return addedLemmas; + d_statistics.d_exh_inst_lemmas += d_addedLemmas; + return d_addedLemmas; } -int ModelEngine::exhaustiveInstantiate( Node f, bool useRelInstDomain ){ - int addedLemmas = 0; - Trace("inst-fmf-ei") << "Exhaustive instantiate " << f << "..." << std::endl; - Debug("inst-fmf-ei") << " Instantiation Constants: "; - for( size_t i=0; i<f[0].getNumChildren(); i++ ){ - Debug("inst-fmf-ei") << d_quantEngine->getTermDatabase()->getInstantiationConstant( f, i ) << " "; - } - Debug("inst-fmf-ei") << std::endl; - - //create a rep set iterator and iterate over the (relevant) domain of the quantifier - RepSetIterator riter( &(d_quantEngine->getModel()->d_rep_set) ); - if( riter.setQuantifier( f ) ){ - //set the domain for the iterator (the sufficient set of instantiations to try) - if( useRelInstDomain ){ - riter.setDomain( d_rel_domain.d_quant_inst_domain[f] ); +void ModelEngine::exhaustiveInstantiate( Node f, int effort ){ + //first check if the builder can do the exhaustive instantiation + d_builder->d_triedLemmas = 0; + d_builder->d_addedLemmas = 0; + d_builder->d_incomplete_check = false; + if( d_builder->doExhaustiveInstantiation( d_quantEngine->getModel(), f, effort ) ){ + d_triedLemmas += d_builder->d_triedLemmas; + d_addedLemmas += d_builder->d_addedLemmas; + d_incomplete_check = d_incomplete_check || d_builder->d_incomplete_check; + }else{ + Trace("inst-fmf-ei") << "Exhaustive instantiate " << f << ", effort = " << effort << "..." << std::endl; + Debug("inst-fmf-ei") << " Instantiation Constants: "; + for( size_t i=0; i<f[0].getNumChildren(); i++ ){ + Debug("inst-fmf-ei") << d_quantEngine->getTermDatabase()->getInstantiationConstant( f, i ) << " "; } - d_quantEngine->getModel()->resetEvaluate(); - int tests = 0; - int triedLemmas = 0; - while( !riter.isFinished() && ( addedLemmas==0 || !optOneInstPerQuantRound() ) ){ - d_testLemmas++; - int eval = 0; - int depIndex; - if( d_builder->optUseModel() ){ - //see if instantiation is already true in current model - Debug("fmf-model-eval") << "Evaluating "; - riter.debugPrintSmall("fmf-model-eval"); - Debug("fmf-model-eval") << "Done calculating terms." << std::endl; - tests++; - //if evaluate(...)==1, then the instantiation is already true in the model - // depIndex is the index of the least significant variable that this evaluation relies upon - depIndex = riter.getNumTerms()-1; - eval = d_quantEngine->getModel()->evaluate( d_quantEngine->getTermDatabase()->getInstConstantBody( f ), depIndex, &riter ); - if( eval==1 ){ - Debug("fmf-model-eval") << " Returned success with depIndex = " << depIndex << std::endl; - }else{ - Debug("fmf-model-eval") << " Returned " << (eval==-1 ? "failure" : "unknown") << ", depIndex = " << depIndex << std::endl; - } - } - if( eval==1 ){ - //instantiation is already true -> skip - riter.increment2( depIndex ); - }else{ + Debug("inst-fmf-ei") << std::endl; + //create a rep set iterator and iterate over the (relevant) domain of the quantifier + RepSetIterator riter( d_quantEngine, &(d_quantEngine->getModel()->d_rep_set) ); + if( riter.setQuantifier( f ) ){ + Debug("inst-fmf-ei") << "Begin instantiation..." << std::endl; + int triedLemmas = 0; + int addedLemmas = 0; + while( !riter.isFinished() && ( addedLemmas==0 || !options::fmfOneInstPerRound() ) ){ //instantiation was not shown to be true, construct the match InstMatch m; for( int i=0; i<riter.getNumTerms(); i++ ){ - m.set( d_quantEngine->getTermDatabase()->getInstantiationConstant( f, riter.d_index_order[i] ), riter.getTerm( i ) ); + m.set( d_quantEngine, f, riter.d_index_order[i], riter.getTerm( i ) ); } Debug("fmf-model-eval") << "* Add instantiation " << m << std::endl; triedLemmas++; - d_triedLemmas++; //add as instantiation if( d_quantEngine->addInstantiation( f, m ) ){ addedLemmas++; - //if the instantiation is show to be false, and we wish to skip multiple instantiations at once - if( eval==-1 && optExhInstEvalSkipMultiple() ){ - riter.increment2( depIndex ); - }else{ - riter.increment(); - } }else{ Debug("fmf-model-eval") << "* Failed Add instantiation " << m << std::endl; - riter.increment(); } + riter.increment(); } + d_addedLemmas += addedLemmas; + d_triedLemmas += triedLemmas; } - //print debugging information - d_statistics.d_eval_formulas += d_quantEngine->getModel()->d_eval_formulas; - d_statistics.d_eval_uf_terms += d_quantEngine->getModel()->d_eval_uf_terms; - d_statistics.d_eval_lits += d_quantEngine->getModel()->d_eval_lits; - d_statistics.d_eval_lits_unknown += d_quantEngine->getModel()->d_eval_lits_unknown; - int relevantInst = 1; - for( size_t i=0; i<f[0].getNumChildren(); i++ ){ - relevantInst = relevantInst * (int)riter.d_domain[i].size(); - } - d_relevantLemmas += relevantInst; - Trace("inst-fmf-ei") << "Finished: " << std::endl; - //Debug("inst-fmf-ei") << " Inst Total: " << totalInst << std::endl; - Trace("inst-fmf-ei") << " Inst Relevant: " << relevantInst << std::endl; - Trace("inst-fmf-ei") << " Inst Tried: " << triedLemmas << std::endl; - Trace("inst-fmf-ei") << " Inst Added: " << addedLemmas << std::endl; - Trace("inst-fmf-ei") << " # Tests: " << tests << std::endl; - if( addedLemmas>1000 ){ - Trace("model-engine-warn") << "WARNING: many instantiations produced for " << f << ": " << std::endl; - //Trace("model-engine-warn") << " Inst Total: " << totalInst << std::endl; - Trace("model-engine-warn") << " Inst Relevant: " << relevantInst << std::endl; - Trace("model-engine-warn") << " Inst Tried: " << triedLemmas << std::endl; - Trace("model-engine-warn") << " Inst Added: " << addedLemmas << std::endl; - Trace("model-engine-warn") << " # Tests: " << tests << std::endl; - Trace("model-engine-warn") << std::endl; - } + //if the iterator is incomplete, we will return unknown instead of sat if no instantiations are added this round + d_incomplete_check = d_incomplete_check || riter.d_incomplete; } - //if the iterator is incomplete, we will return unknown instead of sat if no instantiations are added this round - d_incomplete_check = d_incomplete_check || riter.d_incomplete; - return addedLemmas; } void ModelEngine::debugPrint( const char* c ){ @@ -336,26 +274,14 @@ void ModelEngine::debugPrint( const char* c ){ ModelEngine::Statistics::Statistics(): d_inst_rounds("ModelEngine::Inst_Rounds", 0), - d_eval_formulas("ModelEngine::Eval_Formulas", 0 ), - d_eval_uf_terms("ModelEngine::Eval_Uf_Terms", 0 ), - d_eval_lits("ModelEngine::Eval_Lits", 0 ), - d_eval_lits_unknown("ModelEngine::Eval_Lits_Unknown", 0 ), d_exh_inst_lemmas("ModelEngine::Exhaustive_Instantiation_Lemmas", 0 ) { StatisticsRegistry::registerStat(&d_inst_rounds); - StatisticsRegistry::registerStat(&d_eval_formulas); - StatisticsRegistry::registerStat(&d_eval_uf_terms); - StatisticsRegistry::registerStat(&d_eval_lits); - StatisticsRegistry::registerStat(&d_eval_lits_unknown); StatisticsRegistry::registerStat(&d_exh_inst_lemmas); } ModelEngine::Statistics::~Statistics(){ StatisticsRegistry::unregisterStat(&d_inst_rounds); - StatisticsRegistry::unregisterStat(&d_eval_formulas); - StatisticsRegistry::unregisterStat(&d_eval_uf_terms); - StatisticsRegistry::unregisterStat(&d_eval_lits); - StatisticsRegistry::unregisterStat(&d_eval_lits_unknown); StatisticsRegistry::unregisterStat(&d_exh_inst_lemmas); } diff --git a/src/theory/quantifiers/model_engine.h b/src/theory/quantifiers/model_engine.h index 386864164..1c2c38c51 100644 --- a/src/theory/quantifiers/model_engine.h +++ b/src/theory/quantifiers/model_engine.h @@ -20,6 +20,7 @@ #include "theory/quantifiers_engine.h" #include "theory/quantifiers/model_builder.h" #include "theory/model.h" +#include "theory/quantifiers/full_model_check.h" #include "theory/quantifiers/relevant_domain.h" namespace CVC4 { @@ -31,38 +32,31 @@ class ModelEngine : public QuantifiersModule friend class RepSetIterator; private: /** builder class */ - ModelEngineBuilder* d_builder; + QModelBuilder* d_builder; private: //analysis of current model: - //relevant domain - RelevantDomain d_rel_domain; - //is the exhaustive instantiation incomplete? - bool d_incomplete_check; + RelevantDomain* d_rel_dom; private: //options - bool optOneInstPerQuantRound(); - bool optUseRelevantDomain(); bool optOneQuantPerRound(); - bool optExhInstEvalSkipMultiple(); private: - enum{ - check_model_full, - check_model_no_inst_gen, - }; //check model - int checkModel( int checkOption ); - //exhaustively instantiate quantifier (possibly using mbqi), return number of lemmas produced - int exhaustiveInstantiate( Node f, bool useRelInstDomain = false ); + int checkModel(); + //exhaustively instantiate quantifier (possibly using mbqi) + void exhaustiveInstantiate( Node f, int effort = 0 ); private: //temporary statistics + //is the exhaustive instantiation incomplete? + bool d_incomplete_check; + int d_addedLemmas; int d_triedLemmas; - int d_testLemmas; int d_totalLemmas; - int d_relevantLemmas; public: ModelEngine( context::Context* c, QuantifiersEngine* qe ); ~ModelEngine(){} + //get relevant domain + RelevantDomain * getRelevantDomain() { return d_rel_dom; } //get the builder - ModelEngineBuilder* getModelBuilder() { return d_builder; } + QModelBuilder* getModelBuilder() { return d_builder; } public: void check( Theory::Effort e ); void registerQuantifier( Node f ); @@ -74,10 +68,6 @@ public: class Statistics { public: IntStat d_inst_rounds; - IntStat d_eval_formulas; - IntStat d_eval_uf_terms; - IntStat d_eval_lits; - IntStat d_eval_lits_unknown; IntStat d_exh_inst_lemmas; Statistics(); ~Statistics(); diff --git a/src/theory/quantifiers/options b/src/theory/quantifiers/options index 60f5a171d..57211ade7 100644 --- a/src/theory/quantifiers/options +++ b/src/theory/quantifiers/options @@ -24,8 +24,11 @@ option prenexQuant /--disable-prenex-quant bool :default true # Whether to variable-eliminate quantifiers. # For example, forall x y. ( P( x, y ) V x != c ) will be rewritten to # forall y. P( c, y ) -option varElimQuant --var-elim-quant bool :default false - enable variable elimination of quantified formulas +option varElimQuant /--disable-var-elim-quant bool :default true + disable simple variable elimination for quantified formulas + +option simpleIteLiftQuant /--disable-ite-lift-quant bool :default true + disable simple ite lifting for quantified formulas # Whether to CNF quantifier bodies option cnfQuant --cnf-quant bool :default false @@ -47,6 +50,9 @@ option aggressiveMiniscopeQuant --ag-miniscope-quant bool :default false # Whether to perform quantifier macro expansion option macrosQuant --macros-quant bool :default false perform quantifiers macro expansions +# Whether to perform first-order propagation +option foPropQuant --fo-prop-quant bool :default false + perform first-order propagation on quantifiers # Whether to use smart triggers option smartTriggers /--disable-smart-triggers bool :default true @@ -54,6 +60,8 @@ option smartTriggers /--disable-smart-triggers bool :default true # Whether to use relevent triggers option relevantTriggers /--disable-relevant-triggers bool :default true prefer triggers that are more relevant based on SInE style analysis +option relationalTriggers --relational-triggers bool :default false + choose relational triggers such as x = f(y), x >= f(y) # Whether to consider terms in the bodies of quantifiers for matching option registerQuantBodyTerms --register-quant-body-terms bool :default false @@ -72,6 +80,8 @@ option cbqi --enable-cbqi/--disable-cbqi bool :default false turns on counterexample-based quantifier instantiation [off by default] /turns off counterexample-based quantifier instantiation +option recurseCbqi --cbqi-recurse bool :default false + turns on recursive counterexample-based quantifier instantiation option userPatternsQuant /--ignore-user-patterns bool :default true ignore user-provided patterns for quantifier instantiation @@ -88,6 +98,15 @@ option finiteModelFind --finite-model-find bool :default false option fmfModelBasedInst /--disable-fmf-mbqi bool :default true disable model-based quantifier instantiation for finite model finding +option fmfFullModelCheck --fmf-fmc bool :default false + enable full model check for finite model finding +option fmfFmcSimple /--disable-fmf-fmc-simple bool :default true + disable simple models in full model check for finite model finding +option fmfFmcCoverSimplify /--disable-fmf-fmc-cover-simplify bool :default true + disable covering simplification of fmc models +option fmfFmcInterval --fmf-fmc-interval bool :default false + construct interval models for fmc models + option fmfOneInstPerRound --fmf-one-inst-per-round bool :default false only add one instantiation per quantifier per round for fmf option fmfOneQuantPerRound --fmf-one-quant-per-round bool :default false @@ -98,13 +117,17 @@ option fmfRelevantDomain --fmf-relevant-domain bool :default false use relevant domain computation, similar to complete instantiation (Ge, deMoura 09) option fmfNewInstGen --fmf-new-inst-gen bool :default false use new inst gen technique for answering sat without exhaustive instantiation -option fmfInstGen /--disable-fmf-inst-gen bool :default true - disable Inst-Gen instantiation techniques for finite model finding +option fmfInstGen --fmf-inst-gen/--disable-fmf-inst-gen bool :read-write :default true + enable Inst-Gen instantiation techniques for finite model finding (default) +/disable Inst-Gen instantiation techniques for finite model finding option fmfInstGenOneQuantPerRound --fmf-inst-gen-one-quant-per-round bool :default false only perform Inst-Gen instantiation techniques on one quantifier per round option fmfFreshDistConst --fmf-fresh-dc bool :default false use fresh distinguished representative when applying Inst-Gen techniques +option fmfBoundInt --fmf-bound-int bool :default false + finite model finding on bounded integer quantification + option axiomInstMode --axiom-inst=MODE CVC4::theory::quantifiers::AxiomInstMode :default CVC4::theory::quantifiers::AXIOM_INST_MODE_DEFAULT :include "theory/quantifiers/modes.h" :handler CVC4::theory::quantifiers::stringToAxiomInstMode :handler-include "theory/quantifiers/options_handlers.h" policy for instantiating axioms diff --git a/src/theory/quantifiers/quant_util.cpp b/src/theory/quantifiers/quant_util.cpp index 36db56d0d..59995a510 100644 --- a/src/theory/quantifiers/quant_util.cpp +++ b/src/theory/quantifiers/quant_util.cpp @@ -22,6 +22,102 @@ using namespace CVC4::kind; using namespace CVC4::context; using namespace CVC4::theory; + +bool QuantArith::getMonomial( Node n, std::map< Node, Node >& msum ) { + if ( n.getKind()==MULT ){ + if( n.getNumChildren()==2 && msum.find(n[1])==msum.end() && n[0].isConst() ){ + msum[n[1]] = n[0]; + return true; + } + }else{ + if( msum.find(n)==msum.end() ){ + msum[n] = Node::null(); + return true; + } + } + return false; +} + +bool QuantArith::getMonomialSum( Node n, std::map< Node, Node >& msum ) { + if ( n.getKind()==PLUS ){ + for( unsigned i=0; i<n.getNumChildren(); i++) { + if (!getMonomial( n[i], msum )){ + return false; + } + } + return true; + }else{ + return getMonomial( n, msum ); + } +} + +bool QuantArith::getMonomialSumLit( Node lit, std::map< Node, Node >& msum ) { + if( lit.getKind()==GEQ || lit.getKind()==EQUAL ){ + if( getMonomialSum( lit[0], msum ) ){ + if( lit[1].isConst() ){ + if( !lit[1].getConst<Rational>().isZero() ){ + msum[Node::null()] = negate( lit[1] ); + } + return true; + } + } + } + return false; +} + +bool QuantArith::isolate( Node v, std::map< Node, Node >& msum, Node & veq, Kind k ) { + if( msum.find(v)!=msum.end() ){ + std::vector< Node > children; + Rational r = msum[v].isNull() ? Rational(1) : msum[v].getConst<Rational>(); + if ( r.sgn()!=0 ){ + for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){ + if( it->first.isNull() || it->first!=v ){ + Node m; + if( !it->first.isNull() ){ + if ( !it->second.isNull() ){ + m = NodeManager::currentNM()->mkNode( MULT, it->second, it->first ); + }else{ + m = it->first; + } + }else{ + m = it->second; + } + children.push_back(m); + } + } + veq = children.size()>1 ? NodeManager::currentNM()->mkNode( PLUS, children ) : + (children.size()==1 ? children[0] : NodeManager::currentNM()->mkConst( Rational(0) )); + if( !r.isNegativeOne() ){ + if( r.isOne() ){ + veq = negate(veq); + }else{ + //TODO + return false; + } + } + veq = Rewriter::rewrite( veq ); + veq = NodeManager::currentNM()->mkNode( k, r.sgn()==1 ? v : veq, r.sgn()==1 ? veq : v ); + return true; + } + return false; + }else{ + return false; + } +} + +Node QuantArith::negate( Node t ) { + Node tt = NodeManager::currentNM()->mkNode( MULT, NodeManager::currentNM()->mkConst( Rational(-1) ), t ); + tt = Rewriter::rewrite( tt ); + return tt; +} + +Node QuantArith::offset( Node t, int i ) { + Node tt = NodeManager::currentNM()->mkNode( PLUS, NodeManager::currentNM()->mkConst( Rational(i) ), t ); + tt = Rewriter::rewrite( tt ); + return tt; +} + + void QuantRelevance::registerQuantifier( Node f ){ //compute symbols in f std::vector< Node > syms; @@ -93,13 +189,13 @@ QuantPhaseReq::QuantPhaseReq( Node n, bool computeEq ){ for( std::map< Node, bool >::iterator it = d_phase_reqs.begin(); it != d_phase_reqs.end(); ++it ){ Debug("inst-engine-phase-req") << " " << it->first << " -> " << it->second << std::endl; if( it->first.getKind()==EQUAL ){ - if( it->first[0].hasAttribute(InstConstantAttribute()) ){ - if( !it->first[1].hasAttribute(InstConstantAttribute()) ){ + if( quantifiers::TermDb::hasInstConstAttr(it->first[0]) ){ + if( !quantifiers::TermDb::hasInstConstAttr(it->first[1]) ){ d_phase_reqs_equality_term[ it->first[0] ] = it->first[1]; d_phase_reqs_equality[ it->first[0] ] = it->second; Debug("inst-engine-phase-req") << " " << it->first[0] << ( it->second ? " == " : " != " ) << it->first[1] << std::endl; } - }else if( it->first[1].hasAttribute(InstConstantAttribute()) ){ + }else if( quantifiers::TermDb::hasInstConstAttr(it->first[1]) ){ d_phase_reqs_equality_term[ it->first[1] ] = it->first[0]; d_phase_reqs_equality[ it->first[1] ] = it->second; Debug("inst-engine-phase-req") << " " << it->first[1] << ( it->second ? " == " : " != " ) << it->first[0] << std::endl; diff --git a/src/theory/quantifiers/quant_util.h b/src/theory/quantifiers/quant_util.h index 6a5726cc7..86c7bc3a0 100644 --- a/src/theory/quantifiers/quant_util.h +++ b/src/theory/quantifiers/quant_util.h @@ -28,6 +28,18 @@ namespace CVC4 { namespace theory { +class QuantArith +{ +public: + static bool getMonomial( Node n, std::map< Node, Node >& msum ); + static bool getMonomialSum( Node n, std::map< Node, Node >& msum ); + static bool getMonomialSumLit( Node lit, std::map< Node, Node >& msum ); + static bool isolate( Node v, std::map< Node, Node >& msum, Node & veq, Kind k ); + static Node negate( Node t ); + static Node offset( Node t, int i ); +}; + + class QuantRelevance { private: diff --git a/src/theory/quantifiers/quantifiers_attributes.cpp b/src/theory/quantifiers/quantifiers_attributes.cpp index 5bdce5fac..909c9c388 100644 --- a/src/theory/quantifiers/quantifiers_attributes.cpp +++ b/src/theory/quantifiers/quantifiers_attributes.cpp @@ -32,6 +32,10 @@ void QuantifiersAttributes::setUserAttribute( const std::string& attr, Node n ){ Trace("quant-attr") << "Set conjecture " << n << std::endl; ConjectureAttribute ca; n.setAttribute( ca, true ); + }else if( attr=="rr_priority" ){ + //Trace("quant-attr") << "Set rr priority " << n << std::endl; + //RrPriorityAttribute rra; + } }else{ for( size_t i=0; i<n.getNumChildren(); i++ ){ diff --git a/src/theory/quantifiers/quantifiers_attributes.h b/src/theory/quantifiers/quantifiers_attributes.h index 878d3ac50..1aebbfcc5 100644 --- a/src/theory/quantifiers/quantifiers_attributes.h +++ b/src/theory/quantifiers/quantifiers_attributes.h @@ -34,6 +34,10 @@ typedef expr::Attribute< AxiomAttributeId, bool > AxiomAttribute; struct ConjectureAttributeId {}; typedef expr::Attribute< ConjectureAttributeId, bool > ConjectureAttribute; +/** Attribute priority for rewrite rules */ +//struct RrPriorityAttributeId {}; +//typedef expr::Attribute< RrPriorityAttributeId, uint64_t > RrPriorityAttribute; + struct QuantifiersAttributes { /** set user attribute diff --git a/src/theory/quantifiers/quantifiers_rewriter.cpp b/src/theory/quantifiers/quantifiers_rewriter.cpp index 5c71f6e6f..e27897a96 100644 --- a/src/theory/quantifiers/quantifiers_rewriter.cpp +++ b/src/theory/quantifiers/quantifiers_rewriter.cpp @@ -211,10 +211,6 @@ RewriteResponse QuantifiersRewriter::postRewrite(TNode in) { if( in.hasAttribute(NestedQuantAttribute()) ){ setNestedQuantifiers( ret, in.getAttribute(NestedQuantAttribute()) ); } - if( in.hasAttribute(InstConstantAttribute()) ){ - InstConstantAttribute ica; - ret.setAttribute(ica,in.getAttribute(InstConstantAttribute()) ); - } Trace("quantifiers-rewrite") << "*** rewrite " << in << std::endl; Trace("quantifiers-rewrite") << " to " << std::endl; Trace("quantifiers-rewrite") << ret << std::endl; @@ -311,7 +307,7 @@ Node QuantifiersRewriter::computeSimpleIteLift( Node body ) { } Node QuantifiersRewriter::computeVarElimination( Node body, std::vector< Node >& args, Node& ipl ){ - Trace("var-elim-quant") << "Compute var elimination for " << body << std::endl; + Trace("var-elim-quant-debug") << "Compute var elimination for " << body << std::endl; QuantPhaseReq qpr( body ); std::vector< Node > vars; std::vector< Node > subs; @@ -918,7 +914,7 @@ bool QuantifiersRewriter::doOperation( Node f, bool isNested, int computeOption }else if( computeOption==COMPUTE_NNF ){ return false;//TODO: compute NNF (current bad idea since arithmetic rewrites equalities) }else if( computeOption==COMPUTE_SIMPLE_ITE_LIFT ){ - return !options::finiteModelFind(); + return options::simpleIteLiftQuant();//!options::finiteModelFind(); }else if( computeOption==COMPUTE_PRENEX ){ return options::prenexQuant() && !options::aggressiveMiniscopeQuant(); }else if( computeOption==COMPUTE_VAR_ELIMINATION ){ diff --git a/src/theory/quantifiers/relevant_domain.cpp b/src/theory/quantifiers/relevant_domain.cpp index 2b011552c..b079ae75c 100644..100755 --- a/src/theory/quantifiers/relevant_domain.cpp +++ b/src/theory/quantifiers/relevant_domain.cpp @@ -24,175 +24,159 @@ using namespace CVC4::context; using namespace CVC4::theory; using namespace CVC4::theory::quantifiers; +void RelevantDomain::RDomain::merge( RDomain * r ) { + Assert( !d_parent ); + Assert( !r->d_parent ); + d_parent = r; + for( unsigned i=0; i<d_terms.size(); i++ ){ + r->addTerm( d_terms[i] ); + } + d_terms.clear(); +} + +void RelevantDomain::RDomain::addTerm( Node t ) { + if( std::find( d_terms.begin(), d_terms.end(), t )==d_terms.end() ){ + d_terms.push_back( t ); + } +} + +RelevantDomain::RDomain * RelevantDomain::RDomain::getParent() { + if( !d_parent ){ + return this; + }else{ + RDomain * p = d_parent->getParent(); + d_parent = p; + return p; + } +} + +void RelevantDomain::RDomain::removeRedundantTerms( FirstOrderModel * fm ) { + std::map< Node, Node > rterms; + for( unsigned i=0; i<d_terms.size(); i++ ){ + Node r = d_terms[i]; + if( !TermDb::hasInstConstAttr( d_terms[i] ) ){ + r = fm->getRepresentative( d_terms[i] ); + } + if( rterms.find( r )==rterms.end() ){ + rterms[r] = d_terms[i]; + } + } + d_terms.clear(); + for( std::map< Node, Node >::iterator it = rterms.begin(); it != rterms.end(); ++it ){ + d_terms.push_back( it->second ); + } +} + + + RelevantDomain::RelevantDomain( QuantifiersEngine* qe, FirstOrderModel* m ) : d_qe( qe ), d_model( m ){ } +RelevantDomain::RDomain * RelevantDomain::getRDomain( Node n, int i ) { + if( d_rel_doms.find( n )==d_rel_doms.end() || d_rel_doms[n].find( i )==d_rel_doms[n].end() ){ + d_rel_doms[n][i] = new RDomain; + d_rn_map[d_rel_doms[n][i]] = n; + d_ri_map[d_rel_doms[n][i]] = i; + } + return d_rel_doms[n][i]->getParent(); +} + void RelevantDomain::compute(){ - Trace("rel-dom") << "compute relevant domain" << std::endl; - d_quant_inst_domain.clear(); + for( std::map< Node, std::map< int, RDomain * > >::iterator it = d_rel_doms.begin(); it != d_rel_doms.end(); ++it ){ + for( std::map< int, RDomain * >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){ + it2->second->reset(); + } + } for( int i=0; i<(int)d_model->getNumAssertedQuantifiers(); i++ ){ Node f = d_model->getAssertedQuantifier( i ); - d_quant_inst_domain[f].resize( f[0].getNumChildren() ); + Node icf = d_qe->getTermDatabase()->getInstConstantBody( f ); + Trace("rel-dom-debug") << "compute relevant domain for " << icf << std::endl; + computeRelevantDomain( icf, true, true ); } - Trace("rel-dom") << "account for ground terms" << std::endl; - //add ground terms to domain (rule 1 of complete instantiation essentially uf fragment) - for( std::map< Node, uf::UfModelTree >::iterator it = d_model->d_uf_model_tree.begin(); - it != d_model->d_uf_model_tree.end(); ++it ){ + + Trace("rel-dom-debug") << "account for ground terms" << std::endl; + for( std::map< Node, std::vector< Node > >::iterator it = d_model->d_uf_terms.begin(); it != d_model->d_uf_terms.end(); ++it ){ Node op = it->first; - for( size_t i=0; i<d_model->d_uf_terms[op].size(); i++ ){ - Node n = d_model->d_uf_terms[op][i]; - //add arguments to domain - for( int j=0; j<(int)n.getNumChildren(); j++ ){ - if( d_model->d_rep_set.hasType( n[j].getType() ) ){ - Node ra = d_model->getRepresentative( n[j] ); - int raIndex = d_model->d_rep_set.getIndexFor( ra ); - if( raIndex==-1 ) Trace("rel-dom-warn") << "WARNING: Ground domain: rep set does not contain : " << ra << std::endl; - Assert( raIndex!=-1 ); - if( std::find( d_active_domain[op][j].begin(), d_active_domain[op][j].end(), raIndex )==d_active_domain[op][j].end() ){ - d_active_domain[op][j].push_back( raIndex ); - } + for( unsigned i=0; i<it->second.size(); 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] ); } } - //add to range - Node r = d_model->getRepresentative( n ); - int raIndex = d_model->d_rep_set.getIndexFor( r ); - if( raIndex==-1 ) Trace("rel-dom-warn") << "WARNING: Ground range: rep set does not contain : " << r << std::endl; - Assert( raIndex!=-1 ); - if( std::find( d_active_range[op].begin(), d_active_range[op].end(), raIndex )==d_active_range[op].end() ){ - d_active_range[op].push_back( raIndex ); - } } } - Trace("rel-dom") << "do quantifiers" << std::endl; - //find fixed point for relevant domain computation - bool success; - do{ - success = true; - for( int i=0; i<(int)d_model->getNumAssertedQuantifiers(); i++ ){ - Node f = d_model->getAssertedQuantifier( i ); - //compute the domain of relevant instantiations (rule 3 of complete instantiation, essentially uf fragment) - if( computeRelevantInstantiationDomain( d_qe->getTermDatabase()->getInstConstantBody( f ), Node::null(), -1, f ) ){ - success = false; - } - //extend the possible domain for functions (rule 2 of complete instantiation, essentially uf fragment) - RepDomain range; - if( extendFunctionDomains( d_qe->getTermDatabase()->getInstConstantBody( f ), range ) ){ - success = false; - } - } - }while( !success ); - Trace("rel-dom") << "done compute relevant domain" << std::endl; - /* - //debug printing - Trace("rel-dom") << "Exhaustive instantiate " << f << std::endl; - if( useRelInstDomain ){ - Trace("rel-dom") << "Relevant domain : " << std::endl; - for( size_t i=0; i<d_rel_domain.d_quant_inst_domain[f].size(); i++ ){ - Trace("rel-dom") << " " << i << " : "; - for( size_t j=0; j<d_rel_domain.d_quant_inst_domain[f][i].size(); j++ ){ - Trace("rel-dom") << d_rel_domain.d_quant_inst_domain[f][i][j] << " "; + //print debug + for( std::map< Node, std::map< int, RDomain * > >::iterator it = d_rel_doms.begin(); it != d_rel_doms.end(); ++it ){ + Trace("rel-dom") << "Relevant domain for " << it->first << " : " << std::endl; + for( std::map< int, RDomain * >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){ + Trace("rel-dom") << " " << it2->first << " : "; + RDomain * r = it2->second; + RDomain * rp = r->getParent(); + if( r==rp ){ + r->removeRedundantTerms( d_model ); + for( unsigned i=0; i<r->d_terms.size(); i++ ){ + Trace("rel-dom") << r->d_terms[i] << " "; + } + }else{ + Trace("rel-dom") << "Dom( " << d_rn_map[rp] << ", " << d_ri_map[rp] << " ) "; } Trace("rel-dom") << std::endl; } } - */ + } -bool RelevantDomain::computeRelevantInstantiationDomain( Node n, Node parent, int arg, Node f ){ - bool domainChanged = false; - if( n.getKind()==INST_CONSTANT ){ - bool domainSet = false; - int vi = n.getAttribute(InstVarNumAttribute()); - Assert( !parent.isNull() ); - if( parent.getKind()==APPLY_UF ){ - //if the child of APPLY_UF term f( ... ), only consider the active domain of f at given argument - Node op = parent.getOperator(); - if( d_active_domain.find( op )!=d_active_domain.end() ){ - for( size_t i=0; i<d_active_domain[op][arg].size(); i++ ){ - int d = d_active_domain[op][arg][i]; - if( std::find( d_quant_inst_domain[f][vi].begin(), d_quant_inst_domain[f][vi].end(), d )== - d_quant_inst_domain[f][vi].end() ){ - d_quant_inst_domain[f][vi].push_back( d ); - domainChanged = true; - } - } - domainSet = true; - } - } - if( !domainSet ){ - //otherwise, we must consider the entire domain - TypeNode tn = n.getType(); - if( d_quant_inst_domain_complete[f].find( vi )==d_quant_inst_domain_complete[f].end() ){ - if( d_model->d_rep_set.hasType( tn ) ){ - //it is the complete domain - d_quant_inst_domain[f][vi].clear(); - for( size_t i=0; i<d_model->d_rep_set.d_type_reps[tn].size(); i++ ){ - d_quant_inst_domain[f][vi].push_back( i ); - } - domainChanged = true; +void RelevantDomain::computeRelevantDomain( Node n, bool hasPol, bool pol ) { + + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + if( n.getKind()==APPLY_UF ){ + RDomain * rf = getRDomain( n.getOperator(), i ); + if( n[i].getKind()==INST_CONSTANT ){ + Node q = d_qe->getTermDatabase()->getInstConstAttr( n[i] ); + //merge the RDomains + unsigned id = n[i].getAttribute(InstVarNumAttribute()); + Trace("rel-dom-debug") << n[i] << " is variable # " << id << " for " << q; + Trace("rel-dom-debug") << " with body : " << d_qe->getTermDatabase()->getInstConstantBody( q ) << std::endl; + RDomain * rq = getRDomain( q, id ); + if( rf!=rq ){ + rq->merge( rf ); } - d_quant_inst_domain_complete[f][vi] = true; + }else{ + //term to add + rf->addTerm( n[i] ); } } - }else{ - for( int i=0; i<(int)n.getNumChildren(); i++ ){ - if( computeRelevantInstantiationDomain( n[i], n, i, f ) ){ - domainChanged = true; - } + + //TODO + if( n[i].getKind()!=FORALL ){ + bool newHasPol = hasPol; + bool newPol = pol; + computeRelevantDomain( n[i], newHasPol, newPol ); } } - return domainChanged; } -bool RelevantDomain::extendFunctionDomains( Node n, RepDomain& range ){ - if( n.getKind()==INST_CONSTANT ){ - Node f = n.getAttribute(InstConstantAttribute()); - int var = n.getAttribute(InstVarNumAttribute()); - range.insert( range.begin(), d_quant_inst_domain[f][var].begin(), d_quant_inst_domain[f][var].end() ); - return false; +Node RelevantDomain::getRelevantTerm( Node f, int i, Node r ) { + RDomain * rf = getRDomain( f, i ); + Trace("rel-dom-debug") << "Get relevant domain " << rf << " " << r << std::endl; + if( !d_qe->getEqualityQuery()->getEngine()->hasTerm( r ) || rf->hasTerm( r ) ){ + return r; }else{ - Node op; - if( n.getKind()==APPLY_UF ){ - op = n.getOperator(); - } - bool domainChanged = false; - for( int i=0; i<(int)n.getNumChildren(); i++ ){ - RepDomain childRange; - if( extendFunctionDomains( n[i], childRange ) ){ - domainChanged = true; - } - if( n.getKind()==APPLY_UF ){ - if( d_active_domain.find( op )!=d_active_domain.end() ){ - for( int j=0; j<(int)childRange.size(); j++ ){ - int v = childRange[j]; - if( std::find( d_active_domain[op][i].begin(), d_active_domain[op][i].end(), v )==d_active_domain[op][i].end() ){ - d_active_domain[op][i].push_back( v ); - domainChanged = true; - } - } - }else{ - //do this? - } - } - } - //get the range - if( n.hasAttribute(InstConstantAttribute()) ){ - if( n.getKind()==APPLY_UF && d_active_range.find( op )!=d_active_range.end() ){ - range.insert( range.end(), d_active_range[op].begin(), d_active_range[op].end() ); - }else{ - //infinite range? - } - }else{ - Node r = d_model->getRepresentative( n ); - int index = d_model->d_rep_set.getIndexFor( r ); - if( index==-1 ){ - //we consider all ground terms in bodies of quantifiers to be the first ground representative - range.push_back( 0 ); - }else{ - range.push_back( index ); + Node rr = d_model->getRepresentative( r ); + eq::EqClassIterator eqc( rr, d_qe->getEqualityQuery()->getEngine() ); + while( !eqc.isFinished() ){ + Node en = (*eqc); + if( rf->hasTerm( en ) ){ + return en; } + ++eqc; } - return domainChanged; + //otherwise, may be equal to some non-ground term + + return r; } -}
\ No newline at end of file +} diff --git a/src/theory/quantifiers/relevant_domain.h b/src/theory/quantifiers/relevant_domain.h index 6fc035e8a..c4345f828 100644..100755 --- a/src/theory/quantifiers/relevant_domain.h +++ b/src/theory/quantifiers/relevant_domain.h @@ -26,25 +26,33 @@ namespace quantifiers { class RelevantDomain { private: + class RDomain + { + public: + RDomain() : d_parent( NULL ) {} + void reset() { d_parent = NULL; d_terms.clear(); } + RDomain * d_parent; + std::vector< Node > d_terms; + void merge( RDomain * r ); + void addTerm( Node t ); + RDomain * getParent(); + void removeRedundantTerms( FirstOrderModel * fm ); + bool hasTerm( Node n ) { return std::find( d_terms.begin(), d_terms.end(), n )!=d_terms.end(); } + }; + std::map< Node, std::map< int, RDomain * > > d_rel_doms; + std::map< RDomain *, Node > d_rn_map; + std::map< RDomain *, int > d_ri_map; + RDomain * getRDomain( Node n, int i ); QuantifiersEngine* d_qe; FirstOrderModel* d_model; - - //the domain of the arguments for each operator - std::map< Node, std::map< int, RepDomain > > d_active_domain; - //the range for each operator - std::map< Node, RepDomain > d_active_range; - //for computing relevant instantiation domain, return true if changed - bool computeRelevantInstantiationDomain( Node n, Node parent, int arg, Node f ); - //for computing extended - bool extendFunctionDomains( Node n, RepDomain& range ); + void computeRelevantDomain( Node n, bool hasPol, bool pol ); public: RelevantDomain( QuantifiersEngine* qe, FirstOrderModel* m ); virtual ~RelevantDomain(){} //compute the relevant domain void compute(); - //relevant instantiation domain for each quantifier - std::map< Node, std::vector< RepDomain > > d_quant_inst_domain; - std::map< Node, std::map< int, bool > > d_quant_inst_domain_complete; + + Node getRelevantTerm( Node f, int i, Node r ); };/* class RelevantDomain */ }/* CVC4::theory::quantifiers namespace */ diff --git a/src/theory/quantifiers/rewrite_engine.cpp b/src/theory/quantifiers/rewrite_engine.cpp new file mode 100755 index 000000000..107a99014 --- /dev/null +++ b/src/theory/quantifiers/rewrite_engine.cpp @@ -0,0 +1,184 @@ +/********************* */ +/*! \file bounded_integers.cpp + ** \verbatim + ** Original author: 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 Rewrite engine module + ** + ** This class manages rewrite rules for quantifiers + **/ + +#include "theory/quantifiers/rewrite_engine.h" +#include "theory/quantifiers/quant_util.h" +#include "theory/quantifiers/first_order_model.h" +#include "theory/quantifiers/model_engine.h" +#include "theory/quantifiers/options.h" +#include "theory/quantifiers/inst_match_generator.h" +#include "theory/theory_engine.h" + +using namespace CVC4; +using namespace std; +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; +using namespace CVC4::kind; + +struct PrioritySort { + std::vector< double > d_priority; + bool operator() (int i,int j) { + return d_priority[i] < d_priority[j]; + } +}; + + +RewriteEngine::RewriteEngine( context::Context* c, QuantifiersEngine* qe ) : QuantifiersModule(qe) { + +} + +double RewriteEngine::getPriority( Node f ) { + Node rr = f.getAttribute(QRewriteRuleAttribute()); + 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 ){ + return deterministic ? 1.0 : 4.0; + }else if( rrr.getKind()==RR_REDUCTION ){ + return deterministic ? 2.0 : 5.0; + }else{ + return 6.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]] ); + } + + //apply rewrite rules + int addedLemmas = 0; + //per priority level + int index = 0; + bool success = true; + while( success && index<(int)priority_order.size() ) { + addedLemmas += checkRewriteRule( priority_order[index] ); + index++; + if( index<(int)priority_order.size() ){ + success = addedLemmas==0 || getPriority( priority_order[index] )==getPriority( priority_order[index-1] ); + } + } + + Trace("inst-engine") << "Rewrite engine added " << addedLemmas << " lemmas." << std::endl; + if (addedLemmas==0) { + }else{ + //otherwise, the search will continue + d_quantEngine->flushLemmas( &d_quantEngine->getOutputChannel() ); + } + } +} + +int RewriteEngine::checkRewriteRule( Node f ) { + Trace("rewrite-engine-inst") << "Check " << f << ", priority = " << getPriority( f ) << "..." << std::endl; + 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; + 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); + //} + }else{ + Trace("rewrite-engine-inst-debug") << "failure." << std::endl; + } + } + } + }while(success); + } + Trace("rewrite-engine-inst") << "Rule " << f << " 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; + d_rr_quant.push_back( f ); + d_rr_guard[f] = rr[1]; + Trace("rewrite-engine") << " guard is : " << d_rr_guard[f] << std::endl; + } +} + +void RewriteEngine::assertNode( Node n ) { + +} + diff --git a/src/theory/quantifiers/rewrite_engine.h b/src/theory/quantifiers/rewrite_engine.h new file mode 100755 index 000000000..2d9d751c2 --- /dev/null +++ b/src/theory/quantifiers/rewrite_engine.h @@ -0,0 +1,54 @@ +/********************* */ +/*! \file bounded_integers.h +** \verbatim +** Original author: 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 This class manages integer bounds for quantifiers +**/ + +#include "cvc4_private.h" + +#ifndef __CVC4__REWRITE_ENGINE_H +#define __CVC4__REWRITE_ENGINE_H + +#include "theory/quantifiers_engine.h" +#include "theory/quantifiers/trigger.h" + +#include "context/context.h" +#include "context/context_mm.h" +#include "context/cdchunk_list.h" + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +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; + Node d_true; + /** explicitly provided patterns */ + std::map< Node, std::vector< inst::Trigger* > > d_rr_triggers; + double getPriority( Node f ); +private: + int checkRewriteRule( Node f ); +public: + RewriteEngine( context::Context* c, QuantifiersEngine* qe ); + + void check( Theory::Effort e ); + void registerQuantifier( Node f ); + void assertNode( Node n ); +}; + +} +} +} + +#endif diff --git a/src/theory/quantifiers/symmetry_breaking.cpp b/src/theory/quantifiers/symmetry_breaking.cpp new file mode 100755 index 000000000..507a50838 --- /dev/null +++ b/src/theory/quantifiers/symmetry_breaking.cpp @@ -0,0 +1,314 @@ +/********************* */ +/*! \file symmetry_breaking.cpp + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009-2012 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief symmetry breaking module + ** + **/ + +#include <vector> + +#include "theory/quantifiers/symmetry_breaking.h" +#include "theory/rewriter.h" +#include "theory/quantifiers_engine.h" +#include "theory/theory_engine.h" +#include "util/sort_inference.h" +#include "theory/uf/theory_uf_strong_solver.h" + +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::theory; +using namespace std; + +namespace CVC4 { + + +SubsortSymmetryBreaker::SubsortSymmetryBreaker(QuantifiersEngine* qe, context::Context* c) : +d_qe(qe), d_conflict(c,false) { + d_true = NodeManager::currentNM()->mkConst( true ); +} + +eq::EqualityEngine * SubsortSymmetryBreaker::getEqualityEngine() { + return ((uf::TheoryUF*)d_qe->getTheoryEngine()->theoryOf( theory::THEORY_UF ))->getEqualityEngine(); +} + +bool SubsortSymmetryBreaker::areEqual( Node n1, Node n2 ) { + return getEqualityEngine()->hasTerm( n1 ) && getEqualityEngine()->hasTerm( n2 ) && getEqualityEngine()->areEqual( n1,n2 ); +} + +bool SubsortSymmetryBreaker::areDisequal( Node n1, Node n2 ) { + return getEqualityEngine()->hasTerm( n1 ) && getEqualityEngine()->hasTerm( n2 ) && getEqualityEngine()->areDisequal( n1,n2, false ); +} + + +Node SubsortSymmetryBreaker::getRepresentative( Node n ) { + return getEqualityEngine()->getRepresentative( n ); +} + +uf::StrongSolverTheoryUF * SubsortSymmetryBreaker::getStrongSolver() { + return ((uf::TheoryUF*)d_qe->getTheoryEngine()->theoryOf( theory::THEORY_UF ))->getStrongSolver(); +} + +SubsortSymmetryBreaker::TypeInfo::TypeInfo( context::Context * c ) : +d_max_dom_const_sort(c,0), d_has_dom_const_sort(c,false) { +} + +SubsortSymmetryBreaker::SubSortInfo::SubSortInfo( context::Context * c ) : +d_dom_constants( c ), d_first_active( c, 0 ){ + d_dc_nodes = 0; +} + +unsigned SubsortSymmetryBreaker::SubSortInfo::getNumDomainConstants() { + if( d_nodes.empty() ){ + return 0; + }else{ + return 1 + d_dom_constants.size(); + } +} + +Node SubsortSymmetryBreaker::SubSortInfo::getDomainConstant( int i ) { + if( i==0 ){ + return d_nodes[0]; + }else{ + Assert( i<=(int)d_dom_constants.size() ); + return d_dom_constants[i-1]; + } +} + +Node SubsortSymmetryBreaker::SubSortInfo::getFirstActive(eq::EqualityEngine * ee) { + if( d_first_active.get()<(int)d_nodes.size() ){ + Node fa = d_nodes[d_first_active.get()]; + return ee->hasTerm( fa ) ? fa : Node::null(); + }else{ + return Node::null(); + } +} + +SubsortSymmetryBreaker::TypeInfo * SubsortSymmetryBreaker::getTypeInfo( TypeNode tn ) { + if( d_t_info.find( tn )==d_t_info.end() ){ + d_t_info[tn] = new TypeInfo( d_qe->getSatContext() ); + } + return d_t_info[tn]; +} + +SubsortSymmetryBreaker::SubSortInfo * SubsortSymmetryBreaker::getSubSortInfo( TypeNode tn, int sid ) { + if( d_type_info.find( sid )==d_type_info.end() ){ + d_type_info[sid] = new SubSortInfo( d_qe->getSatContext() ); + d_sub_sorts[tn].push_back( sid ); + d_sid_to_type[sid] = tn; + } + return d_type_info[sid]; +} + +void SubsortSymmetryBreaker::newEqClass( Node n ) { + Trace("sym-break-temp") << "New eq class " << n << std::endl; + if( !d_conflict ){ + TypeNode tn = n.getType(); + SortInference * si = d_qe->getTheoryEngine()->getSortInference(); + if( si->isWellSorted( n ) ){ + int sid = si->getSortId( n ); + Trace("sym-break-debug") << "SSB: New eq class " << n << " : " << n.getType() << " : " << sid << std::endl; + SubSortInfo * ti = getSubSortInfo( tn, sid ); + if( std::find( ti->d_nodes.begin(), ti->d_nodes.end(), n )==ti->d_nodes.end() ){ + if( ti->d_nodes.empty() ){ + //for first subsort, we add unit equality + if( d_sub_sorts[tn][0]!=sid ){ + Trace("sym-break-temp") << "Do sym break unit with " << d_type_info[d_sub_sorts[tn][0]]->getBaseConstant() << std::endl; + //add unit symmetry breaking lemma + Node eq = n.eqNode( d_type_info[d_sub_sorts[tn][0]]->getBaseConstant() ); + eq = Rewriter::rewrite( eq ); + d_unit_lemmas.push_back( eq ); + Trace("sym-break-lemma") << "*** SymBreak : Unit lemma (" << sid << "==" << d_sub_sorts[tn][0] << ") : " << eq << std::endl; + d_pending_lemmas.push_back( eq ); + } + Trace("sym-break-dc") << "* Set first domain constant : " << n << " for " << tn << " : " << sid << std::endl; + ti->d_dc_nodes++; + } + ti->d_node_to_id[n] = ti->d_nodes.size(); + ti->d_nodes.push_back( n ); + } + TypeInfo * tti = getTypeInfo( tn ); + if( !tti->d_has_dom_const_sort.get() ){ + tti->d_has_dom_const_sort.set( true ); + tti->d_max_dom_const_sort.set( sid ); + } + } + } + Trace("sym-break-temp") << "Done new eq class" << std::endl; +} + + + +void SubsortSymmetryBreaker::merge( Node a, Node b ) { + if( Trace.isOn("sym-break-debug") ){ + SortInference * si = d_qe->getTheoryEngine()->getSortInference(); + int as = si->getSortId( a ); + int bs = si->getSortId( b ); + Trace("sym-break-debug") << "SSB: New merge " << a.getType() << " :: " << a << " : " << as; + Trace("sym-break-debug") << " == " << b << " : " << bs << std::endl; + } +} + +void SubsortSymmetryBreaker::assertDisequal( Node a, Node b ) { + if( Trace.isOn("sym-break-debug") ){ + SortInference * si = d_qe->getTheoryEngine()->getSortInference(); + int as = si->getSortId( a ); + int bs = si->getSortId( b ); + Trace("sym-break-debug") << "SSB: New assert disequal " << a.getType() << " :: " << a << " : " << as; + Trace("sym-break-debug") << " != " << b << " : " << bs << std::endl; + } +} + +void SubsortSymmetryBreaker::processFirstActive( TypeNode tn, int sid, int curr_card ){ + SubSortInfo * ti = getSubSortInfo( tn, sid ); + if( (int)ti->getNumDomainConstants()<curr_card ){ + //static int checkCount = 0; + //checkCount++; + //if( checkCount%1000==0 ){ + // std::cout << "Check count = " << checkCount << std::endl; + //} + + Trace("sym-break-dc-debug") << "Check for domain constants " << tn << " : " << sid << ", curr_card = " << curr_card << ", "; + Trace("sym-break-dc-debug") << "#domain constants = " << ti->getNumDomainConstants() << std::endl; + Node fa = ti->getFirstActive(getEqualityEngine()); + bool invalid = true; + while( invalid && !fa.isNull() && (int)ti->getNumDomainConstants()<curr_card ){ + invalid = false; + unsigned deq = 0; + for( unsigned i=0; i<ti->getNumDomainConstants(); i++ ){ + Node dc = ti->getDomainConstant( i ); + if( areEqual( fa, dc ) ){ + invalid = true; + break; + }else if( areDisequal( fa, dc ) ){ + deq++; + } + } + if( deq==ti->getNumDomainConstants() ){ + Trace("sym-break-dc") << "* Can infer domain constant #" << ti->getNumDomainConstants()+1; + Trace("sym-break-dc") << " : " << fa << " for " << tn << " : " << sid << std::endl; + //add to domain constants + ti->d_dom_constants.push_back( fa ); + if( ti->d_node_to_id[fa]>ti->d_dc_nodes ){ + Trace("sym-break-dc-debug") << "Swap nodes... " << ti->d_dc_nodes << " " << ti->d_node_to_id[fa] << " " << ti->d_nodes.size() << std::endl; + //swap + Node on = ti->d_nodes[ti->d_dc_nodes]; + int id = ti->d_node_to_id[fa]; + + ti->d_nodes[ti->d_dc_nodes] = fa; + ti->d_nodes[id] = on; + ti->d_node_to_id[fa] = ti->d_dc_nodes; + ti->d_node_to_id[on] = id; + } + ti->d_dc_nodes++; + Trace("sym-break-dc-debug") << "Get max type info..." << std::endl; + TypeInfo * tti = getTypeInfo( tn ); + Assert( tti->d_has_dom_const_sort.get() ); + int msid = tti->d_max_dom_const_sort.get(); + SubSortInfo * max_ti = getSubSortInfo( d_sid_to_type[msid], msid ); + Trace("sym-break-dc-debug") << "Swap nodes..." << std::endl; + //now, check if we can apply symmetry breaking to another sort + if( ti->getNumDomainConstants()>max_ti->getNumDomainConstants() ){ + Trace("sym-break-dc") << "Max domain constant subsort for " << tn << " becomes " << sid << std::endl; + tti->d_max_dom_const_sort.set( sid ); + }else if( ti!=max_ti ){ + //construct symmetry breaking lemma + //current domain constant must be disequal from all current ones + Trace("sym-break-dc") << "Get domain constant " << ti->getNumDomainConstants()-1; + Trace("sym-break-dc") << " from max_ti, " << max_ti->getNumDomainConstants() << std::endl; + //apply a symmetry breaking lemma + Node m = max_ti->getDomainConstant(ti->getNumDomainConstants()-1); + //if fa and m are disequal from all previous domain constants in the other sort + std::vector< Node > cc; + for( unsigned r=0; r<2; r++ ){ + Node n = ((r==0)==(msid>sid)) ? fa : m; + Node on = ((r==0)==(msid>sid)) ? m : fa; + SubSortInfo * t = ((r==0)==(msid>sid)) ? max_ti : ti; + for( unsigned i=0; i<t->d_node_to_id[on]; i++ ){ + cc.push_back( n.eqNode( t->d_nodes[i] ) ); + } + } + //then, we can assume fa = m + cc.push_back( fa.eqNode( m ) ); + Node lem = NodeManager::currentNM()->mkNode( kind::OR, cc ); + lem = Rewriter::rewrite( lem ); + if( std::find( d_lemmas.begin(), d_lemmas.end(), lem )==d_lemmas.end() ){ + d_lemmas.push_back( lem ); + Trace("sym-break-lemma") << "*** Symmetry break lemma for " << tn << " (" << sid << "==" << tti->d_max_dom_const_sort.get() << ") : "; + Trace("sym-break-lemma") << lem << std::endl; + d_pending_lemmas.push_back( lem ); + } + } + invalid = true; + } + if( invalid ){ + ti->d_first_active.set( ti->d_first_active + 1 ); + fa = ti->getFirstActive(getEqualityEngine()); + } + } + } +} + +void SubsortSymmetryBreaker::printDebugSubSortInfo( const char * c, TypeNode tn, int sid ) { + Trace(c) << "SubSortInfo( " << tn << ", " << sid << " ) = " << std::endl; + Trace(c) << " Domain constants : "; + SubSortInfo * ti = getSubSortInfo( tn, sid ); + for( NodeList::const_iterator it = ti->d_dom_constants.begin(); it != ti->d_dom_constants.end(); ++it ){ + Node dc = *it; + Trace(c) << dc << " "; + } + Trace(c) << std::endl; + Trace(c) << " First active node : " << ti->getFirstActive(getEqualityEngine()) << std::endl; +} + +bool SubsortSymmetryBreaker::check( Theory::Effort level ) { + + Trace("sym-break-debug") << "SymBreak : check " << level << std::endl; + /* + while( d_fact_index.get()<d_fact_list.size() ){ + Node f = d_fact_list[d_fact_index.get()]; + d_fact_index.set( d_fact_index.get() + 1 ); + if( f.getKind()==EQUAL ){ + merge( f[0], f[1] ); + }else if( f.getKind()==NOT && f[0].getKind()==EQUAL ){ + assertDisequal( f[0][0], f[0][1] ); + }else{ + newEqClass( f ); + } + } + */ + Trace("sym-break-debug") << "SymBreak : update first actives" << std::endl; + for( std::map< TypeNode, std::vector< int > >::iterator it = d_sub_sorts.begin(); it != d_sub_sorts.end(); ++it ){ + int card = getStrongSolver()->getCardinality( it->first ); + for( unsigned i=0; i<it->second.size(); i++ ){ + //check if the first active is disequal from all domain constants + processFirstActive( it->first, it->second[i], card ); + } + } + + + Trace("sym-break-debug") << "SymBreak : finished check, now flush lemmas... (#lemmas = " << d_pending_lemmas.size() << ")" << std::endl; + //flush pending lemmas + if( !d_pending_lemmas.empty() ){ + for( unsigned i=0; i<d_pending_lemmas.size(); i++ ){ + getStrongSolver()->getOutputChannel().lemma( d_pending_lemmas[i] ); + ++( getStrongSolver()->d_statistics.d_sym_break_lemmas ); + } + d_pending_lemmas.clear(); + return true; + }else{ + return false; + } +} + + + +} + diff --git a/src/theory/quantifiers/symmetry_breaking.h b/src/theory/quantifiers/symmetry_breaking.h new file mode 100755 index 000000000..05461d378 --- /dev/null +++ b/src/theory/quantifiers/symmetry_breaking.h @@ -0,0 +1,118 @@ +/********************* */ +/*! \file symmetry_breaking.h + ** \verbatim + ** Original author: ajreynol + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009-2012 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Pre-process step for first-order reasoning + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__QUANT_SYMMETRY_BREAKING_H +#define __CVC4__QUANT_SYMMETRY_BREAKING_H + +#include "theory/theory.h" + +#include <iostream> +#include <string> +#include <vector> +#include <map> +#include "expr/node.h" +#include "expr/type_node.h" + +#include "util/sort_inference.h" +#include "context/context.h" +#include "context/context_mm.h" +#include "context/cdchunk_list.h" + +namespace CVC4 { +namespace theory { + +namespace uf { + class StrongSolverTheoryUF; +} + +class SubsortSymmetryBreaker { + typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap; + typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap; + typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap; + //typedef context::CDChunkList<int> IntList; + typedef context::CDList<Node> NodeList; + typedef context::CDHashMap<Node, NodeList*, NodeHashFunction> NodeListMap; +private: + /** quantifiers engine */ + QuantifiersEngine* d_qe; + eq::EqualityEngine * getEqualityEngine(); + bool areDisequal( Node n1, Node n2 ); + bool areEqual( Node n1, Node n2 ); + Node getRepresentative( Node n ); + uf::StrongSolverTheoryUF * getStrongSolver(); + std::vector< Node > d_unit_lemmas; + Node d_true; + context::CDO< bool > d_conflict; +public: + SubsortSymmetryBreaker( QuantifiersEngine* qe, context::Context* c ); + ~SubsortSymmetryBreaker(){} + +private: + class TypeInfo { + public: + TypeInfo( context::Context* c ); + context::CDO< int > d_max_dom_const_sort; + context::CDO< bool > d_has_dom_const_sort; + }; + class SubSortInfo { + public: + SubSortInfo( context::Context* c ); + //list of all nodes from this (sub)type + std::vector< Node > d_nodes; + //the current domain constants for this (sub)type + NodeList d_dom_constants; + //# nodes in d_nodes that have been domain constants, size of this distinct # of domain constants seen + unsigned d_dc_nodes; + //the node we are currently watching to become a domain constant + context::CDO< int > d_first_active; + //node to id + std::map< Node, unsigned > d_node_to_id; + Node getBaseConstant() { return d_nodes.empty() ? Node::null() : d_nodes[0]; } + bool hasDomainConstant( Node n ); + unsigned getNumDomainConstants(); + Node getDomainConstant( int i ); + Node getFirstActive(eq::EqualityEngine * ee); + }; + std::map< TypeNode, std::vector< int > > d_sub_sorts; + std::map< int, TypeNode > d_sid_to_type; + std::map< TypeNode, TypeInfo * > d_t_info; + std::map< int, SubSortInfo * > d_type_info; + + TypeInfo * getTypeInfo( TypeNode tn ); + SubSortInfo * getSubSortInfo( TypeNode tn, int sid ); + + void processFirstActive( TypeNode tn, int sid, int curr_card ); +private: + //void printDebugNodeInfo( const char * c, Node n ); + void printDebugSubSortInfo( const char * c, TypeNode tn, int sid ); + /** fact list */ + std::vector< Node > d_pending_lemmas; + std::vector< Node > d_lemmas; +public: + /** new node */ + void newEqClass( Node n ); + /** merge */ + void merge( Node a, Node b ); + /** assert disequal */ + void assertDisequal( Node a, Node b ); + /** check */ + bool check( Theory::Effort level ); +}; + +} +} + +#endif diff --git a/src/theory/quantifiers/term_database.cpp b/src/theory/quantifiers/term_database.cpp index 3153a3c64..e18a4e0dc 100644 --- a/src/theory/quantifiers/term_database.cpp +++ b/src/theory/quantifiers/term_database.cpp @@ -74,7 +74,7 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant ){ //if this is an atomic trigger, consider adding it //Call the children? if( inst::Trigger::isAtomicTrigger( n ) ){ - if( !n.hasAttribute(InstConstantAttribute()) ){ + if( !TermDb::hasInstConstAttr(n) ){ Trace("term-db") << "register term in db " << n << std::endl; //std::cout << "register trigger term " << n << std::endl; Node op = n.getOperator(); @@ -117,7 +117,7 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant ){ } } }else{ - if( options::efficientEMatching() && !n.hasAttribute(InstConstantAttribute())){ + 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; @@ -167,7 +167,7 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant ){ while( !eqc.isFinished() ){ Node en = (*eqc); computeModelBasisArgAttribute( en ); - if( en.getKind()==APPLY_UF && !en.hasAttribute(InstConstantAttribute()) ){ + if( en.getKind()==APPLY_UF && !TermDb::hasInstConstAttr(en) ){ if( !en.getAttribute(NoMatchAttribute()) ){ Node op = en.getOperator(); if( !d_pred_map_trie[i][op].addTerm( d_quantEngine, en ) ){ @@ -194,14 +194,20 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant ){ Node TermDb::getModelBasisTerm( TypeNode tn, int i ){ if( d_model_basis_term.find( tn )==d_model_basis_term.end() ){ Node mbt; - if( options::fmfFreshDistConst() || d_type_map[ tn ].empty() ){ - std::stringstream ss; - ss << Expr::setlanguage(options::outputLanguage()); - ss << "e_" << tn; - mbt = NodeManager::currentNM()->mkSkolem( ss.str(), tn, "is a model basis term" ); - Trace("mkVar") << "ModelBasis:: Make variable " << mbt << " : " << tn << std::endl; + if( tn.isInteger() || tn.isReal() ){ + mbt = NodeManager::currentNM()->mkConst( Rational( 0 ) ); + }else if( !tn.isSort() ){ + mbt = tn.mkGroundTerm(); }else{ - mbt = d_type_map[ tn ][ 0 ]; + if( options::fmfFreshDistConst() || d_type_map[ tn ].empty() ){ + std::stringstream ss; + ss << Expr::setlanguage(options::outputLanguage()); + ss << "e_" << tn; + mbt = NodeManager::currentNM()->mkSkolem( ss.str(), tn, "is a model basis term" ); + Trace("mkVar") << "ModelBasis:: Make variable " << mbt << " : " << tn << std::endl; + }else{ + mbt = d_type_map[ tn ][ 0 ]; + } } ModelBasisAttribute mba; mbt.setAttribute(mba,true); @@ -254,8 +260,7 @@ void TermDb::computeModelBasisArgAttribute( Node n ){ //determine if it has model basis attribute for( int j=0; j<(int)n.getNumChildren(); j++ ){ if( n[j].getAttribute(ModelBasisAttribute()) ){ - val = 1; - break; + val++; } } ModelBasisArgAttribute mbaa; @@ -276,33 +281,75 @@ void TermDb::makeInstantiationConstantsFor( Node f ){ //set the var number attribute InstVarNumAttribute ivna; ic.setAttribute(ivna,i); + InstConstantAttribute ica; + ic.setAttribute(ica,f); + //also set the no-match attribute + NoMatchAttribute nma; + ic.setAttribute(nma,true); } } } -void TermDb::setInstantiationConstantAttr( Node n, Node f ){ - if( !n.hasAttribute(InstConstantAttribute()) ){ - bool setAttr = false; - if( n.getKind()==INST_CONSTANT ){ - setAttr = true; - }else{ - for( int i=0; i<(int)n.getNumChildren(); i++ ){ - setInstantiationConstantAttr( n[i], f ); - if( n[i].hasAttribute(InstConstantAttribute()) ){ - setAttr = true; - } + +Node TermDb::getInstConstAttr( Node n ) { + if (!n.hasAttribute(InstConstantAttribute()) ){ + Node f; + for( int i=0; i<(int)n.getNumChildren(); i++ ){ + f = getInstConstAttr(n[i]); + if( !f.isNull() ){ + break; } } - if( setAttr ){ - InstConstantAttribute ica; - n.setAttribute(ica,f); + InstConstantAttribute ica; + n.setAttribute(ica,f); + if( !f.isNull() ){ //also set the no-match attribute NoMatchAttribute nma; n.setAttribute(nma,true); } } + return n.getAttribute(InstConstantAttribute()); +} + +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() ){ + bvs.push_back( n ); + } + }else{ + for( unsigned i=0; i<n.getNumChildren(); i++) { + getBoundVars(n[i], bvs); + } + } } + /** get the i^th instantiation constant of f */ Node TermDb::getInstantiationConstant( Node f, int i ) const { std::map< Node, std::vector< Node > >::const_iterator it = d_inst_constants.find( f ); @@ -348,7 +395,6 @@ Node TermDb::getCounterexampleLiteral( Node f ){ //otherwise, ensure literal Node ceLit = d_quantEngine->getValuation().ensureLiteral( ceBody.notNode() ); d_ce_lit[ f ] = ceLit; - setInstantiationConstantAttr( ceLit, f ); } return d_ce_lit[ f ]; } @@ -362,7 +408,6 @@ Node TermDb::convertNodeToPattern( Node n, Node f, const std::vector<Node> & var Node n2 = n.substitute( vars.begin(), vars.end(), inst_constants.begin(), inst_constants.end() ); - setInstantiationConstantAttr( n2, f ); return n2; } @@ -390,16 +435,19 @@ Node TermDb::getSkolemizedBody( Node f ){ Node TermDb::getFreeVariableForInstConstant( Node n ){ TypeNode tn = n.getType(); if( d_free_vars.find( tn )==d_free_vars.end() ){ - //if integer or real, make zero - if( tn.isInteger() || tn.isReal() ){ - Rational z(0); - d_free_vars[tn] = NodeManager::currentNM()->mkConst( z ); - }else{ - if( d_type_map[ tn ].empty() ){ - d_free_vars[tn] = NodeManager::currentNM()->mkSkolem( "freevar_$$", tn, "is a free variable created by termdb" ); - Trace("mkVar") << "FreeVar:: Make variable " << d_free_vars[tn] << " : " << tn << std::endl; + for( unsigned i=0; i<d_type_map[ tn ].size(); i++ ){ + if( !quantifiers::TermDb::hasInstConstAttr(d_type_map[ tn ][ i ]) ){ + d_free_vars[tn] = d_type_map[ tn ][ i ]; + } + } + if( d_free_vars.find( tn )==d_free_vars.end() ){ + //if integer or real, make zero + if( tn.isInteger() || tn.isReal() ){ + Rational z(0); + d_free_vars[tn] = NodeManager::currentNM()->mkConst( z ); }else{ - d_free_vars[tn] = d_type_map[ tn ][ 0 ]; + d_free_vars[tn] = NodeManager::currentNM()->mkSkolem( "freevar_$$", tn, "is a free variable created by termdb" ); + Trace("mkVar") << "FreeVar:: Make variable " << d_free_vars[tn] << " : " << tn << std::endl; } } } diff --git a/src/theory/quantifiers/term_database.h b/src/theory/quantifiers/term_database.h index 231d0ee9e..1688479f3 100644 --- a/src/theory/quantifiers/term_database.h +++ b/src/theory/quantifiers/term_database.h @@ -59,6 +59,19 @@ 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; + class QuantifiersEngine; @@ -83,10 +96,15 @@ public: };/* class TermArgTrie */ +namespace fmcheck { + class FullModelChecker; +} + class TermDb { friend class ::CVC4::theory::QuantifiersEngine; friend class ::CVC4::theory::inst::Trigger; friend class ::CVC4::theory::rrinst::Trigger; + friend class ::CVC4::theory::quantifiers::fmcheck::FullModelChecker; private: /** reference to the quantifiers engine */ QuantifiersEngine* d_quantEngine; @@ -181,9 +199,15 @@ public: Node convertNodeToPattern( Node n, Node f, const std::vector<Node> & vars, const std::vector<Node> & nvars); - /** set instantiation constant attr */ - void setInstantiationConstantAttr( Node n, Node f ); + static Node getInstConstAttr( Node n ); + 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 private: /** map from universal quantifiers to the list of skolem constants */ diff --git a/src/theory/quantifiers/theory_quantifiers.cpp b/src/theory/quantifiers/theory_quantifiers.cpp index 9843cd09e..066282c2c 100644 --- a/src/theory/quantifiers/theory_quantifiers.cpp +++ b/src/theory/quantifiers/theory_quantifiers.cpp @@ -65,7 +65,7 @@ void TheoryQuantifiers::notifyEq(TNode lhs, TNode rhs) { void TheoryQuantifiers::preRegisterTerm(TNode n) { Debug("quantifiers-prereg") << "TheoryQuantifiers::preRegisterTerm() " << n << endl; - if( n.getKind()==FORALL && !n.hasAttribute(InstConstantAttribute()) ){ + if( n.getKind()==FORALL && !TermDb::hasInstConstAttr(n) ){ getQuantifiersEngine()->registerQuantifier( n ); } } @@ -149,7 +149,7 @@ Node TheoryQuantifiers::getNextDecisionRequest(){ void TheoryQuantifiers::assertUniversal( Node n ){ Assert( n.getKind()==FORALL ); - if( !n.hasAttribute(InstConstantAttribute()) ){ + if( options::recurseCbqi() || !TermDb::hasInstConstAttr(n) ){ getQuantifiersEngine()->registerQuantifier( n ); getQuantifiersEngine()->assertNode( n ); } @@ -157,13 +157,13 @@ void TheoryQuantifiers::assertUniversal( Node n ){ void TheoryQuantifiers::assertExistential( Node n ){ Assert( n.getKind()== NOT && n[0].getKind()==FORALL ); - if( !n[0].hasAttribute(InstConstantAttribute()) ){ + if( options::recurseCbqi() || !TermDb::hasInstConstAttr(n[0]) ){ if( d_skolemized.find( n )==d_skolemized.end() ){ Node body = getQuantifiersEngine()->getTermDatabase()->getSkolemizedBody( n[0] ); NodeBuilder<> nb(kind::OR); nb << n[0] << body.notNode(); Node lem = nb; - Debug("quantifiers-sk") << "Skolemize lemma : " << lem << std::endl; + Trace("quantifiers-sk") << "Skolemize lemma : " << lem << std::endl; d_out->lemma( lem ); d_skolemized[n] = true; } diff --git a/src/theory/quantifiers/trigger.cpp b/src/theory/quantifiers/trigger.cpp index cab94fb5c..39063942d 100644 --- a/src/theory/quantifiers/trigger.cpp +++ b/src/theory/quantifiers/trigger.cpp @@ -28,8 +28,6 @@ using namespace CVC4::context; using namespace CVC4::theory; using namespace CVC4::theory::inst; -//#define NESTED_PATTERN_SELECTION - /** trigger class constructor */ Trigger::Trigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, int matchOption, bool smartTriggers ) : d_quantEngine( qe ), d_f( f ){ @@ -46,7 +44,7 @@ d_quantEngine( qe ), d_f( f ){ d_mg = new InstMatchGeneratorSimple( f, d_nodes[0] ); }else{ d_mg = InstMatchGenerator::mkInstMatchGenerator( d_nodes[0], qe ); - d_mg->setActiveAdd(); + d_mg->setActiveAdd(true); } }else{ d_mg = new InstMatchGeneratorMulti( f, d_nodes, qe, matchOption ); @@ -55,7 +53,7 @@ d_quantEngine( qe ), d_f( f ){ } }else{ d_mg = InstMatchGenerator::mkInstMatchGenerator( d_nodes, qe ); - d_mg->setActiveAdd(); + d_mg->setActiveAdd(true); } if( d_nodes.size()==1 ){ if( isSimpleTrigger( d_nodes[0] ) ){ @@ -75,6 +73,7 @@ d_quantEngine( qe ), d_f( f ){ qe->getTermDatabase()->registerTrigger( this, d_nodes[i].getOperator() ); } } + Trace("trigger-debug") << "Finished making trigger." << std::endl; } void Trigger::resetInstantiationRound(){ @@ -126,7 +125,7 @@ Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >& 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( v.getAttribute(InstConstantAttribute())==f ){ + if( quantifiers::TermDb::getInstConstAttr(v)==f ){ if( vars.find( v )==vars.end() ){ varCount++; vars[ v ] = true; @@ -146,6 +145,12 @@ Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >& } } if( varCount<f[0].getNumChildren() ){ + Trace("trigger-debug") << "Don't consider trigger since it does not contain all variables in " << f << std::endl; + for( unsigned i=0; i<nodes.size(); i++) { + Trace("trigger-debug") << nodes[i] << " "; + } + Trace("trigger-debug") << std::endl; + //do not generate multi-trigger if it does not contain all variables return NULL; }else{ @@ -225,7 +230,7 @@ bool Trigger::isUsableTrigger( std::vector< Node >& nodes, Node f ){ } bool Trigger::isUsable( Node n, Node f ){ - if( n.getAttribute(InstConstantAttribute())==f ){ + if( quantifiers::TermDb::getInstConstAttr(n)==f ){ if( isAtomicTrigger( n ) ){ for( int i=0; i<(int)n.getNumChildren(); i++ ){ if( !isUsable( n[i], f ) ){ @@ -249,11 +254,71 @@ bool Trigger::isUsable( Node n, Node f ){ } } +bool nodeContainsVar( Node n, Node v ){ + if( n==v) { + return true; + }else{ + for( unsigned i=0; i<n.getNumChildren(); i++) { + if( nodeContainsVar(n[i], v) ){ + return true; + } + } + return false; + } +} + +Node Trigger::getIsUsableTrigger( Node n, Node f, bool pol, bool hasPol ) { + if( options::relationalTriggers() ){ + if( n.getKind()==EQUAL || n.getKind()==IFF || n.getKind()==GEQ ){ + Node rtr; + for( unsigned i=0; i<2; i++) { + unsigned j = (i==0) ? 1 : 0; + if( n[j].getKind()==INST_CONSTANT && isUsableTrigger(n[i], f) && !nodeContainsVar( n[i], n[j] ) ) { + rtr = n; + break; + } + } + if( n[0].getType().isInteger() ){ + //try to rearrange? + std::map< Node, Node > m; + if (QuantArith::getMonomialSumLit(n, m) ){ + for( std::map< Node, Node >::iterator it = m.begin(); it!=m.end(); ++it ){ + if( !it->first.isNull() && it->first.getKind()==INST_CONSTANT ){ + Node veq; + if( QuantArith::isolate( it->first, m, veq, n.getKind() ) ){ + int vti = veq[0]==it->first ? 1 : 0; + if( isUsableTrigger(veq[vti], f) && !nodeContainsVar( veq[vti], veq[vti==0 ? 1 : 0]) ){ + rtr = veq; + } + } + } + } + } + } + if( !rtr.isNull() ){ + Trace("relational-trigger") << "Relational trigger : " << std::endl; + Trace("relational-trigger") << " " << rtr << " (from " << n << ")" << std::endl; + Trace("relational-trigger") << " in quantifier " << f << std::endl; + if( hasPol ){ + Trace("relational-trigger") << " polarity : " << pol << std::endl; + } + Node rtr2 = (hasPol && pol) ? rtr.negate() : rtr; + return rtr2; + } + } + } + bool usable = quantifiers::TermDb::getInstConstAttr(n)==f && isAtomicTrigger( n ) && isUsable( n, f ); + Trace("usable") << n << " usable : " << (quantifiers::TermDb::getInstConstAttr(n)==f) << " " << isAtomicTrigger( n ) << " " << isUsable( n, f ) << std::endl; + if( usable ){ + return n; + }else{ + return Node::null(); + } +} + bool Trigger::isUsableTrigger( Node n, Node f ){ - //return n.getAttribute(InstConstantAttribute())==f && n.getKind()==APPLY_UF; - bool usable = n.getAttribute(InstConstantAttribute())==f && isAtomicTrigger( n ) && isUsable( n, f ); - Trace("usable") << n << " usable : " << usable << std::endl; - return usable; + Node nu = getIsUsableTrigger(n,f); + return !nu.isNull(); } bool Trigger::isAtomicTrigger( Node n ){ @@ -263,7 +328,7 @@ bool Trigger::isAtomicTrigger( Node n ){ bool Trigger::isSimpleTrigger( Node n ){ if( isAtomicTrigger( n ) ){ for( int i=0; i<(int)n.getNumChildren(); i++ ){ - if( n[i].getKind()!=INST_CONSTANT && n[i].hasAttribute(InstConstantAttribute()) ){ + if( n[i].getKind()!=INST_CONSTANT && quantifiers::TermDb::hasInstConstAttr(n[i]) ){ return false; } } @@ -274,55 +339,51 @@ bool Trigger::isSimpleTrigger( Node n ){ } -bool Trigger::collectPatTerms2( QuantifiersEngine* qe, Node f, Node n, std::map< Node, bool >& patMap, int tstrt ){ +bool Trigger::collectPatTerms2( QuantifiersEngine* qe, Node f, Node n, std::map< Node, bool >& patMap, int tstrt, bool pol, bool hasPol ){ if( patMap.find( n )==patMap.end() ){ patMap[ n ] = false; + bool newHasPol = (n.getKind()==IFF || n.getKind()==XOR) ? false : hasPol; + bool newPol = n.getKind()==NOT ? !pol : pol; 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 ) ){ + bool newPol2 = (n.getKind()==IMPLIES && i==0) ? !newPol : newPol; + bool newHasPol2 = (n.getKind()==ITE && i==0) ? false : newHasPol; + if( collectPatTerms2( qe, f, n[i], patMap, tstrt, newPol2, newHasPol2 ) ){ retVal = true; } } if( retVal ){ return true; - }else if( isUsableTrigger( n, f ) ){ - patMap[ n ] = true; - return true; }else{ - return false; + Node nu = getIsUsableTrigger( n, f, pol, hasPol ); + if( !nu.isNull() ){ + patMap[ nu ] = true; + return true; + }else{ + return false; + } } } }else{ bool retVal = false; - if( isUsableTrigger( n, f ) ){ - patMap[ n ] = true; + Node nu = getIsUsableTrigger( n, f, pol, hasPol ); + if( !nu.isNull() ){ + patMap[ nu ] = 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{ + if( n.getKind()!=FORALL ){ for( int i=0; i<(int)n.getNumChildren(); i++ ){ - if( collectPatTerms2( qe, f, n[i], patMap, tstrt ) ){ + bool newPol2 = (n.getKind()==IMPLIES && i==0) ? !newPol : newPol; + bool newHasPol2 = (n.getKind()==ITE && i==0) ? false : newHasPol; + if( collectPatTerms2( qe, f, n[i], patMap, tstrt, newPol2, newHasPol2 ) ){ retVal = true; } } @@ -367,7 +428,7 @@ void Trigger::collectPatTerms( QuantifiersEngine* qe, Node f, Node n, std::vecto } } } - collectPatTerms2( qe, f, n, patMap, tstrt ); + collectPatTerms2( qe, f, n, patMap, tstrt, true, true ); for( std::map< Node, bool >::iterator it = patMap.begin(); it != patMap.end(); ++it ){ if( it->second ){ patTerms.push_back( it->first ); @@ -380,9 +441,9 @@ bool Trigger::isArithmeticTrigger( Node f, Node n, std::map< Node, Node >& coeff Assert( coeffs.empty() ); NodeBuilder<> t(kind::PLUS); for( int i=0; i<(int)n.getNumChildren(); i++ ){ - if( n[i].hasAttribute(InstConstantAttribute()) ){ + if( quantifiers::TermDb::hasInstConstAttr(n[i]) ){ if( n[i].getKind()==INST_CONSTANT ){ - if( n[i].getAttribute(InstConstantAttribute())==f ){ + if( quantifiers::TermDb::getInstConstAttr(n[i])==f ){ coeffs[ n[i] ] = Node::null(); }else{ coeffs.clear(); @@ -405,13 +466,13 @@ bool Trigger::isArithmeticTrigger( Node f, Node n, std::map< Node, Node >& coeff } return true; }else if( n.getKind()==MULT ){ - if( n[0].getKind()==INST_CONSTANT && n[0].getAttribute(InstConstantAttribute())==f ){ - if( !n[1].hasAttribute(InstConstantAttribute()) ){ + if( n[0].getKind()==INST_CONSTANT && quantifiers::TermDb::getInstConstAttr(n[0])==f ){ + if( !quantifiers::TermDb::hasInstConstAttr(n[1]) ){ coeffs[ n[0] ] = n[1]; return true; } - }else if( n[1].getKind()==INST_CONSTANT && n[1].getAttribute(InstConstantAttribute())==f ){ - if( !n[0].hasAttribute(InstConstantAttribute()) ){ + }else if( n[1].getKind()==INST_CONSTANT && quantifiers::TermDb::getInstConstAttr(n[1])==f ){ + if( !quantifiers::TermDb::hasInstConstAttr(n[0]) ){ coeffs[ n[1] ] = n[0]; return true; } diff --git a/src/theory/quantifiers/trigger.h b/src/theory/quantifiers/trigger.h index ca9124751..28fb2acda 100644 --- a/src/theory/quantifiers/trigger.h +++ b/src/theory/quantifiers/trigger.h @@ -92,8 +92,9 @@ public: private: /** is subterm of trigger usable */ static bool isUsable( Node n, Node f ); + static Node getIsUsableTrigger( Node n, Node f, bool pol = true, bool hasPol = false ); /** 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 ); + static bool collectPatTerms2( QuantifiersEngine* qe, Node f, Node n, std::map< Node, bool >& patMap, int tstrt, bool pol, bool hasPol ); public: //different strategies for choosing trigger terms enum { diff --git a/src/theory/quantifiers_engine.cpp b/src/theory/quantifiers_engine.cpp index 0bb0f1f79..0fe50aad6 100644..100755 --- a/src/theory/quantifiers_engine.cpp +++ b/src/theory/quantifiers_engine.cpp @@ -27,6 +27,9 @@ #include "theory/quantifiers/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/uf/options.h" using namespace std; using namespace CVC4; @@ -47,7 +50,11 @@ d_lemmas_produced_c(u){ d_hasAddedLemma = false; //the model object - d_model = new quantifiers::FirstOrderModel( c, "FirstOrderModel" ); + if( options::fmfFullModelCheck() ){ + d_model = new quantifiers::fmcheck::FirstOrderModelFmc( this, c, "FirstOrderModelFmc" ); + }else{ + d_model = new quantifiers::FirstOrderModelIG( c, "FirstOrderModelIG" ); + } //add quantifiers modules if( !options::finiteModelFind() || options::fmfInstEngine() ){ @@ -57,11 +64,24 @@ d_lemmas_produced_c(u){ }else{ d_inst_engine = NULL; } - if( options::finiteModelFind() ){ + if( options::finiteModelFind() ){ d_model_engine = new quantifiers::ModelEngine( c, this ); d_modules.push_back( d_model_engine ); + if( options::fmfBoundInt() ){ + d_bint = new quantifiers::BoundedIntegers( c, this ); + d_modules.push_back( d_bint ); + }else{ + d_bint = NULL; + } }else{ d_model_engine = NULL; + d_bint = NULL; + } + if( options::rewriteRulesAsAxioms() ){ + d_rr_engine = new quantifiers::RewriteEngine( c, this ); + d_modules.push_back(d_rr_engine); + }else{ + d_rr_engine = NULL; } //options @@ -251,7 +271,6 @@ void QuantifiersEngine::computeTermVector( Node f, InstMatch& m, std::vector< No bool QuantifiersEngine::addInstantiation( Node f, std::vector< Node >& vars, std::vector< Node >& terms ){ Assert( f.getKind()==FORALL ); - Assert( !f.hasAttribute(InstConstantAttribute()) ); Assert( vars.size()==terms.size() ); Node body = getInstantiation( f, vars, terms ); //make the lemma @@ -266,7 +285,7 @@ bool QuantifiersEngine::addInstantiation( Node f, std::vector< Node >& vars, std Trace("inst") << "*** Instantiate " << f << " with " << std::endl; uint64_t maxInstLevel = 0; for( int i=0; i<(int)terms.size(); i++ ){ - if( terms[i].hasAttribute(InstConstantAttribute()) ){ + if( quantifiers::TermDb::hasInstConstAttr(terms[i]) ){ Debug("inst")<< "***& Bad Instantiate " << f << " with " << std::endl; for( int i=0; i<(int)terms.size(); i++ ){ Debug("inst") << " " << terms[i] << std::endl; @@ -432,8 +451,6 @@ bool QuantifiersEngine::addSplit( Node n, bool reqPhase, bool reqPhasePol ){ } bool QuantifiersEngine::addSplitEquality( Node n1, Node n2, bool reqPhase, bool reqPhasePol ){ - //Assert( !n1.hasAttribute(InstConstantAttribute()) ); - //Assert( !n2.hasAttribute(InstConstantAttribute()) ); //Assert( !areEqual( n1, n2 ) ); //Assert( !areDisequal( n1, n2 ) ); Kind knd = n1.getType()==NodeManager::currentNM()->booleanType() ? IFF : EQUAL; @@ -463,7 +480,6 @@ void QuantifiersEngine::getPhaseReqTerms( Node f, std::vector< Node >& nodes ){ if( d_phase_reqs[f]->isPhaseReq( nodes[i] ) ){ bool preq = d_phase_reqs[f]->getPhaseReq( nodes[i] ); nodes[i] = NodeManager::currentNM()->mkNode( IFF, nodes[i], NodeManager::currentNM()->mkConst<bool>(preq) ); - d_term_db->setInstantiationConstantAttr( nodes[i], f ); nodeChanged = true; } //else if( qe->isPhaseReqEquality( f, trNodes[i] ) ){ @@ -617,35 +633,48 @@ Node EqualityQueryQuantifiersEngine::getInternalRepresentative( Node a, Node f, }else{ int sortId = 0; if( optInternalRepSortInference() ){ + //if( options::ufssSymBreak() ){ sortId = d_qe->getTheoryEngine()->getSortInference()->getSortId( f, f[0][index] ); } if( d_int_rep[sortId].find( r )==d_int_rep[sortId].end() ){ - std::vector< Node > eqc; - getEquivalenceClass( r, eqc ); //find best selection for representative Node r_best; - int r_best_score = -1; - for( size_t i=0; i<eqc.size(); i++ ){ - int score = getRepScore( eqc[i], f, index ); - if( optInternalRepSortInference() ){ - int e_sortId = d_qe->getTheoryEngine()->getSortInference()->getSortId( eqc[i]); - if( score>=0 && e_sortId!=sortId ){ - score += 100; - } + if( options::fmfRelevantDomain() ){ + Trace("internal-rep-debug") << "Consult relevant domain to mkRep " << r << std::endl; + r_best = d_qe->getModelEngine()->getRelevantDomain()->getRelevantTerm( f, index, r ); + Trace("internal-rep-debug") << "Returned " << r_best << " " << r << std::endl; + }else{ + std::vector< Node > eqc; + getEquivalenceClass( r, eqc ); + int r_best_score = -1; + for( size_t i=0; i<eqc.size(); i++ ){ + int score = getRepScore( eqc[i], f, index ); + if( !options::cbqi() || !quantifiers::TermDb::hasInstConstAttr(eqc[i]) ){ + if( optInternalRepSortInference() ){ + int e_sortId = d_qe->getTheoryEngine()->getSortInference()->getSortId( eqc[i]); + if( score>=0 && e_sortId!=sortId ){ + score += 100; + } + } + //score prefers earliest use of this term as a representative + if( r_best.isNull() || ( score>=0 && ( r_best_score<0 || score<r_best_score ) ) ){ + r_best = eqc[i]; + r_best_score = score; + } + } } - //score prefers earliest use of this term as a representative - if( r_best.isNull() || ( score>=0 && ( r_best_score<0 || score<r_best_score ) ) ){ - r_best = eqc[i]; - r_best_score = score; + if( r_best.isNull() ){ + Node ic = d_qe->getTermDatabase()->getInstantiationConstant( f, index ); + r_best = d_qe->getTermDatabase()->getFreeVariableForInstConstant( ic ); + } + //now, make sure that no other member of the class is an instance + if( !optInternalRepSortInference() ){ + r_best = getInstance( r_best, eqc ); + } + //store that this representative was chosen at this point + if( d_rep_score.find( r_best )==d_rep_score.end() ){ + d_rep_score[ r_best ] = d_reset_count; } - } - //now, make sure that no other member of the class is an instance - if( !optInternalRepSortInference() ){ - r_best = getInstance( r_best, eqc ); - } - //store that this representative was chosen at this point - if( d_rep_score.find( r_best )==d_rep_score.end() ){ - d_rep_score[ r_best ] = d_reset_count; } d_int_rep[sortId][r] = r_best; if( r_best!=a ){ @@ -723,4 +752,4 @@ int EqualityQueryQuantifiersEngine::getRepScore( Node n, Node f, int index ){ bool EqualityQueryQuantifiersEngine::optInternalRepSortInference() { return false; //shown to be not effective -}
\ No newline at end of file +} diff --git a/src/theory/quantifiers_engine.h b/src/theory/quantifiers_engine.h index bfa19bb98..b075f7be8 100644 --- a/src/theory/quantifiers_engine.h +++ b/src/theory/quantifiers_engine.h @@ -56,14 +56,17 @@ public: virtual void assertNode( Node n ) = 0; virtual void propagate( Theory::Effort level ){} virtual Node getNextDecisionRequest() { return TNode::null(); } - virtual Node explain(TNode n) = 0; + virtual Node explain(TNode n) { return TNode::null(); } };/* class QuantifiersModule */ namespace quantifiers { - class InstantiationEngine; - class ModelEngine; class TermDb; class FirstOrderModel; + //modules + class InstantiationEngine; + class ModelEngine; + class BoundedIntegers; + class RewriteEngine; }/* CVC4::theory::quantifiers */ namespace inst { @@ -80,6 +83,7 @@ class EqualityQueryQuantifiersEngine; class QuantifiersEngine { friend class quantifiers::InstantiationEngine; friend class quantifiers::ModelEngine; + friend class quantifiers::RewriteEngine; friend class inst::InstMatch; private: typedef context::CDHashMap< Node, bool, NodeHashFunction > BoolMap; @@ -87,10 +91,6 @@ private: TheoryEngine* d_te; /** vector of modules for quantifiers */ std::vector< QuantifiersModule* > d_modules; - /** instantiation engine */ - quantifiers::InstantiationEngine* d_inst_engine; - /** model engine */ - quantifiers::ModelEngine* d_model_engine; /** equality query class */ EqualityQueryQuantifiersEngine* d_eq_query; /** for computing relevance of quantifiers */ @@ -99,6 +99,14 @@ private: std::map< Node, QuantPhaseReq* > d_phase_reqs; /** efficient e-matcher */ EfficientEMatcher* d_eem; + /** instantiation engine */ + quantifiers::InstantiationEngine* d_inst_engine; + /** model engine */ + quantifiers::ModelEngine* d_model_engine; + /** bounded integers utility */ + quantifiers::BoundedIntegers * d_bint; + /** rewrite rules utility */ + quantifiers::RewriteEngine * d_rr_engine; private: /** list of all quantifiers seen */ std::vector< Node > d_quants; @@ -155,6 +163,8 @@ public: 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; } public: /** initialize */ void finishInit(); diff --git a/src/theory/rep_set.cpp b/src/theory/rep_set.cpp index f6da32bbf..800e007f7 100644 --- a/src/theory/rep_set.cpp +++ b/src/theory/rep_set.cpp @@ -14,6 +14,7 @@ #include "theory/rep_set.h" #include "theory/type_enumerator.h" +#include "theory/quantifiers/bounded_integers.h" using namespace std; using namespace CVC4; @@ -94,26 +95,38 @@ void RepSet::toStream(std::ostream& out){ } -RepSetIterator::RepSetIterator( RepSet* rs ) : d_rep_set( rs ){ +RepSetIterator::RepSetIterator( QuantifiersEngine * qe, RepSet* rs ) : d_qe(qe), d_rep_set( rs ){ d_incomplete = false; +} +int RepSetIterator::domainSize( int i ) { + Assert(i>=0); + if( d_enum_type[i]==ENUM_DOMAIN_ELEMENTS ){ + return d_domain[i].size(); + }else{ + return d_domain[i][0]; + } } bool RepSetIterator::setQuantifier( Node f ){ + Trace("rsi") << "Make rsi for " << f << std::endl; Assert( d_types.empty() ); //store indicies for( size_t i=0; i<f[0].getNumChildren(); i++ ){ d_types.push_back( f[0][i].getType() ); } + d_owner = f; return initialize(); } bool RepSetIterator::setFunctionDomain( Node op ){ + Trace("rsi") << "Make rsi for " << op << std::endl; Assert( d_types.empty() ); TypeNode tn = op.getType(); for( size_t i=0; i<tn.getNumChildren()-1; i++ ){ d_types.push_back( tn[i] ); } + d_owner = op; return initialize(); } @@ -132,22 +145,80 @@ bool RepSetIterator::initialize(){ Trace("mkVar") << "RepSetIterator:: Make variable " << var << " : " << tn << std::endl; d_rep_set->add( var ); } - }else if( tn.isInteger() || tn.isReal() ){ - Trace("fmf-incomplete") << "Incomplete because of infinite type " << tn << std::endl; - d_incomplete = true; - //enumerate if the sort is reasonably small, the upper bound of 128 is chosen arbitrarily for now - }else if( tn.getCardinality().isFinite() && tn.getCardinality().getFiniteCardinality().toUnsignedInt()<=128 ){ + }else if( tn.isInteger() ){ + bool inc = false; + //check if it is bound + if( d_owner.getKind()==FORALL && d_qe && d_qe->getBoundedIntegers() ){ + if( d_qe->getBoundedIntegers()->isBoundVar( d_owner, d_owner[0][i] ) ){ + Trace("bound-int-rsi") << "Rep set iterator: variable #" << i << " is bounded integer." << std::endl; + d_enum_type.push_back( ENUM_RANGE ); + }else{ + inc = true; + } + }else{ + inc = true; + } + if( inc ){ + //check if it is otherwise bound + if( d_bounds[0].find(i)!=d_bounds[0].end() && d_bounds[1].find(i)!=d_bounds[1].end() ){ + Trace("bound-int-rsi") << "Rep set iterator: variable #" << i << " is bounded." << std::endl; + d_enum_type.push_back( ENUM_RANGE ); + }else{ + Trace("fmf-incomplete") << "Incomplete because of integer quantification of " << d_owner[0][i] << "." << std::endl; + d_incomplete = true; + } + } + //enumerate if the sort is reasonably small, the upper bound of 1000 is chosen arbitrarily for now + }else if( tn.getCardinality().isFinite() && tn.getCardinality().getFiniteCardinality().toUnsignedInt()<=1000 ){ d_rep_set->complete( tn ); }else{ - Trace("fmf-incomplete") << "Incomplete because of unknown type " << tn << std::endl; + Trace("fmf-incomplete") << "Incomplete because of quantification of type " << tn << std::endl; d_incomplete = true; } - if( d_rep_set->hasType( tn ) ){ - for( size_t j=0; j<d_rep_set->d_type_reps[tn].size(); j++ ){ - d_domain[i].push_back( j ); + if( d_enum_type.size()<=i ){ + d_enum_type.push_back( ENUM_DOMAIN_ELEMENTS ); + if( d_rep_set->hasType( tn ) ){ + for( size_t j=0; j<d_rep_set->d_type_reps[tn].size(); j++ ){ + d_domain[i].push_back( j ); + } + }else{ + return false; } - }else{ - return false; + } + } + //must set a variable index order based on bounded integers + if (d_owner.getKind()==FORALL && d_qe && d_qe->getBoundedIntegers()) { + Trace("bound-int-rsi") << "Calculating variable order..." << std::endl; + std::vector< int > varOrder; + for( unsigned i=0; i<d_qe->getBoundedIntegers()->getNumBoundVars(d_owner); i++ ){ + varOrder.push_back(d_qe->getBoundedIntegers()->getBoundVarNum(d_owner,i)); + } + for( unsigned i=0; i<d_owner[0].getNumChildren(); i++) { + if( !d_qe->getBoundedIntegers()->isBoundVar(d_owner, d_owner[0][i])) { + varOrder.push_back(i); + } + } + Trace("bound-int-rsi") << "Variable order : "; + for( unsigned i=0; i<varOrder.size(); i++) { + Trace("bound-int-rsi") << varOrder[i] << " "; + } + Trace("bound-int-rsi") << std::endl; + std::vector< int > indexOrder; + indexOrder.resize(varOrder.size()); + for( unsigned i=0; i<varOrder.size(); i++){ + indexOrder[varOrder[i]] = i; + } + Trace("bound-int-rsi") << "Will use index order : "; + for( unsigned i=0; i<indexOrder.size(); i++) { + Trace("bound-int-rsi") << indexOrder[i] << " "; + } + Trace("bound-int-rsi") << std::endl; + setIndexOrder(indexOrder); + } + //now reset the indices + for (unsigned i=0; i<d_index.size(); i++) { + if (!resetIndex(i, true)){ + break; } } return true; @@ -161,7 +232,7 @@ void RepSetIterator::setIndexOrder( std::vector< int >& indexOrder ){ d_var_order[d_index_order[i]] = i; } } - +/* void RepSetIterator::setDomain( std::vector< RepDomain >& domain ){ d_domain.clear(); d_domain.insert( d_domain.begin(), domain.begin(), domain.end() ); @@ -172,29 +243,104 @@ void RepSetIterator::setDomain( std::vector< RepDomain >& domain ){ } } } +*/ +bool RepSetIterator::resetIndex( int i, bool initial ) { + d_index[i] = 0; + int ii = d_index_order[i]; + Trace("bound-int-rsi") << "Reset " << i << " " << ii << " " << initial << std::endl; + //determine the current range + if( d_enum_type[ii]==ENUM_RANGE ){ + if( initial || ( d_qe->getBoundedIntegers() && !d_qe->getBoundedIntegers()->isGroundRange( d_owner, d_owner[0][ii] ) ) ){ + Trace("bound-int-rsi") << "Getting range of " << d_owner[0][ii] << std::endl; + Node l, u; + if( d_qe->getBoundedIntegers() && d_qe->getBoundedIntegers()->isBoundVar( d_owner, d_owner[0][ii] ) ){ + d_qe->getBoundedIntegers()->getBoundValues( d_owner, d_owner[0][ii], this, l, u ); + } + for( unsigned b=0; b<2; b++ ){ + if( d_bounds[b].find(ii)!=d_bounds[b].end() ){ + Trace("bound-int-rsi") << "May further limit bound(" << b << ") based on " << d_bounds[b][ii] << std::endl; + if( b==0 && (l.isNull() || d_bounds[b][ii].getConst<Rational>() > l.getConst<Rational>()) ){ + l = d_bounds[b][ii]; + }else if( b==1 && (u.isNull() || d_bounds[b][ii].getConst<Rational>() <= u.getConst<Rational>()) ){ + u = NodeManager::currentNM()->mkNode( MINUS, d_bounds[b][ii], + NodeManager::currentNM()->mkConst( Rational(1) ) ); + u = Rewriter::rewrite( u ); + } + } + } + + if( l.isNull() || u.isNull() ){ + //failed, abort the iterator + d_index.clear(); + return false; + }else{ + Trace("bound-int-rsi") << "Can limit bounds of " << d_owner[0][ii] << " to " << l << "..." << u << std::endl; + Node range = Rewriter::rewrite( NodeManager::currentNM()->mkNode( MINUS, u, l ) ); + Node ra = Rewriter::rewrite( NodeManager::currentNM()->mkNode( LEQ, range, NodeManager::currentNM()->mkConst( Rational( 9999 ) ) ) ); + d_domain[ii].clear(); + Node tl = l; + Node tu = u; + if( d_qe->getBoundedIntegers() && d_qe->getBoundedIntegers()->isBoundVar( d_owner, d_owner[0][ii] ) ){ + d_qe->getBoundedIntegers()->getBounds( d_owner, d_owner[0][ii], this, tl, tu ); + } + d_lower_bounds[ii] = tl; + if( ra==NodeManager::currentNM()->mkConst(true) ){ + long rr = range.getConst<Rational>().getNumerator().getLong()+1; + Trace("bound-int-rsi") << "Actual bound range is " << rr << std::endl; + d_domain[ii].push_back( (int)rr ); + }else{ + Trace("fmf-incomplete") << "Incomplete because of integer quantification, bounds are too big for " << d_owner[0][ii] << "." << std::endl; + d_incomplete = true; + d_domain[ii].push_back( 0 ); + } + } + }else{ + Trace("bound-int-rsi") << d_owner[0][ii] << " has ground range, skip." << std::endl; + } + } + return true; +} -void RepSetIterator::increment2( int counter ){ +int RepSetIterator::increment2( int counter ){ Assert( !isFinished() ); #ifdef DISABLE_EVAL_SKIP_MULTIPLE counter = (int)d_index.size()-1; #endif //increment d_index - while( counter>=0 && d_index[counter]==(int)(d_domain[counter].size()-1) ){ + if( counter>=0){ + Trace("rsi-debug") << "domain size of " << counter << " is " << domainSize(counter) << std::endl; + } + while( counter>=0 && d_index[counter]>=(int)(domainSize(counter)-1) ){ counter--; + if( counter>=0){ + Trace("rsi-debug") << "domain size of " << counter << " is " << domainSize(counter) << std::endl; + } } if( counter==-1 ){ d_index.clear(); }else{ + d_index[counter]++; + bool emptyDomain = false; for( int i=(int)d_index.size()-1; i>counter; i-- ){ - d_index[i] = 0; + if (!resetIndex(i)){ + break; + }else if( domainSize(i)<=0 ){ + emptyDomain = true; + } + } + if( emptyDomain ){ + Trace("rsi-debug") << "This is an empty domain, increment again." << std::endl; + return increment(); } - d_index[counter]++; } + return counter; } -void RepSetIterator::increment(){ +int RepSetIterator::increment(){ if( !isFinished() ){ - increment2( (int)d_index.size()-1 ); + return increment2( (int)d_index.size()-1 ); + }else{ + return -1; } } @@ -203,10 +349,18 @@ bool RepSetIterator::isFinished(){ } Node RepSetIterator::getTerm( int i ){ - TypeNode tn = d_types[d_index_order[i]]; - Assert( d_rep_set->d_type_reps.find( tn )!=d_rep_set->d_type_reps.end() ); int index = d_index_order[i]; - return d_rep_set->d_type_reps[tn][d_domain[index][d_index[index]]]; + if( d_enum_type[index]==ENUM_DOMAIN_ELEMENTS ){ + TypeNode tn = d_types[index]; + Assert( d_rep_set->d_type_reps.find( tn )!=d_rep_set->d_type_reps.end() ); + return d_rep_set->d_type_reps[tn][d_domain[index][d_index[index]]]; + }else{ + Trace("rsi-debug") << index << " " << d_lower_bounds[index] << " " << d_index[index] << std::endl; + Node t = NodeManager::currentNM()->mkNode(PLUS, d_lower_bounds[index], + NodeManager::currentNM()->mkConst( Rational(d_index[index]) ) ); + t = Rewriter::rewrite( t ); + return t; + } } void RepSetIterator::debugPrint( const char* c ){ diff --git a/src/theory/rep_set.h b/src/theory/rep_set.h index 24fa7473e..a619915ee 100644 --- a/src/theory/rep_set.h +++ b/src/theory/rep_set.h @@ -23,6 +23,8 @@ namespace CVC4 { namespace theory { +class QuantifiersEngine; + /** this class stores a representative set */ class RepSet { public: @@ -52,11 +54,27 @@ typedef std::vector< int > RepDomain; /** this class iterates over a RepSet */ class RepSetIterator { +public: + enum { + ENUM_DOMAIN_ELEMENTS, + ENUM_RANGE, + }; private: + QuantifiersEngine * d_qe; //initialize function bool initialize(); + //for enum ranges + std::map< int, Node > d_lower_bounds; + //domain size + int domainSize( int i ); + //node this is rep set iterator is for + Node d_owner; + //reset index + bool resetIndex( int i, bool initial = false ); + /** set index order */ + void setIndexOrder( std::vector< int >& indexOrder ); public: - RepSetIterator( RepSet* rs ); + RepSetIterator( QuantifiersEngine * qe, RepSet* rs ); ~RepSetIterator(){} //set that this iterator will be iterating over instantiations for a quantifier bool setQuantifier( Node f ); @@ -65,6 +83,8 @@ public: public: //pointer to model RepSet* d_rep_set; + //enumeration type? + std::vector< int > d_enum_type; //index we are considering std::vector< int > d_index; //types we are considering @@ -86,15 +106,13 @@ public: // for example, if d_index_order = { 2, 0, 1 } // then d_var_order = { 0 -> 1, 1 -> 2, 2 -> 0 } std::map< int, int > d_var_order; + //intervals + std::map< int, Node > d_bounds[2]; public: - /** set index order */ - void setIndexOrder( std::vector< int >& indexOrder ); - /** set domain */ - void setDomain( std::vector< RepDomain >& domain ); /** increment the iterator at index=counter */ - void increment2( int counter ); + int increment2( int counter ); /** increment the iterator */ - void increment(); + int increment(); /** is the iterator finished? */ bool isFinished(); /** get the i_th term we are considering */ diff --git a/src/theory/rewriterules/rr_inst_match.cpp b/src/theory/rewriterules/rr_inst_match.cpp index a4c111f86..4908e5ec0 100644 --- a/src/theory/rewriterules/rr_inst_match.cpp +++ b/src/theory/rewriterules/rr_inst_match.cpp @@ -137,14 +137,13 @@ class DumbPatMatcher: public PatMatcher{ /* The order of the matching is: reset arg1, nextMatch arg1, reset arg2, nextMatch arg2, ... */ ApplyMatcher::ApplyMatcher( Node pat, QuantifiersEngine* qe): d_pattern(pat){ - // Assert( pat.hasAttribute(InstConstantAttribute()) ); //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( d_pattern[i].hasAttribute(InstConstantAttribute()) ){ + 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)); @@ -172,7 +171,7 @@ bool ApplyMatcher::reset(TNode t, InstMatch & m, QuantifiersEngine* qe){ //if t is null Assert( !t.isNull() ); - Assert( !t.hasAttribute(InstConstantAttribute()) ); + 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() ); @@ -411,7 +410,7 @@ public: size_t numFreeVar(TNode t){ size_t n = 0; for( size_t i=0, size =t.getNumChildren(); i < size; ++i ){ - if( t[i].hasAttribute(InstConstantAttribute()) ){ + if( quantifiers::TermDb::hasInstConstAttr(t[i]) ){ if( t[i].getKind()==INST_CONSTANT ){ //variable ++n; @@ -958,7 +957,7 @@ Matcher* mkMatcher( Node pat, QuantifiersEngine* qe ){ /** TODO: something simpler to see if the pattern is a good arithmetic pattern */ std::map< Node, Node > d_arith_coeffs; - if( !Trigger::getPatternArithmetic( pat.getAttribute(InstConstantAttribute()), pat, 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(); @@ -983,17 +982,17 @@ Matcher* mkMatcher( Node pat, QuantifiersEngine* qe ){ PatMatcher* mkPattern( Node pat, QuantifiersEngine* qe ){ Debug("inst-match-gen") << "Pattern term is " << pat << std::endl; - Assert( pat.hasAttribute(InstConstantAttribute()) ); + 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( !pat[0].hasAttribute(InstConstantAttribute() )){ - Assert(pat[1].hasAttribute(InstConstantAttribute())); + if( !quantifiers::TermDb::hasInstConstAttr(pat[0])){ + Assert(quantifiers::TermDb::hasInstConstAttr(pat[1])); return new UfCstEqMatcher(pat[1], pat[0], qe); - }else if( !pat[1].hasAttribute(InstConstantAttribute() )){ - Assert(pat[0].hasAttribute(InstConstantAttribute())); + }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()); @@ -1009,7 +1008,7 @@ PatMatcher* mkPattern( Node pat, QuantifiersEngine* qe ){ /** TODO: something simpler to see if the pattern is a good arithmetic pattern */ std::map< Node, Node > d_arith_coeffs; - if( !Trigger::getPatternArithmetic( pat.getAttribute(InstConstantAttribute()), pat, 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(); @@ -1033,7 +1032,7 @@ PatMatcher* mkPattern( Node pat, QuantifiersEngine* qe ){ ArithMatcher::ArithMatcher(Node pat, QuantifiersEngine* qe): d_pattern(pat){ - if(Trigger::getPatternArithmetic(pat.getAttribute(InstConstantAttribute()), pat, d_arith_coeffs ) ) + 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); diff --git a/src/theory/rewriterules/rr_inst_match.h b/src/theory/rewriterules/rr_inst_match.h index 3ec9438fd..aa89430c1 100644 --- a/src/theory/rewriterules/rr_inst_match.h +++ b/src/theory/rewriterules/rr_inst_match.h @@ -67,7 +67,7 @@ public: /** legal candidate */ static inline bool isLegalCandidate( TNode n ){ return !n.getAttribute(NoMatchAttribute()) && - ( !options::cbqi() || !n.hasAttribute(InstConstantAttribute())) && + ( !options::cbqi() || !quantifiers::TermDb::hasInstConstAttr(n)) && ( !options::efficientEMatching() || n.hasAttribute(AvailableInTermDb()) ); } diff --git a/src/theory/rewriterules/rr_trigger.cpp b/src/theory/rewriterules/rr_trigger.cpp index 7e35be7ad..13250cf1b 100644 --- a/src/theory/rewriterules/rr_trigger.cpp +++ b/src/theory/rewriterules/rr_trigger.cpp @@ -65,7 +65,7 @@ bool Trigger::getNextMatch(){ int Trigger::addInstantiations( InstMatch& baseMatch ){ int addedLemmas = d_mg->addInstantiations( baseMatch, - d_nodes[0].getAttribute(InstConstantAttribute()), + quantifiers::TermDb::getInstConstAttr(d_nodes[0]), d_quantEngine); if( addedLemmas>0 ){ Debug("inst-trigger") << "Added " << addedLemmas << " lemmas, trigger was "; @@ -91,7 +91,7 @@ Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >& 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( v.getAttribute(InstConstantAttribute())==f ){ + if( quantifiers::TermDb::getInstConstAttr(v)==f ){ if( vars.find( v )==vars.end() ){ vars[ v ] = true; foundVar = true; @@ -181,7 +181,7 @@ bool Trigger::isUsableTrigger( std::vector< Node >& nodes, Node f ){ } bool Trigger::isUsable( Node n, Node f ){ - if( n.getAttribute(InstConstantAttribute())==f ){ + if( quantifiers::TermDb::getInstConstAttr(n)==f ){ if( !isAtomicTrigger( n ) && n.getKind()!=INST_CONSTANT ){ std::map< Node, Node > coeffs; return getPatternArithmetic( f, n, coeffs ); @@ -201,7 +201,7 @@ bool Trigger::isUsable( Node n, Node f ){ bool Trigger::isSimpleTrigger( Node n ){ if( isAtomicTrigger( n ) ){ for( int i=0; i<(int)n.getNumChildren(); i++ ){ - if( n[i].getKind()!=INST_CONSTANT && n[i].hasAttribute(InstConstantAttribute()) ){ + if( n[i].getKind()!=INST_CONSTANT && quantifiers::TermDb::hasInstConstAttr(n[i]) ){ return false; } } @@ -318,9 +318,9 @@ bool Trigger::getPatternArithmetic( Node f, Node n, std::map< Node, Node >& coef Assert( coeffs.empty() ); NodeBuilder<> t(kind::PLUS); for( int i=0; i<(int)n.getNumChildren(); i++ ){ - if( n[i].hasAttribute(InstConstantAttribute()) ){ + if( quantifiers::TermDb::hasInstConstAttr(n[i]) ){ if( n[i].getKind()==INST_CONSTANT ){ - if( n[i].getAttribute(InstConstantAttribute())==f ){ + if( quantifiers::TermDb::getInstConstAttr(n[i])==f ){ coeffs[ n[i] ] = Node::null(); }else{ coeffs.clear(); @@ -343,12 +343,12 @@ bool Trigger::getPatternArithmetic( Node f, Node n, std::map< Node, Node >& coef } return true; }else if( n.getKind()==MULT ){ - if( n[0].getKind()==INST_CONSTANT && n[0].getAttribute(InstConstantAttribute())==f ){ - Assert( !n[1].hasAttribute(InstConstantAttribute()) ); + 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 && n[1].getAttribute(InstConstantAttribute())==f ){ - Assert( !n[0].hasAttribute(InstConstantAttribute()) ); + }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; } diff --git a/src/theory/rewriterules/rr_trigger.h b/src/theory/rewriterules/rr_trigger.h index f1a37d937..f02f38d0e 100644 --- a/src/theory/rewriterules/rr_trigger.h +++ b/src/theory/rewriterules/rr_trigger.h @@ -94,8 +94,7 @@ public: public: /** is usable trigger */ static inline bool isUsableTrigger( TNode n, TNode f ){ - //return n.getAttribute(InstConstantAttribute())==f && n.getKind()==APPLY_UF; - return n.getAttribute(InstConstantAttribute())==f && isAtomicTrigger( n ) && isUsable( n, f ); + return quantifiers::TermDb::getInstConstAttr(n)==f && isAtomicTrigger( n ) && isUsable( n, f ); } static inline bool isAtomicTrigger( TNode n ){ return diff --git a/src/theory/rewriterules/theory_rewriterules.h b/src/theory/rewriterules/theory_rewriterules.h index 19cb3e987..a542214b2 100644 --- a/src/theory/rewriterules/theory_rewriterules.h +++ b/src/theory/rewriterules/theory_rewriterules.h @@ -263,7 +263,7 @@ private: rewriter::Subst & vars); //create inst variable - std::vector<Node> createInstVariable( std::vector<Node> & vars ); + std::vector<Node> createInstVariable( Node r, std::vector<Node> & vars ); /** statistics class */ class Statistics { diff --git a/src/theory/rewriterules/theory_rewriterules_rules.cpp b/src/theory/rewriterules/theory_rewriterules_rules.cpp index 589556802..7e1d42bb2 100644 --- a/src/theory/rewriterules/theory_rewriterules_rules.cpp +++ b/src/theory/rewriterules/theory_rewriterules_rules.cpp @@ -151,7 +151,7 @@ void TheoryRewriteRules::addRewriteRule(const Node r) vars.push_back(*v); }; /* Instantiation version */ - std::vector<Node> inst_constants = createInstVariable(vars); + std::vector<Node> inst_constants = createInstVariable(r,vars); /* Body/Remove_term/Guards/Triggers */ Node body = r[2][1]; TNode new_terms = r[2][1]; @@ -232,7 +232,11 @@ void TheoryRewriteRules::addRewriteRule(const Node r) NodeBuilder<> patternListB(kind::INST_PATTERN_LIST); patternListB << static_cast<Node>(patternB); forallB << static_cast<Node>(patternListB); - getOutputChannel().lemma((Node) forallB); + Node lem = (Node) forallB; + lem = Rewriter::rewrite(lem); + QRewriteRuleAttribute qra; + lem.setAttribute(qra,r); + getOutputChannel().lemma(lem); return; } @@ -376,7 +380,7 @@ bool TheoryRewriteRules::addRewritePattern(TNode pattern, TNode body, } -std::vector<Node> TheoryRewriteRules::createInstVariable( std::vector<Node> & 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(); @@ -384,6 +388,11 @@ std::vector<Node> TheoryRewriteRules::createInstVariable( std::vector<Node> & va //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; } diff --git a/src/theory/rewriterules/theory_rewriterules_type_rules.h b/src/theory/rewriterules/theory_rewriterules_type_rules.h index 256957855..fa6bb2227 100644 --- a/src/theory/rewriterules/theory_rewriterules_type_rules.h +++ b/src/theory/rewriterules/theory_rewriterules_type_rules.h @@ -33,6 +33,8 @@ 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. */ diff --git a/src/theory/shared_terms_database.cpp b/src/theory/shared_terms_database.cpp index 58b8cf697..3a767b5c3 100644 --- a/src/theory/shared_terms_database.cpp +++ b/src/theory/shared_terms_database.cpp @@ -131,9 +131,9 @@ bool SharedTermsDatabase::propagateSharedEquality(TheoryId theory, TNode a, TNod // Propagate away Node equality = a.eqNode(b); if (value) { - d_theoryEngine->assertToTheory(equality, theory, THEORY_BUILTIN); + d_theoryEngine->assertToTheory(equality, equality, theory, THEORY_BUILTIN); } else { - d_theoryEngine->assertToTheory(equality.notNode(), theory, THEORY_BUILTIN); + d_theoryEngine->assertToTheory(equality.notNode(), equality.notNode(), theory, THEORY_BUILTIN); } // As you were diff --git a/src/theory/strings/Makefile b/src/theory/strings/Makefile new file mode 100644 index 000000000..e92c24ab7 --- /dev/null +++ b/src/theory/strings/Makefile @@ -0,0 +1,4 @@ +topdir = ../../.. +srcdir = src/theory/strings + +include $(topdir)/Makefile.subdir diff --git a/src/theory/strings/Makefile.am b/src/theory/strings/Makefile.am new file mode 100644 index 000000000..38efa33f3 --- /dev/null +++ b/src/theory/strings/Makefile.am @@ -0,0 +1,19 @@ +AM_CPPFLAGS = \ + -D__BUILDING_CVC4LIB \ + -I@srcdir@/../../include -I@srcdir@/../.. -I@builddir@/../.. +AM_CXXFLAGS = -Wall $(FLAG_VISIBILITY_HIDDEN) + +noinst_LTLIBRARIES = libstrings.la + +libstrings_la_SOURCES = \ + theory_strings.h \ + theory_strings.cpp \ + theory_strings_rewriter.h \ + theory_strings_rewriter.cpp \ + theory_strings_type_rules.h \ + type_enumerator.h \ + theory_strings_preprocess.h \ + theory_strings_preprocess.cpp + +EXTRA_DIST = \ + kinds diff --git a/src/theory/strings/kinds b/src/theory/strings/kinds new file mode 100644 index 000000000..814276a7c --- /dev/null +++ b/src/theory/strings/kinds @@ -0,0 +1,105 @@ +# kinds [for strings theory] +# + +theory THEORY_STRINGS ::CVC4::theory::strings::TheoryStrings "theory/strings/theory_strings.h" + +properties check parametric propagate + +rewriter ::CVC4::theory::strings::TheoryStringsRewriter "theory/strings/theory_strings_rewriter.h" + +typechecker "theory/strings/theory_strings_type_rules.h" + + +operator STRING_CONCAT 2: "string concat" + +operator STRING_IN_REGEXP 2 "membership" + +operator STRING_LENGTH 1 "string length" + +#sort CHAR_TYPE \ +# Cardinality::INTEGERS \ +# well-founded \ +# "NodeManager::currentNM()->mkConst(::CVC4::String())" \ +# "util/regexp.h" \ +# "String type" + +sort STRING_TYPE \ + Cardinality::INTEGERS \ + well-founded \ + "NodeManager::currentNM()->mkConst(::CVC4::String())" \ + "util/regexp.h" \ + "String type" + +sort REGEXP_TYPE \ + Cardinality::INTEGERS \ + well-founded \ + "NodeManager::currentNM()->mkConst(::CVC4::RegExp())" \ + "util/regexp.h" \ + "RegExp type" + +enumerator STRING_TYPE \ + "::CVC4::theory::strings::StringEnumerator" \ + "theory/strings/type_enumerator.h" + +#enumerator REGEXP_TYPE \ +# "::CVC4::theory::strings::RegExpEnumerator" \ +# "theory/strings/type_enumerator.h" + +constant CONST_STRING \ + ::CVC4::String \ + ::CVC4::strings::StringHashFunction \ + "util/regexp.h" \ + "a string of characters" + +constant CONST_REGEXP \ + ::CVC4::RegExp \ + ::CVC4::RegExpHashFunction \ + "util/regexp.h" \ + "a regular expression" + +typerule CONST_STRING ::CVC4::theory::strings::StringConstantTypeRule +typerule CONST_REGEXP ::CVC4::theory::strings::RegExpConstantTypeRule + +# equal equal / less than / output +operator STRING_TO_REGEXP 1 "convert string to regexp" +operator REGEXP_CONCAT 2: "regexp concat" +operator REGEXP_OR 2: "regexp or" +operator REGEXP_INTER 2: "regexp intersection" +operator REGEXP_STAR 1 "regexp *" +operator REGEXP_PLUS 1 "regexp +" +operator REGEXP_OPT 1 "regexp ?" + +#constant REGEXP_EMPTY \ +# ::CVC4::RegExp \ +# ::CVC4::RegExpHashFunction \ +# "util/string.h" \ +# "a regexp contains nothing" + +#constant REGEXP_ALL \ +# ::CVC4::RegExp \ +# ::CVC4::RegExpHashFunction \ +# "util/string.h" \ +# "a regexp contains all strings" + +#constant REGEXP_SIGMA \ +# ::CVC4::RegExp \ +# ::CVC4::RegExpHashFunction \ +# "util/string.h" \ +# "a regexp contains an arbitrary charactor" + +typerule REGEXP_CONCAT ::CVC4::theory::strings::RegExpConcatTypeRule +typerule REGEXP_OR ::CVC4::theory::strings::RegExpOrTypeRule +typerule REGEXP_INTER ::CVC4::theory::strings::RegExpInterTypeRule +typerule REGEXP_STAR ::CVC4::theory::strings::RegExpStarTypeRule +typerule REGEXP_PLUS ::CVC4::theory::strings::RegExpPlusTypeRule +typerule REGEXP_OPT ::CVC4::theory::strings::RegExpOptTypeRule + +typerule STRING_TO_REGEXP ::CVC4::theory::strings::StringToRegExpTypeRule + + +typerule STRING_CONCAT ::CVC4::theory::strings::StringConcatTypeRule +typerule STRING_LENGTH ::CVC4::theory::strings::StringLengthTypeRule + +typerule STRING_IN_REGEXP ::CVC4::theory::strings::StringInRegExpTypeRule + +endtheory diff --git a/src/theory/strings/options b/src/theory/strings/options new file mode 100644 index 000000000..9226f9999 --- /dev/null +++ b/src/theory/strings/options @@ -0,0 +1,11 @@ +# +# Option specification file for CVC4 +# See src/options/base_options for a description of this file format +# + +module STRINGS "theory/strings/options.h" Strings theory + +option stringCharCardinality str-alphabet-card --str-alphabet-card=N int16_t :default 256 :read-write + the cardinality of the characters used by the theory of string, default 256 + +endmodule diff --git a/src/theory/strings/theory_strings.cpp b/src/theory/strings/theory_strings.cpp new file mode 100644 index 000000000..7d5edd0f7 --- /dev/null +++ b/src/theory/strings/theory_strings.cpp @@ -0,0 +1,1711 @@ +/********************* */ +/*! \file theory_strings.cpp + ** \verbatim + ** Original author: Tianyi Liang + ** Major contributors: Tianyi Liang, Andrew Reynolds + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2013-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 the theory of strings. + ** + ** Implementation of the theory of strings. + **/ + + +#include "theory/strings/theory_strings.h" +#include "theory/valuation.h" +#include "expr/kind.h" +#include "theory/rewriter.h" +#include "expr/command.h" +#include "theory/model.h" +#include "smt/logic_exception.h" +#include "theory/strings/options.h" +#include "theory/strings/type_enumerator.h" +#include <cmath> + +#define STR_UNROLL_INDUCTION + +using namespace std; +using namespace CVC4::context; + +namespace CVC4 { +namespace theory { +namespace strings { + +TheoryStrings::TheoryStrings(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe) + : Theory(THEORY_STRINGS, c, u, out, valuation, logicInfo, qe), + d_notify( *this ), + d_equalityEngine(d_notify, c, "theory::strings::TheoryStrings"), + d_conflict( c, false ), + d_infer(c), + d_infer_exp(c), + d_nf_pairs(c), + d_ind_map1(c), + d_ind_map2(c), + d_ind_map_exp(c), + d_ind_map_lemma(c), + //d_lit_to_decide_index( c, 0 ), + //d_lit_to_decide( c ), + d_lit_to_unroll( c ) +{ + // The kinds we are treating as function application in congruence + d_equalityEngine.addFunctionKind(kind::STRING_IN_REGEXP); + d_equalityEngine.addFunctionKind(kind::STRING_LENGTH); + d_equalityEngine.addFunctionKind(kind::STRING_CONCAT); + + d_zero = NodeManager::currentNM()->mkConst( Rational( 0 ) ); + d_emptyString = NodeManager::currentNM()->mkConst( ::CVC4::String("") ); + d_true = NodeManager::currentNM()->mkConst( true ); + d_false = NodeManager::currentNM()->mkConst( false ); +} + +TheoryStrings::~TheoryStrings() { + +} + +Node TheoryStrings::getRepresentative( Node t ) { + if( d_equalityEngine.hasTerm( t ) ){ + return d_equalityEngine.getRepresentative( t ); + }else{ + return t; + } +} + +bool TheoryStrings::hasTerm( Node a ){ + return d_equalityEngine.hasTerm( a ); +} + +bool TheoryStrings::areEqual( Node a, Node b ){ + if( a==b ){ + return true; + }else if( hasTerm( a ) && hasTerm( b ) ){ + return d_equalityEngine.areEqual( a, b ); + }else{ + return false; + } +} + +bool TheoryStrings::areDisequal( Node a, Node b ){ + if( hasTerm( a ) && hasTerm( b ) ){ + return d_equalityEngine.areDisequal( a, b, false ); + }else{ + return false; + } +} + +Node TheoryStrings::getLength( Node t ) { + EqcInfo * ei = getOrMakeEqcInfo( t ); + Node length_term = ei->d_length_term; + if( length_term.isNull()) { + //typically shouldnt be necessary + length_term = t; + } + return NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, length_term ); +} + +void TheoryStrings::setMasterEqualityEngine(eq::EqualityEngine* eq) { + d_equalityEngine.setMasterEqualityEngine(eq); +} + +void TheoryStrings::addSharedTerm(TNode t) { + Debug("strings") << "TheoryStrings::addSharedTerm(): " + << t << " " << t.getType().isBoolean() << endl; + d_equalityEngine.addTriggerTerm(t, THEORY_STRINGS); + Debug("strings") << "TheoryStrings::addSharedTerm() finished" << std::endl; +} + +EqualityStatus TheoryStrings::getEqualityStatus(TNode a, TNode b) { + if( d_equalityEngine.hasTerm(a) && d_equalityEngine.hasTerm(b) ){ + if (d_equalityEngine.areEqual(a, b)) { + // The terms are implied to be equal + return EQUALITY_TRUE; + } + if (d_equalityEngine.areDisequal(a, b, false)) { + // The terms are implied to be dis-equal + return EQUALITY_FALSE; + } + } + return EQUALITY_UNKNOWN; +} + +void TheoryStrings::propagate(Effort e) +{ + // direct propagation now +} + +bool TheoryStrings::propagate(TNode literal) { + Debug("strings-propagate") << "TheoryStrings::propagate(" << literal << ")" << std::endl; + // If already in conflict, no more propagation + if (d_conflict) { + Debug("strings-propagate") << "TheoryStrings::propagate(" << literal << "): already in conflict" << std::endl; + return false; + } + Trace("strings-prop") << "strPropagate " << literal << std::endl; + // Propagate out + bool ok = d_out->propagate(literal); + if (!ok) { + d_conflict = true; + } + return ok; +} + +/** explain */ +void TheoryStrings::explain(TNode literal, std::vector<TNode>& assumptions){ + Debug("strings-explain") << "Explain " << literal << std::endl; + bool polarity = literal.getKind() != kind::NOT; + TNode atom = polarity ? literal : literal[0]; + if (atom.getKind() == kind::EQUAL || atom.getKind() == kind::IFF) { + d_equalityEngine.explainEquality(atom[0], atom[1], polarity, assumptions); + } else { + d_equalityEngine.explainPredicate(atom, polarity, assumptions); + } +} + +Node TheoryStrings::explain( TNode literal ){ + std::vector< TNode > assumptions; + explain( literal, assumptions ); + if( assumptions.empty() ){ + return d_true; + }else if( assumptions.size()==1 ){ + return assumptions[0]; + }else{ + return NodeManager::currentNM()->mkNode( kind::AND, assumptions ); + } +} + +///////////////////////////////////////////////////////////////////////////// +// MODEL GENERATION +///////////////////////////////////////////////////////////////////////////// + + +void TheoryStrings::collectModelInfo( TheoryModel* m, bool fullModel ) { + Trace("strings-model") << "TheoryStrings : Collect model info, fullModel = " << fullModel << std::endl; + Trace("strings-model") << "TheoryStrings : assertEqualityEngine." << std::endl; + m->assertEqualityEngine( &d_equalityEngine ); + // Generate model + std::vector< Node > nodes; + getEquivalenceClasses( nodes ); + std::map< Node, Node > processed; + std::vector< std::vector< Node > > col; + std::vector< Node > lts; + seperateByLength( nodes, col, lts ); + //step 1 : get all values for known lengths + std::vector< Node > lts_values; + //std::map< Node, bool > values_used; + for( unsigned i=0; i<col.size(); i++ ){ + Trace("strings-model") << "Checking length for " << col[i][0] << " (length is " << lts[i] << ")" << std::endl; + if( lts[i].isConst() ){ + lts_values.push_back( lts[i] ); + //values_used[ lts[i] ] = true; + }else{ + //get value for lts[i]; + if( !lts[i].isNull() ){ + Node v = d_valuation.getModelValue(lts[i]); + //Node v = m->getValue(lts[i]); + Trace("strings-model") << "Model value for " << lts[i] << " is " << v << std::endl; + lts_values.push_back( v ); + //values_used[ v ] = true; + }else{ + Trace("strings-model-warn") << "No length for eqc " << col[i][0] << std::endl; + Assert( false ); + lts_values.push_back( Node::null() ); + } + } + } + ////step 2 : assign arbitrary values for unknown lengths? + //for( unsigned i=0; i<col.size(); i++ ){ + // if( + //} + Trace("strings-model") << "Assign to equivalence classes..." << std::endl; + //step 3 : assign values to equivalence classes that are pure variables + for( unsigned i=0; i<col.size(); i++ ){ + std::vector< Node > pure_eq; + Trace("strings-model") << "The equivalence classes "; + for( unsigned j=0; j<col[i].size(); j++ ) { + Trace("strings-model") << col[i][j] << " "; + //check if col[i][j] has only variables + EqcInfo* ei = getOrMakeEqcInfo( col[i][j], false ); + Node cst = ei ? ei->d_const_term : Node::null(); + if( cst.isNull() ){ + Assert( d_normal_forms.find( col[i][j] )!=d_normal_forms.end() ); + if( d_normal_forms[col[i][j]].size()==1 ){//&& d_normal_forms[col[i][j]][0]==col[i][j] ){ + pure_eq.push_back( col[i][j] ); + } + }else{ + processed[col[i][j]] = cst; + } + } + Trace("strings-model") << "have length " << lts_values[i] << std::endl; + + Trace("strings-model") << "Need to assign values of length " << lts_values[i] << " to equivalence classes "; + for( unsigned j=0; j<pure_eq.size(); j++ ){ + Trace("strings-model") << pure_eq[j] << " "; + } + Trace("strings-model") << std::endl; + + //use type enumerator + StringEnumeratorLength sel(lts_values[i].getConst<Rational>().getNumerator().toUnsignedInt()); + for( unsigned j=0; j<pure_eq.size(); j++ ){ + Assert( !sel.isFinished() ); + Node c = *sel; + while( d_equalityEngine.hasTerm( c ) ){ + ++sel; + Assert( !sel.isFinished() ); + c = *sel; + } + ++sel; + Trace("strings-model") << "*** Assigned constant " << c << " for " << pure_eq[j] << std::endl; + processed[pure_eq[j]] = c; + m->assertEquality( pure_eq[j], c, true ); + } + } + Trace("strings-model") << "String Model : Finished." << std::endl; + //step 4 : assign constants to all other equivalence classes + for( unsigned i=0; i<nodes.size(); i++ ){ + if( processed.find( nodes[i] )==processed.end() ){ + Assert( d_normal_forms.find( nodes[i] )!=d_normal_forms.end() ); + Trace("strings-model") << "Construct model for " << nodes[i] << " based on normal form "; + for( unsigned j=0; j<d_normal_forms[nodes[i]].size(); j++ ) { + if( j>0 ) Trace("strings-model") << " ++ "; + Trace("strings-model") << d_normal_forms[nodes[i]][j]; + Node r = getRepresentative( d_normal_forms[nodes[i]][j] ); + if( !r.isConst() && processed.find( r )==processed.end() ){ + Trace("strings-model") << "(UNPROCESSED)"; + } + } + Trace("strings-model") << std::endl; + std::vector< Node > nc; + for( unsigned j=0; j<d_normal_forms[nodes[i]].size(); j++ ) { + Node r = getRepresentative( d_normal_forms[nodes[i]][j] ); + Assert( r.isConst() || processed.find( r )!=processed.end() ); + nc.push_back(r.isConst() ? r : processed[r]); + } + Node cc = mkConcat( nc ); + Assert( cc.getKind()==kind::CONST_STRING ); + Trace("strings-model") << "*** Determined constant " << cc << " for " << nodes[i] << std::endl; + processed[nodes[i]] = cc; + m->assertEquality( nodes[i], cc, true ); + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// MAIN SOLVER +///////////////////////////////////////////////////////////////////////////// + +void TheoryStrings::preRegisterTerm(TNode n) { + Debug("strings-prereg") << "TheoryStrings::preRegisterTerm() " << n << endl; + //collectTerms( n ); + switch (n.getKind()) { + case kind::EQUAL: + d_equalityEngine.addTriggerEquality(n); + break; + case kind::STRING_IN_REGEXP: + d_equalityEngine.addTriggerPredicate(n); + break; + default: + if(n.getKind() == kind::VARIABLE || n.getKind()==kind::SKOLEM) { + if( std::find( d_length_intro_vars.begin(), d_length_intro_vars.end(), n )==d_length_intro_vars.end() ){ + Node n_len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, n); + Node n_len_geq_zero = NodeManager::currentNM()->mkNode( kind::GEQ, n_len, d_zero); + Trace("strings-lemma") << "Strings: Add lemma " << n_len_geq_zero << std::endl; + d_out->lemma(n_len_geq_zero); + } + } + if (n.getType().isBoolean()) { + // Get triggered for both equal and dis-equal + d_equalityEngine.addTriggerPredicate(n); + } else { + // Function applications/predicates + d_equalityEngine.addTerm(n); + } + break; + } +} + +void TheoryStrings::check(Effort e) { + bool polarity; + TNode atom; + + if( !done() && !hasTerm( d_emptyString ) ){ + preRegisterTerm( d_emptyString ); + } + + // Trace("strings-process") << "Theory of strings, check : " << e << std::endl; + Trace("strings-check") << "Theory of strings, check : " << e << std::endl; + while ( !done() && !d_conflict) + { + // Get all the assertions + Assertion assertion = get(); + TNode fact = assertion.assertion; + + Trace("strings-assertion") << "get assertion: " << fact << endl; + + polarity = fact.getKind() != kind::NOT; + atom = polarity ? fact : fact[0]; + if (atom.getKind() == kind::EQUAL) { + d_equalityEngine.assertEquality(atom, polarity, fact); + } else { + d_equalityEngine.assertPredicate(atom, polarity, fact); + } +#ifdef STR_UNROLL_INDUCTION + //check if it is a literal to unroll? + if( d_lit_to_unroll.find( atom )!=d_lit_to_unroll.end() ){ + Trace("strings-ind") << "Strings-ind : Possibly unroll for : " << atom << ", polarity = " << polarity << std::endl; + } +#endif + } + doPendingFacts(); + + + bool addedLemma = false; + if( e == EFFORT_FULL && !d_conflict ) { + eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine ); + while( !eqcs_i.isFinished() ){ + Node eqc = (*eqcs_i); + //if eqc.getType is string + if (eqc.getType().isString()) { + //EqcInfo* ei = getOrMakeEqcInfo( eqc, true ); + //get the constant for the equivalence class + //int c_len = ...; + eq::EqClassIterator eqc_i = eq::EqClassIterator( eqc, &d_equalityEngine ); + while( !eqc_i.isFinished() ){ + Node n = (*eqc_i); + + //if n is concat, and + //if n has not instantiatied the concat..length axiom + //then, add lemma + if( n.getKind() == kind::STRING_CONCAT || n.getKind() == kind::CONST_STRING ){ + if( d_length_inst.find(n)==d_length_inst.end() ){ + d_length_inst[n] = true; + Trace("strings-debug") << "get n: " << n << endl; + Node sk = NodeManager::currentNM()->mkSkolem( "lsym_$$", n.getType(), "created for concat lemma" ); + d_length_intro_vars.push_back( sk ); + Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL, sk, n ); + eq = Rewriter::rewrite(eq); + Trace("strings-lemma") << "Strings: Add lemma " << 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 = NodeManager::currentNM()->mkNode( kind::PLUS, node_vec ); + }else{ + //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: Add lemma " << ceq << std::endl; + d_out->lemma(ceq); + addedLemma = true; + } + } + ++eqc_i; + } + } + ++eqcs_i; + } + if( !addedLemma ){ + addedLemma = checkNormalForms(); + Trace("strings-process") << "Done check normal forms, addedLemma = " << addedLemma << ", d_conflict = " << d_conflict << std::endl; + if(!d_conflict && !addedLemma) { + addedLemma = checkCardinality(); + Trace("strings-process") << "Done check cardinality, addedLemma = " << addedLemma << ", d_conflict = " << d_conflict << std::endl; + if( !d_conflict && !addedLemma ){ + addedLemma = checkInductiveEquations(); + Trace("strings-process") << "Done check inductive equations, addedLemma = " << addedLemma << ", d_conflict = " << d_conflict << std::endl; + } + } + } + } + Trace("strings-process") << "Theory of strings, done check : " << e << std::endl; +} + +TheoryStrings::EqcInfo::EqcInfo( context::Context* c ) : d_const_term(c), d_length_term(c), d_cardinality_lem_k(c) { + +} + +TheoryStrings::EqcInfo * TheoryStrings::getOrMakeEqcInfo( Node eqc, bool doMake ) { + std::map< Node, EqcInfo* >::iterator eqc_i = d_eqc_info.find( eqc ); + if( eqc_i==d_eqc_info.end() ){ + if( doMake ){ + EqcInfo* ei = new EqcInfo( getSatContext() ); + d_eqc_info[eqc] = ei; + return ei; + }else{ + return NULL; + } + }else{ + return (*eqc_i).second; + } +} + + +/** Conflict when merging two constants */ +void TheoryStrings::conflict(TNode a, TNode b){ + Node conflictNode; + if (a.getKind() == kind::CONST_BOOLEAN) { + conflictNode = explain( a.iffNode(b) ); + } else { + conflictNode = explain( a.eqNode(b) ); + } + Debug("strings-conflict") << "CONFLICT: Eq engine conflict : " << conflictNode << std::endl; + d_out->conflict( conflictNode ); + d_conflict = true; +} + +/** called when a new equivalance class is created */ +void TheoryStrings::eqNotifyNewClass(TNode t){ + if( t.getKind() == kind::CONST_STRING ){ + EqcInfo * ei =getOrMakeEqcInfo( t, true ); + ei->d_const_term = t; + } + if( t.getKind() == kind::STRING_LENGTH ){ + Trace("strings-debug") << "New length eqc : " << t << std::endl; + Node r = d_equalityEngine.getRepresentative(t[0]); + EqcInfo * ei = getOrMakeEqcInfo( r, true ); + ei->d_length_term = t[0]; + } +} + +/** called when two equivalance classes will merge */ +void TheoryStrings::eqNotifyPreMerge(TNode t1, TNode t2){ + EqcInfo * e2 = getOrMakeEqcInfo(t2, false); + if( e2 ){ + EqcInfo * e1 = getOrMakeEqcInfo( t1 ); + //add information from e2 to e1 + if( !e2->d_const_term.get().isNull() ){ + e1->d_const_term.set( e2->d_const_term ); + } + if( !e2->d_length_term.get().isNull() ){ + e1->d_length_term.set( e2->d_length_term ); + } + if( e2->d_cardinality_lem_k.get()>e1->d_cardinality_lem_k.get() ) { + e1->d_cardinality_lem_k.set( e2->d_cardinality_lem_k ); + } + } + if( hasTerm( d_zero ) ){ + Node leqc; + if( areEqual(d_zero, t1) ){ + leqc = t2; + }else if( areEqual(d_zero, t2) ){ + leqc = t1; + } + if( !leqc.isNull() ){ + //scan equivalence class to see if we apply + eq::EqClassIterator eqc_i = eq::EqClassIterator( leqc, &d_equalityEngine ); + while( !eqc_i.isFinished() ){ + Node n = (*eqc_i); + if( n.getKind()==kind::STRING_LENGTH ){ + if( !hasTerm( d_emptyString ) || !areEqual(n[0], d_emptyString ) ){ + //apply the rule length(n[0])==0 => n[0] == "" + Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL, n[0], d_emptyString ); + d_pending.push_back( eq ); + Node eq_exp = NodeManager::currentNM()->mkNode( kind::EQUAL, n, d_zero ); + d_pending_exp[eq] = eq_exp; + Trace("strings-infer") << "Strings : Infer " << eq << " from " << eq_exp << std::endl; + d_infer.push_back(eq); + d_infer_exp.push_back(eq_exp); + } + } + ++eqc_i; + } + } + } +} + +/** called when two equivalance classes have merged */ +void TheoryStrings::eqNotifyPostMerge(TNode t1, TNode t2) { + +} + +/** called when two equivalance classes are disequal */ +void TheoryStrings::eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { + +} + +void TheoryStrings::computeCareGraph(){ + Theory::computeCareGraph(); +} + +void TheoryStrings::doPendingFacts() { + int i=0; + while( !d_conflict && i<(int)d_pending.size() ){ + Node fact = d_pending[i]; + Node exp = d_pending_exp[ fact ]; + Trace("strings-pending") << "Process pending fact : " << fact << " from " << exp << std::endl; + bool polarity = fact.getKind() != kind::NOT; + TNode atom = polarity ? fact : fact[0]; + if (atom.getKind() == kind::EQUAL) { + Assert( d_equalityEngine.hasTerm( atom[0] ) ); + Assert( d_equalityEngine.hasTerm( atom[1] ) ); + d_equalityEngine.assertEquality( atom, polarity, exp ); + }else{ + d_equalityEngine.assertPredicate( atom, polarity, exp ); + } + i++; + } + d_pending.clear(); + d_pending_exp.clear(); +} +void TheoryStrings::doPendingLemmas() { + if( !d_conflict && !d_lemma_cache.empty() ){ + for( unsigned i=0; i<d_lemma_cache.size(); i++ ){ + Trace("strings-pending") << "Process pending lemma : " << d_lemma_cache[i] << std::endl; + d_out->lemma( d_lemma_cache[i] ); + } + for( std::map< Node, bool >::iterator it = d_pending_req_phase.begin(); it != d_pending_req_phase.end(); ++it ){ + Trace("strings-pending") << "Require phase : " << it->first << ", polarity = " << it->second << std::endl; + d_out->requirePhase( it->first, it->second ); + } + d_lemma_cache.clear(); + d_pending_req_phase.clear(); + } +} + +void TheoryStrings::getNormalForms(Node &eqc, std::vector< Node > & visited, std::vector< Node > & nf, + std::vector< std::vector< Node > > &normal_forms, std::vector< std::vector< Node > > &normal_forms_exp, std::vector< Node > &normal_form_src) { + // EqcItr + eq::EqClassIterator eqc_i = eq::EqClassIterator( eqc, &d_equalityEngine ); + while( !eqc_i.isFinished() ) { + Node n = (*eqc_i); + Trace("strings-process") << "Process term " << n << std::endl; + if( n.getKind() == kind::CONST_STRING || n.getKind() == kind::STRING_CONCAT ) { + std::vector<Node> nf_n; + std::vector<Node> nf_exp_n; + if( n.getKind() == kind::CONST_STRING ){ + if( n!=d_emptyString ) { + nf_n.push_back( n ); + } + } else if( n.getKind() == kind::STRING_CONCAT ) { + for( unsigned i=0; i<n.getNumChildren(); i++ ) { + Node nr = d_equalityEngine.getRepresentative( n[i] ); + std::vector< Node > nf_temp; + std::vector< Node > nf_exp_temp; + Trace("strings-process") << "Normalizing subterm " << n[i] << " = " << nr << std::endl; + normalizeEquivalenceClass( nr, visited, nf_temp, nf_exp_temp ); + if( d_conflict || !d_pending.empty() || !d_lemma_cache.empty() ) { + return; + } + if( nf.size()!=1 || nf[0]!=d_emptyString ) { + for( unsigned r=0; r<nf_temp.size(); r++ ) { + Assert( nf_temp[r].getKind()!=kind::STRING_CONCAT ); + } + nf_n.insert( nf_n.end(), nf_temp.begin(), nf_temp.end() ); + } + nf_exp_n.insert( nf_exp_n.end(), nf_exp_temp.begin(), nf_exp_temp.end() ); + if( nr!=n[i] ) { + nf_exp_n.push_back( NodeManager::currentNM()->mkNode( kind::EQUAL, n[i], nr ) ); + } + } + } + normal_forms.push_back(nf_n); + normal_forms_exp.push_back(nf_exp_n); + normal_form_src.push_back(n); + } + /* should we add these? + else { + //var/sk? + std::vector<Node> nf_n; + std::vector<Node> nf_exp_n; + nf_n.push_back(n); + normal_forms.push_back(nf_n); + normal_forms_exp.push_back(nf_exp_n); + normal_form_src.push_back(n); + }*/ + ++eqc_i; + } + + // Test the result + if( !normal_forms.empty() ) { + Trace("strings-solve") << "--- Normal forms for equivlance class " << eqc << " : " << std::endl; + for( unsigned i=0; i<normal_forms.size(); i++ ) { + Trace("strings-solve") << "#" << i << " (from " << normal_form_src[i] << ") : "; + for( unsigned j=0; j<normal_forms[i].size(); j++ ) { + if(j>0) Trace("strings-solve") << ", "; + Trace("strings-solve") << normal_forms[i][j]; + } + Trace("strings-solve") << std::endl; + Trace("strings-solve") << " Explanation is : "; + if(normal_forms_exp[i].size() == 0) { + Trace("strings-solve") << "NONE"; + } else { + for( unsigned j=0; j<normal_forms_exp[i].size(); j++ ) { + if(j>0) Trace("strings-solve") << " AND "; + Trace("strings-solve") << normal_forms_exp[i][j]; + } + } + Trace("strings-solve") << std::endl; + } + } +} +//nf_exp is conjunction +void TheoryStrings::normalizeEquivalenceClass( Node eqc, std::vector< Node > & visited, std::vector< Node > & nf, std::vector< Node > & nf_exp ) { + Trace("strings-process") << "Process equivalence class " << eqc << std::endl; + if( std::find( visited.begin(), visited.end(), eqc )!=visited.end() ){ + //nf.push_back( eqc ); + if( eqc.getKind()==kind::STRING_CONCAT ){ + for( unsigned i=0; i<eqc.getNumChildren(); i++ ){ + if( !d_equalityEngine.hasTerm(d_emptyString) || !d_equalityEngine.areEqual( eqc[i], d_emptyString ) ){ + nf.push_back( eqc[i] ); + } + } + }else if( !d_equalityEngine.hasTerm(d_emptyString) || !d_equalityEngine.areEqual( eqc, d_emptyString ) ){ + nf.push_back( eqc ); + } + Trace("strings-process") << "Return process equivalence class " << eqc << " : already visited." << std::endl; + } else if (d_equalityEngine.hasTerm(d_emptyString) && d_equalityEngine.areEqual( eqc, d_emptyString )){ + //do nothing + Trace("strings-process") << "Return process equivalence class " << eqc << " : empty." << std::endl; + d_normal_forms[eqc].clear(); + d_normal_forms_exp[eqc].clear(); + } else { + visited.push_back( eqc ); + if(d_normal_forms.find(eqc)==d_normal_forms.end() ){ + //phi => t = s1 * ... * sn + // normal form for each non-variable term in this eqc (s1...sn) + std::vector< std::vector< Node > > normal_forms; + // explanation for each normal form (phi) + std::vector< std::vector< Node > > normal_forms_exp; + // record terms for each normal form (t) + std::vector< Node > normal_form_src; + //Get Normal Forms + getNormalForms(eqc, visited, nf, normal_forms, normal_forms_exp, normal_form_src); + if( d_conflict || !d_pending.empty() || !d_lemma_cache.empty() ) { + return; + } + + unsigned i = 0; + //unify each normal form > 0 with normal_forms[0] + for( unsigned j=1; j<normal_forms.size(); j++ ) { + + Trace("strings-solve") << "Process normal form #0 against #" << j << "..." << std::endl; + if( isNormalFormPair( normal_form_src[i], normal_form_src[j] ) ){ + Trace("strings-solve") << "Already normalized (in cache)." << std::endl; + }else{ + Trace("strings-solve") << "Not in cache." << std::endl; + //the current explanation for why the prefix is equal + std::vector< Node > curr_exp; + curr_exp.insert(curr_exp.end(), normal_forms_exp[i].begin(), normal_forms_exp[i].end() ); + curr_exp.insert(curr_exp.end(), normal_forms_exp[j].begin(), normal_forms_exp[j].end() ); + curr_exp.push_back( NodeManager::currentNM()->mkNode( kind::EQUAL, normal_form_src[i], normal_form_src[j] ) ); + //ensure that normal_forms[i] and normal_forms[j] are the same modulo equality + unsigned index_i = 0; + unsigned index_j = 0; + bool success; + do + { + success = false; + //if we are at the end + if(index_i==normal_forms[i].size() || index_j==normal_forms[j].size() ) { + if( index_i==normal_forms[i].size() && index_j==normal_forms[j].size() ){ + //we're done + addNormalFormPair( normal_form_src[i], normal_form_src[j] ); + }else{ + //the remainder must be empty + unsigned k = index_i==normal_forms[i].size() ? j : i; + unsigned index_k = index_i==normal_forms[i].size() ? index_j : index_i; + while(!d_conflict && index_k<normal_forms[k].size()) { + //can infer that this string must be empty + Node eq_exp; + if( curr_exp.empty() ) { + eq_exp = d_true; + } else if( curr_exp.size() == 1 ) { + eq_exp = curr_exp[0]; + } else { + eq_exp = NodeManager::currentNM()->mkNode( kind::AND, curr_exp ); + } + Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL, d_emptyString, normal_forms[k][index_k] ); + Trace("strings-lemma") << "Strings : Infer " << eq << " from " << eq_exp << std::endl; + Assert( !d_equalityEngine.areEqual( d_emptyString, normal_forms[k][index_k] ) ); + d_pending.push_back( eq ); + d_pending_exp[eq] = eq_exp; + d_infer.push_back(eq); + d_infer_exp.push_back(eq_exp); + index_k++; + } + } + }else { + Trace("strings-solve-debug") << "Process " << normal_forms[i][index_i] << " ... " << normal_forms[j][index_j] << std::endl; + if(areEqual(normal_forms[i][index_i],normal_forms[j][index_j])){ + Trace("strings-solve-debug") << "Case 1 : strings are equal" << std::endl; + //terms are equal, continue + if( normal_forms[i][index_i]!=normal_forms[j][index_j] ){ + Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL,normal_forms[i][index_i], + normal_forms[j][index_j]); + Trace("strings-solve-debug") << "Add to explanation : " << eq << std::endl; + curr_exp.push_back(eq); + } + index_j++; + index_i++; + success = true; + }else{ + Node length_term_i = getLength( normal_forms[i][index_i] ); + Node length_term_j = getLength( normal_forms[j][index_j] ); + //check if length(normal_forms[i][index]) == length(normal_forms[j][index]) + if( areEqual(length_term_i, length_term_j) ){ + Trace("strings-solve-debug") << "Case 2 : string lengths are equal" << std::endl; + //length terms are equal, merge equivalence classes if not already done so + Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL, normal_forms[i][index_i], normal_forms[j][index_j] ); + std::vector< Node > temp_exp; + temp_exp.insert(temp_exp.end(), curr_exp.begin(), curr_exp.end() ); + temp_exp.push_back(NodeManager::currentNM()->mkNode( kind::EQUAL, length_term_i, length_term_j )); + Node eq_exp = temp_exp.empty() ? d_true : + temp_exp.size() == 1 ? temp_exp[0] : NodeManager::currentNM()->mkNode( kind::AND, temp_exp ); + Trace("strings-lemma") << "Strings : Infer " << eq << " from " << eq_exp << std::endl; + //d_equalityEngine.assertEquality( eq, true, eq_exp ); + d_pending.push_back( eq ); + d_pending_exp[eq] = eq_exp; + d_infer.push_back(eq); + d_infer_exp.push_back(eq_exp); + return; + }else if( ( normal_forms[i][index_i].getKind()!=kind::CONST_STRING && index_i==normal_forms[i].size()-1 ) || + ( normal_forms[j][index_j].getKind()!=kind::CONST_STRING && index_j==normal_forms[j].size()-1 ) ){ + Trace("strings-solve-debug") << "Case 3 : at endpoint" << std::endl; + Node conc; + std::vector< Node > antec; + antec.insert(antec.end(), curr_exp.begin(), curr_exp.end() ); + std::vector< Node > antec_new_lits; + std::vector< Node > eqn; + for( unsigned r=0; r<2; r++ ){ + int index_k = r==0 ? index_i : index_j; + int k = r==0 ? i : j; + std::vector< Node > eqnc; + for( unsigned index_l=index_k; index_l<normal_forms[k].size(); index_l++ ){ + eqnc.push_back( normal_forms[k][index_l] ); + } + eqn.push_back( mkConcat( eqnc ) ); + } + conc = eqn[0].eqNode( eqn[1] ); + Node ant = mkExplain( antec, antec_new_lits ); + sendLemma( ant, conc, "Endpoint" ); + return; + }else{ + Trace("strings-solve-debug") << "Case 4 : must compare strings" << std::endl; + Node conc; + std::vector< Node > antec; + std::vector< Node > antec_new_lits; + //check for loops + //Trace("strings-loop") << "Check for loops i,j = " << (index_i+1) << "/" << normal_forms[i].size() << " " << (index_j+1) << "/" << normal_forms[j].size() << std::endl; + int has_loop[2] = { -1, -1 }; + for( unsigned r=0; r<2; r++ ){ + int index = (r==0 ? index_i : index_j); + int other_index = (r==0 ? index_j : index_i ); + int n_index = (r==0 ? i : j); + int other_n_index = (r==0 ? j : i); + if( normal_forms[other_n_index][other_index].getKind() != kind::CONST_STRING ) { + for( unsigned lp = index+1; lp<normal_forms[n_index].size(); lp++ ){ + if( normal_forms[n_index][lp]==normal_forms[other_n_index][other_index] ){ + has_loop[r] = lp; + break; + } + } + } + } + if( has_loop[0]!=-1 || has_loop[1]!=-1 ){ + int loop_n_index = has_loop[0]!=-1 ? i : j; + int other_n_index = has_loop[0]!=-1 ? j : i; + int loop_index = has_loop[0]!=-1 ? has_loop[0] : has_loop[1]; + int index = has_loop[0]!=-1 ? index_i : index_j; + int other_index = has_loop[0]!=-1 ? index_j : index_i; + Trace("strings-loop") << "Detected possible loop for " << normal_forms[loop_n_index][loop_index]; + Trace("strings-loop") << " ... " << normal_forms[other_n_index][other_index] << std::endl; + + //we have x * s1 * .... * sm = t1 * ... * tn * x * r1 * ... * rp + //check if + //t1 * ... * tn = n[loop_n_index][index]....n[loop_n_index][loop_index-1] = y * z + // and + //s1 * ... * sk = n[other_n_index][other_index+1].....n[other_n_index][k+1] = z * y + // for some y,z,k + + Trace("strings-loop") << "Must add lemma." << std::endl; + //need to break + Node sk_y= NodeManager::currentNM()->mkSkolem( "ysym_$$", normal_forms[i][index_i].getType(), "created for loop detection split" ); + Node sk_z= NodeManager::currentNM()->mkSkolem( "zsym_$$", normal_forms[i][index_i].getType(), "created for loop detection split" ); + + antec.insert(antec.end(), curr_exp.begin(), curr_exp.end() ); + //require that x is non-empty + Node x_empty = normal_forms[loop_n_index][loop_index].eqNode( d_emptyString ); + x_empty = Rewriter::rewrite( x_empty ); + //if( d_equalityEngine.hasTerm( d_emptyString ) && d_equalityEngine.areDisequal( normal_forms[loop_n_index][loop_index], d_emptyString, true ) ){ + // antec.push_back( x_empty.negate() ); + //}else{ + antec_new_lits.push_back( x_empty.negate() ); + //} + d_pending_req_phase[ x_empty ] = true; + + + //t1 * ... * tn = y * z + std::vector< Node > c1c; + //n[loop_n_index][index]....n[loop_n_index][loop_lindex-1] + for( int r=index; r<=loop_index-1; r++ ) { + c1c.push_back( normal_forms[loop_n_index][r] ); + } + Node conc1 = mkConcat( c1c ); + conc1 = NodeManager::currentNM()->mkNode( kind::EQUAL, conc1, + NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk_y, sk_z ) ); + std::vector< Node > c2c; + //s1 * ... * sk = n[other_n_index][other_index+1].....n[other_n_index][k+1] + for( int r=other_index+1; r < (int)normal_forms[other_n_index].size(); r++ ) { + c2c.push_back( normal_forms[other_n_index][r] ); + } + Node left2 = mkConcat( c2c ); + std::vector< Node > c3c; + c3c.push_back( sk_z ); + c3c.push_back( sk_y ); + //r1 * ... * rk = n[loop_n_index][loop_index+1]....n[loop_n_index][loop_index-1] + for( int r=loop_index+1; r < (int)normal_forms[loop_n_index].size(); r++ ) { + c3c.push_back( normal_forms[loop_n_index][r] ); + } + Node conc2 = NodeManager::currentNM()->mkNode( kind::EQUAL, left2, + mkConcat( c3c ) ); + + Node sk_y_len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk_y ); + //Node sk_z_len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk_z ); + //Node len_y_eq_zero = NodeManager::currentNM()->mkNode( kind::EQUAL, sk_y_len, d_zero); + //Node len_z_eq_zero = NodeManager::currentNM()->mkNode( kind::EQUAL, sk_z_len, d_zero); + //Node len_y_eq_zero = NodeManager::currentNM()->mkNode( kind::EQUAL, sk_y, d_emptyString); + //Node zz_imp_yz = NodeManager::currentNM()->mkNode( kind::IMPLIES, len_z_eq_zero, len_y_eq_zero); + + //Node z_neq_empty = NodeManager::currentNM()->mkNode( kind::EQUAL, sk_z, d_emptyString).negate(); + //Node len_x_gt_len_y = NodeManager::currentNM()->mkNode( kind::GT, + // NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, normal_forms[other_n_index][other_index]), + // sk_y_len ); + Node ant = mkExplain( antec, antec_new_lits ); + conc = NodeManager::currentNM()->mkNode( kind::AND, conc1, conc2 );//, x_eq_y_rest );// , z_neq_empty //, len_x_gt_len_y + + //Node x_eq_empty = NodeManager::currentNM()->mkNode( kind::EQUAL, normal_forms[other_n_index][other_index], d_emptyString); + //conc = NodeManager::currentNM()->mkNode( kind::OR, x_eq_empty, conc ); + + //we will be done + addNormalFormPair( normal_form_src[i], normal_form_src[j] ); + sendLemma( ant, conc, "Loop" ); + addInductiveEquation( normal_forms[other_n_index][other_index], sk_y, sk_z, ant, "Loop Induction" ); + return; + }else{ + Trace("strings-solve-debug") << "No loops detected." << std::endl; + if( normal_forms[i][index_i].getKind() == kind::CONST_STRING || + normal_forms[j][index_j].getKind() == kind::CONST_STRING) { + unsigned const_k = normal_forms[i][index_i].getKind() == kind::CONST_STRING ? i : j; + unsigned const_index_k = normal_forms[i][index_i].getKind() == kind::CONST_STRING ? index_i : index_j; + unsigned nconst_k = normal_forms[i][index_i].getKind() == kind::CONST_STRING ? j : i; + unsigned nconst_index_k = normal_forms[i][index_i].getKind() == kind::CONST_STRING ? index_j : index_i; + Node const_str = normal_forms[const_k][const_index_k]; + Node other_str = normal_forms[nconst_k][nconst_index_k]; + if( other_str.getKind() == kind::CONST_STRING ) { + unsigned len_short = const_str.getConst<String>().size() <= other_str.getConst<String>().size() ? const_str.getConst<String>().size() : other_str.getConst<String>().size(); + if( const_str.getConst<String>().strncmp(other_str.getConst<String>(), len_short) ) { + //same prefix + //k is the index of the string that is shorter + int k = const_str.getConst<String>().size()<other_str.getConst<String>().size() ? i : j; + int index_k = const_str.getConst<String>().size()<other_str.getConst<String>().size() ? index_i : index_j; + int l = const_str.getConst<String>().size()<other_str.getConst<String>().size() ? j : i; + int index_l = const_str.getConst<String>().size()<other_str.getConst<String>().size() ? index_j : index_i; + Node remainderStr = NodeManager::currentNM()->mkConst( normal_forms[l][index_l].getConst<String>().substr(len_short) ); + Trace("strings-solve-debug-test") << "Break normal form of " << normal_forms[l][index_l] << " into " << normal_forms[k][index_k] << ", " << remainderStr << std::endl; + normal_forms[l].insert( normal_forms[l].begin()+index_l + 1, remainderStr ); + normal_forms[l][index_l] = normal_forms[k][index_k]; + success = true; + } else { + //curr_exp is conflict + antec.insert(antec.end(), curr_exp.begin(), curr_exp.end() ); + Node ant = mkExplain( antec, antec_new_lits ); + sendLemma( ant, conc, "Conflict" ); + return; + } + } else { + Assert( other_str.getKind()!=kind::STRING_CONCAT ); + antec.insert(antec.end(), curr_exp.begin(), curr_exp.end() ); + Node firstChar = const_str.getConst<String>().size() == 1 ? const_str : + NodeManager::currentNM()->mkConst( const_str.getConst<String>().substr(0, 1) ); + //split the string + Node sk = NodeManager::currentNM()->mkSkolem( "ssym_$$", normal_forms[i][index_i].getType(), "created for split" ); + + Node eq1 = NodeManager::currentNM()->mkNode( kind::EQUAL, other_str, d_emptyString ); + Node eq2_m = NodeManager::currentNM()->mkNode( kind::EQUAL, other_str, + NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, firstChar, sk ) ); + Node eq2 = eq2_m;//NodeManager::currentNM()->mkNode( kind::AND, eq2_m, sk_len_geq_zero ); + conc = NodeManager::currentNM()->mkNode( kind::OR, eq1, eq2 ); + Trace("strings-solve-debug") << "Break normal form constant/variable " << std::endl; + + Node ant = mkExplain( antec, antec_new_lits ); + sendLemma( ant, conc, "Constant Split" ); + return; + } + }else{ + antec.insert(antec.end(), curr_exp.begin(), curr_exp.end() ); + + Node ldeq = NodeManager::currentNM()->mkNode( kind::EQUAL, length_term_i, length_term_j ).negate(); + if( d_equalityEngine.areDisequal( length_term_i, length_term_j, true ) ){ + antec.push_back( ldeq ); + }else{ + antec_new_lits.push_back(ldeq); + } + Node sk = NodeManager::currentNM()->mkSkolem( "ssym_$$", normal_forms[i][index_i].getType(), "created for split" ); + Node eq1 = NodeManager::currentNM()->mkNode( kind::EQUAL, normal_forms[i][index_i], + NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, normal_forms[j][index_j], sk ) ); + Node eq2 = NodeManager::currentNM()->mkNode( kind::EQUAL, normal_forms[j][index_j], + NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, normal_forms[i][index_i], sk ) ); + conc = NodeManager::currentNM()->mkNode( kind::OR, eq1, eq2 ); + // |sk| > 0 + //Node sk_len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk ); + //Node sk_gt_zero = NodeManager::currentNM()->mkNode( kind::GT, sk_len, d_zero); + Node sk_gt_zero = NodeManager::currentNM()->mkNode( kind::EQUAL, sk, d_emptyString).negate(); + Trace("strings-lemma") << "Strings lemma : " << sk_gt_zero << std::endl; + //d_out->lemma(sk_gt_zero); + d_lemma_cache.push_back( sk_gt_zero ); + + Node ant = mkExplain( antec, antec_new_lits ); + sendLemma( ant, conc, "Split" ); + return; + } + } + } + } + } + }while(success); + } + } + + //construct the normal form + if( normal_forms.empty() ){ + Trace("strings-solve-debug2") << "construct the normal form" << std::endl; + nf.push_back( eqc ); + } else { + Trace("strings-solve-debug2") << "just take the first normal form" << std::endl; + //just take the first normal form + nf.insert( nf.end(), normal_forms[0].begin(), normal_forms[0].end() ); + nf_exp.insert( nf_exp.end(), normal_forms_exp[0].begin(), normal_forms_exp[0].end() ); + if( eqc!=normal_form_src[0] ){ + nf_exp.push_back( NodeManager::currentNM()->mkNode( kind::EQUAL, eqc, normal_form_src[0] ) ); + } + Trace("strings-solve-debug2") << "just take the first normal form ... done" << std::endl; + } + //if( visited.empty() ){ + //TODO : cache? + //} + d_normal_forms[eqc].insert( d_normal_forms[eqc].end(), nf.begin(), nf.end() ); + d_normal_forms_exp[eqc].insert( d_normal_forms_exp[eqc].end(), nf_exp.begin(), nf_exp.end() ); + Trace("strings-process") << "Return process equivalence class " << eqc << " : returned." << std::endl; + }else{ + Trace("strings-process") << "Return process equivalence class " << eqc << " : already computed." << std::endl; + nf.insert( nf.end(), d_normal_forms[eqc].begin(), d_normal_forms[eqc].end() ); + nf_exp.insert( nf_exp.end(), d_normal_forms_exp[eqc].begin(), d_normal_forms_exp[eqc].end() ); + } + visited.pop_back(); + } +} + +bool TheoryStrings::normalizeDisequality( Node ni, Node nj ) { + //Assert( areDisequal( ni, nj ) ); + if( d_normal_forms[ni].size()>1 || d_normal_forms[nj].size()>1 ){ + unsigned index = 0; + while( index<d_normal_forms[ni].size() ){ + Node i = d_normal_forms[ni][index]; + Node j = d_normal_forms[nj][index]; + Trace("strings-solve-debug") << "...Processing " << i << " " << j << std::endl; + if( !areEqual( i, j ) ){ + Node li = getLength( i ); + Node lj = getLength( j ); + if( !areEqual(li, lj) ){ + Trace("strings-solve") << "Case 2 : add lemma " << std::endl; + //must add lemma + std::vector< Node > antec; + std::vector< Node > antec_new_lits; + antec.insert( antec.end(), d_normal_forms_exp[ni].begin(), d_normal_forms_exp[ni].end() ); + antec.insert( antec.end(), d_normal_forms_exp[nj].begin(), d_normal_forms_exp[nj].end() ); + antec.push_back( ni.eqNode( nj ).negate() ); + antec_new_lits.push_back( li.eqNode( lj ) ); + std::vector< Node > conc; + Node sk1 = NodeManager::currentNM()->mkSkolem( "w1sym_$$", ni.getType(), "created for disequality normalization" ); + Node sk2 = NodeManager::currentNM()->mkSkolem( "w2sym_$$", ni.getType(), "created for disequality normalization" ); + Node sk3 = NodeManager::currentNM()->mkSkolem( "w3sym_$$", ni.getType(), "created for disequality normalization" ); + Node sk4 = NodeManager::currentNM()->mkSkolem( "w4sym_$$", ni.getType(), "created for disequality normalization" ); + Node sk5 = NodeManager::currentNM()->mkSkolem( "w5sym_$$", ni.getType(), "created for disequality normalization" ); + Node w1w2w3 = NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk1, sk2, sk3 ); + Node w1w4w5 = NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk1, sk4, sk5 ); + Node s_eq_w1w2w3 = NodeManager::currentNM()->mkNode( kind::EQUAL, ni, w1w2w3 ); + conc.push_back( s_eq_w1w2w3 ); + Node t_eq_w1w4w5 = NodeManager::currentNM()->mkNode( kind::EQUAL, nj, w1w4w5 ); + conc.push_back( t_eq_w1w4w5 ); + Node w2_neq_w4 = sk2.eqNode( sk4 ).negate(); + conc.push_back( w2_neq_w4 ); + Node one = NodeManager::currentNM()->mkConst( ::CVC4::Rational( 1 ) ); + Node w2_len_one = NodeManager::currentNM()->mkNode( kind::EQUAL, NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk2), one); + conc.push_back( w2_len_one ); + Node w4_len_one = NodeManager::currentNM()->mkNode( kind::EQUAL, NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk4), one); + conc.push_back( w4_len_one ); + + //Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL, NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk2), + // NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk4) ); + //conc.push_back( eq ); + sendLemma( mkExplain( antec, antec_new_lits ), NodeManager::currentNM()->mkNode( kind::AND, conc ), "Disequality Normalize" ); + return true; + }else if( areDisequal( i, j ) ){ + Trace("strings-solve") << "Case 1 : found equal length disequal sub strings " << i << " " << j << std::endl; + //we are done + return false; + } + } + index++; + } + Assert( false ); + } + return false; +} + +void TheoryStrings::addNormalFormPair( Node n1, Node n2 ) { + if( !isNormalFormPair( n1, n2 ) ){ + NodeList* lst; + NodeListMap::iterator nf_i = d_nf_pairs.find( n1 ); + if( nf_i == d_nf_pairs.end() ){ + if( d_nf_pairs.find( n2 )!=d_nf_pairs.end() ){ + addNormalFormPair( n2, n1 ); + return; + } + lst = new(getSatContext()->getCMM()) NodeList( true, getSatContext(), false, + ContextMemoryAllocator<TNode>(getSatContext()->getCMM()) ); + d_nf_pairs.insertDataFromContextMemory( n1, lst ); + Trace("strings-nf") << "Create cache for " << n1 << std::endl; + }else{ + lst = (*nf_i).second; + } + Trace("strings-nf") << "Add normal form pair : " << n1 << " " << n2 << std::endl; + lst->push_back( n2 ); + Assert( isNormalFormPair( n1, n2 ) ); + }else{ + Trace("strings-nf-debug") << "Already a normal form pair " << n1 << " " << n2 << std::endl; + } + +} +bool TheoryStrings::isNormalFormPair( Node n1, Node n2 ) { + //TODO: modulo equality? + return isNormalFormPair2( n1, n2 ) || isNormalFormPair2( n2, n1 ); +} +bool TheoryStrings::isNormalFormPair2( Node n1, Node n2 ) { + //Trace("strings-debug") << "is normal form pair. " << n1 << " " << n2 << std::endl; + NodeList* lst; + NodeListMap::iterator nf_i = d_nf_pairs.find( n1 ); + if( nf_i != d_nf_pairs.end() ){ + lst = (*nf_i).second; + for( NodeList::const_iterator i = lst->begin(); i != lst->end(); ++i ) { + Node n = *i; + if( n==n2 ){ + return true; + } + } + } + return false; +} + +bool TheoryStrings::addInductiveEquation( Node x, Node y, Node z, Node exp, const char * c ) { + Trace("strings-solve-debug") << "add inductive equation for " << x << " = (" << y << " " << z << ")* " << y << std::endl; +#ifdef STR_UNROLL_INDUCTION + Node w = NodeManager::currentNM()->mkSkolem( "wsym_$$", x.getType(), "created for induction" ); + Node x_eq_y_w = NodeManager::currentNM()->mkNode( kind::EQUAL, x, + NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, y, w ) ); + Node lem = NodeManager::currentNM()->mkNode( kind::IMPLIES, exp, x_eq_y_w ); + Trace("strings-lemma") << "Strings " << c << " lemma : " << lem << std::endl; + d_lemma_cache.push_back( lem ); + + //add initial induction + Node lit1 = w.eqNode( d_emptyString ); + lit1 = Rewriter::rewrite( lit1 ); + Node wp = NodeManager::currentNM()->mkSkolem( "wpsym_$$", x.getType(), "created for induction" ); + Node lit2 = w.eqNode( NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, z, y, wp ) ); + lit2 = Rewriter::rewrite( lit2 ); + Node split_lem = NodeManager::currentNM()->mkNode( kind::OR, lit1, lit2 ); + Trace("strings-ind") << "Strings : Lemma " << c << " for unrolling " << split_lem << std::endl; + Trace("strings-lemma") << "Strings : Lemma " << c << " for unrolling " << split_lem << std::endl; + d_lemma_cache.push_back( split_lem ); + + //d_lit_to_decide.push_back( lit1 ); + d_lit_to_unroll[lit2] = true; + d_pending_req_phase[lit1] = true; + d_pending_req_phase[lit2] = false; + + x = w; + std::vector< Node > skc; + skc.push_back( y ); + skc.push_back( z ); + y = d_emptyString; + z = mkConcat( skc ); +#endif + + NodeListMap::iterator itr_x_y = d_ind_map1.find(x); + NodeList* lst1; + NodeList* lst2; + NodeList* lste; + NodeList* lstl; + if( itr_x_y == d_ind_map1.end() ) { + // add x->y + lst1 = new(getSatContext()->getCMM()) NodeList( true, getSatContext(), false, + ContextMemoryAllocator<TNode>(getSatContext()->getCMM()) ); + d_ind_map1.insertDataFromContextMemory( x, lst1 ); + // add x->z + lst2 = new(getSatContext()->getCMM()) NodeList( true, getSatContext(), false, + ContextMemoryAllocator<TNode>(getSatContext()->getCMM()) ); + d_ind_map2.insertDataFromContextMemory( x, lst2 ); + // add x->exp + lste = new(getSatContext()->getCMM()) NodeList( true, getSatContext(), false, + ContextMemoryAllocator<TNode>(getSatContext()->getCMM()) ); + d_ind_map_exp.insertDataFromContextMemory( x, lste ); + // add x->hasLemma false + lstl = new(getSatContext()->getCMM()) NodeList( true, getSatContext(), false, + ContextMemoryAllocator<TNode>(getSatContext()->getCMM()) ); + d_ind_map_lemma.insertDataFromContextMemory( x, lstl ); + } else { + //TODO: x in (yz)*y (exp) vs x in (y1 z1)*y1 (exp1) + lst1 = (*itr_x_y).second; + lst2 = (*d_ind_map2.find(x)).second; + lste = (*d_ind_map_exp.find(x)).second; + lstl = (*d_ind_map_lemma.find(x)).second; + Trace("strings-solve-debug") << "Already in maps " << x << " = (" << lst1 << " " << lst2 << ")* " << lst1 << std::endl; + Trace("strings-solve-debug") << "... with exp = " << lste << std::endl; + } + lst1->push_back( y ); + lst2->push_back( z ); + lste->push_back( exp ); +#ifdef STR_UNROLL_INDUCTION + return true; +#else + return false; +#endif +} + +void TheoryStrings::sendLemma( Node ant, Node conc, const char * c ) { + if( conc.isNull() ){ + d_out->conflict(ant); + Trace("strings-conflict") << "CONFLICT : Strings conflict : " << ant << std::endl; + d_conflict = true; + }else{ + Node lem = NodeManager::currentNM()->mkNode( kind::IMPLIES, ant, conc ); + Trace("strings-lemma") << "Strings " << c << " lemma : " << lem << std::endl; + d_lemma_cache.push_back( lem ); + } +} + +void TheoryStrings::sendSplit( Node a, Node b, const char * c ) { + Node eq = a.eqNode( b ); + eq = Rewriter::rewrite( eq ); + Node neq = NodeManager::currentNM()->mkNode( kind::NOT, eq ); + Node lemma_or = NodeManager::currentNM()->mkNode( kind::OR, eq, neq ); + Trace("strings-lemma") << "Strings " << c << " split lemma : " << lemma_or << std::endl; + d_lemma_cache.push_back(lemma_or); + d_pending_req_phase[eq] = true; +} + +Node TheoryStrings::mkConcat( std::vector< Node >& c ) { + Node cc = c.size()>1 ? NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, c ) : ( c.size()==1 ? c[0] : d_emptyString ); + return Rewriter::rewrite( cc ); +} + +Node TheoryStrings::mkExplain( std::vector< Node >& a, std::vector< Node >& an ) { + std::vector< TNode > antec_exp; + for( unsigned i=0; i<a.size(); i++ ){ + Trace("strings-solve-debug") << "Ask for explanation of " << a[i] << std::endl; + //assert + if(a[i].getKind() == kind::EQUAL) { + //assert( hasTerm(a[i][0]) ); + //assert( hasTerm(a[i][1]) ); + Assert( areEqual(a[i][0], a[i][1]) ); + } else if( a[i].getKind()==kind::NOT && a[i][0].getKind()==kind::EQUAL ){ + Assert( hasTerm(a[i][0][0]) ); + Assert( hasTerm(a[i][0][1]) ); + Assert( d_equalityEngine.areDisequal(a[i][0][0], a[i][0][1], true) ); + } + unsigned ps = antec_exp.size(); + explain(a[i], antec_exp); + Trace("strings-solve-debug") << "Done, explanation was : " << std::endl; + for( unsigned j=ps; j<antec_exp.size(); j++ ){ + Trace("strings-solve-debug") << " " << antec_exp[j] << std::endl; + } + Trace("strings-solve-debug") << std::endl; + } + for( unsigned i=0; i<an.size(); i++ ){ + Trace("strings-solve-debug") << "Add to explanation (new literal) " << an[i] << std::endl; + antec_exp.push_back(an[i]); + } + Node ant; + if( antec_exp.empty() ) { + ant = d_true; + } else if( antec_exp.size()==1 ) { + ant = antec_exp[0]; + } else { + ant = NodeManager::currentNM()->mkNode( kind::AND, antec_exp ); + } + ant = Rewriter::rewrite( ant ); + return ant; +} + +bool TheoryStrings::checkNormalForms() { + Trace("strings-process") << "Normalize equivalence classes...." << std::endl; + eq::EqClassesIterator eqcs2_i = eq::EqClassesIterator( &d_equalityEngine ); + for( unsigned t=0; t<2; t++ ){ + Trace("strings-eqc") << (t==0 ? "STRINGS:" : "OTHER:") << std::endl; + while( !eqcs2_i.isFinished() ){ + Node eqc = (*eqcs2_i); + bool print = (t==0 && eqc.getType().isString() ) || (t==1 && !eqc.getType().isString() ); + if (print) { + eq::EqClassIterator eqc2_i = eq::EqClassIterator( eqc, &d_equalityEngine ); + Trace("strings-eqc") << "Eqc( " << eqc << " ) : "; + while( !eqc2_i.isFinished() ) { + if( (*eqc2_i)!=eqc ){ + Trace("strings-eqc") << (*eqc2_i) << " "; + } + ++eqc2_i; + } + Trace("strings-eqc") << std::endl; + } + ++eqcs2_i; + } + Trace("strings-eqc") << std::endl; + } + Trace("strings-eqc") << std::endl; + for( NodeListMap::const_iterator it = d_nf_pairs.begin(); it != d_nf_pairs.end(); ++it ){ + NodeList* lst = (*it).second; + NodeList::const_iterator it2 = lst->begin(); + Trace("strings-nf") << (*it).first << " has been unified with "; + while( it2!=lst->end() ){ + Trace("strings-nf") << (*it2); + ++it2; + } + Trace("strings-nf") << std::endl; + } + Trace("strings-nf") << std::endl; + Trace("strings-nf") << "Current inductive equations : " << std::endl; + for( NodeListMap::const_iterator it = d_ind_map1.begin(); it != d_ind_map1.end(); ++it ){ + Node x = (*it).first; + NodeList* lst1 = (*it).second; + NodeList* lst2 = (*d_ind_map2.find(x)).second; + NodeList::const_iterator i1 = lst1->begin(); + NodeList::const_iterator i2 = lst2->begin(); + while( i1!=lst1->end() ){ + Node y = *i1; + Node z = *i2; + Trace("strings-nf") << "Inductive equation : " << x << " = ( " << y << " ++ " << z << " ) * " << y << std::endl; + ++i1; + ++i2; + } + } + + bool addedFact; + do { + Trace("strings-process") << "Check Normal Forms........next round" << std::endl; + //calculate normal forms for each equivalence class, possibly adding splitting lemmas + d_normal_forms.clear(); + d_normal_forms_exp.clear(); + std::map< Node, Node > nf_to_eqc; + std::map< Node, Node > eqc_to_exp; + d_lemma_cache.clear(); + d_pending_req_phase.clear(); + //get equivalence classes + std::vector< Node > eqcs; + getEquivalenceClasses( eqcs ); + for( unsigned i=0; i<eqcs.size(); i++ ){ + Node eqc = eqcs[i]; + Trace("strings-process") << "- Verify normal forms are the same for " << eqc << std::endl; + std::vector< Node > visited; + std::vector< Node > nf; + std::vector< Node > nf_exp; + normalizeEquivalenceClass(eqc, visited, nf, nf_exp); + if( d_conflict ){ + return true; + }else if ( d_pending.empty() && d_lemma_cache.empty() ){ + Node nf_term; + if( nf.size()==0 ){ + nf_term = d_emptyString; + }else if( nf.size()==1 ) { + nf_term = nf[0]; + } else { + nf_term = NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, nf ); + } + nf_term = Rewriter::rewrite( nf_term ); + Trace("strings-debug") << "Make nf_term_exp..." << std::endl; + Node nf_term_exp = nf_exp.empty() ? d_true : + nf_exp.size()==1 ? nf_exp[0] : NodeManager::currentNM()->mkNode( kind::AND, nf_exp ); + if( nf_to_eqc.find(nf_term)!=nf_to_eqc.end() ){ + //Trace("strings-debug") << "Merge because of normal form : " << eqc << " and " << nf_to_eqc[nf_term] << " both have normal form " << nf_term << std::endl; + //two equivalence classes have same normal form, merge + Node eq_exp = NodeManager::currentNM()->mkNode( kind::AND, nf_term_exp, eqc_to_exp[nf_to_eqc[nf_term]] ); + Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL, eqc, nf_to_eqc[nf_term] ); + Trace("strings-lemma") << "Strings (by normal forms) : Infer " << eq << " from " << eq_exp << std::endl; + //d_equalityEngine.assertEquality( eq, true, eq_exp ); + d_pending.push_back( eq ); + d_pending_exp[eq] = eq_exp; + d_infer.push_back(eq); + d_infer_exp.push_back(eq_exp); + }else{ + nf_to_eqc[nf_term] = eqc; + eqc_to_exp[eqc] = nf_term_exp; + } + } + Trace("strings-process") << "Done verifying normal forms are the same for " << eqc << std::endl; + } + + Trace("strings-nf-debug") << "**** Normal forms are : " << std::endl; + for( std::map< Node, Node >::iterator it = nf_to_eqc.begin(); it != nf_to_eqc.end(); ++it ){ + Trace("strings-nf-debug") << " normal_form(" << it->second << ") = " << it->first << std::endl; + } + Trace("strings-nf-debug") << std::endl; + addedFact = !d_pending.empty(); + doPendingFacts(); + } while ( !d_conflict && d_lemma_cache.empty() && addedFact ); + + + //process disequalities between equivalence classes + if( !d_conflict && d_lemma_cache.empty() ){ + std::vector< Node > eqcs; + getEquivalenceClasses( eqcs ); + std::vector< std::vector< Node > > cols; + std::vector< Node > lts; + seperateByLength( eqcs, cols, lts ); + for( unsigned i=0; i<cols.size(); i++ ){ + if( cols[i].size()>1 && d_lemma_cache.empty() ){ + Trace("strings-solve") << "- Verify disequalities are processed for "; + printConcat( d_normal_forms[cols[i][0]], "strings-solve" ); + Trace("strings-solve") << "..." << std::endl; + //must ensure that normal forms are disequal + for( unsigned j=1; j<cols[i].size(); j++ ){ + if( !d_equalityEngine.areDisequal( cols[i][0], cols[i][j], false ) ){ + sendSplit( cols[i][0], cols[i][j], "Disequality Normalization" ); + break; + }else{ + Trace("strings-solve") << " against "; + printConcat( d_normal_forms[cols[i][j]], "strings-solve" ); + Trace("strings-solve") << "..." << std::endl; + if( normalizeDisequality( cols[i][0], cols[i][j] ) ){ + break; + } + } + } + } + } + } + + //flush pending lemmas + if( !d_conflict && !d_lemma_cache.empty() ){ + doPendingLemmas(); + return true; + }else{ + return false; + } +} + +bool TheoryStrings::checkCardinality() { + int cardinality = options::stringCharCardinality(); + Trace("strings-solve-debug2") << "get cardinality: " << cardinality << endl; + + std::vector< Node > eqcs; + getEquivalenceClasses( eqcs ); + + std::vector< std::vector< Node > > cols; + std::vector< Node > lts; + seperateByLength( eqcs, cols, lts ); + + for( unsigned i = 0; i<cols.size(); ++i ){ + Node lr = lts[i]; + Trace("string-cardinality") << "Number of strings with length equal to " << lr << " is " << cols[i].size() << std::endl; + // size > c^k + double k = std::log( cols[i].size() ) / log((double) cardinality); + unsigned int int_k = (unsigned int)k; + Node k_node = NodeManager::currentNM()->mkConst( ::CVC4::Rational( int_k ) ); + //double c_k = pow ( (double)cardinality, (double)lr ); + if( cols[i].size() > 1 ) { + bool allDisequal = true; + for( std::vector< Node >::iterator itr1 = cols[i].begin(); + itr1 != cols[i].end(); ++itr1) { + for( std::vector< Node >::iterator itr2 = itr1 + 1; + itr2 != cols[i].end(); ++itr2) { + if(!d_equalityEngine.areDisequal( *itr1, *itr2, false )) { + allDisequal = false; + // add split lemma + sendSplit( *itr1, *itr2, "Cardinality" ); + doPendingLemmas(); + return true; + } + } + } + if(allDisequal) { + EqcInfo* ei = getOrMakeEqcInfo( lr, true ); + Trace("string-cardinality") << "Previous cardinality used for " << lr << " is " << ((int)ei->d_cardinality_lem_k.get()-1) << std::endl; + if( int_k+1 > ei->d_cardinality_lem_k.get() ){ + //add cardinality lemma + Node dist = NodeManager::currentNM()->mkNode( kind::DISTINCT, cols[i] ); + std::vector< Node > vec_node; + vec_node.push_back( dist ); + for( std::vector< Node >::iterator itr1 = cols[i].begin(); + itr1 != cols[i].end(); ++itr1) { + Node len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, *itr1 ); + if( len!=lr ){ + Node len_eq_lr = NodeManager::currentNM()->mkNode( kind::EQUAL, lr, len ); + vec_node.push_back( len_eq_lr ); + } + } + Node antc = NodeManager::currentNM()->mkNode( kind::AND, vec_node ); + Node len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, cols[i][0] ); + Node cons = NodeManager::currentNM()->mkNode( kind::GT, len, k_node ); + /* + sendLemma( antc, cons, "Cardinality" ); + ei->d_cardinality_lem_k.set( int_k+1 ); + if( !d_lemma_cache.empty() ){ + doPendingLemmas(); + return true; + } + */ + Node lemma = NodeManager::currentNM()->mkNode( kind::IMPLIES, antc, cons ); + lemma = Rewriter::rewrite( lemma ); + ei->d_cardinality_lem_k.set( int_k+1 ); + if( lemma!=d_true ){ + Trace("strings-lemma") << "Strings cardinality lemma : " << lemma << std::endl; + d_out->lemma(lemma); + return true; + } + } + } + } + } + return false; +} + +int TheoryStrings::gcd ( int a, int b ) { + int c; + while ( a != 0 ) { + c = a; a = b%a; b = c; + } + return b; +} + +bool TheoryStrings::checkInductiveEquations() { + bool hasEq = false; + if(d_ind_map1.size() != 0){ + Trace("strings-ind") << "We are sat, with these inductive equations : " << std::endl; + for( NodeListMap::const_iterator it = d_ind_map1.begin(); it != d_ind_map1.end(); ++it ){ + Node x = (*it).first; + Trace("strings-ind-debug") << "Check eq for " << x << std::endl; + NodeList* lst1 = (*it).second; + NodeList* lst2 = (*d_ind_map2.find(x)).second; + NodeList* lste = (*d_ind_map_exp.find(x)).second; + //NodeList* lstl = (*d_ind_map_lemma.find(x)).second; + NodeList::const_iterator i1 = lst1->begin(); + NodeList::const_iterator i2 = lst2->begin(); + NodeList::const_iterator ie = lste->begin(); + //NodeList::const_iterator il = lstl->begin(); + while( i1!=lst1->end() ){ + Node y = *i1; + Node z = *i2; + //Trace("strings-ind-debug") << "Check y=" << y << " , z=" << z << std::endl; + //if( il==lstl->end() ) { + std::vector< Node > nf_y, nf_z, exp_y, exp_z; + + //getFinalNormalForm( y, nf_y, exp_y); + //getFinalNormalForm( z, nf_z, exp_z); + //std::vector< Node > vec_empty; + //Node nexp_y = mkExplain( exp_y, vec_empty ); + //Trace("strings-ind-debug") << "Check nexp_y=" << nexp_y << std::endl; + //Node nexp_z = mkExplain( exp_z, vec_empty ); + + //Node exp = *ie; + //Trace("strings-ind-debug") << "Check exp=" << exp << std::endl; + + //exp = NodeManager::currentNM()->mkNode( kind::AND, exp, nexp_y, nexp_z ); + //exp = Rewriter::rewrite( exp ); + + Trace("strings-ind") << "Inductive equation : " << x << " = ( " << y << " ++ " << z << " )* " << y << std::endl; + /* + for( std::vector< Node >::const_iterator itr = nf_y.begin(); itr != nf_y.end(); ++itr) { + Trace("strings-ind") << (*itr) << " "; + } + Trace("strings-ind") << " ++ "; + for( std::vector< Node >::const_iterator itr = nf_z.begin(); itr != nf_z.end(); ++itr) { + Trace("strings-ind") << (*itr) << " "; + } + Trace("strings-ind") << " )* "; + for( std::vector< Node >::const_iterator itr = nf_y.begin(); itr != nf_y.end(); ++itr) { + Trace("strings-ind") << (*itr) << " "; + } + Trace("strings-ind") << std::endl; + */ + /* + Trace("strings-ind") << "Explanation is : " << exp << std::endl; + std::vector< Node > nf_yz; + nf_yz.insert( nf_yz.end(), nf_y.begin(), nf_y.end() ); + nf_yz.insert( nf_yz.end(), nf_z.begin(), nf_z.end() ); + std::vector< std::vector< Node > > cols; + std::vector< Node > lts; + seperateByLength( nf_yz, cols, lts ); + Trace("strings-ind") << "This can be grouped into collections : " << std::endl; + for( unsigned j=0; j<cols.size(); j++ ){ + Trace("strings-ind") << " : "; + for( unsigned k=0; k<cols[j].size(); k++ ){ + Trace("strings-ind") << cols[j][k] << " "; + } + Trace("strings-ind") << std::endl; + } + Trace("strings-ind") << std::endl; + + Trace("strings-ind") << "Add length lemma..." << std::endl; + std::vector< int > co; + co.push_back(0); + for(unsigned int k=0; k<lts.size(); ++k) { + if(lts[k].isConst() && lts[k].getType().isInteger()) { + int len = lts[k].getConst<Rational>().getNumerator().toUnsignedInt(); + co[0] += cols[k].size() * len; + } else { + co.push_back( cols[k].size() ); + } + } + int g_co = co[0]; + for(unsigned k=1; k<co.size(); ++k) { + g_co = gcd(g_co, co[k]); + } + Node lemma_len; + // both constants + Node len_x = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, x ); + Node sk = NodeManager::currentNM()->mkSkolem( "argsym_$$", NodeManager::currentNM()->integerType(), "created for length inductive lemma" ); + Node g_co_node = NodeManager::currentNM()->mkConst( CVC4::Rational(g_co) ); + Node sk_m_gcd = NodeManager::currentNM()->mkNode( kind::MULT, g_co_node, sk ); + Node len_y = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, y ); + Node sk_m_g_p_y = NodeManager::currentNM()->mkNode( kind::PLUS, sk_m_gcd, len_y ); + lemma_len = NodeManager::currentNM()->mkNode( kind::EQUAL, sk_m_g_p_y, len_x ); + //Node sk_geq_zero = NodeManager::currentNM()->mkNode( kind::GEQ, sk, d_zero ); + //lemma_len = NodeManager::currentNM()->mkNode( kind::AND, lemma_len, sk_geq_zero ); + lemma_len = NodeManager::currentNM()->mkNode( kind::IMPLIES, exp, lemma_len ); + Trace("strings-lemma") << "Strings: Add lemma " << lemma_len << std::endl; + d_out->lemma(lemma_len); + lstl->push_back( d_true ); + return true;*/ + //} + ++i1; + ++i2; + ++ie; + //++il; + if( !d_equalityEngine.hasTerm( d_emptyString ) || !d_equalityEngine.areEqual( y, d_emptyString ) || !d_equalityEngine.areEqual( x, d_emptyString ) ){ + hasEq = true; + } + } + } + } + if( hasEq ){ + Trace("strings-ind") << "It is incomplete." << std::endl; + d_out->setIncomplete(); + }else{ + Trace("strings-ind") << "We can answer SAT." << std::endl; + } + return false; +} + +void TheoryStrings::getEquivalenceClasses( std::vector< Node >& eqcs ) { + eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine ); + while( !eqcs_i.isFinished() ) { + Node eqc = (*eqcs_i); + //if eqc.getType is string + if (eqc.getType().isString()) { + eqcs.push_back( eqc ); + } + ++eqcs_i; + } +} + +void TheoryStrings::getFinalNormalForm( Node n, std::vector< Node >& nf, std::vector< Node >& exp ) { + if( n!=d_emptyString ){ + if( n.getKind()==kind::STRING_CONCAT ){ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + getFinalNormalForm( n[i], nf, exp ); + } + }else{ + Trace("strings-debug") << "Get final normal form " << n << std::endl; + Assert( d_equalityEngine.hasTerm( n ) ); + Node nr = d_equalityEngine.getRepresentative( n ); + EqcInfo *eqc_n = getOrMakeEqcInfo( nr, false ); + Node nc = eqc_n ? eqc_n->d_const_term.get() : Node::null(); + if( !nc.isNull() ){ + nf.push_back( nc ); + if( n!=nc ){ + exp.push_back( NodeManager::currentNM()->mkNode( kind::EQUAL, n, nc ) ); + } + }else{ + Assert( d_normal_forms.find( nr )!=d_normal_forms.end() ); + if( d_normal_forms[nr][0]==nr ){ + Assert( d_normal_forms[nr].size()==1 ); + nf.push_back( nr ); + if( n!=nr ){ + exp.push_back( NodeManager::currentNM()->mkNode( kind::EQUAL, n, nr ) ); + } + }else{ + for( unsigned i=0; i<d_normal_forms[nr].size(); i++ ){ + Assert( d_normal_forms[nr][i]!=nr ); + getFinalNormalForm( d_normal_forms[nr][i], nf, exp ); + } + exp.insert( exp.end(), d_normal_forms_exp[nr].begin(), d_normal_forms_exp[nr].end() ); + } + } + Trace("strings-ind-nf") << "The final normal form of " << n << " is " << nf << std::endl; + } + } +} + +void TheoryStrings::seperateByLength( std::vector< Node >& n, std::vector< std::vector< Node > >& cols, + std::vector< Node >& lts ) { + unsigned leqc_counter = 0; + std::map< Node, unsigned > eqc_to_leqc; + std::map< unsigned, Node > leqc_to_eqc; + std::map< unsigned, std::vector< Node > > eqc_to_strings; + for( unsigned i=0; i<n.size(); i++ ){ + Node eqc = n[i]; + Assert( d_equalityEngine.getRepresentative(eqc)==eqc ); + EqcInfo* ei = getOrMakeEqcInfo( eqc, false ); + Node lt = ei ? ei->d_length_term : Node::null(); + if( !lt.isNull() ){ + lt = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, lt ); + Node r = d_equalityEngine.getRepresentative( lt ); + if( eqc_to_leqc.find( r )==eqc_to_leqc.end() ){ + eqc_to_leqc[r] = leqc_counter; + leqc_to_eqc[leqc_counter] = r; + leqc_counter++; + } + eqc_to_strings[ eqc_to_leqc[r] ].push_back( eqc ); + }else{ + eqc_to_strings[leqc_counter].push_back( eqc ); + leqc_counter++; + } + } + for( std::map< unsigned, std::vector< Node > >::iterator it = eqc_to_strings.begin(); it != eqc_to_strings.end(); ++it ){ + std::vector< Node > vec; + vec.insert( vec.end(), it->second.begin(), it->second.end() ); + lts.push_back( leqc_to_eqc[it->first] ); + cols.push_back( vec ); + } +} + +void TheoryStrings::printConcat( std::vector< Node >& n, const char * c ) { + for( unsigned i=0; i<n.size(); i++ ){ + if( i>0 ) Trace(c) << " ++ "; + Trace(c) << n[i]; + } +} + +/* +Node TheoryStrings::getNextDecisionRequest() { + if( d_lit_to_decide_index.get()<d_lit_to_decide.size() ){ + Node l = d_lit_to_decide[d_lit_to_decide_index.get()]; + d_lit_to_decide_index.set( d_lit_to_decide_index.get() + 1 ); + Trace("strings-ind") << "Strings-ind : decide on " << l << std::endl; + return l; + }else{ + return Node::null(); + } +} +*/ +}/* CVC4::theory::strings namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/strings/theory_strings.h b/src/theory/strings/theory_strings.h new file mode 100644 index 000000000..6b8144d6e --- /dev/null +++ b/src/theory/strings/theory_strings.h @@ -0,0 +1,244 @@ +/********************* */ +/*! \file theory_strings.h + ** \verbatim + ** Original author: Tianyi Liang + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2013-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Theory of strings + ** + ** Theory of strings. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__STRINGS__THEORY_STRINGS_H +#define __CVC4__THEORY__STRINGS__THEORY_STRINGS_H + +#include "theory/theory.h" +#include "theory/uf/equality_engine.h" + +#include "context/cdchunk_list.h" + +namespace CVC4 { +namespace theory { +namespace strings { + +/** + * Decision procedure for strings. + * + */ + +class TheoryStrings : public Theory { + typedef context::CDChunkList<Node> NodeList; + typedef context::CDHashMap<Node, NodeList*, NodeHashFunction> NodeListMap; + typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap; + public: + + TheoryStrings(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo, QuantifiersEngine* qe); + ~TheoryStrings(); + + void setMasterEqualityEngine(eq::EqualityEngine* eq); + + std::string identify() const { return std::string("TheoryStrings"); } + + Node getRepresentative( Node t ); + bool hasTerm( Node a ); + bool areEqual( Node a, Node b ); + bool areDisequal( Node a, Node b ); + Node getLength( Node t ); + public: + + void propagate(Effort e); + bool propagate(TNode literal); + void explain( TNode literal, std::vector<TNode>& assumptions ); + Node explain( TNode literal ); + + + // NotifyClass for equality engine + class NotifyClass : public eq::EqualityEngineNotify { + TheoryStrings& d_str; + public: + NotifyClass(TheoryStrings& t_str): d_str(t_str) {} + bool eqNotifyTriggerEquality(TNode equality, bool value) { + Debug("strings") << "NotifyClass::eqNotifyTriggerEquality(" << equality << ", " << (value ? "true" : "false" )<< ")" << std::endl; + if (value) { + return d_str.propagate(equality); + } else { + // We use only literal triggers so taking not is safe + return d_str.propagate(equality.notNode()); + } + } + bool eqNotifyTriggerPredicate(TNode predicate, bool value) { + Debug("strings") << "NotifyClass::eqNotifyTriggerPredicate(" << predicate << ", " << (value ? "true" : "false") << ")" << std::endl; + if (value) { + return d_str.propagate(predicate); + } else { + return d_str.propagate(predicate.notNode()); + } + } + bool eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value) { + Debug("strings") << "NotifyClass::eqNotifyTriggerTermMerge(" << tag << ", " << t1 << ", " << t2 << ")" << std::endl; + if (value) { + return d_str.propagate(t1.eqNode(t2)); + } else { + return d_str.propagate(t1.eqNode(t2).notNode()); + } + } + void eqNotifyConstantTermMerge(TNode t1, TNode t2) { + Debug("strings") << "NotifyClass::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << ")" << std::endl; + d_str.conflict(t1, t2); + } + void eqNotifyNewClass(TNode t) { + Debug("strings") << "NotifyClass::eqNotifyNewClass(" << t << std::endl; + d_str.eqNotifyNewClass(t); + } + void eqNotifyPreMerge(TNode t1, TNode t2) { + Debug("strings") << "NotifyClass::eqNotifyPreMerge(" << t1 << ", " << t2 << std::endl; + d_str.eqNotifyPreMerge(t1, t2); + } + void eqNotifyPostMerge(TNode t1, TNode t2) { + Debug("strings") << "NotifyClass::eqNotifyPostMerge(" << t1 << ", " << t2 << std::endl; + d_str.eqNotifyPostMerge(t1, t2); + } + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { + Debug("strings") << "NotifyClass::eqNotifyDisequal(" << t1 << ", " << t2 << ", " << reason << std::endl; + d_str.eqNotifyDisequal(t1, t2, reason); + } + };/* class TheoryStrings::NotifyClass */ + + private: + /** The notify class */ + NotifyClass d_notify; + /** Equaltity engine */ + eq::EqualityEngine d_equalityEngine; + /** Are we in conflict */ + context::CDO<bool> d_conflict; + std::vector< Node > d_length_intro_vars; + Node d_emptyString; + Node d_true; + Node d_false; + Node d_zero; + //list of pairs of nodes to merge + std::map< Node, Node > d_pending_exp; + std::vector< Node > d_pending; + std::vector< Node > d_lemma_cache; + std::map< Node, bool > d_pending_req_phase; + /** inferences */ + NodeList d_infer; + NodeList d_infer_exp; + //map of pairs of terms that have the same normal form + NodeListMap d_nf_pairs; + void addNormalFormPair( Node n1, Node n2 ); + bool isNormalFormPair( Node n1, Node n2 ); + bool isNormalFormPair2( Node n1, Node n2 ); + + NodeListMap d_ind_map1; + NodeListMap d_ind_map2; + NodeListMap d_ind_map_exp; + NodeListMap d_ind_map_lemma; + bool addInductiveEquation( Node x, Node y, Node z, Node exp, const char * c ); + + //for unrolling inductive equations + NodeBoolMap d_lit_to_unroll; + + + ///////////////////////////////////////////////////////////////////////////// + // MODEL GENERATION + ///////////////////////////////////////////////////////////////////////////// + public: + + void collectModelInfo(TheoryModel* m, bool fullModel); + + ///////////////////////////////////////////////////////////////////////////// + // NOTIFICATIONS + ///////////////////////////////////////////////////////////////////////////// + + public: + + void shutdown() { } + + ///////////////////////////////////////////////////////////////////////////// + // MAIN SOLVER + ///////////////////////////////////////////////////////////////////////////// + private: + void addSharedTerm(TNode n); + EqualityStatus getEqualityStatus(TNode a, TNode b); + + private: + class EqcInfo + { + public: + EqcInfo( context::Context* c ); + ~EqcInfo(){} + //constant in this eqc + context::CDO< Node > d_const_term; + context::CDO< Node > d_length_term; + context::CDO< unsigned > d_cardinality_lem_k; + }; + /** map from representatives to information necessary for equivalence classes */ + std::map< Node, EqcInfo* > d_eqc_info; + EqcInfo * getOrMakeEqcInfo( Node eqc, bool doMake = true ); + //maintain which concat terms have the length lemma instantiatied + std::map< Node, bool > d_length_inst; + private: + std::map< Node, std::vector< Node > > d_normal_forms; + std::map< Node, std::vector< Node > > d_normal_forms_exp; + void getNormalForms(Node &eqc, std::vector< Node > & visited, std::vector< Node > & nf, + std::vector< std::vector< Node > > &normal_forms, std::vector< std::vector< Node > > &normal_forms_exp, std::vector< Node > &normal_form_src); + void normalizeEquivalenceClass( Node n, std::vector< Node > & visited, std::vector< Node > & nf, std::vector< Node > & nf_exp ); + bool normalizeDisequality( Node n1, Node n2 ); + + bool checkNormalForms(); + bool checkCardinality(); + bool checkInductiveEquations(); + int gcd(int a, int b); + public: + void preRegisterTerm(TNode n); + void check(Effort e); + + /** Conflict when merging two constants */ + void conflict(TNode a, TNode b); + /** called when a new equivalance class is created */ + void eqNotifyNewClass(TNode t); + /** called when two equivalance classes will merge */ + void eqNotifyPreMerge(TNode t1, TNode t2); + /** called when two equivalance classes have merged */ + void eqNotifyPostMerge(TNode t1, TNode t2); + /** called when two equivalence classes are made disequal */ + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason); +protected: + /** compute care graph */ + void computeCareGraph(); + + //do pending merges + void doPendingFacts(); + void doPendingLemmas(); + + void sendLemma( Node ant, Node conc, const char * c ); + void sendSplit( Node a, Node b, const char * c ); + /** mkConcat **/ + Node mkConcat( std::vector< Node >& c ); + /** mkExplain **/ + Node mkExplain( std::vector< Node >& a, std::vector< Node >& an ); + + //get equivalence classes + void getEquivalenceClasses( std::vector< Node >& eqcs ); + //get final normal form + void getFinalNormalForm( Node n, std::vector< Node >& nf, std::vector< Node >& exp ); + + //seperate into collections with equal length + void seperateByLength( std::vector< Node >& n, std::vector< std::vector< Node > >& col, std::vector< Node >& lts ); +private: + void printConcat( std::vector< Node >& n, const char * c ); +};/* class TheoryStrings */ + +}/* CVC4::theory::strings namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__STRINGS__THEORY_STRINGS_H */ diff --git a/src/theory/strings/theory_strings_preprocess.cpp b/src/theory/strings/theory_strings_preprocess.cpp new file mode 100644 index 000000000..8fa4345e5 --- /dev/null +++ b/src/theory/strings/theory_strings_preprocess.cpp @@ -0,0 +1,135 @@ +/********************* */ +/*! \file theory_strings_preprocess.cpp + ** \verbatim + ** Original author: Tianyi Liang + ** Major contributors: Tianyi Liang, Andrew Reynolds + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2013-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Strings Preprocess + ** + ** Strings Preprocess. + **/ + +#include "theory/strings/theory_strings_preprocess.h" +#include "expr/kind.h" + +namespace CVC4 { +namespace theory { +namespace strings { + +void StringsPreprocess::simplifyRegExp( Node s, Node r, std::vector< Node > &ret ) { + int k = r.getKind(); + switch( k ) { + case kind::STRING_TO_REGEXP: + { + Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL, s, r[0] ); + ret.push_back( eq ); + } + break; + case kind::REGEXP_CONCAT: + { + std::vector< Node > cc; + for(unsigned i=0; i<r.getNumChildren(); ++i) { + Node sk = NodeManager::currentNM()->mkSkolem( "recsym_$$", s.getType(), "created for regular expression concat" ); + simplifyRegExp( sk, r[i], ret ); + cc.push_back( sk ); + } + Node cc_eq = NodeManager::currentNM()->mkNode( kind::EQUAL, s, + NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, cc ) ); + ret.push_back( cc_eq ); + } + break; + case kind::REGEXP_OR: + { + std::vector< Node > c_or; + for(unsigned i=0; i<r.getNumChildren(); ++i) { + simplifyRegExp( s, r[i], c_or ); + } + Node eq = NodeManager::currentNM()->mkNode( kind::OR, c_or ); + ret.push_back( eq ); + } + break; + case kind::REGEXP_INTER: + for(unsigned i=0; i<r.getNumChildren(); ++i) { + simplifyRegExp( s, r[i], ret ); + } + break; + case kind::REGEXP_STAR: + { + Node sk = NodeManager::currentNM()->mkSkolem( "ressym_$$", s.getType(), "created for regular expression star" ); + Node eq = NodeManager::currentNM()->mkNode( kind::EQUAL, + NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk, s ), + NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, s, sk )); + ret.push_back( eq ); + simplifyRegExp( sk, r[0], ret ); + } + break; + case kind::REGEXP_OPT: + { + Node eq_empty = NodeManager::currentNM()->mkNode( kind::EQUAL, s, NodeManager::currentNM()->mkConst( ::CVC4::String("") ) ); + std::vector< Node > rr; + simplifyRegExp( s, r[0], rr ); + Node nrr = rr.size()==1 ? rr[0] : NodeManager::currentNM()->mkNode( kind::AND, rr ); + ret.push_back( NodeManager::currentNM()->mkNode( kind::OR, eq_empty, nrr) ); + } + break; + default: + //TODO:case kind::REGEXP_PLUS: + //TODO: special sym: sigma, none, all + break; + } +} + +Node StringsPreprocess::simplify( Node t ) { + std::hash_map<TNode, Node, TNodeHashFunction>::const_iterator i = d_cache.find(t); + if(i != d_cache.end()) { + return (*i).second.isNull() ? t : (*i).second; + } + + if( t.getKind() == kind::STRING_IN_REGEXP ){ + // t0 in t1 + //rewrite it + std::vector< Node > ret; + simplifyRegExp( t[0], t[1], ret ); + + Node n = ret.size() == 1 ? ret[0] : NodeManager::currentNM()->mkNode( kind::AND, ret ); + d_cache[t] = (t == n) ? Node::null() : n; + return n; + }else if( t.getNumChildren()>0 ){ + std::vector< Node > cc; + if (t.getMetaKind() == kind::metakind::PARAMETERIZED) { + cc.push_back(t.getOperator()); + } + bool changed = false; + for( unsigned i=0; i<t.getNumChildren(); i++ ){ + Node tn = simplify( t[i] ); + cc.push_back( tn ); + changed = changed || tn!=t[i]; + } + if(changed) { + Node n = NodeManager::currentNM()->mkNode( t.getKind(), cc ); + d_cache[t] = n; + return n; + } else { + d_cache[t] = Node::null(); + return t; + } + }else{ + d_cache[t] = Node::null(); + return t; + } +} + +void StringsPreprocess::simplify(std::vector< Node > &vec_node) { + for( unsigned i=0; i<vec_node.size(); i++ ){ + vec_node[i] = simplify( vec_node[i] ); + } +} + +}/* CVC4::theory::strings namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/strings/theory_strings_preprocess.h b/src/theory/strings/theory_strings_preprocess.h new file mode 100644 index 000000000..f82a3cf24 --- /dev/null +++ b/src/theory/strings/theory_strings_preprocess.h @@ -0,0 +1,44 @@ +/********************* */ +/*! \file theory_strings_preprocess.h + ** \verbatim + ** Original author: Tianyi Liang + ** Major contributors: Tianyi Liang, Andrew Reynolds + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2013-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Strings Preprocess + ** + ** Strings Preprocess. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__STRINGS__PREPROCESS_H +#define __CVC4__THEORY__STRINGS__PREPROCESS_H + +#include <vector> +#include "util/hash.h" +#include "theory/theory.h" + +namespace CVC4 { +namespace theory { +namespace strings { + +class StringsPreprocess { + // NOTE: this class is NOT context-dependent + std::hash_map<TNode, Node, TNodeHashFunction> d_cache; +private: + void simplifyRegExp( Node s, Node r, std::vector< Node > &ret ); + Node simplify( Node t ); +public: +void simplify(std::vector< Node > &vec_node); +}; + +}/* CVC4::theory::strings namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__STRINGS__PREPROCESS_H */ diff --git a/src/theory/strings/theory_strings_rewriter.cpp b/src/theory/strings/theory_strings_rewriter.cpp new file mode 100644 index 000000000..412135675 --- /dev/null +++ b/src/theory/strings/theory_strings_rewriter.cpp @@ -0,0 +1,156 @@ +/********************* */ +/*! \file theory_strings_rewriter.cpp + ** \verbatim + ** Original author: Tianyi Liang + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2013-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 the theory of strings. + ** + ** Implementation of the theory of strings. + **/ +#include "theory/strings/theory_strings_rewriter.h" + +using namespace std; +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::theory; +using namespace CVC4::theory::strings; + +Node TheoryStringsRewriter::rewriteConcatString(TNode node) { + Trace("strings-prerewrite") << "Strings::rewriteConcatString start " << node << std::endl; + Node retNode = node; + std::vector<Node> node_vec; + Node preNode = Node::null(); + for(unsigned int i=0; i<node.getNumChildren(); ++i) { + Node tmpNode = node[i]; + if(node[i].getKind() == kind::STRING_CONCAT) { + tmpNode = rewriteConcatString(node[i]); + if(tmpNode.getKind() == kind::STRING_CONCAT) { + unsigned int j=0; + if(!preNode.isNull()) { + if(tmpNode[0].isConst()) { + preNode = NodeManager::currentNM()->mkConst( preNode.getConst<String>().concat( tmpNode[0].getConst<String>() ) ); + node_vec.push_back( preNode ); + preNode = Node::null(); + ++j; + } else { + node_vec.push_back( preNode ); + preNode = Node::null(); + node_vec.push_back( tmpNode[0] ); + ++j; + } + } + for(; j<tmpNode.getNumChildren() - 1; ++j) { + node_vec.push_back( tmpNode[j] ); + } + tmpNode = tmpNode[j]; + } + } + if(!tmpNode.isConst()) { + if(preNode != Node::null()) { + if(preNode.getKind() == kind::CONST_STRING && preNode.getConst<String>().toString()=="" ) { + preNode = Node::null(); + } else { + node_vec.push_back( preNode ); + preNode = Node::null(); + } + } + node_vec.push_back( tmpNode ); + } else { + if(preNode.isNull()) { + preNode = tmpNode; + } else { + preNode = NodeManager::currentNM()->mkConst( preNode.getConst<String>().concat( tmpNode.getConst<String>() ) ); + } + } + } + if(preNode != Node::null()) { + node_vec.push_back( preNode ); + } + if(node_vec.size() > 1) { + retNode = NodeManager::currentNM()->mkNode(kind::STRING_CONCAT, node_vec); + } else { + retNode = node_vec[0]; + } + Trace("strings-prerewrite") << "Strings::rewriteConcatString end " << retNode << std::endl; + return retNode; +} + +RewriteResponse TheoryStringsRewriter::postRewrite(TNode node) { + Trace("strings-postrewrite") << "Strings::postRewrite start " << node << std::endl; + Node retNode = node; + + if(node.getKind() == kind::STRING_CONCAT) { + retNode = rewriteConcatString(node); + } else if(node.getKind() == kind::EQUAL) { + Node leftNode = node[0]; + if(node[0].getKind() == kind::STRING_CONCAT) { + leftNode = rewriteConcatString(node[0]); + } + Node rightNode = node[1]; + if(node[1].getKind() == kind::STRING_CONCAT) { + rightNode = rewriteConcatString(node[1]); + } + + if(leftNode == rightNode) { + retNode = NodeManager::currentNM()->mkConst(true); + } else if(leftNode.isConst() && rightNode.isConst()) { + retNode = NodeManager::currentNM()->mkConst(false); + } else if(leftNode > rightNode) { + retNode = NodeManager::currentNM()->mkNode(kind::EQUAL, rightNode, leftNode); + } else if( leftNode != node[0] || rightNode != node[1]) { + retNode = NodeManager::currentNM()->mkNode(kind::EQUAL, leftNode, rightNode); + } + } else if(node.getKind() == kind::STRING_IN_REGEXP) { + Node leftNode = node[0]; + if(node[0].getKind() == kind::STRING_CONCAT) { + leftNode = rewriteConcatString(node[0]); + } + // TODO: right part + Node rightNode = node[1]; + // merge + if( leftNode != node[0] || rightNode != node[1]) { + retNode = NodeManager::currentNM()->mkNode(kind::STRING_IN_REGEXP, leftNode, rightNode); + } + } else if(node.getKind() == kind::STRING_LENGTH) { + if(node[0].isConst()) { + retNode = NodeManager::currentNM()->mkConst( ::CVC4::Rational( node[0].getConst<String>().size() ) ); + } else if(node[0].getKind() == kind::STRING_CONCAT) { + Node tmpNode = rewriteConcatString(node[0]); + if(tmpNode.isConst()) { + retNode = NodeManager::currentNM()->mkConst( ::CVC4::Rational( tmpNode.getConst<String>().size() ) ); + } else { + // it has to be string concat + std::vector<Node> node_vec; + for(unsigned int i=0; i<tmpNode.getNumChildren(); ++i) { + if(tmpNode[i].isConst()) { + node_vec.push_back( NodeManager::currentNM()->mkConst( ::CVC4::Rational( tmpNode[i].getConst<String>().size() ) ) ); + } else { + node_vec.push_back( NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, tmpNode[i]) ); + } + } + retNode = NodeManager::currentNM()->mkNode(kind::PLUS, node_vec); + } + } + } + + Trace("strings-postrewrite") << "Strings::postRewrite returning " << node << std::endl; + return RewriteResponse(REWRITE_DONE, retNode); +} + +RewriteResponse TheoryStringsRewriter::preRewrite(TNode node) { + Node retNode = node; + Trace("strings-prerewrite") << "Strings::preRewrite start " << node << std::endl; + + if(node.getKind() == kind::STRING_CONCAT) { + retNode = rewriteConcatString(node); + } + + Trace("strings-prerewrite") << "Strings::preRewrite returning " << retNode << std::endl; + return RewriteResponse(REWRITE_DONE, retNode); +} diff --git a/src/theory/strings/theory_strings_rewriter.h b/src/theory/strings/theory_strings_rewriter.h new file mode 100644 index 000000000..3bccd91de --- /dev/null +++ b/src/theory/strings/theory_strings_rewriter.h @@ -0,0 +1,49 @@ +/********************* */ +/*! \file theory_strings_rewriter.h + ** \verbatim + ** Original author: Tianyi Liang + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2013-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__STRINGS__THEORY_STRINGS_REWRITER_H +#define __CVC4__THEORY__STRINGS__THEORY_STRINGS_REWRITER_H + +#include "theory/rewriter.h" +#include "theory/type_enumerator.h" +#include "expr/attribute.h" + +namespace CVC4 { +namespace theory { +namespace strings { + +class TheoryStringsRewriter { + +public: + static Node rewriteConcatString(TNode node); + + static RewriteResponse postRewrite(TNode node); + + static RewriteResponse preRewrite(TNode node); + + static inline void init() {} + static inline void shutdown() {} + +};/* class TheoryStringsRewriter */ + +}/* CVC4::theory::strings namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__STRINGS__THEORY_STRINGS_REWRITER_H */ diff --git a/src/theory/strings/theory_strings_type_rules.h b/src/theory/strings/theory_strings_type_rules.h new file mode 100644 index 000000000..8fc630206 --- /dev/null +++ b/src/theory/strings/theory_strings_type_rules.h @@ -0,0 +1,223 @@ +/********************* */ +/*! \file theory_strings_type_rules.h + ** \verbatim + ** Original author: Tianyi Liang + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2013-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Typing and cardinality rules for the theory of arrays + ** + ** Typing and cardinality rules for the theory of arrays. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__STRINGS__THEORY_STRINGS_TYPE_RULES_H +#define __CVC4__THEORY__STRINGS__THEORY_STRINGS_TYPE_RULES_H + +namespace CVC4 { +namespace theory { +namespace strings { + +class StringConstantTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + return nodeManager->stringType(); + } +}; + +class StringConcatTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + TNode::iterator it = n.begin(); + TNode::iterator it_end = n.end(); + for (; it != it_end; ++ it) { + TypeNode t = (*it).getType(check); + if (!t.isString()) { + throw TypeCheckingExceptionPrivate(n, "expecting string terms in string concat"); + } + } + return nodeManager->stringType(); + } +}; + +class StringLengthTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + if( check ){ + TypeNode t = n[0].getType(check); + if (!t.isString()) { + throw TypeCheckingExceptionPrivate(n, "expecting string terms in string length"); + } + } + return nodeManager->integerType(); + } +}; + +class RegExpConstantTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + return nodeManager->regexpType(); + } +}; + +class RegExpConcatTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + TNode::iterator it = n.begin(); + TNode::iterator it_end = n.end(); + for (; it != it_end; ++ it) { + TypeNode t = (*it).getType(check); + if (!t.isRegExp()) { + throw TypeCheckingExceptionPrivate(n, "expecting regexp terms"); + } + } + return nodeManager->regexpType(); + } +}; + +class RegExpOrTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + TNode::iterator it = n.begin(); + TNode::iterator it_end = n.end(); + for (; it != it_end; ++ it) { + TypeNode t = (*it).getType(check); + if (!t.isRegExp()) { + throw TypeCheckingExceptionPrivate(n, "expecting regexp terms"); + } + } + return nodeManager->regexpType(); + } +}; + +class RegExpInterTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + TNode::iterator it = n.begin(); + TNode::iterator it_end = n.end(); + for (; it != it_end; ++ it) { + TypeNode t = (*it).getType(check); + if (!t.isRegExp()) { + throw TypeCheckingExceptionPrivate(n, "expecting regexp terms"); + } + } + return nodeManager->regexpType(); + } +}; + +class RegExpStarTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + TNode::iterator it = n.begin(); + TNode::iterator it_end = n.end(); + TypeNode t = (*it).getType(check); + if (!t.isRegExp()) { + throw TypeCheckingExceptionPrivate(n, "expecting regexp terms"); + } + if(++it != it_end) { + throw TypeCheckingExceptionPrivate(n, "too many regexp"); + } + + return nodeManager->regexpType(); + } +}; + +class RegExpPlusTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + TNode::iterator it = n.begin(); + TNode::iterator it_end = n.end(); + TypeNode t = (*it).getType(check); + if (!t.isRegExp()) { + throw TypeCheckingExceptionPrivate(n, "expecting regexp terms"); + } + if(++it != it_end) { + throw TypeCheckingExceptionPrivate(n, "too many regexp"); + } + + return nodeManager->regexpType(); + } +}; + +class RegExpOptTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + TNode::iterator it = n.begin(); + TNode::iterator it_end = n.end(); + TypeNode t = (*it).getType(check); + if (!t.isRegExp()) { + throw TypeCheckingExceptionPrivate(n, "expecting regexp terms"); + } + if(++it != it_end) { + throw TypeCheckingExceptionPrivate(n, "too many regexp"); + } + + return nodeManager->regexpType(); + } +}; + +class StringToRegExpTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + TNode::iterator it = n.begin(); + TNode::iterator it_end = n.end(); + TypeNode t = (*it).getType(check); + if (!t.isString()) { + throw TypeCheckingExceptionPrivate(n, "expecting string terms"); + } + if( (*it).getKind() != kind::CONST_STRING ) { + throw TypeCheckingExceptionPrivate(n, "expecting constant string terms"); + } + if(++it != it_end) { + throw TypeCheckingExceptionPrivate(n, "too many terms"); + } + + return nodeManager->regexpType(); + } +}; + +class StringInRegExpTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + TNode::iterator it = n.begin(); + TNode::iterator it_end = n.end(); + TypeNode t = (*it).getType(check); + if (!t.isString()) { + throw TypeCheckingExceptionPrivate(n, "expecting string terms"); + } + ++it; + t = (*it).getType(check); + if (!t.isRegExp()) { + throw TypeCheckingExceptionPrivate(n, "expecting regexp terms"); + } + if(++it != it_end) { + throw TypeCheckingExceptionPrivate(n, "too many terms"); + } + + return nodeManager->booleanType(); + } +}; + + +}/* CVC4::theory::strings namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__STRINGS__THEORY_STRINGS_TYPE_RULES_H */ diff --git a/src/theory/strings/type_enumerator.h b/src/theory/strings/type_enumerator.h new file mode 100644 index 000000000..3dc12009b --- /dev/null +++ b/src/theory/strings/type_enumerator.h @@ -0,0 +1,130 @@ +/********************* */ +/*! \file type_enumerator.h + ** \verbatim + ** Original author: Tianyi Liang + ** 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 Enumerators for strings + ** + ** Enumerators for strings. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__STRINGS__TYPE_ENUMERATOR_H +#define __CVC4__THEORY__STRINGS__TYPE_ENUMERATOR_H + +#include <sstream> + +#include "util/regexp.h" +#include "theory/type_enumerator.h" +#include "expr/type_node.h" +#include "expr/kind.h" + +namespace CVC4 { +namespace theory { +namespace strings { + +class StringEnumerator : public TypeEnumeratorBase<StringEnumerator> { + std::vector< unsigned > d_data; + unsigned d_cardinality; + Node d_curr; + void mkCurr() { + //make constant from d_data + d_curr = NodeManager::currentNM()->mkConst( ::CVC4::String( d_data ) ); + } +public: + + StringEnumerator(TypeNode type) throw(AssertionException) : + TypeEnumeratorBase<StringEnumerator>(type) { + Assert(type.getKind() == kind::TYPE_CONSTANT && + type.getConst<TypeConstant>() == STRING_TYPE); + d_cardinality = 256; + mkCurr(); + } + Node operator*() throw() { + return d_curr; + } + StringEnumerator& operator++() throw() { + bool changed = false; + do{ + for(unsigned i=0; i<d_data.size(); ++i) { + if( d_data[i] + 1 < d_cardinality ) { + ++d_data[i]; changed = true; + break; + } else { + d_data[i] = 0; + } + } + + if(!changed) { + d_data.push_back( 0 ); + } + }while(!changed); + + mkCurr(); + return *this; + } + + bool isFinished() throw() { + return d_curr.isNull(); + } + +};/* class StringEnumerator */ + + +class StringEnumeratorLength { +private: + unsigned d_cardinality; + std::vector< unsigned > d_data; + Node d_curr; + void mkCurr() { + //make constant from d_data + d_curr = NodeManager::currentNM()->mkConst( ::CVC4::String( d_data ) ); + } +public: + StringEnumeratorLength(unsigned length, unsigned card = 256) : d_cardinality(card) { + for( unsigned i=0; i<length; i++ ){ + d_data.push_back( 0 ); + } + mkCurr(); + } + + Node operator*() throw() { + return d_curr; + } + + StringEnumeratorLength& operator++() throw() { + bool changed = false; + for(unsigned i=0; i<d_data.size(); ++i) { + if( d_data[i] + 1 < d_cardinality ) { + ++d_data[i]; changed = true; + break; + } else { + d_data[i] = 0; + } + } + + if(!changed) { + d_curr = Node::null(); + }else{ + mkCurr(); + } + return *this; + } + + bool isFinished() throw() { + return d_curr.isNull(); + } +}; + +}/* CVC4::theory::strings namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__STRINGS__TYPE_ENUMERATOR_H */ diff --git a/src/theory/substitutions.cpp b/src/theory/substitutions.cpp index c12129d01..c4f06e396 100644 --- a/src/theory/substitutions.cpp +++ b/src/theory/substitutions.cpp @@ -29,7 +29,7 @@ struct substitution_stack_element { : node(node), children_added(false) {} };/* struct substitution_stack_element */ -Node SubstitutionMap::internalSubstitute(TNode t) { +Node SubstitutionMap::internalSubstitute(TNode t, NodeCache& cache) { Debug("substitution::internal") << "SubstitutionMap::internalSubstitute(" << t << ")" << endl; @@ -50,8 +50,8 @@ Node SubstitutionMap::internalSubstitute(TNode t) { Debug("substitution::internal") << "SubstitutionMap::internalSubstitute(" << t << "): processing " << current << endl; // If node already in the cache we're done, pop from the stack - NodeCache::iterator find = d_substitutionCache.find(current); - if (find != d_substitutionCache.end()) { + NodeCache::iterator find = cache.find(current); + if (find != cache.end()) { toVisit.pop_back(); continue; } @@ -59,7 +59,7 @@ Node SubstitutionMap::internalSubstitute(TNode t) { if (!d_substituteUnderQuantifiers && (current.getKind() == kind::FORALL || current.getKind() == kind::EXISTS)) { Debug("substitution::internal") << "--not substituting under quantifier" << endl; - d_substitutionCache[current] = current; + cache[current] = current; toVisit.pop_back(); continue; } @@ -68,9 +68,9 @@ Node SubstitutionMap::internalSubstitute(TNode t) { if (find2 != d_substitutions.end()) { Node rhs = (*find2).second; Assert(rhs != current); - internalSubstitute(rhs); - d_substitutions[current] = d_substitutionCache[rhs]; - d_substitutionCache[current] = d_substitutionCache[rhs]; + internalSubstitute(rhs, cache); + d_substitutions[current] = cache[rhs]; + cache[current] = cache[rhs]; toVisit.pop_back(); continue; } @@ -80,17 +80,17 @@ Node SubstitutionMap::internalSubstitute(TNode t) { // Children have been processed, so substitute NodeBuilder<> builder(current.getKind()); if (current.getMetaKind() == kind::metakind::PARAMETERIZED) { - builder << Node(d_substitutionCache[current.getOperator()]); + builder << Node(cache[current.getOperator()]); } for (unsigned i = 0; i < current.getNumChildren(); ++ i) { - Assert(d_substitutionCache.find(current[i]) != d_substitutionCache.end()); - builder << Node(d_substitutionCache[current[i]]); + Assert(cache.find(current[i]) != cache.end()); + builder << Node(cache[current[i]]); } // Mark the substitution and continue Node result = builder; if (result != current) { - find = d_substitutionCache.find(result); - if (find != d_substitutionCache.end()) { + find = cache.find(result); + if (find != cache.end()) { result = find->second; } else { @@ -98,15 +98,15 @@ Node SubstitutionMap::internalSubstitute(TNode t) { if (find2 != d_substitutions.end()) { Node rhs = (*find2).second; Assert(rhs != result); - internalSubstitute(rhs); - d_substitutions[result] = d_substitutionCache[rhs]; - d_substitutionCache[result] = d_substitutionCache[rhs]; - result = d_substitutionCache[rhs]; + internalSubstitute(rhs, cache); + d_substitutions[result] = cache[rhs]; + cache[result] = cache[rhs]; + result = cache[rhs]; } } } Debug("substitution::internal") << "SubstitutionMap::internalSubstitute(" << t << "): setting " << current << " -> " << result << endl; - d_substitutionCache[current] = result; + cache[current] = result; toVisit.pop_back(); } else { // Mark that we have added the children if any @@ -115,34 +115,33 @@ Node SubstitutionMap::internalSubstitute(TNode t) { // We need to add the operator, if any if(current.getMetaKind() == kind::metakind::PARAMETERIZED) { TNode opNode = current.getOperator(); - NodeCache::iterator opFind = d_substitutionCache.find(opNode); - if (opFind == d_substitutionCache.end()) { + NodeCache::iterator opFind = cache.find(opNode); + if (opFind == cache.end()) { toVisit.push_back(opNode); } } // We need to add the children for(TNode::iterator child_it = current.begin(); child_it != current.end(); ++ child_it) { TNode childNode = *child_it; - NodeCache::iterator childFind = d_substitutionCache.find(childNode); - if (childFind == d_substitutionCache.end()) { + NodeCache::iterator childFind = cache.find(childNode); + if (childFind == cache.end()) { toVisit.push_back(childNode); } } } else { // No children, so we're done Debug("substitution::internal") << "SubstitutionMap::internalSubstitute(" << t << "): setting " << current << " -> " << current << endl; - d_substitutionCache[current] = current; + cache[current] = current; toVisit.pop_back(); } } } // Return the substituted version - return d_substitutionCache[t]; + return cache[t]; }/* SubstitutionMap::internalSubstitute() */ - /* void SubstitutionMap::simplifyRHS(const SubstitutionMap& subMap) { // Put the new substitutions into the old ones @@ -160,10 +159,10 @@ void SubstitutionMap::simplifyRHS(TNode x, TNode t) { tempCache[x] = t; // Put the new substitution into the old ones - NodeMap::iterator it = d_substitutionsOld.begin(); - NodeMap::iterator it_end = d_substitutionsOld.end(); + NodeMap::iterator it = d_substitutions.begin(); + NodeMap::iterator it_end = d_substitutions.end(); for(; it != it_end; ++ it) { - d_substitutionsOld[(*it).first] = internalSubstituteOld((*it).second, tempCache); + d_substitutions[(*it).first] = internalSubstitute((*it).second, tempCache); } // it = d_substitutionsLazy.begin(); // it_end = d_substitutionsLazy.end(); @@ -171,7 +170,7 @@ void SubstitutionMap::simplifyRHS(TNode x, TNode t) { // d_substitutionsLazy[(*it).first] = internalSubstitute((*it).second, tempCache); // } } -*/ + /* We use subMap to simplify the left-hand sides of the current substitution map. If rewrite is true, * we also apply the rewriter to the result. @@ -283,6 +282,24 @@ void SubstitutionMap::addSubstitution(TNode x, TNode t, bool invalidateCache) } } + +void SubstitutionMap::addSubstitutions(SubstitutionMap& subMap, bool invalidateCache) +{ + SubstitutionMap::NodeMap::const_iterator it = subMap.begin(); + SubstitutionMap::NodeMap::const_iterator it_end = subMap.end(); + for (; it != it_end; ++ it) { + Assert(d_substitutions.find((*it).first) == d_substitutions.end()); + d_substitutions[(*it).first] = (*it).second; + if (!invalidateCache) { + d_substitutionCache[(*it).first] = d_substitutions[(*it).first]; + } + } + if (invalidateCache) { + d_cacheInvalidated = true; + } +} + + static bool check(TNode node, const SubstitutionMap::NodeMap& substitutions) CVC4_UNUSED; static bool check(TNode node, const SubstitutionMap::NodeMap& substitutions) { SubstitutionMap::NodeMap::const_iterator it = substitutions.begin(); @@ -311,7 +328,7 @@ Node SubstitutionMap::apply(TNode t) { } // Perform the substitution - Node result = internalSubstitute(t); + Node result = internalSubstitute(t, d_substitutionCache); Debug("substitution") << "SubstitutionMap::apply(" << t << ") => " << result << endl; // Assert(check(result, d_substitutions)); diff --git a/src/theory/substitutions.h b/src/theory/substitutions.h index bde7dfdbc..5a478a250 100644 --- a/src/theory/substitutions.h +++ b/src/theory/substitutions.h @@ -68,8 +68,11 @@ private: /** Has the cache been invalidated? */ bool d_cacheInvalidated; + /** Whether to keep substitutions in solved form */ + bool d_solvedForm; + /** Internal method that performs substitution */ - Node internalSubstitute(TNode t); + Node internalSubstitute(TNode t, NodeCache& cache); /** Helper class to invalidate cache on user pop */ class CacheInvalidator : public context::ContextNotifyObj { @@ -98,13 +101,15 @@ private: public: - SubstitutionMap(context::Context* context, bool substituteUnderQuantifiers = true) : + SubstitutionMap(context::Context* context, bool substituteUnderQuantifiers = true, bool solvedForm = false) : d_context(context), d_substitutions(context), d_substitutionCache(), d_substituteUnderQuantifiers(substituteUnderQuantifiers), d_cacheInvalidated(false), - d_cacheInvalidator(context, d_cacheInvalidated) { + d_solvedForm(solvedForm), + d_cacheInvalidator(context, d_cacheInvalidated) + { } /** @@ -113,6 +118,11 @@ public: void addSubstitution(TNode x, TNode t, bool invalidateCache = true); /** + * Merge subMap into current set of substitutions + */ + void addSubstitutions(SubstitutionMap& subMap, bool invalidateCache = true); + + /** * Returns true iff x is in the substitution map */ bool hasSubstitution(TNode x) const { @@ -170,13 +180,13 @@ public: // should best interact with cache invalidation on context // pops. - /* // Simplify right-hand sides of current map using the given substitutions void simplifyRHS(const SubstitutionMap& subMap); // Simplify right-hand sides of current map with lhs -> rhs void simplifyRHS(TNode lhs, TNode rhs); + /* // Simplify left-hand sides of current map using the given substitutions void simplifyLHS(const SubstitutionMap& subMap, std::vector<std::pair<Node,Node> >& equalities, @@ -188,6 +198,8 @@ public: bool rewrite = true); */ + bool isSolvedForm() const { return d_solvedForm; } + /** * Print to the output stream */ diff --git a/src/theory/term_registration_visitor.cpp b/src/theory/term_registration_visitor.cpp index 48d00130c..558975a72 100644 --- a/src/theory/term_registration_visitor.cpp +++ b/src/theory/term_registration_visitor.cpp @@ -127,15 +127,6 @@ void PreRegisterVisitor::visit(TNode current, TNode parent) { Assert(alreadyVisited(current, parent)); } -void PreRegisterVisitor::start(TNode node) { - d_multipleTheories = false; -} - -bool PreRegisterVisitor::done(TNode node) { - // We have multiple theories if removing the node theory from others is non-empty - return Theory::setRemove(Theory::theoryOf(node), d_theories); -} - std::string SharedTermsVisitor::toString() const { std::stringstream ss; TNodeVisitedMap::const_iterator it = d_visited.begin(); diff --git a/src/theory/term_registration_visitor.h b/src/theory/term_registration_visitor.h index 768508d2c..478e82d19 100644 --- a/src/theory/term_registration_visitor.h +++ b/src/theory/term_registration_visitor.h @@ -55,25 +55,19 @@ class PreRegisterVisitor { theory::Theory::Set d_theories; /** - * Is true if the term we're traversing involves multiple theories. - */ - bool d_multipleTheories; - - /** * String representation of the visited map, for debugging purposes. */ std::string toString() const; public: - /** Return type tells us if there are more than one theory or not */ - typedef bool return_type; + /** Returned set tells us which theories there are */ + typedef theory::Theory::Set return_type; PreRegisterVisitor(TheoryEngine* engine, context::Context* context) : d_engine(engine) , d_visited(context) , d_theories(0) - , d_multipleTheories(false) {} /** @@ -89,13 +83,12 @@ public: /** * Marks the node as the starting literal. */ - void start(TNode node); + void start(TNode node) { } /** * Notifies the engine of all the theories used. */ - bool done(TNode node); - + theory::Theory::Set done(TNode node) { return d_theories; } }; diff --git a/src/theory/theory.cpp b/src/theory/theory.cpp index 9ed20cc99..9a23d5518 100644 --- a/src/theory/theory.cpp +++ b/src/theory/theory.cpp @@ -17,6 +17,7 @@ #include "theory/theory.h" #include "util/cvc4_assert.h" #include "theory/quantifiers_engine.h" +#include "theory/substitutions.h" #include <vector> @@ -28,9 +29,6 @@ namespace theory { /** Default value for the uninterpreted sorts is the UF theory */ TheoryId Theory::s_uninterpretedSortOwner = THEORY_UF; -/** By default, we use the type based theoryOf */ -TheoryOfMode Theory::s_theoryOfMode = THEORY_OF_TYPE_BASED; - std::ostream& operator<<(std::ostream& os, Theory::Effort level){ switch(level){ case Theory::EFFORT_STANDARD: @@ -72,7 +70,7 @@ TheoryId Theory::theoryOf(TheoryOfMode mode, TNode node) { // Variables if (node.isVar()) { if (theoryOf(node.getType()) != theory::THEORY_BOOL) { - // We treat the varibables as uninterpreted + // We treat the variables as uninterpreted return s_uninterpretedSortOwner; } else { // Except for the Boolean ones, which we just ignore anyhow @@ -209,5 +207,27 @@ void Theory::computeRelevantTerms(set<Node>& termSet) } +Theory::PPAssertStatus Theory::ppAssert(TNode in, SubstitutionMap& outSubstitutions) +{ + if (in.getKind() == kind::EQUAL) { + if (in[0].isVar() && !in[1].hasSubterm(in[0])) { + outSubstitutions.addSubstitution(in[0], in[1]); + return PP_ASSERT_STATUS_SOLVED; + } + if (in[1].isVar() && !in[0].hasSubterm(in[1])) { + outSubstitutions.addSubstitution(in[1], in[0]); + return PP_ASSERT_STATUS_SOLVED; + } + if (in[0].isConst() && in[1].isConst()) { + if (in[0] != in[1]) { + return PP_ASSERT_STATUS_CONFLICT; + } + } + } + + return PP_ASSERT_STATUS_UNSOLVED; +} + + }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/theory.h b/src/theory/theory.h index 23fd67b23..21a5aacf5 100644 --- a/src/theory/theory.h +++ b/src/theory/theory.h @@ -23,9 +23,9 @@ #include "expr/attribute.h" #include "expr/command.h" #include "theory/valuation.h" -#include "theory/substitutions.h" #include "theory/output_channel.h" #include "theory/logic_info.h" +#include "theory/options.h" #include "theory/theoryof_mode.h" #include "context/context.h" #include "context/cdlist.h" @@ -49,6 +49,7 @@ namespace theory { class QuantifiersEngine; class TheoryModel; +class SubstitutionMap; namespace rrinst { class CandidateGenerator; @@ -298,9 +299,6 @@ protected: void printFacts(std::ostream& os) const; void debugPrintFacts() const; - /** Mode of the theoryOf operation */ - static TheoryOfMode s_theoryOfMode; - public: /** @@ -333,12 +331,7 @@ public: * Returns the ID of the theory responsible for the given node. */ static inline TheoryId theoryOf(TNode node) { - return theoryOf(s_theoryOfMode, node); - } - - /** Set the theoryOf mode */ - static void setTheoryOfMode(TheoryOfMode mode) { - s_theoryOfMode = mode; + return theoryOf(options::theoryOfMode(), node); } /** @@ -349,7 +342,7 @@ public: } /** - * Set the owner of the uninterpreted sort. + * Get the owner of the uninterpreted sort. */ static TheoryId getUninterpretedSortOwner() { return s_uninterpretedSortOwner; @@ -516,7 +509,7 @@ public: virtual EqualityStatus getEqualityStatus(TNode a, TNode b) { return EQUALITY_UNKNOWN; } /** - * Return the model value of the give shared term (or null if not avalilable. + * Return the model value of the give shared term (or null if not available). */ virtual Node getModelValue(TNode var) { return Node::null(); } @@ -583,25 +576,7 @@ public: * Given a literal, add the solved substitutions to the map, if any. * The method should return true if the literal can be safely removed. */ - virtual PPAssertStatus ppAssert(TNode in, SubstitutionMap& outSubstitutions) { - if (in.getKind() == kind::EQUAL) { - if (in[0].isVar() && !in[1].hasSubterm(in[0])) { - outSubstitutions.addSubstitution(in[0], in[1]); - return PP_ASSERT_STATUS_SOLVED; - } - if (in[1].isVar() && !in[0].hasSubterm(in[1])) { - outSubstitutions.addSubstitution(in[1], in[0]); - return PP_ASSERT_STATUS_SOLVED; - } - if (in[0].isConst() && in[1].isConst()) { - if (in[0] != in[1]) { - return PP_ASSERT_STATUS_CONFLICT; - } - } - } - - return PP_ASSERT_STATUS_UNSOLVED; - } + virtual PPAssertStatus ppAssert(TNode in, SubstitutionMap& outSubstitutions); /** * Given an atom of the theory coming from the input formula, this diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp index 6c8341345..78710e4b9 100644 --- a/src/theory/theory_engine.cpp +++ b/src/theory/theory_engine.cpp @@ -110,6 +110,7 @@ TheoryEngine::TheoryEngine(context::Context* context, d_propagationMapTimestamp(context, 0), d_propagatedLiterals(context), d_propagatedLiteralsIndex(context, 0), + d_atomRequests(context), d_iteRemover(iteRemover), d_combineTheoriesTime("TheoryEngine::combineTheoriesTime"), d_true(), @@ -186,9 +187,32 @@ void TheoryEngine::preRegister(TNode preprocessed) { } // Pre-register the terms in the atom - bool multipleTheories = NodeVisitor<PreRegisterVisitor>::run(d_preRegistrationVisitor, preprocessed); + Theory::Set theories = NodeVisitor<PreRegisterVisitor>::run(d_preRegistrationVisitor, preprocessed); + theories = Theory::setRemove(THEORY_BOOL, theories); + // Remove the top theory, if any more that means multiple theories were involved + bool multipleTheories = Theory::setRemove(Theory::theoryOf(preprocessed), theories); + TheoryId i; + // These checks don't work with finite model finding, because it + // uses Rational constants to represent cardinality constraints, + // even though arithmetic isn't actually involved. + if(!options::finiteModelFind()) { + while((i = Theory::setPop(theories)) != THEORY_LAST) { + if(!d_logicInfo.isTheoryEnabled(i)) { + LogicInfo newLogicInfo = d_logicInfo.getUnlockedCopy(); + newLogicInfo.enableTheory(i); + newLogicInfo.lock(); + stringstream ss; + ss << "The logic was specified as " << d_logicInfo.getLogicString() + << ", which doesn't include " << i + << ", but found a term in that theory." << endl + << "You might want to extend your logic to " + << newLogicInfo.getLogicString() << endl; + throw LogicException(ss.str()); + } + } + } if (multipleTheories) { - // Collect the shared terms if there are multipe theories + // Collect the shared terms if there are multiple theories NodeVisitor<SharedTermsVisitor>::run(d_sharedTermsVisitor, preprocessed); } } @@ -204,8 +228,8 @@ void TheoryEngine::printAssertions(const char* tag) { for (TheoryId theoryId = THEORY_FIRST; theoryId < THEORY_LAST; ++theoryId) { Theory* theory = d_theoryTable[theoryId]; if (theory && d_logicInfo.isTheoryEnabled(theoryId)) { - Trace(tag) << "--------------------------------------------" << std::endl; - Trace(tag) << "Assertions of " << theory->getId() << ": " << std::endl; + Trace(tag) << "--------------------------------------------" << endl; + Trace(tag) << "Assertions of " << theory->getId() << ": " << endl; context::CDList<Assertion>::const_iterator it = theory->facts_begin(), it_end = theory->facts_end(); for (unsigned i = 0; it != it_end; ++ it, ++i) { if ((*it).isPreregistered) { @@ -217,7 +241,7 @@ void TheoryEngine::printAssertions(const char* tag) { } if (d_logicInfo.isSharingEnabled()) { - Trace(tag) << "Shared terms of " << theory->getId() << ": " << std::endl; + Trace(tag) << "Shared terms of " << theory->getId() << ": " << endl; context::CDList<TNode>::const_iterator it = theory->shared_terms_begin(), it_end = theory->shared_terms_end(); for (unsigned i = 0; it != it_end; ++ it, ++i) { Trace(tag) << "[" << i << "]: " << (*it) << endl; @@ -287,9 +311,7 @@ void TheoryEngine::check(Theory::Effort effort) { #endif #define CVC4_FOR_EACH_THEORY_STATEMENT(THEORY) \ if (theory::TheoryTraits<THEORY>::hasCheck && d_logicInfo.isTheoryEnabled(THEORY)) { \ -Debug("theory") << "check<" << THEORY << ">" << std::endl; \ theoryOf(THEORY)->check(effort); \ -Debug("theory") << "done<" << THEORY << ">" << std::endl; \ if (d_inConflict) { \ break; \ } \ @@ -305,7 +327,7 @@ Debug("theory") << "done<" << THEORY << ">" << std::endl; \ // Mark the lemmas flag (no lemmas added) d_lemmasAdded = false; - Debug("theory") << "TheoryEngine::check(" << effort << "): d_factsAsserted = " << (d_factsAsserted ? "true" : "false") << std::endl; + Debug("theory") << "TheoryEngine::check(" << effort << "): d_factsAsserted = " << (d_factsAsserted ? "true" : "false") << endl; // If in full effort, we have a fake new assertion just to jumpstart the checking if (Theory::fullEffort(effort)) { @@ -315,9 +337,9 @@ Debug("theory") << "done<" << THEORY << ">" << std::endl; \ // Check until done while (d_factsAsserted && !d_inConflict && !d_lemmasAdded) { - Debug("theory") << "TheoryEngine::check(" << effort << "): running check" << std::endl; + Debug("theory") << "TheoryEngine::check(" << effort << "): running check" << endl; - Trace("theory::assertions") << std::endl; + Trace("theory::assertions") << endl; if (Trace.isOn("theory::assertions")) { printAssertions("theory::assertions"); } @@ -334,7 +356,7 @@ Debug("theory") << "done<" << THEORY << ">" << std::endl; \ << CheckSatCommand(); } - Debug("theory") << "TheoryEngine::check(" << effort << "): running propagation after the initial check" << std::endl; + Debug("theory") << "TheoryEngine::check(" << effort << "): running propagation after the initial check" << endl; // We are still satisfiable, propagate as much as possible propagate(effort); @@ -342,13 +364,14 @@ Debug("theory") << "done<" << THEORY << ">" << std::endl; \ // We do combination if all has been processed and we are in fullcheck if (Theory::fullEffort(effort) && d_logicInfo.isSharingEnabled() && !d_factsAsserted && !d_lemmasAdded) { // Do the combination - Debug("theory") << "TheoryEngine::check(" << effort << "): running combination" << std::endl; + Debug("theory") << "TheoryEngine::check(" << effort << "): running combination" << endl; combineTheories(); } } // Must consult quantifiers theory for last call to ensure sat, or otherwise add a lemma if( effort == Theory::EFFORT_FULL && ! d_inConflict && ! needCheck() ) { + //d_theoryTable[THEORY_STRINGS]->check(Theory::EFFORT_LAST_CALL); if(d_logicInfo.isQuantified()) { // quantifiers engine must pass effort last call check d_quantEngine->check(Theory::EFFORT_LAST_CALL); @@ -365,7 +388,11 @@ Debug("theory") << "done<" << THEORY << ">" << std::endl; \ } } - Debug("theory") << "TheoryEngine::check(" << effort << "): done, we are " << (d_inConflict ? "unsat" : "sat") << (d_lemmasAdded ? " with new lemmas" : " with no new lemmas") << std::endl; + Debug("theory") << "TheoryEngine::check(" << effort << "): done, we are " << (d_inConflict ? "unsat" : "sat") << (d_lemmasAdded ? " with new lemmas" : " with no new lemmas") << endl; + + if(!d_inConflict && Theory::fullEffort(effort) && d_masterEqualityEngine != NULL && !d_lemmasAdded) { + AlwaysAssert(d_masterEqualityEngine->consistent()); + } } catch(const theory::Interrupted&) { Trace("theory") << "TheoryEngine::check() => interrupted" << endl; @@ -381,7 +408,7 @@ Debug("theory") << "done<" << THEORY << ">" << std::endl; \ void TheoryEngine::combineTheories() { - Debug("sharing") << "TheoryEngine::combineTheories()" << std::endl; + Debug("sharing") << "TheoryEngine::combineTheories()" << endl; TimerStat::CodeTimer combineTheoriesTimer(d_combineTheoriesTime); @@ -399,7 +426,7 @@ void TheoryEngine::combineTheories() { // Call on each parametric theory to give us its care graph CVC4_FOR_EACH_THEORY; - Debug("sharing") << "TheoryEngine::combineTheories(): care graph size = " << careGraph.size() << std::endl; + Debug("sharing") << "TheoryEngine::combineTheories(): care graph size = " << careGraph.size() << endl; // Now add splitters for the ones we are interested in CareGraph::const_iterator care_it = careGraph.begin(); @@ -407,47 +434,17 @@ void TheoryEngine::combineTheories() { for (; care_it != care_it_end; ++ care_it) { const CarePair& carePair = *care_it; - Debug("sharing") << "TheoryEngine::combineTheories(): checking " << carePair.a << " = " << carePair.b << " from " << carePair.theory << std::endl; + Debug("sharing") << "TheoryEngine::combineTheories(): checking " << carePair.a << " = " << carePair.b << " from " << carePair.theory << endl; Assert(d_sharedTerms.isShared(carePair.a) || carePair.a.isConst()); Assert(d_sharedTerms.isShared(carePair.b) || carePair.b.isConst()); - //AJR-temp Assert(!d_sharedTerms.areEqual(carePair.a, carePair.b), "Please don't care about stuff you were notified about"); - //AJR-temp Assert(!d_sharedTerms.areDisequal(carePair.a, carePair.b), "Please don't care about stuff you were notified about"); - // The equality in question - Node equality = carePair.a < carePair.b ? - carePair.a.eqNode(carePair.b) : - carePair.b.eqNode(carePair.a); - - // Normalize the equality - Node normalizedEquality = Rewriter::rewrite(equality); - - // Check if the normalized equality already has a value (this also - // covers constants, since they always have values - if (d_propEngine->isSatLiteral(normalizedEquality)) { - bool value; - if (d_propEngine->hasValue(normalizedEquality, value)) { - Assert(equality != normalizedEquality); - Node literal = value ? equality : equality.notNode(); - Node normalizedLiteral = value ? normalizedEquality : normalizedEquality.notNode(); - // We're sending the original literal back, backed by the normalized one - if (markPropagation(literal, normalizedLiteral, /* to */ carePair.theory, /* from */ THEORY_SAT_SOLVER)) { - // We assert it, and we know it's preregistereed if it's the same theory - bool preregistered = Theory::theoryOf(literal) == carePair.theory; - theoryOf(carePair.theory)->assertFact(literal, preregistered); - // Mark that we have more information - d_factsAsserted = true; - continue; - } else { - Message() << "mark propagation fail: " << literal << " " << normalizedLiteral << " " << carePair.theory << std::endl; - Unreachable(); - } - } - } + // The equality in question (order for no repetition) + Node equality = carePair.a.eqNode(carePair.b); // We need to split on it - Debug("sharing") << "TheoryEngine::combineTheories(): requesting a split " << std::endl; - lemma(equality.orNode(equality.notNode()), false, false); + Debug("sharing") << "TheoryEngine::combineTheories(): requesting a split " << endl; + lemma(equality.orNode(equality.notNode()), false, false, carePair.theory); } } @@ -471,15 +468,19 @@ void TheoryEngine::propagate(Theory::Effort effort) { CVC4_FOR_EACH_THEORY; if(Dump.isOn("missed-t-propagations")) { - for(unsigned i = 0; i < d_possiblePropagations.size(); ++ i) { + for(unsigned i = 0; i < d_possiblePropagations.size(); ++i) { Node atom = d_possiblePropagations[i]; bool value; - if (!d_propEngine->hasValue(atom, value)) continue; + if(d_propEngine->hasValue(atom, value)) { + continue; + } // Doesn't have a value, check it (and the negation) if(d_hasPropagated.find(atom) == d_hasPropagated.end()) { Dump("missed-t-propagations") << CommentCommand("Completeness check for T-propagations; expect invalid") + << EchoCommand(atom.toString()) << QueryCommand(atom.toExpr()) + << EchoCommand(atom.notNode().toString()) << QueryCommand(atom.notNode().toExpr()); } } @@ -600,12 +601,12 @@ void TheoryEngine::collectModelInfo( theory::TheoryModel* m, bool fullModel ){ /* get model */ TheoryModel* TheoryEngine::getModel() { - Debug("model") << "TheoryEngine::getModel()" << std::endl; + Debug("model") << "TheoryEngine::getModel()" << endl; if( d_logicInfo.isQuantified() ) { - Debug("model") << "Get model from quantifiers engine." << std::endl; + Debug("model") << "Get model from quantifiers engine." << endl; return d_quantEngine->getModel(); } else { - Debug("model") << "Get default model." << std::endl; + Debug("model") << "Get default model." << endl; return d_curr_model; } } @@ -879,11 +880,11 @@ bool TheoryEngine::markPropagation(TNode assertion, TNode originalAssertion, the PropagationMap::const_iterator find = d_propagationMap.find(toAssert); if (find != d_propagationMap.end()) { // The theory already knows this - Trace("theory::assertToTheory") << "TheoryEngine::markPropagation(): already there" << std::endl; + Trace("theory::assertToTheory") << "TheoryEngine::markPropagation(): already there" << endl; return false; } - Trace("theory::assertToTheory") << "TheoryEngine::markPropagation(): marking [" << d_propagationMapTimestamp << "] " << assertion << ", " << toTheoryId << " from " << originalAssertion << ", " << fromTheoryId << std::endl; + Trace("theory::assertToTheory") << "TheoryEngine::markPropagation(): marking [" << d_propagationMapTimestamp << "] " << assertion << ", " << toTheoryId << " from " << originalAssertion << ", " << fromTheoryId << endl; // Mark the propagation d_propagationMap[toAssert] = toExplain; @@ -893,9 +894,9 @@ bool TheoryEngine::markPropagation(TNode assertion, TNode originalAssertion, the } -void TheoryEngine::assertToTheory(TNode assertion, theory::TheoryId toTheoryId, theory::TheoryId fromTheoryId) { +void TheoryEngine::assertToTheory(TNode assertion, TNode originalAssertion, theory::TheoryId toTheoryId, theory::TheoryId fromTheoryId) { - Trace("theory::assertToTheory") << "TheoryEngine::assertToTheory(" << assertion << ", " << toTheoryId << ", " << fromTheoryId << ")" << std::endl; + Trace("theory::assertToTheory") << "TheoryEngine::assertToTheory(" << assertion << ", " << toTheoryId << ", " << fromTheoryId << ")" << endl; Assert(toTheoryId != fromTheoryId); if(! d_logicInfo.isTheoryEnabled(toTheoryId) && @@ -915,6 +916,7 @@ void TheoryEngine::assertToTheory(TNode assertion, theory::TheoryId toTheoryId, // If sharing is disabled, things are easy if (!d_logicInfo.isSharingEnabled()) { + Assert(assertion == originalAssertion); if (fromTheoryId == THEORY_SAT_SOLVER) { // Send to the apropriate theory theory::Theory* toTheory = theoryOf(toTheoryId); @@ -928,7 +930,7 @@ void TheoryEngine::assertToTheory(TNode assertion, theory::TheoryId toTheoryId, bool value; if (d_propEngine->hasValue(assertion, value)) { if (!value) { - Trace("theory::propagate") << "TheoryEngine::assertToTheory(" << assertion << ", " << toTheoryId << ", " << fromTheoryId << "): conflict" << std::endl; + Trace("theory::propagate") << "TheoryEngine::assertToTheory(" << assertion << ", " << toTheoryId << ", " << fromTheoryId << "): conflict" << endl; d_inConflict = true; } else { return; @@ -948,7 +950,7 @@ void TheoryEngine::assertToTheory(TNode assertion, theory::TheoryId toTheoryId, // If sending to the shared terms database, it's also simple if (toTheoryId == THEORY_BUILTIN) { Assert(atom.getKind() == kind::EQUAL, "atom should be an EQUALity, not `%s'", atom.toString().c_str()); - if (markPropagation(assertion, assertion, toTheoryId, fromTheoryId)) { + if (markPropagation(assertion, originalAssertion, toTheoryId, fromTheoryId)) { d_sharedTerms.assertEquality(atom, polarity, assertion); } return; @@ -958,9 +960,11 @@ void TheoryEngine::assertToTheory(TNode assertion, theory::TheoryId toTheoryId, // directly to the apropriate theory if (fromTheoryId == THEORY_SAT_SOLVER) { // We know that this is normalized, so just send it off to the theory - if (markPropagation(assertion, assertion, toTheoryId, fromTheoryId)) { - // We assert it, and we know it's preregistereed coming from the SAT solver directly - theoryOf(toTheoryId)->assertFact(assertion, true); + if (markPropagation(assertion, originalAssertion, toTheoryId, fromTheoryId)) { + // Is it preregistered + bool preregistered = d_propEngine->isSatLiteral(assertion) && Theory::theoryOf(assertion) == toTheoryId; + // We assert it + theoryOf(toTheoryId)->assertFact(assertion, preregistered); // Mark that we have more information d_factsAsserted = true; } @@ -970,7 +974,7 @@ void TheoryEngine::assertToTheory(TNode assertion, theory::TheoryId toTheoryId, // Propagations to the SAT solver are just enqueued for pickup by // the SAT solver later if (toTheoryId == THEORY_SAT_SOLVER) { - if (markPropagation(assertion, assertion, toTheoryId, fromTheoryId)) { + if (markPropagation(assertion, originalAssertion, toTheoryId, fromTheoryId)) { // Enqueue for propagation to the SAT solver d_propagatedLiterals.push_back(assertion); // Check for propositional conflicts @@ -982,18 +986,16 @@ void TheoryEngine::assertToTheory(TNode assertion, theory::TheoryId toTheoryId, return; } - // See if it rewrites to true or false directly + Assert(atom.getKind() == kind::EQUAL); + + // Normalize Node normalizedLiteral = Rewriter::rewrite(assertion); + + // See if it rewrites false directly -> conflict if (normalizedLiteral.isConst()) { - if (normalizedLiteral.getConst<bool>()) { - // trivially true, but theories need to share even trivially true facts - // unless of course it is the theory that normalized it - if (Theory::theoryOf(atom) == toTheoryId) { - return; - } - } else { + if (!normalizedLiteral.getConst<bool>()) { // Mark the propagation for explanations - if (markPropagation(normalizedLiteral, assertion, toTheoryId, fromTheoryId)) { + if (markPropagation(normalizedLiteral, originalAssertion, toTheoryId, fromTheoryId)) { // Get the explanation (conflict will figure out where it came from) conflict(normalizedLiteral, toTheoryId); } else { @@ -1003,25 +1005,12 @@ void TheoryEngine::assertToTheory(TNode assertion, theory::TheoryId toTheoryId, } } - // Normalize to lhs < rhs if not a sat literal - Assert(atom.getKind() == kind::EQUAL); - Assert(atom[0] != atom[1]); - - Node normalizedAtom = atom; - if (!d_propEngine->isSatLiteral(normalizedAtom)) { - Node reverse = atom[1].eqNode(atom[0]); - if (d_propEngine->isSatLiteral(reverse) || atom[0] > atom[1]) { - normalizedAtom = reverse; - } - } - Node normalizedAssertion = polarity ? normalizedAtom : normalizedAtom.notNode(); - // Try and assert (note that we assert the non-normalized one) - if (markPropagation(normalizedAssertion, assertion, toTheoryId, fromTheoryId)) { + if (markPropagation(assertion, originalAssertion, toTheoryId, fromTheoryId)) { // Check if has been pre-registered with the theory - bool preregistered = d_propEngine->isSatLiteral(normalizedAssertion) && Theory::theoryOf(normalizedAtom) == toTheoryId; + bool preregistered = d_propEngine->isSatLiteral(assertion) && Theory::theoryOf(assertion) == toTheoryId; // Assert away - theoryOf(toTheoryId)->assertFact(normalizedAssertion, preregistered); + theoryOf(toTheoryId)->assertFact(assertion, preregistered); d_factsAsserted = true; } @@ -1030,7 +1019,7 @@ void TheoryEngine::assertToTheory(TNode assertion, theory::TheoryId toTheoryId, void TheoryEngine::assertFact(TNode literal) { - Trace("theory") << "TheoryEngine::assertFact(" << literal << ")" << std::endl; + Trace("theory") << "TheoryEngine::assertFact(" << literal << ")" << endl; d_propEngine->checkTime(); @@ -1067,22 +1056,33 @@ void TheoryEngine::assertFact(TNode literal) // to the assert the equality to the interested theories if (atom.getKind() == kind::EQUAL) { // Assert it to the the owning theory - assertToTheory(literal, /* to */ Theory::theoryOf(atom), /* from */ THEORY_SAT_SOLVER); + assertToTheory(literal, literal, /* to */ Theory::theoryOf(atom), /* from */ THEORY_SAT_SOLVER); // Shared terms manager will assert to interested theories directly, as the terms become shared - assertToTheory(literal, /* to */ THEORY_BUILTIN, /* from */ THEORY_SAT_SOLVER); + assertToTheory(literal, literal, /* to */ THEORY_BUILTIN, /* from */ THEORY_SAT_SOLVER); + + // Now, let's check for any atom triggers from lemmas + AtomRequests::atom_iterator it = d_atomRequests.getAtomIterator(atom); + while (!it.done()) { + const AtomRequests::Request& request = it.get(); + Node toAssert = polarity ? (Node) request.atom : request.atom.notNode(); + Debug("theory::atoms") << "TheoryEngine::assertFact(" << literal << "): sending requested " << toAssert << endl; + assertToTheory(toAssert, literal, request.toTheory, THEORY_SAT_SOLVER); + it.next(); + } + } else { // Not an equality, just assert to the appropriate theory - assertToTheory(literal, /* to */ Theory::theoryOf(atom), /* from */ THEORY_SAT_SOLVER); + assertToTheory(literal, literal, /* to */ Theory::theoryOf(atom), /* from */ THEORY_SAT_SOLVER); } } else { // Assert the fact to the appropriate theory directly - assertToTheory(literal, /* to */ Theory::theoryOf(atom), /* from */ THEORY_SAT_SOLVER); + assertToTheory(literal, literal, /* to */ Theory::theoryOf(atom), /* from */ THEORY_SAT_SOLVER); } } bool TheoryEngine::propagate(TNode literal, theory::TheoryId theory) { - Debug("theory::propagate") << "TheoryEngine::propagate(" << literal << ", " << theory << ")" << std::endl; + Debug("theory::propagate") << "TheoryEngine::propagate(" << literal << ", " << theory << ")" << endl; d_propEngine->checkTime(); @@ -1101,16 +1101,16 @@ bool TheoryEngine::propagate(TNode literal, theory::TheoryId theory) { if (d_logicInfo.isSharingEnabled() && atom.getKind() == kind::EQUAL) { if (d_propEngine->isSatLiteral(literal)) { // We propagate SAT literals to SAT - assertToTheory(literal, /* to */ THEORY_SAT_SOLVER, /* from */ theory); + assertToTheory(literal, literal, /* to */ THEORY_SAT_SOLVER, /* from */ theory); } if (theory != THEORY_BUILTIN) { // Assert to the shared terms database - assertToTheory(literal, /* to */ THEORY_BUILTIN, /* from */ theory); + assertToTheory(literal, literal, /* to */ THEORY_BUILTIN, /* from */ theory); } } else { // Just send off to the SAT solver Assert(d_propEngine->isSatLiteral(literal)); - assertToTheory(literal, /* to */ THEORY_SAT_SOLVER, /* from */ theory); + assertToTheory(literal, literal, /* to */ THEORY_SAT_SOLVER, /* from */ theory); } return !d_inConflict; @@ -1166,7 +1166,7 @@ static Node mkExplanation(const std::vector<NodeTheoryPair>& explanation) { Node TheoryEngine::getExplanation(TNode node) { - Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << "): current propagation index = " << d_propagationMapTimestamp << std::endl; + Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << "): current propagation index = " << d_propagationMapTimestamp << endl; bool polarity = node.getKind() != kind::NOT; TNode atom = polarity ? node : node[0]; @@ -1174,7 +1174,7 @@ Node TheoryEngine::getExplanation(TNode node) { // If we're not in shared mode, explanations are simple if (!d_logicInfo.isSharingEnabled()) { Node explanation = theoryOf(atom)->explain(node); - Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << ") => " << explanation << std::endl; + Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << ") => " << explanation << endl; return explanation; } @@ -1188,12 +1188,115 @@ Node TheoryEngine::getExplanation(TNode node) { getExplanation(explanationVector); Node explanation = mkExplanation(explanationVector); - Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << ") => " << explanation << std::endl; + Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << ") => " << explanation << endl; return explanation; } -theory::LemmaStatus TheoryEngine::lemma(TNode node, bool negated, bool removable) { +struct AtomsCollect { + + std::vector<TNode> d_atoms; + std::hash_set<TNode, TNodeHashFunction> d_visited; + +public: + + typedef void return_type; + + bool alreadyVisited(TNode current, TNode parent) { + // Check if already visited + d_visited.find(current) != d_visited.end(); + // Don't visit non-boolean + if (!current.getType().isBoolean()) return true; + // New node + return false; + } + + void visit(TNode current, TNode parent) { + if (Theory::theoryOf(current) != theory::THEORY_BOOL) { + d_atoms.push_back(current); + } + d_visited.insert(current); + } + + void start(TNode node) {} + void done(TNode node) {} + + std::vector<TNode> getAtoms() const { + return d_atoms; + } +}; + +void TheoryEngine::ensureLemmaAtoms(const std::vector<TNode>& atoms, theory::TheoryId atomsTo) { + for (unsigned i = 0; i < atoms.size(); ++ i) { + + // Non-equality atoms are either owned by theory or they don't make sense + if (atoms[i].getKind() != kind::EQUAL) { + continue; + } + + // The equality + Node eq = atoms[i]; + // Simple normalization to not repeat stuff + if (eq[0] > eq[1]) { + eq = eq[1].eqNode(eq[0]); + } + + // Rewrite the equality + Node eqNormalized = Rewriter::rewrite(atoms[i]); + + Debug("theory::atoms") << "TheoryEngine::ensureLemmaAtoms(): " << eq << " with nf " << eqNormalized << endl; + + // If the equality is a boolean constant, we send immediately + if (eqNormalized.isConst()) { + if (eqNormalized.getConst<bool>()) { + assertToTheory(eq, eqNormalized, /** to */ atomsTo, /** Sat solver */ theory::THEORY_SAT_SOLVER); + } else { + assertToTheory(eq.notNode(), eqNormalized.notNode(), /** to */ atomsTo, /** Sat solver */ theory::THEORY_SAT_SOLVER); + } + continue; + } + + Assert(eqNormalized.getKind() == kind::EQUAL); + + + // If the normalization did the just flips, keep the flip + if (eqNormalized[0] == eq[1] && eqNormalized[1] == eq[0]) { + eq = eqNormalized; + } + + // Check if the equality is already known by the sat solver + if (d_propEngine->isSatLiteral(eqNormalized)) { + bool value; + if (d_propEngine->hasValue(eqNormalized, value)) { + if (value) { + assertToTheory(eq, eqNormalized, atomsTo, theory::THEORY_SAT_SOLVER); + continue; + } else { + assertToTheory(eq.notNode(), eqNormalized.notNode(), atomsTo, theory::THEORY_SAT_SOLVER); + continue; + } + } + } + + // If the theory is asking about a different form, or the form is ok but if will go to a different theory + // then we must figure it out + if (eqNormalized != eq || Theory::theoryOf(eq) != atomsTo) { + // If you get eqNormalized, send atoms[i] to atomsTo + d_atomRequests.add(eqNormalized, eq, atomsTo); + } + } +} + +theory::LemmaStatus TheoryEngine::lemma(TNode node, bool negated, bool removable, theory::TheoryId atomsTo) { + + // Do we need to check atoms + if (atomsTo != theory::THEORY_LAST) { + Debug("theory::atoms") << "TheoryEngine::lemma(" << node << ", " << atomsTo << ")" << endl; + AtomsCollect collectAtoms; + NodeVisitor<AtomsCollect>::run(collectAtoms, node); + ensureLemmaAtoms(collectAtoms.getAtoms(), atomsTo); + } + if(Dump.isOn("t-lemmas")) { Node n = node; if (negated) { @@ -1215,6 +1318,18 @@ theory::LemmaStatus TheoryEngine::lemma(TNode node, bool negated, bool removable d_iteRemover.run(additionalLemmas, iteSkolemMap); additionalLemmas[0] = theory::Rewriter::rewrite(additionalLemmas[0]); + if(Debug.isOn("lemma-ites")) { + Debug("lemma-ites") << "removed ITEs from lemma: " << node << endl; + Debug("lemma-ites") << " + now have the following " + << additionalLemmas.size() << " lemma(s):" << endl; + for(std::vector<Node>::const_iterator i = additionalLemmas.begin(); + i != additionalLemmas.end(); + ++i) { + Debug("lemma-ites") << " + " << *i << endl; + } + Debug("lemma-ites") << endl; + } + // assert to prop engine d_propEngine->assertLemma(additionalLemmas[0], negated, removable); for (unsigned i = 1; i < additionalLemmas.size(); ++ i) { @@ -1249,7 +1364,7 @@ theory::LemmaStatus TheoryEngine::lemma(TNode node, bool negated, bool removable void TheoryEngine::conflict(TNode conflict, TheoryId theoryId) { - Debug("theory::conflict") << "TheoryEngine::conflict(" << conflict << ", " << theoryId << ")" << std::endl; + Debug("theory::conflict") << "TheoryEngine::conflict(" << conflict << ", " << theoryId << ")" << endl; // Mark that we are in conflict d_inConflict = true; @@ -1267,13 +1382,13 @@ void TheoryEngine::conflict(TNode conflict, TheoryId theoryId) { // Process the explanation getExplanation(explanationVector); Node fullConflict = mkExplanation(explanationVector); - Debug("theory::conflict") << "TheoryEngine::conflict(" << conflict << ", " << theoryId << "): full = " << fullConflict << std::endl; + Debug("theory::conflict") << "TheoryEngine::conflict(" << conflict << ", " << theoryId << "): full = " << fullConflict << endl; Assert(properConflict(fullConflict)); - lemma(fullConflict, true, true); + lemma(fullConflict, true, true, THEORY_LAST); } else { // When only one theory, the conflict should need no processing Assert(properConflict(conflict)); - lemma(conflict, true, true); + lemma(conflict, true, true, THEORY_LAST); } } @@ -1301,9 +1416,9 @@ void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector // Get the current literal to explain NodeTheoryPair toExplain = explanationVector[i]; - Debug("theory::explain") << "TheoryEngine::explain(): processing [" << toExplain.timestamp << "] " << toExplain.node << " sent from " << toExplain.theory << std::endl; + Debug("theory::explain") << "TheoryEngine::explain(): processing [" << toExplain.timestamp << "] " << toExplain.node << " sent from " << toExplain.theory << endl; - // If a treu constant or a negation of a false constant we can ignore it + // If a true constant or a negation of a false constant we can ignore it if (toExplain.node.isConst() && toExplain.node.getConst<bool>()) { ++ i; continue; @@ -1321,7 +1436,7 @@ void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector // If an and, expand it if (toExplain.node.getKind() == kind::AND) { - Debug("theory::explain") << "TheoryEngine::explain(): expanding " << toExplain.node << " got from " << toExplain.theory << std::endl; + Debug("theory::explain") << "TheoryEngine::explain(): expanding " << toExplain.node << " got from " << toExplain.theory << endl; for (unsigned k = 0; k < toExplain.node.getNumChildren(); ++ k) { NodeTheoryPair newExplain(toExplain.node[k], toExplain.theory, toExplain.timestamp); explanationVector.push_back(newExplain); @@ -1348,7 +1463,7 @@ void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector } else { explanation = theoryOf(toExplain.theory)->explain(toExplain.node); } - Debug("theory::explain") << "TheoryEngine::explain(): got explanation " << explanation << " got from " << toExplain.theory << std::endl; + Debug("theory::explain") << "TheoryEngine::explain(): got explanation " << explanation << " got from " << toExplain.theory << endl; Assert(explanation != toExplain.node, "wasn't sent to you, so why are you explaining it trivially"); // Mark the explanation NodeTheoryPair newExplain(explanation, toExplain.theory, toExplain.timestamp); @@ -1368,7 +1483,7 @@ void TheoryEngine::ppUnconstrainedSimp(vector<Node>& assertions) void TheoryEngine::setUserAttribute(const std::string& attr, Node n) { - Trace("te-attr") << "set user attribute " << attr << " " << n << std::endl; + Trace("te-attr") << "set user attribute " << attr << " " << n << endl; if( d_attr_handle.find( attr )!=d_attr_handle.end() ){ for( size_t i=0; i<d_attr_handle[attr].size(); i++ ){ d_attr_handle[attr][i]->setUserAttribute(attr, n); @@ -1379,7 +1494,7 @@ void TheoryEngine::setUserAttribute(const std::string& attr, Node n) { } void TheoryEngine::handleUserAttribute(const char* attr, Theory* t) { - Trace("te-attr") << "Handle user attribute " << attr << " " << t << std::endl; + Trace("te-attr") << "Handle user attribute " << attr << " " << t << endl; std::string str( attr ); d_attr_handle[ str ].push_back( t ); } @@ -1395,7 +1510,13 @@ void TheoryEngine::checkTheoryAssertionsWithModel() { ++it) { Node assertion = (*it).assertion; Node val = getModel()->getValue(assertion); - Assert(val == d_true); + 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 c21819ea1..53ff4d167 100644 --- a/src/theory/theory_engine.h +++ b/src/theory/theory_engine.h @@ -28,7 +28,6 @@ #include "prop/prop_engine.h" #include "context/cdhashset.h" #include "theory/theory.h" -#include "theory/substitutions.h" #include "theory/rewriter.h" #include "theory/substitutions.h" #include "theory/shared_terms_database.h" @@ -46,6 +45,7 @@ #include "theory/unconstrained_simplifier.h" #include "theory/uf/equality_engine.h" #include "theory/bv/bv_to_bool.h" +#include "theory/atom_requests.h" namespace CVC4 { @@ -75,6 +75,9 @@ struct NodeTheoryPairHashFunction { } };/* struct NodeTheoryPairHashFunction */ + + + namespace theory { class TheoryModel; class TheoryEngineModelBuilder; @@ -271,8 +274,10 @@ class TheoryEngine { } void safePoint() throw(theory::Interrupted, AssertionException) { - if (d_engine->d_interrupted) + spendResource(); + if (d_engine->d_interrupted) { throw theory::Interrupted(); + } } void conflict(TNode conflictNode) throw(AssertionException) { @@ -293,7 +298,14 @@ class TheoryEngine { Trace("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::lemma(" << lemma << ")" << std::endl; ++ d_statistics.lemmas; d_engine->d_outputChannelUsed = true; - return d_engine->lemma(lemma, false, removable); + return d_engine->lemma(lemma, false, removable, theory::THEORY_LAST); + } + + theory::LemmaStatus splitLemma(TNode lemma, bool removable = false) throw(TypeCheckingExceptionPrivate, AssertionException) { + Trace("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::lemma(" << lemma << ")" << std::endl; + ++ d_statistics.lemmas; + d_engine->d_outputChannelUsed = true; + return d_engine->lemma(lemma, false, removable, d_theory); } void demandRestart() throw(TypeCheckingExceptionPrivate, AssertionException) { @@ -329,6 +341,7 @@ class TheoryEngine { void spendResource() throw() { d_engine->spendResource(); } + void handleUserAttribute( const char* attr, theory::Theory* t ){ d_engine->handleUserAttribute( attr, t ); } @@ -430,10 +443,20 @@ class TheoryEngine { */ bool d_outputChannelUsed; + /** Atom requests from lemmas */ + AtomRequests d_atomRequests; + /** * Adds a new lemma, returning its status. + * @param node the lemma + * @param negated should the lemma be asserted negated + * @param removable can the lemma be remove (restrictions apply) + * @param needAtoms if not THEORY_LAST, then */ - theory::LemmaStatus lemma(TNode node, bool negated, bool removable); + theory::LemmaStatus lemma(TNode node, bool negated, bool removable, theory::TheoryId atomsTo); + + /** Enusre that the given atoms are send to the given theory */ + void ensureLemmaAtoms(const std::vector<TNode>& atoms, theory::TheoryId theory); RemoveITE& d_iteRemover; @@ -535,13 +558,18 @@ private: context::CDO<bool> d_factsAsserted; /** + * Map from equality atoms to theories that would like to be notified about them. + */ + + + /** * Assert the formula to the given theory. * @param assertion the assertion to send (not necesserily normalized) * @param original the assertion as it was sent in from the propagating theory * @param toTheoryId the theory to assert to * @param fromTheoryId the theory that sent it */ - void assertToTheory(TNode assertion, theory::TheoryId toTheoryId, theory::TheoryId fromTheoryId); + void assertToTheory(TNode assertion, TNode originalAssertion, theory::TheoryId toTheoryId, theory::TheoryId fromTheoryId); /** * Marks a theory propagation from a theory to a theory where a @@ -744,7 +772,7 @@ private: void dumpAssertions(const char* tag); /** For preprocessing pass simplifying ITEs */ - ITESimplifier d_iteSimplifier; + theory::ITESimplifier d_iteSimplifier; /** For preprocessing pass simplifying unconstrained expressions */ UnconstrainedSimplifier d_unconstrainedSimp; diff --git a/src/theory/theory_test_utils.h b/src/theory/theory_test_utils.h index 237b09bc1..e4921b163 100644 --- a/src/theory/theory_test_utils.h +++ b/src/theory/theory_test_utils.h @@ -107,6 +107,11 @@ public: d_callHistory.clear(); } + LemmaStatus splitLemma(TNode n, bool removable = false) throw(TypeCheckingExceptionPrivate, AssertionException){ + push(LEMMA, n); + return LemmaStatus(Node::null(), 0); + } + Node getIthNode(int i) { Node tmp = (d_callHistory[i]).second; return tmp; diff --git a/src/theory/theoryof_mode.h b/src/theory/theoryof_mode.h index cd8c68b1a..c50960257 100644 --- a/src/theory/theoryof_mode.h +++ b/src/theory/theoryof_mode.h @@ -14,10 +14,10 @@ ** Option selection for theoryOf() operation. **/ -#pragma once - #include "cvc4_public.h" +#pragma once + namespace CVC4 { namespace theory { @@ -29,6 +29,18 @@ enum TheoryOfMode { THEORY_OF_TERM_BASED };/* enum TheoryOfMode */ +inline std::ostream& operator<<(std::ostream& out, TheoryOfMode m) throw() CVC4_PUBLIC; + +inline std::ostream& operator<<(std::ostream& out, TheoryOfMode m) throw() { + switch(m) { + case THEORY_OF_TYPE_BASED: return out << "THEORY_OF_TYPE_BASED"; + case THEORY_OF_TERM_BASED: return out << "THEORY_OF_TERM_BASED"; + default: return out << "TheoryOfMode!UNKNOWN"; + } + + Unreachable(); +} + }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/type_enumerator.h b/src/theory/type_enumerator.h index 7a4fcdaff..467bf1927 100644 --- a/src/theory/type_enumerator.h +++ b/src/theory/type_enumerator.h @@ -98,13 +98,21 @@ public: ~TypeEnumerator() { delete d_te; } bool isFinished() throw() { -#ifdef CVC4_ASSERTIONS +// On Mac clang, there appears to be a code generation bug in an exception +// block here. For now, there doesn't appear a good workaround; just disable +// assertions on that setup. +#if defined(CVC4_ASSERTIONS) && !(defined(__APPLE__) && defined(__clang__)) if(d_te->isFinished()) { try { **d_te; Assert(false, "expected an NoMoreValuesException to be thrown"); } catch(NoMoreValuesException&) { // ignore the exception, we're just asserting that it would be thrown + // + // This block can crash on clang 3.0 on Mac OS, perhaps related to + // bug: http://llvm.org/bugs/show_bug.cgi?id=13359 + // + // Hence the #if !(defined(__APPLE__) && defined(__clang__)) above } } else { try { @@ -113,11 +121,14 @@ public: Assert(false, "didn't expect a NoMoreValuesException to be thrown"); } } -#endif /* CVC4_ASSERTIONS */ +#endif /* CVC4_ASSERTIONS && !(APPLE || clang) */ return d_te->isFinished(); } Node operator*() throw(NoMoreValuesException) { -#ifdef CVC4_ASSERTIONS +// On Mac clang, there appears to be a code generation bug in an exception +// block above (and perhaps here, too). For now, there doesn't appear a +// good workaround; just disable assertions on that setup. +#if defined(CVC4_ASSERTIONS) && !(defined(__APPLE__) && defined(__clang__)) try { Node n = **d_te; Assert(n.isConst()); @@ -127,9 +138,9 @@ public: Assert(isFinished()); throw; } -#else /* CVC4_ASSERTIONS */ +#else /* CVC4_ASSERTIONS && !(APPLE || clang) */ return **d_te; -#endif /* CVC4_ASSERTIONS */ +#endif /* CVC4_ASSERTIONS && !(APPLE || clang) */ } TypeEnumerator& operator++() throw() { ++*d_te; return *this; } TypeEnumerator operator++(int) throw() { TypeEnumerator te = *this; ++*d_te; return te; } diff --git a/src/theory/uf/equality_engine.cpp b/src/theory/uf/equality_engine.cpp index 7f0f79ebc..199581b98 100644 --- a/src/theory/uf/equality_engine.cpp +++ b/src/theory/uf/equality_engine.cpp @@ -1348,7 +1348,7 @@ void EqualityEngine::propagate() { } // If not merging internal nodes, notify the master - if (d_masterEqualityEngine && !d_isInternal[mergeInto]) { + if (d_masterEqualityEngine && !d_isInternal[t1classId] && !d_isInternal[t2classId]) { d_masterEqualityEngine->assertEqualityInternal(d_nodes[t1classId], d_nodes[t2classId], TNode::null()); d_masterEqualityEngine->propagate(); } diff --git a/src/theory/uf/equality_engine.h b/src/theory/uf/equality_engine.h index 206fb61c7..8d1b6f1d9 100644 --- a/src/theory/uf/equality_engine.h +++ b/src/theory/uf/equality_engine.h @@ -211,11 +211,6 @@ public: } };/* struct EqualityEngine::statistics */ - /** - * Store the application lookup, with enough information to backtrack - */ - void storeApplicationLookup(FunctionApplication& funNormalized, EqualityNodeId funId); - private: /** The context we are using */ @@ -254,6 +249,11 @@ private: /** Number of application lookups, for backtracking. */ context::CDO<DefaultSizeType> d_applicationLookupsCount; + /** + * Store the application lookup, with enough information to backtrack + */ + void storeApplicationLookup(FunctionApplication& funNormalized, EqualityNodeId funId); + /** Map from ids to the nodes (these need to be nodes as we pick up the operators) */ std::vector<Node> d_nodes; @@ -741,7 +741,7 @@ public: /** * Adds a predicate p with given polarity. The predicate asserted * should be in the congruence closure kinds (otherwise it's - * useless. + * useless). * * @param p the (non-negated) predicate * @param polarity true if asserting the predicate, false if @@ -777,7 +777,7 @@ public: void getUseListTerms(TNode t, std::set<TNode>& output); /** - * Get an explanation of the equality t1 = t2 begin true of false. + * Get an explanation of the equality t1 = t2 being true or false. * Returns the reasons (added when asserting) that imply it * in the assertions vector. */ diff --git a/src/theory/uf/options b/src/theory/uf/options index 33d1255ef..b9f60b83d 100644 --- a/src/theory/uf/options +++ b/src/theory/uf/options @@ -8,6 +8,9 @@ module UF "theory/uf/options.h" Uninterpreted functions theory option ufSymmetryBreaker uf-symmetry-breaker --symmetry-breaker bool :read-write :default true use UF symmetry breaker (Deharbe et al., CADE 2011) +option condenseFunctionValues condense-function-values --condense-function-values bool :default true + condense models for functions rather than explicitly representing them + option ufssRegions /--disable-uf-ss-regions bool :default true disable region-based method for discovering cliques and splits in uf strong solver option ufssEagerSplits --uf-ss-eager-split bool :default false @@ -20,6 +23,8 @@ option ufssTotalityLimited --uf-ss-totality-limited=N int :default -1 apply totality axioms, but only up to cardinality N (-1 == do not apply totality axioms, default) option ufssTotalityLazy --uf-ss-totality-lazy bool :default false apply totality axioms lazily +option ufssTotalitySymBreak --uf-ss-totality-sym-break bool :default false + apply symmetry breaking for totality axioms option ufssAbortCardinality --uf-ss-abort-card=N int :default -1 tells the uf strong solver a cardinality to abort at (-1 == no limit, default) option ufssSmartSplits --uf-ss-smart-split bool :default false @@ -30,5 +35,12 @@ option ufssSimpleCliques --uf-ss-simple-cliques bool :default true always use simple clique lemmas for uf strong solver option ufssDiseqPropagation --uf-ss-deq-prop bool :default false eagerly propagate disequalities for uf strong solver +option ufssMinimalModel /--disable-uf-ss-min-model bool :default true + disable finding a minimal model in uf strong solver +option ufssCliqueSplits --uf-ss-clique-splits bool :default false + use cliques instead of splitting on demand to shrink model + +option ufssSymBreak --uf-ss-sym-break bool :default false + finite model finding symmetry breaking techniques endmodule diff --git a/src/theory/uf/symmetry_breaker.cpp b/src/theory/uf/symmetry_breaker.cpp index fcb6c3cd5..f5d7f9235 100644 --- a/src/theory/uf/symmetry_breaker.cpp +++ b/src/theory/uf/symmetry_breaker.cpp @@ -52,6 +52,8 @@ namespace CVC4 { namespace theory { namespace uf { +using namespace ::CVC4::context; + SymmetryBreaker::Template::Template() : d_template(), d_sets(), @@ -165,7 +167,10 @@ void SymmetryBreaker::Template::reset() { d_reps.clear(); } -SymmetryBreaker::SymmetryBreaker() : +SymmetryBreaker::SymmetryBreaker(context::Context* context) : + ContextNotifyObj(context), + d_assertionsToRerun(context), + d_rerunningAssertions(false), d_phi(), d_phiSet(), d_permutations(), @@ -175,6 +180,31 @@ SymmetryBreaker::SymmetryBreaker() : d_termEqs() { } +class SBGuard { + bool& d_ref; + bool d_old; +public: + SBGuard(bool& b) : d_ref(b), d_old(b) {} + ~SBGuard() { Debug("uf") << "reset to " << d_old << std::endl; d_ref = d_old; } +};/* class SBGuard */ + +void SymmetryBreaker::rerunAssertionsIfNecessary() { + if(d_rerunningAssertions || !d_phi.empty() || d_assertionsToRerun.empty()) { + return; + } + + SBGuard g(d_rerunningAssertions); + d_rerunningAssertions = true; + + Debug("ufsymm") << "UFSYMM: rerunning assertions..." << std::endl; + for(CDList<Node>::const_iterator i = d_assertionsToRerun.begin(); + i != d_assertionsToRerun.end(); + ++i) { + assertFormula(*i); + } + Debug("ufsymm") << "UFSYMM: DONE rerunning assertions..." << std::endl; +} + Node SymmetryBreaker::norm(TNode phi) { Node n = Rewriter::rewrite(phi); return normInternal(n); @@ -254,6 +284,10 @@ Node SymmetryBreaker::normInternal(TNode n) { } void SymmetryBreaker::assertFormula(TNode phi) { + rerunAssertionsIfNecessary(); + if(!d_rerunningAssertions) { + d_assertionsToRerun.push_back(phi); + } // use d_phi, put into d_permutations Debug("ufsymm") << "UFSYMM assertFormula(): phi is " << phi << endl; d_phi.push_back(phi); @@ -322,6 +356,7 @@ void SymmetryBreaker::clear() { } void SymmetryBreaker::apply(std::vector<Node>& newClauses) { + rerunAssertionsIfNecessary(); guessPermutations(); Debug("ufsymm") << "UFSYMM =====================================================" << endl << "UFSYMM have " << d_permutations.size() << " permutation sets" << endl; diff --git a/src/theory/uf/symmetry_breaker.h b/src/theory/uf/symmetry_breaker.h index cf54b62c2..d04da048a 100644 --- a/src/theory/uf/symmetry_breaker.h +++ b/src/theory/uf/symmetry_breaker.h @@ -50,13 +50,15 @@ #include "expr/node.h" #include "expr/node_builder.h" +#include "context/context.h" +#include "context/cdlist.h" #include "util/statistics_registry.h" namespace CVC4 { namespace theory { namespace uf { -class SymmetryBreaker { +class SymmetryBreaker : public context::ContextNotifyObj { class Template { Node d_template; @@ -92,6 +94,19 @@ public: private: + /** + * This class wasn't initially built to be incremental. It should + * be attached to a UserContext so that it clears everything when + * a pop occurs. This "assertionsToRerun" is a set of assertions to + * feed back through assertFormula() when we started getting things + * again. It's not just a matter of effectiveness, but also soundness; + * if some assertions (still in scope) are not seen by a symmetry-breaking + * round, then some symmetries that don't actually exist might be broken, + * leading to unsound results! + */ + context::CDList<Node> d_assertionsToRerun; + bool d_rerunningAssertions; + std::vector<Node> d_phi; std::set<TNode> d_phiSet; Permutations d_permutations; @@ -101,6 +116,7 @@ private: TermEqs d_termEqs; void clear(); + void rerunAssertionsIfNecessary(); void guessPermutations(); bool invariantByPermutations(const Permutation& p); @@ -140,9 +156,17 @@ private: d_initNormalizationTimer, "theory::uf::symmetry_breaker::timers::initNormalization"); +protected: + + void contextNotifyPop() { + Debug("ufsymm") << "UFSYMM: clearing state due to pop" << std::endl; + clear(); + } + public: - SymmetryBreaker(); + SymmetryBreaker(context::Context* context); + ~SymmetryBreaker() throw() {} void assertFormula(TNode phi); void apply(std::vector<Node>& newClauses); diff --git a/src/theory/uf/theory_uf.cpp b/src/theory/uf/theory_uf.cpp index 69a963360..41935984f 100644 --- a/src/theory/uf/theory_uf.cpp +++ b/src/theory/uf/theory_uf.cpp @@ -38,7 +38,8 @@ TheoryUF::TheoryUF(context::Context* c, context::UserContext* u, OutputChannel& d_conflict(c, false), d_literalsToPropagate(c), d_literalsToPropagateIndex(c, 0), - d_functionsTerms(c) + d_functionsTerms(c), + d_symb(u) { // The kinds we are treating as function application in congruence d_equalityEngine.addFunctionKind(kind::APPLY_UF); diff --git a/src/theory/uf/theory_uf_model.cpp b/src/theory/uf/theory_uf_model.cpp index 228cfd2c4..c0d114052 100644 --- a/src/theory/uf/theory_uf_model.cpp +++ b/src/theory/uf/theory_uf_model.cpp @@ -17,6 +17,10 @@ #include "theory/uf/equality_engine.h" #include "theory/uf/theory_uf.h" #include "theory/quantifiers/term_database.h" +#include "theory/quantifiers/options.h" + +#include <vector> +#include <stack> #define RECONSIDER_FUNC_DEFAULT_VALUE #define USE_PARTIAL_DEFAULT_VALUES @@ -133,28 +137,60 @@ Node UfModelTreeNode::getValue( TheoryModel* m, Node n, std::vector< int >& inde } } -Node UfModelTreeNode::getFunctionValue( std::vector< Node >& args, int index, Node argDefaultValue ){ - if( !d_data.empty() ){ +Node UfModelTreeNode::getFunctionValue(std::vector<Node>& args, int index, Node argDefaultValue, bool simplify) { + if(!d_data.empty()) { Node defaultValue = argDefaultValue; - if( d_data.find( Node::null() )!=d_data.end() ){ - defaultValue = d_data[Node::null()].getFunctionValue( args, index+1, argDefaultValue ); + if(d_data.find(Node::null()) != d_data.end()) { + defaultValue = d_data[Node::null()].getFunctionValue(args, index + 1, argDefaultValue, simplify); } - std::vector< Node > caseArgs; - std::map< Node, Node > caseValues; - for( std::map< Node, UfModelTreeNode >::iterator it = d_data.begin(); it != d_data.end(); ++it ){ - if( !it->first.isNull() ){ - Node val = it->second.getFunctionValue( args, index+1, defaultValue ); - caseArgs.push_back( it->first ); - caseValues[ it->first ] = val; + + vector<Node> caseArgs; + map<Node, Node> caseValues; + + for(map< Node, UfModelTreeNode>::iterator it = d_data.begin(); it != d_data.end(); ++it) { + if(!it->first.isNull()) { + Node val = it->second.getFunctionValue(args, index + 1, defaultValue, simplify); + caseArgs.push_back(it->first); + caseValues[it->first] = val; } } + + NodeManager* nm = NodeManager::currentNM(); Node retNode = defaultValue; - for( int i=((int)caseArgs.size()-1); i>=0; i-- ){ - retNode = NodeManager::currentNM()->mkNode( ITE, args[index].eqNode( caseArgs[ i ] ), caseValues[ caseArgs[ i ] ], retNode ); + + if(!simplify) { + // "non-simplifying" mode - expand function values to things like: + // IF (x=0 AND y=0 AND z=0) THEN value1 + // ELSE IF (x=0 AND y=0 AND z=1) THEN value2 + // [...etc...] + for(int i = (int)caseArgs.size() - 1; i >= 0; --i) { + Node val = caseValues[ caseArgs[ i ] ]; + if(val.getKind() == ITE) { + // use a stack to reverse the order, since we're traversing outside-in + stack<TNode> stk; + do { + stk.push(val); + val = val[2]; + } while(val.getKind() == ITE); + AlwaysAssert(val == defaultValue, "default values don't match when constructing function definition!"); + while(!stk.empty()) { + val = stk.top(); + stk.pop(); + retNode = nm->mkNode(ITE, nm->mkNode(AND, args[index].eqNode(caseArgs[i]), val[0]), val[1], retNode); + } + } else { + retNode = nm->mkNode(ITE, args[index].eqNode(caseArgs[i]), caseValues[caseArgs[i]], retNode); + } + } + } else { + // "simplifying" mode - condense function values + for(int i = (int)caseArgs.size() - 1; i >= 0; --i) { + retNode = nm->mkNode(ITE, args[index].eqNode(caseArgs[i]), caseValues[caseArgs[i]], retNode); + } } return retNode; - }else{ - Assert( !d_value.isNull() ); + } else { + Assert(!d_value.isNull()); return d_value; } } @@ -259,14 +295,16 @@ void UfModelTreeNode::debugPrint( std::ostream& out, TheoryModel* m, std::vector } } -Node UfModelTree::getFunctionValue( std::vector< Node >& args ){ - Node body = d_tree.getFunctionValue( args, 0, Node::null() ); - body = Rewriter::rewrite( body ); +Node UfModelTree::getFunctionValue( std::vector< Node >& args, bool simplify ){ + Node body = d_tree.getFunctionValue( args, 0, Node::null(), simplify ); + if(simplify) { + body = Rewriter::rewrite( body ); + } Node boundVarList = NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, args); return NodeManager::currentNM()->mkNode(kind::LAMBDA, boundVarList, body); } -Node UfModelTree::getFunctionValue( const char* argPrefix ){ +Node UfModelTree::getFunctionValue( const char* argPrefix, bool simplify ){ TypeNode type = d_op.getType(); std::vector< Node > vars; for( size_t i=0; i<type.getNumChildren()-1; i++ ){ @@ -274,7 +312,7 @@ Node UfModelTree::getFunctionValue( const char* argPrefix ){ ss << argPrefix << (i+1); vars.push_back( NodeManager::currentNM()->mkBoundVar( ss.str(), type[i] ) ); } - return getFunctionValue( vars ); + return getFunctionValue( vars, simplify ); } Node UfModelTreeGenerator::getIntersection( TheoryModel* m, Node n1, Node n2, bool& isGround ){ @@ -309,19 +347,21 @@ void UfModelTreeGenerator::setValue( TheoryModel* m, Node n, Node v, bool ground if( !ground ){ int defSize = (int)d_defaults.size(); for( int i=0; i<defSize; i++ ){ - bool isGround; //for soundness, to allow variable order-independent function interpretations, // we must ensure that the intersection of all default terms // is also defined. //for example, if we have that f( e, a ) = ..., and f( b, e ) = ..., // then we must define f( b, a ). - Node ni = getIntersection( m, n, d_defaults[i], isGround ); - if( !ni.isNull() ){ - //if the intersection exists, and is not already defined - if( d_set_values[0][ isGround ? 1 : 0 ].find( ni )==d_set_values[0][ isGround ? 1 : 0 ].end() && - d_set_values[1][ isGround ? 1 : 0 ].find( ni )==d_set_values[1][ isGround ? 1 : 0 ].end() ){ - //use the current value - setValue( m, ni, v, isGround, false ); + if (!options::fmfFullModelCheck()) { + bool isGround; + Node ni = getIntersection( m, n, d_defaults[i], isGround ); + if( !ni.isNull() ){ + //if the intersection exists, and is not already defined + if( d_set_values[0][ isGround ? 1 : 0 ].find( ni )==d_set_values[0][ isGround ? 1 : 0 ].end() && + d_set_values[1][ isGround ? 1 : 0 ].find( ni )==d_set_values[1][ isGround ? 1 : 0 ].end() ){ + //use the current value + setValue( m, ni, v, isGround, false ); + } } } } diff --git a/src/theory/uf/theory_uf_model.h b/src/theory/uf/theory_uf_model.h index 12c1cf244..133cd2cf2 100644 --- a/src/theory/uf/theory_uf_model.h +++ b/src/theory/uf/theory_uf_model.h @@ -46,7 +46,7 @@ public: /** getConstant Value function */ Node getConstantValue( TheoryModel* m, Node n, std::vector< int >& indexOrder, int argIndex ); /** getFunctionValue */ - Node getFunctionValue( std::vector< Node >& args, int index, Node argDefaultValue ); + Node getFunctionValue( std::vector< Node >& args, int index, Node argDefaultValue, bool simplify = true ); /** update function */ void update( TheoryModel* m ); /** simplify function */ @@ -125,9 +125,9 @@ public: /** getFunctionValue * Returns a representation of this function. */ - Node getFunctionValue( std::vector< Node >& args ); + Node getFunctionValue( std::vector< Node >& args, bool simplify = true ); /** getFunctionValue for args with set prefix */ - Node getFunctionValue( const char* argPrefix ); + Node getFunctionValue( const char* argPrefix, bool simplify = true ); /** update * This will update all values in the tree to be representatives in m. */ @@ -144,18 +144,12 @@ public: void debugPrint( std::ostream& out, TheoryModel* m, int ind = 0 ){ d_tree.debugPrint( out, m, d_index_order, ind ); } -private: - //helper for to ITE function. - static Node toIte2( Node fm_node, std::vector< Node >& args, int index, Node defaultNode ); -public: - /** to ITE function for function model nodes */ - static Node toIte( Node fm_node, std::vector< Node >& args ) { return toIte2( fm_node, args, 0, Node::null() ); } - static Node toIte( TypeNode type, Node fm_node, const char* argPrefix ); }; + class UfModelTreeGenerator { -private: +public: //store for set values Node d_default_value; std::map< Node, Node > d_set_values[2][2]; diff --git a/src/theory/uf/theory_uf_rewriter.h b/src/theory/uf/theory_uf_rewriter.h index 94ab47d23..40713fa41 100644 --- a/src/theory/uf/theory_uf_rewriter.h +++ b/src/theory/uf/theory_uf_rewriter.h @@ -21,6 +21,7 @@ #define __CVC4__THEORY__UF__THEORY_UF_REWRITER_H #include "theory/rewriter.h" +#include "theory/substitutions.h" namespace CVC4 { namespace theory { diff --git a/src/theory/uf/theory_uf_strong_solver.cpp b/src/theory/uf/theory_uf_strong_solver.cpp index d64f7df60..163dd3c1f 100644 --- a/src/theory/uf/theory_uf_strong_solver.cpp +++ b/src/theory/uf/theory_uf_strong_solver.cpp @@ -20,6 +20,8 @@ #include "theory/quantifiers/term_database.h" #include "theory/uf/options.h" #include "theory/model.h" +#include "theory/quantifiers/symmetry_breaking.h" + //#define ONE_SPLIT_REGION //#define DISABLE_QUICK_CLIQUE_CHECKS @@ -117,6 +119,9 @@ void StrongSolverTheoryUF::SortModel::Region::setEqual( Node a, Node b ){ if( options::ufssDiseqPropagation() ){ d_cf->d_thss->getDisequalityPropagator()->assertDisequal(a, n, Node::null()); } + if( options::ufssSymBreak() ){ + d_cf->d_thss->getSymmetryBreaker()->assertDisequal( a, n ); + } } setDisequal( b, n, t, false ); nr->setDisequal( n, b, t, false ); @@ -322,6 +327,20 @@ bool StrongSolverTheoryUF::SortModel::Region::check( Theory::Effort level, int c return false; } +bool StrongSolverTheoryUF::SortModel::Region::getCandidateClique( int cardinality, std::vector< Node >& clique ) { + if( d_testCliqueSize>=long(cardinality+1) ){ + //test clique is a clique + for( NodeBoolMap::iterator it = d_testClique.begin(); it != d_testClique.end(); ++it ){ + if( (*it).second ){ + clique.push_back( (*it).first ); + } + } + return true; + } + return false; +} + + void StrongSolverTheoryUF::SortModel::Region::getRepresentatives( std::vector< Node >& reps ){ for( std::map< Node, RegionNodeInfo* >::iterator it = d_nodes.begin(); it != d_nodes.end(); ++it ){ RegionNodeInfo* rni = it->second; @@ -394,9 +413,9 @@ void StrongSolverTheoryUF::SortModel::Region::debugPrint( const char* c, bool in -StrongSolverTheoryUF::SortModel::SortModel( Node n, context::Context* c, StrongSolverTheoryUF* thss ) : d_type( n.getType() ), +StrongSolverTheoryUF::SortModel::SortModel( Node n, context::Context* c, context::UserContext* u, StrongSolverTheoryUF* thss ) : d_type( n.getType() ), d_thss( thss ), d_regions_index( c, 0 ), d_regions_map( c ), d_split_score( c ), d_disequalities_index( c, 0 ), - d_reps( c, 0 ), d_conflict( c, false ), d_cardinality( c, 1 ), d_aloc_cardinality( 0 ), + d_reps( c, 0 ), d_conflict( c, false ), d_cardinality( c, 1 ), d_aloc_cardinality( u, 0 ), d_cardinality_assertions( c ), d_hasCard( c, false ){ d_cardinality_term = n; } @@ -501,9 +520,14 @@ void StrongSolverTheoryUF::SortModel::merge( Node a, Node b ){ } d_reps = d_reps - 1; - if( options::ufssDiseqPropagation() && !d_conflict ){ - //notify the disequality propagator - d_thss->getDisequalityPropagator()->merge(a, b); + if( !d_conflict ){ + if( options::ufssDiseqPropagation() ){ + //notify the disequality propagator + d_thss->getDisequalityPropagator()->merge(a, b); + } + if( options::ufssSymBreak() ){ + d_thss->getSymmetryBreaker()->merge(a, b); + } } } } @@ -551,9 +575,14 @@ void StrongSolverTheoryUF::SortModel::assertDisequal( Node a, Node b, Node reaso checkRegion( bi ); } - if( options::ufssDiseqPropagation() && !d_conflict ){ - //notify the disequality propagator - d_thss->getDisequalityPropagator()->assertDisequal(a, b, Node::null()); + if( !d_conflict ){ + if( options::ufssDiseqPropagation() ){ + //notify the disequality propagator + d_thss->getDisequalityPropagator()->assertDisequal(a, b, Node::null()); + } + if( options::ufssSymBreak() ){ + d_thss->getSymmetryBreaker()->assertDisequal(a, b); + } } } } @@ -595,9 +624,11 @@ void StrongSolverTheoryUF::SortModel::check( Theory::Effort level, OutputChannel if( d_regions[i]->d_valid ){ std::vector< Node > clique; if( d_regions[i]->check( level, d_cardinality, clique ) ){ - //add clique lemma - addCliqueLemma( clique, out ); - return; + if( options::ufssMinimalModel() ){ + //add clique lemma + addCliqueLemma( clique, out ); + return; + } }else{ Trace("uf-ss-debug") << "No clique in Region #" << i << std::endl; } @@ -623,11 +654,21 @@ void StrongSolverTheoryUF::SortModel::check( Theory::Effort level, OutputChannel //see if we have any recommended splits from large regions for( int i=0; i<(int)d_regions_index; i++ ){ if( d_regions[i]->d_valid && d_regions[i]->getNumReps()>d_cardinality ){ - if( addSplit( d_regions[i], out ) ){ - addedLemma = true; + //just add the clique lemma + if( level==Theory::EFFORT_FULL && options::ufssCliqueSplits() ){ + std::vector< Node > clique; + if( d_regions[i]->getCandidateClique( d_cardinality, clique ) ){ + //add clique lemma + addCliqueLemma( clique, out ); + return; + } + }else{ + if( addSplit( d_regions[i], out ) ){ + addedLemma = true; #ifdef ONE_SPLIT_REGION - break; + break; #endif + } } } } @@ -644,7 +685,7 @@ void StrongSolverTheoryUF::SortModel::check( Theory::Effort level, OutputChannel for( int i=0; i<(int)d_regions_index; i++ ){ if( d_regions[i]->d_valid ){ Node op = d_regions[i]->d_nodes.begin()->first; - int sort_id = d_thss->getTheory()->getQuantifiersEngine()->getTheoryEngine()->getSortInference()->getSortId(op); + int sort_id = d_thss->getSortInference()->getSortId(op); if( sortsFound.find( sort_id )!=sortsFound.end() ){ combineRegions( sortsFound[sort_id], i ); recheck = true; @@ -659,13 +700,17 @@ void StrongSolverTheoryUF::SortModel::check( Theory::Effort level, OutputChannel //naive strategy, force region combination involving the first valid region for( int i=0; i<(int)d_regions_index; i++ ){ if( d_regions[i]->d_valid ){ - forceCombineRegion( i, false ); - recheck = true; - break; + int fcr = forceCombineRegion( i, false ); + Trace("uf-ss-debug") << "Combined regions " << i << " " << fcr << std::endl; + if( options::ufssMinimalModel() || fcr!=-1 ){ + recheck = true; + break; + } } } } if( recheck ){ + Trace("uf-ss-debug") << "Must recheck." << std::endl; check( level, out ); } } @@ -869,8 +914,10 @@ void StrongSolverTheoryUF::SortModel::checkRegion( int ri, bool checkCombine ){ //now check if region is in conflict std::vector< Node > clique; if( d_regions[ri]->check( Theory::EFFORT_STANDARD, d_cardinality, clique ) ){ - //explain clique - addCliqueLemma( clique, &d_thss->getOutputChannel() ); + if( options::ufssMinimalModel() ){ + //explain clique + addCliqueLemma( clique, &d_thss->getOutputChannel() ); + } } } } @@ -947,18 +994,33 @@ void StrongSolverTheoryUF::SortModel::moveNode( Node n, int ri ){ void StrongSolverTheoryUF::SortModel::allocateCardinality( OutputChannel* out ){ if( d_aloc_cardinality>0 ){ Trace("uf-ss-fmf") << "No model of size " << d_aloc_cardinality << " exists for type " << d_type << " in this branch" << std::endl; - if( Trace.isOn("uf-ss-cliques") ){ - Trace("uf-ss-cliques") << "Cliques of size " << (d_aloc_cardinality+1) << " : " << std::endl; - for( size_t i=0; i<d_cliques[ d_aloc_cardinality ].size(); i++ ){ - Trace("uf-ss-cliques") << " "; - for( size_t j=0; j<d_cliques[ d_aloc_cardinality ][i].size(); j++ ){ - Trace("uf-ss-cliques") << d_cliques[ d_aloc_cardinality ][i][j] << " "; - } - Trace("uf-ss-cliques") << std::endl; + } + if( Trace.isOn("uf-ss-cliques") ){ + Trace("uf-ss-cliques") << "Cliques of size " << (d_aloc_cardinality+1) << " for " << d_type << " : " << std::endl; + for( size_t i=0; i<d_cliques[ d_aloc_cardinality ].size(); i++ ){ + Trace("uf-ss-cliques") << " "; + for( size_t j=0; j<d_cliques[ d_aloc_cardinality ][i].size(); j++ ){ + Trace("uf-ss-cliques") << d_cliques[ d_aloc_cardinality ][i][j] << " "; } + Trace("uf-ss-cliques") << std::endl; + } + } + /* + if( options::ufssSymBreak() ){ + std::vector< Node > reps; + getRepresentatives( reps ); + if( d_aloc_cardinality>0 ){ + d_thss->getSymmetryBreaker()->allocateCardinality( out, d_type, d_aloc_cardinality+1, d_cliques[ d_aloc_cardinality ], reps ); + }else{ + std::vector< Node > clique; + clique.push_back( d_cardinality_term ); + std::vector< std::vector< Node > > cliques; + cliques.push_back( clique ); + d_thss->getSymmetryBreaker()->allocateCardinality( out, d_type, 1, cliques, reps ); } } - d_aloc_cardinality++; + */ + d_aloc_cardinality = d_aloc_cardinality + 1; //check for abort case if( options::ufssAbortCardinality()==d_aloc_cardinality ){ @@ -969,7 +1031,7 @@ void StrongSolverTheoryUF::SortModel::allocateCardinality( OutputChannel* out ){ if( applyTotality( d_aloc_cardinality ) ){ //must generate new cardinality lemma term Node var; - if( d_aloc_cardinality==1 ){ + if( d_aloc_cardinality==1 && !options::ufssTotalitySymBreak() ){ //get arbitrary ground term var = d_cardinality_term; }else{ @@ -1013,8 +1075,8 @@ void StrongSolverTheoryUF::SortModel::allocateCardinality( OutputChannel* out ){ } bool StrongSolverTheoryUF::SortModel::addSplit( Region* r, OutputChannel* out ){ + Node s; if( r->hasSplits() ){ - Node s; if( !options::ufssSmartSplits() ){ //take the first split you find for( NodeBoolMap::iterator it = r->d_splits.begin(); it != r->d_splits.end(); ++it ){ @@ -1038,13 +1100,31 @@ bool StrongSolverTheoryUF::SortModel::addSplit( Region* r, OutputChannel* out ){ } } } + Assert( s!=Node::null() ); + }else{ + if( !options::ufssMinimalModel() ){ + //since candidate clique is not reported, we may need to find splits manually + for ( std::map< Node, Region::RegionNodeInfo* >::iterator it = r->d_nodes.begin(); it != r->d_nodes.end(); ++it ){ + if ( it->second->d_valid ){ + for ( std::map< Node, Region::RegionNodeInfo* >::iterator it2 = r->d_nodes.begin(); it2 != r->d_nodes.end(); ++it2 ){ + if ( it->second!=it2->second && it2->second->d_valid ){ + if( !r->isDisequal( it->first, it2->first, 1 ) ){ + s = NodeManager::currentNM()->mkNode( EQUAL, it->first, it2->first ); + } + } + } + } + } + } + } + if (!s.isNull() ){ //add lemma to output channel - Assert( s!=Node::null() && s.getKind()==EQUAL ); + Assert( s.getKind()==EQUAL ); s = Rewriter::rewrite( s ); Trace("uf-ss-lemma") << "*** Split on " << s << std::endl; if( options::sortInference()) { for( int i=0; i<2; i++ ){ - int si = d_thss->getTheory()->getQuantifiersEngine()->getTheoryEngine()->getSortInference()->getSortId( s[i] ); + int si = d_thss->getSortInference()->getSortId( s[i] ); Trace("uf-ss-split-si") << si << " "; } Trace("uf-ss-split-si") << std::endl; @@ -1071,6 +1151,12 @@ void StrongSolverTheoryUF::SortModel::addCliqueLemma( std::vector< Node >& cliqu while( clique.size()>size_t(d_cardinality+1) ){ clique.pop_back(); } + //debugging information + if( options::ufssSymBreak() ){ + std::vector< Node > clique_vec; + clique_vec.insert( clique_vec.begin(), clique.begin(), clique.end() ); + addClique( d_cardinality, clique_vec ); + } if( options::ufssSimpleCliques() && !options::ufssExplainedCliques() ){ //add as lemma std::vector< Node > eqs; @@ -1083,16 +1169,10 @@ void StrongSolverTheoryUF::SortModel::addCliqueLemma( std::vector< Node >& cliqu } eqs.push_back( d_cardinality_literal[ d_cardinality ].notNode() ); Node lem = NodeManager::currentNM()->mkNode( OR, eqs ); - Trace("uf-ss-lemma") << "*** Add clique conflict " << lem << std::endl; + Trace("uf-ss-lemma") << "*** Add clique lemma " << lem << std::endl; ++( d_thss->d_statistics.d_clique_lemmas ); out->lemma( lem ); }else{ - //debugging information - if( Trace.isOn("uf-ss-cliques") ){ - std::vector< Node > clique_vec; - clique_vec.insert( clique_vec.begin(), clique.begin(), clique.end() ); - d_cliques[ d_cardinality ].push_back( clique_vec ); - } //found a clique Debug("uf-ss-cliques") << "Found a clique (cardinality=" << d_cardinality << ") :" << std::endl; Debug("uf-ss-cliques") << " "; @@ -1217,19 +1297,49 @@ void StrongSolverTheoryUF::SortModel::addCliqueLemma( std::vector< Node >& cliqu } void StrongSolverTheoryUF::SortModel::addTotalityAxiom( Node n, int cardinality, OutputChannel* out ){ - Node cardLit = d_cardinality_literal[ cardinality ]; - std::vector< Node > eqs; - for( int i=0; i<cardinality; i++ ){ - eqs.push_back( n.eqNode( getTotalityLemmaTerm( cardinality, i ) ) ); + if( std::find( d_totality_terms[0].begin(), d_totality_terms[0].end(), n )==d_totality_terms[0].end() ){ + if( std::find( d_totality_lems[n].begin(), d_totality_lems[n].end(), cardinality ) == d_totality_lems[n].end() ){ + d_totality_lems[n].push_back( cardinality ); + Node cardLit = d_cardinality_literal[ cardinality ]; + int sort_id = 0; + if( options::sortInference() ){ + sort_id = d_thss->getSortInference()->getSortId(n); + } + Trace("uf-ss-totality") << "Add totality lemma for " << n << " " << cardinality << ", sort id is " << sort_id << std::endl; + int use_cardinality = cardinality; + if( options::ufssTotalitySymBreak() ){ + if( d_sym_break_index.find(n)!=d_sym_break_index.end() ){ + use_cardinality = d_sym_break_index[n]; + }else if( (int)d_sym_break_terms[n.getType()][sort_id].size()<cardinality-1 ){ + use_cardinality = d_sym_break_terms[n.getType()][sort_id].size() + 1; + d_sym_break_terms[n.getType()][sort_id].push_back( n ); + d_sym_break_index[n] = use_cardinality; + Trace("uf-ss-totality") << "Allocate symmetry breaking term " << n << ", index = " << use_cardinality << std::endl; + } + } + + std::vector< Node > eqs; + for( int i=0; i<use_cardinality; i++ ){ + eqs.push_back( n.eqNode( getTotalityLemmaTerm( cardinality, i ) ) ); + } + Node ax = NodeManager::currentNM()->mkNode( OR, eqs ); + Node lem = NodeManager::currentNM()->mkNode( IMPLIES, cardLit, ax ); + Trace("uf-ss-lemma") << "*** Add totality axiom " << lem << std::endl; + //send as lemma to the output channel + d_thss->getOutputChannel().lemma( lem ); + ++( d_thss->d_statistics.d_totality_lemmas ); + } } - Node ax = NodeManager::currentNM()->mkNode( OR, eqs ); - Node lem = NodeManager::currentNM()->mkNode( IMPLIES, cardLit, ax ); - Trace("uf-ss-lemma") << "*** Add totality axiom " << lem << std::endl; - //send as lemma to the output channel - d_thss->getOutputChannel().lemma( lem ); - ++( d_thss->d_statistics.d_totality_lemmas ); } +void StrongSolverTheoryUF::SortModel::addClique( int c, std::vector< Node >& clique ) { + + if( d_clique_trie[c].add( clique ) ){ + d_cliques[ c ].push_back( clique ); + } +} + + /** apply totality */ bool StrongSolverTheoryUF::SortModel::applyTotality( int cardinality ){ return options::ufssTotality() || cardinality<=options::ufssTotalityLimited(); @@ -1307,22 +1417,16 @@ int StrongSolverTheoryUF::SortModel::getNumRegions(){ } void StrongSolverTheoryUF::SortModel::getRepresentatives( std::vector< Node >& reps ){ - //if( !options::ufssColoringSat() ){ - bool foundRegion = false; - for( int i=0; i<(int)d_regions_index; i++ ){ - //should not have multiple regions at this point - if( foundRegion ){ - Assert( !d_regions[i]->d_valid ); - } - if( d_regions[i]->d_valid ){ - //this is the only valid region - d_regions[i]->getRepresentatives( reps ); - foundRegion = true; - } + for( int i=0; i<(int)d_regions_index; i++ ){ + //should not have multiple regions at this point + //if( foundRegion ){ + // Assert( !d_regions[i]->d_valid ); + //} + if( d_regions[i]->d_valid ){ + //this is the only valid region + d_regions[i]->getRepresentatives( reps ); } - //}else{ - // Unimplemented("Build representatives for fmf region sat is not implemented"); - //} + } } StrongSolverTheoryUF::StrongSolverTheoryUF(context::Context* c, context::UserContext* u, OutputChannel& out, TheoryUF* th) : @@ -1343,6 +1447,15 @@ d_rep_model_init( c ) }else{ d_deq_prop = NULL; } + if( options::ufssSymBreak() ){ + d_sym_break = new SubsortSymmetryBreaker( th->getQuantifiersEngine(), c ); + }else{ + d_sym_break = NULL; + } +} + +SortInference* StrongSolverTheoryUF::getSortInference() { + return d_th->getQuantifiersEngine()->getTheoryEngine()->getSortInference(); } /** get default sat context */ @@ -1361,6 +1474,9 @@ void StrongSolverTheoryUF::newEqClass( Node n ){ if( c ){ Trace("uf-ss-solver") << "StrongSolverTheoryUF: New eq class " << n << " : " << n.getType() << std::endl; c->newEqClass( n ); + if( options::ufssSymBreak() ){ + d_sym_break->newEqClass( n ); + } } } @@ -1467,6 +1583,10 @@ void StrongSolverTheoryUF::check( Theory::Effort level ){ break; } } + //check symmetry breaker + if( !d_conflict && options::ufssSymBreak() ){ + d_sym_break->check( level ); + } //disambiguate terms if necessary //if( !d_conflict && level==Theory::EFFORT_FULL && options::ufssColoringSat() ){ // Assert( d_term_amb!=NULL ); @@ -1502,7 +1622,7 @@ void StrongSolverTheoryUF::preRegisterTerm( TNode n ){ SortModel* rm = NULL; if( tn.isSort() ){ Trace("uf-ss-register") << "Preregister sort " << tn << "." << std::endl; - rm = new SortModel( n, d_th->getSatContext(), this ); + rm = new SortModel( n, d_th->getSatContext(), d_th->getUserContext(), this ); }else{ /* if( tn==NodeManager::currentNM()->integerType() || tn==NodeManager::currentNM()->realType() ){ @@ -1572,6 +1692,14 @@ int StrongSolverTheoryUF::getCardinality( Node n ) { } } +int StrongSolverTheoryUF::getCardinality( TypeNode tn ) { + std::map< TypeNode, SortModel* >::iterator it = d_rep_model.find( tn ); + if( it!=d_rep_model.end() && it->second ){ + return it->second->getCardinality(); + } + return -1; +} + void StrongSolverTheoryUF::getRepresentatives( Node n, std::vector< Node >& reps ){ SortModel* c = getSortModel( n ); if( c ){ @@ -1626,6 +1754,7 @@ StrongSolverTheoryUF::Statistics::Statistics(): d_clique_lemmas("StrongSolverTheoryUF::Clique_Lemmas", 0), d_split_lemmas("StrongSolverTheoryUF::Split_Lemmas", 0), d_disamb_term_lemmas("StrongSolverTheoryUF::Disambiguate_Term_Lemmas", 0), + d_sym_break_lemmas("StrongSolverTheoryUF::Symmetry_Breaking_Lemmas", 0), d_totality_lemmas("StrongSolverTheoryUF::Totality_Lemmas", 0), d_max_model_size("StrongSolverTheoryUF::Max_Model_Size", 1) { @@ -1633,6 +1762,7 @@ StrongSolverTheoryUF::Statistics::Statistics(): StatisticsRegistry::registerStat(&d_clique_lemmas); StatisticsRegistry::registerStat(&d_split_lemmas); StatisticsRegistry::registerStat(&d_disamb_term_lemmas); + StatisticsRegistry::registerStat(&d_sym_break_lemmas); StatisticsRegistry::registerStat(&d_totality_lemmas); StatisticsRegistry::registerStat(&d_max_model_size); } @@ -1642,6 +1772,7 @@ StrongSolverTheoryUF::Statistics::~Statistics(){ StatisticsRegistry::unregisterStat(&d_clique_lemmas); StatisticsRegistry::unregisterStat(&d_split_lemmas); StatisticsRegistry::unregisterStat(&d_disamb_term_lemmas); + StatisticsRegistry::unregisterStat(&d_sym_break_lemmas); StatisticsRegistry::unregisterStat(&d_totality_lemmas); StatisticsRegistry::unregisterStat(&d_max_model_size); } @@ -1785,4 +1916,4 @@ DisequalityPropagator::Statistics::Statistics(): DisequalityPropagator::Statistics::~Statistics(){ StatisticsRegistry::unregisterStat(& d_propagations); -}
\ No newline at end of file +} diff --git a/src/theory/uf/theory_uf_strong_solver.h b/src/theory/uf/theory_uf_strong_solver.h index 0cc995723..8e568444b 100644 --- a/src/theory/uf/theory_uf_strong_solver.h +++ b/src/theory/uf/theory_uf_strong_solver.h @@ -26,7 +26,13 @@ #include "util/statistics_registry.h" namespace CVC4 { + +class SortInference; + namespace theory { + +class SubsortSymmetryBreaker; + namespace uf { class TheoryUF; @@ -40,11 +46,14 @@ protected: typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap; typedef context::CDChunkList<Node> NodeList; typedef context::CDList<bool> BoolList; - typedef context::CDList<bool> IntList; typedef context::CDHashMap<TypeNode, bool, TypeNodeHashFunction> TypeNodeBoolMap; public: /** information for incremental conflict/clique finding for a particular sort */ class SortModel { + private: + std::map< Node, std::vector< int > > d_totality_lems; + std::map< TypeNode, std::map< int, std::vector< Node > > > d_sym_break_terms; + std::map< Node, int > d_sym_break_index; public: /** a partition of the current equality graph for which cliques can occur internally */ class Region { @@ -146,6 +155,8 @@ public: public: /** check for cliques */ bool check( Theory::Effort level, int cardinality, std::vector< Node >& clique ); + /** get candidate clique */ + bool getCandidateClique( int cardinality, std::vector< Node >& clique ); //print debug void debugPrint( const char* c, bool incClique = false ); }; @@ -196,12 +207,29 @@ public: /** add totality axiom */ void addTotalityAxiom( Node n, int cardinality, OutputChannel* out ); private: + class NodeTrie { + std::map< Node, NodeTrie > d_children; + public: + bool add( std::vector< Node >& n, unsigned i = 0 ){ + Assert( i<n.size() ); + if( i==(n.size()-1) ){ + bool ret = d_children.find( n[i] )==d_children.end(); + d_children[n[i]].d_children.clear(); + return ret; + }else{ + return d_children[n[i]].add( n, i+1 ); + } + } + }; + std::map< int, NodeTrie > d_clique_trie; + void addClique( int c, std::vector< Node >& clique ); + private: /** Are we in conflict */ context::CDO<bool> d_conflict; /** cardinality */ context::CDO< int > d_cardinality; /** maximum allocated cardinality */ - int d_aloc_cardinality; + context::CDO< int > d_aloc_cardinality; /** cardinality lemma term */ Node d_cardinality_term; /** cardinality totality terms */ @@ -222,7 +250,7 @@ public: /** get totality lemma terms */ Node getTotalityLemmaTerm( int cardinality, int i ); public: - SortModel( Node n, context::Context* c, StrongSolverTheoryUF* thss ); + SortModel( Node n, context::Context* c, context::UserContext* u, StrongSolverTheoryUF* thss ); virtual ~SortModel(){} /** initialize */ void initialize( OutputChannel* out ); @@ -280,6 +308,8 @@ private: TermDisambiguator* d_term_amb; /** disequality propagator */ DisequalityPropagator* d_deq_prop; + /** symmetry breaking techniques */ + SubsortSymmetryBreaker* d_sym_break; public: StrongSolverTheoryUF(context::Context* c, context::UserContext* u, OutputChannel& out, TheoryUF* th); ~StrongSolverTheoryUF() {} @@ -289,6 +319,10 @@ public: TermDisambiguator* getTermDisambiguator() { return d_term_amb; } /** disequality propagator */ DisequalityPropagator* getDisequalityPropagator() { return d_deq_prop; } + /** symmetry breaker */ + SubsortSymmetryBreaker* getSymmetryBreaker() { return d_sym_break; } + /** get sort inference module */ + SortInference* getSortInference(); /** get default sat context */ context::Context* getSatContext(); /** get default output channel */ @@ -330,8 +364,10 @@ public: TypeNode getCardinalityType( int i ) { return d_conf_types[i]; } /** get is in conflict */ bool isConflict() { return d_conflict; } - /** get cardinality for sort */ + /** get cardinality for node */ int getCardinality( Node n ); + /** get cardinality for type */ + int getCardinality( TypeNode tn ); /** get representatives */ void getRepresentatives( Node n, std::vector< Node >& reps ); /** minimize */ @@ -343,6 +379,7 @@ public: IntStat d_clique_lemmas; IntStat d_split_lemmas; IntStat d_disamb_term_lemmas; + IntStat d_sym_break_lemmas; IntStat d_totality_lemmas; IntStat d_max_model_size; Statistics(); diff --git a/src/util/Makefile.am b/src/util/Makefile.am index 4f93f5a61..156288600 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -39,9 +39,11 @@ libutil_la_SOURCES = \ datatype.h \ datatype.cpp \ tuple.h \ - maybe.h \ record.h \ record.cpp \ + divisible.h \ + divisible.cpp \ + maybe.h \ matcher.h \ gmp_util.h \ sexpr.h \ @@ -75,6 +77,7 @@ libutil_la_SOURCES = \ ite_removal.h \ ite_removal.cpp \ node_visitor.h \ + chain.h \ index.h \ uninterpreted_constant.h \ uninterpreted_constant.cpp \ @@ -85,7 +88,8 @@ libutil_la_SOURCES = \ util_model.h \ util_model.cpp \ sort_inference.h \ - sort_inference.cpp + sort_inference.cpp \ + regexp.h libstatistics_la_SOURCES = \ statistics_registry.h \ @@ -122,7 +126,7 @@ EXTRA_DIST = \ datatype.i \ tuple.i \ record.i \ - output.i \ + divisible.i \ cardinality.i \ result.i \ configuration.i \ @@ -136,7 +140,8 @@ EXTRA_DIST = \ rational.i \ hash.i \ predicate.i \ - uninterpreted_constant.i + uninterpreted_constant.i \ + chain.i DISTCLEANFILES = \ integer.h.tmp \ diff --git a/src/util/bitvector.h b/src/util/bitvector.h index 2d5d29339..04e23217b 100644 --- a/src/util/bitvector.h +++ b/src/util/bitvector.h @@ -396,7 +396,7 @@ struct CVC4_PUBLIC BitVectorHashFunction { /** * The structure representing the extraction operation for bit-vectors. The - * operation map bit-vectors to bit-vector of size <code>high - low + 1</code> + * operation maps bit-vectors to bit-vector of size <code>high - low + 1</code> * by taking the bits at indices <code>high ... low</code> */ struct CVC4_PUBLIC BitVectorExtract { @@ -492,6 +492,13 @@ struct CVC4_PUBLIC BitVectorRotateRight { operator unsigned () const { return rotateRightAmount; } };/* struct BitVectorRotateRight */ +struct CVC4_PUBLIC IntToBitVector { + unsigned size; + IntToBitVector(unsigned size) + : size(size) {} + operator unsigned () const { return size; } +};/* struct IntToBitVector */ + template <typename T> struct CVC4_PUBLIC UnsignedHashFunction { inline size_t operator()(const T& x) const { @@ -514,6 +521,11 @@ inline std::ostream& operator <<(std::ostream& os, const BitVectorBitOf& bv) { return os << "[" << bv.bitIndex << "]"; } +inline std::ostream& operator <<(std::ostream& os, const IntToBitVector& bv) CVC4_PUBLIC; +inline std::ostream& operator <<(std::ostream& os, const IntToBitVector& bv) { + return os << "[" << bv.size << "]"; +} + }/* CVC4 namespace */ #endif /* __CVC4__BITVECTOR_H */ diff --git a/src/util/boolean_simplification.h b/src/util/boolean_simplification.h index e5c4ead6c..be39f69c1 100644 --- a/src/util/boolean_simplification.h +++ b/src/util/boolean_simplification.h @@ -22,6 +22,7 @@ #include <vector> #include <algorithm> +#include "expr/expr_manager_scope.h" #include "expr/node.h" #include "util/cvc4_assert.h" @@ -202,6 +203,7 @@ public: * @param e the Expr to negate (cannot be the null Expr) */ static Expr negate(Expr e) throw(AssertionException) { + ExprManagerScope ems(e); return negate(Node::fromExpr(e)).toExpr(); } diff --git a/src/util/chain.h b/src/util/chain.h new file mode 100644 index 000000000..2e9cf7bf6 --- /dev/null +++ b/src/util/chain.h @@ -0,0 +1,50 @@ +/********************* */ +/*! \file chain.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 [[ Add one-line brief description here ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#include "cvc4_public.h" + +#ifndef __CVC4__CHAIN_H +#define __CVC4__CHAIN_H + +#include "expr/kind.h" +#include <iostream> + +namespace CVC4 { + +/** A class to represent a chained, built-in operator. */ +class Chain { + Kind d_kind; +public: + explicit Chain(Kind k) : d_kind(k) { } + bool operator==(const Chain& ch) const { return d_kind == ch.d_kind; } + bool operator!=(const Chain& ch) const { return d_kind != ch.d_kind; } + Kind getOperator() const { return d_kind; } +};/* class Chain */ + +inline std::ostream& operator<<(std::ostream& out, const Chain& ch) { + return out << ch.getOperator(); +} + +struct ChainHashFunction { + size_t operator()(const Chain& ch) const { + return kind::KindHashFunction()(ch.getOperator()); + } +};/* struct ChainHashFunction */ + +}/* CVC4 namespace */ + +#endif /* __CVC4__CHAIN_H */ diff --git a/src/util/chain.i b/src/util/chain.i new file mode 100644 index 000000000..1c97a527f --- /dev/null +++ b/src/util/chain.i @@ -0,0 +1,12 @@ +%{ +#include "util/chain.h" +%} + +%rename(equals) CVC4::Chain::operator==(const Chain&) const; +%ignore CVC4::Chain::operator!=(const Chain&) const; + +%ignore CVC4::operator<<(std::ostream&, const Chain&); + +%rename(apply) CVC4::ChainHashFunction::operator()(const CVC4::Chain&) const; + +%include "util/chain.h" diff --git a/src/util/cvc4_assert.cpp b/src/util/cvc4_assert.cpp index eb7b81a39..08e2867f6 100644 --- a/src/util/cvc4_assert.cpp +++ b/src/util/cvc4_assert.cpp @@ -136,7 +136,7 @@ void debugAssertionFailed(const AssertionException& thisException, const char* propagatingException) { static CVC4_THREADLOCAL(bool) alreadyFired = false; - if(EXPECT_TRUE( !std::uncaught_exception() ) || alreadyFired) { + if(__builtin_expect( ( !std::uncaught_exception() ), true ) || alreadyFired) { throw thisException; } diff --git a/src/util/cvc4_assert.h b/src/util/cvc4_assert.h index c070fc389..7d359a56b 100644 --- a/src/util/cvc4_assert.h +++ b/src/util/cvc4_assert.h @@ -252,7 +252,7 @@ void debugAssertionFailed(const AssertionException& thisException, const char* l // details of the exception # define AlwaysAssert(cond, msg...) \ do { \ - if(EXPECT_FALSE( ! (cond) )) { \ + if(__builtin_expect( ( ! (cond) ), false )) { \ /* save the last assertion failure */ \ const char* lastException = ::CVC4::s_debugLastException; \ ::CVC4::AssertionException exception(#cond, __PRETTY_FUNCTION__, __FILE__, __LINE__, ## msg); \ @@ -265,7 +265,7 @@ void debugAssertionFailed(const AssertionException& thisException, const char* l // will terminate() if thrown during stack unwinding. # define AlwaysAssert(cond, msg...) \ do { \ - if(EXPECT_FALSE( ! (cond) )) { \ + if(__builtin_expect( ( ! (cond) ), false )) { \ throw ::CVC4::AssertionException(#cond, __PRETTY_FUNCTION__, __FILE__, __LINE__, ## msg); \ } \ } while(0) @@ -283,13 +283,13 @@ void debugAssertionFailed(const AssertionException& thisException, const char* l throw ::CVC4::IllegalArgumentException("", #arg, __PRETTY_FUNCTION__, ## msg) #define CheckArgument(cond, arg, msg...) \ do { \ - if(EXPECT_FALSE( ! (cond) )) { \ + if(__builtin_expect( ( ! (cond) ), false )) { \ throw ::CVC4::IllegalArgumentException(#cond, #arg, __PRETTY_FUNCTION__, ## msg); \ } \ } while(0) #define AlwaysAssertArgument(cond, arg, msg...) \ do { \ - if(EXPECT_FALSE( ! (cond) )) { \ + if(__builtin_expect( ( ! (cond) ), false )) { \ throw ::CVC4::AssertArgumentException(#cond, #arg, __PRETTY_FUNCTION__, __FILE__, __LINE__, ## msg); \ } \ } while(0) @@ -299,9 +299,9 @@ void debugAssertionFailed(const AssertionException& thisException, const char* l # define AssertArgument(cond, arg, msg...) AlwaysAssertArgument(cond, arg, ## msg) # define DebugCheckArgument(cond, arg, msg...) CheckArgument(cond, arg, ## msg) #else /* ! CVC4_ASSERTIONS */ -# define Assert(cond, msg...) /*EXPECT_TRUE( cond )*/ -# define AssertArgument(cond, arg, msg...) /*EXPECT_TRUE( cond )*/ -# define DebugCheckArgument(cond, arg, msg...) /*EXPECT_TRUE( cond )*/ +# define Assert(cond, msg...) /*__builtin_expect( ( cond ), true )*/ +# define AssertArgument(cond, arg, msg...) /*__builtin_expect( ( cond ), true )*/ +# define DebugCheckArgument(cond, arg, msg...) /*__builtin_expect( ( cond ), true )*/ #endif /* CVC4_ASSERTIONS */ }/* CVC4 namespace */ diff --git a/src/util/datatype.h b/src/util/datatype.h index 3da441f1f..c46c10c97 100644 --- a/src/util/datatype.h +++ b/src/util/datatype.h @@ -33,7 +33,6 @@ namespace CVC4 { #include "expr/expr.h" #include "expr/type.h" -#include "util/output.h" #include "util/hash.h" #include "util/exception.h" diff --git a/src/util/debug.h b/src/util/debug.h index 3e7c4d8be..97c176f02 100644 --- a/src/util/debug.h +++ b/src/util/debug.h @@ -27,11 +27,11 @@ #ifdef CVC4_ASSERTIONS // the __builtin_expect() helps us if assert is built-in or a macro -# define cvc4assert(x) assert(EXPECT_TRUE( x )) +# define cvc4assert(x) assert(__builtin_expect( ( x ), true )) #else // TODO: use a compiler annotation when assertions are off ? // (to improve optimization) -# define cvc4assert(x) /*EXPECT_TRUE( x )*/ +# define cvc4assert(x) /*__builtin_expect( ( x ), true )*/ #endif /* CVC4_ASSERTIONS */ #endif /* __CVC4__DEBUG_H */ diff --git a/src/util/divisible.cpp b/src/util/divisible.cpp new file mode 100644 index 000000000..4e20d6b5f --- /dev/null +++ b/src/util/divisible.cpp @@ -0,0 +1,29 @@ +/********************* */ +/*! \file divisible.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-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 "util/divisible.h" +#include "util/exception.h" + +using namespace std; + +namespace CVC4 { + +Divisible::Divisible(const Integer& n) : k(n) { + CheckArgument(n > 0, n, "Divisible predicate must be constructed over positive N"); +} + +}/* CVC4 namespace */ diff --git a/src/util/divisible.h b/src/util/divisible.h new file mode 100644 index 000000000..0c0c7bc5b --- /dev/null +++ b/src/util/divisible.h @@ -0,0 +1,62 @@ +/********************* */ +/*! \file divisible.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 [[ Add one-line brief description here ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#include "cvc4_public.h" + +#ifndef __CVC4__DIVISIBLE_H +#define __CVC4__DIVISIBLE_H + +#include <iostream> +#include "util/integer.h" +#include "util/exception.h" + +namespace CVC4 { + +/** + * The structure representing the divisibility-by-k predicate. + */ +struct CVC4_PUBLIC Divisible { + const Integer k; + + Divisible(const Integer& n); + + bool operator==(const Divisible& d) const { + return k == d.k; + } + + bool operator!=(const Divisible& d) const { + return !(*this == d); + } +};/* struct Divisible */ + +/** + * Hash function for the Divisible objects. + */ +struct CVC4_PUBLIC DivisibleHashFunction { + size_t operator()(const Divisible& d) const { + return d.k.hash(); + } +};/* struct DivisibleHashFunction */ + +inline std::ostream& operator <<(std::ostream& os, const Divisible& d) CVC4_PUBLIC; +inline std::ostream& operator <<(std::ostream& os, const Divisible& d) { + return os << "divisible-by-" << d.k; +} + +}/* CVC4 namespace */ + +#endif /* __CVC4__DIVISIBLE_H */ diff --git a/src/util/divisible.i b/src/util/divisible.i new file mode 100644 index 000000000..7599360ca --- /dev/null +++ b/src/util/divisible.i @@ -0,0 +1,10 @@ +%{ +#include "util/divisible.h" +%} + +%rename(equals) CVC4::Divisible::operator==(const Divisible&) const; +%ignore CVC4::Divisible::operator!=(const Divisible&) const; + +%ignore CVC4::operator<<(std::ostream&, const Divisible&); + +%include "util/divisible.h" diff --git a/src/util/dump.h b/src/util/dump.h index 2ef6010e3..0bde68d76 100644 --- a/src/util/dump.h +++ b/src/util/dump.h @@ -19,7 +19,6 @@ #ifndef __CVC4__DUMP_H #define __CVC4__DUMP_H -#include "util/output.h" #include "expr/command.h" namespace CVC4 { diff --git a/src/util/exception.h b/src/util/exception.h index 082a50ef2..cd4b763ef 100644 --- a/src/util/exception.h +++ b/src/util/exception.h @@ -138,13 +138,13 @@ namespace CVC4 { #ifndef CheckArgument template <class T> inline void CheckArgument(bool cond, const T& arg, const char* fmt, ...) CVC4_PUBLIC; template <class T> inline void CheckArgument(bool cond, const T& arg, const char* fmt, ...) { - if(EXPECT_FALSE( !cond )) { \ + if(__builtin_expect( ( !cond ), false )) { \ throw ::CVC4::IllegalArgumentException("", "", ""); \ } \ } template <class T> inline void CheckArgument(bool cond, const T& arg) CVC4_PUBLIC; template <class T> inline void CheckArgument(bool cond, const T& arg) { - if(EXPECT_FALSE( !cond )) { \ + if(__builtin_expect( ( !cond ), false )) { \ throw ::CVC4::IllegalArgumentException("", "", ""); \ } \ } @@ -153,13 +153,13 @@ template <class T> inline void CheckArgument(bool cond, const T& arg) { #ifndef DebugCheckArgument template <class T> inline void DebugCheckArgument(bool cond, const T& arg, const char* fmt, ...) CVC4_PUBLIC; template <class T> inline void DebugCheckArgument(bool cond, const T& arg, const char* fmt, ...) { - if(EXPECT_FALSE( !cond )) { \ + if(__builtin_expect( ( !cond ), false )) { \ throw ::CVC4::IllegalArgumentException("", "", ""); \ } \ } template <class T> inline void DebugCheckArgument(bool cond, const T& arg) CVC4_PUBLIC; template <class T> inline void DebugCheckArgument(bool cond, const T& arg) { - if(EXPECT_FALSE( !cond )) { \ + if(__builtin_expect( ( !cond ), false )) { \ throw ::CVC4::IllegalArgumentException("", "", ""); \ } \ } diff --git a/src/util/ite_removal.cpp b/src/util/ite_removal.cpp index f26bbe0aa..7d4948251 100644 --- a/src/util/ite_removal.cpp +++ b/src/util/ite_removal.cpp @@ -30,7 +30,11 @@ 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; - output[i] = run(output[i], output, iteSkolemMap, 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); + output[i] = itesRemoved; } } diff --git a/src/util/output.h b/src/util/output.h index 263d5a144..7394f24ab 100644 --- a/src/util/output.h +++ b/src/util/output.h @@ -14,7 +14,7 @@ ** Output utility classes and functions. **/ -#include "cvc4_public.h" +#include "cvc4_private_library.h" #ifndef __CVC4__OUTPUT_H #define __CVC4__OUTPUT_H diff --git a/src/util/output.i b/src/util/output.i deleted file mode 100644 index 74953ba53..000000000 --- a/src/util/output.i +++ /dev/null @@ -1,70 +0,0 @@ -%{ -#include "util/output.h" -%} - -%ignore CVC4::null_streambuf; -%ignore std::streambuf; -%feature("valuewrapper") std::ostream; - -// There are issues with SWIG's attempted wrapping of these variables when -// it tries to generate the getters and setters. For now, just ignore them. -%ignore CVC4::null_sb; -%ignore CVC4::null_os; -%ignore CVC4::DumpOutC::dump_cout; -%ignore CVC4::CVC4ostream; - -%ignore operator<<; -%ignore on(std::string); -%ignore isOn(std::string); -%ignore off(std::string); -%ignore printf(std::string, const char*, ...); - -%ignore CVC4::IndentedScope; -%ignore CVC4::push(CVC4ostream&); -%ignore CVC4::pop(CVC4ostream&); - -%ignore CVC4::ScopedDebug::ScopedDebug(std::string); -%ignore CVC4::ScopedDebug::ScopedDebug(std::string, bool); - -%ignore CVC4::ScopedTrace::ScopedTrace(std::string); -%ignore CVC4::ScopedTrace::ScopedTrace(std::string, bool); - -%ignore CVC4::WarningC::WarningC(std::ostream*); -%ignore CVC4::MessageC::MessageC(std::ostream*); -%ignore CVC4::NoticeC::NoticeC(std::ostream*); -%ignore CVC4::ChatC::ChatC(std::ostream*); -%ignore CVC4::TraceC::TraceC(std::ostream*); -%ignore CVC4::DebugC::DebugC(std::ostream*); -%ignore CVC4::DumpOutC::DumpOutC(std::ostream*); - -%ignore CVC4::WarningC::operator(); -%ignore CVC4::MessageC::operator(); -%ignore CVC4::NoticeC::operator(); -%ignore CVC4::ChatC::operator(); -%ignore CVC4::TraceC::operator(); -%ignore CVC4::DebugC::operator(); -%ignore CVC4::DumpOutC::operator(); - -%ignore CVC4::WarningC::getStream(); -%ignore CVC4::MessageC::getStream(); -%ignore CVC4::NoticeC::getStream(); -%ignore CVC4::ChatC::getStream(); -%ignore CVC4::TraceC::getStream(); -%ignore CVC4::DebugC::getStream(); -%ignore CVC4::DumpOutC::getStream(); - -%ignore CVC4::WarningC::setStream(std::ostream&); -%ignore CVC4::MessageC::setStream(std::ostream&); -%ignore CVC4::NoticeC::setStream(std::ostream&); -%ignore CVC4::ChatC::setStream(std::ostream&); -%ignore CVC4::TraceC::setStream(std::ostream&); -%ignore CVC4::DebugC::setStream(std::ostream&); -%ignore CVC4::DumpOutC::setStream(std::ostream&); - -%ignore operator std::ostream&; -%ignore operator CVC4ostream; - -%rename(get) operator (); -%rename(ok) operator bool; - -%include "util/output.h" diff --git a/src/util/rational_cln_imp.h b/src/util/rational_cln_imp.h index 1e27fa859..da2af6c1f 100644 --- a/src/util/rational_cln_imp.h +++ b/src/util/rational_cln_imp.h @@ -155,10 +155,10 @@ public: #ifdef CVC4_NEED_INT64_T_OVERLOADS Rational(int64_t n, int64_t d) : d_value(static_cast<long>(n)) { - d_value /= static_cast<long>(d); + d_value /= cln::cl_I(d); } Rational(uint64_t n, uint64_t d) : d_value(static_cast<unsigned long>(n)) { - d_value /= static_cast<unsigned long>(d); + d_value /= cln::cl_I(d); } #endif /* CVC4_NEED_INT64_T_OVERLOADS */ diff --git a/src/util/record.h b/src/util/record.h index 3d6481320..63c54930e 100644 --- a/src/util/record.h +++ b/src/util/record.h @@ -91,7 +91,6 @@ public: Record(const std::vector< std::pair<std::string, Type> >& fields) : d_fields(fields) { - CheckArgument(! fields.empty(), fields, "fields in record description cannot be empty"); } const_iterator find(std::string name) const { diff --git a/src/util/recursion_breaker.h b/src/util/recursion_breaker.h index 07bf10984..a4177f600 100644 --- a/src/util/recursion_breaker.h +++ b/src/util/recursion_breaker.h @@ -86,7 +86,7 @@ class RecursionBreaker { static CVC4_THREADLOCAL(Map*) s_maps; std::string d_tag; - const T& d_item; + const T d_item; bool d_firstToTag; bool d_recursion; diff --git a/src/util/regexp.h b/src/util/regexp.h new file mode 100644 index 000000000..58f58a40f --- /dev/null +++ b/src/util/regexp.h @@ -0,0 +1,353 @@ +/********************* */ +/*! \file regexp.h + ** \verbatim + ** Original author: Tianyi Liang + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 prototype. + ** 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_public.h" + +#ifndef __CVC4__REGEXP_H +#define __CVC4__REGEXP_H + +#include <iostream> +#include <string> +//#include "util/exception.h" +//#include "util/integer.h" +#include "util/hash.h" + +namespace CVC4 { + +class CVC4_PUBLIC Char { + +private: + unsigned int d_char; + +public: + Char() {} + + Char(const unsigned int c) + : d_char(c) {} + + ~Char() {} + + Char& operator =(const Char& y) { + if(this != &y) d_char = y.d_char; + return *this; + } + + bool operator ==(const Char& y) const { + return d_char == y.d_char ; + } + + bool operator !=(const Char& y) const { + return d_char != y.d_char ; + } + + bool operator <(const Char& y) const { + return d_char < y.d_char; + } + + bool operator >(const Char& y) const { + return d_char > y.d_char ; + } + + bool operator <=(const Char& y) const { + return d_char <= y.d_char; + } + + bool operator >=(const Char& y) const { + return d_char >= y.d_char ; + } + + /* + * Convenience functions + */ + std::string toString() const { + std::string str = "1"; + str[0] = (char) d_char; + return str; + } + + unsigned size() const { + return 1; + } + + const char* c_str() const { + return toString().c_str(); + } +};/* class Char */ + +namespace strings { + +struct CharHashFunction { + size_t operator()(const ::CVC4::Char& c) const { + return __gnu_cxx::hash<const char*>()(c.toString().c_str()); + } +};/* struct CharHashFunction */ + +} + +inline std::ostream& operator <<(std::ostream& os, const Char& c) CVC4_PUBLIC; +inline std::ostream& operator <<(std::ostream& os, const Char& c) { + return os << "\"" << c.toString() << "\""; +} + +class CVC4_PUBLIC String { + +private: + std::vector<unsigned int> d_str; + + bool isVecSame(const std::vector<unsigned int> &a, const std::vector<unsigned int> &b) const { + if(a.size() != b.size()) return false; + else { + for(unsigned int i=0; i<a.size(); ++i) + if(a[i] != b[i]) return false; + return true; + } + } + +public: + String() {} + + String(const std::string &s) { + for(unsigned int i=0; i<s.size(); ++i) { + d_str.push_back( convertCharToUnsignedInt(s[i]) ); + } + } + + String(const char* s) { + for(unsigned int i=0,len=strlen(s); i<len; ++i) { + d_str.push_back( convertCharToUnsignedInt(s[i]) ); + } + } + + String(const std::vector<unsigned int> &s) : d_str(s) { } + + ~String() {} + + String& operator =(const String& y) { + if(this != &y) d_str = y.d_str; + return *this; + } + + bool operator ==(const String& y) const { + return isVecSame(d_str, y.d_str); + } + + bool operator !=(const String& y) const { + return ! ( isVecSame(d_str, y.d_str) ); + } + + String concat (const String& other) const { + std::vector<unsigned int> ret_vec(d_str); + ret_vec.insert( ret_vec.end(), other.d_str.begin(), other.d_str.end() ); + return String(ret_vec); + } + + bool operator <(const String& y) const { + if(d_str.size() != y.d_str.size()) return d_str.size() < y.d_str.size(); + else { + for(unsigned int i=0; i<d_str.size(); ++i) + if(d_str[i] != y.d_str[i]) return d_str[i] < y.d_str[i]; + + return false; + } + } + + bool operator >(const String& y) const { + if(d_str.size() != y.d_str.size()) return d_str.size() > y.d_str.size(); + else { + for(unsigned int i=0; i<d_str.size(); ++i) + if(d_str[i] != y.d_str[i]) return d_str[i] > y.d_str[i]; + + return false; + } + } + + bool operator <=(const String& y) const { + if(d_str.size() != y.d_str.size()) return d_str.size() < y.d_str.size(); + else { + for(unsigned int i=0; i<d_str.size(); ++i) + if(d_str[i] != y.d_str[i]) return d_str[i] < y.d_str[i]; + + return true; + } + } + + bool operator >=(const String& y) const { + if(d_str.size() != y.d_str.size()) return d_str.size() > y.d_str.size(); + else { + for(unsigned int i=0; i<d_str.size(); ++i) + if(d_str[i] != y.d_str[i]) return d_str[i] > y.d_str[i]; + + return true; + } + } + + bool strncmp(const String &y, unsigned int n) const { + for(unsigned int i=0; i<n; ++i) + if(d_str[i] != y.d_str[i]) return false; + return true; + } + + /* + * Convenience functions + */ + std::string toString() const { + std::string str; + for(unsigned int i=0; i<d_str.size(); ++i) { + str += convertUnsignedIntToChar( d_str[i] ); + //TODO isPrintable: ( "\\" + (convertUnsignedIntToChar( d_str[i] ) ); + } + return str; + } + + unsigned size() const { + return d_str.size(); + } + + String substr(unsigned i) const { + std::vector<unsigned int> ret_vec; + std::vector<unsigned int>::const_iterator itr = d_str.begin() + i; + //for(unsigned k=0; k<i; k++) ++itr; + ret_vec.insert(ret_vec.end(), itr, d_str.end()); + return String(ret_vec); + } + String substr(unsigned i, unsigned j) const { + std::vector<unsigned int> ret_vec; + std::vector<unsigned int>::const_iterator itr = d_str.begin() + i; + //for(unsigned k=0; k<i; k++) ++itr; + //std::vector<unsigned int>::const_iterator itr2 = itr; + //for(unsigned k=0; k<j; k++) ++itr2; + ret_vec.insert( ret_vec.end(), itr, itr + j ); + return String(ret_vec); + } + +public: + static unsigned int convertCharToUnsignedInt( char c ) { + int i = (int)c; + i = i-65; + return (unsigned int)(i<0 ? i+256 : i); + } + static char convertUnsignedIntToChar( unsigned int i ){ + int ii = i+65; + return (char)(ii>=256 ? ii-256 : ii); + } + static bool isPrintable( unsigned int i ){ + char c = convertUnsignedIntToChar( i ); + return isprint( (int)c ); + } + +};/* class String */ + +namespace strings { + +struct StringHashFunction { + size_t operator()(const ::CVC4::String& s) const { + return __gnu_cxx::hash<const char*>()(s.toString().c_str()); + } +};/* struct StringHashFunction */ + +} + +inline std::ostream& operator <<(std::ostream& os, const String& s) CVC4_PUBLIC; +inline std::ostream& operator <<(std::ostream& os, const String& s) { + return os << "\"" << s.toString() << "\""; +} + +class CVC4_PUBLIC RegExp { + +private: + std::string d_str; + +public: + RegExp() {} + + RegExp(const std::string s) + : d_str(s) {} + + ~RegExp() {} + + RegExp& operator =(const RegExp& y) { + if(this != &y) d_str = y.d_str; + return *this; + } + + bool operator ==(const RegExp& y) const { + return d_str == y.d_str ; + } + + bool operator !=(const RegExp& y) const { + return d_str != y.d_str ; + } + + String concat (const RegExp& other) const { + return String(d_str + other.d_str); + } + + bool operator <(const RegExp& y) const { + return d_str < y.d_str; + } + + bool operator >(const RegExp& y) const { + return d_str > y.d_str ; + } + + bool operator <=(const RegExp& y) const { + return d_str <= y.d_str; + } + + bool operator >=(const RegExp& y) const { + return d_str >= y.d_str ; + } + + /* + * Convenience functions + */ + + size_t hash() const { + unsigned int h = 1; + + for (size_t i = 0; i < d_str.length(); ++i) { + h = (h << 5) + d_str[i]; + } + + return h; + } + + std::string toString() const { + return d_str; + } + + unsigned size() const { + return d_str.size(); + } +};/* class String */ + +/** + * Hash function for the RegExp constants. + */ +struct CVC4_PUBLIC RegExpHashFunction { + inline size_t operator()(const RegExp& s) const { + return s.hash(); + } +};/* struct RegExpHashFunction */ + +inline std::ostream& operator <<(std::ostream& os, const RegExp& s) CVC4_PUBLIC; +inline std::ostream& operator <<(std::ostream& os, const RegExp& s) { + return os << s.toString(); +} +}/* CVC4 namespace */ + +#endif /* __CVC4__STRING_H */ diff --git a/src/util/result.cpp b/src/util/result.cpp index e0e34f07d..909a7d8c6 100644 --- a/src/util/result.cpp +++ b/src/util/result.cpp @@ -27,11 +27,12 @@ using namespace std; namespace CVC4 { -Result::Result(const std::string& instr) : +Result::Result(const std::string& instr, std::string inputName) : d_sat(SAT_UNKNOWN), d_validity(VALIDITY_UNKNOWN), d_which(TYPE_NONE), - d_unknownExplanation(UNKNOWN_REASON) { + d_unknownExplanation(UNKNOWN_REASON), + d_inputName(inputName) { string s = instr; transform(s.begin(), s.end(), s.begin(), ::tolower); if(s == "sat" || s == "satisfiable") { @@ -115,13 +116,13 @@ Result Result::asSatisfiabilityResult() const throw() { switch(d_validity) { case INVALID: - return Result(SAT); + return Result(SAT, d_inputName); case VALID: - return Result(UNSAT); + return Result(UNSAT, d_inputName); case VALIDITY_UNKNOWN: - return Result(SAT_UNKNOWN, d_unknownExplanation); + return Result(SAT_UNKNOWN, d_unknownExplanation, d_inputName); default: Unhandled(d_validity); @@ -129,7 +130,7 @@ Result Result::asSatisfiabilityResult() const throw() { } // TYPE_NONE - return Result(SAT_UNKNOWN, NO_STATUS); + return Result(SAT_UNKNOWN, NO_STATUS, d_inputName); } Result Result::asValidityResult() const throw() { @@ -141,13 +142,13 @@ Result Result::asValidityResult() const throw() { switch(d_sat) { case SAT: - return Result(INVALID); + return Result(INVALID, d_inputName); case UNSAT: - return Result(VALID); + return Result(VALID, d_inputName); case SAT_UNKNOWN: - return Result(VALIDITY_UNKNOWN, d_unknownExplanation); + return Result(VALIDITY_UNKNOWN, d_unknownExplanation, d_inputName); default: Unhandled(d_sat); @@ -155,7 +156,7 @@ Result Result::asValidityResult() const throw() { } // TYPE_NONE - return Result(VALIDITY_UNKNOWN, NO_STATUS); + return Result(VALIDITY_UNKNOWN, NO_STATUS, d_inputName); } string Result::toString() const { diff --git a/src/util/result.h b/src/util/result.h index cb1bd50fa..21bf563bd 100644 --- a/src/util/result.h +++ b/src/util/result.h @@ -71,47 +71,58 @@ private: enum Validity d_validity; enum Type d_which; enum UnknownExplanation d_unknownExplanation; + std::string d_inputName; public: Result() : d_sat(SAT_UNKNOWN), d_validity(VALIDITY_UNKNOWN), d_which(TYPE_NONE), - d_unknownExplanation(UNKNOWN_REASON) { + d_unknownExplanation(UNKNOWN_REASON), + d_inputName("") { } - Result(enum Sat s) : + Result(enum Sat s, std::string inputName = "") : d_sat(s), d_validity(VALIDITY_UNKNOWN), d_which(TYPE_SAT), - d_unknownExplanation(UNKNOWN_REASON) { + d_unknownExplanation(UNKNOWN_REASON), + d_inputName(inputName) { CheckArgument(s != SAT_UNKNOWN, "Must provide a reason for satisfiability being unknown"); } - Result(enum Validity v) : + Result(enum Validity v, std::string inputName = "") : d_sat(SAT_UNKNOWN), d_validity(v), d_which(TYPE_VALIDITY), - d_unknownExplanation(UNKNOWN_REASON) { + d_unknownExplanation(UNKNOWN_REASON), + d_inputName(inputName) { CheckArgument(v != VALIDITY_UNKNOWN, "Must provide a reason for validity being unknown"); } - Result(enum Sat s, enum UnknownExplanation unknownExplanation) : + Result(enum Sat s, enum UnknownExplanation unknownExplanation, std::string inputName = "") : d_sat(s), d_validity(VALIDITY_UNKNOWN), d_which(TYPE_SAT), - d_unknownExplanation(unknownExplanation) { + d_unknownExplanation(unknownExplanation), + d_inputName(inputName) { CheckArgument(s == SAT_UNKNOWN, "improper use of unknown-result constructor"); } - Result(enum Validity v, enum UnknownExplanation unknownExplanation) : + Result(enum Validity v, enum UnknownExplanation unknownExplanation, std::string inputName = "") : d_sat(SAT_UNKNOWN), d_validity(v), d_which(TYPE_VALIDITY), - d_unknownExplanation(unknownExplanation) { + d_unknownExplanation(unknownExplanation), + d_inputName(inputName) { CheckArgument(v == VALIDITY_UNKNOWN, "improper use of unknown-result constructor"); } - Result(const std::string& s); + Result(const std::string& s, std::string inputName = ""); + + Result(const Result& r, std::string inputName) { + *this = r; + d_inputName = inputName; + } enum Sat isSat() const { return d_which == TYPE_SAT ? d_sat : SAT_UNKNOWN; @@ -142,6 +153,8 @@ public: std::string toString() const; + std::string getInputName() const { return d_inputName; } + };/* class Result */ inline bool Result::operator!=(const Result& r) const throw() { diff --git a/src/util/result.i b/src/util/result.i index 029a3618a..b77bfd881 100644 --- a/src/util/result.i +++ b/src/util/result.i @@ -12,8 +12,9 @@ %ignore CVC4::operator<<(std::ostream&, enum Result::UnknownExplanation); %ignore CVC4::operator==(enum Result::Sat, const Result&); -%ignore CVC4::operator==(enum Result::Validity, const Result&); %ignore CVC4::operator!=(enum Result::Sat, const Result&); + +%ignore CVC4::operator==(enum Result::Validity, const Result&); %ignore CVC4::operator!=(enum Result::Validity, const Result&); %include "util/result.h" diff --git a/src/util/sort_inference.cpp b/src/util/sort_inference.cpp index d44499fa8..b66d1cbe4 100644 --- a/src/util/sort_inference.cpp +++ b/src/util/sort_inference.cpp @@ -20,15 +20,70 @@ #include <vector> #include "util/sort_inference.h" +#include "theory/uf/options.h" +//#include "theory/rewriter.h" using namespace CVC4; using namespace std; namespace CVC4 { +void SortInference::UnionFind::print(const char * c){ + for( std::map< int, int >::iterator it = d_eqc.begin(); it != d_eqc.end(); ++it ){ + Trace(c) << "s_" << it->first << " = s_" << it->second << ", "; + } + for( unsigned i=0; i<d_deq.size(); i++ ){ + Trace(c) << "s_" << d_deq[i].first << " != s_" << d_deq[i].second << ", "; + } + Trace(c) << std::endl; +} +void SortInference::UnionFind::set( UnionFind& c ) { + clear(); + for( std::map< int, int >::iterator it = c.d_eqc.begin(); it != c.d_eqc.end(); ++it ){ + d_eqc[ it->first ] = it->second; + } + d_deq.insert( d_deq.end(), c.d_deq.begin(), c.d_deq.end() ); +} +int SortInference::UnionFind::getRepresentative( int t ){ + std::map< int, int >::iterator it = d_eqc.find( t ); + if( it==d_eqc.end() || it->second==t ){ + return t; + }else{ + int rt = getRepresentative( it->second ); + d_eqc[t] = rt; + return rt; + } +} +void SortInference::UnionFind::setEqual( int t1, int t2 ){ + if( t1!=t2 ){ + int rt1 = getRepresentative( t1 ); + int rt2 = getRepresentative( t2 ); + if( rt1>rt2 ){ + d_eqc[rt1] = rt2; + }else{ + d_eqc[rt2] = rt1; + } + } +} +bool SortInference::UnionFind::isValid() { + for( unsigned i=0; i<d_deq.size(); i++ ){ + if( areEqual( d_deq[i].first, d_deq[i].second ) ){ + return false; + } + } + return true; +} + + +void SortInference::recordSubsort( int s ){ + s = d_type_union_find.getRepresentative( s ); + if( std::find( d_sub_sorts.begin(), d_sub_sorts.end(), s )==d_sub_sorts.end() ){ + d_sub_sorts.push_back( s ); + } +} void SortInference::printSort( const char* c, int t ){ - int rt = getRepresentative( t ); + int rt = d_type_union_find.getRepresentative( t ); if( d_type_types.find( rt )!=d_type_types.end() ){ Trace(c) << d_type_types[rt]; }else{ @@ -43,30 +98,49 @@ void SortInference::simplify( std::vector< Node >& assertions, bool doRewrite ){ std::map< Node, Node > var_bound; process( assertions[i], var_bound ); } - //print debug - if( Trace.isOn("sort-inference") ){ - for( std::map< Node, int >::iterator it = d_op_return_types.begin(); it != d_op_return_types.end(); ++it ){ - Trace("sort-inference") << it->first << " : "; - if( !d_op_arg_types[ it->first ].empty() ){ - Trace("sort-inference") << "( "; - for( size_t i=0; i<d_op_arg_types[ it->first ].size(); i++ ){ - printSort( "sort-inference", d_op_arg_types[ it->first ][i] ); - Trace("sort-inference") << " "; - } - Trace("sort-inference") << ") -> "; + for( std::map< Node, int >::iterator it = d_op_return_types.begin(); it != d_op_return_types.end(); ++it ){ + Trace("sort-inference") << it->first << " : "; + if( !d_op_arg_types[ it->first ].empty() ){ + Trace("sort-inference") << "( "; + for( size_t i=0; i<d_op_arg_types[ it->first ].size(); i++ ){ + recordSubsort( d_op_arg_types[ it->first ][i] ); + printSort( "sort-inference", d_op_arg_types[ it->first ][i] ); + Trace("sort-inference") << " "; } - printSort( "sort-inference", it->second ); - Trace("sort-inference") << std::endl; + Trace("sort-inference") << ") -> "; } - for( std::map< Node, std::map< Node, int > >::iterator it = d_var_types.begin(); it != d_var_types.end(); ++it ){ - Trace("sort-inference") << "Quantified formula " << it->first << " : " << std::endl; - for( std::map< Node, int >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){ - printSort( "sort-inference", it2->second ); - Trace("sort-inference") << std::endl; - } + recordSubsort( it->second ); + printSort( "sort-inference", it->second ); + Trace("sort-inference") << std::endl; + } + for( std::map< Node, std::map< Node, int > >::iterator it = d_var_types.begin(); it != d_var_types.end(); ++it ){ + Trace("sort-inference") << "Quantified formula : " << it->first << " : " << std::endl; + for( std::map< Node, int >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){ + printSort( "sort-inference", it2->second ); Trace("sort-inference") << std::endl; } + Trace("sort-inference") << std::endl; } + + //determine monotonicity of sorts + for( unsigned i=0; i<assertions.size(); i++ ){ + Trace("sort-inference-debug") << "Process monotonicity for " << assertions[i] << std::endl; + std::map< Node, Node > var_bound; + processMonotonic( assertions[i], true, true, var_bound ); + } + + Trace("sort-inference") << "We have " << d_sub_sorts.size() << " sub-sorts : " << std::endl; + for( unsigned i=0; i<d_sub_sorts.size(); i++ ){ + printSort( "sort-inference", d_sub_sorts[i] ); + if( d_type_types.find( d_sub_sorts[i] )!=d_type_types.end() ){ + Trace("sort-inference") << " is interpreted." << std::endl; + }else if( d_non_monotonic_sorts.find( d_sub_sorts[i] )==d_non_monotonic_sorts.end() ){ + Trace("sort-inference") << " is monotonic." << std::endl; + }else{ + Trace("sort-inference") << " is not monotonic." << std::endl; + } + } + if( doRewrite ){ //simplify all assertions by introducing new symbols wherever necessary (NOTE: this is unsound for quantifiers) for( unsigned i=0; i<assertions.size(); i++ ){ @@ -82,47 +156,43 @@ void SortInference::simplify( std::vector< Node >& assertions, bool doRewrite ){ } //add lemma enforcing introduced constants to be distinct? } - } -} - -int SortInference::getRepresentative( int t ){ - std::map< int, int >::iterator it = d_type_union_find.find( t ); - if( it!=d_type_union_find.end() ){ - if( it->second==t ){ - return t; - }else{ - int rt = getRepresentative( it->second ); - d_type_union_find[t] = rt; - return rt; + }else if( !options::ufssSymBreak() ){ + std::map< int, Node > constants; + //just add a bunch of unit lemmas + for( std::map< Node, int >::iterator it = d_op_return_types.begin(); it != d_op_return_types.end(); ++it ){ + int rt = d_type_union_find.getRepresentative( it->second ); + if( d_op_arg_types[ it->first ].empty() && constants.find( rt )==constants.end() ){ + constants[ rt ] = it->first; + } } - }else{ - return t; + //add unit lemmas for each constant + Node first_const; + for( std::map< int, Node >::iterator it = constants.begin(); it != constants.end(); ++it ){ + if( first_const.isNull() ){ + first_const = it->second; + }else{ + Node eq = first_const.eqNode( it->second ); + //eq = Rewriter::rewrite( eq ); + Trace("sort-inference-lemma") << "Sort inference lemma : " << eq << std::endl; + assertions.push_back( eq ); + } + } + + } + initialSortCount = sortCount; } void SortInference::setEqual( int t1, int t2 ){ if( t1!=t2 ){ - int rt1 = getRepresentative( t1 ); - int rt2 = getRepresentative( t2 ); + int rt1 = d_type_union_find.getRepresentative( t1 ); + int rt2 = d_type_union_find.getRepresentative( t2 ); if( rt1!=rt2 ){ Trace("sort-inference-debug") << "Set equal : "; printSort( "sort-inference-debug", rt1 ); Trace("sort-inference-debug") << " "; printSort( "sort-inference-debug", rt2 ); Trace("sort-inference-debug") << std::endl; - //check if they must be a type - std::map< int, TypeNode >::iterator it1 = d_type_types.find( rt1 ); - std::map< int, TypeNode >::iterator it2 = d_type_types.find( rt2 ); - if( it2!=d_type_types.end() ){ - if( it1==d_type_types.end() ){ - //swap sides - int swap = rt1; - rt1 = rt2; - rt2 = swap; - }else{ - Assert( rt1==rt2 ); - } - } /* d_type_eq_class[rt1].insert( d_type_eq_class[rt1].end(), d_type_eq_class[rt2].begin(), d_type_eq_class[rt2].end() ); d_type_eq_class[rt2].clear(); @@ -132,7 +202,19 @@ void SortInference::setEqual( int t1, int t2 ){ } Trace("sort-inference-debug") << "}" << std::endl; */ - d_type_union_find[rt2] = rt1; + if( rt2>rt1 ){ + //swap + int swap = rt1; + rt1 = rt2; + rt2 = swap; + } + d_type_union_find.d_eqc[rt1] = rt2; + std::map< int, TypeNode >::iterator it1 = d_type_types.find( rt1 ); + if( it1!=d_type_types.end() ){ + Assert( d_type_types.find( rt2 )==d_type_types.end() ); + d_type_types[rt2] = it1->second; + d_type_types.erase( rt1 ); + } } } } @@ -155,14 +237,17 @@ int SortInference::process( Node n, std::map< Node, Node >& var_bound ){ Trace("sort-inference-debug") << "Process " << n << std::endl; //add to variable bindings if( n.getKind()==kind::FORALL || n.getKind()==kind::EXISTS ){ - for( size_t i=0; i<n[0].getNumChildren(); i++ ){ - //TODO: try applying sort inference to quantified variables - d_var_types[n][ n[0][i] ] = sortCount; - sortCount++; + if( d_var_types.find( n )!=d_var_types.end() ){ + return getIdForType( n.getType() ); + }else{ + for( size_t i=0; i<n[0].getNumChildren(); i++ ){ + //apply sort inference to quantified variables + d_var_types[n][ n[0][i] ] = sortCount; + sortCount++; - //type of the quantified variable must be the same - //d_var_types[n][ n[0][i] ] = getIdForType( n[0][i].getType() ); - var_bound[ n[0][i] ] = n; + //type of the quantified variable must be the same + var_bound[ n[0][i] ] = n; + } } } @@ -192,6 +277,9 @@ int SortInference::process( Node n, std::map< Node, Node >& var_bound ){ if( n.getKind()==kind::EQUAL ){ //we only require that the left and right hand side must be equal setEqual( child_types[0], child_types[1] ); + //int eqType = getIdForType( n[0].getType() ); + //setEqual( child_types[0], eqType ); + //setEqual( child_types[1], eqType ); retType = getIdForType( n.getType() ); }else if( n.getKind()==kind::APPLY_UF ){ Node op = n.getOperator(); @@ -227,11 +315,11 @@ int SortInference::process( Node n, std::map< Node, Node >& var_bound ){ //d_type_eq_class[sortCount].push_back( n ); } retType = d_op_return_types[n]; - }else if( n.isConst() ){ - Trace("sort-inference-debug") << n << " is a constant." << std::endl; + //}else if( n.isConst() ){ + // Trace("sort-inference-debug") << n << " is a constant." << std::endl; //can be any type we want - retType = sortCount; - sortCount++; + // retType = sortCount; + // sortCount++; }else{ Trace("sort-inference-debug") << n << " is a interpreted symbol." << std::endl; //it is an interpretted term @@ -251,9 +339,43 @@ int SortInference::process( Node n, std::map< Node, Node >& var_bound ){ return retType; } +void SortInference::processMonotonic( Node n, bool pol, bool hasPol, std::map< Node, Node >& var_bound ) { + if( n.getKind()==kind::FORALL ){ + for( unsigned i=0; i<n[0].getNumChildren(); i++ ){ + var_bound[n[0][i]] = n; + } + processMonotonic( n[1], pol, hasPol, var_bound ); + for( unsigned i=0; i<n[0].getNumChildren(); i++ ){ + var_bound.erase( n[0][i] ); + } + }else if( n.getKind()==kind::EQUAL ){ + if( !hasPol || pol ){ + for( unsigned i=0; i<2; i++ ){ + if( var_bound.find( n[i] )==var_bound.end() ){ + int sid = getSortId( var_bound[n[i]], n[i] ); + d_non_monotonic_sorts[sid] = true; + break; + } + } + } + }else{ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + bool npol = pol; + bool nhasPol = hasPol; + if( n.getKind()==kind::NOT || ( n.getKind()==kind::IMPLIES && i==0 ) ){ + npol = !npol; + } + if( ( n.getKind()==kind::ITE && i==0 ) || n.getKind()==kind::XOR || n.getKind()==kind::IFF ){ + nhasPol = false; + } + processMonotonic( n[i], npol, nhasPol, var_bound ); + } + } +} + TypeNode SortInference::getOrCreateTypeForId( int t, TypeNode pref ){ - int rt = getRepresentative( t ); + int rt = d_type_union_find.getRepresentative( t ); if( d_type_types.find( rt )!=d_type_types.end() ){ return d_type_types[rt]; }else{ @@ -278,7 +400,7 @@ TypeNode SortInference::getOrCreateTypeForId( int t, TypeNode pref ){ } TypeNode SortInference::getTypeForId( int t ){ - int rt = getRepresentative( t ); + int rt = d_type_union_find.getRepresentative( t ); if( d_type_types.find( rt )!=d_type_types.end() ){ return d_type_types[rt]; }else{ @@ -414,15 +536,71 @@ Node SortInference::simplify( Node n, std::map< Node, Node >& var_bound ){ } int SortInference::getSortId( Node n ) { Node op = n.getKind()==kind::APPLY_UF ? n.getOperator() : n; - return getRepresentative( d_op_return_types[op] ); + if( d_op_return_types.find( op )!=d_op_return_types.end() ){ + return d_type_union_find.getRepresentative( d_op_return_types[op] ); + }else{ + return 0; + } } int SortInference::getSortId( Node f, Node v ) { - return getRepresentative( d_var_types[f][v] ); + if( d_var_types.find( f )!=d_var_types.end() ){ + return d_type_union_find.getRepresentative( d_var_types[f][v] ); + }else{ + return 0; + } } void SortInference::setSkolemVar( Node f, Node v, Node sk ){ + Trace("sort-inference-temp") << "Set skolem var for " << f << ", variable " << v << std::endl; + if( isWellSortedFormula( f ) && d_var_types.find( f )==d_var_types.end() ){ + std::map< Node, Node > var_bound; + process( f, var_bound ); + } d_op_return_types[sk] = getSortId( f, v ); + Trace("sort-inference-temp") << "Set skolem sort id for " << sk << " to " << d_op_return_types[sk] << std::endl; +} + +bool SortInference::isWellSortedFormula( Node n ) { + if( n.getType().isBoolean() && n.getKind()!=kind::APPLY_UF ){ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + if( !isWellSortedFormula( n[i] ) ){ + return false; + } + } + return true; + }else{ + return isWellSorted( n ); + } +} + +bool SortInference::isWellSorted( Node n ) { + if( getSortId( n )==0 ){ + return false; + }else{ + if( n.getKind()==kind::APPLY_UF ){ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + int s1 = getSortId( n[i] ); + int s2 = d_type_union_find.getRepresentative( d_op_arg_types[ n.getOperator() ][i] ); + if( s1!=s2 ){ + return false; + } + if( !isWellSorted( n[i] ) ){ + return false; + } + } + } + return true; + } +} + +void SortInference::getSortConstraints( Node n, UnionFind& uf ) { + if( n.getKind()==kind::APPLY_UF ){ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + getSortConstraints( n[i], uf ); + uf.setEqual( getSortId( n[i] ), d_type_union_find.getRepresentative( d_op_arg_types[ n.getOperator() ][i] ) ); + } + } } }/* CVC4 namespace */ diff --git a/src/util/sort_inference.h b/src/util/sort_inference.h index 1bcb8a208..8f0fc5e9f 100644 --- a/src/util/sort_inference.h +++ b/src/util/sort_inference.h @@ -28,11 +28,33 @@ namespace CVC4 { class SortInference{ private: - //for debugging - //std::map< int, std::vector< Node > > d_type_eq_class; + //all subsorts + std::vector< int > d_sub_sorts; + std::map< int, bool > d_non_monotonic_sorts; + void recordSubsort( int s ); +public: + class UnionFind { + public: + UnionFind(){} + UnionFind( UnionFind& c ){ + set( c ); + } + std::map< int, int > d_eqc; + //pairs that must be disequal + std::vector< std::pair< int, int > > d_deq; + void print(const char * c); + void clear() { d_eqc.clear(); d_deq.clear(); } + void set( UnionFind& c ); + int getRepresentative( int t ); + void setEqual( int t1, int t2 ); + void setDisequal( int t1, int t2 ){ d_deq.push_back( std::pair< int, int >( t1, t2 ) ); } + bool areEqual( int t1, int t2 ) { return getRepresentative( t1 )==getRepresentative( t2 ); } + bool isValid(); + }; private: int sortCount; - std::map< int, int > d_type_union_find; + int initialSortCount; + UnionFind d_type_union_find; std::map< int, TypeNode > d_type_types; std::map< TypeNode, int > d_id_for_types; //for apply uf operators @@ -41,12 +63,17 @@ private: //for bound variables std::map< Node, std::map< Node, int > > d_var_types; //get representative - int getRepresentative( int t ); void setEqual( int t1, int t2 ); int getIdForType( TypeNode tn ); void printSort( const char* c, int t ); //process int process( Node n, std::map< Node, Node >& var_bound ); + +//for monotonicity inference +private: + void processMonotonic( Node n, bool pol, bool hasPol, std::map< Node, Node >& var_bound ); + +//for rewriting private: //mapping from old symbols to new symbols std::map< Node, Node > d_symbol_map; @@ -60,15 +87,24 @@ private: Node getNewSymbol( Node old, TypeNode tn ); //simplify Node simplify( Node n, std::map< Node, Node >& var_bound ); + public: - SortInference() : sortCount( 0 ){} + SortInference() : sortCount( 1 ){} ~SortInference(){} void simplify( std::vector< Node >& assertions, bool doRewrite = false ); + //get sort id for term n int getSortId( Node n ); + //get sort id for variable of quantified formula f int getSortId( Node f, Node v ); //set that sk is the skolem variable of v for quantifier f void setSkolemVar( Node f, Node v, Node sk ); +public: + //is well sorted + bool isWellSortedFormula( Node n ); + bool isWellSorted( Node n ); + //get constraints for being well-typed according to computed sub-types + void getSortConstraints( Node n, SortInference::UnionFind& uf ); }; } diff --git a/src/util/statistics_registry.h b/src/util/statistics_registry.h index 3bec559d5..8ffc60d17 100644 --- a/src/util/statistics_registry.h +++ b/src/util/statistics_registry.h @@ -847,7 +847,7 @@ public: * like in this example, which takes the place of the declaration of a * statistics field "d_checkTimer": * - * KEEP_STATISTIC(TimerStat, d_checkTimer, "theory::uf::morgan::checkTime"); + * KEEP_STATISTIC(TimerStat, d_checkTimer, "theory::uf::checkTime"); * * If any args need to be passed to the constructor, you can specify * them after the string. diff --git a/src/util/util_model.cpp b/src/util/util_model.cpp index ab4c95ea5..1c2dc2edf 100644 --- a/src/util/util_model.cpp +++ b/src/util/util_model.cpp @@ -24,7 +24,7 @@ using namespace std; namespace CVC4 { -std::ostream& operator<<(std::ostream& out, Model& m) { +std::ostream& operator<<(std::ostream& out, const Model& m) { smt::SmtScope smts(&m.d_smt); Expr::dag::Scope scope(out, false); Printer::getPrinter(options::outputLanguage())->toStream(out, m); diff --git a/src/util/util_model.h b/src/util/util_model.h index 535493a2d..e5bd1f955 100644 --- a/src/util/util_model.h +++ b/src/util/util_model.h @@ -29,10 +29,14 @@ class Command; class SmtEngine; class Model; -std::ostream& operator<<(std::ostream&, Model&); +std::ostream& operator<<(std::ostream&, const Model&); class Model { - friend std::ostream& operator<<(std::ostream&, Model&); + friend std::ostream& operator<<(std::ostream&, const Model&); + friend class SmtEngine; + + /** the input name (file name, etc.) this model is associated to */ + std::string d_inputName; protected: /** The SmtEngine we're associated with */ @@ -50,6 +54,10 @@ public: const Command* getCommand(size_t i) const; /** get the smt engine that this model is hooked up to */ SmtEngine* getSmtEngine() { return &d_smt; } + /** get the smt engine (as a pointer-to-const) that this model is hooked up to */ + const SmtEngine* getSmtEngine() const { return &d_smt; } + /** get the input name (file name, etc.) this model is associated to */ + std::string getInputName() const { return d_inputName; } public: /** get value for expression */ |