diff options
Diffstat (limited to 'src')
214 files changed, 10632 insertions, 4087 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 3ffda9f28..0e3f4823b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -96,10 +96,14 @@ libcvc4_la_SOURCES = \ proof/skolemization_manager.h \ proof/theory_proof.cpp \ proof/theory_proof.h \ + proof/lemma_proof.cpp \ + proof/lemma_proof.h \ proof/uf_proof.cpp \ proof/uf_proof.h \ proof/unsat_core.cpp \ proof/unsat_core.h \ + proof/proof_output_channel.cpp \ + proof/proof_output_channel.h \ prop/cnf_stream.cpp \ prop/cnf_stream.h \ prop/prop_engine.cpp \ @@ -109,6 +113,8 @@ libcvc4_la_SOURCES = \ prop/sat_solver_factory.cpp \ prop/sat_solver_factory.h \ prop/sat_solver_types.h \ + prop/cryptominisat.h \ + prop/cryptominisat.cpp \ prop/theory_proxy.cpp \ prop/theory_proxy.h \ smt/boolean_terms.cpp \ @@ -443,7 +449,12 @@ libcvc4_la_SOURCES = \ theory/fp/theory_fp.cpp \ theory/fp/theory_fp_rewriter.h \ theory/fp/theory_fp_rewriter.cpp \ - theory/fp/theory_fp_type_rules.h + theory/fp/theory_fp_type_rules.h \ + theory/sep/theory_sep.h \ + theory/sep/theory_sep.cpp \ + theory/sep/theory_sep_rewriter.h \ + theory/sep/theory_sep_rewriter.cpp \ + theory/sep/theory_sep_type_rules.h nodist_libcvc4_la_SOURCES = \ theory/rewriter_tables.h \ @@ -477,6 +488,12 @@ libcvc4_la_LIBADD += $(ABC_LIBS) libcvc4_la_LDFLAGS += $(ABC_LDFLAGS) endif +if CVC4_USE_CRYPTOMINISAT +libcvc4_la_LIBADD += $(CRYPTOMINISAT_LIBS) +libcvc4_la_LDFLAGS += $(CRYPTOMINISAT_LDFLAGS) +endif + + BUILT_SOURCES = \ theory/rewriter_tables.h \ theory/theory_traits.h \ @@ -526,6 +543,7 @@ EXTRA_DIST = \ theory/mktheorytraits \ theory/quantifiers/kinds \ theory/rewriter_tables_template.h \ + theory/sep/kinds \ theory/sets/kinds \ theory/strings/kinds \ theory/theory_traits_template.h \ diff --git a/src/Makefile.theories b/src/Makefile.theories index 8b5cef4d5..003128a3c 100644 --- a/src/Makefile.theories +++ b/src/Makefile.theories @@ -1,3 +1,3 @@ -THEORIES = builtin booleans uf arith bv fp arrays datatypes sets strings quantifiers idl +THEORIES = builtin booleans uf arith bv fp arrays datatypes sets sep strings quantifiers idl diff --git a/src/base/configuration.cpp b/src/base/configuration.cpp index ce8967a18..db19469fd 100644 --- a/src/base/configuration.cpp +++ b/src/base/configuration.cpp @@ -134,6 +134,10 @@ bool Configuration::isBuiltWithAbc() { return IS_ABC_BUILD; } +bool Configuration::isBuiltWithCryptominisat() { + return IS_CRYPTOMINISAT_BUILD; +} + bool Configuration::isBuiltWithReadline() { return IS_READLINE_BUILD; } diff --git a/src/base/configuration.h b/src/base/configuration.h index 498337c4c..5d499174f 100644 --- a/src/base/configuration.h +++ b/src/base/configuration.h @@ -93,6 +93,8 @@ public: static bool isBuiltWithAbc(); + static bool isBuiltWithCryptominisat(); + static bool isBuiltWithReadline(); static bool isBuiltWithCudd(); diff --git a/src/base/configuration_private.h b/src/base/configuration_private.h index 74ce00bdf..f0ef1a795 100644 --- a/src/base/configuration_private.h +++ b/src/base/configuration_private.h @@ -114,6 +114,12 @@ namespace CVC4 { # define IS_ABC_BUILD false #endif /* CVC4_USE_ABC */ +#if CVC4_USE_CRYPTOMINISAT +# define IS_CRYPTOMINISAT_BUILD true +#else /* CVC4_USE_CRYPTOMINISAT */ +# define IS_CRYPTOMINISAT_BUILD false +#endif /* CVC4_USE_CRYPTOMINISAT */ + #ifdef HAVE_LIBREADLINE # define IS_READLINE_BUILD true #else /* HAVE_LIBREADLINE */ diff --git a/src/context/cdhashmap.h b/src/context/cdhashmap.h index d080da333..884234eb8 100644 --- a/src/context/cdhashmap.h +++ b/src/context/cdhashmap.h @@ -163,7 +163,7 @@ class CDOhash_map : public ContextObj { d_data = p->d_data; } } - // Explicitly call destructors fro the key and the date as they will not + // Explicitly call destructors for the key and the date as they will not // otherwise get called. p->d_key.~Key(); p->d_data.~Data(); @@ -478,18 +478,13 @@ public: typename table_type::iterator i = d_map.find(k); if(i != d_map.end()) { Debug("gc") << "key " << k << " obliterated" << std::endl; - // We can't call ->deleteSelf() here, because it calls the - // ContextObj destructor, which calls CDOhash_map::destroy(), which - // restore()'s, which puts the CDOhash_map on the trash, which causes - // a double-delete. - (*i).second->~Element(); - // Writing ...->~CDOhash_map() in the above is legal (?) but breaks - // g++ 4.1, though later versions have no problem. + // Restore this object to level 0. If it was inserted after level 0, + // nothing else to do as restore will put it in the trash. + (*i).second->destroy(); + // Check if object was inserted at level 0: in that case, still have + // to do some work. typename table_type::iterator j = d_map.find(k); - // This if() succeeds for objects inserted when in the - // zero-scope: they were never save()'d there, so restore() - // never gets a NULL map and so they leak. if(j != d_map.end()) { Element* elt = (*j).second; if(d_first == elt) { @@ -505,9 +500,8 @@ public: } d_map.erase(j);//FIXME multithreading Debug("gc") << "key " << k << " obliterated zero-scope: " << elt << std::endl; - // was already destructed, so don't call ->deleteSelf() if(!elt->d_noTrash) { - ::operator delete(elt); + elt->deleteSelf(); } } } diff --git a/src/expr/Makefile.am b/src/expr/Makefile.am index c04de4421..9dcbc3b4b 100644 --- a/src/expr/Makefile.am +++ b/src/expr/Makefile.am @@ -40,6 +40,8 @@ libexpr_la_SOURCES = \ pickle_data.h \ pickler.cpp \ pickler.h \ + sepconst.cpp \ + sepconst.h \ symbol_table.cpp \ symbol_table.h \ type.cpp \ diff --git a/src/expr/datatype.cpp b/src/expr/datatype.cpp index d14ac26d4..12cab48cc 100644 --- a/src/expr/datatype.cpp +++ b/src/expr/datatype.cpp @@ -297,7 +297,7 @@ bool Datatype::isFinite() const throw(IllegalArgumentException) { return true; } -bool Datatype::isUFinite() const throw(IllegalArgumentException) { +bool Datatype::isInterpretedFinite() const throw(IllegalArgumentException) { PrettyCheckArgument(isResolved(), this, "this datatype is not yet resolved"); // we're using some internals, so we have to set up this library context ExprManagerScope ems(d_self); @@ -310,7 +310,7 @@ bool Datatype::isUFinite() const throw(IllegalArgumentException) { self.setAttribute(DatatypeUFiniteComputedAttr(), true); self.setAttribute(DatatypeUFiniteAttr(), false); for(const_iterator i = begin(), i_end = end(); i != i_end; ++i) { - if(! (*i).isUFinite()) { + if(! (*i).isInterpretedFinite()) { return false; } } @@ -850,7 +850,7 @@ bool DatatypeConstructor::isFinite() const throw(IllegalArgumentException) { return true; } -bool DatatypeConstructor::isUFinite() const throw(IllegalArgumentException) { +bool DatatypeConstructor::isInterpretedFinite() const throw(IllegalArgumentException) { PrettyCheckArgument(isResolved(), this, "this datatype constructor is not yet resolved"); // we're using some internals, so we have to set up this library context ExprManagerScope ems(d_constructor); @@ -861,16 +861,8 @@ bool DatatypeConstructor::isUFinite() const throw(IllegalArgumentException) { } bool success = true; for(const_iterator i = begin(), i_end = end(); i != i_end; ++i) { - Type t = (*i).getRangeType(); - if( t.isDatatype() ){ - const Datatype& dt = ((DatatypeType)t).getDatatype(); - if( !dt.isUFinite() ){ - success = false; - } - }else if(!t.isSort() && !t.getCardinality().isFinite()) { - success = false; - } - if(!success ){ + TypeNode t = TypeNode::fromType( (*i).getRangeType() ); + if(!t.isInterpretedFinite()) { self.setAttribute(DatatypeUFiniteComputedAttr(), true); self.setAttribute(DatatypeUFiniteAttr(), false); return false; diff --git a/src/expr/datatype.h b/src/expr/datatype.h index 1197b4a3b..a0c9cbe6b 100644 --- a/src/expr/datatype.h +++ b/src/expr/datatype.h @@ -342,7 +342,7 @@ public: * uninterpreted sorts are finite. This function can * only be called for resolved constructors. */ - bool isUFinite() const throw(IllegalArgumentException); + bool isInterpretedFinite() const throw(IllegalArgumentException); /** * Returns true iff this Datatype constructor has already been @@ -613,6 +613,9 @@ public: /** get the record representation for this datatype */ inline Record * getRecord() const; + /** get the record representation for this datatype */ + inline Record * getRecord() const; + /** * Return the cardinality of this datatype (the sum of the * cardinalities of its constructors). The Datatype must be @@ -634,7 +637,7 @@ public: * datatype is not well-founded, this function returns false. The * Datatype must be resolved or an exception is thrown. */ - bool isUFinite() const throw(IllegalArgumentException); + bool isInterpretedFinite() const throw(IllegalArgumentException); /** * Return true iff this datatype is well-founded (there exist ground diff --git a/src/expr/expr_manager_template.cpp b/src/expr/expr_manager_template.cpp index 84f674d2b..53e16751e 100644 --- a/src/expr/expr_manager_template.cpp +++ b/src/expr/expr_manager_template.cpp @@ -30,7 +30,7 @@ ${includes} // compiler directs the user to the template file instead of the // generated one. We don't want the user to modify the generated one, // since it'll get overwritten on a later build. -#line 33 "${template}" +#line 34 "${template}" #ifdef CVC4_STATISTICS_ON #define INC_STAT(kind) \ @@ -941,6 +941,11 @@ Expr ExprManager::mkBoundVar(Type type) { return Expr(this, d_nodeManager->mkBoundVarPtr(*type.d_typeNode)); } +Expr ExprManager::mkSepNil(Type type) { + NodeManagerScope nms(d_nodeManager); + return Expr(this, d_nodeManager->mkSepNilPtr(*type.d_typeNode)); +} + Expr ExprManager::mkAssociative(Kind kind, const std::vector<Expr>& children) { PrettyCheckArgument( diff --git a/src/expr/expr_manager_template.h b/src/expr/expr_manager_template.h index 04f2f4289..9c4e554e1 100644 --- a/src/expr/expr_manager_template.h +++ b/src/expr/expr_manager_template.h @@ -124,6 +124,9 @@ public: /** Get the type for regular expressions. */ RegExpType regExpType() const; + /** Get the type for regular expressions. */ + RegExpType regExpType() const; + /** Get the type for reals. */ RealType realType() const; @@ -545,6 +548,11 @@ public: * @param type the type for the new bound variable */ Expr mkBoundVar(Type type); + + /** + * Create a (nameless) new nil reference for separation logic of type + */ + Expr mkSepNil(Type type); /** Get a reference to the statistics registry for this ExprManager */ Statistics getStatistics() const throw(); diff --git a/src/expr/node_manager.cpp b/src/expr/node_manager.cpp index 0809a0331..d2ac7e2a1 100644 --- a/src/expr/node_manager.cpp +++ b/src/expr/node_manager.cpp @@ -681,6 +681,20 @@ Node NodeManager::mkInstConstant(const TypeNode& type) { return n; } +Node NodeManager::mkSepNil(const TypeNode& type) { + Node n = NodeBuilder<0>(this, kind::SEP_NIL); + n.setAttribute(TypeAttr(), type); + n.setAttribute(TypeCheckedAttr(), true); + return n; +} + +Node* NodeManager::mkSepNilPtr(const TypeNode& type) { + Node* n = NodeBuilder<0>(this, kind::SEP_NIL).constructNodePtr(); + setAttribute(*n, TypeAttr(), type); + setAttribute(*n, TypeCheckedAttr(), true); + return n; +} + Node NodeManager::mkAbstractValue(const TypeNode& type) { Node n = mkConst(AbstractValue(++d_abstractValueCount)); n.setAttribute(TypeAttr(), type); diff --git a/src/expr/node_manager.h b/src/expr/node_manager.h index 7d2b13e4c..dcd7005f8 100644 --- a/src/expr/node_manager.h +++ b/src/expr/node_manager.h @@ -332,7 +332,7 @@ class NodeManager { /** Create a variable with the given type. */ 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: explicit NodeManager(ExprManager* exprManager); @@ -486,6 +486,10 @@ public: /** Create a instantiation constant with the given type. */ Node mkInstConstant(const TypeNode& type); + + /** Create nil reference for separation logic with the given type. */ + Node mkSepNil(const TypeNode& type); + Node* mkSepNilPtr(const TypeNode& type); /** Make a new abstract value with the given type. */ Node mkAbstractValue(const TypeNode& type); diff --git a/src/expr/sepconst.cpp b/src/expr/sepconst.cpp new file mode 100644 index 000000000..7646b90d3 --- /dev/null +++ b/src/expr/sepconst.cpp @@ -0,0 +1,29 @@ +/********************* */ +/*! \file sepconst.cpp + ** \verbatim + ** Original author: Andrew Reynolds + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2014 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief [[ Add one-line brief description here ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#include "expr/sepconst.h" +#include <iostream> + +using namespace std; + +namespace CVC4 { + +std::ostream& operator<<(std::ostream& out, const NilRef& asa) { + return out << "(nil " << asa.getType() << ")"; +} + +}/* CVC4 namespace */ diff --git a/src/expr/sepconst.h b/src/expr/sepconst.h new file mode 100644 index 000000000..9f86c7efc --- /dev/null +++ b/src/expr/sepconst.h @@ -0,0 +1,72 @@ +/********************* */ +/*! \file sepconst.h + ** \verbatim + ** Original author: Andrew Reynolds + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2014 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief [[ Add one-line brief description here ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#include "cvc4_public.h" + +#pragma once + +namespace CVC4 { + // messy; Expr needs NilRef (because it's the payload of a + // CONSTANT-kinded expression), and NilRef needs Expr. + class CVC4_PUBLIC NilRef; + //class CVC4_PUBLIC EmpStar; +}/* CVC4 namespace */ + +#include "expr/expr.h" +#include "expr/type.h" +#include <iostream> + +namespace CVC4 { + +class CVC4_PUBLIC NilRef { + const Type d_type; + NilRef() { } +public: + NilRef(Type refType):d_type(refType) { } + + ~NilRef() throw() { + } + Type getType() const { return d_type; } + bool operator==(const NilRef& es) const throw() { + return d_type == es.d_type; + } + bool operator!=(const NilRef& es) const throw() { + return !(*this == es); + } + bool operator<(const NilRef& es) const throw() { + return d_type < es.d_type; + } + bool operator<=(const NilRef& es) const throw() { + return d_type <= es.d_type; + } + bool operator>(const NilRef& es) const throw() { + return !(*this <= es); + } + bool operator>=(const NilRef& es) const throw() { + return !(*this < es); + } +};/* class NilRef */ + +std::ostream& operator<<(std::ostream& out, const NilRef& es) CVC4_PUBLIC; + +struct CVC4_PUBLIC NilRefHashFunction { + inline size_t operator()(const NilRef& es) const { + return TypeHashFunction()(es.getType()); + } +};/* struct NilRefHashFunction */ + +}/* CVC4 namespace */ diff --git a/src/expr/type_node.cpp b/src/expr/type_node.cpp index 5d672e6ac..dc2370bea 100644 --- a/src/expr/type_node.cpp +++ b/src/expr/type_node.cpp @@ -21,6 +21,7 @@ #include "expr/type_properties.h" #include "options/base_options.h" #include "options/expr_options.h" +#include "options/quantifiers_options.h" using namespace std; @@ -64,6 +65,26 @@ Cardinality TypeNode::getCardinality() const { return kind::getCardinality(*this); } +bool TypeNode::isInterpretedFinite() const { + if( getCardinality().isFinite() ){ + return true; + }else{ + if( options::finiteModelFind() ){ + if( isSort() ){ + return true; + }else if( isDatatype() || isParametricDatatype() ){ + const Datatype& dt = getDatatype(); + return dt.isInterpretedFinite(); + }else if( isArray() ){ + return getArrayIndexType().isInterpretedFinite() && getArrayConstituentType().isInterpretedFinite(); + }else if( isSet() ) { + return getSetElementType().isInterpretedFinite(); + } + } + return false; + } +} + bool TypeNode::isWellFounded() const { return kind::isWellFounded(*this); } diff --git a/src/expr/type_node.h b/src/expr/type_node.h index cfb61a085..46fdaa143 100644 --- a/src/expr/type_node.h +++ b/src/expr/type_node.h @@ -419,6 +419,13 @@ public: * @return a finite or infinite cardinality */ Cardinality getCardinality() const; + + /** + * Is this type interpreted as being finite. + * If finite model finding is enabled, this assumes all uninterpreted sorts + * are interpreted as finite. + */ + bool isInterpretedFinite() const; /** * Returns whether this type is well-founded. diff --git a/src/main/interactive_shell.cpp b/src/main/interactive_shell.cpp index e11f82a40..334373642 100644 --- a/src/main/interactive_shell.cpp +++ b/src/main/interactive_shell.cpp @@ -172,6 +172,7 @@ InteractiveShell::~InteractiveShell() { << ": " << strerror(err) << std::endl; } #endif /* HAVE_LIBREADLINE */ + delete d_parser; } Command* InteractiveShell::readCommand() throw (UnsafeInterruptException) { diff --git a/src/main/portfolio_util.cpp b/src/main/portfolio_util.cpp index 03827a917..8e38eb528 100644 --- a/src/main/portfolio_util.cpp +++ b/src/main/portfolio_util.cpp @@ -98,12 +98,6 @@ void parseThreadSpecificOptions(OptionsList& threadOptions, const Options& opts) ss << optid << ": " << e.getMessage(); throw OptionException(ss.str()); } - if(optind != targc) { - stringstream ss; - ss << "unused argument `" << targv[optind] - << "' in thread configuration " << optid << " !"; - throw OptionException(ss.str()); - } if(tOpts.getThreads() != numThreads || tOpts.getThreadArgv() != opts.getThreadArgv()) { stringstream ss; diff --git a/src/options/Makefile.am b/src/options/Makefile.am index 643932781..1eb84b45f 100644 --- a/src/options/Makefile.am +++ b/src/options/Makefile.am @@ -10,7 +10,7 @@ # Step 4: Generate X_options.h from X_options.sed # Step 5: Generate X_options.cpp from X_options.sed. # This stage also waits for X_options.h as otherwise it cannot compile. -# +# OPTIONS_SRC_FILES = \ arith_options \ @@ -30,6 +30,7 @@ OPTIONS_SRC_FILES = \ proof_options \ prop_options \ quantifiers_options \ + sep_options \ sets_options \ smt_options \ strings_options \ @@ -54,6 +55,7 @@ OPTIONS_TEMPS = \ proof_options.tmp \ prop_options.tmp \ quantifiers_options.tmp \ + sep_options.tmp \ sets_options.tmp \ smt_options.tmp \ strings_options.tmp \ @@ -78,6 +80,7 @@ OPTIONS_OPTIONS_FILES = \ proof_options.options \ prop_options.options \ quantifiers_options.options \ + sep_options.options \ sets_options.options \ smt_options.options \ strings_options.options \ @@ -102,6 +105,7 @@ OPTIONS_SEDS = \ proof_options.sed \ prop_options.sed \ quantifiers_options.sed \ + sep_options.sed \ sets_options.sed \ smt_options.sed \ strings_options.sed \ @@ -126,6 +130,7 @@ OPTIONS_HEADS = \ proof_options.h \ prop_options.h \ quantifiers_options.h \ + sep_options.h \ sets_options.h \ smt_options.h \ strings_options.h \ @@ -150,6 +155,7 @@ OPTIONS_CPPS = \ proof_options.cpp \ prop_options.cpp \ quantifiers_options.cpp \ + sep_options.cpp \ sets_options.cpp \ smt_options.cpp \ strings_options.cpp \ @@ -295,14 +301,14 @@ options_holder_template.h options_template.cpp options_get_option_template.cpp o # Make sure the implicit rules never mistake X_options for the -o file for a # CPP file. -arith_options arrays_options base_options booleans_options builtin_options bv_options datatypes_options decision_options expr_options fp_options idl_options main_options parser_options printer_options proof_options prop_options quantifiers_options sets_options smt_options strings_options theory_options uf_options:; +arith_options arrays_options base_options booleans_options builtin_options bv_options datatypes_options decision_options expr_options fp_options idl_options main_options parser_options printer_options proof_options prop_options quantifiers_options sep_options sets_options smt_options strings_options theory_options uf_options:; # These are phony to force them to be made everytime. -.PHONY: arith_options.tmp arrays_options.tmp base_options.tmp booleans_options.tmp builtin_options.tmp bv_options.tmp datatypes_options.tmp decision_options.tmp expr_options.tmp fp_options.tmp idl_options.tmp main_options.tmp parser_options.tmp printer_options.tmp proof_options.tmp prop_options.tmp quantifiers_options.tmp sets_options.tmp smt_options.tmp strings_options.tmp theory_options.tmp uf_options.tmp +.PHONY: arith_options.tmp arrays_options.tmp base_options.tmp booleans_options.tmp builtin_options.tmp bv_options.tmp datatypes_options.tmp decision_options.tmp expr_options.tmp fp_options.tmp idl_options.tmp main_options.tmp parser_options.tmp printer_options.tmp proof_options.tmp prop_options.tmp quantifiers_options.tmp sep_options.tmp sets_options.tmp smt_options.tmp strings_options.tmp theory_options.tmp uf_options.tmp # Make is happier being listed explictly. Not sure why. -arith_options.tmp arrays_options.tmp base_options.tmp booleans_options.tmp builtin_options.tmp bv_options.tmp datatypes_options.tmp decision_options.tmp expr_options.tmp fp_options.tmp idl_options.tmp main_options.tmp parser_options.tmp printer_options.tmp proof_options.tmp prop_options.tmp quantifiers_options.tmp sets_options.tmp smt_options.tmp strings_options.tmp theory_options.tmp uf_options.tmp: +arith_options.tmp arrays_options.tmp base_options.tmp booleans_options.tmp builtin_options.tmp bv_options.tmp datatypes_options.tmp decision_options.tmp expr_options.tmp fp_options.tmp idl_options.tmp main_options.tmp parser_options.tmp printer_options.tmp proof_options.tmp prop_options.tmp quantifiers_options.tmp sep_options.tmp sets_options.tmp smt_options.tmp strings_options.tmp theory_options.tmp uf_options.tmp: echo "$@" "$(@:.tmp=)" $(AM_V_GEN)(cp "@srcdir@/$(@:.tmp=)" "$@" || true) #TIM: @@ -424,4 +430,3 @@ $(DOCUMENTATION_FILES) : % : %_template %_template.sed mkoptions summary.sed # directories that are cleaned first. Without this rule, "distclean" # fails. %.Plo:; $(MKDIR_P) "$(dir $@)" && : > "$@" - diff --git a/src/options/bv_bitblast_mode.cpp b/src/options/bv_bitblast_mode.cpp index 9cf47fe33..f331345f7 100644 --- a/src/options/bv_bitblast_mode.cpp +++ b/src/options/bv_bitblast_mode.cpp @@ -53,4 +53,19 @@ std::ostream& operator<<(std::ostream& out, theory::bv::BvSlicerMode mode) { return out; } +std::ostream& operator<<(std::ostream& out, theory::bv::SatSolverMode solver) { + switch(solver) { + case theory::bv::SAT_SOLVER_MINISAT: + out << "SAT_SOLVER_MINISAT"; + break; + case theory::bv::SAT_SOLVER_CRYPTOMINISAT: + out << "SAT_SOLVER_CRYPTOMINISAT"; + break; + default: + out << "SatSolverMode:UNKNOWN![" << unsigned(solver) << "]"; + } + + return out; +} + }/* CVC4 namespace */ diff --git a/src/options/bv_bitblast_mode.h b/src/options/bv_bitblast_mode.h index 4c8c4f626..3a6474104 100644 --- a/src/options/bv_bitblast_mode.h +++ b/src/options/bv_bitblast_mode.h @@ -60,12 +60,19 @@ enum BvSlicerMode { };/* enum BvSlicerMode */ +/** Enumeration of sat solvers that can be used. */ +enum SatSolverMode { + SAT_SOLVER_MINISAT, + SAT_SOLVER_CRYPTOMINISAT, +};/* enum SatSolver */ + }/* CVC4::theory::bv namespace */ }/* CVC4::theory namespace */ std::ostream& operator<<(std::ostream& out, theory::bv::BitblastMode mode); std::ostream& operator<<(std::ostream& out, theory::bv::BvSlicerMode mode); +std::ostream& operator<<(std::ostream& out, theory::bv::SatSolverMode mode); }/* CVC4 namespace */ diff --git a/src/options/bv_options b/src/options/bv_options index 8edc809e3..2e6fa2e7a 100644 --- a/src/options/bv_options +++ b/src/options/bv_options @@ -7,6 +7,9 @@ module BV "options/bv_options.h" Bitvector theory # Option to set the bit-blasting mode (lazy, eager) +expert-option bvSatSolver bv-sat-solver --bv-sat-solver=MODE CVC4::theory::bv::SatSolverMode :predicate satSolverEnabledBuild :handler stringToSatSolver :default CVC4::theory::bv::SAT_SOLVER_MINISAT :read-write :include "options/bv_bitblast_mode.h" + choose which sat solver to use, see --bv-sat-solver=help + option bitblastMode bitblast --bitblast=MODE CVC4::theory::bv::BitblastMode :handler stringToBitblastMode :default CVC4::theory::bv::BITBLAST_MODE_LAZY :read-write :include "options/bv_bitblast_mode.h" choose bitblasting mode, see --bitblast=help diff --git a/src/options/datatypes_options b/src/options/datatypes_options index e9578f8d7..bb92b4e05 100644 --- a/src/options/datatypes_options +++ b/src/options/datatypes_options @@ -27,5 +27,7 @@ option dtInferAsLemmas --dt-infer-as-lemmas bool :default false always send lemmas out instead of making internal inferences #option dtRExplainLemmas --dt-rexplain-lemmas bool :default true # regression explanations for datatype lemmas +option dtBlastSplits --dt-blast-splits bool :default false + when applicable, blast splitting lemmas for all variables at once endmodule diff --git a/src/options/options_handler.cpp b/src/options/options_handler.cpp index a2809bd67..6a5f6cd39 100644 --- a/src/options/options_handler.cpp +++ b/src/options/options_handler.cpp @@ -254,15 +254,21 @@ last-call\n\ const std::string OptionsHandler::s_literalMatchHelp = "\ Literal match modes currently supported by the --literal-match option:\n\ \n\ -none (default)\n\ +none \n\ + Do not use literal matching.\n\ \n\ -predicate\n\ -+ Consider the phase requirements of predicate literals when applying heuristic\n\ - quantifier instantiation. For example, the trigger P( x ) in the quantified \n\ - formula forall( x ). ( P( x ) V ~Q( x ) ) will only be matched with ground\n\ - terms P( t ) where P( t ) is in the equivalence class of false, and likewise\n\ - Q( x ) with Q( s ) where Q( s ) is in the equivalence class of true.\n\ +use (default)\n\ ++ Consider phase requirements of triggers conservatively. For example, the\n\ + trigger P( x ) in forall( x ). ( P( x ) V ~Q( x ) ) will not be matched with\n\ + terms in the equivalence class of true, and likewise Q( x ) will not be matched\n\ + terms in the equivalence class of false. Extends to equality.\n\ +\n\ +agg-predicate \n\ ++ Consider phase requirements aggressively for predicates. In the above example,\n\ + only match P( x ) with terms that are in the equivalence class of false.\n\ +\n\ +agg \n\ ++ Consider the phase requirements aggressively for all triggers.\n\ \n\ "; @@ -384,7 +390,7 @@ uf-dt-size \n\ + Enforce fairness using an uninterpreted function for datatypes size.\n\ \n\ default | dt-size \n\ -+ Default, enforce fairness using size theory operator.\n\ ++ Default, enforce fairness using size operator.\n\ \n\ dt-height-bound \n\ + Enforce fairness by height bound predicate.\n\ @@ -419,6 +425,24 @@ all \n\ \n\ "; +const std::string OptionsHandler::s_cegqiSingleInvHelp = "\ +Modes for single invocation techniques, supported by --cegqi-si:\n\ +\n\ +none \n\ ++ Do not use single invocation techniques.\n\ +\n\ +use (default) \n\ ++ Use single invocation techniques only if grammar is not restrictive.\n\ +\n\ +all-abort \n\ ++ Always use single invocation techniques, abort if solution reconstruction will likely fail,\ + for instance, when the grammar does not have ITE and solution requires it.\n\ +\n\ +all \n\ ++ Always use single invocation techniques. \n\ +\n\ +"; + const std::string OptionsHandler::s_sygusInvTemplHelp = "\ Template modes for sygus invariant synthesis, supported by --sygus-inv-templ:\n\ \n\ @@ -506,10 +530,12 @@ void OptionsHandler::checkInstWhenMode(std::string option, theory::quantifiers:: theory::quantifiers::LiteralMatchMode OptionsHandler::stringToLiteralMatchMode(std::string option, std::string optarg) throw(OptionException) { if(optarg == "none") { return theory::quantifiers::LITERAL_MATCH_NONE; - } else if(optarg == "predicate") { - return theory::quantifiers::LITERAL_MATCH_PREDICATE; - } else if(optarg == "equality") { - return theory::quantifiers::LITERAL_MATCH_EQUALITY; + } else if(optarg == "use") { + return theory::quantifiers::LITERAL_MATCH_USE; + } else if(optarg == "agg-predicate") { + return theory::quantifiers::LITERAL_MATCH_AGG_PREDICATE; + } else if(optarg == "agg") { + return theory::quantifiers::LITERAL_MATCH_AGG; } else if(optarg == "help") { puts(s_literalMatchHelp.c_str()); exit(1); @@ -520,9 +546,7 @@ theory::quantifiers::LiteralMatchMode OptionsHandler::stringToLiteralMatchMode(s } void OptionsHandler::checkLiteralMatchMode(std::string option, theory::quantifiers::LiteralMatchMode mode) throw(OptionException) { - if(mode == theory::quantifiers::LITERAL_MATCH_EQUALITY) { - throw OptionException(std::string("Mode equality for ") + option + " is not supported in this release."); - } + } theory::quantifiers::MbqiMode OptionsHandler::stringToMbqiMode(std::string option, std::string optarg) throw(OptionException) { @@ -651,6 +675,8 @@ theory::quantifiers::CegqiFairMode OptionsHandler::stringToCegqiFairMode(std::st return theory::quantifiers::CEGQI_FAIR_DT_SIZE; } else if(optarg == "dt-height-bound" ){ return theory::quantifiers::CEGQI_FAIR_DT_HEIGHT_PRED; + //} else if(optarg == "dt-size-bound" ){ + // return theory::quantifiers::CEGQI_FAIR_DT_SIZE_PRED; } else if(optarg == "none") { return theory::quantifiers::CEGQI_FAIR_NONE; } else if(optarg == "help") { @@ -692,6 +718,24 @@ theory::quantifiers::IteLiftQuantMode OptionsHandler::stringToIteLiftQuantMode(s } } +theory::quantifiers::CegqiSingleInvMode OptionsHandler::stringToCegqiSingleInvMode(std::string option, std::string optarg) throw(OptionException) { + if(optarg == "none" ) { + return theory::quantifiers::CEGQI_SI_MODE_NONE; + } else if(optarg == "use" || optarg == "default") { + return theory::quantifiers::CEGQI_SI_MODE_USE; + } else if(optarg == "all-abort") { + return theory::quantifiers::CEGQI_SI_MODE_ALL_ABORT; + } else if(optarg == "all") { + return theory::quantifiers::CEGQI_SI_MODE_ALL; + } else if(optarg == "help") { + puts(s_cegqiSingleInvHelp.c_str()); + exit(1); + } else { + throw OptionException(std::string("unknown option for --cegqi-si: `") + + optarg + "'. Try --cegqi-si help."); + } +} + theory::quantifiers::SygusInvTemplMode OptionsHandler::stringToSygusInvTemplMode(std::string option, std::string optarg) throw(OptionException) { if(optarg == "none" ) { return theory::quantifiers::SYGUS_INV_TEMPL_MODE_NONE; @@ -777,6 +821,72 @@ void OptionsHandler::abcEnabledBuild(std::string option, std::string value) thro #endif /* CVC4_USE_ABC */ } +void OptionsHandler::satSolverEnabledBuild(std::string option, + bool value) throw(OptionException) { +#ifndef CVC4_USE_CRYPTOMINISAT + if(value) { + std::stringstream ss; + ss << "option `" << option << "' requires an cryptominisat-enabled build of CVC4; this binary was not built with cryptominisat support"; + throw OptionException(ss.str()); + } +#endif /* CVC4_USE_CRYPTOMINISAT */ +} + +void OptionsHandler::satSolverEnabledBuild(std::string option, + std::string value) throw(OptionException) { +#ifndef CVC4_USE_CRYPTOMINISAT + if(!value.empty()) { + std::stringstream ss; + ss << "option `" << option << "' requires an cryptominisat-enabled build of CVC4; this binary was not built with cryptominisat support"; + throw OptionException(ss.str()); + } +#endif /* CVC4_USE_CRYPTOMINISAT */ +} + +const std::string OptionsHandler::s_bvSatSolverHelp = "\ +Sat solvers currently supported by the --bv-sat-solver option:\n\ +\n\ +minisat (default)\n\ +\n\ +cryptominisat\n\ +"; + +theory::bv::SatSolverMode OptionsHandler::stringToSatSolver(std::string option, + std::string optarg) throw(OptionException) { + if(optarg == "minisat") { + return theory::bv::SAT_SOLVER_MINISAT; + } else if(optarg == "cryptominisat") { + + if (options::incrementalSolving() && + options::incrementalSolving.wasSetByUser()) { + throw OptionException(std::string("Cryptominsat does not support incremental mode. \n\ + Try --bv-sat-solver=minisat")); + } + + if (options::bitblastMode() == theory::bv::BITBLAST_MODE_LAZY && + options::bitblastMode.wasSetByUser()) { + throw OptionException(std::string("Cryptominsat does not support lazy bit-blsating. \n\ + Try --bv-sat-solver=minisat")); + } + if (!options::bitvectorToBool.wasSetByUser()) { + options::bitvectorToBool.set(true); + } + + // if (!options::bvAbstraction.wasSetByUser() && + // !options::skolemizeArguments.wasSetByUser()) { + // options::bvAbstraction.set(true); + // options::skolemizeArguments.set(true); + // } + return theory::bv::SAT_SOLVER_CRYPTOMINISAT; + } else if(optarg == "help") { + puts(s_bvSatSolverHelp.c_str()); + exit(1); + } else { + throw OptionException(std::string("unknown option for --bv-sat-solver: `") + + optarg + "'. Try --bv-sat-solver=help."); + } +} + const std::string OptionsHandler::s_bitblastingModeHelp = "\ Bit-blasting modes currently supported by the --bitblast option:\n\ \n\ diff --git a/src/options/options_handler.h b/src/options/options_handler.h index baa6cea96..5db2887c0 100644 --- a/src/options/options_handler.h +++ b/src/options/options_handler.h @@ -98,6 +98,7 @@ public: theory::quantifiers::CegqiFairMode stringToCegqiFairMode(std::string option, std::string optarg) throw(OptionException); theory::quantifiers::TermDbMode stringToTermDbMode(std::string option, std::string optarg) throw(OptionException); theory::quantifiers::IteLiftQuantMode stringToIteLiftQuantMode(std::string option, std::string optarg) throw(OptionException); + theory::quantifiers::CegqiSingleInvMode stringToCegqiSingleInvMode(std::string option, std::string optarg) throw(OptionException); theory::quantifiers::SygusInvTemplMode stringToSygusInvTemplMode(std::string option, std::string optarg) throw(OptionException); theory::quantifiers::MacrosQuantMode stringToMacrosQuantMode(std::string option, std::string optarg) throw(OptionException); theory::quantifiers::QuantDSplitMode stringToQuantDSplitMode(std::string option, std::string optarg) throw(OptionException); @@ -106,10 +107,15 @@ public: // theory/bv/options_handlers.h void abcEnabledBuild(std::string option, bool value) throw(OptionException); void abcEnabledBuild(std::string option, std::string value) throw(OptionException); + void satSolverEnabledBuild(std::string option, bool value) throw(OptionException); + void satSolverEnabledBuild(std::string option, std::string optarg) throw(OptionException); + theory::bv::BitblastMode stringToBitblastMode(std::string option, std::string optarg) throw(OptionException); theory::bv::BvSlicerMode stringToBvSlicerMode(std::string option, std::string optarg) throw(OptionException); void setBitblastAig(std::string option, bool arg) throw(OptionException); + theory::bv::SatSolverMode stringToSatSolver(std::string option, std::string optarg) throw(OptionException); + // theory/booleans/options_handlers.h theory::booleans::BooleanTermConversionMode stringToBooleanTermConversionMode(std::string option, std::string optarg) throw(OptionException); @@ -192,6 +198,7 @@ public: /* Help strings */ static const std::string s_bitblastingModeHelp; + static const std::string s_bvSatSolverHelp; static const std::string s_booleanTermConversionModeHelp; static const std::string s_bvSlicerModeHelp; static const std::string s_cegqiFairModeHelp; @@ -209,6 +216,7 @@ public: static const std::string s_qcfModeHelp; static const std::string s_qcfWhenModeHelp; static const std::string s_simplificationHelp; + static const std::string s_cegqiSingleInvHelp; static const std::string s_sygusInvTemplHelp; static const std::string s_termDbModeHelp; static const std::string s_theoryOfModeHelp; diff --git a/src/options/options_template.cpp b/src/options/options_template.cpp index f029dfd17..694d46d31 100644 --- a/src/options/options_template.cpp +++ b/src/options/options_template.cpp @@ -720,7 +720,7 @@ void Options::parseOptionsRecursive(Options* options, switch(c) { ${all_modules_option_handlers} -#line 722 "${template}" +#line 724 "${template}" case ':': // This can be a long or short option, and the way to get at the @@ -798,7 +798,7 @@ std::string Options::suggestCommandLineOptions(const std::string& optionName) th static const char* smtOptions[] = { ${all_modules_smt_options}, -#line 800 "${template}" +#line 802 "${template}" NULL };/* smtOptions[] */ @@ -820,7 +820,7 @@ std::vector< std::vector<std::string> > Options::getOptions() const throw() { ${all_modules_get_options} -#line 762 "${template}" +#line 824 "${template}" return opts; } diff --git a/src/options/proof_options b/src/options/proof_options index 7feb00b0d..a99d858bc 100644 --- a/src/options/proof_options +++ b/src/options/proof_options @@ -5,4 +5,7 @@ module PROOF "options/proof_options.h" Proof +option lfscLetification --lfsc-letification bool :default true + turns on global letification in LFSC proofs + endmodule diff --git a/src/options/quantifiers_modes.cpp b/src/options/quantifiers_modes.cpp index a58120974..e2cd78de5 100644 --- a/src/options/quantifiers_modes.cpp +++ b/src/options/quantifiers_modes.cpp @@ -46,11 +46,14 @@ std::ostream& operator<<(std::ostream& out, theory::quantifiers::LiteralMatchMod case theory::quantifiers::LITERAL_MATCH_NONE: out << "LITERAL_MATCH_NONE"; break; - case theory::quantifiers::LITERAL_MATCH_PREDICATE: - out << "LITERAL_MATCH_PREDICATE"; + case theory::quantifiers::LITERAL_MATCH_USE: + out << "LITERAL_MATCH_USE"; break; - case theory::quantifiers::LITERAL_MATCH_EQUALITY: - out << "LITERAL_MATCH_EQUALITY"; + case theory::quantifiers::LITERAL_MATCH_AGG_PREDICATE: + out << "LITERAL_MATCH_AGG_PREDICATE"; + break; + case theory::quantifiers::LITERAL_MATCH_AGG: + out << "LITERAL_MATCH_AGG"; break; default: out << "LiteralMatchMode!UNKNOWN"; diff --git a/src/options/quantifiers_modes.h b/src/options/quantifiers_modes.h index 5749da972..65445be17 100644 --- a/src/options/quantifiers_modes.h +++ b/src/options/quantifiers_modes.h @@ -44,10 +44,12 @@ enum InstWhenMode { enum LiteralMatchMode { /** Do not consider polarity of patterns */ LITERAL_MATCH_NONE, - /** Consider polarity of boolean predicates only */ - LITERAL_MATCH_PREDICATE, - /** Consider polarity of boolean predicates, as well as equalities */ - LITERAL_MATCH_EQUALITY, + /** Conservatively consider polarity of patterns */ + LITERAL_MATCH_USE, + /** Aggressively consider polarity of Boolean predicates */ + LITERAL_MATCH_AGG_PREDICATE, + /** Aggressively consider polarity of all terms */ + LITERAL_MATCH_AGG, }; enum MbqiMode { @@ -129,6 +131,8 @@ enum CegqiFairMode { CEGQI_FAIR_DT_SIZE, /** enforce fairness by datatypes height bound */ CEGQI_FAIR_DT_HEIGHT_PRED, + /** enforce fairness by datatypes size bound */ + CEGQI_FAIR_DT_SIZE_PRED, /** do not use fair strategy for CEGQI */ CEGQI_FAIR_NONE, }; @@ -149,6 +153,17 @@ enum IteLiftQuantMode { ITE_LIFT_QUANT_MODE_ALL, }; +enum CegqiSingleInvMode { + /** do not use single invocation techniques */ + CEGQI_SI_MODE_NONE, + /** use single invocation techniques */ + CEGQI_SI_MODE_USE, + /** always use single invocation techniques, abort if solution reconstruction will fail */ + CEGQI_SI_MODE_ALL_ABORT, + /** always use single invocation techniques */ + CEGQI_SI_MODE_ALL, +}; + enum SygusInvTemplMode { /** synthesize I( x ) */ SYGUS_INV_TEMPL_MODE_NONE, diff --git a/src/options/quantifiers_options b/src/options/quantifiers_options index 74b3011a6..4d228bbad 100644 --- a/src/options/quantifiers_options +++ b/src/options/quantifiers_options @@ -54,6 +54,8 @@ option purifyQuant --purify-quant bool :default false purify quantified formulas option elimExtArithQuant --elim-ext-arith-quant bool :default true eliminate extended arithmetic symbols in quantified formulas +option condRewriteQuant --cond-rewrite-quant bool :default true + conditional rewriting of quantified formulas #### E-matching options @@ -109,6 +111,8 @@ option instLevelInputOnly --inst-level-input-only bool :default true only input terms are assigned instantiation level zero option quantRepMode --quant-rep-mode=MODE CVC4::theory::quantifiers::QuantRepMode :default CVC4::theory::quantifiers::QUANT_REP_MODE_FIRST :read-write :include "options/quantifiers_modes.h" :handler stringToQuantRepMode selection mode for representatives in quantifiers engine +option instRelevantCond --inst-rlv-cond bool :default false + add relevancy conditions for instantiations option eagerInstQuant --eager-inst-quant bool :default false apply quantifier instantiation eagerly @@ -120,7 +124,7 @@ option fullSaturateQuantRd --full-saturate-quant-rd bool :default true option fullSaturateInst --fs-inst bool :default false interleave full saturate instantiation with other techniques -option literalMatchMode --literal-matching=MODE CVC4::theory::quantifiers::LiteralMatchMode :default CVC4::theory::quantifiers::LITERAL_MATCH_NONE :include "options/quantifiers_modes.h" :handler stringToLiteralMatchMode :predicate checkLiteralMatchMode +option literalMatchMode --literal-matching=MODE CVC4::theory::quantifiers::LiteralMatchMode :default CVC4::theory::quantifiers::LITERAL_MATCH_USE :include "options/quantifiers_modes.h" :handler stringToLiteralMatchMode :predicate checkLiteralMatchMode choose literal matching mode ### finite model finding options @@ -144,7 +148,7 @@ option fmfOneInstPerRound --mbqi-one-inst-per-round bool :read-write :default fa option fmfOneQuantPerRound --mbqi-one-quant-per-round bool :default false only add instantiations for one quantifier per round for mbqi -option fmfInstEngine --fmf-inst-engine bool :default false +option fmfInstEngine --fmf-inst-engine bool :default false :read-write use instantiation engine in conjunction with finite model finding option fmfInstGen --fmf-inst-gen bool :default true enable Inst-Gen instantiation techniques for finite model finding @@ -171,13 +175,23 @@ option qcfTConstraint --qcf-tconstraint bool :read-write :default false enable entailment checks for t-constraints in qcf algorithm option qcfAllConflict --qcf-all-conflict bool :read-write :default false add all available conflicting instances during conflict-based instantiation +option qcfNestedConflict --qcf-nested-conflict bool :default false + consider conflicts for nested quantifiers +option qcfVoExp --qcf-vo-exp bool :default false + qcf experimental variable ordering + + option instNoEntail --inst-no-entail bool :read-write :default true do not consider instances of quantified formulas that are currently entailed -option instPropagate --inst-propagate bool :read-write :default false +option instPropagate --inst-prop bool :read-write :default false internal propagation for instantiations for selecting relevant instances +option qcfEagerTest --qcf-eager-test bool :default true + optimization, test qcf instances eagerly +option qcfSkipRd --qcf-skip-rd bool :default false + optimization, skip instances based on possibly irrelevant portions of quantified formulas ### rewrite rules options option quantRewriteRules --rewrite-rules bool :default false @@ -219,8 +233,8 @@ option ceGuidedInst --cegqi bool :default false :read-write counterexample-guided quantifier instantiation option ceGuidedInstFair --cegqi-fair=MODE CVC4::theory::quantifiers::CegqiFairMode :default CVC4::theory::quantifiers::CEGQI_FAIR_DT_SIZE :include "options/quantifiers_modes.h" :handler stringToCegqiFairMode if and how to apply fairness for cegqi -option cegqiSingleInv --cegqi-si bool :default false :read-write - process single invocation synthesis conjectures +option cegqiSingleInvMode --cegqi-si=MODE CVC4::theory::quantifiers::CegqiSingleInvMode :default CVC4::theory::quantifiers::CEGQI_SI_MODE_NONE :include "options/quantifiers_modes.h" :handler stringToCegqiSingleInvMode :read-write + mode for processing single invocation synthesis conjectures option cegqiSingleInvPartial --cegqi-si-partial bool :default false combined techniques for synthesis conjectures that are partially single invocation option cegqiSingleInvReconstruct --cegqi-si-reconstruct bool :default true @@ -229,8 +243,6 @@ option cegqiSingleInvReconstructConst --cegqi-si-reconstruct-const bool :default include constants when reconstruct solutions for single invocation conjectures in original grammar option cegqiSingleInvAbort --cegqi-si-abort bool :default false abort if synthesis conjecture is not single invocation -option cegqiSingleInvMultiInstAbort --cegqi-si-multi-inst-abort bool :default false - abort if synthesis conjecture is single invocation with no ITE in grammar and multiple instantiations are tried option sygusNormalForm --sygus-nf bool :default true only search for sygus builtin terms that are in normal form @@ -248,6 +260,9 @@ option sygusNormalFormGlobalContent --sygus-nf-sym-content bool :default true option sygusInvTemplMode --sygus-inv-templ=MODE CVC4::theory::quantifiers::SygusInvTemplMode :default CVC4::theory::quantifiers::SYGUS_INV_TEMPL_MODE_NONE :include "options/quantifiers_modes.h" :handler stringToSygusInvTemplMode template mode for sygus invariant synthesis +option sygusDirectEval --sygus-direct-eval bool :default true + direct unfolding of evaluation functions + # approach applied to general quantified formulas option cbqiSplx --cbqi-splx bool :read-write :default false turns on old implementation of counterexample-based quantifier instantiation diff --git a/src/options/sep_options b/src/options/sep_options new file mode 100644 index 000000000..043355bda --- /dev/null +++ b/src/options/sep_options @@ -0,0 +1,20 @@ +# +# Option specification file for CVC4 +# See src/options/base_options for a description of this file format +# + +module SEP "options/sep_options.h" Sep + +option sepCheckNeg --sep-check-neg bool :default true + check negated spatial assertions + +option sepExp --sep-exp bool :default false + experimental flag for sep +option sepMinimalRefine --sep-min-refine bool :default false + only add refinement lemmas for minimal (innermost) assertions +option sepPreciseBound --sep-prec-bound bool :default false + calculate precise bounds for labels +option sepDisequalC --sep-deq-c bool :default true + assume cardinality elements are distinct + +endmodule diff --git a/src/parser/cvc/Cvc.g b/src/parser/cvc/Cvc.g index c497fcd0d..e6d7f9d86 100644 --- a/src/parser/cvc/Cvc.g +++ b/src/parser/cvc/Cvc.g @@ -931,7 +931,10 @@ toplevelDeclaration[CVC4::Command*& cmd] * A bound variable declaration. */ boundVarDecl[std::vector<std::string>& ids, CVC4::Type& t] - : identifierList[ids,CHECK_NONE,SYM_VARIABLE] COLON declareVariables[*(Command**)NULL,t,ids,false] +@init { + Command* local_cmd = NULL; +} + : identifierList[ids,CHECK_NONE,SYM_VARIABLE] COLON declareVariables[local_cmd,t,ids,false] ; /** diff --git a/src/parser/smt2/Smt2.g b/src/parser/smt2/Smt2.g index 38163c579..29d3e45b6 100644 --- a/src/parser/smt2/Smt2.g +++ b/src/parser/smt2/Smt2.g @@ -749,7 +749,7 @@ sygusCommand returns [CVC4::Command* cmd = NULL] $cmd = new EmptyCommand(); } | /* check-synth */ - CHECK_SYNTH_TOK { PARSER_STATE->checkThatLogicIsSet(); } + CHECK_SYNTH_TOK { PARSER_STATE->checkThatLogicIsSet();PARSER_STATE->defineSygusFuns(); } { Expr sygusVar = EXPR_MANAGER->mkVar("sygus", EXPR_MANAGER->booleanType()); Expr sygusAttr = EXPR_MANAGER->mkExpr(kind::INST_PATTERN_LIST, EXPR_MANAGER->mkExpr(kind::INST_ATTRIBUTE, sygusVar)); std::vector<Expr> bodyv; @@ -760,7 +760,9 @@ sygusCommand returns [CVC4::Command* cmd = NULL] body = EXPR_MANAGER->mkExpr(kind::EXISTS, EXPR_MANAGER->mkExpr(kind::BOUND_VAR_LIST, PARSER_STATE->getSygusVars()), body); Debug("parser-sygus") << "...constructed exists " << body << std::endl; } - body = EXPR_MANAGER->mkExpr(kind::FORALL, EXPR_MANAGER->mkExpr(kind::BOUND_VAR_LIST, PARSER_STATE->getSygusFunSymbols()), body, sygusAttr); + if( !PARSER_STATE->getSygusFunSymbols().empty() ){ + body = EXPR_MANAGER->mkExpr(kind::FORALL, EXPR_MANAGER->mkExpr(kind::BOUND_VAR_LIST, PARSER_STATE->getSygusFunSymbols()), body, sygusAttr); + } Debug("parser-sygus") << "...constructed forall " << body << std::endl; Command* c = new SetUserAttributeCommand("sygus", sygusVar); c->setMuted(true); @@ -791,6 +793,7 @@ sygusGTerm[CVC4::SygusGTerm& sgt, std::string& fun] std::string sname; std::vector< Expr > let_vars; bool readingLet = false; + std::string s; } : LPAREN_TOK //read operator @@ -799,6 +802,8 @@ sygusGTerm[CVC4::SygusGTerm& sgt, std::string& fun] //since we enforce satisfaction completeness, immediately convert to total version if( k==CVC4::kind::BITVECTOR_UDIV ){ k = CVC4::kind::BITVECTOR_UDIV_TOTAL; + }else if( k==CVC4::kind::BITVECTOR_UREM ){ + k = CVC4::kind::BITVECTOR_UREM_TOTAL; } sgt.d_name = kind::kindToString(k); sgt.d_gterm_type = SygusGTerm::gterm_op; @@ -834,6 +839,8 @@ sygusGTerm[CVC4::SygusGTerm& sgt, std::string& fun] k = PARSER_STATE->getOperatorKind(name); if( k==CVC4::kind::BITVECTOR_UDIV ){ k = CVC4::kind::BITVECTOR_UDIV_TOTAL; + }else if( k==CVC4::kind::BITVECTOR_UREM ){ + k = CVC4::kind::BITVECTOR_UREM_TOTAL; } sgt.d_name = kind::kindToString(k); sgt.d_gterm_type = SygusGTerm::gterm_op; @@ -890,6 +897,12 @@ sygusGTerm[CVC4::SygusGTerm& sgt, std::string& fun] sgt.d_name = AntlrInput::tokenText($BINARY_LITERAL); sgt.d_gterm_type = SygusGTerm::gterm_op; } + | str[s,false] + { Debug("parser-sygus") << "Sygus grammar " << fun << " : string literal \"" << s << "\"" << std::endl; + sgt.d_expr = MK_CONST( ::CVC4::String(s) ); + sgt.d_name = s; + sgt.d_gterm_type = SygusGTerm::gterm_op; + } | symbol[name,CHECK_NONE,SYM_VARIABLE] ( SYGUS_ENUM_CONS_TOK symbol[name2,CHECK_NONE,SYM_VARIABLE] { readEnum = true; } )? { if( readEnum ){ name = name + "__Enum__" + name2; @@ -1594,6 +1607,10 @@ term[CVC4::Expr& expr, CVC4::Expr& expr2] Debug("parser") << "Empty set encountered: " << f << " " << f2 << " " << type << std::endl; expr = MK_CONST( ::CVC4::EmptySet(type) ); + } else if(f.getKind() == CVC4::kind::SEP_NIL_REF) { + //We don't want the nil reference to be a constant: for instance, it could be of type Int but is not a const rational. + //However, the expression has 0 children. So we convert to a SEP_NIL variable. + expr = EXPR_MANAGER->mkSepNil(type); } else { if(f.getType() != type) { PARSER_STATE->parseError("Type ascription not satisfied."); @@ -1898,6 +1915,8 @@ term[CVC4::Expr& expr, CVC4::Expr& expr2] | EMPTYSET_TOK { expr = MK_CONST( ::CVC4::EmptySet(Type())); } + | NILREF_TOK + { expr = MK_CONST( ::CVC4::NilRef(Type())); } // NOTE: Theory constants go here ; @@ -2633,6 +2652,7 @@ FMFCARDVAL_TOK : 'fmf.card.val'; INST_CLOSURE_TOK : 'inst-closure'; EMPTYSET_TOK: { PARSER_STATE->isTheoryEnabled(Smt2::THEORY_SETS) }? 'emptyset'; +NILREF_TOK: { PARSER_STATE->isTheoryEnabled(Smt2::THEORY_SEP) }? 'sep.nil'; // Other set theory operators are not // tokenized and handled directly when // processing a term @@ -2773,7 +2793,7 @@ STRING_LITERAL_2_0 * will be part of the token text. Use the str[] parser rule instead. */ STRING_LITERAL_2_5 - : { PARSER_STATE->v2_5() }?=> + : { PARSER_STATE->v2_5() || PARSER_STATE->sygus() }?=> '"' (~('"') | '""')* '"' ; diff --git a/src/parser/smt2/smt2.cpp b/src/parser/smt2/smt2.cpp index 463adcb54..9f3c43f09 100644 --- a/src/parser/smt2/smt2.cpp +++ b/src/parser/smt2/smt2.cpp @@ -166,6 +166,16 @@ void Smt2::addFloatingPointOperators() { Parser::addOperator(kind::FLOATINGPOINT_TO_SBV); } +void Smt2::addSepOperators() { + addOperator(kind::SEP_STAR, "sep"); + addOperator(kind::SEP_PTO, "pto"); + addOperator(kind::SEP_WAND, "wand"); + addOperator(kind::SEP_EMP, "emp"); + Parser::addOperator(kind::SEP_STAR); + Parser::addOperator(kind::SEP_PTO); + Parser::addOperator(kind::SEP_WAND); + Parser::addOperator(kind::SEP_EMP); +} void Smt2::addTheory(Theory theory) { switch(theory) { @@ -258,7 +268,11 @@ void Smt2::addTheory(Theory theory) { defineType("Float128", getExprManager()->mkFloatingPointType(15, 113)); addFloatingPointOperators(); break; - + + case THEORY_SEP: + addSepOperators(); + break; + default: std::stringstream ss; ss << "internal error: unsupported theory " << theory; @@ -311,6 +325,8 @@ bool Smt2::isTheoryEnabled(Theory theory) const { return d_logic.isTheoryEnabled(theory::THEORY_UF); case THEORY_FP: return d_logic.isTheoryEnabled(theory::THEORY_FP); + case THEORY_SEP: + return d_logic.isTheoryEnabled(theory::THEORY_SEP); default: std::stringstream ss; ss << "internal error: unsupported theory " << theory; @@ -354,6 +370,8 @@ void Smt2::setLogic(std::string name) { name = "UFLIRA"; } else if(name == "BV") { name = "UFBV"; + } else if(name == "SLIA") { + name = "UFSLIA"; } else if(name == "ALL_SUPPORTED") { //no change } else { @@ -417,6 +435,10 @@ void Smt2::setLogic(std::string name) { addTheory(THEORY_FP); } + if (d_logic.isTheoryEnabled(theory::THEORY_SEP)) { + addTheory(THEORY_SEP); + } + }/* Smt2::setLogic() */ void Smt2::setInfo(const std::string& flag, const SExpr& sexpr) { diff --git a/src/parser/smt2/smt2.h b/src/parser/smt2/smt2.h index 1ae2c9dd7..b99e142ba 100644 --- a/src/parser/smt2/smt2.h +++ b/src/parser/smt2/smt2.h @@ -51,7 +51,8 @@ public: THEORY_SETS, THEORY_STRINGS, THEORY_UF, - THEORY_FP + THEORY_FP, + THEORY_SEP }; private: @@ -344,6 +345,7 @@ private: void addFloatingPointOperators(); + void addSepOperators(); };/* class Smt2 */ }/* CVC4::parser namespace */ diff --git a/src/printer/smt2/smt2_printer.cpp b/src/printer/smt2/smt2_printer.cpp index 9d61e5ef8..4006a9e08 100644 --- a/src/printer/smt2/smt2_printer.cpp +++ b/src/printer/smt2/smt2_printer.cpp @@ -117,21 +117,26 @@ void Smt2Printer::toStream(std::ostream& out, TNode n, // variable if(n.isVar()) { - string s; - if(n.getAttribute(expr::VarNameAttr(), s)) { - out << maybeQuoteSymbol(s); - } else { - if(n.getKind() == kind::VARIABLE) { - out << "var_"; + if( n.getKind() == kind::SEP_NIL ){ + out << "(as sep.nil " << n.getType() << ")"; + return; + }else{ + string s; + if(n.getAttribute(expr::VarNameAttr(), s)) { + out << maybeQuoteSymbol(s); } else { - out << n.getKind() << '_'; + if(n.getKind() == kind::VARIABLE) { + out << "var_"; + } else { + out << n.getKind() << '_'; + } + out << n.getId(); + } + if(types) { + // print the whole type, but not *its* type + out << ":"; + n.getType().toStream(out, language::output::LANG_SMTLIB_V2_5); } - out << n.getId(); - } - if(types) { - // print the whole type, but not *its* type - out << ":"; - n.getType().toStream(out, language::output::LANG_SMTLIB_V2_5); } return; @@ -291,7 +296,7 @@ void Smt2Printer::toStream(std::ostream& out, TNode n, case kind::EMPTYSET: out << "(as emptyset " << n.getConst<EmptySet>().getType() << ")"; break; - + default: // fall back on whatever operator<< does on underlying type; we // might luck out and be SMT-LIB v2 compliant @@ -318,7 +323,7 @@ void Smt2Printer::toStream(std::ostream& out, TNode n, } return; } - + bool stillNeedToPrintParams = true; bool forceBinary = false; // force N-ary to binary when outputing children // operator @@ -585,6 +590,12 @@ void Smt2Printer::toStream(std::ostream& out, TNode n, case kind::APPLY_SELECTOR_TOTAL: case kind::PARAMETRIC_DATATYPE: break; + + //separation + case kind::SEP_EMP: + case kind::SEP_PTO: + case kind::SEP_STAR: + case kind::SEP_WAND:out << smtKindString(k) << " "; break; // quantifiers case kind::FORALL: @@ -834,6 +845,39 @@ static string smtKindString(Kind k) throw() { case kind::FLOATINGPOINT_TO_SBV: return "fp.to_sbv"; case kind::FLOATINGPOINT_TO_REAL: return "fp.to_real"; + //string theory + case kind::STRING_CONCAT: return "str.++"; + case kind::STRING_LENGTH: return "str.len"; + case kind::STRING_SUBSTR: return "str.substr" ; + case kind::STRING_STRCTN: return "str.contains" ; + case kind::STRING_CHARAT: return "str.at" ; + case kind::STRING_STRIDOF: return "str.indexof" ; + case kind::STRING_STRREPL: return "str.replace" ; + case kind::STRING_PREFIX: return "str.prefixof" ; + case kind::STRING_SUFFIX: return "str.suffixof" ; + case kind::STRING_ITOS: return "int.to.str" ; + case kind::STRING_STOI: return "str.to.int" ; + case kind::STRING_U16TOS: return "u16.to.str" ; + case kind::STRING_STOU16: return "str.to.u16" ; + case kind::STRING_U32TOS: return "u32.to.str" ; + case kind::STRING_STOU32: return "str.to.u32" ; + case kind::STRING_IN_REGEXP: return "str.in.re"; + case kind::STRING_TO_REGEXP: return "str.to.re"; + case kind::REGEXP_CONCAT: return "re.++"; + case kind::REGEXP_UNION: return "re.union"; + case kind::REGEXP_INTER: return "re.inter"; + case kind::REGEXP_STAR: return "re.*"; + case kind::REGEXP_PLUS: return "re.+"; + case kind::REGEXP_OPT: return "re.opt"; + case kind::REGEXP_RANGE: return "re.range"; + case kind::REGEXP_LOOP: return "re.loop"; + + //sep theory + case kind::SEP_STAR: return "sep"; + case kind::SEP_PTO: return "pto"; + case kind::SEP_WAND: return "wand"; + case kind::SEP_EMP: return "emp"; + default: ; /* fall through */ } diff --git a/src/proof/arith_proof.cpp b/src/proof/arith_proof.cpp index a1287b667..4864cbf46 100644 --- a/src/proof/arith_proof.cpp +++ b/src/proof/arith_proof.cpp @@ -69,18 +69,18 @@ inline static bool match(TNode n1, TNode n2) { void ProofArith::toStream(std::ostream& out) { Trace("theory-proof-debug") << "; Print Arith proof..." << std::endl; //AJR : carry this further? - LetMap map; + ProofLetMap map; toStreamLFSC(out, ProofManager::getArithProof(), d_proof, map); } -void ProofArith::toStreamLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, const LetMap& map) { +void ProofArith::toStreamLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, const ProofLetMap& map) { Debug("lfsc-arith") << "Printing arith proof in LFSC : " << std::endl; pf->debug_print("lfsc-arith"); Debug("lfsc-arith") << std::endl; toStreamRecLFSC( out, tp, pf, 0, map ); } -Node ProofArith::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, unsigned tb, const LetMap& map) { +Node ProofArith::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, unsigned tb, const ProofLetMap& map) { Debug("pf::arith") << std::endl << std::endl << "toStreamRecLFSC called. tb = " << tb << " . proof:" << std::endl; pf->debug_print("pf::arith"); Debug("pf::arith") << std::endl; @@ -643,7 +643,7 @@ void ArithProof::registerTerm(Expr term) { } } -void LFSCArithProof::printOwnedTerm(Expr term, std::ostream& os, const LetMap& map) { +void LFSCArithProof::printOwnedTerm(Expr term, std::ostream& os, const ProofLetMap& map) { Debug("pf::arith") << "Arith print term: " << term << ". Kind: " << term.getKind() << ". Type: " << term.getType() << ". Number of children: " << term.getNumChildren() << std::endl; @@ -810,14 +810,14 @@ void LFSCArithProof::printOwnedSort(Type type, std::ostream& os) { } } -void LFSCArithProof::printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren) { +void LFSCArithProof::printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren, const ProofLetMap& map) { os << " ;; Arith Theory Lemma \n;;"; for (unsigned i = 0; i < lemma.size(); ++i) { os << lemma[i] <<" "; } os <<"\n"; //os << " (clausify_false trust)"; - ArithProof::printTheoryLemmaProof( lemma, os, paren ); + ArithProof::printTheoryLemmaProof(lemma, os, paren, map); } void LFSCArithProof::printSortDeclarations(std::ostream& os, std::ostream& paren) { @@ -830,4 +830,8 @@ void LFSCArithProof::printDeferredDeclarations(std::ostream& os, std::ostream& p // Nothing to do here at this point. } +void LFSCArithProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren) { + // Nothing to do here at this point. +} + } /* CVC4 namespace */ diff --git a/src/proof/arith_proof.h b/src/proof/arith_proof.h index 788e4bd86..d980654c4 100644 --- a/src/proof/arith_proof.h +++ b/src/proof/arith_proof.h @@ -29,13 +29,13 @@ namespace CVC4 { //proof object outputted by TheoryArith class ProofArith : public Proof { private: - static Node toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, unsigned tb, const LetMap& map); + static Node toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, unsigned tb, const ProofLetMap& map); public: ProofArith( theory::eq::EqProof * pf ) : d_proof( pf ) {} //it is simply an equality engine proof theory::eq::EqProof * d_proof; void toStream(std::ostream& out); - static void toStreamLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, const LetMap& map); + static void toStreamLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, const ProofLetMap& map); }; @@ -68,12 +68,13 @@ public: LFSCArithProof(theory::arith::TheoryArith* arith, TheoryProofEngine* proofEngine) : ArithProof(arith, proofEngine) {} - virtual void printOwnedTerm(Expr term, std::ostream& os, const LetMap& map); + virtual void printOwnedTerm(Expr term, std::ostream& os, const ProofLetMap& map); virtual void printOwnedSort(Type type, std::ostream& os); - virtual void printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren); + virtual void printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren, const ProofLetMap& map); virtual void printSortDeclarations(std::ostream& os, std::ostream& paren); virtual void printTermDeclarations(std::ostream& os, std::ostream& paren); virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren); + virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren); }; diff --git a/src/proof/array_proof.cpp b/src/proof/array_proof.cpp index 8aba8dce9..484bc70c8 100644 --- a/src/proof/array_proof.cpp +++ b/src/proof/array_proof.cpp @@ -81,27 +81,45 @@ inline static bool match(TNode n1, TNode n2) { void ProofArray::setRowMergeTag(unsigned tag) { d_reasonRow = tag; + d_proofPrinter.d_row = tag; } void ProofArray::setRow1MergeTag(unsigned tag) { d_reasonRow1 = tag; + d_proofPrinter.d_row1 = tag; } void ProofArray::setExtMergeTag(unsigned tag) { d_reasonExt = tag; + d_proofPrinter.d_ext = tag; +} + +unsigned ProofArray::getRowMergeTag() const { + return d_reasonRow; +} + +unsigned ProofArray::getRow1MergeTag() const { + return d_reasonRow1; +} + +unsigned ProofArray::getExtMergeTag() const { + return d_reasonExt; } void ProofArray::toStream(std::ostream& out) { + ProofLetMap map; + toStream(out, map); +} + +void ProofArray::toStream(std::ostream& out, const ProofLetMap& map) { Trace("pf::array") << "; Print Array proof..." << std::endl; - //AJR : carry this further? - LetMap map; toStreamLFSC(out, ProofManager::getArrayProof(), d_proof, map); Debug("pf::array") << "; Print Array proof done!" << std::endl; } -void ProofArray::toStreamLFSC(std::ostream& out, TheoryProof* tp, theory::eq::EqProof* pf, const LetMap& map) { +void ProofArray::toStreamLFSC(std::ostream& out, TheoryProof* tp, theory::eq::EqProof* pf, const ProofLetMap& map) { Debug("pf::array") << "Printing array proof in LFSC : " << std::endl; - pf->debug_print("pf::array"); + pf->debug_print("pf::array", 0, &d_proofPrinter); Debug("pf::array") << std::endl; toStreamRecLFSC( out, tp, pf, 0, map ); Debug("pf::array") << "Printing array proof in LFSC DONE" << std::endl; @@ -111,10 +129,10 @@ Node ProofArray::toStreamRecLFSC(std::ostream& out, TheoryProof* tp, theory::eq::EqProof* pf, unsigned tb, - const LetMap& map) { + const ProofLetMap& map) { Debug("pf::array") << std::endl << std::endl << "toStreamRecLFSC called. tb = " << tb << " . proof:" << std::endl; - pf->debug_print("pf::array"); + pf->debug_print("pf::array", 0, &d_proofPrinter); Debug("pf::array") << std::endl; if(tb == 0) { @@ -150,7 +168,7 @@ Node ProofArray::toStreamRecLFSC(std::ostream& out, pf->d_children[i + count]->d_node.isNull(); ++count) { Debug("pf::array") << "Found a congruence: " << std::endl; - pf->d_children[i+count]->debug_print("pf::array"); + pf->d_children[i+count]->debug_print("pf::array", 0, &d_proofPrinter); congruenceClosures.push_back(pf->d_children[i+count]); } @@ -220,48 +238,48 @@ Node ProofArray::toStreamRecLFSC(std::ostream& out, ++i; } } - Assert(neg >= 0); + + bool disequalityFound = (neg >= 0); + if (!disequalityFound) { + Debug("pf::array") << "A disequality was NOT found. UNSAT due to merged constants" << std::endl; + Debug("pf::array") << "Proof for: " << pf->d_node << std::endl; + Assert(pf->d_node.getKind() == kind::EQUAL); + Assert(pf->d_node.getNumChildren() == 2); + Assert (pf->d_node[0].isConst() && pf->d_node[1].isConst()); + } Node n1; std::stringstream ss, ss2; //Assert(subTrans.d_children.size() == pf->d_children.size() - 1); Debug("mgdx") << "\nsubtrans has " << subTrans.d_children.size() << " children\n"; - if(pf->d_children.size() > 2) { + if(!disequalityFound || pf->d_children.size() > 2) { n1 = toStreamRecLFSC(ss, tp, &subTrans, 1, map); } else { n1 = toStreamRecLFSC(ss, tp, subTrans.d_children[0], 1, map); Debug("mgdx") << "\nsubTrans unique child " << subTrans.d_children[0]->d_id << " was proven\ngot: " << n1 << std::endl; } - Node n2 = pf->d_children[neg]->d_node; - Assert(n2.getKind() == kind::NOT); - Debug("mgdx") << "\nhave proven: " << n1 << std::endl; - Debug("mgdx") << "n2 is " << n2 << std::endl; - Debug("mgdx") << "n2->d_id is " << pf->d_children[neg]->d_id << std::endl; - Debug("mgdx") << "n2[0] is " << n2[0] << std::endl; + out << "(clausify_false (contra _ "; - if (n2[0].getNumChildren() > 0) { Debug("mgdx") << "\nn2[0]: " << n2[0][0] << std::endl; } - if (n1.getNumChildren() > 1) { Debug("mgdx") << "n1[1]: " << n1[1] << std::endl; } + if (disequalityFound) { + Node n2 = pf->d_children[neg]->d_node; + Assert(n2.getKind() == kind::NOT); + Debug("mgdx") << "\nhave proven: " << n1 << std::endl; + Debug("mgdx") << "n2 is " << n2 << std::endl; + Debug("mgdx") << "n2->d_id is " << pf->d_children[neg]->d_id << std::endl; + Debug("mgdx") << "n2[0] is " << n2[0] << std::endl; - if (pf->d_children[neg]->d_id == d_reasonExt) { - // The negative node was created by an EXT rule; e.g. it is a[k]!=b[k], due to a!=b. - - // (clausify_false (contra _ .gl2 (or_elim_1 _ _ .gl1 FIXME))))))) (\ .glemc6 - - out << "(clausify_false (contra _ "; - out << ss.str(); + if (n2[0].getNumChildren() > 0) { Debug("mgdx") << "\nn2[0]: " << n2[0][0] << std::endl; } + if (n1.getNumChildren() > 1) { Debug("mgdx") << "n1[1]: " << n1[1] << std::endl; } - toStreamRecLFSC(ss2, tp, pf->d_children[neg], 1, map); - - out << " "; - out << ss2.str(); - out << "))"; - - } else { - // The negative node is, e.g., a pure equality - out << "(clausify_false (contra _ "; - - if(n2[0].getKind() == kind::APPLY_UF) { + if ((pf->d_children[neg]->d_id == d_reasonExt) || + (pf->d_children[neg]->d_id == theory::eq::MERGED_THROUGH_TRANS)) { + // Ext case: The negative node was created by an EXT rule; e.g. it is a[k]!=b[k], due to a!=b. + out << ss.str(); + out << " "; + toStreamRecLFSC(ss2, tp, pf->d_children[neg], 1, map); + out << ss2.str(); + } else if (n2[0].getKind() == kind::APPLY_UF) { out << "(trans _ _ _ _ "; out << "(symm _ _ _ "; out << ss.str(); @@ -276,16 +294,27 @@ Node ProofArray::toStreamRecLFSC(std::ostream& out, Debug("pf::array") << "ArrayProof::toStream: getLitName( " << n2[0] << " ) = " << ProofManager::getLitName(n2[0]) << std::endl; - out << " " << ProofManager::getLitName(n2[0]) << "))" << std::endl; + out << " " << ProofManager::getLitName(n2[0]); } + } else { + Node n2 = pf->d_node; + Assert(n2.getKind() == kind::EQUAL); + Assert((n1[0] == n2[0] && n1[1] == n2[1]) || (n1[1] == n2[0] && n1[0] == n2[1])); + + out << ss.str(); + out << " "; + ProofManager::getTheoryProofEngine()->printConstantDisequalityProof(out, + n1[0].toExpr(), + n1[1].toExpr()); } + out << "))" << std::endl; return Node(); } if (pf->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE) { Debug("mgd") << "\nok, looking at congruence:\n"; - pf->debug_print("mgd"); + pf->debug_print("mgd", 0, &d_proofPrinter); std::stack<const theory::eq::EqProof*> stk; for(const theory::eq::EqProof* pf2 = pf; pf2->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE; pf2 = pf2->d_children[0]) { Assert(!pf2->d_node.isNull()); @@ -315,7 +344,7 @@ Node ProofArray::toStreamRecLFSC(std::ostream& out, Debug("mgd") << "\nok, in FIRST cong[" << stk.size() << "]" << "\n"; - pf2->debug_print("mgd"); + pf2->debug_print("mgd", 0, &d_proofPrinter); // Temp Debug("mgd") << "n1 is a proof for: " << pf2->d_children[0]->d_node << ". It is: " << n1 << std::endl; Debug("mgd") << "n2 is a proof for: " << pf2->d_children[1]->d_node << ". It is: " << n2 << std::endl; @@ -332,7 +361,7 @@ Node ProofArray::toStreamRecLFSC(std::ostream& out, Debug("mgd") << "SIDE IS 1\n"; if(!match(pf2->d_node, n1[1])) { Debug("mgd") << "IN BAD CASE, our first subproof is\n"; - pf2->d_children[0]->debug_print("mgd"); + pf2->d_children[0]->debug_print("mgd", 0, &d_proofPrinter); } Assert(match(pf2->d_node, n1[1])); side = 1; @@ -546,6 +575,20 @@ Node ProofArray::toStreamRecLFSC(std::ostream& out, return pf->d_node; } + else if (pf->d_id == theory::eq::MERGED_THROUGH_CONSTANTS) { + Debug("pf::array") << "Proof for: " << pf->d_node << std::endl; + Assert(pf->d_node.getKind() == kind::NOT); + Node n = pf->d_node[0]; + Assert(n.getKind() == kind::EQUAL); + Assert(n.getNumChildren() == 2); + Assert(n[0].isConst() && n[1].isConst()); + + ProofManager::getTheoryProofEngine()->printConstantDisequalityProof(out, + n[0].toExpr(), + n[1].toExpr()); + return pf->d_node; + } + else if (pf->d_id == theory::eq::MERGED_THROUGH_TRANS) { bool firstNeg = false; bool secondNeg = false; @@ -554,7 +597,7 @@ Node ProofArray::toStreamRecLFSC(std::ostream& out, Assert(pf->d_children.size() >= 2); std::stringstream ss; Debug("mgd") << "\ndoing trans proof[[\n"; - pf->debug_print("mgd"); + pf->debug_print("mgd", 0, &d_proofPrinter); Debug("mgd") << "\n"; Node n1 = toStreamRecLFSC(ss, tp, pf->d_children[0], tb + 1, map); Debug("mgd") << "\ndoing trans proof, got n1 " << n1 << "\n"; @@ -772,7 +815,7 @@ Node ProofArray::toStreamRecLFSC(std::ostream& out, Warning() << "\n\ntrans proof failure at step " << i << "\n\n"; Warning() << "0 proves " << n1 << "\n"; Warning() << "1 proves " << n2 << "\n\n"; - pf->debug_print("mgdx",0); + pf->debug_print("mgdx", 0, &d_proofPrinter); //toStreamRec(Warning.getStream(), pf, 0); Warning() << "\n\n"; Unreachable(); @@ -931,6 +974,9 @@ Node ProofArray::toStreamRecLFSC(std::ostream& out, t4 = pf->d_children[0]->d_node[0][side][0][2]; ret = pf->d_node; + // The order of indices needs to match; we might have to swap t1 and t2 and then apply symmetry. + bool swap = (t2 == pf->d_children[0]->d_node[0][side][0][1]); + Debug("mgd") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 << "\nt4 " << t4 << "\n"; Assert(pf->d_children.size() == 1); @@ -939,28 +985,31 @@ Node ProofArray::toStreamRecLFSC(std::ostream& out, Debug("pf::array") << "Subproof is: " << ss.str() << std::endl; + if (swap) { + out << "(symm _ _ _ "; + } + out << "(negativerow _ _ "; - tp->printTerm(t1.toExpr(), out, map); + tp->printTerm(swap ? t2.toExpr() : t1.toExpr(), out, map); out << " "; - tp->printTerm(t2.toExpr(), out, map); + tp->printTerm(swap ? t1.toExpr() : t2.toExpr(), out, map); out << " "; tp->printTerm(t3.toExpr(), out, map); out << " "; tp->printTerm(t4.toExpr(), out, map); out << " "; - - // if (subproof[0][1] == t3) { - Debug("pf::array") << "Dont need symmetry!" << std::endl; - out << ss.str(); - // } else { - // Debug("pf::array") << "Need symmetry!" << std::endl; - // out << "(negsymm _ _ _ " << ss.str() << ")"; - // } + if (side != 0) { + out << "(negsymm _ _ _ " << ss.str() << ")"; + } else { + out << ss.str(); + } out << ")"; - // Unreachable(); + if (swap) { + out << ") "; + } return ret; } @@ -1071,13 +1120,12 @@ void ArrayProof::registerTerm(Expr term) { } std::string ArrayProof::skolemToLiteral(Expr skolem) { + Debug("pf::array") << "ArrayProof::skolemToLiteral( " << skolem << ")" << std::endl; Assert(d_skolemToLiteral.find(skolem) != d_skolemToLiteral.end()); return d_skolemToLiteral[skolem]; } -void LFSCArrayProof::printOwnedTerm(Expr term, std::ostream& os, const LetMap& map) { - Debug("pf::array") << std::endl << "(pf::array) LFSCArrayProof::printOwnedTerm: term = " << term << std::endl; - +void LFSCArrayProof::printOwnedTerm(Expr term, std::ostream& os, const ProofLetMap& map) { Assert (theory::Theory::theoryOf(term) == theory::THEORY_ARRAY); if (theory::Theory::theoryOf(term) != theory::THEORY_ARRAY) { @@ -1154,23 +1202,35 @@ void LFSCArrayProof::printOwnedTerm(Expr term, std::ostream& os, const LetMap& m void LFSCArrayProof::printOwnedSort(Type type, std::ostream& os) { Debug("pf::array") << std::endl << "(pf::array) LFSCArrayProof::printOwnedSort: type is: " << type << std::endl; Assert (type.isArray() || type.isSort()); - os << type <<" "; + if (type.isArray()){ + ArrayType array_type(type); + + Debug("pf::array") << "LFSCArrayProof::printOwnedSort: type is an array. Index type: " + << array_type.getIndexType() + << ", element type: " << array_type.getConstituentType() << std::endl; + + os << "(Array "; + printSort(array_type.getIndexType(), os); + os << " "; + printSort(array_type.getConstituentType(), os); + os << ")"; + } else { + os << type <<" "; + } } -void LFSCArrayProof::printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren) { +void LFSCArrayProof::printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren, const ProofLetMap& map) { os << " ;; Array Theory Lemma \n;;"; for (unsigned i = 0; i < lemma.size(); ++i) { os << lemma[i] <<" "; } os <<"\n"; //os << " (clausify_false trust)"; - ArrayProof::printTheoryLemmaProof(lemma, os, paren); + ArrayProof::printTheoryLemmaProof(lemma, os, paren, map); } void LFSCArrayProof::printSortDeclarations(std::ostream& os, std::ostream& paren) { // declaring the sorts - Debug("pf::array") << "Arrays declaring sorts..." << std::endl; - for (TypeSet::const_iterator it = d_sorts.begin(); it != d_sorts.end(); ++it) { if (!ProofManager::currentPM()->wasPrinted(*it)) { os << "(% " << *it << " sort\n"; @@ -1229,6 +1289,7 @@ void LFSCArrayProof::printTermDeclarations(std::ostream& os, std::ostream& paren void LFSCArrayProof::printDeferredDeclarations(std::ostream& os, std::ostream& paren) { Debug("pf::array") << "Array: print deferred declarations called" << std::endl; + unsigned count = 1; for (ExprSet::const_iterator it = d_skolemDeclarations.begin(); it != d_skolemDeclarations.end(); ++it) { Expr term = *it; Node equality = ProofManager::getSkolemizationManager()->getDisequality(*it); @@ -1237,7 +1298,7 @@ void LFSCArrayProof::printDeferredDeclarations(std::ostream& os, std::ostream& p << "It is a witness for: " << equality << std::endl; std::ostringstream newSkolemLiteral; - newSkolemLiteral << ".sl" << d_skolemToLiteral.size(); + newSkolemLiteral << ".sl" << count++; std::string skolemLiteral = newSkolemLiteral.str(); d_skolemToLiteral[*it] = skolemLiteral; @@ -1250,14 +1311,13 @@ void LFSCArrayProof::printDeferredDeclarations(std::ostream& os, std::ostream& p Node array_one = equality[0][0]; Node array_two = equality[0][1]; - LetMap map; - + ProofLetMap map; os << "(ext _ _ "; printTerm(array_one.toExpr(), os, map); os << " "; printTerm(array_two.toExpr(), os, map); os << " (\\ "; - printTerm(*it, os, map); + os << ProofManager::sanitize(*it); os << " (\\ "; os << skolemLiteral.c_str(); os << "\n"; @@ -1266,4 +1326,8 @@ void LFSCArrayProof::printDeferredDeclarations(std::ostream& os, std::ostream& p } } +void LFSCArrayProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren) { + // Nothing to do here at this point. +} + } /* CVC4 namespace */ diff --git a/src/proof/array_proof.h b/src/proof/array_proof.h index fb25c9433..69e62dbf3 100644 --- a/src/proof/array_proof.h +++ b/src/proof/array_proof.h @@ -30,10 +30,36 @@ namespace CVC4 { //proof object outputted by TheoryARRAY class ProofArray : public Proof { private: + class ArrayProofPrinter : public theory::eq::EqProof::PrettyPrinter { + public: + ArrayProofPrinter() : d_row(0), d_row1(0), d_ext(0) { + } + + std::string printTag(unsigned tag) { + if (tag == theory::eq::MERGED_THROUGH_CONGRUENCE) return "Congruence"; + if (tag == theory::eq::MERGED_THROUGH_EQUALITY) return "Pure Equality"; + if (tag == theory::eq::MERGED_THROUGH_REFLEXIVITY) return "Reflexivity"; + if (tag == theory::eq::MERGED_THROUGH_CONSTANTS) return "Constants"; + if (tag == theory::eq::MERGED_THROUGH_TRANS) return "Transitivity"; + + if (tag == d_row) return "Read Over Write"; + if (tag == d_row1) return "Read Over Write (1)"; + if (tag == d_ext) return "Extensionality"; + + std::ostringstream result; + result << tag; + return result.str(); + } + + unsigned d_row; + unsigned d_row1; + unsigned d_ext; + }; + Node toStreamRecLFSC(std::ostream& out, TheoryProof* tp, theory::eq::EqProof* pf, unsigned tb, - const LetMap& map); + const ProofLetMap& map); /** Merge tag for ROW applications */ unsigned d_reasonRow; @@ -41,18 +67,25 @@ private: unsigned d_reasonRow1; /** Merge tag for EXT applications */ unsigned d_reasonExt; + + ArrayProofPrinter d_proofPrinter; public: ProofArray(theory::eq::EqProof* pf) : d_proof(pf) {} //it is simply an equality engine proof theory::eq::EqProof *d_proof; void toStream(std::ostream& out); - void toStreamLFSC(std::ostream& out, TheoryProof* tp, theory::eq::EqProof* pf, const LetMap& map); + void toStream(std::ostream& out, const ProofLetMap& map); + void toStreamLFSC(std::ostream& out, TheoryProof* tp, theory::eq::EqProof* pf, const ProofLetMap& map); void registerSkolem(Node equality, Node skolem); void setRowMergeTag(unsigned tag); void setRow1MergeTag(unsigned tag); void setExtMergeTag(unsigned tag); + + unsigned getRowMergeTag() const; + unsigned getRow1MergeTag() const; + unsigned getExtMergeTag() const; }; namespace theory { @@ -85,12 +118,13 @@ public: : ArrayProof(arrays, proofEngine) {} - virtual void printOwnedTerm(Expr term, std::ostream& os, const LetMap& map); + virtual void printOwnedTerm(Expr term, std::ostream& os, const ProofLetMap& map); virtual void printOwnedSort(Type type, std::ostream& os); - virtual void printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren); + virtual void printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren, const ProofLetMap& map); virtual void printSortDeclarations(std::ostream& os, std::ostream& paren); virtual void printTermDeclarations(std::ostream& os, std::ostream& paren); virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren); + virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren); }; diff --git a/src/proof/bitvector_proof.cpp b/src/proof/bitvector_proof.cpp index b63782226..f19e45920 100644 --- a/src/proof/bitvector_proof.cpp +++ b/src/proof/bitvector_proof.cpp @@ -15,14 +15,17 @@ **/ -#include "proof/bitvector_proof.h" #include "options/bv_options.h" +#include "proof/array_proof.h" +#include "proof/bitvector_proof.h" #include "proof/clause_id.h" +#include "proof/proof_output_channel.h" #include "proof/proof_utils.h" #include "proof/sat_proof_implementation.h" #include "prop/bvminisat/bvminisat.h" #include "theory/bv/bitblaster_template.h" #include "theory/bv/theory_bv.h" +#include "theory/bv/theory_bv_rewrite_rules.h" using namespace CVC4::theory; using namespace CVC4::theory::bv; @@ -80,20 +83,40 @@ BVSatProof* BitVectorProof::getSatProof() { } void BitVectorProof::registerTermBB(Expr term) { + Debug("pf::bv") << "BitVectorProof::registerTermBB( " << term << " )" << std::endl; + if (d_seenBBTerms.find(term) != d_seenBBTerms.end()) return; d_seenBBTerms.insert(term); d_bbTerms.push_back(term); + + // If this term gets used in the final proof, we will want to register it. However, + // we don't know this at this point; and when the theory proof engine sees it, if it belongs + // to another theory, it won't register it with this proof. So, we need to tell the + // engine to inform us. + + if (theory::Theory::theoryOf(term) != theory::THEORY_BV) { + Debug("pf::bv") << "\tMarking term " << term << " for future BV registration" << std::endl; + d_proofEngine->markTermForFutureRegistration(term, theory::THEORY_BV); + } } void BitVectorProof::registerAtomBB(Expr atom, Expr atom_bb) { + Debug("pf::bv") << "BitVectorProof::registerAtomBB( " << atom << ", " << atom_bb << " )" << std::endl; + Expr def = atom.iffExpr(atom_bb); - d_bbAtoms.insert(std::make_pair(atom, def)); + d_bbAtoms.insert(std::make_pair(atom, def)); registerTerm(atom); + + // Register the atom's terms for bitblasting + registerTermBB(atom[0]); + registerTermBB(atom[1]); } void BitVectorProof::registerTerm(Expr term) { + Debug("pf::bv") << "BitVectorProof::registerTerm( " << term << " )" << std::endl; + d_usedBB.insert(term); if (Theory::isLeafOf(term, theory::THEORY_BV) && @@ -101,6 +124,11 @@ void BitVectorProof::registerTerm(Expr term) { d_declarations.insert(term); } + Debug("pf::bv") << "Going to register children: " << std::endl; + for (unsigned i = 0; i < term.getNumChildren(); ++i) { + Debug("pf::bv") << "\t" << term[i] << std::endl; + } + // don't care about parametric operators for bv? for (unsigned i = 0; i < term.getNumChildren(); ++i) { d_proofEngine->registerTerm(term[i]); @@ -108,6 +136,7 @@ void BitVectorProof::registerTerm(Expr term) { } std::string BitVectorProof::getBBTermName(Expr expr) { + Debug("pf::bv") << "BitVectorProof::getBBTermName( " << expr << " ) = bt" << expr.getId() << std::endl; std::ostringstream os; os << "bt"<< expr.getId(); return os.str(); @@ -122,6 +151,8 @@ void BitVectorProof::startBVConflict(CVC4::BVMinisat::Solver::TLit lit) { } void BitVectorProof::endBVConflict(const CVC4::BVMinisat::Solver::TLitVec& confl) { + Debug("pf::bv") << "BitVectorProof::endBVConflict called" << std::endl; + std::vector<Expr> expr_confl; for (int i = 0; i < confl.size(); ++i) { prop::SatLiteral lit = prop::BVMinisatSatSolver::toSatLiteral(confl[i]); @@ -129,6 +160,7 @@ void BitVectorProof::endBVConflict(const CVC4::BVMinisat::Solver::TLitVec& confl Expr expr_lit = lit.isNegated() ? atom.notExpr() : atom; expr_confl.push_back(expr_lit); } + Expr conflict = utils::mkSortedExpr(kind::OR, expr_confl); Debug("pf::bv") << "Make conflict for " << conflict << std::endl; @@ -144,30 +176,104 @@ void BitVectorProof::endBVConflict(const CVC4::BVMinisat::Solver::TLitVec& confl ClauseId clause_id = d_resolutionProof->registerAssumptionConflict(confl); d_bbConflictMap[conflict] = clause_id; d_resolutionProof->endResChain(clause_id); - Debug("pf::bv") << "BitVectorProof::endBVConflict id"<<clause_id<< " => " << conflict << "\n"; + Debug("pf::bv") << "BitVectorProof::endBVConflict id" <<clause_id<< " => " << conflict << "\n"; d_isAssumptionConflict = false; } void BitVectorProof::finalizeConflicts(std::vector<Expr>& conflicts) { + if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { Debug("pf::bv") << "Construct full proof." << std::endl; d_resolutionProof->constructProof(); return; } - for(unsigned i = 0; i < conflicts.size(); ++i) { + + for (unsigned i = 0; i < conflicts.size(); ++i) { Expr confl = conflicts[i]; - Debug("pf::bv") << "Finalize conflict " << confl << std::endl; - //Assert (d_bbConflictMap.find(confl) != d_bbConflictMap.end()); - if(d_bbConflictMap.find(confl) != d_bbConflictMap.end()){ + Debug("pf::bv") << "Finalize conflict #" << i << ": " << confl << std::endl; + + // Special case: if the conflict has a (true) or a (not false) in it, it is trivial... + bool ignoreConflict = false; + if ((confl.isConst() && confl.getConst<bool>()) || + (confl.getKind() == kind::NOT && confl[0].isConst() && !confl[0].getConst<bool>())) { + ignoreConflict = true; + } else if (confl.getKind() == kind::OR) { + for (unsigned k = 0; k < confl.getNumChildren(); ++k) { + if ((confl[k].isConst() && confl[k].getConst<bool>()) || + (confl[k].getKind() == kind::NOT && confl[k][0].isConst() && !confl[k][0].getConst<bool>())) { + ignoreConflict = true; + } + } + } + if (ignoreConflict) { + Debug("pf::bv") << "Ignoring conflict due to (true) or (not false)" << std::endl; + continue; + } + + if (d_bbConflictMap.find(confl) != d_bbConflictMap.end()) { ClauseId id = d_bbConflictMap[confl]; d_resolutionProof->collectClauses(id); - }else{ - Debug("pf::bv") << "Do not collect clauses for " << confl << std::endl; + } else { + // There is no exact match for our conflict, but maybe it is a subset of another conflict + ExprToClauseId::const_iterator it; + bool matchFound = false; + for (it = d_bbConflictMap.begin(); it != d_bbConflictMap.end(); ++it) { + Expr possibleMatch = it->first; + if (possibleMatch.getKind() != kind::OR) { + // This is a single-node conflict. If this node is in the conflict we're trying to prove, + // we have a match. + for (unsigned k = 0; k < confl.getNumChildren(); ++k) { + if (confl[k] == possibleMatch) { + matchFound = true; + d_resolutionProof->collectClauses(it->second); + break; + } + } + } else { + if (possibleMatch.getNumChildren() > confl.getNumChildren()) + continue; + + unsigned k = 0; + bool matching = true; + for (unsigned j = 0; j < possibleMatch.getNumChildren(); ++j) { + // j is the index in possibleMatch + // k is the index in confl + while (k < confl.getNumChildren() && confl[k] != possibleMatch[j]) { + ++k; + } + if (k == confl.getNumChildren()) { + // We couldn't find a match for possibleMatch[j], so not a match + matching = false; + break; + } + } + + if (matching) { + Debug("pf::bv") << "Collecting info from a sub-conflict" << std::endl; + d_resolutionProof->collectClauses(it->second); + matchFound = true; + break; + } + } + } + + if (!matchFound) { + Debug("pf::bv") << "Do not collect clauses for " << confl << std::endl + << "Dumping existing conflicts:" << std::endl; + + i = 0; + for (it = d_bbConflictMap.begin(); it != d_bbConflictMap.end(); ++it) { + ++i; + Debug("pf::bv") << "\tConflict #" << i << ": " << it->first << std::endl; + } + + Unreachable(); + } } } } -void LFSCBitVectorProof::printOwnedTerm(Expr term, std::ostream& os, const LetMap& map) { +void LFSCBitVectorProof::printOwnedTerm(Expr term, std::ostream& os, const ProofLetMap& map) { Debug("pf::bv") << std::endl << "(pf::bv) LFSCBitVectorProof::printOwnedTerm( " << term << " ), theory is: " << Theory::theoryOf(term) << std::endl; @@ -238,17 +344,31 @@ void LFSCBitVectorProof::printOwnedTerm(Expr term, std::ostream& os, const LetMa printBitOf(term, os, map); return; } - case kind::VARIABLE: + + case kind::VARIABLE: { + os << "(a_var_bv " << utils::getSize(term)<< " " << ProofManager::sanitize(term) << ")"; + return; + } + case kind::SKOLEM: { - os << "(a_var_bv " << utils::getSize(term)<<" " << ProofManager::sanitize(term) <<")"; + + // TODO: we need to distinguish between "real" skolems (e.g. from array) and "fake" skolems, + // like ITE terms. Is there a more elegant way? + + if (ProofManager::getSkolemizationManager()->isSkolem(term)) { + os << ProofManager::sanitize(term); + } else { + os << "(a_var_bv " << utils::getSize(term)<< " " << ProofManager::sanitize(term) << ")"; + } return; } + default: Unreachable(); } } -void LFSCBitVectorProof::printBitOf(Expr term, std::ostream& os, const LetMap& map) { +void LFSCBitVectorProof::printBitOf(Expr term, std::ostream& os, const ProofLetMap& map) { Assert (term.getKind() == kind::BITVECTOR_BITOF); unsigned bit = term.getOperator().getConst<BitVectorBitOf>().bitIndex; Expr var = term[0]; @@ -258,14 +378,7 @@ void LFSCBitVectorProof::printBitOf(Expr term, std::ostream& os, const LetMap& m << ", var = " << var << std::endl; os << "(bitof "; - if (var.getKind() == kind::VARIABLE || var.getKind() == kind::SKOLEM) { - // If var is "simple", we can just sanitize and print - os << ProofManager::sanitize(var); - } else { - // If var is "complex", it can belong to another theory. Therefore, dispatch again. - d_proofEngine->printBoundTerm(var, os, map); - } - + os << d_exprToVariableName[var]; os << " " << bit << ")"; } @@ -283,7 +396,7 @@ void LFSCBitVectorProof::printConstant(Expr term, std::ostream& os) { os << paren.str(); } -void LFSCBitVectorProof::printOperatorNary(Expr term, std::ostream& os, const LetMap& map) { +void LFSCBitVectorProof::printOperatorNary(Expr term, std::ostream& os, const ProofLetMap& map) { std::string op = utils::toLFSCKind(term.getKind()); std::ostringstream paren; std::string holes = term.getKind() == kind::BITVECTOR_CONCAT ? "_ _ " : ""; @@ -301,7 +414,7 @@ void LFSCBitVectorProof::printOperatorNary(Expr term, std::ostream& os, const Le } } -void LFSCBitVectorProof::printOperatorUnary(Expr term, std::ostream& os, const LetMap& map) { +void LFSCBitVectorProof::printOperatorUnary(Expr term, std::ostream& os, const ProofLetMap& map) { os <<"("; os << utils::toLFSCKind(term.getKind()) << " " << utils::getSize(term) <<" "; os << " "; @@ -309,7 +422,7 @@ void LFSCBitVectorProof::printOperatorUnary(Expr term, std::ostream& os, const L os <<")"; } -void LFSCBitVectorProof::printPredicate(Expr term, std::ostream& os, const LetMap& map) { +void LFSCBitVectorProof::printPredicate(Expr term, std::ostream& os, const ProofLetMap& map) { os <<"("; os << utils::toLFSCKind(term.getKind()) << " " << utils::getSize(term[0]) <<" "; os << " "; @@ -319,7 +432,7 @@ void LFSCBitVectorProof::printPredicate(Expr term, std::ostream& os, const LetMa os <<")"; } -void LFSCBitVectorProof::printOperatorParametric(Expr term, std::ostream& os, const LetMap& map) { +void LFSCBitVectorProof::printOperatorParametric(Expr term, std::ostream& os, const ProofLetMap& map) { os <<"("; os << utils::toLFSCKind(term.getKind()) << " " << utils::getSize(term) <<" "; os <<" "; @@ -349,14 +462,16 @@ void LFSCBitVectorProof::printOperatorParametric(Expr term, std::ostream& os, co void LFSCBitVectorProof::printOwnedSort(Type type, std::ostream& os) { Debug("pf::bv") << std::endl << "(pf::bv) LFSCBitVectorProof::printOwnedSort( " << type << " )" << std::endl; - Assert (type.isBitVector()); unsigned width = utils::getSize(type); os << "(BitVec "<<width<<")"; } -void LFSCBitVectorProof::printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren) { +void LFSCBitVectorProof::printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren, const ProofLetMap& map) { + Debug("pf::bv") << "(pf::bv) LFSCBitVectorProof::printTheoryLemmaProof called" << std::endl; Expr conflict = utils::mkSortedExpr(kind::OR, lemma); + Debug("pf::bv") << "\tconflict = " << conflict << std::endl; + if (d_bbConflictMap.find(conflict) != d_bbConflictMap.end()) { std::ostringstream lemma_paren; for (unsigned i = 0; i < lemma.size(); ++i) { @@ -377,7 +492,7 @@ void LFSCBitVectorProof::printTheoryLemmaProof(std::vector<Expr>& lemma, std::os // print corresponding literal in bv sat solver prop::SatVariable bb_var = d_cnfProof->getLiteral(lit).getSatVariable(); os << pm->getAtomName(bb_var, "bb"); - os <<"(\\unit"<<bb_var<<"\n"; + os <<"(\\ unit"<<bb_var<<"\n"; lemma_paren <<")"; } Expr lem = utils::mkOr(lemma); @@ -386,11 +501,134 @@ void LFSCBitVectorProof::printTheoryLemmaProof(std::vector<Expr>& lemma, std::os d_resolutionProof->printAssumptionsResolution(lemma_id, os, lemma_paren); os <<lemma_paren.str(); } else { - Unreachable(); // If we were to reach here, we would crash because BV replay is currently not supported - // in TheoryProof::printTheoryLemmaProof() - Debug("pf::bv") << std::endl << "; Print non-bitblast theory conflict " << conflict << std::endl; - BitVectorProof::printTheoryLemmaProof( lemma, os, paren ); + Debug("pf::bv") << "Found a non-recorded conflict. Looking for a matching sub-conflict..." + << std::endl; + + bool matching; + + ExprToClauseId::const_iterator it; + unsigned i = 0; + for (it = d_bbConflictMap.begin(); it != d_bbConflictMap.end(); ++it) { + // Our conflict is sorted, and the records are also sorted. + ++i; + Expr possibleMatch = it->first; + + if (possibleMatch.getKind() != kind::OR) { + // This is a single-node conflict. If this node is in the conflict we're trying to prove, + // we have a match. + matching = false; + + for (unsigned k = 0; k < conflict.getNumChildren(); ++k) { + if (conflict[k] == possibleMatch) { + matching = true; + break; + } + } + } else { + if (possibleMatch.getNumChildren() > conflict.getNumChildren()) + continue; + + unsigned k = 0; + + matching = true; + for (unsigned j = 0; j < possibleMatch.getNumChildren(); ++j) { + // j is the index in possibleMatch + // k is the index in conflict + while (k < conflict.getNumChildren() && conflict[k] != possibleMatch[j]) { + ++k; + } + if (k == conflict.getNumChildren()) { + // We couldn't find a match for possibleMatch[j], so not a match + matching = false; + break; + } + } + } + + if (matching) { + Debug("pf::bv") << "Found a match with conflict #" << i << ": " << std::endl << possibleMatch << std::endl; + // The rest is just a copy of the usual handling, if a precise match is found. + // We only use the literals that appear in the matching conflict, though, and not in the + // original lemma - as these may not have even been bit blasted! + std::ostringstream lemma_paren; + + if (possibleMatch.getKind() == kind::OR) { + for (unsigned i = 0; i < possibleMatch.getNumChildren(); ++i) { + Expr lit = possibleMatch[i]; + + if (lit.getKind() == kind::NOT) { + os << "(intro_assump_t _ _ _ "; + } else { + os << "(intro_assump_f _ _ _ "; + } + lemma_paren <<")"; + // print corresponding literal in main sat solver + ProofManager* pm = ProofManager::currentPM(); + CnfProof* cnf = pm->getCnfProof(); + prop::SatLiteral main_lit = cnf->getLiteral(lit); + os << pm->getLitName(main_lit); + os <<" "; + // print corresponding literal in bv sat solver + prop::SatVariable bb_var = d_cnfProof->getLiteral(lit).getSatVariable(); + os << pm->getAtomName(bb_var, "bb"); + os <<"(\\ unit"<<bb_var<<"\n"; + lemma_paren <<")"; + } + } else { + // The conflict only consists of one node, either positive or negative. + Expr lit = possibleMatch; + if (lit.getKind() == kind::NOT) { + os << "(intro_assump_t _ _ _ "; + } else { + os << "(intro_assump_f _ _ _ "; + } + lemma_paren <<")"; + // print corresponding literal in main sat solver + ProofManager* pm = ProofManager::currentPM(); + CnfProof* cnf = pm->getCnfProof(); + prop::SatLiteral main_lit = cnf->getLiteral(lit); + os << pm->getLitName(main_lit); + os <<" "; + // print corresponding literal in bv sat solver + prop::SatVariable bb_var = d_cnfProof->getLiteral(lit).getSatVariable(); + os << pm->getAtomName(bb_var, "bb"); + os <<"(\\ unit"<<bb_var<<"\n"; + lemma_paren <<")"; + } + + ClauseId lemma_id = it->second; + d_resolutionProof->printAssumptionsResolution(lemma_id, os, lemma_paren); + os <<lemma_paren.str(); + + return; + } + } + + // We failed to find a matching sub conflict. The last hope is that the + // conflict has a FALSE assertion in it; this can happen in some corner cases, + // where the FALSE is the result of a rewrite. + + for (unsigned i = 0; i < lemma.size(); ++i) { + if (lemma[i].getKind() == kind::NOT && lemma[i][0] == utils::mkFalse()) { + Debug("pf::bv") << "Lemma has a (not false) literal" << std::endl; + os << "(clausify_false "; + os << ProofManager::getLitName(lemma[i]); + os << ")"; + return; + } + } + + Debug("pf::bv") << "Failed to find a matching sub-conflict..." << std::endl + << "Dumping existing conflicts:" << std::endl; + + i = 0; + for (it = d_bbConflictMap.begin(); it != d_bbConflictMap.end(); ++it) { + ++i; + Debug("pf::bv") << "\tConflict #" << i << ": " << it->first << std::endl; + } + + Unreachable(); } } @@ -402,7 +640,14 @@ void LFSCBitVectorProof::printTermDeclarations(std::ostream& os, std::ostream& p ExprSet::const_iterator it = d_declarations.begin(); ExprSet::const_iterator end = d_declarations.end(); for (; it != end; ++it) { - os << "(% " << ProofManager::sanitize(*it) <<" var_bv\n"; + if ((it->isVariable() || it->isConst()) && !ProofManager::getSkolemizationManager()->isSkolem(*it)) { + d_exprToVariableName[*it] = ProofManager::sanitize(*it); + } else { + std::string newAlias = assignAlias(*it); + d_exprToVariableName[*it] = newAlias; + } + + os << "(% " << d_exprToVariableName[*it] <<" var_bv" << "\n"; paren <<")"; } } @@ -411,15 +656,43 @@ void LFSCBitVectorProof::printDeferredDeclarations(std::ostream& os, std::ostrea // Nothing to do here at this point. } +void LFSCBitVectorProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren) { + // Print "trust" statements to bind complex bv variables to their associated terms + + ExprToString::const_iterator it = d_assignedAliases.begin(); + ExprToString::const_iterator end = d_assignedAliases.end(); + + for (; it != end; ++it) { + Debug("pf::bv") << "Printing aliasing declaration for: " << *it << std::endl; + std::stringstream declaration; + declaration << ".fbvd" << d_aliasToBindDeclaration.size(); + d_aliasToBindDeclaration[it->second] = declaration.str(); + + os << "(th_let_pf _ "; + + os << "(trust_f "; + os << "(= (BitVec " << utils::getSize(it->first) << ") "; + os << "(a_var_bv " << utils::getSize(it->first) << " " << it->second << ") "; + ProofLetMap emptyMap; + d_proofEngine->printBoundTerm(it->first, os, emptyMap); + os << ")) "; + os << "(\\ "<< d_aliasToBindDeclaration[it->second] << "\n"; + paren << "))"; + } + + os << "\n"; +} + void LFSCBitVectorProof::printTermBitblasting(Expr term, std::ostream& os) { // TODO: once we have the operator elimination rules remove those that we // eliminated Assert (term.getType().isBitVector()); Kind kind = term.getKind(); - if (Theory::isLeafOf(term, theory::THEORY_BV) && - !term.isConst()) { - os << "(bv_bbl_var "<<utils::getSize(term) << " " << ProofManager::sanitize(term) <<" _ )"; + if (Theory::isLeafOf(term, theory::THEORY_BV) && !term.isConst()) { + // A term is a leaf if it has no children, or if it belongs to another theory + os << "(bv_bbl_var " << utils::getSize(term) << " " << d_exprToVariableName[term]; + os << " _)"; return; } @@ -448,14 +721,18 @@ void LFSCBitVectorProof::printTermBitblasting(Expr term, std::ostream& os) { case kind::BITVECTOR_PLUS : case kind::BITVECTOR_SUB : case kind::BITVECTOR_CONCAT : { - for (unsigned i =0; i < term.getNumChildren() - 1; ++i) { + Debug("pf::bv") << "Bitblasing kind = " << kind << std::endl; + + for (int i = term.getNumChildren() - 1; i > 0; --i) { os <<"(bv_bbl_"<< utils::toLFSCKind(kind); + if (kind == kind::BITVECTOR_CONCAT) { - os << " " << utils::getSize(term) <<" _ "; + os << " " << utils::getSize(term) << " _"; } - os <<" _ _ _ _ _ _ "; + os << " _ _ _ _ _ _ "; } - os << getBBTermName(term[0]) <<" "; + + os << getBBTermName(term[0]) << " "; for (unsigned i = 1; i < term.getNumChildren(); ++i) { os << getBBTermName(term[i]); @@ -463,22 +740,25 @@ void LFSCBitVectorProof::printTermBitblasting(Expr term, std::ostream& os) { } return; } + case kind::BITVECTOR_NEG : case kind::BITVECTOR_NOT : case kind::BITVECTOR_ROTATE_LEFT : case kind::BITVECTOR_ROTATE_RIGHT : { - os <<"(bv_bbl_"<<utils::toLFSCKind(kind); - os <<" _ _ _ _ "; + os << "(bv_bbl_"<<utils::toLFSCKind(kind); + os << " _ _ _ _ "; os << getBBTermName(term[0]); - os <<")"; + os << ")"; return; } case kind::BITVECTOR_EXTRACT : { - os <<"(bv_bbl_"<<utils::toLFSCKind(kind) <<" "; - os << utils::getSize(term) << " "; + os <<"(bv_bbl_"<<utils::toLFSCKind(kind); + + os << " " << utils::getSize(term) << " "; os << utils::getExtractHigh(term) << " "; os << utils::getExtractLow(term) << " "; os << " _ _ _ _ "; + os << getBBTermName(term[0]); os <<")"; return; @@ -486,8 +766,8 @@ void LFSCBitVectorProof::printTermBitblasting(Expr term, std::ostream& os) { case kind::BITVECTOR_REPEAT : case kind::BITVECTOR_ZERO_EXTEND : case kind::BITVECTOR_SIGN_EXTEND : { - os <<"(bv_bbl_"<<utils::toLFSCKind(kind) <<" "; - os << utils::getSize(term) <<" "; + os << "(bv_bbl_" << utils::toLFSCKind(kind) << " "; + os << utils::getSize(term) << " "; if (term.getKind() == kind::BITVECTOR_REPEAT) { unsigned amount = term.getOperator().getConst<BitVectorRepeat>().repeatAmount; os << amount; @@ -501,6 +781,7 @@ void LFSCBitVectorProof::printTermBitblasting(Expr term, std::ostream& os) { unsigned amount = term.getOperator().getConst<BitVectorZeroExtend>().zeroExtendAmount; os << amount; } + os <<" _ _ _ _ "; os << getBBTermName(term[0]); os <<")"; @@ -516,7 +797,7 @@ void LFSCBitVectorProof::printTermBitblasting(Expr term, std::ostream& os) { case kind::BITVECTOR_SHL : case kind::BITVECTOR_LSHR : case kind::BITVECTOR_ASHR : { - // these are terms for which bit-blasting is not supported yet + // these are terms for which bit-blasting is not supported yet std::ostringstream paren; os <<"(trust_bblast_term _ "; paren <<")"; @@ -539,7 +820,7 @@ void LFSCBitVectorProof::printTermBitblasting(Expr term, std::ostream& os) { } } -void LFSCBitVectorProof::printAtomBitblasting(Expr atom, std::ostream& os) { +void LFSCBitVectorProof::printAtomBitblasting(Expr atom, std::ostream& os, bool swap) { Kind kind = atom.getKind(); switch(kind) { case kind::BITVECTOR_ULT : @@ -550,11 +831,19 @@ void LFSCBitVectorProof::printAtomBitblasting(Expr atom, std::ostream& os) { case kind::BITVECTOR_SLE : case kind::BITVECTOR_SGT : case kind::BITVECTOR_SGE : - case kind::EQUAL: - { - os <<"(bv_bbl_" << utils::toLFSCKind(atom.getKind()); + case kind::EQUAL: { + Debug("pf::bv") << "Bitblasing kind = " << kind << std::endl; + + os << "(bv_bbl_" << utils::toLFSCKind(atom.getKind()); + + if (swap) {os << "_swap";} + os << " _ _ _ _ _ _ "; - os << getBBTermName(atom[0])<<" " << getBBTermName(atom[1]) <<")"; + os << getBBTermName(atom[0]); + os << " "; + os << getBBTermName(atom[1]); + os << ")"; + return; } default: @@ -562,19 +851,62 @@ void LFSCBitVectorProof::printAtomBitblasting(Expr atom, std::ostream& os) { } } +void LFSCBitVectorProof::printAtomBitblastingToFalse(Expr atom, std::ostream& os) { + Assert(atom.getKind() == kind::EQUAL); + + os << "(bv_bbl_=_false"; + os << " _ _ _ _ _ _ "; + os << getBBTermName(atom[0]); + + os << " "; + + os << getBBTermName(atom[1]); + + os << ")"; +} void LFSCBitVectorProof::printBitblasting(std::ostream& os, std::ostream& paren) { // bit-blast terms + { + Debug("pf::bv") << "LFSCBitVectorProof::printBitblasting: the bitblasted terms are: " << std::endl; + std::vector<Expr>::const_iterator it = d_bbTerms.begin(); + std::vector<Expr>::const_iterator end = d_bbTerms.end(); + + Assert(options::bitblastMode() != theory::bv::BITBLAST_MODE_EAGER); + + for (; it != end; ++it) { + if (d_usedBB.find(*it) == d_usedBB.end()) { + Debug("pf::bv") << "\t" << *it << "\t(UNUSED)" << std::endl; + } else { + Debug("pf::bv") << "\t" << *it << std::endl; + } + } + + Debug("pf::bv") << std::endl; + } + std::vector<Expr>::const_iterator it = d_bbTerms.begin(); std::vector<Expr>::const_iterator end = d_bbTerms.end(); for (; it != end; ++it) { if (d_usedBB.find(*it) == d_usedBB.end() && options::bitblastMode() != theory::bv::BITBLAST_MODE_EAGER) continue; - os <<"(decl_bblast _ _ _ "; - printTermBitblasting(*it, os); - os << "(\\ "<< getBBTermName(*it); - paren <<"\n))"; + + // Is this term has an alias, we inject it through the decl_bblast statement + if (hasAlias(*it)) { + os << "(decl_bblast_with_alias _ _ _ _ "; + printTermBitblasting(*it, os); + os << " " << d_aliasToBindDeclaration[d_assignedAliases[*it]] << " "; + os << "(\\ "<< getBBTermName(*it); + os << "\n"; + paren <<"))"; + } else { + os << "(decl_bblast _ _ _ "; + printTermBitblasting(*it, os); + os << "(\\ "<< getBBTermName(*it); + os << "\n"; + paren <<"))"; + } } // bit-blast atoms ExprToExpr::const_iterator ait = d_bbAtoms.begin(); @@ -589,7 +921,35 @@ void LFSCBitVectorProof::printBitblasting(std::ostream& os, std::ostream& paren) bool val = ait->first.getConst<bool>(); os << "(iff_symm " << (val ? "true" : "false" ) << ")"; } else { - printAtomBitblasting(ait->first, os); + Assert(ait->first == ait->second[0]); + + bool swap = false; + if (ait->first.getKind() == kind::EQUAL) { + Expr bitwiseEquivalence = ait->second[1]; + if ((bitwiseEquivalence.getKind() == kind::CONST_BOOLEAN) && !bitwiseEquivalence.getConst<bool>()) { + printAtomBitblastingToFalse(ait->first, os); + } else { + if (bitwiseEquivalence.getKind() != kind::AND) { + // Just one bit + if (bitwiseEquivalence.getNumChildren() > 0 && bitwiseEquivalence[0].getKind() == kind::BITVECTOR_BITOF) { + swap = (ait->first[1] == bitwiseEquivalence[0][0]); + } + } else { + // Multiple bits + if (bitwiseEquivalence[0].getNumChildren() > 0 && + bitwiseEquivalence[0][0].getKind() == kind::BITVECTOR_BITOF) { + swap = (ait->first[1] == bitwiseEquivalence[0][0][0]); + } else if (bitwiseEquivalence[0].getNumChildren() > 0 && + bitwiseEquivalence[0][1].getKind() == kind::BITVECTOR_BITOF) { + swap = (ait->first[0] == bitwiseEquivalence[0][1][0]); + } + } + + printAtomBitblasting(ait->first, os, swap); + } + } else { + printAtomBitblasting(ait->first, os, swap); + } } os <<"(\\ " << ProofManager::getPreprocessedAssertionName(ait->second) <<"\n"; @@ -597,34 +957,102 @@ void LFSCBitVectorProof::printBitblasting(std::ostream& os, std::ostream& paren) } } -void LFSCBitVectorProof::printResolutionProof(std::ostream& os, - std::ostream& paren) { - // collect the input clauses used +void LFSCBitVectorProof::calculateAtomsInBitblastingProof() { + // Collect the input clauses used IdToSatClause used_lemmas; IdToSatClause used_inputs; - d_resolutionProof->collectClausesUsed(used_inputs, - used_lemmas); - Assert (used_lemmas.empty()); + d_resolutionProof->collectClausesUsed(used_inputs, used_lemmas); + d_cnfProof->collectAtomsForClauses(used_inputs, d_atomsInBitblastingProof); + Assert(used_lemmas.empty()); +} + +const std::set<Node>* LFSCBitVectorProof::getAtomsInBitblastingProof() { + return &d_atomsInBitblastingProof; +} +void LFSCBitVectorProof::printResolutionProof(std::ostream& os, + std::ostream& paren, + ProofLetMap& letMap) { // print mapping between theory atoms and internal SAT variables - os << ";; BB atom mapping\n"; + os << std::endl << ";; BB atom mapping\n" << std::endl; - NodeSet atoms; - d_cnfProof->collectAtomsForClauses(used_inputs,atoms); + std::set<Node>::iterator atomIt; + Debug("pf::bv") << std::endl << "BV Dumping atoms from inputs: " << std::endl << std::endl; + for (atomIt = d_atomsInBitblastingProof.begin(); atomIt != d_atomsInBitblastingProof.end(); ++atomIt) { + Debug("pf::bv") << "\tAtom: " << *atomIt << std::endl; + } + Debug("pf::bv") << std::endl; // first print bit-blasting printBitblasting(os, paren); // print CNF conversion proof for bit-blasted facts - d_cnfProof->printAtomMapping(atoms, os, paren); - os << ";; Bit-blasting definitional clauses \n"; + IdToSatClause used_lemmas; + IdToSatClause used_inputs; + d_resolutionProof->collectClausesUsed(used_inputs, used_lemmas); + + d_cnfProof->printAtomMapping(d_atomsInBitblastingProof, os, paren, letMap); + os << std::endl << ";; Bit-blasting definitional clauses \n" << std::endl; for (IdToSatClause::iterator it = used_inputs.begin(); it != used_inputs.end(); ++it) { d_cnfProof->printCnfProofForClause(it->first, it->second, os, paren); } - os << ";; Bit-blasting learned clauses \n"; + os << std::endl << " ;; Bit-blasting learned clauses \n" << std::endl; d_resolutionProof->printResolutions(os, paren); } +std::string LFSCBitVectorProof::assignAlias(Expr expr) { + Assert(d_exprToVariableName.find(expr) == d_exprToVariableName.end()); + + std::stringstream ss; + ss << "fbv" << d_assignedAliases.size(); + Debug("pf::bv") << "assignAlias( " << expr << ") = " << ss.str() << std::endl; + d_assignedAliases[expr] = ss.str(); + return ss.str(); +} + +bool LFSCBitVectorProof::hasAlias(Expr expr) { + return d_assignedAliases.find(expr) != d_assignedAliases.end(); +} + +void LFSCBitVectorProof::printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2) { + Assert (c1.isConst()); + Assert (c2.isConst()); + Assert (utils::getSize(c1) == utils::getSize(c2)); + + os << "(bv_disequal_constants " << utils::getSize(c1) << " "; + + std::ostringstream paren; + + for (int i = utils::getSize(c1) - 1; i >= 0; --i) { + os << "(bvc "; + os << (utils::getBit(c1, i) ? "b1" : "b0") << " "; + paren << ")"; + } + os << "bvn"; + os << paren.str(); + + os << " "; + + for (int i = utils::getSize(c2) - 1; i >= 0; --i) { + os << "(bvc "; + os << (utils::getBit(c2, i) ? "b1" : "b0") << " "; + + } + os << "bvn"; + os << paren.str(); + + os << ")"; +} + +void LFSCBitVectorProof::printRewriteProof(std::ostream& os, const Node &n1, const Node &n2) { + ProofLetMap emptyMap; + os << "(rr_bv_default "; + d_proofEngine->printBoundTerm(n2.toExpr(), os, emptyMap); + os << " "; + d_proofEngine->printBoundTerm(n1.toExpr(), os, emptyMap); + os << ")"; +} + } /* namespace CVC4 */ diff --git a/src/proof/bitvector_proof.h b/src/proof/bitvector_proof.h index 4a1f4015d..a52292ec9 100644 --- a/src/proof/bitvector_proof.h +++ b/src/proof/bitvector_proof.h @@ -60,6 +60,7 @@ typedef __gnu_cxx::hash_set<Expr, ExprHashFunction> ExprSet; typedef __gnu_cxx::hash_map<Expr, ClauseId, ExprHashFunction> ExprToClauseId; typedef __gnu_cxx::hash_map<Expr, unsigned, ExprHashFunction> ExprToId; typedef __gnu_cxx::hash_map<Expr, Expr, ExprHashFunction> ExprToExpr; +typedef __gnu_cxx::hash_map<Expr, std::string, ExprHashFunction> ExprToString; class BitVectorProof : public TheoryProof { protected: @@ -108,35 +109,52 @@ public: virtual void registerTerm(Expr term); virtual void printTermBitblasting(Expr term, std::ostream& os) = 0; - virtual void printAtomBitblasting(Expr term, std::ostream& os) = 0; + virtual void printAtomBitblasting(Expr term, std::ostream& os, bool swap) = 0; + virtual void printAtomBitblastingToFalse(Expr term, std::ostream& os) = 0; virtual void printBitblasting(std::ostream& os, std::ostream& paren) = 0; - virtual void printResolutionProof(std::ostream& os, std::ostream& paren) = 0; - + virtual void printResolutionProof(std::ostream& os, std::ostream& paren, ProofLetMap& letMap) = 0; + virtual const std::set<Node>* getAtomsInBitblastingProof() = 0; + virtual void calculateAtomsInBitblastingProof() = 0; }; class LFSCBitVectorProof: public BitVectorProof { void printConstant(Expr term, std::ostream& os); - void printOperatorNary(Expr term, std::ostream& os, const LetMap& map); - void printOperatorUnary(Expr term, std::ostream& os, const LetMap& map); - void printPredicate(Expr term, std::ostream& os, const LetMap& map); - void printOperatorParametric(Expr term, std::ostream& os, const LetMap& map); - void printBitOf(Expr term, std::ostream& os, const LetMap& map); + void printOperatorNary(Expr term, std::ostream& os, const ProofLetMap& map); + void printOperatorUnary(Expr term, std::ostream& os, const ProofLetMap& map); + void printPredicate(Expr term, std::ostream& os, const ProofLetMap& map); + void printOperatorParametric(Expr term, std::ostream& os, const ProofLetMap& map); + void printBitOf(Expr term, std::ostream& os, const ProofLetMap& map); + + ExprToString d_exprToVariableName; + ExprToString d_assignedAliases; + std::map<std::string, std::string> d_aliasToBindDeclaration; + std::string assignAlias(Expr expr); + bool hasAlias(Expr expr); + + std::set<Node> d_atomsInBitblastingProof; + public: LFSCBitVectorProof(theory::bv::TheoryBV* bv, TheoryProofEngine* proofEngine) :BitVectorProof(bv, proofEngine) {} - virtual void printOwnedTerm(Expr term, std::ostream& os, const LetMap& map); + virtual void printOwnedTerm(Expr term, std::ostream& os, const ProofLetMap& map); virtual void printOwnedSort(Type type, std::ostream& os); virtual void printTermBitblasting(Expr term, std::ostream& os); - virtual void printAtomBitblasting(Expr term, std::ostream& os); - virtual void printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren); + virtual void printAtomBitblasting(Expr term, std::ostream& os, bool swap); + virtual void printAtomBitblastingToFalse(Expr term, std::ostream& os); + virtual void printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren, const ProofLetMap& map); virtual void printSortDeclarations(std::ostream& os, std::ostream& paren); virtual void printTermDeclarations(std::ostream& os, std::ostream& paren); virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren); + virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren); virtual void printBitblasting(std::ostream& os, std::ostream& paren); - virtual void printResolutionProof(std::ostream& os, std::ostream& paren); + virtual void printResolutionProof(std::ostream& os, std::ostream& paren, ProofLetMap& letMap); + void calculateAtomsInBitblastingProof(); + const std::set<Node>* getAtomsInBitblastingProof(); + void printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2); + void printRewriteProof(std::ostream& os, const Node &n1, const Node &n2); }; }/* CVC4 namespace */ diff --git a/src/proof/cnf_proof.cpp b/src/proof/cnf_proof.cpp index 19e9cbac9..b58ade35e 100644 --- a/src/proof/cnf_proof.cpp +++ b/src/proof/cnf_proof.cpp @@ -19,6 +19,7 @@ #include "proof/clause_id.h" #include "proof/proof_manager.h" +#include "proof/proof_utils.h" #include "proof/theory_proof.h" #include "prop/cnf_stream.h" #include "prop/minisat/minisat.h" @@ -32,7 +33,6 @@ CnfProof::CnfProof(prop::CnfStream* stream, : d_cnfStream(stream) , d_clauseToAssertion(ctx) , d_assertionToProofRule(ctx) - , d_clauseIdToOwnerTheory(ctx) , d_currentAssertionStack() , d_currentDefinitionStack() , d_clauseToDefinition(ctx) @@ -103,7 +103,6 @@ void CnfProof::registerConvertedClause(ClauseId clause, bool explanation) { setClauseAssertion(clause, current_assertion); setClauseDefinition(clause, current_expr); - registerExplanationLemma(clause); } void CnfProof::setClauseAssertion(ClauseId clause, Node expr) { @@ -143,16 +142,15 @@ void CnfProof::registerAssertion(Node assertion, ProofRule reason) { d_assertionToProofRule.insert(assertion, reason); } -void CnfProof::registerExplanationLemma(ClauseId clauseId) { - d_clauseIdToOwnerTheory.insert(clauseId, getExplainerTheory()); +LemmaProofRecipe CnfProof::getProofRecipe(const std::set<Node> &lemma) { + Assert(d_lemmaToProofRecipe.find(lemma) != d_lemmaToProofRecipe.end()); + return d_lemmaToProofRecipe[lemma]; } -theory::TheoryId CnfProof::getOwnerTheory(ClauseId clause) { - Assert(d_clauseIdToOwnerTheory.find(clause) != d_clauseIdToOwnerTheory.end()); - return d_clauseIdToOwnerTheory[clause]; +bool CnfProof::haveProofRecipe(const std::set<Node> &lemma) { + return d_lemmaToProofRecipe.find(lemma) != d_lemmaToProofRecipe.end(); } - void CnfProof::setCnfDependence(Node from, Node to) { Debug("proof:cnf") << "CnfProof::setCnfDependence " << "from " << from << std::endl @@ -183,12 +181,10 @@ Node CnfProof::getCurrentAssertion() { return d_currentAssertionStack.back(); } -void CnfProof::setExplainerTheory(theory::TheoryId theory) { - d_explainerTheory = theory; -} - -theory::TheoryId CnfProof::getExplainerTheory() { - return d_explainerTheory; +void CnfProof::setProofRecipe(LemmaProofRecipe* proofRecipe) { + Assert(proofRecipe); + Assert(proofRecipe->getNumSteps() > 0); + d_lemmaToProofRecipe[proofRecipe->getBaseAssertions()] = *proofRecipe; } void CnfProof::pushCurrentDefinition(Node definition) { @@ -212,22 +208,19 @@ Node CnfProof::getCurrentDefinition() { return d_currentDefinitionStack.back(); } - Node CnfProof::getAtom(prop::SatVariable var) { prop::SatLiteral lit (var); Node node = d_cnfStream->getNode(lit); return node; } - void CnfProof::collectAtoms(const prop::SatClause* clause, - NodeSet& atoms) { + std::set<Node>& atoms) { for (unsigned i = 0; i < clause->size(); ++i) { prop::SatLiteral lit = clause->operator[](i); prop::SatVariable var = lit.getSatVariable(); TNode atom = getAtom(var); if (atoms.find(atom) == atoms.end()) { - Assert (atoms.find(atom) == atoms.end()); atoms.insert(atom); } } @@ -237,14 +230,75 @@ prop::SatLiteral CnfProof::getLiteral(TNode atom) { return d_cnfStream->getLiteral(atom); } +bool CnfProof::hasLiteral(TNode atom) { + return d_cnfStream->hasLiteral(atom); +} + +void CnfProof::ensureLiteral(TNode atom, bool noPreregistration) { + d_cnfStream->ensureLiteral(atom, noPreregistration); +} + void CnfProof::collectAtomsForClauses(const IdToSatClause& clauses, - NodeSet& atom_map) { + std::set<Node>& atoms) { IdToSatClause::const_iterator it = clauses.begin(); for (; it != clauses.end(); ++it) { const prop::SatClause* clause = it->second; - collectAtoms(clause, atom_map); + collectAtoms(clause, atoms); } +} + +void CnfProof::collectAtomsAndRewritesForLemmas(const IdToSatClause& lemmaClauses, + std::set<Node>& atoms, + NodePairSet& rewrites) { + IdToSatClause::const_iterator it = lemmaClauses.begin(); + for (; it != lemmaClauses.end(); ++it) { + const prop::SatClause* clause = it->second; + + // TODO: just calculate the map from ID to recipe once, + // instead of redoing this over and over again + std::vector<Expr> clause_expr; + std::set<Node> clause_expr_nodes; + for(unsigned i = 0; i < clause->size(); ++i) { + prop::SatLiteral lit = (*clause)[i]; + Node node = getAtom(lit.getSatVariable()); + Expr atom = node.toExpr(); + if (atom.isConst()) { + Assert (atom == utils::mkTrue()); + continue; + } + clause_expr_nodes.insert(lit.isNegated() ? node.notNode() : node); + } + + LemmaProofRecipe recipe = getProofRecipe(clause_expr_nodes); + + for (unsigned i = 0; i < recipe.getNumSteps(); ++i) { + const LemmaProofRecipe::ProofStep* proofStep = recipe.getStep(i); + Node atom = proofStep->getLiteral(); + + if (atom == Node()) { + // The last proof step always has the empty node as its target... + continue; + } + + if (atom.getKind() == kind::NOT) { + atom = atom[0]; + } + + atoms.insert(atom); + } + + LemmaProofRecipe::RewriteIterator rewriteIt; + for (rewriteIt = recipe.rewriteBegin(); rewriteIt != recipe.rewriteEnd(); ++rewriteIt) { + rewrites.insert(NodePair(rewriteIt->first, rewriteIt->second)); + // The unrewritten terms also need to have literals, so insert them into atoms + Node rewritten = rewriteIt->first; + if (rewritten.getKind() == kind::NOT) { + rewritten = rewritten[0]; + } + atoms.insert(rewritten); + } + } } void CnfProof::collectAssertionsForClauses(const IdToSatClause& clauses, @@ -263,13 +317,13 @@ void CnfProof::collectAssertionsForClauses(const IdToSatClause& clauses, } } -void LFSCCnfProof::printAtomMapping(const NodeSet& atoms, +void LFSCCnfProof::printAtomMapping(const std::set<Node>& atoms, std::ostream& os, std::ostream& paren) { - NodeSet::const_iterator it = atoms.begin(); - NodeSet::const_iterator end = atoms.end(); + std::set<Node>::const_iterator it = atoms.begin(); + std::set<Node>::const_iterator end = atoms.end(); - for (;it != end; ++it) { + for (;it != end; ++it) { os << "(decl_atom "; Node atom = *it; prop::SatVariable var = getLiteral(atom).getSatVariable(); @@ -277,8 +331,30 @@ void LFSCCnfProof::printAtomMapping(const NodeSet& atoms, LFSCTheoryProofEngine* pe = (LFSCTheoryProofEngine*)ProofManager::currentPM()->getTheoryProofEngine(); pe->printLetTerm(atom.toExpr(), os); - os << " (\\ " << ProofManager::getVarName(var, d_name) - << " (\\ " << ProofManager::getAtomName(var, d_name) << "\n"; + os << " (\\ " << ProofManager::getVarName(var, d_name); + os << " (\\ " << ProofManager::getAtomName(var, d_name) << "\n"; + paren << ")))"; + } +} + +void LFSCCnfProof::printAtomMapping(const std::set<Node>& atoms, + std::ostream& os, + std::ostream& paren, + ProofLetMap &letMap) { + std::set<Node>::const_iterator it = atoms.begin(); + std::set<Node>::const_iterator end = atoms.end(); + + for (;it != end; ++it) { + os << "(decl_atom "; + Node atom = *it; + prop::SatVariable var = getLiteral(atom).getSatVariable(); + //FIXME hideous + LFSCTheoryProofEngine* pe = (LFSCTheoryProofEngine*)ProofManager::currentPM()->getTheoryProofEngine(); + // pe->printLetTerm(atom.toExpr(), os); + pe->printBoundTerm(atom.toExpr(), os, letMap); + + os << " (\\ " << ProofManager::getVarName(var, d_name); + os << " (\\ " << ProofManager::getAtomName(var, d_name) << "\n"; paren << ")))"; } } @@ -304,6 +380,9 @@ void LFSCCnfProof::printCnfProofForClause(ClauseId id, const prop::SatClause* clause, std::ostream& os, std::ostream& paren) { + Debug("cnf-pf") << std::endl << std::endl << "LFSCCnfProof::printCnfProofForClause( " << id << " ) starting " + << std::endl; + os << "(satlem _ _ "; std::ostringstream clause_paren; printClause(*clause, os, clause_paren); @@ -336,6 +415,10 @@ void LFSCCnfProof::printCnfProofForClause(ClauseId id, // and prints the proof of the top-level formula bool is_input = printProofTopLevel(base_assertion, os_base); + if (is_input) { + Debug("cnf-pf") << std::endl << "; base assertion is input. proof: " << os_base.str() << std::endl; + } + //get base assertion with polarity bool base_pol = base_assertion.getKind()!=kind::NOT; base_assertion = base_assertion.getKind()==kind::NOT ? base_assertion[0] : base_assertion; @@ -564,6 +647,7 @@ void LFSCCnfProof::printCnfProofForClause(ClauseId id, if( !pols[0] || num_nots_1==1 ){ os_base_n << "(not_not_intro _ " << ProofManager::getLitName(lit1, d_name) << ") "; }else{ + Trace("cnf-pf-debug") << "CALLING getlitname" << std::endl; os_base_n << ProofManager::getLitName(lit1, d_name) << " "; } Assert( elimNum!=0 ); @@ -662,6 +746,7 @@ void LFSCCnfProof::printCnfProofForClause(ClauseId id, os << ")" << clause_paren.str() << " (\\ " << ProofManager::getInputClauseName(id, d_name) << "\n"; + paren << "))"; } diff --git a/src/proof/cnf_proof.h b/src/proof/cnf_proof.h index a21cb1c0e..788526b80 100644 --- a/src/proof/cnf_proof.h +++ b/src/proof/cnf_proof.h @@ -27,6 +27,7 @@ #include "context/cdhashmap.h" #include "proof/clause_id.h" +#include "proof/lemma_proof.h" #include "proof/sat_proof.h" #include "util/proof.h" @@ -43,7 +44,9 @@ typedef __gnu_cxx::hash_set<ClauseId> ClauseIdSet; typedef context::CDHashMap<ClauseId, Node> ClauseIdToNode; typedef context::CDHashMap<Node, ProofRule, NodeHashFunction> NodeToProofRule; -typedef context::CDHashMap<ClauseId, theory::TheoryId> ClauseIdToTheory; +typedef std::map<std::set<Node>, LemmaProofRecipe> LemmaToRecipe; +typedef std::pair<Node, Node> NodePair; +typedef std::set<NodePair> NodePairSet; class CnfProof { protected: @@ -55,11 +58,8 @@ protected: /** Map from assertion to reason for adding assertion **/ NodeToProofRule d_assertionToProofRule; - /** Map from assertion to the theory that added this assertion **/ - ClauseIdToTheory d_clauseIdToOwnerTheory; - - /** The last theory to explain a lemma **/ - theory::TheoryId d_explainerTheory; + /** Map from lemma to the recipe for proving it **/ + LemmaToRecipe d_lemmaToProofRecipe; /** Top of stack is assertion currently being converted to CNF **/ std::vector<Node> d_currentAssertionStack; @@ -91,10 +91,16 @@ public: Node getAtom(prop::SatVariable var); prop::SatLiteral getLiteral(TNode node); + bool hasLiteral(TNode node); + void ensureLiteral(TNode node, bool noPreregistration = false); + void collectAtoms(const prop::SatClause* clause, - NodeSet& atoms); + std::set<Node>& atoms); void collectAtomsForClauses(const IdToSatClause& clauses, - NodeSet& atoms); + std::set<Node>& atoms); + void collectAtomsAndRewritesForLemmas(const IdToSatClause& lemmaClauses, + std::set<Node>& atoms, + NodePairSet& rewrites); void collectAssertionsForClauses(const IdToSatClause& clauses, NodeSet& assertions); @@ -121,11 +127,9 @@ public: void popCurrentDefinition(); Node getCurrentDefinition(); - void setExplainerTheory(theory::TheoryId theory); - theory::TheoryId getExplainerTheory(); - theory::TheoryId getOwnerTheory(ClauseId clause); - - void registerExplanationLemma(ClauseId clauseId); + void setProofRecipe(LemmaProofRecipe* proofRecipe); + LemmaProofRecipe getProofRecipe(const std::set<Node> &lemma); + bool haveProofRecipe(const std::set<Node> &lemma); // accessors for the leaf assertions that are being converted to CNF bool isAssertion(Node node); @@ -134,9 +138,13 @@ public: Node getAssertionForClause(ClauseId clause); /** Virtual methods for printing things **/ - virtual void printAtomMapping(const NodeSet& atoms, + virtual void printAtomMapping(const std::set<Node>& atoms, std::ostream& os, std::ostream& paren) = 0; + virtual void printAtomMapping(const std::set<Node>& atoms, + std::ostream& os, + std::ostream& paren, + ProofLetMap &letMap) = 0; virtual void printClause(const prop::SatClause& clause, std::ostream& os, @@ -161,10 +169,15 @@ public: {} ~LFSCCnfProof() {} - void printAtomMapping(const NodeSet& atoms, + void printAtomMapping(const std::set<Node>& atoms, std::ostream& os, std::ostream& paren); + void printAtomMapping(const std::set<Node>& atoms, + std::ostream& os, + std::ostream& paren, + ProofLetMap &letMap); + void printClause(const prop::SatClause& clause, std::ostream& os, std::ostream& paren); diff --git a/src/proof/lemma_proof.cpp b/src/proof/lemma_proof.cpp new file mode 100644 index 000000000..a12a516cf --- /dev/null +++ b/src/proof/lemma_proof.cpp @@ -0,0 +1,193 @@ +/********************* */ +/*! \file lemma_proof.h +** \verbatim +** +** \brief A class for recoding the steps required in order to prove a theory lemma. +** +** A class for recoding the steps required in order to prove a theory lemma. +** +**/ + +#include "proof/lemma_proof.h" +#include "theory/rewriter.h" + +namespace CVC4 { + +LemmaProofRecipe::ProofStep::ProofStep(theory::TheoryId theory, Node literalToProve) : + d_theory(theory), d_literalToProve(literalToProve) { +} + +theory::TheoryId LemmaProofRecipe::ProofStep::getTheory() const { + return d_theory; +} + +Node LemmaProofRecipe::ProofStep::getLiteral() const { + return d_literalToProve; +} + +void LemmaProofRecipe::ProofStep::addAssertion(const Node& assertion) { + d_assertions.insert(assertion); +} + +std::set<Node> LemmaProofRecipe::ProofStep::getAssertions() const { + return d_assertions; +} + +void LemmaProofRecipe::addStep(ProofStep& proofStep) { + std::list<ProofStep>::iterator existingFirstStep = d_proofSteps.begin(); + d_proofSteps.push_front(proofStep); +} + +std::set<Node> LemmaProofRecipe::getMissingAssertionsForStep(unsigned index) const { + Assert(index < d_proofSteps.size()); + + std::set<Node> existingAssertions = getBaseAssertions(); + + std::list<ProofStep>::const_iterator step = d_proofSteps.begin(); + while (index != 0) { + existingAssertions.insert(step->getLiteral().negate()); + ++step; + --index; + } + + std::set<Node> neededAssertions = step->getAssertions(); + + std::set<Node> result; + std::set_difference(neededAssertions.begin(), neededAssertions.end(), + existingAssertions.begin(), existingAssertions.end(), + std::inserter(result, result.begin())); + return result; +} + +void LemmaProofRecipe::dump(const char *tag) const { + + if (d_proofSteps.size() == 1) { + Debug(tag) << std::endl << "[Simple lemma]" << std::endl << std::endl; + } + + unsigned count = 1; + Debug(tag) << "Base assertions:" << std::endl; + for (std::set<Node>::iterator baseIt = d_baseAssertions.begin(); + baseIt != d_baseAssertions.end(); + ++baseIt) { + Debug(tag) << "\t#" << count << ": " << "\t" << *baseIt << std::endl; + ++count; + } + + Debug(tag) << std::endl << std::endl << "Proof steps:" << std::endl; + + count = 1; + for (std::list<ProofStep>::const_iterator step = d_proofSteps.begin(); step != d_proofSteps.end(); ++step) { + Debug(tag) << "\tStep #" << count << ": " << "\t[" << step->getTheory() << "] "; + if (step->getLiteral() == Node()) { + Debug(tag) << "Contradiction"; + } else { + Debug(tag) << step->getLiteral(); + } + + Debug(tag) << std::endl; + + std::set<Node> missingAssertions = getMissingAssertionsForStep(count - 1); + for (std::set<Node>::const_iterator it = missingAssertions.begin(); it != missingAssertions.end(); ++it) { + Debug(tag) << "\t\t\tMissing assertion for step: " << *it << std::endl; + } + + Debug(tag) << std::endl; + ++count; + } + + if (!d_assertionToExplanation.empty()) { + Debug(tag) << std::endl << "Rewrites used:" << std::endl; + count = 1; + for (std::map<Node, Node>::const_iterator rewrite = d_assertionToExplanation.begin(); + rewrite != d_assertionToExplanation.end(); + ++rewrite) { + Debug(tag) << "\tRewrite #" << count << ":" << std::endl + << "\t\t" << rewrite->first + << std::endl << "\t\trewritten into" << std::endl + << "\t\t" << rewrite->second + << std::endl << std::endl; + ++count; + } + } +} + +void LemmaProofRecipe::addBaseAssertion(Node baseAssertion) { + d_baseAssertions.insert(baseAssertion); +} + +std::set<Node> LemmaProofRecipe::getBaseAssertions() const { + return d_baseAssertions; +} + +theory::TheoryId LemmaProofRecipe::getTheory() const { + Assert(d_proofSteps.size() > 0); + return d_proofSteps.back().getTheory(); +} + +void LemmaProofRecipe::addRewriteRule(Node assertion, Node explanation) { + if (d_assertionToExplanation.find(assertion) != d_assertionToExplanation.end()) { + Assert(d_assertionToExplanation[assertion] == explanation); + } + + d_assertionToExplanation[assertion] = explanation; +} + +bool LemmaProofRecipe::wasRewritten(Node assertion) const { + return d_assertionToExplanation.find(assertion) != d_assertionToExplanation.end(); +} + +Node LemmaProofRecipe::getExplanation(Node assertion) const { + Assert(d_assertionToExplanation.find(assertion) != d_assertionToExplanation.end()); + return d_assertionToExplanation.find(assertion)->second; +} + +LemmaProofRecipe::RewriteIterator LemmaProofRecipe::rewriteBegin() const { + return d_assertionToExplanation.begin(); +} + +LemmaProofRecipe::RewriteIterator LemmaProofRecipe::rewriteEnd() const { + return d_assertionToExplanation.end(); +} + +bool LemmaProofRecipe::operator<(const LemmaProofRecipe& other) const { + return d_baseAssertions < other.d_baseAssertions; + } + +bool LemmaProofRecipe::simpleLemma() const { + return d_proofSteps.size() == 1; +} + +bool LemmaProofRecipe::compositeLemma() const { + return !simpleLemma(); +} + +const LemmaProofRecipe::ProofStep* LemmaProofRecipe::getStep(unsigned index) const { + Assert(index < d_proofSteps.size()); + + std::list<ProofStep>::const_iterator it = d_proofSteps.begin(); + while (index != 0) { + ++it; + --index; + } + + return &(*it); +} + +LemmaProofRecipe::ProofStep* LemmaProofRecipe::getStep(unsigned index) { + Assert(index < d_proofSteps.size()); + + std::list<ProofStep>::iterator it = d_proofSteps.begin(); + while (index != 0) { + ++it; + --index; + } + + return &(*it); +} + +unsigned LemmaProofRecipe::getNumSteps() const { + return d_proofSteps.size(); +} + +} /* namespace CVC4 */ diff --git a/src/proof/lemma_proof.h b/src/proof/lemma_proof.h new file mode 100644 index 000000000..e96ff5337 --- /dev/null +++ b/src/proof/lemma_proof.h @@ -0,0 +1,79 @@ +/********************* */ +/*! \file lemma_proof.h +** \verbatim +** +** \brief A class for recoding the steps required in order to prove a theory lemma. +** +** A class for recoding the steps required in order to prove a theory lemma. +** +**/ + +#include "cvc4_private.h" + +#ifndef __CVC4__LEMMA_PROOF_H +#define __CVC4__LEMMA_PROOF_H + +#include "expr/expr.h" +#include "proof/clause_id.h" +#include "prop/sat_solver_types.h" +#include "util/proof.h" +#include "expr/node.h" + +namespace CVC4 { + +class LemmaProofRecipe { +public: + class ProofStep { + public: + ProofStep(theory::TheoryId theory, Node literalToProve); + theory::TheoryId getTheory() const; + Node getLiteral() const; + void addAssertion(const Node& assertion); + std::set<Node> getAssertions() const; + + private: + theory::TheoryId d_theory; + Node d_literalToProve; + std::set<Node> d_assertions; + }; + + //* The lemma assertions and owner */ + void addBaseAssertion(Node baseAssertion); + std::set<Node> getBaseAssertions() const; + theory::TheoryId getTheory() const; + + //* Rewrite rules */ + typedef std::map<Node, Node>::const_iterator RewriteIterator; + RewriteIterator rewriteBegin() const; + RewriteIterator rewriteEnd() const; + + void addRewriteRule(Node assertion, Node explanation); + bool wasRewritten(Node assertion) const; + Node getExplanation(Node assertion) const; + + //* Proof Steps */ + void addStep(ProofStep& proofStep); + const ProofStep* getStep(unsigned index) const; + ProofStep* getStep(unsigned index); + unsigned getNumSteps() const; + std::set<Node> getMissingAssertionsForStep(unsigned index) const; + bool simpleLemma() const; + bool compositeLemma() const; + + void dump(const char *tag) const; + bool operator<(const LemmaProofRecipe& other) const; + +private: + //* The list of assertions for this lemma */ + std::set<Node> d_baseAssertions; + + //* The various steps needed to derive the empty clause */ + std::list<ProofStep> d_proofSteps; + + //* A map from assertions to their rewritten explanations (toAssert --> toExplain) */ + std::map<Node, Node> d_assertionToExplanation; +}; + +} /* CVC4 namespace */ + +#endif /* __CVC4__LEMMA_PROOF_H */ diff --git a/src/proof/proof_manager.cpp b/src/proof/proof_manager.cpp index a3689d746..d155630e5 100644 --- a/src/proof/proof_manager.cpp +++ b/src/proof/proof_manager.cpp @@ -20,6 +20,7 @@ #include "base/cvc4_assert.h" #include "context/context.h" #include "options/bv_options.h" +#include "options/proof_options.h" #include "proof/bitvector_proof.h" #include "proof/clause_id.h" #include "proof/cnf_proof.h" @@ -61,10 +62,10 @@ ProofManager::ProofManager(ProofFormat format): } ProofManager::~ProofManager() { - delete d_satProof; - delete d_cnfProof; - delete d_theoryProof; - delete d_fullProof; + if (d_satProof) delete d_satProof; + if (d_cnfProof) delete d_cnfProof; + if (d_theoryProof) delete d_theoryProof; + if (d_fullProof) delete d_fullProof; } ProofManager* ProofManager::currentPM() { @@ -95,7 +96,6 @@ CnfProof* ProofManager::getCnfProof() { } TheoryProofEngine* ProofManager::getTheoryProofEngine() { - Assert (options::proof()); Assert (currentPM()->d_theoryProof != NULL); return currentPM()->d_theoryProof; } @@ -200,7 +200,6 @@ std::string ProofManager::getLitName(prop::SatLiteral lit, return append(prefix+".l", lit.toInt()); } - std::string ProofManager::getPreprocessedAssertionName(Node node, const std::string& prefix) { node = node.getKind() == kind::BITVECTOR_EAGER_ATOM ? node[0] : node; @@ -217,9 +216,15 @@ std::string ProofManager::getAtomName(TNode atom, Assert(!lit.isNegated()); return getAtomName(lit.getSatVariable(), prefix); } + std::string ProofManager::getLitName(TNode lit, const std::string& prefix) { - return getLitName(currentPM()->d_cnfProof->getLiteral(lit), prefix); + std::string litName = getLitName(currentPM()->d_cnfProof->getLiteral(lit), prefix); + if (currentPM()->d_rewriteFilters.find(litName) != currentPM()->d_rewriteFilters.end()) { + return currentPM()->d_rewriteFilters[litName]; + } + + return litName; } std::string ProofManager::sanitize(TNode node) { @@ -330,7 +335,14 @@ LFSCProof::LFSCProof(SmtEngine* smtEngine, , d_smtEngine(smtEngine) {} +void LFSCProof::toStream(std::ostream& out, const ProofLetMap& map) { + Unreachable(); +} + void LFSCProof::toStream(std::ostream& out) { + + Assert(options::bitblastMode() != theory::bv::BITBLAST_MODE_EAGER); + d_satProof->constructProof(); // collecting leaf clauses in resolution proof @@ -384,8 +396,37 @@ void LFSCProof::toStream(std::ostream& out) { for (it3 = used_assertions.begin(); it3 != used_assertions.end(); ++it3) Debug("pf::pm") << "\t assertion = " << *it3 << std::endl; - NodeSet atoms; + std::set<Node> atoms; + NodePairSet rewrites; // collects the atoms in the clauses + d_cnfProof->collectAtomsAndRewritesForLemmas(used_lemmas, atoms, rewrites); + + if (!rewrites.empty()) { + Debug("pf::pm") << std::endl << "Rewrites used in lemmas: " << std::endl; + NodePairSet::const_iterator rewriteIt; + for (rewriteIt = rewrites.begin(); rewriteIt != rewrites.end(); ++rewriteIt) { + Debug("pf::pm") << "\t" << rewriteIt->first << " --> " << rewriteIt->second << std::endl; + } + Debug("pf::pm") << std::endl << "Rewrite printing done" << std::endl; + } else { + Debug("pf::pm") << "No rewrites in lemmas found" << std::endl; + } + + // The derived/unrewritten atoms may not have CNF literals required later on. + // If they don't, add them. + std::set<Node>::const_iterator it; + for (it = atoms.begin(); it != atoms.end(); ++it) { + Debug("pf::pm") << "Ensure literal for atom: " << *it << std::endl; + if (!d_cnfProof->hasLiteral(*it)) { + // For arithmetic: these literals are not normalized, causing an error in Arith. + if (theory::Theory::theoryOf(*it) == theory::THEORY_ARITH) { + d_cnfProof->ensureLiteral(*it, true); // This disables preregistration with the theory solver. + } else { + d_cnfProof->ensureLiteral(*it); // Normal method, with theory solver preregisteration. + } + } + } + d_cnfProof->collectAtomsForClauses(used_inputs, atoms); d_cnfProof->collectAtomsForClauses(used_lemmas, atoms); @@ -393,38 +434,23 @@ void LFSCProof::toStream(std::ostream& out) { for (NodeSet::const_iterator it = used_assertions.begin(); it != used_assertions.end(); ++it) { utils::collectAtoms(*it, atoms); + // utils::collectAtoms(*it, newAtoms); } - NodeSet::iterator atomIt; - Debug("pf::pm") << std::endl << "Dumping atoms from lemmas, inputs and assertions: " << std::endl << std::endl; + std::set<Node>::iterator atomIt; + Debug("pf::pm") << std::endl << "Dumping atoms from lemmas, inputs and assertions: " + << std::endl << std::endl; for (atomIt = atoms.begin(); atomIt != atoms.end(); ++atomIt) { Debug("pf::pm") << "\tAtom: " << *atomIt << std::endl; - - if (Debug.isOn("proof:pm")) { - // std::cout << NodeManager::currentNM(); - Debug("proof:pm") << "LFSCProof::Used assertions: "<< std::endl; - for(NodeSet::const_iterator it = used_assertions.begin(); it != used_assertions.end(); ++it) { - Debug("proof:pm") << " " << *it << std::endl; - } - - Debug("proof:pm") << "LFSCProof::Used atoms: "<< std::endl; - for(NodeSet::const_iterator it = atoms.begin(); it != atoms.end(); ++it) { - Debug("proof:pm") << " " << *it << std::endl; - } - } } - smt::SmtScope scope(d_smtEngine); std::ostringstream paren; out << "(check\n"; out << " ;; Declarations\n"; // declare the theory atoms - NodeSet::const_iterator it = atoms.begin(); - NodeSet::const_iterator end = atoms.end(); - Debug("pf::pm") << "LFSCProof::toStream: registering terms:" << std::endl; - for(; it != end; ++it) { + for(it = atoms.begin(); it != atoms.end(); ++it) { Debug("pf::pm") << "\tTerm: " << (*it).toExpr() << std::endl; d_theoryProof->registerTerm((*it).toExpr()); } @@ -444,15 +470,31 @@ void LFSCProof::toStream(std::ostream& out) { out << "(: (holds cln)\n\n"; // Have the theory proofs print deferred declarations, e.g. for skolem variables. - out << " ;; Printing deferred declarations \n"; + out << " ;; Printing deferred declarations \n\n"; d_theoryProof->printDeferredDeclarations(out, paren); + d_theoryProof->finalizeBvConflicts(used_lemmas, out); + ProofManager::getBitVectorProof()->calculateAtomsInBitblastingProof(); + + out << "\n ;; Printing the global let map \n"; + + ProofLetMap globalLetMap; + if (options::lfscLetification()) { + ProofManager::currentPM()->printGlobalLetMap(atoms, globalLetMap, out, paren); + } + + out << " ;; Printing aliasing declarations \n\n"; + d_theoryProof->printAliasingDeclarations(out, paren); + + out << " ;; Rewrites for Lemmas \n"; + d_theoryProof->printLemmaRewrites(rewrites, out, paren); + // print trust that input assertions are their preprocessed form - printPreprocessedAssertions(used_assertions, out, paren); + printPreprocessedAssertions(used_assertions, out, paren, globalLetMap); // print mapping between theory atoms and internal SAT variables out << ";; Printing mapping from preprocessed assertions into atoms \n"; - d_cnfProof->printAtomMapping(atoms, out, paren); + d_cnfProof->printAtomMapping(atoms, out, paren, globalLetMap); Debug("pf::pm") << std::endl << "Printing cnf proof for clauses" << std::endl; @@ -464,12 +506,8 @@ void LFSCProof::toStream(std::ostream& out) { Debug("pf::pm") << std::endl << "Printing cnf proof for clauses DONE" << std::endl; - // FIXME: for now assume all theory lemmas are in CNF form so - // distinguish between them and inputs - // print theory lemmas for resolution proof - Debug("pf::pm") << "Proof manager: printing theory lemmas" << std::endl; - d_theoryProof->printTheoryLemmas(used_lemmas, out, paren); + d_theoryProof->printTheoryLemmas(used_lemmas, out, paren, globalLetMap); Debug("pf::pm") << "Proof manager: printing theory lemmas DONE!" << std::endl; if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER && ProofManager::getBitVectorProof()) { @@ -491,22 +529,24 @@ void LFSCProof::toStream(std::ostream& out) { void LFSCProof::printPreprocessedAssertions(const NodeSet& assertions, std::ostream& os, - std::ostream& paren) { + std::ostream& paren, + ProofLetMap& globalLetMap) { os << "\n ;; In the preprocessor we trust \n"; NodeSet::const_iterator it = assertions.begin(); NodeSet::const_iterator end = assertions.end(); + Debug("pf::pm") << "LFSCProof::printPreprocessedAssertions starting" << std::endl; + for (; it != end; ++it) { os << "(th_let_pf _ "; //TODO os << "(trust_f "; - ProofManager::currentPM()->getTheoryProofEngine()->printLetTerm((*it).toExpr(), os); + ProofManager::currentPM()->getTheoryProofEngine()->printTheoryTerm((*it).toExpr(), os, globalLetMap); os << ") "; os << "(\\ "<< ProofManager::getPreprocessedAssertionName(*it, "") << "\n"; paren << "))"; - } os << "\n"; @@ -568,6 +608,14 @@ void ProofManager::markPrinted(const Type& type) { d_printedTypes.insert(type); } +void ProofManager::addRewriteFilter(const std::string &original, const std::string &substitute) { + d_rewriteFilters[original] = substitute; +} + +void ProofManager::clearRewriteFilters() { + d_rewriteFilters.clear(); +} + std::ostream& operator<<(std::ostream& out, CVC4::ProofRule k) { switch(k) { case RULE_GIVEN: @@ -607,5 +655,88 @@ std::ostream& operator<<(std::ostream& out, CVC4::ProofRule k) { return out; } +void ProofManager::registerRewrite(unsigned ruleId, Node original, Node result){ + Assert (currentPM()->d_theoryProof != NULL); + currentPM()->d_rewriteLog.push_back(RewriteLogEntry(ruleId, original, result)); +} + +void ProofManager::clearRewriteLog() { + Assert (currentPM()->d_theoryProof != NULL); + currentPM()->d_rewriteLog.clear(); +} + +std::vector<RewriteLogEntry> ProofManager::getRewriteLog() { + return currentPM()->d_rewriteLog; +} + +void ProofManager::dumpRewriteLog() const { + Debug("pf::rr") << "Dumpign rewrite log:" << std::endl; + + for (unsigned i = 0; i < d_rewriteLog.size(); ++i) { + Debug("pf::rr") << "\tRule " << d_rewriteLog[i].getRuleId() + << ": " + << d_rewriteLog[i].getOriginal() + << " --> " + << d_rewriteLog[i].getResult() << std::endl; + } +} + +void bind(Expr term, ProofLetMap& map, Bindings& letOrder) { + ProofLetMap::iterator it = map.find(term); + if (it != map.end()) + return; + + for (unsigned i = 0; i < term.getNumChildren(); ++i) + bind(term[i], map, letOrder); + + // Special case: chain operators. If we have and(a,b,c), it will be prineted as and(a,and(b,c)). + // The subterm and(b,c) may repeat elsewhere, so we need to bind it, too. + Kind k = term.getKind(); + if (((k == kind::OR) || (k == kind::AND)) && term.getNumChildren() > 2) { + Node currentExpression = term[term.getNumChildren() - 1]; + for (int i = term.getNumChildren() - 2; i >= 0; --i) { + NodeBuilder<> builder(k); + builder << term[i]; + builder << currentExpression.toExpr(); + currentExpression = builder; + bind(currentExpression.toExpr(), map, letOrder); + } + } else { + unsigned newId = ProofLetCount::newId(); + ProofLetCount letCount(newId); + map[term] = letCount; + letOrder.push_back(LetOrderElement(term, newId)); + } +} + +void ProofManager::printGlobalLetMap(std::set<Node>& atoms, + ProofLetMap& letMap, + std::ostream& out, + std::ostringstream& paren) { + Bindings letOrder; + std::set<Node>::const_iterator atom; + for (atom = atoms.begin(); atom != atoms.end(); ++atom) { + bind(atom->toExpr(), letMap, letOrder); + } + + // TODO: give each theory a chance to add atoms. For now, just query BV directly... + const std::set<Node>* additionalAtoms = ProofManager::getBitVectorProof()->getAtomsInBitblastingProof(); + for (atom = additionalAtoms->begin(); atom != additionalAtoms->end(); ++atom) { + bind(atom->toExpr(), letMap, letOrder); + } + + for (unsigned i = 0; i < letOrder.size(); ++i) { + Expr currentExpr = letOrder[i].expr; + unsigned letId = letOrder[i].id; + ProofLetMap::iterator it = letMap.find(currentExpr); + Assert(it != letMap.end()); + out << "\n(@ let" << letId << " "; + d_theoryProof->printBoundTerm(currentExpr, out, letMap); + paren << ")"; + it->second.increment(); + } + + out << std::endl << std::endl; +} } /* CVC4 namespace */ diff --git a/src/proof/proof_manager.h b/src/proof/proof_manager.h index c74aac237..14793f380 100644 --- a/src/proof/proof_manager.h +++ b/src/proof/proof_manager.h @@ -22,6 +22,7 @@ #include <iosfwd> #include <map> #include "proof/proof.h" +#include "proof/proof_utils.h" #include "proof/skolemization_manager.h" #include "util/proof.h" #include "expr/node.h" @@ -34,6 +35,8 @@ namespace CVC4 { +class SmtGlobals; + // forward declarations namespace Minisat { class Solver; @@ -113,6 +116,30 @@ enum ProofRule { RULE_ARRAYS_ROW, /* arrays, read-over-write */ };/* enum ProofRules */ +class RewriteLogEntry { +public: + RewriteLogEntry(unsigned ruleId, Node original, Node result) + : d_ruleId(ruleId), d_original(original), d_result(result) { + } + + unsigned getRuleId() const { + return d_ruleId; + } + + Node getOriginal() const { + return d_original; + } + + Node getResult() const { + return d_result; + } + +private: + unsigned d_ruleId; + Node d_original; + Node d_result; +}; + class ProofManager { CoreSatProof* d_satProof; CnfProof* d_cnfProof; @@ -136,6 +163,10 @@ class ProofManager { std::set<Type> d_printedTypes; + std::map<std::string, std::string> d_rewriteFilters; + + std::vector<RewriteLogEntry> d_rewriteLog; + protected: LogicInfo d_logic; @@ -224,6 +255,19 @@ public: void markPrinted(const Type& type); bool wasPrinted(const Type& type) const; + void addRewriteFilter(const std::string &original, const std::string &substitute); + void clearRewriteFilters(); + + static void registerRewrite(unsigned ruleId, Node original, Node result); + static void clearRewriteLog(); + + std::vector<RewriteLogEntry> getRewriteLog(); + void dumpRewriteLog() const; + + void printGlobalLetMap(std::set<Node>& atoms, + ProofLetMap& letMap, + std::ostream& out, + std::ostringstream& paren); };/* class ProofManager */ class LFSCProof : public Proof { @@ -235,13 +279,15 @@ class LFSCProof : public Proof { // FIXME: hack until we get preprocessing void printPreprocessedAssertions(const NodeSet& assertions, std::ostream& os, - std::ostream& paren); + std::ostream& paren, + ProofLetMap& globalLetMap); public: LFSCProof(SmtEngine* smtEngine, LFSCCoreSatProof* sat, LFSCCnfProof* cnf, LFSCTheoryProofEngine* theory); virtual void toStream(std::ostream& out); + virtual void toStream(std::ostream& out, const ProofLetMap& map); virtual ~LFSCProof() {} };/* class LFSCProof */ diff --git a/src/proof/proof_output_channel.cpp b/src/proof/proof_output_channel.cpp new file mode 100644 index 000000000..6d729db1f --- /dev/null +++ b/src/proof/proof_output_channel.cpp @@ -0,0 +1,82 @@ +/********************* */ +/*! \file proof_output_channel.cpp +** \verbatim +** \brief [[ Add one-line brief description here ]] +** +** [[ Add lengthier description here ]] +** \todo document this file +**/ + +#include "base/cvc4_assert.h" +#include "proof_output_channel.h" +#include "theory/term_registration_visitor.h" +#include "theory/valuation.h" + +namespace CVC4 { + +ProofOutputChannel::ProofOutputChannel() : d_conflict(), d_proof(NULL) {} + +void ProofOutputChannel::conflict(TNode n, Proof* pf) throw() { + Trace("pf::tp") << "ProofOutputChannel: CONFLICT: " << n << std::endl; + Assert(d_conflict.isNull()); + Assert(!n.isNull()); + d_conflict = n; + Assert(pf != NULL); + d_proof = pf; +} + +bool ProofOutputChannel::propagate(TNode x) throw() { + Trace("pf::tp") << "ProofOutputChannel: got a propagation: " << x << std::endl; + d_propagations.insert(x); + return true; +} + +theory::LemmaStatus ProofOutputChannel::lemma(TNode n, ProofRule rule, bool, bool, bool) throw() { + Trace("pf::tp") << "ProofOutputChannel: new lemma: " << n << std::endl; + d_lemma = n; + return theory::LemmaStatus(TNode::null(), 0); +} + +theory::LemmaStatus ProofOutputChannel::splitLemma(TNode, bool) throw() { + AlwaysAssert(false); + return theory::LemmaStatus(TNode::null(), 0); +} + +void ProofOutputChannel::requirePhase(TNode n, bool b) throw() { + Debug("pf::tp") << "ProofOutputChannel::requirePhase called" << std::endl; + Trace("pf::tp") << "requirePhase " << n << " " << b << std::endl; +} + +bool ProofOutputChannel::flipDecision() throw() { + Debug("pf::tp") << "ProofOutputChannel::flipDecision called" << std::endl; + AlwaysAssert(false); + return false; +} + +void ProofOutputChannel::setIncomplete() throw() { + Debug("pf::tp") << "ProofOutputChannel::setIncomplete called" << std::endl; + AlwaysAssert(false); +} + + +MyPreRegisterVisitor::MyPreRegisterVisitor(theory::Theory* theory) + : d_theory(theory) + , d_visited() { +} + +bool MyPreRegisterVisitor::alreadyVisited(TNode current, TNode parent) { + return d_visited.find(current) != d_visited.end(); +} + +void MyPreRegisterVisitor::visit(TNode current, TNode parent) { + d_theory->preRegisterTerm(current); + d_visited.insert(current); +} + +void MyPreRegisterVisitor::start(TNode node) { +} + +void MyPreRegisterVisitor::done(TNode node) { +} + +} /* namespace CVC4 */ diff --git a/src/proof/proof_output_channel.h b/src/proof/proof_output_channel.h new file mode 100644 index 000000000..b85af5fb5 --- /dev/null +++ b/src/proof/proof_output_channel.h @@ -0,0 +1,50 @@ +/********************* */ +/*! \file proof_output_channel.h + ** \verbatim + ** + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__PROOF_OUTPUT_CHANNEL_H +#define __CVC4__PROOF_OUTPUT_CHANNEL_H + +#include "theory/output_channel.h" + +namespace CVC4 { + +class ProofOutputChannel : public theory::OutputChannel { +public: + Node d_conflict; + Proof* d_proof; + Node d_lemma; + std::set<Node> d_propagations; + + ProofOutputChannel(); + + virtual ~ProofOutputChannel() throw() {} + + void conflict(TNode n, Proof* pf) throw(); + bool propagate(TNode x) throw(); + theory::LemmaStatus lemma(TNode n, ProofRule rule, bool, bool, bool) throw(); + theory::LemmaStatus splitLemma(TNode, bool) throw(); + void requirePhase(TNode n, bool b) throw(); + bool flipDecision() throw(); + void setIncomplete() throw(); +};/* class ProofOutputChannel */ + +class MyPreRegisterVisitor { + theory::Theory* d_theory; + __gnu_cxx::hash_set<TNode, TNodeHashFunction> d_visited; +public: + typedef void return_type; + MyPreRegisterVisitor(theory::Theory* theory); + bool alreadyVisited(TNode current, TNode parent); + void visit(TNode current, TNode parent); + void start(TNode node); + void done(TNode node); +}; /* class MyPreRegisterVisitor */ + +} /* CVC4 namespace */ + +#endif /* __CVC4__PROOF_OUTPUT_CHANNEL_H */ diff --git a/src/proof/proof_utils.cpp b/src/proof/proof_utils.cpp index 5b04c281d..fe0d42242 100644 --- a/src/proof/proof_utils.cpp +++ b/src/proof/proof_utils.cpp @@ -21,14 +21,14 @@ namespace CVC4 { namespace utils { -void collectAtoms(TNode node, CVC4::NodeSet& seen) { +void collectAtoms(TNode node, std::set<Node>& seen) { if (seen.find(node) != seen.end()) return; if (theory::Theory::theoryOf(node) != theory::THEORY_BOOL || node.isVar()) { seen.insert(node); return; } - + for (unsigned i = 0; i < node.getNumChildren(); ++i) { collectAtoms(node[i], seen); } @@ -47,23 +47,23 @@ std::string toLFSCKind(Kind kind) { // bit-vector kinds case kind::BITVECTOR_AND : - return "bvand"; + return "bvand"; case kind::BITVECTOR_OR : - return "bvor"; + return "bvor"; case kind::BITVECTOR_XOR : - return "bvxor"; + return "bvxor"; case kind::BITVECTOR_NAND : - return "bvnand"; + return "bvnand"; case kind::BITVECTOR_NOR : - return "bvnor"; + return "bvnor"; case kind::BITVECTOR_XNOR : - return "bvxnor"; + return "bvxnor"; case kind::BITVECTOR_COMP : - return "bvcomp"; + return "bvcomp"; case kind::BITVECTOR_MULT : return "bvmul"; case kind::BITVECTOR_PLUS : - return "bvadd"; + return "bvadd"; case kind::BITVECTOR_SUB : return "bvsub"; case kind::BITVECTOR_UDIV : @@ -71,49 +71,49 @@ std::string toLFSCKind(Kind kind) { return "bvudiv"; case kind::BITVECTOR_UREM : case kind::BITVECTOR_UREM_TOTAL : - return "bvurem"; + return "bvurem"; case kind::BITVECTOR_SDIV : - return "bvsdiv"; + return "bvsdiv"; case kind::BITVECTOR_SREM : return "bvsrem"; case kind::BITVECTOR_SMOD : - return "bvsmod"; + return "bvsmod"; case kind::BITVECTOR_SHL : - return "bvshl"; + return "bvshl"; case kind::BITVECTOR_LSHR : return "bvlshr"; case kind::BITVECTOR_ASHR : return "bvashr"; case kind::BITVECTOR_CONCAT : - return "concat"; + return "concat"; case kind::BITVECTOR_NEG : - return "bvneg"; + return "bvneg"; case kind::BITVECTOR_NOT : - return "bvnot"; + return "bvnot"; case kind::BITVECTOR_ROTATE_LEFT : - return "rotate_left"; + return "rotate_left"; case kind::BITVECTOR_ROTATE_RIGHT : return "rotate_right"; case kind::BITVECTOR_ULT : - return "bvult"; + return "bvult"; case kind::BITVECTOR_ULE : - return "bvule"; + return "bvule"; case kind::BITVECTOR_UGT : return "bvugt"; case kind::BITVECTOR_UGE : return "bvuge"; case kind::BITVECTOR_SLT : - return "bvslt"; + return "bvslt"; case kind::BITVECTOR_SLE : - return "bvsle"; + return "bvsle"; case kind::BITVECTOR_SGT : - return "bvsgt"; + return "bvsgt"; case kind::BITVECTOR_SGE : - return "bvsge"; + return "bvsge"; case kind::BITVECTOR_EXTRACT : - return "extract"; + return "extract"; case kind::BITVECTOR_REPEAT : - return "repeat"; + return "repeat"; case kind::BITVECTOR_ZERO_EXTEND : return "zero_extend"; case kind::BITVECTOR_SIGN_EXTEND : diff --git a/src/proof/proof_utils.h b/src/proof/proof_utils.h index da10c33a0..546d419fc 100644 --- a/src/proof/proof_utils.h +++ b/src/proof/proof_utils.h @@ -17,7 +17,7 @@ #include "cvc4_private.h" -#pragma once +#pragma once #include <set> #include <vector> @@ -29,6 +29,61 @@ namespace CVC4 { typedef __gnu_cxx::hash_set<Expr, ExprHashFunction> ExprSet; typedef __gnu_cxx::hash_set<Node, NodeHashFunction> NodeSet; +typedef std::pair<Node, Node> NodePair; +typedef std::set<NodePair> NodePairSet; + + +struct ProofLetCount { + static unsigned counter; + static void resetCounter() { counter = 0; } + static unsigned newId() { return ++counter; } + + unsigned count; + unsigned id; + ProofLetCount() + : count(0) + , id(-1) + {} + + void increment() { ++count; } + ProofLetCount(unsigned i) + : count(1) + , id(i) + {} + + ProofLetCount(const ProofLetCount& other) + : count(other.count) + , id (other.id) + {} + + bool operator==(const ProofLetCount &other) const { + return other.id == id && other.count == count; + } + + ProofLetCount& operator=(const ProofLetCount &rhs) { + if (&rhs == this) return *this; + id = rhs.id; + count = rhs.count; + return *this; + } +}; + +struct LetOrderElement { + Expr expr; + unsigned id; + LetOrderElement(Expr e, unsigned i) + : expr(e) + , id(i) + {} + + LetOrderElement() + : expr() + , id(-1) + {} +}; + +typedef std::vector<LetOrderElement> Bindings; + namespace utils { std::string toLFSCKind(Kind kind); @@ -42,7 +97,7 @@ inline unsigned getExtractLow(Expr node) { } inline unsigned getSize(Type type) { - BitVectorType bv(type); + BitVectorType bv(type); return bv.getSize(); } @@ -60,8 +115,8 @@ inline Expr mkFalse() { return NodeManager::currentNM()->toExprManager()->mkConst<bool>(false); } inline BitVector mkBitVectorOnes(unsigned size) { - Assert(size > 0); - return BitVector(1, Integer(1)).signExtend(size - 1); + Assert(size > 0); + return BitVector(1, Integer(1)).signExtend(size - 1); } inline Expr mkExpr(Kind k , Expr expr) { @@ -73,16 +128,16 @@ inline Expr mkExpr(Kind k , Expr e1, Expr e2) { inline Expr mkExpr(Kind k , std::vector<Expr>& children) { return NodeManager::currentNM()->toExprManager()->mkExpr(k, children); } - - + + inline Expr mkOnes(unsigned size) { - BitVector val = mkBitVectorOnes(size); - return NodeManager::currentNM()->toExprManager()->mkConst<BitVector>(val); + BitVector val = mkBitVectorOnes(size); + return NodeManager::currentNM()->toExprManager()->mkConst<BitVector>(val); } inline Expr mkConst(unsigned size, unsigned int value) { BitVector val(size, value); - return NodeManager::currentNM()->toExprManager()->mkConst<BitVector>(val); + return NodeManager::currentNM()->toExprManager()->mkConst<BitVector>(val); } inline Expr mkConst(const BitVector& value) { @@ -92,14 +147,14 @@ inline Expr mkConst(const BitVector& value) { inline Expr mkOr(const std::vector<Expr>& nodes) { std::set<Expr> all; all.insert(nodes.begin(), nodes.end()); - Assert(all.size() != 0 ); + Assert(all.size() != 0 ); if (all.size() == 1) { // All the same, or just one return nodes[0]; } - - + + NodeBuilder<> disjunction(kind::OR); std::set<Expr>::const_iterator it = all.begin(); std::set<Expr>::const_iterator it_end = all.end(); @@ -109,23 +164,23 @@ inline Expr mkOr(const std::vector<Expr>& nodes) { } Node res = disjunction; - return res.toExpr(); + return res.toExpr(); }/* mkOr() */ - + inline Expr mkAnd(const std::vector<Expr>& conjunctions) { std::set<Expr> all; all.insert(conjunctions.begin(), conjunctions.end()); if (all.size() == 0) { - return mkTrue(); + return mkTrue(); } - + if (all.size() == 1) { // All the same, or just one return conjunctions[0]; } - + NodeBuilder<> conjunction(kind::AND); std::set<Expr>::const_iterator it = all.begin(); @@ -135,7 +190,7 @@ inline Expr mkAnd(const std::vector<Expr>& conjunctions) { ++ it; } - Node res = conjunction; + Node res = conjunction; return res.toExpr(); }/* mkAnd() */ @@ -144,14 +199,14 @@ inline Expr mkSortedExpr(Kind kind, const std::vector<Expr>& children) { all.insert(children.begin(), children.end()); if (all.size() == 0) { - return mkTrue(); + return mkTrue(); } - + if (all.size() == 1) { // All the same, or just one return children[0]; } - + NodeBuilder<> res(kind); std::set<Expr>::const_iterator it = all.begin(); @@ -165,13 +220,13 @@ inline Expr mkSortedExpr(Kind kind, const std::vector<Expr>& children) { }/* mkSortedNode() */ inline const bool getBit(Expr expr, unsigned i) { - Assert (i < utils::getSize(expr) && - expr.isConst()); + Assert (i < utils::getSize(expr) && + expr.isConst()); Integer bit = expr.getConst<BitVector>().extract(i, i).getValue(); - return (bit == 1u); + return (bit == 1u); } -void collectAtoms(TNode node, NodeSet& seen); +void collectAtoms(TNode node, std::set<Node>& seen); } diff --git a/src/proof/sat_proof.h b/src/proof/sat_proof.h index d94b61bf3..bda8094be 100644 --- a/src/proof/sat_proof.h +++ b/src/proof/sat_proof.h @@ -34,175 +34,99 @@ #include "util/proof.h" #include "util/statistics_registry.h" +// Forward declarations. namespace CVC4 { - class CnfProof; +template <class Solver> +class ProofProxy; +} /* namespace CVC4 */ +namespace CVC4 { /** * Helper debugging functions */ -template <class Solver> void printDebug(typename Solver::TLit l); -template <class Solver> void printDebug(typename Solver::TClause& c); +template <class Solver> +void printDebug(typename Solver::TLit l); +template <class Solver> +void printDebug(typename Solver::TClause& c); enum ClauseKind { INPUT, - THEORY_LEMMA, // we need to distinguish because we must reprove deleted theory lemmas + THEORY_LEMMA, // we need to distinguish because we must reprove deleted + // theory lemmas LEARNT -};/* enum ClauseKind */ - +}; /* enum ClauseKind */ template <class Solver> struct ResStep { typename Solver::TLit lit; ClauseId id; bool sign; - ResStep(typename Solver::TLit l, ClauseId i, bool s) : - lit(l), - id(i), - sign(s) - {} -};/* struct ResStep */ + ResStep(typename Solver::TLit l, ClauseId i, bool s) + : lit(l), id(i), sign(s) {} +}; /* struct ResStep */ template <class Solver> class ResChain { -public: - typedef std::vector< ResStep<Solver> > ResSteps; - typedef std::set < typename Solver::TLit> LitSet; - -private: - ClauseId d_start; - ResSteps d_steps; - LitSet* d_redundantLits; -public: + public: + typedef std::vector<ResStep<Solver> > ResSteps; + typedef std::set<typename Solver::TLit> LitSet; + ResChain(ClauseId start); + ~ResChain(); + void addStep(typename Solver::TLit, ClauseId, bool); - bool redundantRemoved() { return (d_redundantLits == NULL || d_redundantLits->empty()); } + bool redundantRemoved() { + return (d_redundantLits == NULL || d_redundantLits->empty()); + } void addRedundantLit(typename Solver::TLit lit); - ~ResChain(); - // accessor methods - ClauseId getStart() { return d_start; } - ResSteps& getSteps() { return d_steps; } - LitSet* getRedundant() { return d_redundantLits; } -};/* class ResChain */ -template <class Solver> class ProofProxy; + // accessor methods + ClauseId getStart() const { return d_start; } + const ResSteps& getSteps() const { return d_steps; } + LitSet* getRedundant() const { return d_redundantLits; } -class CnfProof; + private: + ClauseId d_start; + ResSteps d_steps; + LitSet* d_redundantLits; +}; /* class ResChain */ -template<class Solver> +template <class Solver> class TSatProof { -protected: - typedef std::set < typename Solver::TLit> LitSet; - typedef std::set < typename Solver::TVar> VarSet; - typedef std::hash_map < ClauseId, typename Solver::TCRef > IdCRefMap; - typedef std::hash_map < typename Solver::TCRef, ClauseId > ClauseIdMap; - typedef std::hash_map < ClauseId, typename Solver::TLit> IdUnitMap; - typedef std::hash_map < int, ClauseId> UnitIdMap; - typedef std::hash_map < ClauseId, ResChain<Solver>* > IdResMap; - typedef std::hash_map < ClauseId, uint64_t > IdProofRuleMap; - typedef std::vector < ResChain<Solver>* > ResStack; - //typedef std::hash_map <ClauseId, prop::SatClause* > IdToSatClause; - typedef std::set < ClauseId > IdSet; - typedef std::vector < typename Solver::TLit > LitVector; - typedef __gnu_cxx::hash_map<ClauseId, typename Solver::TClause& > IdToMinisatClause; - typedef __gnu_cxx::hash_map<ClauseId, LitVector* > IdToConflicts; - - typename Solver::Solver* d_solver; - CnfProof* d_cnfProof; - - // clauses - IdCRefMap d_idClause; - ClauseIdMap d_clauseId; - IdUnitMap d_idUnit; - UnitIdMap d_unitId; - IdHashSet d_deleted; - IdToSatClause d_deletedTheoryLemmas; - -protected: - IdHashSet d_inputClauses; - IdHashSet d_lemmaClauses; - VarSet d_assumptions; // assumption literals for bv solver - IdHashSet d_assumptionConflicts; // assumption conflicts not actually added to SAT solver - IdToConflicts d_assumptionConflictsDebug; - - // resolutions - IdResMap d_resChains; - ResStack d_resStack; - bool d_checkRes; - - const ClauseId d_emptyClauseId; - const ClauseId d_nullId; - // proxy class to break circular dependencies - ProofProxy<Solver>* d_proxy; - - // temporary map for updating CRefs - ClauseIdMap d_temp_clauseId; - IdCRefMap d_temp_idClause; - - // unit conflict - ClauseId d_unitConflictId; - bool d_storedUnitConflict; - - ClauseId d_trueLit; - ClauseId d_falseLit; - - std::string d_name; -public: + protected: + typedef ResChain<Solver> ResolutionChain; + + typedef std::set<typename Solver::TLit> LitSet; + typedef std::set<typename Solver::TVar> VarSet; + typedef std::hash_map<ClauseId, typename Solver::TCRef> IdCRefMap; + typedef std::hash_map<typename Solver::TCRef, ClauseId> ClauseIdMap; + typedef std::hash_map<ClauseId, typename Solver::TLit> IdUnitMap; + typedef std::hash_map<int, ClauseId> UnitIdMap; + typedef std::hash_map<ClauseId, ResolutionChain*> IdResMap; + typedef std::hash_map<ClauseId, uint64_t> IdProofRuleMap; + typedef std::vector<ResolutionChain*> ResStack; + typedef std::set<ClauseId> IdSet; + typedef std::vector<typename Solver::TLit> LitVector; + typedef __gnu_cxx::hash_map<ClauseId, typename Solver::TClause&> + IdToMinisatClause; + typedef __gnu_cxx::hash_map<ClauseId, LitVector*> IdToConflicts; + + public: TSatProof(Solver* solver, const std::string& name, bool checkRes = false); virtual ~TSatProof(); void setCnfProof(CnfProof* cnf_proof); -protected: - void print(ClauseId id); - void printRes(ClauseId id); - void printRes(ResChain<Solver>* res); - - bool isInputClause(ClauseId id); - bool isLemmaClause(ClauseId id); - bool isAssumptionConflict(ClauseId id); - bool isUnit(ClauseId id); - bool isUnit(typename Solver::TLit lit); - bool hasResolution(ClauseId id); - void createLitSet(ClauseId id, LitSet& set); - void registerResolution(ClauseId id, ResChain<Solver>* res); - - ClauseId getClauseId(typename Solver::TCRef clause); - ClauseId getClauseId(typename Solver::TLit lit); - typename Solver::TCRef getClauseRef(ClauseId id); - typename Solver::TLit getUnit(ClauseId id); - ClauseId getUnitId(typename Solver::TLit lit); - typename Solver::TClause& getClause(typename Solver::TCRef ref); - void getLitVec(ClauseId id, LitVector& vec); - virtual void toStream(std::ostream& out); - bool checkResolution(ClauseId id); - /** - * Constructs a resolution tree that proves lit - * and returns the ClauseId for the unit clause lit - * @param lit the literal we are proving - * - * @return - */ - ClauseId resolveUnit(typename Solver::TLit lit); - /** - * Does a depth first search on removed literals and adds the literals - * to be removed in the proper order to the stack. - * - * @param lit the literal we are recursing on - * @param removedSet the previously computed set of redundant literals - * @param removeStack the stack of literals in reverse order of resolution - */ - void removedDfs(typename Solver::TLit lit, LitSet* removedSet, LitVector& removeStack, LitSet& inClause, LitSet& seen); - void removeRedundantFromRes(ResChain<Solver>* res, ClauseId id); -public: void startResChain(typename Solver::TLit start); void startResChain(typename Solver::TCRef start); - void addResolutionStep(typename Solver::TLit lit, typename Solver::TCRef clause, bool sign); + void addResolutionStep(typename Solver::TLit lit, + typename Solver::TCRef clause, bool sign); /** * Pops the current resolution of the stack and stores it * in the resolution map. Also registers the 'clause' parameter * @param clause the clause the resolution is proving */ - //void endResChain(typename Solver::TCRef clause); + // void endResChain(typename Solver::TCRef clause); void endResChain(typename Solver::TLit lit); void endResChain(ClauseId id); @@ -219,7 +143,8 @@ public: void storeLitRedundant(typename Solver::TLit lit); /// update the CRef Id maps when Minisat does memory reallocation x - void updateCRef(typename Solver::TCRef old_ref, typename Solver::TCRef new_ref); + void updateCRef(typename Solver::TCRef old_ref, + typename Solver::TCRef new_ref); void finishUpdateCRef(); /** @@ -231,25 +156,22 @@ public: /// clause registration methods - ClauseId registerClause(const typename Solver::TCRef clause, - ClauseKind kind); - ClauseId registerUnitClause(const typename Solver::TLit lit, - ClauseKind kind); + ClauseId registerClause(const typename Solver::TCRef clause, ClauseKind kind); + ClauseId registerUnitClause(const typename Solver::TLit lit, ClauseKind kind); void registerTrueLit(const typename Solver::TLit lit); void registerFalseLit(const typename Solver::TLit lit); ClauseId getTrueUnit() const; ClauseId getFalseUnit() const; - void registerAssumption(const typename Solver::TVar var); ClauseId registerAssumptionConflict(const typename Solver::TLitVec& confl); - ClauseId storeUnitConflict(typename Solver::TLit lit, - ClauseKind kind); + ClauseId storeUnitConflict(typename Solver::TLit lit, ClauseKind kind); /** - * Marks the deleted clauses as deleted. Note we may still use them in the final + * Marks the deleted clauses as deleted. Note we may still use them in the + * final * resolution. * @param clause */ @@ -268,43 +190,171 @@ public: */ void storeUnitResolution(typename Solver::TLit lit); - ProofProxy<Solver>* getProxy() {return d_proxy; } + ProofProxy<Solver>* getProxy() { return d_proxy; } /** * Constructs the SAT proof for the given clause, * by collecting the needed clauses in the d_seen * data-structures, also notifying the proofmanager. */ void constructProof(ClauseId id); - void constructProof() { - constructProof(d_emptyClauseId); - } + void constructProof() { constructProof(d_emptyClauseId); } void collectClauses(ClauseId id); prop::SatClause* buildClause(ClauseId id); -protected: - IdSet d_seenLearnt; - IdToSatClause d_seenInputs; - IdToSatClause d_seenLemmas; + + virtual void printResolution(ClauseId id, std::ostream& out, + std::ostream& paren) = 0; + virtual void printResolutions(std::ostream& out, std::ostream& paren) = 0; + virtual void printResolutionEmptyClause(std::ostream& out, + std::ostream& paren) = 0; + virtual void printAssumptionsResolution(ClauseId id, std::ostream& out, + std::ostream& paren) = 0; + + void collectClausesUsed(IdToSatClause& inputs, IdToSatClause& lemmas); + + void storeClauseGlue(ClauseId clause, int glue); + + protected: + void print(ClauseId id) const; + void printRes(ClauseId id) const; + void printRes(const ResolutionChain& res) const; + + bool isInputClause(ClauseId id) const; + bool isLemmaClause(ClauseId id) const; + bool isAssumptionConflict(ClauseId id) const; + + bool isUnit(ClauseId id) const; + typename Solver::TLit getUnit(ClauseId id) const; + + bool isUnit(typename Solver::TLit lit) const; + ClauseId getUnitId(typename Solver::TLit lit) const; + + + + bool hasResolutionChain(ClauseId id) const; + + /** Returns the resolution chain associated with id. Assert fails if + * hasResolution(id) does not hold. */ + const ResolutionChain& getResolutionChain(ClauseId id) const; + + /** Returns a mutable pointer to the resolution chain associated with id. + * Assert fails if hasResolution(id) does not hold. */ + ResolutionChain* getMutableResolutionChain(ClauseId id); + + void createLitSet(ClauseId id, LitSet& set); + + /** + * Registers a ClauseId with a resolution chain res. + * Takes ownership of the memory associated with res. + */ + void registerResolution(ClauseId id, ResolutionChain* res); + + + bool hasClauseIdForCRef(typename Solver::TCRef clause) const; + ClauseId getClauseIdForCRef(typename Solver::TCRef clause) const; + + bool hasClauseIdForLiteral(typename Solver::TLit lit) const; + ClauseId getClauseIdForLiteral(typename Solver::TLit lit) const; + + bool hasClauseRef(ClauseId id) const; + typename Solver::TCRef getClauseRef(ClauseId id) const; + + + const typename Solver::TClause& getClause(typename Solver::TCRef ref) const; + typename Solver::TClause* getMutableClause(typename Solver::TCRef ref); + + void getLitVec(ClauseId id, LitVector& vec); + virtual void toStream(std::ostream& out); + + bool checkResolution(ClauseId id); + + /** + * Constructs a resolution tree that proves lit + * and returns the ClauseId for the unit clause lit + * @param lit the literal we are proving + * + * @return + */ + ClauseId resolveUnit(typename Solver::TLit lit); + + /** + * Does a depth first search on removed literals and adds the literals + * to be removed in the proper order to the stack. + * + * @param lit the literal we are recursing on + * @param removedSet the previously computed set of redundant literals + * @param removeStack the stack of literals in reverse order of resolution + */ + void removedDfs(typename Solver::TLit lit, LitSet* removedSet, + LitVector& removeStack, LitSet& inClause, LitSet& seen); + void removeRedundantFromRes(ResChain<Solver>* res, ClauseId id); std::string varName(typename Solver::TLit lit); std::string clauseName(ClauseId id); - void addToProofManager(ClauseId id, ClauseKind kind); void addToCnfProof(ClauseId id); -public: - virtual void printResolution(ClauseId id, std::ostream& out, std::ostream& paren) = 0; - virtual void printResolutions(std::ostream& out, std::ostream& paren) = 0; - virtual void printResolutionEmptyClause(std::ostream& out, std::ostream& paren) = 0; - virtual void printAssumptionsResolution(ClauseId id, std::ostream& out, std::ostream& paren) = 0; - void collectClausesUsed(IdToSatClause& inputs, - IdToSatClause& lemmas); + // Internal data. + typename Solver::Solver* d_solver; + CnfProof* d_cnfProof; - void storeClauseGlue(ClauseId clause, int glue); + // clauses + IdCRefMap d_idClause; + ClauseIdMap d_clauseId; + IdUnitMap d_idUnit; + UnitIdMap d_unitId; + IdHashSet d_deleted; + IdToSatClause d_deletedTheoryLemmas; + + IdHashSet d_inputClauses; + IdHashSet d_lemmaClauses; + VarSet d_assumptions; // assumption literals for bv solver + IdHashSet d_assumptionConflicts; // assumption conflicts not actually added + // to SAT solver + IdToConflicts d_assumptionConflictsDebug; + + // Resolutions. + /** + * Map from ClauseId to resolution chain corresponding proving the + * clause corresponding to the ClauseId. d_resolutionChains owns the + * memory of the ResChain* it contains. + */ + IdResMap d_resolutionChains; + + /* + * Stack containting current ResChain* we are working on. d_resStack + * owns the memory for the ResChain* it contains. Invariant: no + * ResChain* pointer can be both in d_resStack and + * d_resolutionChains. Memory ownership is transfered from + * d_resStack to d_resolutionChains via registerResolution. + */ + ResStack d_resStack; + bool d_checkRes; + + const ClauseId d_emptyClauseId; + const ClauseId d_nullId; + // proxy class to break circular dependencies + ProofProxy<Solver>* d_proxy; + // temporary map for updating CRefs + ClauseIdMap d_temp_clauseId; + IdCRefMap d_temp_idClause; -private: + // unit conflict + ClauseId d_unitConflictId; + bool d_storedUnitConflict; + + ClauseId d_trueLit; + ClauseId d_falseLit; + + std::string d_name; + + IdSet d_seenLearnt; + IdToSatClause d_seenInputs; + IdToSatClause d_seenLemmas; + + private: __gnu_cxx::hash_map<ClauseId, int> d_glueMap; struct Statistics { IntStat d_numLearnedClauses; @@ -320,49 +370,47 @@ private: }; Statistics d_statistics; -};/* class TSatProof */ +}; /* class TSatProof */ template <class S> class ProofProxy { -private: + private: TSatProof<S>* d_proof; -public: + + public: ProofProxy(TSatProof<S>* pf); void updateCRef(typename S::TCRef oldref, typename S::TCRef newref); -};/* class ProofProxy */ - +}; /* class ProofProxy */ template <class SatSolver> class LFSCSatProof : public TSatProof<SatSolver> { -private: - -public: - LFSCSatProof(SatSolver* solver, const std::string& name, bool checkRes = false) - : TSatProof<SatSolver>(solver, name, checkRes) - {} - virtual void printResolution(ClauseId id, std::ostream& out, std::ostream& paren); + private: + public: + LFSCSatProof(SatSolver* solver, const std::string& name, + bool checkRes = false) + : TSatProof<SatSolver>(solver, name, checkRes) {} + virtual void printResolution(ClauseId id, std::ostream& out, + std::ostream& paren); virtual void printResolutions(std::ostream& out, std::ostream& paren); - virtual void printResolutionEmptyClause(std::ostream& out, std::ostream& paren); - virtual void printAssumptionsResolution(ClauseId id, std::ostream& out, std::ostream& paren); -};/* class LFSCSatProof */ - - + virtual void printResolutionEmptyClause(std::ostream& out, + std::ostream& paren); + virtual void printAssumptionsResolution(ClauseId id, std::ostream& out, + std::ostream& paren); +}; /* class LFSCSatProof */ -template<class Solver> +template <class Solver> prop::SatLiteral toSatLiteral(typename Solver::TLit lit); - /** * Convert from minisat clause to SatClause * * @param minisat_cl * @param sat_cl */ -template<class Solver> +template <class Solver> void toSatClause(const typename Solver::TClause& minisat_cl, prop::SatClause& sat_cl); - -}/* CVC4 namespace */ +} /* CVC4 namespace */ #endif /* __CVC4__SAT__PROOF_H */ diff --git a/src/proof/sat_proof_implementation.h b/src/proof/sat_proof_implementation.h index e773e4b47..1e01e4dce 100644 --- a/src/proof/sat_proof_implementation.h +++ b/src/proof/sat_proof_implementation.h @@ -32,29 +32,28 @@ namespace CVC4 { template <class Solver> -void printLit (typename Solver::TLit l) { +void printLit(typename Solver::TLit l) { Debug("proof:sat") << (sign(l) ? "-" : "") << var(l) + 1; } template <class Solver> -void printClause (typename Solver::TClause& c) { +void printClause(const typename Solver::TClause& c) { for (int i = 0; i < c.size(); i++) { Debug("proof:sat") << (sign(c[i]) ? "-" : "") << var(c[i]) + 1 << " "; } } template <class Solver> -void printClause (std::vector<typename Solver::TLit>& c) { +void printClause(const std::vector<typename Solver::TLit>& c) { for (unsigned i = 0; i < c.size(); i++) { Debug("proof:sat") << (sign(c[i]) ? "-" : "") << var(c[i]) + 1 << " "; } } - template <class Solver> void printLitSet(const std::set<typename Solver::TLit>& s) { - typename std::set < typename Solver::TLit>::const_iterator it = s.begin(); - for(; it != s.end(); ++it) { + typename std::set<typename Solver::TLit>::const_iterator it = s.begin(); + for (; it != s.end(); ++it) { printLit<Solver>(*it); Debug("proof:sat") << " "; } @@ -63,18 +62,17 @@ void printLitSet(const std::set<typename Solver::TLit>& s) { // purely debugging functions template <class Solver> -void printDebug (typename Solver::TLit l) { +void printDebug(typename Solver::TLit l) { Debug("proof:sat") << (sign(l) ? "-" : "") << var(l) + 1 << std::endl; } template <class Solver> -void printDebug (typename Solver::TClause& c) { +void printDebug(typename Solver::TClause& c) { for (int i = 0; i < c.size(); i++) { Debug("proof:sat") << (sign(c[i]) ? "-" : "") << var(c[i]) + 1 << " "; } Debug("proof:sat") << std::endl; } - /** * Converts the clause associated to id to a set of literals * @@ -84,11 +82,11 @@ void printDebug (typename Solver::TClause& c) { template <class Solver> void TSatProof<Solver>::createLitSet(ClauseId id, LitSet& set) { Assert(set.empty()); - if(isUnit(id)) { + if (isUnit(id)) { set.insert(getUnit(id)); return; } - if ( id == d_emptyClauseId) { + if (id == d_emptyClauseId) { return; } // if it's an assumption @@ -101,13 +99,12 @@ void TSatProof<Solver>::createLitSet(ClauseId id, LitSet& set) { } typename Solver::TCRef ref = getClauseRef(id); - typename Solver::TClause& c = getClause(ref); + const typename Solver::TClause& c = getClause(ref); for (int i = 0; i < c.size(); i++) { set.insert(c[i]); } } - /** * Resolves clause1 and clause2 on variable var and stores the * result in clause1 @@ -124,8 +121,8 @@ bool resolve(const typename Solver::TLit v, typename Solver::TLit var = sign(v) ? ~v : v; if (s) { // literal appears positive in the first clause - if( !clause2.count(~var)) { - if(Debug.isOn("proof:sat")) { + if (!clause2.count(~var)) { + if (Debug.isOn("proof:sat")) { Debug("proof:sat") << "proof:resolve: Missing literal "; printLit<Solver>(var); Debug("proof:sat") << std::endl; @@ -135,13 +132,13 @@ bool resolve(const typename Solver::TLit v, clause1.erase(var); clause2.erase(~var); typename std::set<typename Solver::TLit>::iterator it = clause2.begin(); - for (; it!= clause2.end(); ++it) { + for (; it != clause2.end(); ++it) { clause1.insert(*it); } } else { // literal appears negative in the first clause - if( !clause1.count(~var) || !clause2.count(var)) { - if(Debug.isOn("proof:sat")) { + if (!clause1.count(~var) || !clause2.count(var)) { + if (Debug.isOn("proof:sat")) { Debug("proof:sat") << "proof:resolve: Missing literal "; printLit<Solver>(var); Debug("proof:sat") << std::endl; @@ -151,7 +148,7 @@ bool resolve(const typename Solver::TLit v, clause1.erase(~var); clause2.erase(var); typename std::set<typename Solver::TLit>::iterator it = clause2.begin(); - for (; it!= clause2.end(); ++it) { + for (; it != clause2.end(); ++it) { clause1.insert(*it); } } @@ -160,13 +157,19 @@ bool resolve(const typename Solver::TLit v, /// ResChain template <class Solver> -ResChain<Solver>::ResChain(ClauseId start) : - d_start(start), - d_steps(), - d_redundantLits(NULL) - {} +ResChain<Solver>::ResChain(ClauseId start) + : d_start(start), d_steps(), d_redundantLits(NULL) {} + +template <class Solver> +ResChain<Solver>::~ResChain() { + if (d_redundantLits != NULL) { + delete d_redundantLits; + } +} + template <class Solver> -void ResChain<Solver>::addStep(typename Solver::TLit lit, ClauseId id, bool sign) { +void ResChain<Solver>::addStep(typename Solver::TLit lit, ClauseId id, + bool sign) { ResStep<Solver> step(lit, id, sign); d_steps.push_back(step); } @@ -181,50 +184,47 @@ void ResChain<Solver>::addRedundantLit(typename Solver::TLit lit) { } } - /// ProxyProof template <class Solver> -ProofProxy<Solver>::ProofProxy(TSatProof<Solver>* proof): - d_proof(proof) -{} +ProofProxy<Solver>::ProofProxy(TSatProof<Solver>* proof) : d_proof(proof) {} template <class Solver> -void ProofProxy<Solver>::updateCRef(typename Solver::TCRef oldref, typename Solver::TCRef newref) { +void ProofProxy<Solver>::updateCRef(typename Solver::TCRef oldref, + typename Solver::TCRef newref) { d_proof->updateCRef(oldref, newref); } - /// SatProof template <class Solver> -TSatProof<Solver>::TSatProof(Solver* solver, const std::string& name, bool checkRes) - : d_solver(solver) - , d_cnfProof(NULL) - , d_idClause() - , d_clauseId() - , d_idUnit() - , d_deleted() - , d_inputClauses() - , d_lemmaClauses() - , d_assumptions() - , d_assumptionConflicts() - , d_assumptionConflictsDebug() - , d_resChains() - , d_resStack() - , d_checkRes(checkRes) - , d_emptyClauseId(ClauseIdEmpty) - , d_nullId(-2) - , d_temp_clauseId() - , d_temp_idClause() - , d_unitConflictId() - , d_storedUnitConflict(false) - , d_trueLit(ClauseIdUndef) - , d_falseLit(ClauseIdUndef) - , d_name(name) - , d_seenLearnt() - , d_seenInputs() - , d_seenLemmas() - , d_statistics(name) -{ +TSatProof<Solver>::TSatProof(Solver* solver, const std::string& name, + bool checkRes) + : d_solver(solver), + d_cnfProof(NULL), + d_idClause(), + d_clauseId(), + d_idUnit(), + d_deleted(), + d_inputClauses(), + d_lemmaClauses(), + d_assumptions(), + d_assumptionConflicts(), + d_assumptionConflictsDebug(), + d_resolutionChains(), + d_resStack(), + d_checkRes(checkRes), + d_emptyClauseId(ClauseIdEmpty), + d_nullId(-2), + d_temp_clauseId(), + d_temp_idClause(), + d_unitConflictId(), + d_storedUnitConflict(false), + d_trueLit(ClauseIdUndef), + d_falseLit(ClauseIdUndef), + d_name(name), + d_seenLearnt(), + d_seenInputs(), + d_seenLemmas(), + d_statistics(name) { d_proxy = new ProofProxy<Solver>(this); } @@ -233,34 +233,53 @@ TSatProof<Solver>::~TSatProof() { delete d_proxy; // FIXME: double free if deleted clause also appears in d_seenLemmas? - IdToSatClause::iterator it = d_deletedTheoryLemmas.begin(); - IdToSatClause::iterator end = d_deletedTheoryLemmas.end(); + IdToSatClause::const_iterator it = d_deletedTheoryLemmas.begin(); + IdToSatClause::const_iterator end = d_deletedTheoryLemmas.end(); for (; it != end; ++it) { ClauseId id = it->first; // otherwise deleted in next loop - if (d_seenLemmas.find(id) == d_seenLemmas.end()) + if (d_seenLemmas.find(id) == d_seenLemmas.end()) { delete it->second; + } } - IdToSatClause::iterator seen_it = d_seenLemmas.begin(); - IdToSatClause::iterator seen_end = d_seenLemmas.end(); + IdToSatClause::const_iterator seen_lemma_it = d_seenLemmas.begin(); + IdToSatClause::const_iterator seen_lemma_end = d_seenLemmas.end(); - for (; seen_it != seen_end; ++seen_it) { - delete seen_it->second; + for (; seen_lemma_it != seen_lemma_end; ++seen_lemma_it) { + delete seen_lemma_it->second; } - seen_it = d_seenInputs.begin(); - seen_end = d_seenInputs.end(); + IdToSatClause::const_iterator seen_input_it = d_seenInputs.begin(); + IdToSatClause::const_iterator seen_input_end = d_seenInputs.end(); - for (; seen_it != seen_end; ++seen_it) { - delete seen_it->second; + for (; seen_input_it != seen_input_end; ++seen_input_it) { + delete seen_input_it->second; + } + + typedef typename IdResMap::const_iterator ResolutionChainIterator; + ResolutionChainIterator resolution_it = d_resolutionChains.begin(); + ResolutionChainIterator resolution_it_end = d_resolutionChains.end(); + for (; resolution_it != resolution_it_end; ++resolution_it) { + ResChain<Solver>* current = resolution_it->second; + delete current; + } + + // It could be the case that d_resStack is not empty at destruction time + // (for example in the SAT case). + typename ResStack::const_iterator resolution_stack_it = d_resStack.begin(); + typename ResStack::const_iterator resolution_stack_it_end = d_resStack.end(); + for (; resolution_stack_it != resolution_stack_it_end; + ++resolution_stack_it) { + ResChain<Solver>* current = *resolution_stack_it; + delete current; } } template <class Solver> void TSatProof<Solver>::setCnfProof(CnfProof* cnf_proof) { - Assert (d_cnfProof == NULL); + Assert(d_cnfProof == NULL); d_cnfProof = cnf_proof; } @@ -273,19 +292,19 @@ void TSatProof<Solver>::setCnfProof(CnfProof* cnf_proof) { */ template <class Solver> bool TSatProof<Solver>::checkResolution(ClauseId id) { - if(d_checkRes) { + if (d_checkRes) { bool validRes = true; - Assert(d_resChains.find(id) != d_resChains.end()); - ResChain<Solver>* res = d_resChains[id]; + Assert(hasResolutionChain(id)); + const ResolutionChain& res = getResolutionChain(id); LitSet clause1; - createLitSet(res->getStart(), clause1); - typename ResChain<Solver>::ResSteps& steps = res->getSteps(); + createLitSet(res.getStart(), clause1); + const typename ResolutionChain::ResSteps& steps = res.getSteps(); for (unsigned i = 0; i < steps.size(); i++) { - typename Solver::TLit var = steps[i].lit; + typename Solver::TLit var = steps[i].lit; LitSet clause2; - createLitSet (steps[i].id, clause2); - bool res = resolve<Solver> (var, clause1, clause2, steps[i].sign); - if(res == false) { + createLitSet(steps[i].id, clause2); + bool res = resolve<Solver>(var, clause1, clause2, steps[i].sign); + if (res == false) { validRes = false; break; } @@ -307,8 +326,9 @@ bool TSatProof<Solver>::checkResolution(ClauseId id) { for (unsigned i = 0; i < c.size(); ++i) { int count = clause1.erase(c[i]); if (count == 0) { - if(Debug.isOn("proof:sat")) { - Debug("proof:sat") << "proof:checkResolution::literal not in computed result "; + if (Debug.isOn("proof:sat")) { + Debug("proof:sat") + << "proof:checkResolution::literal not in computed result "; printLit<Solver>(c[i]); Debug("proof:sat") << "\n"; } @@ -316,9 +336,10 @@ bool TSatProof<Solver>::checkResolution(ClauseId id) { } } validRes = clause1.empty(); - if (! validRes) { - if(Debug.isOn("proof:sat")) { - Debug("proof:sat") << "proof:checkResolution::Invalid Resolution, unremoved literals: \n"; + if (!validRes) { + if (Debug.isOn("proof:sat")) { + Debug("proof:sat") << "proof:checkResolution::Invalid Resolution, " + "unremoved literals: \n"; printLitSet<Solver>(clause1); Debug("proof:sat") << "proof:checkResolution:: result should be: \n"; printClause<Solver>(c); @@ -331,37 +352,54 @@ bool TSatProof<Solver>::checkResolution(ClauseId id) { } } - - - /// helper methods template <class Solver> -ClauseId TSatProof<Solver>::getClauseId(typename Solver::TCRef ref) { - if(d_clauseId.find(ref) == d_clauseId.end()) { +bool TSatProof<Solver>::hasClauseIdForCRef(typename Solver::TCRef ref) const { + return d_clauseId.find(ref) != d_clauseId.end(); +} + +template <class Solver> +ClauseId TSatProof<Solver>::getClauseIdForCRef( + typename Solver::TCRef ref) const { + if (d_clauseId.find(ref) == d_clauseId.end()) { Debug("proof:sat") << "Missing clause \n"; } - Assert(d_clauseId.find(ref) != d_clauseId.end()); - return d_clauseId[ref]; + Assert(hasClauseIdForCRef(ref)); + return d_clauseId.find(ref)->second; +} + +template <class Solver> +bool TSatProof<Solver>::hasClauseIdForLiteral(typename Solver::TLit lit) const { + return d_unitId.find(toInt(lit)) != d_unitId.end(); +} + +template <class Solver> +ClauseId TSatProof<Solver>::getClauseIdForLiteral( + typename Solver::TLit lit) const { + Assert(hasClauseIdForLiteral(lit)); + return d_unitId.find(toInt(lit))->second; } template <class Solver> -ClauseId TSatProof<Solver>::getClauseId(typename Solver::TLit lit) { - Assert(d_unitId.find(toInt(lit)) != d_unitId.end()); - return d_unitId[toInt(lit)]; +bool TSatProof<Solver>::hasClauseRef(ClauseId id) const { + return d_idClause.find(id) != d_idClause.end(); } + template <class Solver> -typename Solver::TCRef TSatProof<Solver>::getClauseRef(ClauseId id) { - if (d_idClause.find(id) == d_idClause.end()) { - Debug("proof:sat") << "proof:getClauseRef cannot find clause "<<id<<" " - << ((d_deleted.find(id) != d_deleted.end()) ? "deleted" : "") - << (isUnit(id)? "Unit" : "") << std::endl; +typename Solver::TCRef TSatProof<Solver>::getClauseRef(ClauseId id) const { + if (!hasClauseRef(id)) { + Debug("proof:sat") << "proof:getClauseRef cannot find clause " << id << " " + << ((d_deleted.find(id) != d_deleted.end()) ? "deleted" + : "") + << (isUnit(id) ? "Unit" : "") << std::endl; } - Assert(d_idClause.find(id) != d_idClause.end()); - return d_idClause[id]; + Assert(hasClauseRef(id)); + return d_idClause.find(id)->second; } template <class Solver> -typename Solver::TClause& TSatProof<Solver>::getClause(typename Solver::TCRef ref) { +const typename Solver::TClause& TSatProof<Solver>::getClause( + typename Solver::TCRef ref) const { Assert(ref != Solver::TCRef_Undef); Assert(ref >= 0 && ref < d_solver->ca.size()); return d_solver->ca[ref]; @@ -379,85 +417,106 @@ void TSatProof<Solver>::getLitVec(ClauseId id, LitVector& vec) { return; } typename Solver::TCRef cref = getClauseRef(id); - typename Solver::TClause& cl = getClause(cref); + const typename Solver::TClause& cl = getClause(cref); for (int i = 0; i < cl.size(); ++i) { vec.push_back(cl[i]); } } - template <class Solver> -typename Solver::TLit TSatProof<Solver>::getUnit(ClauseId id) { - Assert(d_idUnit.find(id) != d_idUnit.end()); - return d_idUnit[id]; +bool TSatProof<Solver>::isUnit(ClauseId id) const { + return d_idUnit.find(id) != d_idUnit.end(); } + template <class Solver> -bool TSatProof<Solver>::isUnit(ClauseId id) { - return d_idUnit.find(id) != d_idUnit.end(); +typename Solver::TLit TSatProof<Solver>::getUnit(ClauseId id) const { + Assert(isUnit(id)); + return d_idUnit.find(id)->second; } + template <class Solver> -bool TSatProof<Solver>::isUnit(typename Solver::TLit lit) { +bool TSatProof<Solver>::isUnit(typename Solver::TLit lit) const { return d_unitId.find(toInt(lit)) != d_unitId.end(); } + template <class Solver> -ClauseId TSatProof<Solver>::getUnitId(typename Solver::TLit lit) { +ClauseId TSatProof<Solver>::getUnitId(typename Solver::TLit lit) const { Assert(isUnit(lit)); - return d_unitId[toInt(lit)]; + return d_unitId.find(toInt(lit))->second; } + template <class Solver> -bool TSatProof<Solver>::hasResolution(ClauseId id) { - return d_resChains.find(id) != d_resChains.end(); +bool TSatProof<Solver>::hasResolutionChain(ClauseId id) const { + return d_resolutionChains.find(id) != d_resolutionChains.end(); } + template <class Solver> -bool TSatProof<Solver>::isInputClause(ClauseId id) { - return (d_inputClauses.find(id) != d_inputClauses.end()); +const typename TSatProof<Solver>::ResolutionChain& +TSatProof<Solver>::getResolutionChain(ClauseId id) const { + Assert(hasResolutionChain(id)); + const ResolutionChain* chain = d_resolutionChains.find(id)->second; + return *chain; } + template <class Solver> -bool TSatProof<Solver>::isLemmaClause(ClauseId id) { - return (d_lemmaClauses.find(id) != d_lemmaClauses.end()); +typename TSatProof<Solver>::ResolutionChain* +TSatProof<Solver>::getMutableResolutionChain(ClauseId id) { + Assert(hasResolutionChain(id)); + ResolutionChain* chain = d_resolutionChains.find(id)->second; + return chain; } template <class Solver> -bool TSatProof<Solver>::isAssumptionConflict(ClauseId id) { - return d_assumptionConflicts.find(id) != d_assumptionConflicts.end(); +bool TSatProof<Solver>::isInputClause(ClauseId id) const { + return d_inputClauses.find(id) != d_inputClauses.end(); } +template <class Solver> +bool TSatProof<Solver>::isLemmaClause(ClauseId id) const { + return d_lemmaClauses.find(id) != d_lemmaClauses.end(); +} + +template <class Solver> +bool TSatProof<Solver>::isAssumptionConflict(ClauseId id) const { + return d_assumptionConflicts.find(id) != d_assumptionConflicts.end(); +} template <class Solver> -void TSatProof<Solver>::print(ClauseId id) { +void TSatProof<Solver>::print(ClauseId id) const { if (d_deleted.find(id) != d_deleted.end()) { - Debug("proof:sat") << "del"<<id; + Debug("proof:sat") << "del" << id; } else if (isUnit(id)) { printLit<Solver>(getUnit(id)); } else if (id == d_emptyClauseId) { - Debug("proof:sat") << "empty "<< std::endl; - } - else { + Debug("proof:sat") << "empty " << std::endl; + } else { typename Solver::TCRef ref = getClauseRef(id); printClause<Solver>(getClause(ref)); } } + template <class Solver> -void TSatProof<Solver>::printRes(ClauseId id) { - Assert(hasResolution(id)); - Debug("proof:sat") << "id "<< id <<": "; - printRes(d_resChains[id]); +void TSatProof<Solver>::printRes(ClauseId id) const { + Assert(hasResolutionChain(id)); + Debug("proof:sat") << "id " << id << ": "; + printRes(getResolutionChain(id)); } + template <class Solver> -void TSatProof<Solver>::printRes(ResChain<Solver>* res) { - ClauseId start_id = res->getStart(); +void TSatProof<Solver>::printRes(const ResolutionChain& res) const { + ClauseId start_id = res.getStart(); - if(Debug.isOn("proof:sat")) { + if (Debug.isOn("proof:sat")) { Debug("proof:sat") << "("; print(start_id); } - typename ResChain<Solver>::ResSteps& steps = res->getSteps(); - for(unsigned i = 0; i < steps.size(); i++ ) { + const typename ResolutionChain::ResSteps& steps = res.getSteps(); + for (unsigned i = 0; i < steps.size(); i++) { typename Solver::TLit v = steps[i].lit; ClauseId id = steps[i].id; - if(Debug.isOn("proof:sat")) { + if (Debug.isOn("proof:sat")) { Debug("proof:sat") << "["; printLit<Solver>(v); Debug("proof:sat") << "] "; @@ -469,8 +528,8 @@ void TSatProof<Solver>::printRes(ResChain<Solver>* res) { /// registration methods template <class Solver> - ClauseId TSatProof<Solver>::registerClause(typename Solver::TCRef clause, - ClauseKind kind) { +ClauseId TSatProof<Solver>::registerClause(typename Solver::TCRef clause, + ClauseKind kind) { Assert(clause != Solver::TCRef_Undef); typename ClauseIdMap::iterator it = d_clauseId.find(clause); if (it == d_clauseId.end()) { @@ -485,10 +544,8 @@ template <class Solver> Assert(d_lemmaClauses.find(newId) == d_lemmaClauses.end()); d_lemmaClauses.insert(newId); Debug("pf::sat") << "TSatProof::registerClause registering a new lemma clause: " - << newId << " = " << *buildClause(newId) - << ". Explainer theory: " << d_cnfProof->getExplainerTheory() - << std::endl; - d_cnfProof->registerExplanationLemma(newId); + << newId << " = " << *buildClause(newId) + << std::endl; } } @@ -496,15 +553,16 @@ template <class Solver> Assert(kind != INPUT || d_inputClauses.count(id)); Assert(kind != THEORY_LEMMA || d_lemmaClauses.count(id)); - Debug("proof:sat:detailed") << "registerClause CRef: " << clause << " id: " << d_clauseId[clause] - <<" kind: " << kind << "\n"; - //ProofManager::currentPM()->setRegisteredClauseId( d_clauseId[clause] ); + Debug("proof:sat:detailed") << "registerClause CRef: " << clause + << " id: " << d_clauseId[clause] + << " kind: " << kind << "\n"; + // ProofManager::currentPM()->setRegisteredClauseId( d_clauseId[clause] ); return id; } template <class Solver> ClauseId TSatProof<Solver>::registerUnitClause(typename Solver::TLit lit, - ClauseKind kind) { + ClauseKind kind) { Debug("cores") << "registerUnitClause " << kind << std::endl; typename UnitIdMap::iterator it = d_unitId.find(toInt(lit)); if (it == d_unitId.end()) { @@ -519,59 +577,57 @@ ClauseId TSatProof<Solver>::registerUnitClause(typename Solver::TLit lit, if (kind == THEORY_LEMMA) { Assert(d_lemmaClauses.find(newId) == d_lemmaClauses.end()); Debug("pf::sat") << "TSatProof::registerUnitClause: registering a new lemma (UNIT CLAUSE): " - << lit - << ". Explainer theory: " << d_cnfProof->getExplainerTheory() - << std::endl; + << lit + << std::endl; d_lemmaClauses.insert(newId); - d_cnfProof->registerExplanationLemma(newId); } } ClauseId id = d_unitId[toInt(lit)]; Assert(kind != INPUT || d_inputClauses.count(id)); Assert(kind != THEORY_LEMMA || d_lemmaClauses.count(id)); Debug("proof:sat:detailed") << "registerUnitClause id: " << id - <<" kind: " << kind << "\n"; + << " kind: " << kind << "\n"; // ProofManager::currentPM()->setRegisteredClauseId( d_unitId[toInt(lit)] ); return id; } template <class Solver> void TSatProof<Solver>::registerTrueLit(const typename Solver::TLit lit) { - Assert (d_trueLit == ClauseIdUndef); + Assert(d_trueLit == ClauseIdUndef); d_trueLit = registerUnitClause(lit, INPUT); } template <class Solver> void TSatProof<Solver>::registerFalseLit(const typename Solver::TLit lit) { - Assert (d_falseLit == ClauseIdUndef); + Assert(d_falseLit == ClauseIdUndef); d_falseLit = registerUnitClause(lit, INPUT); } template <class Solver> ClauseId TSatProof<Solver>::getTrueUnit() const { - Assert (d_trueLit != ClauseIdUndef); + Assert(d_trueLit != ClauseIdUndef); return d_trueLit; } template <class Solver> ClauseId TSatProof<Solver>::getFalseUnit() const { - Assert (d_falseLit != ClauseIdUndef); + Assert(d_falseLit != ClauseIdUndef); return d_falseLit; } - template <class Solver> void TSatProof<Solver>::registerAssumption(const typename Solver::TVar var) { - Assert (d_assumptions.find(var) == d_assumptions.end()); + Assert(d_assumptions.find(var) == d_assumptions.end()); d_assumptions.insert(var); } template <class Solver> -ClauseId TSatProof<Solver>::registerAssumptionConflict(const typename Solver::TLitVec& confl) { +ClauseId TSatProof<Solver>::registerAssumptionConflict( + const typename Solver::TLitVec& confl) { Debug("proof:sat:detailed") << "registerAssumptionConflict " << std::endl; // Uniqueness is checked in the bit-vector proof // should be vars for (int i = 0; i < confl.size(); ++i) { - Assert (d_assumptions.find(var(confl[i])) != d_assumptions.end()); + Assert(d_assumptions.find(var(confl[i])) != d_assumptions.end()); } ClauseId new_id = ProofManager::currentPM()->nextId(); d_assumptionConflicts.insert(new_id); @@ -588,9 +644,10 @@ ClauseId TSatProof<Solver>::registerAssumptionConflict(const typename Solver::TL return new_id; } - template <class Solver> -void TSatProof<Solver>::removedDfs(typename Solver::TLit lit, LitSet* removedSet, LitVector& removeStack, LitSet& inClause, LitSet& seen) { +void TSatProof<Solver>::removedDfs(typename Solver::TLit lit, + LitSet* removedSet, LitVector& removeStack, + LitSet& inClause, LitSet& seen) { // if we already added the literal return if (seen.count(lit)) { return; @@ -604,20 +661,21 @@ void TSatProof<Solver>::removedDfs(typename Solver::TLit lit, LitSet* removedSet } int size = getClause(reason_ref).size(); - for (int i = 1; i < size; i++ ) { + for (int i = 1; i < size; i++) { typename Solver::TLit v = getClause(reason_ref)[i]; - if(inClause.count(v) == 0 && seen.count(v) == 0) { + if (inClause.count(v) == 0 && seen.count(v) == 0) { removedDfs(v, removedSet, removeStack, inClause, seen); } } - if(seen.count(lit) == 0) { + if (seen.count(lit) == 0) { seen.insert(lit); removeStack.push_back(lit); } } template <class Solver> -void TSatProof<Solver>::removeRedundantFromRes(ResChain<Solver>* res, ClauseId id) { +void TSatProof<Solver>::removeRedundantFromRes(ResChain<Solver>* res, + ClauseId id) { LitSet* removed = res->getRedundant(); if (removed == NULL) { return; @@ -628,11 +686,12 @@ void TSatProof<Solver>::removeRedundantFromRes(ResChain<Solver>* res, ClauseId i LitVector removeStack; LitSet seen; - for (typename LitSet::iterator it = removed->begin(); it != removed->end(); ++it) { + for (typename LitSet::iterator it = removed->begin(); it != removed->end(); + ++it) { removedDfs(*it, removed, removeStack, inClause, seen); } - for (int i = removeStack.size()-1; i >= 0; --i) { + for (int i = removeStack.size() - 1; i >= 0; --i) { typename Solver::TLit lit = removeStack[i]; typename Solver::TCRef reason_ref = d_solver->reason(var(lit)); ClauseId reason_id; @@ -655,41 +714,50 @@ void TSatProof<Solver>::registerResolution(ClauseId id, ResChain<Solver>* res) { removeRedundantFromRes(res, id); Assert(res->redundantRemoved()); - d_resChains[id] = res; - if(Debug.isOn("proof:sat")) { + // Because the SAT solver can add the same clause multiple times, it + // could be the case that a resolution chain for this clause already + // exists (e.g. when removing units in addClause). + if (hasResolutionChain(id)) { + ResChain<Solver>* current = d_resolutionChains.find(id)->second; + delete current; + d_resolutionChains.erase(id); + } + Assert(!hasResolutionChain(id)); + + d_resolutionChains.insert(std::make_pair(id, res)); + if (Debug.isOn("proof:sat")) { printRes(id); } - if(d_checkRes) { + if (d_checkRes) { Assert(checkResolution(id)); } - PSTATS( - d_statistics.d_resChainLengths << ((uint64_t)res->getSteps().size()); - d_statistics.d_avgChainLength.addEntry((uint64_t)res->getSteps().size()); - ++(d_statistics.d_numLearnedClauses); - ) + PSTATS(uint64_t resolutionSteps = + static_cast<uint64_t>(res.getSteps().size()); + d_statistics.d_resChainLengths << resolutionSteps; + d_statistics.d_avgChainLength.addEntry(resolutionSteps); + ++(d_statistics.d_numLearnedClauses);) } - /// recording resolutions template <class Solver> void TSatProof<Solver>::startResChain(typename Solver::TCRef start) { - ClauseId id = getClauseId(start); - ResChain<Solver>* res = new ResChain<Solver>(id); + ClauseId id = getClauseIdForCRef(start); + ResolutionChain* res = new ResolutionChain(id); d_resStack.push_back(res); } template <class Solver> void TSatProof<Solver>::startResChain(typename Solver::TLit start) { ClauseId id = getUnitId(start); - ResChain<Solver>* res = new ResChain<Solver>(id); + ResolutionChain* res = new ResolutionChain(id); d_resStack.push_back(res); } - template <class Solver> void TSatProof<Solver>::addResolutionStep(typename Solver::TLit lit, - typename Solver::TCRef clause, bool sign) { + typename Solver::TCRef clause, + bool sign) { ClauseId id = registerClause(clause, LEARNT); ResChain<Solver>* res = d_resStack.back(); res->addStep(lit, id, sign); @@ -697,14 +765,13 @@ void TSatProof<Solver>::addResolutionStep(typename Solver::TLit lit, template <class Solver> void TSatProof<Solver>::endResChain(ClauseId id) { - Debug("proof:sat:detailed") <<"endResChain " << id << "\n"; + Debug("proof:sat:detailed") << "endResChain " << id << "\n"; Assert(d_resStack.size() > 0); ResChain<Solver>* res = d_resStack.back(); registerResolution(id, res); d_resStack.pop_back(); } - // template <class Solver> // void TSatProof<Solver>::endResChain(typename Solver::TCRef clause) { // Assert(d_resStack.size() > 0); @@ -718,25 +785,25 @@ template <class Solver> void TSatProof<Solver>::endResChain(typename Solver::TLit lit) { Assert(d_resStack.size() > 0); ClauseId id = registerUnitClause(lit, LEARNT); - Debug("proof:sat:detailed") <<"endResChain unit " << id << "\n"; - ResChain<Solver>* res = d_resStack.back(); + Debug("proof:sat:detailed") << "endResChain unit " << id << "\n"; + ResolutionChain* res = d_resStack.back(); d_glueMap[id] = 1; registerResolution(id, res); d_resStack.pop_back(); } - template <class Solver> void TSatProof<Solver>::cancelResChain() { Assert(d_resStack.size() > 0); + ResolutionChain* back = d_resStack.back(); + delete back; d_resStack.pop_back(); } - template <class Solver> void TSatProof<Solver>::storeLitRedundant(typename Solver::TLit lit) { Assert(d_resStack.size() > 0); - ResChain<Solver>* res = d_resStack.back(); + ResolutionChain* res = d_resStack.back(); res->addRedundantLit(lit); } @@ -755,9 +822,9 @@ void TSatProof<Solver>::storeUnitResolution(typename Solver::TLit lit) { template <class Solver> ClauseId TSatProof<Solver>::resolveUnit(typename Solver::TLit lit) { // first check if we already have a resolution for lit - if(isUnit(lit)) { - ClauseId id = getClauseId(lit); - Assert(hasResolution(id) || isInputClause(id) || isLemmaClause(id)); + if (isUnit(lit)) { + ClauseId id = getClauseIdForLiteral(lit); + Assert(hasResolutionChain(id) || isInputClause(id) || isLemmaClause(id)); return id; } typename Solver::TCRef reason_ref = d_solver->reason(var(lit)); @@ -768,12 +835,13 @@ ClauseId TSatProof<Solver>::resolveUnit(typename Solver::TLit lit) { ResChain<Solver>* res = new ResChain<Solver>(reason_id); // Here, the call to resolveUnit() can reallocate memory in the // clause allocator. So reload reason ptr each time. - typename Solver::TClause* reason = &getClause(reason_ref); - for (int i = 0; - i < reason->size(); - i++, reason = &getClause(reason_ref)) { - typename Solver::TLit l = (*reason)[i]; - if(lit != l) { + const typename Solver::TClause& initial_reason = getClause(reason_ref); + size_t current_reason_size = initial_reason.size(); + for (size_t i = 0; i < current_reason_size; i++) { + const typename Solver::TClause& current_reason = getClause(reason_ref); + current_reason_size = current_reason.size(); + typename Solver::TLit l = current_reason[i]; + if (lit != l) { ClauseId res_id = resolveUnit(~l); res->addStep(l, res_id, !sign(l)); } @@ -787,16 +855,19 @@ void TSatProof<Solver>::toStream(std::ostream& out) { Debug("proof:sat") << "TSatProof<Solver>::printProof\n"; Unimplemented("native proof printing not supported yet"); } + template <class Solver> -ClauseId TSatProof<Solver>::storeUnitConflict(typename Solver::TLit conflict_lit, - ClauseKind kind) { +ClauseId TSatProof<Solver>::storeUnitConflict( + typename Solver::TLit conflict_lit, ClauseKind kind) { Debug("cores") << "STORE UNIT CONFLICT" << std::endl; Assert(!d_storedUnitConflict); d_unitConflictId = registerUnitClause(conflict_lit, kind); d_storedUnitConflict = true; - Debug("proof:sat:detailed") <<"storeUnitConflict " << d_unitConflictId << "\n"; + Debug("proof:sat:detailed") << "storeUnitConflict " << d_unitConflictId + << "\n"; return d_unitConflictId; } + template <class Solver> void TSatProof<Solver>::finalizeProof(typename Solver::TCRef conflict_ref) { Assert(d_resStack.size() == 0); @@ -816,10 +887,10 @@ void TSatProof<Solver>::finalizeProof(typename Solver::TCRef conflict_ref) { return; } else { Assert(!d_storedUnitConflict); - conflict_id = registerClause(conflict_ref, LEARNT); //FIXME + conflict_id = registerClause(conflict_ref, LEARNT); // FIXME } - if(Debug.isOn("proof:sat")) { + if (Debug.isOn("proof:sat")) { Debug("proof:sat") << "proof::finalizeProof Final Conflict "; print(conflict_id); } @@ -827,13 +898,13 @@ void TSatProof<Solver>::finalizeProof(typename Solver::TCRef conflict_ref) { ResChain<Solver>* res = new ResChain<Solver>(conflict_id); // Here, the call to resolveUnit() can reallocate memory in the // clause allocator. So reload conflict ptr each time. - typename Solver::TClause* conflict = &getClause(conflict_ref); - for (int i = 0; - i < conflict->size(); - ++i, conflict = &getClause(conflict_ref)) { - typename Solver::TLit lit = (*conflict)[i]; + size_t conflict_size = getClause(conflict_ref).size(); + for (size_t i = 0; i < conflict_size; ++i) { + const typename Solver::TClause& conflict = getClause(conflict_ref); + typename Solver::TLit lit = conflict[i]; ClauseId res_id = resolveUnit(~lit); res->addStep(lit, res_id, !sign(lit)); + conflict_size = conflict.size(); } registerResolution(d_emptyClauseId, res); } @@ -845,12 +916,13 @@ void TSatProof<Solver>::updateCRef(typename Solver::TCRef oldref, if (d_clauseId.find(oldref) == d_clauseId.end()) { return; } - ClauseId id = getClauseId(oldref); + ClauseId id = getClauseIdForCRef(oldref); Assert(d_temp_clauseId.find(newref) == d_temp_clauseId.end()); Assert(d_temp_idClause.find(id) == d_temp_idClause.end()); d_temp_clauseId[newref] = id; d_temp_idClause[id] = newref; } + template <class Solver> void TSatProof<Solver>::finishUpdateCRef() { d_clauseId.swap(d_temp_clauseId); @@ -859,10 +931,11 @@ void TSatProof<Solver>::finishUpdateCRef() { d_idClause.swap(d_temp_idClause); d_temp_idClause.clear(); } + template <class Solver> void TSatProof<Solver>::markDeleted(typename Solver::TCRef clause) { - if (d_clauseId.find(clause) != d_clauseId.end()) { - ClauseId id = getClauseId(clause); + if (hasClauseIdForCRef(clause)) { + ClauseId id = getClauseIdForCRef(clause); Assert(d_deleted.find(id) == d_deleted.end()); d_deleted.insert(id); if (isLemmaClause(id)) { @@ -875,14 +948,13 @@ void TSatProof<Solver>::markDeleted(typename Solver::TCRef clause) { } // template<> -// void toSatClause< ::BVMinisat::Solver> (const BVMinisat::Solver::TClause& minisat_cl, +// void toSatClause< ::BVMinisat::Solver> (const BVMinisat::Solver::TClause& +// minisat_cl, // prop::SatClause& sat_cl) { // prop::BVMinisatSatSolver::toSatClause(minisat_cl, sat_cl); // } - - template <class Solver> void TSatProof<Solver>::constructProof(ClauseId conflict) { collectClauses(conflict); @@ -894,11 +966,10 @@ std::string TSatProof<Solver>::clauseName(ClauseId id) { if (isInputClause(id)) { os << ProofManager::getInputClauseName(id, d_name); return os.str(); - } else - if (isLemmaClause(id)) { + } else if (isLemmaClause(id)) { os << ProofManager::getLemmaClauseName(id, d_name); return os.str(); - }else { + } else { os << ProofManager::getLearntClauseName(id, d_name); return os.str(); } @@ -944,17 +1015,15 @@ void TSatProof<Solver>::collectClauses(ClauseId id) { d_seenLearnt.insert(id); } - Assert(d_resChains.find(id) != d_resChains.end()); - ResChain<Solver>* res = d_resChains[id]; - PSTATS( - d_statistics.d_usedResChainLengths << ((uint64_t)res->getSteps().size()); - d_statistics.d_usedClauseGlue << ((uint64_t) d_glueMap[id]); - ); - ClauseId start = res->getStart(); + const ResolutionChain& res = getResolutionChain(id); + const typename ResolutionChain::ResSteps& steps = res.getSteps(); + PSTATS(d_statistics.d_usedResChainLengths + << ((uint64_t)steps.size()); + d_statistics.d_usedClauseGlue << ((uint64_t)d_glueMap[id]);); + ClauseId start = res.getStart(); collectClauses(start); - typename ResChain<Solver>::ResSteps steps = res->getSteps(); - for(size_t i = 0; i < steps.size(); i++) { + for (size_t i = 0; i < steps.size(); i++) { collectClauses(steps[i].id); } } @@ -964,28 +1033,27 @@ void TSatProof<Solver>::collectClausesUsed(IdToSatClause& inputs, IdToSatClause& lemmas) { inputs = d_seenInputs; lemmas = d_seenLemmas; - PSTATS ( - d_statistics.d_numLearnedInProof.setData(d_seenLearnt.size()); - d_statistics.d_numLemmasInProof.setData(d_seenLemmas.size()); - ); + PSTATS(d_statistics.d_numLearnedInProof.setData(d_seenLearnt.size()); + d_statistics.d_numLemmasInProof.setData(d_seenLemmas.size());); } template <class Solver> void TSatProof<Solver>::storeClauseGlue(ClauseId clause, int glue) { - Assert (d_glueMap.find(clause) == d_glueMap.end()); + Assert(d_glueMap.find(clause) == d_glueMap.end()); d_glueMap.insert(std::make_pair(clause, glue)); } template <class Solver> TSatProof<Solver>::Statistics::Statistics(const std::string& prefix) - : d_numLearnedClauses("satproof::"+prefix+"::NumLearnedClauses", 0) - , d_numLearnedInProof("satproof::"+prefix+"::NumLearnedInProof", 0) - , d_numLemmasInProof("satproof::"+prefix+"::NumLemmasInProof", 0) - , d_avgChainLength("satproof::"+prefix+"::AvgResChainLength") - , d_resChainLengths("satproof::"+prefix+"::ResChainLengthsHist") - , d_usedResChainLengths("satproof::"+prefix+"::UsedResChainLengthsHist") - , d_clauseGlue("satproof::"+prefix+"::ClauseGlueHist") - , d_usedClauseGlue("satproof::"+prefix+"::UsedClauseGlueHist") { + : d_numLearnedClauses("satproof::" + prefix + "::NumLearnedClauses", 0), + d_numLearnedInProof("satproof::" + prefix + "::NumLearnedInProof", 0), + d_numLemmasInProof("satproof::" + prefix + "::NumLemmasInProof", 0), + d_avgChainLength("satproof::" + prefix + "::AvgResChainLength"), + d_resChainLengths("satproof::" + prefix + "::ResChainLengthsHist"), + d_usedResChainLengths("satproof::" + prefix + + "::UsedResChainLengthsHist"), + d_clauseGlue("satproof::" + prefix + "::ClauseGlueHist"), + d_usedClauseGlue("satproof::" + prefix + "::UsedClauseGlueHist") { smtStatisticsRegistry()->registerStat(&d_numLearnedClauses); smtStatisticsRegistry()->registerStat(&d_numLearnedInProof); smtStatisticsRegistry()->registerStat(&d_numLemmasInProof); @@ -1008,73 +1076,78 @@ TSatProof<Solver>::Statistics::~Statistics() { smtStatisticsRegistry()->unregisterStat(&d_usedClauseGlue); } - /// LFSCSatProof class template <class Solver> -void LFSCSatProof<Solver>::printResolution(ClauseId id, std::ostream& out, std::ostream& paren) { +void LFSCSatProof<Solver>::printResolution(ClauseId id, std::ostream& out, + std::ostream& paren) { out << "(satlem_simplify _ _ _ "; - ResChain<Solver>* res = this->d_resChains[id]; - typename ResChain<Solver>::ResSteps& steps = res->getSteps(); + const ResChain<Solver>& res = this->getResolutionChain(id); + const typename ResChain<Solver>::ResSteps& steps = res.getSteps(); - for (int i = steps.size()-1; i >= 0; i--) { + for (int i = steps.size() - 1; i >= 0; i--) { out << "("; - out << (steps[i].sign? "R" : "Q") << " _ _ "; + out << (steps[i].sign ? "R" : "Q") << " _ _ "; } - ClauseId start_id = res->getStart(); + ClauseId start_id = res.getStart(); out << this->clauseName(start_id) << " "; - for(unsigned i = 0; i < steps.size(); i++) { - prop::SatVariable v = prop::MinisatSatSolver::toSatVariable(var(steps[i].lit)); - out << this->clauseName(steps[i].id) << " "<<ProofManager::getVarName(v, this->d_name) <<")"; + for (unsigned i = 0; i < steps.size(); i++) { + prop::SatVariable v = + prop::MinisatSatSolver::toSatVariable(var(steps[i].lit)); + out << this->clauseName(steps[i].id) << " " + << ProofManager::getVarName(v, this->d_name) << ")"; } if (id == this->d_emptyClauseId) { - out <<"(\\empty empty)"; + out <<"(\\ empty empty)"; return; } - out << "(\\" << this->clauseName(id) << "\n"; // bind to lemma name + out << "(\\ " << this->clauseName(id) << "\n"; // bind to lemma name paren << "))"; // closing parethesis for lemma binding and satlem } /// LFSCSatProof class template <class Solver> -void LFSCSatProof<Solver>::printAssumptionsResolution(ClauseId id, std::ostream& out, std::ostream& paren) { - Assert (this->isAssumptionConflict(id)); +void LFSCSatProof<Solver>::printAssumptionsResolution(ClauseId id, + std::ostream& out, + std::ostream& paren) { + Assert(this->isAssumptionConflict(id)); // print the resolution proving the assumption conflict printResolution(id, out, paren); // resolve out assumptions to prove empty clause out << "(satlem_simplify _ _ _ "; - std::vector<typename Solver::TLit>& confl = *(this->d_assumptionConflictsDebug[id]); + std::vector<typename Solver::TLit>& confl = + *(this->d_assumptionConflictsDebug[id]); - Assert (confl.size()); + Assert(confl.size()); for (unsigned i = 0; i < confl.size(); ++i) { prop::SatLiteral lit = toSatLiteral<Solver>(confl[i]); - out <<"("; - out << (lit.isNegated() ? "Q" : "R") <<" _ _ "; + out << "("; + out << (lit.isNegated() ? "Q" : "R") << " _ _ "; } - out << this->clauseName(id)<< " "; + out << this->clauseName(id) << " "; for (int i = confl.size() - 1; i >= 0; --i) { prop::SatLiteral lit = toSatLiteral<Solver>(confl[i]); prop::SatVariable v = lit.getSatVariable(); - out << "unit"<< v <<" "; - out << ProofManager::getVarName(v, this->d_name) <<")"; + out << "unit" << v << " "; + out << ProofManager::getVarName(v, this->d_name) << ")"; } - out <<"(\\ e e)\n"; - paren <<")"; + out << "(\\ e e)\n"; + paren << ")"; } - template <class Solver> -void LFSCSatProof<Solver>::printResolutions(std::ostream& out, std::ostream& paren) { +void LFSCSatProof<Solver>::printResolutions(std::ostream& out, + std::ostream& paren) { Debug("bv-proof") << "; print resolutions" << std::endl; std::set<ClauseId>::iterator it = this->d_seenLearnt.begin(); - for(; it!= this->d_seenLearnt.end(); ++it) { - if(*it != this->d_emptyClauseId) { + for (; it != this->d_seenLearnt.end(); ++it) { + if (*it != this->d_emptyClauseId) { Debug("bv-proof") << "; print resolution for " << *it << std::endl; printResolution(*it, out, paren); } @@ -1083,29 +1156,29 @@ void LFSCSatProof<Solver>::printResolutions(std::ostream& out, std::ostream& par } template <class Solver> -void LFSCSatProof<Solver>::printResolutionEmptyClause(std::ostream& out, std::ostream& paren) { +void LFSCSatProof<Solver>::printResolutionEmptyClause(std::ostream& out, + std::ostream& paren) { printResolution(this->d_emptyClauseId, out, paren); } - inline std::ostream& operator<<(std::ostream& out, CVC4::ClauseKind k) { - switch(k) { - case CVC4::INPUT: - out << "INPUT"; - break; - case CVC4::THEORY_LEMMA: - out << "THEORY_LEMMA"; - break; - case CVC4::LEARNT: - out << "LEARNT"; - break; - default: - out << "ClauseKind Unknown! [" << unsigned(k) << "]"; + switch (k) { + case CVC4::INPUT: + out << "INPUT"; + break; + case CVC4::THEORY_LEMMA: + out << "THEORY_LEMMA"; + break; + case CVC4::LEARNT: + out << "LEARNT"; + break; + default: + out << "ClauseKind Unknown! [" << unsigned(k) << "]"; } return out; } -}/* CVC4 namespace */ +} /* CVC4 namespace */ #endif /* __CVC4__SAT__PROOF_IMPLEMENTATION_H */ diff --git a/src/proof/skolemization_manager.h b/src/proof/skolemization_manager.h index de510e514..b026e21c2 100644 --- a/src/proof/skolemization_manager.h +++ b/src/proof/skolemization_manager.h @@ -37,7 +37,6 @@ public: Node getSkolem(Node disequality); Node getDisequality(Node skolem); bool isSkolem(Node skolem); - void clear(); std::hash_map<Node, Node, NodeHashFunction>::const_iterator begin(); diff --git a/src/proof/theory_proof.cpp b/src/proof/theory_proof.cpp index 088275b3f..d12b561a6 100644 --- a/src/proof/theory_proof.cpp +++ b/src/proof/theory_proof.cpp @@ -19,12 +19,14 @@ #include "base/cvc4_assert.h" #include "context/context.h" #include "options/bv_options.h" +#include "options/proof_options.h" #include "proof/arith_proof.h" #include "proof/array_proof.h" #include "proof/bitvector_proof.h" #include "proof/clause_id.h" #include "proof/cnf_proof.h" #include "proof/proof_manager.h" +#include "proof/proof_output_channel.h" #include "proof/proof_utils.h" #include "proof/sat_proof.h" #include "proof/uf_proof.h" @@ -45,77 +47,9 @@ namespace CVC4 { -unsigned CVC4::LetCount::counter = 0; +unsigned CVC4::ProofLetCount::counter = 0; static unsigned LET_COUNT = 1; -//for proof replay -class ProofOutputChannel : public theory::OutputChannel { -public: - Node d_conflict; - Proof* d_proof; - Node d_lemma; - - ProofOutputChannel() : d_conflict(), d_proof(NULL) {} - virtual ~ProofOutputChannel() throw() {} - - void conflict(TNode n, Proof* pf) throw() { - Trace("theory-proof-debug") << "; CONFLICT: " << n << std::endl; - Assert(d_conflict.isNull()); - Assert(!n.isNull()); - d_conflict = n; - Assert(pf != NULL); - d_proof = pf; - } - bool propagate(TNode x) throw() { - Trace("theory-proof-debug") << "got a propagation: " << x << std::endl; - return true; - } - theory::LemmaStatus lemma(TNode n, ProofRule rule, bool, bool, bool) throw() { - Trace("theory-proof-debug") << "new lemma: " << n << std::endl; - d_lemma = n; - return theory::LemmaStatus(TNode::null(), 0); - } - theory::LemmaStatus splitLemma(TNode, bool) throw() { - AlwaysAssert(false); - return theory::LemmaStatus(TNode::null(), 0); - } - void requirePhase(TNode n, bool b) throw() { - Debug("theory-proof-debug") << "ProofOutputChannel::requirePhase called" << std::endl; - Trace("theory-proof-debug") << "requirePhase " << n << " " << b << std::endl; - } - bool flipDecision() throw() { - Debug("theory-proof-debug") << "ProofOutputChannel::flipDecision called" << std::endl; - AlwaysAssert(false); - return false; - } - void setIncomplete() throw() { - Debug("theory-proof-debug") << "ProofOutputChannel::setIncomplete called" << std::endl; - AlwaysAssert(false); - } -};/* class ProofOutputChannel */ - -//for proof replay -class MyPreRegisterVisitor { - theory::Theory* d_theory; - __gnu_cxx::hash_set<TNode, TNodeHashFunction> d_visited; -public: - typedef void return_type; - MyPreRegisterVisitor(theory::Theory* theory) - : d_theory(theory) - , d_visited() - {} - bool alreadyVisited(TNode current, TNode parent) { return d_visited.find(current) != d_visited.end(); } - void visit(TNode current, TNode parent) { - if(theory::Theory::theoryOf(current) == d_theory->getId()) { - //Trace("theory-proof-debug") << "preregister " << current << std::endl; - d_theory->preRegisterTerm(current); - d_visited.insert(current); - } - } - void start(TNode node) { } - void done(TNode node) { } -}; /* class MyPreRegisterVisitor */ - TheoryProofEngine::TheoryProofEngine() : d_registrationCache() , d_theoryProofTable() @@ -131,13 +65,12 @@ TheoryProofEngine::~TheoryProofEngine() { } } - void TheoryProofEngine::registerTheory(theory::Theory* th) { - if( th ){ + if (th) { theory::TheoryId id = th->getId(); if(d_theoryProofTable.find(id) == d_theoryProofTable.end()) { - Trace("theory-proof-debug") << "; register theory " << id << std::endl; + Trace("pf::tp") << "TheoryProofEngine::registerTheory: " << id << std::endl; if (id == theory::THEORY_UF) { d_theoryProofTable[id] = new LFSCUFProof((theory::uf::TheoryUF*)th, this); @@ -147,7 +80,6 @@ void TheoryProofEngine::registerTheory(theory::Theory* th) { if (id == theory::THEORY_BV) { BitVectorProof * bvp = new LFSCBitVectorProof((theory::bv::TheoryBV*)th, this); d_theoryProofTable[id] = bvp; - ((theory::bv::TheoryBV*)th)->setProofLog( bvp ); return; } @@ -166,20 +98,54 @@ void TheoryProofEngine::registerTheory(theory::Theory* th) { } } +void TheoryProofEngine::finishRegisterTheory(theory::Theory* th) { + if (th) { + theory::TheoryId id = th->getId(); + if (id == theory::THEORY_BV) { + Assert(d_theoryProofTable.find(id) != d_theoryProofTable.end()); + + BitVectorProof *bvp = (BitVectorProof *)d_theoryProofTable[id]; + ((theory::bv::TheoryBV*)th)->setProofLog( bvp ); + return; + } + } +} + TheoryProof* TheoryProofEngine::getTheoryProof(theory::TheoryId id) { + // The UF theory handles queries for the Builtin theory. + if (id == theory::THEORY_BUILTIN) { + Debug("pf::tp") << "TheoryProofEngine::getTheoryProof: BUILTIN --> UF" << std::endl; + id = theory::THEORY_UF; + } + Assert (d_theoryProofTable.find(id) != d_theoryProofTable.end()); return d_theoryProofTable[id]; } +void TheoryProofEngine::markTermForFutureRegistration(Expr term, theory::TheoryId id) { + d_exprToTheoryIds[term].insert(id); +} + +void TheoryProofEngine::printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2) { + Assert(c1.isConst()); + Assert(c2.isConst()); + + Assert(theory::Theory::theoryOf(c1) == theory::Theory::theoryOf(c2)); + getTheoryProof(theory::Theory::theoryOf(c1))->printConstantDisequalityProof(os, c1, c2); +} + void TheoryProofEngine::registerTerm(Expr term) { + Debug("pf::tp") << "TheoryProofEngine::registerTerm: registering term: " << term << std::endl; + if (d_registrationCache.count(term)) { return; } - Debug("pf::tp") << "TheoryProofEngine::registerTerm: registering new term: " << term << std::endl; + + Debug("pf::tp") << "TheoryProofEngine::registerTerm: registering NEW term: " << term << std::endl; theory::TheoryId theory_id = theory::Theory::theoryOf(term); - Debug("pf::tp") << "Term's theory: " << theory_id << std::endl; + Debug("pf::tp") << "Term's theory( " << term << " ) = " << theory_id << std::endl; // don't need to register boolean terms if (theory_id == theory::THEORY_BUILTIN || @@ -193,58 +159,64 @@ void TheoryProofEngine::registerTerm(Expr term) { if (!supportedTheory(theory_id)) return; + // Register the term with its owner theory getTheoryProof(theory_id)->registerTerm(term); + + // A special case: the array theory needs to know of every skolem, even if + // it belongs to another theory (e.g., a BV skolem) + if (ProofManager::getSkolemizationManager()->isSkolem(term) && theory_id != theory::THEORY_ARRAY) { + Debug("pf::tp") << "TheoryProofEngine::registerTerm: Special case: registering a non-array skolem: " << term << std::endl; + getTheoryProof(theory::THEORY_ARRAY)->registerTerm(term); + } + d_registrationCache.insert(term); } -theory::TheoryId TheoryProofEngine::getTheoryForLemma(ClauseId id) { +theory::TheoryId TheoryProofEngine::getTheoryForLemma(const prop::SatClause* clause) { ProofManager* pm = ProofManager::currentPM(); - Debug("pf::tp") << "TheoryProofEngine::getTheoryForLemma( " << id << " )" - << " = " << pm->getCnfProof()->getOwnerTheory(id) << std::endl; + std::set<Node> nodes; + for(unsigned i = 0; i < clause->size(); ++i) { + prop::SatLiteral lit = (*clause)[i]; + Node node = pm->getCnfProof()->getAtom(lit.getSatVariable()); + Expr atom = node.toExpr(); + if (atom.isConst()) { + Assert (atom == utils::mkTrue()); + continue; + } - if ((pm->getLogic() == "QF_UFLIA") || (pm->getLogic() == "QF_UFLRA")) { - Debug("pf::tp") << "TheoryProofEngine::getTheoryForLemma: special hack for Arithmetic-with-holes support. " - << "Returning THEORY_ARITH" << std::endl; - return theory::THEORY_ARITH; + nodes.insert(lit.isNegated() ? node.notNode() : node); } - return pm->getCnfProof()->getOwnerTheory(id); - - // if (pm->getLogic() == "QF_UF") return theory::THEORY_UF; - // if (pm->getLogic() == "QF_BV") return theory::THEORY_BV; - // if (pm->getLogic() == "QF_AX") return theory::THEORY_ARRAY; - // if (pm->getLogic() == "ALL_SUPPORTED") return theory::THEORY_BV; - - // Debug("pf::tp") << "Unsupported logic (" << pm->getLogic() << ")" << std::endl; - - // Unreachable(); + // Ensure that the lemma is in the database. + Assert (pm->getCnfProof()->haveProofRecipe(nodes)); + return pm->getCnfProof()->getProofRecipe(nodes).getTheory(); } -void LFSCTheoryProofEngine::bind(Expr term, LetMap& map, Bindings& let_order) { - LetMap::iterator it = map.find(term); +void LFSCTheoryProofEngine::bind(Expr term, ProofLetMap& map, Bindings& let_order) { + ProofLetMap::iterator it = map.find(term); if (it != map.end()) { - LetCount& count = it->second; + ProofLetCount& count = it->second; count.increment(); return; } for (unsigned i = 0; i < term.getNumChildren(); ++i) { bind(term[i], map, let_order); } - unsigned new_id = LetCount::newId(); - map[term] = LetCount(new_id); + unsigned new_id = ProofLetCount::newId(); + map[term] = ProofLetCount(new_id); let_order.push_back(LetOrderElement(term, new_id)); } void LFSCTheoryProofEngine::printLetTerm(Expr term, std::ostream& os) { - LetMap map; + ProofLetMap map; Bindings let_order; bind(term, map, let_order); std::ostringstream paren; for (unsigned i = 0; i < let_order.size(); ++i) { Expr current_expr = let_order[i].expr; unsigned let_id = let_order[i].id; - LetMap::const_iterator it = map.find(current_expr); + ProofLetMap::const_iterator it = map.find(current_expr); Assert (it != map.end()); unsigned let_count = it->second.count; Assert(let_count); @@ -253,7 +225,7 @@ void LFSCTheoryProofEngine::printLetTerm(Expr term, std::ostream& os) { continue; } - os << "(@ let"<<let_id << " "; + os << "(@ let" <<let_id << " "; printTheoryTerm(current_expr, os, map); paren <<")"; } @@ -264,16 +236,13 @@ void LFSCTheoryProofEngine::printLetTerm(Expr term, std::ostream& os) { printTheoryTerm(last, os, map); } else { - os << " let"<< last_let_id; + os << " let" << last_let_id; } os << paren.str(); } - -void LFSCTheoryProofEngine::printTheoryTerm(Expr term, std::ostream& os, const LetMap& map) { +void LFSCTheoryProofEngine::printTheoryTerm(Expr term, std::ostream& os, const ProofLetMap& map) { theory::TheoryId theory_id = theory::Theory::theoryOf(term); - Debug("pf::tp") << std::endl << "LFSCTheoryProofEngine::printTheoryTerm: term = " << term - << ", theory_id = " << theory_id << std::endl; // boolean terms and ITEs are special because they // are common to all theories @@ -315,6 +284,29 @@ void LFSCTheoryProofEngine::printSort(Type type, std::ostream& os) { Unreachable(); } +void LFSCTheoryProofEngine::performExtraRegistrations() { + ExprToTheoryIds::const_iterator it; + for (it = d_exprToTheoryIds.begin(); it != d_exprToTheoryIds.end(); ++it) { + if (d_registrationCache.count(it->first)) { // Only register if the term appeared + TheoryIdSet::const_iterator theoryIt; + for (theoryIt = it->second.begin(); theoryIt != it->second.end(); ++theoryIt) { + Debug("pf::tp") << "\tExtra registration of term " << it->first + << " with theory: " << *theoryIt << std::endl; + Assert(supportedTheory(*theoryIt)); + getTheoryProof(*theoryIt)->registerTerm(it->first); + } + } + } +} + +void LFSCTheoryProofEngine::treatBoolsAsFormulas(bool value) { + TheoryProofTable::const_iterator it = d_theoryProofTable.begin(); + TheoryProofTable::const_iterator end = d_theoryProofTable.end(); + for (; it != end; ++it) { + it->second->treatBoolsAsFormulas(value); + } +} + void LFSCTheoryProofEngine::registerTermsFromAssertions() { ProofManager::assertions_iterator it = ProofManager::currentPM()->begin_assertions(); ProofManager::assertions_iterator end = ProofManager::currentPM()->end_assertions(); @@ -322,6 +314,8 @@ void LFSCTheoryProofEngine::registerTermsFromAssertions() { for(; it != end; ++it) { registerTerm(*it); } + + performExtraRegistrations(); } void LFSCTheoryProofEngine::printAssertions(std::ostream& os, std::ostream& paren) { @@ -333,17 +327,51 @@ void LFSCTheoryProofEngine::printAssertions(std::ostream& os, std::ostream& pare for (; it != end; ++it) { Debug("pf::tp") << "printAssertions: assertion is: " << *it << std::endl; - // FIXME: merge this with counter - os << "(% A" << counter++ << " (th_holds "; - printLetTerm(*it, os); + std::ostringstream name; + name << "A" << counter++; + os << "(% " << name.str() << " (th_holds "; + + // Assertions appear before the global let map, so we use a dummpMap to avoid letification here. + ProofLetMap dummyMap; + printBoundTerm(*it, os, dummyMap); + os << ")\n"; paren << ")"; } - //store map between assertion and counter - // ProofManager::currentPM()->setAssertion( *it ); + Debug("pf::tp") << "LFSCTheoryProofEngine::printAssertions done" << std::endl << std::endl; } +void LFSCTheoryProofEngine::printLemmaRewrites(NodePairSet& rewrites, + std::ostream& os, + std::ostream& paren) { + Debug("pf::tp") << "LFSCTheoryProofEngine::printLemmaRewrites called" << std::endl << std::endl; + + NodePairSet::const_iterator it; + + for (it = rewrites.begin(); it != rewrites.end(); ++it) { + Debug("pf::tp") << "printLemmaRewrites: " << it->first << " --> " << it->second << std::endl; + + Node n1 = it->first; + Node n2 = it->second; + Assert(n1.toExpr() == utils::mkFalse() || + theory::Theory::theoryOf(n1) == theory::Theory::theoryOf(n2)); + + std::ostringstream rewriteRule; + rewriteRule << ".lrr" << d_assertionToRewrite.size(); + + os << "(th_let_pf _ "; + getTheoryProof(theory::Theory::theoryOf(n1))->printRewriteProof(os, n1, n2); + os << "(\\ " << rewriteRule.str() << "\n"; + + d_assertionToRewrite[it->first] = rewriteRule.str(); + Debug("pf::tp") << "d_assertionToRewrite[" << it->first << "] = " << rewriteRule.str() << std::endl; + paren << "))"; + } + + Debug("pf::tp") << "LFSCTheoryProofEngine::printLemmaRewrites done" << std::endl << std::endl; +} + void LFSCTheoryProofEngine::printSortDeclarations(std::ostream& os, std::ostream& paren) { Debug("pf::tp") << "LFSCTheoryProofEngine::printSortDeclarations called" << std::endl << std::endl; @@ -378,55 +406,150 @@ void LFSCTheoryProofEngine::printDeferredDeclarations(std::ostream& os, std::ost } } -void LFSCTheoryProofEngine::printTheoryLemmas(const IdToSatClause& lemmas, - std::ostream& os, - std::ostream& paren) { - os << " ;; Theory Lemmas \n"; - ProofManager* pm = ProofManager::currentPM(); - IdToSatClause::const_iterator it = lemmas.begin(); - IdToSatClause::const_iterator end = lemmas.end(); - - Debug("pf::tp") << "LFSCTheoryProofEngine::printTheoryLemmas: checking lemma owners..." << std::endl; +void LFSCTheoryProofEngine::printAliasingDeclarations(std::ostream& os, std::ostream& paren) { + Debug("pf::tp") << "LFSCTheoryProofEngine::printAliasingDeclarations called" << std::endl; + TheoryProofTable::const_iterator it = d_theoryProofTable.begin(); + TheoryProofTable::const_iterator end = d_theoryProofTable.end(); for (; it != end; ++it) { - Debug("pf::tp") << "LFSCTheoryProofEngine::printTheoryLemmas: new lemma" << std::endl; - ClauseId id = it->first; - Debug("pf::tp") << "\tLemma = " << id - << ". Owner theory: " << pm->getCnfProof()->getOwnerTheory(id) << std::endl; + it->second->printAliasingDeclarations(os, paren); } - it = lemmas.begin(); +} - // BitVector theory is special case: must know all - // conflicts needed ahead of time for resolution - // proof lemmas - std::vector<Expr> bv_lemmas; - for (; it != end; ++it) { +void LFSCTheoryProofEngine::dumpTheoryLemmas(const IdToSatClause& lemmas) { + Debug("pf::dumpLemmas") << "Dumping ALL theory lemmas" << std::endl << std::endl; + + ProofManager* pm = ProofManager::currentPM(); + for (IdToSatClause::const_iterator it = lemmas.begin(); it != lemmas.end(); ++it) { ClauseId id = it->first; + Debug("pf::dumpLemmas") << "**** \tLemma ID = " << id << std::endl; const prop::SatClause* clause = it->second; + std::set<Node> nodes; + for(unsigned i = 0; i < clause->size(); ++i) { + prop::SatLiteral lit = (*clause)[i]; + Node node = pm->getCnfProof()->getAtom(lit.getSatVariable()); + if (node.isConst()) { + Assert (node.toExpr() == utils::mkTrue()); + continue; + } + nodes.insert(lit.isNegated() ? node.notNode() : node); + } + + LemmaProofRecipe recipe = pm->getCnfProof()->getProofRecipe(nodes); + recipe.dump("pf::dumpLemmas"); + } + + Debug("pf::dumpLemmas") << "Theory lemma printing DONE" << std::endl << std::endl; +} - theory::TheoryId theory_id = getTheoryForLemma(id); - if (theory_id != theory::THEORY_BV) continue; +// TODO: this function should be moved into the BV prover. +void LFSCTheoryProofEngine::finalizeBvConflicts(const IdToSatClause& lemmas, std::ostream& os) { + // BitVector theory is special case: must know all conflicts needed + // ahead of time for resolution proof lemmas + std::vector<Expr> bv_lemmas; + + for (IdToSatClause::const_iterator it = lemmas.begin(); it != lemmas.end(); ++it) { + const prop::SatClause* clause = it->second; std::vector<Expr> conflict; + std::set<Node> conflictNodes; for(unsigned i = 0; i < clause->size(); ++i) { prop::SatLiteral lit = (*clause)[i]; - Expr atom = pm->getCnfProof()->getAtom(lit.getSatVariable()).toExpr(); + Node node = ProofManager::currentPM()->getCnfProof()->getAtom(lit.getSatVariable()); + Expr atom = node.toExpr(); + + // The literals (true) and (not false) are omitted from conflicts if (atom.isConst()) { Assert (atom == utils::mkTrue() || (atom == utils::mkFalse() && lit.isNegated())); continue; } + Expr expr_lit = lit.isNegated() ? atom.notExpr() : atom; conflict.push_back(expr_lit); + conflictNodes.insert(lit.isNegated() ? node.notNode() : node); + } + + LemmaProofRecipe recipe = ProofManager::currentPM()->getCnfProof()->getProofRecipe(conflictNodes); + + unsigned numberOfSteps = recipe.getNumSteps(); + + prop::SatClause currentClause = *clause; + std::vector<Expr> currentClauseExpr = conflict; + + for (unsigned i = 0; i < numberOfSteps; ++i) { + const LemmaProofRecipe::ProofStep* currentStep = recipe.getStep(i); + + if (currentStep->getTheory() != theory::THEORY_BV) { + continue; + } + + // If any rewrites took place, we need to update the conflict clause accordingly + std::set<Node> missingAssertions = recipe.getMissingAssertionsForStep(i); + std::map<Node, Node> explanationToMissingAssertion; + std::set<Node>::iterator assertionIt; + for (assertionIt = missingAssertions.begin(); + assertionIt != missingAssertions.end(); + ++assertionIt) { + Node negated = (*assertionIt).negate(); + explanationToMissingAssertion[recipe.getExplanation(negated)] = negated; + } + + currentClause = *clause; + currentClauseExpr = conflict; + + for (unsigned j = 0; j < i; ++j) { + // Literals already used in previous steps need to be negated + Node previousLiteralNode = recipe.getStep(j)->getLiteral(); + + // If this literal is the result of a rewrite, we need to translate it + if (explanationToMissingAssertion.find(previousLiteralNode) != + explanationToMissingAssertion.end()) { + previousLiteralNode = explanationToMissingAssertion[previousLiteralNode]; + } + + Node previousLiteralNodeNegated = previousLiteralNode.negate(); + prop::SatLiteral previousLiteralNegated = + ProofManager::currentPM()->getCnfProof()->getLiteral(previousLiteralNodeNegated); + + currentClause.push_back(previousLiteralNegated); + currentClauseExpr.push_back(previousLiteralNodeNegated.toExpr()); + } + + // If we're in the final step, the last literal is Null and should not be added. + // Otherwise, the current literal does NOT need to be negated + Node currentLiteralNode = currentStep->getLiteral(); + + if (currentLiteralNode != Node()) { + prop::SatLiteral currentLiteral = + ProofManager::currentPM()->getCnfProof()->getLiteral(currentLiteralNode); + + currentClause.push_back(currentLiteral); + currentClauseExpr.push_back(currentLiteralNode.toExpr()); + } + + bv_lemmas.push_back(utils::mkSortedExpr(kind::OR, currentClauseExpr)); } - bv_lemmas.push_back(utils::mkSortedExpr(kind::OR, conflict)); } - // FIXME: ugly, move into bit-vector proof by adding lemma - // queue inside each theory_proof + BitVectorProof* bv = ProofManager::getBitVectorProof(); bv->finalizeConflicts(bv_lemmas); + // bv->printResolutionProof(os, paren, letMap); +} - bv->printResolutionProof(os, paren); +void LFSCTheoryProofEngine::printTheoryLemmas(const IdToSatClause& lemmas, + std::ostream& os, + std::ostream& paren, + ProofLetMap& map) { + os << " ;; Theory Lemmas \n"; + Debug("pf::tp") << "LFSCTheoryProofEngine::printTheoryLemmas: starting" << std::endl; + + if (Debug.isOn("pf::dumpLemmas")) { + dumpTheoryLemmas(lemmas); + } + + // finalizeBvConflicts(lemmas, os, paren, map); + ProofManager::getBitVectorProof()->printResolutionProof(os, paren, map); if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { Assert (lemmas.size() == 1); @@ -434,61 +557,255 @@ void LFSCTheoryProofEngine::printTheoryLemmas(const IdToSatClause& lemmas, return; } - it = lemmas.begin(); - + ProofManager* pm = ProofManager::currentPM(); Debug("pf::tp") << "LFSCTheoryProofEngine::printTheoryLemmas: printing lemmas..." << std::endl; - for (; it != end; ++it) { - Debug("pf::tp") << "LFSCTheoryProofEngine::printTheoryLemmas: printing a new lemma!" << std::endl; - - // Debug("pf::tp") << "\tLemma = " << it->first << ", " << *(it->second) << std::endl; + for (IdToSatClause::const_iterator it = lemmas.begin(); it != lemmas.end(); ++it) { ClauseId id = it->first; - Debug("pf::tp") << "Owner theory:" << pm->getCnfProof()->getOwnerTheory(id) << std::endl; const prop::SatClause* clause = it->second; - // printing clause as it appears in resolution proof - os << "(satlem _ _ "; - std::ostringstream clause_paren; - Debug("pf::tp") << "CnfProof printing clause..." << std::endl; - pm->getCnfProof()->printClause(*clause, os, clause_paren); - Debug("pf::tp") << "CnfProof printing clause - Done!" << std::endl; + Debug("pf::tp") << "LFSCTheoryProofEngine::printTheoryLemmas: printing lemma. ID = " + << id << std::endl; std::vector<Expr> clause_expr; + std::set<Node> clause_expr_nodes; for(unsigned i = 0; i < clause->size(); ++i) { prop::SatLiteral lit = (*clause)[i]; - Expr atom = pm->getCnfProof()->getAtom(lit.getSatVariable()).toExpr(); + Node node = pm->getCnfProof()->getAtom(lit.getSatVariable()); + Expr atom = node.toExpr(); if (atom.isConst()) { Assert (atom == utils::mkTrue()); continue; } Expr expr_lit = lit.isNegated() ? atom.notExpr(): atom; clause_expr.push_back(expr_lit); + clause_expr_nodes.insert(lit.isNegated() ? node.notNode() : node); } - Debug("pf::tp") << "Expression printing done!" << std::endl; - - // query appropriate theory for proof of clause - theory::TheoryId theory_id = getTheoryForLemma(id); - Debug("pf::tp") << "Get theory lemma from " << theory_id << "..." << std::endl; - Debug("theory-proof-debug") << ";; Get theory lemma from " << theory_id << "..." << std::endl; - getTheoryProof(theory_id)->printTheoryLemmaProof(clause_expr, os, paren); - Debug("pf::tp") << "Get theory lemma from " << theory_id << "... DONE!" << std::endl; - // os << " (clausify_false trust)"; - os << clause_paren.str(); - os << "( \\ " << pm->getLemmaClauseName(id) <<"\n"; - paren << "))"; + LemmaProofRecipe recipe = pm->getCnfProof()->getProofRecipe(clause_expr_nodes); + + if (recipe.simpleLemma()) { + // In a simple lemma, there will be no propositional resolution in the end + + Debug("pf::tp") << "Simple lemma" << std::endl; + // Printing the clause as it appears in resolution proof + os << "(satlem _ _ "; + std::ostringstream clause_paren; + pm->getCnfProof()->printClause(*clause, os, clause_paren); + + // Find and handle missing assertions, due to rewrites + std::set<Node> missingAssertions = recipe.getMissingAssertionsForStep(0); + if (!missingAssertions.empty()) { + Debug("pf::tp") << "Have missing assertions for this simple lemma!" << std::endl; + } + + std::set<Node>::const_iterator missingAssertion; + for (missingAssertion = missingAssertions.begin(); + missingAssertion != missingAssertions.end(); + ++missingAssertion) { + + Debug("pf::tp") << "Working on missing assertion: " << *missingAssertion << std::endl; + Assert(recipe.wasRewritten(missingAssertion->negate())); + Node explanation = recipe.getExplanation(missingAssertion->negate()).negate(); + Debug("pf::tp") << "Found explanation: " << explanation << std::endl; + + // We have a missing assertion. + // rewriteIt->first is the assertion after the rewrite (the explanation), + // rewriteIt->second is the original assertion that needs to be fed into the theory. + + bool found = false; + unsigned k; + for (k = 0; k < clause_expr.size(); ++k) { + if (clause_expr[k] == explanation.toExpr()) { + found = true; + break; + } + } + + AlwaysAssert(found); + Debug("pf::tp") << "Replacing theory assertion " + << clause_expr[k] + << " with " + << *missingAssertion + << std::endl; + + clause_expr[k] = missingAssertion->toExpr(); + + std::ostringstream rewritten; + rewritten << "(or_elim_1 _ _ "; + rewritten << "(not_not_intro _ "; + rewritten << pm->getLitName(explanation); + rewritten << ") (iff_elim_1 _ _ "; + rewritten << d_assertionToRewrite[missingAssertion->negate()]; + rewritten << "))"; + + Debug("pf::tp") << "Setting a rewrite filter for this proof: " << std::endl + << pm->getLitName(*missingAssertion) << " --> " << rewritten.str() + << std::endl << std::endl; + + pm->addRewriteFilter(pm->getLitName(*missingAssertion), rewritten.str()); + } + + // Query the appropriate theory for a proof of this clause + theory::TheoryId theory_id = getTheoryForLemma(clause); + Debug("pf::tp") << "Get theory lemma from " << theory_id << "..." << std::endl; + getTheoryProof(theory_id)->printTheoryLemmaProof(clause_expr, os, paren, map); + + // Turn rewrite filter OFF + pm->clearRewriteFilters(); + + Debug("pf::tp") << "Get theory lemma from " << theory_id << "... DONE!" << std::endl; + os << clause_paren.str(); + os << "( \\ " << pm->getLemmaClauseName(id) <<"\n"; + paren << "))"; + } else { // This is a composite lemma + + unsigned numberOfSteps = recipe.getNumSteps(); + prop::SatClause currentClause = *clause; + std::vector<Expr> currentClauseExpr = clause_expr; + + for (unsigned i = 0; i < numberOfSteps; ++i) { + const LemmaProofRecipe::ProofStep* currentStep = recipe.getStep(i); + + currentClause = *clause; + currentClauseExpr = clause_expr; + + for (unsigned j = 0; j < i; ++j) { + // Literals already used in previous steps need to be negated + Node previousLiteralNode = recipe.getStep(j)->getLiteral(); + Node previousLiteralNodeNegated = previousLiteralNode.negate(); + prop::SatLiteral previousLiteralNegated = + ProofManager::currentPM()->getCnfProof()->getLiteral(previousLiteralNodeNegated); + currentClause.push_back(previousLiteralNegated); + currentClauseExpr.push_back(previousLiteralNodeNegated.toExpr()); + } + + // If the current literal is NULL, can ignore (final step) + // Otherwise, the current literal does NOT need to be negated + Node currentLiteralNode = currentStep->getLiteral(); + if (currentLiteralNode != Node()) { + prop::SatLiteral currentLiteral = + ProofManager::currentPM()->getCnfProof()->getLiteral(currentLiteralNode); + + currentClause.push_back(currentLiteral); + currentClauseExpr.push_back(currentLiteralNode.toExpr()); + } + + os << "(satlem _ _ "; + std::ostringstream clause_paren; + + pm->getCnfProof()->printClause(currentClause, os, clause_paren); + + // query appropriate theory for proof of clause + theory::TheoryId theory_id = currentStep->getTheory(); + Debug("pf::tp") << "Get theory lemma from " << theory_id << "..." << std::endl; + + std::set<Node> missingAssertions = recipe.getMissingAssertionsForStep(i); + if (!missingAssertions.empty()) { + Debug("pf::tp") << "Have missing assertions for this step!" << std::endl; + } + + // Turn rewrite filter ON + std::set<Node>::const_iterator missingAssertion; + for (missingAssertion = missingAssertions.begin(); + missingAssertion != missingAssertions.end(); + ++missingAssertion) { + + Debug("pf::tp") << "Working on missing assertion: " << *missingAssertion << std::endl; + + Assert(recipe.wasRewritten(missingAssertion->negate())); + Node explanation = recipe.getExplanation(missingAssertion->negate()).negate(); + + Debug("pf::tp") << "Found explanation: " << explanation << std::endl; + + // We have a missing assertion. + // rewriteIt->first is the assertion after the rewrite (the explanation), + // rewriteIt->second is the original assertion that needs to be fed into the theory. + + bool found = false; + unsigned k; + for (k = 0; k < currentClauseExpr.size(); ++k) { + if (currentClauseExpr[k] == explanation.toExpr()) { + found = true; + break; + } + } + + AlwaysAssert(found); + + Debug("pf::tp") << "Replacing theory assertion " + << currentClauseExpr[k] + << " with " + << *missingAssertion + << std::endl; + + currentClauseExpr[k] = missingAssertion->toExpr(); + + std::ostringstream rewritten; + rewritten << "(or_elim_1 _ _ "; + rewritten << "(not_not_intro _ "; + rewritten << pm->getLitName(explanation); + rewritten << ") (iff_elim_1 _ _ "; + rewritten << d_assertionToRewrite[missingAssertion->negate()]; + rewritten << "))"; + + Debug("pf::tp") << "Setting a rewrite filter for this proof: " << std::endl + << pm->getLitName(*missingAssertion) << " --> " << rewritten.str() + << std::endl << std::endl; + + pm->addRewriteFilter(pm->getLitName(*missingAssertion), rewritten.str()); + } + + getTheoryProof(theory_id)->printTheoryLemmaProof(currentClauseExpr, os, paren, map); + + // Turn rewrite filter OFF + pm->clearRewriteFilters(); + + Debug("pf::tp") << "Get theory lemma from " << theory_id << "... DONE!" << std::endl; + os << clause_paren.str(); + os << "( \\ " << pm->getLemmaClauseName(id) << "s" << i <<"\n"; + paren << "))"; + } + + Assert(numberOfSteps >= 2); + + os << "(satlem_simplify _ _ _ "; + for (unsigned i = 0; i < numberOfSteps - 1; ++i) { + // Resolve step i with step i + 1 + if (recipe.getStep(i)->getLiteral().getKind() == kind::NOT) { + os << "(Q _ _ "; + } else { + os << "(R _ _ "; + } + + os << pm->getLemmaClauseName(id) << "s" << i; + os << " "; + } + + os << pm->getLemmaClauseName(id) << "s" << numberOfSteps - 1 << " "; + + prop::SatLiteral v; + for (int i = numberOfSteps - 2; i >= 0; --i) { + v = ProofManager::currentPM()->getCnfProof()->getLiteral(recipe.getStep(i)->getLiteral()); + os << ProofManager::getVarName(v.getSatVariable(), "") << ") "; + } + + os << "( \\ " << pm->getLemmaClauseName(id) << "\n"; + paren << "))"; + } } } -void LFSCTheoryProofEngine::printBoundTerm(Expr term, std::ostream& os, const LetMap& map) { - // Debug("pf::tp") << "LFSCTheoryProofEngine::printBoundTerm( " << term << " ) " << std::endl; +void LFSCTheoryProofEngine::printBoundTerm(Expr term, std::ostream& os, const ProofLetMap& map) { + Debug("pf::tp") << "LFSCTheoryProofEngine::printBoundTerm( " << term << " ) " << std::endl; - LetMap::const_iterator it = map.find(term); + ProofLetMap::const_iterator it = map.find(term); if (it != map.end()) { unsigned id = it->second.id; unsigned count = it->second.count; + if (count > LET_COUNT) { - os <<"let"<<id; + os << "let" << id; return; } } @@ -496,7 +813,7 @@ void LFSCTheoryProofEngine::printBoundTerm(Expr term, std::ostream& os, const Le printTheoryTerm(term, os, map); } -void LFSCTheoryProofEngine::printCoreTerm(Expr term, std::ostream& os, const LetMap& map) { +void LFSCTheoryProofEngine::printCoreTerm(Expr term, std::ostream& os, const ProofLetMap& map) { if (term.isVariable()) { os << ProofManager::sanitize(term); return; @@ -596,20 +913,25 @@ void LFSCTheoryProofEngine::printCoreTerm(Expr term, std::ostream& os, const Let } -void TheoryProof::printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren) { - //default method for replaying proofs: assert (negated) literals back to a fresh copy of the theory - Assert( d_theory!=NULL ); +void TheoryProof::printTheoryLemmaProof(std::vector<Expr>& lemma, + std::ostream& os, + std::ostream& paren, + const ProofLetMap& map) { + // Default method for replaying proofs: assert (negated) literals back to a fresh copy of the theory + Assert(d_theory!=NULL); + context::UserContext fakeContext; ProofOutputChannel oc; theory::Valuation v(NULL); //make new copy of theory theory::Theory* th; - Trace("theory-proof-debug") << ";; Print theory lemma proof, theory id = " << d_theory->getId() << std::endl; - if(d_theory->getId()==theory::THEORY_UF) { + Trace("pf::tp") << ";; Print theory lemma proof, theory id = " << d_theory->getId() << std::endl; + + if (d_theory->getId()==theory::THEORY_UF) { th = new theory::uf::TheoryUF(&fakeContext, &fakeContext, oc, v, ProofManager::currentPM()->getLogicInfo(), "replay::"); - } else if(d_theory->getId()==theory::THEORY_ARRAY) { + } else if (d_theory->getId()==theory::THEORY_ARRAY) { th = new theory::arrays::TheoryArrays(&fakeContext, &fakeContext, oc, v, ProofManager::currentPM()->getLogicInfo(), "replay::"); @@ -626,11 +948,15 @@ void TheoryProof::printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& Debug("pf::tp") << "TheoryProof::printTheoryLemmaProof - th->ProduceProofs() DONE" << std::endl; MyPreRegisterVisitor preRegVisitor(th); - for( unsigned i=0; i<lemma.size(); i++ ){ - Node lit = Node::fromExpr( lemma[i] ).negate(); - Trace("pf::tp") << "; preregistering and asserting " << lit << std::endl; - NodeVisitor<MyPreRegisterVisitor>::run(preRegVisitor, lit); - th->assertFact(lit, false); + for (unsigned i=0; i<lemma.size(); i++) { + Node strippedLit = (lemma[i].getKind() == kind::NOT) ? lemma[i][0] : lemma[i]; + if (strippedLit.getKind() == kind::EQUAL || + d_theory->getId() == theory::Theory::theoryOf(strippedLit)) { + Node lit = Node::fromExpr( lemma[i] ).negate(); + Trace("pf::tp") << "; preregistering and asserting " << lit << std::endl; + NodeVisitor<MyPreRegisterVisitor>::run(preRegVisitor, lit); + th->assertFact(lit, false); + } } Debug("pf::tp") << "TheoryProof::printTheoryLemmaProof - calling th->check()" << std::endl; @@ -679,7 +1005,7 @@ void TheoryProof::printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& th->check(theory::Theory::EFFORT_FULL); } Debug("pf::tp") << "Calling oc.d_proof->toStream(os)" << std::endl; - oc.d_proof->toStream(os); + oc.d_proof->toStream(os, map); Debug("pf::tp") << "Calling oc.d_proof->toStream(os) -- DONE!" << std::endl; Debug("pf::tp") << "About to delete the theory solver used for proving the lemma... " << std::endl; @@ -711,10 +1037,13 @@ void BooleanProof::registerTerm(Expr term) { } } -void LFSCBooleanProof::printOwnedTerm(Expr term, std::ostream& os, const LetMap& map) { +void LFSCBooleanProof::printOwnedTerm(Expr term, std::ostream& os, const ProofLetMap& map) { Assert (term.getType().isBoolean()); if (term.isVariable()) { - os << "(p_app " << ProofManager::sanitize(term) <<")"; + if (d_treatBoolsAsFormulas) + os << "(p_app " << ProofManager::sanitize(term) <<")"; + else + os << ProofManager::sanitize(term); return; } @@ -722,6 +1051,32 @@ void LFSCBooleanProof::printOwnedTerm(Expr term, std::ostream& os, const LetMap& switch(k) { case kind::OR: case kind::AND: + if (options::lfscLetification() && term.getNumChildren() > 2) { + // If letification is on, the entire term is probably a let expression. + // However, we need to transform it from (and a b c) into (and a (and b c)) form first. + Node currentExpression = term[term.getNumChildren() - 1]; + for (int i = term.getNumChildren() - 2; i >= 0; --i) { + NodeBuilder<> builder(k); + builder << term[i]; + builder << currentExpression.toExpr(); + currentExpression = builder; + } + + // The let map should already have the current expression. + ProofLetMap::const_iterator it = map.find(term); + if (it != map.end()) { + unsigned id = it->second.id; + unsigned count = it->second.count; + + if (count > LET_COUNT) { + os << "let" << id; + break; + } + } + } + + // If letification is off or there were 2 children, same treatment as the other cases. + // (No break is intentional). case kind::XOR: case kind::IFF: case kind::IMPLIES: @@ -753,7 +1108,10 @@ void LFSCBooleanProof::printOwnedTerm(Expr term, std::ostream& os, const LetMap& return; case kind::CONST_BOOLEAN: - os << (term.getConst<bool>() ? "true" : "false"); + if (d_treatBoolsAsFormulas) + os << (term.getConst<bool>() ? "true" : "false"); + else + os << (term.getConst<bool>() ? "t_true" : "t_false"); return; default: @@ -786,10 +1144,36 @@ void LFSCBooleanProof::printDeferredDeclarations(std::ostream& os, std::ostream& // Nothing to do here at this point. } +void LFSCBooleanProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren) { + // Nothing to do here at this point. +} + void LFSCBooleanProof::printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren) { Unreachable("No boolean lemmas yet!"); } +void TheoryProof::printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2) { + // By default, we just print a trust statement. Specific theories can implement + // better proofs. + ProofLetMap emptyMap; + + os << "(trust_f (not (= _ "; + d_proofEngine->printBoundTerm(c1, os, emptyMap); + os << " "; + d_proofEngine->printBoundTerm(c2, os, emptyMap); + os << ")))"; +} + +void TheoryProof::printRewriteProof(std::ostream& os, const Node &n1, const Node &n2) { + // This is the default for a rewrite proof: just a trust statement. + ProofLetMap emptyMap; + os << "(trust_f (iff "; + d_proofEngine->printBoundTerm(n1.toExpr(), os, emptyMap); + os << " "; + d_proofEngine->printBoundTerm(n2.toExpr(), os, emptyMap); + os << "))"; +} + } /* namespace CVC4 */ diff --git a/src/proof/theory_proof.h b/src/proof/theory_proof.h index 54c86f3f3..5907f9bd5 100644 --- a/src/proof/theory_proof.h +++ b/src/proof/theory_proof.h @@ -27,6 +27,7 @@ #include "proof/clause_id.h" #include "prop/sat_solver_types.h" #include "util/proof.h" +#include "proof/proof_utils.h" namespace CVC4 { @@ -34,66 +35,21 @@ namespace theory { class Theory; } /* namespace CVC4::theory */ -struct LetCount { - static unsigned counter; - static void resetCounter() { counter = 0; } - static unsigned newId() { return ++counter; } - - unsigned count; - unsigned id; - LetCount() - : count(0) - , id(-1) - {} - - void increment() { ++count; } - LetCount(unsigned i) - : count(1) - , id(i) - {} - LetCount(const LetCount& other) - : count(other.count) - , id (other.id) - {} - bool operator==(const LetCount &other) const { - return other.id == id && other.count == count; - } - LetCount& operator=(const LetCount &rhs) { - if (&rhs == this) return *this; - id = rhs.id; - count = rhs.count; - return *this; - } -}; - -struct LetOrderElement { - Expr expr; - unsigned id; - LetOrderElement(Expr e, unsigned i) - : expr(e) - , id(i) - {} - - LetOrderElement() - : expr() - , id(-1) - {} -}; - typedef __gnu_cxx::hash_map < ClauseId, prop::SatClause* > IdToSatClause; -typedef __gnu_cxx::hash_map<Expr, LetCount, ExprHashFunction> LetMap; -typedef std::vector<LetOrderElement> Bindings; - class TheoryProof; typedef __gnu_cxx::hash_set<Expr, ExprHashFunction > ExprSet; typedef std::map<theory::TheoryId, TheoryProof* > TheoryProofTable; +typedef std::set<theory::TheoryId> TheoryIdSet; +typedef std::map<Expr, TheoryIdSet> ExprToTheoryIds; + class TheoryProofEngine { protected: ExprSet d_registrationCache; TheoryProofTable d_theoryProofTable; + ExprToTheoryIds d_exprToTheoryIds; /** * Returns whether the theory is currently supported in proof @@ -113,7 +69,7 @@ public: */ virtual void printLetTerm(Expr term, std::ostream& os) = 0; virtual void printBoundTerm(Expr term, std::ostream& os, - const LetMap& map) = 0; + const ProofLetMap& map) = 0; /** * Print the proof representation of the given sort. @@ -148,6 +104,14 @@ public: virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren) = 0; /** + * Print aliasing declarations. + * + * @param os + * @param paren closing parenthesis + */ + virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren) = 0; + + /** * Print proofs of all the theory lemmas (must prove * actual clause used in resolution proof). * @@ -155,7 +119,7 @@ public: * @param paren */ virtual void printTheoryLemmas(const IdToSatClause& lemmas, std::ostream& os, - std::ostream& paren) = 0; + std::ostream& paren, ProofLetMap& map) = 0; /** * Register theory atom (ensures all terms and atoms are declared). @@ -166,35 +130,67 @@ public: /** * Ensures that a theory proof class for the given theory is created. + * This method can be invoked regardless of whether the "proof" option + * has been set. * * @param theory */ void registerTheory(theory::Theory* theory); + /** + * Additional configuration of the theory proof class for the given theory. + * This method should only be invoked when the "proof" option has been set. + * + * @param theory + */ + void finishRegisterTheory(theory::Theory* theory); - theory::TheoryId getTheoryForLemma(ClauseId id); + theory::TheoryId getTheoryForLemma(const prop::SatClause* clause); TheoryProof* getTheoryProof(theory::TheoryId id); + + void markTermForFutureRegistration(Expr term, theory::TheoryId id); + + void printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2); + + virtual void treatBoolsAsFormulas(bool value) {}; + + virtual void printTheoryTerm(Expr term, std::ostream& os, const ProofLetMap& map) = 0; }; class LFSCTheoryProofEngine : public TheoryProofEngine { - LetMap d_letMap; - void printTheoryTerm(Expr term, std::ostream& os, const LetMap& map); - void bind(Expr term, LetMap& map, Bindings& let_order); + void bind(Expr term, ProofLetMap& map, Bindings& let_order); public: LFSCTheoryProofEngine() : TheoryProofEngine() {} + void printTheoryTerm(Expr term, std::ostream& os, const ProofLetMap& map); + void registerTermsFromAssertions(); void printSortDeclarations(std::ostream& os, std::ostream& paren); void printTermDeclarations(std::ostream& os, std::ostream& paren); - virtual void printCoreTerm(Expr term, std::ostream& os, const LetMap& map); + virtual void printCoreTerm(Expr term, std::ostream& os, const ProofLetMap& map); virtual void printLetTerm(Expr term, std::ostream& os); - virtual void printBoundTerm(Expr term, std::ostream& os, const LetMap& map); + virtual void printBoundTerm(Expr term, std::ostream& os, const ProofLetMap& map); virtual void printAssertions(std::ostream& os, std::ostream& paren); + virtual void printLemmaRewrites(NodePairSet& rewrites, std::ostream& os, std::ostream& paren); virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren); + virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren); virtual void printTheoryLemmas(const IdToSatClause& lemmas, std::ostream& os, - std::ostream& paren); + std::ostream& paren, + ProofLetMap& map); virtual void printSort(Type type, std::ostream& os); + + void performExtraRegistrations(); + + void treatBoolsAsFormulas(bool value); + void finalizeBvConflicts(const IdToSatClause& lemmas, std::ostream& os); + +private: + static void dumpTheoryLemmas(const IdToSatClause& lemmas); + + // TODO: this function should be moved into the BV prover. + + std::map<Node, std::string> d_assertionToRewrite; }; class TheoryProof { @@ -214,7 +210,7 @@ public: * @param term expresion representing term * @param os output stream */ - void printTerm(Expr term, std::ostream& os, const LetMap& map) { + void printTerm(Expr term, std::ostream& os, const ProofLetMap& map) { d_proofEngine->printBoundTerm(term, os, map); } /** @@ -223,7 +219,7 @@ public: * @param term expresion representing term * @param os output stream */ - virtual void printOwnedTerm(Expr term, std::ostream& os, const LetMap& map) = 0; + virtual void printOwnedTerm(Expr term, std::ostream& os, const ProofLetMap& map) = 0; /** * Print the proof representation of the given type that belongs to some theory. * @@ -246,7 +242,10 @@ public: * * @param os output stream */ - virtual void printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren); + virtual void printTheoryLemmaProof(std::vector<Expr>& lemma, + std::ostream& os, + std::ostream& paren, + const ProofLetMap& map); /** * Print the sorts declarations for this theory. * @@ -270,11 +269,32 @@ public: */ virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren) = 0; /** + * Print any aliasing declarations. + * + * @param os + * @param paren + */ + virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren) = 0; + /** * Register a term of this theory that appears in the proof. * * @param term */ virtual void registerTerm(Expr term) = 0; + /** + * Print a proof for the disequality of two constants that belong to this theory. + * + * @param term + */ + virtual void printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2); + /** + * Print a proof for the equivalence of n1 and n2. + * + * @param term + */ + virtual void printRewriteProof(std::ostream& os, const Node &n1, const Node &n2); + + virtual void treatBoolsAsFormulas(bool value) {} }; class BooleanProof : public TheoryProof { @@ -285,26 +305,35 @@ public: virtual void registerTerm(Expr term); - virtual void printOwnedTerm(Expr term, std::ostream& os, const LetMap& map) = 0; + virtual void printOwnedTerm(Expr term, std::ostream& os, const ProofLetMap& map) = 0; virtual void printOwnedSort(Type type, std::ostream& os) = 0; virtual void printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren) = 0; virtual void printSortDeclarations(std::ostream& os, std::ostream& paren) = 0; virtual void printTermDeclarations(std::ostream& os, std::ostream& paren) = 0; virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren) = 0; + virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren) = 0; }; class LFSCBooleanProof : public BooleanProof { public: LFSCBooleanProof(TheoryProofEngine* proofEngine) - : BooleanProof(proofEngine) + : BooleanProof(proofEngine), d_treatBoolsAsFormulas(true) {} - virtual void printOwnedTerm(Expr term, std::ostream& os, const LetMap& map); + virtual void printOwnedTerm(Expr term, std::ostream& os, const ProofLetMap& map); virtual void printOwnedSort(Type type, std::ostream& os); virtual void printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren); virtual void printSortDeclarations(std::ostream& os, std::ostream& paren); virtual void printTermDeclarations(std::ostream& os, std::ostream& paren); virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren); + virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren); + + void treatBoolsAsFormulas(bool value) { + d_treatBoolsAsFormulas = value; + } + +private: + bool d_treatBoolsAsFormulas; }; } /* CVC4 namespace */ diff --git a/src/proof/uf_proof.cpp b/src/proof/uf_proof.cpp index 32ca122b0..139ce5ed2 100644 --- a/src/proof/uf_proof.cpp +++ b/src/proof/uf_proof.cpp @@ -67,13 +67,17 @@ inline static bool match(TNode n1, TNode n2) { void ProofUF::toStream(std::ostream& out) { + ProofLetMap map; + toStream(out, map); +} + +void ProofUF::toStream(std::ostream& out, const ProofLetMap& map) { Trace("theory-proof-debug") << "; Print UF proof..." << std::endl; //AJR : carry this further? - LetMap map; toStreamLFSC(out, ProofManager::getUfProof(), d_proof, map); } -void ProofUF::toStreamLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, const LetMap& map) { +void ProofUF::toStreamLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, const ProofLetMap& map) { Debug("pf::uf") << "ProofUF::toStreamLFSC starting" << std::endl; Debug("lfsc-uf") << "Printing uf proof in LFSC : " << std::endl; pf->debug_print("lfsc-uf"); @@ -81,12 +85,21 @@ void ProofUF::toStreamLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqPr toStreamRecLFSC( out, tp, pf, 0, map ); } -Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, unsigned tb, const LetMap& map) { +Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, unsigned tb, const ProofLetMap& map) { Debug("pf::uf") << std::endl << std::endl << "toStreamRecLFSC called. tb = " << tb << " . proof:" << std::endl; pf->debug_print("pf::uf"); Debug("pf::uf") << std::endl; - if(tb == 0) { + if (tb == 0) { + // Special case: false was an input, so the proof is just "false". + if (pf->d_id == theory::eq::MERGED_THROUGH_EQUALITY && + pf->d_node == NodeManager::currentNM()->mkConst(false)) { + out << "(clausify_false "; + out << ProofManager::getLitName(NodeManager::currentNM()->mkConst(false).notNode()); + out << ")" << std::endl; + return Node(); + } + Assert(pf->d_id == theory::eq::MERGED_THROUGH_TRANS); Assert(!pf->d_node.isNull()); Assert(pf->d_children.size() >= 2); @@ -190,42 +203,71 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E ++i; } } - Assert(neg >= 0); + + bool disequalityFound = (neg >= 0); + if (!disequalityFound) { + Debug("pf::uf") << "A disequality was NOT found. UNSAT due to merged constants" << std::endl; + Debug("pf::uf") << "Proof for: " << pf->d_node << std::endl; + Assert(pf->d_node.getKind() == kind::EQUAL); + Assert(pf->d_node.getNumChildren() == 2); + Assert (pf->d_node[0].isConst() && pf->d_node[1].isConst()); + } Node n1; std::stringstream ss; - //Assert(subTrans.d_children.size() == pf->d_children.size() - 1); Debug("pf::uf") << "\nsubtrans has " << subTrans.d_children.size() << " children\n"; - if(pf->d_children.size() > 2) { + + if(!disequalityFound || subTrans.d_children.size() >= 2) { n1 = toStreamRecLFSC(ss, tp, &subTrans, 1, map); } else { n1 = toStreamRecLFSC(ss, tp, subTrans.d_children[0], 1, map); Debug("pf::uf") << "\nsubTrans unique child " << subTrans.d_children[0]->d_id << " was proven\ngot: " << n1 << std::endl; } - Node n2 = pf->d_children[neg]->d_node; - Assert(n2.getKind() == kind::NOT); - out << "(clausify_false (contra _ "; Debug("pf::uf") << "\nhave proven: " << n1 << std::endl; - Debug("pf::uf") << "n2 is " << n2[0] << std::endl; - if (n2[0].getNumChildren() > 0) { Debug("pf::uf") << "\nn2[0]: " << n2[0][0] << std::endl; } - if (n1.getNumChildren() > 1) { Debug("pf::uf") << "n1[1]: " << n1[1] << std::endl; } + out << "(clausify_false (contra _ "; - if(n2[0].getKind() == kind::APPLY_UF) { - out << "(trans _ _ _ _ "; - out << "(symm _ _ _ "; - out << ss.str(); - out << ") (pred_eq_f _ " << ProofManager::getLitName(n2[0]) << ")) t_t_neq_f))" << std::endl; - } else { - Assert((n1[0] == n2[0][0] && n1[1] == n2[0][1]) || (n1[1] == n2[0][0] && n1[0] == n2[0][1])); - if(n1[1] == n2[0][0]) { - out << "(symm _ _ _ " << ss.str() << ")"; + if (disequalityFound) { + Node n2 = pf->d_children[neg]->d_node; + Assert(n2.getKind() == kind::NOT); + Debug("pf::uf") << "n2 is " << n2[0] << std::endl; + + if (n2[0].getNumChildren() > 0) { Debug("pf::uf") << "\nn2[0]: " << n2[0][0] << std::endl; } + if (n1.getNumChildren() > 1) { Debug("pf::uf") << "n1[1]: " << n1[1] << std::endl; } + + if(n2[0].getKind() == kind::APPLY_UF) { + out << "(trans _ _ _ _ "; + + if (n1[0] == n2[0]) { + out << "(symm _ _ _ "; + out << ss.str(); + out << ") "; + } else { + Assert(n1[1] == n2[0]); + out << ss.str(); + } + out << "(pred_eq_f _ " << ProofManager::getLitName(n2[0]) << ")) t_t_neq_f))" << std::endl; } else { - out << ss.str(); + Assert((n1[0] == n2[0][0] && n1[1] == n2[0][1]) || (n1[1] == n2[0][0] && n1[0] == n2[0][1])); + if(n1[1] == n2[0][0]) { + out << "(symm _ _ _ " << ss.str() << ")"; + } else { + out << ss.str(); + } + out << " " << ProofManager::getLitName(n2[0]) << "))" << std::endl; } - out << " " << ProofManager::getLitName(n2[0]) << "))" << std::endl; + } else { + Node n2 = pf->d_node; + Assert(n2.getKind() == kind::EQUAL); + Assert((n1[0] == n2[0] && n1[1] == n2[1]) || (n1[1] == n2[0] && n1[0] == n2[1])); + + out << ss.str(); + out << " "; + ProofManager::getTheoryProofEngine()->printConstantDisequalityProof(out, n1[0].toExpr(), n1[1].toExpr()); + out << "))" << std::endl; } + return Node(); } @@ -584,10 +626,10 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E } else if(n1.getKind() == kind::EQUAL || n1.getKind() == kind::IFF) { // n1 is an equality/iff, but n2 is a predicate if(n1[0] == n2) { - n1 = n1[1]; + n1 = n1[1].iffNode(NodeManager::currentNM()->mkConst(true)); ss << "(symm _ _ _ " << ss1.str() << ") (pred_eq_t _ " << ss2.str() << ")"; } else if(n1[1] == n2) { - n1 = n1[0]; + n1 = n1[0].iffNode(NodeManager::currentNM()->mkConst(true)); ss << ss1.str() << " (pred_eq_t _ " << ss2.str() << ")"; } else { Unreachable(); @@ -595,16 +637,16 @@ Node ProofUF::toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::E } else if(n2.getKind() == kind::EQUAL || n2.getKind() == kind::IFF) { // n2 is an equality/iff, but n1 is a predicate if(n2[0] == n1) { - n1 = n2[1]; + n1 = n2[1].iffNode(NodeManager::currentNM()->mkConst(true)); ss << "(symm _ _ _ " << ss2.str() << ") (pred_eq_t _ " << ss1.str() << ")"; } else if(n2[1] == n1) { - n1 = n2[0]; + n1 = n2[0].iffNode(NodeManager::currentNM()->mkConst(true)); ss << ss2.str() << " (pred_eq_t _ " << ss1.str() << ")"; } else { Unreachable(); } } else { - // Both n1 and n2 are prediacates. Don't know what to do... + // Both n1 and n2 are predicates. Don't know what to do... Unreachable(); } @@ -655,7 +697,7 @@ void UFProof::registerTerm(Expr term) { } } -void LFSCUFProof::printOwnedTerm(Expr term, std::ostream& os, const LetMap& map) { +void LFSCUFProof::printOwnedTerm(Expr term, std::ostream& os, const ProofLetMap& map) { Debug("pf::uf") << std::endl << "(pf::uf) LFSCUfProof::printOwnedTerm: term = " << term << std::endl; Assert (theory::Theory::theoryOf(term) == theory::THEORY_UF); @@ -667,6 +709,7 @@ void LFSCUFProof::printOwnedTerm(Expr term, std::ostream& os, const LetMap& map) } Assert (term.getKind() == kind::APPLY_UF); + d_proofEngine->treatBoolsAsFormulas(false); if(term.getType().isBoolean()) { os << "(p_app "; @@ -683,6 +726,7 @@ void LFSCUFProof::printOwnedTerm(Expr term, std::ostream& os, const LetMap& map) if(term.getType().isBoolean()) { os << ")"; } + d_proofEngine->treatBoolsAsFormulas(true); } void LFSCUFProof::printOwnedSort(Type type, std::ostream& os) { @@ -692,14 +736,14 @@ void LFSCUFProof::printOwnedSort(Type type, std::ostream& os) { os << type <<" "; } -void LFSCUFProof::printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren) { +void LFSCUFProof::printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren, const ProofLetMap& map) { os << " ;; UF Theory Lemma \n;;"; for (unsigned i = 0; i < lemma.size(); ++i) { os << lemma[i] <<" "; } os <<"\n"; //os << " (clausify_false trust)"; - UFProof::printTheoryLemmaProof( lemma, os, paren ); + UFProof::printTheoryLemmaProof(lemma, os, paren, map); } void LFSCUFProof::printSortDeclarations(std::ostream& os, std::ostream& paren) { @@ -714,6 +758,8 @@ void LFSCUFProof::printSortDeclarations(std::ostream& os, std::ostream& paren) { void LFSCUFProof::printTermDeclarations(std::ostream& os, std::ostream& paren) { // declaring the terms + Debug("pf::uf") << "LFSCUFProof::printTermDeclarations called" << std::endl; + for (ExprSet::const_iterator it = d_declarations.begin(); it != d_declarations.end(); ++it) { Expr term = *it; @@ -729,7 +775,8 @@ void LFSCUFProof::printTermDeclarations(std::ostream& os, std::ostream& paren) { os << "(arrow"; for (unsigned i = 0; i < args.size(); i++) { Type arg_type = args[i]; - os << " " << arg_type; + os << " "; + d_proofEngine->printSort(arg_type, os); if (i < args.size() - 2) { os << " (arrow"; fparen << ")"; @@ -742,10 +789,16 @@ void LFSCUFProof::printTermDeclarations(std::ostream& os, std::ostream& paren) { } paren << ")"; } + + Debug("pf::uf") << "LFSCUFProof::printTermDeclarations done" << std::endl; } void LFSCUFProof::printDeferredDeclarations(std::ostream& os, std::ostream& paren) { // Nothing to do here at this point. } +void LFSCUFProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren) { + // Nothing to do here at this point. +} + } /* namespace CVC4 */ diff --git a/src/proof/uf_proof.h b/src/proof/uf_proof.h index e359eaebd..444b602dc 100644 --- a/src/proof/uf_proof.h +++ b/src/proof/uf_proof.h @@ -28,13 +28,14 @@ namespace CVC4 { //proof object outputted by TheoryUF class ProofUF : public Proof { private: - static Node toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, unsigned tb, const LetMap& map); + static Node toStreamRecLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, unsigned tb, const ProofLetMap& map); public: ProofUF( theory::eq::EqProof * pf ) : d_proof( pf ) {} //it is simply an equality engine proof theory::eq::EqProof * d_proof; void toStream(std::ostream& out); - static void toStreamLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, const LetMap& map); + void toStream(std::ostream& out, const ProofLetMap& map); + static void toStreamLFSC(std::ostream& out, TheoryProof * tp, theory::eq::EqProof * pf, const ProofLetMap& map); }; @@ -63,12 +64,13 @@ public: LFSCUFProof(theory::uf::TheoryUF* uf, TheoryProofEngine* proofEngine) : UFProof(uf, proofEngine) {} - virtual void printOwnedTerm(Expr term, std::ostream& os, const LetMap& map); + virtual void printOwnedTerm(Expr term, std::ostream& os, const ProofLetMap& map); virtual void printOwnedSort(Type type, std::ostream& os); - virtual void printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren); + virtual void printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren, const ProofLetMap& map); virtual void printSortDeclarations(std::ostream& os, std::ostream& paren); virtual void printTermDeclarations(std::ostream& os, std::ostream& paren); virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren); + virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren); }; diff --git a/src/prop/bvminisat/bvminisat.h b/src/prop/bvminisat/bvminisat.h index 20724efd2..56a7c61e2 100644 --- a/src/prop/bvminisat/bvminisat.h +++ b/src/prop/bvminisat/bvminisat.h @@ -78,6 +78,10 @@ public: ClauseId addClause(SatClause& clause, bool removable); + ClauseId addXorClause(SatClause& clause, bool rhs, bool removable) { + Unreachable("Minisat does not support native XOR reasoning"); + } + SatValue propagate(); SatVariable newVar(bool isTheoryAtom = false, bool preRegister = false, bool canErase = true); diff --git a/src/prop/cnf_stream.cpp b/src/prop/cnf_stream.cpp index aa1fc9587..eda736b8a 100644 --- a/src/prop/cnf_stream.cpp +++ b/src/prop/cnf_stream.cpp @@ -120,7 +120,7 @@ bool CnfStream::hasLiteral(TNode n) const { return find != d_nodeToLiteralMap.end(); } -void TseitinCnfStream::ensureLiteral(TNode n) { +void TseitinCnfStream::ensureLiteral(TNode n, bool noPreregistration) { // These are not removable and have no proof ID d_removable = false; @@ -163,7 +163,7 @@ void TseitinCnfStream::ensureLiteral(TNode n) { d_literalToNodeMap.insert_safe(~lit, n.notNode()); } else { // We have a theory atom or variable. - lit = convertAtom(n); + lit = convertAtom(n, noPreregistration); } Assert(hasLiteral(n) && getNode(lit) == n); @@ -232,7 +232,7 @@ void CnfStream::setProof(CnfProof* proof) { d_cnfProof = proof; } -SatLiteral CnfStream::convertAtom(TNode node) { +SatLiteral CnfStream::convertAtom(TNode node, bool noPreregistration) { Debug("cnf") << "convertAtom(" << node << ")" << endl; Assert(!hasLiteral(node), "atom already mapped!"); @@ -247,7 +247,7 @@ SatLiteral CnfStream::convertAtom(TNode node) { } else { theoryLiteral = true; canEliminate = false; - preRegister = true; + preRegister = !noPreregistration; } // Make a new literal (variables are not considered theory literals) @@ -258,7 +258,11 @@ SatLiteral CnfStream::convertAtom(TNode node) { SatLiteral CnfStream::getLiteral(TNode node) { Assert(!node.isNull(), "CnfStream: can't getLiteral() of null node"); - Assert(d_nodeToLiteralMap.contains(node), "Literal not in the CNF Cache: %s\n", node.toString().c_str()); + + Assert(d_nodeToLiteralMap.contains(node), + "Literal not in the CNF Cache: %s\n", + node.toString().c_str()); + SatLiteral literal = d_nodeToLiteralMap[node]; Debug("cnf") << "CnfStream::getLiteral(" << node << ") => " << literal << std::endl; return literal; @@ -675,7 +679,7 @@ void TseitinCnfStream::convertAndAssert(TNode node, bool negated, ProofRule proof_id, TNode from, - theory::TheoryId ownerTheory) { + LemmaProofRecipe* proofRecipe) { Debug("cnf") << "convertAndAssert(" << node << ", removable = " << (removable ? "true" : "false") << ", negated = " << (negated ? "true" : "false") << ")" << endl; @@ -685,7 +689,12 @@ void TseitinCnfStream::convertAndAssert(TNode node, Node assertion = negated ? node.notNode() : (Node)node; Node from_assertion = negated? from.notNode() : (Node) from; - d_cnfProof->setExplainerTheory(ownerTheory); + if (proofRecipe) { + Debug("pf::sat") << "TseitinCnfStream::convertAndAssert: setting proof recipe" << std::endl; + proofRecipe->dump("pf::sat"); + d_cnfProof->setProofRecipe(proofRecipe); + } + if (proof_id != RULE_INVALID) { d_cnfProof->pushCurrentAssertion(from.isNull() ? assertion : from_assertion); d_cnfProof->registerAssertion(from.isNull() ? assertion : from_assertion, proof_id); diff --git a/src/prop/cnf_stream.h b/src/prop/cnf_stream.h index cf9d519a7..6cc10d878 100644 --- a/src/prop/cnf_stream.h +++ b/src/prop/cnf_stream.h @@ -36,6 +36,9 @@ #include "smt_util/lemma_channels.h" namespace CVC4 { + +class LemmaProofRecipe; + namespace prop { class PropEngine; @@ -174,7 +177,7 @@ protected: * structure in this expression. Assumed to not be in the * translation cache. */ - SatLiteral convertAtom(TNode node); + SatLiteral convertAtom(TNode node, bool noPreprocessing = false); public: @@ -207,14 +210,10 @@ public: * @param node node to convert and assert * @param removable whether the sat solver can choose to remove the clauses * @param negated whether we are asserting the node negated - * @param ownerTheory indicates the theory that should invoked to prove the formula. + * @param proofRecipe contains the proof recipe for proving this node */ - virtual void convertAndAssert(TNode node, - bool removable, - bool negated, - ProofRule proof_id, - TNode from = TNode::null(), - theory::TheoryId ownerTheory = theory::THEORY_LAST) = 0; + virtual void convertAndAssert(TNode node, bool removable, bool negated, ProofRule proof_id, TNode from = TNode::null(), LemmaProofRecipe* proofRecipe = NULL) = 0; + /** * Get the node that is represented by the given SatLiteral. * @param literal the literal from the sat solver @@ -235,7 +234,7 @@ public: * this is like a "convert-but-don't-assert" version of * convertAndAssert(). */ - virtual void ensureLiteral(TNode n) = 0; + virtual void ensureLiteral(TNode n, bool noPreregistration = false) = 0; /** * Returns the literal that represents the given node in the SAT CNF @@ -284,7 +283,7 @@ public: */ void convertAndAssert(TNode node, bool removable, bool negated, ProofRule rule, TNode from = TNode::null(), - theory::TheoryId ownerTheory = theory::THEORY_LAST); + LemmaProofRecipe* proofRecipe = NULL); /** * Constructs the stream to use the given sat solver. @@ -337,7 +336,7 @@ private: */ SatLiteral toCNF(TNode node, bool negated = false); - void ensureLiteral(TNode n); + void ensureLiteral(TNode n, bool noPreregistration = false); };/* class TseitinCnfStream */ diff --git a/src/prop/cryptominisat.cpp b/src/prop/cryptominisat.cpp new file mode 100644 index 000000000..d8f25a786 --- /dev/null +++ b/src/prop/cryptominisat.cpp @@ -0,0 +1,230 @@ +/********************* */ +/*! \file cryptominisat.cpp + ** \verbatim + ** Original author: lianah + ** Major contributors: + ** Minor contributors (to current version): + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009-2014 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief SAT Solver. + ** + ** Implementation of the cryptominisat for cvc4 (bitvectors). + **/ + +#include "prop/cryptominisat.h" + +#include "proof/clause_id.h" +#include "proof/sat_proof.h" + +using namespace CVC4; +using namespace prop; + +#ifdef CVC4_USE_CRYPTOMINISAT + +CryptoMinisatSolver::CryptoMinisatSolver(StatisticsRegistry* registry, + const std::string& name) +: d_solver(new CMSat::SATSolver()) +, d_numVariables(0) +, d_okay(true) +, d_statistics(registry, name) +{ + d_true = newVar(); + d_false = newVar(); + + std::vector<CMSat::Lit> clause(1); + clause[0] = CMSat::Lit(d_true, false); + d_solver->add_clause(clause); + + clause[0] = CMSat::Lit(d_false, true); + d_solver->add_clause(clause); +} + + +CryptoMinisatSolver::~CryptoMinisatSolver() throw(AssertionException) { + delete d_solver; +} + +ClauseId CryptoMinisatSolver::addXorClause(SatClause& clause, + bool rhs, + bool removable) { + Debug("sat::cryptominisat") << "Add xor clause " << clause <<" = " << rhs << "\n"; + + if (!d_okay) { + Debug("sat::cryptominisat") << "Solver unsat: not adding clause.\n"; + return ClauseIdError; + } + + ++(d_statistics.d_xorClausesAdded); + + // ensure all sat literals have positive polarity by pushing + // the negation on the result + std::vector<CMSat::Var> xor_clause; + for (unsigned i = 0; i < clause.size(); ++i) { + xor_clause.push_back(toInternalLit(clause[i]).var()); + rhs ^= clause[i].isNegated(); + } + bool res = d_solver->add_xor_clause(xor_clause, rhs); + d_okay &= res; + return ClauseIdError; +} + +ClauseId CryptoMinisatSolver::addClause(SatClause& clause, bool removable){ + Debug("sat::cryptominisat") << "Add clause " << clause <<"\n"; + + if (!d_okay) { + Debug("sat::cryptominisat") << "Solver unsat: not adding clause.\n"; + return ClauseIdError; + } + + ++(d_statistics.d_clausesAdded); + + std::vector<CMSat::Lit> internal_clause; + toInternalClause(clause, internal_clause); + bool res = d_solver->add_clause(internal_clause); + d_okay &= res; + return ClauseIdError; +} + +bool CryptoMinisatSolver::ok() const { + return d_okay; +} + + +SatVariable CryptoMinisatSolver::newVar(bool isTheoryAtom, bool preRegister, bool canErase){ + d_solver->new_var(); + ++d_numVariables; + Assert (d_numVariables == d_solver->nVars()); + return d_numVariables - 1; +} + +SatVariable CryptoMinisatSolver::trueVar() { + return d_true; +} + +SatVariable CryptoMinisatSolver::falseVar() { + return d_false; +} + +void CryptoMinisatSolver::markUnremovable(SatLiteral lit) { + // cryptominisat supports dynamically adding back variables (?) + // so this is a no-op + return; +} + +void CryptoMinisatSolver::interrupt(){ + d_solver->interrupt_asap(); +} + +SatValue CryptoMinisatSolver::solve(){ + TimerStat::CodeTimer codeTimer(d_statistics.d_solveTime); + ++d_statistics.d_statCallsToSolve; + return toSatLiteralValue(d_solver->solve()); +} + +SatValue CryptoMinisatSolver::solve(long unsigned int& resource) { + // CMSat::SalverConf conf = d_solver->getConf(); + Unreachable("Not sure how to set different limits for calls to solve in Cryptominisat"); + return solve(); +} + +SatValue CryptoMinisatSolver::value(SatLiteral l){ + const std::vector<CMSat::lbool> model = d_solver->get_model(); + CMSat::Var var = l.getSatVariable(); + Assert (var < model.size()); + CMSat::lbool value = model[var]; + return toSatLiteralValue(value); +} + +SatValue CryptoMinisatSolver::modelValue(SatLiteral l){ + return value(l); +} + +unsigned CryptoMinisatSolver::getAssertionLevel() const { + Unreachable("No interface to get assertion level in Cryptominisat"); + return -1; +} + +// converting from internal sat solver representation + +SatVariable CryptoMinisatSolver::toSatVariable(CMSat::Var var) { + if (var == CMSat::var_Undef) { + return undefSatVariable; + } + return SatVariable(var); +} + +CMSat::Lit CryptoMinisatSolver::toInternalLit(SatLiteral lit) { + if (lit == undefSatLiteral) { + return CMSat::lit_Undef; + } + return CMSat::Lit(lit.getSatVariable(), lit.isNegated()); +} + +SatLiteral CryptoMinisatSolver::toSatLiteral(CMSat::Lit lit) { + Assert (lit != CMSat::lit_Error); + if (lit == CMSat::lit_Undef) { + return undefSatLiteral; + } + + return SatLiteral(SatVariable(lit.var()), + lit.sign()); +} + +SatValue CryptoMinisatSolver::toSatLiteralValue(CMSat::lbool res) { + if(res == CMSat::l_True) return SAT_VALUE_TRUE; + if(res == CMSat::l_Undef) return SAT_VALUE_UNKNOWN; + Assert(res == CMSat::l_False); + return SAT_VALUE_FALSE; +} + +void CryptoMinisatSolver::toInternalClause(SatClause& clause, + std::vector<CMSat::Lit>& internal_clause) { + for (unsigned i = 0; i < clause.size(); ++i) { + internal_clause.push_back(toInternalLit(clause[i])); + } + Assert(clause.size() == internal_clause.size()); +} + +void CryptoMinisatSolver::toSatClause(std::vector<CMSat::Lit>& clause, + SatClause& sat_clause) { + for (unsigned i = 0; i < clause.size(); ++i) { + sat_clause.push_back(toSatLiteral(clause[i])); + } + Assert(clause.size() == sat_clause.size()); +} + + +// Satistics for CryptoMinisatSolver + +CryptoMinisatSolver::Statistics::Statistics(StatisticsRegistry* registry, + const std::string& prefix) : + d_registry(registry), + d_statCallsToSolve("theory::bv::"+prefix+"::cryptominisat::calls_to_solve", 0), + d_xorClausesAdded("theory::bv::"+prefix+"::cryptominisat::xor_clauses", 0), + d_clausesAdded("theory::bv::"+prefix+"::cryptominisat::clauses", 0), + d_solveTime("theory::bv::"+prefix+"::cryptominisat::solve_time"), + d_registerStats(!prefix.empty()) +{ + if (!d_registerStats) + return; + + d_registry->registerStat(&d_statCallsToSolve); + d_registry->registerStat(&d_xorClausesAdded); + d_registry->registerStat(&d_clausesAdded); + d_registry->registerStat(&d_solveTime); +} + +CryptoMinisatSolver::Statistics::~Statistics() { + if (!d_registerStats) + return; + d_registry->unregisterStat(&d_statCallsToSolve); + d_registry->unregisterStat(&d_xorClausesAdded); + d_registry->unregisterStat(&d_clausesAdded); + d_registry->unregisterStat(&d_solveTime); +} +#endif diff --git a/src/prop/cryptominisat.h b/src/prop/cryptominisat.h new file mode 100644 index 000000000..54d52af0e --- /dev/null +++ b/src/prop/cryptominisat.h @@ -0,0 +1,138 @@ +/********************* */ +/*! \file cryptominisat.h + ** \verbatim + ** Original author: lianah + ** Major contributors: + ** Minor contributors (to current version): + ** This file is part of the CVC4 prototype. + ** Copyright (c) 2009-2014 The Analysis of Computer Systems Group (ACSys) + ** Courant Institute of Mathematical Sciences + ** New York University + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief SAT Solver. + ** + ** Implementation of the cryptominisat sat solver for cvc4 (bitvectors). + **/ + +#include "cvc4_private.h" + +#pragma once + +#include "prop/sat_solver.h" + +#ifdef CVC4_USE_CRYPTOMINISAT +#include <cryptominisat4/cryptominisat.h> +namespace CVC4 { +namespace prop { + +class CryptoMinisatSolver : public SatSolver { + +private: + CMSat::SATSolver* d_solver; + unsigned d_numVariables; + bool d_okay; + SatVariable d_true; + SatVariable d_false; +public: + CryptoMinisatSolver(StatisticsRegistry* registry, + const std::string& name = ""); + ~CryptoMinisatSolver() throw(AssertionException); + + ClauseId addClause(SatClause& clause, bool removable); + ClauseId addXorClause(SatClause& clause, bool rhs, bool removable); + + bool nativeXor() { return true; } + + SatVariable newVar(bool isTheoryAtom = false, bool preRegister = false, bool canErase = true); + + SatVariable trueVar(); + SatVariable falseVar(); + + void markUnremovable(SatLiteral lit); + + void interrupt(); + + SatValue solve(); + SatValue solve(long unsigned int&); + bool ok() const; + SatValue value(SatLiteral l); + SatValue modelValue(SatLiteral l); + + unsigned getAssertionLevel() const; + + + // helper methods for converting from the internal Minisat representation + + static SatVariable toSatVariable(CMSat::Var var); + static CMSat::Lit toInternalLit(SatLiteral lit); + static SatLiteral toSatLiteral(CMSat::Lit lit); + static SatValue toSatLiteralValue(bool res); + static SatValue toSatLiteralValue(CMSat::lbool res); + + static void toInternalClause(SatClause& clause, std::vector<CMSat::Lit>& internal_clause); + static void toSatClause (std::vector<CMSat::Lit>& clause, SatClause& sat_clause); + + class Statistics { + public: + StatisticsRegistry* d_registry; + IntStat d_statCallsToSolve; + IntStat d_xorClausesAdded; + IntStat d_clausesAdded; + TimerStat d_solveTime; + bool d_registerStats; + Statistics(StatisticsRegistry* registry, + const std::string& prefix); + ~Statistics(); + }; + + Statistics d_statistics; +}; +} // CVC4::prop +} // CVC4 + +#else // CVC4_USE_CRYPTOMINISAT + +namespace CVC4 { +namespace prop { + +class CryptoMinisatSolver : public SatSolver { + +public: + CryptoMinisatSolver(StatisticsRegistry* registry, + const std::string& name = "") { Unreachable(); } + /** Assert a clause in the solver. */ + ClauseId addClause(SatClause& clause, bool removable) { + Unreachable(); + } + + /** Return true if the solver supports native xor resoning */ + bool nativeXor() { Unreachable(); } + + /** Add a clause corresponding to rhs = l1 xor .. xor ln */ + ClauseId addXorClause(SatClause& clause, bool rhs, bool removable) { + Unreachable(); + } + + SatVariable newVar(bool isTheoryAtom, bool preRegister, bool canErase) { Unreachable(); } + SatVariable trueVar() { Unreachable(); } + SatVariable falseVar() { Unreachable(); } + SatValue solve() { Unreachable(); } + SatValue solve(long unsigned int&) { Unreachable(); } + void interrupt() { Unreachable(); } + SatValue value(SatLiteral l) { Unreachable(); } + SatValue modelValue(SatLiteral l) { Unreachable(); } + unsigned getAssertionLevel() const { Unreachable(); } + bool ok() const { return false;}; + + +};/* class CryptoMinisatSolver */ +} // CVC4::prop +} // CVC4 + +#endif // CVC4_USE_CRYPTOMINISAT + + + + diff --git a/src/prop/minisat/core/Solver.cc b/src/prop/minisat/core/Solver.cc index 411b89514..a682a893b 100644 --- a/src/prop/minisat/core/Solver.cc +++ b/src/prop/minisat/core/Solver.cc @@ -232,6 +232,8 @@ CRef Solver::reason(Var x) { vec<Lit> explanation; MinisatSatSolver::toMinisatClause(explanation_cl, explanation); + Debug("pf::sat") << "Solver::reason: explanation_cl = " << explanation_cl << std::endl; + // Sort the literals by trail index level lemma_lt lt(*this); sort(explanation, lt); @@ -266,6 +268,12 @@ CRef Solver::reason(Var x) { } explanation.shrink(i - j); + Debug("pf::sat") << "Solver::reason: explanation = " ; + for (int i = 0; i < explanation.size(); ++i) { + Debug("pf::sat") << explanation[i] << " "; + } + Debug("pf::sat") << std::endl; + // We need an explanation clause so we add a fake literal if (j == 1) { // Add not TRUE to the clause @@ -276,6 +284,7 @@ CRef Solver::reason(Var x) { CRef real_reason = ca.alloc(explLevel, explanation, true); // FIXME: at some point will need more information about where this explanation // came from (ie. the theory/sharing) + Debug("pf::sat") << "Minisat::Solver registering a THEORY_LEMMA (1)" << std::endl; PROOF (ClauseId id = ProofManager::getSatProof()->registerClause(real_reason, THEORY_LEMMA); ProofManager::getCnfProof()->registerConvertedClause(id, true); // no need to pop current assertion as this is not converted to cnf @@ -336,6 +345,12 @@ bool Solver::addClause_(vec<Lit>& ps, bool removable, ClauseId& id) // If we are in solve or decision level > 0 if (minisat_busy || decisionLevel() > 0) { + Debug("pf::sat") << "Add clause adding a new lemma: "; + for (int k = 0; k < ps.size(); ++k) { + Debug("pf::sat") << ps[k] << " "; + } + Debug("pf::sat") << std::endl; + lemmas.push(); ps.copyTo(lemmas.last()); lemmas_removable.push(removable); @@ -379,7 +394,7 @@ bool Solver::addClause_(vec<Lit>& ps, bool removable, ClauseId& id) cr = ca.alloc(clauseLevel, ps, false); clauses_persistent.push(cr); - attachClause(cr); + attachClause(cr); if(PROOF_ON()) { PROOF( @@ -1234,12 +1249,12 @@ lbool Solver::search(int nof_conflicts) } else { - // If this was a final check, we are satisfiable + // If this was a final check, we are satisfiable if (check_type == CHECK_FINAL) { - bool decisionEngineDone = proxy->isDecisionEngineDone(); + bool decisionEngineDone = proxy->isDecisionEngineDone(); // Unless a lemma has added more stuff to the queues if (!decisionEngineDone && - (!order_heap.empty() || qhead < trail.size()) ) { + (!order_heap.empty() || qhead < trail.size()) ) { check_type = CHECK_WITH_THEORY; continue; } else if (recheck) { @@ -1666,6 +1681,13 @@ CRef Solver::updateLemmas() { { // The current lemma vec<Lit>& lemma = lemmas[i]; + + Debug("pf::sat") << "Solver::updateLemmas: working on lemma: "; + for (int k = 0; k < lemma.size(); ++k) { + Debug("pf::sat") << lemma[k] << " "; + } + Debug("pf::sat") << std::endl; + // If it's an empty lemma, we have a conflict at zero level if (lemma.size() == 0) { Assert (! PROOF_ON()); @@ -1725,6 +1747,7 @@ CRef Solver::updateLemmas() { TNode cnf_assertion = lemmas_cnf_assertion[i].first; TNode cnf_def = lemmas_cnf_assertion[i].second; + Debug("pf::sat") << "Minisat::Solver registering a THEORY_LEMMA (2)" << std::endl; ClauseId id = ProofManager::getSatProof()->registerClause(lemma_ref, THEORY_LEMMA); ProofManager::getCnfProof()->setClauseAssertion(id, cnf_assertion); ProofManager::getCnfProof()->setClauseDefinition(id, cnf_def); @@ -1741,6 +1764,7 @@ CRef Solver::updateLemmas() { Node cnf_assertion = lemmas_cnf_assertion[i].first; Node cnf_def = lemmas_cnf_assertion[i].second; + Debug("pf::sat") << "Minisat::Solver registering a THEORY_LEMMA (3)" << std::endl; ClauseId id = ProofManager::getSatProof()->registerUnitClause(lemma[0], THEORY_LEMMA); ProofManager::getCnfProof()->setClauseAssertion(id, cnf_assertion); ProofManager::getCnfProof()->setClauseDefinition(id, cnf_def); diff --git a/src/prop/minisat/minisat.cpp b/src/prop/minisat/minisat.cpp index bfbf9da6f..ff726e299 100644 --- a/src/prop/minisat/minisat.cpp +++ b/src/prop/minisat/minisat.cpp @@ -150,7 +150,7 @@ ClauseId MinisatSatSolver::addClause(SatClause& clause, bool removable) { // FIXME: This relies on the invariant that when ok() is false // the SAT solver does not add the clause (which is what Minisat currently does) if (!ok()) { - return ClauseIdUndef; + return ClauseIdUndef; } d_minisat->addClause(minisat_clause, removable, clause_id); PROOF( Assert (clause_id != ClauseIdError);); @@ -185,7 +185,7 @@ SatValue MinisatSatSolver::solve() { } bool MinisatSatSolver::ok() const { - return d_minisat->okay(); + return d_minisat->okay(); } void MinisatSatSolver::interrupt() { @@ -204,20 +204,20 @@ bool MinisatSatSolver::properExplanation(SatLiteral lit, SatLiteral expl) const return true; } -void MinisatSatSolver::requirePhase(SatLiteral lit) { +void MinisatSatSolver::requirePhase(SatLiteral lit) { Assert(!d_minisat->rnd_pol); Debug("minisat") << "requirePhase(" << lit << ")" << " " << lit.getSatVariable() << " " << lit.isNegated() << std::endl; SatVariable v = lit.getSatVariable(); d_minisat->freezePolarity(v, lit.isNegated()); } -bool MinisatSatSolver::flipDecision() { +bool MinisatSatSolver::flipDecision() { Debug("minisat") << "flipDecision()" << std::endl; return d_minisat->flipDecision(); } -bool MinisatSatSolver::isDecision(SatVariable decn) const { - return d_minisat->isDecision( decn ); +bool MinisatSatSolver::isDecision(SatVariable decn) const { + return d_minisat->isDecision( decn ); } /** Incremental interface */ @@ -291,7 +291,7 @@ namespace CVC4 { template<> prop::SatLiteral toSatLiteral< CVC4::Minisat::Solver>(Minisat::Solver::TLit lit) { return prop::MinisatSatSolver::toSatLiteral(lit); -} +} template<> void toSatClause< CVC4::Minisat::Solver> (const CVC4::Minisat::Solver::TClause& minisat_cl, @@ -300,5 +300,3 @@ void toSatClause< CVC4::Minisat::Solver> (const CVC4::Minisat::Solver::TClause& } } /* namespace CVC4 */ - - diff --git a/src/prop/minisat/minisat.h b/src/prop/minisat/minisat.h index 535ce1a47..ec5297bb7 100644 --- a/src/prop/minisat/minisat.h +++ b/src/prop/minisat/minisat.h @@ -44,6 +44,9 @@ public: void initialize(context::Context* context, TheoryProxy* theoryProxy); ClauseId addClause(SatClause& clause, bool removable); + ClauseId addXorClause(SatClause& clause, bool rhs, bool removable) { + Unreachable("Minisat does not support native XOR reasoning"); + } SatVariable newVar(bool isTheoryAtom, bool preRegister, bool canErase); SatVariable trueVar() { return d_minisat->trueVar(); } diff --git a/src/prop/prop_engine.cpp b/src/prop/prop_engine.cpp index 54cf4c457..eb607e901 100644 --- a/src/prop/prop_engine.cpp +++ b/src/prop/prop_engine.cpp @@ -132,13 +132,13 @@ void PropEngine::assertFormula(TNode node) { void PropEngine::assertLemma(TNode node, bool negated, bool removable, ProofRule rule, - theory::TheoryId ownerTheory, + LemmaProofRecipe* proofRecipe, TNode from) { //Assert(d_inCheckSat, "Sat solver should be in solve()!"); Debug("prop::lemmas") << "assertLemma(" << node << ")" << endl; // Assert as (possibly) removable - d_cnfStream->convertAndAssert(node, removable, negated, rule, from, ownerTheory); + d_cnfStream->convertAndAssert(node, removable, negated, rule, from, proofRecipe); } void PropEngine::requirePhase(TNode n, bool phase) { diff --git a/src/prop/prop_engine.h b/src/prop/prop_engine.h index b9ce7ca7e..c02015931 100644 --- a/src/prop/prop_engine.h +++ b/src/prop/prop_engine.h @@ -37,6 +37,7 @@ namespace CVC4 { class ResourceManager; class DecisionEngine; class TheoryEngine; +class LemmaProofRecipe; namespace theory { class TheoryRegistrar; @@ -134,7 +135,7 @@ public: * @param removable whether this lemma can be quietly removed based * on an activity heuristic (or not) */ - void assertLemma(TNode node, bool negated, bool removable, ProofRule rule, theory::TheoryId ownerTheory, TNode from = TNode::null()); + void assertLemma(TNode node, bool negated, bool removable, ProofRule rule, LemmaProofRecipe* proofRecipe, TNode from = TNode::null()); /** * If ever n is decided upon, it must be in the given phase. This diff --git a/src/prop/sat_solver.h b/src/prop/sat_solver.h index 92696ae25..81fb94242 100644 --- a/src/prop/sat_solver.h +++ b/src/prop/sat_solver.h @@ -49,6 +49,12 @@ public: virtual ClauseId addClause(SatClause& clause, bool removable) = 0; + /** Return true if the solver supports native xor resoning */ + virtual bool nativeXor() { return false; } + + /** Add a clause corresponding to rhs = l1 xor .. xor ln */ + virtual ClauseId addXorClause(SatClause& clause, bool rhs, bool removable) = 0; + /** * Create a new boolean variable in the solver. * @param isTheoryAtom is this a theory atom that needs to be asserted to theory @@ -84,7 +90,8 @@ public: /** Check if the solver is in an inconsistent state */ virtual bool ok() const = 0; - + + virtual void setProofLog( BitVectorProof * bvp ) {} };/* class SatSolver */ diff --git a/src/prop/sat_solver_factory.cpp b/src/prop/sat_solver_factory.cpp index 092ec72f2..7fdc44e66 100644 --- a/src/prop/sat_solver_factory.cpp +++ b/src/prop/sat_solver_factory.cpp @@ -16,6 +16,7 @@ #include "prop/sat_solver_factory.h" +#include "prop/cryptominisat.h" #include "prop/minisat/minisat.h" #include "prop/bvminisat/bvminisat.h" @@ -26,6 +27,12 @@ BVSatSolverInterface* SatSolverFactory::createMinisat(context::Context* mainSatC return new BVMinisatSatSolver(registry, mainSatContext, name); } +SatSolver* SatSolverFactory::createCryptoMinisat(StatisticsRegistry* registry, + const std::string& name) { +return new CryptoMinisatSolver(registry, name); +} + + DPLLSatSolverInterface* SatSolverFactory::createDPLLMinisat(StatisticsRegistry* registry) { return new MinisatSatSolver(registry); } diff --git a/src/prop/sat_solver_factory.h b/src/prop/sat_solver_factory.h index 7cc23a8e8..a04bcaaf4 100644 --- a/src/prop/sat_solver_factory.h +++ b/src/prop/sat_solver_factory.h @@ -35,6 +35,8 @@ public: StatisticsRegistry* registry, const std::string& name = ""); static DPLLSatSolverInterface* createDPLLMinisat(StatisticsRegistry* registry); + static SatSolver* createCryptoMinisat(StatisticsRegistry* registry, + const std::string& name = ""); };/* class SatSolverFactory */ diff --git a/src/prop/theory_proxy.cpp b/src/prop/theory_proxy.cpp index 4a4515eb9..6e8f1fbbf 100644 --- a/src/prop/theory_proxy.cpp +++ b/src/prop/theory_proxy.cpp @@ -99,29 +99,34 @@ void TheoryProxy::explainPropagation(SatLiteral l, SatClause& explanation) { TNode lNode = d_cnfStream->getNode(l); Debug("prop-explain") << "explainPropagation(" << lNode << ")" << std::endl; - NodeTheoryPair theoryExplanation = d_theoryEngine->getExplanationAndExplainer(lNode); - Node explanationNode = theoryExplanation.node; - theory::TheoryId explainerTheory = theoryExplanation.theory; + LemmaProofRecipe* proofRecipe = NULL; + PROOF(proofRecipe = new LemmaProofRecipe;); + + Node theoryExplanation = d_theoryEngine->getExplanationAndRecipe(lNode, proofRecipe); PROOF({ - ProofManager::getCnfProof()->pushCurrentAssertion(explanationNode); - ProofManager::getCnfProof()->setExplainerTheory(explainerTheory); + ProofManager::getCnfProof()->pushCurrentAssertion(theoryExplanation); + ProofManager::getCnfProof()->setProofRecipe(proofRecipe); + + Debug("pf::sat") << "TheoryProxy::explainPropagation: setting lemma recipe to: " + << std::endl; + proofRecipe->dump("pf::sat"); - Debug("pf::sat") << "TheoryProxy::explainPropagation: setting explainer theory to: " - << explainerTheory << std::endl; + delete proofRecipe; + proofRecipe = NULL; }); - Debug("prop-explain") << "explainPropagation() => " << explanationNode << std::endl; - if (explanationNode.getKind() == kind::AND) { - Node::const_iterator it = explanationNode.begin(); - Node::const_iterator it_end = explanationNode.end(); + Debug("prop-explain") << "explainPropagation() => " << theoryExplanation << std::endl; + if (theoryExplanation.getKind() == kind::AND) { + Node::const_iterator it = theoryExplanation.begin(); + Node::const_iterator it_end = theoryExplanation.end(); explanation.push_back(l); for (; it != it_end; ++ it) { explanation.push_back(~d_cnfStream->getLiteral(*it)); } } else { explanation.push_back(l); - explanation.push_back(~d_cnfStream->getLiteral(explanationNode)); + explanation.push_back(~d_cnfStream->getLiteral(theoryExplanation)); } } @@ -175,7 +180,9 @@ void TheoryProxy::notifyRestart() { if(lemmaCount % 1 == 0) { Debug("shared") << "=) " << asNode << std::endl; } - d_propEngine->assertLemma(d_theoryEngine->preprocess(asNode), false, true, RULE_INVALID, theory::THEORY_LAST); + + LemmaProofRecipe* noProofRecipe = NULL; + d_propEngine->assertLemma(d_theoryEngine->preprocess(asNode), false, true, RULE_INVALID, noProofRecipe); } else { Debug("shared") << "=(" << asNode << std::endl; } diff --git a/src/smt/boolean_terms.cpp b/src/smt/boolean_terms.cpp index 40b757598..8957ad7f7 100644 --- a/src/smt/boolean_terms.cpp +++ b/src/smt/boolean_terms.cpp @@ -458,7 +458,7 @@ Node BooleanTermConverter::rewriteBooleanTermsRec(TNode top, theory::TheoryId pa goto next_worklist; } - if(parentTheory != theory::THEORY_BOOL && top.getType().isBoolean()) { + if(parentTheory != theory::THEORY_BOOL && top.getType().isBoolean() && top.getKind()!=kind::SEP_STAR && top.getKind()!=kind::SEP_WAND) { // still need to rewrite e.g. function applications over boolean Node topRewritten = rewriteBooleanTermsRec(top, theory::THEORY_BOOL, quantBoolVars); Node n; @@ -682,20 +682,22 @@ Node BooleanTermConverter::rewriteBooleanTermsRec(TNode top, theory::TheoryId pa goto next_worklist; } } 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()); - replace(argTypes.begin(), argTypes.end(), *i, d_tt.getType()); - TypeNode newType = nm->mkTypeNode(t.getKind(), argTypes); - Node n = nm->mkSkolem(top.getAttribute(expr::VarNameAttr()), - newType, "a variable introduced by Boolean-term conversion", - NodeManager::SKOLEM_EXACT_NAME); - Debug("boolean-terms") << "constructed: " << n << " of type " << newType << endl; - top.setAttribute(BooleanTermAttr(), n); - d_varCache[top] = n; - result.top() << n; - worklist.pop(); - goto next_worklist; + if( t.getKind()!=kind::SEP_STAR && t.getKind()!=kind::SEP_WAND ){ + for(TypeNode::iterator i = t.begin(); i != t.end(); ++i) { + if((*i).isBoolean()) { + vector<TypeNode> argTypes(t.begin(), t.end()); + replace(argTypes.begin(), argTypes.end(), *i, d_tt.getType()); + TypeNode newType = nm->mkTypeNode(t.getKind(), argTypes); + Node n = nm->mkSkolem(top.getAttribute(expr::VarNameAttr()), + newType, "a variable introduced by Boolean-term conversion", + NodeManager::SKOLEM_EXACT_NAME); + Debug("boolean-terms") << "constructed: " << n << " of type " << newType << endl; + top.setAttribute(BooleanTermAttr(), n); + d_varCache[top] = n; + result.top() << n; + worklist.pop(); + goto next_worklist; + } } } } @@ -714,6 +716,8 @@ Node BooleanTermConverter::rewriteBooleanTermsRec(TNode top, theory::TheoryId pa case kind::RR_REWRITE: case kind::RR_DEDUCTION: case kind::RR_REDUCTION: + case kind::SEP_STAR: + case kind::SEP_WAND: // not yet supported result.top() << top; worklist.pop(); diff --git a/src/smt/smt_engine.cpp b/src/smt/smt_engine.cpp index 5bd1cbdfc..08495c936 100644 --- a/src/smt/smt_engine.cpp +++ b/src/smt/smt_engine.cpp @@ -94,6 +94,7 @@ #include "theory/quantifiers/quantifiers_rewriter.h" #include "theory/sort_inference.h" #include "theory/strings/theory_strings.h" +#include "theory/sep/theory_sep.h" #include "theory/substitutions.h" #include "theory/theory_engine.h" #include "theory/theory_model.h" @@ -552,10 +553,10 @@ class SmtEnginePrivate : public NodeManagerListener { */ unsigned d_simplifyAssertionsDepth; - /** whether certain preprocess steps are necessary */ - bool d_needsExpandDefs; - bool d_needsRewriteBoolTerms; - bool d_needsConstrainSubTypes; + /** TODO: whether certain preprocess steps are necessary */ + //bool d_needsExpandDefs; + //bool d_needsRewriteBoolTerms; + //bool d_needsConstrainSubTypes; public: /** @@ -684,9 +685,9 @@ public: d_abstractValueMap(&d_fakeContext), d_abstractValues(), d_simplifyAssertionsDepth(0), - d_needsExpandDefs(true), - d_needsRewriteBoolTerms(true), - d_needsConstrainSubTypes(true), //TODO + //d_needsExpandDefs(true), + //d_needsRewriteBoolTerms(true), + //d_needsConstrainSubTypes(true), //TODO d_iteSkolemMap(), d_iteRemover(smt.d_userContext), d_pbsProcessor(smt.d_userContext), @@ -990,7 +991,7 @@ public: Trace("smt-qe-debug") << " return : " << ret << std::endl; //recursive (for nested quantification) ret = replaceQuantifiersWithInstantiations( reti, insts, visited ); - } + } }else if( n.getNumChildren()>0 ){ bool childChanged = false; std::vector< Node > children; @@ -1061,12 +1062,16 @@ SmtEngine::SmtEngine(ExprManager* em) throw() : // The ProofManager is constructed before any other proof objects such as // SatProof and TheoryProofs. The TheoryProofEngine and the SatProof are - // initialized in TheoryEngine and PropEngine respectively. + // initialized in TheoryEngine and PropEngine respectively. Assert(d_proofManager == NULL); + + // d_proofManager must be created before Options has been finished + // being parsed from the input file. Because of this, we cannot trust + // that options::proof() is set correctly yet. #ifdef CVC4_PROOF d_proofManager = new ProofManager(); #endif - + // We have mutual dependency here, so we add the prop engine to the theory // engine later (it is non-essential there) d_theoryEngine = new TheoryEngine(d_context, d_userContext, @@ -1078,7 +1083,9 @@ SmtEngine::SmtEngine(ExprManager* em) throw() : for(TheoryId id = theory::THEORY_FIRST; id < theory::THEORY_LAST; ++id) { TheoryConstructor::addTheory(d_theoryEngine, id); //register with proof engine if applicable - THEORY_PROOF(ProofManager::currentPM()->getTheoryProofEngine()->registerTheory(d_theoryEngine->theoryOf(id)); ); +#ifdef CVC4_PROOF + ProofManager::currentPM()->getTheoryProofEngine()->registerTheory(d_theoryEngine->theoryOf(id)); +#endif } d_private->addUseTheoryListListener(d_theoryEngine); @@ -1100,7 +1107,7 @@ void SmtEngine::finishInit() { Trace("smt-debug") << "SmtEngine::finishInit" << std::endl; // ensure that our heuristics are properly set up setDefaults(); - + Trace("smt-debug") << "Making decision engine..." << std::endl; d_decisionEngine = new DecisionEngine(d_context, d_userContext); @@ -1147,6 +1154,13 @@ void SmtEngine::finishInit() { d_dumpCommands.clear(); PROOF( ProofManager::currentPM()->setLogic(d_logic); ); + PROOF({ + for(TheoryId id = theory::THEORY_FIRST; id < theory::THEORY_LAST; ++id) { + ProofManager::currentPM()->getTheoryProofEngine()-> + finishRegisterTheory(d_theoryEngine->theoryOf(id)); + } + }); + Trace("smt-debug") << "SmtEngine::finishInit done" << std::endl; } @@ -1237,8 +1251,14 @@ SmtEngine::~SmtEngine() throw() { delete d_decisionEngine; d_decisionEngine = NULL; - PROOF(delete d_proofManager;); - PROOF(d_proofManager = NULL;); + +// d_proofManager is always created when proofs are enabled at configure time. +// Becuase of this, this code should not be wrapped in PROOF() which +// additionally checks flags such as options::proof(). +#ifdef CVC4_PROOF + delete d_proofManager; + d_proofManager = NULL; +#endif delete d_stats; d_stats = NULL; @@ -1306,9 +1326,9 @@ void SmtEngine::setDefaults() { } else if (options::solveIntAsBV() > 0) { d_logic = LogicInfo("QF_BV"); - // } else if (d_logic.getLogicString() == "QF_UFBV" && - // options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { - // d_logic = LogicInfo("QF_BV"); + } else if (d_logic.getLogicString() == "QF_UFBV" && + options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { + d_logic = LogicInfo("QF_BV"); } // set strings-exp @@ -1333,6 +1353,10 @@ void SmtEngine::setDefaults() { options::fmfBoundInt.set( true ); Trace("smt") << "turning on fmf-bound-int, for strings-exp" << std::endl; } + if(! options::fmfInstEngine.wasSetByUser()) { + options::fmfInstEngine.set( true ); + Trace("smt") << "turning on fmf-inst-engine, for strings-exp" << std::endl; + } /* if(! options::rewriteDivk.wasSetByUser()) { options::rewriteDivk.set( true ); @@ -1709,20 +1733,24 @@ void SmtEngine::setDefaults() { //must have finite model finding on options::finiteModelFind.set( true ); } - + //if it contains a theory with non-termination, do not strictly enforce that quantifiers and theory combination must be interleaved if( d_logic.isTheoryEnabled(THEORY_STRINGS) || (d_logic.isTheoryEnabled(THEORY_ARITH) && !d_logic.isLinear()) ){ if( !options::instWhenStrictInterleave.wasSetByUser() ){ options::instWhenStrictInterleave.set( false ); } } - + //local theory extensions if( options::localTheoryExt() ){ if( !options::instMaxLevel.wasSetByUser() ){ options::instMaxLevel.set( 0 ); } } + if( options::instMaxLevel()!=-1 ){ + Notice() << "SmtEngine: turning off cbqi to support instMaxLevel" << endl; + options::cbqi.set(false); + } if(options::fmfBoundIntLazy.wasSetByUser() && options::fmfBoundIntLazy()) { options::fmfBoundInt.set( true ); @@ -1783,13 +1811,15 @@ void SmtEngine::setDefaults() { } //apply counterexample guided instantiation options - if( options::cegqiSingleInv() ){ - options::ceGuidedInst.set( true ); + if( options::cegqiSingleInvMode()!=quantifiers::CEGQI_SI_MODE_NONE ){ + if( !options::ceGuidedInst.wasSetByUser() ){ + options::ceGuidedInst.set( true ); + } } if( options::ceGuidedInst() ){ //counterexample-guided instantiation for sygus - if( !options::cegqiSingleInv.wasSetByUser() ){ - options::cegqiSingleInv.set( true ); + if( !options::cegqiSingleInvMode.wasSetByUser() ){ + options::cegqiSingleInvMode.set( quantifiers::CEGQI_SI_MODE_USE ); } if( !options::quantConflictFind.wasSetByUser() ){ options::quantConflictFind.set( false ); @@ -1824,8 +1854,8 @@ void SmtEngine::setDefaults() { } } //counterexample-guided instantiation for non-sygus - // enable if any quantifiers with arithmetic or datatypes - if( ( d_logic.isQuantified() && ( d_logic.isTheoryEnabled(THEORY_ARITH) || d_logic.isTheoryEnabled(THEORY_DATATYPES) ) ) || + // enable if any possible quantifiers with arithmetic, datatypes or bitvectors + if( ( d_logic.isQuantified() && ( d_logic.isTheoryEnabled(THEORY_ARITH) || d_logic.isTheoryEnabled(THEORY_DATATYPES) || d_logic.isTheoryEnabled(THEORY_BV) ) ) || options::cbqiAll() ){ if( !options::cbqi.wasSetByUser() ){ options::cbqi.set( true ); @@ -2583,8 +2613,9 @@ Node SmtEnginePrivate::intToBV(TNode n, NodeMap& cache) { case kind::CONST_RATIONAL: { Rational constant = current.getConst<Rational>(); AlwaysAssert(constant.isIntegral()); + AlwaysAssert(constant >= 0); BitVector bv(size, constant.getNumerator()); - if (bv.getValue() != constant.getNumerator()) { + if (bv.toSignedInt() != constant.getNumerator()) { throw TypeCheckingException(current.toExpr(), string("Not enough bits for constant in intToBV: ") + current.toString()); } result = nm->mkConst(bv); @@ -3837,7 +3868,7 @@ void SmtEnginePrivate::processAssertions() { ProofManager::currentPM()->addAssertion(d_assertions[i].toExpr()); } ); - + Debug("smt") << " d_assertions : " << d_assertions.size() << endl; if( options::ceGuidedInst() ){ @@ -3856,8 +3887,8 @@ void SmtEnginePrivate::processAssertions() { } if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER && - !d_smt.d_logic.isPure(THEORY_BV)) { - // && d_smt.d_logic.getLogicString() != "QF_UFBV") + !d_smt.d_logic.isPure(THEORY_BV) && + d_smt.d_logic.getLogicString() != "QF_UFBV") { throw ModalException("Eager bit-blasting does not currently support theory combination. " "Note that in a QF_BV problem UF symbols can be introduced for division. " "Try --bv-div-zero-const to interpret division by zero as a constant."); @@ -3885,7 +3916,6 @@ void SmtEnginePrivate::processAssertions() { Debug("smt") << " d_assertions : " << d_assertions.size() << endl; - dumpAssertions("pre-constrain-subtypes", d_assertions); { // Any variables of subtype types need to be constrained properly. @@ -3965,6 +3995,10 @@ void SmtEnginePrivate::processAssertions() { Trace("smt-proc") << "SmtEnginePrivate::processAssertions() : post-strings-preprocess" << endl; dumpAssertions("post-strings-pp", d_assertions); } + if( d_smt.d_logic.isTheoryEnabled(THEORY_SEP) ) { + //separation logic solver needs to register the entire input + ((theory::sep::TheorySep*)d_smt.d_theoryEngine->theoryOf(THEORY_SEP))->processAssertions( d_assertions.ref() ); + } if( d_smt.d_logic.isQuantified() ){ Trace("smt-proc") << "SmtEnginePrivate::processAssertions() : pre-quant-preprocess" << endl; //remove rewrite rules @@ -4220,7 +4254,7 @@ void SmtEnginePrivate::processAssertions() { Trace("smt-proc") << "SmtEnginePrivate::processAssertions() end" << endl; dumpAssertions("post-everything", d_assertions); - + //set instantiation level of everything to zero if( options::instLevelInputOnly() && options::instMaxLevel()!=-1 ){ @@ -4410,74 +4444,75 @@ Result SmtEngine::checkSynth(const Expr& e) throw(TypeCheckingException, ModalEx Trace("smt-synth") << "Check synthesis conjecture: " << e << std::endl; Expr e_check = e; Node conj = Node::fromExpr( e ); - Assert( conj.getKind()==kind::FORALL ); - //possibly run quantifier elimination to make formula into single invocation - if( conj[1].getKind()==kind::EXISTS ){ - Node conj_se = conj[1][1]; - - Trace("smt-synth") << "Compute single invocation for " << conj_se << "..." << std::endl; - quantifiers::SingleInvocationPartition sip( kind::APPLY ); - sip.init( conj_se ); - Trace("smt-synth") << "...finished, got:" << std::endl; - sip.debugPrint("smt-synth"); - - if( !sip.isPurelySingleInvocation() && sip.isNonGroundSingleInvocation() ){ - //We are in the case where our synthesis conjecture is exists f. forall xy. P( f( x ), x, y ), P does not contain f. - //The following will run QE on (exists z x.) exists y. P( z, x, y ) to obtain Q( z, x ), - // and then constructs exists f. forall x. Q( f( x ), x ), where Q does not contain f. We invoke synthesis solver on this result. - - //create new smt engine to do quantifier elimination - SmtEngine smt_qe( d_exprManager ); - smt_qe.setLogic(getLogicInfo()); - Trace("smt-synth") << "Property is non-ground single invocation, run QE to obtain single invocation." << std::endl; - //partition variables - std::vector< Node > qe_vars; - std::vector< Node > nqe_vars; - for( unsigned i=0; i<sip.d_all_vars.size(); i++ ){ - Node v = sip.d_all_vars[i]; - if( std::find( sip.d_si_vars.begin(), sip.d_si_vars.end(), v )==sip.d_si_vars.end() ){ - qe_vars.push_back( v ); - }else{ - nqe_vars.push_back( v ); + if( conj.getKind()==kind::FORALL ){ + //possibly run quantifier elimination to make formula into single invocation + if( conj[1].getKind()==kind::EXISTS ){ + Node conj_se = conj[1][1]; + + Trace("smt-synth") << "Compute single invocation for " << conj_se << "..." << std::endl; + quantifiers::SingleInvocationPartition sip( kind::APPLY ); + sip.init( conj_se ); + Trace("smt-synth") << "...finished, got:" << std::endl; + sip.debugPrint("smt-synth"); + + if( !sip.isPurelySingleInvocation() && sip.isNonGroundSingleInvocation() ){ + //We are in the case where our synthesis conjecture is exists f. forall xy. P( f( x ), x, y ), P does not contain f. + //The following will run QE on (exists z x.) exists y. P( z, x, y ) to obtain Q( z, x ), + // and then constructs exists f. forall x. Q( f( x ), x ), where Q does not contain f. We invoke synthesis solver on this result. + + //create new smt engine to do quantifier elimination + SmtEngine smt_qe( d_exprManager ); + smt_qe.setLogic(getLogicInfo()); + Trace("smt-synth") << "Property is non-ground single invocation, run QE to obtain single invocation." << std::endl; + //partition variables + std::vector< Node > qe_vars; + std::vector< Node > nqe_vars; + for( unsigned i=0; i<sip.d_all_vars.size(); i++ ){ + Node v = sip.d_all_vars[i]; + if( std::find( sip.d_si_vars.begin(), sip.d_si_vars.end(), v )==sip.d_si_vars.end() ){ + qe_vars.push_back( v ); + }else{ + nqe_vars.push_back( v ); + } } + std::vector< Node > orig; + std::vector< Node > subs; + //skolemize non-qe variables + for( unsigned i=0; i<nqe_vars.size(); i++ ){ + Node k = NodeManager::currentNM()->mkSkolem( "k", nqe_vars[i].getType(), "qe for non-ground single invocation" ); + orig.push_back( nqe_vars[i] ); + subs.push_back( k ); + Trace("smt-synth") << " subs : " << nqe_vars[i] << " -> " << k << std::endl; + } + for( std::map< Node, bool >::iterator it = sip.d_funcs.begin(); it != sip.d_funcs.end(); ++it ){ + orig.push_back( sip.d_func_inv[it->first] ); + Node k = NodeManager::currentNM()->mkSkolem( "k", sip.d_func_fo_var[it->first].getType(), "qe for function in non-ground single invocation" ); + subs.push_back( k ); + Trace("smt-synth") << " subs : " << sip.d_func_inv[it->first] << " -> " << k << std::endl; + } + Node conj_se_ngsi = sip.getFullSpecification(); + Node conj_se_ngsi_subs = conj_se_ngsi.substitute( orig.begin(), orig.end(), subs.begin(), subs.end() ); + Assert( !qe_vars.empty() ); + conj_se_ngsi_subs = NodeManager::currentNM()->mkNode( kind::EXISTS, NodeManager::currentNM()->mkNode( kind::BOUND_VAR_LIST, qe_vars ), conj_se_ngsi_subs ); + + Trace("smt-synth") << "Run quantifier elimination on " << conj_se_ngsi_subs << std::endl; + Expr qe_res = smt_qe.doQuantifierElimination( conj_se_ngsi_subs.toExpr(), true, false ); + Trace("smt-synth") << "Result : " << qe_res << std::endl; + + //create single invocation conjecture + Node qe_res_n = Node::fromExpr( qe_res ); + qe_res_n = qe_res_n.substitute( subs.begin(), subs.end(), orig.begin(), orig.end() ); + if( !nqe_vars.empty() ){ + qe_res_n = NodeManager::currentNM()->mkNode( kind::EXISTS, NodeManager::currentNM()->mkNode( kind::BOUND_VAR_LIST, nqe_vars ), qe_res_n ); + } + Assert( conj.getNumChildren()==3 ); + qe_res_n = NodeManager::currentNM()->mkNode( kind::FORALL, conj[0], qe_res_n, conj[2] ); + Trace("smt-synth") << "Converted conjecture after QE : " << qe_res_n << std::endl; + e_check = qe_res_n.toExpr(); } - std::vector< Node > orig; - std::vector< Node > subs; - //skolemize non-qe variables - for( unsigned i=0; i<nqe_vars.size(); i++ ){ - Node k = NodeManager::currentNM()->mkSkolem( "k", nqe_vars[i].getType(), "qe for non-ground single invocation" ); - orig.push_back( nqe_vars[i] ); - subs.push_back( k ); - Trace("smt-synth") << " subs : " << nqe_vars[i] << " -> " << k << std::endl; - } - for( std::map< Node, bool >::iterator it = sip.d_funcs.begin(); it != sip.d_funcs.end(); ++it ){ - orig.push_back( sip.d_func_inv[it->first] ); - Node k = NodeManager::currentNM()->mkSkolem( "k", sip.d_func_fo_var[it->first].getType(), "qe for function in non-ground single invocation" ); - subs.push_back( k ); - Trace("smt-synth") << " subs : " << sip.d_func_inv[it->first] << " -> " << k << std::endl; - } - Node conj_se_ngsi = sip.getFullSpecification(); - Node conj_se_ngsi_subs = conj_se_ngsi.substitute( orig.begin(), orig.end(), subs.begin(), subs.end() ); - Assert( !qe_vars.empty() ); - conj_se_ngsi_subs = NodeManager::currentNM()->mkNode( kind::EXISTS, NodeManager::currentNM()->mkNode( kind::BOUND_VAR_LIST, qe_vars ), conj_se_ngsi_subs ); - - Trace("smt-synth") << "Run quantifier elimination on " << conj_se_ngsi_subs << std::endl; - Expr qe_res = smt_qe.doQuantifierElimination( conj_se_ngsi_subs.toExpr(), true, false ); - Trace("smt-synth") << "Result : " << qe_res << std::endl; - - //create single invocation conjecture - Node qe_res_n = Node::fromExpr( qe_res ); - qe_res_n = qe_res_n.substitute( subs.begin(), subs.end(), orig.begin(), orig.end() ); - if( !nqe_vars.empty() ){ - qe_res_n = NodeManager::currentNM()->mkNode( kind::EXISTS, NodeManager::currentNM()->mkNode( kind::BOUND_VAR_LIST, nqe_vars ), qe_res_n ); - } - Assert( conj.getNumChildren()==3 ); - qe_res_n = NodeManager::currentNM()->mkNode( kind::FORALL, conj[0], qe_res_n, conj[2] ); - Trace("smt-synth") << "Converted conjecture after QE : " << qe_res_n << std::endl; - e_check = qe_res_n.toExpr(); } } - + return checkSatisfiability( e_check, true, false ); } @@ -5104,7 +5139,7 @@ Expr SmtEngine::doQuantifierElimination(const Expr& e, bool doFull, bool strict) Warning() << "Unexpected logic for quantifier elimination " << d_logic << endl; } Trace("smt-qe") << "Do quantifier elimination " << e << std::endl; - Node n_e = Node::fromExpr( e ); + Node n_e = Node::fromExpr( e ); if( n_e.getKind()!=kind::EXISTS ){ throw ModalException("Expecting an existentially quantified formula as argument to get-qe."); } @@ -5131,7 +5166,7 @@ Expr SmtEngine::doQuantifierElimination(const Expr& e, bool doFull, bool strict) InternalError(ss.str().c_str()); } //get the instantiations for all quantified formulas - std::map< Node, std::vector< Node > > insts; + std::map< Node, std::vector< Node > > insts; d_theoryEngine->getInstantiations( insts ); //find the quantified formula that corresponds to the input Node top_q; diff --git a/src/smt/smt_engine_check_proof.cpp b/src/smt/smt_engine_check_proof.cpp index 5634a4651..808f5162c 100644 --- a/src/smt/smt_engine_check_proof.cpp +++ b/src/smt/smt_engine_check_proof.cpp @@ -63,14 +63,21 @@ void SmtEngine::checkProof() { Chat() << "checking proof..." << endl; - if ( !(d_logic.isPure(theory::THEORY_BOOL) || - d_logic.isPure(theory::THEORY_BV) || - d_logic.isPure(theory::THEORY_ARRAY) || - (d_logic.isPure(theory::THEORY_UF) && - ! d_logic.hasCardinalityConstraints())) || - d_logic.isQuantified()) { - // no checking for these yet - Notice() << "Notice: no proof-checking for non-UF/Bool/BV proofs yet" << endl; + std::string logicString = d_logic.getLogicString(); + + if (!( + // Pure logics + logicString == "QF_UF" || + logicString == "QF_AX" || + logicString == "QF_BV" || + // Non-pure logics + logicString == "QF_AUF" || + logicString == "QF_UFBV" || + logicString == "QF_ABV" || + logicString == "QF_AUFBV" + )) { + // This logic is not yet supported + Notice() << "Notice: no proof-checking for " << logicString << " proofs yet" << endl; return; } diff --git a/src/smt/smt_engine_scope.h b/src/smt/smt_engine_scope.h index e00be40d4..9407ff498 100644 --- a/src/smt/smt_engine_scope.h +++ b/src/smt/smt_engine_scope.h @@ -49,7 +49,6 @@ inline bool smtEngineInScope() { // FIXME: Maybe move into SmtScope? inline ProofManager* currentProofManager() { #if IS_PROOFS_BUILD - Assert(options::proof() || options::unsatCores()); Assert(s_smtEngine_current != NULL); return s_smtEngine_current->d_proofManager; #else /* IS_PROOFS_BUILD */ diff --git a/src/theory/arrays/array_proof_reconstruction.cpp b/src/theory/arrays/array_proof_reconstruction.cpp index 11c3dc081..6dfd14157 100644 --- a/src/theory/arrays/array_proof_reconstruction.cpp +++ b/src/theory/arrays/array_proof_reconstruction.cpp @@ -101,8 +101,59 @@ void ArrayProofReconstruction::notify(unsigned reasonType, Node reason, Node a, Debug("pf::ee") << "Getting explanation for ROW guard: " << indexOne << " != " << indexTwo << std::endl; + eq::EqProof* childProof = new eq::EqProof; d_equalityEngine->explainEquality(indexOne, indexTwo, false, equalities, childProof); + + // It could be that the guard condition is a constant disequality. In this case, + // we need to change it to a different format. + if (childProof->d_id == theory::eq::MERGED_THROUGH_CONSTANTS) { + // The proof has two children, explaining why each index is a (different) constant. + Assert(childProof->d_children.size() == 2); + + Node constantOne, constantTwo; + // Each subproof explains why one of the indices is constant. + + if (childProof->d_children[0]->d_id == theory::eq::MERGED_THROUGH_REFLEXIVITY) { + constantOne = childProof->d_children[0]->d_node; + } else { + Assert(childProof->d_children[0]->d_id == theory::eq::MERGED_THROUGH_EQUALITY); + if ((childProof->d_children[0]->d_node[0] == indexOne) || + (childProof->d_children[0]->d_node[0] == indexTwo)) { + constantOne = childProof->d_children[0]->d_node[1]; + } else { + constantOne = childProof->d_children[0]->d_node[0]; + } + } + + if (childProof->d_children[1]->d_id == theory::eq::MERGED_THROUGH_REFLEXIVITY) { + constantTwo = childProof->d_children[1]->d_node; + } else { + Assert(childProof->d_children[1]->d_id == theory::eq::MERGED_THROUGH_EQUALITY); + if ((childProof->d_children[1]->d_node[0] == indexOne) || + (childProof->d_children[1]->d_node[0] == indexTwo)) { + constantTwo = childProof->d_children[1]->d_node[1]; + } else { + constantTwo = childProof->d_children[1]->d_node[0]; + } + } + + eq::EqProof* constantDisequalityProof = new eq::EqProof; + constantDisequalityProof->d_id = theory::eq::MERGED_THROUGH_CONSTANTS; + constantDisequalityProof->d_node = + NodeManager::currentNM()->mkNode(kind::EQUAL, constantOne, constantTwo).negate(); + + // Middle is where we need to insert the new disequality + std::vector<eq::EqProof *>::iterator middle = childProof->d_children.begin(); + ++middle; + + childProof->d_children.insert(middle, constantDisequalityProof); + + childProof->d_id = theory::eq::MERGED_THROUGH_TRANS; + childProof->d_node = + NodeManager::currentNM()->mkNode(kind::EQUAL, indexOne, indexTwo).negate(); + } + proof->d_children.push_back(childProof); } else { // This is the case of (i == k) because ((a[i]:=t)[k] != a[k]), diff --git a/src/theory/arrays/theory_arrays.cpp b/src/theory/arrays/theory_arrays.cpp index 6add1b55f..28a08630e 100644 --- a/src/theory/arrays/theory_arrays.cpp +++ b/src/theory/arrays/theory_arrays.cpp @@ -829,7 +829,8 @@ void TheoryArrays::propagate(Effort e) Node TheoryArrays::explain(TNode literal) { - return explain(literal, NULL); + Node explanation = explain(literal, NULL); + return explanation; } Node TheoryArrays::explain(TNode literal, eq::EqProof *proof) @@ -1394,6 +1395,7 @@ void TheoryArrays::check(Effort e) { break; default: Unreachable(); + break; } } @@ -2231,6 +2233,7 @@ bool TheoryArrays::dischargeLemmas() void TheoryArrays::conflict(TNode a, TNode b) { Debug("pf::array") << "TheoryArrays::Conflict called" << std::endl; eq::EqProof* proof = d_proofsEnabled ? new eq::EqProof() : NULL; + if (a.getKind() == kind::CONST_BOOLEAN) { d_conflictNode = explain(a.iffNode(b), proof); } else { diff --git a/src/theory/bv/abstraction.cpp b/src/theory/bv/abstraction.cpp index fdc36ce72..dc5520411 100644 --- a/src/theory/bv/abstraction.cpp +++ b/src/theory/bv/abstraction.cpp @@ -41,7 +41,7 @@ bool AbstractionModule::applyAbstraction(const std::vector<Node>& assertions, st continue; } Node signature = computeSignature(assertions[i][j]); - storeSignature(signature, assertions[i][j]); + storeSignature(signature, assertions[i][j]); Debug("bv-abstraction") << " assertion: " << assertions[i][j] <<"\n"; Debug("bv-abstraction") << " signature: " << signature <<"\n"; } @@ -52,7 +52,7 @@ bool AbstractionModule::applyAbstraction(const std::vector<Node>& assertions, st for (unsigned i = 0; i < assertions.size(); ++i) { if (assertions[i].getKind() == kind::OR && assertions[i][0].getKind() == kind::AND) { - std::vector<Node> new_children; + std::vector<Node> new_children; for (unsigned j = 0; j < assertions[i].getNumChildren(); ++j) { if (hasSignature(assertions[i][j])) { new_children.push_back(abstractSignatures(assertions[i][j])); @@ -60,10 +60,10 @@ bool AbstractionModule::applyAbstraction(const std::vector<Node>& assertions, st new_children.push_back(assertions[i][j]); } } - new_assertions.push_back(utils::mkNode(kind::OR, new_children)); + new_assertions.push_back(utils::mkNode(kind::OR, new_children)); } else { // assertions that are not changed - new_assertions.push_back(assertions[i]); + new_assertions.push_back(assertions[i]); } } @@ -71,21 +71,21 @@ bool AbstractionModule::applyAbstraction(const std::vector<Node>& assertions, st skolemizeArguments(new_assertions); } - + // if we are using the eager solver reverse the abstraction if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { if (d_funcToSignature.size() == 0) { // we did not change anything return false; } - NodeNodeMap seen; + NodeNodeMap seen; for (unsigned i = 0; i < new_assertions.size(); ++i) { - new_assertions[i] = reverseAbstraction(new_assertions[i], seen); + new_assertions[i] = reverseAbstraction(new_assertions[i], seen); } // we undo the abstraction functions so the logic is QF_BV still - return true; + return true; } - + // return true if we have created new function symbols for the problem return d_funcToSignature.size() != 0; } @@ -99,7 +99,7 @@ bool AbstractionModule::isConjunctionOfAtoms(TNode node) { bool AbstractionModule::isConjunctionOfAtomsRec(TNode node, TNodeSet& seen) { if (seen.find(node)!= seen.end()) return true; - + if (!node.getType().isBitVector()) { return (node.getKind() == kind::AND || utils::isBVPredicate(node)); } @@ -120,30 +120,30 @@ Node AbstractionModule::reverseAbstraction(Node assertion, NodeNodeMap& seen) { if (seen.find(assertion) != seen.end()) return seen[assertion]; - + if (isAbstraction(assertion)) { Node interp = getInterpretation(assertion); seen[assertion] = interp; - Assert (interp.getType() == assertion.getType()); + Assert (interp.getType() == assertion.getType()); return interp; } if (assertion.getNumChildren() == 0) { seen[assertion] = assertion; - return assertion; + return assertion; } - + NodeBuilder<> result(assertion.getKind()); if (assertion.getMetaKind() == kind::metakind::PARAMETERIZED) { - result << assertion.getOperator(); + result << assertion.getOperator(); } for (unsigned i = 0; i < assertion.getNumChildren(); ++i) { - result << reverseAbstraction(assertion[i], seen); + result << reverseAbstraction(assertion[i], seen); } Node res = result; seen[assertion] = res; - return res; + return res; } void AbstractionModule::skolemizeArguments(std::vector<Node>& assertions) { @@ -151,7 +151,7 @@ void AbstractionModule::skolemizeArguments(std::vector<Node>& assertions) { TNode assertion = assertions[i]; if (assertion.getKind() != kind::OR) continue; - + bool is_skolemizable = true; for (unsigned k = 0; k < assertion.getNumChildren(); ++k) { if (assertion[k].getKind() != kind::EQUAL || @@ -191,54 +191,54 @@ void AbstractionModule::skolemizeArguments(std::vector<Node>& assertions) { NodeBuilder<> skolem_func (kind::APPLY_UF); skolem_func << func; std::vector<Node> skolem_args; - + for (unsigned j = 0; j < args.getArity(); ++j) { bool all_same = true; for (unsigned k = 1; k < args.getNumEntries(); ++k) { if ( args.getEntry(k)[j] != args.getEntry(0)[j]) - all_same = false; + all_same = false; } - Node new_arg = all_same ? (Node)args.getEntry(0)[j] : utils::mkVar(utils::getSize(args.getEntry(0)[j])); + Node new_arg = all_same ? (Node)args.getEntry(0)[j] : utils::mkVar(utils::getSize(args.getEntry(0)[j])); skolem_args.push_back(new_arg); - skolem_func << new_arg; + skolem_func << new_arg; } - - Node skolem_func_eq1 = utils::mkNode(kind::EQUAL, (Node)skolem_func, utils::mkConst(1, 1u)); - + + Node skolem_func_eq1 = utils::mkNode(kind::EQUAL, (Node)skolem_func, utils::mkConst(1, 1u)); + // enumerate arguments assignments - std::vector<Node> or_assignments; + std::vector<Node> or_assignments; for (ArgsTableEntry::iterator it = args.begin(); it != args.end(); ++it) { NodeBuilder<> arg_assignment(kind::AND); ArgsVec& args = *it; for (unsigned k = 0; k < args.size(); ++k) { Node eq = utils::mkNode(kind::EQUAL, args[k], skolem_args[k]); - arg_assignment << eq; + arg_assignment << eq; } or_assignments.push_back(arg_assignment); } - + Node new_func_def = utils::mkNode(kind::AND, skolem_func_eq1, utils::mkNode(kind::OR, or_assignments)); - assertion_builder << new_func_def; + assertion_builder << new_func_def; } Node new_assertion = assertion_builder; Debug("bv-abstraction-dbg") << "AbstractionModule::skolemizeArguments " << assertions[i] << " => \n"; - Debug("bv-abstraction-dbg") << " " << new_assertion; + Debug("bv-abstraction-dbg") << " " << new_assertion; assertions[i] = new_assertion; } } void AbstractionModule::storeSignature(Node signature, TNode assertion) { if(d_signatures.find(signature) == d_signatures.end()) { - d_signatures[signature] = 0; + d_signatures[signature] = 0; } - d_signatures[signature] = d_signatures[signature] + 1; - d_assertionToSignature[assertion] = signature; + d_signatures[signature] = d_signatures[signature] + 1; + d_assertionToSignature[assertion] = signature; } Node AbstractionModule::computeSignature(TNode node) { - resetSignatureIndex(); - NodeNodeMap cache; + resetSignatureIndex(); + NodeNodeMap cache; Node sig = computeSignatureRec(node, cache); return sig; } @@ -247,17 +247,17 @@ Node AbstractionModule::getSignatureSkolem(TNode node) { Assert (node.getKind() == kind::VARIABLE); unsigned bitwidth = utils::getSize(node); if (d_signatureSkolems.find(bitwidth) == d_signatureSkolems.end()) { - d_signatureSkolems[bitwidth] = vector<Node>(); + d_signatureSkolems[bitwidth] = vector<Node>(); } - + vector<Node>& skolems = d_signatureSkolems[bitwidth]; // get the index of bv variables of this size - unsigned index = getBitwidthIndex(bitwidth); + unsigned index = getBitwidthIndex(bitwidth); Assert (skolems.size() + 1 >= index ); if (skolems.size() == index) { ostringstream os; os << "sig_" <<bitwidth <<"_" << index; - NodeManager* nm = NodeManager::currentNM(); + NodeManager* nm = NodeManager::currentNM(); skolems.push_back(nm->mkSkolem(os.str(), nm->mkBitVectorType(bitwidth), "skolem for computing signatures")); } ++(d_signatureIndices[bitwidth]); @@ -268,12 +268,12 @@ unsigned AbstractionModule::getBitwidthIndex(unsigned bitwidth) { if (d_signatureIndices.find(bitwidth) == d_signatureIndices.end()) { d_signatureIndices[bitwidth] = 0; } - return d_signatureIndices[bitwidth]; + return d_signatureIndices[bitwidth]; } void AbstractionModule::resetSignatureIndex() { for (IndexMap::iterator it = d_signatureIndices.begin(); it != d_signatureIndices.end(); ++it) { - it->second = 0; + it->second = 0; } } @@ -282,24 +282,24 @@ bool AbstractionModule::hasSignature(Node node) { } Node AbstractionModule::getGeneralizedSignature(Node node) { - NodeNodeMap::const_iterator it = d_assertionToSignature.find(node); + NodeNodeMap::const_iterator it = d_assertionToSignature.find(node); Assert (it != d_assertionToSignature.end()); - Node generalized_signature = getGeneralization(it->second); - return generalized_signature; + Node generalized_signature = getGeneralization(it->second); + return generalized_signature; } Node AbstractionModule::computeSignatureRec(TNode node, NodeNodeMap& cache) { if (cache.find(node) != cache.end()) { - return cache.find(node)->second; + return cache.find(node)->second; } - + if (node.getNumChildren() == 0) { if (node.getKind() == kind::CONST_BITVECTOR) return node; Node sig = getSignatureSkolem(node); - cache[node] = sig; - return sig; + cache[node] = sig; + return sig; } NodeBuilder<> builder(node.getKind()); @@ -308,30 +308,30 @@ Node AbstractionModule::computeSignatureRec(TNode node, NodeNodeMap& cache) { } for (unsigned i = 0; i < node.getNumChildren(); ++i) { Node converted = computeSignatureRec(node[i], cache); - builder << converted; + builder << converted; } Node result = builder; cache[node] = result; - return result; + return result; } -/** +/** * Returns 0, if the two are equal, * 1 if s is a generalization of t * 2 if t is a generalization of s * -1 if the two cannot be unified * - * @param s - * @param t - * - * @return + * @param s + * @param t + * + * @return */ int AbstractionModule::comparePatterns(TNode s, TNode t) { if (s.getKind() == kind::SKOLEM && t.getKind() == kind::SKOLEM) { return 0; } - + if (s.getKind() == kind::CONST_BITVECTOR && t.getKind() == kind::CONST_BITVECTOR) { if (s == t) { @@ -350,7 +350,7 @@ int AbstractionModule::comparePatterns(TNode s, TNode t) { t.getKind() == kind::SKOLEM) { return 2; } - + if (s.getNumChildren() != t.getNumChildren() || s.getKind() != t.getKind()) return -1; @@ -370,26 +370,26 @@ int AbstractionModule::comparePatterns(TNode s, TNode t) { } TNode AbstractionModule::getGeneralization(TNode term) { - NodeNodeMap::iterator it = d_sigToGeneralization.find(term); + NodeNodeMap::iterator it = d_sigToGeneralization.find(term); // if not in the map we add it if (it == d_sigToGeneralization.end()) { d_sigToGeneralization[term] = term; - return term; + return term; } - // doesn't have a generalization + // doesn't have a generalization if (it->second == term) return term; - + TNode generalization = getGeneralization(it->second); Assert (generalization != term); d_sigToGeneralization[term] = generalization; - return generalization; + return generalization; } void AbstractionModule::storeGeneralization(TNode s, TNode t) { Assert (s == getGeneralization(s)); Assert (t == getGeneralization(t)); - d_sigToGeneralization[s] = t; + d_sigToGeneralization[s] = t; } void AbstractionModule::finalizeSignatures() { @@ -402,29 +402,29 @@ void AbstractionModule::finalizeSignatures() { for (SignatureMap::const_iterator tt = ss; tt != d_signatures.end(); ++tt) { TNode t = getGeneralization(tt->first); TNode s = getGeneralization(ss->first); - + if (t != s) { int status = comparePatterns(s, t); - Assert (status); + Assert (status); if (status < 0) continue; if (status == 1) { - storeGeneralization(t, s); + storeGeneralization(t, s); } else { - storeGeneralization(s, t); + storeGeneralization(s, t); } } } } // keep only most general signatures for (SignatureMap::iterator it = d_signatures.begin(); it != d_signatures.end(); ) { - TNode sig = it->first; + TNode sig = it->first; TNode gen = getGeneralization(sig); if (sig != gen) { - Assert (d_signatures.find(gen) != d_signatures.end()); + Assert (d_signatures.find(gen) != d_signatures.end()); // update the count d_signatures[gen]+= d_signatures[sig]; - d_signatures.erase(it++); + d_signatures.erase(it++); } else { ++it; } @@ -434,12 +434,12 @@ void AbstractionModule::finalizeSignatures() { // remove signatures that are not frequent enough for (SignatureMap::iterator it = d_signatures.begin(); it != d_signatures.end(); ) { if (it->second <= 7) { - d_signatures.erase(it++); + d_signatures.erase(it++); } else { ++it; } } - + for (SignatureMap::const_iterator it = d_signatures.begin(); it != d_signatures.end(); ++it) { TNode signature = it->first; // we already processed this signature @@ -451,31 +451,31 @@ void AbstractionModule::finalizeSignatures() { collectArgumentTypes(signature, arg_types, seen); Assert (signature.getType().isBoolean()); // make function return a bitvector of size 1 - //Node bv_function = utils::mkNode(kind::ITE, signature, utils::mkConst(1, 1u), utils::mkConst(1, 0u)); + //Node bv_function = utils::mkNode(kind::ITE, signature, utils::mkConst(1, 1u), utils::mkConst(1, 0u)); TypeNode range = NodeManager::currentNM()->mkBitVectorType(1); - + TypeNode abs_type = nm->mkFunctionType(arg_types, range); Node abs_func = nm->mkSkolem("abs_$$", abs_type, "abstraction function for bv theory"); Debug("bv-abstraction") << " abstracted by function " << abs_func << "\n"; // NOTE: signature expression type is BOOLEAN d_signatureToFunc[signature] = abs_func; - d_funcToSignature[abs_func] = signature; + d_funcToSignature[abs_func] = signature; } d_statistics.d_numFunctionsAbstracted.setData(d_signatureToFunc.size()); - + Debug("bv-abstraction") << "AbstractionModule::finalizeSignatures abstracted " << d_signatureToFunc.size() << " signatures. \n"; } void AbstractionModule::collectArgumentTypes(TNode sig, std::vector<TypeNode>& types, TNodeSet& seen) { if (seen.find(sig) != seen.end()) return; - + if (sig.getKind() == kind::SKOLEM) { types.push_back(sig.getType()); - seen.insert(sig); - return; + seen.insert(sig); + return; } for (unsigned i = 0; i < sig.getNumChildren(); ++i) { @@ -487,36 +487,36 @@ void AbstractionModule::collectArgumentTypes(TNode sig, std::vector<TypeNode>& t void AbstractionModule::collectArguments(TNode node, TNode signature, std::vector<Node>& args, TNodeSet& seen) { if (seen.find(node)!= seen.end()) return; - + if (node.getKind() == kind::VARIABLE || node.getKind() == kind::CONST_BITVECTOR) { // a constant in the node can either map to an argument of the abstraction - // or can be hard-coded and part of the abstraction + // or can be hard-coded and part of the abstraction if (signature.getKind() == kind::SKOLEM) { args.push_back(node); seen.insert(node); } else { - Assert (signature.getKind() == kind::CONST_BITVECTOR); + Assert (signature.getKind() == kind::CONST_BITVECTOR); } - // - return; + // + return; } Assert (node.getKind() == signature.getKind() && - node.getNumChildren() == signature.getNumChildren()); + node.getNumChildren() == signature.getNumChildren()); for (unsigned i = 0; i < node.getNumChildren(); ++i) { - collectArguments(node[i], signature[i], args, seen); - seen.insert(node); + collectArguments(node[i], signature[i], args, seen); + seen.insert(node); } } Node AbstractionModule::abstractSignatures(TNode assertion) { - Debug("bv-abstraction") << "AbstractionModule::abstractSignatures "<< assertion <<"\n"; + Debug("bv-abstraction") << "AbstractionModule::abstractSignatures "<< assertion <<"\n"; // assume the assertion has been fully abstracted Node signature = getGeneralizedSignature(assertion); - - Debug("bv-abstraction") << " with sig "<< signature <<"\n"; + + Debug("bv-abstraction") << " with sig "<< signature <<"\n"; NodeNodeMap::iterator it = d_signatureToFunc.find(signature); if (it!= d_signatureToFunc.end()) { std::vector<Node> args; @@ -527,16 +527,16 @@ Node AbstractionModule::abstractSignatures(TNode assertion) { collectArguments(assertion, signature, args, seen); std::vector<TNode> real_args; for (unsigned i = 1; i < args.size(); ++i) { - real_args.push_back(args[i]); + real_args.push_back(args[i]); } - d_argsTable.addEntry(func, real_args); - Node result = utils::mkNode(kind::EQUAL, utils::mkNode(kind::APPLY_UF, args), + d_argsTable.addEntry(func, real_args); + Node result = utils::mkNode(kind::EQUAL, utils::mkNode(kind::APPLY_UF, args), utils::mkConst(1, 1u)); - Debug("bv-abstraction") << "=> "<< result << "\n"; - Assert (result.getType() == assertion.getType()); - return result; + Debug("bv-abstraction") << "=> "<< result << "\n"; + Assert (result.getType() == assertion.getType()); + return result; } - return assertion; + return assertion; } bool AbstractionModule::isAbstraction(TNode node) { @@ -557,11 +557,11 @@ bool AbstractionModule::isAbstraction(TNode node) { if (constant != utils::mkConst(1, 1u)) return false; - TNode func_symbol = func.getOperator(); + TNode func_symbol = func.getOperator(); if (d_funcToSignature.find(func_symbol) == d_funcToSignature.end()) return false; - return true; + return true; } Node AbstractionModule::getInterpretation(TNode node) { @@ -571,51 +571,51 @@ Node AbstractionModule::getInterpretation(TNode node) { Assert (constant.getKind() == kind::CONST_BITVECTOR && apply.getKind() == kind::APPLY_UF); - Node func = apply.getOperator(); + Node func = apply.getOperator(); Assert (d_funcToSignature.find(func) != d_funcToSignature.end()); - + Node sig = d_funcToSignature[func]; - + // substitute arguments in signature TNodeTNodeMap seen; unsigned index = 0; Node result = substituteArguments(sig, apply, index, seen); - Assert (result.getType().isBoolean()); + Assert (result.getType().isBoolean()); Assert (index == apply.getNumChildren()); // Debug("bv-abstraction") << "AbstractionModule::getInterpretation " << node << "\n"; // Debug("bv-abstraction") << " => " << result << "\n"; - return result; + return result; } Node AbstractionModule::substituteArguments(TNode signature, TNode apply, unsigned& index, TNodeTNodeMap& seen) { if (seen.find(signature) != seen.end()) { - return seen[signature]; + return seen[signature]; } - + if (signature.getKind() == kind::SKOLEM) { // return corresponding argument and increment counter seen[signature] = apply[index]; - return apply[index++]; + return apply[index++]; } if (signature.getNumChildren() == 0) { Assert (signature.getKind() != kind::VARIABLE && - signature.getKind() != kind::SKOLEM); + signature.getKind() != kind::SKOLEM); seen[signature] = signature; - return signature; + return signature; } - + NodeBuilder<> builder(signature.getKind()); if (signature.getMetaKind() == kind::metakind::PARAMETERIZED) { builder << signature.getOperator(); } - + for (unsigned i = 0; i < signature.getNumChildren(); ++i) { Node child = substituteArguments(signature[i], apply, index, seen); - builder << child; + builder << child; } - Node result = builder; + Node result = builder; seen[signature]= result; return result; @@ -625,20 +625,20 @@ Node AbstractionModule::simplifyConflict(TNode conflict) { if (Dump.isOn("bv-abstraction")) { NodeNodeMap seen; Node c = reverseAbstraction(conflict, seen); - Dump("bv-abstraction") << PushCommand(); + Dump("bv-abstraction") << PushCommand(); Dump("bv-abstraction") << AssertCommand(c.toExpr()); Dump("bv-abstraction") << CheckSatCommand(); - Dump("bv-abstraction") << PopCommand(); + Dump("bv-abstraction") << PopCommand(); } - Debug("bv-abstraction-dbg") << "AbstractionModule::simplifyConflict " << conflict << "\n"; + Debug("bv-abstraction-dbg") << "AbstractionModule::simplifyConflict " << conflict << "\n"; if (conflict.getKind() != kind::AND) - return conflict; + return conflict; std::vector<Node> conjuncts; for (unsigned i = 0; i < conflict.getNumChildren(); ++i) conjuncts.push_back(conflict[i]); - + theory::SubstitutionMap subst(new context::Context()); for (unsigned i = 0; i < conjuncts.size(); ++i) { TNode conjunct = conjuncts[i]; @@ -658,12 +658,12 @@ Node AbstractionModule::simplifyConflict(TNode conflict) { } else { continue; } - + Assert (!subst.hasSubstitution(s)); Assert (!t.isNull() && !s.isNull() && s!= t); - subst.addSubstitution(s, t); + subst.addSubstitution(s, t); for (unsigned k = 0; k < conjuncts.size(); k++) { conjuncts[k] = subst.apply(conjuncts[k]); @@ -671,28 +671,28 @@ Node AbstractionModule::simplifyConflict(TNode conflict) { } } Node new_conflict = Rewriter::rewrite(utils::mkAnd(conjuncts)); - + Debug("bv-abstraction") << "AbstractionModule::simplifyConflict conflict " << conflict <<"\n"; Debug("bv-abstraction") << " => " << new_conflict <<"\n"; if (Dump.isOn("bv-abstraction")) { - + NodeNodeMap seen; Node nc = reverseAbstraction(new_conflict, seen); - Dump("bv-abstraction") << PushCommand(); + Dump("bv-abstraction") << PushCommand(); Dump("bv-abstraction") << AssertCommand(nc.toExpr()); Dump("bv-abstraction") << CheckSatCommand(); - Dump("bv-abstraction") << PopCommand(); + Dump("bv-abstraction") << PopCommand(); } - - return new_conflict; + + return new_conflict; } void DebugPrintInstantiations(const std::vector< std::vector<ArgsVec> >& instantiations, const std::vector<TNode> functions) { // print header - Debug("bv-abstraction-dbg") <<"[ "; + Debug("bv-abstraction-dbg") <<"[ "; for (unsigned i = 0; i < functions.size(); ++i) { for (unsigned j = 1; j < functions[i].getNumChildren(); ++j) { Debug("bv-abstraction-dgb") << functions[i][j] <<" "; @@ -706,16 +706,16 @@ void DebugPrintInstantiations(const std::vector< std::vector<ArgsVec> >& instant const std::vector<ArgsVec>& inst = instantiations[i]; for (unsigned j = 0; j < inst.size(); ++j) { for (unsigned k = 0; k < inst[j].size(); ++k) { - Debug("bv-abstraction-dbg") << inst[j][k] << " "; + Debug("bv-abstraction-dbg") << inst[j][k] << " "; } - Debug("bv-abstraction-dbg") << " || "; + Debug("bv-abstraction-dbg") << " || "; } Debug("bv-abstraction-dbg") <<"]\n"; } } void AbstractionModule::generalizeConflict(TNode conflict, std::vector<Node>& lemmas) { - Debug("bv-abstraction") << "AbstractionModule::generalizeConflict " << conflict << "\n"; + Debug("bv-abstraction") << "AbstractionModule::generalizeConflict " << conflict << "\n"; std::vector<TNode> functions; // collect abstract functions @@ -737,11 +737,11 @@ void AbstractionModule::generalizeConflict(TNode conflict, std::vector<Node>& le // if (functions.size() >= 3) { // // dump conflict // NodeNodeMap seen; - // Node reversed = reverseAbstraction(conflict, seen); - // std::cout << "CONFLICT " << reversed << "\n"; + // Node reversed = reverseAbstraction(conflict, seen); + // std::cout << "CONFLICT " << reversed << "\n"; // } - + if (functions.size() == 0 || functions.size() > options::bvNumFunc()) { return; } @@ -751,31 +751,31 @@ void AbstractionModule::generalizeConflict(TNode conflict, std::vector<Node>& le SubstitutionMap skolem_subst(new context::Context()); SubstitutionMap reverse_skolem(new context::Context()); makeFreshSkolems(conflict, skolem_subst, reverse_skolem); - + Node skolemized_conflict = skolem_subst.apply(conflict); for (unsigned i = 0; i < functions.size(); ++i) { functions[i] = skolem_subst.apply(functions[i]); } - conflict = skolem_subst.apply(conflict); + conflict = skolem_subst.apply(conflict); LemmaInstantiatior inst(functions, d_argsTable, conflict); std::vector<Node> new_lemmas; - inst.generateInstantiations(new_lemmas); + inst.generateInstantiations(new_lemmas); for (unsigned i = 0; i < new_lemmas.size(); ++i) { TNode lemma = reverse_skolem.apply(new_lemmas[i]); if (d_addedLemmas.find(lemma) == d_addedLemmas.end()) { lemmas.push_back(lemma); - Debug("bv-abstraction-gen") << "adding lemma " << lemma << "\n"; + Debug("bv-abstraction-gen") << "adding lemma " << lemma << "\n"; storeLemma(lemma); if (Dump.isOn("bv-abstraction")) { NodeNodeMap seen; Node l = reverseAbstraction(lemma, seen); - Dump("bv-abstraction") << PushCommand(); + Dump("bv-abstraction") << PushCommand(); Dump("bv-abstraction") << AssertCommand(l.toExpr()); Dump("bv-abstraction") << CheckSatCommand(); - Dump("bv-abstraction") << PopCommand(); + Dump("bv-abstraction") << PopCommand(); } } } @@ -787,18 +787,18 @@ int AbstractionModule::LemmaInstantiatior::next(int val, int index) { return -1; } -/** +/** * Assumes the stack without top is consistent, and checks that the * full stack is consistent - * - * @param stack - * - * @return + * + * @param stack + * + * @return */ bool AbstractionModule::LemmaInstantiatior::isConsistent(const vector<int>& stack) { if (stack.empty()) return true; - + unsigned current = stack.size() - 1; TNode func = d_functions[current]; ArgsTableEntry& matches = d_argsTable.getEntry(func.getOperator()); @@ -807,12 +807,12 @@ bool AbstractionModule::LemmaInstantiatior::isConsistent(const vector<int>& stac for (unsigned k = 0; k < args.size(); ++k) { TNode s = func[k]; TNode t = args[k]; - + TNode s0 = s; while (d_subst.hasSubstitution(s0)) { s0 = d_subst.getSubstitution(s0); } - + TNode t0 = t; while (d_subst.hasSubstitution(t0)) { t0 = d_subst.getSubstitution(t0); @@ -824,7 +824,7 @@ bool AbstractionModule::LemmaInstantiatior::isConsistent(const vector<int>& stac else continue; } - + if(s0.getMetaKind() == kind::metakind::VARIABLE && t0.isConst()) { d_subst.addSubstitution(s0, t0); @@ -839,12 +839,12 @@ bool AbstractionModule::LemmaInstantiatior::isConsistent(const vector<int>& stac Assert (s0.getMetaKind() == kind::metakind::VARIABLE && t0.getMetaKind() == kind::metakind::VARIABLE); - + if (s0 != t0) { d_subst.addSubstitution(s0, t0); } } - return true; + return true; } bool AbstractionModule::LemmaInstantiatior::accept(const vector<int>& stack) { @@ -854,13 +854,13 @@ bool AbstractionModule::LemmaInstantiatior::accept(const vector<int>& stack) { void AbstractionModule::LemmaInstantiatior::mkLemma() { Node lemma = d_subst.apply(d_conflict); // Debug("bv-abstraction-gen") << "AbstractionModule::LemmaInstantiatior::mkLemma " << lemma <<"\n"; - d_lemmas.push_back(lemma); + d_lemmas.push_back(lemma); } void AbstractionModule::LemmaInstantiatior::backtrack(vector<int>& stack) { if (!isConsistent(stack)) return; - + if (accept(stack)) { mkLemma(); return; @@ -871,7 +871,7 @@ void AbstractionModule::LemmaInstantiatior::backtrack(vector<int>& stack) { d_ctx->push(); stack.push_back(x); backtrack(stack); - + d_ctx->pop(); stack.pop_back(); x = next(x, stack.size()); @@ -880,13 +880,13 @@ void AbstractionModule::LemmaInstantiatior::backtrack(vector<int>& stack) { void AbstractionModule::LemmaInstantiatior::generateInstantiations(std::vector<Node>& lemmas) { - Debug("bv-abstraction-gen") << "AbstractionModule::LemmaInstantiatior::generateInstantiations "; + Debug("bv-abstraction-gen") << "AbstractionModule::LemmaInstantiatior::generateInstantiations "; std::vector<int> stack; backtrack(stack); - Assert (d_ctx->getLevel() == 0); - Debug("bv-abstraction-gen") << "numLemmas=" << d_lemmas.size() <<"\n"; - lemmas.swap(d_lemmas); + Assert (d_ctx->getLevel() == 0); + Debug("bv-abstraction-gen") << "numLemmas=" << d_lemmas.size() <<"\n"; + lemmas.swap(d_lemmas); } void AbstractionModule::makeFreshSkolems(TNode node, SubstitutionMap& map, SubstitutionMap& reverse_map) { @@ -910,7 +910,7 @@ void AbstractionModule::makeFreshSkolems(TNode node, SubstitutionMap& map, Subst void AbstractionModule::makeFreshArgs(TNode func, std::vector<Node>& fresh_args) { Assert (fresh_args.size() == 0); Assert (func.getKind() == kind::APPLY_UF); - TNodeNodeMap d_map; + TNodeNodeMap d_map; for (unsigned i = 0; i < func.getNumChildren(); ++i) { TNode arg = func[i]; if (arg.isConst()) { @@ -918,9 +918,9 @@ void AbstractionModule::makeFreshArgs(TNode func, std::vector<Node>& fresh_args) continue; } Assert (arg.getMetaKind() == kind::metakind::VARIABLE); - TNodeNodeMap::iterator it = d_map.find(arg); + TNodeNodeMap::iterator it = d_map.find(arg); if (it != d_map.end()) { - fresh_args.push_back(it->second); + fresh_args.push_back(it->second); } else { Node skolem = utils::mkVar(utils::getSize(arg)); d_map[arg] = skolem; @@ -947,7 +947,7 @@ Node AbstractionModule::tryMatching(const std::vector<Node>& ss, const std::vect Debug("bv-abstraction-dbg") <<"\n"; } - + SubstitutionMap subst(new context::Context()); for (unsigned i = 0; i < ss.size(); ++i) { @@ -982,10 +982,10 @@ Node AbstractionModule::tryMatching(const std::vector<Node>& ss, const std::vect Assert (s0 != t0); subst.addSubstitution(s0, t0); } - + Node res = subst.apply(conflict); - Debug("bv-abstraction-dbg") << " Lemma: " << res <<"\n"; - return res; + Debug("bv-abstraction-dbg") << " Lemma: " << res <<"\n"; + return res; } void AbstractionModule::storeLemma(TNode lemma) { @@ -996,7 +996,7 @@ void AbstractionModule::storeLemma(TNode lemma) { atom = atom.getKind() == kind::NOT ? atom[0] : atom; Assert (atom.getKind() != kind::NOT); Assert (utils::isBVPredicate(atom)); - d_lemmaAtoms.insert(atom); + d_lemmaAtoms.insert(atom); } } else { lemma = lemma.getKind() == kind::NOT? lemma[0] : lemma; @@ -1009,9 +1009,9 @@ void AbstractionModule::storeLemma(TNode lemma) { bool AbstractionModule::isLemmaAtom(TNode node) const { Assert (node.getType().isBoolean()); node = node.getKind() == kind::NOT? node[0] : node; - + return d_inputAtoms.find(node) == d_inputAtoms.end() && - d_lemmaAtoms.find(node) != d_lemmaAtoms.end(); + d_lemmaAtoms.find(node) != d_lemmaAtoms.end(); } void AbstractionModule::addInputAtom(TNode atom) { @@ -1022,31 +1022,31 @@ void AbstractionModule::addInputAtom(TNode atom) { void AbstractionModule::ArgsTableEntry::addArguments(const ArgsVec& args) { Assert (args.size() == d_arity); - d_data.push_back(args); + d_data.push_back(args); } void AbstractionModule::ArgsTable::addEntry(TNode signature, const ArgsVec& args) { if (d_data.find(signature) == d_data.end()) { - d_data[signature] = ArgsTableEntry(args.size()); + d_data[signature] = ArgsTableEntry(args.size()); } ArgsTableEntry& entry = d_data[signature]; - entry.addArguments(args); + entry.addArguments(args); } bool AbstractionModule::ArgsTable::hasEntry(TNode signature) const { - return d_data.find(signature) != d_data.end(); + return d_data.find(signature) != d_data.end(); } AbstractionModule::ArgsTableEntry& AbstractionModule::ArgsTable::getEntry(TNode signature) { Assert (hasEntry(signature)); - return d_data.find(signature)->second; + return d_data.find(signature)->second; } -AbstractionModule::Statistics::Statistics() - : d_numFunctionsAbstracted("theory::bv::AbstractioModule::NumFunctionsAbstracted", 0) - , d_numArgsSkolemized("theory::bv::AbstractioModule::NumArgsSkolemized", 0) - , d_abstractionTime("theory::bv::AbstractioModule::AbstractionTime") +AbstractionModule::Statistics::Statistics(const std::string& name) + : d_numFunctionsAbstracted(name + "theory::bv::AbstractioModule::NumFunctionsAbstracted", 0) + , d_numArgsSkolemized(name + "theory::bv::AbstractioModule::NumArgsSkolemized", 0) + , d_abstractionTime(name + "theory::bv::AbstractioModule::AbstractionTime") { smtStatisticsRegistry()->registerStat(&d_numFunctionsAbstracted); smtStatisticsRegistry()->registerStat(&d_numArgsSkolemized); diff --git a/src/theory/bv/abstraction.h b/src/theory/bv/abstraction.h index 5d580f6ce..5d48b926e 100644 --- a/src/theory/bv/abstraction.h +++ b/src/theory/bv/abstraction.h @@ -38,11 +38,11 @@ class AbstractionModule { IntStat d_numFunctionsAbstracted; IntStat d_numArgsSkolemized; TimerStat d_abstractionTime; - Statistics(); + Statistics(const std::string& name); ~Statistics(); }; - + class ArgsTableEntry { std::vector<ArgsVec> d_data; unsigned d_arity; @@ -61,11 +61,11 @@ class AbstractionModule { unsigned getArity() { return d_arity; } unsigned getNumEntries() { return d_data.size(); } ArgsVec& getEntry(unsigned i ) { Assert (i < d_data.size()); return d_data[i]; } - }; + }; class ArgsTable { __gnu_cxx::hash_map<TNode, ArgsTableEntry, TNodeHashFunction > d_data; - bool hasEntry(TNode signature) const; + bool hasEntry(TNode signature) const; public: typedef __gnu_cxx::hash_map<TNode, ArgsTableEntry, TNodeHashFunction >::iterator iterator; ArgsTable() {} @@ -75,12 +75,12 @@ class AbstractionModule { iterator end() { return d_data.end(); } }; - /** + /** * Checks if one pattern is a generalization of the other - * - * @param s - * @param t - * + * + * @param s + * @param t + * * @return 1 if s :> t, 2 if s <: t, 0 if they equivalent and -1 if they are incomparable */ static int comparePatterns(TNode s, TNode t); @@ -93,7 +93,7 @@ class AbstractionModule { theory::SubstitutionMap d_subst; TNode d_conflict; std::vector<Node> d_lemmas; - + void backtrack(std::vector<int>& stack); int next(int val, int index); bool isConsistent(const std::vector<int>& stack); @@ -108,7 +108,7 @@ class AbstractionModule { , d_conflict(conflict) , d_lemmas() { - Debug("bv-abstraction-gen") << "LemmaInstantiator conflict:" << conflict << "\n"; + Debug("bv-abstraction-gen") << "LemmaInstantiator conflict:" << conflict << "\n"; // initializing the search space for (unsigned i = 0; i < functions.size(); ++i) { TNode func_op = functions[i].getOperator(); @@ -118,31 +118,31 @@ class AbstractionModule { } } - void generateInstantiations(std::vector<Node>& lemmas); - + void generateInstantiations(std::vector<Node>& lemmas); + }; - + typedef __gnu_cxx::hash_map<Node, std::vector<Node>, NodeHashFunction> NodeVecMap; typedef __gnu_cxx::hash_map<Node, TNode, NodeHashFunction> NodeTNodeMap; typedef __gnu_cxx::hash_map<TNode, TNode, TNodeHashFunction> TNodeTNodeMap; typedef __gnu_cxx::hash_map<Node, Node, NodeHashFunction> NodeNodeMap; typedef __gnu_cxx::hash_map<Node, TNode, NodeHashFunction> TNodeNodeMap; typedef __gnu_cxx::hash_set<TNode, TNodeHashFunction> TNodeSet; - typedef __gnu_cxx::hash_map<unsigned, Node> IntNodeMap; + typedef __gnu_cxx::hash_map<unsigned, Node> IntNodeMap; typedef __gnu_cxx::hash_map<unsigned, unsigned> IndexMap; typedef __gnu_cxx::hash_map<unsigned, std::vector<Node> > SkolemMap; typedef __gnu_cxx::hash_map<TNode, unsigned, TNodeHashFunction > SignatureMap; - - ArgsTable d_argsTable; + + ArgsTable d_argsTable; // mapping between signature and uninterpreted function symbol used to // abstract the signature NodeNodeMap d_signatureToFunc; - NodeNodeMap d_funcToSignature; + NodeNodeMap d_funcToSignature; - NodeNodeMap d_assertionToSignature; + NodeNodeMap d_assertionToSignature; SignatureMap d_signatures; - NodeNodeMap d_sigToGeneralization; + NodeNodeMap d_sigToGeneralization; TNodeSet d_skolems; // skolems maps @@ -165,7 +165,7 @@ class AbstractionModule { Node getGeneralizedSignature(Node node); Node getSignatureSkolem(TNode node); - unsigned getBitwidthIndex(unsigned bitwidth); + unsigned getBitwidthIndex(unsigned bitwidth); void resetSignatureIndex(); Node computeSignatureRec(TNode, NodeNodeMap&); void storeSignature(Node signature, TNode assertion); @@ -175,14 +175,14 @@ class AbstractionModule { // crazy instantiation methods void generateInstantiations(unsigned current, - std::vector<ArgsTableEntry>& matches, + std::vector<ArgsTableEntry>& matches, std::vector<std::vector<ArgsVec> >& instantiations, std::vector<std::vector<ArgsVec> >& new_instantiations); Node tryMatching(const std::vector<Node>& ss, const std::vector<TNode>& tt, TNode conflict); void makeFreshArgs(TNode func, std::vector<Node>& fresh_args); void makeFreshSkolems(TNode node, SubstitutionMap& map, SubstitutionMap& reverse_map); - + void skolemizeArguments(std::vector<Node>& assertions); Node reverseAbstraction(Node assertion, NodeNodeMap& seen); @@ -192,9 +192,9 @@ class AbstractionModule { void storeLemma(TNode lemma); Statistics d_statistics; - + public: - AbstractionModule() + AbstractionModule(const std::string& name) : d_argsTable() , d_signatureToFunc() , d_funcToSignature() @@ -207,34 +207,34 @@ public: , d_addedLemmas() , d_lemmaAtoms() , d_inputAtoms() - , d_statistics() + , d_statistics(name) {} - /** + /** * returns true if there are new uninterepreted functions symbols in the output - * - * @param assertions - * @param new_assertions - * - * @return + * + * @param assertions + * @param new_assertions + * + * @return */ bool applyAbstraction(const std::vector<Node>& assertions, std::vector<Node>& new_assertions); - /** - * Returns true if the node represents an abstraction predicate. - * - * @param node - * - * @return + /** + * Returns true if the node represents an abstraction predicate. + * + * @param node + * + * @return */ bool isAbstraction(TNode node); - /** - * Returns the interpretation of the abstraction predicate. - * - * @param node - * - * @return + /** + * Returns the interpretation of the abstraction predicate. + * + * @param node + * + * @return */ Node getInterpretation(TNode node); - Node simplifyConflict(TNode conflict); + Node simplifyConflict(TNode conflict); void generalizeConflict(TNode conflict, std::vector<Node>& lemmas); void addInputAtom(TNode atom); bool isLemmaAtom(TNode node) const; diff --git a/src/theory/bv/aig_bitblaster.cpp b/src/theory/bv/aig_bitblaster.cpp index 887daa1bd..37e9f4476 100644 --- a/src/theory/bv/aig_bitblaster.cpp +++ b/src/theory/bv/aig_bitblaster.cpp @@ -19,7 +19,7 @@ #include "options/bv_options.h" #include "prop/cnf_stream.h" #include "prop/sat_solver_factory.h" - +#include "smt/smt_statistics_registry.h" #ifdef CVC4_USE_ABC // Function is defined as static in ABC. Not sure how else to do this. @@ -140,10 +140,24 @@ AigBitblaster::AigBitblaster() , d_bbAtoms() , d_aigOutputNode(NULL) { - d_nullContext = new context::Context(); - d_satSolver = prop::SatSolverFactory::createMinisat(d_nullContext, "AigBitblaster"); - MinisatEmptyNotify* notify = new MinisatEmptyNotify(); - d_satSolver->setNotify(notify); + d_nullContext = new context::Context(); + switch(options::bvSatSolver()) { + case SAT_SOLVER_MINISAT: { + prop::BVSatSolverInterface* minisat = prop::SatSolverFactory::createMinisat(d_nullContext, + smtStatisticsRegistry(), + "AigBitblaster"); + MinisatEmptyNotify* notify = new MinisatEmptyNotify(); + minisat->setNotify(notify); + d_satSolver = minisat; + break; + } + case SAT_SOLVER_CRYPTOMINISAT: + d_satSolver = prop::SatSolverFactory::createCryptoMinisat(smtStatisticsRegistry(), + "AigBitblaster"); + break; + default: + Unreachable("Unknown SAT solver type"); + } } AigBitblaster::~AigBitblaster() { @@ -402,7 +416,7 @@ void AigBitblaster::assertToSatSolver(Cnf_Dat_t* pCnf) { prop::SatLiteral lit(sat_variables[index-1], int_lit < 0); clause.push_back(lit); } - d_satSolver->addClause(clause, false, RULE_INVALID); + d_satSolver->addClause(clause, false); } } diff --git a/src/theory/bv/bitblaster_template.h b/src/theory/bv/bitblaster_template.h index cfbadbf32..c6c0d6def 100644 --- a/src/theory/bv/bitblaster_template.h +++ b/src/theory/bv/bitblaster_template.h @@ -257,7 +257,7 @@ public: class EagerBitblaster : public TBitblaster<Node> { typedef __gnu_cxx::hash_set<TNode, TNodeHashFunction> TNodeSet; // sat solver used for bitblasting and associated CnfStream - prop::BVSatSolverInterface* d_satSolver; + prop::SatSolver* d_satSolver; BitblastingRegistrar* d_bitblastingRegistrar; context::Context* d_nullContext; prop::CnfStream* d_cnfStream; @@ -268,6 +268,8 @@ class EagerBitblaster : public TBitblaster<Node> { MinisatEmptyNotify d_notify; + MinisatEmptyNotify d_notify; + Node getModelFromSatSolver(TNode a, bool fullModel); bool isSharedTerm(TNode node); @@ -306,7 +308,7 @@ class AigBitblaster : public TBitblaster<Abc_Obj_t*> { static Abc_Ntk_t* abcAigNetwork; context::Context* d_nullContext; - prop::BVSatSolverInterface* d_satSolver; + prop::SatSolver* d_satSolver; TNodeAigMap d_aigCache; NodeAigMap d_bbAtoms; @@ -459,7 +461,7 @@ Node TBitblaster<T>::getTermModel(TNode node, bool fullModel) { if (Theory::isLeafOf(node, theory::THEORY_BV)) { // if it is a leaf may ask for fullModel - value = getModelFromSatSolver(node, fullModel); + value = getModelFromSatSolver(node, true); Debug("bv-equality-status")<< "TLazyBitblaster::getTermModel from VarValue" << node <<" => " << value <<"\n"; Assert ((fullModel && !value.isNull() && value.isConst()) || !fullModel); if (!value.isNull()) { diff --git a/src/theory/bv/bv_subtheory_algebraic.cpp b/src/theory/bv/bv_subtheory_algebraic.cpp index 00d337395..b7e973928 100644 --- a/src/theory/bv/bv_subtheory_algebraic.cpp +++ b/src/theory/bv/bv_subtheory_algebraic.cpp @@ -714,7 +714,8 @@ void AlgebraicSolver::collectModelInfo(TheoryModel* model, bool fullModel) { Assert (!value.isNull() || !fullModel); // may be a shared term that did not appear in the current assertions - if (!value.isNull()) { + // AJR: need to check whether already in map for cases where collectModelInfo is called multiple times in the same context + if (!value.isNull() && !d_modelMap->hasSubstitution(var)) { Debug("bitvector-model") << " " << var << " => " << value << "\n"; Assert (value.getKind() == kind::CONST_BITVECTOR); d_modelMap->addSubstitution(var, value); diff --git a/src/theory/bv/bv_subtheory_bitblast.cpp b/src/theory/bv/bv_subtheory_bitblast.cpp index b7619c4bb..6c6c13ee8 100644 --- a/src/theory/bv/bv_subtheory_bitblast.cpp +++ b/src/theory/bv/bv_subtheory_bitblast.cpp @@ -37,9 +37,9 @@ namespace bv { BitblastSolver::BitblastSolver(context::Context* c, TheoryBV* bv) : SubtheorySolver(c, bv), - d_bitblaster(new TLazyBitblaster(c, bv, "lazy")), + d_bitblaster(new TLazyBitblaster(c, bv, bv->getFullInstanceName() + "lazy")), d_bitblastQueue(c), - d_statistics(), + d_statistics(bv->getFullInstanceName()), d_validModelCache(c, true), d_lemmaAtomsQueue(c), d_useSatPropagation(options::bitvectorPropagate()), @@ -55,9 +55,9 @@ BitblastSolver::~BitblastSolver() { delete d_bitblaster; } -BitblastSolver::Statistics::Statistics() - : d_numCallstoCheck("theory::bv::BitblastSolver::NumCallsToCheck", 0) - , d_numBBLemmas("theory::bv::BitblastSolver::NumTimesLemmasBB", 0) +BitblastSolver::Statistics::Statistics(const std::string &instanceName) + : d_numCallstoCheck(instanceName + "theory::bv::BitblastSolver::NumCallsToCheck", 0) + , d_numBBLemmas(instanceName + "theory::bv::BitblastSolver::NumTimesLemmasBB", 0) { smtStatisticsRegistry()->registerStat(&d_numCallstoCheck); smtStatisticsRegistry()->registerStat(&d_numBBLemmas); @@ -68,8 +68,8 @@ BitblastSolver::Statistics::~Statistics() { } void BitblastSolver::setAbstraction(AbstractionModule* abs) { - d_abstractionModule = abs; - d_bitblaster->setAbstraction(abs); + d_abstractionModule = abs; + d_bitblaster->setAbstraction(abs); } void BitblastSolver::preRegister(TNode node) { @@ -117,7 +117,7 @@ void BitblastSolver::bitblastQueue() { bool BitblastSolver::check(Theory::Effort e) { Debug("bv-bitblast") << "BitblastSolver::check (" << e << ")\n"; - Assert(options::bitblastMode() == theory::bv::BITBLAST_MODE_LAZY); + Assert(options::bitblastMode() == theory::bv::BITBLAST_MODE_LAZY); ++(d_statistics.d_numCallstoCheck); @@ -135,10 +135,10 @@ bool BitblastSolver::check(Theory::Effort e) { // skip atoms that are the result of abstraction lemmas if (d_abstractionModule->isLemmaAtom(fact)) { d_lemmaAtomsQueue.push_back(fact); - continue; + continue; } } - + if (!d_bv->inConflict() && (!d_bv->wasPropagatedBySubtheory(fact) || d_bv->getPropagatingSubtheory(fact) != SUB_BITBLAST)) { // Some atoms have not been bit-blasted yet @@ -183,7 +183,7 @@ bool BitblastSolver::check(Theory::Effort e) { if (options::bvAbstraction() && e == Theory::EFFORT_FULL && d_lemmaAtomsQueue.size()) { - + // bit-blast lemma atoms while(!d_lemmaAtomsQueue.empty()) { TNode lemma_atom = d_lemmaAtomsQueue.front(); @@ -199,7 +199,7 @@ bool BitblastSolver::check(Theory::Effort e) { return false; } } - + Assert(!d_bv->inConflict()); bool ok = d_bitblaster->solve(); if (!ok) { @@ -210,9 +210,9 @@ bool BitblastSolver::check(Theory::Effort e) { ++(d_statistics.d_numBBLemmas); return false; } - + } - + return true; } @@ -228,9 +228,9 @@ void BitblastSolver::collectModelInfo(TheoryModel* m, bool fullModel) { Node BitblastSolver::getModelValue(TNode node) { if (d_bv->d_invalidateModelCache.get()) { - d_bitblaster->invalidateModelCache(); + d_bitblaster->invalidateModelCache(); } - d_bv->d_invalidateModelCache.set(false); + d_bv->d_invalidateModelCache.set(false); Node val = d_bitblaster->getTermModel(node, true); return val; } @@ -241,9 +241,9 @@ void BitblastSolver::setConflict(TNode conflict) { Node final_conflict = conflict; if (options::bitvectorQuickXplain() && conflict.getKind() == kind::AND) { - // std::cout << "Original conflict " << conflict.getNumChildren() << "\n"; + // std::cout << "Original conflict " << conflict.getNumChildren() << "\n"; final_conflict = d_quickXplain->minimizeConflict(conflict); - //std::cout << "Minimized conflict " << final_conflict.getNumChildren() << "\n"; + //std::cout << "Minimized conflict " << final_conflict.getNumChildren() << "\n"; } d_bv->setConflict(final_conflict); } @@ -256,4 +256,3 @@ void BitblastSolver::setProofLog( BitVectorProof * bvp ) { }/* namespace CVC4::theory::bv */ }/* namespace CVC4::theory */ }/* namespace CVC4 */ - diff --git a/src/theory/bv/bv_subtheory_bitblast.h b/src/theory/bv/bv_subtheory_bitblast.h index e9300138b..fe16d2702 100644 --- a/src/theory/bv/bv_subtheory_bitblast.h +++ b/src/theory/bv/bv_subtheory_bitblast.h @@ -37,7 +37,7 @@ class BitblastSolver : public SubtheorySolver { struct Statistics { IntStat d_numCallstoCheck; IntStat d_numBBLemmas; - Statistics(); + Statistics(const std::string &instanceName); ~Statistics(); }; /** Bitblaster */ @@ -71,8 +71,8 @@ public: Node getModelValue(TNode node); bool isComplete() { return true; } void bitblastQueue(); - void setAbstraction(AbstractionModule* module); - uint64_t computeAtomWeight(TNode atom); + void setAbstraction(AbstractionModule* module); + uint64_t computeAtomWeight(TNode atom); void setProofLog( BitVectorProof * bvp ); }; diff --git a/src/theory/bv/eager_bitblaster.cpp b/src/theory/bv/eager_bitblaster.cpp index 3b54e3794..53fb4f94b 100644 --- a/src/theory/bv/eager_bitblaster.cpp +++ b/src/theory/bv/eager_bitblaster.cpp @@ -47,15 +47,30 @@ EagerBitblaster::EagerBitblaster(TheoryBV* theory_bv) { d_bitblastingRegistrar = new BitblastingRegistrar(this); d_nullContext = new context::Context(); - - d_satSolver = prop::SatSolverFactory::createMinisat( - d_nullContext, smtStatisticsRegistry(), "EagerBitblaster"); + + switch(options::bvSatSolver()) { + case SAT_SOLVER_MINISAT: { + prop::BVSatSolverInterface* minisat = + prop::SatSolverFactory::createMinisat(d_nullContext, + smtStatisticsRegistry(), + "EagerBitblaster"); + MinisatEmptyNotify* notify = new MinisatEmptyNotify(); + minisat->setNotify(notify); + d_satSolver = minisat; + break; + } + case SAT_SOLVER_CRYPTOMINISAT: + d_satSolver = prop::SatSolverFactory::createCryptoMinisat(smtStatisticsRegistry(), + "EagerBitblaster"); + break; + default: + Unreachable("Unknown SAT solver type"); + } d_cnfStream = new prop::TseitinCnfStream( d_satSolver, d_bitblastingRegistrar, d_nullContext, options::proof(), "EagerBitblaster"); - d_satSolver->setNotify(&d_notify); d_bvp = NULL; } @@ -216,7 +231,7 @@ void EagerBitblaster::collectModelInfo(TheoryModel* m, bool fullModel) { // only shared terms could not have been bit-blasted Assert (hasBBTerm(var) || isSharedTerm(var)); - Node const_value = getModelFromSatSolver(var, fullModel); + Node const_value = getModelFromSatSolver(var, true); if(const_value != Node()) { Debug("bitvector-model") << "EagerBitblaster::collectModelInfo (assert (= " diff --git a/src/theory/bv/lazy_bitblaster.cpp b/src/theory/bv/lazy_bitblaster.cpp index c821b50cd..b549c329a 100644 --- a/src/theory/bv/lazy_bitblaster.cpp +++ b/src/theory/bv/lazy_bitblaster.cpp @@ -491,7 +491,7 @@ void TLazyBitblaster::collectModelInfo(TheoryModel* m, bool fullModel) { // only shared terms could not have been bit-blasted Assert (hasBBTerm(var) || isSharedTerm(var)); - Node const_value = getModelFromSatSolver(var, fullModel); + Node const_value = getModelFromSatSolver(var, true); Assert (const_value.isNull() || const_value.isConst()); if(const_value != Node()) { Debug("bitvector-model") << "TLazyBitblaster::collectModelInfo (assert (= " diff --git a/src/theory/bv/theory_bv.cpp b/src/theory/bv/theory_bv.cpp index 2edadce72..fec93e033 100644 --- a/src/theory/bv/theory_bv.cpp +++ b/src/theory/bv/theory_bv.cpp @@ -44,25 +44,29 @@ namespace bv { TheoryBV::TheoryBV(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, - const LogicInfo& logicInfo) - : Theory(THEORY_BV, c, u, out, valuation, logicInfo), - d_context(c), - d_alreadyPropagatedSet(c), - d_sharedTermsSet(c), - d_subtheories(), - d_subtheoryMap(), - d_statistics(), - d_staticLearnCache(), - d_lemmasAdded(c, false), - d_conflict(c, false), - d_invalidateModelCache(c, true), - d_literalsToPropagate(c), - d_literalsToPropagateIndex(c, 0), - d_propagatedBy(c), - d_eagerSolver(NULL), - d_abstractionModule(new AbstractionModule()), - d_isCoreTheory(false), - d_calledPreregister(false) + const LogicInfo& logicInfo, std::string name) + : Theory(THEORY_BV, c, u, out, valuation, logicInfo, name), + d_context(c), + d_alreadyPropagatedSet(c), + d_sharedTermsSet(c), + d_subtheories(), + d_subtheoryMap(), + d_statistics(getFullInstanceName()), + d_staticLearnCache(), + d_BVDivByZero(), + d_BVRemByZero(), + d_funcToArgs(), + d_funcToSkolem(u), + d_lemmasAdded(c, false), + d_conflict(c, false), + d_invalidateModelCache(c, true), + d_literalsToPropagate(c), + d_literalsToPropagateIndex(c, 0), + d_propagatedBy(c), + d_eagerSolver(NULL), + d_abstractionModule(new AbstractionModule(getFullInstanceName())), + d_isCoreTheory(false), + d_calledPreregister(false) { if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { @@ -117,14 +121,14 @@ void TheoryBV::spendResource(unsigned ammount) throw(UnsafeInterruptException) { getOutputChannel().spendResource(ammount); } -TheoryBV::Statistics::Statistics(): - d_avgConflictSize("theory::bv::AvgBVConflictSize"), - d_solveSubstitutions("theory::bv::NumberOfSolveSubstitutions", 0), - d_solveTimer("theory::bv::solveTimer"), - d_numCallsToCheckFullEffort("theory::bv::NumberOfFullCheckCalls", 0), - d_numCallsToCheckStandardEffort("theory::bv::NumberOfStandardCheckCalls", 0), - d_weightComputationTimer("theory::bv::weightComputationTimer"), - d_numMultSlice("theory::bv::NumMultSliceApplied", 0) +TheoryBV::Statistics::Statistics(const std::string &name): + d_avgConflictSize(name + "theory::bv::AvgBVConflictSize"), + d_solveSubstitutions(name + "theory::bv::NumberOfSolveSubstitutions", 0), + d_solveTimer(name + "theory::bv::solveTimer"), + d_numCallsToCheckFullEffort(name + "theory::bv::NumberOfFullCheckCalls", 0), + d_numCallsToCheckStandardEffort(name + "theory::bv::NumberOfStandardCheckCalls", 0), + d_weightComputationTimer(name + "theory::bv::weightComputationTimer"), + d_numMultSlice(name + "theory::bv::NumMultSliceApplied", 0) { smtStatisticsRegistry()->registerStat(&d_avgConflictSize); smtStatisticsRegistry()->registerStat(&d_solveSubstitutions); @@ -175,30 +179,31 @@ Node TheoryBV::getBVDivByZero(Kind k, unsigned width) { } -void TheoryBV::collectNumerators(TNode term, TNodeSet& seen) { +void TheoryBV::collectFunctionSymbols(TNode term, TNodeSet& seen) { if (seen.find(term) != seen.end()) return; - if (term.getKind() == kind::BITVECTOR_ACKERMANIZE_UDIV) { - unsigned size = utils::getSize(term[0]); - if (d_BVDivByZeroAckerman.find(size) == d_BVDivByZeroAckerman.end()) { - d_BVDivByZeroAckerman[size] = TNodeSet(); - } - d_BVDivByZeroAckerman[size].insert(term[0]); - seen.insert(term); - } else if (term.getKind() == kind::BITVECTOR_ACKERMANIZE_UREM) { - unsigned size = utils::getSize(term[0]); - if (d_BVRemByZeroAckerman.find(size) == d_BVRemByZeroAckerman.end()) { - d_BVRemByZeroAckerman[size] = TNodeSet(); - } - d_BVRemByZeroAckerman[size].insert(term[0]); - seen.insert(term); + if (term.getKind() == kind::APPLY_UF) { + TNode func = term.getOperator(); + storeFunction(func, term); } for (unsigned i = 0; i < term.getNumChildren(); ++i) { - collectNumerators(term[i], seen); + collectFunctionSymbols(term[i], seen); } seen.insert(term); } +void TheoryBV::storeFunction(TNode func, TNode term) { + if (d_funcToArgs.find(func) == d_funcToArgs.end()) { + d_funcToArgs.insert(make_pair(func, NodeSet())); + } + NodeSet& set = d_funcToArgs[func]; + if (set.find(term) == set.end()) { + set.insert(term); + Node skolem = utils::mkVar(utils::getSize(term)); + d_funcToSkolem.addSubstitution(term, skolem); + } +} + void TheoryBV::mkAckermanizationAsssertions(std::vector<Node>& assertions) { Debug("bv-ackermanize") << "TheoryBV::mkAckermanizationAsssertions\n"; @@ -206,51 +211,44 @@ void TheoryBV::mkAckermanizationAsssertions(std::vector<Node>& assertions) { AlwaysAssert(!options::incrementalSolving()); TNodeSet seen; for (unsigned i = 0; i < assertions.size(); ++i) { - collectNumerators(assertions[i], seen); - } - - // process division UF - Debug("bv-ackermanize") << "Process division UF...\n"; - for (WidthToNumerators::const_iterator it = d_BVDivByZeroAckerman.begin(); it != d_BVDivByZeroAckerman.end(); ++it) { - const TNodeSet& numerators= it->second; - for (TNodeSet::const_iterator i = numerators.begin(); i != numerators.end(); ++i) { - TNodeSet::const_iterator j = i; - j++; - for (; j != numerators.end(); ++j) { - TNode arg1 = *i; - TNode arg2 = *j; - TNode acker1 = utils::mkNode(kind::BITVECTOR_ACKERMANIZE_UDIV, arg1); - TNode acker2 = utils::mkNode(kind::BITVECTOR_ACKERMANIZE_UDIV, arg2); - - Node arg_eq = utils::mkNode(kind::EQUAL, arg1, arg2); - Node acker_eq = utils::mkNode(kind::EQUAL, acker1, acker2); - Node lemma = utils::mkNode(kind::IMPLIES, arg_eq, acker_eq); - Debug("bv-ackermanize") << " " << lemma << "\n"; - assertions.push_back(lemma); - } - } + collectFunctionSymbols(assertions[i], seen); } - // process remainder UF - Debug("bv-ackermanize") << "Process remainder UF...\n"; - for (WidthToNumerators::const_iterator it = d_BVRemByZeroAckerman.begin(); it != d_BVRemByZeroAckerman.end(); ++it) { - const TNodeSet& numerators= it->second; - for (TNodeSet::const_iterator i = numerators.begin(); i != numerators.end(); ++i) { - TNodeSet::const_iterator j = i; - j++; - for (; j != numerators.end(); ++j) { - TNode arg1 = *i; - TNode arg2 = *j; - TNode acker1 = utils::mkNode(kind::BITVECTOR_ACKERMANIZE_UREM, arg1); - TNode acker2 = utils::mkNode(kind::BITVECTOR_ACKERMANIZE_UREM, arg2); - - Node arg_eq = utils::mkNode(kind::EQUAL, arg1, arg2); - Node acker_eq = utils::mkNode(kind::EQUAL, acker1, acker2); - Node lemma = utils::mkNode(kind::IMPLIES, arg_eq, acker_eq); - Debug("bv-ackermanize") << " " << lemma << "\n"; + + FunctionToArgs::const_iterator it = d_funcToArgs.begin(); + NodeManager* nm = NodeManager::currentNM(); + for (; it!= d_funcToArgs.end(); ++it) { + TNode func = it->first; + const NodeSet& args = it->second; + NodeSet::const_iterator it1 = args.begin(); + for ( ; it1 != args.end(); ++it1) { + for(NodeSet::const_iterator it2 = it1; it2 != args.end(); ++it2) { + TNode args1 = *it1; + TNode args2 = *it2; + + AlwaysAssert (args1.getKind() == kind::APPLY_UF && + args1.getOperator() == func); + AlwaysAssert (args2.getKind() == kind::APPLY_UF && + args2.getOperator() == func); + AlwaysAssert (args1.getNumChildren() == args2.getNumChildren()); + + std::vector<Node> eqs(args1.getNumChildren()); + + for (unsigned i = 0; i < args1.getNumChildren(); ++i) { + eqs[i] = nm->mkNode(kind::EQUAL, args1[i], args2[i]); + } + + Node args_eq = eqs.size() == 1 ? eqs[0] : nm->mkNode(kind::AND, eqs); + Node func_eq = nm->mkNode(kind::EQUAL, args1, args2); + Node lemma = nm->mkNode(kind::IMPLIES, args_eq, func_eq); assertions.push_back(lemma); } } } + + // replace applications of UF by skolems (FIXME for model building) + for(unsigned i = 0; i < assertions.size(); ++i) { + assertions[i] = d_funcToSkolem.apply(assertions[i]); + } } Node TheoryBV::expandDefinition(LogicRequest &logicRequest, Node node) { @@ -278,18 +276,18 @@ Node TheoryBV::expandDefinition(LogicRequest &logicRequest, Node node) { Node divTotalNumDen = nm->mkNode(node.getKind() == kind::BITVECTOR_UDIV ? kind::BITVECTOR_UDIV_TOTAL : kind::BITVECTOR_UREM_TOTAL, num, den); - if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { - // Ackermanize UF if using eager bit-blasting - Node ackerman_var = nm->mkNode(node.getKind() == kind::BITVECTOR_UDIV ? kind::BITVECTOR_ACKERMANIZE_UDIV : kind::BITVECTOR_ACKERMANIZE_UREM, num); - node = nm->mkNode(kind::ITE, den_eq_0, ackerman_var, divTotalNumDen); - return node; - } else { + // if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) { + // // Ackermanize UF if using eager bit-blasting + // Node ackerman_var = nm->mkNode(node.getKind() == kind::BITVECTOR_UDIV ? kind::BITVECTOR_ACKERMANIZE_UDIV : kind::BITVECTOR_ACKERMANIZE_UREM, num); + // node = nm->mkNode(kind::ITE, den_eq_0, ackerman_var, divTotalNumDen); + // return node; + // } else { Node divByZero = getBVDivByZero(node.getKind(), width); Node divByZeroNum = nm->mkNode(kind::APPLY_UF, divByZero, num); node = nm->mkNode(kind::ITE, den_eq_0, divByZeroNum, divTotalNumDen); logicRequest.widenLogic(THEORY_UF); return node; - } + //} } break; @@ -331,7 +329,7 @@ void TheoryBV::sendConflict() { if (d_conflictNode.isNull()) { return; } else { - Debug("bitvector") << indent() << "TheoryBV::check(): conflict " << d_conflictNode; + Debug("bitvector") << indent() << "TheoryBV::check(): conflict " << d_conflictNode << std::endl; d_out->conflict(d_conflictNode); d_statistics.d_avgConflictSize.addEntry(d_conflictNode.getNumChildren()); d_conflictNode = Node::null(); @@ -707,7 +705,7 @@ Node TheoryBV::explain(TNode node) { // return the explanation Node explanation = utils::mkAnd(assumptions); Debug("bitvector::explain") << "TheoryBV::explain(" << node << ") => " << explanation << std::endl; - Debug("bitvector::explain") << "TheoryBV::explain done. \n"; + Debug("bitvector::explain") << "TheoryBV::explain done. \n"; return explanation; } @@ -725,6 +723,8 @@ void TheoryBV::addSharedTerm(TNode t) { EqualityStatus TheoryBV::getEqualityStatus(TNode a, TNode b) { + if (options::bitblastMode() == theory::bv::BITBLAST_MODE_EAGER) + return EQUALITY_UNKNOWN; Assert (options::bitblastMode() == theory::bv::BITBLAST_MODE_LAZY); for (unsigned i = 0; i < d_subtheories.size(); ++i) { EqualityStatus status = d_subtheories[i]->getEqualityStatus(a, b); diff --git a/src/theory/bv/theory_bv.h b/src/theory/bv/theory_bv.h index 0bbcba9b0..ba2a4fc2a 100644 --- a/src/theory/bv/theory_bv.h +++ b/src/theory/bv/theory_bv.h @@ -56,7 +56,8 @@ class TheoryBV : public Theory { public: TheoryBV(context::Context* c, context::UserContext* u, OutputChannel& out, - Valuation valuation, const LogicInfo& logicInfo); + Valuation valuation, const LogicInfo& logicInfo, + std::string name = ""); ~TheoryBV(); @@ -88,8 +89,8 @@ public: void presolve(); - bool applyAbstraction(const std::vector<Node>& assertions, std::vector<Node>& new_assertions); - + bool applyAbstraction(const std::vector<Node>& assertions, std::vector<Node>& new_assertions); + void setProofLog( BitVectorProof * bvp ); private: @@ -100,10 +101,10 @@ private: IntStat d_solveSubstitutions; TimerStat d_solveTimer; IntStat d_numCallsToCheckFullEffort; - IntStat d_numCallsToCheckStandardEffort; + IntStat d_numCallsToCheckStandardEffort; TimerStat d_weightComputationTimer; IntStat d_numMultSlice; - Statistics(); + Statistics(const std::string &name); ~Statistics(); }; @@ -121,12 +122,12 @@ private: */ Node getBVDivByZero(Kind k, unsigned width); - typedef __gnu_cxx::hash_set<TNode, TNodeHashFunction> TNodeSet; - void collectNumerators(TNode term, TNodeSet& seen); - + typedef __gnu_cxx::hash_set<TNode, TNodeHashFunction> TNodeSet; + void collectFunctionSymbols(TNode term, TNodeSet& seen); + void storeFunction(TNode func, TNode term); typedef __gnu_cxx::hash_set<Node, NodeHashFunction> NodeSet; NodeSet d_staticLearnCache; - + /** * Maps from bit-vector width to division-by-zero uninterpreted * function symbols. @@ -134,17 +135,15 @@ private: __gnu_cxx::hash_map<unsigned, Node> d_BVDivByZero; __gnu_cxx::hash_map<unsigned, Node> d_BVRemByZero; - /** - * Maps from bit-vector width to numerators - * of uninterpreted function symbol - */ - typedef __gnu_cxx::hash_map<unsigned, TNodeSet > WidthToNumerators; - WidthToNumerators d_BVDivByZeroAckerman; - WidthToNumerators d_BVRemByZeroAckerman; + typedef __gnu_cxx::hash_map<Node, NodeSet, NodeHashFunction> FunctionToArgs; + typedef __gnu_cxx::hash_map<Node, Node, NodeHashFunction> NodeToNode; + // for ackermanization + FunctionToArgs d_funcToArgs; + CVC4::theory::SubstitutionMap d_funcToSkolem; context::CDO<bool> d_lemmasAdded; - + // Are we in conflict? context::CDO<bool> d_conflict; @@ -167,17 +166,17 @@ private: typedef context::CDHashMap<Node, SubTheory, NodeHashFunction> PropagatedMap; PropagatedMap d_propagatedBy; - EagerBitblastSolver* d_eagerSolver; + EagerBitblastSolver* d_eagerSolver; AbstractionModule* d_abstractionModule; bool d_isCoreTheory; bool d_calledPreregister; - + bool wasPropagatedBySubtheory(TNode literal) const { - return d_propagatedBy.find(literal) != d_propagatedBy.end(); + return d_propagatedBy.find(literal) != d_propagatedBy.end(); } - + SubTheory getPropagatingSubtheory(TNode literal) const { - Assert(wasPropagatedBySubtheory(literal)); + Assert(wasPropagatedBySubtheory(literal)); PropagatedMap::const_iterator find = d_propagatedBy.find(literal); return (*find).second; } @@ -193,7 +192,7 @@ private: void addSharedTerm(TNode t); bool isSharedTerm(TNode t) { return d_sharedTermsSet.contains(t); } - + EqualityStatus getEqualityStatus(TNode a, TNode b); Node getModelValue(TNode var); @@ -214,7 +213,7 @@ private: void lemma(TNode node) { d_out->lemma(node, RULE_CONFLICT); d_lemmasAdded = true; } - void checkForLemma(TNode node); + void checkForLemma(TNode node); friend class LazyBitblaster; friend class TLazyBitblaster; 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 152a335a5..2bcb6ca1b 100644 --- a/src/theory/bv/theory_bv_rewrite_rules_operator_elimination.h +++ b/src/theory/bv/theory_bv_rewrite_rules_operator_elimination.h @@ -349,7 +349,7 @@ Node RewriteRule<SdivEliminate>::apply(TNode node) { Node abs_a = utils::mkNode(kind::ITE, a_lt_0, utils::mkNode(kind::BITVECTOR_NEG, a), a); Node abs_b = utils::mkNode(kind::ITE, b_lt_0, utils::mkNode(kind::BITVECTOR_NEG, b), b); - Node a_udiv_b = utils::mkNode(kind::BITVECTOR_UDIV, abs_a, abs_b); + Node a_udiv_b = utils::mkNode(options::bitvectorDivByZeroConst() ? kind::BITVECTOR_UDIV_TOTAL : kind::BITVECTOR_UDIV, abs_a, abs_b); Node neg_result = utils::mkNode(kind::BITVECTOR_NEG, a_udiv_b); Node condition = utils::mkNode(kind::XOR, a_lt_0, b_lt_0); @@ -377,7 +377,7 @@ Node RewriteRule<SremEliminate>::apply(TNode node) { Node abs_a = utils::mkNode(kind::ITE, a_lt_0, utils::mkNode(kind::BITVECTOR_NEG, a), a); Node abs_b = utils::mkNode(kind::ITE, b_lt_0, utils::mkNode(kind::BITVECTOR_NEG, b), b); - Node a_urem_b = utils::mkNode(kind::BITVECTOR_UREM, abs_a, abs_b); + Node a_urem_b = utils::mkNode( options::bitvectorDivByZeroConst() ? kind::BITVECTOR_UREM_TOTAL : kind::BITVECTOR_UREM, abs_a, abs_b); Node neg_result = utils::mkNode(kind::BITVECTOR_NEG, a_urem_b); Node result = utils::mkNode(kind::ITE, a_lt_0, neg_result, a_urem_b); diff --git a/src/theory/datatypes/datatypes_sygus.cpp b/src/theory/datatypes/datatypes_sygus.cpp index 5bd6680f2..4514453db 100644 --- a/src/theory/datatypes/datatypes_sygus.cpp +++ b/src/theory/datatypes/datatypes_sygus.cpp @@ -402,6 +402,60 @@ void SygusSplit::registerSygusTypeConstructorArg( TypeNode tnn, const Datatype& } } +class ReqTrie { +public: + ReqTrie() : d_req_kind( UNDEFINED_KIND ){} + std::map< unsigned, ReqTrie > d_children; + Kind d_req_kind; + TypeNode d_req_type; + Node d_req_const; + void print( const char * c, int indent = 0 ){ + if( d_req_kind!=UNDEFINED_KIND ){ + Trace(c) << d_req_kind << " "; + }else if( !d_req_type.isNull() ){ + Trace(c) << d_req_type; + }else if( !d_req_const.isNull() ){ + Trace(c) << d_req_const; + }else{ + Trace(c) << "_"; + } + Trace(c) << std::endl; + for( std::map< unsigned, ReqTrie >::iterator it = d_children.begin(); it != d_children.end(); ++it ){ + for( int i=0; i<=indent; i++ ) { Trace(c) << " "; } + Trace(c) << it->first << " : "; + it->second.print( c, indent+1 ); + } + } + bool satisfiedBy( quantifiers::TermDbSygus * tdb, TypeNode tn ){ + if( d_req_kind!=UNDEFINED_KIND ){ + int c = tdb->getKindArg( tn, d_req_kind ); + if( c!=-1 ){ + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + for( std::map< unsigned, ReqTrie >::iterator it = d_children.begin(); it != d_children.end(); ++it ){ + if( it->first<dt[c].getNumArgs() ){ + TypeNode tnc = tdb->getArgType( dt[c], it->first ); + if( !it->second.satisfiedBy( tdb, tnc ) ){ + return false; + } + }else{ + return false; + } + } + return true; + }else{ + return false; + } + }else if( !d_req_const.isNull() ){ + return tdb->hasConst( tn, d_req_const ); + }else if( !d_req_type.isNull() ){ + return tn==d_req_type; + }else{ + return true; + } + } +}; + + //this function gets all easy redundant cases, before consulting rewriters bool SygusSplit::considerSygusSplitKind( const Datatype& dt, const Datatype& pdt, TypeNode tn, TypeNode tnp, Kind k, Kind parent, int arg ) { Assert( d_tds->hasKind( tn, k ) ); @@ -419,101 +473,168 @@ bool SygusSplit::considerSygusSplitKind( const Datatype& dt, const Datatype& pdt return arg==firstArg; } } - //push + //describes the shape of an alternate term to construct + // we check whether this term is in the sygus grammar below + ReqTrie rt; + bool rt_valid = false; + + //construct rt by cases if( parent==NOT || parent==BITVECTOR_NOT || parent==UMINUS || parent==BITVECTOR_NEG ){ - //negation normal form - if( parent==k && isArgDatatype( dt[c], 0, pdt ) ){ - return false; - } - Kind nk = UNDEFINED_KIND; - Kind reqk = UNDEFINED_KIND; //required kind for all children - if( parent==NOT ){ - if( k==AND ) { - nk = OR;reqk = NOT; - }else if( k==OR ){ - nk = AND;reqk = NOT; - }else if( k==IFF ) { - nk = XOR; - }else if( k==XOR ) { - nk = IFF; - } - } - if( parent==BITVECTOR_NOT ){ - if( k==BITVECTOR_AND ) { - nk = BITVECTOR_OR;reqk = BITVECTOR_NOT; - }else if( k==BITVECTOR_OR ){ - nk = BITVECTOR_AND;reqk = BITVECTOR_NOT; - }else if( k==BITVECTOR_XNOR ) { - nk = BITVECTOR_XOR; - }else if( k==BITVECTOR_XOR ) { - nk = BITVECTOR_XNOR; - } - } - if( parent==UMINUS ){ - if( k==PLUS ){ - nk = PLUS;reqk = UMINUS; - } - } - if( parent==BITVECTOR_NEG ){ - if( k==PLUS ){ - nk = PLUS;reqk = BITVECTOR_NEG; - } - } - if( nk!=UNDEFINED_KIND ){ - Trace("sygus-split-debug") << "Push " << parent << " over " << k << " to " << nk; - if( reqk!=UNDEFINED_KIND ){ - Trace("sygus-split-debug") << ", reqk = " << reqk; + rt_valid = true; + //negation normal form + if( parent==k ){ + rt.d_req_type = d_tds->getArgType( dt[c], 0 ); + }else{ + Kind reqk = UNDEFINED_KIND; //required kind for all children + std::map< unsigned, Kind > reqkc; //required kind for some children + if( parent==NOT ){ + if( k==AND ) { + rt.d_req_kind = OR;reqk = NOT; + }else if( k==OR ){ + rt.d_req_kind = AND;reqk = NOT; + }else if( k==IFF ) { + rt.d_req_kind = XOR; + }else if( k==XOR ) { + rt.d_req_kind = IFF; + }else if( k==ITE ){ + rt.d_req_kind = ITE;reqkc[1] = NOT;reqkc[2] = NOT; + rt.d_children[0].d_req_type = d_tds->getArgType( dt[c], 0 ); + }else if( k==LEQ || k==GT ){ + // (not (~ x y)) -----> (~ (+ y 1) x) + rt.d_req_kind = k; + rt.d_children[0].d_req_kind = PLUS; + rt.d_children[0].d_children[0].d_req_type = d_tds->getArgType( dt[c], 1 ); + rt.d_children[0].d_children[1].d_req_const = NodeManager::currentNM()->mkConst( Rational( 1 ) ); + rt.d_children[1].d_req_type = d_tds->getArgType( dt[c], 0 ); + //TODO: other possibilities? + }else if( k==LT || k==GEQ ){ + // (not (~ x y)) -----> (~ y (+ x 1)) + rt.d_req_kind = k; + rt.d_children[0].d_req_type = d_tds->getArgType( dt[c], 1 ); + rt.d_children[1].d_req_kind = PLUS; + rt.d_children[1].d_children[0].d_req_type = d_tds->getArgType( dt[c], 0 ); + rt.d_children[1].d_children[1].d_req_const = NodeManager::currentNM()->mkConst( Rational( 1 ) ); + }else{ + rt_valid = false; + } + }else if( parent==BITVECTOR_NOT ){ + if( k==BITVECTOR_AND ) { + rt.d_req_kind = BITVECTOR_OR;reqk = BITVECTOR_NOT; + }else if( k==BITVECTOR_OR ){ + rt.d_req_kind = BITVECTOR_AND;reqk = BITVECTOR_NOT; + }else if( k==BITVECTOR_XNOR ) { + rt.d_req_kind = BITVECTOR_XOR; + }else if( k==BITVECTOR_XOR ) { + rt.d_req_kind = BITVECTOR_XNOR; + }else{ + rt_valid = false; + } + }else if( parent==UMINUS ){ + if( k==PLUS ){ + rt.d_req_kind = PLUS;reqk = UMINUS; + }else{ + rt_valid = false; + } + }else if( parent==BITVECTOR_NEG ){ + if( k==PLUS ){ + rt.d_req_kind = PLUS;reqk = BITVECTOR_NEG; + }else{ + rt_valid = false; + } } - Trace("sygus-split-debug") << "?" << std::endl; - int pcr = d_tds->getKindArg( tnp, nk ); - if( pcr!=-1 ){ - Assert( pcr<(int)pdt.getNumConstructors() ); - if( reqk!=UNDEFINED_KIND ){ + if( rt_valid && ( reqk!=UNDEFINED_KIND || !reqkc.empty() ) ){ + int pcr = d_tds->getKindArg( tnp, rt.d_req_kind ); + if( pcr!=-1 ){ + Assert( pcr<(int)pdt.getNumConstructors() ); //must have same number of arguments if( pdt[pcr].getNumArgs()==dt[c].getNumArgs() ){ - bool success = true; - std::map< int, TypeNode > childTypes; for( unsigned i=0; i<pdt[pcr].getNumArgs(); i++ ){ - TypeNode tna = d_tds->getArgType( pdt[pcr], i ); - Assert( datatypes::DatatypesRewriter::isTypeDatatype( tna ) ); - if( reqk!=UNDEFINED_KIND ){ - //child must have a NOT - int nindex = d_tds->getKindArg( tna, reqk ); - if( nindex!=-1 ){ - const Datatype& adt = ((DatatypeType)(tn).toType()).getDatatype(); - if( d_tds->getArgType( dt[c], i )!=d_tds->getArgType( adt[nindex], 0 ) ){ - Trace("sygus-split-debug") << "...arg " << i << " type mismatch." << std::endl; - success = false; - break; - } - }else{ - Trace("sygus-split-debug") << "...argument " << i << " does not have " << reqk << "." << std::endl; - success = false; - break; + Kind rk = reqk; + if( reqk==UNDEFINED_KIND ){ + std::map< unsigned, Kind >::iterator itr = reqkc.find( i ); + if( itr!=reqkc.end() ){ + rk = itr->second; } - }else{ - childTypes[i] = tna; } - } - if( success ){ - Trace("sygus-split-debug") << "...success" << std::endl; - return false; + if( rk!=UNDEFINED_KIND ){ + rt.d_children[i].d_req_kind = rk; + rt.d_children[i].d_children[0].d_req_type = d_tds->getArgType( dt[c], i ); + } } }else{ - Trace("sygus-split-debug") << "...#arg mismatch." << std::endl; + rt_valid = false; } }else{ - return !isTypeMatch( pdt[pcr], dt[c] ); + rt_valid = false; } - }else{ - Trace("sygus-split-debug") << "...operator not available." << std::endl; } } + }else if( k==MINUS || k==BITVECTOR_SUB ){ + if( parent==EQUAL || + parent==MINUS || parent==BITVECTOR_SUB || + parent==LEQ || parent==LT || parent==GEQ || parent==GT ){ + int oarg = arg==0 ? 1 : 0; + // (~ x (- y z)) ----> (~ (+ x z) y) + // (~ (- y z) x) ----> (~ y (+ x z)) + rt.d_req_kind = parent; + rt.d_children[arg].d_req_type = d_tds->getArgType( dt[c], 0 ); + rt.d_children[oarg].d_req_kind = k==MINUS ? PLUS : BITVECTOR_PLUS; + rt.d_children[oarg].d_children[0].d_req_type = d_tds->getArgType( pdt[pc], oarg ); + rt.d_children[oarg].d_children[1].d_req_type = d_tds->getArgType( dt[c], 1 ); + rt_valid = true; + }else if( parent==PLUS || parent==BITVECTOR_PLUS ){ + // (+ x (- y z)) -----> (- (+ x y) z) + // (+ (- y z) x) -----> (- (+ x y) z) + rt.d_req_kind = parent==PLUS ? MINUS : BITVECTOR_SUB; + int oarg = arg==0 ? 1 : 0; + rt.d_children[0].d_req_kind = parent; + rt.d_children[0].d_children[0].d_req_type = d_tds->getArgType( pdt[pc], oarg ); + rt.d_children[0].d_children[1].d_req_type = d_tds->getArgType( dt[c], 0 ); + rt.d_children[1].d_req_type = d_tds->getArgType( dt[c], 1 ); + rt_valid = true; + } + }else if( k==ITE ){ + if( parent!=ITE ){ + // (o X (ite y z w) X') -----> (ite y (o X z X') (o X w X')) + rt.d_req_kind = ITE; + rt.d_children[0].d_req_type = d_tds->getArgType( dt[c], 0 ); + unsigned n_args = pdt[pc].getNumArgs(); + for( unsigned r=1; r<=2; r++ ){ + rt.d_children[r].d_req_kind = parent; + for( unsigned q=0; q<n_args; q++ ){ + if( (int)q==arg ){ + rt.d_children[r].d_children[q].d_req_type = d_tds->getArgType( dt[c], r ); + }else{ + rt.d_children[r].d_children[q].d_req_type = d_tds->getArgType( pdt[pc], q ); + } + } + } + rt_valid = true; + //TODO: this increases term size but is probably a good idea + } + }else if( k==NOT ){ + if( parent==ITE ){ + // (ite (not y) z w) -----> (ite y w z) + rt.d_req_kind = ITE; + rt.d_children[0].d_req_type = d_tds->getArgType( dt[c], 0 ); + rt.d_children[1].d_req_type = d_tds->getArgType( pdt[pc], 2 ); + rt.d_children[2].d_req_type = d_tds->getArgType( pdt[pc], 1 ); + } } - if( parent==MINUS || parent==BITVECTOR_SUB ){ - - + Trace("sygus-consider-split") << "Consider sygus split kind " << k << ", parent = " << parent << ", arg = " << arg << "?" << std::endl; + if( rt_valid ){ + rt.print("sygus-consider-split"); + //check if it meets the requirements + if( rt.satisfiedBy( d_tds, tnp ) ){ + Trace("sygus-consider-split") << "...success!" << std::endl; + //do not need to consider the kind in the search since there are ways to construct equivalent terms + return false; + }else{ + Trace("sygus-consider-split") << "...failed." << std::endl; + } + Trace("sygus-consider-split") << std::endl; } + //must consider this kind in the search return true; } @@ -616,6 +737,7 @@ bool SygusSplit::isGenericRedundant( TypeNode tn, Node g, bool active ) { d_gen_terms[tn][gr] = g; d_gen_terms_inactive[tn][gr] = g; Trace("sygus-gnf-debug") << "...not redundant." << std::endl; + Trace("sygus-nf-reg") << "*** Sygus (generic) normal form : normal form of " << g << " is " << gr << std::endl; }else{ Trace("sygus-gnf-debug") << "...redundant." << std::endl; Trace("sygus-nf") << "* Sygus normal form : simplify since " << g << " and " << itg->second << " both rewrite to " << gr << std::endl; @@ -849,6 +971,7 @@ bool SygusSymBreak::processCurrentProgram( Node a, TypeNode at, int depth, Node }else{ d_redundant[at][prog] = false; red = false; + Trace("sygus-nf-reg") << "*** Sygus normal form : normal form of " << prog << " is " << progr << std::endl; } }else{ rep_prog = itnp->second; @@ -858,6 +981,7 @@ bool SygusSymBreak::processCurrentProgram( Node a, TypeNode at, int depth, Node d_redundant[at].erase( rep_prog ); d_redundant[at][prog] = false; red = false; + Trace("sygus-nf-reg") << "*** Sygus normal form : normal form of " << prog << " is " << progr << " (redundant but smaller than " << rep_prog << ") " << std::endl; }else{ Assert( prog!=itnp->second ); d_redundant[at][prog] = true; @@ -1090,6 +1214,7 @@ bool SygusSymBreak::processCurrentProgram( Node a, TypeNode at, int depth, Node } }else{ red = it->second; + Trace("sygus-nf-debug") << "Already processed, redundant : " << red << std::endl; } if( red ){ if( std::find( d_lemmas_reported[at][prog].begin(), d_lemmas_reported[at][prog].end(), a )==d_lemmas_reported[at][prog].end() ){ diff --git a/src/theory/datatypes/kinds b/src/theory/datatypes/kinds index d035f0fa7..3338e5f31 100644 --- a/src/theory/datatypes/kinds +++ b/src/theory/datatypes/kinds @@ -105,7 +105,7 @@ typerule RECORD_UPDATE ::CVC4::theory::datatypes::RecordUpdateTypeRule operator DT_SIZE 1 "datatypes size" typerule DT_SIZE ::CVC4::theory::datatypes::DtSizeTypeRule -operator DT_HEIGHT_BOUND 1 "datatypes height bound" +operator DT_HEIGHT_BOUND 2 "datatypes height bound" typerule DT_HEIGHT_BOUND ::CVC4::theory::datatypes::DtHeightBoundTypeRule endtheory diff --git a/src/theory/datatypes/theory_datatypes.cpp b/src/theory/datatypes/theory_datatypes.cpp index eb8b23973..a2f995935 100644 --- a/src/theory/datatypes/theory_datatypes.cpp +++ b/src/theory/datatypes/theory_datatypes.cpp @@ -27,9 +27,11 @@ #include "theory/datatypes/datatypes_rewriter.h" #include "theory/datatypes/theory_datatypes_type_rules.h" #include "theory/quantifiers_engine.h" +#include "theory/quantifiers/term_database.h" #include "theory/theory_model.h" #include "theory/type_enumerator.h" #include "theory/valuation.h" +#include "options/theory_options.h" using namespace std; using namespace CVC4::kind; @@ -54,8 +56,7 @@ TheoryDatatypes::TheoryDatatypes(Context* c, UserContext* u, OutputChannel& out, //d_consEqc( c ), d_conflict( c, false ), d_collectTermsCache( c ), - d_consTerms( c ), - d_selTerms( c ), + d_functionTerms( c ), d_singleton_eq( u ), d_lemmas_produced_c( u ) { @@ -81,6 +82,8 @@ TheoryDatatypes::~TheoryDatatypes() { Assert(current != NULL); delete current; } + delete d_sygus_split; + delete d_sygus_sym_break; } void TheoryDatatypes::setMasterEqualityEngine(eq::EqualityEngine* eq) { @@ -91,9 +94,7 @@ TheoryDatatypes::EqcInfo* TheoryDatatypes::getOrMakeEqcInfo( TNode n, bool doMak if( !hasEqcInfo( n ) ){ if( doMake ){ //add to labels - NodeList* lbl = new(getSatContext()->getCMM()) NodeList( true, getSatContext(), false, - ContextMemoryAllocator<TNode>(getSatContext()->getCMM()) ); - d_labels.insertDataFromContextMemory( n, lbl ); + d_labels[ n ] = 0; std::map< Node, EqcInfo* >::iterator eqc_i = d_eqc_info.find( n ); EqcInfo* ei; @@ -106,10 +107,10 @@ TheoryDatatypes::EqcInfo* TheoryDatatypes::getOrMakeEqcInfo( TNode n, bool doMak if( n.getKind()==APPLY_CONSTRUCTOR ){ ei->d_constructor = n; } + //add to selectors - NodeList* sel = new(getSatContext()->getCMM()) NodeList( true, getSatContext(), false, - ContextMemoryAllocator<TNode>(getSatContext()->getCMM()) ); - d_selector_apps.insertDataFromContextMemory( n, sel ); + d_selector_apps[n] = 0; + return ei; }else{ return NULL; @@ -178,6 +179,7 @@ void TheoryDatatypes::check(Effort e) { Trace("datatypes-debug") << "Check for splits " << e << endl; do { d_addedFact = false; + bool added_split = false; std::map< TypeNode, Node > rec_singletons; eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &d_equalityEngine ); while( !eqcs_i.isFinished() ){ @@ -190,7 +192,7 @@ void TheoryDatatypes::check(Effort e) { if( !hasLabel( eqc, n ) ){ Trace("datatypes-debug") << "No constructor..." << std::endl; const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); - Trace("datatypes-debug") << "Datatype " << dt << " is " << dt.isFinite() << " " << dt.isUFinite() << " " << dt.isRecursiveSingleton() << std::endl; + Trace("datatypes-debug") << "Datatype " << dt << " is " << dt.isFinite() << " " << dt.isInterpretedFinite() << " " << dt.isRecursiveSingleton() << std::endl; bool continueProc = true; if( dt.isRecursiveSingleton() ){ Trace("datatypes-debug") << "Check recursive singleton..." << std::endl; @@ -251,7 +253,7 @@ void TheoryDatatypes::check(Effort e) { if( consIndex==-1 ){ consIndex = j; } - if( options::finiteModelFind() ? !dt[ j ].isUFinite() : !dt[ j ].isFinite() ) { + if( !dt[ j ].isInterpretedFinite() ) { if( !eqc || !eqc->d_selectors ){ needSplit = false; } @@ -311,7 +313,10 @@ void TheoryDatatypes::check(Effort e) { //doSendLemma( lemma ); d_out->lemma( lemma, false, false, true ); } - return; + added_split = true; + if( !options::dtBlastSplits() ){ + return; + } } }else{ Trace("dt-split-debug") << "Do not split constructor for " << n << " : " << n.getType() << " " << dt.getNumConstructors() << std::endl; @@ -323,6 +328,9 @@ void TheoryDatatypes::check(Effort e) { } ++eqcs_i; } + if( added_split ){ + return; + } Trace("datatypes-debug") << "Flush pending facts..." << std::endl; flushPendingFacts(); /* @@ -870,33 +878,36 @@ void TheoryDatatypes::merge( Node t1, Node t2 ){ //merge labels - NodeListMap::iterator lbl_i = d_labels.find( t2 ); + NodeIntMap::iterator lbl_i = d_labels.find( t2 ); if( lbl_i != d_labels.end() ){ Trace("datatypes-debug") << " merge labels from " << eqc2 << " " << t2 << std::endl; - NodeList* lbl = (*lbl_i).second; - for( NodeList::const_iterator j = lbl->begin(); j != lbl->end(); ++j ){ - Node tt = (*j).getKind()==kind::NOT ? (*j)[0] : (*j); + int n_label = (*lbl_i).second; + for( int i=0; i<n_label; i++ ){ + Assert( i<(int)d_labels_data[ t2 ].size() ); + Node t = d_labels_data[ t2 ][i]; + Node tt = t.getKind()==kind::NOT ? t[0] : t; Node t_arg; int tindex = DatatypesRewriter::isTester( tt, t_arg ); Assert( tindex!=-1 ); - addTester( tindex, *j, eqc1, t1, t_arg ); + addTester( tindex, t, eqc1, t1, t_arg ); if( d_conflict ){ Trace("datatypes-debug") << " conflict!" << std::endl; return; } } + } //merge selectors if( !eqc1->d_selectors && eqc2->d_selectors ){ eqc1->d_selectors = true; checkInst = true; } - NodeListMap::iterator sel_i = d_selector_apps.find( t2 ); + NodeIntMap::iterator sel_i = d_selector_apps.find( t2 ); if( sel_i != d_selector_apps.end() ){ Trace("datatypes-debug") << " merge selectors from " << eqc2 << " " << t2 << std::endl; - NodeList* sel = (*sel_i).second; - for( NodeList::const_iterator j = sel->begin(); j != sel->end(); ++j ){ - addSelector( *j, eqc1, t1, eqc2->d_constructor.get().isNull() ); + int n_sel = (*sel_i).second; + for( int j=0; j<n_sel; j++ ){ + addSelector( d_selector_apps_data[t2][j], eqc1, t1, eqc2->d_constructor.get().isNull() ); } } if( checkInst ){ @@ -927,11 +938,11 @@ bool TheoryDatatypes::hasLabel( EqcInfo* eqc, Node n ){ } Node TheoryDatatypes::getLabel( Node n ) { - NodeListMap::iterator lbl_i = d_labels.find( n ); + NodeIntMap::iterator lbl_i = d_labels.find( n ); if( lbl_i != d_labels.end() ){ - NodeList* lbl = (*lbl_i).second; - if( !(*lbl).empty() && (*lbl)[ (*lbl).size() - 1 ].getKind()!=kind::NOT ){ - return (*lbl)[ (*lbl).size() - 1 ]; + unsigned n_lbl = (*lbl_i).second; + if( n_lbl>0 && d_labels_data[n][ n_lbl-1 ].getKind()!=kind::NOT ){ + return d_labels_data[n][ n_lbl-1 ]; } } return Node::null(); @@ -954,9 +965,9 @@ int TheoryDatatypes::getLabelIndex( EqcInfo* eqc, Node n ){ } bool TheoryDatatypes::hasTester( Node n ) { - NodeListMap::iterator lbl_i = d_labels.find( n ); + NodeIntMap::iterator lbl_i = d_labels.find( n ); if( lbl_i != d_labels.end() ){ - return !(*(*lbl_i).second).empty(); + return (*lbl_i).second>0; }else{ return false; } @@ -969,13 +980,14 @@ void TheoryDatatypes::getPossibleCons( EqcInfo* eqc, Node n, std::vector< bool > if( lindex!=-1 ){ pcons[ lindex ] = true; }else{ - NodeListMap::iterator lbl_i = d_labels.find( n ); + NodeIntMap::iterator lbl_i = d_labels.find( n ); if( lbl_i != d_labels.end() ){ - NodeList* lbl = (*lbl_i).second; - for( NodeList::const_iterator i = lbl->begin(); i != lbl->end(); i++ ) { - Assert( (*i).getKind()==NOT ); - //pcons[ Datatype::indexOf( (*i)[0].getOperator().toExpr() ) ] = false; - int tindex = DatatypesRewriter::isTester( (*i)[0] ); + int n_lbl = (*lbl_i).second; + for( int i=0; i<n_lbl; i++ ){ + Node t = d_labels_data[n][i]; + Assert( t.getKind()==NOT ); + //pcons[ Datatype::indexOf( t[0].getOperator().toExpr() ) ] = false; + int tindex = DatatypesRewriter::isTester( t[0] ); Assert( tindex!=-1 ); pcons[ tindex ] = false; } @@ -984,11 +996,11 @@ void TheoryDatatypes::getPossibleCons( EqcInfo* eqc, Node n, std::vector< bool > } void TheoryDatatypes::getSelectorsForCons( Node r, std::map< int, bool >& sels ) { - NodeListMap::iterator sel_i = d_selector_apps.find( r ); + NodeIntMap::iterator sel_i = d_selector_apps.find( r ); if( sel_i != d_selector_apps.end() ){ - NodeList* sel = (*sel_i).second; - for( NodeList::const_iterator j = sel->begin(); j != sel->end(); j++ ){ - int sindex = Datatype::indexOf( (*j).getOperator().toExpr() ); + int n_sel = (*sel_i).second; + for( int j=0; j<n_sel; j++ ){ + int sindex = Datatype::indexOf( d_selector_apps_data[r][j].getOperator().toExpr() ); sels[sindex] = true; } } @@ -1055,12 +1067,13 @@ void TheoryDatatypes::addTester( int ttindex, Node t, EqcInfo* eqc, Node n, Node } }else{ //otherwise, scan list of labels - NodeListMap::iterator lbl_i = d_labels.find( n ); + NodeIntMap::iterator lbl_i = d_labels.find( n ); Assert( lbl_i != d_labels.end() ); - NodeList* lbl = (*lbl_i).second; - for( NodeList::const_iterator i = lbl->begin(); i != lbl->end(); i++ ) { - Assert( (*i).getKind()==NOT ); - j = *i; + int n_lbl = (*lbl_i).second; + for( int i=0; i<n_lbl; i++ ){ + Node ti = d_labels_data[n][i]; + Assert( ti.getKind()==NOT ); + j = ti; jt = j[0]; //int jtindex = Datatype::indexOf( jt.getOperator().toExpr() ); int jtindex = DatatypesRewriter::isTester( jt ); @@ -1076,16 +1089,24 @@ void TheoryDatatypes::addTester( int ttindex, Node t, EqcInfo* eqc, Node n, Node } if( !makeConflict ){ Debug("datatypes-labels") << "Add to labels " << t << std::endl; - lbl->push_back( t ); + //lbl->push_back( t ); + d_labels[n] = n_lbl + 1; + if( n_lbl<(int)d_labels_data[n].size() ){ + d_labels_data[n][n_lbl] = t; + }else{ + d_labels_data[n].push_back( t ); + } + n_lbl++; + const Datatype& dt = ((DatatypeType)(t_arg.getType()).toType()).getDatatype(); - Debug("datatypes-labels") << "Labels at " << lbl->size() << " / " << dt.getNumConstructors() << std::endl; + Debug("datatypes-labels") << "Labels at " << n_lbl << " / " << dt.getNumConstructors() << std::endl; if( tpolarity ){ instantiate( eqc, n ); }else{ //check if we have reached the maximum number of testers // in this case, add the positive tester //this should not be done for sygus, since cases may be limited - if( lbl->size()==dt.getNumConstructors()-1 && !dt.isSygus() ){ + if( n_lbl==(int)dt.getNumConstructors()-1 && !dt.isSygus() ){ std::vector< bool > pcons; getPossibleCons( eqc, n, pcons ); int testerIndex = -1; @@ -1099,11 +1120,14 @@ void TheoryDatatypes::addTester( int ttindex, Node t, EqcInfo* eqc, Node n, Node //we must explain why each term in the set of testers for this equivalence class is equal std::vector< Node > eq_terms; NodeBuilder<> nb(kind::AND); - for( NodeList::const_iterator i = lbl->begin(); i != lbl->end(); i++ ) { - nb << (*i); - Assert( (*i).getKind()==NOT ); + //for( NodeList::const_iterator i = lbl->begin(); i != lbl->end(); i++ ) { + + for( int i=0; i<n_lbl; i++ ){ + Node ti = d_labels_data[n][i]; + nb << ti; + Assert( ti.getKind()==NOT ); Node t_arg2; - DatatypesRewriter::isTester( (*i)[0], t_arg2 ); + DatatypesRewriter::isTester( ti[0], t_arg2 ); //Assert( tindex!=-1 ); if( std::find( eq_terms.begin(), eq_terms.end(), t_arg2 )==eq_terms.end() ){ eq_terms.push_back( t_arg2 ); @@ -1140,19 +1164,26 @@ void TheoryDatatypes::addTester( int ttindex, Node t, EqcInfo* eqc, Node n, Node void TheoryDatatypes::addSelector( Node s, EqcInfo* eqc, Node n, bool assertFacts ) { Trace("dt-collapse-sel") << "Add selector : " << s << " to eqc(" << n << ")" << std::endl; //check to see if it is redundant - NodeListMap::iterator sel_i = d_selector_apps.find( n ); + NodeIntMap::iterator sel_i = d_selector_apps.find( n ); Assert( sel_i != d_selector_apps.end() ); if( sel_i != d_selector_apps.end() ){ - NodeList* sel = (*sel_i).second; - for( NodeList::const_iterator j = sel->begin(); j != sel->end(); ++j ){ - Node ss = *j; + int n_sel = (*sel_i).second; + for( int j=0; j<n_sel; j++ ){ + Node ss = d_selector_apps_data[n][j]; if( s.getOperator()==ss.getOperator() && ( s.getKind()!=DT_HEIGHT_BOUND || s[1]==ss[1] ) ){ Trace("dt-collapse-sel") << "...redundant." << std::endl; return; } } //add it to the vector - sel->push_back( s ); + //sel->push_back( s ); + d_selector_apps[n] = n_sel + 1; + if( n_sel<(int)d_selector_apps_data[n].size() ){ + d_selector_apps_data[n][n_sel] = s; + }else{ + d_selector_apps_data[n].push_back( s ); + } + eqc->d_selectors = true; } if( assertFacts && !eqc->d_constructor.get().isNull() ){ @@ -1165,19 +1196,19 @@ void TheoryDatatypes::addConstructor( Node c, EqcInfo* eqc, Node n ){ Trace("datatypes-debug") << "Add constructor : " << c << " to eqc(" << n << ")" << std::endl; Assert( eqc->d_constructor.get().isNull() ); //check labels - NodeListMap::iterator lbl_i = d_labels.find( n ); + NodeIntMap::iterator lbl_i = d_labels.find( n ); if( lbl_i != d_labels.end() ){ size_t constructorIndex = Datatype::indexOf(c.getOperator().toExpr()); - NodeList* lbl = (*lbl_i).second; - for( NodeList::const_iterator i = lbl->begin(); i != lbl->end(); i++ ) { - if( (*i).getKind()==NOT ){ - int tindex = DatatypesRewriter::isTester( (*i)[0] ); + int n_lbl = (*lbl_i).second; + for( int i=0; i<n_lbl; i++ ){ + Node t = d_labels_data[n][i]; + if( t.getKind()==NOT ){ + int tindex = DatatypesRewriter::isTester( t[0] ); Assert( tindex!=-1 ); if( tindex==(int)constructorIndex ){ - Node n = *i; std::vector< TNode > assumptions; - explain( *i, assumptions ); - explainEquality( c, (*i)[0][0], true, assumptions ); + explain( t, assumptions ); + explainEquality( c, t[0][0], true, assumptions ); d_conflictNode = mkAnd( assumptions ); Trace("dt-conflict") << "CONFLICT: Tester merge eq conflict : " << d_conflictNode << std::endl; d_out->conflict( d_conflictNode ); @@ -1188,12 +1219,13 @@ void TheoryDatatypes::addConstructor( Node c, EqcInfo* eqc, Node n ){ } } //check selectors - NodeListMap::iterator sel_i = d_selector_apps.find( n ); + NodeIntMap::iterator sel_i = d_selector_apps.find( n ); if( sel_i != d_selector_apps.end() ){ - NodeList* sel = (*sel_i).second; - for( NodeList::const_iterator j = sel->begin(); j != sel->end(); ++j ){ + int n_sel = (*sel_i).second; + for( int j=0; j<n_sel; j++ ){ + Node s = d_selector_apps_data[n][j]; //collapse the selector - collapseSelector( *j, c ); + collapseSelector( s, c ); } } eqc->d_constructor.set( c ); @@ -1277,61 +1309,114 @@ EqualityStatus TheoryDatatypes::getEqualityStatus(TNode a, TNode b){ return EQUALITY_FALSE_IN_MODEL; } -void TheoryDatatypes::computeCareGraph(){ - Trace("dt-cg") << "Compute graph for dt..." << std::endl; - vector< pair<TNode, TNode> > currentPairs; - for( unsigned r=0; r<2; r++ ){ - unsigned functionTerms = r==0 ? d_consTerms.size() : d_selTerms.size(); - for( unsigned i=0; i<functionTerms; i++ ){ - TNode f1 = r==0 ? d_consTerms[i] : d_selTerms[i]; - Assert(d_equalityEngine.hasTerm(f1)); - for( unsigned j=i+1; j<functionTerms; j++ ){ - TNode f2 = r==0 ? d_consTerms[j] : d_selTerms[j]; - Trace("dt-cg-debug") << "dt-cg(" << r << "): " << f1 << " and " << f2 << " " << (f1.getOperator()==f2.getOperator()) << " " << areEqual( f1, f2 ) << std::endl; - Assert(d_equalityEngine.hasTerm(f2)); - if( f1.getOperator()==f2.getOperator() && - ( ( f1.getKind()!=DT_SIZE && f1.getKind()!=DT_HEIGHT_BOUND ) || f1[0].getType()==f2[0].getType() ) && - !areEqual( f1, f2 ) ){ - Trace("dt-cg") << "Check " << f1 << " and " << f2 << std::endl; - bool somePairIsDisequal = false; - currentPairs.clear(); - for (unsigned k = 0; k < f1.getNumChildren(); ++ k) { - TNode x = f1[k]; - TNode y = f2[k]; - Assert(d_equalityEngine.hasTerm(x)); - Assert(d_equalityEngine.hasTerm(y)); - //need to consider types for parametric selectors - if( x.getType()!=y.getType() || areDisequal(x, y) ){ - somePairIsDisequal = true; - break; - }else if( !d_equalityEngine.areEqual( x, y ) ){ - Trace("dt-cg") << "Arg #" << k << " is " << x << " " << y << std::endl; - if( d_equalityEngine.isTriggerTerm(x, THEORY_DATATYPES) && d_equalityEngine.isTriggerTerm(y, THEORY_DATATYPES) ){ - TNode x_shared = d_equalityEngine.getTriggerTermRepresentative(x, THEORY_DATATYPES); - TNode y_shared = d_equalityEngine.getTriggerTermRepresentative(y, THEORY_DATATYPES); - Trace("dt-cg") << "Arg #" << k << " shared term is " << x_shared << " " << y_shared << std::endl; - EqualityStatus eqStatus = d_valuation.getEqualityStatus(x_shared, y_shared); - Trace("dt-cg") << "...eq status is " << eqStatus << std::endl; - if( eqStatus==EQUALITY_FALSE_AND_PROPAGATED || eqStatus==EQUALITY_FALSE || eqStatus==EQUALITY_FALSE_IN_MODEL ){ - somePairIsDisequal = true; - break; - }else{ - currentPairs.push_back(make_pair(x_shared, y_shared)); - } + + +void TheoryDatatypes::addCarePairs( quantifiers::TermArgTrie * t1, quantifiers::TermArgTrie * t2, unsigned arity, unsigned depth, unsigned& n_pairs ){ + if( depth==arity ){ + if( t2!=NULL ){ + Node f1 = t1->getNodeData(); + Node f2 = t2->getNodeData(); + if( !areEqual( f1, f2 ) ){ + Trace("dt-cg") << "Check " << f1 << " and " << f2 << std::endl; + vector< pair<TNode, TNode> > currentPairs; + for (unsigned k = 0; k < f1.getNumChildren(); ++ k) { + TNode x = f1[k]; + TNode y = f2[k]; + Assert( d_equalityEngine.hasTerm(x) ); + Assert( d_equalityEngine.hasTerm(y) ); + Assert( !areDisequal( x, y ) ); + if( !d_equalityEngine.areEqual( x, y ) ){ + Trace("dt-cg") << "Arg #" << k << " is " << x << " " << y << std::endl; + if( d_equalityEngine.isTriggerTerm(x, THEORY_DATATYPES) && d_equalityEngine.isTriggerTerm(y, THEORY_DATATYPES) ){ + TNode x_shared = d_equalityEngine.getTriggerTermRepresentative(x, THEORY_DATATYPES); + TNode y_shared = d_equalityEngine.getTriggerTermRepresentative(y, THEORY_DATATYPES); + Trace("dt-cg") << "Arg #" << k << " shared term is " << x_shared << " " << y_shared << std::endl; + EqualityStatus eqStatus = d_valuation.getEqualityStatus(x_shared, y_shared); + Trace("dt-cg") << "...eq status is " << eqStatus << std::endl; + if( eqStatus==EQUALITY_FALSE_AND_PROPAGATED || eqStatus==EQUALITY_FALSE || eqStatus==EQUALITY_FALSE_IN_MODEL ){ + //an argument is disequal, we are done + return; + }else{ + currentPairs.push_back(make_pair(x_shared, y_shared)); } } } - if (!somePairIsDisequal) { - for (unsigned c = 0; c < currentPairs.size(); ++ c) { - Trace("dt-cg-pair") << "Pair : " << currentPairs[c].first << " " << currentPairs[c].second << std::endl; - addCarePair(currentPairs[c].first, currentPairs[c].second); - } + } + for (unsigned c = 0; c < currentPairs.size(); ++ c) { + Trace("dt-cg-pair") << "Pair : " << currentPairs[c].first << " " << currentPairs[c].second << std::endl; + addCarePair(currentPairs[c].first, currentPairs[c].second); + n_pairs++; + } + } + } + }else{ + if( t2==NULL ){ + if( depth<(arity-1) ){ + //add care pairs internal to each child + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = t1->d_data.begin(); it != t1->d_data.end(); ++it ){ + addCarePairs( &it->second, NULL, arity, depth+1, n_pairs ); + } + } + //add care pairs based on each pair of non-disequal arguments + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = t1->d_data.begin(); it != t1->d_data.end(); ++it ){ + std::map< TNode, quantifiers::TermArgTrie >::iterator it2 = it; + ++it2; + for( ; it2 != t1->d_data.end(); ++it2 ){ + if( !areDisequal(it->first, it2->first) ){ + addCarePairs( &it->second, &it2->second, arity, depth+1, n_pairs ); + } + } + } + }else{ + //add care pairs based on product of indices, non-disequal arguments + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = t1->d_data.begin(); it != t1->d_data.end(); ++it ){ + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it2 = t2->d_data.begin(); it2 != t2->d_data.end(); ++it2 ){ + if( !areDisequal(it->first, it2->first) ){ + addCarePairs( &it->second, &it2->second, arity, depth+1, n_pairs ); } } } } } - Trace("dt-cg") << "Done Compute graph for dt." << std::endl; +} + +void TheoryDatatypes::computeCareGraph(){ + unsigned n_pairs = 0; + Trace("dt-cg-summary") << "Compute graph for dt..." << d_functionTerms.size() << " " << d_sharedTerms.size() << std::endl; + Trace("dt-cg") << "Build indices..." << std::endl; + std::map< TypeNode, std::map< Node, quantifiers::TermArgTrie > > index; + std::map< Node, unsigned > arity; + //populate indices + unsigned functionTerms = d_functionTerms.size(); + for( unsigned i=0; i<functionTerms; i++ ){ + TNode f1 = d_functionTerms[i]; + Assert(d_equalityEngine.hasTerm(f1)); + Trace("dt-cg-debug") << "...build for " << f1 << std::endl; + //break into index based on operator, and type of first argument (since some operators are parametric) + Node op = f1.getOperator(); + TypeNode tn = f1[0].getType(); + std::vector< TNode > reps; + bool has_trigger_arg = false; + for( unsigned j=0; j<f1.getNumChildren(); j++ ){ + reps.push_back( d_equalityEngine.getRepresentative( f1[j] ) ); + if( d_equalityEngine.isTriggerTerm( f1[j], THEORY_DATATYPES ) ){ + has_trigger_arg = true; + } + } + //only may contribute to care pairs if has at least one trigger argument + if( has_trigger_arg ){ + index[tn][op].addTerm( f1, reps ); + arity[op] = reps.size(); + } + } + //for each index + for( std::map< TypeNode, std::map< Node, quantifiers::TermArgTrie > >::iterator iti = index.begin(); iti != index.end(); ++iti ){ + for( std::map< Node, quantifiers::TermArgTrie >::iterator itii = iti->second.begin(); itii != iti->second.end(); ++itii ){ + Trace("dt-cg") << "Process index " << itii->first << ", " << iti->first << "..." << std::endl; + addCarePairs( &itii->second, NULL, arity[ itii->first ], 0, n_pairs ); + } + } + Trace("dt-cg-summary") << "...done, # pairs = " << n_pairs << std::endl; } void TheoryDatatypes::collectModelInfo( TheoryModel* m, bool fullModel ){ @@ -1443,7 +1528,7 @@ void TheoryDatatypes::collectModelInfo( TheoryModel* m, bool fullModel ){ if( neqc.isNull() ){ for( unsigned i=0; i<pcons.size(); i++ ){ //must try the infinite ones first - bool cfinite = options::finiteModelFind() ? dt[ i ].isUFinite() : dt[ i ].isFinite(); + bool cfinite = dt[ i ].isInterpretedFinite(); if( pcons[i] && (r==1)==cfinite ){ neqc = DatatypesRewriter::getInstCons( eqc, dt, i ); //for( unsigned j=0; j<neqc.getNumChildren(); j++ ){ @@ -1543,16 +1628,18 @@ Node TheoryDatatypes::getSingletonLemma( TypeNode tn, bool pol ) { void TheoryDatatypes::collectTerms( Node n ) { if( d_collectTermsCache.find( n )==d_collectTermsCache.end() ){ d_collectTermsCache[n] = true; - for( int i=0; i<(int)n.getNumChildren(); i++ ) { - collectTerms( n[i] ); - } + //for( int i=0; i<(int)n.getNumChildren(); i++ ) { + // collectTerms( n[i] ); + //} if( n.getKind() == APPLY_CONSTRUCTOR ){ Debug("datatypes") << " Found constructor " << n << endl; - d_consTerms.push_back( n ); + if( n.getNumChildren()>0 ){ + d_functionTerms.push_back( n ); + } }else{ if( n.getKind() == APPLY_SELECTOR_TOTAL || n.getKind() == DT_SIZE || n.getKind() == DT_HEIGHT_BOUND ){ - d_selTerms.push_back( n ); + d_functionTerms.push_back( n ); //we must also record which selectors exist Trace("dt-collapse-sel") << " Found selector " << n << endl; Node rep = getRepresentative( n[0] ); @@ -2040,21 +2127,19 @@ void TheoryDatatypes::printModelDebug( const char* c ){ if( hasLabel( ei, eqc ) ){ Trace( c ) << getLabel( eqc ); }else{ - NodeListMap::iterator lbl_i = d_labels.find( eqc ); + NodeIntMap::iterator lbl_i = d_labels.find( eqc ); if( lbl_i != d_labels.end() ){ - NodeList* lbl = (*lbl_i).second; - for( NodeList::const_iterator j = lbl->begin(); j != lbl->end(); j++ ){ - Trace( c ) << *j << " "; + for( int j=0; j<(*lbl_i).second; j++ ){ + Trace( c ) << d_labels_data[eqc][j] << " "; } } } Trace( c ) << std::endl; Trace( c ) << " Selectors : " << ( ei->d_selectors ? "yes, " : "no " ); - NodeListMap::iterator sel_i = d_selector_apps.find( eqc ); + NodeIntMap::iterator sel_i = d_selector_apps.find( eqc ); if( sel_i != d_selector_apps.end() ){ - NodeList* sel = (*sel_i).second; - for( NodeList::const_iterator j = sel->begin(); j != sel->end(); j++ ){ - Trace( c ) << *j << " "; + for( int j=0; j<(*sel_i).second; j++ ){ + Trace( c ) << d_selector_apps_data[eqc][j] << " "; } } Trace( c ) << std::endl; diff --git a/src/theory/datatypes/theory_datatypes.h b/src/theory/datatypes/theory_datatypes.h index 4bf04e08c..5722e7822 100644 --- a/src/theory/datatypes/theory_datatypes.h +++ b/src/theory/datatypes/theory_datatypes.h @@ -32,12 +32,17 @@ namespace CVC4 { namespace theory { + +namespace quantifiers{ + class TermArgTrie; +} + namespace datatypes { class TheoryDatatypes : public Theory { private: typedef context::CDChunkList<Node> NodeList; - typedef context::CDHashMap<Node, NodeList*, NodeHashFunction> NodeListMap; + typedef context::CDHashMap< Node, int, NodeHashFunction> NodeIntMap; typedef context::CDHashMap< Node, bool, NodeHashFunction > BoolMap; typedef context::CDHashMap< Node, Node, NodeHashFunction > NodeMap; @@ -157,9 +162,11 @@ private: * NOT is_[constructor_1]( t )...NOT is_[constructor_n]( t ) followed by * is_[constructor_(n+1)]( t ), each of which is a unique tester. */ - NodeListMap d_labels; + NodeIntMap d_labels; + std::map< Node, std::vector< Node > > d_labels_data; /** selector apps for eqch equivalence class */ - NodeListMap d_selector_apps; + NodeIntMap d_selector_apps; + std::map< Node, std::vector< Node > > d_selector_apps_data; /** constructor terms */ //BoolMap d_consEqc; /** Are we in conflict */ @@ -176,10 +183,8 @@ private: std::vector< Node > d_pending; std::map< Node, Node > d_pending_exp; std::vector< Node > d_pending_merge; - /** All the constructor terms that the theory has seen */ - context::CDList<TNode> d_consTerms; - /** All the selector terms that the theory has seen */ - context::CDList<TNode> d_selTerms; + /** All the function terms that the theory has seen */ + context::CDList<TNode> d_functionTerms; /** counter for forcing assignments (ensures fairness) */ unsigned d_dtfCounter; /** expand definition skolem functions */ @@ -216,6 +221,7 @@ private: TNode getEqcConstructor( TNode r ); protected: + void addCarePairs( quantifiers::TermArgTrie * t1, quantifiers::TermArgTrie * t2, unsigned arity, unsigned depth, unsigned& n_pairs ); /** compute care graph */ void computeCareGraph(); diff --git a/src/theory/datatypes/theory_datatypes_type_rules.h b/src/theory/datatypes/theory_datatypes_type_rules.h index 5a3645691..9c8387958 100644 --- a/src/theory/datatypes/theory_datatypes_type_rules.h +++ b/src/theory/datatypes/theory_datatypes_type_rules.h @@ -299,11 +299,10 @@ public: throw TypeCheckingExceptionPrivate(n, "datatype height bound must be non-negative"); } } - return nodeManager->integerType(); + return nodeManager->booleanType(); } };/* class DtHeightBoundTypeRule */ - }/* CVC4::theory::datatypes namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/datatypes/type_enumerator.cpp b/src/theory/datatypes/type_enumerator.cpp index 6c1155237..c0539743f 100644 --- a/src/theory/datatypes/type_enumerator.cpp +++ b/src/theory/datatypes/type_enumerator.cpp @@ -172,7 +172,7 @@ Node DatatypesEnumerator::getTermEnum( TypeNode tn, unsigned i ){ Debug("dt-enum") << "datatype is " << d_type << std::endl; Debug("dt-enum") << "properties : " << d_datatype.isCodatatype() << " " << d_datatype.isRecursiveSingleton(); Debug("dt-enum") << " " << d_datatype.isFinite() << std::endl; - Debug("dt-enum") << " " << d_datatype.isUFinite() << std::endl; + Debug("dt-enum") << " " << d_datatype.isInterpretedFinite() << std::endl; if( d_datatype.isCodatatype() && hasCyclesDt( d_datatype ) ){ //start with uninterpreted constant diff --git a/src/theory/datatypes/type_enumerator.h b/src/theory/datatypes/type_enumerator.h index bbfd951b3..8473b5d69 100644 --- a/src/theory/datatypes/type_enumerator.h +++ b/src/theory/datatypes/type_enumerator.h @@ -159,7 +159,7 @@ public: } if( d_ctor>=d_has_debruijn+d_datatype.getNumConstructors() ){ //try next size limit as long as new terms were generated at last size, or other cases - if( prevSize==d_size_limit || ( d_size_limit==0 && d_datatype.isCodatatype() ) || ( options::finiteModelFind() ? !d_datatype.isUFinite() : !d_datatype.isFinite() ) ){ + if( prevSize==d_size_limit || ( d_size_limit==0 && d_datatype.isCodatatype() ) || !d_datatype.isInterpretedFinite() ){ d_size_limit++; d_ctor = d_zeroCtor; for( unsigned i=0; i<d_sel_sum.size(); i++ ){ diff --git a/src/theory/logic_info.cpp b/src/theory/logic_info.cpp index 04cac7ae5..6ac1c5e32 100644 --- a/src/theory/logic_info.cpp +++ b/src/theory/logic_info.cpp @@ -270,7 +270,7 @@ std::string LogicInfo::getLogicString() const { if(d_theories[THEORY_FP]) { ss << "FP"; ++seen; - } + } if(d_theories[THEORY_DATATYPES]) { ss << "DT"; ++seen; @@ -296,7 +296,10 @@ std::string LogicInfo::getLogicString() const { ss << "FS"; ++seen; } - + if(d_theories[THEORY_SEP]) { + ss << "SEP"; + ++seen; + } if(seen != d_sharingTheories) { Unhandled("can't extract a logic string from LogicInfo; at least one " "active theory is unknown to LogicInfo::getLogicString() !"); @@ -439,6 +442,10 @@ void LogicInfo::setLogicString(std::string logicString) throw(IllegalArgumentExc enableTheory(THEORY_SETS); p += 2; } + if(!strncmp(p, "SEP", 3)) { + enableTheory(THEORY_SEP); + p += 3; + } } } if(*p != '\0') { diff --git a/src/theory/quantifiers/alpha_equivalence.cpp b/src/theory/quantifiers/alpha_equivalence.cpp index 80066d690..a00d6d8a1 100644..100755 --- a/src/theory/quantifiers/alpha_equivalence.cpp +++ b/src/theory/quantifiers/alpha_equivalence.cpp @@ -30,17 +30,30 @@ struct sortTypeOrder { }; Node AlphaEquivalenceNode::registerNode( AlphaEquivalenceNode* aen, QuantifiersEngine* qe, Node q, std::vector< Node >& tt, std::vector< int >& arg_index ) { + std::map< Node, bool > visited; while( !tt.empty() ){ if( tt.size()==arg_index.size()+1 ){ Node t = tt.back(); - Node op = t.hasOperator() ? t.getOperator() : t; - arg_index.push_back( 0 ); + Node op; + if( t.hasOperator() ){ + if( visited.find( t )==visited.end() ){ + visited[t] = true; + op = t.getOperator(); + arg_index.push_back( 0 ); + }else{ + op = t; + arg_index.push_back( -1 ); + } + }else{ + op = t; + arg_index.push_back( 0 ); + } Trace("aeq-debug") << op << " "; aen = &(aen->d_children[op][t.getNumChildren()]); }else{ Node t = tt.back(); int i = arg_index.back(); - if( i==(int)t.getNumChildren() ){ + if( i==-1 || i==(int)t.getNumChildren() ){ tt.pop_back(); arg_index.pop_back(); }else{ @@ -56,9 +69,9 @@ Node AlphaEquivalenceNode::registerNode( AlphaEquivalenceNode* aen, QuantifiersE }else{ if( q.getNumChildren()==2 ){ //lemma ( q <=> d_quant ) - Trace("quant-ae") << "Alpha equivalent : " << std::endl; - Trace("quant-ae") << " " << q << std::endl; - Trace("quant-ae") << " " << aen->d_quant << std::endl; + Trace("alpha-eq") << "Alpha equivalent : " << std::endl; + Trace("alpha-eq") << " " << q << std::endl; + Trace("alpha-eq") << " " << aen->d_quant << std::endl; lem = q.iffNode( aen->d_quant ); }else{ //do not reduce annotated quantified formulas based on alpha equivalence diff --git a/src/theory/quantifiers/alpha_equivalence.h b/src/theory/quantifiers/alpha_equivalence.h index 8e7556eb6..8e7556eb6 100644..100755 --- a/src/theory/quantifiers/alpha_equivalence.h +++ b/src/theory/quantifiers/alpha_equivalence.h diff --git a/src/theory/quantifiers/ambqi_builder.cpp b/src/theory/quantifiers/ambqi_builder.cpp index 5192da7de..97116dee4 100644..100755 --- a/src/theory/quantifiers/ambqi_builder.cpp +++ b/src/theory/quantifiers/ambqi_builder.cpp @@ -787,7 +787,7 @@ void AbsMbqiBuilder::processBuildModel(TheoryModel* m, bool fullModel) { Trace("ambqi-model-debug") << "Initial terms: " << std::endl; for( size_t i=0; i<fm->d_uf_terms[f].size(); i++ ){ Node n = fm->d_uf_terms[f][i]; - if( !n.getAttribute(NoMatchAttribute()) ){ + if( d_qe->getTermDatabase()->isTermActive( n ) ){ Trace("ambqi-model-debug") << " " << n << " -> " << fm->getRepresentativeId( n ) << std::endl; fapps.push_back( n ); } diff --git a/src/theory/quantifiers/ambqi_builder.h b/src/theory/quantifiers/ambqi_builder.h index 3669d38b7..3669d38b7 100644..100755 --- a/src/theory/quantifiers/ambqi_builder.h +++ b/src/theory/quantifiers/ambqi_builder.h diff --git a/src/theory/quantifiers/anti_skolem.cpp b/src/theory/quantifiers/anti_skolem.cpp index c8d18aced..c8d18aced 100644..100755 --- a/src/theory/quantifiers/anti_skolem.cpp +++ b/src/theory/quantifiers/anti_skolem.cpp diff --git a/src/theory/quantifiers/anti_skolem.h b/src/theory/quantifiers/anti_skolem.h index 721371159..721371159 100644..100755 --- a/src/theory/quantifiers/anti_skolem.h +++ b/src/theory/quantifiers/anti_skolem.h diff --git a/src/theory/quantifiers/bounded_integers.cpp b/src/theory/quantifiers/bounded_integers.cpp index d32ef59a1..7184624da 100644..100755 --- a/src/theory/quantifiers/bounded_integers.cpp +++ b/src/theory/quantifiers/bounded_integers.cpp @@ -28,7 +28,7 @@ using namespace CVC4::theory::quantifiers; using namespace CVC4::kind; -BoundedIntegers::RangeModel::RangeModel(BoundedIntegers * bi, Node r, context::Context* c, context::Context* u, bool isProxy) : d_bi(bi), +BoundedIntegers::IntRangeModel::IntRangeModel(BoundedIntegers * bi, Node r, context::Context* c, context::Context* u, bool isProxy) : d_bi(bi), d_range(r), d_curr_max(-1), d_lit_to_range(u), d_range_assertions(c), d_has_range(c,false), d_curr_range(c,-1), d_ranges_proxied(u) { if( options::fmfBoundIntLazy() ){ d_proxy_range = isProxy ? r : NodeManager::currentNM()->mkSkolem( "pbir", r.getType() ); @@ -40,7 +40,7 @@ BoundedIntegers::RangeModel::RangeModel(BoundedIntegers * bi, Node r, context::C } } -void BoundedIntegers::RangeModel::initialize() { +void BoundedIntegers::IntRangeModel::initialize() { //add initial split lemma Node ltr = NodeManager::currentNM()->mkNode( LT, d_proxy_range, NodeManager::currentNM()->mkConst( Rational(0) ) ); ltr = Rewriter::rewrite( ltr ); @@ -55,7 +55,7 @@ void BoundedIntegers::RangeModel::initialize() { d_bi->addLiteralFromRange(ltr_lit, d_range); } -void BoundedIntegers::RangeModel::assertNode(Node n) { +void BoundedIntegers::IntRangeModel::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() ){ @@ -93,7 +93,7 @@ void BoundedIntegers::RangeModel::assertNode(Node n) { } } -void BoundedIntegers::RangeModel::allocateRange() { +void BoundedIntegers::IntRangeModel::allocateRange() { d_curr_max++; int newBound = d_curr_max; Trace("bound-int-proc") << "Allocate range bound " << newBound << " for " << d_range << std::endl; @@ -110,7 +110,7 @@ void BoundedIntegers::RangeModel::allocateRange() { d_bi->addLiteralFromRange(ltr_lit, d_range); } -Node BoundedIntegers::RangeModel::getNextDecisionRequest() { +Node BoundedIntegers::IntRangeModel::getNextDecisionRequest() { //request the current cardinality as a decision literal, if not already asserted for( NodeIntMap::iterator it = d_lit_to_range.begin(); it != d_lit_to_range.end(); ++it ){ int i = (*it).second; @@ -129,7 +129,7 @@ Node BoundedIntegers::RangeModel::getNextDecisionRequest() { return Node::null(); } -bool BoundedIntegers::RangeModel::proxyCurrentRange() { +bool BoundedIntegers::IntRangeModel::proxyCurrentRange() { //Trace("model-engine") << "Range(" << d_range << ") currently is " << d_curr_max.get() << std::endl; if( d_range!=d_proxy_range ){ //int curr = d_curr_range.get(); @@ -148,11 +148,24 @@ bool BoundedIntegers::RangeModel::proxyCurrentRange() { } + + + BoundedIntegers::BoundedIntegers(context::Context* c, QuantifiersEngine* qe) : QuantifiersModule(qe), d_assertions(c){ } +BoundedIntegers::~BoundedIntegers() { + for( std::map< Node, RangeModel * >::iterator it = d_rms.begin(); it != d_rms.end(); ++it ){ + delete it->second; + } +} + +void BoundedIntegers::presolve() { + d_bnd_it.clear(); +} + bool BoundedIntegers::isBound( Node f, Node v ) { return std::find( d_set[f].begin(), d_set[f].end(), v )!=d_set[f].end(); } @@ -172,62 +185,79 @@ bool BoundedIntegers::hasNonBoundVar( Node f, Node b ) { return false; } -void BoundedIntegers::processLiteral( Node f, Node lit, bool pol, +void BoundedIntegers::processLiteral( Node q, Node lit, bool pol, + std::map< Node, unsigned >& bound_lit_type_map, 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; - QuantArith::debugPrintMonomialSum( msum, "bound-int-debug" ); - 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 )!=0 ){ - 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 ); + std::map< int, std::map< Node, bool > >& bound_lit_pol_map, + std::map< int, std::map< Node, Node > >& bound_int_range_term ) { + if( lit.getKind()==GEQ ){ + if( 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; + QuantArith::debugPrintMonomialSum( msum, "bound-int-debug" ); + for( std::map< Node, Node >::iterator it = msum.begin(); it != msum.end(); ++it ){ + if ( !it->first.isNull() && it->first.getKind()==BOUND_VARIABLE && !isBound( q, it->first ) ){ + Node veq; + if( QuantArith::isolate( it->first, msum, veq, GEQ )!=0 ){ + 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 t = n1==it->first ? n2 : n1; + if( !hasNonBoundVar( q, t ) ) { + Trace("bound-int-debug") << "The bound is relevant." << std::endl; + int loru = n1==it->first ? 0 : 1; + bound_lit_type_map[it->first] = BOUND_INT_RANGE; + bound_int_range_term[loru][it->first] = t; + bound_lit_map[loru][it->first] = lit; + bound_lit_pol_map[loru][it->first] = pol; }else{ - n1 = QuantArith::offset( n1, -1 ); + Trace("bound-int-debug") << "The term " << t << " has non-bound variable." << std::endl; } - veq = NodeManager::currentNM()->mkNode( GEQ, n1, n2 ); - } - Trace("bound-int-debug") << "Isolated for " << it->first << " : (" << n1 << " >= " << n2 << ")" << std::endl; - Node t = n1==it->first ? n2 : n1; - if( !hasNonBoundVar( f, t ) ) { - Trace("bound-int-debug") << "The bound is relevant." << std::endl; - int loru = n1==it->first ? 0 : 1; - d_bounds[loru][f][it->first] = t; - bound_lit_map[loru][it->first] = lit; - bound_lit_pol_map[loru][it->first] = pol; - }else{ - Trace("bound-int-debug") << "The term " << t << " has non-bound variable." << std::endl; } } } } } + }else if( lit.getKind()==MEMBER ){ + //TODO: enable this when sets models are fixed + /* + if( !pol && lit[0].getKind()==BOUND_VARIABLE && !isBound( q, lit[0] ) && !lit[1].hasBoundVar() ){ + Trace("bound-int-debug") << "Literal (polarity = " << pol << ") " << lit << " is membership." << std::endl; + bound_lit_type_map[lit[0]] = BOUND_SET_MEMBER; + bound_lit_map[0][lit[0]] = lit; + bound_lit_pol_map[0][lit[0]] = pol; + } + */ }else if( lit.getKind()==LEQ || lit.getKind()==LT || lit.getKind()==GT ) { Message() << "BoundedIntegers : Bad kind for literal : " << lit << std::endl; } } -void BoundedIntegers::process( Node f, Node n, bool pol, +void BoundedIntegers::process( Node q, Node n, bool pol, + std::map< Node, unsigned >& bound_lit_type_map, std::map< int, std::map< Node, Node > >& bound_lit_map, - std::map< int, std::map< Node, bool > >& bound_lit_pol_map ){ + std::map< int, std::map< Node, bool > >& bound_lit_pol_map, + std::map< int, std::map< Node, Node > >& bound_int_range_term ){ if( (n.getKind()==OR && pol) || (n.getKind()==AND && !pol) ){ for( unsigned i=0; i<n.getNumChildren(); i++) { - process( f, n[i], pol, bound_lit_map, bound_lit_pol_map ); + process( q, n[i], pol, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term ); } }else if( n.getKind()==NOT ){ - process( f, n[0], !pol, bound_lit_map, bound_lit_pol_map ); + process( q, n[0], !pol, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term ); }else { - processLiteral( f, n, pol, bound_lit_map, bound_lit_pol_map ); + processLiteral( q, n, pol, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term ); } } @@ -258,58 +288,99 @@ void BoundedIntegers::addLiteralFromRange( Node lit, Node r ) { } } +void BoundedIntegers::setBoundedVar( Node q, Node v, unsigned bound_type ) { + d_bound_type[q][v] = bound_type; + d_set_nums[q][v] = d_set[q].size(); + d_set[q].push_back( v ); + Trace("bound-int-var") << "Bound variable #" << d_set_nums[q][v] << " : " << v << std::endl; +} + 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() || f[0][i].getType().getCardinality().isFinite() ){ - 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]); + + bool success; + do{ + std::map< Node, unsigned > bound_lit_type_map; + std::map< int, std::map< Node, Node > > bound_lit_map; + std::map< int, std::map< Node, bool > > bound_lit_pol_map; + std::map< int, std::map< Node, Node > > bound_int_range_term; + success = false; + process( f, f[1], true, bound_lit_type_map, bound_lit_map, bound_lit_pol_map, bound_int_range_term ); + //for( std::map< Node, Node >::iterator it = d_bounds[0][f].begin(); it != d_bounds[0][f].end(); ++it ){ + for( std::map< Node, unsigned >::iterator it = bound_lit_type_map.begin(); it != bound_lit_type_map.end(); ++it ){ + Node v = it->first; + if( !isBound( f, v ) ){ + bool setBoundVar = false; + if( it->second==BOUND_INT_RANGE ){ + //must have both + if( bound_lit_map[0].find( v )!=bound_lit_map[0].end() && bound_lit_map[1].find( v )!=bound_lit_map[1].end() ){ + setBoundedVar( f, v, BOUND_INT_RANGE ); + setBoundVar = true; 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() ); + //set the bounds + Assert( bound_int_range_term[b].find( v )!=bound_int_range_term[b].end() ); + d_bounds[b][f][v] = bound_int_range_term[b][v]; + } + 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") << "Variable " << v << " is bound because of int range literals " << bound_lit_map[0][v] << " and " << bound_lit_map[1][v] << std::endl; + } + }else if( it->second==BOUND_SET_MEMBER ){ + setBoundedVar( f, v, BOUND_SET_MEMBER ); + setBoundVar = true; + d_setm_range[f][v] = bound_lit_map[0][v][1]; + Trace("bound-int") << "Variable " << v << " is bound because of set membership literal " << bound_lit_map[0][v] << std::endl; + } + if( setBoundVar ){ + //set Attributes on literals + for( unsigned b=0; b<2; b++ ){ + if( 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 ); + }else{ + Assert( it->second!=BOUND_INT_RANGE ); } - 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 ); + } + }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]; + if( d_bound_type[f][v]==BOUND_INT_RANGE ){ Trace("bound-int") << " " << d_bounds[0][f][v] << " <= " << v << " <= " << d_bounds[1][f][v] << " (range is " << d_range[f][v] << ")" << std::endl; + }else if( d_bound_type[f][v]==BOUND_SET_MEMBER ){ + Trace("bound-int") << " " << v << " in " << d_setm_range[f][v] << std::endl; + } + } + + bool bound_success = true; + for( unsigned i=0; i<f[0].getNumChildren(); i++) { + if( d_bound_type[f].find( f[0][i] )==d_bound_type[f].end() ){ + TypeNode tn = f[0][i].getType(); + if( !tn.isSort() && !getTermDatabase()->mayComplete( tn ) ){ + Trace("bound-int-warn") << "Warning : Bounded Integers : Due to quantification on " << f[0][i] << ", could not find bounds for " << f << std::endl; + bound_success = false; + break; + } } - 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( bound_success ){ + d_bound_quants.push_back( f ); + for( unsigned i=0; i<d_set[f].size(); i++) { + Node v = d_set[f][i]; + if( d_bound_type[f][v]==BOUND_INT_RANGE || d_bound_type[f][v]==BOUND_SET_MEMBER ){ + Node r; + if( d_bound_type[f][v]==BOUND_INT_RANGE ){ + r = d_range[f][v]; + }else if( d_bound_type[f][v]==BOUND_SET_MEMBER ){ + r = NodeManager::currentNM()->mkNode( CARD, d_setm_range[f][v] ); + } bool isProxy = false; if( r.hasBoundVar() ){ //introduce a new bound @@ -319,18 +390,15 @@ void BoundedIntegers::registerQuantifier( Node f ) { r = new_range; isProxy = true; } - if( r.getKind()!=CONST_RATIONAL ){ + if( !r.isConst() ){ 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; + Trace("bound-int") << "For " << v << ", bounded Integer Module will try to minimize : " << r << std::endl; d_ranges.push_back( r ); - d_rms[r] = new RangeModel(this, r, d_quantEngine->getSatContext(), d_quantEngine->getUserContext(), isProxy ); + d_rms[r] = new IntRangeModel( this, r, d_quantEngine->getSatContext(), d_quantEngine->getUserContext(), isProxy ); d_rms[r]->initialize(); } } } - }else{ - Trace("bound-int-warn") << "Warning : Bounded Integers : Could not find bounds for " << f << std::endl; - //Message() << "Bound integers : Cannot infer bounds of " << f << std::endl; } } } @@ -376,39 +444,28 @@ Node BoundedIntegers::getNextDecisionRequest() { return Node::null(); } +unsigned BoundedIntegers::getBoundVarType( Node q, Node v ) { + std::map< Node, unsigned >::iterator it = d_bound_type[q].find( v ); + if( it==d_bound_type[q].end() ){ + return BOUND_NONE; + }else{ + return it->second; + } +} + 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 + //get the 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, false, true); - l = Node::null(); - u = Node::null(); - return; - }else{ + if( getRsiSubsitution( f, v, vars, subs, rsi ) ){ u = u.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); l = l.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + }else{ + u = Node::null(); + l = Node::null(); } } } @@ -416,12 +473,86 @@ void BoundedIntegers::getBounds( Node f, Node v, RepSetIterator * rsi, Node & l, 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 ); + if( !l.isNull() ){ + l = d_quantEngine->getModel()->getCurrentModelValue( l ); + } + if( !u.isNull() ){ + 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) && !getLowerBound(f,v).hasBoundVar() && !getUpperBound(f,v).hasBoundVar(); +bool BoundedIntegers::isGroundRange( Node q, Node v ) { + if( isBoundVar(q,v) ){ + if( d_bound_type[q][v]==BOUND_INT_RANGE ){ + return !getLowerBound(q,v).hasBoundVar() && !getUpperBound(q,v).hasBoundVar(); + }else if( d_bound_type[q][v]==BOUND_SET_MEMBER ){ + return !d_setm_range[q][v].hasBoundVar(); + } + } + return false; } + +Node BoundedIntegers::getSetRange( Node q, Node v, RepSetIterator * rsi ) { + Node sr = d_setm_range[q][v]; + if( d_nground_range[q].find(v)!=d_nground_range[q].end() ){ + //get the substitution + std::vector< Node > vars; + std::vector< Node > subs; + if( getRsiSubsitution( q, v, vars, subs, rsi ) ){ + sr = sr.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + }else{ + sr = Node::null(); + } + } + return sr; +} + +Node BoundedIntegers::getSetRangeValue( Node q, Node v, RepSetIterator * rsi ) { + Node sr = getSetRange( q, v, rsi ); + if( !sr.isNull() ){ + Trace("bound-int-rsi") << "Get value in model for..." << sr << std::endl; + sr = d_quantEngine->getModel()->getCurrentModelValue( sr ); + Trace("bound-int-rsi") << "Value is " << sr << std::endl; + } + return sr; +} + +bool BoundedIntegers::getRsiSubsitution( Node q, Node v, std::vector< Node >& vars, std::vector< Node >& subs, RepSetIterator * rsi ) { + + Trace("bound-int-rsi") << "Get bound value in model of variable " << v << std::endl; + Assert( d_set_nums[q].find( v )!=d_set_nums[q].end() ); + int vindex = d_set_nums[q][v]; + Assert( d_set_nums[q][v]==vindex ); + Trace("bound-int-rsi-debug") << " index order is " << vindex << std::endl; + //must take substitution for all variables that are iterating at higher level + for( int i=0; i<vindex; i++) { + Assert( d_set_nums[q][d_set[q][i]]==i ); + Trace("bound-int-rsi") << "Look up the value for " << d_set[q][i] << " " << i << std::endl; + int v = rsi->getVariableOrder( i ); + Assert( q[0][v]==d_set[q][i] ); + Node t = rsi->getCurrentTerm( v ); + Trace("bound-int-rsi") << "term : " << t << std::endl; + vars.push_back( d_set[q][i] ); + subs.push_back( t ); + } + + //check if it has been instantiated + if( !vars.empty() && !d_bnd_it[q][v].hasInstantiated(subs) ){ + if( d_bound_type[q][v]==BOUND_INT_RANGE ){ + //must add the lemma + Node nn = d_nground_range[q][v]; + nn = nn.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + Node lem = NodeManager::currentNM()->mkNode( LEQ, nn, d_range[q][v] ); + Trace("bound-int-lemma") << "*** Add lemma to minimize instantiated non-ground term " << lem << std::endl; + d_quantEngine->getOutputChannel().lemma(lem, false, true); + }else{ + //TODO : sets + } + return false; + }else{ + return true; + } +} + diff --git a/src/theory/quantifiers/bounded_integers.h b/src/theory/quantifiers/bounded_integers.h index 7d15097bd..ab4bcba96 100644..100755 --- a/src/theory/quantifiers/bounded_integers.h +++ b/src/theory/quantifiers/bounded_integers.h @@ -39,31 +39,57 @@ class BoundedIntegers : public QuantifiersModule typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap; typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap; typedef context::CDHashMap<int, bool> IntBoolMap; +public: + enum { + BOUND_FINITE, + BOUND_INT_RANGE, + BOUND_SET_MEMBER, + BOUND_NONE + }; 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]; + //bound type + std::map< Node, std::map< Node, unsigned > > d_bound_type; std::map< Node, std::vector< Node > > d_set; - std::map< Node, std::vector< int > > d_set_nums; + std::map< Node, std::map< Node, int > > d_set_nums; + //integer lower/upper bounds + std::map< Node, std::map< Node, Node > > d_bounds[2]; std::map< Node, std::map< Node, Node > > d_range; std::map< Node, std::map< Node, Node > > d_nground_range; + //set membership range + std::map< Node, std::map< Node, Node > > d_setm_range; void hasFreeVar( Node f, Node n ); void process( Node f, Node n, bool pol, + std::map< Node, unsigned >& bound_lit_type_map, std::map< int, std::map< Node, Node > >& bound_lit_map, - std::map< int, std::map< Node, bool > >& bound_lit_pol_map ); + std::map< int, std::map< Node, bool > >& bound_lit_pol_map, + std::map< int, std::map< Node, Node > >& bound_int_range_term ); void processLiteral( Node f, Node lit, bool pol, + std::map< Node, unsigned >& bound_lit_type_map, std::map< int, std::map< Node, Node > >& bound_lit_map, - std::map< int, std::map< Node, bool > >& bound_lit_pol_map ); + std::map< int, std::map< Node, bool > >& bound_lit_pol_map, + std::map< int, std::map< Node, Node > >& bound_int_range_term ); std::vector< Node > d_bound_quants; private: class RangeModel { + public: + RangeModel(){} + virtual ~RangeModel(){} + virtual void initialize() = 0; + virtual void assertNode(Node n) = 0; + virtual Node getNextDecisionRequest() = 0; + virtual bool proxyCurrentRange() = 0; + }; + class IntRangeModel : public RangeModel { private: BoundedIntegers * d_bi; void allocateRange(); Node d_proxy_range; public: - RangeModel(BoundedIntegers * bi, Node r, context::Context* c, context::Context* u, bool isProxy); + IntRangeModel( BoundedIntegers * bi, Node r, context::Context* c, context::Context* u, bool isProxy); + virtual ~IntRangeModel(){} Node d_range; int d_curr_max; std::map< int, Node > d_range_literal; @@ -108,27 +134,36 @@ private: std::map< Node, std::map< Node, BoundInstTrie > > d_bnd_it; private: void addLiteralFromRange( Node lit, Node r ); + + void setBoundedVar( Node f, Node v, unsigned bound_type ); public: BoundedIntegers( context::Context* c, QuantifiersEngine* qe ); - ~BoundedIntegers() throw() {} - + virtual ~BoundedIntegers(); + + void presolve(); bool needsCheck( Theory::Effort e ); void check( Theory::Effort e, unsigned quant_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]; } + bool isBoundVar( Node q, Node v ) { return std::find( d_set[q].begin(), d_set[q].end(), v )!=d_set[q].end(); } + unsigned getBoundVarType( Node q, Node v ); + unsigned getNumBoundVars( Node q ) { return d_set[q].size(); } + Node getBoundVar( Node q, int i ) { return d_set[q][i]; } + //for integer range + Node getLowerBound( Node q, Node v ){ return d_bounds[0][q][v]; } + Node getUpperBound( Node q, Node v ){ return d_bounds[1][q][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); + //for set range + Node getSetRange( Node q, Node v, RepSetIterator * rsi ); + Node getSetRangeValue( Node q, Node v, RepSetIterator * rsi ); /** Identify this module */ std::string identify() const { return "BoundedIntegers"; } +private: + bool getRsiSubsitution( Node q, Node v, std::vector< Node >& vars, std::vector< Node >& subs, RepSetIterator * rsi ); }; } diff --git a/src/theory/quantifiers/candidate_generator.cpp b/src/theory/quantifiers/candidate_generator.cpp index 43f5ee2fd..a0d9bda0f 100644..100755 --- a/src/theory/quantifiers/candidate_generator.cpp +++ b/src/theory/quantifiers/candidate_generator.cpp @@ -28,7 +28,7 @@ using namespace CVC4::theory; using namespace CVC4::theory::inst; bool CandidateGenerator::isLegalCandidate( Node n ){ - return ( !n.getAttribute(NoMatchAttribute()) && ( !options::cbqi() || !quantifiers::TermDb::hasInstConstAttr(n) ) ); + return d_qe->getTermDatabase()->isTermActive( n ) && ( !options::cbqi() || !quantifiers::TermDb::hasInstConstAttr(n) ); } void CandidateGeneratorQueue::addCandidate( Node n ) { @@ -59,7 +59,7 @@ Node CandidateGeneratorQueue::getNextCandidate(){ } CandidateGeneratorQE::CandidateGeneratorQE( QuantifiersEngine* qe, Node pat ) : - d_qe( qe ), d_term_iter( -1 ){ +CandidateGenerator( qe ), d_term_iter( -1 ){ d_op = qe->getTermDatabase()->getMatchOperator( pat ); Assert( !d_op.isNull() ); d_op_arity = pat.getNumChildren(); @@ -186,7 +186,7 @@ Node CandidateGeneratorQE::getNextCandidate(){ } CandidateGeneratorQELitEq::CandidateGeneratorQELitEq( QuantifiersEngine* qe, Node mpat ) : - d_match_pattern( mpat ), d_qe( qe ){ + CandidateGenerator( qe ), d_match_pattern( mpat ){ Assert( mpat.getKind()==EQUAL ); for( unsigned i=0; i<2; i++ ){ if( !quantifiers::TermDb::hasInstConstAttr(mpat[i]) ){ @@ -225,7 +225,7 @@ Node CandidateGeneratorQELitEq::getNextCandidate(){ CandidateGeneratorQELitDeq::CandidateGeneratorQELitDeq( QuantifiersEngine* qe, Node mpat ) : - d_match_pattern( mpat ), d_qe( qe ){ +CandidateGenerator( qe ), d_match_pattern( mpat ){ Assert( d_match_pattern.getKind()==EQUAL || d_match_pattern.getKind()==IFF ); d_match_pattern_type = d_match_pattern[0].getType(); @@ -259,7 +259,7 @@ Node CandidateGeneratorQELitDeq::getNextCandidate(){ CandidateGeneratorQEAll::CandidateGeneratorQEAll( QuantifiersEngine* qe, Node mpat ) : - d_match_pattern( mpat ), d_qe( qe ){ + CandidateGenerator( qe ), d_match_pattern( mpat ){ d_match_pattern_type = mpat.getType(); Assert( mpat.getKind()==INST_CONSTANT ); d_f = quantifiers::TermDb::getInstConstAttr( mpat ); diff --git a/src/theory/quantifiers/candidate_generator.h b/src/theory/quantifiers/candidate_generator.h index 18ef6a086..4fc6969fc 100644..100755 --- a/src/theory/quantifiers/candidate_generator.h +++ b/src/theory/quantifiers/candidate_generator.h @@ -33,8 +33,10 @@ namespace inst { /** base class for generating candidates for matching */ class CandidateGenerator { +protected: + QuantifiersEngine* d_qe; public: - CandidateGenerator(){} + CandidateGenerator( QuantifiersEngine* qe ) : d_qe( qe ){} virtual ~CandidateGenerator(){} /** Get candidates functions. These set up a context to get all match candidates. @@ -54,7 +56,7 @@ public: virtual void resetInstantiationRound() = 0; public: /** legal candidate */ - static bool isLegalCandidate( Node n ); + bool isLegalCandidate( Node n ); };/* class CandidateGenerator */ /** candidate generator queue (for manual candidate generation) */ @@ -63,7 +65,7 @@ private: std::vector< Node > d_candidates; int d_candidate_index; public: - CandidateGeneratorQueue() : d_candidate_index( 0 ){} + CandidateGeneratorQueue( QuantifiersEngine* qe ) : CandidateGenerator( qe ), d_candidate_index( 0 ){} ~CandidateGeneratorQueue() throw() {} void addCandidate( Node n ); @@ -80,8 +82,6 @@ class CandidateGeneratorQE : public CandidateGenerator private: //operator you are looking for Node d_op; - //instantiator pointer - QuantifiersEngine* d_qe; //the equality class iterator unsigned d_op_arity; std::vector< quantifiers::TermArgTrie* > d_tindex; @@ -122,8 +122,6 @@ private: Node d_match_pattern; Node d_match_gterm; bool d_do_mgt; - //einstantiator pointer - QuantifiersEngine* d_qe; public: CandidateGeneratorQELitEq( QuantifiersEngine* qe, Node mpat ); ~CandidateGeneratorQELitEq() throw() {} @@ -142,8 +140,6 @@ private: Node d_match_pattern; //type of disequality TypeNode d_match_pattern_type; - //einstantiator pointer - QuantifiersEngine* d_qe; public: CandidateGeneratorQELitDeq( QuantifiersEngine* qe, Node mpat ); ~CandidateGeneratorQELitDeq() throw() {} @@ -161,8 +157,6 @@ private: //equality you are trying to match equalities for Node d_match_pattern; TypeNode d_match_pattern_type; - //einstantiator pointer - QuantifiersEngine* d_qe; // quantifier/index for the variable we are matching Node d_f; unsigned d_index; diff --git a/src/theory/quantifiers/ce_guided_instantiation.cpp b/src/theory/quantifiers/ce_guided_instantiation.cpp index d9059a3e6..71bf7c426 100644..100755 --- a/src/theory/quantifiers/ce_guided_instantiation.cpp +++ b/src/theory/quantifiers/ce_guided_instantiation.cpp @@ -21,6 +21,8 @@ #include "theory/quantifiers/first_order_model.h" #include "theory/quantifiers/term_database.h" #include "theory/theory_engine.h" +#include "prop/prop_engine.h" +#include "theory/bv/theory_bv_rewriter.h" using namespace CVC4::kind; using namespace std; @@ -46,7 +48,7 @@ void CegConjecture::assign( Node q ) { Assert( q.getKind()==FORALL ); d_assert_quant = q; //register with single invocation if applicable - if( d_qe->getTermDatabase()->isQAttrSygus( d_assert_quant ) && options::cegqiSingleInv() ){ + if( d_qe->getTermDatabase()->isQAttrSygus( d_assert_quant ) && options::cegqiSingleInvMode()!=CEGQI_SI_MODE_NONE ){ d_ceg_si->initialize( q ); if( q!=d_ceg_si->d_quant ){ //Node red_lem = NodeManager::currentNM()->mkNode( OR, q.negate(), d_cegqi_si->d_quant ); @@ -55,12 +57,15 @@ void CegConjecture::assign( Node q ) { } } d_quant = q; + Assert( d_candidates.empty() ); + std::vector< Node > vars; for( unsigned i=0; i<q[0].getNumChildren(); i++ ){ + vars.push_back( q[0][i] ); d_candidates.push_back( NodeManager::currentNM()->mkSkolem( "e", q[0][i].getType() ) ); } Trace("cegqi") << "Base quantified formula is : " << q << std::endl; //construct base instantiation - d_base_inst = Rewriter::rewrite( d_qe->getInstantiation( q, d_candidates ) ); + d_base_inst = Rewriter::rewrite( d_qe->getInstantiation( q, vars, d_candidates ) ); Trace("cegqi") << "Base instantiation is : " << d_base_inst << std::endl; if( d_qe->getTermDatabase()->isQAttrSygus( d_assert_quant ) ){ CegInstantiation::collectDisjuncts( d_base_inst, d_base_disj ); @@ -130,14 +135,18 @@ Node CegConjecture::getLiteral( QuantifiersEngine * qe, int i ) { qe->getOutputChannel().lemma( lem ); qe->getOutputChannel().requirePhase( lit, true ); - if( getCegqiFairMode()==CEGQI_FAIR_DT_HEIGHT_PRED ){ + if( getCegqiFairMode()==CEGQI_FAIR_DT_HEIGHT_PRED || getCegqiFairMode()==CEGQI_FAIR_DT_SIZE_PRED ){ //implies height bounds on each candidate variable std::vector< Node > lem_c; for( unsigned j=0; j<d_candidates.size(); j++ ){ - lem_c.push_back( NodeManager::currentNM()->mkNode( DT_HEIGHT_BOUND, d_candidates[j], c ) ); + if( getCegqiFairMode()==CEGQI_FAIR_DT_HEIGHT_PRED ){ + lem_c.push_back( NodeManager::currentNM()->mkNode( DT_HEIGHT_BOUND, d_candidates[j], c ) ); + }else{ + //lem_c.push_back( NodeManager::currentNM()->mkNode( DT_SIZE_BOUND, d_candidates[j], c ) ); + } } Node hlem = NodeManager::currentNM()->mkNode( OR, lit.negate(), lem_c.size()==1 ? lem_c[0] : NodeManager::currentNM()->mkNode( AND, lem_c ) ); - Trace("cegqi-lemma") << "Cegqi::Lemma : Fairness expansion (dt-height-pred) : " << hlem << std::endl; + Trace("cegqi-lemma") << "Cegqi::Lemma : Fairness expansion (pred) : " << hlem << std::endl; qe->getOutputChannel().lemma( hlem ); } return lit; @@ -257,8 +266,45 @@ void CegInstantiation::check( Theory::Effort e, unsigned quant_e ) { } } +void CegInstantiation::preRegisterQuantifier( Node q ) { + if( options::sygusDirectEval() ){ + if( q.getNumChildren()==3 && q[2].getKind()==INST_PATTERN_LIST && q[2][0].getKind()==INST_PATTERN ){ + //check whether it is an evaluation axiom + Node pat = q[2][0][0]; + if( pat.getKind()==APPLY_UF ){ + TypeNode tn = pat[0].getType(); + if( datatypes::DatatypesRewriter::isTypeDatatype(tn) ){ + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + if( dt.isSygus() ){ + //do unfolding if it induces Boolean structure, + //do direct evaluation if it does not induce Boolean structure, + // the reasoning is unfolding over these terms does not lead to helpful conflict analysis, and introduces many shared terms + bool directEval = true; + TypeNode ptn = pat.getType(); + if( ptn.isBoolean() || ptn.isBitVector() ){ + directEval = false; + }else{ + unsigned cindex = Datatype::indexOf(pat[0].getOperator().toExpr() ); + Node base = d_quantEngine->getTermDatabaseSygus()->getGenericBase( tn, dt, cindex ); + Trace("cegqi-debug") << "Generic base term for " << pat[0] << " is " << base << std::endl; + if( base.getKind()==ITE ){ + directEval = false; + } + } + if( directEval ){ + //take ownership of this quantified formula (will use direct evaluation instead of unfolding instantiation) + d_quantEngine->setOwner( q, this ); + d_eval_axioms[q] = true; + } + } + } + } + } + } +} + void CegInstantiation::registerQuantifier( Node q ) { - if( d_quantEngine->getOwner( q )==this ){ + if( d_quantEngine->getOwner( q )==this && d_eval_axioms.find( q )==d_eval_axioms.end() ){ if( !d_conj->isAssigned() ){ Trace("cegqi") << "Register conjecture : " << q << std::endl; d_conj->assign( q ); @@ -278,7 +324,7 @@ void CegInstantiation::registerQuantifier( Node q ) { if( it!=d_uf_measure.end() ){ mc.push_back( NodeManager::currentNM()->mkNode( APPLY_UF, it->second, d_conj->d_candidates[j] ) ); } - }else if( d_conj->getCegqiFairMode()==CEGQI_FAIR_DT_HEIGHT_PRED ){ + }else if( d_conj->getCegqiFairMode()==CEGQI_FAIR_DT_HEIGHT_PRED || d_conj->getCegqiFairMode()==CEGQI_FAIR_DT_SIZE_PRED ){ //measure term is a fresh constant mc.push_back( NodeManager::currentNM()->mkSkolem( "K", NodeManager::currentNM()->integerType() ) ); } @@ -291,6 +337,8 @@ void CegInstantiation::registerQuantifier( Node q ) { }else{ Assert( d_conj->d_quant==q ); } + }else{ + Trace("cegqi-debug") << "Register quantifier : " << q << std::endl; } } @@ -317,7 +365,7 @@ Node CegInstantiation::getNextDecisionRequest() { Trace("cegqi-debug") << "CEGQI : Decide next on : " << req_dec[i] << "..." << std::endl; return req_dec[i]; }else{ - Trace("cegqi-debug") << "CEGQI : " << req_dec[i] << " already has value " << value << std::endl; + Trace("cegqi-debug2") << "CEGQI : " << req_dec[i] << " already has value " << value << std::endl; } } @@ -350,6 +398,11 @@ void CegInstantiation::checkCegConjecture( CegConjecture * conj ) { Trace("cegqi-engine-debug") << conj->d_candidates[i] << " "; } Trace("cegqi-engine-debug") << std::endl; + Trace("cegqi-engine-debug") << " * Candidate ce skolems : "; + for( unsigned i=0; i<conj->d_ce_sk.size(); i++ ){ + Trace("cegqi-engine-debug") << conj->d_ce_sk[i] << " "; + } + Trace("cegqi-engine-debug") << std::endl; if( conj->getCegqiFairMode()!=CEGQI_FAIR_NONE ){ Trace("cegqi-engine") << " * Current term size : " << conj->d_curr_lit.get() << std::endl; } @@ -374,6 +427,24 @@ void CegInstantiation::checkCegConjecture( CegConjecture * conj ) { } std::vector< Node > model_values; if( getModelValues( conj, conj->d_candidates, model_values ) ){ + if( options::sygusDirectEval() ){ + std::vector< Node > eager_eval_lem; + for( unsigned j=0; j<conj->d_candidates.size(); j++ ){ + d_quantEngine->getTermDatabaseSygus()->registerModelValue( conj->d_candidates[j], model_values[j], eager_eval_lem ); + } + if( !eager_eval_lem.empty() ){ + for( unsigned j=0; j<eager_eval_lem.size(); j++ ){ + Node lem = eager_eval_lem[j]; + if( d_quantEngine->getTheoryEngine()->isTheoryEnabled(THEORY_BV) ){ + //FIXME: hack to incorporate hacks from BV for division by zero + lem = bv::TheoryBVRewriter::eliminateBVSDiv( lem ); + } + Trace("cegqi-lemma") << "Cegqi::Lemma : evaluation : " << lem << std::endl; + d_quantEngine->addLemma( lem ); + } + return; + } + } //check if we must apply fairness lemmas if( conj->getCegqiFairMode()==CEGQI_FAIR_UF_DT_SIZE ){ std::vector< Node > lems; @@ -390,6 +461,7 @@ void CegInstantiation::checkCegConjecture( CegConjecture * conj ) { } } //must get a counterexample to the value of the current candidate + Assert( conj->d_candidates.size()==model_values.size() ); Node inst = conj->d_base_inst.substitute( conj->d_candidates.begin(), conj->d_candidates.end(), model_values.begin(), model_values.end() ); //check whether we will run CEGIS on inner skolem variables bool sk_refine = ( !conj->isGround() || conj->d_refine_count==0 ); @@ -422,10 +494,30 @@ void CegInstantiation::checkCegConjecture( CegConjecture * conj ) { Node lem = NodeManager::currentNM()->mkNode( OR, ic ); lem = Rewriter::rewrite( lem ); d_last_inst_si = false; + //eagerly unfold applications of evaluation function + if( options::sygusDirectEval() ){ + Trace("cegqi-eager") << "pre-unfold counterexample : " << lem << std::endl; + std::map< Node, Node > visited_n; + lem = getEagerUnfold( lem, visited_n ); + } + Trace("cegqi-lemma") << "Cegqi::Lemma : counterexample : " << lem << std::endl; - d_quantEngine->addLemma( lem ); - ++(d_statistics.d_cegqi_lemmas_ce); - Trace("cegqi-engine") << " ...find counterexample." << std::endl; + if( d_quantEngine->addLemma( lem ) ){ + ++(d_statistics.d_cegqi_lemmas_ce); + Trace("cegqi-engine") << " ...find counterexample." << std::endl; + }else{ + //this may happen if we eagerly unfold, simplify to true + if( !options::sygusDirectEval() ){ + Trace("cegqi-engine") << " ...FAILED to add candidate!" << std::endl; + }else{ + Trace("cegqi-engine-debug") << " ...FAILED to add candidate!" << std::endl; + } + if( conj->d_refine_count==0 ){ + //immediately go to refine candidate + checkCegConjecture( conj ); + return; + } + } } }else{ @@ -589,6 +681,74 @@ void CegInstantiation::getMeasureLemmas( Node n, Node v, std::vector< Node >& le } } +Node CegInstantiation::getEagerUnfold( Node n, std::map< Node, Node >& visited ) { + std::map< Node, Node >::iterator itv = visited.find( n ); + if( itv==visited.end() ){ + Trace("cegqi-eager-debug") << "getEagerUnfold " << n << std::endl; + Node ret; + if( n.getKind()==APPLY_UF ){ + TypeNode tn = n[0].getType(); + Trace("cegqi-eager-debug") << "check " << n[0].getType() << std::endl; + if( datatypes::DatatypesRewriter::isTypeDatatype(tn) ){ + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + if( dt.isSygus() ){ + Trace("cegqi-eager") << "Unfold eager : " << n << std::endl; + Node bTerm = d_quantEngine->getTermDatabaseSygus()->sygusToBuiltin( n[0], tn ); + Trace("cegqi-eager") << "Built-in term : " << bTerm << std::endl; + std::vector< Node > vars; + std::vector< Node > subs; + Node var_list = Node::fromExpr( dt.getSygusVarList() ); + Assert( var_list.getNumChildren()+1==n.getNumChildren() ); + for( unsigned j=0; j<var_list.getNumChildren(); j++ ){ + vars.push_back( var_list[j] ); + } + for( unsigned j=1; j<n.getNumChildren(); j++ ){ + Node nc = getEagerUnfold( n[j], visited ); + if( var_list[j-1].getType().isBoolean() ){ + //TODO: remove this case when boolean term conversion is eliminated + Node c = NodeManager::currentNM()->mkConst(BitVector(1u, 1u)); + subs.push_back( nc.eqNode( c ) ); + }else{ + subs.push_back( nc ); + } + Assert( subs[j-1].getType()==var_list[j-1].getType() ); + } + Assert( vars.size()==subs.size() ); + bTerm = bTerm.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + Trace("cegqi-eager") << "Built-in term after subs : " << bTerm << std::endl; + Trace("cegqi-eager-debug") << "Types : " << bTerm.getType() << " " << n.getType() << std::endl; + Assert( n.getType()==bTerm.getType() ); + ret = bTerm; + } + } + } + if( ret.isNull() ){ + if( n.getKind()!=FORALL ){ + bool childChanged = false; + std::vector< Node > children; + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + Node nc = getEagerUnfold( n[i], visited ); + childChanged = childChanged || n[i]!=nc; + children.push_back( nc ); + } + if( childChanged ){ + if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ + children.insert( children.begin(), n.getOperator() ); + } + ret = NodeManager::currentNM()->mkNode( n.getKind(), children ); + } + } + if( ret.isNull() ){ + ret = n; + } + } + visited[n] = ret; + return ret; + }else{ + return itv->second; + } +} + void CegInstantiation::printSynthSolution( std::ostream& out ) { if( d_conj->isAssigned() ){ Trace("cegqi-debug") << "Printing synth solution..." << std::endl; diff --git a/src/theory/quantifiers/ce_guided_instantiation.h b/src/theory/quantifiers/ce_guided_instantiation.h index 57dc31850..c8b41c035 100644..100755 --- a/src/theory/quantifiers/ce_guided_instantiation.h +++ b/src/theory/quantifiers/ce_guided_instantiation.h @@ -126,6 +126,8 @@ private: CegConjecture * d_conj; /** last instantiation by single invocation module? */ bool d_last_inst_si; + /** evaluation axioms */ + std::map< Node, bool > d_eval_axioms; private: //for enforcing fairness /** measure functions */ std::map< TypeNode, Node > d_uf_measure; @@ -139,6 +141,8 @@ private: //for enforcing fairness std::map< Node, std::map< int, Node > > d_size_term_lemma; /** get measure lemmas */ void getMeasureLemmas( Node n, Node v, std::vector< Node >& lems ); + /** get eager unfolding */ + Node getEagerUnfold( Node n, std::map< Node, Node >& visited ); private: /** check conjecture */ void checkCegConjecture( CegConjecture * conj ); @@ -156,6 +160,7 @@ public: /* Call during quantifier engine's check */ void check( Theory::Effort e, unsigned quant_e ); /* Called for new quantifiers */ + void preRegisterQuantifier( Node q ); void registerQuantifier( Node q ); void assertNode( Node n ); Node getNextDecisionRequest(); diff --git a/src/theory/quantifiers/ce_guided_single_inv.cpp b/src/theory/quantifiers/ce_guided_single_inv.cpp index 33856d226..3177739ac 100644..100755 --- a/src/theory/quantifiers/ce_guided_single_inv.cpp +++ b/src/theory/quantifiers/ce_guided_single_inv.cpp @@ -111,6 +111,7 @@ void CegConjectureSingleInv::getInitialSingleInvLemma( std::vector< Node >& lems void CegConjectureSingleInv::initialize( Node q ) { Assert( d_quant.isNull() ); + Assert( options::cegqiSingleInvMode()!=CEGQI_SI_MODE_NONE ); //initialize data d_quant = q; //process @@ -121,6 +122,7 @@ void CegConjectureSingleInv::initialize( Node q ) { std::map< Node, std::map< Node, std::vector< Node > > > prog_invoke; std::vector< Node > progs; std::map< Node, std::map< Node, bool > > contains; + bool is_syntax_restricted = false; for( unsigned i=0; i<q[0].getNumChildren(); i++ ){ progs.push_back( q[0][i] ); //check whether all types have ITE @@ -131,161 +133,173 @@ void CegConjectureSingleInv::initialize( Node q ) { d_has_ites = false; } } + Assert( datatypes::DatatypesRewriter::isTypeDatatype(tn) ); + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + Assert( dt.isSygus() ); + if( !dt.getSygusAllowAll() ){ + is_syntax_restricted = true; + } } - Node qq = q[1]; - if( q[1].getKind()==NOT && q[1][0].getKind()==FORALL ){ - qq = q[1][0][1]; - }else{ - qq = TermDb::simpleNegate( qq ); - } - //remove the deep embedding - std::map< Node, Node > visited; - std::vector< TypeNode > types; - std::vector< Node > order_vars; - std::map< Node, Node > single_inv_app_map; - int type_valid = 0; - qq = removeDeepEmbedding( qq, progs, types, type_valid, visited ); - Trace("cegqi-si-debug") << "- Remove deep embedding, got : " << qq << ", type valid = " << type_valid << std::endl; + //abort if not aggressive bool singleInvocation = true; - if( type_valid==0 ){ - //process the single invocation-ness of the property - d_sip->init( types, qq ); - Trace("cegqi-si") << "- Partitioned to single invocation parts : " << std::endl; - d_sip->debugPrint( "cegqi-si" ); - //map from program to bound variables - for( unsigned j=0; j<progs.size(); j++ ){ - Node prog = progs[j]; - std::map< Node, Node >::iterator it_nsi = d_nsi_op_map.find( prog ); - if( it_nsi!=d_nsi_op_map.end() ){ - Node op = it_nsi->second; - std::map< Node, Node >::iterator it_fov = d_sip->d_func_fo_var.find( op ); - if( it_fov!=d_sip->d_func_fo_var.end() ){ - Node pv = it_fov->second; - Assert( d_sip->d_func_inv.find( op )!=d_sip->d_func_inv.end() ); - Node inv = d_sip->d_func_inv[op]; - single_inv_app_map[prog] = inv; - Trace("cegqi-si") << " " << pv << ", " << inv << " is associated with program " << prog << std::endl; - d_prog_to_sol_index[prog] = order_vars.size(); - order_vars.push_back( pv ); + if( options::cegqiSingleInvMode()==CEGQI_SI_MODE_USE && is_syntax_restricted ){ + singleInvocation = false; + Trace("cegqi-si") << "...grammar is restricted, do not use single invocation techniques." << std::endl; + }else{ + Node qq = q[1]; + if( q[1].getKind()==NOT && q[1][0].getKind()==FORALL ){ + qq = q[1][0][1]; + }else{ + qq = TermDb::simpleNegate( qq ); + } + //remove the deep embedding + std::map< Node, Node > visited; + std::vector< TypeNode > types; + std::vector< Node > order_vars; + std::map< Node, Node > single_inv_app_map; + int type_valid = 0; + qq = removeDeepEmbedding( qq, progs, types, type_valid, visited ); + Trace("cegqi-si-debug") << "- Remove deep embedding, got : " << qq << ", type valid = " << type_valid << std::endl; + if( type_valid==0 ){ + //process the single invocation-ness of the property + d_sip->init( types, qq ); + Trace("cegqi-si") << "- Partitioned to single invocation parts : " << std::endl; + d_sip->debugPrint( "cegqi-si" ); + //map from program to bound variables + for( unsigned j=0; j<progs.size(); j++ ){ + Node prog = progs[j]; + std::map< Node, Node >::iterator it_nsi = d_nsi_op_map.find( prog ); + if( it_nsi!=d_nsi_op_map.end() ){ + Node op = it_nsi->second; + std::map< Node, Node >::iterator it_fov = d_sip->d_func_fo_var.find( op ); + if( it_fov!=d_sip->d_func_fo_var.end() ){ + Node pv = it_fov->second; + Assert( d_sip->d_func_inv.find( op )!=d_sip->d_func_inv.end() ); + Node inv = d_sip->d_func_inv[op]; + single_inv_app_map[prog] = inv; + Trace("cegqi-si") << " " << pv << ", " << inv << " is associated with program " << prog << std::endl; + d_prog_to_sol_index[prog] = order_vars.size(); + order_vars.push_back( pv ); + } + }else{ + //does not mention the function } - }else{ - //does not mention the function } - } - //reorder the variables - Assert( d_sip->d_func_vars.size()==order_vars.size() ); - d_sip->d_func_vars.clear(); - d_sip->d_func_vars.insert( d_sip->d_func_vars.begin(), order_vars.begin(), order_vars.end() ); + //reorder the variables + Assert( d_sip->d_func_vars.size()==order_vars.size() ); + d_sip->d_func_vars.clear(); + d_sip->d_func_vars.insert( d_sip->d_func_vars.begin(), order_vars.begin(), order_vars.end() ); - //check if it is single invocation - if( !d_sip->d_conjuncts[1].empty() ){ - singleInvocation = false; - if( options::cegqiSingleInvPartial() ){ - //this enables partially single invocation techniques - d_nsingle_inv = d_sip->getNonSingleInvocation(); - d_nsingle_inv = TermDb::simpleNegate( d_nsingle_inv ); - d_full_inv = d_sip->getFullSpecification(); - d_full_inv = TermDb::simpleNegate( d_full_inv ); - singleInvocation = true; - }else if( options::sygusInvTemplMode() != SYGUS_INV_TEMPL_MODE_NONE ){ - //if we are doing invariant templates, then construct the template - std::map< Node, bool > has_inv; - std::map< Node, std::vector< Node > > inv_pre_post[2]; - for( unsigned i=0; i<d_sip->d_conjuncts[2].size(); i++ ){ - std::vector< Node > disjuncts; - Node func; - int pol = -1; - Trace("cegqi-inv") << "INV process " << d_sip->d_conjuncts[2][i] << std::endl; - d_sip->extractInvariant( d_sip->d_conjuncts[2][i], func, pol, disjuncts ); - if( pol>=0 ){ - Assert( d_nsi_op_map_to_prog.find( func )!=d_nsi_op_map_to_prog.end() ); - Node prog = d_nsi_op_map_to_prog[func]; - Trace("cegqi-inv") << "..." << ( pol==0 ? "pre" : "post" ) << "-condition for " << prog << "." << std::endl; - Node c = disjuncts.empty() ? d_qe->getTermDatabase()->d_false : ( disjuncts.size()==1 ? disjuncts[0] : NodeManager::currentNM()->mkNode( OR, disjuncts ) ); - c = pol==0 ? TermDb::simpleNegate( c ) : c; - Trace("cegqi-inv-debug") << "...extracted : " << c << std::endl; - inv_pre_post[pol][prog].push_back( c ); - has_inv[prog] = true; - }else{ - Trace("cegqi-inv") << "...no status." << std::endl; + //check if it is single invocation + if( !d_sip->d_conjuncts[1].empty() ){ + singleInvocation = false; + if( options::cegqiSingleInvPartial() ){ + //this enables partially single invocation techniques + d_nsingle_inv = d_sip->getNonSingleInvocation(); + d_nsingle_inv = TermDb::simpleNegate( d_nsingle_inv ); + d_full_inv = d_sip->getFullSpecification(); + d_full_inv = TermDb::simpleNegate( d_full_inv ); + singleInvocation = true; + }else if( options::sygusInvTemplMode() != SYGUS_INV_TEMPL_MODE_NONE ){ + //if we are doing invariant templates, then construct the template + std::map< Node, bool > has_inv; + std::map< Node, std::vector< Node > > inv_pre_post[2]; + for( unsigned i=0; i<d_sip->d_conjuncts[2].size(); i++ ){ + std::vector< Node > disjuncts; + Node func; + int pol = -1; + Trace("cegqi-inv") << "INV process " << d_sip->d_conjuncts[2][i] << std::endl; + d_sip->extractInvariant( d_sip->d_conjuncts[2][i], func, pol, disjuncts ); + if( pol>=0 ){ + Assert( d_nsi_op_map_to_prog.find( func )!=d_nsi_op_map_to_prog.end() ); + Node prog = d_nsi_op_map_to_prog[func]; + Trace("cegqi-inv") << "..." << ( pol==0 ? "pre" : "post" ) << "-condition for " << prog << "." << std::endl; + Node c = disjuncts.empty() ? d_qe->getTermDatabase()->d_false : ( disjuncts.size()==1 ? disjuncts[0] : NodeManager::currentNM()->mkNode( OR, disjuncts ) ); + c = pol==0 ? TermDb::simpleNegate( c ) : c; + Trace("cegqi-inv-debug") << "...extracted : " << c << std::endl; + inv_pre_post[pol][prog].push_back( c ); + has_inv[prog] = true; + }else{ + Trace("cegqi-inv") << "...no status." << std::endl; + } } - } - Trace("cegqi-inv") << "Constructing invariant templates..." << std::endl; - //now, contruct the template for the invariant(s) - std::map< Node, Node > prog_templ; - for( std::map< Node, bool >::iterator iti = has_inv.begin(); iti != has_inv.end(); ++iti ){ - Node prog = iti->first; - Trace("cegqi-inv") << "...for " << prog << "..." << std::endl; - Trace("cegqi-inv") << " args : "; + Trace("cegqi-inv") << "Constructing invariant templates..." << std::endl; + //now, contruct the template for the invariant(s) + std::map< Node, Node > prog_templ; + for( std::map< Node, bool >::iterator iti = has_inv.begin(); iti != has_inv.end(); ++iti ){ + Node prog = iti->first; + Trace("cegqi-inv") << "...for " << prog << "..." << std::endl; + Trace("cegqi-inv") << " args : "; + for( unsigned j=0; j<d_sip->d_si_vars.size(); j++ ){ + std::stringstream ss; + ss << "i_" << j; + Node v = NodeManager::currentNM()->mkBoundVar( ss.str(), d_sip->d_si_vars[j].getType() ); + d_prog_templ_vars[prog].push_back( v ); + Trace("cegqi-inv") << v << " "; + } + Trace("cegqi-inv") << std::endl; + Node pre = inv_pre_post[0][prog].empty() ? NodeManager::currentNM()->mkConst( false ) : + ( inv_pre_post[0][prog].size()==1 ? inv_pre_post[0][prog][0] : NodeManager::currentNM()->mkNode( OR, inv_pre_post[0][prog] ) ); + d_trans_pre[prog] = pre.substitute( d_sip->d_si_vars.begin(), d_sip->d_si_vars.end(), d_prog_templ_vars[prog].begin(), d_prog_templ_vars[prog].end() ); + Node post = inv_pre_post[1][prog].empty() ? NodeManager::currentNM()->mkConst( true ) : + ( inv_pre_post[1][prog].size()==1 ? inv_pre_post[1][prog][0] : NodeManager::currentNM()->mkNode( AND, inv_pre_post[1][prog] ) ); + d_trans_post[prog] = post.substitute( d_sip->d_si_vars.begin(), d_sip->d_si_vars.end(), d_prog_templ_vars[prog].begin(), d_prog_templ_vars[prog].end() ); + Trace("cegqi-inv") << " precondition : " << d_trans_pre[prog] << std::endl; + Trace("cegqi-inv") << " postcondition : " << d_trans_post[prog] << std::endl; + Node invariant = single_inv_app_map[prog]; + invariant = invariant.substitute( d_sip->d_si_vars.begin(), d_sip->d_si_vars.end(), d_prog_templ_vars[prog].begin(), d_prog_templ_vars[prog].end() ); + Trace("cegqi-inv") << " invariant : " << invariant << std::endl; + //construct template + Node templ; + if( options::sygusInvTemplMode() == SYGUS_INV_TEMPL_MODE_PRE ){ + //templ = NodeManager::currentNM()->mkNode( AND, NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], invariant ), d_trans_post[prog] ); + templ = NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], invariant ); + }else{ + Assert( options::sygusInvTemplMode() == SYGUS_INV_TEMPL_MODE_POST ); + //templ = NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], NodeManager::currentNM()->mkNode( AND, d_trans_post[prog], invariant ) ); + templ = NodeManager::currentNM()->mkNode( AND, d_trans_post[prog], invariant ); + } + visited.clear(); + templ = addDeepEmbedding( templ, visited ); + Trace("cegqi-inv") << " template : " << templ << std::endl; + prog_templ[prog] = templ; + } + Node bd = d_sip->d_conjuncts[2].size()==1 ? d_sip->d_conjuncts[2][0] : NodeManager::currentNM()->mkNode( AND, d_sip->d_conjuncts[2] ); + visited.clear(); + bd = addDeepEmbedding( bd, visited ); + Trace("cegqi-inv") << " body : " << bd << std::endl; + bd = substituteInvariantTemplates( bd, prog_templ, d_prog_templ_vars ); + Trace("cegqi-inv-debug") << " templ-subs body : " << bd << std::endl; + //make inner existential + std::vector< Node > new_var_bv; for( unsigned j=0; j<d_sip->d_si_vars.size(); j++ ){ std::stringstream ss; - ss << "i_" << j; - Node v = NodeManager::currentNM()->mkBoundVar( ss.str(), d_sip->d_si_vars[j].getType() ); - d_prog_templ_vars[prog].push_back( v ); - Trace("cegqi-inv") << v << " "; + ss << "ss_" << j; + new_var_bv.push_back( NodeManager::currentNM()->mkBoundVar( ss.str(), d_sip->d_si_vars[j].getType() ) ); } - Trace("cegqi-inv") << std::endl; - Node pre = inv_pre_post[0][prog].empty() ? NodeManager::currentNM()->mkConst( false ) : - ( inv_pre_post[0][prog].size()==1 ? inv_pre_post[0][prog][0] : NodeManager::currentNM()->mkNode( OR, inv_pre_post[0][prog] ) ); - d_trans_pre[prog] = pre.substitute( d_sip->d_si_vars.begin(), d_sip->d_si_vars.end(), d_prog_templ_vars[prog].begin(), d_prog_templ_vars[prog].end() ); - Node post = inv_pre_post[1][prog].empty() ? NodeManager::currentNM()->mkConst( true ) : - ( inv_pre_post[1][prog].size()==1 ? inv_pre_post[1][prog][0] : NodeManager::currentNM()->mkNode( AND, inv_pre_post[1][prog] ) ); - d_trans_post[prog] = post.substitute( d_sip->d_si_vars.begin(), d_sip->d_si_vars.end(), d_prog_templ_vars[prog].begin(), d_prog_templ_vars[prog].end() ); - Trace("cegqi-inv") << " precondition : " << d_trans_pre[prog] << std::endl; - Trace("cegqi-inv") << " postcondition : " << d_trans_post[prog] << std::endl; - Node invariant = single_inv_app_map[prog]; - invariant = invariant.substitute( d_sip->d_si_vars.begin(), d_sip->d_si_vars.end(), d_prog_templ_vars[prog].begin(), d_prog_templ_vars[prog].end() ); - Trace("cegqi-inv") << " invariant : " << invariant << std::endl; - //construct template - Node templ; - if( options::sygusInvTemplMode() == SYGUS_INV_TEMPL_MODE_PRE ){ - //templ = NodeManager::currentNM()->mkNode( AND, NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], invariant ), d_trans_post[prog] ); - templ = NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], invariant ); - }else{ - Assert( options::sygusInvTemplMode() == SYGUS_INV_TEMPL_MODE_POST ); - //templ = NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], NodeManager::currentNM()->mkNode( AND, d_trans_post[prog], invariant ) ); - templ = NodeManager::currentNM()->mkNode( AND, d_trans_post[prog], invariant ); + bd = bd.substitute( d_sip->d_si_vars.begin(), d_sip->d_si_vars.end(), new_var_bv.begin(), new_var_bv.end() ); + Assert( q[1].getKind()==NOT && q[1][0].getKind()==FORALL ); + for( unsigned j=0; j<q[1][0][0].getNumChildren(); j++ ){ + new_var_bv.push_back( q[1][0][0][j] ); } - visited.clear(); - templ = addDeepEmbedding( templ, visited ); - Trace("cegqi-inv") << " template : " << templ << std::endl; - prog_templ[prog] = templ; - } - Node bd = d_sip->d_conjuncts[2].size()==1 ? d_sip->d_conjuncts[2][0] : NodeManager::currentNM()->mkNode( AND, d_sip->d_conjuncts[2] ); - visited.clear(); - bd = addDeepEmbedding( bd, visited ); - Trace("cegqi-inv") << " body : " << bd << std::endl; - bd = substituteInvariantTemplates( bd, prog_templ, d_prog_templ_vars ); - Trace("cegqi-inv-debug") << " templ-subs body : " << bd << std::endl; - //make inner existential - std::vector< Node > new_var_bv; - for( unsigned j=0; j<d_sip->d_si_vars.size(); j++ ){ - std::stringstream ss; - ss << "ss_" << j; - new_var_bv.push_back( NodeManager::currentNM()->mkBoundVar( ss.str(), d_sip->d_si_vars[j].getType() ) ); - } - bd = bd.substitute( d_sip->d_si_vars.begin(), d_sip->d_si_vars.end(), new_var_bv.begin(), new_var_bv.end() ); - Assert( q[1].getKind()==NOT && q[1][0].getKind()==FORALL ); - for( unsigned j=0; j<q[1][0][0].getNumChildren(); j++ ){ - new_var_bv.push_back( q[1][0][0][j] ); - } - if( !new_var_bv.empty() ){ - Node bvl = NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, new_var_bv ); - bd = NodeManager::currentNM()->mkNode( FORALL, bvl, bd ).negate(); + if( !new_var_bv.empty() ){ + Node bvl = NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, new_var_bv ); + bd = NodeManager::currentNM()->mkNode( FORALL, bvl, bd ).negate(); + } + //make outer universal + bd = NodeManager::currentNM()->mkNode( FORALL, q[0], bd ); + bd = Rewriter::rewrite( bd ); + Trace("cegqi-inv") << " rtempl-subs body : " << bd << std::endl; + d_quant = bd; } - //make outer universal - bd = NodeManager::currentNM()->mkNode( FORALL, q[0], bd ); - bd = Rewriter::rewrite( bd ); - Trace("cegqi-inv") << " rtempl-subs body : " << bd << std::endl; - d_quant = bd; + }else{ + //we are fully single invocation } }else{ - //we are fully single invocation + Trace("cegqi-si") << "...property is not single invocation, involves functions with different argument sorts." << std::endl; + singleInvocation = false; } - }else{ - Trace("cegqi-si") << "...property is not single invocation, involves functions with different argument sorts." << std::endl; - singleInvocation = false; } if( singleInvocation ){ d_single_inv = d_sip->getSingleInvocation(); @@ -534,6 +548,7 @@ bool CegConjectureSingleInv::doAddInstantiation( std::vector< Node >& subs ){ return false; }else{ Trace("cegqi-engine") << siss.str() << std::endl; + Assert( d_single_inv_var.size()==subs.size() ); Node lem = d_single_inv[1].substitute( d_single_inv_var.begin(), d_single_inv_var.end(), subs.begin(), subs.end() ); if( d_qe->getTermDatabase()->containsVtsTerm( lem ) ){ Trace("cegqi-engine-debug") << "Rewrite based on vts symbols..." << std::endl; @@ -595,6 +610,7 @@ bool CegConjectureSingleInv::check( std::vector< Node >& lems ) { for( unsigned i=0; i<d_sip->d_all_vars.size(); i++ ){ subs.push_back( NodeManager::currentNM()->mkSkolem( "kv", d_sip->d_all_vars[i].getType(), "created for verifying nsi" ) ); } + Assert( d_sip->d_all_vars.size()==subs.size() ); inst = inst.substitute( d_sip->d_all_vars.begin(), d_sip->d_all_vars.end(), subs.begin(), subs.end() ); Trace("cegqi-nsi") << "NSI : verification : " << inst << std::endl; Trace("cegqi-lemma") << "Cegqi::Lemma : verification lemma : " << inst << std::endl; @@ -751,6 +767,7 @@ Node CegConjectureSingleInv::getSolution( unsigned sol_index, TypeNode stn, int& std::sort( indices.begin(), indices.end(), ssii ); Trace("csi-sol") << "Construct solution" << std::endl; s = constructSolution( indices, sol_index, 0 ); + Assert( vars.size()==d_sol->d_varList.size() ); s = s.substitute( vars.begin(), vars.end(), d_sol->d_varList.begin(), d_sol->d_varList.end() ); } d_orig_solution = s; @@ -826,8 +843,8 @@ Node CegConjectureSingleInv::reconstructToSyntax( Node s, TypeNode stn, int& rec } bool CegConjectureSingleInv::needsCheck() { - if( options::cegqiSingleInvMultiInstAbort() ){ - if( !hasITEs() ){ + if( options::cegqiSingleInvMode()==CEGQI_SI_MODE_ALL_ABORT ){ + if( !d_has_ites ){ return d_inst.empty(); } } @@ -922,6 +939,7 @@ void SingleInvocationPartition::process( Node n ) { std::vector< Node > funcs; //normalize the invocations if( !terms.empty() ){ + Assert( terms.size()==subs.size() ); cr = cr.substitute( terms.begin(), terms.end(), subs.begin(), subs.end() ); } std::vector< Node > children; @@ -940,6 +958,7 @@ void SingleInvocationPartition::process( Node n ) { } Trace("si-prt") << std::endl; cr = children.size()==1 ? children[0] : NodeManager::currentNM()->mkNode( OR, children ); + Assert( terms.size()==subs.size() ); cr = cr.substitute( terms.begin(), terms.end(), subs.begin(), subs.end() ); Trace("si-prt-debug") << "...normalized invocations to " << cr << std::endl; //now must check if it has other bound variables @@ -974,6 +993,7 @@ void SingleInvocationPartition::process( Node n ) { } } } + Assert( terms.size()==subs.size() ); cr = cr.substitute( terms.begin(), terms.end(), subs.begin(), subs.end() ); } cr = Rewriter::rewrite( cr ); @@ -982,6 +1002,7 @@ void SingleInvocationPartition::process( Node n ) { TermDb::getBoundVars( cr, d_all_vars ); if( singleInvocation ){ //replace with single invocation formulation + Assert( si_terms.size()==si_subs.size() ); cr = cr.substitute( si_terms.begin(), si_terms.end(), si_subs.begin(), si_subs.end() ); cr = Rewriter::rewrite( cr ); Trace("si-prt") << ".....si version=" << cr << std::endl; diff --git a/src/theory/quantifiers/ce_guided_single_inv.h b/src/theory/quantifiers/ce_guided_single_inv.h index 4d2f9a0e5..4d2f9a0e5 100644..100755 --- a/src/theory/quantifiers/ce_guided_single_inv.h +++ b/src/theory/quantifiers/ce_guided_single_inv.h diff --git a/src/theory/quantifiers/ce_guided_single_inv_ei.cpp b/src/theory/quantifiers/ce_guided_single_inv_ei.cpp index 6394fca3d..6394fca3d 100644..100755 --- a/src/theory/quantifiers/ce_guided_single_inv_ei.cpp +++ b/src/theory/quantifiers/ce_guided_single_inv_ei.cpp diff --git a/src/theory/quantifiers/ce_guided_single_inv_ei.h b/src/theory/quantifiers/ce_guided_single_inv_ei.h index 42e0b0820..42e0b0820 100644..100755 --- a/src/theory/quantifiers/ce_guided_single_inv_ei.h +++ b/src/theory/quantifiers/ce_guided_single_inv_ei.h diff --git a/src/theory/quantifiers/ce_guided_single_inv_sol.cpp b/src/theory/quantifiers/ce_guided_single_inv_sol.cpp index 240c2ed12..240c2ed12 100644..100755 --- a/src/theory/quantifiers/ce_guided_single_inv_sol.cpp +++ b/src/theory/quantifiers/ce_guided_single_inv_sol.cpp diff --git a/src/theory/quantifiers/ce_guided_single_inv_sol.h b/src/theory/quantifiers/ce_guided_single_inv_sol.h index cb6f6bc41..cb6f6bc41 100644..100755 --- a/src/theory/quantifiers/ce_guided_single_inv_sol.h +++ b/src/theory/quantifiers/ce_guided_single_inv_sol.h diff --git a/src/theory/quantifiers/ceg_instantiator.cpp b/src/theory/quantifiers/ceg_instantiator.cpp index da488ea98..cd263e90c 100644..100755 --- a/src/theory/quantifiers/ceg_instantiator.cpp +++ b/src/theory/quantifiers/ceg_instantiator.cpp @@ -22,6 +22,9 @@ #include "theory/quantifiers/term_database.h" #include "theory/theory_engine.h" +#include "theory/bv/theory_bv_utils.h" +#include "util/bitvector.h" + //#define MBP_STRICT_ASSERTIONS using namespace std; @@ -466,6 +469,36 @@ bool CegInstantiator::doAddInstantiation( SolvedForm& sf, SolvedForm& ssf, std:: } } } + /* TODO: algebraic reasoning for bitvector instantiation + else if( pvtn.isBitVector() ){ + if( atom.getKind()==BITVECTOR_ULT || atom.getKind()==BITVECTOR_ULE ){ + for( unsigned t=0; t<2; t++ ){ + if( atom[t]==pv ){ + computeProgVars( atom[1-t] ); + if( d_inelig.find( atom[1-t] )==d_inelig.end() ){ + //only ground terms TODO: more + if( d_prog_var[atom[1-t]].empty() ){ + Node veq_c; + Node uval; + if( ( !pol && atom.getKind()==BITVECTOR_ULT ) || ( pol && atom.getKind()==BITVECTOR_ULE ) ){ + uval = atom[1-t]; + }else{ + uval = NodeManager::currentNM()->mkNode( (atom.getKind()==BITVECTOR_ULT)==(t==1) ? BITVECTOR_PLUS : BITVECTOR_SUB, atom[1-t], + bv::utils::mkConst(pvtn.getConst<BitVectorSize>(), 1) ); + } + if( subs_proc[uval].find( veq_c )==subs_proc[uval].end() ){ + subs_proc[uval][veq_c] = true; + if( doAddInstantiationInc( uval, pv, veq_c, 0, sf, ssf, vars, btyp, theta, i, effort, cons, curr_var ) ){ + return true; + } + } + } + } + } + } + } + } + */ } } } @@ -685,7 +718,7 @@ bool CegInstantiator::doAddInstantiation( SolvedForm& sf, SolvedForm& ssf, std:: //[5] resort to using value in model // do so if we are in effort=1, or if the variable is boolean, or if we are solving for a subfield of a datatype - if( ( effort>0 || pvtn.isBoolean() || !curr_var.empty() ) && d_qe->getTermDatabase()->isClosedEnumerableType( pvtn ) ){ + if( ( effort>0 || pvtn.isBoolean() || pvtn.isBitVector() || !curr_var.empty() ) && d_qe->getTermDatabase()->isClosedEnumerableType( pvtn ) ){ Node mv = getModelValue( pv ); Node pv_coeff_m; Trace("cbqi-inst-debug") << "[5] " << i << "...try model value " << mv << std::endl; @@ -781,6 +814,7 @@ bool CegInstantiator::doAddInstantiationInc( Node n, Node pv, Node pv_coeff, int } } if( success ){ + Trace("cbqi-inst-debug2") << "Adding to vectors..." << std::endl; vars.push_back( pv ); btyp.push_back( bt ); sf.push_back( pv, n, pv_coeff ); @@ -800,8 +834,10 @@ bool CegInstantiator::doAddInstantiationInc( Node n, Node pv, Node pv_coeff, int curr_var.pop_back(); is_cv = true; } + Trace("cbqi-inst-debug2") << "Recurse..." << std::endl; success = doAddInstantiation( sf, ssf, vars, btyp, new_theta, curr_var.empty() ? i+1 : i, effort, cons, curr_var ); if( !success ){ + Trace("cbqi-inst-debug2") << "Removing from vectors..." << std::endl; if( is_cv ){ curr_var.push_back( pv ); } @@ -814,6 +850,7 @@ bool CegInstantiator::doAddInstantiationInc( Node n, Node pv, Node pv_coeff, int if( success ){ return true; }else{ + Trace("cbqi-inst-debug2") << "Revert substitutions..." << std::endl; //revert substitution information for( std::map< int, Node >::iterator it = prev_subs.begin(); it != prev_subs.end(); it++ ){ sf.d_subs[it->first] = it->second; @@ -1035,7 +1072,13 @@ Node CegInstantiator::applySubstitution( TypeNode tn, Node n, std::vector< Node if( !it->second.isNull() ){ c_coeff = NodeManager::currentNM()->mkNode( MULT, c_coeff, it->second ); } - Node c = NodeManager::currentNM()->mkNode( MULT, c_coeff, msum_term[it->first] ); + Assert( !c_coeff.isNull() ); + Node c; + if( msum_term[it->first].isNull() ){ + c = c_coeff; + }else{ + c = NodeManager::currentNM()->mkNode( MULT, c_coeff, msum_term[it->first] ); + } children.push_back( c ); Trace("cegqi-si-apply-subs-debug") << "Add child : " << c << std::endl; } @@ -1594,6 +1637,7 @@ int CegInstantiator::solve_arith( Node pv, Node atom, Node& veq_c, Node& val, No realPart = real_part.empty() ? d_zero : ( real_part.size()==1 ? real_part[0] : NodeManager::currentNM()->mkNode( PLUS, real_part ) ); Assert( d_out->isEligibleForInstantiation( realPart ) ); //re-isolate + Trace("cbqi-inst-debug") << "Re-isolate..." << std::endl; ires = QuantArith::isolate( pv, msum, veq_c, val, atom.getKind() ); Trace("cbqi-inst-debug") << "Isolate for mixed Int/Real : " << veq_c << " * " << pv << " " << atom.getKind() << " " << val << std::endl; Trace("cbqi-inst-debug") << " real part : " << realPart << std::endl; diff --git a/src/theory/quantifiers/ceg_instantiator.h b/src/theory/quantifiers/ceg_instantiator.h index 3d7bbcb55..3d7bbcb55 100644..100755 --- a/src/theory/quantifiers/ceg_instantiator.h +++ b/src/theory/quantifiers/ceg_instantiator.h diff --git a/src/theory/quantifiers/conjecture_generator.cpp b/src/theory/quantifiers/conjecture_generator.cpp index 2cc49ef5a..f4eb67d74 100644..100755 --- a/src/theory/quantifiers/conjecture_generator.cpp +++ b/src/theory/quantifiers/conjecture_generator.cpp @@ -285,7 +285,7 @@ Node ConjectureGenerator::getFreeVar( TypeNode tn, unsigned i ) { } bool ConjectureGenerator::isHandledTerm( TNode n ){ - return !n.getAttribute(NoMatchAttribute()) && inst::Trigger::isAtomicTrigger( n ) && ( n.getKind()!=APPLY_UF || n.getOperator().getKind()!=SKOLEM ); + return d_quantEngine->getTermDatabase()->isTermActive( n ) && inst::Trigger::isAtomicTrigger( n ) && ( n.getKind()!=APPLY_UF || n.getOperator().getKind()!=SKOLEM ); } Node ConjectureGenerator::getGroundEqc( TNode r ) { @@ -425,7 +425,7 @@ void ConjectureGenerator::check( Theory::Effort e, unsigned quant_e ) { eq::EqClassIterator eqc_i = eq::EqClassIterator( r, ee ); while( !eqc_i.isFinished() ){ TNode n = (*eqc_i); - if( getTermDatabase()->hasTermCurrent( n ) && !n.getAttribute(NoMatchAttribute()) && ( n.getKind()!=EQUAL || isFalse ) ){ + if( getTermDatabase()->hasTermCurrent( n ) && getTermDatabase()->isTermActive( n ) && ( n.getKind()!=EQUAL || isFalse ) ){ if( firstTime ){ Trace("sg-gen-eqc") << "e" << d_em[r] << " : { " << std::endl; firstTime = false; @@ -1572,7 +1572,6 @@ bool TermGenerator::getNextMatch( TermGenEnv * s, TNode eqc, std::map< TypeNode, if( d_match_status_child_num==0 ){ //initial binding TNode f = s->getTgFunc( d_typ, d_status_num ); - //std::map< TNode, TermArgTrie >::iterator it = s->getTermDatabase()->d_func_map_eqc_trie[f].d_data.find( eqc ); Assert( !eqc.isNull() ); TermArgTrie * tat = s->getTermDatabase()->getTermArgTrie( eqc, f ); if( tat ){ @@ -1726,9 +1725,9 @@ void TermGenEnv::collectSignatureInformation() { d_func_kind.clear(); d_func_args.clear(); TypeNode tnull; - for( std::map< Node, TermArgTrie >::iterator it = getTermDatabase()->d_func_map_trie.begin(); it != getTermDatabase()->d_func_map_trie.end(); ++it ){ - if( getTermDatabase()->getNumGroundTerms( it->first )>0 ){ - Node nn = getTermDatabase()->getGroundTerm( it->first, 0 ); + for( std::map< Node, std::vector< Node > >::iterator it = getTermDatabase()->d_op_map.begin(); it != getTermDatabase()->d_op_map.end(); ++it ){ + if( !it->second.empty() ){ + Node nn = it->second[0]; Trace("sg-rel-sig-debug") << "Check in signature : " << nn << std::endl; if( d_cg->isHandledTerm( nn ) && nn.getKind()!=APPLY_SELECTOR_TOTAL && !nn.getType().isBoolean() ){ bool do_enum = true; @@ -1750,7 +1749,7 @@ void TermGenEnv::collectSignatureInformation() { d_typ_tg_funcs[nn.getType()].push_back( it->first ); d_tg_func_param[it->first] = ( nn.getMetaKind() == kind::metakind::PARAMETERIZED ); Trace("sg-rel-sig") << "Will enumerate function applications of : " << it->first << ", #args = " << d_func_args[it->first].size() << ", kind = " << nn.getKind() << std::endl; - getTermDatabase()->computeUfEqcTerms( it->first ); + //getTermDatabase()->computeUfEqcTerms( it->first ); } } Trace("sg-rel-sig-debug") << "Done check in signature : " << nn << std::endl; diff --git a/src/theory/quantifiers/conjecture_generator.h b/src/theory/quantifiers/conjecture_generator.h index c89d0f2ee..c89d0f2ee 100644..100755 --- a/src/theory/quantifiers/conjecture_generator.h +++ b/src/theory/quantifiers/conjecture_generator.h diff --git a/src/theory/quantifiers/equality_infer.cpp b/src/theory/quantifiers/equality_infer.cpp index c3064116f..5190025ee 100644..100755 --- a/src/theory/quantifiers/equality_infer.cpp +++ b/src/theory/quantifiers/equality_infer.cpp @@ -53,10 +53,10 @@ void EqualityInference::addToExplanation( std::vector< Node >& exp, Node e ) { } void EqualityInference::addToExplanationEqc( std::vector< Node >& exp, Node eqc ) { - NodeListMap::iterator re_i = d_rep_exp.find( eqc ); + NodeIntMap::iterator re_i = d_rep_exp.find( eqc ); if( re_i!=d_rep_exp.end() ){ - for( unsigned i=0; i<(*re_i).second->size(); i++ ){ - addToExplanation( exp, (*(*re_i).second)[i] ); + for( int i=0; i<(*re_i).second; i++ ){ + addToExplanation( exp, d_rep_exp_data[eqc][i] ); } } //for( unsigned i=0; i<d_eqci[n]->d_rep_exp.size(); i++ ){ @@ -65,16 +65,19 @@ void EqualityInference::addToExplanationEqc( std::vector< Node >& exp, Node eqc } void EqualityInference::addToExplanationEqc( Node eqc, std::vector< Node >& exp_to_add ) { - NodeListMap::iterator re_i = d_rep_exp.find( eqc ); - NodeList* re; + NodeIntMap::iterator re_i = d_rep_exp.find( eqc ); + int n_re = 0; if( re_i != d_rep_exp.end() ){ - re = (*re_i).second; - }else{ - re = new(d_c->getCMM()) NodeList( true, d_c, false, context::ContextMemoryAllocator<TNode>(d_c->getCMM()) ); - d_rep_exp.insertDataFromContextMemory( eqc, re ); + n_re = (*re_i).second; } + d_rep_exp[eqc] = n_re + exp_to_add.size(); for( unsigned i=0; i<exp_to_add.size(); i++ ){ - re->push_back( exp_to_add[i] ); + if( n_re<(int)d_rep_exp_data[eqc].size() ){ + d_rep_exp_data[eqc][n_re] = exp_to_add[i]; + }else{ + d_rep_exp_data[eqc].push_back( exp_to_add[i] ); + } + n_re++; } //for( unsigned i=0; i<exp_to_add.size(); i++ ){ // eqci->d_rep_exp.push_back( exp_to_add[i] ); @@ -204,16 +207,18 @@ void EqualityInference::eqNotifyNewClass(TNode t) { void EqualityInference::addToUseList( Node used, Node eqc ) { #if 1 - NodeListMap::iterator ul_i = d_uselist.find( used ); - NodeList* ul; + NodeIntMap::iterator ul_i = d_uselist.find( used ); + int n_ul = 0; if( ul_i != d_uselist.end() ){ - ul = (*ul_i).second; - }else{ - ul = new(d_c->getCMM()) NodeList( true, d_c, false, context::ContextMemoryAllocator<TNode>(d_c->getCMM()) ); - d_uselist.insertDataFromContextMemory( used, ul ); + n_ul = (*ul_i).second; } + d_uselist[ used ] = n_ul + 1; Trace("eq-infer-debug") << " add to use list : " << used << " -> " << eqc << std::endl; - (*ul).push_back( eqc ); + if( n_ul<(int)d_uselist_data[used].size() ){ + d_uselist_data[used][n_ul] = eqc; + }else{ + d_uselist_data[used].push_back( eqc ); + } #else std::map< Node, EqcInfo * >::iterator itu = d_eqci.find( used ); EqcInfo * eqci_used; @@ -356,12 +361,12 @@ void EqualityInference::eqNotifyMerge(TNode t1, TNode t2) { //go through all equivalence classes that may refer to v_solve std::map< Node, bool > processed; processed[v_solve] = true; - NodeListMap::iterator ul_i = d_uselist.find( v_solve ); + NodeIntMap::iterator ul_i = d_uselist.find( v_solve ); if( ul_i != d_uselist.end() ){ - NodeList* ul = (*ul_i).second; - Trace("eq-infer-debug") << " use list size = " << ul->size() << std::endl; - for( unsigned j=0; j<ul->size(); j++ ){ - Node r = (*ul)[j]; + int n_ul = (*ul_i).second; + Trace("eq-infer-debug") << " use list size = " << n_ul << std::endl; + for( int j=0; j<n_ul; j++ ){ + Node r = d_uselist_data[v_solve][j]; //Trace("eq-infer-debug") << " use list size = " << eqci_solved->d_uselist.size() << std::endl; //for( unsigned j=0; j<eqci_solved->d_uselist.size(); j++ ){ // Node r = eqci_solved->d_uselist[j]; diff --git a/src/theory/quantifiers/equality_infer.h b/src/theory/quantifiers/equality_infer.h index 93c7bd080..80d6ef98b 100644..100755 --- a/src/theory/quantifiers/equality_infer.h +++ b/src/theory/quantifiers/equality_infer.h @@ -39,7 +39,7 @@ class EqualityInference typedef context::CDHashMap< Node, Node, NodeHashFunction > NodeMap; typedef context::CDHashMap< Node, bool, NodeHashFunction > BoolMap; typedef context::CDChunkList<Node> NodeList; - typedef context::CDHashMap< Node, NodeList *, NodeHashFunction > NodeListMap; + typedef context::CDHashMap< Node, int, NodeHashFunction > NodeIntMap; private: context::Context * d_c; Node d_one; @@ -67,11 +67,13 @@ private: BoolMap d_elim_vars; std::map< Node, EqcInfo * > d_eqci; NodeMap d_rep_to_eqc; - NodeListMap d_rep_exp; + NodeIntMap d_rep_exp; + std::map< Node, std::vector< Node > > d_rep_exp_data; /** set eqc rep */ void setEqcRep( Node t, Node r, std::vector< Node >& exp_to_add, EqcInfo * eqci ); /** use list */ - NodeListMap d_uselist; + NodeIntMap d_uselist; + std::map< Node, std::vector< Node > > d_uselist_data; void addToUseList( Node used, Node eqc ); /** pending merges */ NodeList d_pending_merges; diff --git a/src/theory/quantifiers/first_order_model.cpp b/src/theory/quantifiers/first_order_model.cpp index a833f48d2..670f0eff3 100644..100755 --- a/src/theory/quantifiers/first_order_model.cpp +++ b/src/theory/quantifiers/first_order_model.cpp @@ -406,8 +406,8 @@ Node FirstOrderModelIG::evaluateTerm( Node n, int& depIndex, RepSetIterator* ri //check the type of n if( n.getKind()==INST_CONSTANT ){ int v = n.getAttribute(InstVarNumAttribute()); - depIndex = ri->d_var_order[ v ]; - val = ri->getTerm( v ); + depIndex = ri->getIndexOrder( v ); + val = ri->getCurrentTerm( v ); }else if( n.getKind()==ITE ){ int depIndex1, depIndex2; int eval = evaluate( n[0], depIndex1, ri ); diff --git a/src/theory/quantifiers/first_order_model.h b/src/theory/quantifiers/first_order_model.h index cbe83cfa5..cbe83cfa5 100644..100755 --- a/src/theory/quantifiers/first_order_model.h +++ b/src/theory/quantifiers/first_order_model.h diff --git a/src/theory/quantifiers/full_model_check.cpp b/src/theory/quantifiers/full_model_check.cpp index 33c853328..a0665cb7f 100644..100755 --- a/src/theory/quantifiers/full_model_check.cpp +++ b/src/theory/quantifiers/full_model_check.cpp @@ -405,7 +405,7 @@ void FullModelChecker::processBuildModel(TheoryModel* m, bool fullModel){ 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()) ){ + if( d_qe->getTermDatabase()->isTermActive( n ) ){ add_conds.push_back( n ); add_values.push_back( n ); Node r = fm->getUsedRepresentative(n); @@ -764,7 +764,7 @@ bool FullModelChecker::exhaustiveInstantiate(FirstOrderModelFmc * fm, Node f, No 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) { + if( riter.d_enum_type[i]==RepSetIterator::ENUM_DOMAIN_ELEMENTS || riter.d_enum_type[i]==RepSetIterator::ENUM_SET_MEMBERS ){ TypeNode tn = c[i].getType(); if( d_rep_ids.find(tn)!=d_rep_ids.end() ){ if( fm->isInterval(c[i]) || fm->isStar(c[i]) ){ @@ -773,6 +773,7 @@ bool FullModelChecker::exhaustiveInstantiate(FirstOrderModelFmc * fm, Node f, No 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]]); + riter.d_enum_type[i] = RepSetIterator::ENUM_DOMAIN_ELEMENTS; }else{ Trace("fmc-exh") << "---- Does not have rep : " << c[i] << " for type " << tn << std::endl; return false; @@ -792,7 +793,7 @@ bool FullModelChecker::exhaustiveInstantiate(FirstOrderModelFmc * fm, Node f, No std::vector< Node > ev_inst; std::vector< Node > inst; for( int i=0; i<riter.getNumTerms(); i++ ){ - Node rr = riter.getTerm( i ); + Node rr = riter.getCurrentTerm( i ); Node r = rr; //if( r.getType().isSort() ){ r = fm->getUsedRepresentative( r ); @@ -826,18 +827,18 @@ bool FullModelChecker::exhaustiveInstantiate(FirstOrderModelFmc * fm, Node f, No int index = riter.increment(); Trace("fmc-exh-debug") << "Incremented index " << index << std::endl; if( !riter.isFinished() ){ - if (index>=0 && riter.d_index[index]>0 && addedLemmas>0 && riter.d_enum_type[index]==RepSetIterator::ENUM_RANGE) { + if (index>=0 && riter.d_index[index]>0 && addedLemmas>0 && riter.d_enum_type[index]==RepSetIterator::ENUM_INT_RANGE) { Trace("fmc-exh-debug") << "Since this is a range enumeration, skip to the next..." << std::endl; riter.increment2( index-1 ); } } } d_addedLemmas += addedLemmas; - Trace("fmc-exh") << "----Finished Exhaustive instantiate, lemmas = " << addedLemmas << ", incomplete=" << riter.d_incomplete << std::endl; - return addedLemmas>0 || !riter.d_incomplete; + Trace("fmc-exh") << "----Finished Exhaustive instantiate, lemmas = " << addedLemmas << ", incomplete=" << riter.isIncomplete() << std::endl; + return addedLemmas>0 || !riter.isIncomplete(); }else{ Trace("fmc-exh") << "----Finished Exhaustive instantiate, failed." << std::endl; - return false; + return !riter.isIncomplete(); } } diff --git a/src/theory/quantifiers/full_model_check.h b/src/theory/quantifiers/full_model_check.h index 411b7a5eb..411b7a5eb 100644..100755 --- a/src/theory/quantifiers/full_model_check.h +++ b/src/theory/quantifiers/full_model_check.h diff --git a/src/theory/quantifiers/fun_def_engine.cpp b/src/theory/quantifiers/fun_def_engine.cpp index cf1d14663..cf1d14663 100644..100755 --- a/src/theory/quantifiers/fun_def_engine.cpp +++ b/src/theory/quantifiers/fun_def_engine.cpp diff --git a/src/theory/quantifiers/fun_def_engine.h b/src/theory/quantifiers/fun_def_engine.h index 3b95281c0..3b95281c0 100644..100755 --- a/src/theory/quantifiers/fun_def_engine.h +++ b/src/theory/quantifiers/fun_def_engine.h diff --git a/src/theory/quantifiers/fun_def_process.cpp b/src/theory/quantifiers/fun_def_process.cpp index 9109aab8a..9109aab8a 100644..100755 --- a/src/theory/quantifiers/fun_def_process.cpp +++ b/src/theory/quantifiers/fun_def_process.cpp diff --git a/src/theory/quantifiers/fun_def_process.h b/src/theory/quantifiers/fun_def_process.h index 1f6ee6562..1f6ee6562 100644..100755 --- a/src/theory/quantifiers/fun_def_process.h +++ b/src/theory/quantifiers/fun_def_process.h diff --git a/src/theory/quantifiers/inst_match.cpp b/src/theory/quantifiers/inst_match.cpp index 8818175db..8818175db 100644..100755 --- a/src/theory/quantifiers/inst_match.cpp +++ b/src/theory/quantifiers/inst_match.cpp diff --git a/src/theory/quantifiers/inst_match.h b/src/theory/quantifiers/inst_match.h index ad287c1a3..ad287c1a3 100644..100755 --- a/src/theory/quantifiers/inst_match.h +++ b/src/theory/quantifiers/inst_match.h diff --git a/src/theory/quantifiers/inst_match_generator.cpp b/src/theory/quantifiers/inst_match_generator.cpp index bf05de3bb..2d3bf76f6 100644..100755 --- a/src/theory/quantifiers/inst_match_generator.cpp +++ b/src/theory/quantifiers/inst_match_generator.cpp @@ -32,6 +32,7 @@ namespace theory { namespace inst { InstMatchGenerator::InstMatchGenerator( Node pat ){ + d_cg = NULL; d_needsReset = true; d_active_add = false; Assert( quantifiers::TermDb::hasInstConstAttr(pat) ); @@ -43,12 +44,20 @@ InstMatchGenerator::InstMatchGenerator( Node pat ){ } InstMatchGenerator::InstMatchGenerator() { + d_cg = NULL; d_needsReset = true; d_active_add = false; d_next = NULL; d_matchPolicy = MATCH_GEN_DEFAULT; } +InstMatchGenerator::~InstMatchGenerator() throw() { + for( unsigned i=0; i<d_children.size(); i++ ){ + delete d_children[i]; + } + delete d_cg; +} + void InstMatchGenerator::setActiveAdd(bool val){ d_active_add = val; if( d_next!=NULL ){ @@ -150,7 +159,7 @@ void InstMatchGenerator::initialize( Node q, QuantifiersEngine* qe, std::vector< d_cg = new inst::CandidateGeneratorQELitDeq( qe, d_match_pattern ); } }else{ - d_cg = new CandidateGeneratorQueue; + d_cg = new CandidateGeneratorQueue( qe ); Trace("inst-match-gen-warn") << "(?) Unknown matching pattern is " << d_match_pattern << std::endl; d_matchPolicy = MATCH_GEN_INTERNAL_ERROR; } @@ -249,7 +258,7 @@ bool InstMatchGenerator::getMatch( Node f, Node t, InstMatch& m, QuantifiersEngi Trace("matching-debug2") << "Reset children..." << std::endl; //now, fit children into match //we will be requesting candidates for matching terms for each child - for( int i=0; i<(int)d_children.size(); i++ ){ + for( unsigned i=0; i<d_children.size(); i++ ){ d_children[i]->reset( t[ d_children_index[i] ], qe ); } Trace("matching-debug2") << "Continue next " << d_next << std::endl; @@ -484,7 +493,7 @@ d_f( q ){ } Debug("smart-multi-trigger") << std::endl; } - for( int i=0; i<(int)pats.size(); i++ ){ + for( unsigned i=0; i<pats.size(); i++ ){ Node n = pats[i]; //make the match generator d_children.push_back( InstMatchGenerator::mkInstMatchGenerator(q, n, qe ) ); @@ -492,7 +501,7 @@ d_f( q ){ std::vector< int > unique_vars; std::map< int, bool > shared_vars; int numSharedVars = 0; - for( int j=0; j<(int)d_var_contains[n].size(); j++ ){ + for( unsigned j=0; j<d_var_contains[n].size(); j++ ){ if( d_var_to_node[ d_var_contains[n][j] ].size()==1 ){ Debug("smart-multi-trigger") << "Var " << d_var_contains[n][j] << " is unique to " << pats[i] << std::endl; unique_vars.push_back( d_var_contains[n][j] ); @@ -503,7 +512,7 @@ d_f( q ){ } //we use the latest shared variables, then unique variables std::vector< int > vars; - int index = i==0 ? (int)(pats.size()-1) : (i-1); + unsigned index = i==0 ? pats.size()-1 : (i-1); while( numSharedVars>0 && index!=i ){ for( std::map< int, bool >::iterator it = shared_vars.begin(); it != shared_vars.end(); ++it ){ if( it->second ){ @@ -519,16 +528,24 @@ d_f( q ){ } vars.insert( vars.end(), unique_vars.begin(), unique_vars.end() ); Debug("smart-multi-trigger") << " Index[" << i << "]: "; - for( int i=0; i<(int)vars.size(); i++ ){ - Debug("smart-multi-trigger") << vars[i] << " "; + for( unsigned j=0; j<vars.size(); j++ ){ + Debug("smart-multi-trigger") << vars[j] << " "; } Debug("smart-multi-trigger") << std::endl; //make ordered inst match trie - InstMatchTrie::ImtIndexOrder* imtio = new InstMatchTrie::ImtIndexOrder; - imtio->d_order.insert( imtio->d_order.begin(), vars.begin(), vars.end() ); - d_children_trie.push_back( InstMatchTrieOrdered( imtio ) ); + d_imtio[i] = new InstMatchTrie::ImtIndexOrder; + d_imtio[i]->d_order.insert( d_imtio[i]->d_order.begin(), vars.begin(), vars.end() ); + d_children_trie.push_back( InstMatchTrieOrdered( d_imtio[i] ) ); } +} +InstMatchGeneratorMulti::~InstMatchGeneratorMulti() throw() { + for( unsigned i=0; i<d_children.size(); i++ ){ + delete d_children[i]; + } + for( std::map< unsigned, InstMatchTrie::ImtIndexOrder* >::iterator it = d_imtio.begin(); it != d_imtio.end(); ++it ){ + delete it->second; + } } /** reset instantiation round (call this whenever equivalence classes have changed) */ @@ -697,7 +714,7 @@ int InstMatchGeneratorMulti::addTerm( Node q, Node t, QuantifiersEngine* qe ){ return addedLemmas; } -InstMatchGeneratorSimple::InstMatchGeneratorSimple( Node q, Node pat ) : d_f( q ), d_match_pattern( pat ) { +InstMatchGeneratorSimple::InstMatchGeneratorSimple( Node q, Node pat, QuantifiersEngine* qe ) : d_f( q ), d_match_pattern( pat ) { if( d_match_pattern.getKind()==NOT ){ d_match_pattern = d_match_pattern[0]; d_pol = false; @@ -720,10 +737,11 @@ InstMatchGeneratorSimple::InstMatchGeneratorSimple( Node q, Node pat ) : d_f( q } d_match_pattern_arg_types.push_back( d_match_pattern[i].getType() ); } + d_op = qe->getTermDatabase()->getMatchOperator( d_match_pattern ); } void InstMatchGeneratorSimple::resetInstantiationRound( QuantifiersEngine* qe ) { - d_op = qe->getTermDatabase()->getMatchOperator( d_match_pattern ); + } int InstMatchGeneratorSimple::addInstantiations( Node q, InstMatch& baseMatch, QuantifiersEngine* qe ){ @@ -751,6 +769,7 @@ int InstMatchGeneratorSimple::addInstantiations( Node q, InstMatch& baseMatch, Q tat = NULL; } } + Debug("simple-trigger-debug") << "Adding instantiations based on " << tat << " from " << d_op << " " << d_eqc << std::endl; if( tat ){ InstMatch m( q ); m.add( baseMatch ); diff --git a/src/theory/quantifiers/inst_match_generator.h b/src/theory/quantifiers/inst_match_generator.h index a1d907001..096774c51 100644..100755 --- a/src/theory/quantifiers/inst_match_generator.h +++ b/src/theory/quantifiers/inst_match_generator.h @@ -91,7 +91,7 @@ public: InstMatchGenerator( Node pat ); InstMatchGenerator(); /** destructor */ - ~InstMatchGenerator() throw() {} + virtual ~InstMatchGenerator() throw(); /** The pattern we are producing matches for. If null, this is a multi trigger that is merging matches from d_children. */ @@ -125,7 +125,7 @@ public: class VarMatchGeneratorBooleanTerm : public InstMatchGenerator { public: VarMatchGeneratorBooleanTerm( Node var, Node comp ); - ~VarMatchGeneratorBooleanTerm() throw() {} + virtual ~VarMatchGeneratorBooleanTerm() throw() {} Node d_comp; bool d_rm_prev; /** reset instantiation round (call this at beginning of instantiation round) */ @@ -142,7 +142,7 @@ public: class VarMatchGeneratorTermSubs : public InstMatchGenerator { public: VarMatchGeneratorTermSubs( Node var, Node subs ); - ~VarMatchGeneratorTermSubs() throw() {} + virtual ~VarMatchGeneratorTermSubs() throw() {} TNode d_var; TypeNode d_var_type; Node d_subs; @@ -183,6 +183,8 @@ private: int d_matchPolicy; /** children generators */ std::vector< InstMatchGenerator* > d_children; + /** order */ + std::map< unsigned, InstMatchTrie::ImtIndexOrder* > d_imtio; /** inst match tries for each child */ std::vector< InstMatchTrieOrdered > d_children_trie; /** calculate matches */ @@ -191,7 +193,7 @@ public: /** constructors */ InstMatchGeneratorMulti( Node q, std::vector< Node >& pats, QuantifiersEngine* qe ); /** destructor */ - ~InstMatchGeneratorMulti() throw() {} + virtual ~InstMatchGeneratorMulti() throw(); /** reset instantiation round (call this whenever equivalence classes have changed) */ void resetInstantiationRound( QuantifiersEngine* qe ); /** reset, eqc is the equivalence class to search in (any if eqc=null) */ @@ -224,7 +226,7 @@ private: void addInstantiations( InstMatch& m, QuantifiersEngine* qe, int& addedLemmas, int argIndex, quantifiers::TermArgTrie* tat ); public: /** constructors */ - InstMatchGeneratorSimple( Node q, Node pat ); + InstMatchGeneratorSimple( Node q, Node pat, QuantifiersEngine* qe ); /** destructor */ ~InstMatchGeneratorSimple() throw() {} /** reset instantiation round (call this whenever equivalence classes have changed) */ diff --git a/src/theory/quantifiers/inst_propagator.cpp b/src/theory/quantifiers/inst_propagator.cpp index d4be58636..41c9c40c8 100644..100755 --- a/src/theory/quantifiers/inst_propagator.cpp +++ b/src/theory/quantifiers/inst_propagator.cpp @@ -34,6 +34,7 @@ bool EqualityQueryInstProp::reset( Theory::Effort e ) { d_uf.clear(); d_uf_exp.clear(); d_diseq_list.clear(); + d_uf_func_map_trie.clear(); return true; } @@ -103,8 +104,7 @@ TNode EqualityQueryInstProp::getCongruentTerm( Node f, std::vector< TNode >& arg if( !t.isNull() ){ return t; }else{ - //TODO? - return TNode::null(); + return d_uf_func_map_trie[f].existsTerm( args ); } } @@ -118,6 +118,7 @@ Node EqualityQueryInstProp::getRepresentativeExp( Node a, std::vector< Node >& e Node ar = getUfRepresentative( a, exp ); if( !ar.isNull() ){ if( engine_has_a || getEngine()->hasTerm( ar ) ){ + Trace("qip-eq") << "getRepresentativeExp " << a << " returns " << ar << std::endl; Assert( getEngine()->hasTerm( ar ) ); Assert( getEngine()->getRepresentative( ar )==ar ); return ar; @@ -168,6 +169,21 @@ bool EqualityQueryInstProp::areDisequalExp( Node a, Node b, std::vector< Node >& } } +TNode EqualityQueryInstProp::getCongruentTermExp( Node f, std::vector< TNode >& args, std::vector< Node >& exp ) { + TNode t = d_qe->getTermDatabase()->getCongruentTerm( f, args ); + if( !t.isNull() ){ + return t; + }else{ + TNode tt = d_uf_func_map_trie[f].existsTerm( args ); + if( !tt.isNull() ){ + //TODO? + return tt; + }else{ + return tt; + } + } +} + Node EqualityQueryInstProp::getUfRepresentative( Node a, std::vector< Node >& exp ) { Assert( exp.empty() ); std::map< Node, Node >::iterator it = d_uf.find( a ); @@ -252,7 +268,7 @@ int EqualityQueryInstProp::setEqual( Node& a, Node& b, bool pol, std::vector< No } } } - + if( swap ){ //swap Node temp_r = ar; @@ -262,7 +278,7 @@ int EqualityQueryInstProp::setEqual( Node& a, Node& b, bool pol, std::vector< No Assert( !getEngine()->hasTerm( ar ) || getEngine()->hasTerm( br ) ); Assert( ar!=br ); - + std::vector< Node > exp_d; if( areDisequalExp( ar, br, exp_d ) ){ if( pol ){ @@ -279,18 +295,20 @@ int EqualityQueryInstProp::setEqual( Node& a, Node& b, bool pol, std::vector< No Assert( d_uf_exp[ar].empty() ); Assert( d_uf_exp[br].empty() ); + //registerUfTerm( ar ); d_uf[ar] = br; merge_exp( d_uf_exp[ar], exp_a ); merge_exp( d_uf_exp[ar], exp_b ); merge_exp( d_uf_exp[ar], reason ); + //registerUfTerm( br ); d_uf[br] = br; d_uf_exp[br].clear(); Trace("qip-eq") << "EqualityQueryInstProp::setEqual : merge " << ar << " -> " << br << ", exp size = " << d_uf_exp[ar].size() << ", status = " << status << std::endl; a = ar; b = br; - + //carry disequality list std::map< Node, std::map< Node, std::vector< Node > > >::iterator itd = d_diseq_list.find( ar ); if( itd!=d_diseq_list.end() ){ @@ -302,13 +320,13 @@ int EqualityQueryInstProp::setEqual( Node& a, Node& b, bool pol, std::vector< No } } } - + return status; }else{ Trace("qip-eq") << "EqualityQueryInstProp::setEqual : disequal " << ar << " <> " << br << std::endl; Assert( d_diseq_list[ar].find( br )==d_diseq_list[ar].end() ); Assert( d_diseq_list[br].find( ar )==d_diseq_list[br].end() ); - + merge_exp( d_diseq_list[ar][br], reason ); merge_exp( d_diseq_list[br][ar], reason ); return STATUS_NONE; @@ -316,187 +334,234 @@ int EqualityQueryInstProp::setEqual( Node& a, Node& b, bool pol, std::vector< No } } -void EqualityQueryInstProp::addArgument( std::vector< Node >& args, std::vector< Node >& props, Node n, bool is_prop, bool pol ) { - if( is_prop ){ - if( isLiteral( n ) ){ - props.push_back( pol ? n : n.negate() ); - return; +void EqualityQueryInstProp::registerUfTerm( TNode n ) { + if( d_uf.find( n )==d_uf.end() ){ + if( !getEngine()->hasTerm( n ) ){ + TNode f = d_qe->getTermDatabase()->getMatchOperator( n ); + if( !f.isNull() ){ + std::vector< TNode > args; + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + if( !getEngine()->hasTerm( n[i] ) ){ + return; + }else{ + args.push_back( n[i] ); + } + } + d_uf_func_map_trie[f].addTerm( n, args ); + } } } +} + +//void EqualityQueryInstProp::addArgument( std::vector< Node >& args, std::vector< Node >& props, Node n, bool is_prop, bool pol ) { +void EqualityQueryInstProp::addArgument( Node n, std::vector< Node >& args, std::vector< Node >& watch, bool is_watch ) { + if( is_watch ){ + watch.push_back( n ); + } args.push_back( n ); } -bool EqualityQueryInstProp::isLiteral( Node n ) { - Kind ak = n.getKind()==NOT ? n[0].getKind() : n.getKind(); - Assert( ak!=NOT ); - return ak!=AND && ak!=OR && ak!=IFF && ak!=ITE; +bool EqualityQueryInstProp::isPropagateLiteral( Node n ) { + if( n==d_true || n==d_false ){ + return false; + }else{ + Kind ak = n.getKind()==NOT ? n[0].getKind() : n.getKind(); + Assert( ak!=NOT ); + return ak!=AND && ak!=OR && ak!=IFF && ak!=ITE; + } +} + +void EqualityQueryInstProp::setWatchList( Node n, std::vector< Node >& watch, std::map< Node, std::vector< Node > >& watch_list_out ) { + if( watch.empty() ){ + watch.push_back( n ); + } + for( unsigned j=0; j<watch.size(); j++ ){ + Trace("qip-eval") << "Watch : " << n << " -> " << watch[j] << std::endl; + watch_list_out[n].push_back( watch[j] ); + } +} + +void EqualityQueryInstProp::collectWatchList( Node n, std::map< Node, std::vector< Node > >& watch_list_out, std::vector< Node >& watch_list ) { + std::map< Node, std::vector< Node > >::iterator it = watch_list_out.find( n ); + if( it!=watch_list_out.end() && std::find( watch_list.begin(), watch_list.end(), n )==watch_list.end() ){ + watch_list.push_back( n ); + for( unsigned j=0; j<it->second.size(); j++ ){ + collectWatchList( it->second[j], watch_list_out, watch_list ); + } + } } -//this is identical to TermDb::evaluateTerm2, but tracks more information -Node EqualityQueryInstProp::evaluateTermExp( Node n, std::vector< Node >& exp, std::map< Node, Node >& visited, bool hasPol, bool pol, - std::map< Node, bool >& watch_list_out, std::vector< Node >& props ) { - std::map< Node, Node >::iterator itv = visited.find( n ); - if( itv != visited.end() ){ +//this is similar to TermDb::evaluateTerm2, but tracks more information +Node EqualityQueryInstProp::evaluateTermExp( Node n, std::vector< Node >& exp, std::map< int, std::map< Node, Node > >& visited, + bool hasPol, bool pol, std::map< Node, std::vector< Node > >& watch_list_out, std::vector< Node >& props ) { + int polIndex = hasPol ? ( pol ? 1 : -1 ) : 0; + std::map< Node, Node >::iterator itv = visited[polIndex].find( n ); + if( itv!=visited[polIndex].end() ){ return itv->second; }else{ - visited[n] = n; - Trace("qip-eval") << "evaluate term : " << n << std::endl; - std::vector< Node > exp_n; - Node ret = getRepresentativeExp( n, exp_n ); - if( ret.isNull() ){ - //term is not known to be equal to a representative in equality engine, evaluate it - Kind k = n.getKind(); - if( k==FORALL ){ - ret = Node::null(); - }else{ - std::map< Node, bool > watch_list_out_curr; - TNode f = d_qe->getTermDatabase()->getMatchOperator( n ); - std::vector< Node > args; - bool ret_set = false; - bool childChanged = false; - int abort_i = -1; - //get the child entailed polarity - Assert( n.getKind()!=IMPLIES ); - bool newHasPol, newPol; - QuantPhaseReq::getEntailPolarity( n, 0, hasPol, pol, newHasPol, newPol ); - //for each child - for( unsigned i=0; i<n.getNumChildren(); i++ ){ - Node c = evaluateTermExp( n[i], exp, visited, newHasPol, newPol, watch_list_out_curr, props ); - if( c.isNull() ){ - ret = Node::null(); - ret_set = true; - break; - }else if( c==d_true || c==d_false ){ - //short-circuiting - if( k==kind::AND || k==kind::OR ){ - if( (k==kind::AND)==(c==d_false) ){ - ret = c; - ret_set = true; - break; - }else{ - //redundant - c = Node::null(); - childChanged = true; - } - }else if( k==kind::ITE && i==0 ){ - Assert( watch_list_out_curr.empty() ); - ret = evaluateTermExp( n[ c==d_true ? 1 : 2], exp, visited, hasPol, pol, watch_list_out_curr, props ); - ret_set = true; - break; - }else if( k==kind::NOT ){ - ret = c==d_true ? d_false : d_true; + visited[polIndex][n] = n; + Node ret; + //check if it should be propagated in this context + if( hasPol && isPropagateLiteral( n ) ){ + Assert( n.getType().isBoolean() ); + //must be Boolean + ret = evaluateTermExp( n, exp, visited, false, pol, watch_list_out, props ); + if( isPropagateLiteral( ret ) ){ + Trace("qip-eval") << "-----> propagate : " << ret << std::endl; + props.push_back( pol ? ret : ret.negate() ); + ret = pol ? d_true : d_false; + } + }else{ + Trace("qip-eval") << "evaluate term : " << n << " [" << polIndex << "]" << std::endl; + std::vector< Node > exp_n; + ret = getRepresentativeExp( n, exp_n ); + if( ret.isNull() ){ + //term is not known to be equal to a representative in equality engine, evaluate it + Kind k = n.getKind(); + if( k!=FORALL ){ + TNode f = d_qe->getTermDatabase()->getMatchOperator( n ); + std::vector< Node > args; + bool ret_set = false; + bool childChanged = false; + int abort_i = -1; + //get the child entailed polarity + Assert( n.getKind()!=IMPLIES ); + bool newHasPol, newPol; + QuantPhaseReq::getEntailPolarity( n, 0, hasPol, pol, newHasPol, newPol ); + std::vector< Node > watch; + //for each child + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + Node c = evaluateTermExp( n[i], exp, visited, newHasPol, newPol, watch_list_out, props ); + if( c.isNull() ){ + ret = Node::null(); ret_set = true; break; - } - } - if( !c.isNull() ){ - childChanged = childChanged || n[i]!=c; - if( !f.isNull() && !watch_list_out_curr.empty() ){ - // we are done if this is an UF application and an argument is unevaluated - args.push_back( c ); - abort_i = i; - break; - }else if( ( k==kind::AND || k==kind::OR ) ){ - if( c.getKind()==k ){ - //flatten - for( unsigned j=0; j<c.getNumChildren(); j++ ){ - addArgument( args, props, c[j], newHasPol, newPol ); + }else if( c==d_true || c==d_false ){ + //short-circuiting + if( k==kind::AND || k==kind::OR ){ + if( (k==kind::AND)==(c==d_false) ){ + ret = c; + ret_set = true; + break; + }else{ + //redundant + c = Node::null(); + childChanged = true; } - }else{ - addArgument( args, props, c, newHasPol, newPol ); + }else if( k==kind::ITE && i==0 ){ + ret = evaluateTermExp( n[ c==d_true ? 1 : 2], exp, visited, hasPol, pol, watch_list_out, props ); + ret_set = true; + break; + }else if( k==kind::NOT ){ + ret = c==d_true ? d_false : d_true; + ret_set = true; + break; } - //if we are in a branching position - if( hasPol && !newHasPol && args.size()>=2 ){ - //we are done if at least two args are unevaluated + } + if( !c.isNull() ){ + childChanged = childChanged || n[i]!=c; + bool is_watch = watch_list_out.find( c )!=watch_list_out.end(); + if( !f.isNull() && is_watch ){ + // we are done if this is an UF application and an argument is unevaluated + addArgument( c, args, watch, is_watch ); abort_i = i; break; + }else if( k==kind::AND || k==kind::OR || k==kind::ITE || k==IFF ){ + Trace("qip-eval-debug") << "Adding argument " << c << " to " << k << ", isProp = " << newHasPol << std::endl; + if( ( k==kind::AND || k==kind::OR ) && c.getKind()==k ){ + //flatten + for( unsigned j=0; j<c.getNumChildren(); j++ ){ + addArgument( c[j], args, watch, is_watch ); + } + }else{ + addArgument( c, args, watch, is_watch ); + } + Trace("qip-eval-debug") << "props/args = " << props.size() << "/" << args.size() << std::endl; + //if we are in a branching position + if( hasPol && !newHasPol && args.size()>=2 ){ + //we are done if at least two args are unevaluated + abort_i = i; + break; + } + }else{ + addArgument( c, args, watch, is_watch ); } - }else if( k==kind::ITE ){ - //we are done if we are ITE and condition is unevaluated - Assert( i==0 ); - args.push_back( c ); - abort_i = i; - break; - }else{ - args.push_back( c ); } } - } - //add remaining children if we aborted - if( abort_i!=-1 ){ - for( int i=(abort_i+1); i<(int)n.getNumChildren(); i++ ){ - args.push_back( n[i] ); - } - } - //copy over the watch list - for( std::map< Node, bool >::iterator itc = watch_list_out_curr.begin(); itc != watch_list_out_curr.end(); ++itc ){ - watch_list_out[itc->first] = itc->second; - } - - //if we have not short-circuited evaluation - if( !ret_set ){ - //if it is an indexed term, return the congruent term - if( !f.isNull() && watch_list_out.empty() ){ - std::vector< TNode > t_args; - for( unsigned i=0; i<args.size(); i++ ) { - t_args.push_back( args[i] ); - } - Assert( args.size()==n.getNumChildren() ); - //args contains terms known by the equality engine - TNode nn = getCongruentTerm( f, t_args ); - Trace("qip-eval") << " got congruent term " << nn << " from DB for " << n << std::endl; - if( !nn.isNull() ){ - //successfully constructed representative in EE - Assert( exp_n.empty() ); - ret = getRepresentativeExp( nn, exp_n ); - Trace("qip-eval") << "return rep, exp size = " << exp_n.size() << std::endl; - merge_exp( exp, exp_n ); - ret_set = true; - Assert( !ret.isNull() ); + //add remaining children if we aborted + if( abort_i!=-1 ){ + Trace("qip-eval-debug") << "..." << n << " aborted at " << abort_i << std::endl; + for( int i=(abort_i+1); i<(int)n.getNumChildren(); i++ ){ + args.push_back( n[i] ); } } + //if we have not short-circuited evaluation if( !ret_set ){ - if( childChanged ){ - Trace("qip-eval") << "return rewrite" << std::endl; - if( ( k==kind::AND || k==kind::OR ) ){ - if( args.empty() ){ - ret = k==kind::AND ? d_true : d_false; - ret_set = true; - }else if( args.size()==1 ){ - ret = args[0]; - ret_set = true; - } + //if it is an indexed term, return the congruent term + if( !f.isNull() && watch.empty() ){ + std::vector< TNode > t_args; + for( unsigned i=0; i<args.size(); i++ ) { + Trace("qip-eval") << "arg " << i << " : " << args[i] << std::endl; + t_args.push_back( args[i] ); + } + Assert( args.size()==n.getNumChildren() ); + //args contains terms known by the equality engine + TNode nn = getCongruentTerm( f, t_args ); + Trace("qip-eval") << " got congruent term " << nn << " for " << n << std::endl; + if( !nn.isNull() ){ + //successfully constructed representative in EE + Assert( exp_n.empty() ); + ret = getRepresentativeExp( nn, exp_n ); + Trace("qip-eval") << "return rep, exp size = " << exp_n.size() << std::endl; + merge_exp( exp, exp_n ); + ret_set = true; + Assert( !ret.isNull() ); + Assert( ret!=n ); + // we have that n == ret, check if the union find should be updated TODO? }else{ - Assert( args.size()==n.getNumChildren() ); + watch.push_back( ret ); } - if( !ret_set ){ - if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ - args.insert( args.begin(), n.getOperator() ); + } + if( !ret_set ){ + if( childChanged || args.size()!=n.getNumChildren() ){ + Trace("qip-eval") << "return rewrite" << std::endl; + if( k==kind::AND || k==kind::OR ){ + if( args.empty() ){ + ret = k==kind::AND ? d_true : d_false; + ret_set = true; + }else if( args.size()==1 ){ + //need to re-evaluate (may be new propagations) + ret = evaluateTermExp( args[0], exp, visited, hasPol, pol, watch_list_out, props ); + ret_set = true; + } + }else{ + Assert( args.size()==n.getNumChildren() ); } - ret = NodeManager::currentNM()->mkNode( k, args ); - ret = Rewriter::rewrite( ret ); - //re-evaluate - Node ret_eval = getRepresentativeExp( ret, exp_n ); - if( !ret_eval.isNull() ){ - ret = ret_eval; - watch_list_out.clear(); - }else{ - watch_list_out[ret] = true; + if( !ret_set ){ + if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ + args.insert( args.begin(), n.getOperator() ); + } + ret = NodeManager::currentNM()->mkNode( k, args ); + setWatchList( ret, watch, watch_list_out ); + ret = Rewriter::rewrite( ret ); + //need to re-evaluate + ret = evaluateTermExp( ret, exp, visited, hasPol, pol, watch_list_out, props ); } + }else{ + ret = n; + setWatchList( ret, watch, watch_list_out ); } - }else{ - ret = n; - watch_list_out[ret] = true; } } } + }else{ + Trace("qip-eval") << "...exists in ee, return rep, exp size = " << exp_n.size() << std::endl; + merge_exp( exp, exp_n ); } - }else{ - Trace("qip-eval") << "...exists in ee, return rep, exp size = " << exp_n.size() << std::endl; - merge_exp( exp, exp_n ); } - Trace("qip-eval") << "evaluated term : " << n << ", got : " << ret << ", exp size = " << exp.size() << std::endl; - visited[n] = ret; + + Trace("qip-eval") << "evaluated term : " << n << " [" << polIndex << "], got : " << ret << ", exp size = " << exp.size() << ", watch list size = " << watch_list_out.size() << std::endl; + visited[polIndex][n] = ret; return ret; } } @@ -545,6 +610,7 @@ bool InstPropagator::reset( Theory::Effort e ) { d_watch_list.clear(); d_update_list.clear(); d_relevant_inst.clear(); + d_has_relevant_inst = false; return d_qy.reset( e ); } @@ -556,10 +622,7 @@ bool InstPropagator::notifyInstantiation( unsigned quant_e, Node q, Node lem, st Trace("qip-prop") << " " << terms[i] << std::endl; } } - unsigned id = d_icount; - d_icount++; - Trace("qip-prop") << "...assign id=" << id << std::endl; - d_ii[id].init( q, lem, terms, body ); + unsigned id = allocateInstantiation( q, lem, terms, body ); //initialize the information if( cacheConclusion( id, body ) ){ Assert( d_update_list.empty() ); @@ -582,35 +645,67 @@ bool InstPropagator::notifyInstantiation( unsigned quant_e, Node q, Node lem, st return !d_conflict; }else{ Assert( false ); - return true; + return false; + } +} + +void InstPropagator::filterInstantiations() { + if( d_has_relevant_inst ){ + //now, inform quantifiers engine which instances should be retracted + Trace("qip-prop-debug") << "...remove instantiation ids : "; + for( std::map< unsigned, InstInfo >::iterator it = d_ii.begin(); it != d_ii.end(); ++it ){ + if( !it->second.d_q.isNull() ){ + if( d_relevant_inst.find( it->first )==d_relevant_inst.end() ){ + if( !d_qe->removeInstantiation( it->second.d_q, it->second.d_lem, it->second.d_terms ) ){ + Trace("qip-warn") << "WARNING : did not remove instantiation id " << it->first << std::endl; + Assert( false ); + }else{ + Trace("qip-prop-debug") << it->first << " "; + } + }else{ + //mark the quantified formula as relevant + d_qe->markRelevant( it->second.d_q ); + } + } + } + Trace("qip-prop-debug") << std::endl; + Trace("quant-engine-conflict") << "-----> InstPropagator::" << ( d_conflict ? "conflict" : "propagate" ) << " with " << d_relevant_inst.size() << " instances." << std::endl; } } +unsigned InstPropagator::allocateInstantiation( Node q, Node lem, std::vector< Node >& terms, Node body ) { + unsigned id = d_icount; + d_icount++; + Trace("qip-prop") << "...assign id=" << id << std::endl; + d_ii[id].init( q, lem, terms, body ); + return id; +} + bool InstPropagator::update( unsigned id, InstInfo& ii, bool firstTime ) { Assert( !d_conflict ); Assert( ii.d_active ); Trace("qip-prop-debug") << "Update info [" << id << "]..." << std::endl; //update the evaluation of the current lemma - std::map< Node, Node > visited; - std::map< Node, bool > watch_list; + std::map< Node, std::vector< Node > > watch_list_out; + std::map< int, std::map< Node, Node > > visited; + std::vector< Node > exp; std::vector< Node > props; - Node eval = d_qy.evaluateTermExp( ii.d_curr, ii.d_curr_exp, visited, true, true, watch_list, props ); + Node eval = d_qy.evaluateTermExp( ii.d_curr, exp, visited, true, true, watch_list_out, props ); + EqualityQueryInstProp::merge_exp( ii.d_curr_exp, exp ); if( eval.isNull() ){ ii.d_active = false; }else if( firstTime || eval!=ii.d_curr ){ - if( EqualityQueryInstProp::isLiteral( eval ) ){ - props.push_back( eval ); - eval = d_qy.d_true; - watch_list.clear(); - } + std::vector< Node > watch_list; + d_qy.collectWatchList( eval, watch_list_out, watch_list ); if( Trace.isOn("qip-prop") ){ Trace("qip-prop") << "Update info [" << id << "]..." << std::endl; - Trace("qip-prop") << "...updated lemma " << ii.d_curr << " -> " << eval << ", exp = "; + Trace("qip-prop") << "...updated lemma " << ii.d_curr << " -> " << eval << std::endl; + Trace("qip-prop") << "...explanation = "; debugPrintExplanation( ii.d_curr_exp, "qip-prop" ); Trace("qip-prop") << std::endl; Trace("qip-prop") << "...watch list: " << std::endl; - for( std::map< Node, bool >::iterator itw = watch_list.begin(); itw!=watch_list.end(); ++itw ){ - Trace("qip-prop") << " " << itw->first << std::endl; + for( unsigned i=0; i<watch_list.size(); i++ ){ + Trace("qip-prop") << " " << watch_list[i] << std::endl; } Trace("qip-prop") << "...new propagations: " << std::endl; for( unsigned i=0; i<props.size(); i++ ){ @@ -627,8 +722,13 @@ bool InstPropagator::update( unsigned id, InstInfo& ii, bool firstTime ) { }else{ for( unsigned i=0; i<props.size(); i++ ){ Trace("qip-prop-debug2") << "Process propagation " << props[i] << std::endl; + Assert( d_qy.isPropagateLiteral( props[i] ) ); //if we haven't propagated this literal yet if( cacheConclusion( id, props[i], 1 ) ){ + //watch list for propagated literal: may not yet be purely EE representatives + std::vector< Node > prop_watch_list; + d_qy.collectWatchList( props[i], watch_list_out, prop_watch_list ); + Node lit = props[i].getKind()==NOT ? props[i][0] : props[i]; bool pol = props[i].getKind()!=NOT; if( lit.getKind()==EQUAL ){ @@ -647,10 +747,10 @@ bool InstPropagator::update( unsigned id, InstInfo& ii, bool firstTime ) { ii.d_curr = eval; //update the watch list Trace("qip-prop-debug") << "...updating watch list for [" << id << "], curr is " << ii.d_curr << std::endl; - //Here, we need to be notified of enough terms such that if we are not notified, then update( ii ) will return no propagations. - // Similar to two-watched literals, but since we are in UF, we need to watch all terms on a complete path of two terms. - for( std::map< Node, bool >::iterator itw = watch_list.begin(); itw != watch_list.end(); ++itw ){ - d_watch_list[ itw->first ][ id ] = true; + //Here, we need to be notified of enough terms such that if we are not notified, then update( id, ii ) will return no propagations. + // Similar to two-watched literals, but since we are taking into account UF, we need to watch all terms on a complete path of two terms. + for( unsigned i=0; i<watch_list.size(); i++ ){ + d_watch_list[ watch_list[i] ][ id ] = true; } }else{ Trace("qip-prop-debug") << "...conclusion " << eval << " is duplicate." << std::endl; @@ -682,10 +782,12 @@ void InstPropagator::propagate( Node a, Node b, bool pol, std::vector< Node >& e } if( pol ){ if( status==EqualityQueryInstProp::STATUS_MERGED_KNOWN ){ + Trace("qip-rlv-propagate") << "Relevant propagation : " << a << ( pol ? " == " : " != " ) << b << std::endl; Assert( d_qy.getEngine()->hasTerm( a ) ); Assert( d_qy.getEngine()->hasTerm( b ) ); Trace("qip-prop-debug") << "...equality between known terms." << std::endl; addRelevantInstances( exp, "qip-propagate" ); + //d_has_relevant_inst = true; } Trace("qip-prop-debug") << "...merged representatives " << a << " and " << b << std::endl; for( unsigned i=0; i<2; i++ ){ @@ -712,25 +814,7 @@ void InstPropagator::conflict( std::vector< Node >& exp ) { d_conflict = true; d_relevant_inst.clear(); addRelevantInstances( exp, "qip-propagate" ); - - //now, inform quantifiers engine which instances should be retracted - Trace("qip-prop-debug") << "...remove instantiation ids : "; - for( std::map< unsigned, InstInfo >::iterator it = d_ii.begin(); it != d_ii.end(); ++it ){ - if( d_relevant_inst.find( it->first )==d_relevant_inst.end() ){ - if( !d_qe->removeInstantiation( it->second.d_q, it->second.d_lem, it->second.d_terms ) ){ - Trace("qip-warn") << "WARNING : did not remove instantiation id " << it->first << std::endl; - Assert( false ); - }else{ - Trace("qip-prop-debug") << it->first << " "; - } - }else{ - //mark the quantified formula as relevant - d_qe->markRelevant( it->second.d_q ); - } - } - Trace("qip-prop-debug") << std::endl; - //will interupt the quantifiers engine - Trace("quant-engine-conflict") << "-----> InstPropagator::conflict with " << exp.size() << " instances." << std::endl; + d_has_relevant_inst = true; } bool InstPropagator::cacheConclusion( unsigned id, Node body, int prop_index ) { diff --git a/src/theory/quantifiers/inst_propagator.h b/src/theory/quantifiers/inst_propagator.h index 0c02c7f95..6201cf152 100644..100755 --- a/src/theory/quantifiers/inst_propagator.h +++ b/src/theory/quantifiers/inst_propagator.h @@ -64,9 +64,11 @@ public: bool areEqualExp( Node a, Node b, std::vector< Node >& exp ); /** returns true is a and b are disequal in the current context */ bool areDisequalExp( Node a, Node b, std::vector< Node >& exp ); + /** get congruent term */ + TNode getCongruentTermExp( Node f, std::vector< TNode >& args, std::vector< Node >& exp ); private: /** term index */ - std::map< Node, TermArgTrie > d_func_map_trie; + std::map< Node, TermArgTrie > d_uf_func_map_trie; /** union find for terms beyond what is stored in equality engine */ std::map< Node, Node > d_uf; std::map< Node, std::vector< Node > > d_uf_exp; @@ -74,7 +76,9 @@ private: /** disequality list, stores explanations */ std::map< Node, std::map< Node, std::vector< Node > > > d_diseq_list; /** add arg */ - void addArgument( std::vector< Node >& args, std::vector< Node >& props, Node n, bool is_prop, bool pol ); + void addArgument( Node n, std::vector< Node >& args, std::vector< Node >& watch, bool is_watch ); + /** register term */ + void registerUfTerm( TNode n ); public: enum { STATUS_CONFLICT, @@ -89,10 +93,13 @@ public: public: //for explanations static void merge_exp( std::vector< Node >& v, std::vector< Node >& v_to_merge, int up_to_size = -1 ); + //for watch list + static void setWatchList( Node n, std::vector< Node >& watch, std::map< Node, std::vector< Node > >& watch_list_out ); + static void collectWatchList( Node n, std::map< Node, std::vector< Node > >& watch_list_out, std::vector< Node >& watch_list ); - Node evaluateTermExp( Node n, std::vector< Node >& exp, std::map< Node, Node >& visited, bool hasPol, bool pol, - std::map< Node, bool >& watch_list_out, std::vector< Node >& props ); - static bool isLiteral( Node n ); + Node evaluateTermExp( Node n, std::vector< Node >& exp, std::map< int, std::map< Node, Node > >& visited, + bool hasPol, bool pol, std::map< Node, std::vector< Node > >& watch_list_out, std::vector< Node >& props ); + bool isPropagateLiteral( Node n ); }; class InstPropagator : public QuantifiersUtil { @@ -104,13 +111,18 @@ private: InstPropagator& d_ip; public: InstantiationNotifyInstPropagator(InstPropagator& ip): d_ip(ip) {} - virtual bool notifyInstantiation( unsigned quant_e, Node q, Node lem, std::vector< Node >& terms, Node body ) { - return d_ip.notifyInstantiation( quant_e, q, lem, terms, body ); + virtual bool notifyInstantiation( unsigned quant_e, Node q, Node lem, std::vector< Node >& terms, Node body ) { + return d_ip.notifyInstantiation( quant_e, q, lem, terms, body ); } + virtual void filterInstantiations() { d_ip.filterInstantiations(); } }; InstantiationNotifyInstPropagator d_notify; /** notify instantiation method */ bool notifyInstantiation( unsigned quant_e, Node q, Node lem, std::vector< Node >& terms, Node body ); + /** remove instance ids */ + void filterInstantiations(); + /** allocate instantiation */ + unsigned allocateInstantiation( Node q, Node lem, std::vector< Node >& terms, Node body ); /** equality query */ EqualityQueryInstProp d_qy; class InstInfo { @@ -137,13 +149,13 @@ private: std::vector< unsigned > d_update_list; /** relevant instances */ std::map< unsigned, bool > d_relevant_inst; + bool d_has_relevant_inst; private: bool update( unsigned id, InstInfo& i, bool firstTime = false ); void propagate( Node a, Node b, bool pol, std::vector< Node >& exp ); void conflict( std::vector< Node >& exp ); bool cacheConclusion( unsigned id, Node body, int prop_index = 0 ); void addRelevantInstances( std::vector< Node >& exp, const char * c ); - void debugPrintExplanation( std::vector< Node >& exp, const char * c ); public: InstPropagator( QuantifiersEngine* qe ); diff --git a/src/theory/quantifiers/inst_strategy_cbqi.cpp b/src/theory/quantifiers/inst_strategy_cbqi.cpp index 149330c61..523d868b5 100644..100755 --- a/src/theory/quantifiers/inst_strategy_cbqi.cpp +++ b/src/theory/quantifiers/inst_strategy_cbqi.cpp @@ -190,7 +190,7 @@ bool InstStrategyCbqi::hasNonCbqiOperator( Node n, std::map< Node, bool >& visit bool InstStrategyCbqi::hasNonCbqiVariable( Node q ){ for( unsigned i=0; i<q[0].getNumChildren(); i++ ){ TypeNode tn = q[0][i].getType(); - if( !tn.isInteger() && !tn.isReal() && !tn.isBoolean() ){ + if( !tn.isInteger() && !tn.isReal() && !tn.isBoolean() && !tn.isBitVector() ){ if( options::cbqiSplx() ){ return true; }else{ @@ -242,7 +242,7 @@ Node InstStrategyCbqi::getNextDecisionRequest(){ Node cel = d_quantEngine->getTermDatabase()->getCounterexampleLiteral( q ); bool value; if( !d_quantEngine->getValuation().hasSatValue( cel, value ) ){ - Trace("cbqi-debug2") << "CBQI: get next decision " << cel << std::endl; + Trace("cbqi-dec") << "CBQI: get next decision " << cel << std::endl; return cel; } } @@ -692,8 +692,10 @@ CegInstantiator * InstStrategyCegqi::getInstantiator( Node q ) { void InstStrategyCegqi::registerQuantifier( Node q ) { if( options::cbqiPreRegInst() ){ - //just get the instantiator - getInstantiator( q ); + if( doCbqi( q ) ){ + //just get the instantiator + getInstantiator( q ); + } } } diff --git a/src/theory/quantifiers/inst_strategy_cbqi.h b/src/theory/quantifiers/inst_strategy_cbqi.h index 8ed59778b..8ed59778b 100644..100755 --- a/src/theory/quantifiers/inst_strategy_cbqi.h +++ b/src/theory/quantifiers/inst_strategy_cbqi.h diff --git a/src/theory/quantifiers/inst_strategy_e_matching.cpp b/src/theory/quantifiers/inst_strategy_e_matching.cpp index 630880690..efd765c86 100644..100755 --- a/src/theory/quantifiers/inst_strategy_e_matching.cpp +++ b/src/theory/quantifiers/inst_strategy_e_matching.cpp @@ -34,9 +34,10 @@ using namespace CVC4::theory::quantifiers; struct sortQuantifiersForSymbol { QuantifiersEngine* d_qe; + std::map< Node, Node > d_op_map; bool operator() (Node i, Node j) { - int nqfsi = d_qe->getQuantifierRelevance()->getNumQuantifiersForSymbol( i.getOperator() ); - int nqfsj = d_qe->getQuantifierRelevance()->getNumQuantifiersForSymbol( j.getOperator() ); + int nqfsi = d_qe->getQuantifierRelevance()->getNumQuantifiersForSymbol( d_op_map[i] ); + int nqfsj = d_qe->getQuantifierRelevance()->getNumQuantifiersForSymbol( d_op_map[j] ); if( nqfsi<nqfsj ){ return true; }else if( nqfsi>nqfsj ){ @@ -83,9 +84,8 @@ int InstStrategyUserPatterns::process( Node f, Theory::Effort effort, int e ){ Trace("inst-alg") << "-> User-provided instantiate " << f << "..." << std::endl; if( d_quantEngine->getInstUserPatMode()==USER_PAT_MODE_RESORT ){ - int matchOption = 0; for( unsigned i=0; i<d_user_gen_wait[f].size(); i++ ){ - Trigger * t = Trigger::mkTrigger( d_quantEngine, f, d_user_gen_wait[f][i], matchOption, true, Trigger::TR_RETURN_NULL ); + Trigger * t = Trigger::mkTrigger( d_quantEngine, f, d_user_gen_wait[f][i], true, Trigger::TR_RETURN_NULL ); if( t ){ d_user_gen[f].push_back( t ); } @@ -134,11 +134,10 @@ void InstStrategyUserPatterns::addUserPattern( Node q, Node pat ){ if( usable ){ Trace("user-pat") << "Add user pattern: " << pat << " for " << q << std::endl; //check match option - int matchOption = 0; if( d_quantEngine->getInstUserPatMode()==USER_PAT_MODE_RESORT ){ d_user_gen_wait[q].push_back( nodes ); }else{ - Trigger * t = Trigger::mkTrigger( d_quantEngine, q, nodes, matchOption, true, Trigger::TR_MAKE_NEW ); + Trigger * t = Trigger::mkTrigger( d_quantEngine, q, nodes, true, Trigger::TR_MAKE_NEW ); if( t ){ d_user_gen[q].push_back( t ); }else{ @@ -279,8 +278,8 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ Trace("auto-gen-trigger-debug") << "Collected pat terms for " << bd << ", no-patterns : " << d_user_no_gen[f].size() << std::endl; for( unsigned i=0; i<patTermsF.size(); i++ ){ Assert( tinfo.find( patTermsF[i] )!=tinfo.end() ); - Trace("auto-gen-trigger-debug") << " " << patTermsF[i]; - Trace("auto-gen-trigger-debug") << " info[" << tinfo[patTermsF[i]].d_reqPol << ", " << tinfo[patTermsF[i]].d_reqPolEq << ", " << tinfo[patTermsF[i]].d_fv.size() << "]" << std::endl; + Trace("auto-gen-trigger-debug") << " " << patTermsF[i] << std::endl; + Trace("auto-gen-trigger-debug2") << " info = [" << tinfo[patTermsF[i]].d_reqPol << ", " << tinfo[patTermsF[i]].d_reqPolEq << ", " << tinfo[patTermsF[i]].d_fv.size() << "]" << std::endl; } Trace("auto-gen-trigger-debug") << std::endl; } @@ -306,10 +305,28 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ last_weight = curr_w; } } + d_num_trigger_vars[f] = vcMap.size(); + if( d_num_trigger_vars[f]>0 && d_num_trigger_vars[f]<f[0].getNumChildren() ){ + Trace("auto-gen-trigger-partial") << "Quantified formula : " << f << std::endl; + Trace("auto-gen-trigger-partial") << "...does not contain all variables in triggers!!!" << std::endl; + if( options::partialTriggers() ){ + std::vector< Node > vcs[2]; + for( unsigned i=0; i<f[0].getNumChildren(); i++ ){ + Node ic = d_quantEngine->getTermDatabase()->getInstantiationConstant( f, i ); + vcs[ vcMap.find( ic )==vcMap.end() ? 0 : 1 ].push_back( f[0][i] ); + } + for( unsigned i=0; i<2; i++ ){ + d_vc_partition[i][f] = NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, vcs[i] ); + } + }else{ + return; + } + } for( unsigned i=0; i<patTermsF.size(); i++ ){ Node pat = patTermsF[i]; if( rmPatTermsF.find( pat )==rmPatTermsF.end() ){ Trace("auto-gen-trigger-debug") << "...processing pattern " << pat << std::endl; + Node mpat = pat; //process the pattern: if it has a required polarity, consider it Assert( tinfo.find( pat )!=tinfo.end() ); int rpol = tinfo[pat].d_reqPol; @@ -317,19 +334,30 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ unsigned num_fv = tinfo[pat].d_fv.size(); Trace("auto-gen-trigger-debug") << "...required polarity for " << pat << " is " << rpol << ", eq=" << rpoleq << std::endl; if( rpol!=0 ){ + Assert( rpol==1 || rpol==-1 ); if( Trigger::isRelationalTrigger( pat ) ){ pat = rpol==-1 ? pat.negate() : pat; }else{ Assert( Trigger::isAtomicTrigger( pat ) ); if( pat.getType().isBoolean() && rpoleq.isNull() ){ - pat = NodeManager::currentNM()->mkNode( IFF, pat, NodeManager::currentNM()->mkConst( rpol==-1 ) ).negate(); + if( options::literalMatchMode()==LITERAL_MATCH_USE ){ + pat = NodeManager::currentNM()->mkNode( IFF, pat, NodeManager::currentNM()->mkConst( rpol==-1 ) ).negate(); + }else if( options::literalMatchMode()!=LITERAL_MATCH_NONE ){ + pat = NodeManager::currentNM()->mkNode( IFF, pat, NodeManager::currentNM()->mkConst( rpol==1 ) ); + } }else{ Assert( !rpoleq.isNull() ); if( rpol==-1 ){ - //all equivalence classes except rpoleq - pat = NodeManager::currentNM()->mkNode( EQUAL, pat, rpoleq ).negate(); + if( options::literalMatchMode()!=LITERAL_MATCH_NONE ){ + //all equivalence classes except rpoleq + pat = NodeManager::currentNM()->mkNode( EQUAL, pat, rpoleq ).negate(); + } }else if( rpol==1 ){ - //all equivalence classes that are not disequal to rpoleq TODO + if( options::literalMatchMode()==LITERAL_MATCH_AGG ){ + //only equivalence class rpoleq + pat = NodeManager::currentNM()->mkNode( EQUAL, pat, rpoleq ); + } + //all equivalence classes that are not disequal to rpoleq TODO? } } } @@ -337,10 +365,10 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ }else{ if( Trigger::isRelationalTrigger( pat ) ){ //consider both polarities - addPatternToPool( f, pat.negate(), num_fv ); + addPatternToPool( f, pat.negate(), num_fv, mpat ); } } - addPatternToPool( f, pat, num_fv ); + addPatternToPool( f, pat, num_fv, mpat ); } } //tinfo not used below this point @@ -372,19 +400,23 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ if( options::relevantTriggers() ){ sortQuantifiersForSymbol sqfs; sqfs.d_qe = d_quantEngine; + for( unsigned i=0; i<patTerms.size(); i++ ){ + Assert( d_pat_to_mpat.find( patTerms[i] )!=d_pat_to_mpat.end() ); + Assert( d_pat_to_mpat[patTerms[i]].hasOperator() ); + sqfs.d_op_map[ patTerms[i] ] = d_pat_to_mpat[patTerms[i]].getOperator(); + } //sort based on # occurrences (this will cause Trigger to select rarer symbols) std::sort( patTerms.begin(), patTerms.end(), sqfs ); Debug("relevant-trigger") << "Terms based on relevance: " << std::endl; for( unsigned i=0; i<patTerms.size(); i++ ){ - Debug("relevant-trigger") << " " << patTerms[i] << " ("; - Debug("relevant-trigger") << d_quantEngine->getQuantifierRelevance()->getNumQuantifiersForSymbol( patTerms[i].getOperator() ) << ")" << std::endl; + Debug("relevant-trigger") << " " << patTerms[i] << " from " << d_pat_to_mpat[patTerms[i]] << " ("; + Debug("relevant-trigger") << d_quantEngine->getQuantifierRelevance()->getNumQuantifiersForSymbol( d_pat_to_mpat[patTerms[i]].getOperator() ) << ")" << std::endl; } } //now, generate the trigger... - int matchOption = 0; Trigger* tr = NULL; if( d_is_single_trigger[ patTerms[0] ] ){ - tr = Trigger::mkTrigger( d_quantEngine, f, patTerms[0], matchOption, false, Trigger::TR_RETURN_NULL ); + tr = Trigger::mkTrigger( d_quantEngine, f, patTerms[0], false, Trigger::TR_RETURN_NULL, d_num_trigger_vars[f] ); d_single_trigger_gen[ patTerms[0] ] = true; }else{ //only generate multi trigger if option set, or if no single triggers exist @@ -402,29 +434,14 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ d_made_multi_trigger[f] = true; } //will possibly want to get an old trigger - tr = Trigger::mkTrigger( d_quantEngine, f, patTerms, matchOption, false, Trigger::TR_GET_OLD ); + tr = Trigger::mkTrigger( d_quantEngine, f, patTerms, false, Trigger::TR_GET_OLD, d_num_trigger_vars[f] ); } if( tr ){ - unsigned tindex; - if( tr->isMultiTrigger() ){ - //disable all other multi triggers - for( std::map< Trigger*, bool >::iterator it = d_auto_gen_trigger[1][f].begin(); it != d_auto_gen_trigger[1][f].end(); ++it ){ - d_auto_gen_trigger[1][f][ it->first ] = false; - } - tindex = 1; - }else{ - tindex = 0; - } - //making it during an instantiation round, so must reset - if( d_auto_gen_trigger[tindex][f].find( tr )==d_auto_gen_trigger[tindex][f].end() ){ - tr->resetInstantiationRound(); - tr->reset( Node::null() ); - } - d_auto_gen_trigger[tindex][f][tr] = true; + addTrigger( tr, f ); //if we are generating additional triggers... - if( tindex==0 ){ - int index = 0; - if( index<(int)patTerms.size() ){ + if( !tr->isMultiTrigger() ){ + unsigned index = 0; + if( index<patTerms.size() ){ //Notice() << "check add additional" << std::endl; //check if similar patterns exist, and if so, add them additionally int nqfs_curr = 0; @@ -433,18 +450,13 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ } index++; bool success = true; - while( success && index<(int)patTerms.size() && d_is_single_trigger[ patTerms[index] ] ){ + while( success && index<patTerms.size() && d_is_single_trigger[ patTerms[index] ] ){ success = false; if( !options::relevantTriggers() || d_quantEngine->getQuantifierRelevance()->getNumQuantifiersForSymbol( patTerms[index].getOperator() )<=nqfs_curr ){ d_single_trigger_gen[ patTerms[index] ] = true; - Trigger* tr2 = Trigger::mkTrigger( d_quantEngine, f, patTerms[index], matchOption, false, Trigger::TR_RETURN_NULL ); - if( tr2 ){ - //Notice() << "Add additional trigger " << patTerms[index] << std::endl; - tr2->resetInstantiationRound(); - tr2->reset( Node::null() ); - d_auto_gen_trigger[0][f][tr2] = true; - } + Trigger* tr2 = Trigger::mkTrigger( d_quantEngine, f, patTerms[index], false, Trigger::TR_RETURN_NULL, d_num_trigger_vars[f] ); + addTrigger( tr2, f ); success = true; } index++; @@ -457,8 +469,10 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ } } -void InstStrategyAutoGenTriggers::addPatternToPool( Node q, Node pat, unsigned num_fv ) { - if( num_fv==q[0].getNumChildren() && ( options::pureThTriggers() || !Trigger::isPureTheoryTrigger( pat ) ) ){ +void InstStrategyAutoGenTriggers::addPatternToPool( Node q, Node pat, unsigned num_fv, Node mpat ) { + d_pat_to_mpat[pat] = mpat; + unsigned num_vars = options::partialTriggers() ? d_num_trigger_vars[q] : q[0].getNumChildren(); + if( num_fv==num_vars && ( options::pureThTriggers() || !Trigger::isPureTheoryTrigger( pat ) ) ){ d_patTerms[0][q].push_back( pat ); d_is_single_trigger[ pat ] = true; }else{ @@ -467,6 +481,38 @@ void InstStrategyAutoGenTriggers::addPatternToPool( Node q, Node pat, unsigned n } } + +void InstStrategyAutoGenTriggers::addTrigger( inst::Trigger * tr, Node q ) { + if( tr ){ + if( d_num_trigger_vars[q]<q[0].getNumChildren() ){ + //partial trigger : generate implication to mark user pattern + Node ipl = NodeManager::currentNM()->mkNode( INST_PATTERN_LIST, d_quantEngine->getTermDatabase()->getVariableNode( tr->getInstPattern(), q ) ); + Node qq = NodeManager::currentNM()->mkNode( FORALL, d_vc_partition[1][q], NodeManager::currentNM()->mkNode( FORALL, d_vc_partition[0][q], q[1] ), ipl ); + Trace("auto-gen-trigger-partial") << "Make partially specified user pattern: " << std::endl; + Trace("auto-gen-trigger-partial") << " " << qq << std::endl; + Node lem = NodeManager::currentNM()->mkNode( OR, q.negate(), qq ); + d_quantEngine->addLemma( lem ); + }else{ + unsigned tindex; + if( tr->isMultiTrigger() ){ + //disable all other multi triggers + for( std::map< Trigger*, bool >::iterator it = d_auto_gen_trigger[1][q].begin(); it != d_auto_gen_trigger[1][q].end(); ++it ){ + d_auto_gen_trigger[1][q][ it->first ] = false; + } + tindex = 1; + }else{ + tindex = 0; + } + //making it during an instantiation round, so must reset + if( d_auto_gen_trigger[tindex][q].find( tr )==d_auto_gen_trigger[tindex][q].end() ){ + tr->resetInstantiationRound(); + tr->reset( Node::null() ); + } + d_auto_gen_trigger[tindex][q][tr] = true; + } + } +} + bool InstStrategyAutoGenTriggers::hasUserPatterns( Node q ) { if( q.getNumChildren()==3 ){ std::map< Node, bool >::iterator it = d_hasUserPatterns.find( q ); @@ -519,8 +565,7 @@ bool InstStrategyLocalTheoryExt::isLocalTheoryExt( Node f ) { Trace("local-t-ext") << " " << patTerms[i] << std::endl; } Trace("local-t-ext") << std::endl; - int matchOption = 0; - Trigger * tr = Trigger::mkTrigger( d_quantEngine, f, patTerms, matchOption, true, Trigger::TR_GET_OLD ); + Trigger * tr = Trigger::mkTrigger( d_quantEngine, f, patTerms, true, Trigger::TR_GET_OLD ); d_lte_trigger[f] = tr; }else{ Trace("local-t-ext") << "No local theory extensions trigger for " << f << "." << std::endl; diff --git a/src/theory/quantifiers/inst_strategy_e_matching.h b/src/theory/quantifiers/inst_strategy_e_matching.h index 028f24b27..e6d993294 100644..100755 --- a/src/theory/quantifiers/inst_strategy_e_matching.h +++ b/src/theory/quantifiers/inst_strategy_e_matching.h @@ -83,14 +83,18 @@ private: std::map< Node, std::map< inst::Trigger*, bool > > d_processed_trigger; //instantiation no patterns std::map< Node, std::vector< Node > > d_user_no_gen; + // number of trigger variables per quantifier + std::map< Node, unsigned > d_num_trigger_vars; + std::map< Node, Node > d_vc_partition[2]; + std::map< Node, Node > d_pat_to_mpat; private: /** process functions */ void processResetInstantiationRound( Theory::Effort effort ); int process( Node q, Theory::Effort effort, int e ); /** generate triggers */ void generateTriggers( Node q ); - void addPatternToPool( Node q, Node pat, unsigned num_fv ); - //bool addTrigger( inst::Trigger * tr, Node f, unsigned r ); + void addPatternToPool( Node q, Node pat, unsigned num_fv, Node mpat ); + void addTrigger( inst::Trigger * tr, Node f ); /** has user patterns */ bool hasUserPatterns( Node q ); /** has user patterns */ diff --git a/src/theory/quantifiers/instantiation_engine.cpp b/src/theory/quantifiers/instantiation_engine.cpp index 955dc5d86..db597d031 100644..100755 --- a/src/theory/quantifiers/instantiation_engine.cpp +++ b/src/theory/quantifiers/instantiation_engine.cpp @@ -137,11 +137,7 @@ void InstantiationEngine::check( Theory::Effort e, unsigned quant_e ){ doInstantiationRound( e ); if( d_quantEngine->inConflict() ){ Assert( d_quantEngine->getNumLemmasWaiting()>lastWaiting ); - Trace("inst-engine") << "Conflict = " << d_quantEngine->getNumLemmasWaiting() << " / " << d_quantEngine->getNumLemmasAddedThisRound(); - if( lastWaiting>0 ){ - Trace("inst-engine") << " (prev " << lastWaiting << ")"; - } - Trace("inst-engine") << std::endl; + Trace("inst-engine") << "Conflict, added lemmas = " << (d_quantEngine->getNumLemmasWaiting()-lastWaiting) << std::endl; }else if( d_quantEngine->hasAddedLemma() ){ Trace("inst-engine") << "Added lemmas = " << (d_quantEngine->getNumLemmasWaiting()-lastWaiting) << std::endl; } diff --git a/src/theory/quantifiers/instantiation_engine.h b/src/theory/quantifiers/instantiation_engine.h index d2b3740a1..d2b3740a1 100644..100755 --- a/src/theory/quantifiers/instantiation_engine.h +++ b/src/theory/quantifiers/instantiation_engine.h diff --git a/src/theory/quantifiers/kinds b/src/theory/quantifiers/kinds index b03c4ad3b..b03c4ad3b 100644..100755 --- a/src/theory/quantifiers/kinds +++ b/src/theory/quantifiers/kinds diff --git a/src/theory/quantifiers/local_theory_ext.cpp b/src/theory/quantifiers/local_theory_ext.cpp index ada28c084..ada28c084 100644..100755 --- a/src/theory/quantifiers/local_theory_ext.cpp +++ b/src/theory/quantifiers/local_theory_ext.cpp diff --git a/src/theory/quantifiers/local_theory_ext.h b/src/theory/quantifiers/local_theory_ext.h index 94abf3c90..94abf3c90 100644..100755 --- a/src/theory/quantifiers/local_theory_ext.h +++ b/src/theory/quantifiers/local_theory_ext.h diff --git a/src/theory/quantifiers/macros.cpp b/src/theory/quantifiers/macros.cpp index 582599680..582599680 100644..100755 --- a/src/theory/quantifiers/macros.cpp +++ b/src/theory/quantifiers/macros.cpp diff --git a/src/theory/quantifiers/macros.h b/src/theory/quantifiers/macros.h index 39ec2f0a1..39ec2f0a1 100644..100755 --- a/src/theory/quantifiers/macros.h +++ b/src/theory/quantifiers/macros.h diff --git a/src/theory/quantifiers/model_builder.cpp b/src/theory/quantifiers/model_builder.cpp index 3ae36b1d4..10a5ae41b 100644..100755 --- a/src/theory/quantifiers/model_builder.cpp +++ b/src/theory/quantifiers/model_builder.cpp @@ -66,7 +66,7 @@ void QModelBuilder::debugModel( FirstOrderModel* fm ){ tests++; std::vector< Node > terms; for( int k=0; k<riter.getNumTerms(); k++ ){ - terms.push_back( riter.getTerm( k ) ); + terms.push_back( riter.getCurrentTerm( k ) ); } Node n = d_qe->getInstantiation( f, vars, terms ); Node val = fm->getValue( n ); @@ -84,7 +84,9 @@ void QModelBuilder::debugModel( FirstOrderModel* fm ){ } Trace("quant-check-model") << "." << std::endl; }else{ - Trace("quant-check-model") << "Warning: Could not test quantifier " << f << std::endl; + if( riter.isIncomplete() ){ + Trace("quant-check-model") << "Warning: Could not test quantifier " << f << std::endl; + } } } } @@ -114,7 +116,7 @@ bool TermArgBasisTrie::addTerm2( FirstOrderModel* fm, Node n, int argIndex ){ QModelBuilderIG::QModelBuilderIG( context::Context* c, QuantifiersEngine* qe ) : -QModelBuilder( c, qe ) { +QModelBuilder( c, qe ), d_basisNoMatch( c ) { } @@ -302,7 +304,7 @@ void QModelBuilderIG::analyzeModel( FirstOrderModel* fm ){ 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()) ){ + if( d_qe->getTermDatabase()->isTermActive( n ) ){ Node v = fmig->getRepresentative( n ); if( i==0 ){ d_uf_prefs[op].d_const_val = v; @@ -312,12 +314,11 @@ void QModelBuilderIG::analyzeModel( FirstOrderModel* fm ){ } } //for calculating terms that we don't need to consider - if( !n.getAttribute(NoMatchAttribute()) || n.getAttribute(ModelBasisArgAttribute())!=0 ){ - if( !n.getAttribute(BasisNoMatchAttribute()) ){ + if( d_qe->getTermDatabase()->isTermActive( n ) || n.getAttribute(ModelBasisArgAttribute())!=0 ){ + if( d_basisNoMatch.find( n )==d_basisNoMatch.end() ){ //need to consider if it is not congruent modulo model basis if( !tabt.addTerm( fmig, n ) ){ - BasisNoMatchAttribute bnma; - n.setAttribute(bnma,true); + d_basisNoMatch[n] = true; } } } @@ -382,8 +383,8 @@ bool QModelBuilderIG::isQuantifierActive( Node f ){ } bool QModelBuilderIG::isTermActive( Node n ){ - return !n.getAttribute(NoMatchAttribute()) || //it is not congruent to another active term - ( n.getAttribute(ModelBasisArgAttribute())!=0 && !n.getAttribute(BasisNoMatchAttribute()) ); //or it has model basis arguments + return d_qe->getTermDatabase()->isTermActive( n ) || //it is not congruent to another active term + ( n.getAttribute(ModelBasisArgAttribute())!=0 && d_basisNoMatch.find( n )==d_basisNoMatch.end() ); //or it has model basis arguments //and is not congruent modulo model basis //to another active term } @@ -400,15 +401,19 @@ bool QModelBuilderIG::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, i 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; + if( Debug.isOn("inst-fmf-ei-debug") ){ + for( int i=0; i<(int)riter.d_index.size(); i++ ){ + Debug("inst-fmf-ei-debug") << i << " : " << riter.d_index[i] << " : " << riter.getCurrentTerm( 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( Debug.isOn("fmf-model-eval") ){ + 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; @@ -426,7 +431,7 @@ bool QModelBuilderIG::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, i //instantiation was not shown to be true, construct the match InstMatch m( f ); for( int i=0; i<riter.getNumTerms(); i++ ){ - m.set( d_qe, riter.d_index_order[i], riter.getTerm( i ) ); + m.set( d_qe, i, riter.getCurrentTerm( i ) ); } Debug("fmf-model-eval") << "* Add instantiation " << m << std::endl; //add as instantiation @@ -464,8 +469,8 @@ bool QModelBuilderIG::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, i 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; + //if the iterator is incomplete, we will return unknown instead of sat if no instantiations are added this round + d_incomplete_check = riter.isIncomplete(); return true; }else{ return false; @@ -667,7 +672,7 @@ int QModelBuilderDefault::doInstGen( FirstOrderModel* fm, Node f ){ //if applicable, try to add exceptions here if( !tr_terms.empty() ){ //make a trigger for these terms, add instantiations - inst::Trigger* tr = inst::Trigger::mkTrigger( d_qe, f, tr_terms, 0, true, inst::Trigger::TR_MAKE_NEW ); + inst::Trigger* tr = inst::Trigger::mkTrigger( d_qe, f, tr_terms, true, inst::Trigger::TR_MAKE_NEW ); //Notice() << "Trigger = " << (*tr) << std::endl; tr->resetInstantiationRound(); tr->reset( Node::null() ); diff --git a/src/theory/quantifiers/model_builder.h b/src/theory/quantifiers/model_builder.h index 906673903..e4f9529a8 100644..100755 --- a/src/theory/quantifiers/model_builder.h +++ b/src/theory/quantifiers/model_builder.h @@ -57,15 +57,6 @@ public: -/** Attribute true for nodes that should not be used when considered for inst-gen basis */ -struct BasisNoMatchAttributeId {}; -/** use the special for boolean flag */ -typedef expr::Attribute< BasisNoMatchAttributeId, - bool, - expr::attr::NullCleanupStrategy, - true // context dependent - > BasisNoMatchAttribute; - class TermArgBasisTrie { private: bool addTerm2( FirstOrderModel* fm, Node n, int argIndex ); @@ -85,7 +76,9 @@ public: */ class QModelBuilderIG : public QModelBuilder { + typedef context::CDHashMap<Node, bool, NodeHashFunction> BoolMap; protected: + BoolMap d_basisNoMatch; //map from operators to model preference data std::map< Node, uf::UfModelPreferenceData > d_uf_prefs; //built model uf diff --git a/src/theory/quantifiers/model_engine.cpp b/src/theory/quantifiers/model_engine.cpp index 0bbca88eb..5d575969f 100644..100755 --- a/src/theory/quantifiers/model_engine.cpp +++ b/src/theory/quantifiers/model_engine.cpp @@ -153,27 +153,23 @@ int ModelEngine::checkModel(){ //d_quantEngine->getEqualityQuery()->flattenRepresentatives( fm->d_rep_set.d_type_reps ); //for debugging - if( Trace.isOn("model-engine") || Trace.isOn("model-engine-debug") ){ - for( std::map< TypeNode, std::vector< Node > >::iterator it = fm->d_rep_set.d_type_reps.begin(); - it != fm->d_rep_set.d_type_reps.end(); ++it ){ - if( it->first.isSort() ){ - Trace("model-engine") << "Cardinality( " << it->first << " )" << " = " << it->second.size() << std::endl; - if( Trace.isOn("model-engine-debug") ){ - Trace("model-engine-debug") << " Reps : "; - for( size_t i=0; i<it->second.size(); i++ ){ - Trace("model-engine-debug") << it->second[i] << " "; - } - Trace("model-engine-debug") << std::endl; - Trace("model-engine-debug") << " Term reps : "; - for( size_t i=0; i<it->second.size(); i++ ){ - Node r = d_quantEngine->getEqualityQuery()->getInternalRepresentative( it->second[i], Node::null(), 0 ); - Trace("model-engine-debug") << r << " "; - } - Trace("model-engine-debug") << std::endl; - Node mbt = d_quantEngine->getTermDatabase()->getModelBasisTerm(it->first); - Trace("model-engine-debug") << " Basis term : " << mbt << std::endl; - } + for( std::map< TypeNode, std::vector< Node > >::iterator it = fm->d_rep_set.d_type_reps.begin(); + it != fm->d_rep_set.d_type_reps.end(); ++it ){ + if( it->first.isSort() ){ + Trace("model-engine") << "Cardinality( " << it->first << " )" << " = " << it->second.size() << std::endl; + Trace("model-engine-debug") << " Reps : "; + for( size_t i=0; i<it->second.size(); i++ ){ + Trace("model-engine-debug") << it->second[i] << " "; } + Trace("model-engine-debug") << std::endl; + Trace("model-engine-debug") << " Term reps : "; + for( size_t i=0; i<it->second.size(); i++ ){ + Node r = d_quantEngine->getEqualityQuery()->getInternalRepresentative( it->second[i], Node::null(), 0 ); + Trace("model-engine-debug") << r << " "; + } + Trace("model-engine-debug") << std::endl; + Node mbt = d_quantEngine->getTermDatabase()->getModelBasisTerm(it->first); + Trace("model-engine-debug") << " Basis term : " << mbt << std::endl; } } @@ -221,11 +217,12 @@ int ModelEngine::checkModel(){ //print debug information if( d_quantEngine->inConflict() ){ - Trace("model-engine") << "Conflict = " << d_quantEngine->getNumLemmasWaiting() << " / " << d_quantEngine->getNumLemmasAddedThisRound() << std::endl; + Trace("model-engine") << "Conflict, added lemmas = "; }else{ - Trace("model-engine") << "Added Lemmas = " << d_addedLemmas << " / " << d_triedLemmas << " / "; - Trace("model-engine") << d_totalLemmas << std::endl; - } + Trace("model-engine") << "Added Lemmas = "; + } + Trace("model-engine") << d_addedLemmas << " / " << d_triedLemmas << " / "; + Trace("model-engine") << d_totalLemmas << std::endl; return d_addedLemmas; } @@ -281,15 +278,15 @@ void ModelEngine::exhaustiveInstantiate( Node f, int effort ){ //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 ) ){ - Trace("fmf-exh-inst") << "...exhaustive instantiation set, incomplete=" << riter.d_incomplete << "..." << std::endl; - if( !riter.d_incomplete ){ + Trace("fmf-exh-inst") << "...exhaustive instantiation set, incomplete=" << riter.isIncomplete() << "..." << std::endl; + if( !riter.isIncomplete() ){ 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( f ); for( int i=0; i<riter.getNumTerms(); i++ ){ - m.set( d_quantEngine, riter.d_index_order[i], riter.getTerm( i ) ); + m.set( d_quantEngine, i, riter.getCurrentTerm( i ) ); } Debug("fmf-model-eval") << "* Add instantiation " << m << std::endl; triedLemmas++; @@ -309,11 +306,10 @@ void ModelEngine::exhaustiveInstantiate( Node f, int effort ){ d_statistics.d_exh_inst_lemmas += addedLemmas; } }else{ - Trace("fmf-exh-inst") << "...exhaustive instantiation failed to set, incomplete=" << riter.d_incomplete << "..." << std::endl; - Assert( riter.d_incomplete ); + Trace("fmf-exh-inst") << "...exhaustive instantiation did set, incomplete=" << riter.isIncomplete() << "..." << 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; + d_incomplete_check = d_incomplete_check || riter.isIncomplete(); } } diff --git a/src/theory/quantifiers/model_engine.h b/src/theory/quantifiers/model_engine.h index 12f18aa08..12f18aa08 100644..100755 --- a/src/theory/quantifiers/model_engine.h +++ b/src/theory/quantifiers/model_engine.h diff --git a/src/theory/quantifiers/quant_conflict_find.cpp b/src/theory/quantifiers/quant_conflict_find.cpp index ca87a607d..bac2aa35c 100644..100755 --- a/src/theory/quantifiers/quant_conflict_find.cpp +++ b/src/theory/quantifiers/quant_conflict_find.cpp @@ -49,6 +49,7 @@ QuantInfo::~QuantInfo() { void QuantInfo::initialize( QuantConflictFind * p, Node q, Node qn ) { d_q = q; + d_extra_var.clear(); for( unsigned i=0; i<q[0].getNumChildren(); i++ ){ d_match.push_back( TNode::null() ); d_match_term.push_back( TNode::null() ); @@ -77,33 +78,31 @@ void QuantInfo::initialize( QuantConflictFind * p, Node q, Node qn ) { } } */ - if( d_mg->isValid() ){ - for( unsigned j=q[0].getNumChildren(); j<d_vars.size(); j++ ){ - if( d_vars[j].getKind()!=BOUND_VARIABLE ){ - d_var_mg[j] = NULL; - bool is_tsym = false; - if( !MatchGen::isHandledUfTerm( d_vars[j] ) && d_vars[j].getKind()!=ITE ){ - is_tsym = true; - d_tsym_vars.push_back( j ); - } - if( !is_tsym || options::qcfTConstraint() ){ - d_var_mg[j] = new MatchGen( this, d_vars[j], true ); - } - if( !d_var_mg[j] || !d_var_mg[j]->isValid() ){ - Trace("qcf-invalid") << "QCF invalid : cannot match for " << d_vars[j] << std::endl; - d_mg->setInvalid(); - break; - }else{ - std::vector< int > bvars; - d_var_mg[j]->determineVariableOrder( this, bvars ); - } + for( unsigned j=q[0].getNumChildren(); j<d_vars.size(); j++ ){ + if( d_vars[j].getKind()!=BOUND_VARIABLE ){ + d_var_mg[j] = NULL; + bool is_tsym = false; + if( !MatchGen::isHandledUfTerm( d_vars[j] ) && d_vars[j].getKind()!=ITE ){ + is_tsym = true; + d_tsym_vars.push_back( j ); + } + if( !is_tsym || options::qcfTConstraint() ){ + d_var_mg[j] = new MatchGen( this, d_vars[j], true ); + } + if( !d_var_mg[j] || !d_var_mg[j]->isValid() ){ + Trace("qcf-invalid") << "QCF invalid : cannot match for " << d_vars[j] << std::endl; + d_mg->setInvalid(); + break; + }else{ + std::vector< int > bvars; + d_var_mg[j]->determineVariableOrder( this, bvars ); } - } - if( d_mg->isValid() ){ - std::vector< int > bvars; - d_mg->determineVariableOrder( this, bvars ); } } + if( d_mg->isValid() ){ + std::vector< int > bvars; + d_mg->determineVariableOrder( this, bvars ); + } }else{ Trace("qcf-invalid") << "QCF invalid : body of formula cannot be processed." << std::endl; } @@ -113,14 +112,15 @@ void QuantInfo::initialize( QuantConflictFind * p, Node q, Node qn ) { //optimization : record variable argument positions for terms that must be matched std::vector< TNode > vars; //TODO: revisit this, makes QCF faster, but misses conflicts due to caring about paths that may not be relevant (starExec jobs 14136/14137) - //if( options::qcfSkipRd() ){ - // for( unsigned j=q[0].getNumChildren(); j<d_vars.size(); j++ ){ - // vars.push_back( d_vars[j] ); - // } - //} - //get all variables that are always relevant - std::map< TNode, bool > visited; - getPropagateVars( vars, q[1], false, visited ); + if( options::qcfSkipRd() ){ + for( unsigned j=q[0].getNumChildren(); j<d_vars.size(); j++ ){ + vars.push_back( d_vars[j] ); + } + }else{ + //get all variables that are always relevant + std::map< TNode, bool > visited; + getPropagateVars( p, vars, q[1], false, visited ); + } for( unsigned j=0; j<vars.size(); j++ ){ Node v = vars[j]; TNode f = p->getTermDatabase()->getMatchOperator( v ); @@ -141,7 +141,7 @@ void QuantInfo::initialize( QuantConflictFind * p, Node q, Node qn ) { } } -void QuantInfo::getPropagateVars( std::vector< TNode >& vars, TNode n, bool pol, std::map< TNode, bool >& visited ){ +void QuantInfo::getPropagateVars( QuantConflictFind * p, std::vector< TNode >& vars, TNode n, bool pol, std::map< TNode, bool >& visited ){ std::map< TNode, bool >::iterator itv = visited.find( n ); if( itv==visited.end() ){ visited[n] = true; @@ -150,6 +150,12 @@ void QuantInfo::getPropagateVars( std::vector< TNode >& vars, TNode n, bool pol, if( d_var_num.find( n )!=d_var_num.end() ){ Assert( std::find( vars.begin(), vars.end(), n )==vars.end() ); vars.push_back( n ); + TNode f = p->getTermDatabase()->getMatchOperator( n ); + if( !f.isNull() ){ + if( std::find( p->d_func_rel_dom[f].begin(), p->d_func_rel_dom[f].end(), d_q )==p->d_func_rel_dom[f].end() ){ + p->d_func_rel_dom[f].push_back( d_q ); + } + } }else if( MatchGen::isHandledBoolConnective( n ) ){ Assert( n.getKind()!=IMPLIES ); QuantPhaseReq::getEntailPolarity( n, 0, true, pol, rec, newPol ); @@ -157,12 +163,16 @@ void QuantInfo::getPropagateVars( std::vector< TNode >& vars, TNode n, bool pol, Trace("qcf-opt-debug") << "getPropagateVars " << n << ", pol = " << pol << ", rec = " << rec << std::endl; if( rec ){ for( unsigned i=0; i<n.getNumChildren(); i++ ){ - getPropagateVars( vars, n[i], pol, visited ); + getPropagateVars( p, vars, n[i], pol, visited ); } } } } +bool QuantInfo::isBaseMatchComplete() { + return d_vars_set.size()==(d_q[0].getNumChildren()+d_extra_var.size()); +} + void QuantInfo::registerNode( Node n, bool hasPol, bool pol, bool beneathQuant ) { Trace("qcf-qregister-debug2") << "Register : " << n << std::endl; if( n.getKind()==FORALL ){ @@ -209,7 +219,6 @@ void QuantInfo::flatten( Node n, bool beneathQuant ) { if( n.getKind()==BOUND_VARIABLE ){ d_inMatchConstraint[n] = true; } - //if( MatchGen::isHandledUfTerm( n ) || n.getKind()==ITE ){ if( d_var_num.find( n )==d_var_num.end() ){ Trace("qcf-qregister-debug2") << "Add FLATTEN VAR : " << n << std::endl; d_var_num[n] = d_vars.size(); @@ -219,6 +228,8 @@ void QuantInfo::flatten( Node n, bool beneathQuant ) { d_match_term.push_back( TNode::null() ); if( n.getKind()==ITE ){ registerNode( n, false, false ); + }else if( n.getKind()==BOUND_VARIABLE ){ + d_extra_var.push_back( n ); }else{ for( unsigned i=0; i<n.getNumChildren(); i++ ){ flatten( n[i], beneathQuant ); @@ -233,13 +244,15 @@ void QuantInfo::flatten( Node n, bool beneathQuant ) { } -void QuantInfo::reset_round( QuantConflictFind * p ) { +bool QuantInfo::reset_round( QuantConflictFind * p ) { for( unsigned i=0; i<d_match.size(); i++ ){ d_match[i] = TNode::null(); d_match_term[i] = TNode::null(); } + d_vars_set.clear(); d_curr_var_deq.clear(); d_tconstraints.clear(); + //add built-in variable constraints for( unsigned r=0; r<2; r++ ){ for( std::map< int, std::vector< Node > >::iterator it = d_var_constraint[r].begin(); @@ -257,7 +270,7 @@ void QuantInfo::reset_round( QuantConflictFind * p ) { d_mg->d_children.clear(); d_mg->d_n = NodeManager::currentNM()->mkConst( true ); d_mg->d_type = MatchGen::typ_ground; - return; + return false; } } } @@ -268,6 +281,7 @@ void QuantInfo::reset_round( QuantConflictFind * p ) { } //now, reset for matching d_mg->reset( p, false, this ); + return true; } int QuantInfo::getCurrentRepVar( int v ) { @@ -377,11 +391,12 @@ int QuantInfo::addConstraint( QuantConflictFind * p, int v, TNode n, int vn, boo } } } - d_match[v] = TNode::null(); + unsetMatch( p, v ); return 1; }else{ //std::map< int, TNode >::iterator itm = d_match.find( v ); bool isGroundRep = false; + bool isGround = false; if( vn!=-1 ){ Debug("qcf-match-debug") << " ...Variable bound to variable" << std::endl; //std::map< int, TNode >::iterator itmn = d_match.find( vn ); @@ -428,13 +443,14 @@ int QuantInfo::addConstraint( QuantConflictFind * p, int v, TNode n, int vn, boo Debug("qcf-match-debug") << " ...Variable bound to ground" << std::endl; if( d_match[v].isNull() ){ //isGroundRep = true; ?? + isGround = true; }else{ //compare ground values Debug("qcf-match-debug") << " -> Ground value, compare " << d_match[v] << " "<< n << std::endl; return p->areMatchEqual( d_match[v], n ) ? 0 : -1; } } - if( setMatch( p, v, n, isGroundRep ) ){ + if( setMatch( p, v, n, isGroundRep, isGround ) ){ Debug("qcf-match-debug") << " -> success" << std::endl; return 1; }else{ @@ -500,7 +516,7 @@ bool QuantInfo::isConstrainedVar( int v ) { } } -bool QuantInfo::setMatch( QuantConflictFind * p, int v, TNode n, bool isGroundRep ) { +bool QuantInfo::setMatch( QuantConflictFind * p, int v, TNode n, bool isGroundRep, bool isGround ) { if( getCurrentCanBeEqual( p, v, n ) ){ if( isGroundRep ){ //fail if n does not exist in the relevant domain of each of the argument positions @@ -518,6 +534,12 @@ bool QuantInfo::setMatch( QuantConflictFind * p, int v, TNode n, bool isGroundRe } } Debug("qcf-match-debug") << "-- bind : " << v << " -> " << n << ", checked " << d_curr_var_deq[v].size() << " disequalities" << std::endl; + if( isGround ){ + if( d_vars[v].getKind()==BOUND_VARIABLE ){ + d_vars_set[v] = true; + Debug("qcf-match-debug") << "---- now bound " << d_vars_set.size() << " / " << d_q[0].getNumChildren() << " base variables." << std::endl; + } + } d_match[v] = n; return true; }else{ @@ -525,6 +547,14 @@ bool QuantInfo::setMatch( QuantConflictFind * p, int v, TNode n, bool isGroundRe } } +void QuantInfo::unsetMatch( QuantConflictFind * p, int v ) { + Debug("qcf-match-debug") << "-- unbind : " << v << std::endl; + if( d_vars[v].getKind()==BOUND_VARIABLE && d_vars_set.find( v )!=d_vars_set.end() ){ + d_vars_set.erase( v ); + } + d_match[ v ] = TNode::null(); +} + bool QuantInfo::isMatchSpurious( QuantConflictFind * p ) { for( int i=0; i<getNumVars(); i++ ){ //std::map< int, TNode >::iterator it = d_match.find( i ); @@ -538,6 +568,42 @@ bool QuantInfo::isMatchSpurious( QuantConflictFind * p ) { } bool QuantInfo::isTConstraintSpurious( QuantConflictFind * p, std::vector< Node >& terms ) { + if( options::qcfEagerTest() ){ + //check whether the instantiation evaluates as expected + if( p->d_effort==QuantConflictFind::effort_conflict ){ + Trace("qcf-instance-check") << "Possible conflict instance for " << d_q << " : " << std::endl; + std::map< TNode, TNode > subs; + for( unsigned i=0; i<terms.size(); i++ ){ + Trace("qcf-instance-check") << " " << terms[i] << std::endl; + subs[d_q[0][i]] = terms[i]; + } + for( unsigned i=0; i<d_extra_var.size(); i++ ){ + Node n = getCurrentExpValue( d_extra_var[i] ); + Trace("qcf-instance-check") << " " << d_extra_var[i] << " -> " << n << std::endl; + subs[d_extra_var[i]] = n; + } + if( !p->getTermDatabase()->isEntailed( d_q[1], subs, false, false ) ){ + Trace("qcf-instance-check") << "...not entailed to be false." << std::endl; + return true; + } + }else{ + Node inst = p->d_quantEngine->getInstantiation( d_q, terms ); + Node inst_eval = p->getTermDatabase()->evaluateTerm( inst, NULL, options::qcfTConstraint() ); + if( Trace.isOn("qcf-instance-check") ){ + Trace("qcf-instance-check") << "Possible propagating instance for " << d_q << " : " << std::endl; + for( unsigned i=0; i<terms.size(); i++ ){ + Trace("qcf-instance-check") << " " << terms[i] << std::endl; + } + Trace("qcf-instance-check") << "...evaluates to " << inst_eval << std::endl; + } + if( inst_eval.isNull() || inst_eval==p->getTermDatabase()->d_true || !isPropagatingInstance( p, inst_eval ) ){ + Trace("qcf-instance-check") << "...spurious." << std::endl; + return true; + }else{ + Trace("qcf-instance-check") << "...not spurious." << std::endl; + } + } + } if( !d_tconstraints.empty() ){ //check constraints for( std::map< Node, bool >::iterator it = d_tconstraints.begin(); it != d_tconstraints.end(); ++it ){ @@ -552,6 +618,26 @@ bool QuantInfo::isTConstraintSpurious( QuantConflictFind * p, std::vector< Node return false; } +bool QuantInfo::isPropagatingInstance( QuantConflictFind * p, Node n ) { + if( n.getKind()==FORALL ){ + //TODO? + return true; + }else if( n.getKind()==NOT || n.getKind()==AND || n.getKind()==OR || n.getKind()==EQUAL || n.getKind()==ITE || n.getKind()==IFF ){ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + if( !isPropagatingInstance( p, n[i] ) ){ + return false; + } + } + return true; + }else{ + if( p->getEqualityEngine()->hasTerm( n ) || isGroundSubterm( n ) ){ + return true; + } + } + Trace("qcf-instance-check-debug") << "...not propagating instance because of " << n << std::endl; + return false; +} + bool QuantInfo::entailmentTest( QuantConflictFind * p, Node lit, bool chEnt ) { Trace("qcf-tconstraint-debug") << "Check : " << lit << std::endl; Node rew = Rewriter::rewrite( lit ); @@ -606,6 +692,9 @@ bool QuantInfo::completeMatch( QuantConflictFind * p, std::vector< int >& assign doFail = true; success = false; }else{ + if( isBaseMatchComplete() && options::qcfEagerTest() ){ + return true; + } //solve for interpreted symbol matches // this breaks the invariant that all introduced constraints are over existing terms for( int i=(int)(d_tsym_vars.size()-1); i>=0; i-- ){ @@ -636,7 +725,7 @@ bool QuantInfo::completeMatch( QuantConflictFind * p, std::vector< int >& assign if( !z.isNull() ){ Trace("qcf-tconstraint-debug") << "...set " << d_vars[vn] << " = " << z << std::endl; assigned.push_back( vn ); - if( !setMatch( p, vn, z, false ) ){ + if( !setMatch( p, vn, z, false, true ) ){ success = false; break; } @@ -678,7 +767,7 @@ bool QuantInfo::completeMatch( QuantConflictFind * p, std::vector< int >& assign if( !sum.isNull() ){ assigned.push_back( slv_v ); Trace("qcf-tconstraint-debug") << "...set " << d_vars[slv_v] << " = " << sum << std::endl; - if( !setMatch( p, slv_v, sum, false ) ){ + if( !setMatch( p, slv_v, sum, false, true ) ){ success = false; } p->d_tempCache.push_back( sum ); @@ -764,7 +853,7 @@ bool QuantInfo::completeMatch( QuantConflictFind * p, std::vector< int >& assign int currIndex = d_una_eqc_count[d_una_index]; d_una_eqc_count[d_una_index]++; Trace("qcf-check-unassign") << d_unassigned[d_una_index] << "->" << p->d_eqcs[d_unassigned_tn[d_una_index]][currIndex] << std::endl; - if( setMatch( p, d_unassigned[d_una_index], p->d_eqcs[d_unassigned_tn[d_una_index]][currIndex], true ) ){ + if( setMatch( p, d_unassigned[d_una_index], p->d_eqcs[d_unassigned_tn[d_una_index]][currIndex], true, true ) ){ d_match_term[d_unassigned[d_una_index]] = TNode::null(); Trace("qcf-check-unassign") << "Succeeded match " << d_una_index << std::endl; d_una_index++; @@ -813,9 +902,7 @@ bool QuantInfo::completeMatch( QuantConflictFind * p, std::vector< int >& assign } return true; }else{ - for( unsigned i=0; i<assigned.size(); i++ ){ - d_match[ assigned[i] ] = TNode::null(); - } + revertMatch( p, assigned ); assigned.clear(); return false; } @@ -837,9 +924,9 @@ void QuantInfo::getMatch( std::vector< Node >& terms ){ } } -void QuantInfo::revertMatch( std::vector< int >& assigned ) { +void QuantInfo::revertMatch( QuantConflictFind * p, std::vector< int >& assigned ) { for( unsigned i=0; i<assigned.size(); i++ ){ - d_match[ assigned[i] ] = TNode::null(); + unsetMatch( p, assigned[i] ); } } @@ -899,26 +986,7 @@ MatchGen::MatchGen( QuantInfo * qi, Node n, bool isVar ) if( isVar ){ Assert( qi->d_var_num.find( n )!=qi->d_var_num.end() ); if( n.getKind()==ITE ){ - /* - d_type = typ_ite_var; - d_type_not = false; - d_n = n; - d_children.push_back( MatchGen( qi, d_n[0] ) ); - if( d_children[0].isValid() ){ - d_type = typ_ite_var; - for( unsigned i=1; i<=2; i++ ){ - Node nn = n.eqNode( n[i] ); - d_children.push_back( MatchGen( qi, nn ) ); - d_children[d_children.size()-1].d_qni_bound_except.push_back( 0 ); - if( !d_children[d_children.size()-1].isValid() ){ - setInvalid(); - break; - } - } - }else{ -*/ - d_type = typ_invalid; - //} + d_type = typ_invalid; }else{ d_type = isHandledUfTerm( n ) ? typ_var : typ_tsym; d_qni_var_num[0] = qi->getVarNum( n ); @@ -961,26 +1029,6 @@ MatchGen::MatchGen( QuantInfo * qi, Node n, bool isVar ) break; } } - /* - else if( isTop && n.getKind()==OR && d_children[d_children.size()-1].d_type==typ_var_eq ){ - Trace("qcf-qregister-debug") << "Remove child, make built-in constraint" << std::endl; - //if variable equality/disequality at top level, remove immediately - bool cIsNot = d_children[d_children.size()-1].d_type_not; - Node cn = d_children[d_children.size()-1].d_n; - Assert( cn.getKind()==EQUAL ); - Assert( p->d_qinfo[q].isVar( cn[0] ) || p->d_qinfo[q].isVar( cn[1] ) ); - //make it a built-in constraint instead - for( unsigned i=0; i<2; i++ ){ - if( p->d_qinfo[q].isVar( cn[i] ) ){ - int v = p->d_qinfo[q].getVarNum( cn[i] ); - Node cno = cn[i==0 ? 1 : 0]; - p->d_qinfo[q].d_var_constraint[ cIsNot ? 0 : 1 ][v].push_back( cno ); - break; - } - } - d_children.pop_back(); - } - */ } }else{ d_type = typ_invalid; @@ -1003,6 +1051,7 @@ MatchGen::MatchGen( QuantInfo * qi, Node n, bool isVar ) } }else{ d_qni_gterm[i] = d_n[i]; + qi->setGroundSubterm( d_n[i] ); } } d_type = d_n.getKind()==EQUAL ? typ_eq : typ_tconstraint; @@ -1013,21 +1062,8 @@ MatchGen::MatchGen( QuantInfo * qi, Node n, bool isVar ) //we will just evaluate d_n = n; d_type = typ_ground; + qi->setGroundSubterm( d_n ); } - //if( d_type!=typ_invalid ){ - //determine an efficient children ordering - //if( !d_children.empty() ){ - //for( unsigned i=0; i<d_children.size(); i++ ){ - // d_children_order.push_back( i ); - //} - //if( !d_n.isNull() && ( d_n.getKind()==OR || d_n.getKind()==AND || d_n.getKind()==IFF ) ){ - //sort based on the type of the constraint : ground comes first, then literals, then others - //MatchGenSort mgs; - //mgs.d_mg = this; - //std::sort( d_children_order.begin(), d_children_order.end(), mgs ); - //} - //} - //} } Trace("qcf-qregister-debug") << "Done make match gen " << n << ", type = "; debugPrintType( "qcf-qregister-debug", d_type, true ); @@ -1036,78 +1072,96 @@ MatchGen::MatchGen( QuantInfo * qi, Node n, bool isVar ) } -void MatchGen::collectBoundVar( QuantInfo * qi, Node n, std::vector< int >& cbvars ) { - int v = qi->getVarNum( n ); - if( v!=-1 && std::find( cbvars.begin(), cbvars.end(), v )==cbvars.end() ){ - cbvars.push_back( v ); - } - for( unsigned i=0; i<n.getNumChildren(); i++ ){ - collectBoundVar( qi, n[i], cbvars ); +void MatchGen::collectBoundVar( QuantInfo * qi, Node n, std::vector< int >& cbvars, std::map< Node, bool >& visited, bool& hasNested ) { + if( visited.find( n )==visited.end() ){ + visited[n] = true; + if( n.getKind()==FORALL ){ + hasNested = true; + } + int v = qi->getVarNum( n ); + if( v!=-1 && std::find( cbvars.begin(), cbvars.end(), v )==cbvars.end() ){ + cbvars.push_back( v ); + } + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + collectBoundVar( qi, n[i], cbvars, visited, hasNested ); + } } } void MatchGen::determineVariableOrder( QuantInfo * qi, std::vector< int >& bvars ) { - Trace("qcf-qregister-debug") << "Determine variable order " << d_n << std::endl; - bool isCom = d_type==typ_formula && ( d_n.getKind()==OR || d_n.getKind()==AND || d_n.getKind()==IFF ); - std::map< int, std::vector< int > > c_to_vars; - std::map< int, std::vector< int > > vars_to_c; - std::map< int, int > vb_count; - std::map< int, int > vu_count; - std::vector< bool > assigned; - Trace("qcf-qregister-debug") << "Calculate bound variables..." << std::endl; - for( unsigned i=0; i<d_children.size(); i++ ){ - collectBoundVar( qi, d_children[i].d_n, c_to_vars[i] ); - assigned.push_back( false ); - vb_count[i] = 0; - vu_count[i] = 0; - for( unsigned j=0; j<c_to_vars[i].size(); j++ ){ - int v = c_to_vars[i][j]; - vars_to_c[v].push_back( i ); - if( std::find( bvars.begin(), bvars.end(), v )==bvars.end() ){ - vu_count[i]++; - if( !isCom ){ - bvars.push_back( v ); + Trace("qcf-qregister-debug") << "Determine variable order " << d_n << ", #bvars = " << bvars.size() << std::endl; + bool isComm = d_type==typ_formula && ( d_n.getKind()==OR || d_n.getKind()==AND || d_n.getKind()==IFF ); + if( isComm ){ + std::map< int, std::vector< int > > c_to_vars; + std::map< int, std::vector< int > > vars_to_c; + std::map< int, int > vb_count; + std::map< int, int > vu_count; + std::map< int, bool > has_nested; + std::vector< bool > assigned; + Trace("qcf-qregister-debug") << "Calculate bound variables..." << std::endl; + for( unsigned i=0; i<d_children.size(); i++ ){ + std::map< Node, bool > visited; + has_nested[i] = false; + collectBoundVar( qi, d_children[i].d_n, c_to_vars[i], visited, has_nested[i] ); + assigned.push_back( false ); + vb_count[i] = 0; + vu_count[i] = 0; + for( unsigned j=0; j<c_to_vars[i].size(); j++ ){ + int v = c_to_vars[i][j]; + vars_to_c[v].push_back( i ); + if( std::find( bvars.begin(), bvars.end(), v )==bvars.end() ){ + vu_count[i]++; + }else{ + vb_count[i]++; } - }else{ - vb_count[i]++; } } - } - if( isCom ){ - //children that bind the least number of unbound variables go first + //children that bind no unbound variable, then the most number of bound, unbound variables go first + Trace("qcf-qregister-vo") << "Variable order for " << d_n << " : " << std::endl; do { + int min_score0 = -1; int min_score = -1; int min_score_index = -1; for( unsigned i=0; i<d_children.size(); i++ ){ if( !assigned[i] ){ - int score = vu_count[i]; - if( min_score==-1 || score<min_score ){ + Trace("qcf-qregister-debug2") << "Child " << i << " has b/ub : " << vb_count[i] << "/" << vu_count[i] << std::endl; + int score0 = 0;//has_nested[i] ? 0 : 1; + int score; + if( !options::qcfVoExp() ){ + score = vu_count[i]; + }else{ + score = vu_count[i]==0 ? 0 : ( 1 + qi->d_vars.size()*( qi->d_vars.size() - vb_count[i] ) + ( qi->d_vars.size() - vu_count[i] ) ); + } + if( min_score==-1 || score0<min_score0 || ( score0==min_score0 && score<min_score ) ){ + min_score0 = score0; min_score = score; min_score_index = i; } } } - Trace("qcf-qregister-debug") << "...assign child " << min_score_index << "/" << d_children.size() << std::endl; + Trace("qcf-qregister-vo") << " " << d_children_order.size()+1 << ": " << d_children[min_score_index].d_n << " : "; + Trace("qcf-qregister-vo") << vu_count[min_score_index] << " " << vb_count[min_score_index] << " " << has_nested[min_score_index] << std::endl; + Trace("qcf-qregister-debug") << "...assign child " << min_score_index << std::endl; + Trace("qcf-qregister-debug") << "...score : " << min_score << std::endl; Assert( min_score_index!=-1 ); //add to children order d_children_order.push_back( min_score_index ); assigned[min_score_index] = true; - //if( vb_count[min_score_index]==0 ){ - // d_independent.push_back( min_score_index ); - //} //determine order internal to children d_children[min_score_index].determineVariableOrder( qi, bvars ); Trace("qcf-qregister-debug") << "...bind variables" << std::endl; //now, make it a bound variable - for( unsigned i=0; i<c_to_vars[min_score_index].size(); i++ ){ - int v = c_to_vars[min_score_index][i]; - if( std::find( bvars.begin(), bvars.end(), v )==bvars.end() ){ - for( unsigned j=0; j<vars_to_c[v].size(); j++ ){ - int vc = vars_to_c[v][j]; - vu_count[vc]--; - vb_count[vc]++; + if( vu_count[min_score_index]>0 ){ + for( unsigned i=0; i<c_to_vars[min_score_index].size(); i++ ){ + int v = c_to_vars[min_score_index][i]; + if( std::find( bvars.begin(), bvars.end(), v )==bvars.end() ){ + for( unsigned j=0; j<vars_to_c[v].size(); j++ ){ + int vc = vars_to_c[v][j]; + vu_count[vc]--; + vb_count[vc]++; + } + bvars.push_back( v ); } - bvars.push_back( v ); } } Trace("qcf-qregister-debug") << "...done assign child " << min_score_index << std::endl; @@ -1117,6 +1171,16 @@ void MatchGen::determineVariableOrder( QuantInfo * qi, std::vector< int >& bvars for( unsigned i=0; i<d_children.size(); i++ ){ d_children_order.push_back( i ); d_children[i].determineVariableOrder( qi, bvars ); + //now add to bvars + std::map< Node, bool > visited; + std::vector< int > cvars; + bool has_nested = false; + collectBoundVar( qi, d_children[i].d_n, cvars, visited, has_nested ); + for( unsigned j=0; j<cvars.size(); j++ ){ + if( std::find( bvars.begin(), bvars.end(), cvars[j] )==bvars.end() ){ + bvars.push_back( cvars[j] ); + } + } } } } @@ -1169,15 +1233,20 @@ void MatchGen::reset( QuantConflictFind * p, bool tgt, QuantInfo * qi ) { d_qni.clear(); d_qni_bound.clear(); d_child_counter = -1; + d_use_children = true; d_tgt_orig = d_tgt; //set up processing matches if( d_type==typ_invalid ){ - //do nothing + d_use_children = false; }else if( d_type==typ_ground ){ + d_use_children = false; if( d_ground_eval[0]==( d_tgt ? p->d_true : p->d_false ) ){ d_child_counter = 0; } + }else if( qi->isBaseMatchComplete() && options::qcfEagerTest() ){ + d_use_children = false; + d_child_counter = 0; }else if( d_type==typ_bool_var ){ //get current value of the variable TNode n = qi->getCurrentValue( d_n ); @@ -1195,7 +1264,7 @@ void MatchGen::reset( QuantConflictFind * p, bool tgt, QuantInfo * qi ) { }else{ //unassigned, set match to true/false d_qni_bound[0] = vn; - qi->setMatch( p, vn, d_tgt ? p->d_true : p->d_false, false ); + qi->setMatch( p, vn, d_tgt ? p->d_true : p->d_false, false, true ); d_child_counter = 0; } if( d_child_counter==0 ){ @@ -1203,11 +1272,14 @@ void MatchGen::reset( QuantConflictFind * p, bool tgt, QuantInfo * qi ) { } }else if( d_type==typ_var ){ Assert( isHandledUfTerm( d_n ) ); - Node f = getMatchOperator( p, d_n ); + TNode f = getMatchOperator( p, d_n ); Debug("qcf-match-debug") << " reset: Var will match operators of " << f << std::endl; TermArgTrie * qni = p->getTermDatabase()->getTermArgTrie( Node::null(), f ); if( qni!=NULL ){ d_qn.push_back( qni ); + }else{ + //inform irrelevant quantifiers + p->setIrrelevantFunction( f ); } d_matched_basis = false; }else if( d_type==typ_tsym || d_type==typ_tconstraint ){ @@ -1257,7 +1329,7 @@ void MatchGen::reset( QuantConflictFind * p, bool tgt, QuantInfo * qi ) { } } }else{ - //otherwise, add a constraint to a variable + //otherwise, add a constraint to a variable TODO: this may be over-eager at effort > conflict, since equality may be a propagation if( vn[1]!=-1 && vn[0]==-1 ){ //swap Node t = nn[1]; @@ -1292,7 +1364,9 @@ void MatchGen::reset( QuantConflictFind * p, bool tgt, QuantInfo * qi ) { d_qn.push_back( NULL ); }else{ if( d_tgt && d_n.getKind()==FORALL ){ - //do nothing + //fail + }else if( d_n.getKind()==FORALL && p->d_effort==QuantConflictFind::effort_conflict && !options::qcfNestedConflict() ){ + //fail }else{ //reset the first child to d_tgt d_child_counter = 0; @@ -1309,7 +1383,7 @@ bool MatchGen::getNextMatch( QuantConflictFind * p, QuantInfo * qi ) { Debug("qcf-match") << " Get next match for : " << d_n << ", type = "; debugPrintType( "qcf-match", d_type ); Debug("qcf-match") << ", children = " << d_children.size() << ", binding = " << d_binding << std::endl; - if( d_type==typ_invalid || d_type==typ_ground ){ + if( !d_use_children ){ if( d_child_counter==0 ){ d_child_counter = -1; return true; @@ -1423,7 +1497,7 @@ bool MatchGen::getNextMatch( QuantConflictFind * p, QuantInfo * qi ) { for( std::map< int, int >::iterator it = d_qni_bound.begin(); it != d_qni_bound.end(); ++it ){ Debug("qcf-match") << " Clean up bound var " << it->second << std::endl; Assert( it->second<qi->getNumVars() ); - qi->d_match[ it->second ] = TNode::null(); + qi->unsetMatch( p, it->second ); qi->d_match_term[ it->second ] = TNode::null(); } d_qni_bound.clear(); @@ -1654,7 +1728,7 @@ bool MatchGen::doMatching( QuantConflictFind * p, QuantInfo * qi ) { if( it != d_qn[index]->d_data.end() ) { d_qni.push_back( it ); //set the match - if( it->first.getType().isComparableTo( qi->d_var_types[repVar] ) && qi->setMatch( p, d_qni_bound[index], it->first, true ) ){ + if( it->first.getType().isComparableTo( qi->d_var_types[repVar] ) && qi->setMatch( p, d_qni_bound[index], it->first, true, true ) ){ Debug("qcf-match-debug") << " Binding variable" << std::endl; if( d_qn.size()<d_qni_size ){ d_qn.push_back( &it->second ); @@ -1699,7 +1773,7 @@ bool MatchGen::doMatching( QuantConflictFind * p, QuantInfo * qi ) { d_qni[index]++; if( d_qni[index]!=d_qn[index]->d_data.end() ){ success = true; - if( qi->setMatch( p, itb->second, d_qni[index]->first, true ) ){ + if( qi->setMatch( p, itb->second, d_qni[index]->first, true, true ) ){ Debug("qcf-match-debug") << " Bind next variable" << std::endl; if( d_qn.size()<d_qni_size ){ d_qn.push_back( &d_qni[index]->second ); @@ -1709,7 +1783,7 @@ bool MatchGen::doMatching( QuantConflictFind * p, QuantInfo * qi ) { invalidMatch = true; } }else{ - qi->d_match[ itb->second ] = TNode::null(); + qi->unsetMatch( p, itb->second ); qi->d_match_term[ itb->second ] = TNode::null(); Debug("qcf-match-debug") << " Bind next variable, no more variables to bind" << std::endl; } @@ -1779,7 +1853,7 @@ void MatchGen::setInvalid() { } bool MatchGen::isHandledBoolConnective( TNode n ) { - return n.getType().isBoolean() && TermDb::isBoolConnective( n.getKind() ); + return TermDb::isBoolConnective( n.getKind() ) && ( n.getKind()!=ITE || n.getType().isBoolean() ); } bool MatchGen::isHandledUfTerm( TNode n ) { @@ -1835,28 +1909,31 @@ void QuantConflictFind::registerQuantifier( Node q ) { if( d_quantEngine->hasOwnership( q, this ) ){ d_quants.push_back( q ); d_quant_id[q] = d_quants.size(); - Trace("qcf-qregister") << "Register "; - debugPrintQuant( "qcf-qregister", q ); - Trace("qcf-qregister") << " : " << q << std::endl; + if( Trace.isOn("qcf-qregister") ){ + Trace("qcf-qregister") << "Register "; + debugPrintQuant( "qcf-qregister", q ); + Trace("qcf-qregister") << " : " << q << std::endl; + } //make QcfNode structure Trace("qcf-qregister") << "- Get relevant equality/disequality pairs, calculate flattening..." << std::endl; d_qinfo[q].initialize( this, q, q[1] ); //debug print - Trace("qcf-qregister") << "- Flattened structure is :" << std::endl; - Trace("qcf-qregister") << " "; - debugPrintQuantBody( "qcf-qregister", q, q[1] ); - Trace("qcf-qregister") << std::endl; - if( d_qinfo[q].d_vars.size()>q[0].getNumChildren() ){ - Trace("qcf-qregister") << " with additional constraints : " << std::endl; - for( unsigned j=q[0].getNumChildren(); j<d_qinfo[q].d_vars.size(); j++ ){ - Trace("qcf-qregister") << " ?x" << j << " = "; - debugPrintQuantBody( "qcf-qregister", q, d_qinfo[q].d_vars[j], false ); - Trace("qcf-qregister") << std::endl; - } - } - - Trace("qcf-qregister") << "Done registering quantifier." << std::endl; + if( Trace.isOn("qcf-qregister") ){ + Trace("qcf-qregister") << "- Flattened structure is :" << std::endl; + Trace("qcf-qregister") << " "; + debugPrintQuantBody( "qcf-qregister", q, q[1] ); + Trace("qcf-qregister") << std::endl; + if( d_qinfo[q].d_vars.size()>q[0].getNumChildren() ){ + Trace("qcf-qregister") << " with additional constraints : " << std::endl; + for( unsigned j=q[0].getNumChildren(); j<d_qinfo[q].d_vars.size(); j++ ){ + Trace("qcf-qregister") << " ?x" << j << " = "; + debugPrintQuantBody( "qcf-qregister", q, d_qinfo[q].d_vars[j], false ); + Trace("qcf-qregister") << std::endl; + } + } + Trace("qcf-qregister") << "Done registering quantifier." << std::endl; + } } } @@ -1933,6 +2010,18 @@ void QuantConflictFind::reset_round( Theory::Effort level ) { d_needs_computeRelEqr = true; } +void QuantConflictFind::setIrrelevantFunction( TNode f ) { + if( d_irr_func.find( f )==d_irr_func.end() ){ + d_irr_func[f] = true; + std::map< TNode, std::vector< Node > >::iterator it = d_func_rel_dom.find( f ); + if( it != d_func_rel_dom.end()){ + for( unsigned j=0; j<it->second.size(); j++ ){ + d_irr_quant[it->second[j]] = true; + } + } + } +} + /** check */ void QuantConflictFind::check( Theory::Effort level, unsigned quant_e ) { if( quant_e==QuantifiersEngine::QEFFORT_CONFLICT ){ @@ -1955,14 +2044,9 @@ void QuantConflictFind::check( Theory::Effort level, unsigned quant_e ) { } computeRelevantEqr(); - //determine order for quantified formulas - std::vector< Node > qorder; - for( unsigned i=0; i<d_quantEngine->getModel()->getNumAssertedQuantifiers(); i++ ){ - Node q = d_quantEngine->getModel()->getAssertedQuantifier( i, true ); - if( d_quantEngine->hasOwnership( q, this ) ){ - qorder.push_back( q ); - } - } + d_irr_func.clear(); + d_irr_quant.clear(); + if( Trace.isOn("qcf-debug") ){ Trace("qcf-debug") << std::endl; debugPrint("qcf-debug"); @@ -1973,77 +2057,83 @@ void QuantConflictFind::check( Theory::Effort level, unsigned quant_e ) { for( short e = effort_conflict; e<=end_e; e++ ){ d_effort = e; Trace("qcf-check") << "Checking quantified formulas at effort " << e << "..." << std::endl; - for( unsigned j=0; j<qorder.size(); j++ ){ - Node q = qorder[j]; - QuantInfo * qi = &d_qinfo[q]; - - Assert( d_qinfo.find( q )!=d_qinfo.end() ); - if( qi->matchGeneratorIsValid() ){ - Trace("qcf-check") << "Check quantified formula "; - debugPrintQuant("qcf-check", q); - Trace("qcf-check") << " : " << q << "..." << std::endl; - - Trace("qcf-check-debug") << "Reset round..." << std::endl; - qi->reset_round( this ); - //try to make a matches making the body false - Trace("qcf-check-debug") << "Get next match..." << std::endl; - while( qi->getNextMatch( this ) ){ - Trace("qcf-inst") << "*** Produced match at effort " << e << " : " << std::endl; - qi->debugPrintMatch("qcf-inst"); - Trace("qcf-inst") << std::endl; - std::vector< int > assigned; - if( !qi->isMatchSpurious( this ) ){ - if( qi->completeMatch( this, assigned ) ){ - std::vector< Node > terms; - qi->getMatch( terms ); - if( !qi->isTConstraintSpurious( this, terms ) ){ - //for debugging - if( Debug.isOn("qcf-check-inst") ){ - Node inst = d_quantEngine->getInstantiation( q, terms ); - Debug("qcf-check-inst") << "Check instantiation " << inst << "..." << std::endl; - Assert( !getTermDatabase()->isEntailed( inst, true ) ); - Assert( getTermDatabase()->isEntailed( inst, false ) || e>effort_conflict ); - } - if( d_quantEngine->addInstantiation( q, terms ) ){ - Trace("qcf-check") << " ... Added instantiation" << std::endl; - Trace("qcf-inst") << "*** Was from effort " << e << " : " << std::endl; - qi->debugPrintMatch("qcf-inst"); - Trace("qcf-inst") << std::endl; - ++addedLemmas; - if( e==effort_conflict ){ - d_quantEngine->markRelevant( q ); - ++(d_statistics.d_conflict_inst); - if( options::qcfAllConflict() ){ - isConflict = true; + for( unsigned i=0; i<d_quantEngine->getModel()->getNumAssertedQuantifiers(); i++ ){ + Node q = d_quantEngine->getModel()->getAssertedQuantifier( i, true ); + if( d_quantEngine->hasOwnership( q, this ) && d_irr_quant.find( q )==d_irr_quant.end() ){ + QuantInfo * qi = &d_qinfo[q]; + + Assert( d_qinfo.find( q )!=d_qinfo.end() ); + if( qi->matchGeneratorIsValid() ){ + Trace("qcf-check") << "Check quantified formula "; + debugPrintQuant("qcf-check", q); + Trace("qcf-check") << " : " << q << "..." << std::endl; + + Trace("qcf-check-debug") << "Reset round..." << std::endl; + if( qi->reset_round( this ) ){ + //try to make a matches making the body false + Trace("qcf-check-debug") << "Get next match..." << std::endl; + while( qi->getNextMatch( this ) ){ + Trace("qcf-inst") << "*** Produced match at effort " << e << " : " << std::endl; + qi->debugPrintMatch("qcf-inst"); + Trace("qcf-inst") << std::endl; + if( !qi->isMatchSpurious( this ) ){ + std::vector< int > assigned; + if( qi->completeMatch( this, assigned ) ){ + std::vector< Node > terms; + qi->getMatch( terms ); + bool tcs = qi->isTConstraintSpurious( this, terms ); + if( !tcs ){ + //for debugging + if( Debug.isOn("qcf-check-inst") ){ + Node inst = d_quantEngine->getInstantiation( q, terms ); + Debug("qcf-check-inst") << "Check instantiation " << inst << "..." << std::endl; + Assert( !getTermDatabase()->isEntailed( inst, true ) ); + Assert( getTermDatabase()->isEntailed( inst, false ) || e>effort_conflict ); + } + if( d_quantEngine->addInstantiation( q, terms ) ){ + Trace("qcf-check") << " ... Added instantiation" << std::endl; + Trace("qcf-inst") << "*** Was from effort " << e << " : " << std::endl; + qi->debugPrintMatch("qcf-inst"); + Trace("qcf-inst") << std::endl; + ++addedLemmas; + if( e==effort_conflict ){ + d_quantEngine->markRelevant( q ); + ++(d_statistics.d_conflict_inst); + if( options::qcfAllConflict() ){ + isConflict = true; + }else{ + d_conflict.set( true ); + } + break; + }else if( e==effort_prop_eq ){ + d_quantEngine->markRelevant( q ); + ++(d_statistics.d_prop_inst); + } }else{ - d_conflict.set( true ); + Trace("qcf-inst") << " ... Failed to add instantiation" << std::endl; + //this should only happen if the algorithm generates the same propagating instance twice this round + //in this case, break to avoid exponential behavior + break; } - break; - }else if( e==effort_prop_eq ){ - d_quantEngine->markRelevant( q ); - ++(d_statistics.d_prop_inst); + }else{ + Trace("qcf-inst") << " ... Spurious instantiation (match is T-inconsistent)" << std::endl; } + //clean up assigned + qi->revertMatch( this, assigned ); + d_tempCache.clear(); }else{ - Trace("qcf-inst") << " ... Failed to add instantiation" << std::endl; - //this should only happen if the algorithm generates the same propagating instance twice this round - //in this case, break to avoid exponential behavior - break; + Trace("qcf-inst") << " ... Spurious instantiation (cannot assign unassigned variables)" << std::endl; } + }else{ + Trace("qcf-inst") << " ... Spurious instantiation (match is inconsistent)" << std::endl; } - //clean up assigned - qi->revertMatch( assigned ); - d_tempCache.clear(); - }else{ - Trace("qcf-inst") << " ... Spurious instantiation (cannot assign unassigned variables)" << std::endl; } - }else{ - Trace("qcf-inst") << " ... Spurious instantiation (match is inconsistent)" << std::endl; + Trace("qcf-check") << "Done, conflict = " << d_conflict << std::endl; + if( d_conflict ){ + break; + } } } - Trace("qcf-check") << "Done, conflict = " << d_conflict << std::endl; - if( d_conflict ){ - break; - } } } if( addedLemmas>0 ){ @@ -2074,17 +2164,9 @@ void QuantConflictFind::check( Theory::Effort level, unsigned quant_e ) { void QuantConflictFind::computeRelevantEqr() { if( d_needs_computeRelEqr ){ d_needs_computeRelEqr = false; - Trace("qcf-check") << "Compute relevant equalities..." << std::endl; - //d_uf_terms.clear(); - //d_eqc_uf_terms.clear(); + Trace("qcf-check") << "Compute relevant equivalence classes..." << std::endl; d_eqcs.clear(); - //d_arg_reps.clear(); - //double clSet = 0; - //if( Trace.isOn("qcf-opt") ){ - // clSet = double(clock())/double(CLOCKS_PER_SEC); - //} - //now, store matches eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( getEqualityEngine() ); while( !eqcs_i.isFinished() ){ Node r = (*eqcs_i); diff --git a/src/theory/quantifiers/quant_conflict_find.h b/src/theory/quantifiers/quant_conflict_find.h index 8b42b0916..47a66b1b1 100644..100755 --- a/src/theory/quantifiers/quant_conflict_find.h +++ b/src/theory/quantifiers/quant_conflict_find.h @@ -35,6 +35,7 @@ class MatchGen { private: //current children information int d_child_counter; + bool d_use_children; //children of this object std::vector< int > d_children_order; unsigned getNumChildren() { return d_children.size(); } @@ -61,7 +62,7 @@ private: std::map< int, Node > d_ground_eval; //determine variable order void determineVariableOrder( QuantInfo * qi, std::vector< int >& bvars ); - void collectBoundVar( QuantInfo * qi, Node n, std::vector< int >& cbvars ); + void collectBoundVar( QuantInfo * qi, Node n, std::vector< int >& cbvars, std::map< Node, bool >& visited, bool& hasNested ); public: //type of the match generator enum { @@ -116,7 +117,16 @@ private: //for completing match std::vector< int > d_una_eqc_count; //optimization: track which arguments variables appear under UF terms in std::map< int, std::map< TNode, std::vector< unsigned > > > d_var_rel_dom; - void getPropagateVars( std::vector< TNode >& vars, TNode n, bool pol, std::map< TNode, bool >& visited ); + void getPropagateVars( QuantConflictFind * p, std::vector< TNode >& vars, TNode n, bool pol, std::map< TNode, bool >& visited ); + //optimization: number of variables set, to track when we can stop + std::map< int, bool > d_vars_set; + std::map< Node, bool > d_ground_terms; + std::vector< Node > d_extra_var; +public: + void setGroundSubterm( Node t ) { d_ground_terms[t] = true; } + bool isGroundSubterm( Node t ) { return d_ground_terms.find( t )!=d_ground_terms.end(); } + bool isBaseMatchComplete(); + bool isPropagatingInstance( QuantConflictFind * p, Node n ); public: QuantInfo(); ~QuantInfo(); @@ -146,7 +156,7 @@ public: } Node d_q; - void reset_round( QuantConflictFind * p ); + bool reset_round( QuantConflictFind * p ); public: //initialize void initialize( QuantConflictFind * p, Node q, Node qn ); @@ -161,12 +171,13 @@ public: bool getCurrentCanBeEqual( QuantConflictFind * p, int v, TNode n, bool chDiseq = false ); int addConstraint( QuantConflictFind * p, int v, TNode n, bool polarity ); int addConstraint( QuantConflictFind * p, int v, TNode n, int vn, bool polarity, bool doRemove ); - bool setMatch( QuantConflictFind * p, int v, TNode n, bool isGroundRep ); + bool setMatch( QuantConflictFind * p, int v, TNode n, bool isGroundRep, bool isGround ); + void unsetMatch( QuantConflictFind * p, int v ); bool isMatchSpurious( QuantConflictFind * p ); bool isTConstraintSpurious( QuantConflictFind * p, std::vector< Node >& terms ); bool entailmentTest( QuantConflictFind * p, Node lit, bool chEnt = true ); bool completeMatch( QuantConflictFind * p, std::vector< int >& assigned, bool doContinue = false ); - void revertMatch( std::vector< int >& assigned ); + void revertMatch( QuantConflictFind * p, std::vector< int >& assigned ); void debugPrintMatch( const char * c ); bool isConstrainedVar( int v ); public: @@ -184,6 +195,11 @@ private: std::map< Kind, Node > d_zero; //for storing nodes created during t-constraint solving (prevents memory leaks) std::vector< Node > d_tempCache; + //optimization: list of quantifiers that depend on ground function applications + std::map< TNode, std::vector< Node > > d_func_rel_dom; + std::map< TNode, bool > d_irr_func; + std::map< Node, bool > d_irr_quant; + void setIrrelevantFunction( TNode f ); private: std::map< Node, Node > d_op_node; int d_fid_count; diff --git a/src/theory/quantifiers/quant_equality_engine.cpp b/src/theory/quantifiers/quant_equality_engine.cpp index 3f89a799c..3f89a799c 100644..100755 --- a/src/theory/quantifiers/quant_equality_engine.cpp +++ b/src/theory/quantifiers/quant_equality_engine.cpp diff --git a/src/theory/quantifiers/quant_equality_engine.h b/src/theory/quantifiers/quant_equality_engine.h index 26654de4d..26654de4d 100644..100755 --- a/src/theory/quantifiers/quant_equality_engine.h +++ b/src/theory/quantifiers/quant_equality_engine.h diff --git a/src/theory/quantifiers/quant_split.cpp b/src/theory/quantifiers/quant_split.cpp index 9fb943e5e..5aff1a848 100644..100755 --- a/src/theory/quantifiers/quant_split.cpp +++ b/src/theory/quantifiers/quant_split.cpp @@ -48,11 +48,11 @@ void QuantDSplit::preRegisterQuantifier( Node q ) { }else{ int score = -1; if( options::quantDynamicSplit()==quantifiers::QUANT_DSPLIT_MODE_AGG ){ - score = dt.isUFinite() ? 1 : -1; + score = dt.isInterpretedFinite() ? 1 : -1; }else if( options::quantDynamicSplit()==quantifiers::QUANT_DSPLIT_MODE_DEFAULT ){ - score = dt.isUFinite() ? 1 : -1; + score = dt.isInterpretedFinite() ? 1 : -1; } - Trace("quant-dsplit-debug") << "Datatype " << dt.getName() << " is score " << score << " (" << dt.isUFinite() << " " << dt.isFinite() << ")" << std::endl; + Trace("quant-dsplit-debug") << "Datatype " << dt.getName() << " is score " << score << " (" << dt.isInterpretedFinite() << " " << dt.isFinite() << ")" << std::endl; if( score>max_score ){ max_index = i; max_score = score; diff --git a/src/theory/quantifiers/quant_split.h b/src/theory/quantifiers/quant_split.h index d36824998..d36824998 100644..100755 --- a/src/theory/quantifiers/quant_split.h +++ b/src/theory/quantifiers/quant_split.h diff --git a/src/theory/quantifiers/quant_util.cpp b/src/theory/quantifiers/quant_util.cpp index 3b7787a20..b9aab0236 100644..100755 --- a/src/theory/quantifiers/quant_util.cpp +++ b/src/theory/quantifiers/quant_util.cpp @@ -33,22 +33,15 @@ eq::EqualityEngine * QuantifiersModule::getEqualityEngine() { } bool QuantifiersModule::areEqual( TNode n1, TNode n2 ) { - eq::EqualityEngine * ee = getEqualityEngine(); - return n1==n2 || ( ee->hasTerm( n1 ) && ee->hasTerm( n2 ) && ee->areEqual( n1, n2 ) ); + return d_quantEngine->getEqualityQuery()->areEqual( n1, n2 ); } bool QuantifiersModule::areDisequal( TNode n1, TNode n2 ) { - eq::EqualityEngine * ee = getEqualityEngine(); - return n1!=n2 && ee->hasTerm( n1 ) && ee->hasTerm( n2 ) && ee->areDisequal( n1, n2, false ); + return d_quantEngine->getEqualityQuery()->areDisequal( n1, n2 ); } TNode QuantifiersModule::getRepresentative( TNode n ) { - eq::EqualityEngine * ee = getEqualityEngine(); - if( ee->hasTerm( n ) ){ - return ee->getRepresentative( n ); - }else{ - return n; - } + return d_quantEngine->getEqualityQuery()->getRepresentative( n ); } quantifiers::TermDb * QuantifiersModule::getTermDatabase() { @@ -389,7 +382,7 @@ void QuantPhaseReq::computePhaseReqs( Node n, bool polarity, std::map< Node, int } void QuantPhaseReq::getPolarity( Node n, int child, bool hasPol, bool pol, bool& newHasPol, bool& newPol ) { - if( n.getKind()==AND || n.getKind()==OR ){ + if( n.getKind()==AND || n.getKind()==OR || n.getKind()==SEP_STAR ){ newHasPol = hasPol; newPol = pol; }else if( n.getKind()==IMPLIES ){ diff --git a/src/theory/quantifiers/quant_util.h b/src/theory/quantifiers/quant_util.h index 79cdae437..79cdae437 100644..100755 --- a/src/theory/quantifiers/quant_util.h +++ b/src/theory/quantifiers/quant_util.h diff --git a/src/theory/quantifiers/quantifiers_attributes.cpp b/src/theory/quantifiers/quantifiers_attributes.cpp index b797f4ce9..b797f4ce9 100644..100755 --- a/src/theory/quantifiers/quantifiers_attributes.cpp +++ b/src/theory/quantifiers/quantifiers_attributes.cpp diff --git a/src/theory/quantifiers/quantifiers_attributes.h b/src/theory/quantifiers/quantifiers_attributes.h index 53cef796a..53cef796a 100644..100755 --- a/src/theory/quantifiers/quantifiers_attributes.h +++ b/src/theory/quantifiers/quantifiers_attributes.h diff --git a/src/theory/quantifiers/quantifiers_rewriter.cpp b/src/theory/quantifiers/quantifiers_rewriter.cpp index 5aae4d640..68f824c57 100644..100755 --- a/src/theory/quantifiers/quantifiers_rewriter.cpp +++ b/src/theory/quantifiers/quantifiers_rewriter.cpp @@ -48,7 +48,7 @@ bool QuantifiersRewriter::isClause( Node n ){ bool QuantifiersRewriter::isLiteral( Node n ){ switch( n.getKind() ){ case NOT: - return isLiteral( n[0] ); + return n[0].getKind()!=NOT && isLiteral( n[0] ); break; case OR: case AND: @@ -59,7 +59,8 @@ bool QuantifiersRewriter::isLiteral( Node n ){ return false; break; case EQUAL: - return n[0].getType()!=NodeManager::currentNM()->booleanType(); + //for boolean terms + return !n[0].getType().isBoolean(); break; default: break; @@ -185,19 +186,10 @@ RewriteResponse QuantifiersRewriter::preRewrite(TNode in) { if( in.getKind()==kind::EXISTS || in.getKind()==kind::FORALL ){ Trace("quantifiers-rewrite-debug") << "pre-rewriting " << in << std::endl; std::vector< Node > args; - for( int i=0; i<(int)in[0].getNumChildren(); i++ ){ - args.push_back( in[0][i] ); - } - Node body = in[1]; + Node body = in; bool doRewrite = false; - std::vector< Node > ipl; - while( body.getNumChildren()>=2 && body.getKind()==in.getKind() ){ - if( body.getNumChildren()==3 ){ - for( unsigned i=0; i<body[2].getNumChildren(); i++ ){ - ipl.push_back( body[2][i] ); - } - } - for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ + while( body.getNumChildren()==2 && body.getKind()==body[1].getKind() ){ + for( unsigned i=0; i<body[0].getNumChildren(); i++ ){ args.push_back( body[0][i] ); } body = body[1]; @@ -205,16 +197,11 @@ RewriteResponse QuantifiersRewriter::preRewrite(TNode in) { } if( doRewrite ){ std::vector< Node > children; + for( unsigned i=0; i<body[0].getNumChildren(); i++ ){ + args.push_back( body[0][i] ); + } children.push_back( NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST,args) ); - children.push_back( body ); - if( in.getNumChildren()==3 ){ - for( unsigned i=0; i<in[2].getNumChildren(); i++ ){ - ipl.push_back( in[2][i] ); - } - } - if( !ipl.empty() ){ - children.push_back( NodeManager::currentNM()->mkNode( INST_PATTERN_LIST, ipl ) ); - } + children.push_back( body[1] ); Node n = NodeManager::currentNM()->mkNode( in.getKind(), children ); if( in!=n ){ Trace("quantifiers-pre-rewrite") << "*** pre-rewrite " << in << std::endl; @@ -244,7 +231,7 @@ RewriteResponse QuantifiersRewriter::postRewrite(TNode in) { ret = ret.negate(); status = REWRITE_AGAIN_FULL; }else if( in.getKind()==FORALL ){ - if( in[1].isConst() ){ + if( in[1].isConst() && in.getNumChildren()==2 ){ return RewriteResponse( status, in[1] ); }else{ //compute attributes @@ -273,121 +260,98 @@ RewriteResponse QuantifiersRewriter::postRewrite(TNode in) { return RewriteResponse( status, ret ); } -Node QuantifiersRewriter::computeElimSymbols( Node body ) { - if( isLiteral( body ) ){ - return body; - }else{ - bool childrenChanged = false; - Kind k = body.getKind(); - if( body.getKind()==IMPLIES ){ - k = OR; - childrenChanged = true; - }else if( body.getKind()==XOR ){ - k = IFF; +bool QuantifiersRewriter::addCheckElimChild( std::vector< Node >& children, Node c, Kind k, std::map< Node, bool >& lit_pol, bool& childrenChanged ){ + if( ( k==OR || k==AND ) && options::elimTautQuant() ){ + Node lit = c.getKind()==NOT ? c[0] : c; + bool pol = c.getKind()!=NOT; + std::map< Node, bool >::iterator it = lit_pol.find( lit ); + if( it==lit_pol.end() ){ + lit_pol[lit] = pol; + children.push_back( c ); + }else{ childrenChanged = true; - } - std::vector< Node > children; - std::map< Node, bool > lit_pol; - for( unsigned i=0; i<body.getNumChildren(); i++ ){ - Node c = computeElimSymbols( body[i] ); - if( i==0 && ( body.getKind()==IMPLIES || body.getKind()==XOR ) ){ - c = c.negate(); - } - if( ( k==OR || k==AND ) && options::elimTautQuant() ){ - Node lit = c.getKind()==NOT ? c[0] : c; - bool pol = c.getKind()!=NOT; - std::map< Node, bool >::iterator it = lit_pol.find( lit ); - if( it==lit_pol.end() ){ - lit_pol[lit] = pol; - children.push_back( c ); - }else{ - childrenChanged = true; - if( it->second!=pol ){ - return NodeManager::currentNM()->mkConst( k==OR ); - } - } - }else{ - children.push_back( c ); + if( it->second!=pol ){ + return false; } - childrenChanged = childrenChanged || c!=body[i]; - } - //if( body.getKind()==ITE && isLiteral( body[0] ) ){ - // ret = NodeManager::currentNM()->mkNode( AND, NodeManager::currentNM()->mkNode( OR, body[0].negate(), body[1] ), - // NodeManager::currentNM()->mkNode( OR, body[0], body[2] ) ); - //} - if( childrenChanged ){ - return ( children.size()==1 && k!=NOT ) ? children[0] : NodeManager::currentNM()->mkNode( k, children ); - }else{ - return body; } + }else{ + children.push_back( c ); } + return true; } -Node QuantifiersRewriter::computeNNF( Node body ){ - if( body.getKind()==NOT ){ +// eliminates IMPLIES/XOR, removes duplicates/infers tautologies of AND/OR, and computes NNF +Node QuantifiersRewriter::computeElimSymbols( Node body ) { + Kind ok = body.getKind(); + Kind k = ok; + bool negAllCh = false; + bool negCh1 = false; + if( ok==IMPLIES ){ + k = OR; + negCh1 = true; + }else if( ok==XOR ){ + k = IFF; + negCh1 = true; + }else if( ok==NOT ){ if( body[0].getKind()==NOT ){ - return computeNNF( body[0][0] ); - }else if( isLiteral( body[0] ) ){ - return body; + return computeElimSymbols( body[0][0] ); + }else if( body[0].getKind()==OR || body[0].getKind()==IMPLIES ){ + k = AND; + negAllCh = true; + negCh1 = body[0].getKind()==IMPLIES; + body = body[0]; + }else if( body[0].getKind()==AND ){ + k = OR; + negAllCh = true; + body = body[0]; + }else if( body[0].getKind()==XOR || body[0].getKind()==IFF ){ + k = IFF; + negCh1 = ( body[0].getKind()==IFF ); + body = body[0]; + }else if( body[0].getKind()==ITE ){ + k = body[0].getKind(); + negAllCh = true; + negCh1 = true; + body = body[0]; }else{ - std::vector< Node > children; - Kind k = body[0].getKind(); - - if( body[0].getKind()==OR || body[0].getKind()==AND ){ - k = body[0].getKind()==AND ? OR : AND; - for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ - Node nc = computeNNF( body[0][i].notNode() ); - if( nc.getKind()==k ){ - for( unsigned j=0; j<nc.getNumChildren(); j++ ){ - children.push_back( nc[j] ); - } - }else{ - children.push_back( nc ); - } - } - }else if( body[0].getKind()==IFF ){ - for( int i=0; i<2; i++ ){ - Node nn = i==0 ? body[0][i] : body[0][i].notNode(); - children.push_back( computeNNF( nn ) ); - } - }else if( body[0].getKind()==ITE ){ - for( int i=0; i<3; i++ ){ - Node nn = i==0 ? body[0][i] : body[0][i].notNode(); - children.push_back( computeNNF( nn ) ); - } - }else{ - Notice() << "Unhandled Quantifiers NNF: " << body << std::endl; - return body; - } - return NodeManager::currentNM()->mkNode( k, children ); + return body; } - }else if( isLiteral( body ) ){ + }else if( ok!=IFF && ok!=ITE && ok!=AND && ok!=OR ){ + //a literal return body; - }else{ - std::vector< Node > children; - bool childrenChanged = false; - bool isAssoc = body.getKind()==AND || body.getKind()==OR; - for( int i=0; i<(int)body.getNumChildren(); i++ ){ - Node nc = computeNNF( body[i] ); - if( isAssoc && nc.getKind()==body.getKind() ){ - for( unsigned j=0; j<nc.getNumChildren(); j++ ){ - children.push_back( nc[j] ); + } + bool childrenChanged = false; + std::vector< Node > children; + std::map< Node, bool > lit_pol; + for( unsigned i=0; i<body.getNumChildren(); i++ ){ + Node c = computeElimSymbols( ( i==0 && negCh1 )!=negAllCh ? body[i].negate() : body[i] ); + bool success = true; + if( c.getKind()==k && ( k==OR || k==AND ) ){ + //flatten + childrenChanged = true; + for( unsigned j=0; j<c.getNumChildren(); j++ ){ + if( !addCheckElimChild( children, c[j], k, lit_pol, childrenChanged ) ){ + success = false; + break; } - childrenChanged = true; - }else{ - children.push_back( nc ); - childrenChanged = childrenChanged || nc!=body[i]; } - } - if( childrenChanged ){ - return NodeManager::currentNM()->mkNode( body.getKind(), children ); }else{ - return body; + success = addCheckElimChild( children, c, k, lit_pol, childrenChanged ); } + if( !success ){ + // tautology + Assert( k==OR || k==AND ); + return NodeManager::currentNM()->mkConst( k==OR ); + } + childrenChanged = childrenChanged || c!=body[i]; + } + if( childrenChanged || k!=ok ){ + return ( children.size()==1 && k!=NOT ) ? children[0] : NodeManager::currentNM()->mkNode( k, children ); + }else{ + return body; } } - void QuantifiersRewriter::computeDtTesterIteSplit( Node n, std::map< Node, Node >& pcons, std::map< Node, std::map< int, Node > >& ncons, std::vector< Node >& conj ){ if( n.getKind()==ITE && n[0].getKind()==APPLY_TESTER && n[1].getType().isBoolean() ){ @@ -463,6 +427,8 @@ int getEntailedCond( Node n, std::map< Node, bool >& currCond ){ std::map< Node, bool >::iterator it = currCond.find( n ); if( it!=currCond.end() ){ return it->second ? 1 : -1; + }else if( n.getKind()==NOT ){ + return -getEntailedCond( n[0], currCond ); }else if( n.getKind()==AND || n.getKind()==OR ){ bool hasZero = false; for( unsigned i=0; i<n.getNumChildren(); i++ ){ @@ -483,8 +449,7 @@ int getEntailedCond( Node n, std::map< Node, bool >& currCond ){ }else if( res==-1 ){ return getEntailedCond( n[2], currCond ); } - } - if( n.getKind()==IFF || n.getKind()==ITE ){ + }else if( n.getKind()==IFF || n.getKind()==ITE ){ unsigned start = n.getKind()==IFF ? 0 : 1; int res1 = 0; for( unsigned j=start; j<=(start+1); j++ ){ @@ -1049,144 +1014,6 @@ Node QuantifiersRewriter::computeVarElimination( Node body, std::vector< Node >& return body; } -Node QuantifiersRewriter::computeClause( Node n ){ - Assert( isClause( n ) ); - if( isLiteral( n ) ){ - return n; - }else{ - NodeBuilder<> t(OR); - if( n.getKind()==NOT ){ - if( n[0].getKind()==NOT ){ - return computeClause( n[0][0] ); - }else{ - //De-Morgan's law - Assert( n[0].getKind()==AND ); - for( int i=0; i<(int)n[0].getNumChildren(); i++ ){ - Node nn = computeClause( n[0][i].notNode() ); - addNodeToOrBuilder( nn, t ); - } - } - }else{ - for( int i=0; i<(int)n.getNumChildren(); i++ ){ - Node nn = computeClause( n[i] ); - addNodeToOrBuilder( nn, t ); - } - } - return t.constructNode(); - } -} - -Node QuantifiersRewriter::computeCNF( Node n, std::vector< Node >& args, NodeBuilder<>& defs, bool forcePred ){ - if( isLiteral( n ) ){ - return n; - }else if( !forcePred && isClause( n ) ){ - return computeClause( n ); - }else{ - Kind k = n.getKind(); - NodeBuilder<> t(k); - for( int i=0; i<(int)n.getNumChildren(); i++ ){ - Node nc = n[i]; - Node ncnf = computeCNF( nc, args, defs, k!=OR ); - if( k==OR ){ - addNodeToOrBuilder( ncnf, t ); - }else{ - t << ncnf; - } - } - if( !forcePred && k==OR ){ - return t.constructNode(); - }else{ - //compute the free variables - Node nt = t; - std::vector< Node > activeArgs; - computeArgVec( args, activeArgs, nt ); - std::vector< TypeNode > argTypes; - for( int i=0; i<(int)activeArgs.size(); i++ ){ - argTypes.push_back( activeArgs[i].getType() ); - } - //create the predicate - Assert( argTypes.size()>0 ); - TypeNode typ = NodeManager::currentNM()->mkFunctionType( argTypes, NodeManager::currentNM()->booleanType() ); - std::stringstream ss; - ss << "cnf_" << n.getKind() << "_" << n.getId(); - Node op = NodeManager::currentNM()->mkSkolem( ss.str(), typ, "was created by the quantifiers rewriter" ); - std::vector< Node > predArgs; - predArgs.push_back( op ); - predArgs.insert( predArgs.end(), activeArgs.begin(), activeArgs.end() ); - Node pred = NodeManager::currentNM()->mkNode( APPLY_UF, predArgs ); - Trace("quantifiers-rewrite-cnf-debug") << "Made predicate " << pred << " for " << nt << std::endl; - //create the bound var list - Node bvl = NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, activeArgs ); - //now, look at the structure of nt - if( nt.getKind()==NOT ){ - //case for NOT - for( int i=0; i<2; i++ ){ - NodeBuilder<> tt(OR); - tt << ( i==0 ? nt[0].notNode() : nt[0] ); - tt << ( i==0 ? pred.notNode() : pred ); - defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); - } - }else if( nt.getKind()==OR ){ - //case for OR - for( int i=0; i<(int)nt.getNumChildren(); i++ ){ - NodeBuilder<> tt(OR); - tt << nt[i].notNode() << pred; - defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); - } - NodeBuilder<> tt(OR); - for( int i=0; i<(int)nt.getNumChildren(); i++ ){ - tt << nt[i]; - } - tt << pred.notNode(); - defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); - }else if( nt.getKind()==AND ){ - //case for AND - for( int i=0; i<(int)nt.getNumChildren(); i++ ){ - NodeBuilder<> tt(OR); - tt << nt[i] << pred.notNode(); - defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); - } - NodeBuilder<> tt(OR); - for( int i=0; i<(int)nt.getNumChildren(); i++ ){ - tt << nt[i].notNode(); - } - tt << pred; - defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); - }else if( nt.getKind()==IFF ){ - //case for IFF - for( int i=0; i<4; i++ ){ - NodeBuilder<> tt(OR); - tt << ( ( i==0 || i==3 ) ? nt[0].notNode() : nt[0] ); - tt << ( ( i==1 || i==3 ) ? nt[1].notNode() : nt[1] ); - tt << ( ( i==0 || i==1 ) ? pred.notNode() : pred ); - defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); - } - }else if( nt.getKind()==ITE ){ - //case for ITE - for( int j=1; j<=2; j++ ){ - for( int i=0; i<2; i++ ){ - NodeBuilder<> tt(OR); - tt << ( ( j==1 ) ? nt[0].notNode() : nt[0] ); - tt << ( ( i==1 ) ? nt[j].notNode() : nt[j] ); - tt << ( ( i==0 ) ? pred.notNode() : pred ); - defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); - } - } - for( int i=0; i<2; i++ ){ - NodeBuilder<> tt(OR); - tt << ( i==0 ? nt[1].notNode() : nt[1] ); - tt << ( i==0 ? nt[2].notNode() : nt[2] ); - tt << ( i==1 ? pred.notNode() : pred ); - defs << NodeManager::currentNM()->mkNode( FORALL, bvl, tt.constructNode() ); - } - }else{ - Notice() << "Unhandled Quantifiers CNF: " << nt << std::endl; - } - return pred; - } - } -} - Node QuantifiersRewriter::computePrenex( Node body, std::vector< Node >& args, bool pol ){ if( body.getKind()==FORALL ){ if( pol && ( options::prenexQuant()==PRENEX_ALL || body.getNumChildren()==2 ) ){ @@ -1194,15 +1021,13 @@ Node QuantifiersRewriter::computePrenex( Node body, std::vector< Node >& args, b std::vector< Node > subs; //for doing prenexing of same-signed quantifiers //must rename each variable that already exists - for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ - //if( std::find( args.begin(), args.end(), body[0][i] )!=args.end() ){ + for( unsigned i=0; i<body[0].getNumChildren(); i++ ){ terms.push_back( body[0][i] ); subs.push_back( NodeManager::currentNM()->mkBoundVar( body[0][i].getType() ) ); } args.insert( args.end(), subs.begin(), subs.end() ); Node newBody = body[1]; newBody = newBody.substitute( terms.begin(), terms.end(), subs.begin(), subs.end() ); - Debug("quantifiers-substitute-debug") << "Did substitute have an effect" << (body[1] != newBody) << body[1] << " became " << newBody << endl; return newBody; }else{ return body; @@ -1211,7 +1036,7 @@ Node QuantifiersRewriter::computePrenex( Node body, std::vector< Node >& args, b Assert( body.getKind()!=EXISTS ); bool childrenChanged = false; std::vector< Node > newChildren; - for( int i=0; i<(int)body.getNumChildren(); i++ ){ + for( unsigned i=0; i<body.getNumChildren(); i++ ){ bool newHasPol; bool newPol; QuantPhaseReq::getPolarity( body, i, true, pol, newHasPol, newPol ); @@ -1237,7 +1062,7 @@ Node QuantifiersRewriter::computePrenex( Node body, std::vector< Node >& args, b } } -Node QuantifiersRewriter::computeSplit( Node f, std::vector< Node >& args, Node body ) { +Node QuantifiersRewriter::computeSplit( std::vector< Node >& args, Node body, QAttributes& qa ) { Assert( body.getKind()==OR ); size_t var_found_count = 0; size_t eqc_count = 0; @@ -1253,8 +1078,8 @@ Node QuantifiersRewriter::computeSplit( Node f, std::vector< Node >& args, Node Node n = body[i]; std::vector< Node > lit_args; computeArgVec( args, lit_args, n ); - if (lit_args.empty()) { - lits.push_back(n); + if( lit_args.empty() ){ + lits.push_back( n ); }else { //collect the equivalence classes this literal belongs to, and the new variables it contributes std::vector< int > eqcs; @@ -1306,8 +1131,8 @@ Node QuantifiersRewriter::computeSplit( Node f, std::vector< Node >& args, Node eqc_to_lit[eqcz].push_back(n); } } - if ( eqc_active>1 || !lits.empty() ){ - Trace("clause-split-debug") << "Split clause " << f << std::endl; + if ( eqc_active>1 || !lits.empty() || var_to_eqc.size()!=args.size() ){ + Trace("clause-split-debug") << "Split quantified formula with body " << body << std::endl; Trace("clause-split-debug") << " Ground literals: " << std::endl; for( size_t i=0; i<lits.size(); i++) { Trace("clause-split-debug") << " " << lits[i] << std::endl; @@ -1330,27 +1155,21 @@ Node QuantifiersRewriter::computeSplit( Node f, std::vector< Node >& args, Node Node fa = NodeManager::currentNM()->mkNode( FORALL, bvl, body ); lits.push_back(fa); } - Assert( lits.size()>1 ); - Node nf = NodeManager::currentNM()->mkNode(OR,lits); + Assert( !lits.empty() ); + Node nf = lits.size()==1 ? lits[0] : NodeManager::currentNM()->mkNode(OR,lits); Trace("clause-split-debug") << "Made node : " << nf << std::endl; return nf; + }else{ + return mkForAll( args, body, qa ); } - return f; } Node QuantifiersRewriter::mkForAll( std::vector< Node >& args, Node body, QAttributes& qa ){ - std::vector< Node > activeArgs; - //if cegqi is on, may be synthesis conjecture, in which case we want to keep all variables - if( options::ceGuidedInst() && qa.d_sygus ){ - activeArgs.insert( activeArgs.end(), args.begin(), args.end() ); - }else{ - computeArgVec2( args, activeArgs, body, qa.d_ipl ); - } - if( activeArgs.empty() ){ + if( args.empty() ){ return body; }else{ std::vector< Node > children; - children.push_back( NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, activeArgs ) ); + children.push_back( NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, args ) ); children.push_back( body ); if( !qa.d_ipl.isNull() ){ children.push_back( qa.d_ipl ); @@ -1359,82 +1178,59 @@ Node QuantifiersRewriter::mkForAll( std::vector< Node >& args, Node body, QAttri } } -Node QuantifiersRewriter::computeMiniscoping( Node f, std::vector< Node >& args, Node body, QAttributes& qa ){ +//computes miniscoping, also eliminates variables that do not occur free in body +Node QuantifiersRewriter::computeMiniscoping( std::vector< Node >& args, Node body, QAttributes& qa ){ if( body.getKind()==FORALL ){ - //combine arguments + //combine prenex std::vector< Node > newArgs; - for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ + newArgs.insert( newArgs.end(), args.begin(), args.end() ); + for( unsigned i=0; i<body[0].getNumChildren(); i++ ){ newArgs.push_back( body[0][i] ); } - newArgs.insert( newArgs.end(), args.begin(), args.end() ); - return mkForAll( newArgs, body[ 1 ], qa ); - }else{ - if( body.getKind()==NOT ){ - //push not downwards - if( body[0].getKind()==NOT ){ - return computeMiniscoping( f, args, body[0][0], qa ); - }else if( body[0].getKind()==AND ){ - if( options::miniscopeQuantFreeVar() ){ - NodeBuilder<> t(kind::OR); - for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ - t << ( body[0][i].getKind()==NOT ? body[0][i][0] : body[0][i].notNode() ); - } - return computeMiniscoping( f, args, t.constructNode(), qa ); - } - }else if( body[0].getKind()==OR ){ - if( options::miniscopeQuant() ){ - NodeBuilder<> t(kind::AND); - for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ - Node trm = body[0][i].negate(); - t << computeMiniscoping( f, args, trm, qa ); - } - return t.constructNode(); + return mkForAll( newArgs, body[1], qa ); + }else if( body.getKind()==AND ){ + if( options::miniscopeQuant() ){ + //break apart + NodeBuilder<> t(kind::AND); + for( unsigned i=0; i<body.getNumChildren(); i++ ){ + t << computeMiniscoping( args, body[i], qa ); + } + Node retVal = t; + return retVal; + } + }else if( body.getKind()==OR ){ + if( options::quantSplit() ){ + //splitting subsumes free variable miniscoping, apply it with higher priority + return computeSplit( args, body, qa ); + }else if( options::miniscopeQuantFreeVar() ){ + Node newBody = body; + NodeBuilder<> body_split(kind::OR); + NodeBuilder<> tb(kind::OR); + for( unsigned i=0; i<body.getNumChildren(); i++ ){ + Node trm = body[i]; + if( TermDb::containsTerms( body[i], args ) ){ + tb << trm; + }else{ + body_split << trm; } } - }else if( body.getKind()==AND ){ - if( options::miniscopeQuant() ){ - //break apart - NodeBuilder<> t(kind::AND); - for( unsigned i=0; i<body.getNumChildren(); i++ ){ - t << computeMiniscoping( f, args, body[i], qa ); - } - Node retVal = t; - return retVal; - } - }else if( body.getKind()==OR ){ - if( options::quantSplit() ){ - //splitting subsumes free variable miniscoping, apply it with higher priority - Node nf = computeSplit( f, args, body ); - if( nf!=f ){ - return nf; - } - }else if( options::miniscopeQuantFreeVar() ){ - Node newBody = body; - NodeBuilder<> body_split(kind::OR); - NodeBuilder<> tb(kind::OR); - for( unsigned i=0; i<body.getNumChildren(); i++ ){ - Node trm = body[i]; - if( TermDb::containsTerms( body[i], args ) ){ - tb << trm; - }else{ - body_split << trm; - } - } - if( tb.getNumChildren()==0 ){ - return body_split; - }else if( body_split.getNumChildren()>0 ){ - newBody = tb.getNumChildren()==1 ? tb.getChild( 0 ) : tb; - body_split << mkForAll( args, newBody, qa ); - return body_split.getNumChildren()==1 ? body_split.getChild( 0 ) : body_split; - } + if( tb.getNumChildren()==0 ){ + return body_split; + }else if( body_split.getNumChildren()>0 ){ + newBody = tb.getNumChildren()==1 ? tb.getChild( 0 ) : tb; + std::vector< Node > activeArgs; + computeArgVec2( args, activeArgs, newBody, qa.d_ipl ); + body_split << mkForAll( activeArgs, newBody, qa ); + return body_split.getNumChildren()==1 ? body_split.getChild( 0 ) : body_split; } } + }else if( body.getKind()==NOT ){ + Assert( isLiteral( body[0] ) ); } - //if( body==f[1] ){ - // return f; - //}else{ - return mkForAll( args, body, qa ); - //} + //remove variables that don't occur + std::vector< Node > activeArgs; + computeArgVec2( args, activeArgs, body, qa.d_ipl ); + return mkForAll( activeArgs, body, qa ); } Node QuantifiersRewriter::computeAggressiveMiniscoping( std::vector< Node >& args, Node body ){ @@ -1552,19 +1348,14 @@ bool QuantifiersRewriter::doOperation( Node q, int computeOption, QAttributes& q return is_std; }else if( computeOption==COMPUTE_AGGRESSIVE_MINISCOPING ){ return options::aggressiveMiniscopeQuant() && is_std; - }else if( computeOption==COMPUTE_NNF ){ - return true; }else if( computeOption==COMPUTE_PROCESS_TERMS ){ - return true; - //return options::iteLiftQuant()!=ITE_LIFT_QUANT_MODE_NONE || options::iteCondVarSplitQuant(); + return options::condRewriteQuant(); }else if( computeOption==COMPUTE_COND_SPLIT ){ return ( options::iteDtTesterSplitQuant() || options::condVarSplitQuant() ) && !is_strict_trigger; }else if( computeOption==COMPUTE_PRENEX ){ return options::prenexQuant()!=PRENEX_NONE && !options::aggressiveMiniscopeQuant() && is_std; }else if( computeOption==COMPUTE_VAR_ELIMINATION ){ return ( options::varElimQuant() || options::dtVarExpandQuant() || options::purifyQuant() ) && is_std; - //}else if( computeOption==COMPUTE_CNF ){ - // return options::cnfQuant(); }else if( computeOption==COMPUTE_PURIFY_EXPAND ){ return options::purifyQuant() && is_std; }else{ @@ -1584,11 +1375,9 @@ Node QuantifiersRewriter::computeOperation( Node f, int computeOption, QAttribut n = computeElimSymbols( n ); }else if( computeOption==COMPUTE_MINISCOPING ){ //return directly - return computeMiniscoping( f, args, n, qa ); + return computeMiniscoping( args, n, qa ); }else if( computeOption==COMPUTE_AGGRESSIVE_MINISCOPING ){ return computeAggressiveMiniscoping( args, n ); - }else if( computeOption==COMPUTE_NNF ){ - n = computeNNF( n ); }else if( computeOption==COMPUTE_PROCESS_TERMS ){ std::vector< Node > new_conds; n = computeProcessTerms( n, args, new_conds, f, qa ); @@ -1602,9 +1391,6 @@ Node QuantifiersRewriter::computeOperation( Node f, int computeOption, QAttribut n = computePrenex( n, args, true ); }else if( computeOption==COMPUTE_VAR_ELIMINATION ){ n = computeVarElimination( n, args, qa ); - //}else if( computeOption==COMPUTE_CNF ){ - //n = computeCNF( n, args, defs, false ); - //ipl = Node::null(); }else if( computeOption==COMPUTE_PURIFY_EXPAND ){ std::vector< Node > conj; computePurifyExpand( n, conj, args, qa ); @@ -1738,15 +1524,15 @@ struct ContainsQuantAttributeId {}; typedef expr::Attribute<ContainsQuantAttributeId, uint64_t> ContainsQuantAttribute; // check if the given node contains a universal quantifier -bool QuantifiersRewriter::containsQuantifiers(Node n) { +bool QuantifiersRewriter::containsQuantifiers( Node n ){ if( n.hasAttribute(ContainsQuantAttribute()) ){ return n.getAttribute(ContainsQuantAttribute())==1; - } else if(n.getKind() == kind::FORALL) { + }else if( n.getKind() == kind::FORALL ){ return true; - } else { + }else{ bool cq = false; for( unsigned i = 0; i < n.getNumChildren(); ++i ){ - if( containsQuantifiers(n[i]) ){ + if( containsQuantifiers( n[i] ) ){ cq = true; break; } diff --git a/src/theory/quantifiers/quantifiers_rewriter.h b/src/theory/quantifiers/quantifiers_rewriter.h index 2071d1793..776517109 100644..100755 --- a/src/theory/quantifiers/quantifiers_rewriter.h +++ b/src/theory/quantifiers/quantifiers_rewriter.h @@ -38,6 +38,7 @@ public: static int getPurifyId( Node n ); static int getPurifyIdLit( Node n ); private: + static bool addCheckElimChild( std::vector< Node >& children, Node c, Kind k, std::map< Node, bool >& lit_pol, bool& childrenChanged ); static void addNodeToOrBuilder( Node n, NodeBuilder<>& t ); static Node mkForAll( std::vector< Node >& args, Node body, QAttributes& qa ); static void computeArgs( std::vector< Node >& args, std::map< Node, bool >& activeMap, Node n, std::map< Node, bool >& visited ); @@ -46,7 +47,6 @@ private: static Node computeProcessTerms2( Node body, bool hasPol, bool pol, std::map< Node, bool >& currCond, int nCurrCond, std::map< Node, Node >& cache, std::map< Node, Node >& icache, std::vector< Node >& new_vars, std::vector< Node >& new_conds ); - static Node computeClause( Node n ); static void computeDtTesterIteSplit( Node n, std::map< Node, Node >& pcons, std::map< Node, std::map< int, Node > >& ncons, std::vector< Node >& conj ); static bool isConditionalVariableElim( Node n, int pol=0 ); static bool isVariableElim( Node v, Node s, std::map< Node, std::vector< int > >& var_parent ); @@ -57,15 +57,13 @@ private: static Node computeVarElimination2( Node body, std::vector< Node >& args, QAttributes& qa, std::map< Node, std::vector< int > >& var_parent ); private: static Node computeElimSymbols( Node body ); - static Node computeMiniscoping( Node f, std::vector< Node >& args, Node body, QAttributes& qa ); + static Node computeMiniscoping( std::vector< Node >& args, Node body, QAttributes& qa ); static Node computeAggressiveMiniscoping( std::vector< Node >& args, Node body ); - static Node computeNNF( Node body ); //cache is dependent upon currCond, icache is not, new_conds are negated conditions static Node computeProcessTerms( Node body, std::vector< Node >& new_vars, std::vector< Node >& new_conds, Node q, QAttributes& qa ); static Node computeCondSplit( Node body, QAttributes& qa ); - static Node computeCNF( Node body, std::vector< Node >& args, NodeBuilder<>& defs, bool forcePred ); static Node computePrenex( Node body, std::vector< Node >& args, bool pol ); - static Node computeSplit( Node f, std::vector< Node >& args, Node body ); + static Node computeSplit( std::vector< Node >& args, Node body, QAttributes& qa ); static Node computeVarElimination( Node body, std::vector< Node >& args, QAttributes& qa ); static Node computePurify( Node body, std::vector< Node >& args, std::map< Node, std::vector< int > >& var_parent ); static void computePurifyExpand( Node body, std::vector< Node >& conj, std::vector< Node >& args, QAttributes& qa ); @@ -74,7 +72,6 @@ private: COMPUTE_ELIM_SYMBOLS = 0, COMPUTE_MINISCOPING, COMPUTE_AGGRESSIVE_MINISCOPING, - COMPUTE_NNF, COMPUTE_PROCESS_TERMS, COMPUTE_PRENEX, COMPUTE_VAR_ELIMINATION, diff --git a/src/theory/quantifiers/relevant_domain.cpp b/src/theory/quantifiers/relevant_domain.cpp index b353fce2f..b4b51fd84 100644..100755 --- a/src/theory/quantifiers/relevant_domain.cpp +++ b/src/theory/quantifiers/relevant_domain.cpp @@ -110,7 +110,7 @@ void RelevantDomain::compute(){ for( unsigned i=0; i<sz; i++ ){ Node n = it->second[i]; //if it is a non-redundant term - if( !n.getAttribute(NoMatchAttribute()) ){ + if( db->isTermActive( n ) ){ for( unsigned j=0; j<n.getNumChildren(); j++ ){ RDomain * rf = getRDomain( op, j ); rf->addTerm( n[j] ); diff --git a/src/theory/quantifiers/relevant_domain.h b/src/theory/quantifiers/relevant_domain.h index 2b90520fd..2b90520fd 100644..100755 --- a/src/theory/quantifiers/relevant_domain.h +++ b/src/theory/quantifiers/relevant_domain.h diff --git a/src/theory/quantifiers/rewrite_engine.cpp b/src/theory/quantifiers/rewrite_engine.cpp index 5365dbcfa..2c58b8f77 100644..100755 --- a/src/theory/quantifiers/rewrite_engine.cpp +++ b/src/theory/quantifiers/rewrite_engine.cpp @@ -175,7 +175,7 @@ int RewriteEngine::checkRewriteRule( Node f, Theory::Effort e ) { for( unsigned j=0; j<to_remove.size(); j++ ){ Node ns = d_quantEngine->getSubstitute( to_remove[j], inst ); Trace("rewrite-engine-inst-debug") << "Will remove : " << ns << std::endl; - ns.setAttribute(NoMatchAttribute(),true); + d_quantEngine->getTermDatabase()->setTermInactive( ns ); } */ }else{ diff --git a/src/theory/quantifiers/rewrite_engine.h b/src/theory/quantifiers/rewrite_engine.h index 424530696..424530696 100644..100755 --- a/src/theory/quantifiers/rewrite_engine.h +++ b/src/theory/quantifiers/rewrite_engine.h diff --git a/src/theory/quantifiers/symmetry_breaking.cpp b/src/theory/quantifiers/symmetry_breaking.cpp index 2a2b13583..2a2b13583 100644..100755 --- a/src/theory/quantifiers/symmetry_breaking.cpp +++ b/src/theory/quantifiers/symmetry_breaking.cpp diff --git a/src/theory/quantifiers/symmetry_breaking.h b/src/theory/quantifiers/symmetry_breaking.h index 38fea4f45..e682955e7 100644..100755 --- a/src/theory/quantifiers/symmetry_breaking.h +++ b/src/theory/quantifiers/symmetry_breaking.h @@ -42,9 +42,7 @@ 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; diff --git a/src/theory/quantifiers/term_database.cpp b/src/theory/quantifiers/term_database.cpp index 8b09d8e5d..d3a5e178f 100644..100755 --- a/src/theory/quantifiers/term_database.cpp +++ b/src/theory/quantifiers/term_database.cpp @@ -84,13 +84,13 @@ void TermArgTrie::debugPrint( const char * c, Node n, unsigned depth ) { } } -TermDb::TermDb( context::Context* c, context::UserContext* u, QuantifiersEngine* qe ) : d_quantEngine( qe ), d_op_id_count( 0 ), d_typ_id_count( 0 ) { +TermDb::TermDb( context::Context* c, context::UserContext* u, QuantifiersEngine* qe ) : d_quantEngine( qe ), d_inactive_map( c ), d_op_id_count( 0 ), d_typ_id_count( 0 ) { d_true = NodeManager::currentNM()->mkConst( true ); d_false = NodeManager::currentNM()->mkConst( false ); d_zero = NodeManager::currentNM()->mkConst( Rational( 0 ) ); d_one = NodeManager::currentNM()->mkConst( Rational( 1 ) ); if( options::ceGuidedInst() ){ - d_sygus_tdb = new TermDbSygus; + d_sygus_tdb = new TermDbSygus( c, qe ); }else{ d_sygus_tdb = NULL; } @@ -163,9 +163,18 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant, bool wi //if this is an atomic trigger, consider adding it if( inst::Trigger::isAtomicTrigger( n ) ){ Trace("term-db") << "register term in db " << n << std::endl; + if( options::finiteModelFind() ){ + computeModelBasisArgAttribute( n ); + } + Node op = getMatchOperator( n ); + Trace("term-db-debug") << " match operator is : " << op << std::endl; d_op_map[op].push_back( n ); added.insert( n ); + + if( d_sygus_tdb ){ + d_sygus_tdb->registerEvalTerm( n ); + } if( options::eagerInstQuant() ){ for( unsigned i=0; i<n.getNumChildren(); i++ ){ @@ -178,6 +187,8 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant, bool wi } } } + }else{ + setTermInactive( n ); } rec = true; } @@ -210,7 +221,7 @@ void TermDb::computeUfEqcTerms( TNode f ) { for( unsigned i=0; i<d_op_map[f].size(); i++ ){ TNode n = d_op_map[f][i]; if( hasTermCurrent( n ) ){ - if( !n.getAttribute(NoMatchAttribute()) ){ + if( isTermActive( n ) ){ computeArgReps( n ); TNode r = ee->hasTerm( n ) ? ee->getRepresentative( n ) : n; d_func_map_eqc_trie[f].d_data[r].addTerm( n, d_arg_reps[n] ); @@ -220,7 +231,84 @@ void TermDb::computeUfEqcTerms( TNode f ) { } } +void TermDb::computeUfTerms( TNode f ) { + if( d_op_nonred_count.find( f )==d_op_nonred_count.end() ){ + d_op_nonred_count[ f ] = 0; + std::map< Node, std::vector< Node > >::iterator it = d_op_map.find( f ); + if( it!=d_op_map.end() ){ + eq::EqualityEngine* ee = d_quantEngine->getMasterEqualityEngine(); + Trace("term-db-debug") << "Adding terms for operator " << f << std::endl; + unsigned congruentCount = 0; + unsigned nonCongruentCount = 0; + unsigned alreadyCongruentCount = 0; + unsigned relevantCount = 0; + for( unsigned i=0; i<it->second.size(); i++ ){ + Node n = it->second[i]; + //to be added to term index, term must be relevant, and exist in EE + if( hasTermCurrent( n ) && ee->hasTerm( n ) ){ + relevantCount++; + if( isTermActive( n ) ){ + computeArgReps( n ); + + Trace("term-db-debug") << "Adding term " << n << " with arg reps : "; + for( unsigned i=0; i<d_arg_reps[n].size(); i++ ){ + Trace("term-db-debug") << d_arg_reps[n][i] << " "; + if( std::find( d_func_map_rel_dom[f][i].begin(), + d_func_map_rel_dom[f][i].end(), d_arg_reps[n][i] ) == d_func_map_rel_dom[f][i].end() ){ + d_func_map_rel_dom[f][i].push_back( d_arg_reps[n][i] ); + } + } + Trace("term-db-debug") << std::endl; + Trace("term-db-debug") << " and value : " << ee->getRepresentative( n ) << std::endl; + Node at = d_func_map_trie[ f ].addOrGetTerm( n, d_arg_reps[n] ); + Trace("term-db-debug2") << "...add term returned " << at << std::endl; + if( at!=n && ee->areEqual( at, n ) ){ + setTermInactive( n ); + Trace("term-db-debug") << n << " is redundant." << std::endl; + congruentCount++; + }else{ + if( at!=n && ee->areDisequal( at, n, false ) ){ + std::vector< Node > lits; + lits.push_back( NodeManager::currentNM()->mkNode( at.getType().isBoolean() ? IFF : EQUAL, at, n ) ); + for( unsigned i=0; i<at.getNumChildren(); i++ ){ + if( at[i]!=n[i] ){ + lits.push_back( NodeManager::currentNM()->mkNode( at[i].getType().isBoolean() ? IFF : EQUAL, at[i], n[i] ).negate() ); + } + } + Node lem = lits.size()==1 ? lits[0] : NodeManager::currentNM()->mkNode( OR, lits ); + if( Trace.isOn("term-db-lemma") ){ + Trace("term-db-lemma") << "Disequal congruent terms : " << at << " " << n << "!!!!" << std::endl; + if( !d_quantEngine->getTheoryEngine()->needCheck() ){ + Trace("term-db-lemma") << " all theories passed with no lemmas." << std::endl; + } + Trace("term-db-lemma") << " add lemma : " << lem << std::endl; + } + d_quantEngine->addLemma( lem ); + d_consistent_ee = false; + return; + } + nonCongruentCount++; + d_op_nonred_count[ f ]++; + } + }else{ + Trace("term-db-debug") << n << " is already redundant." << std::endl; + alreadyCongruentCount++; + } + }else{ + Trace("term-db-debug") << n << " is not relevant." << std::endl; + } + } + if( Trace.isOn("tdb") ){ + Trace("tdb") << "Term db size [" << f << "] : " << nonCongruentCount << " / "; + Trace("tdb") << ( nonCongruentCount + congruentCount ) << " / " << ( nonCongruentCount + congruentCount + alreadyCongruentCount ) << " / "; + Trace("tdb") << relevantCount << " / " << it->second.size() << std::endl; + } + } + } +} + bool TermDb::inRelevantDomain( TNode f, unsigned i, TNode r ) { + computeUfTerms( f ); Assert( d_quantEngine->getTheoryEngine()->getMasterEqualityEngine()->getRepresentative( r )==r ); std::map< Node, std::map< unsigned, std::vector< Node > > >::iterator it = d_func_map_rel_dom.find( f ); if( it != d_func_map_rel_dom.end() ){ @@ -237,25 +325,23 @@ bool TermDb::inRelevantDomain( TNode f, unsigned i, TNode r ) { //return a term n' equivalent to n // maximal subterms of n' are representatives in the equality engine qy -Node TermDb::evaluateTerm2( TNode n, std::map< TNode, Node >& visited, EqualityQuery * qy ) { +Node TermDb::evaluateTerm2( TNode n, std::map< TNode, Node >& visited, EqualityQuery * qy, bool useEntailmentTests ) { std::map< TNode, Node >::iterator itv = visited.find( n ); if( itv != visited.end() ){ return itv->second; } Trace("term-db-eval") << "evaluate term : " << n << std::endl; - Node ret; - if( n.getKind()==BOUND_VARIABLE ){ - return n; + Node ret = n; + if( n.getKind()==FORALL || n.getKind()==BOUND_VARIABLE ){ + //do nothing }else if( !qy->hasTerm( n ) ){ //term is not known to be equal to a representative in equality engine, evaluate it - if( n.getKind()==FORALL ){ - ret = Node::null(); - }else if( n.hasOperator() ){ + if( n.hasOperator() ){ TNode f = getMatchOperator( n ); std::vector< TNode > args; bool ret_set = false; for( unsigned i=0; i<n.getNumChildren(); i++ ){ - TNode c = evaluateTerm2( n[i], visited, qy ); + TNode c = evaluateTerm2( n[i], visited, qy, useEntailmentTests ); if( c.isNull() ){ ret = Node::null(); ret_set = true; @@ -267,7 +353,7 @@ Node TermDb::evaluateTerm2( TNode n, std::map< TNode, Node >& visited, EqualityQ ret_set = true; break; }else if( n.getKind()==kind::ITE && i==0 ){ - ret = evaluateTerm2( n[ c==d_true ? 1 : 2], visited, qy ); + ret = evaluateTerm2( n[ c==d_true ? 1 : 2], visited, qy, useEntailmentTests ); ret_set = true; break; } @@ -295,6 +381,22 @@ Node TermDb::evaluateTerm2( TNode n, std::map< TNode, Node >& visited, EqualityQ } ret = NodeManager::currentNM()->mkNode( n.getKind(), args ); ret = Rewriter::rewrite( ret ); + if( ret.getKind()==kind::EQUAL ){ + if( qy->areDisequal( ret[0], ret[1] ) ){ + ret = d_false; + } + } + if( useEntailmentTests ){ + if( ret.getKind()==kind::EQUAL || ret.getKind()==kind::GEQ ){ + for( unsigned j=0; j<2; j++ ){ + std::pair<bool, Node> et = d_quantEngine->getTheoryEngine()->entailmentCheck(THEORY_OF_TYPE_BASED, j==0 ? ret : ret.negate() ); + if( et.first ){ + ret = j==0 ? d_true : d_false; + break; + } + } + } + } } } } @@ -316,14 +418,16 @@ TNode TermDb::getEntailedTerm2( TNode n, std::map< TNode, TNode >& subs, bool su return n; }else if( n.getKind()==BOUND_VARIABLE ){ if( hasSubs ){ - Assert( subs.find( n )!=subs.end() ); - Trace("term-db-entail") << "...substitution is : " << subs[n] << std::endl; - if( subsRep ){ - Assert( qy->getEngine()->hasTerm( subs[n] ) ); - Assert( qy->getEngine()->getRepresentative( subs[n] )==subs[n] ); - return subs[n]; - }else{ - return getEntailedTerm2( subs[n], subs, subsRep, hasSubs, qy ); + std::map< TNode, TNode >::iterator it = subs.find( n ); + if( it!=subs.end() ){ + Trace("term-db-entail") << "...substitution is : " << it->second << std::endl; + if( subsRep ){ + Assert( qy->getEngine()->hasTerm( it->second ) ); + Assert( qy->getEngine()->getRepresentative( it->second )==it->second ); + return it->second; + }else{ + return getEntailedTerm2( it->second, subs, subsRep, hasSubs, qy ); + } } } }else if( n.getKind()==ITE ){ @@ -355,12 +459,12 @@ TNode TermDb::getEntailedTerm2( TNode n, std::map< TNode, TNode >& subs, bool su return TNode::null(); } -Node TermDb::evaluateTerm( TNode n, EqualityQuery * qy ) { +Node TermDb::evaluateTerm( TNode n, EqualityQuery * qy, bool useEntailmentTests ) { if( qy==NULL ){ qy = d_quantEngine->getEqualityQuery(); } std::map< TNode, Node > visited; - return evaluateTerm2( n, visited, qy ); + return evaluateTerm2( n, visited, qy, useEntailmentTests ); } TNode TermDb::getEntailedTerm( TNode n, std::map< TNode, TNode >& subs, bool subsRep, EqualityQuery * qy ) { @@ -436,6 +540,8 @@ bool TermDb::isEntailed2( TNode n, std::map< TNode, TNode >& subs, bool subsRep, return qy->getEngine()->getRepresentative( n1 ) == ( pol ? d_true : d_false ); } } + }else if( n.getKind()==FORALL && !pol ){ + return isEntailed2( n[1], subs, subsRep, hasSubs, pol, qy ); } return false; } @@ -457,6 +563,18 @@ bool TermDb::isEntailed( TNode n, std::map< TNode, TNode >& subs, bool subsRep, return isEntailed2( n, subs, subsRep, true, pol, qy ); } +bool TermDb::isTermActive( Node n ) { + return d_inactive_map.find( n )==d_inactive_map.end(); + //return !n.getAttribute(NoMatchAttribute()); +} + +void TermDb::setTermInactive( Node n ) { + d_inactive_map[n] = true; + //Trace("term-db-debug2") << "set no match attribute" << std::endl; + //NoMatchAttribute nma; + //n.setAttribute(nma,true); +} + bool TermDb::hasTermCurrent( Node n, bool useMode ) { if( !useMode ){ return d_has_map.find( n )!=d_has_map.end(); @@ -556,10 +674,6 @@ void TermDb::presolve() { } bool TermDb::reset( Theory::Effort effort ){ - int nonCongruentCount = 0; - int congruentCount = 0; - int alreadyCongruentCount = 0; - int nonRelevantCount = 0; d_op_nonred_count.clear(); d_arg_reps.clear(); d_func_map_trie.clear(); @@ -614,89 +728,20 @@ bool TermDb::reset( Theory::Effort effort ){ } } +/* //rebuild d_func/pred_map_trie for each operation, this will calculate all congruent terms for( std::map< Node, std::vector< Node > >::iterator it = d_op_map.begin(); it != d_op_map.end(); ++it ){ - d_op_nonred_count[ it->first ] = 0; - Trace("term-db-debug") << "Adding terms for operator " << it->first << std::endl; - for( unsigned i=0; i<it->second.size(); i++ ){ - Node n = it->second[i]; - //to be added to term index, term must be relevant, and exist in EE - if( hasTermCurrent( n ) && ee->hasTerm( n ) ){ - if( !n.getAttribute(NoMatchAttribute()) ){ - if( options::finiteModelFind() ){ - computeModelBasisArgAttribute( n ); - } - computeArgReps( n ); - - Trace("term-db-debug") << "Adding term " << n << " with arg reps : "; - for( unsigned i=0; i<d_arg_reps[n].size(); i++ ){ - Trace("term-db-debug") << d_arg_reps[n][i] << " "; - if( std::find( d_func_map_rel_dom[it->first][i].begin(), - d_func_map_rel_dom[it->first][i].end(), d_arg_reps[n][i] ) == d_func_map_rel_dom[it->first][i].end() ){ - d_func_map_rel_dom[it->first][i].push_back( d_arg_reps[n][i] ); - } - } - Trace("term-db-debug") << std::endl; - if( ee->hasTerm( n ) ){ - Trace("term-db-debug") << " and value : " << ee->getRepresentative( n ) << std::endl; - } - Node at = d_func_map_trie[ it->first ].addOrGetTerm( n, d_arg_reps[n] ); - if( at!=n && ee->areEqual( at, n ) ){ - NoMatchAttribute nma; - n.setAttribute(nma,true); - Trace("term-db-debug") << n << " is redundant." << std::endl; - congruentCount++; - }else{ - if( at!=n && ee->areDisequal( at, n, false ) ){ - std::vector< Node > lits; - lits.push_back( NodeManager::currentNM()->mkNode( at.getType().isBoolean() ? IFF : EQUAL, at, n ) ); - for( unsigned i=0; i<at.getNumChildren(); i++ ){ - if( at[i]!=n[i] ){ - lits.push_back( NodeManager::currentNM()->mkNode( at[i].getType().isBoolean() ? IFF : EQUAL, at[i], n[i] ).negate() ); - } - } - Node lem = lits.size()==1 ? lits[0] : NodeManager::currentNM()->mkNode( OR, lits ); - if( Trace.isOn("term-db-lemma") ){ - Trace("term-db-lemma") << "Disequal congruent terms : " << at << " " << n << "!!!!" << std::endl; - if( !d_quantEngine->getTheoryEngine()->needCheck() ){ - Trace("term-db-lemma") << " all theories passed with no lemmas." << std::endl; - } - Trace("term-db-lemma") << " add lemma : " << lem << std::endl; - } - d_quantEngine->addLemma( lem ); - d_consistent_ee = false; - return false; - } - nonCongruentCount++; - d_op_nonred_count[ it->first ]++; - } - }else{ - Trace("term-db-debug") << n << " is already redundant." << std::endl; - congruentCount++; - alreadyCongruentCount++; - } - }else{ - Trace("term-db-debug") << n << " is not relevant." << std::endl; - nonRelevantCount++; - } - } - } - Trace("term-db-stats") << "TermDb: Reset" << std::endl; - Trace("term-db-stats") << "Non-Congruent/Congruent/Non-Relevant = "; - Trace("term-db-stats") << nonCongruentCount << " / " << congruentCount << " (" << alreadyCongruentCount << ") / " << nonRelevantCount << std::endl; - if( Trace.isOn("term-db-index") ){ - Trace("term-db-index") << "functions : " << std::endl; - for( std::map< Node, std::vector< Node > >::iterator it = d_op_map.begin(); it != d_op_map.end(); ++it ){ - if( it->second.size()>0 ){ - Trace("term-db-index") << "- " << it->first << std::endl; - d_func_map_trie[ it->first ].debugPrint("term-db-index", it->second[0]); - } + computeUfTerms( it->first ); + if( !d_consistent_ee ){ + return false; } } +*/ return true; } TermArgTrie * TermDb::getTermArgTrie( Node f ) { + computeUfTerms( f ); std::map< Node, TermArgTrie >::iterator itut = d_func_map_trie.find( f ); if( itut!=d_func_map_trie.end() ){ return &itut->second; @@ -725,11 +770,18 @@ TermArgTrie * TermDb::getTermArgTrie( Node eqc, Node f ) { } TNode TermDb::getCongruentTerm( Node f, Node n ) { - computeArgReps( n ); - return d_func_map_trie[f].existsTerm( d_arg_reps[n] ); + computeUfTerms( f ); + std::map< Node, TermArgTrie >::iterator itut = d_func_map_trie.find( f ); + if( itut!=d_func_map_trie.end() ){ + computeArgReps( n ); + return itut->second.existsTerm( d_arg_reps[n] ); + }else{ + return TNode::null(); + } } TNode TermDb::getCongruentTerm( Node f, std::vector< TNode >& args ) { + computeUfTerms( f ); return d_func_map_trie[f].existsTerm( args ); } @@ -822,6 +874,7 @@ void TermDb::makeInstantiationConstantsFor( Node q ){ Debug("quantifiers-engine") << "Instantiation constants for " << q << " : " << std::endl; for( unsigned i=0; i<q[0].getNumChildren(); i++ ){ d_vars[q].push_back( q[0][i] ); + d_var_num[q][q[0][i]] = i; //make instantiation constants Node ic = NodeManager::currentNM()->mkInstConstant( q[0][i].getType() ); d_inst_constants_map[ic] = q; @@ -832,9 +885,8 @@ void TermDb::makeInstantiationConstantsFor( Node q ){ ic.setAttribute( ivna, i ); InstConstantAttribute ica; ic.setAttribute( ica, q ); - //also set the no-match attribute - NoMatchAttribute nma; - ic.setAttribute(nma,true); + //also set the term inactive + setTermInactive( ic ); } } } @@ -892,11 +944,6 @@ Node TermDb::getInstConstAttr( Node n ) { } InstConstantAttribute ica; n.setAttribute(ica, q); - if( !q.isNull() ){ - //also set the no-match attribute - NoMatchAttribute nma; - n.setAttribute(nma,true); - } } return n.getAttribute(InstConstantAttribute()); } @@ -1005,9 +1052,12 @@ Node TermDb::getCounterexampleLiteral( Node q ){ Node TermDb::getInstConstantNode( Node n, Node q ){ makeInstantiationConstantsFor( q ); - Node n2 = n.substitute( d_vars[q].begin(), d_vars[q].end(), - d_inst_constants[q].begin(), d_inst_constants[q].end() ); - return n2; + return n.substitute( d_vars[q].begin(), d_vars[q].end(), d_inst_constants[q].begin(), d_inst_constants[q].end() ); +} + +Node TermDb::getVariableNode( Node n, Node q ) { + makeInstantiationConstantsFor( q ); + return n.substitute( d_inst_constants[q].begin(), d_inst_constants[q].end(), d_vars[q].begin(), d_vars[q].end() ); } Node TermDb::getInstantiatedNode( Node n, Node q, std::vector< Node >& terms ) { @@ -1512,7 +1562,7 @@ struct sortTermOrder { //this function makes a canonical representation of a term ( // - orders variables left to right // - if apply_torder, then sort direct subterms of commutative operators -Node TermDb::getCanonicalTerm( TNode n, std::map< TypeNode, unsigned >& var_count, std::map< TNode, TNode >& subs, bool apply_torder ) { +Node TermDb::getCanonicalTerm( TNode n, std::map< TypeNode, unsigned >& var_count, std::map< TNode, TNode >& subs, bool apply_torder, std::map< TNode, Node >& visited ) { Trace("canon-term-debug") << "Get canonical term for " << n << std::endl; if( n.getKind()==BOUND_VARIABLE ){ std::map< TNode, TNode >::iterator it = subs.find( n ); @@ -1529,32 +1579,38 @@ Node TermDb::getCanonicalTerm( TNode n, std::map< TypeNode, unsigned >& var_coun return it->second; } }else if( n.getNumChildren()>0 ){ - //collect children - Trace("canon-term-debug") << "Collect children" << std::endl; - std::vector< Node > cchildren; - for( unsigned i=0; i<n.getNumChildren(); i++ ){ - cchildren.push_back( n[i] ); - } - //if applicable, first sort by term order - if( apply_torder && isComm( n.getKind() ) ){ - Trace("canon-term-debug") << "Sort based on commutative operator " << n.getKind() << std::endl; - sortTermOrder sto; - sto.d_tdb = this; - std::sort( cchildren.begin(), cchildren.end(), sto ); - } - //now make canonical - Trace("canon-term-debug") << "Make canonical children" << std::endl; - for( unsigned i=0; i<cchildren.size(); i++ ){ - cchildren[i] = getCanonicalTerm( cchildren[i], var_count, subs, apply_torder ); - } - if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ - Trace("canon-term-debug") << "Insert operator " << n.getOperator() << std::endl; - cchildren.insert( cchildren.begin(), n.getOperator() ); - } - Trace("canon-term-debug") << "...constructing for " << n << "." << std::endl; - Node ret = NodeManager::currentNM()->mkNode( n.getKind(), cchildren ); - Trace("canon-term-debug") << "...constructed " << ret << " for " << n << "." << std::endl; - return ret; + std::map< TNode, Node >::iterator it = visited.find( n ); + if( it!=visited.end() ){ + return it->second; + }else{ + //collect children + Trace("canon-term-debug") << "Collect children" << std::endl; + std::vector< Node > cchildren; + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + cchildren.push_back( n[i] ); + } + //if applicable, first sort by term order + if( apply_torder && isComm( n.getKind() ) ){ + Trace("canon-term-debug") << "Sort based on commutative operator " << n.getKind() << std::endl; + sortTermOrder sto; + sto.d_tdb = this; + std::sort( cchildren.begin(), cchildren.end(), sto ); + } + //now make canonical + Trace("canon-term-debug") << "Make canonical children" << std::endl; + for( unsigned i=0; i<cchildren.size(); i++ ){ + cchildren[i] = getCanonicalTerm( cchildren[i], var_count, subs, apply_torder, visited ); + } + if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ + Trace("canon-term-debug") << "Insert operator " << n.getOperator() << std::endl; + cchildren.insert( cchildren.begin(), n.getOperator() ); + } + Trace("canon-term-debug") << "...constructing for " << n << "." << std::endl; + Node ret = NodeManager::currentNM()->mkNode( n.getKind(), cchildren ); + Trace("canon-term-debug") << "...constructed " << ret << " for " << n << "." << std::endl; + visited[n] = ret; + return ret; + } }else{ Trace("canon-term-debug") << "...return 0-child term." << std::endl; return n; @@ -1564,7 +1620,8 @@ Node TermDb::getCanonicalTerm( TNode n, std::map< TypeNode, unsigned >& var_coun Node TermDb::getCanonicalTerm( TNode n, bool apply_torder ){ std::map< TypeNode, unsigned > var_count; std::map< TNode, TNode > subs; - return getCanonicalTerm( n, var_count, subs, apply_torder ); + std::map< TNode, Node > visited; + return getCanonicalTerm( n, var_count, subs, apply_torder, visited ); } void TermDb::getVtsTerms( std::vector< Node >& t, bool isFree, bool create, bool inc_delta ) { @@ -1790,6 +1847,18 @@ bool TermDb::getEnsureTypeCondition( Node n, TypeNode tn, std::vector< Node >& c } } +void TermDb::getRelevancyCondition( Node n, std::vector< Node >& cond ) { + if( n.getKind()==APPLY_SELECTOR_TOTAL ){ + unsigned scindex = Datatype::cindexOf(n.getOperator().toExpr()); + const Datatype& dt = ((DatatypeType)(n[0].getType()).toType()).getDatatype(); + Node rc = NodeManager::currentNM()->mkNode( APPLY_TESTER, Node::fromExpr( dt[scindex].getTester() ), n[0] ).negate(); + if( std::find( cond.begin(), cond.end(), rc )==cond.end() ){ + cond.push_back( rc ); + } + getRelevancyCondition( n[0], cond ); + } +} + bool TermDb::containsTerm2( Node n, Node t, std::map< Node, bool >& visited ) { if( n==t ){ return true; @@ -1856,7 +1925,7 @@ bool TermDb::containsUninterpretedConstant( Node n ) { bool ret = false; if( n.getKind()==UNINTERPRETED_CONSTANT ){ ret = true; - }else{ + }else{ for( unsigned i=0; i<n.getNumChildren(); i++ ){ if( containsUninterpretedConstant( n[i] ) ){ ret = true; @@ -1884,7 +1953,8 @@ Node TermDb::simpleNegate( Node n ){ bool TermDb::isAssoc( Kind k ) { return k==PLUS || k==MULT || k==AND || k==OR || k==XOR || k==IFF || - k==BITVECTOR_PLUS || k==BITVECTOR_MULT || k==BITVECTOR_AND || k==BITVECTOR_OR || k==BITVECTOR_XOR || k==BITVECTOR_XNOR || k==BITVECTOR_CONCAT; + k==BITVECTOR_PLUS || k==BITVECTOR_MULT || k==BITVECTOR_AND || k==BITVECTOR_OR || k==BITVECTOR_XOR || k==BITVECTOR_XNOR || k==BITVECTOR_CONCAT || + k==STRING_CONCAT; } bool TermDb::isComm( Kind k ) { @@ -2184,11 +2254,15 @@ bool TermDb::isQAttrQuantElimPartial( Node q ) { } } -TermDbSygus::TermDbSygus(){ +TermDbSygus::TermDbSygus( context::Context* c, QuantifiersEngine* qe ) : d_quantEngine( qe ){ d_true = NodeManager::currentNM()->mkConst( true ); d_false = NodeManager::currentNM()->mkConst( false ); } +bool TermDbSygus::reset( Theory::Effort e ) { + return true; +} + TNode TermDbSygus::getVar( TypeNode tn, int i ) { while( i>=(int)d_fv[tn].size() ){ std::stringstream ss; @@ -2400,8 +2474,8 @@ Node TermDbSygus::sygusToBuiltin( Node n, TypeNode tn ) { std::map< Node, Node >::iterator it = d_sygus_to_builtin[tn].find( n ); if( it==d_sygus_to_builtin[tn].end() ){ Trace("sygus-db-debug") << "SygusToBuiltin : compute for " << n << ", type = " << tn << std::endl; - Assert( n.getKind()==APPLY_CONSTRUCTOR ); const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + Assert( n.getKind()==APPLY_CONSTRUCTOR ); unsigned i = Datatype::indexOf( n.getOperator().toExpr() ); Assert( n.getNumChildren()==dt[i].getNumArgs() ); std::map< TypeNode, int > var_count; @@ -3051,6 +3125,7 @@ void TermDbSygus::printSygusTerm( std::ostream& out, Node n, std::vector< Node > std::string name = std::string( str.begin() + found +1, str.end() ); out << name; }else{ + Trace("ajr-temp") << "[[print operator " << op << "]]" << std::endl; out << op; } if( n.getNumChildren()>0 ){ @@ -3118,3 +3193,91 @@ void TermDbSygus::printSygusTerm( std::ostream& out, Node n, std::vector< Node > out << n; } } + +Node TermDbSygus::getAnchor( Node n ) { + if( n.getKind()==APPLY_SELECTOR_TOTAL ){ + return getAnchor( n[0] ); + }else{ + return n; + } +} + +void TermDbSygus::registerEvalTerm( Node n ) { + if( options::sygusDirectEval() ){ + if( n.getKind()==APPLY_UF && !n.getType().isBoolean() ){ + Trace("sygus-eager") << "TermDbSygus::eager: Register eval term : " << n << std::endl; + TypeNode tn = n[0].getType(); + if( datatypes::DatatypesRewriter::isTypeDatatype(tn) ){ + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + if( dt.isSygus() ){ + Node f = n.getOperator(); + Trace("sygus-eager") << "...the evaluation function is : " << f << std::endl; + if( n[0].getKind()!=APPLY_CONSTRUCTOR ){ + d_evals[n[0]].push_back( n ); + TypeNode tn = n[0].getType(); + Assert( datatypes::DatatypesRewriter::isTypeDatatype(tn) ); + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + Node var_list = Node::fromExpr( dt.getSygusVarList() ); + Assert( dt.isSygus() ); + d_eval_args[n[0]].push_back( std::vector< Node >() ); + for( unsigned j=1; j<n.getNumChildren(); j++ ){ + if( var_list[j-1].getType().isBoolean() ){ + //TODO: remove this case when boolean term conversion is eliminated + Node c = NodeManager::currentNM()->mkConst(BitVector(1u, 1u)); + d_eval_args[n[0]].back().push_back( n[j].eqNode( c ) ); + }else{ + d_eval_args[n[0]].back().push_back( n[j] ); + } + } + Node a = getAnchor( n[0] ); + d_subterms[a][n[0]] = true; + } + } + } + } + } +} + +void TermDbSygus::registerModelValue( Node a, Node v, std::vector< Node >& lems ) { + std::map< Node, std::map< Node, bool > >::iterator its = d_subterms.find( a ); + if( its!=d_subterms.end() ){ + Trace("sygus-eager") << "registerModelValue : " << a << ", has " << its->second.size() << " registered subterms." << std::endl; + for( std::map< Node, bool >::iterator itss = its->second.begin(); itss != its->second.end(); ++itss ){ + Node n = itss->first; + Trace("sygus-eager-debug") << "...process : " << n << std::endl; + std::map< Node, std::vector< std::vector< Node > > >::iterator it = d_eval_args.find( n ); + if( it!=d_eval_args.end() && !it->second.empty() ){ + TNode at = a; + TNode vt = v; + Node vn = n.substitute( at, vt ); + vn = Rewriter::rewrite( vn ); + unsigned start = d_node_mv_args_proc[n][vn]; + Node antec = n.eqNode( vn ).negate(); + TypeNode tn = n.getType(); + Assert( datatypes::DatatypesRewriter::isTypeDatatype(tn) ); + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + Assert( dt.isSygus() ); + Trace("sygus-eager") << "TermDbSygus::eager: Register model value : " << vn << " for " << n << std::endl; + Trace("sygus-eager") << "...it has " << it->second.size() << " evaluations, already processed " << start << "." << std::endl; + Node bTerm = d_quantEngine->getTermDatabaseSygus()->sygusToBuiltin( vn, tn ); + Trace("sygus-eager") << "Built-in term : " << bTerm << std::endl; + std::vector< Node > vars; + Node var_list = Node::fromExpr( dt.getSygusVarList() ); + for( unsigned j=0; j<var_list.getNumChildren(); j++ ){ + vars.push_back( var_list[j] ); + } + //for each evaluation + for( unsigned i=start; i<it->second.size(); i++ ){ + Assert( vars.size()==it->second[i].size() ); + Node sBTerm = bTerm.substitute( vars.begin(), vars.end(), it->second[i].begin(), it->second[i].end() ); + Node lem = NodeManager::currentNM()->mkNode( n.getType().isBoolean() ? IFF : EQUAL, d_evals[n][i], sBTerm ); + lem = NodeManager::currentNM()->mkNode( OR, antec, lem ); + Trace("sygus-eager") << "Lemma : " << lem << std::endl; + lems.push_back( lem ); + } + d_node_mv_args_proc[n][vn] = it->second.size(); + } + } + } +} + diff --git a/src/theory/quantifiers/term_database.h b/src/theory/quantifiers/term_database.h index a62b343a2..7ab3668eb 100644..100755 --- a/src/theory/quantifiers/term_database.h +++ b/src/theory/quantifiers/term_database.h @@ -47,15 +47,6 @@ typedef expr::Attribute< SygusAttributeId, bool > SygusAttribute; struct SynthesisAttributeId {}; typedef expr::Attribute< SynthesisAttributeId, bool > SynthesisAttribute; -/** Attribute true for nodes that should not be used for matching */ -struct NoMatchAttributeId {}; -/** use the special for boolean flag */ -typedef expr::Attribute< NoMatchAttributeId, - bool, - expr::attr::NullCleanupStrategy, - true // context dependent - > NoMatchAttribute; - // attribute for "contains instantiation constants from" struct InstConstantAttributeId {}; typedef expr::Attribute<InstConstantAttributeId, Node> InstConstantAttribute; @@ -179,6 +170,7 @@ class TermDb : public QuantifiersUtil { friend class ::CVC4::theory::quantifiers::ConjectureGenerator; friend class ::CVC4::theory::quantifiers::TermGenEnv; typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap; + typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap; private: /** reference to the quantifiers engine */ QuantifiersEngine* d_quantEngine; @@ -211,6 +203,8 @@ private: std::map< Node, std::vector< Node > > d_op_map; /** map from type nodes to terms of that type */ std::map< TypeNode, std::vector< Node > > d_type_map; + /** inactive map */ + NodeBoolMap d_inactive_map; /** count number of non-redundant ground terms per operator */ std::map< Node, int > d_op_nonred_count; @@ -228,7 +222,7 @@ private: /** set has term */ void setHasTerm( Node n ); /** evaluate term */ - Node evaluateTerm2( TNode n, std::map< TNode, Node >& visited, EqualityQuery * qy ); + Node evaluateTerm2( TNode n, std::map< TNode, Node >& visited, EqualityQuery * qy, bool useEntailmentTests ); TNode getEntailedTerm2( TNode n, std::map< TNode, TNode >& subs, bool subsRep, bool hasSubs, EqualityQuery * qy ); bool isEntailed2( TNode n, std::map< TNode, TNode >& subs, bool subsRep, bool hasSubs, bool pol, EqualityQuery * qy ); public: @@ -254,18 +248,23 @@ public: void computeArgReps( TNode n ); /** compute uf eqc terms */ void computeUfEqcTerms( TNode f ); + /** compute uf terms */ + void computeUfTerms( TNode f ); /** in relevant domain */ bool inRelevantDomain( TNode f, unsigned i, TNode r ); /** evaluate a term under a substitution. Return representative in EE if possible. * subsRep is whether subs contains only representatives */ - Node evaluateTerm( TNode n, EqualityQuery * qy = NULL ); + Node evaluateTerm( TNode n, EqualityQuery * qy = NULL, bool useEntailmentTests = false ); /** get entailed term, does not construct new terms, less aggressive */ TNode getEntailedTerm( TNode n, EqualityQuery * qy = NULL ); TNode getEntailedTerm( TNode n, std::map< TNode, TNode >& subs, bool subsRep, EqualityQuery * qy = NULL ); /** is entailed (incomplete check) */ bool isEntailed( TNode n, bool pol, EqualityQuery * qy = NULL ); bool isEntailed( TNode n, std::map< TNode, TNode >& subs, bool subsRep, bool pol, EqualityQuery * qy = NULL ); + /** is active */ + bool isTermActive( Node n ); + void setTermInactive( Node n ); /** has term */ bool hasTermCurrent( Node n, bool useMode = true ); /** is term eligble for instantiation? */ @@ -274,7 +273,7 @@ public: Node getEligibleTermInEqc( TNode r ); /** is inst closure */ bool isInstClosure( Node r ); - + //for model basis private: //map from types to model basis terms @@ -301,6 +300,7 @@ public: private: /** map from universal quantifiers to the list of variables */ std::map< Node, std::vector< Node > > d_vars; + std::map< Node, std::map< Node, unsigned > > d_var_num; /** map from universal quantifiers to the list of instantiation constants */ std::map< Node, std::vector< Node > > d_inst_constants; /** map from universal quantifiers to their inst constant body */ @@ -312,6 +312,8 @@ private: /** make instantiation constants for */ void makeInstantiationConstantsFor( Node q ); public: + /** get variable number */ + unsigned getVariableNum( Node q, Node v ) { return d_var_num[q][v]; } /** get the i^th instantiation constant of q */ Node getInstantiationConstant( Node q, int i ) const; /** get number of instantiation constants for q */ @@ -327,6 +329,7 @@ public: instantiation. */ Node getInstConstantNode( Node n, Node q ); + Node getVariableNode( Node n, Node q ); /** get substituted node */ Node getInstantiatedNode( Node n, Node q, std::vector< Node >& terms ); @@ -419,7 +422,8 @@ private: //free variables std::map< TypeNode, std::vector< Node > > d_cn_free_var; // get canonical term, return null if it contains a term apart from handled signature - Node getCanonicalTerm( TNode n, std::map< TypeNode, unsigned >& var_count, std::map< TNode, TNode >& subs, bool apply_torder ); + Node getCanonicalTerm( TNode n, std::map< TypeNode, unsigned >& var_count, std::map< TNode, TNode >& subs, bool apply_torder, + std::map< TNode, Node >& visited ); public: /** get id for operator */ int getIdForOperator( Node op ); @@ -461,6 +465,8 @@ public: static Node ensureType( Node n, TypeNode tn ); /** get ensure type condition */ static bool getEnsureTypeCondition( Node n, TypeNode tn, std::vector< Node >& cond ); + /** get relevancy condition */ + static void getRelevancyCondition( Node n, std::vector< Node >& cond ); private: //helper for contains term static bool containsTerm2( Node n, Node t, std::map< Node, bool >& visited ); @@ -541,6 +547,8 @@ public: class TermDbSygus { private: + /** reference to the quantifiers engine */ + QuantifiersEngine* d_quantEngine; std::map< TypeNode, std::vector< Node > > d_fv; std::map< Node, TypeNode > d_fv_stype; std::map< Node, int > d_fv_num; @@ -554,7 +562,6 @@ public: private: std::map< TypeNode, std::map< int, Node > > d_generic_base; std::map< TypeNode, std::vector< Node > > d_generic_templ; - Node getGenericBase( TypeNode tn, const Datatype& dt, int c ); bool getMatch( Node p, Node n, std::map< int, Node >& s ); bool getMatch2( Node p, Node n, std::map< int, Node >& s, std::vector< int >& new_s ); public: @@ -581,7 +588,11 @@ private: std::map< TypeNode, std::map< Node, Node > > d_sygus_to_builtin; std::map< TypeNode, std::map< Node, Node > > d_builtin_const_to_sygus; public: - TermDbSygus(); + TermDbSygus( context::Context* c, QuantifiersEngine* qe ); + ~TermDbSygus(){} + bool reset( Theory::Effort e ); + std::string identify() const { return "TermDbSygus"; } + bool isRegistered( TypeNode tn ); TypeNode sygusToBuiltinType( TypeNode tn ); int getKindArg( TypeNode tn, Kind k ); @@ -615,6 +626,7 @@ public: /** get value */ Node getTypeMaxValue( TypeNode tn ); TypeNode getSygusTypeForVar( Node v ); + Node getGenericBase( TypeNode tn, const Datatype& dt, int c ); Node mkGeneric( const Datatype& dt, int c, std::map< TypeNode, int >& var_count, std::map< int, Node >& pre ); Node sygusToBuiltin( Node n, TypeNode tn ); Node builtinToSygusConst( Node c, TypeNode tn, int rcons_depth = 0 ); @@ -633,6 +645,18 @@ public: static Kind getOperatorKind( Node op ); /** print sygus term */ static void printSygusTerm( std::ostream& out, Node n, std::vector< Node >& lvs ); + + /** get anchor */ + static Node getAnchor( Node n ); +//for eager instantiation +private: + std::map< Node, std::map< Node, bool > > d_subterms; + std::map< Node, std::vector< Node > > d_evals; + std::map< Node, std::vector< std::vector< Node > > > d_eval_args; + std::map< Node, std::map< Node, unsigned > > d_node_mv_args_proc; +public: + void registerEvalTerm( Node n ); + void registerModelValue( Node n, Node v, std::vector< Node >& lems ); }; }/* CVC4::theory::quantifiers namespace */ diff --git a/src/theory/quantifiers/theory_quantifiers.cpp b/src/theory/quantifiers/theory_quantifiers.cpp index efe40aaa8..7ad13b3a8 100644..100755 --- a/src/theory/quantifiers/theory_quantifiers.cpp +++ b/src/theory/quantifiers/theory_quantifiers.cpp @@ -72,8 +72,11 @@ void TheoryQuantifiers::notifyEq(TNode lhs, TNode rhs) { void TheoryQuantifiers::preRegisterTerm(TNode n) { Debug("quantifiers-prereg") << "TheoryQuantifiers::preRegisterTerm() " << n << endl; - if( n.getKind()==FORALL && !TermDb::hasInstConstAttr(n) ){ - getQuantifiersEngine()->registerQuantifier( n ); + if( n.getKind()==FORALL ){ + if( !options::cbqi() || options::recurseCbqi() || !TermDb::hasInstConstAttr(n) ){ + getQuantifiersEngine()->registerQuantifier( n ); + Debug("quantifiers-prereg") << "TheoryQuantifiers::preRegisterTerm() done " << n << endl; + } } } diff --git a/src/theory/quantifiers/theory_quantifiers.h b/src/theory/quantifiers/theory_quantifiers.h index 6775e0536..6775e0536 100644..100755 --- a/src/theory/quantifiers/theory_quantifiers.h +++ b/src/theory/quantifiers/theory_quantifiers.h diff --git a/src/theory/quantifiers/theory_quantifiers_type_rules.h b/src/theory/quantifiers/theory_quantifiers_type_rules.h index 6ba57afb4..6ba57afb4 100644..100755 --- a/src/theory/quantifiers/theory_quantifiers_type_rules.h +++ b/src/theory/quantifiers/theory_quantifiers_type_rules.h diff --git a/src/theory/quantifiers/trigger.cpp b/src/theory/quantifiers/trigger.cpp index 38635b37b..ee091919d 100644..100755 --- a/src/theory/quantifiers/trigger.cpp +++ b/src/theory/quantifiers/trigger.cpp @@ -43,9 +43,8 @@ void TriggerTermInfo::init( Node q, Node n, int reqPol, Node reqPolEq ){ } /** trigger class constructor */ -Trigger::Trigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, int matchOption ) - : d_quantEngine( qe ), d_f( f ) -{ +Trigger::Trigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes ) + : d_quantEngine( qe ), d_f( f ) { d_nodes.insert( d_nodes.begin(), nodes.begin(), nodes.end() ); Trace("trigger") << "Trigger for " << f << ": " << std::endl; for( unsigned i=0; i<d_nodes.size(); i++ ){ @@ -53,7 +52,7 @@ Trigger::Trigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, int } if( d_nodes.size()==1 ){ if( isSimpleTrigger( d_nodes[0] ) ){ - d_mg = new InstMatchGeneratorSimple( f, d_nodes[0] ); + d_mg = new InstMatchGeneratorSimple( f, d_nodes[0], qe ); }else{ d_mg = InstMatchGenerator::mkInstMatchGenerator( f, d_nodes[0], qe ); d_mg->setActiveAdd(true); @@ -87,7 +86,7 @@ Trigger::Trigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, int } Trigger::~Trigger() { - if(d_mg != NULL) { delete d_mg; } + delete d_mg; } void Trigger::resetInstantiationRound(){ @@ -112,6 +111,10 @@ int Trigger::addTerm( Node t ){ return d_mg->addTerm( d_f, t, d_quantEngine ); } +Node Trigger::getInstPattern(){ + return NodeManager::currentNM()->mkNode( INST_PATTERN, d_nodes ); +} + int Trigger::addInstantiations( InstMatch& baseMatch ){ int addedLemmas = d_mg->addInstantiations( d_f, baseMatch, d_quantEngine ); if( addedLemmas>0 ){ @@ -124,77 +127,85 @@ int Trigger::addInstantiations( InstMatch& baseMatch ){ return addedLemmas; } -Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, int matchOption, bool keepAll, int trOption ){ - std::vector< Node > trNodes; - if( !keepAll ){ - //only take nodes that contribute variables to the trigger when added - std::vector< Node > temp; - temp.insert( temp.begin(), nodes.begin(), nodes.end() ); - std::map< Node, bool > vars; - std::map< Node, std::vector< Node > > patterns; - size_t varCount = 0; - std::map< Node, std::vector< Node > > varContains; - quantifiers::TermDb::getVarContains( f, temp, varContains ); - for( unsigned i=0; i<temp.size(); i++ ){ - bool foundVar = false; +bool Trigger::mkTriggerTerms( Node q, std::vector< Node >& nodes, unsigned n_vars, std::vector< Node >& trNodes ) { + //only take nodes that contribute variables to the trigger when added + std::vector< Node > temp; + temp.insert( temp.begin(), nodes.begin(), nodes.end() ); + std::map< Node, bool > vars; + std::map< Node, std::vector< Node > > patterns; + size_t varCount = 0; + std::map< Node, std::vector< Node > > varContains; + quantifiers::TermDb::getVarContains( q, temp, varContains ); + for( unsigned i=0; i<temp.size(); i++ ){ + bool foundVar = false; + for( unsigned j=0; j<varContains[ temp[i] ].size(); j++ ){ + Node v = varContains[ temp[i] ][j]; + Assert( quantifiers::TermDb::getInstConstAttr(v)==q ); + if( vars.find( v )==vars.end() ){ + varCount++; + vars[ v ] = true; + foundVar = true; + } + } + if( foundVar ){ + trNodes.push_back( temp[i] ); for( unsigned j=0; j<varContains[ temp[i] ].size(); j++ ){ Node v = varContains[ temp[i] ][j]; - Assert( quantifiers::TermDb::getInstConstAttr(v)==f ); - if( vars.find( v )==vars.end() ){ - varCount++; - vars[ v ] = true; - foundVar = true; - } - } - if( foundVar ){ - trNodes.push_back( temp[i] ); - for( unsigned j=0; j<varContains[ temp[i] ].size(); j++ ){ - Node v = varContains[ temp[i] ][j]; - patterns[ v ].push_back( temp[i] ); - } - } - if( varCount==f[0].getNumChildren() ){ - break; + patterns[ v ].push_back( temp[i] ); } } - if( varCount<f[0].getNumChildren() && !options::partialTriggers() ){ - 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; + if( varCount==n_vars ){ + break; + } + } + if( varCount<n_vars ){ + Trace("trigger-debug") << "Don't consider trigger since it does not contain specified number of variables." << 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{ - //now, minimize the trigger - for( unsigned i=0; i<trNodes.size(); i++ ){ - bool keepPattern = false; - Node n = trNodes[i]; + //do not generate multi-trigger if it does not contain all variables + return false; + }else{ + //now, minimize the trigger + for( unsigned i=0; i<trNodes.size(); i++ ){ + bool keepPattern = false; + Node n = trNodes[i]; + for( unsigned j=0; j<varContains[ n ].size(); j++ ){ + Node v = varContains[ n ][j]; + if( patterns[v].size()==1 ){ + keepPattern = true; + break; + } + } + if( !keepPattern ){ + //remove from pattern vector for( unsigned j=0; j<varContains[ n ].size(); j++ ){ Node v = varContains[ n ][j]; - if( patterns[v].size()==1 ){ - keepPattern = true; - break; - } - } - if( !keepPattern ){ - //remove from pattern vector - for( unsigned j=0; j<varContains[ n ].size(); j++ ){ - Node v = varContains[ n ][j]; - for( unsigned k=0; k<patterns[v].size(); k++ ){ - if( patterns[v][k]==n ){ - patterns[v].erase( patterns[v].begin() + k, patterns[v].begin() + k + 1 ); - break; - } + for( unsigned k=0; k<patterns[v].size(); k++ ){ + if( patterns[v][k]==n ){ + patterns[v].erase( patterns[v].begin() + k, patterns[v].begin() + k + 1 ); + break; } } - //remove from trigger nodes - trNodes.erase( trNodes.begin() + i, trNodes.begin() + i + 1 ); - i--; } + //remove from trigger nodes + trNodes.erase( trNodes.begin() + i, trNodes.begin() + i + 1 ); + i--; } } + } + return true; +} + +Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, bool keepAll, int trOption, unsigned use_n_vars ){ + std::vector< Node > trNodes; + if( !keepAll ){ + unsigned n_vars = use_n_vars==0 ? f[0].getNumChildren() : use_n_vars; + if( !mkTriggerTerms( f, nodes, n_vars, trNodes ) ){ + return NULL; + } }else{ trNodes.insert( trNodes.begin(), nodes.begin(), nodes.end() ); } @@ -211,15 +222,15 @@ Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >& } } } - Trigger* t = new Trigger( qe, f, trNodes, matchOption ); + Trigger* t = new Trigger( qe, f, trNodes ); qe->getTriggerDatabase()->addTrigger( trNodes, t ); return t; } -Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, Node n, int matchOption, bool keepAll, int trOption ){ +Trigger* Trigger::mkTrigger( QuantifiersEngine* qe, Node f, Node n, bool keepAll, int trOption, unsigned use_n_vars ){ std::vector< Node > nodes; nodes.push_back( n ); - return mkTrigger( qe, f, nodes, matchOption, keepAll, trOption ); + return mkTrigger( qe, f, nodes, keepAll, trOption, use_n_vars ); } bool Trigger::isUsable( Node n, Node q ){ @@ -273,7 +284,7 @@ bool Trigger::isUsableEqTerms( Node q, Node n1, Node n2 ) { return true; } } - }else if( isAtomicTrigger( n1 ) && isUsable( n1, q ) ){ + }else if( isUsableAtomicTrigger( n1, q ) ){ if( options::relationalTriggers() && n2.getKind()==INST_CONSTANT && !quantifiers::TermDb::containsTerm( n1, n2 ) ){ return true; }else if( !quantifiers::TermDb::hasInstConstAttr(n2) ){ @@ -328,24 +339,25 @@ Node Trigger::getIsUsableTrigger( Node n, Node q ) { return rtr2; } }else{ - bool usable = quantifiers::TermDb::getInstConstAttr(n)==q && isAtomicTrigger( n ) && isUsable( n, q ); - Trace("trigger-debug") << n << " usable : " << (quantifiers::TermDb::getInstConstAttr(n)==q) << " " << isAtomicTrigger( n ) << " " << isUsable( n, q ) << std::endl; - if( usable ){ + Trace("trigger-debug") << n << " usable : " << ( quantifiers::TermDb::getInstConstAttr(n)==q ) << " " << isAtomicTrigger( n ) << " " << isUsable( n, q ) << std::endl; + if( isUsableAtomicTrigger( n, q ) ){ return pol ? n : NodeManager::currentNM()->mkNode( IFF, n, NodeManager::currentNM()->mkConst( true ) ).notNode(); } } return Node::null(); } +bool Trigger::isUsableAtomicTrigger( Node n, Node q ) { + return quantifiers::TermDb::getInstConstAttr( n )==q && isAtomicTrigger( n ) && isUsable( n, q ); +} + bool Trigger::isUsableTrigger( Node n, Node q ){ Node nu = getIsUsableTrigger( n, q ); return !nu.isNull(); } bool Trigger::isAtomicTrigger( Node n ){ - Kind k = n.getKind(); - return ( k==APPLY_UF && !n.getOperator().getAttribute(NoMatchAttribute()) ) || - ( k!=APPLY_UF && isAtomicTriggerKind( k ) ); + return isAtomicTriggerKind( n.getKind() ); } bool Trigger::isAtomicTriggerKind( Kind k ) { @@ -363,8 +375,13 @@ bool Trigger::isRelationalTriggerKind( Kind k ) { } bool Trigger::isCbqiKind( Kind k ) { - return quantifiers::TermDb::isBoolConnective( k ) || k==PLUS || k==GEQ || k==EQUAL || k==MULT || - k==APPLY_CONSTRUCTOR || k==APPLY_SELECTOR_TOTAL || k==APPLY_TESTER; + if( quantifiers::TermDb::isBoolConnective( k ) || k==PLUS || k==GEQ || k==EQUAL || k==MULT ){ + return true; + }else{ + //CBQI typically works for satisfaction-complete theories + TheoryId t = kindToTheoryId( k ); + return t==THEORY_BV || t==THEORY_DATATYPES; + } } bool Trigger::isSimpleTrigger( Node n ){ diff --git a/src/theory/quantifiers/trigger.h b/src/theory/quantifiers/trigger.h index 41f2a1c38..a3da4d398 100644..100755 --- a/src/theory/quantifiers/trigger.h +++ b/src/theory/quantifiers/trigger.h @@ -76,6 +76,8 @@ class Trigger { int addTerm( Node t ); /** return whether this is a multi-trigger */ bool isMultiTrigger() { return d_nodes.size()>1; } + /** get inst pattern list */ + Node getInstPattern(); /** add all available instantiations exhaustively, in any equivalence class if limitInst>0, limitInst is the max # of instantiations to try */ @@ -84,8 +86,6 @@ class Trigger { ie : quantifier engine; f : forall something .... nodes : (multi-)trigger - matchOption : which policy to use for creating matches - (one of InstMatchGenerator::MATCH_GEN_* ) keepAll: don't remove unneeded patterns; trOption : policy for dealing with triggers that already existed (see below) @@ -95,18 +95,19 @@ class Trigger { TR_GET_OLD, //return a previous trigger if it had already been created TR_RETURN_NULL //return null if a duplicate is found }; - static Trigger* mkTrigger( QuantifiersEngine* qe, Node f, - std::vector< Node >& nodes, int matchOption = 0, - bool keepAll = true, int trOption = TR_MAKE_NEW ); - static Trigger* mkTrigger( QuantifiersEngine* qe, Node f, Node n, - int matchOption = 0, bool keepAll = true, - int trOption = TR_MAKE_NEW ); + //nodes input, trNodes output + static bool mkTriggerTerms( Node q, std::vector< Node >& nodes, unsigned n_vars, std::vector< Node >& trNodes ); + static Trigger* mkTrigger( QuantifiersEngine* qe, Node f, std::vector< Node >& nodes, + bool keepAll = true, int trOption = TR_MAKE_NEW, unsigned use_n_vars = 0 ); + static Trigger* mkTrigger( QuantifiersEngine* qe, Node f, Node n, bool keepAll = true, + int trOption = TR_MAKE_NEW, unsigned use_n_vars = 0 ); static void collectPatTerms( Node q, Node n, std::vector< Node >& patTerms, quantifiers::TriggerSelMode tstrt, std::vector< Node >& exclude, std::map< Node, TriggerTermInfo >& tinfo, bool filterInst = false ); /** is usable trigger */ static bool isUsableTrigger( Node n, Node q ); static Node getIsUsableTrigger( Node n, Node q ); + static bool isUsableAtomicTrigger( Node n, Node q ); static bool isAtomicTrigger( Node n ); static bool isAtomicTriggerKind( Kind k ); static bool isRelationalTrigger( Node n ); @@ -135,7 +136,7 @@ class Trigger { } private: /** trigger constructor */ - Trigger( QuantifiersEngine* ie, Node f, std::vector< Node >& nodes, int matchOption = 0 ); + Trigger( QuantifiersEngine* ie, Node f, std::vector< Node >& nodes ); /** is subterm of trigger usable */ static bool isUsable( Node n, Node q ); diff --git a/src/theory/quantifiers_engine.cpp b/src/theory/quantifiers_engine.cpp index 6d3b17254..d81efe1da 100644 --- a/src/theory/quantifiers_engine.cpp +++ b/src/theory/quantifiers_engine.cpp @@ -57,6 +57,7 @@ using namespace CVC4::theory::inst; QuantifiersEngine::QuantifiersEngine(context::Context* c, context::UserContext* u, TheoryEngine* te): d_te( te ), + d_conflict_c(c, false), //d_quants(u), d_quants_red(u), d_lemmas_produced_c(u), @@ -88,7 +89,6 @@ QuantifiersEngine::QuantifiersEngine(context::Context* c, context::UserContext* d_tr_trie = new inst::TriggerTrie; d_curr_effort_level = QEFFORT_NONE; d_conflict = false; - d_num_added_lemmas_round = 0; d_hasAddedLemma = false; //don't add true lemma d_lemmas_produced_c[d_term_db->d_true] = true; @@ -352,7 +352,7 @@ void QuantifiersEngine::check( Theory::Effort e ){ std::vector< QuantifiersModule* > qm; if( d_model->checkNeeded() ){ needsCheck = needsCheck || e>=Theory::EFFORT_LAST_CALL; //always need to check at or above last call - for( int i=0; i<(int)d_modules.size(); i++ ){ + for( unsigned i=0; i<d_modules.size(); i++ ){ if( d_modules[i]->needsCheck( e ) ){ qm.push_back( d_modules[i] ); needsCheck = true; @@ -366,7 +366,6 @@ void QuantifiersEngine::check( Theory::Effort e ){ } d_conflict = false; - d_num_added_lemmas_round = 0; d_hasAddedLemma = false; bool setIncomplete = false; if( e==Theory::EFFORT_LAST_CALL ){ @@ -380,6 +379,8 @@ void QuantifiersEngine::check( Theory::Effort e ){ Trace("quant-engine-debug2") << "Quantifiers Engine call to check, level = " << e << ", needsCheck=" << needsCheck << std::endl; if( needsCheck ){ + //this will fail if a set of instances is marked as a conflict, but is not + Assert( !d_conflict_c.get() ); //flush previous lemmas (for instance, if was interupted), or other lemmas to process flushLemmas(); if( d_hasAddedLemma ){ @@ -393,6 +394,12 @@ void QuantifiersEngine::check( Theory::Effort e ){ } d_recorded_inst.clear(); } + + double clSet = 0; + if( Trace.isOn("quant-engine") ){ + clSet = double(clock())/double(CLOCKS_PER_SEC); + Trace("quant-engine") << ">>>>> Quantifiers Engine Round, effort = " << e << " <<<<<" << std::endl; + } if( Trace.isOn("quant-engine-debug") ){ Trace("quant-engine-debug") << "Quantifiers Engine check, level = " << e << std::endl; @@ -453,7 +460,6 @@ void QuantifiersEngine::check( Theory::Effort e ){ flushLemmas(); if( d_hasAddedLemma ){ return; - } if( e==Theory::EFFORT_LAST_CALL ){ @@ -545,6 +551,13 @@ void QuantifiersEngine::check( Theory::Effort e ){ } } } + if( Trace.isOn("quant-engine") ){ + double clSet2 = double(clock())/double(CLOCKS_PER_SEC); + Trace("quant-engine") << "Finished quantifiers engine, total time = " << (clSet2-clSet); + Trace("quant-engine") << ", added lemma = " << d_hasAddedLemma; + Trace("quant-engine") << std::endl; + } + Trace("quant-engine-debug2") << "Finished quantifiers engine check." << std::endl; }else{ Trace("quant-engine-debug2") << "Quantifiers Engine does not need check." << std::endl; @@ -564,7 +577,7 @@ void QuantifiersEngine::check( Theory::Effort e ){ } } if( setIncomplete ){ - Trace("quant-engine-debug") << "Set incomplete flag." << std::endl; + Trace("quant-engine") << "Set incomplete flag." << std::endl; getOutputChannel().setIncomplete(); } //output debug stats @@ -621,6 +634,7 @@ bool QuantifiersEngine::registerQuantifier( Node f ){ //check whether we should apply a reduction if( reduceQuantifier( f ) ){ + Trace("quant") << "...reduced." << std::endl; d_quants[f] = false; return false; }else{ @@ -628,6 +642,7 @@ bool QuantifiersEngine::registerQuantifier( Node f ){ d_term_db->makeInstantiationConstantsFor( f ); d_term_db->computeAttributes( f ); for( unsigned i=0; i<d_modules.size(); i++ ){ + Trace("quant-debug") << "pre-register with " << d_modules[i]->identify() << "..." << std::endl; d_modules[i]->preRegisterQuantifier( f ); } QuantifiersModule * qm = getOwner( f ); @@ -640,11 +655,11 @@ bool QuantifiersEngine::registerQuantifier( Node f ){ } //register with each module for( unsigned i=0; i<d_modules.size(); i++ ){ + Trace("quant-debug") << "register with " << d_modules[i]->identify() << "..." << std::endl; d_modules[i]->registerQuantifier( f ); } + //TODO: remove this Node ceBody = d_term_db->getInstConstantBody( f ); - //generate the phase requirements - //d_phase_reqs[f] = new QuantPhaseReq( ceBody, true ); //also register it with the strong solver //if( options::finiteModelFind() ){ // ((uf::TheoryUF*)d_te->theoryOf( THEORY_UF ))->getStrongSolver()->registerQuantifier( f ); @@ -874,8 +889,9 @@ Node QuantifiersEngine::getSubstitute( Node n, std::vector< Node >& terms ){ Node QuantifiersEngine::getInstantiation( Node q, std::vector< Node >& vars, std::vector< Node >& terms, bool doVts ){ Node body; + Assert( vars.size()==terms.size() ); //process partial instantiation if necessary - if( d_term_db->d_vars[q].size()!=vars.size() ){ + if( q[0].getNumChildren()!=vars.size() ){ body = q[ 1 ].substitute( vars.begin(), vars.end(), terms.begin(), terms.end() ); std::vector< Node > uninst_vars; //doing a partial instantiation, must add quantifier for all uninstantiated variables @@ -884,6 +900,8 @@ Node QuantifiersEngine::getInstantiation( Node q, std::vector< Node >& vars, std uninst_vars.push_back( q[0][i] ); } } + Trace("partial-inst") << "Partially instantiating with " << vars.size() << " / " << q[0].getNumChildren() << " for " << q << std::endl; + Assert( !uninst_vars.empty() ); Node bvl = NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, uninst_vars ); body = NodeManager::currentNM()->mkNode( FORALL, bvl, body ); Trace("partial-inst") << "Partial instantiation : " << q << std::endl; @@ -922,6 +940,7 @@ Node QuantifiersEngine::getInstantiation( Node q, InstMatch& m, bool doVts ){ } Node QuantifiersEngine::getInstantiation( Node q, std::vector< Node >& terms, bool doVts ) { + Assert( d_term_db->d_vars.find( q )!=d_term_db->d_vars.end() ); return getInstantiation( q, d_term_db->d_vars[q], terms, doVts ); } @@ -956,7 +975,6 @@ bool QuantifiersEngine::addLemma( Node lem, bool doCache, bool doRewrite ){ d_lemmas_produced_c[ lem ] = true; d_lemmas_waiting.push_back( lem ); Trace("inst-add-debug") << "Added lemma" << std::endl; - d_num_added_lemmas_round++; return true; }else{ Trace("inst-add-debug") << "Duplicate." << std::endl; @@ -965,7 +983,6 @@ bool QuantifiersEngine::addLemma( Node lem, bool doCache, bool doRewrite ){ }else{ //do not need to rewrite, will be rewritten after sending d_lemmas_waiting.push_back( lem ); - d_num_added_lemmas_round++; return true; } } @@ -997,6 +1014,7 @@ bool QuantifiersEngine::addInstantiation( Node q, std::vector< Node >& terms, bo Assert( !d_conflict ); Assert( terms.size()==q[0].getNumChildren() ); Trace("inst-add-debug") << "For quantified formula " << q << ", add instantiation: " << std::endl; + std::vector< Node > rlv_cond; for( unsigned i=0; i<terms.size(); i++ ){ Trace("inst-add-debug") << " " << q[0][i]; Trace("inst-add-debug2") << " -> " << terms[i]; @@ -1014,20 +1032,29 @@ bool QuantifiersEngine::addInstantiation( Node q, std::vector< Node >& terms, bo if( terms[i].isNull() ){ Trace("inst-add-debug") << " --> Failed to make term vector, due to term/type restrictions." << std::endl; return false; + }else{ + //get relevancy conditions + if( options::instRelevantCond() ){ + quantifiers::TermDb::getRelevancyCondition( terms[i], rlv_cond ); + } } #ifdef CVC4_ASSERTIONS bool bad_inst = false; if( quantifiers::TermDb::containsUninterpretedConstant( terms[i] ) ){ + Trace("inst") << "***& inst contains uninterpreted constant : " << terms[i] << std::endl; bad_inst = true; }else if( !terms[i].getType().isSubtypeOf( q[0][i].getType() ) ){ + Trace("inst") << "***& inst bad type : " << terms[i] << " " << terms[i].getType() << "/" << q[0][i].getType() << std::endl; bad_inst = true; }else if( options::cbqi() ){ Node icf = quantifiers::TermDb::getInstConstAttr(terms[i]); if( !icf.isNull() ){ if( icf==q ){ + Trace("inst") << "***& inst contains inst constant attr : " << terms[i] << std::endl; + bad_inst = true; + }else if( quantifiers::TermDb::containsTerms( terms[i], d_term_db->d_inst_constants[q] ) ){ + Trace("inst") << "***& inst contains inst constants : " << terms[i] << std::endl; bad_inst = true; - }else{ - bad_inst = quantifiers::TermDb::containsTerms( terms[i], d_term_db->d_inst_constants[q] ); } } } @@ -1079,13 +1106,21 @@ bool QuantifiersEngine::addInstantiation( Node q, std::vector< Node >& terms, bo Trace("inst-add-debug") << "Constructing instantiation..." << std::endl; Assert( d_term_db->d_vars[q].size()==terms.size() ); Node body = getInstantiation( q, d_term_db->d_vars[q], terms, doVts ); //do virtual term substitution + Node orig_body = body; body = quantifiers::QuantifiersRewriter::preprocess( body, true ); Trace("inst-debug") << "...preprocess to " << body << std::endl; //construct the lemma Trace("inst-assert") << "(assert " << body << ")" << std::endl; body = Rewriter::rewrite(body); - Node lem = NodeManager::currentNM()->mkNode( kind::OR, q.negate(), body ); + Node lem; + if( rlv_cond.empty() ){ + lem = NodeManager::currentNM()->mkNode( kind::OR, q.negate(), body ); + }else{ + rlv_cond.push_back( q.negate() ); + rlv_cond.push_back( body ); + lem = NodeManager::currentNM()->mkNode( kind::OR, rlv_cond ); + } lem = Rewriter::rewrite(lem); //check for lemma duplication @@ -1106,15 +1141,20 @@ bool QuantifiersEngine::addInstantiation( Node q, std::vector< Node >& terms, bo } } if( options::instMaxLevel()!=-1 ){ - uint64_t maxInstLevel = 0; - for( unsigned i=0; i<terms.size(); i++ ){ - if( terms[i].hasAttribute(InstLevelAttribute()) ){ - if( terms[i].getAttribute(InstLevelAttribute())>maxInstLevel ){ - maxInstLevel = terms[i].getAttribute(InstLevelAttribute()); + if( doVts ){ + //virtual term substitution/instantiation level features are incompatible + Assert( false ); + }else{ + uint64_t maxInstLevel = 0; + for( unsigned i=0; i<terms.size(); i++ ){ + if( terms[i].hasAttribute(InstLevelAttribute()) ){ + if( terms[i].getAttribute(InstLevelAttribute())>maxInstLevel ){ + maxInstLevel = terms[i].getAttribute(InstLevelAttribute()); + } } } + setInstantiationLevelAttr( orig_body, q[1], maxInstLevel+1 ); } - setInstantiationLevelAttr( body, q[1], maxInstLevel+1 ); } if( d_curr_effort_level>QEFFORT_CONFLICT && d_curr_effort_level<QEFFORT_NONE ){ //notify listeners @@ -1122,6 +1162,7 @@ bool QuantifiersEngine::addInstantiation( Node q, std::vector< Node >& terms, bo if( !d_inst_notify[j]->notifyInstantiation( d_curr_effort_level, q, lem, terms, body ) ){ Trace("inst-add-debug") << "...we are in conflict." << std::endl; d_conflict = true; + d_conflict_c = true; Assert( !d_lemmas_waiting.empty() ); break; } @@ -1205,9 +1246,19 @@ quantifiers::UserPatMode QuantifiersEngine::getInstUserPatMode() { void QuantifiersEngine::flushLemmas(){ if( !d_lemmas_waiting.empty() ){ + //filter based on notify classes + if( !d_inst_notify.empty() ){ + unsigned prev_lem_sz = d_lemmas_waiting.size(); + for( unsigned j=0; j<d_inst_notify.size(); j++ ){ + d_inst_notify[j]->filterInstantiations(); + } + if( prev_lem_sz!=d_lemmas_waiting.size() ){ + Trace("quant-engine") << "...filtered instances : " << d_lemmas_waiting.size() << " / " << prev_lem_sz << std::endl; + } + } //take default output channel if none is provided d_hasAddedLemma = true; - for( int i=0; i<(int)d_lemmas_waiting.size(); i++ ){ + for( unsigned i=0; i<d_lemmas_waiting.size(); i++ ){ Trace("qe-lemma") << "Lemma : " << d_lemmas_waiting[i] << std::endl; getOutputChannel().lemma( d_lemmas_waiting[i], false, true ); } @@ -1403,7 +1454,7 @@ bool EqualityQueryQuantifiersEngine::processInferences( Theory::Effort e ) { Trace("quant-engine-ee-proc") << " explanation : " << eq_exp << std::endl; Assert( ee->hasTerm( eq[0] ) ); Assert( ee->hasTerm( eq[1] ) ); - if( ee->areDisequal( eq[0], eq[1], false ) ){ + if( areDisequal( eq[0], eq[1] ) ){ Trace("quant-engine-ee-proc") << "processInferences : Conflict : " << eq << std::endl; if( Trace.isOn("term-db-lemma") ){ Trace("term-db-lemma") << "Disequal terms, equal by normalization : " << eq[0] << " " << eq[1] << "!!!!" << std::endl; @@ -1445,11 +1496,10 @@ bool EqualityQueryQuantifiersEngine::areEqual( Node a, Node b ){ }else{ eq::EqualityEngine* ee = getEngine(); if( ee->hasTerm( a ) && ee->hasTerm( b ) ){ - if( ee->areEqual( a, b ) ){ - return true; - } + return ee->areEqual( a, b ); + }else{ + return false; } - return false; } } @@ -1459,11 +1509,10 @@ bool EqualityQueryQuantifiersEngine::areDisequal( Node a, Node b ){ }else{ eq::EqualityEngine* ee = getEngine(); if( ee->hasTerm( a ) && ee->hasTerm( b ) ){ - if( ee->areDisequal( a, b, false ) ){ - return true; - } + return ee->areDisequal( a, b, false ); + }else{ + return a.isConst() && b.isConst(); } - return false; } } diff --git a/src/theory/quantifiers_engine.h b/src/theory/quantifiers_engine.h index 4ee66f9e7..1f4a04218 100644 --- a/src/theory/quantifiers_engine.h +++ b/src/theory/quantifiers_engine.h @@ -48,6 +48,7 @@ class InstantiationNotify { public: InstantiationNotify(){} virtual bool notifyInstantiation( unsigned quant_e, Node q, Node lem, std::vector< Node >& terms, Node body ) = 0; + virtual void filterInstantiations() = 0; }; namespace quantifiers { @@ -156,8 +157,7 @@ private: //this information is reset during check unsigned d_curr_effort_level; /** are we in conflict */ bool d_conflict; - /** number of lemmas we actually added this round (for debugging) */ - unsigned d_num_added_lemmas_round; + context::CDO< bool > d_conflict_c; /** has added lemma this round */ bool d_hasAddedLemma; private: @@ -295,9 +295,9 @@ private: bool removeInstantiationInternal( Node q, std::vector< Node >& terms ); /** set instantiation level attr */ static void setInstantiationLevelAttr( Node n, Node qn, uint64_t level ); +public: /** flush lemmas */ void flushLemmas(); -public: /** get instantiation */ Node getInstantiation( Node q, std::vector< Node >& vars, std::vector< Node >& terms, bool doVts = false ); /** get instantiation */ @@ -330,8 +330,6 @@ public: bool inConflict() { return d_conflict; } /** get number of waiting lemmas */ unsigned getNumLemmasWaiting() { return d_lemmas_waiting.size(); } - /** get number of waiting lemmas */ - unsigned getNumLemmasAddedThisRound() { return d_num_added_lemmas_round; } /** get needs check */ bool getInstWhenNeedsCheck( Theory::Effort e ); /** get user pat mode */ diff --git a/src/theory/rep_set.cpp b/src/theory/rep_set.cpp index d7178a8c1..184553ba7 100644 --- a/src/theory/rep_set.cpp +++ b/src/theory/rep_set.cpp @@ -127,19 +127,11 @@ int RepSet::getNumRelevantGroundReps( TypeNode t ) { } void RepSet::toStream(std::ostream& out){ -#if 0 - for( std::map< TypeNode, std::vector< Node > >::iterator it = d_type_reps.begin(); it != d_type_reps.end(); ++it ){ - out << it->first << " : " << std::endl; - for( int i=0; i<(int)it->second.size(); i++ ){ - out << " " << i << ": " << it->second[i] << std::endl; - } - } -#else for( std::map< TypeNode, std::vector< Node > >::iterator it = d_type_reps.begin(); it != d_type_reps.end(); ++it ){ if( !it->first.isFunction() && !it->first.isPredicate() ){ out << "(" << it->first << " " << it->second.size(); out << " ("; - for( int i=0; i<(int)it->second.size(); i++ ){ + for( unsigned i=0; i<it->second.size(); i++ ){ if( i>0 ){ out << " "; } out << it->second[i]; } @@ -147,7 +139,6 @@ void RepSet::toStream(std::ostream& out){ out << ")" << std::endl; } } -#endif } @@ -157,10 +148,11 @@ RepSetIterator::RepSetIterator( QuantifiersEngine * qe, RepSet* rs ) : d_qe(qe), int RepSetIterator::domainSize( int i ) { Assert(i>=0); - if( d_enum_type[i]==ENUM_DOMAIN_ELEMENTS ){ - return d_domain[i].size(); + int v = d_var_order[i]; + if( d_enum_type[v]==ENUM_DOMAIN_ELEMENTS ){ + return d_domain[v].size(); }else{ - return d_domain[i][0]; + return d_domain[v][0]; } } @@ -188,15 +180,15 @@ bool RepSetIterator::setFunctionDomain( Node op ){ bool RepSetIterator::initialize(){ Trace("rsi") << "Initialize rep set iterator..." << std::endl; - for( size_t i=0; i<d_types.size(); i++ ){ + for( unsigned v=0; v<d_types.size(); v++ ){ d_index.push_back( 0 ); //store default index order - d_index_order.push_back( i ); - d_var_order[i] = i; + d_index_order.push_back( v ); + d_var_order[v] = v; //store default domain d_domain.push_back( RepDomain() ); - TypeNode tn = d_types[i]; - Trace("rsi") << "Var #" << i << " is type " << tn << "..." << std::endl; + TypeNode tn = d_types[v]; + Trace("rsi") << "Var #" << v << " is type " << tn << "..." << std::endl; if( tn.isSort() ){ //must ensure uninterpreted type is non-empty. if( !d_rep_set->hasType( tn ) ){ @@ -209,59 +201,59 @@ bool RepSetIterator::initialize(){ Trace("mkVar") << "RepSetIterator:: Make variable " << var << " : " << tn << std::endl; d_rep_set->add( tn, var ); } - }else if( tn.isInteger() ){ - bool inc = false; - //check if it is bound + }else{ + bool inc = true; + //check if it is bound by bounded integer module if( d_owner.getKind()==FORALL && d_qe && d_qe->getBoundedIntegers() ){ - if( d_qe->getBoundedIntegers()->isBoundVar( d_owner, d_owner[0][i] ) ){ + unsigned bvt = d_qe->getBoundedIntegers()->getBoundVarType( d_owner, d_owner[0][v] ); + if( bvt==quantifiers::BoundedIntegers::BOUND_INT_RANGE ){ Trace("rsi") << " variable is bounded integer." << std::endl; - d_enum_type.push_back( ENUM_RANGE ); - }else{ - inc = true; + d_enum_type.push_back( ENUM_INT_RANGE ); + inc = false; + }else if( bvt==quantifiers::BoundedIntegers::BOUND_SET_MEMBER ){ + Trace("rsi") << " variable is bounded by set membership." << std::endl; + d_enum_type.push_back( ENUM_SET_MEMBERS ); + inc = false; } - }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() ){ + if( d_bounds[0].find( v )!=d_bounds[0].end() && d_bounds[1].find( v )!=d_bounds[1].end() ){ Trace("rsi") << " variable is bounded." << std::endl; - d_enum_type.push_back( ENUM_RANGE ); + d_enum_type.push_back( ENUM_INT_RANGE ); + }else if( d_qe->getTermDatabase()->mayComplete( tn ) ){ + Trace("rsi") << " do complete, since cardinality is small (" << tn.getCardinality() << ")..." << std::endl; + d_rep_set->complete( tn ); + //must have succeeded + Assert( d_rep_set->hasType( tn ) ); }else{ Trace("rsi") << " variable cannot be bounded." << std::endl; - Trace("fmf-incomplete") << "Incomplete because of integer quantification of " << d_owner[0][i] << "." << std::endl; + Trace("fmf-incomplete") << "Incomplete because of quantification of type " << tn << std::endl; d_incomplete = true; } } - //enumerate if the sort is reasonably small - }else if( d_qe->getTermDatabase()->mayComplete( tn ) ){ - Trace("rsi") << " do complete, since cardinality is small (" << tn.getCardinality() << ")..." << std::endl; - d_rep_set->complete( tn ); - //must have succeeded - Assert( d_rep_set->hasType( tn ) ); - }else{ - Trace("rsi") << " variable cannot be bounded." << std::endl; - Trace("fmf-incomplete") << "Incomplete because of quantification of type " << tn << std::endl; - d_incomplete = true; } //if we have yet to determine the type of enumeration - if( d_enum_type.size()<=i ){ + if( d_enum_type.size()<=v ){ 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 ); + for( unsigned j=0; j<d_rep_set->d_type_reps[tn].size(); j++ ){ + d_domain[v].push_back( j ); } }else{ + Assert( d_incomplete ); return false; } } } //must set a variable index order based on bounded integers - if (d_owner.getKind()==FORALL && d_qe && d_qe->getBoundedIntegers()) { + 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_qe->getBoundedIntegers()->getNumBoundVars( d_owner ); i++ ){ + Node v = d_qe->getBoundedIntegers()->getBoundVar( d_owner, i ); + Trace("bound-int-rsi") << " bound var #" << i << " is " << v << std::endl; + varOrder.push_back( d_qe->getTermDatabase()->getVariableNum( d_owner, v ) ); } for( unsigned i=0; i<d_owner[0].getNumChildren(); i++) { if( !d_qe->getBoundedIntegers()->isBoundVar(d_owner, d_owner[0][i])) { @@ -283,14 +275,10 @@ bool RepSetIterator::initialize(){ Trace("bound-int-rsi") << indexOrder[i] << " "; } Trace("bound-int-rsi") << std::endl; - setIndexOrder(indexOrder); + setIndexOrder( indexOrder ); } //now reset the indices - for (unsigned i=0; i<d_index.size(); i++) { - if (!resetIndex(i, true)){ - break; - } - } + do_reset_increment( -1, true ); return true; } @@ -298,46 +286,40 @@ void RepSetIterator::setIndexOrder( std::vector< int >& indexOrder ){ d_index_order.clear(); d_index_order.insert( d_index_order.begin(), indexOrder.begin(), indexOrder.end() ); //make the d_var_order mapping - for( int i=0; i<(int)d_index_order.size(); i++ ){ + for( unsigned i=0; i<d_index_order.size(); i++ ){ d_var_order[d_index_order[i]] = i; } } -/* -void RepSetIterator::setDomain( std::vector< RepDomain >& domain ){ - d_domain.clear(); - d_domain.insert( d_domain.begin(), domain.begin(), domain.end() ); - //we are done if a domain is empty - for( int i=0; i<(int)d_domain.size(); i++ ){ - if( d_domain[i].empty() ){ - d_index.clear(); - } - } -} -*/ -bool RepSetIterator::resetIndex( int i, bool initial ) { + +int 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; + int v = d_var_order[i]; + Trace("bound-int-rsi") << "Reset " << i << ", var order = " << v << ", initial = " << 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; + if( initial || ( d_owner.getKind()==FORALL && d_qe && d_qe->getBoundedIntegers() && + d_qe->getBoundedIntegers()->isBoundVar( d_owner, d_owner[0][v] ) && + !d_qe->getBoundedIntegers()->isGroundRange( d_owner, d_owner[0][v] ) ) ){ + Trace("bound-int-rsi") << "Getting range of " << d_owner[0][v] << std::endl; + if( d_enum_type[v]==ENUM_INT_RANGE ){ Node actual_l; 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 ); + if( d_qe->getBoundedIntegers() ){ + unsigned bvt = d_qe->getBoundedIntegers()->getBoundVarType( d_owner, d_owner[0][v] ); + if( bvt==quantifiers::BoundedIntegers::BOUND_INT_RANGE ){ + d_qe->getBoundedIntegers()->getBoundValues( d_owner, d_owner[0][v], 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>()) ){ + if( d_bounds[b].find(v)!=d_bounds[b].end() ){ + Trace("bound-int-rsi") << "May further limit bound(" << b << ") based on " << d_bounds[b][v] << std::endl; + if( b==0 && (l.isNull() || d_bounds[b][v].getConst<Rational>() > l.getConst<Rational>()) ){ if( !l.isNull() ){ //bound was limited externally, record that the value lower bound is not equal to the term lower bound - actual_l = NodeManager::currentNM()->mkNode( MINUS, d_bounds[b][ii], l ); + actual_l = NodeManager::currentNM()->mkNode( MINUS, d_bounds[b][v], l ); } - 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], + l = d_bounds[b][v]; + }else if( b==1 && (u.isNull() || d_bounds[b][v].getConst<Rational>() <= u.getConst<Rational>()) ){ + u = NodeManager::currentNM()->mkNode( MINUS, d_bounds[b][v], NodeManager::currentNM()->mkConst( Rational(1) ) ); u = Rewriter::rewrite( u ); } @@ -346,73 +328,109 @@ bool RepSetIterator::resetIndex( int i, bool initial ) { if( l.isNull() || u.isNull() ){ //failed, abort the iterator - d_index.clear(); - return false; + return -1; }else{ - Trace("bound-int-rsi") << "Can limit bounds of " << d_owner[0][ii] << " to " << l << "..." << u << std::endl; + Trace("bound-int-rsi") << "Can limit bounds of " << d_owner[0][v] << " 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(); + d_domain[v].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 ); + if( d_qe->getBoundedIntegers() && d_qe->getBoundedIntegers()->isBoundVar( d_owner, d_owner[0][v] ) ){ + d_qe->getBoundedIntegers()->getBounds( d_owner, d_owner[0][v], this, tl, tu ); } - d_lower_bounds[ii] = tl; + d_lower_bounds[v] = tl; if( !actual_l.isNull() ){ //if bound was limited externally, must consider the offset - d_lower_bounds[ii] = Rewriter::rewrite( NodeManager::currentNM()->mkNode( PLUS, tl, actual_l ) ); + d_lower_bounds[v] = Rewriter::rewrite( NodeManager::currentNM()->mkNode( PLUS, tl, actual_l ) ); } - if( ra==NodeManager::currentNM()->mkConst(true) ){ + if( ra==d_qe->getTermDatabase()->d_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 ); + d_domain[v].push_back( (int)rr ); + if( rr<=0 ){ + return 0; + } }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 ); + Trace("fmf-incomplete") << "Incomplete because of integer quantification, bounds are too big for " << d_owner[0][v] << "." << std::endl; + return -1; } } - }else{ - Trace("bound-int-rsi") << d_owner[0][ii] << " has ground range, skip." << std::endl; + }else if( d_enum_type[v]==ENUM_SET_MEMBERS ){ + Assert( d_qe->getBoundedIntegers()->getBoundVarType( d_owner, d_owner[0][v] )==quantifiers::BoundedIntegers::BOUND_SET_MEMBER ); + Node srv = d_qe->getBoundedIntegers()->getSetRangeValue( d_owner, d_owner[0][v], this ); + if( srv.isNull() ){ + return -1; + } + Trace("bound-int-rsi") << "Bounded by set membership : " << srv << std::endl; + d_domain[v].clear(); + d_setm_bounds[v].clear(); + if( srv.getKind()!=EMPTYSET ){ + //TODO: need term model, not value model + while( srv.getKind()==UNION ){ + Assert( srv[1].getKind()==kind::SINGLETON ); + d_setm_bounds[v].push_back( srv[1][0] ); + srv = srv[0]; + } + d_setm_bounds[v].push_back( srv[0] ); + d_domain[v].push_back( d_setm_bounds[v].size() ); + }else{ + d_domain[v].push_back( 0 ); + return 0; + } } } - return true; + return 1; } -int RepSetIterator::increment2( int counter ){ +int RepSetIterator::increment2( int i ){ Assert( !isFinished() ); #ifdef DISABLE_EVAL_SKIP_MULTIPLE - counter = (int)d_index.size()-1; + i = (int)d_index.size()-1; #endif //increment d_index - if( counter>=0){ - Trace("rsi-debug") << "domain size of " << counter << " is " << domainSize(counter) << std::endl; + if( i>=0){ + Trace("rsi-debug") << "domain size of " << i << " is " << domainSize(i) << 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; + while( i>=0 && d_index[i]>=(int)(domainSize(i)-1) ){ + i--; + if( i>=0){ + Trace("rsi-debug") << "domain size of " << i << " is " << domainSize(i) << std::endl; } } - if( counter==-1 ){ + if( i==-1 ){ d_index.clear(); + return -1; }else{ - d_index[counter]++; - bool emptyDomain = false; - for( int i=(int)d_index.size()-1; i>counter; i-- ){ - if (!resetIndex(i)){ - break; - }else if( domainSize(i)<=0 ){ - emptyDomain = true; - } + Trace("rsi-debug") << "increment " << i << std::endl; + d_index[i]++; + return do_reset_increment( i ); + } +} + +int RepSetIterator::do_reset_increment( int i, bool initial ) { + bool emptyDomain = false; + for( unsigned ii=(i+1); ii<d_index.size(); ii++ ){ + int ri_res = resetIndex( ii, initial ); + if( ri_res==-1 ){ + //failed + d_index.clear(); + d_incomplete = true; + break; + }else if( ri_res==0 ){ + emptyDomain = true; } + //force next iteration if currently an empty domain if( emptyDomain ){ - Trace("rsi-debug") << "This is an empty domain, increment again." << std::endl; - return increment(); + d_index[ii] = domainSize(ii)-1; } } - return counter; + if( emptyDomain ){ + Trace("rsi-debug") << "This is an empty domain, increment." << std::endl; + return increment(); + }else{ + return i; + } } int RepSetIterator::increment(){ @@ -427,33 +445,38 @@ bool RepSetIterator::isFinished(){ return d_index.empty(); } -Node RepSetIterator::getTerm( int i ){ - Trace("rsi-debug") << "rsi : get term " << i << ", index order = " << d_index_order[i] << std::endl; - //int index = d_index_order[i]; - int index = i; - if( d_enum_type[index]==ENUM_DOMAIN_ELEMENTS ){ - TypeNode tn = d_types[index]; +Node RepSetIterator::getCurrentTerm( int v ){ + Trace("rsi-debug") << "rsi : get term " << v << ", index order = " << d_index_order[v] << std::endl; + int ii = d_index_order[v]; + int curr = d_index[ii]; + if( d_enum_type[v]==ENUM_DOMAIN_ELEMENTS ){ + TypeNode tn = d_types[v]; 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]]]; + Assert( 0<=d_domain[v][curr] && d_domain[v][curr]<(int)d_rep_set->d_type_reps[tn].size() ); + return d_rep_set->d_type_reps[tn][d_domain[v][curr]]; + }else if( d_enum_type[v]==ENUM_SET_MEMBERS ){ + Assert( 0<=curr && curr<(int)d_setm_bounds[v].size() ); + return d_setm_bounds[v][curr]; }else{ - Trace("rsi-debug") << "Get, with offset : " << 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]) ) ); + Trace("rsi-debug") << "Get, with offset : " << v << " " << d_lower_bounds[v] << " " << curr << std::endl; + Assert( !d_lower_bounds[v].isNull() ); + Node t = NodeManager::currentNM()->mkNode(PLUS, d_lower_bounds[v], NodeManager::currentNM()->mkConst( Rational(curr) ) ); t = Rewriter::rewrite( t ); return t; } } void RepSetIterator::debugPrint( const char* c ){ - for( int i=0; i<(int)d_index.size(); i++ ){ - Debug( c ) << i << " : " << d_index[i] << " : " << getTerm( i ) << std::endl; + for( unsigned v=0; v<d_index.size(); v++ ){ + Debug( c ) << v << " : " << getCurrentTerm( v ) << std::endl; } } void RepSetIterator::debugPrintSmall( const char* c ){ Debug( c ) << "RI: "; - for( int i=0; i<(int)d_index.size(); i++ ){ - Debug( c ) << d_index[i] << ": " << getTerm( i ) << " "; + for( unsigned v=0; v<d_index.size(); v++ ){ + Debug( c ) << v << ": " << getCurrentTerm( v ) << " "; } Debug( c ) << std::endl; } + diff --git a/src/theory/rep_set.h b/src/theory/rep_set.h index 08fc7dd52..ee927de86 100644 --- a/src/theory/rep_set.h +++ b/src/theory/rep_set.h @@ -64,22 +64,42 @@ class RepSetIterator { public: enum { ENUM_DOMAIN_ELEMENTS, - ENUM_RANGE, + ENUM_INT_RANGE, + ENUM_SET_MEMBERS, }; private: QuantifiersEngine * d_qe; //initialize function bool initialize(); - //for enum ranges + //for int ranges std::map< int, Node > d_lower_bounds; + //for set ranges + std::map< int, std::vector< Node > > d_setm_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 ); + //reset index, 1:success, 0:empty, -1:fail + int resetIndex( int i, bool initial = false ); /** set index order */ void setIndexOrder( std::vector< int >& indexOrder ); + /** do reset increment the iterator at index=counter */ + int do_reset_increment( int counter, bool initial = false ); + //ordering for variables we are indexing over + // for example, given reps = { a, b } and quantifier forall( x, y, z ) P( x, y, z ) with d_index_order = { 2, 0, 1 }, + // then we consider instantiations in this order: + // a/x a/y a/z + // a/x b/y a/z + // b/x a/y a/z + // b/x b/y a/z + // ... + std::vector< int > d_index_order; + //variables to index they are considered at + // for example, if d_index_order = { 2, 0, 1 } + // then d_var_order = { 0 -> 1, 1 -> 2, 2 -> 0 } + std::map< int, int > d_var_order; + //are we only considering a strict subset of the domain of the quantifier? + bool d_incomplete; public: RepSetIterator( QuantifiersEngine * qe, RepSet* rs ); ~RepSetIterator(){} @@ -92,43 +112,34 @@ public: RepSet* d_rep_set; //enumeration type? std::vector< int > d_enum_type; - //index we are considering + //current tuple we are considering std::vector< int > d_index; //types we are considering std::vector< TypeNode > d_types; //domain we are considering std::vector< RepDomain > d_domain; - //are we only considering a strict subset of the domain of the quantifier? - bool d_incomplete; - //ordering for variables we are indexing over - // for example, given reps = { a, b } and quantifier forall( x, y, z ) P( x, y, z ) with d_index_order = { 2, 0, 1 }, - // then we consider instantiations in this order: - // a/x a/y a/z - // a/x b/y a/z - // b/x a/y a/z - // b/x b/y a/z - // ... - std::vector< int > d_index_order; - //variables to index they are considered at - // for example, if d_index_order = { 2, 0, 1 } - // then d_var_order = { 0 -> 1, 1 -> 2, 2 -> 0 } - std::map< int, int > d_var_order; //intervals std::map< int, Node > d_bounds[2]; public: /** increment the iterator at index=counter */ - int increment2( int counter ); + int increment2( int i ); /** increment the iterator */ int increment(); /** is the iterator finished? */ bool isFinished(); /** get the i_th term we are considering */ - Node getTerm( int i ); + Node getCurrentTerm( int v ); /** get the number of terms we are considering */ int getNumTerms() { return (int)d_index_order.size(); } /** debug print */ void debugPrint( const char* c ); void debugPrintSmall( const char* c ); + //get index order, returns var # + int getIndexOrder( int v ) { return d_index_order[v]; } + //get variable order, returns index # + int getVariableOrder( int i ) { return d_var_order[i]; } + //is incomplete + bool isIncomplete() { return d_incomplete; } };/* class RepSetIterator */ }/* CVC4::theory namespace */ diff --git a/src/theory/sep/kinds b/src/theory/sep/kinds new file mode 100644 index 000000000..6c4ad33db --- /dev/null +++ b/src/theory/sep/kinds @@ -0,0 +1,37 @@ +# kinds -*- sh -*- +# +# For documentation on this file format, please refer to +# src/theory/builtin/kinds. +# + +theory THEORY_SEP ::CVC4::theory::sep::TheorySep "theory/sep/theory_sep.h" +typechecker "theory/sep/theory_sep_type_rules.h" + +properties polite stable-infinite parametric +properties check propagate presolve getNextDecisionRequest + +rewriter ::CVC4::theory::sep::TheorySepRewriter "theory/sep/theory_sep_rewriter.h" + +# constants +constant SEP_NIL_REF \ + ::CVC4::NilRef \ + ::CVC4::NilRefHashFunction \ + "expr/sepconst.h" \ + "the nil reference constant; payload is an instance of the CVC4::NilRef class" + +variable SEP_NIL "separation nil" + +operator SEP_EMP 1 "separation empty heap" +operator SEP_PTO 2 "points to relation" +operator SEP_STAR 2: "separation star" +operator SEP_WAND 2 "separation magic wand" +operator SEP_LABEL 2 "separation label" + +typerule SEP_NIL_REF ::CVC4::theory::sep::SepNilRefTypeRule +typerule SEP_EMP ::CVC4::theory::sep::SepEmpTypeRule +typerule SEP_PTO ::CVC4::theory::sep::SepPtoTypeRule +typerule SEP_STAR ::CVC4::theory::sep::SepStarTypeRule +typerule SEP_WAND ::CVC4::theory::sep::SepWandTypeRule +typerule SEP_LABEL ::CVC4::theory::sep::SepLabelTypeRule + +endtheory diff --git a/src/theory/sep/theory_sep.cpp b/src/theory/sep/theory_sep.cpp new file mode 100644 index 000000000..836a04afa --- /dev/null +++ b/src/theory/sep/theory_sep.cpp @@ -0,0 +1,1534 @@ +/********************* */ +/*! \file theory_sep.cpp + ** \verbatim + ** Original author: Andrew Reynolds + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2014 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Implementation of the theory of sep. + ** + ** Implementation of the theory of sep. + **/ + + +#include "theory/sep/theory_sep.h" +#include "theory/valuation.h" +#include "expr/kind.h" +#include <map> +#include "theory/rewriter.h" +#include "theory/theory_model.h" +#include "options/sep_options.h" +#include "options/smt_options.h" +#include "smt/logic_exception.h" +#include "theory/quantifiers_engine.h" +#include "theory/quantifiers/term_database.h" + +using namespace std; + +namespace CVC4 { +namespace theory { +namespace sep { + +TheorySep::TheorySep(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) : + Theory(THEORY_SEP, c, u, out, valuation, logicInfo), + d_notify(*this), + d_equalityEngine(d_notify, c, "theory::sep::TheorySep", true), + d_conflict(c, false), + d_reduce(u), + d_infer(c), + d_infer_exp(c), + d_spatial_assertions(c) +{ + d_true = NodeManager::currentNM()->mkConst<bool>(true); + d_false = NodeManager::currentNM()->mkConst<bool>(false); + + // The kinds we are treating as function application in congruence + d_equalityEngine.addFunctionKind(kind::SEP_PTO); + //d_equalityEngine.addFunctionKind(kind::SEP_STAR); +} + +TheorySep::~TheorySep() { + for( std::map< Node, HeapAssertInfo * >::iterator it = d_eqc_info.begin(); it != d_eqc_info.end(); ++it ){ + delete it->second; + } +} + +void TheorySep::setMasterEqualityEngine(eq::EqualityEngine* eq) { + d_equalityEngine.setMasterEqualityEngine(eq); +} + +Node TheorySep::mkAnd( std::vector< TNode >& assumptions ) { + if( assumptions.empty() ){ + return d_true; + }else if( assumptions.size()==1 ){ + return assumptions[0]; + }else{ + return NodeManager::currentNM()->mkNode( kind::AND, assumptions ); + } +} + +///////////////////////////////////////////////////////////////////////////// +// PREPROCESSING +///////////////////////////////////////////////////////////////////////////// + + + +Node TheorySep::ppRewrite(TNode term) { + Trace("sep-pp") << "ppRewrite : " << term << std::endl; +/* + Node s_atom = term.getKind()==kind::NOT ? term[0] : term; + if( s_atom.getKind()==kind::SEP_PTO || s_atom.getKind()==kind::SEP_STAR || s_atom.getKind()==kind::SEP_WAND || s_atom.getKind()==kind::SEP_EMP ){ + //get the reference type (will compute d_type_references) + int card = 0; + TypeNode tn = getReferenceType( s_atom, card ); + Trace("sep-pp") << " reference type is " << tn << ", card is " << card << std::endl; + } +*/ + return term; +} + +//must process assertions at preprocess so that quantified assertions are processed properly +void TheorySep::processAssertions( std::vector< Node >& assertions ) { + std::map< Node, bool > visited; + for( unsigned i=0; i<assertions.size(); i++ ){ + processAssertion( assertions[i], visited ); + } +} + +void TheorySep::processAssertion( Node n, std::map< Node, bool >& visited ) { + if( visited.find( n )==visited.end() ){ + visited[n] = true; + if( n.getKind()==kind::SEP_PTO || n.getKind()==kind::SEP_STAR || n.getKind()==kind::SEP_WAND || n.getKind()==kind::SEP_EMP ){ + //get the reference type (will compute d_type_references) + int card = 0; + TypeNode tn = getReferenceType( n, card ); + Trace("sep-pp") << " reference type is " << tn << ", card is " << card << std::endl; + }else{ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + processAssertion( n[i], visited ); + } + } + } +} + +Theory::PPAssertStatus TheorySep::ppAssert(TNode in, SubstitutionMap& outSubstitutions) { + + return PP_ASSERT_STATUS_UNSOLVED; +} + + +///////////////////////////////////////////////////////////////////////////// +// T-PROPAGATION / REGISTRATION +///////////////////////////////////////////////////////////////////////////// + + +bool TheorySep::propagate(TNode literal) +{ + Debug("sep") << "TheorySep::propagate(" << literal << ")" << std::endl; + // If already in conflict, no more propagation + if (d_conflict) { + Debug("sep") << "TheorySep::propagate(" << literal << "): already in conflict" << std::endl; + return false; + } + bool ok = d_out->propagate(literal); + if (!ok) { + d_conflict = true; + } + return ok; +}/* TheorySep::propagate(TNode) */ + + +void TheorySep::explain(TNode literal, std::vector<TNode>& assumptions) { + if( literal.getKind()==kind::SEP_LABEL || + ( literal.getKind()==kind::NOT && literal[0].getKind()==kind::SEP_LABEL ) ){ + //labelled assertions are never given to equality engine and should only come from the outside + assumptions.push_back( literal ); + }else{ + // Do the work + 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, NULL ); + } else { + d_equalityEngine.explainPredicate( atom, polarity, assumptions ); + } + } +} + +void TheorySep::preRegisterTermRec(TNode t, std::map< TNode, bool >& visited ) { + if( visited.find( t )==visited.end() ){ + visited[t] = true; + Trace("sep-prereg-debug") << "Preregister : " << t << std::endl; + if( t.getKind()==kind::SEP_NIL ){ + Trace("sep-prereg") << "Preregister nil : " << t << std::endl; + //per type, all nil variable references are equal + TypeNode tn = t.getType(); + std::map< TypeNode, Node >::iterator it = d_nil_ref.find( tn ); + if( it==d_nil_ref.end() ){ + Trace("sep-prereg") << "...set as reference." << std::endl; + d_nil_ref[tn] = t; + }else{ + Node nr = it->second; + Trace("sep-prereg") << "...reference is " << nr << "." << std::endl; + if( t!=nr ){ + if( d_reduce.find( t )==d_reduce.end() ){ + d_reduce.insert( t ); + Node lem = NodeManager::currentNM()->mkNode( tn.isBoolean() ? kind::IFF : kind::EQUAL, t, nr ); + Trace("sep-lemma") << "Sep::Lemma: nil ref eq : " << lem << std::endl; + d_out->lemma( lem ); + } + } + } + }else{ + for( unsigned i=0; i<t.getNumChildren(); i++ ){ + preRegisterTermRec( t[i], visited ); + } + } + } +} + +void TheorySep::preRegisterTerm(TNode term){ + std::map< TNode, bool > visited; + preRegisterTermRec( term, visited ); +} + + +void TheorySep::propagate(Effort e){ + +} + + +Node TheorySep::explain(TNode literal) +{ + Debug("sep") << "TheorySep::explain(" << literal << ")" << std::endl; + std::vector<TNode> assumptions; + explain(literal, assumptions); + return mkAnd(assumptions); +} + + +///////////////////////////////////////////////////////////////////////////// +// SHARING +///////////////////////////////////////////////////////////////////////////// + + +void TheorySep::addSharedTerm(TNode t) { + Debug("sep") << "TheorySep::addSharedTerm(" << t << ")" << std::endl; + d_equalityEngine.addTriggerTerm(t, THEORY_SEP); +} + + +EqualityStatus TheorySep::getEqualityStatus(TNode a, TNode b) { + Assert(d_equalityEngine.hasTerm(a) && d_equalityEngine.hasTerm(b)); + if (d_equalityEngine.areEqual(a, b)) { + // The terms are implied to be equal + return EQUALITY_TRUE; + } + else if (d_equalityEngine.areDisequal(a, b, false)) { + // The terms are implied to be dis-equal + return EQUALITY_FALSE; + } + return EQUALITY_UNKNOWN;//FALSE_IN_MODEL; +} + + +void TheorySep::computeCareGraph() { + Debug("sharing") << "Theory::computeCareGraph<" << getId() << ">()" << endl; + for (unsigned i = 0; i < d_sharedTerms.size(); ++ i) { + TNode a = d_sharedTerms[i]; + TypeNode aType = a.getType(); + for (unsigned j = i + 1; j < d_sharedTerms.size(); ++ j) { + TNode b = d_sharedTerms[j]; + if (b.getType() != aType) { + // We don't care about the terms of different types + continue; + } + switch (d_valuation.getEqualityStatus(a, b)) { + case EQUALITY_TRUE_AND_PROPAGATED: + case EQUALITY_FALSE_AND_PROPAGATED: + // If we know about it, we should have propagated it, so we can skip + break; + default: + // Let's split on it + addCarePair(a, b); + break; + } + } + } +} + + +///////////////////////////////////////////////////////////////////////////// +// MODEL GENERATION +///////////////////////////////////////////////////////////////////////////// + + +void TheorySep::collectModelInfo( TheoryModel* m, bool fullModel ) +{ + // Send the equality engine information to the model + m->assertEqualityEngine( &d_equalityEngine ); + + if( fullModel ){ + for( std::map< TypeNode, Node >::iterator it = d_base_label.begin(); it != d_base_label.end(); ++it ){ + Trace("sep-model") << "; Model for heap, type = " << it->first << " : " << std::endl; + computeLabelModel( it->second, d_tmodel ); + if( d_label_model[it->second].d_heap_locs_model.empty() ){ + Trace("sep-model") << "; [empty]" << std::endl; + }else{ + for( unsigned j=0; j<d_label_model[it->second].d_heap_locs_model.size(); j++ ){ + Assert( d_label_model[it->second].d_heap_locs_model[j].getKind()==kind::SINGLETON ); + Node l = d_label_model[it->second].d_heap_locs_model[j][0]; + Trace("sep-model") << "; " << l << " -> "; + if( d_pto_model[l].isNull() ){ + Trace("sep-model") << "_"; + }else{ + Trace("sep-model") << d_pto_model[l]; + } + Trace("sep-model") << std::endl; + } + } + Node nil = getNilRef( it->first ); + Node vnil = d_valuation.getModel()->getRepresentative( nil ); + Trace("sep-model") << "; sep.nil = " << vnil << std::endl; + Trace("sep-model") << std::endl; + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// NOTIFICATIONS +///////////////////////////////////////////////////////////////////////////// + + +void TheorySep::presolve() { + Trace("sep-pp") << "Presolving" << std::endl; + //TODO: cleanup if incremental? +} + + +///////////////////////////////////////////////////////////////////////////// +// MAIN SOLVER +///////////////////////////////////////////////////////////////////////////// + + +void TheorySep::check(Effort e) { + if (done() && !fullEffort(e) && e != EFFORT_LAST_CALL) { + return; + } + + getOutputChannel().spendResource(options::theoryCheckStep()); + + TimerStat::CodeTimer checkTimer(d_checkTime); + Trace("sep-check") << "Sep::check(): " << e << endl; + + while( !done() && !d_conflict ){ + // Get all the assertions + Assertion assertion = get(); + TNode fact = assertion.assertion; + + Trace("sep-assert") << "TheorySep::check(): processing " << fact << std::endl; + + bool polarity = fact.getKind() != kind::NOT; + TNode atom = polarity ? fact : fact[0]; + if( atom.getKind()==kind::SEP_EMP ){ + TypeNode tn = atom[0].getType(); + if( d_emp_arg.find( tn )==d_emp_arg.end() ){ + d_emp_arg[tn] = atom[0]; + }else{ + //normalize argument TODO + } + } + TNode s_atom = atom.getKind()==kind::SEP_LABEL ? atom[0] : atom; + TNode s_lbl = atom.getKind()==kind::SEP_LABEL ? atom[1] : TNode::null(); + bool is_spatial = s_atom.getKind()==kind::SEP_STAR || s_atom.getKind()==kind::SEP_WAND || s_atom.getKind()==kind::SEP_PTO || s_atom.getKind()==kind::SEP_EMP; + if( is_spatial && s_lbl.isNull() ){ + if( d_reduce.find( fact )==d_reduce.end() ){ + Trace("sep-lemma-debug") << "Reducing unlabelled assertion " << atom << std::endl; + d_reduce.insert( fact ); + //introduce top-level label, add iff + int card; + TypeNode refType = getReferenceType( s_atom, card ); + Trace("sep-lemma-debug") << "...reference type is : " << refType << ", card is " << card << std::endl; + Node b_lbl = getBaseLabel( refType ); + Node s_atom_new = NodeManager::currentNM()->mkNode( kind::SEP_LABEL, s_atom, b_lbl ); + Node lem; + if( polarity ){ + lem = NodeManager::currentNM()->mkNode( kind::OR, s_atom.negate(), s_atom_new ); + }else{ + lem = NodeManager::currentNM()->mkNode( kind::OR, s_atom, s_atom_new.negate() ); + } + Trace("sep-lemma-debug") << "Sep::Lemma : base reduction : " << lem << std::endl; + d_out->lemma( lem ); + } + }else{ + //do reductions + if( is_spatial ){ + if( d_reduce.find( fact )==d_reduce.end() ){ + Trace("sep-lemma-debug") << "Reducing assertion " << fact << std::endl; + d_reduce.insert( fact ); + Node conc; + std::map< Node, Node >::iterator its = d_red_conc[s_lbl].find( s_atom ); + if( its==d_red_conc[s_lbl].end() ){ + //make conclusion based on type of assertion + if( s_atom.getKind()==kind::SEP_STAR || s_atom.getKind()==kind::SEP_WAND ){ + std::vector< Node > children; + std::vector< Node > c_lems; + int card; + TypeNode tn = getReferenceType( s_atom, card ); + Assert( d_reference_bound.find( tn )!=d_reference_bound.end() ); + c_lems.push_back( NodeManager::currentNM()->mkNode( kind::SUBSET, s_lbl, d_reference_bound[tn] ) ); + if( options::sepPreciseBound() ){ + //more precise bound + Trace("sep-bound") << "Propagate Bound(" << s_lbl << ") = "; + Assert( d_lbl_reference_bound.find( s_lbl )!=d_lbl_reference_bound.end() ); + for( unsigned j=0; j<d_lbl_reference_bound[s_lbl].size(); j++ ){ + Trace("sep-bound") << d_lbl_reference_bound[s_lbl][j] << " "; + } + Trace("sep-bound") << std::endl << " to children of " << s_atom << std::endl; + //int rb_start = 0; + for( unsigned j=0; j<s_atom.getNumChildren(); j++ ){ + int ccard = 0; + getReferenceType( s_atom, ccard, j ); + Node c_lbl = getLabel( s_atom, j, s_lbl ); + Trace("sep-bound") << " for " << c_lbl << ", card = " << ccard << " : "; + std::vector< Node > bound_loc; + bound_loc.insert( bound_loc.end(), d_references[s_atom][j].begin(), d_references[s_atom][j].end() ); +/* //this is unsound + for( int k=0; k<ccard; k++ ){ + Assert( rb_start<(int)d_lbl_reference_bound[s_lbl].size() ); + d_lbl_reference_bound[c_lbl].push_back( d_lbl_reference_bound[s_lbl][rb_start] ); + Trace("sep-bound") << d_lbl_reference_bound[s_lbl][rb_start] << " "; + bound_loc.push_back( d_lbl_reference_bound[s_lbl][rb_start] ); + rb_start++; + } +*/ + //carry all locations for now + bound_loc.insert( bound_loc.end(), d_lbl_reference_bound[s_lbl].begin(), d_lbl_reference_bound[s_lbl].end() ); + Trace("sep-bound") << std::endl; + Node bound_v = mkUnion( tn, bound_loc ); + Trace("sep-bound") << " ...bound value : " << bound_v << std::endl; + children.push_back( NodeManager::currentNM()->mkNode( kind::SUBSET, c_lbl, bound_v ) ); + } + Trace("sep-bound") << "Done propagate Bound(" << s_lbl << ")" << std::endl; + } + std::vector< Node > labels; + getLabelChildren( s_atom, s_lbl, children, labels ); + Node empSet = NodeManager::currentNM()->mkConst(EmptySet(s_lbl.getType().toType())); + Assert( children.size()>1 ); + if( s_atom.getKind()==kind::SEP_STAR ){ + //reduction for heap : union, pairwise disjoint + Node ulem = NodeManager::currentNM()->mkNode( kind::UNION, labels[0], labels[1] ); + for( unsigned i=2; i<labels.size(); i++ ){ + ulem = NodeManager::currentNM()->mkNode( kind::UNION, ulem, labels[i] ); + } + ulem = s_lbl.eqNode( ulem ); + Trace("sep-lemma-debug") << "Sep::Lemma : star reduction, union : " << ulem << std::endl; + c_lems.push_back( ulem ); + for( unsigned i=0; i<labels.size(); i++ ){ + for( unsigned j=(i+1); j<labels.size(); j++ ){ + Node s = NodeManager::currentNM()->mkNode( kind::INTERSECTION, labels[i], labels[j] ); + Node ilem = s.eqNode( empSet ); + Trace("sep-lemma-debug") << "Sep::Lemma : star reduction, disjoint : " << ilem << std::endl; + c_lems.push_back( ilem ); + } + } + }else{ + Node ulem = NodeManager::currentNM()->mkNode( kind::UNION, s_lbl, labels[0] ); + ulem = ulem.eqNode( labels[1] ); + Trace("sep-lemma-debug") << "Sep::Lemma : wand reduction, union : " << ulem << std::endl; + c_lems.push_back( ulem ); + Node s = NodeManager::currentNM()->mkNode( kind::INTERSECTION, s_lbl, labels[0] ); + Node ilem = s.eqNode( empSet ); + Trace("sep-lemma-debug") << "Sep::Lemma : wand reduction, disjoint : " << ilem << std::endl; + c_lems.push_back( ilem ); + } + //send out definitional lemmas for introduced sets + for( unsigned j=0; j<c_lems.size(); j++ ){ + Trace("sep-lemma") << "Sep::Lemma : definition : " << c_lems[j] << std::endl; + d_out->lemma( c_lems[j] ); + } + //children.insert( children.end(), c_lems.begin(), c_lems.end() ); + conc = NodeManager::currentNM()->mkNode( kind::AND, children ); + }else if( s_atom.getKind()==kind::SEP_PTO ){ + Node ss = NodeManager::currentNM()->mkNode( kind::SINGLETON, s_atom[0] ); + if( s_lbl!=ss ){ + conc = s_lbl.eqNode( ss ); + } + Node ssn = NodeManager::currentNM()->mkNode( kind::EQUAL, s_atom[0], getNilRef(s_atom[0].getType()) ).negate(); + conc = conc.isNull() ? ssn : NodeManager::currentNM()->mkNode( kind::AND, conc, ssn ); + }else{ + //labeled emp should be rewritten + Assert( false ); + } + d_red_conc[s_lbl][s_atom] = conc; + }else{ + conc = its->second; + } + if( !conc.isNull() ){ + bool use_polarity = s_atom.getKind()==kind::SEP_WAND ? !polarity : polarity; + if( !use_polarity ){ + // introduce guard, assert positive version + Trace("sep-lemma-debug") << "Negated spatial constraint asserted to sep theory: " << fact << std::endl; + Node lit = Rewriter::rewrite( NodeManager::currentNM()->mkSkolem( "G", NodeManager::currentNM()->booleanType() ) ); + lit = getValuation().ensureLiteral( lit ); + d_neg_guard[s_lbl][s_atom] = lit; + Trace("sep-lemma-debug") << "Neg guard : " << s_lbl << " " << s_atom << " " << lit << std::endl; + AlwaysAssert( !lit.isNull() ); + d_out->requirePhase( lit, true ); + d_neg_guards.push_back( lit ); + d_guard_to_assertion[lit] = s_atom; + //Node lem = NodeManager::currentNM()->mkNode( kind::IFF, lit, conc ); + Node lem = NodeManager::currentNM()->mkNode( kind::OR, lit.negate(), conc ); + Trace("sep-lemma") << "Sep::Lemma : (neg) reduction : " << lem << std::endl; + d_out->lemma( lem ); + }else{ + //reduce based on implication + Node ant = atom; + if( polarity ){ + ant = atom.negate(); + } + Node lem = NodeManager::currentNM()->mkNode( kind::OR, ant, conc ); + Trace("sep-lemma") << "Sep::Lemma : reduction : " << lem << std::endl; + d_out->lemma( lem ); + } + }else{ + Trace("sep-lemma-debug") << "Trivial conclusion, do not add lemma." << std::endl; + } + } + } + //assert to equality engine + if( !is_spatial ){ + Trace("sep-assert") << "Asserting " << atom << ", pol = " << polarity << " to EE..." << std::endl; + if( s_atom.getKind()==kind::EQUAL ){ + d_equalityEngine.assertEquality(atom, polarity, fact); + }else{ + d_equalityEngine.assertPredicate(atom, polarity, fact); + } + Trace("sep-assert") << "Done asserting " << atom << " to EE." << std::endl; + }else if( s_atom.getKind()==kind::SEP_PTO ){ + Node pto_lbl = NodeManager::currentNM()->mkNode( kind::SINGLETON, s_atom[0] ); + if( polarity && s_lbl!=pto_lbl ){ + //also propagate equality + Node eq = s_lbl.eqNode( pto_lbl ); + Trace("sep-assert") << "Asserting implied equality " << eq << " to EE..." << std::endl; + d_equalityEngine.assertEquality(eq, true, fact); + Trace("sep-assert") << "Done asserting implied equality " << eq << " to EE." << std::endl; + } + //associate the equivalence class of the lhs with this pto + Node r = getRepresentative( s_lbl ); + HeapAssertInfo * ei = getOrMakeEqcInfo( r, true ); + addPto( ei, r, atom, polarity ); + } + //maybe propagate + doPendingFacts(); + //add to spatial assertions + if( !d_conflict && is_spatial ){ + d_spatial_assertions.push_back( fact ); + } + } + } + + if( e == EFFORT_LAST_CALL && !d_conflict && !d_valuation.needCheck() ){ + Trace("sep-process") << "Checking heap at full effort..." << std::endl; + d_label_model.clear(); + d_tmodel.clear(); + d_pto_model.clear(); + Trace("sep-process") << "---Locations---" << std::endl; + for( std::map< TypeNode, std::vector< Node > >::iterator itt = d_type_references_all.begin(); itt != d_type_references_all.end(); ++itt ){ + for( unsigned k=0; k<itt->second.size(); k++ ){ + Node t = itt->second[k]; + Trace("sep-process") << " " << t << " = "; + if( d_valuation.getModel()->hasTerm( t ) ){ + Node v = d_valuation.getModel()->getRepresentative( t ); + Trace("sep-process") << v << std::endl; + d_tmodel[v] = t; + }else{ + Trace("sep-process") << "?" << std::endl; + } + } + } + Trace("sep-process") << "---" << std::endl; + //build positive/negative assertion lists for labels + std::map< Node, bool > assert_active; + //get the inactive assertions + std::map< Node, std::vector< Node > > lbl_to_assertions; + for( NodeList::const_iterator i = d_spatial_assertions.begin(); i != d_spatial_assertions.end(); ++i ) { + Node fact = (*i); + bool polarity = fact.getKind() != kind::NOT; + TNode atom = polarity ? fact : fact[0]; + Assert( atom.getKind()==kind::SEP_LABEL ); + TNode s_atom = atom[0]; + TNode s_lbl = atom[1]; + lbl_to_assertions[s_lbl].push_back( fact ); + //check whether assertion is active : either polarity=true, or guard is not asserted false + assert_active[fact] = true; + bool use_polarity = s_atom.getKind()==kind::SEP_WAND ? !polarity : polarity; + if( use_polarity ){ + if( s_atom.getKind()==kind::SEP_PTO ){ + Node vv = d_valuation.getModel()->getRepresentative( s_atom[0] ); + if( d_pto_model.find( vv )==d_pto_model.end() ){ + Trace("sep-inst") << "Pto : " << s_atom[0] << " (" << vv << ") -> " << s_atom[1] << std::endl; + d_pto_model[vv] = s_atom[1]; + } + } + }else{ + if( d_neg_guard[s_lbl].find( s_atom )!=d_neg_guard[s_lbl].end() ){ + //check if the guard is asserted positively + Node guard = d_neg_guard[s_lbl][s_atom]; + bool value; + if( getValuation().hasSatValue( guard, value ) ) { + assert_active[fact] = value; + } + } + } + } + //(recursively) set inactive sub-assertions + for( NodeList::const_iterator i = d_spatial_assertions.begin(); i != d_spatial_assertions.end(); ++i ) { + Node fact = (*i); + if( !assert_active[fact] ){ + setInactiveAssertionRec( fact, lbl_to_assertions, assert_active ); + } + } + //set up model information based on active assertions + for( NodeList::const_iterator i = d_spatial_assertions.begin(); i != d_spatial_assertions.end(); ++i ) { + Node fact = (*i); + bool polarity = fact.getKind() != kind::NOT; + TNode atom = polarity ? fact : fact[0]; + TNode s_atom = atom[0]; + TNode s_lbl = atom[1]; + if( assert_active[fact] ){ + computeLabelModel( s_lbl, d_tmodel ); + } + } + //debug print + if( Trace.isOn("sep-process") ){ + Trace("sep-process") << "--- Current spatial assertions : " << std::endl; + for( NodeList::const_iterator i = d_spatial_assertions.begin(); i != d_spatial_assertions.end(); ++i ) { + Node fact = (*i); + Trace("sep-process") << " " << fact; + if( !assert_active[fact] ){ + Trace("sep-process") << " [inactive]"; + } + Trace("sep-process") << std::endl; + } + Trace("sep-process") << "---" << std::endl; + } + if(Trace.isOn("sep-eqc")) { + eq::EqClassesIterator eqcs2_i = eq::EqClassesIterator( &d_equalityEngine ); + Trace("sep-eqc") << "EQC:" << std::endl; + while( !eqcs2_i.isFinished() ){ + Node eqc = (*eqcs2_i); + eq::EqClassIterator eqc2_i = eq::EqClassIterator( eqc, &d_equalityEngine ); + Trace("sep-eqc") << "Eqc( " << eqc << " ) : { "; + while( !eqc2_i.isFinished() ) { + if( (*eqc2_i)!=eqc ){ + Trace("sep-eqc") << (*eqc2_i) << " "; + } + ++eqc2_i; + } + Trace("sep-eqc") << " } " << std::endl; + ++eqcs2_i; + } + Trace("sep-eqc") << std::endl; + } + + if( options::sepCheckNeg() ){ + //get active labels + std::map< Node, bool > active_lbl; + if( options::sepMinimalRefine() ){ + for( NodeList::const_iterator i = d_spatial_assertions.begin(); i != d_spatial_assertions.end(); ++i ) { + Node fact = (*i); + bool polarity = fact.getKind() != kind::NOT; + TNode atom = polarity ? fact : fact[0]; + TNode s_atom = atom[0]; + bool use_polarity = s_atom.getKind()==kind::SEP_WAND ? !polarity : polarity; + if( !use_polarity ){ + Assert( assert_active.find( fact )!=assert_active.end() ); + if( assert_active[fact] ){ + Assert( atom.getKind()==kind::SEP_LABEL ); + TNode s_lbl = atom[1]; + if( d_label_map[s_atom].find( s_lbl )!=d_label_map[s_atom].end() ){ + Trace("sep-process-debug") << "Active lbl : " << s_lbl << std::endl; + active_lbl[s_lbl] = true; + } + } + } + } + } + + //process spatial assertions + bool addedLemma = false; + bool needAddLemma = false; + for( NodeList::const_iterator i = d_spatial_assertions.begin(); i != d_spatial_assertions.end(); ++i ) { + Node fact = (*i); + bool polarity = fact.getKind() != kind::NOT; + TNode atom = polarity ? fact : fact[0]; + TNode s_atom = atom[0]; + + bool use_polarity = s_atom.getKind()==kind::SEP_WAND ? !polarity : polarity; + Trace("sep-process-debug") << " check atom : " << s_atom << " use polarity " << use_polarity << std::endl; + if( !use_polarity ){ + Assert( assert_active.find( fact )!=assert_active.end() ); + if( assert_active[fact] ){ + Assert( atom.getKind()==kind::SEP_LABEL ); + TNode s_lbl = atom[1]; + Trace("sep-process") << "--> Active negated atom : " << s_atom << ", lbl = " << s_lbl << std::endl; + //add refinement lemma + if( d_label_map[s_atom].find( s_lbl )!=d_label_map[s_atom].end() ){ + needAddLemma = true; + int card; + TypeNode tn = getReferenceType( s_atom, card ); + tn = NodeManager::currentNM()->mkSetType(tn); + //tn = NodeManager::currentNM()->mkSetType(NodeManager::currentNM()->mkRefType(tn)); + Node o_b_lbl_mval = d_label_model[s_lbl].getValue( tn ); + Trace("sep-process") << " Model for " << s_lbl << " : " << o_b_lbl_mval << std::endl; + + //get model values + std::map< int, Node > mvals; + for( std::map< int, Node >::iterator itl = d_label_map[s_atom][s_lbl].begin(); itl != d_label_map[s_atom][s_lbl].end(); ++itl ){ + Node sub_lbl = itl->second; + int sub_index = itl->first; + computeLabelModel( sub_lbl, d_tmodel ); + Node lbl_mval = d_label_model[sub_lbl].getValue( tn ); + Trace("sep-process-debug") << " child " << sub_index << " : " << sub_lbl << ", mval = " << lbl_mval << std::endl; + mvals[sub_index] = lbl_mval; + } + + // Now, assert model-instantiated implication based on the negation + Assert( d_label_model.find( s_lbl )!=d_label_model.end() ); + std::vector< Node > conc; + bool inst_success = true; + if( options::sepExp() ){ + //old refinement lemmas + for( std::map< int, Node >::iterator itl = d_label_map[s_atom][s_lbl].begin(); itl != d_label_map[s_atom][s_lbl].end(); ++itl ){ + int sub_index = itl->first; + std::map< Node, Node > visited; + Node c = applyLabel( s_atom[itl->first], mvals[sub_index], visited ); + Trace("sep-process-debug") << " applied inst : " << c << std::endl; + if( s_atom.getKind()==kind::SEP_STAR || sub_index==0 ){ + conc.push_back( c.negate() ); + }else{ + conc.push_back( c ); + } + } + }else{ + //new refinement + std::map< Node, Node > visited; + Node inst = instantiateLabel( s_atom, s_lbl, s_lbl, o_b_lbl_mval, visited, d_pto_model, d_tmodel, tn, active_lbl ); + Trace("sep-inst-debug") << " applied inst : " << inst << std::endl; + if( inst.isNull() ){ + inst_success = false; + }else{ + inst = Rewriter::rewrite( inst ); + if( inst==( polarity ? d_true : d_false ) ){ + inst_success = false; + } + conc.push_back( polarity ? inst : inst.negate() ); + } + } + if( inst_success ){ + std::vector< Node > lemc; + Node pol_atom = atom; + if( polarity ){ + pol_atom = atom.negate(); + } + lemc.push_back( pol_atom ); + //lemc.push_back( s_lbl.eqNode( o_b_lbl_mval ).negate() ); + //lemc.push_back( NodeManager::currentNM()->mkNode( kind::SUBSET, o_b_lbl_mval, s_lbl ).negate() ); + lemc.insert( lemc.end(), conc.begin(), conc.end() ); + Node lem = NodeManager::currentNM()->mkNode( kind::OR, lemc ); + if( std::find( d_refinement_lem[s_atom][s_lbl].begin(), d_refinement_lem[s_atom][s_lbl].end(), lem )==d_refinement_lem[s_atom][s_lbl].end() ){ + d_refinement_lem[s_atom][s_lbl].push_back( lem ); + Trace("sep-process") << "-----> refinement lemma (#" << d_refinement_lem[s_atom][s_lbl].size() << ") : " << lem << std::endl; + Trace("sep-lemma") << "Sep::Lemma : negated star/wand refinement : " << lem << std::endl; + d_out->lemma( lem ); + addedLemma = true; + }else{ + Trace("sep-process") << "*** repeated refinement lemma : " << lem << std::endl; + } + } + }else{ + Trace("sep-process-debug") << " no children." << std::endl; + Assert( s_atom.getKind()==kind::SEP_PTO ); + } + }else{ + Trace("sep-process-debug") << "--> inactive negated assertion " << s_atom << std::endl; + } + } + } + if( !addedLemma ){ + if( needAddLemma ){ + Trace("sep-process") << "WARNING : could not find refinement lemma!!!" << std::endl; + Assert( false ); + d_out->setIncomplete(); + } + } + } + } + Trace("sep-check") << "Sep::check(): " << e << " done, conflict=" << d_conflict.get() << endl; +} + + +Node TheorySep::getNextDecisionRequest() { + for( unsigned i=0; i<d_neg_guards.size(); i++ ){ + Node g = d_neg_guards[i]; + bool success = true; + if( getLogicInfo().isQuantified() ){ + Assert( d_guard_to_assertion.find( g )!= d_guard_to_assertion.end() ); + Node a = d_guard_to_assertion[g]; + Node q = quantifiers::TermDb::getInstConstAttr( a ); + if( !q.isNull() ){ + //must wait to decide on counterexample literal from quantified formula + Node cel = getQuantifiersEngine()->getTermDatabase()->getCounterexampleLiteral( q ); + bool value; + if( d_valuation.hasSatValue( cel, value ) ){ + success = value; + }else{ + Trace("sep-dec-debug") << "TheorySep::getNextDecisionRequest : wait to decide on " << g << " until " << cel << " is set " << std::endl; + success = false; + } + } + } + if( success ){ + bool value; + if( !d_valuation.hasSatValue( g, value ) ) { + Trace("sep-dec") << "TheorySep::getNextDecisionRequest : " << g << " (" << i << "/" << d_neg_guards.size() << ")" << std::endl; + return g; + } + } + } + Trace("sep-dec-debug") << "TheorySep::getNextDecisionRequest : null" << std::endl; + return Node::null(); +} + +void TheorySep::conflict(TNode a, TNode b) { + Trace("sep-conflict") << "Sep::conflict : " << a << " " << b << std::endl; + Node conflictNode; + if (a.getKind() == kind::CONST_BOOLEAN) { + conflictNode = explain(a.iffNode(b)); + } else { + conflictNode = explain(a.eqNode(b)); + } + d_conflict = true; + d_out->conflict( conflictNode ); +} + + +TheorySep::HeapAssertInfo::HeapAssertInfo( context::Context* c ) : d_pto(c), d_has_neg_pto(c,false) { + +} + +TheorySep::HeapAssertInfo * TheorySep::getOrMakeEqcInfo( Node n, bool doMake ) { + std::map< Node, HeapAssertInfo* >::iterator e_i = d_eqc_info.find( n ); + if( e_i==d_eqc_info.end() ){ + if( doMake ){ + HeapAssertInfo* ei = new HeapAssertInfo( getSatContext() ); + d_eqc_info[n] = ei; + return ei; + }else{ + return NULL; + } + }else{ + return (*e_i).second; + } +} + +TypeNode TheorySep::getReferenceType( Node atom, int& card, int index ) { + Trace("sep-type") << "getReference type " << atom << " " << index << std::endl; + Assert( atom.getKind()==kind::SEP_PTO || atom.getKind()==kind::SEP_STAR || atom.getKind()==kind::SEP_WAND || atom.getKind()==kind::SEP_EMP || index!=-1 ); + std::map< int, TypeNode >::iterator it = d_reference_type[atom].find( index ); + if( it==d_reference_type[atom].end() ){ + card = 0; + TypeNode tn; + if( index==-1 && ( atom.getKind()==kind::SEP_STAR || atom.getKind()==kind::SEP_WAND ) ){ + for( unsigned i=0; i<atom.getNumChildren(); i++ ){ + int cardc = 0; + TypeNode ctn = getReferenceType( atom, cardc, i ); + if( !ctn.isNull() ){ + tn = ctn; + } + for( unsigned j=0; j<d_references[atom][i].size(); j++ ){ + if( std::find( d_references[atom][index].begin(), d_references[atom][index].end(), d_references[atom][i][j] )==d_references[atom][index].end() ){ + d_references[atom][index].push_back( d_references[atom][i][j] ); + } + } + card = card + cardc; + } + }else{ + Node n = index==-1 ? atom : atom[index]; + //will compute d_references as well + std::map< Node, int > visited; + tn = getReferenceType2( atom, card, index, n, visited ); + } + if( tn.isNull() && index==-1 ){ + tn = NodeManager::currentNM()->booleanType(); + } + d_reference_type[atom][index] = tn; + d_reference_type_card[atom][index] = card; + Trace("sep-type") << "...ref type return " << card << " for " << atom << " " << index << std::endl; + //add to d_type_references + if( index==-1 ){ + //only contributes if top-level (index=-1) + for( unsigned i=0; i<d_references[atom][index].size(); i++ ){ + Assert( !d_references[atom][index][i].isNull() ); + if( std::find( d_type_references[tn].begin(), d_type_references[tn].end(), d_references[atom][index][i] )==d_type_references[tn].end() ){ + d_type_references[tn].push_back( d_references[atom][index][i] ); + } + } + // update maximum cardinality value + if( card>(int)d_card_max[tn] ){ + d_card_max[tn] = card; + } + } + return tn; + }else{ + Assert( d_reference_type_card[atom].find( index )!=d_reference_type_card[atom].end() ); + card = d_reference_type_card[atom][index]; + return it->second; + } +} + +TypeNode TheorySep::getReferenceType2( Node atom, int& card, int index, Node n, std::map< Node, int >& visited ) { + if( visited.find( n )==visited.end() ){ + Trace("sep-type-debug") << "visit : " << n << " : " << atom << " " << index << std::endl; + visited[n] = -1; + if( n.getKind()==kind::SEP_PTO ){ + TypeNode tn1 = n[0].getType(); + TypeNode tn2 = n[1].getType(); + if( quantifiers::TermDb::hasBoundVarAttr( n[0] ) ){ + d_reference_bound_invalid[tn1] = true; + }else{ + if( std::find( d_references[atom][index].begin(), d_references[atom][index].end(), n[0] )==d_references[atom][index].end() ){ + d_references[atom][index].push_back( n[0] ); + } + } + std::map< TypeNode, TypeNode >::iterator itt = d_loc_to_data_type.find( tn1 ); + if( itt==d_loc_to_data_type.end() ){ + Trace("sep-type") << "Sep: assume location type " << tn1 << " is associated with data type " << tn2 << " (from " << atom << ")" << std::endl; + d_loc_to_data_type[tn1] = tn2; + }else{ + if( itt->second!=tn2 ){ + std::stringstream ss; + ss << "ERROR: location type " << tn1 << " is already associated with data type " << itt->second << ", offending atom is " << atom << " with data type " << tn2 << std::endl; + throw LogicException(ss.str()); + Assert( false ); + } + } + card = 1; + visited[n] = card; + return tn1; + //return n[1].getType(); + }else if( n.getKind()==kind::SEP_EMP ){ + card = 1; + visited[n] = card; + return n[0].getType(); + }else if( n.getKind()==kind::SEP_STAR || n.getKind()==kind::SEP_WAND ){ + Assert( n!=atom ); + //get the references + card = 0; + TypeNode tn = getReferenceType( n, card ); + for( unsigned j=0; j<d_references[n][-1].size(); j++ ){ + if( std::find( d_references[atom][index].begin(), d_references[atom][index].end(), d_references[n][-1][j] )==d_references[atom][index].end() ){ + d_references[atom][index].push_back( d_references[n][-1][j] ); + } + } + visited[n] = card; + return tn; + }else{ + card = 0; + TypeNode otn; + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + int cardc = 0; + TypeNode tn = getReferenceType2( atom, cardc, index, n[i], visited ); + if( !tn.isNull() ){ + otn = tn; + } + card = cardc>card ? cardc : card; + } + visited[n] = card; + return otn; + } + }else{ + Trace("sep-type-debug") << "already visit : " << n << " : " << atom << " " << index << std::endl; + card = 0; + return TypeNode::null(); + } +} +/* + +int TheorySep::getCardinality( Node n, std::vector< Node >& refs ) { + std::map< Node, int > visited; + return getCardinality2( n, refs, visited ); +} + +int TheorySep::getCardinality2( Node n, std::vector< Node >& refs, std::map< Node, int >& visited ) { + std::map< Node, int >::iterator it = visited.find( n ); + if( it!=visited.end() ){ + return it->second; + }else{ + + + } +} +*/ + +Node TheorySep::getBaseLabel( TypeNode tn ) { + std::map< TypeNode, Node >::iterator it = d_base_label.find( tn ); + if( it==d_base_label.end() ){ + Trace("sep") << "Make base label for " << tn << std::endl; + std::stringstream ss; + ss << "__Lb"; + TypeNode ltn = NodeManager::currentNM()->mkSetType(tn); + //TypeNode ltn = NodeManager::currentNM()->mkSetType(NodeManager::currentNM()->mkRefType(tn)); + Node n_lbl = NodeManager::currentNM()->mkSkolem( ss.str(), ltn, "" ); + d_base_label[tn] = n_lbl; + //make reference bound + Trace("sep") << "Make reference bound label for " << tn << std::endl; + std::stringstream ss2; + ss2 << "__Lu"; + d_reference_bound[tn] = NodeManager::currentNM()->mkSkolem( ss2.str(), ltn, "" ); + d_type_references_all[tn].insert( d_type_references_all[tn].end(), d_type_references[tn].begin(), d_type_references[tn].end() ); + //add a reference type for maximum occurrences of empty in a constraint + unsigned n_emp = d_card_max[tn]>d_card_max[TypeNode::null()] ? d_card_max[tn] : d_card_max[TypeNode::null()]; + for( unsigned r=0; r<n_emp; r++ ){ + Node e = NodeManager::currentNM()->mkSkolem( "e", tn, "cardinality bound element for seplog" ); + //d_type_references_all[tn].push_back( NodeManager::currentNM()->mkSkolem( "e", NodeManager::currentNM()->mkRefType(tn) ) ); + if( options::sepDisequalC() ){ + //ensure that it is distinct from all other references so far + for( unsigned j=0; j<d_type_references_all[tn].size(); j++ ){ + Node eq = NodeManager::currentNM()->mkNode( e.getType().isBoolean() ? kind::IFF : kind::EQUAL, e, d_type_references_all[tn][j] ); + d_out->lemma( eq.negate() ); + } + } + d_type_references_all[tn].push_back( e ); + d_lbl_reference_bound[d_base_label[tn]].push_back( e ); + } + //construct bound + d_reference_bound_max[tn] = mkUnion( tn, d_type_references_all[tn] ); + Trace("sep-bound") << "overall bound for " << d_base_label[tn] << " : " << d_reference_bound_max[tn] << std::endl; + + if( d_reference_bound_invalid.find( tn )==d_reference_bound_invalid.end() ){ + Node slem = NodeManager::currentNM()->mkNode( kind::SUBSET, d_reference_bound[tn], d_reference_bound_max[tn] ); + Trace("sep-lemma") << "Sep::Lemma: reference bound for " << tn << " : " << slem << std::endl; + d_out->lemma( slem ); + }else{ + Trace("sep-bound") << "reference cannot be bound (possibly due to quantified pto)." << std::endl; + } + //slem = NodeManager::currentNM()->mkNode( kind::SUBSET, d_base_label[tn], d_reference_bound_max[tn] ); + //Trace("sep-lemma") << "Sep::Lemma: base reference bound for " << tn << " : " << slem << std::endl; + //d_out->lemma( slem ); + return n_lbl; + }else{ + return it->second; + } +} + +Node TheorySep::getNilRef( TypeNode tn ) { + std::map< TypeNode, Node >::iterator it = d_nil_ref.find( tn ); + if( it==d_nil_ref.end() ){ + Node nil = NodeManager::currentNM()->mkSepNil( tn ); + d_nil_ref[tn] = nil; + return nil; + }else{ + return it->second; + } +} + +Node TheorySep::mkUnion( TypeNode tn, std::vector< Node >& locs ) { + Node u; + if( locs.empty() ){ + TypeNode ltn = NodeManager::currentNM()->mkSetType(tn); + return NodeManager::currentNM()->mkConst(EmptySet(ltn.toType())); + }else{ + for( unsigned i=0; i<locs.size(); i++ ){ + Node s = locs[i]; + Assert( !s.isNull() ); + s = NodeManager::currentNM()->mkNode( kind::SINGLETON, s ); + if( u.isNull() ){ + u = s; + }else{ + u = NodeManager::currentNM()->mkNode( kind::UNION, s, u ); + } + } + return u; + } +} + +Node TheorySep::getLabel( Node atom, int child, Node lbl ) { + std::map< int, Node >::iterator it = d_label_map[atom][lbl].find( child ); + if( it==d_label_map[atom][lbl].end() ){ + int card; + TypeNode refType = getReferenceType( atom, card ); + std::stringstream ss; + ss << "__Lc" << child; + TypeNode ltn = NodeManager::currentNM()->mkSetType(refType); + //TypeNode ltn = NodeManager::currentNM()->mkSetType(NodeManager::currentNM()->mkRefType(refType)); + Node n_lbl = NodeManager::currentNM()->mkSkolem( ss.str(), ltn, "" ); + d_label_map[atom][lbl][child] = n_lbl; + d_label_map_parent[n_lbl] = lbl; + return n_lbl; + }else{ + return (*it).second; + } +} + +Node TheorySep::applyLabel( Node n, Node lbl, std::map< Node, Node >& visited ) { + Assert( n.getKind()!=kind::SEP_LABEL ); + if( n.getKind()==kind::SEP_STAR || n.getKind()==kind::SEP_WAND || n.getKind()==kind::SEP_PTO || n.getKind()==kind::SEP_EMP ){ + return NodeManager::currentNM()->mkNode( kind::SEP_LABEL, n, lbl ); + }else if( !n.getType().isBoolean() || n.getNumChildren()==0 ){ + return n; + }else{ + std::map< Node, Node >::iterator it = visited.find( n ); + if( it==visited.end() ){ + std::vector< Node > children; + if (n.getMetaKind() == kind::metakind::PARAMETERIZED) { + children.push_back( n.getOperator() ); + } + bool childChanged = false; + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + Node aln = applyLabel( n[i], lbl, visited ); + children.push_back( aln ); + childChanged = childChanged || aln!=n[i]; + } + Node ret = n; + if( childChanged ){ + ret = NodeManager::currentNM()->mkNode( n.getKind(), children ); + } + visited[n] = ret; + return ret; + }else{ + return it->second; + } + } +} + +Node TheorySep::instantiateLabel( Node n, Node o_lbl, Node lbl, Node lbl_v, std::map< Node, Node >& visited, std::map< Node, Node >& pto_model, std::map< Node, Node >& tmodel, + TypeNode rtn, std::map< Node, bool >& active_lbl, unsigned ind ) { + Trace("sep-inst-debug") << "Instantiate label " << n << " " << lbl << " " << lbl_v << std::endl; + if( options::sepMinimalRefine() && lbl!=o_lbl && active_lbl.find( lbl )!=active_lbl.end() ){ + Trace("sep-inst") << "...do not instantiate " << o_lbl << " since it has an active sublabel " << lbl << std::endl; + return Node::null(); + }else{ + if( Trace.isOn("sep-inst") ){ + if( n.getKind()==kind::SEP_STAR || n.getKind()==kind::SEP_WAND || n.getKind()==kind::SEP_PTO || n.getKind()==kind::SEP_EMP ){ + for( unsigned j=0; j<ind; j++ ){ Trace("sep-inst") << " "; } + Trace("sep-inst") << n << "[" << lbl << "] :: " << lbl_v << std::endl; + } + } + Assert( n.getKind()!=kind::SEP_LABEL ); + if( n.getKind()==kind::SEP_STAR || n.getKind()==kind::SEP_WAND ){ + if( lbl==o_lbl ){ + std::vector< Node > children; + children.resize( n.getNumChildren() ); + Assert( d_label_map[n].find( lbl )!=d_label_map[n].end() ); + for( std::map< int, Node >::iterator itl = d_label_map[n][lbl].begin(); itl != d_label_map[n][lbl].end(); ++itl ){ + Node sub_lbl = itl->second; + int sub_index = itl->first; + Assert( sub_index>=0 && sub_index<(int)children.size() ); + Trace("sep-inst-debug") << "Sublabel " << sub_index << " is " << sub_lbl << std::endl; + Node lbl_mval; + if( n.getKind()==kind::SEP_WAND && sub_index==1 ){ + Assert( d_label_map[n][lbl].find( 0 )!=d_label_map[n][lbl].end() ); + Node sub_lbl_0 = d_label_map[n][lbl][0]; + computeLabelModel( sub_lbl_0, tmodel ); + Assert( d_label_model.find( sub_lbl_0 )!=d_label_model.end() ); + lbl_mval = NodeManager::currentNM()->mkNode( kind::UNION, lbl, d_label_model[sub_lbl_0].getValue( rtn ) ); + }else{ + computeLabelModel( sub_lbl, tmodel ); + Assert( d_label_model.find( sub_lbl )!=d_label_model.end() ); + lbl_mval = d_label_model[sub_lbl].getValue( rtn ); + } + Trace("sep-inst-debug") << "Sublabel value is " << lbl_mval << std::endl; + children[sub_index] = instantiateLabel( n[sub_index], o_lbl, sub_lbl, lbl_mval, visited, pto_model, tmodel, rtn, active_lbl, ind+1 ); + if( children[sub_index].isNull() ){ + return Node::null(); + } + } + if( n.getKind()==kind::SEP_STAR ){ + Assert( children.size()>1 ); + return NodeManager::currentNM()->mkNode( kind::AND, children ); + }else{ + return NodeManager::currentNM()->mkNode( kind::OR, children[0].negate(), children[1] ); + } + }else{ + //nested star/wand, label it and return + return NodeManager::currentNM()->mkNode( kind::SEP_LABEL, n, lbl_v ); + } + }else if( n.getKind()==kind::SEP_PTO ){ + //check if this pto reference is in the base label, if not, then it does not need to be added as an assumption + Assert( d_label_model.find( o_lbl )!=d_label_model.end() ); + Node vr = d_valuation.getModel()->getRepresentative( n[0] ); + Node svr = NodeManager::currentNM()->mkNode( kind::SINGLETON, vr ); + bool inBaseHeap = std::find( d_label_model[o_lbl].d_heap_locs_model.begin(), d_label_model[o_lbl].d_heap_locs_model.end(), svr )!=d_label_model[o_lbl].d_heap_locs_model.end(); + Trace("sep-inst-debug") << "Is in base (non-instantiating) heap : " << inBaseHeap << " for value ref " << vr << " in " << o_lbl << std::endl; + std::vector< Node > children; + if( inBaseHeap ){ + Node s = NodeManager::currentNM()->mkNode( kind::SINGLETON, n[0] ); + children.push_back( NodeManager::currentNM()->mkNode( kind::SEP_LABEL, NodeManager::currentNM()->mkNode( kind::SEP_PTO, n[0], n[1] ), s ) ); + }else{ + //look up value of data + std::map< Node, Node >::iterator it = pto_model.find( vr ); + if( it!=pto_model.end() ){ + if( n[1]!=it->second ){ + children.push_back( NodeManager::currentNM()->mkNode( n[1].getType().isBoolean() ? kind::IFF : kind::EQUAL, n[1], it->second ) ); + } + }else{ + Trace("sep-inst-debug") << "Data for " << vr << " was not specified, do not add condition." << std::endl; + } + } + children.push_back( NodeManager::currentNM()->mkNode( kind::EQUAL, NodeManager::currentNM()->mkNode( kind::SINGLETON, n[0] ), lbl_v ) ); + Node ret = children.empty() ? NodeManager::currentNM()->mkConst( true ) : ( children.size()==1 ? children[0] : NodeManager::currentNM()->mkNode( kind::AND, children ) ); + Trace("sep-inst-debug") << "Return " << ret << std::endl; + return ret; + }else if( n.getKind()==kind::SEP_EMP ){ + //return NodeManager::currentNM()->mkConst( lbl_v.getKind()==kind::EMPTYSET ); + return lbl_v.eqNode( NodeManager::currentNM()->mkConst(EmptySet(lbl_v.getType().toType())) ); + }else{ + std::map< Node, Node >::iterator it = visited.find( n ); + if( it==visited.end() ){ + std::vector< Node > children; + if (n.getMetaKind() == kind::metakind::PARAMETERIZED) { + children.push_back( n.getOperator() ); + } + bool childChanged = false; + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + Node aln = instantiateLabel( n[i], o_lbl, lbl, lbl_v, visited, pto_model, tmodel, rtn, active_lbl, ind ); + if( aln.isNull() ){ + return Node::null(); + }else{ + children.push_back( aln ); + childChanged = childChanged || aln!=n[i]; + } + } + Node ret = n; + if( childChanged ){ + ret = NodeManager::currentNM()->mkNode( n.getKind(), children ); + } + //careful about caching + //visited[n] = ret; + return ret; + }else{ + return it->second; + } + } + } +} + +void TheorySep::setInactiveAssertionRec( Node fact, std::map< Node, std::vector< Node > >& lbl_to_assertions, std::map< Node, bool >& assert_active ) { + Trace("sep-process-debug") << "setInactiveAssertionRec::inactive : " << fact << std::endl; + assert_active[fact] = false; + bool polarity = fact.getKind() != kind::NOT; + TNode atom = polarity ? fact : fact[0]; + TNode s_atom = atom[0]; + TNode s_lbl = atom[1]; + if( s_atom.getKind()==kind::SEP_WAND || s_atom.getKind()==kind::SEP_STAR ){ + for( unsigned j=0; j<s_atom.getNumChildren(); j++ ){ + Node lblc = getLabel( s_atom, j, s_lbl ); + for( unsigned k=0; k<lbl_to_assertions[lblc].size(); k++ ){ + setInactiveAssertionRec( lbl_to_assertions[lblc][k], lbl_to_assertions, assert_active ); + } + } + } +} + +void TheorySep::getLabelChildren( Node s_atom, Node lbl, std::vector< Node >& children, std::vector< Node >& labels ) { + for( unsigned i=0; i<s_atom.getNumChildren(); i++ ){ + Node lblc = getLabel( s_atom, i, lbl ); + Assert( !lblc.isNull() ); + std::map< Node, Node > visited; + Node lc = applyLabel( s_atom[i], lblc, visited ); + Assert( !lc.isNull() ); + if( i==1 && s_atom.getKind()==kind::SEP_WAND ){ + lc = lc.negate(); + } + children.push_back( lc ); + labels.push_back( lblc ); + } + Assert( children.size()>1 ); +} + +void TheorySep::computeLabelModel( Node lbl, std::map< Node, Node >& tmodel ) { + if( !d_label_model[lbl].d_computed ){ + d_label_model[lbl].d_computed = true; + + //we must get the value of lbl from the model: this is being run at last call, after the model is constructed + //Assert(...); TODO + Node v_val = d_valuation.getModel()->getRepresentative( lbl ); + Trace("sep-process") << "Model value (from valuation) for " << lbl << " : " << v_val << std::endl; + if( v_val.getKind()!=kind::EMPTYSET ){ + while( v_val.getKind()==kind::UNION ){ + Assert( v_val[1].getKind()==kind::SINGLETON ); + d_label_model[lbl].d_heap_locs_model.push_back( v_val[1] ); + v_val = v_val[0]; + } + Assert( v_val.getKind()==kind::SINGLETON ); + d_label_model[lbl].d_heap_locs_model.push_back( v_val ); + } + //end hack + for( unsigned j=0; j<d_label_model[lbl].d_heap_locs_model.size(); j++ ){ + Node u = d_label_model[lbl].d_heap_locs_model[j]; + Assert( u.getKind()==kind::SINGLETON ); + u = u[0]; + Node tt; + std::map< Node, Node >::iterator itm = tmodel.find( u ); + if( itm==tmodel.end() ) { + //Trace("sep-process") << "WARNING: could not find symbolic term in model for " << u << std::endl; + //Assert( false ); + //tt = u; + //TypeNode tn = u.getType().getRefConstituentType(); + TypeNode tn = u.getType(); + Trace("sep-process") << "WARNING: could not find symbolic term in model for " << u << ", cref type " << tn << std::endl; + Assert( d_type_references_all.find( tn )!=d_type_references_all.end() && !d_type_references_all[tn].empty() ); + tt = d_type_references_all[tn][0]; + }else{ + tt = itm->second; + } + Node stt = NodeManager::currentNM()->mkNode( kind::SINGLETON, tt ); + Trace("sep-process-debug") << "...model : add " << tt << " for " << u << " in lbl " << lbl << std::endl; + d_label_model[lbl].d_heap_locs.push_back( stt ); + } + } +} + +Node TheorySep::getRepresentative( Node t ) { + if( d_equalityEngine.hasTerm( t ) ){ + return d_equalityEngine.getRepresentative( t ); + }else{ + return t; + } +} + +bool TheorySep::hasTerm( Node a ){ + return d_equalityEngine.hasTerm( a ); +} + +bool TheorySep::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 TheorySep::areDisequal( Node a, Node b ){ + if( a==b ){ + return false; + }else if( hasTerm( a ) && hasTerm( b ) ){ + if( d_equalityEngine.areDisequal( a, b, false ) ){ + return true; + } + } + return false; +} + + +void TheorySep::eqNotifyPreMerge(TNode t1, TNode t2) { + +} + +void TheorySep::eqNotifyPostMerge(TNode t1, TNode t2) { + HeapAssertInfo * e2 = getOrMakeEqcInfo( t2, false ); + if( e2 && ( !e2->d_pto.get().isNull() || e2->d_has_neg_pto.get() ) ){ + HeapAssertInfo * e1 = getOrMakeEqcInfo( t1, true ); + if( !e2->d_pto.get().isNull() ){ + if( !e1->d_pto.get().isNull() ){ + Trace("sep-pto-debug") << "While merging " << t1 << " " << t2 << ", merge pto." << std::endl; + mergePto( e1->d_pto.get(), e2->d_pto.get() ); + }else{ + e1->d_pto.set( e2->d_pto.get() ); + } + } + e1->d_has_neg_pto.set( e1->d_has_neg_pto.get() || e2->d_has_neg_pto.get() ); + //validate + validatePto( e1, t1 ); + } +} + +void TheorySep::validatePto( HeapAssertInfo * ei, Node ei_n ) { + if( !ei->d_pto.get().isNull() && ei->d_has_neg_pto.get() ){ + for( NodeList::const_iterator i = d_spatial_assertions.begin(); i != d_spatial_assertions.end(); ++i ) { + Node fact = (*i); + bool polarity = fact.getKind() != kind::NOT; + if( !polarity ){ + TNode atom = polarity ? fact : fact[0]; + Assert( atom.getKind()==kind::SEP_LABEL ); + TNode s_atom = atom[0]; + if( s_atom.getKind()==kind::SEP_PTO ){ + if( areEqual( atom[1], ei_n ) ){ + addPto( ei, ei_n, atom, false ); + } + } + } + } + //we have now processed all pending negated pto + ei->d_has_neg_pto.set( false ); + } +} + +void TheorySep::addPto( HeapAssertInfo * ei, Node ei_n, Node p, bool polarity ) { + Trace("sep-pto") << "Add pto " << p << ", pol = " << polarity << " to eqc " << ei_n << std::endl; + if( !ei->d_pto.get().isNull() ){ + if( polarity ){ + Trace("sep-pto-debug") << "...eqc " << ei_n << " already has pto " << ei->d_pto.get() << ", merge." << std::endl; + mergePto( ei->d_pto.get(), p ); + }else{ + Node pb = ei->d_pto.get(); + Trace("sep-pto") << "Process positive/negated pto " << " " << pb << " " << p << std::endl; + Assert( pb.getKind()==kind::SEP_LABEL && pb[0].getKind()==kind::SEP_PTO ); + Assert( p.getKind()==kind::SEP_LABEL && p[0].getKind()==kind::SEP_PTO ); + Assert( areEqual( pb[1], p[1] ) ); + std::vector< Node > exp; + if( pb[1]!=p[1] ){ + exp.push_back( pb[1].eqNode( p[1] ) ); + } + exp.push_back( pb ); + exp.push_back( p.negate() ); + std::vector< Node > conc; + if( pb[0][1]!=p[0][1] ){ + conc.push_back( pb[0][1].eqNode( p[0][1] ).negate() ); + } + if( pb[1]!=p[1] ){ + conc.push_back( pb[1].eqNode( p[1] ).negate() ); + } + Node n_conc = conc.empty() ? d_false : ( conc.size()==1 ? conc[0] : NodeManager::currentNM()->mkNode( kind::OR, conc ) ); + sendLemma( exp, n_conc, "PTO_NEG_PROP" ); + } + }else{ + if( polarity ){ + ei->d_pto.set( p ); + validatePto( ei, ei_n ); + }else{ + ei->d_has_neg_pto.set( true ); + } + } +} + +void TheorySep::mergePto( Node p1, Node p2 ) { + Trace("sep-lemma-debug") << "Merge pto " << p1 << " " << p2 << std::endl; + Assert( p1.getKind()==kind::SEP_LABEL && p1[0].getKind()==kind::SEP_PTO ); + Assert( p2.getKind()==kind::SEP_LABEL && p2[0].getKind()==kind::SEP_PTO ); + if( !areEqual( p1[0][1], p2[0][1] ) ){ + std::vector< Node > exp; + if( p1[1]!=p2[1] ){ + Assert( areEqual( p1[1], p2[1] ) ); + exp.push_back( p1[1].eqNode( p2[1] ) ); + } + exp.push_back( p1 ); + exp.push_back( p2 ); + sendLemma( exp, p1[0][1].eqNode( p2[0][1] ), "PTO_PROP" ); + } +} + +void TheorySep::sendLemma( std::vector< Node >& ant, Node conc, const char * c, bool infer ) { + Trace("sep-lemma-debug") << "Do rewrite on inference : " << conc << std::endl; + conc = Rewriter::rewrite( conc ); + Trace("sep-lemma-debug") << "Got : " << conc << std::endl; + if( conc!=d_true ){ + if( infer && conc!=d_false ){ + Node ant_n; + if( ant.empty() ){ + ant_n = d_true; + }else if( ant.size()==1 ){ + ant_n = ant[0]; + }else{ + ant_n = NodeManager::currentNM()->mkNode( kind::AND, ant ); + } + Trace("sep-lemma") << "Sep::Infer: " << conc << " from " << ant_n << " by " << c << std::endl; + d_pending_exp.push_back( ant_n ); + d_pending.push_back( conc ); + d_infer.push_back( ant_n ); + d_infer_exp.push_back( conc ); + }else{ + std::vector< TNode > ant_e; + for( unsigned i=0; i<ant.size(); i++ ){ + Trace("sep-lemma-debug") << "Explain : " << ant[i] << std::endl; + explain( ant[i], ant_e ); + } + Node ant_n; + if( ant_e.empty() ){ + ant_n = d_true; + }else if( ant_e.size()==1 ){ + ant_n = ant_e[0]; + }else{ + ant_n = NodeManager::currentNM()->mkNode( kind::AND, ant_e ); + } + if( conc==d_false ){ + Trace("sep-lemma") << "Sep::Conflict: " << ant_n << " by " << c << std::endl; + d_out->conflict( ant_n ); + d_conflict = true; + }else{ + Trace("sep-lemma") << "Sep::Lemma: " << conc << " from " << ant_n << " by " << c << std::endl; + d_pending_exp.push_back( ant_n ); + d_pending.push_back( conc ); + d_pending_lem.push_back( d_pending.size()-1 ); + } + } + } +} + +void TheorySep::doPendingFacts() { + if( d_pending_lem.empty() ){ + for( unsigned i=0; i<d_pending.size(); i++ ){ + if( d_conflict ){ + break; + } + Node atom = d_pending[i].getKind()==kind::NOT ? d_pending[i][0] : d_pending[i]; + bool pol = d_pending[i].getKind()!=kind::NOT; + Trace("sep-pending") << "Sep : Assert to EE : " << atom << ", pol = " << pol << std::endl; + if( atom.getKind()==kind::EQUAL ){ + d_equalityEngine.assertEquality(atom, pol, d_pending_exp[i]); + }else{ + d_equalityEngine.assertPredicate(atom, pol, d_pending_exp[i]); + } + } + }else{ + for( unsigned i=0; i<d_pending_lem.size(); i++ ){ + if( d_conflict ){ + break; + } + int index = d_pending_lem[i]; + Node lem = NodeManager::currentNM()->mkNode( kind::IMPLIES, d_pending_exp[index], d_pending[index] ); + d_out->lemma( lem ); + Trace("sep-pending") << "Sep : Lemma : " << lem << std::endl; + } + } + d_pending_exp.clear(); + d_pending.clear(); + d_pending_lem.clear(); +} + +void TheorySep::debugPrintHeap( HeapInfo& heap, const char * c ) { + Trace(c) << "[" << std::endl; + Trace(c) << " "; + for( unsigned j=0; j<heap.d_heap_locs.size(); j++ ){ + Trace(c) << heap.d_heap_locs[j] << " "; + } + Trace(c) << std::endl; + Trace(c) << "]" << std::endl; +} + +Node TheorySep::HeapInfo::getValue( TypeNode tn ) { + Assert( d_heap_locs.size()==d_heap_locs_model.size() ); + if( d_heap_locs.empty() ){ + return NodeManager::currentNM()->mkConst(EmptySet(tn.toType())); + }else if( d_heap_locs.size()==1 ){ + return d_heap_locs[0]; + }else{ + Node curr = NodeManager::currentNM()->mkNode( kind::UNION, d_heap_locs[0], d_heap_locs[1] ); + for( unsigned j=2; j<d_heap_locs.size(); j++ ){ + curr = NodeManager::currentNM()->mkNode( kind::UNION, curr, d_heap_locs[j] ); + } + return curr; + } +} + +}/* CVC4::theory::sep namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/sep/theory_sep.h b/src/theory/sep/theory_sep.h new file mode 100644 index 000000000..852a36721 --- /dev/null +++ b/src/theory/sep/theory_sep.h @@ -0,0 +1,294 @@ +/********************* */ +/*! \file theory_sep.h + ** \verbatim + ** Original author: Andrew Reynolds + ** Major contributors: Dejan Jovanovic, Clark Barrett + ** Minor contributors (to current version): Tim King, Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2014 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Theory of sep + ** + ** Theory of sep. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__SEP__THEORY_SEP_H +#define __CVC4__THEORY__SEP__THEORY_SEP_H + +#include "theory/theory.h" +#include "util/statistics_registry.h" +#include "theory/uf/equality_engine.h" +#include "context/cdchunk_list.h" +#include "context/cdhashmap.h" +#include "context/cdhashset.h" +#include "context/cdqueue.h" + +namespace CVC4 { +namespace theory { + +class TheoryModel; + +namespace sep { + +class TheorySep : public Theory { + typedef context::CDChunkList<Node> NodeList; + typedef context::CDHashSet<Node, NodeHashFunction> NodeSet; + typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap; + + ///////////////////////////////////////////////////////////////////////////// + // MISC + ///////////////////////////////////////////////////////////////////////////// + + private: + + /** True node for predicates = true */ + Node d_true; + + /** True node for predicates = false */ + Node d_false; + + Node mkAnd( std::vector< TNode >& assumptions ); + + void processAssertion( Node n, std::map< Node, bool >& visited ); + + public: + + TheorySep(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo); + ~TheorySep(); + + void setMasterEqualityEngine(eq::EqualityEngine* eq); + + std::string identify() const { return std::string("TheorySep"); } + + ///////////////////////////////////////////////////////////////////////////// + // PREPROCESSING + ///////////////////////////////////////////////////////////////////////////// + + public: + + PPAssertStatus ppAssert(TNode in, SubstitutionMap& outSubstitutions); + Node ppRewrite(TNode atom); + + void processAssertions( std::vector< Node >& assertions ); + ///////////////////////////////////////////////////////////////////////////// + // T-PROPAGATION / REGISTRATION + ///////////////////////////////////////////////////////////////////////////// + + private: + + /** Should be called to propagate the literal. */ + bool propagate(TNode literal); + + /** Explain why this literal is true by adding assumptions */ + void explain(TNode literal, std::vector<TNode>& assumptions); + + void preRegisterTermRec(TNode t, std::map< TNode, bool >& visited ); + public: + + void preRegisterTerm(TNode t); + void propagate(Effort e); + Node explain(TNode n); + + public: + + void addSharedTerm(TNode t); + EqualityStatus getEqualityStatus(TNode a, TNode b); + void computeCareGraph(); + + ///////////////////////////////////////////////////////////////////////////// + // MODEL GENERATION + ///////////////////////////////////////////////////////////////////////////// + + public: + + void collectModelInfo(TheoryModel* m, bool fullModel); + + ///////////////////////////////////////////////////////////////////////////// + // NOTIFICATIONS + ///////////////////////////////////////////////////////////////////////////// + + private: + public: + + Node getNextDecisionRequest(); + + void presolve(); + void shutdown() { } + + ///////////////////////////////////////////////////////////////////////////// + // MAIN SOLVER + ///////////////////////////////////////////////////////////////////////////// + public: + + void check(Effort e); + + private: + + // NotifyClass: template helper class for d_equalityEngine - handles call-back from congruence closure module + class NotifyClass : public eq::EqualityEngineNotify { + TheorySep& d_sep; + public: + NotifyClass(TheorySep& sep): d_sep(sep) {} + + bool eqNotifyTriggerEquality(TNode equality, bool value) { + Debug("sep::propagate") << "NotifyClass::eqNotifyTriggerEquality(" << equality << ", " << (value ? "true" : "false") << ")" << std::endl; + // Just forward to sep + if (value) { + return d_sep.propagate(equality); + } else { + return d_sep.propagate(equality.notNode()); + } + } + + bool eqNotifyTriggerPredicate(TNode predicate, bool value) { + Unreachable(); + } + + bool eqNotifyTriggerTermEquality(TheoryId tag, TNode t1, TNode t2, bool value) { + Debug("sep::propagate") << "NotifyClass::eqNotifyTriggerTermEquality(" << t1 << ", " << t2 << ", " << (value ? "true" : "false") << ")" << std::endl; + if (value) { + // Propagate equality between shared terms + return d_sep.propagate(t1.eqNode(t2)); + } else { + return d_sep.propagate(t1.eqNode(t2).notNode()); + } + return true; + } + + void eqNotifyConstantTermMerge(TNode t1, TNode t2) { + Debug("sep::propagate") << "NotifyClass::eqNotifyConstantTermMerge(" << t1 << ", " << t2 << ")" << std::endl; + d_sep.conflict(t1, t2); + } + + void eqNotifyNewClass(TNode t) { } + void eqNotifyPreMerge(TNode t1, TNode t2) { d_sep.eqNotifyPreMerge( t1, t2 ); } + void eqNotifyPostMerge(TNode t1, TNode t2) { d_sep.eqNotifyPostMerge( t1, t2 ); } + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { } + }; + + /** The notify class for d_equalityEngine */ + NotifyClass d_notify; + + /** Equaltity engine */ + eq::EqualityEngine d_equalityEngine; + + /** Are we in conflict? */ + context::CDO<bool> d_conflict; + std::vector< Node > d_pending_exp; + std::vector< Node > d_pending; + std::vector< int > d_pending_lem; + + /** list of all refinement lemms */ + std::map< Node, std::map< Node, std::vector< Node > > > d_refinement_lem; + + /** Conflict when merging constants */ + void conflict(TNode a, TNode b); + + //cache for positive polarity start reduction + NodeSet d_reduce; + std::map< Node, std::map< Node, Node > > d_red_conc; + std::map< Node, std::map< Node, Node > > d_neg_guard; + std::vector< Node > d_neg_guards; + std::map< Node, Node > d_guard_to_assertion; + //cache for references + std::map< Node, std::map< int, TypeNode > > d_reference_type; + std::map< Node, std::map< int, int > > d_reference_type_card; + std::map< Node, std::map< int, std::vector< Node > > > d_references; + /** inferences: maintained to ensure ref count for internally introduced nodes */ + NodeList d_infer; + NodeList d_infer_exp; + NodeList d_spatial_assertions; + + //currently fix one data type for each location type, throw error if using more than one + std::map< TypeNode, TypeNode > d_loc_to_data_type; + //information about types + std::map< TypeNode, Node > d_base_label; + std::map< TypeNode, Node > d_nil_ref; + //reference bound + std::map< TypeNode, Node > d_reference_bound; + std::map< TypeNode, Node > d_reference_bound_max; + std::map< TypeNode, bool > d_reference_bound_invalid; + std::map< TypeNode, std::vector< Node > > d_type_references; + std::map< TypeNode, std::vector< Node > > d_type_references_all; + std::map< TypeNode, unsigned > d_card_max; + //bounds for labels + std::map< Node, std::vector< Node > > d_lbl_reference_bound; + //for empty argument + std::map< TypeNode, Node > d_emp_arg; + //map from ( atom, label, child index ) -> label + std::map< Node, std::map< Node, std::map< int, Node > > > d_label_map; + std::map< Node, Node > d_label_map_parent; + + //term model + std::map< Node, Node > d_tmodel; + std::map< Node, Node > d_pto_model; + + class HeapAssertInfo { + public: + HeapAssertInfo( context::Context* c ); + ~HeapAssertInfo(){} + context::CDO< Node > d_pto; + context::CDO< bool > d_has_neg_pto; + }; + std::map< Node, HeapAssertInfo * > d_eqc_info; + HeapAssertInfo * getOrMakeEqcInfo( Node n, bool doMake = false ); + + //calculate the element type of the heap for spatial assertions + TypeNode getReferenceType( Node atom, int& card, int index = -1 ); + TypeNode getReferenceType2( Node atom, int& card, int index, Node n, std::map< Node, int >& visited); + //get the base label for the spatial assertion + Node getBaseLabel( TypeNode tn ); + Node getNilRef( TypeNode tn ); + Node getLabel( Node atom, int child, Node lbl ); + Node applyLabel( Node n, Node lbl, std::map< Node, Node >& visited ); + void getLabelChildren( Node atom, Node lbl, std::vector< Node >& children, std::vector< Node >& labels ); + + class HeapInfo { + public: + HeapInfo() : d_computed(false) {} + //information about the model + bool d_computed; + std::vector< Node > d_heap_locs; + std::vector< Node > d_heap_locs_model; + //get value + Node getValue( TypeNode tn ); + }; + //heap info ( label -> HeapInfo ) + std::map< Node, HeapInfo > d_label_model; + + void debugPrintHeap( HeapInfo& heap, const char * c ); + void validatePto( HeapAssertInfo * ei, Node ei_n ); + void addPto( HeapAssertInfo * ei, Node ei_n, Node p, bool polarity ); + void mergePto( Node p1, Node p2 ); + void computeLabelModel( Node lbl, std::map< Node, Node >& tmodel ); + Node instantiateLabel( Node n, Node o_lbl, Node lbl, Node lbl_v, std::map< Node, Node >& visited, std::map< Node, Node >& pto_model, std::map< Node, Node >& tmodel, + TypeNode rtn, std::map< Node, bool >& active_lbl, unsigned ind = 0 ); + void setInactiveAssertionRec( Node fact, std::map< Node, std::vector< Node > >& lbl_to_assertions, std::map< Node, bool >& assert_active ); + + Node mkUnion( TypeNode tn, std::vector< Node >& locs ); +private: + Node getRepresentative( Node t ); + bool hasTerm( Node a ); + bool areEqual( Node a, Node b ); + bool areDisequal( Node a, Node b ); + void eqNotifyPreMerge(TNode t1, TNode t2); + void eqNotifyPostMerge(TNode t1, TNode t2); + + void sendLemma( std::vector< Node >& ant, Node conc, const char * c, bool infer = false ); + void doPendingFacts(); +public: + eq::EqualityEngine* getEqualityEngine() { + return &d_equalityEngine; + } + +};/* class TheorySep */ + +}/* CVC4::theory::sep namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__SEP__THEORY_SEP_H */ diff --git a/src/theory/sep/theory_sep_rewriter.cpp b/src/theory/sep/theory_sep_rewriter.cpp new file mode 100644 index 000000000..d58c2c13d --- /dev/null +++ b/src/theory/sep/theory_sep_rewriter.cpp @@ -0,0 +1,184 @@ +/********************* */ +/*! \file theory_sep_rewriter.cpp + ** \verbatim + ** Original author: Andrew Reynolds + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2014 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief [[ Add one-line brief description here ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#include "expr/attribute.h" +#include "theory/sep/theory_sep_rewriter.h" + +namespace CVC4 { +namespace theory { +namespace sep { + +void TheorySepRewriter::getStarChildren( Node n, std::vector< Node >& s_children, std::vector< Node >& ns_children ){ + Assert( n.getKind()==kind::SEP_STAR ); + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + if( n[i].getKind()==kind::SEP_EMP ){ + s_children.push_back( n[i] ); + }else if( n[i].getKind()==kind::SEP_STAR ){ + getStarChildren( n[i], s_children, ns_children ); + }else if( n[i].getKind()==kind::SEP_PTO ){ + s_children.push_back( n[i] ); + }else{ + std::vector< Node > temp_s_children; + getAndChildren( n[i], temp_s_children, ns_children ); + Node to_add; + if( temp_s_children.size()==0 ){ + to_add = NodeManager::currentNM()->mkConst( true ); + }else{ + //remove empty star + std::vector< Node > temp_s_children2; + for( unsigned i=0; i<temp_s_children.size(); i++ ){ + if( temp_s_children[i].getKind()!=kind::SEP_EMP ){ + temp_s_children2.push_back( temp_s_children[i] ); + } + } + if( temp_s_children2.size()==1 ){ + to_add = temp_s_children2[0]; + }else if( temp_s_children2.size()>1 ){ + to_add = NodeManager::currentNM()->mkNode( kind::AND, temp_s_children2 ); + } + } + if( !to_add.isNull() ){ + //flatten star + if( to_add.getKind()==kind::SEP_STAR ){ + getStarChildren( to_add, s_children, ns_children ); + }else if( std::find( s_children.begin(), s_children.end(), to_add )==s_children.end() ){ + s_children.push_back( to_add ); + } + } + } + } +} + +void TheorySepRewriter::getAndChildren( Node n, std::vector< Node >& s_children, std::vector< Node >& ns_children ) { + if( n.getKind()==kind::AND ){ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + getAndChildren( n[i], s_children, ns_children ); + } + }else{ + std::map< Node, bool > visited; + if( isSpatial( n, visited ) ){ + if( std::find( s_children.begin(), s_children.end(), n )==s_children.end() ){ + s_children.push_back( n ); + } + }else{ + if( std::find( ns_children.begin(), ns_children.end(), n )==ns_children.end() ){ + if( n!=NodeManager::currentNM()->mkConst(true) ){ + ns_children.push_back( n ); + } + } + } + } +} + +bool TheorySepRewriter::isSpatial( Node n, std::map< Node, bool >& visited ) { + if( visited.find( n )==visited.end() ){ + visited[n] = true; + if( n.getKind()==kind::SEP_STAR || n.getKind()==kind::SEP_PTO || n.getKind()==kind::SEP_EMP || n.getKind()==kind::SEP_LABEL ){ + return true; + }else if( n.getType().isBoolean() ){ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + if( isSpatial( n[i], visited ) ){ + return true; + } + } + } + } + return false; +} + +RewriteResponse TheorySepRewriter::postRewrite(TNode node) { + Trace("sep-postrewrite") << "Sep::postRewrite start " << node << std::endl; + Node retNode = node; + switch (node.getKind()) { + case kind::SEP_LABEL: { + if( node[0].getKind()==kind::SEP_PTO ){ + Node s = NodeManager::currentNM()->mkNode( kind::SINGLETON, node[0][0] ); + if( node[1]!=s ){ + Node c1 = node[1].eqNode( s ); + Node c2 = NodeManager::currentNM()->mkNode( kind::SEP_LABEL, NodeManager::currentNM()->mkNode( kind::SEP_PTO, node[0][0], node[0][1] ), s ); + retNode = NodeManager::currentNM()->mkNode( kind::AND, c1, c2 ); + } + } + if( node[0].getKind()==kind::SEP_EMP ){ + retNode = node[1].eqNode( NodeManager::currentNM()->mkConst(EmptySet(node[1].getType().toType())) ); + } + break; + } + case kind::SEP_PTO: { + break; + } + case kind::SEP_STAR: { + //flatten + std::vector< Node > s_children; + std::vector< Node > ns_children; + getStarChildren( node, s_children, ns_children ); + if( !s_children.empty() ){ + Node schild; + if( s_children.size()==1 ) { + schild = s_children[0]; + }else{ + schild = NodeManager::currentNM()->mkNode( kind::SEP_STAR, s_children ); + } + ns_children.push_back( schild ); + } + Assert( !ns_children.empty() ); + if( ns_children.size()==1 ){ + retNode = ns_children[0]; + }else{ + retNode = NodeManager::currentNM()->mkNode( kind::AND, ns_children ); + } + break; + } + case kind::EQUAL: + case kind::IFF: { + if(node[0] == node[1]) { + return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkConst(true)); + } + else if (node[0].isConst() && node[1].isConst()) { + return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkConst(false)); + } + if (node[0] > node[1]) { + Node newNode = NodeManager::currentNM()->mkNode(node.getKind(), node[1], node[0]); + return RewriteResponse(REWRITE_DONE, newNode); + } + break; + } + default: + break; + } + if( node!=retNode ){ + Trace("sep-rewrite") << "Sep::rewrite : " << node << " -> " << retNode << std::endl; + } + return RewriteResponse(node==retNode ? REWRITE_DONE : REWRITE_AGAIN_FULL, retNode); +} + + +/* +RewriteResponse TheorySepRewriter::preRewrite(TNode node) { + if( node.getKind()==kind::SEP_EMP ){ + Trace("sep-prerewrite") << "Sep::preRewrite emp star " << std::endl; + return RewriteResponse(REWRITE_DONE, NodeManager::currentNM()->mkNode( kind::SEP_EMP, NodeManager::currentNM()->mkConst( true ) ) ); + }else{ + Trace("sep-prerewrite") << "Sep::preRewrite returning " << node << std::endl; + return RewriteResponse(REWRITE_DONE, node); + } +} +*/ + +}/* CVC4::theory::sep namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ diff --git a/src/theory/sep/theory_sep_rewriter.h b/src/theory/sep/theory_sep_rewriter.h new file mode 100644 index 000000000..02adbebe5 --- /dev/null +++ b/src/theory/sep/theory_sep_rewriter.h @@ -0,0 +1,53 @@ +/********************* */ +/*! \file theory_sep_rewriter.h + ** \verbatim + ** Original author: Andrew Reynolds + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2014 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief [[ Add one-line brief description here ]] + ** + ** [[ Add lengthier description here ]] + ** \todo document this file + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__SEP__THEORY_SEP_REWRITER_H +#define __CVC4__THEORY__SEP__THEORY_SEP_REWRITER_H + +#include "theory/rewriter.h" +#include "theory/type_enumerator.h" + +namespace CVC4 { +namespace theory { +namespace sep { + + +class TheorySepRewriter { +private: + static void getStarChildren( Node n, std::vector< Node >& s_children, std::vector< Node >& ns_children ); + static void getAndChildren( Node n, std::vector< Node >& s_children, std::vector< Node >& ns_children ); + static bool isSpatial( Node n, std::map< Node, bool >& visited ); +public: + + static RewriteResponse postRewrite(TNode node); + static inline RewriteResponse preRewrite(TNode node) { + Trace("sep-prerewrite") << "Sep::preRewrite returning " << node << std::endl; + return RewriteResponse(REWRITE_DONE, node); + } + + static inline void init() {} + static inline void shutdown() {} + +};/* class TheorySepRewriter */ + +}/* CVC4::theory::sep namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__SEP__THEORY_SEP_REWRITER_H */ diff --git a/src/theory/sep/theory_sep_type_rules.h b/src/theory/sep/theory_sep_type_rules.h new file mode 100644 index 000000000..7d4eb303e --- /dev/null +++ b/src/theory/sep/theory_sep_type_rules.h @@ -0,0 +1,114 @@ +/********************* */ +/*! \file theory_sep_type_rules.h + ** \verbatim + ** Original author: Andrew Reynolds + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2014 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief Typing and cardinality rules for the theory of sep + ** + ** Typing and cardinality rules for the theory of sep. + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__SEP__THEORY_SEP_TYPE_RULES_H +#define __CVC4__THEORY__SEP__THEORY_SEP_TYPE_RULES_H + +#include "theory/type_enumerator.h" + +namespace CVC4 { +namespace theory { +namespace sep { + +class SepNilRefTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + return TypeNode::fromType( n.getConst<NilRef>().getType() ); + } +}; + +class SepEmpTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + Assert(n.getKind() == kind::SEP_EMP); + return nodeManager->booleanType(); + } +}; + +struct SepPtoTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + Assert(n.getKind() == kind::SEP_PTO); + if( check ) { + TypeNode refType = n[0].getType(check); + TypeNode ptType = n[1].getType(check); + } + return nodeManager->booleanType(); + } +};/* struct SepSelectTypeRule */ + +struct SepStarTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + TypeNode btype = nodeManager->booleanType(); + Assert(n.getKind() == kind::SEP_STAR); + if( check ){ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + TypeNode ctype = n[i].getType( check ); + if( ctype!=btype ){ + throw TypeCheckingExceptionPrivate(n, "child of sep star is not Boolean"); + } + } + } + return btype; + } +};/* struct SepStarTypeRule */ + +struct SepWandTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + TypeNode btype = nodeManager->booleanType(); + Assert(n.getKind() == kind::SEP_WAND); + if( check ){ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + TypeNode ctype = n[i].getType( check ); + if( ctype!=btype ){ + throw TypeCheckingExceptionPrivate(n, "child of sep magic wand is not Boolean"); + } + } + } + return btype; + } +};/* struct SepWandTypeRule */ + +struct SepLabelTypeRule { + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + TypeNode btype = nodeManager->booleanType(); + Assert(n.getKind() == kind::SEP_LABEL); + if( check ){ + TypeNode ctype = n[0].getType( check ); + if( ctype!=btype ){ + throw TypeCheckingExceptionPrivate(n, "child of sep label is not Boolean"); + } + TypeNode stype = n[1].getType( check ); + if( !stype.isSet() ){ + throw TypeCheckingExceptionPrivate(n, "label of sep label is not a set"); + } + } + return btype; + } +};/* struct SepLabelTypeRule */ + +}/* CVC4::theory::sep namespace */ +}/* CVC4::theory namespace */ +}/* CVC4 namespace */ + +#endif /* __CVC4__THEORY__SEP__THEORY_SEP_TYPE_RULES_H */ diff --git a/src/theory/sets/kinds b/src/theory/sets/kinds index 3fb73749d..c92eab4bd 100644 --- a/src/theory/sets/kinds +++ b/src/theory/sets/kinds @@ -64,6 +64,7 @@ typerule PRODUCT ::CVC4::theory::sets::RelBinaryOperatorTypeRule typerule TRANSPOSE ::CVC4::theory::sets::RelTransposeTypeRule typerule TCLOSURE ::CVC4::theory::sets::RelTransClosureTypeRule + construle UNION ::CVC4::theory::sets::SetsBinaryOperatorTypeRule construle INTERSECTION ::CVC4::theory::sets::SetsBinaryOperatorTypeRule construle SETMINUS ::CVC4::theory::sets::SetsBinaryOperatorTypeRule diff --git a/src/theory/sets/theory_sets_private.h b/src/theory/sets/theory_sets_private.h index 217432670..37071eb2e 100644 --- a/src/theory/sets/theory_sets_private.h +++ b/src/theory/sets/theory_sets_private.h @@ -139,6 +139,20 @@ private: */ bool lemma(Node n, SetsLemmaTag t); + /** send out a lemma */ + enum SetsLemmaTag { + SETS_LEMMA_DISEQUAL, + SETS_LEMMA_MEMBER, + SETS_LEMMA_GRAPH, + SETS_LEMMA_OTHER + }; + + /** + * returns true if a lemmas was generated + * returns false otherwise (found in cache) + */ + bool lemma(Node n, SetsLemmaTag t); + class TermInfoManager { TheorySetsPrivate& d_theory; context::Context* d_context; diff --git a/src/theory/sets/theory_sets_type_rules.h b/src/theory/sets/theory_sets_type_rules.h index 92d6c9b6d..478dcbdb6 100644 --- a/src/theory/sets/theory_sets_type_rules.h +++ b/src/theory/sets/theory_sets_type_rules.h @@ -275,6 +275,7 @@ struct RelTransClosureTypeRule { } };/* struct RelTransClosureTypeRule */ + struct SetsProperties { inline static Cardinality computeCardinality(TypeNode type) { Assert(type.getKind() == kind::SET_TYPE); diff --git a/src/theory/strings/regexp_operation.cpp b/src/theory/strings/regexp_operation.cpp index 53344dd6c..a665a02c1 100644 --- a/src/theory/strings/regexp_operation.cpp +++ b/src/theory/strings/regexp_operation.cpp @@ -39,6 +39,10 @@ RegExpOpr::RegExpOpr() d_sigma_star = NodeManager::currentNM()->mkNode( kind::REGEXP_STAR, d_sigma ); } +RegExpOpr::~RegExpOpr(){ + +} + int RegExpOpr::gcd ( int a, int b ) { int c; while ( a != 0 ) { diff --git a/src/theory/strings/regexp_operation.h b/src/theory/strings/regexp_operation.h index c537553f2..075391370 100644 --- a/src/theory/strings/regexp_operation.h +++ b/src/theory/strings/regexp_operation.h @@ -99,6 +99,7 @@ private: public: RegExpOpr(); + ~RegExpOpr(); bool checkConstRegExp( Node r ); void simplify(Node t, std::vector< Node > &new_nodes, bool polarity); diff --git a/src/theory/strings/theory_strings.cpp b/src/theory/strings/theory_strings.cpp index b3e1925ae..57344236e 100644 --- a/src/theory/strings/theory_strings.cpp +++ b/src/theory/strings/theory_strings.cpp @@ -28,6 +28,7 @@ #include "theory/strings/type_enumerator.h" #include "theory/theory_model.h" #include "theory/valuation.h" +#include "theory/quantifiers/term_database.h" using namespace std; using namespace CVC4::context; @@ -74,9 +75,11 @@ TheoryStrings::TheoryStrings(context::Context* c, context::UserContext* u, d_preproc(u), d_preproc_cache(u), d_extf_infer_cache(c), + d_ee_disequalities(c), d_congruent(c), d_proxy_var(u), d_proxy_var_to_length(u), + d_functionsTerms(c), d_neg_ctn_eqlen(c), d_neg_ctn_ulen(c), d_neg_ctn_cached(u), @@ -124,7 +127,9 @@ TheoryStrings::TheoryStrings(context::Context* c, context::UserContext* u, } TheoryStrings::~TheoryStrings() { - + for( std::map< Node, EqcInfo* >::iterator it = d_eqc_info.begin(); it != d_eqc_info.end(); ++it ){ + delete it->second; + } } Node TheoryStrings::getRepresentative( Node t ) { @@ -467,18 +472,30 @@ void TheoryStrings::preRegisterTerm(TNode n) { break; } default: { - if( n.getType().isString() ) { + TypeNode tn = n.getType(); + if( tn.isString() ) { registerTerm( n, 0 ); // FMF if( n.getKind() == kind::VARIABLE && options::stringFMF() ){ d_input_vars.insert(n); } - } else if (n.getType().isBoolean()) { + d_equalityEngine.addTerm(n); + } else if (tn.isBoolean()) { // Get triggered for both equal and dis-equal d_equalityEngine.addTriggerPredicate(n); } else { // Function applications/predicates d_equalityEngine.addTerm(n); + if( options::stringExp() ){ + //collect extended functions here: some may not be asserted to strings (such as those with return type Int), + // but we need to record them so they are treated properly + std::map< Node, bool > visited; + collectExtendedFuncTerms( n, visited ); + } + } + //concat terms do not contribute to theory combination? TODO: verify + if( n.hasOperator() && kindToTheoryId( n.getKind() )==THEORY_STRINGS && n.getKind()!=kind::STRING_CONCAT ){ + d_functionsTerms.push_back( n ); } } } @@ -486,6 +503,7 @@ void TheoryStrings::preRegisterTerm(TNode n) { } Node TheoryStrings::expandDefinition(LogicRequest &logicRequest, Node node) { + Trace("strings-exp-def") << "TheoryStrings::expandDefinition : " << node << std::endl; return node; } @@ -740,53 +758,23 @@ void TheoryStrings::eqNotifyNewClass(TNode t){ /** 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( !e2->d_normalized_length.get().isNull() ){ - e1->d_normalized_length.set( e2->d_normalized_length ); - } + 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( 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 Empty : " << eq << " from " << eq_exp << std::endl; - d_infer.push_back(eq); - d_infer_exp.push_back(eq_exp); - } - } - ++eqc_i; - } - } + 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( !e2->d_normalized_length.get().isNull() ){ + e1->d_normalized_length.set( e2->d_normalized_length ); + } + } } /** called when two equivalance classes have merged */ @@ -796,11 +784,106 @@ void TheoryStrings::eqNotifyPostMerge(TNode t1, TNode t2) { /** called when two equivalance classes are disequal */ void TheoryStrings::eqNotifyDisequal(TNode t1, TNode t2, TNode reason) { + if( t1.getType().isString() ){ + //store disequalities between strings, may need to check if their lengths are equal/disequal + d_ee_disequalities.push_back( t1.eqNode( t2 ) ); + } +} +void TheoryStrings::addCarePairs( quantifiers::TermArgTrie * t1, quantifiers::TermArgTrie * t2, unsigned arity, unsigned depth ) { + if( depth==arity ){ + if( t2!=NULL ){ + Node f1 = t1->getNodeData(); + Node f2 = t2->getNodeData(); + if( !d_equalityEngine.areEqual( f1, f2 ) ){ + Trace("strings-cg-debug") << "TheoryStrings::computeCareGraph(): checking function " << f1 << " and " << f2 << std::endl; + vector< pair<TNode, TNode> > currentPairs; + for (unsigned k = 0; k < f1.getNumChildren(); ++ k) { + TNode x = f1[k]; + TNode y = f2[k]; + Assert( d_equalityEngine.hasTerm(x) ); + Assert( d_equalityEngine.hasTerm(y) ); + Assert( !d_equalityEngine.areDisequal( x, y, false ) ); + if( !d_equalityEngine.areEqual( x, y ) ){ + if( d_equalityEngine.isTriggerTerm(x, THEORY_STRINGS) && d_equalityEngine.isTriggerTerm(y, THEORY_STRINGS) ){ + TNode x_shared = d_equalityEngine.getTriggerTermRepresentative(x, THEORY_STRINGS); + TNode y_shared = d_equalityEngine.getTriggerTermRepresentative(y, THEORY_STRINGS); + EqualityStatus eqStatus = d_valuation.getEqualityStatus(x_shared, y_shared); + if( eqStatus==EQUALITY_FALSE_AND_PROPAGATED || eqStatus==EQUALITY_FALSE || eqStatus==EQUALITY_FALSE_IN_MODEL ){ + //an argument is disequal, we are done + return; + }else{ + currentPairs.push_back(make_pair(x_shared, y_shared)); + } + } + } + } + for (unsigned c = 0; c < currentPairs.size(); ++ c) { + Trace("strings-cg-pair") << "TheoryStrings::computeCareGraph(): pair : " << currentPairs[c].first << " " << currentPairs[c].second << std::endl; + Trace("ajr-temp") << currentPairs[c].first << ", " << currentPairs[c].second << std::endl; + addCarePair(currentPairs[c].first, currentPairs[c].second); + } + } + } + }else{ + if( t2==NULL ){ + if( depth<(arity-1) ){ + //add care pairs internal to each child + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = t1->d_data.begin(); it != t1->d_data.end(); ++it ){ + addCarePairs( &it->second, NULL, arity, depth+1 ); + } + } + //add care pairs based on each pair of non-disequal arguments + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = t1->d_data.begin(); it != t1->d_data.end(); ++it ){ + std::map< TNode, quantifiers::TermArgTrie >::iterator it2 = it; + ++it2; + for( ; it2 != t1->d_data.end(); ++it2 ){ + if( !d_equalityEngine.areDisequal(it->first, it2->first, false) ){ + addCarePairs( &it->second, &it2->second, arity, depth+1 ); + } + } + } + }else{ + //add care pairs based on product of indices, non-disequal arguments + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = t1->d_data.begin(); it != t1->d_data.end(); ++it ){ + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it2 = t2->d_data.begin(); it2 != t2->d_data.end(); ++it2 ){ + if( !d_equalityEngine.areDisequal(it->first, it2->first, false) ){ + addCarePairs( &it->second, &it2->second, arity, depth+1 ); + } + } + } + } + } } void TheoryStrings::computeCareGraph(){ - Theory::computeCareGraph(); + //computing the care graph here is probably still necessary, due to operators that take non-string arguments TODO: verify + Trace("strings-cg") << "TheoryStrings::computeCareGraph(): Build term indices..." << std::endl; + std::map< Node, quantifiers::TermArgTrie > index; + std::map< Node, unsigned > arity; + unsigned functionTerms = d_functionsTerms.size(); + for (unsigned i = 0; i < functionTerms; ++ i) { + TNode f1 = d_functionsTerms[i]; + Trace("strings-cg") << "...build for " << f1 << std::endl; + Node op = f1.getOperator(); + std::vector< TNode > reps; + bool has_trigger_arg = false; + for( unsigned j=0; j<f1.getNumChildren(); j++ ){ + reps.push_back( d_equalityEngine.getRepresentative( f1[j] ) ); + if( d_equalityEngine.isTriggerTerm( f1[j], THEORY_STRINGS ) ){ + has_trigger_arg = true; + } + } + if( has_trigger_arg ){ + index[op].addTerm( f1, reps ); + arity[op] = reps.size(); + } + } + //for each index + for( std::map< Node, quantifiers::TermArgTrie >::iterator itii = index.begin(); itii != index.end(); ++itii ){ + Trace("strings-cg") << "TheoryStrings::computeCareGraph(): Process index " << itii->first << "..." << std::endl; + addCarePairs( &itii->second, NULL, arity[ itii->first ], 0 ); + } } void TheoryStrings::assertPendingFact(Node atom, bool polarity, Node exp) { @@ -1136,8 +1219,12 @@ void TheoryStrings::checkExtendedFuncsEval( int effort ) { }else{ if( !areEqual( n, nrc ) ){ if( n.getType().isBoolean() ){ - d_extf_exp[n].push_back( nrc==d_true ? n.negate() : n ); - conc = d_false; + if( areEqual( n, nrc==d_true ? d_false : d_true ) ){ + d_extf_exp[n].push_back( nrc==d_true ? n.negate() : n ); + conc = d_false; + }else{ + conc = nrc==d_true ? n : n.negate(); + } }else{ conc = n.eqNode( nrc ); } @@ -1145,7 +1232,7 @@ void TheoryStrings::checkExtendedFuncsEval( int effort ) { } if( !conc.isNull() ){ Trace("strings-extf") << " resolve extf : " << nr << " -> " << nrc << std::endl; - sendInference( d_extf_exp[n], conc, effort==0 ? "EXTF" : "EXTF-N", n.getType().isInteger() || d_extf_exp[n].empty() ); + sendInference( d_extf_exp[n], conc, effort==0 ? "EXTF" : "EXTF-N", true ); if( d_conflict ){ Trace("strings-extf-debug") << " conflict, return." << std::endl; return; @@ -1745,9 +1832,9 @@ bool TheoryStrings::normalizeEquivalenceClass( Node eqc, std::vector< Node > & n } nf.insert( nf.end(), normal_forms[nf_index].begin(), normal_forms[nf_index].end() ); nf_exp.insert( nf_exp.end(), normal_forms_exp[nf_index].begin(), normal_forms_exp[nf_index].end() ); - //if( eqc!=normal_form_src[nf_index] ){ - // nf_exp.push_back( eqc.eqNode( normal_form_src[nf_index] ) ); - //} + if( eqc!=normal_form_src[nf_index] ){ + nf_exp.push_back( eqc.eqNode( normal_form_src[nf_index] ) ); + } Trace("strings-solve-debug2") << "take normal form ... done" << std::endl; d_normal_forms_base[eqc] = normal_form_src[nf_index]; //*/ @@ -2639,23 +2726,17 @@ int TheoryStrings::processSimpleDeq( std::vector< Node >& nfi, std::vector< Node void TheoryStrings::addNormalFormPair( Node n1, Node n2 ){ if( !isNormalFormPair( n1, n2 ) ){ - //Assert( !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 ); + int index = 0; + NodeIntMap::const_iterator it = d_nf_pairs.find( n1 ); + if( it!=d_nf_pairs.end() ){ + index = (*it).second; + } + d_nf_pairs[n1] = index + 1; + if( index<(int)d_nf_pairs_data[n1].size() ){ + d_nf_pairs_data[n1][index] = n2; + }else{ + d_nf_pairs_data[n1].push_back( n2 ); + } Assert( isNormalFormPair( n1, n2 ) ); } else { Trace("strings-nf-debug") << "Already a normal form pair " << n1 << " " << n2 << std::endl; @@ -2668,15 +2749,14 @@ bool TheoryStrings::isNormalFormPair( Node n1, Node n2 ) { } 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; + //Trace("strings-debug") << "is normal form pair. " << n1 << " " << n2 << std::endl; + NodeIntMap::const_iterator it = d_nf_pairs.find( n1 ); + if( it!=d_nf_pairs.end() ){ + Assert( d_nf_pairs_data.find( n1 )!=d_nf_pairs_data.end() ); + for( int i=0; i<(*it).second; i++ ){ + Assert( i<(int)d_nf_pairs_data[n1].size() ); + if( d_nf_pairs_data[n1][i]==n2 ){ + return true; } } } @@ -2752,7 +2832,7 @@ void TheoryStrings::sendInference( std::vector< Node >& exp, std::vector< Node > eq = eq.isNull() ? d_false : Rewriter::rewrite( eq ); if( eq!=d_true ){ if( Trace.isOn("strings-infer-debug") ){ - Trace("strings-infer-debug") << "infer : " << eq << " from: " << std::endl; + Trace("strings-infer-debug") << "By " << c << ", infer : " << eq << " from: " << std::endl; for( unsigned i=0; i<exp.size(); i++ ){ Trace("strings-infer-debug") << " " << exp[i] << std::endl; } @@ -2761,8 +2841,8 @@ void TheoryStrings::sendInference( std::vector< Node >& exp, std::vector< Node > } //Trace("strings-infer-debug") << "as lemma : " << asLemma << std::endl; } - bool doSendLemma = ( asLemma || eq==d_false || eq.getKind()==kind::OR || options::stringInferAsLemmas() ); - if( doSendLemma ){ + //check if we should send a lemma or an inference + if( asLemma || eq==d_false || eq.getKind()==kind::OR || !exp_n.empty() || options::stringInferAsLemmas() ){ Node eq_exp; if( options::stringRExplainLemmas() ){ eq_exp = mkExplain( exp, exp_n ); @@ -2780,7 +2860,6 @@ void TheoryStrings::sendInference( std::vector< Node >& exp, std::vector< Node > } sendLemma( eq_exp, eq, c ); }else{ - Assert( exp_n.empty() ); sendInfer( mkAnd( exp ), eq, c ); } } @@ -3064,30 +3143,56 @@ void TheoryStrings::getConcatVec( Node n, std::vector< Node >& c ) { void TheoryStrings::checkDeqNF() { std::vector< std::vector< Node > > cols; std::vector< Node > lts; - separateByLength( d_strings_eqc, 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 " << cols[i][0]; - printConcat( d_normal_forms[cols[i][0]], "strings-solve" ); - Trace("strings-solve") << "... #eql = " << cols[i].size() << std::endl; - //must ensure that normal forms are disequal - for( unsigned j=0; j<cols[i].size(); j++ ){ - for( unsigned k=(j+1); k<cols[i].size(); k++ ){ - if( areDisequal( cols[i][j], cols[i][k] ) ){ - Assert( !d_conflict ); - //if( !areDisequal( cols[i][j], cols[i][k] ) ){ - // sendSplit( cols[i][j], cols[i][k], "D-NORM", true ); - // return; - //}else{ - Trace("strings-solve") << "- Compare " << cols[i][j] << " "; - printConcat( d_normal_forms[cols[i][j]], "strings-solve" ); - Trace("strings-solve") << " against " << cols[i][k] << " "; - printConcat( d_normal_forms[cols[i][k]], "strings-solve" ); - Trace("strings-solve") << "..." << std::endl; - if( processDeq( cols[i][j], cols[i][k] ) ){ - return; + std::map< Node, std::map< Node, bool > > processed; + + //for each pair of disequal strings, must determine whether their lengths are equal or disequal + bool addedLSplit = false; + for( NodeList::const_iterator id = d_ee_disequalities.begin(); id != d_ee_disequalities.end(); ++id ) { + Node eq = *id; + Node n[2]; + for( unsigned i=0; i<2; i++ ){ + n[i] = d_equalityEngine.getRepresentative( eq[i] ); + } + if( processed[n[0]].find( n[1] )==processed[n[0]].end() ){ + processed[n[0]][n[1]] = true; + Node lt[2]; + for( unsigned i=0; i<2; i++ ){ + EqcInfo* ei = getOrMakeEqcInfo( n[i], false ); + lt[i] = ei ? ei->d_length_term : Node::null(); + if( lt[i].isNull() ){ + lt[i] = eq[i]; + } + lt[i] = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, lt[i] ); + } + if( !areEqual( lt[0], lt[1] ) && !areDisequal( lt[0], lt[1] ) ){ + addedLSplit = true; + sendSplit( lt[0], lt[1], "DEQ-LENGTH-SP" ); + } + } + } + + if( !addedLSplit ){ + separateByLength( d_strings_eqc, 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 " << cols[i][0] << ", normal form : "; + printConcat( d_normal_forms[cols[i][0]], "strings-solve" ); + Trace("strings-solve") << "... #eql = " << cols[i].size() << std::endl; + //must ensure that normal forms are disequal + for( unsigned j=0; j<cols[i].size(); j++ ){ + for( unsigned k=(j+1); k<cols[i].size(); k++ ){ + //for strings that are disequal, but have the same length + if( areDisequal( cols[i][j], cols[i][k] ) ){ + Assert( !d_conflict ); + Trace("strings-solve") << "- Compare " << cols[i][j] << " "; + printConcat( d_normal_forms[cols[i][j]], "strings-solve" ); + Trace("strings-solve") << " against " << cols[i][k] << " "; + printConcat( d_normal_forms[cols[i][k]], "strings-solve" ); + Trace("strings-solve") << "..." << std::endl; + if( processDeq( cols[i][j], cols[i][k] ) ){ + return; + } } - //} } } } @@ -3149,6 +3254,9 @@ void TheoryStrings::checkCardinality() { //int cardinality = options::stringCharCardinality(); //Trace("strings-solve-debug2") << "get cardinality: " << cardinality << endl; + //AJR: this will create a partition of eqc, where each collection has length that are pairwise propagated to be equal. + // we do not require disequalities between the lengths of each collection, since we split on disequalities between lengths of string terms that are disequal (DEQ-LENGTH-SP). + // TODO: revisit this? std::vector< std::vector< Node > > cols; std::vector< Node > lts; separateByLength( d_strings_eqc, cols, lts ); @@ -3159,54 +3267,51 @@ void TheoryStrings::checkCardinality() { if( cols[i].size() > 1 ) { // size > c^k unsigned card_need = 1; - double curr = (double)cols[i].size()-1; + double curr = (double)cols[i].size(); while( curr>d_card_size ){ curr = curr/(double)d_card_size; card_need++; } + Trace("strings-card") << "Need length " << card_need << " for this number of strings (where alphabet size is " << d_card_size << ")." << std::endl; Node cmp = NodeManager::currentNM()->mkNode( kind::GEQ, lr, NodeManager::currentNM()->mkConst( Rational( card_need ) ) ); cmp = Rewriter::rewrite( cmp ); if( cmp!=d_true ){ unsigned int int_k = (unsigned int)card_need; - 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(!areDisequal( *itr1, *itr2 )) { - allDisequal = false; // add split lemma sendSplit( *itr1, *itr2, "CARD-SP" ); return; } } } - if( allDisequal ){ - EqcInfo* ei = getOrMakeEqcInfo( lr, true ); - Trace("strings-card") << "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() ){ - Node k_node = NodeManager::currentNM()->mkConst( ::CVC4::Rational( int_k ) ); - //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 = len.eqNode(lr); - vec_node.push_back( len_eq_lr ); - } - } - Node len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, cols[i][0] ); - Node cons = NodeManager::currentNM()->mkNode( kind::GEQ, len, k_node ); - cons = Rewriter::rewrite( cons ); - ei->d_cardinality_lem_k.set( int_k+1 ); - if( cons!=d_true ){ - sendInference( d_empty_vec, vec_node, cons, "CARDINALITY", true ); - return; + EqcInfo* ei = getOrMakeEqcInfo( lr, true ); + Trace("strings-card") << "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() ){ + Node k_node = NodeManager::currentNM()->mkConst( ::CVC4::Rational( int_k ) ); + //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 = len.eqNode(lr); + vec_node.push_back( len_eq_lr ); } } + Node len = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, cols[i][0] ); + Node cons = NodeManager::currentNM()->mkNode( kind::GEQ, len, k_node ); + cons = Rewriter::rewrite( cons ); + ei->d_cardinality_lem_k.set( int_k+1 ); + if( cons!=d_true ){ + sendInference( d_empty_vec, vec_node, cons, "CARDINALITY", true ); + return; + } } } } @@ -3275,11 +3380,9 @@ void TheoryStrings::separateByLength(std::vector< Node >& n, Assert( d_equalityEngine.getRepresentative(eqc)==eqc ); EqcInfo* ei = getOrMakeEqcInfo( eqc, false ); Node lt = ei ? ei->d_length_term : Node::null(); - Trace("ajr-temp") << "Length term for " << eqc << " is " << lt << std::endl; if( !lt.isNull() ){ lt = NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, lt ); Node r = d_equalityEngine.getRepresentative( lt ); - Trace("ajr-temp") << "Length term rep for " << eqc << " is " << lt << std::endl; if( eqc_to_leqc.find( r )==eqc_to_leqc.end() ){ eqc_to_leqc[r] = leqc_counter; leqc_to_eqc[leqc_counter] = r; @@ -3318,6 +3421,26 @@ void TheoryStrings::updateMpl( Node n, int b ) { } */ + +unsigned TheoryStrings::getNumMemberships( Node n, bool isPos ) { + if( isPos ){ + NodeIntMap::const_iterator it = d_pos_memberships.find( n ); + if( it!=d_pos_memberships.end() ){ + return (*it).second; + } + }else{ + NodeIntMap::const_iterator it = d_neg_memberships.find( n ); + if( it!=d_neg_memberships.end() ){ + return (*it).second; + } + } + return 0; +} + +Node TheoryStrings::getMembership( Node n, bool isPos, unsigned i ) { + return isPos ? d_pos_memberships_data[n][i] : d_neg_memberships_data[n][i]; +} + //// Regular Expressions Node TheoryStrings::mkRegExpAntec(Node atom, Node ant) { if(d_regexp_ant.find(atom) == d_regexp_ant.end()) { @@ -3395,10 +3518,8 @@ bool TheoryStrings::normalizePosMemberships(std::map< Node, std::vector< Node > Trace("regexp-check") << "Normalizing Positive Memberships ... " << std::endl; - for(NodeListMap::const_iterator itr_xr = d_pos_memberships.begin(); - itr_xr != d_pos_memberships.end(); ++itr_xr ) { + for( NodeIntMap::const_iterator itr_xr = d_pos_memberships.begin(); itr_xr != d_pos_memberships.end(); ++itr_xr ){ Node x = (*itr_xr).first; - NodeList* lst = (*itr_xr).second; Node nf_x = x; std::vector< Node > nf_x_exp; if(d_normal_forms.find( x ) != d_normal_forms.end()) { @@ -3412,10 +3533,11 @@ bool TheoryStrings::normalizePosMemberships(std::map< Node, std::vector< Node > std::vector< Node > vec_x; std::vector< Node > vec_r; - for(NodeList::const_iterator itr_lst = lst->begin(); - itr_lst != lst->end(); ++itr_lst) { - Node r = *itr_lst; - Node nf_r = normalizeRegexp((*lst)[0]); + unsigned n_pmem = (*itr_xr).second; + Assert( getNumMemberships( x, true )==n_pmem ); + for( unsigned k=0; k<n_pmem; k++ ){ + Node r = getMembership( x, true, k ); + Node nf_r = normalizeRegexp( r ); //AJR: fixed (was normalizing mem #0 always) Node memb = NodeManager::currentNM()->mkNode(kind::STRING_IN_REGEXP, nf_x, nf_r); if(d_processed_memberships.find(memb) == d_processed_memberships.end()) { if(d_regexp_opr.checkConstRegExp(nf_r)) { @@ -3645,46 +3767,43 @@ void TheoryStrings::checkMemberships() { Trace("regexp-debug") << "Checking Memberships ... " << std::endl; //if(options::stringEIT()) { //TODO: Opt for normal forms - for(NodeListMap::const_iterator itr_xr = d_pos_memberships.begin(); - itr_xr != d_pos_memberships.end(); ++itr_xr ) { + for( NodeIntMap::const_iterator itr_xr = d_pos_memberships.begin(); itr_xr != d_pos_memberships.end(); ++itr_xr ){ bool spflag = false; Node x = (*itr_xr).first; - NodeList* lst = (*itr_xr).second; Trace("regexp-debug") << "Checking Memberships for " << x << std::endl; if(d_inter_index.find(x) == d_inter_index.end()) { d_inter_index[x] = 0; } int cur_inter_idx = d_inter_index[x]; - if(cur_inter_idx != (int)lst->size()) { - if(lst->size() == 1) { - d_inter_cache[x] = (*lst)[0]; + unsigned n_pmem = (*itr_xr).second; + Assert( getNumMemberships( x, true )==n_pmem ); + if( cur_inter_idx != (int)n_pmem ) { + if( n_pmem == 1) { + d_inter_cache[x] = getMembership( x, true, 0 ); d_inter_index[x] = 1; Trace("regexp-debug") << "... only one choice " << std::endl; - } else if(lst->size() > 1) { + } else if(n_pmem > 1) { Node r; if(d_inter_cache.find(x) != d_inter_cache.end()) { r = d_inter_cache[x]; } if(r.isNull()) { - r = (*lst)[0]; + r = getMembership( x, true, 0 ); cur_inter_idx = 1; } - NodeList::const_iterator itr_lst = lst->begin(); - for(int i=0; i<cur_inter_idx; i++) { - ++itr_lst; - } - Trace("regexp-debug") << "... staring from : " << cur_inter_idx << ", we have " << lst->size() << std::endl; - for(;itr_lst != lst->end(); ++itr_lst) { - Node r2 = *itr_lst; + + unsigned k_start = cur_inter_idx; + Trace("regexp-debug") << "... staring from : " << cur_inter_idx << ", we have " << n_pmem << std::endl; + for(unsigned k = k_start; k<n_pmem; k++) { + Node r2 = getMembership( x, true, k ); r = d_regexp_opr.intersect(r, r2, spflag); if(spflag) { break; } else if(r == d_emptyRegexp) { std::vector< Node > vec_nodes; - ++itr_lst; - for(NodeList::const_iterator itr2 = lst->begin(); - itr2 != itr_lst; ++itr2) { - Node n = NodeManager::currentNM()->mkNode(kind::STRING_IN_REGEXP, x, *itr2); + for( unsigned kk=0; kk<=k; kk++ ){ + Node rr = getMembership( x, true, kk ); + Node n = NodeManager::currentNM()->mkNode(kind::STRING_IN_REGEXP, x, rr); vec_nodes.push_back( n ); } Node conc; @@ -3699,7 +3818,7 @@ void TheoryStrings::checkMemberships() { //updates if(!d_conflict && !spflag) { d_inter_cache[x] = r; - d_inter_index[x] = (int)lst->size(); + d_inter_index[x] = (int)n_pmem; } } } @@ -4322,44 +4441,52 @@ void TheoryStrings::addMembership(Node assertion) { Node x = atom[0]; Node r = atom[1]; if(polarity) { - NodeList* lst; - NodeListMap::iterator itr_xr = d_pos_memberships.find( x ); - if( itr_xr == d_pos_memberships.end() ){ - lst = new(getSatContext()->getCMM()) NodeList( true, getSatContext(), false, - ContextMemoryAllocator<TNode>(getSatContext()->getCMM()) ); - d_pos_memberships.insertDataFromContextMemory( x, lst ); - } else { - lst = (*itr_xr).second; - } - //check - for( NodeList::const_iterator itr = lst->begin(); itr != lst->end(); ++itr ) { - if( r == *itr ) { - return; + int index = 0; + NodeIntMap::const_iterator it = d_pos_memberships.find( x ); + if( it!=d_nf_pairs.end() ){ + index = (*it).second; + for( int k=0; k<index; k++ ){ + if( k<(int)d_pos_memberships_data[x].size() ){ + if( d_pos_memberships_data[x][k]==r ){ + return; + } + }else{ + break; + } } } - lst->push_back( r ); + d_pos_memberships[x] = index + 1; + if( index<(int)d_pos_memberships_data[x].size() ){ + d_pos_memberships_data[x][index] = r; + }else{ + d_pos_memberships_data[x].push_back( r ); + } } else if(!options::stringIgnNegMembership()) { /*if(options::stringEIT() && d_regexp_opr.checkConstRegExp(r)) { int rt; Node r2 = d_regexp_opr.complement(r, rt); Node a = NodeManager::currentNM()->mkNode(kind::STRING_IN_REGEXP, x, r2); }*/ - NodeList* lst; - NodeListMap::iterator itr_xr = d_neg_memberships.find( x ); - if( itr_xr == d_neg_memberships.end() ){ - lst = new(getSatContext()->getCMM()) NodeList( true, getSatContext(), false, - ContextMemoryAllocator<TNode>(getSatContext()->getCMM()) ); - d_neg_memberships.insertDataFromContextMemory( x, lst ); - } else { - lst = (*itr_xr).second; - } - //check - for( NodeList::const_iterator itr = lst->begin(); itr != lst->end(); ++itr ) { - if( r == *itr ) { - return; + int index = 0; + NodeIntMap::const_iterator it = d_neg_memberships.find( x ); + if( it!=d_nf_pairs.end() ){ + index = (*it).second; + for( int k=0; k<index; k++ ){ + if( k<(int)d_neg_memberships_data[x].size() ){ + if( d_neg_memberships_data[x][k]==r ){ + return; + } + }else{ + break; + } } } - lst->push_back( r ); + d_neg_memberships[x] = index + 1; + if( index<(int)d_neg_memberships_data[x].size() ){ + d_neg_memberships_data[x][index] = r; + }else{ + d_neg_memberships_data[x].push_back( r ); + } } // old if(polarity || !options::stringIgnNegMembership()) { diff --git a/src/theory/strings/theory_strings.h b/src/theory/strings/theory_strings.h index b9da524de..2deb09654 100644 --- a/src/theory/strings/theory_strings.h +++ b/src/theory/strings/theory_strings.h @@ -33,6 +33,11 @@ namespace CVC4 { namespace theory { + +namespace quantifiers{ + class TermArgTrie; +} + namespace strings { /** @@ -45,7 +50,6 @@ typedef expr::Attribute< StringsProxyVarAttributeId, bool > StringsProxyVarAttri class TheoryStrings : public Theory { typedef context::CDChunkList<Node> NodeList; - typedef context::CDHashMap<Node, NodeList*, NodeHashFunction> NodeListMap; typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap; typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap; typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap; @@ -160,7 +164,8 @@ private: std::map< Node, std::vector< Node > > d_normal_forms_exp; std::map< Node, std::map< Node, std::map< bool, int > > > d_normal_forms_exp_depend; //map of pairs of terms that have the same normal form - NodeListMap d_nf_pairs; + NodeIntMap d_nf_pairs; + std::map< Node, std::vector< Node > > d_nf_pairs_data; void addNormalFormPair( Node n1, Node n2 ); bool isNormalFormPair( Node n1, Node n2 ); bool isNormalFormPair2( Node n1, Node n2 ); @@ -176,6 +181,8 @@ private: // extended functions inferences cache NodeSet d_extf_infer_cache; std::vector< Node > d_empty_vec; + // + NodeList d_ee_disequalities; private: NodeSet d_congruent; std::map< Node, Node > d_eqc_to_const; @@ -236,6 +243,8 @@ private: //maintain which concat terms have the length lemma instantiated NodeNodeMap d_proxy_var; NodeNodeMap d_proxy_var_to_length; + /** All the function terms that the theory has seen */ + context::CDList<TNode> d_functionsTerms; private: //initial check void checkInit(); @@ -304,6 +313,8 @@ private: //cardinality check void checkCardinality(); +private: + void addCarePairs( quantifiers::TermArgTrie * t1, quantifiers::TermArgTrie * t2, unsigned arity, unsigned depth ); public: /** preregister term */ void preRegisterTerm(TNode n); @@ -410,8 +421,12 @@ private: NodeSet d_regexp_ucached; NodeSet d_regexp_ccached; // stored assertions - NodeListMap d_pos_memberships; - NodeListMap d_neg_memberships; + NodeIntMap d_pos_memberships; + std::map< Node, std::vector< Node > > d_pos_memberships_data; + NodeIntMap d_neg_memberships; + std::map< Node, std::vector< Node > > d_neg_memberships_data; + unsigned getNumMemberships( Node n, bool isPos ); + Node getMembership( Node n, bool isPos, unsigned i ); // semi normal forms for symbolic expression std::map< Node, Node > d_nf_regexps; std::map< Node, std::vector< Node > > d_nf_regexps_exp; diff --git a/src/theory/strings/theory_strings_preprocess.cpp b/src/theory/strings/theory_strings_preprocess.cpp index a4f42bddd..ba811644a 100644 --- a/src/theory/strings/theory_strings_preprocess.cpp +++ b/src/theory/strings/theory_strings_preprocess.cpp @@ -33,6 +33,10 @@ StringsPreprocess::StringsPreprocess( context::UserContext* u ) : d_cache( u ){ d_zero = NodeManager::currentNM()->mkConst( ::CVC4::Rational(0) ); } +StringsPreprocess::~StringsPreprocess(){ + +} + /* int StringsPreprocess::checkFixLenVar( Node t ) { int ret = 2; diff --git a/src/theory/strings/theory_strings_preprocess.h b/src/theory/strings/theory_strings_preprocess.h index 5bc9667ea..abc7b5a91 100644 --- a/src/theory/strings/theory_strings_preprocess.h +++ b/src/theory/strings/theory_strings_preprocess.h @@ -40,6 +40,7 @@ private: Node simplify( Node t, std::vector< Node > &new_nodes ); public: StringsPreprocess( context::UserContext* u ); + ~StringsPreprocess(); Node decompose( Node t, std::vector< Node > &new_nodes ); void simplify(std::vector< Node > &vec_node); diff --git a/src/theory/term_registration_visitor.cpp b/src/theory/term_registration_visitor.cpp index 830e7f809..e758002fa 100644 --- a/src/theory/term_registration_visitor.cpp +++ b/src/theory/term_registration_visitor.cpp @@ -37,8 +37,12 @@ bool PreRegisterVisitor::alreadyVisited(TNode current, TNode parent) { if( ( parent.getKind() == kind::FORALL || parent.getKind() == kind::EXISTS || - parent.getKind() == kind::REWRITE_RULE /*|| - parent.getKind() == kind::CARDINALITY_CONSTRAINT*/ ) && + parent.getKind() == kind::REWRITE_RULE || + parent.getKind() == kind::SEP_STAR || + parent.getKind() == kind::SEP_WAND || + ( parent.getKind() == kind::SEP_LABEL && current.getType().isBoolean() ) + // parent.getKind() == kind::CARDINALITY_CONSTRAINT + ) && current != parent ) { Debug("register::internal") << "quantifier:true" << std::endl; return true; @@ -64,14 +68,8 @@ bool PreRegisterVisitor::alreadyVisited(TNode current, TNode parent) { TypeNode type = current.getType(); typeTheoryId = Theory::theoryOf(type); if (typeTheoryId != currentTheoryId) { - if (options::finiteModelFind() && type.isSort()) { - // We're looking for finite models + if (type.isInterpretedFinite()) { useType = true; - } else { - Cardinality card = type.getCardinality(); - if (card.isFinite()) { - useType = true; - } } } } @@ -130,14 +128,8 @@ void PreRegisterVisitor::visit(TNode current, TNode parent) { TypeNode type = current.getType(); typeTheoryId = Theory::theoryOf(type); if (typeTheoryId != currentTheoryId) { - if (options::finiteModelFind() && type.isSort()) { - // We're looking for finite models + if (type.isInterpretedFinite()) { useType = true; - } else { - Cardinality card = type.getCardinality(); - if (card.isFinite()) { - useType = true; - } } } } @@ -189,8 +181,12 @@ bool SharedTermsVisitor::alreadyVisited(TNode current, TNode parent) const { if( ( parent.getKind() == kind::FORALL || parent.getKind() == kind::EXISTS || - parent.getKind() == kind::REWRITE_RULE /*|| - parent.getKind() == kind::CARDINALITY_CONSTRAINT*/ ) && + parent.getKind() == kind::REWRITE_RULE || + parent.getKind() == kind::SEP_STAR || + parent.getKind() == kind::SEP_WAND || + ( parent.getKind() == kind::SEP_LABEL && current.getType().isBoolean() ) + // parent.getKind() == kind::CARDINALITY_CONSTRAINT + ) && current != parent ) { Debug("register::internal") << "quantifier:true" << std::endl; return true; @@ -222,14 +218,8 @@ bool SharedTermsVisitor::alreadyVisited(TNode current, TNode parent) const { TypeNode type = current.getType(); typeTheoryId = Theory::theoryOf(type); if (typeTheoryId != currentTheoryId) { - if (options::finiteModelFind() && type.isSort()) { - // We're looking for finite models + if (type.isInterpretedFinite()) { useType = true; - } else { - Cardinality card = type.getCardinality(); - if (card.isFinite()) { - useType = true; - } } } } @@ -244,14 +234,8 @@ bool SharedTermsVisitor::alreadyVisited(TNode current, TNode parent) const { TypeNode type = current.getType(); typeTheoryId = Theory::theoryOf(type); if (typeTheoryId != currentTheoryId) { - if (options::finiteModelFind() && type.isSort()) { - // We're looking for finite models + if (type.isInterpretedFinite()) { useType = true; - } else { - Cardinality card = type.getCardinality(); - if (card.isFinite()) { - useType = true; - } } } } @@ -297,14 +281,8 @@ void SharedTermsVisitor::visit(TNode current, TNode parent) { TypeNode type = current.getType(); typeTheoryId = Theory::theoryOf(type); if (typeTheoryId != currentTheoryId) { - if (options::finiteModelFind() && type.isSort()) { - // We're looking for finite models + if (type.isInterpretedFinite()) { useType = true; - } else { - Cardinality card = type.getCardinality(); - if (card.isFinite()) { - useType = true; - } } } } diff --git a/src/theory/theory.h b/src/theory/theory.h index 382d4cf65..e8518b1f6 100644 --- a/src/theory/theory.h +++ b/src/theory/theory.h @@ -781,6 +781,14 @@ public: assertions_iterator facts_end() const { return d_facts.end(); } + /** + * Whether facts have been asserted to this theory. + * + * @return true iff facts have been asserted to this theory. + */ + bool hasFacts() { + return !d_facts.empty(); + } typedef context::CDList<TNode>::const_iterator shared_terms_iterator; diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp index f2231ff7a..881acdddd 100644 --- a/src/theory/theory_engine.cpp +++ b/src/theory/theory_engine.cpp @@ -26,6 +26,8 @@ #include "options/bv_options.h" #include "options/options.h" #include "options/quantifiers_options.h" +#include "proof/cnf_proof.h" +#include "proof/lemma_proof.h" #include "proof/proof_manager.h" #include "proof/theory_proof.h" #include "smt/ite_removal.h" @@ -53,6 +55,104 @@ using namespace CVC4::theory; namespace CVC4 { +inline void flattenAnd(Node n, std::vector<TNode>& out){ + Assert(n.getKind() == kind::AND); + for(Node::iterator i=n.begin(), i_end=n.end(); i != i_end; ++i){ + Node curr = *i; + if(curr.getKind() == kind::AND){ + flattenAnd(curr, out); + }else{ + out.push_back(curr); + } + } +} + +inline Node flattenAnd(Node n){ + std::vector<TNode> out; + flattenAnd(n, out); + return NodeManager::currentNM()->mkNode(kind::AND, out); +} + +theory::LemmaStatus TheoryEngine::EngineOutputChannel::lemma(TNode lemma, + ProofRule rule, + bool removable, + bool preprocess, + bool sendAtoms) + throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException) { + Debug("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::lemma(" << lemma << ")" << ", preprocess = " << preprocess << std::endl; + ++ d_statistics.lemmas; + d_engine->d_outputChannelUsed = true; + + LemmaProofRecipe* proofRecipe = NULL; + PROOF({ + // Theory lemmas have one step that proves the empty clause + proofRecipe = new LemmaProofRecipe; + + Node emptyNode; + LemmaProofRecipe::ProofStep proofStep(d_theory, emptyNode); + + Node rewritten; + if (lemma.getKind() == kind::OR) { + for (unsigned i = 0; i < lemma.getNumChildren(); ++i) { + rewritten = theory::Rewriter::rewrite(lemma[i]); + if (rewritten != lemma[i]) { + proofRecipe->addRewriteRule(lemma[i].negate(), rewritten.negate()); + } + proofStep.addAssertion(lemma[i]); + proofRecipe->addBaseAssertion(rewritten); + } + } else { + rewritten = theory::Rewriter::rewrite(lemma); + if (rewritten != lemma) { + proofRecipe->addRewriteRule(lemma.negate(), rewritten.negate()); + } + proofStep.addAssertion(lemma); + proofRecipe->addBaseAssertion(rewritten); + } + proofRecipe->addStep(proofStep); + }); + + theory::LemmaStatus result = d_engine->lemma(lemma, + rule, + false, + removable, + preprocess, + sendAtoms ? d_theory : theory::THEORY_LAST, + proofRecipe); + PROOF(delete proofRecipe;); + return result; +} + +theory::LemmaStatus TheoryEngine::EngineOutputChannel::splitLemma(TNode lemma, bool removable) + throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException) { + Debug("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::lemma(" << lemma << ")" << std::endl; + ++ d_statistics.lemmas; + d_engine->d_outputChannelUsed = true; + + + LemmaProofRecipe* proofRecipe = NULL; + Debug("pf::explain") << "TheoryEngine::EngineOutputChannel::splitLemma( " << lemma << " )" << std::endl; + theory::LemmaStatus result = d_engine->lemma(lemma, RULE_SPLIT, false, removable, false, d_theory, proofRecipe); + return result; +} + +bool TheoryEngine::EngineOutputChannel::propagate(TNode literal) + throw(AssertionException, UnsafeInterruptException) { + Debug("theory::propagate") << "EngineOutputChannel<" << d_theory << ">::propagate(" << literal << ")" << std::endl; + ++ d_statistics.propagations; + d_engine->d_outputChannelUsed = true; + return d_engine->propagate(literal, d_theory); +} + +void TheoryEngine::EngineOutputChannel::conflict(TNode conflictNode, Proof* pf) + throw(AssertionException, UnsafeInterruptException) { + Trace("theory::conflict") << "EngineOutputChannel<" << d_theory << ">::conflict(" << conflictNode << ")" << std::endl; + Assert (pf == NULL); // Theory shouldn't be producing proofs yet + ++ d_statistics.conflicts; + d_engine->d_outputChannelUsed = true; + d_engine->conflict(conflictNode, d_theory); +} + void TheoryEngine::finishInit() { // initialize the quantifiers engine d_quantEngine = new QuantifiersEngine(d_context, d_userContext, this); @@ -161,7 +261,9 @@ TheoryEngine::TheoryEngine(context::Context* context, d_true = NodeManager::currentNM()->mkConst<bool>(true); d_false = NodeManager::currentNM()->mkConst<bool>(false); - PROOF (ProofManager::currentPM()->initTheoryProofEngine(); ); +#ifdef CVC4_PROOF + ProofManager::currentPM()->initTheoryProofEngine(); +#endif d_iteUtilities = new ITEUtilities(d_iteRemover.getContainsVisitor()); @@ -416,18 +518,29 @@ void TheoryEngine::check(Theory::Effort effort) { // Must consult quantifiers theory for last call to ensure sat, or otherwise add a lemma if( effort == Theory::EFFORT_FULL && ! d_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); - // if returning incomplete or SAT, we have ensured that the model in the quantifiers engine has been built - } else if(options::produceModels()) { - // must build model at this point - d_curr_model_builder->buildModel(d_curr_model, true); + //calls to theories requiring the model go here + //FIXME: this should not be theory-specific + if(d_logicInfo.isTheoryEnabled(THEORY_SEP)) { + Assert( d_theoryTable[THEORY_SEP]!=NULL ); + if( d_theoryTable[THEORY_SEP]->hasFacts() ){ + // must build model at this point + d_curr_model_builder->buildModel(getModel(), false); + d_theoryTable[THEORY_SEP]->check(Theory::EFFORT_LAST_CALL); + } } - Trace("theory::assertions-model") << endl; - if (Trace.isOn("theory::assertions-model")) { - printAssertions("theory::assertions-model"); + if( ! d_inConflict && ! needCheck() ){ + if(d_logicInfo.isQuantified()) { + // quantifiers engine must pass effort last call check + d_quantEngine->check(Theory::EFFORT_LAST_CALL); + // if returning incomplete or SAT, we have ensured that the model in the quantifiers engine has been built + } else if(options::produceModels()) { + // must build model at this point + d_curr_model_builder->buildModel(getModel(), true); + } + Trace("theory::assertions-model") << endl; + if (Trace.isOn("theory::assertions-model")) { + printAssertions("theory::assertions-model"); + } } } @@ -497,7 +610,10 @@ void TheoryEngine::combineTheories() { // We need to split on it Debug("combineTheories") << "TheoryEngine::combineTheories(): requesting a split " << endl; - lemma(equality.orNode(equality.notNode()), RULE_INVALID, false, false, false, carePair.theory, carePair.theory); + + LemmaProofRecipe* proofRecipe = NULL; + lemma(equality.orNode(equality.notNode()), RULE_INVALID, false, false, false, carePair.theory, proofRecipe); + // This code is supposed to force preference to follow what the theory models already have // but it doesn't seem to make a big difference - need to explore more -Clark // if (true) { @@ -963,7 +1079,7 @@ bool TheoryEngine::markPropagation(TNode assertion, TNode originalAssertion, the void TheoryEngine::assertToTheory(TNode assertion, TNode originalAssertion, theory::TheoryId toTheoryId, theory::TheoryId fromTheoryId) { - Trace("theory::assertToTheory") << "TheoryEngine::assertToTheory(" << assertion << ", " << toTheoryId << ", " << fromTheoryId << ")" << endl; + Trace("theory::assertToTheory") << "TheoryEngine::assertToTheory(" << assertion << ", " << originalAssertion << "," << toTheoryId << ", " << fromTheoryId << ")" << endl; Assert(toTheoryId != fromTheoryId); if(toTheoryId != THEORY_SAT_SOLVER && @@ -1176,6 +1292,23 @@ bool TheoryEngine::propagate(TNode literal, theory::TheoryId theory) { assertToTheory(literal, literal, /* to */ THEORY_BUILTIN, /* from */ theory); } } else { + // We could be propagating a unit-clause lemma. In this case, we need to provide a + // recipe. + // TODO: Consider putting this someplace else? This is the only refence to the proof + // manager in this class. + + PROOF({ + LemmaProofRecipe proofRecipe; + proofRecipe.addBaseAssertion(literal); + + Node emptyNode; + LemmaProofRecipe::ProofStep proofStep(theory, emptyNode); + proofStep.addAssertion(literal); + proofRecipe.addStep(proofStep); + + ProofManager::getCnfProof()->setProofRecipe(&proofRecipe); + }); + // Just send off to the SAT solver Assert(d_propEngine->isSatLiteral(literal)); assertToTheory(literal, literal, /* to */ THEORY_SAT_SOLVER, /* from */ theory); @@ -1270,7 +1403,7 @@ static Node mkExplanation(const std::vector<NodeTheoryPair>& explanation) { return conjunction; } -NodeTheoryPair TheoryEngine::getExplanationAndExplainer(TNode node) { +Node TheoryEngine::getExplanationAndRecipe(TNode node, LemmaProofRecipe* proofRecipe) { Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << "): current propagation index = " << d_propagationMapTimestamp << endl; bool polarity = node.getKind() != kind::NOT; @@ -1278,32 +1411,90 @@ NodeTheoryPair TheoryEngine::getExplanationAndExplainer(TNode node) { // If we're not in shared mode, explanations are simple if (!d_logicInfo.isSharingEnabled()) { + Debug("theory::explain") << "TheoryEngine::getExplanation: sharing is NOT enabled. " + << " Responsible theory is: " + << theoryOf(atom)->getId() << std::endl; + Node explanation = theoryOf(atom)->explain(node); Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << ") => " << explanation << endl; - return NodeTheoryPair(explanation, theoryOf(atom)->getId()); + PROOF({ + if(proofRecipe) { + Node emptyNode; + LemmaProofRecipe::ProofStep proofStep(theoryOf(atom)->getId(), emptyNode); + proofStep.addAssertion(node); + proofRecipe->addBaseAssertion(node); + + if (explanation.getKind() == kind::AND) { + // If the explanation is a conjunction, the recipe for the corresponding lemma is + // the negation of its conjuncts. + Node flat = flattenAnd(explanation); + for (unsigned i = 0; i < flat.getNumChildren(); ++i) { + if (flat[i].isConst() && flat[i].getConst<bool>()) { + ++ i; + continue; + } + if (flat[i].getKind() == kind::NOT && + flat[i][0].isConst() && !flat[i][0].getConst<bool>()) { + ++ i; + continue; + } + Debug("theory::explain") << "TheoryEngine::getExplanationAndRecipe: adding recipe assertion: " + << flat[i].negate() << std::endl; + proofStep.addAssertion(flat[i].negate()); + proofRecipe->addBaseAssertion(flat[i].negate()); + } + } else { + // The recipe for proving it is by negating it. "True" is not an acceptable reason. + if (!((explanation.isConst() && explanation.getConst<bool>()) || + (explanation.getKind() == kind::NOT && + explanation[0].isConst() && !explanation[0].getConst<bool>()))) { + proofStep.addAssertion(explanation.negate()); + proofRecipe->addBaseAssertion(explanation.negate()); + } + } + + proofRecipe->addStep(proofStep); + } + }); + + return explanation; } + Debug("theory::explain") << "TheoryEngine::getExplanation: sharing IS enabled" << std::endl; + // Initial thing to explain NodeTheoryPair toExplain(node, THEORY_SAT_SOLVER, d_propagationMapTimestamp); Assert(d_propagationMap.find(toExplain) != d_propagationMap.end()); NodeTheoryPair nodeExplainerPair = d_propagationMap[toExplain]; + Debug("theory::explain") << "TheoryEngine::getExplanation: explainer for node " + << nodeExplainerPair.node + << " is theory: " << nodeExplainerPair.theory << std::endl; TheoryId explainer = nodeExplainerPair.theory; // Create the workplace for explanations std::vector<NodeTheoryPair> explanationVector; explanationVector.push_back(d_propagationMap[toExplain]); // Process the explanation - getExplanation(explanationVector); + if (proofRecipe) { + Node emptyNode; + LemmaProofRecipe::ProofStep proofStep(explainer, emptyNode); + proofStep.addAssertion(node); + proofRecipe->addStep(proofStep); + proofRecipe->addBaseAssertion(node); + } + + getExplanation(explanationVector, proofRecipe); Node explanation = mkExplanation(explanationVector); Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << ") => " << explanation << endl; - return NodeTheoryPair(explanation, explainer); + return explanation; } Node TheoryEngine::getExplanation(TNode node) { - return getExplanationAndExplainer(node).node; + LemmaProofRecipe *dontCareRecipe = NULL; + return getExplanationAndRecipe(node, dontCareRecipe); } struct AtomsCollect { @@ -1406,7 +1597,7 @@ theory::LemmaStatus TheoryEngine::lemma(TNode node, bool removable, bool preprocess, theory::TheoryId atomsTo, - theory::TheoryId ownerTheory) { + LemmaProofRecipe* proofRecipe) { // For resource-limiting (also does a time check). // spendResource(); @@ -1456,10 +1647,10 @@ theory::LemmaStatus TheoryEngine::lemma(TNode node, } // assert to prop engine - d_propEngine->assertLemma(additionalLemmas[0], negated, removable, rule, ownerTheory, node); + d_propEngine->assertLemma(additionalLemmas[0], negated, removable, rule, proofRecipe, node); for (unsigned i = 1; i < additionalLemmas.size(); ++ i) { additionalLemmas[i] = theory::Rewriter::rewrite(additionalLemmas[i]); - d_propEngine->assertLemma(additionalLemmas[i], false, removable, rule, ownerTheory, node); + d_propEngine->assertLemma(additionalLemmas[i], false, removable, rule, proofRecipe, node); } // WARNING: Below this point don't assume additionalLemmas[0] to be not negated. @@ -1493,22 +1684,69 @@ void TheoryEngine::conflict(TNode conflict, TheoryId theoryId) { << CheckSatCommand(conflict.toExpr()); } + LemmaProofRecipe* proofRecipe = NULL; + PROOF({ + proofRecipe = new LemmaProofRecipe; + Node emptyNode; + LemmaProofRecipe::ProofStep proofStep(theoryId, emptyNode); + + if (conflict.getKind() == kind::AND) { + for (unsigned i = 0; i < conflict.getNumChildren(); ++i) { + proofStep.addAssertion(conflict[i].negate()); + } + } else { + proofStep.addAssertion(conflict.negate()); + } + + proofRecipe->addStep(proofStep); + }); + // In the multiple-theories case, we need to reconstruct the conflict if (d_logicInfo.isSharingEnabled()) { // Create the workplace for explanations std::vector<NodeTheoryPair> explanationVector; explanationVector.push_back(NodeTheoryPair(conflict, theoryId, d_propagationMapTimestamp)); + // Process the explanation - getExplanation(explanationVector); + getExplanation(explanationVector, proofRecipe); Node fullConflict = mkExplanation(explanationVector); Debug("theory::conflict") << "TheoryEngine::conflict(" << conflict << ", " << theoryId << "): full = " << fullConflict << endl; Assert(properConflict(fullConflict)); - lemma(fullConflict, RULE_CONFLICT, true, true, false, THEORY_LAST, theoryId); + lemma(fullConflict, RULE_CONFLICT, true, true, false, THEORY_LAST, proofRecipe); + } else { // When only one theory, the conflict should need no processing Assert(properConflict(conflict)); - lemma(conflict, RULE_CONFLICT, true, true, false, THEORY_LAST, theoryId); + PROOF({ + if (conflict.getKind() == kind::AND) { + // If the conflict is a conjunction, the corresponding lemma is derived by negating + // its conjuncts. + for (unsigned i = 0; i < conflict.getNumChildren(); ++i) { + if (conflict[i].isConst() && conflict[i].getConst<bool>()) { + ++ i; + continue; + } + if (conflict[i].getKind() == kind::NOT && + conflict[i][0].isConst() && !conflict[i][0].getConst<bool>()) { + ++ i; + continue; + } + proofRecipe->getStep(0)->addAssertion(conflict[i].negate()); + proofRecipe->addBaseAssertion(conflict[i].negate()); + } + } else { + proofRecipe->getStep(0)->addAssertion(conflict.negate()); + proofRecipe->addBaseAssertion(conflict.negate()); + } + }); + + lemma(conflict, RULE_CONFLICT, true, true, false, THEORY_LAST, proofRecipe); } + + PROOF({ + delete proofRecipe; + proofRecipe = NULL; + }); } void TheoryEngine::staticInitializeBVOptions(const std::vector<Node>& assertions) { @@ -1558,15 +1796,9 @@ void TheoryEngine::mkAckermanizationAsssertions(std::vector<Node>& assertions) { Node TheoryEngine::ppSimpITE(TNode assertion) { - if(options::incrementalSolving()){ - // disabling the d_iteUtilities->simpITE(assertion) pass for incremental solving. - // This is paranoia. We do not actually know of a bug coming from this. - // TODO re-enable + if (!d_iteRemover.containsTermITE(assertion)) { return assertion; - } else if(!d_iteRemover.containsTermITE(assertion)){ - return assertion; - }else{ - + } else { Node result = d_iteUtilities->simpITE(assertion); Node res_rewritten = Rewriter::rewrite(result); @@ -1576,10 +1808,9 @@ Node TheoryEngine::ppSimpITE(TNode assertion) Chat() << "ending simplifyWithCare()" << " post simplifyWithCare()" << postSimpWithCare.getId() << endl; result = Rewriter::rewrite(postSimpWithCare); - }else{ + } else { result = res_rewritten; } - return result; } } @@ -1667,19 +1898,21 @@ bool TheoryEngine::donePPSimpITE(std::vector<Node>& assertions){ return result; } -void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector) -{ +void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector, LemmaProofRecipe* proofRecipe) { Assert(explanationVector.size() > 0); unsigned i = 0; // Index of the current literal we are processing unsigned j = 0; // Index of the last literal we are keeping - while (i < explanationVector.size()) { + std::set<Node> inputAssertions; + PROOF(inputAssertions = proofRecipe->getStep(0)->getAssertions();); + while (i < explanationVector.size()) { // Get the current literal to explain NodeTheoryPair toExplain = explanationVector[i]; - Debug("theory::explain") << "TheoryEngine::explain(): processing [" << toExplain.timestamp << "] " << toExplain.node << " sent from " << toExplain.theory << endl; + Debug("theory::explain") << "[i=" << i << "] TheoryEngine::explain(): processing [" << toExplain.timestamp << "] " << toExplain.node << " sent from " << toExplain.theory << endl; + // If a true constant or a negation of a false constant we can ignore it if (toExplain.node.isConst() && toExplain.node.getConst<bool>()) { @@ -1693,6 +1926,7 @@ void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector // If from the SAT solver, keep it if (toExplain.theory == THEORY_SAT_SOLVER) { + Debug("theory::explain") << "\tLiteral came from THEORY_SAT_SOLVER. Kepping it." << endl; explanationVector[j++] = explanationVector[i++]; continue; } @@ -1711,10 +1945,26 @@ void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector // See if it was sent to the theory by another theory PropagationMap::const_iterator find = d_propagationMap.find(toExplain); if (find != d_propagationMap.end()) { + Debug("theory::explain") << "\tTerm was propagated by another theory (theory = " + << theoryOf((*find).second.theory)->getId() << ")" << std::endl; // There is some propagation, check if its a timely one if ((*find).second.timestamp < toExplain.timestamp) { + Debug("theory::explain") << "\tRelevant timetsamp, pushing " + << (*find).second.node << "to index = " << explanationVector.size() << std::endl; explanationVector.push_back((*find).second); - ++ i; + ++i; + + PROOF({ + if (toExplain.node != (*find).second.node) { + Debug("pf::explain") << "TheoryEngine::getExplanation: Rewrite alert! toAssert = " << toExplain.node + << ", toExplain = " << (*find).second.node << std::endl; + + if (proofRecipe) { + proofRecipe->addRewriteRule(toExplain.node, (*find).second.node); + } + } + }) + continue; } } @@ -1723,21 +1973,61 @@ void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector Node explanation; if (toExplain.theory == THEORY_BUILTIN) { explanation = d_sharedTerms.explain(toExplain.node); + Debug("theory::explain") << "\tTerm was propagated by THEORY_BUILTIN. Explanation: " << explanation << std::endl; } else { explanation = theoryOf(toExplain.theory)->explain(toExplain.node); + Debug("theory::explain") << "\tTerm was propagated by owner theory: " + << theoryOf(toExplain.theory)->getId() + << ". Explanation: " << explanation << 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); explanationVector.push_back(newExplain); + ++ i; + + PROOF({ + if (proofRecipe) { + // If we're expanding the target node of the explanation (this is the first expansion...), + // we don't want to add it as a separate proof step. It is already part of the assertions. + if (inputAssertions.find(toExplain.node) == inputAssertions.end()) { + LemmaProofRecipe::ProofStep proofStep(toExplain.theory, toExplain.node); + if (explanation.getKind() == kind::AND) { + Node flat = flattenAnd(explanation); + for (unsigned k = 0; k < flat.getNumChildren(); ++ k) { + // If a true constant or a negation of a false constant we can ignore it + if (! ((flat[k].isConst() && flat[k].getConst<bool>()) || + (flat[k].getKind() == kind::NOT && flat[k][0].isConst() && !flat[k][0].getConst<bool>()))) { + proofStep.addAssertion(flat[k].negate()); + } + } + } else { + if (! ((explanation.isConst() && explanation.getConst<bool>()) || + (explanation.getKind() == kind::NOT && explanation[0].isConst() && !explanation[0].getConst<bool>()))) { + proofStep.addAssertion(explanation.negate()); + } + } + proofRecipe->addStep(proofStep); + } + } + }); } // Keep only the relevant literals explanationVector.resize(j); -} + PROOF({ + if (proofRecipe) { + // The remaining literals are the base of the proof + for (unsigned k = 0; k < explanationVector.size(); ++k) { + proofRecipe->addBaseAssertion(explanationVector[k].node.negate()); + } + } + }); +} void TheoryEngine::ppUnconstrainedSimp(vector<Node>& assertions) { diff --git a/src/theory/theory_engine.h b/src/theory/theory_engine.h index db94edd7c..53c4aac77 100644 --- a/src/theory/theory_engine.h +++ b/src/theory/theory_engine.h @@ -50,6 +50,7 @@ namespace CVC4 { class ResourceManager; +class LemmaProofRecipe; /** * A pair of a theory and a node. This is used to mark the flow of @@ -270,46 +271,18 @@ class TheoryEngine { } } - void conflict(TNode conflictNode, Proof* pf = NULL) throw(AssertionException, UnsafeInterruptException) { - Trace("theory::conflict") << "EngineOutputChannel<" << d_theory << ">::conflict(" << conflictNode << ")" << std::endl; - Assert(pf == NULL); // theory shouldn't be producing proofs yet - ++ d_statistics.conflicts; - d_engine->d_outputChannelUsed = true; - d_engine->conflict(conflictNode, d_theory); - } + void conflict(TNode conflictNode, Proof* pf = NULL) throw(AssertionException, UnsafeInterruptException); - bool propagate(TNode literal) throw(AssertionException, UnsafeInterruptException) { - Trace("theory::propagate") << "EngineOutputChannel<" << d_theory << ">::propagate(" << literal << ")" << std::endl; - ++ d_statistics.propagations; - d_engine->d_outputChannelUsed = true; - return d_engine->propagate(literal, d_theory); - } + bool propagate(TNode literal) throw(AssertionException, UnsafeInterruptException); theory::LemmaStatus lemma(TNode lemma, ProofRule rule, bool removable = false, bool preprocess = false, bool sendAtoms = false) - throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException) { - Trace("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::lemma(" << lemma << ")" << std::endl; - ++ d_statistics.lemmas; - d_engine->d_outputChannelUsed = true; - return d_engine->lemma(lemma, rule, false, removable, preprocess, sendAtoms ? d_theory : theory::THEORY_LAST, d_theory); - } + throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException); - /*theory::LemmaStatus preservedLemma(TNode lemma, bool removable = false, bool preprocess = false) throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException, LogicException) { - Trace("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::preservedLemma(" << lemma << ")" << std::endl; - ++ d_statistics.lemmas; - d_engine->d_outputChannelUsed = true; - return d_engine->lemma(lemma, false, removable, preprocess, d_theory); - }*/ - - theory::LemmaStatus splitLemma(TNode lemma, bool removable = false) throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException) { - Trace("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::splitLemma(" << lemma << ")" << std::endl; - ++ d_statistics.lemmas; - d_engine->d_outputChannelUsed = true; - return d_engine->lemma(lemma, RULE_SPLIT, false, removable, false, d_theory, d_theory); - } + theory::LemmaStatus splitLemma(TNode lemma, bool removable = false) throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException); void demandRestart() throw(TypeCheckingExceptionPrivate, AssertionException, UnsafeInterruptException) { NodeManager* curr = NodeManager::currentNM(); @@ -456,7 +429,7 @@ class TheoryEngine { bool removable, bool preprocess, theory::TheoryId atomsTo, - theory::TheoryId ownerTheory); + LemmaProofRecipe* proofRecipe); /** Enusre that the given atoms are send to the given theory */ void ensureLemmaAtoms(const std::vector<TNode>& atoms, theory::TheoryId theory); @@ -606,9 +579,10 @@ private: * asking relevant theories to explain the propagations. Initially * the explanation vector should contain only the element (node, theory) * where the node is the one to be explained, and the theory is the - * theory that sent the literal. + * theory that sent the literal. The lemmaProofRecipe will contain a list + * of the explanation steps required to produce the original node. */ - void getExplanation(std::vector<NodeTheoryPair>& explanationVector); + void getExplanation(std::vector<NodeTheoryPair>& explanationVector, LemmaProofRecipe* lemmaProofRecipe); public: @@ -730,7 +704,7 @@ public: * Returns an explanation of the node propagated to the SAT solver and the theory * that propagated it. */ - NodeTheoryPair getExplanationAndExplainer(TNode node); + Node getExplanationAndRecipe(TNode node, LemmaProofRecipe* proofRecipe); /** * collect model info diff --git a/src/theory/theory_model.cpp b/src/theory/theory_model.cpp index fa7e497e2..f43a2aa7f 100644 --- a/src/theory/theory_model.cpp +++ b/src/theory/theory_model.cpp @@ -561,7 +561,7 @@ void TheoryEngineModelBuilder::buildModel(Model* m, bool fullModel) TheoryModel* tm = (TheoryModel*)m; // buildModel with fullModel = true should only be called once in any context - Assert(!tm->d_modelBuilt); + Assert(!tm->isBuilt()); tm->d_modelBuilt = fullModel; // Reset model @@ -832,6 +832,7 @@ void TheoryEngineModelBuilder::buildModel(Model* m, bool fullModel) Assert(!t.isBoolean() || (*i2).getKind() == kind::APPLY_UF); Node n; if (t.getCardinality().isInfinite()) { + // if (!t.isInterpretedFinite()) { bool success; do{ Trace("model-builder-debug") << "Enumerate term of type " << t << std::endl; diff --git a/src/theory/theory_model.h b/src/theory/theory_model.h index 6e4f77336..833b124eb 100644 --- a/src/theory/theory_model.h +++ b/src/theory/theory_model.h @@ -36,6 +36,7 @@ class TheoryModel : public Model protected: /** substitution map for this model */ SubstitutionMap d_substitutions; + context::CDO<bool> d_modelBuilt; public: TheoryModel(context::Context* c, std::string name, bool enableFuncModels); virtual ~TheoryModel() throw(); @@ -51,7 +52,6 @@ public: /** true/false nodes */ Node d_true; Node d_false; - context::CDO<bool> d_modelBuilt; mutable std::hash_map<Node, Node, NodeHashFunction> d_modelCache; protected: @@ -62,6 +62,8 @@ protected: */ Node getModelValue(TNode n, bool hasBoundVars = false, bool useDontCares = false) const; public: + /** is built */ + bool isBuilt() { return d_modelBuilt.get(); } /** * Get value function. This should be called only after a ModelBuilder has called buildModel(...) * on this model. diff --git a/src/theory/type_enumerator.h b/src/theory/type_enumerator.h index d1318aaa8..bcd7e695f 100644 --- a/src/theory/type_enumerator.h +++ b/src/theory/type_enumerator.h @@ -115,7 +115,7 @@ public: // 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 defined(CVC4_ASSERTIONS) && !(defined(__clang__)) if(d_te->isFinished()) { try { **d_te; diff --git a/src/theory/uf/equality_engine.cpp b/src/theory/uf/equality_engine.cpp index 9b429765e..25b12f75f 100644 --- a/src/theory/uf/equality_engine.cpp +++ b/src/theory/uf/equality_engine.cpp @@ -964,12 +964,9 @@ void EqualityEngine::explainEquality(TNode t1, TNode t2, bool polarity, std::vec std::vector<EqProof *> orderedChildren; bool nullCongruenceFound = false; for (unsigned i = 0; i < eqpc->d_children.size(); ++i) { - if (eqpc->d_children[i]->d_id==eq::MERGED_THROUGH_CONGRUENCE && eqpc->d_children[i]->d_node.isNull()) { - - // For now, assume there can only be one null congruence child - Assert(!nullCongruenceFound); + if (eqpc->d_children[i]->d_id==eq::MERGED_THROUGH_CONGRUENCE && + eqpc->d_children[i]->d_node.isNull()) { nullCongruenceFound = true; - Debug("pf::ee") << "Have congruence with empty d_node. Splitting..." << std::endl; orderedChildren.insert(orderedChildren.begin(), eqpc->d_children[i]->d_children[0]); orderedChildren.push_back(eqpc->d_children[i]->d_children[1]); @@ -1192,6 +1189,9 @@ void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, st getExplanation(childId, getEqualityNode(childId).getFind(), equalities, eqpcc); if( eqpc ) { eqpc->d_children.push_back( eqpcc ); + + Debug("pf::ee") << "MERGED_THROUGH_CONSTANTS. Dumping the child proof" << std::endl; + eqpc->debug_print("pf::ee", 1); } } @@ -1605,6 +1605,7 @@ void EqualityEngine::propagate() { } void EqualityEngine::debugPrintGraph() const { + Debug("equality::graph") << std::endl << "Dumping graph" << std::endl; for (EqualityNodeId nodeId = 0; nodeId < d_nodes.size(); ++ nodeId) { Debug("equality::graph") << d_nodes[nodeId] << " " << nodeId << "(" << getEqualityNode(nodeId).getFind() << "):"; @@ -1618,6 +1619,7 @@ void EqualityEngine::debugPrintGraph() const { Debug("equality::graph") << std::endl; } + Debug("equality::graph") << std::endl; } bool EqualityEngine::areEqual(TNode t1, TNode t2) const { @@ -2209,9 +2211,15 @@ bool EqClassIterator::isFinished() const { return d_current == null_id; } -void EqProof::debug_print( const char * c, unsigned tb ) const{ - for( unsigned i=0; i<tb; i++ ) { Debug( c ) << " "; } - Debug( c ) << d_id << "("; +void EqProof::debug_print(const char* c, unsigned tb, PrettyPrinter* prettyPrinter) const { + for(unsigned i=0; i<tb; i++) { Debug( c ) << " "; } + + if (prettyPrinter) + Debug( c ) << prettyPrinter->printTag(d_id); + else + Debug( c ) << d_id; + + Debug( c ) << "("; if( !d_children.empty() || !d_node.isNull() ){ if( !d_node.isNull() ){ Debug( c ) << std::endl; @@ -2221,7 +2229,7 @@ void EqProof::debug_print( const char * c, unsigned tb ) const{ for( unsigned i=0; i<d_children.size(); i++ ){ if( i>0 || !d_node.isNull() ) Debug( c ) << ","; Debug( c ) << std::endl; - d_children[i]->debug_print( c, tb+1 ); + d_children[i]->debug_print( c, tb+1, prettyPrinter ); } } Debug( c ) << ")" << std::endl; diff --git a/src/theory/uf/equality_engine.h b/src/theory/uf/equality_engine.h index f30f1e8a0..843e7ce7f 100644 --- a/src/theory/uf/equality_engine.h +++ b/src/theory/uf/equality_engine.h @@ -902,11 +902,17 @@ public: class EqProof { public: + class PrettyPrinter { + public: + virtual ~PrettyPrinter() {} + virtual std::string printTag(unsigned tag) = 0; + }; + EqProof() : d_id(MERGED_THROUGH_REFLEXIVITY){} unsigned d_id; Node d_node; std::vector< EqProof * > d_children; - void debug_print( const char * c, unsigned tb = 0 ) const; + void debug_print(const char * c, unsigned tb = 0, PrettyPrinter* prettyPrinter = NULL) const; };/* class EqProof */ } // Namespace eq diff --git a/src/theory/uf/theory_uf.cpp b/src/theory/uf/theory_uf.cpp index 0c7bed773..ae935798e 100644 --- a/src/theory/uf/theory_uf.cpp +++ b/src/theory/uf/theory_uf.cpp @@ -26,6 +26,8 @@ #include "theory/theory_model.h" #include "theory/type_enumerator.h" #include "theory/uf/theory_uf_strong_solver.h" +#include "theory/quantifiers/term_database.h" +#include "options/theory_options.h" using namespace std; @@ -431,100 +433,107 @@ void TheoryUF::addSharedTerm(TNode t) { d_equalityEngine.addTriggerTerm(t, THEORY_UF); } -void TheoryUF::computeCareGraph() { - - if (d_sharedTerms.size() > 0) { - - vector< pair<TNode, TNode> > currentPairs; - - // Go through the function terms and see if there are any to split on - unsigned functionTerms = d_functionsTerms.size(); - for (unsigned i = 0; i < functionTerms; ++ i) { - - TNode f1 = d_functionsTerms[i]; - Node op = f1.getOperator(); - - for (unsigned j = i + 1; j < functionTerms; ++ j) { - - TNode f2 = d_functionsTerms[j]; - - // If the operators are not the same, we can skip this pair - if (f2.getOperator() != op) { - continue; - } - +//TODO: move quantifiers::TermArgTrie to src/theory/ +void TheoryUF::addCarePairs( quantifiers::TermArgTrie * t1, quantifiers::TermArgTrie * t2, unsigned arity, unsigned depth ){ + if( depth==arity ){ + if( t2!=NULL ){ + Node f1 = t1->getNodeData(); + Node f2 = t2->getNodeData(); + if( !d_equalityEngine.areEqual( f1, f2 ) ){ Debug("uf::sharing") << "TheoryUf::computeCareGraph(): checking function " << f1 << " and " << f2 << std::endl; - - // If the terms are already known to be equal, we are also in good shape - if (d_equalityEngine.areEqual(f1, f2)) { - Debug("uf::sharing") << "TheoryUf::computeCareGraph(): equal, skipping" << std::endl; - continue; - } - - // We have two functions f(x1, ..., xn) and f(y1, ..., yn) no known to be equal - // We split on the argument pairs that are are not known, unless there is some - // argument pair that is already dis-equal. - bool somePairIsDisequal = false; - currentPairs.clear(); + vector< pair<TNode, TNode> > currentPairs; for (unsigned k = 0; k < f1.getNumChildren(); ++ k) { - TNode x = f1[k]; TNode y = f2[k]; - - Debug("uf::sharing") << "TheoryUf::computeCareGraph(): checking arguments " << x << " and " << y << std::endl; - - if (d_equalityEngine.areDisequal(x, y, false)) { - // Mark that there is a dis-equal pair and break - somePairIsDisequal = true; - Debug("uf::sharing") << "TheoryUf::computeCareGraph(): disequal, disregarding all" << std::endl; - break; + Assert( d_equalityEngine.hasTerm(x) ); + Assert( d_equalityEngine.hasTerm(y) ); + Assert( !d_equalityEngine.areDisequal( x, y, false ) ); + if( !d_equalityEngine.areEqual( x, y ) ){ + if( d_equalityEngine.isTriggerTerm(x, THEORY_UF) && d_equalityEngine.isTriggerTerm(y, THEORY_UF) ){ + TNode x_shared = d_equalityEngine.getTriggerTermRepresentative(x, THEORY_UF); + TNode y_shared = d_equalityEngine.getTriggerTermRepresentative(y, THEORY_UF); + EqualityStatus eqStatus = d_valuation.getEqualityStatus(x_shared, y_shared); + if( eqStatus==EQUALITY_FALSE_AND_PROPAGATED || eqStatus==EQUALITY_FALSE || eqStatus==EQUALITY_FALSE_IN_MODEL ){ + //an argument is disequal, we are done + return; + }else{ + currentPairs.push_back(make_pair(x_shared, y_shared)); + } + } } - - if (d_equalityEngine.areEqual(x, y)) { - // We don't need this one - Debug("uf::sharing") << "TheoryUf::computeCareGraph(): equal" << std::endl; - continue; - } - - if (!d_equalityEngine.isTriggerTerm(x, THEORY_UF) || !d_equalityEngine.isTriggerTerm(y, THEORY_UF)) { - // Not connected to shared terms, we don't care - continue; + } + for (unsigned c = 0; c < currentPairs.size(); ++ c) { + Debug("uf::sharing") << "TheoryUf::computeCareGraph(): adding to care-graph" << std::endl; + addCarePair(currentPairs[c].first, currentPairs[c].second); + } + } + } + }else{ + if( t2==NULL ){ + if( depth<(arity-1) ){ + //add care pairs internal to each child + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = t1->d_data.begin(); it != t1->d_data.end(); ++it ){ + addCarePairs( &it->second, NULL, arity, depth+1 ); + } + } + //add care pairs based on each pair of non-disequal arguments + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = t1->d_data.begin(); it != t1->d_data.end(); ++it ){ + std::map< TNode, quantifiers::TermArgTrie >::iterator it2 = it; + ++it2; + for( ; it2 != t1->d_data.end(); ++it2 ){ + if( !d_equalityEngine.areDisequal(it->first, it2->first, false) ){ + addCarePairs( &it->second, &it2->second, arity, depth+1 ); } - - // Get representative trigger terms - TNode x_shared = d_equalityEngine.getTriggerTermRepresentative(x, THEORY_UF); - TNode y_shared = d_equalityEngine.getTriggerTermRepresentative(y, THEORY_UF); - - EqualityStatus eqStatusDomain = d_valuation.getEqualityStatus(x_shared, y_shared); - switch (eqStatusDomain) { - case EQUALITY_FALSE_AND_PROPAGATED: - case EQUALITY_FALSE: - case EQUALITY_FALSE_IN_MODEL: - somePairIsDisequal = true; - continue; - break; - default: - break; - // nothing + } + } + }else{ + //add care pairs based on product of indices, non-disequal arguments + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it = t1->d_data.begin(); it != t1->d_data.end(); ++it ){ + for( std::map< TNode, quantifiers::TermArgTrie >::iterator it2 = t2->d_data.begin(); it2 != t2->d_data.end(); ++it2 ){ + if( !d_equalityEngine.areDisequal(it->first, it2->first, false) ){ + addCarePairs( &it->second, &it2->second, arity, depth+1 ); } - - // Otherwise, we need to figure it out - Debug("uf::sharing") << "TheoryUf::computeCareGraph(): adding to care-graph" << std::endl; - currentPairs.push_back(make_pair(x_shared, y_shared)); } + } + } + } +} - if (!somePairIsDisequal) { - for (unsigned i = 0; i < currentPairs.size(); ++ i) { - addCarePair(currentPairs[i].first, currentPairs[i].second); - } +void TheoryUF::computeCareGraph() { + + if (d_sharedTerms.size() > 0) { + //use term indexing + Debug("uf::sharing") << "TheoryUf::computeCareGraph(): Build term indices..." << std::endl; + std::map< Node, quantifiers::TermArgTrie > index; + std::map< Node, unsigned > arity; + unsigned functionTerms = d_functionsTerms.size(); + for (unsigned i = 0; i < functionTerms; ++ i) { + TNode f1 = d_functionsTerms[i]; + Node op = f1.getOperator(); + std::vector< TNode > reps; + bool has_trigger_arg = false; + for( unsigned j=0; j<f1.getNumChildren(); j++ ){ + reps.push_back( d_equalityEngine.getRepresentative( f1[j] ) ); + if( d_equalityEngine.isTriggerTerm( f1[j], THEORY_UF ) ){ + has_trigger_arg = true; } } + if( has_trigger_arg ){ + index[op].addTerm( f1, reps ); + arity[op] = reps.size(); + } + } + //for each index + for( std::map< Node, quantifiers::TermArgTrie >::iterator itii = index.begin(); itii != index.end(); ++itii ){ + Debug("uf::sharing") << "TheoryUf::computeCareGraph(): Process index " << itii->first << "..." << std::endl; + addCarePairs( &itii->second, NULL, arity[ itii->first ], 0 ); } } }/* TheoryUF::computeCareGraph() */ void TheoryUF::conflict(TNode a, TNode b) { eq::EqProof* pf = d_proofsEnabled ? new eq::EqProof() : NULL; + if (a.getKind() == kind::CONST_BOOLEAN) { d_conflictNode = explain(a.iffNode(b),pf); } else { @@ -542,9 +551,9 @@ void TheoryUF::eqNotifyNewClass(TNode t) { } void TheoryUF::eqNotifyPreMerge(TNode t1, TNode t2) { - if (getLogicInfo().isQuantified()) { + //if (getLogicInfo().isQuantified()) { //getQuantifiersEngine()->getEfficientEMatcher()->merge( t1, t2 ); - } + //} } void TheoryUF::eqNotifyPostMerge(TNode t1, TNode t2) { diff --git a/src/theory/uf/theory_uf.h b/src/theory/uf/theory_uf.h index 42a804c09..3a83decec 100644 --- a/src/theory/uf/theory_uf.h +++ b/src/theory/uf/theory_uf.h @@ -32,6 +32,11 @@ namespace CVC4 { namespace theory { + +namespace quantifiers{ + class TermArgTrie; +} + namespace uf { class UfTermDb; @@ -204,6 +209,8 @@ public: StrongSolverTheoryUF* getStrongSolver() { return d_thss; } +private: + void addCarePairs( quantifiers::TermArgTrie * t1, quantifiers::TermArgTrie * t2, unsigned arity, unsigned depth ); };/* class TheoryUF */ }/* CVC4::theory::uf namespace */ diff --git a/src/theory/uf/theory_uf_strong_solver.cpp b/src/theory/uf/theory_uf_strong_solver.cpp index ed28cc2fc..cda94e1c4 100644 --- a/src/theory/uf/theory_uf_strong_solver.cpp +++ b/src/theory/uf/theory_uf_strong_solver.cpp @@ -1670,6 +1670,12 @@ StrongSolverTheoryUF::StrongSolverTheoryUF(context::Context* c, } } +StrongSolverTheoryUF::~StrongSolverTheoryUF() { + for( std::map< TypeNode, SortModel* >::iterator it = d_rep_model.begin(); it != d_rep_model.end(); ++it ){ + delete it->second; + } +} + SortInference* StrongSolverTheoryUF::getSortInference() { return d_th->getQuantifiersEngine()->getTheoryEngine()->getSortInference(); } diff --git a/src/theory/uf/theory_uf_strong_solver.h b/src/theory/uf/theory_uf_strong_solver.h index 11f0664f3..4e4dbef83 100644 --- a/src/theory/uf/theory_uf_strong_solver.h +++ b/src/theory/uf/theory_uf_strong_solver.h @@ -414,7 +414,7 @@ private: SubsortSymmetryBreaker* d_sym_break; public: StrongSolverTheoryUF(context::Context* c, context::UserContext* u, OutputChannel& out, TheoryUF* th); - ~StrongSolverTheoryUF() {} + ~StrongSolverTheoryUF(); /** get theory */ TheoryUF* getTheory() { return d_th; } /** disequality propagator */ diff --git a/src/theory/valuation.cpp b/src/theory/valuation.cpp index 165937c13..7e13668cd 100644 --- a/src/theory/valuation.cpp +++ b/src/theory/valuation.cpp @@ -87,6 +87,10 @@ Node Valuation::getModelValue(TNode var) { return d_engine->getModelValue(var); } +TheoryModel* Valuation::getModel() { + return d_engine->getModel(); +} + Node Valuation::ensureLiteral(TNode n) { return d_engine->ensureLiteral(n); } diff --git a/src/theory/valuation.h b/src/theory/valuation.h index 4ecdecad0..54af14fdd 100644 --- a/src/theory/valuation.h +++ b/src/theory/valuation.h @@ -32,6 +32,7 @@ namespace theory { class EntailmentCheckParameters; class EntailmentCheckSideEffects; +class TheoryModel; /** * The status of an equality in the current context. @@ -106,6 +107,11 @@ public: Node getModelValue(TNode var); /** + * Returns pointer to model. + */ + TheoryModel* getModel(); + + /** * Ensure that the given node will have a designated SAT literal * that is definitionally equal to it. The result of this function * is a Node that can be queried via getSatValue(). diff --git a/src/util/bitvector.h b/src/util/bitvector.h index a04cbb58f..4a74c1c53 100644 --- a/src/util/bitvector.h +++ b/src/util/bitvector.h @@ -338,6 +338,15 @@ public: return d_value; } + Integer toSignedInt() const { + // returns Integer corresponding to two's complement interpretation of bv + unsigned size = d_size; + Integer sign_bit = d_value.extractBitRange(1,size-1); + Integer val = d_value.extractBitRange(size-1, 0); + Integer res = Integer(-1) * sign_bit.multiplyByPow2(size - 1) + val; + return res; + } + /** Returns k is the integer is equal to 2^{k-1} and zero otherwise @@ -356,14 +365,6 @@ private: unsigned d_size; Integer d_value; - Integer toSignedInt() const { - // returns Integer corresponding to two's complement interpretation of bv - unsigned size = d_size; - Integer sign_bit = d_value.extractBitRange(1,size-1); - Integer val = d_value.extractBitRange(size-1, 0); - Integer res = Integer(-1) * sign_bit.multiplyByPow2(size - 1) + val; - return res; - } };/* class BitVector */ diff --git a/src/util/proof.h b/src/util/proof.h index fc5f7f901..b4a8a3d29 100644 --- a/src/util/proof.h +++ b/src/util/proof.h @@ -21,13 +21,21 @@ #define __CVC4__PROOF_H #include <iosfwd> +#include <ext/hash_map> namespace CVC4 { +class Expr; +class ProofLetCount; +struct ExprHashFunction; + +typedef __gnu_cxx::hash_map<Expr, ProofLetCount, ExprHashFunction> ProofLetMap; + class CVC4_PUBLIC Proof { public: virtual ~Proof() { } virtual void toStream(std::ostream& out) = 0; + virtual void toStream(std::ostream& out, const ProofLetMap& map) = 0; };/* class Proof */ }/* CVC4 namespace */ |