diff options
131 files changed, 3347 insertions, 1261 deletions
diff --git a/.style.yapf b/.style.yapf new file mode 100644 index 000000000..f9f97e806 --- /dev/null +++ b/.style.yapf @@ -0,0 +1,5 @@ +[style] +based_on_style = pep8 +split_before_arithmetic_operator = true +split_before_bitwise_operator = true +split_before_logical_operator = true @@ -38,8 +38,8 @@ Alumni: Other contributors to the cvc5 codebase are listed in the THANKS file. -cvc5 is the fifth in the CVC series of tools (CVC, CVC Lite, CVC3) but does -not directly incorporate code from any previous version prior to CVC4. +cvc5 is the fifth in the CVC series of tools (CVC, CVC Lite, CVC3, CVC4) but +does not directly incorporate code from any previous version prior to CVC4. Information about authors of previous CVC tools is included with their distributions. diff --git a/CMakeLists.txt b/CMakeLists.txt index f5f571046..bb8e305b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -657,18 +657,18 @@ print_config("Java bindings " ${BUILD_BINDINGS_JAVA}) print_config("Python2 " ${USE_PYTHON2}) message("") print_config("ABC " ${USE_ABC}) -print_config("CryptoMiniSat " ${USE_CRYPTOMINISAT}) +print_config("CryptoMiniSat " ${USE_CRYPTOMINISAT} FOUND_SYSTEM ${CryptoMiniSat_FOUND_SYSTEM}) print_config("GLPK " ${USE_GLPK}) -print_config("Kissat " ${USE_KISSAT}) -print_config("LibPoly " ${USE_POLY}) -print_config("CoCoALib " ${USE_COCOA}) +print_config("Kissat " ${USE_KISSAT} FOUND_SYSTEM ${Kissat_FOUND_SYSTEM}) +print_config("LibPoly " ${USE_POLY} FOUND_SYSTEM ${Poly_FOUND_SYSTEM}) +print_config("CoCoALib " ${USE_COCOA} FOUND_SYSTEM ${CoCoA_FOUND_SYSTEM}) message("") print_config("Build libcvc5 only " ${BUILD_LIB_ONLY}) if(CVC5_USE_CLN_IMP) - print_config("MP library " "cln") + print_config("MP library " "cln" FOUND_SYSTEM ${CLN_FOUND_SYSTEM}) else() - print_config("MP library " "gmp") + print_config("MP library " "gmp" FOUND_SYSTEM ${GMP_FOUND_SYSTEM}) endif() print_config("Editline " ${USE_EDITLINE}) message("") @@ -23,6 +23,7 @@ New Features: * Support for `str.indexof_re(s, r, n)`, which returns the index of the first occurrence of a regular expression `r` in a string `s` after index `n` or -1 if `r` does not match a substring after `n`. +* A new option to compute minimal unsat cores (`--minimal-unsat-cores`). Improvements: * New API: Added functions to retrieve the heap/nil term when using separation diff --git a/cmake/FindANTLR3.cmake b/cmake/FindANTLR3.cmake index ea0c4131f..373182904 100644 --- a/cmake/FindANTLR3.cmake +++ b/cmake/FindANTLR3.cmake @@ -112,6 +112,7 @@ if(NOT ANTLR3_FOUND_SYSTEM) --with-pic --disable-antlrdebug --prefix=<INSTALL_DIR> + --libdir=<INSTALL_DIR>/${CMAKE_INSTALL_LIBDIR} --srcdir=<SOURCE_DIR> --disable-shared --enable-static diff --git a/cmake/FindCLN.cmake b/cmake/FindCLN.cmake index b5b37ec59..e802da2ef 100644 --- a/cmake/FindCLN.cmake +++ b/cmake/FindCLN.cmake @@ -58,7 +58,10 @@ if(NOT CLN_FOUND_SYSTEM) URL "https://www.ginac.de/CLN/cln.git/?p=cln.git\\\;a=snapshot\\\;h=cln_${CLN_TAG}\\\;sf=tgz" URL_HASH SHA1=71d02b90ef0575f06b7bafb8690f73e8064d8228 DOWNLOAD_NAME cln.tgz - CONFIGURE_COMMAND cd <SOURCE_DIR> && ./autogen.sh && autoreconf -iv + CONFIGURE_COMMAND + ${CMAKE_COMMAND} -E chdir <SOURCE_DIR> ./autogen.sh + COMMAND + ${CMAKE_COMMAND} -E chdir <SOURCE_DIR> autoreconf -iv COMMAND <SOURCE_DIR>/configure --prefix=<INSTALL_DIR> --disable-shared --enable-static --with-pic BUILD_BYPRODUCTS <INSTALL_DIR>/lib/libcln.a diff --git a/cmake/FindCoCoA.cmake b/cmake/FindCoCoA.cmake index 1a4f82e9f..d84bf0f2c 100644 --- a/cmake/FindCoCoA.cmake +++ b/cmake/FindCoCoA.cmake @@ -56,6 +56,10 @@ if(NOT CoCoA_FOUND_SYSTEM) ${COMMON_EP_CONFIG} URL "http://cocoa.dima.unige.it/cocoalib/tgz/CoCoALib-${CoCoA_VERSION}.tgz" URL_HASH SHA1=873d0b60800cd3852939816ce0aa2e7f72dac4ce + # CoCoA requires C++14, but the check does not work with compilers that + # default to C++17 or newer. The patch fixes the check. + PATCH_COMMAND patch -p1 -d <SOURCE_DIR> + -i ${CMAKE_CURRENT_LIST_DIR}/deps-utils/CoCoA-patch-0.99712.patch BUILD_IN_SOURCE YES CONFIGURE_COMMAND ./configure --prefix=<INSTALL_DIR> BUILD_COMMAND ${make_cmd} library diff --git a/cmake/Helpers.cmake b/cmake/Helpers.cmake index 10bcf96de..283828e29 100644 --- a/cmake/Helpers.cmake +++ b/cmake/Helpers.cmake @@ -131,9 +131,15 @@ macro(print_info msg) message("${Blue}${msg}${ResetColor}") endmacro() -# Helper to print the configuration of a 2-valued or 3-valued option 'var' -# with prefix 'str'. +# Helper to print the configuration of a 2-valued or 3-valued option 'var' with +# prefix 'str'. Optionally takes a `FOUND_SYSTEM` argument that is used to +# indicate when a given dependency is built as part of the cvc5 build. function(print_config str var) + set(options) + set(oneValueArgs FOUND_SYSTEM) + set(multiValueArgs) + cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if("${var}" STREQUAL "ON") set(OPT_VAL_STR "on") elseif("${var}" STREQUAL "OFF" OR "${var}" STREQUAL "IGNORE") @@ -141,7 +147,16 @@ function(print_config str var) else() set(OPT_VAL_STR "${var}") endif() - message("${Blue}${str}: ${Green}${OPT_VAL_STR}${ResetColor}") + + if("${ARGS_FOUND_SYSTEM}" STREQUAL "TRUE") + set(OPT_FOUND_SYSTEM_STR " (system)") + elseif("${ARGS_FOUND_SYSTEM}" STREQUAL "FALSE") + set(OPT_FOUND_SYSTEM_STR " (local)") + else() + set(OPT_FOUND_SYSTEM_STR "") + endif() + + message("${Blue}${str}: ${Green}${OPT_VAL_STR}${OPT_FOUND_SYSTEM_STR}${ResetColor}") endfunction() diff --git a/cmake/deps-utils/CoCoA-patch-0.99712.patch b/cmake/deps-utils/CoCoA-patch-0.99712.patch new file mode 100644 index 000000000..464d39f83 --- /dev/null +++ b/cmake/deps-utils/CoCoA-patch-0.99712.patch @@ -0,0 +1,13 @@ +diff --git a/configuration/cxx14.sh b/configuration/cxx14.sh +index cdbf338..0436983 100755 +--- a/configuration/cxx14.sh ++++ b/configuration/cxx14.sh +@@ -40,7 +40,7 @@ int main() + { + int ReturnCode = 0; // will mean c++14 compliant + std::cout << "C++ version: " << __cplusplus << std::endl; +-#if __cplusplus < 201400L ++#if __cplusplus < 201400L || __cplusplus >= 201703L + ReturnCode = 1; // NOT C++14 compilant + #endif + return ReturnCode; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b6bf49ed8..9eb7ec3c4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -159,6 +159,8 @@ libcvc5_add_sources( proof/lazy_proof_chain.h proof/lazy_tree_proof_generator.cpp proof/lazy_tree_proof_generator.h + proof/lfsc/lfsc_util.cpp + proof/lfsc/lfsc_util.h proof/method_id.cpp proof/method_id.h proof/proof.cpp @@ -169,6 +171,8 @@ libcvc5_add_sources( proof/proof_ensure_closed.h proof/proof_generator.cpp proof/proof_generator.h + proof/proof_letify.cpp + proof/proof_letify.h proof/proof_node.cpp proof/proof_node.h proof/proof_node_algorithm.cpp @@ -662,6 +666,8 @@ libcvc5_add_sources( theory/decision_strategy.h theory/ee_manager.cpp theory/ee_manager.h + theory/ee_manager_central.cpp + theory/ee_manager_central.h theory/ee_manager_distributed.cpp theory/ee_manager_distributed.h theory/ee_setup_info.h @@ -855,6 +861,7 @@ libcvc5_add_sources( theory/quantifiers/sygus/cegis_core_connective.h theory/quantifiers/sygus/cegis_unif.cpp theory/quantifiers/sygus/cegis_unif.h + theory/quantifiers/sygus/enum_val_generator.h theory/quantifiers/sygus/example_eval_cache.cpp theory/quantifiers/sygus/example_eval_cache.h theory/quantifiers/sygus/example_infer.cpp @@ -877,6 +884,8 @@ libcvc5_add_sources( theory/quantifiers/sygus/sygus_enumerator.h theory/quantifiers/sygus/sygus_enumerator_basic.cpp theory/quantifiers/sygus/sygus_enumerator_basic.h + theory/quantifiers/sygus/sygus_enumerator_callback.cpp + theory/quantifiers/sygus/sygus_enumerator_callback.h theory/quantifiers/sygus/sygus_eval_unfold.cpp theory/quantifiers/sygus/sygus_eval_unfold.h theory/quantifiers/sygus/sygus_explain.cpp diff --git a/src/api/cpp/cvc5.cpp b/src/api/cpp/cvc5.cpp index 4e14b8e34..9b920b875 100644 --- a/src/api/cpp/cvc5.cpp +++ b/src/api/cpp/cvc5.cpp @@ -570,6 +570,7 @@ const static std::unordered_map<cvc5::Kind, Kind, cvc5::kind::KindHashFunction> {cvc5::Kind::APPLY_UPDATER, APPLY_UPDATER}, {cvc5::Kind::DT_SIZE, DT_SIZE}, {cvc5::Kind::TUPLE_PROJECT, TUPLE_PROJECT}, + {cvc5::Kind::TUPLE_PROJECT_OP, TUPLE_PROJECT}, /* Separation Logic ------------------------------------------------ */ {cvc5::Kind::SEP_NIL, SEP_NIL}, {cvc5::Kind::SEP_EMP, SEP_EMP}, @@ -1865,6 +1866,14 @@ size_t Op::getNumIndices() const { CVC5_API_TRY_CATCH_BEGIN; CVC5_API_CHECK_NOT_NULL; + //////// all checks before this line + return getNumIndicesHelper(); + //////// + CVC5_API_TRY_CATCH_END; +} + +size_t Op::getNumIndicesHelper() const +{ if (!isIndexedHelper()) { return 0; @@ -1898,9 +1907,185 @@ size_t Op::getNumIndices() const break; default: CVC5_API_CHECK(false) << "Unhandled kind " << kindToString(k); } + return size; +} + +Term Op::operator[](size_t index) const +{ + return getIndexHelper(index); +} + +Term Op::getIndexHelper(size_t index) const +{ + CVC5_API_TRY_CATCH_BEGIN; + CVC5_API_CHECK_NOT_NULL; + CVC5_API_CHECK(!d_node->isNull()) + << "Expecting a non-null internal expression. This Op is not indexed."; + CVC5_API_CHECK(index < getNumIndicesHelper()) << "index out of bound"; + Kind k = intToExtKind(d_node->getKind()); + Term t; + switch (k) + { + case DIVISIBLE: + { + t = d_solver->mkValHelper<Rational>( + Rational(d_node->getConst<Divisible>().k)); + break; + } + case BITVECTOR_REPEAT: + { + t = d_solver->mkValHelper<cvc5::Rational>( + d_node->getConst<BitVectorRepeat>().d_repeatAmount); + break; + } + case BITVECTOR_ZERO_EXTEND: + { + t = d_solver->mkValHelper<cvc5::Rational>( + d_node->getConst<BitVectorZeroExtend>().d_zeroExtendAmount); + break; + } + case BITVECTOR_SIGN_EXTEND: + { + t = d_solver->mkValHelper<cvc5::Rational>( + d_node->getConst<BitVectorSignExtend>().d_signExtendAmount); + break; + } + case BITVECTOR_ROTATE_LEFT: + { + t = d_solver->mkValHelper<cvc5::Rational>( + d_node->getConst<BitVectorRotateLeft>().d_rotateLeftAmount); + break; + } + case BITVECTOR_ROTATE_RIGHT: + { + t = d_solver->mkValHelper<cvc5::Rational>( + d_node->getConst<BitVectorRotateRight>().d_rotateRightAmount); + break; + } + case INT_TO_BITVECTOR: + { + t = d_solver->mkValHelper<cvc5::Rational>( + d_node->getConst<IntToBitVector>().d_size); + break; + } + case IAND: + { + t = d_solver->mkValHelper<cvc5::Rational>( + d_node->getConst<IntAnd>().d_size); + break; + } + case FLOATINGPOINT_TO_UBV: + { + t = d_solver->mkValHelper<cvc5::Rational>( + d_node->getConst<FloatingPointToUBV>().d_bv_size.d_size); + break; + } + case FLOATINGPOINT_TO_SBV: + { + t = d_solver->mkValHelper<cvc5::Rational>( + d_node->getConst<FloatingPointToSBV>().d_bv_size.d_size); + break; + } + case REGEXP_REPEAT: + { + t = d_solver->mkValHelper<cvc5::Rational>( + d_node->getConst<RegExpRepeat>().d_repeatAmount); + break; + } + case BITVECTOR_EXTRACT: + { + cvc5::BitVectorExtract ext = d_node->getConst<BitVectorExtract>(); + t = index == 0 ? d_solver->mkValHelper<cvc5::Rational>(ext.d_high) + : d_solver->mkValHelper<cvc5::Rational>(ext.d_low); + break; + } + case FLOATINGPOINT_TO_FP_IEEE_BITVECTOR: + { + cvc5::FloatingPointToFPIEEEBitVector ext = + d_node->getConst<FloatingPointToFPIEEEBitVector>(); + + t = index == 0 ? d_solver->mkValHelper<cvc5::Rational>( + ext.getSize().exponentWidth()) + : d_solver->mkValHelper<cvc5::Rational>( + ext.getSize().significandWidth()); + break; + } + case FLOATINGPOINT_TO_FP_FLOATINGPOINT: + { + cvc5::FloatingPointToFPFloatingPoint ext = + d_node->getConst<FloatingPointToFPFloatingPoint>(); + t = index == 0 ? d_solver->mkValHelper<cvc5::Rational>( + ext.getSize().exponentWidth()) + : d_solver->mkValHelper<cvc5::Rational>( + ext.getSize().significandWidth()); + break; + } + case FLOATINGPOINT_TO_FP_REAL: + { + cvc5::FloatingPointToFPReal ext = + d_node->getConst<FloatingPointToFPReal>(); + + t = index == 0 ? d_solver->mkValHelper<cvc5::Rational>( + ext.getSize().exponentWidth()) + : d_solver->mkValHelper<cvc5::Rational>( + ext.getSize().significandWidth()); + break; + } + case FLOATINGPOINT_TO_FP_SIGNED_BITVECTOR: + { + cvc5::FloatingPointToFPSignedBitVector ext = + d_node->getConst<FloatingPointToFPSignedBitVector>(); + t = index == 0 ? d_solver->mkValHelper<cvc5::Rational>( + ext.getSize().exponentWidth()) + : d_solver->mkValHelper<cvc5::Rational>( + ext.getSize().significandWidth()); + break; + } + case FLOATINGPOINT_TO_FP_UNSIGNED_BITVECTOR: + { + cvc5::FloatingPointToFPUnsignedBitVector ext = + d_node->getConst<FloatingPointToFPUnsignedBitVector>(); + t = index == 0 ? d_solver->mkValHelper<cvc5::Rational>( + ext.getSize().exponentWidth()) + : d_solver->mkValHelper<cvc5::Rational>( + ext.getSize().significandWidth()); + break; + } + case FLOATINGPOINT_TO_FP_GENERIC: + { + cvc5::FloatingPointToFPGeneric ext = + d_node->getConst<FloatingPointToFPGeneric>(); + t = index == 0 ? d_solver->mkValHelper<cvc5::Rational>( + ext.getSize().exponentWidth()) + : d_solver->mkValHelper<cvc5::Rational>( + ext.getSize().significandWidth()); + break; + } + case REGEXP_LOOP: + { + cvc5::RegExpLoop ext = d_node->getConst<RegExpLoop>(); + t = index == 0 ? d_solver->mkValHelper<cvc5::Rational>(ext.d_loopMinOcc) + : d_solver->mkValHelper<cvc5::Rational>(ext.d_loopMaxOcc); + + break; + } + + case TUPLE_PROJECT: + { + const std::vector<uint32_t>& projectionIndices = + d_node->getConst<TupleProjectOp>().getIndices(); + t = d_solver->mkValHelper<cvc5::Rational>(projectionIndices[index]); + break; + } + default: + { + CVC5_API_CHECK(false) << "Unhandled kind " << kindToString(k); + break; + } + } //////// all checks before this line - return size; + return t; //////// CVC5_API_TRY_CATCH_END; } @@ -2043,6 +2228,25 @@ std::pair<uint32_t, uint32_t> Op::getIndices() const CVC5_API_TRY_CATCH_END; } +template <> +std::vector<api::Term> Op::getIndices() const +{ + CVC5_API_TRY_CATCH_BEGIN; + CVC5_API_CHECK_NOT_NULL; + CVC5_API_CHECK(!d_node->isNull()) + << "Expecting a non-null internal expression. This Op is not indexed."; + size_t size = getNumIndicesHelper(); + std::vector<Term> terms(getNumIndices()); + for (size_t i = 0; i < size; i++) + { + terms[i] = getIndexHelper(i); + } + //////// all checks before this line + return terms; + //////// + CVC5_API_TRY_CATCH_END; +} + std::string Op::toString() const { CVC5_API_TRY_CATCH_BEGIN; diff --git a/src/api/cpp/cvc5.h b/src/api/cpp/cvc5.h index 0ee5990da..672ccf8f8 100644 --- a/src/api/cpp/cvc5.h +++ b/src/api/cpp/cvc5.h @@ -782,6 +782,8 @@ namespace cvc5::api { /* Op */ /* -------------------------------------------------------------------------- */ +class Term; + /** * A cvc5 operator. * An operator is a term that represents certain operators, instantiated @@ -843,6 +845,14 @@ class CVC5_EXPORT Op size_t getNumIndices() const; /** + * Get the index at position i. + * @param i the position of the index to return + * @return the index at position i + */ + + Term operator[](size_t i) const; + + /** * Get the indices used to create this Op. * Supports the following template arguments: * - string @@ -892,6 +902,19 @@ class CVC5_EXPORT Op bool isIndexedHelper() const; /** + * Helper for getNumIndices + * @return the number of indices of this op + */ + size_t getNumIndicesHelper() const; + + /** + * Helper for operator[](size_t i). + * @param i position of the index. Should be less than getNumIndicesHelper(). + * @return the index at position i + */ + Term getIndexHelper(size_t index) const; + + /** * The associated solver object. */ const Solver* d_solver; diff --git a/src/expr/bound_var_manager.h b/src/expr/bound_var_manager.h index b619a36e2..507cebab3 100644 --- a/src/expr/bound_var_manager.h +++ b/src/expr/bound_var_manager.h @@ -77,7 +77,7 @@ class BoundVarManager Node mkBoundVar(Node n, const std::string& name, TypeNode tn) { Node v = mkBoundVar<T>(n, tn); - setNameAttr(n, name); + setNameAttr(v, name); return v; } //---------------------------------- utilities for computing Node hash diff --git a/src/main/driver_unified.cpp b/src/main/driver_unified.cpp index 9d221450b..8c86f03cb 100644 --- a/src/main/driver_unified.cpp +++ b/src/main/driver_unified.cpp @@ -35,6 +35,7 @@ #include "main/time_limit.h" #include "options/base_options.h" #include "options/options.h" +#include "options/options_public.h" #include "options/parser_options.h" #include "options/main_options.h" #include "options/set_language.h" @@ -87,9 +88,9 @@ void printUsage(const Options& opts, bool full) { << endl << "cvc5 options:" << endl; if(full) { - Options::printUsage(ss.str(), *opts.base.out); + options::printUsage(ss.str(), *opts.base.out); } else { - Options::printShortUsage(ss.str(), *opts.base.out); + options::printShortUsage(ss.str(), *opts.base.out); } } @@ -103,8 +104,7 @@ int runCvc5(int argc, char* argv[], Options& opts) progPath = argv[0]; // Parse the options - std::vector<string> filenames = - Options::parseOptions(&opts, argc, argv, progName); + std::vector<string> filenames = options::parse(opts, argc, argv, progName); auto limit = install_time_limit(opts); @@ -115,7 +115,7 @@ int runCvc5(int argc, char* argv[], Options& opts) } else if (opts.base.languageHelp) { - Options::printLanguageHelp(*opts.base.out); + options::printLanguageHelp(*opts.base.out); exit(1); } else if (opts.driver.version) diff --git a/src/options/CMakeLists.txt b/src/options/CMakeLists.txt index 926693185..a548717f3 100644 --- a/src/options/CMakeLists.txt +++ b/src/options/CMakeLists.txt @@ -29,7 +29,6 @@ libcvc5_add_sources( options_handler.cpp options_handler.h options_listener.h - options_public.cpp options_public.h outputc.cpp outputc.h @@ -67,7 +66,7 @@ set(options_toml_files string(REPLACE "toml" "cpp;" options_gen_cpp_files ${options_toml_files}) string(REPLACE "toml" "h;" options_gen_h_files ${options_toml_files}) -libcvc5_add_sources(GENERATED options.h options.cpp ${options_gen_cpp_files}) +libcvc5_add_sources(GENERATED options.h options.cpp options_public.cpp ${options_gen_cpp_files}) list_prepend(options_toml_files "${CMAKE_CURRENT_LIST_DIR}/" abs_toml_files) @@ -82,7 +81,7 @@ endif() add_custom_command( OUTPUT - options.h options.cpp + options.h options.cpp options_public.cpp ${options_gen_cpp_files} ${options_gen_h_files} ${options_gen_doc_files} COMMAND @@ -97,6 +96,7 @@ add_custom_command( ${options_toml_files} module_template.h module_template.cpp + options_public_template.cpp options_template.h options_template.cpp ) @@ -105,6 +105,7 @@ add_custom_target(gen-options DEPENDS options.h options.cpp + options_public.cpp ${options_gen_cpp_files} ${options_gen_h_files} ) diff --git a/src/options/arith_options.toml b/src/options/arith_options.toml index 8766672a5..26b1cfecb 100644 --- a/src/options/arith_options.toml +++ b/src/options/arith_options.toml @@ -595,3 +595,11 @@ name = "Arithmetic Theory" type = "bool" default = "false" help = "whether to use the equality solver in the theory of arithmetic" + +[[option]] + name = "arithCongMan" + category = "regular" + long = "arith-cong-man" + type = "bool" + default = "true" + help = "(experimental) whether to use the congruence manager when the equality solver is enabled" diff --git a/src/options/base_options.toml b/src/options/base_options.toml index 2223664d1..a766dce67 100644 --- a/src/options/base_options.toml +++ b/src/options/base_options.toml @@ -7,21 +7,21 @@ public = true category = "undocumented" type = "std::istream*" default = "&std::cin" - includes = ["<iosfwd>"] + includes = ["<iostream>"] [[option]] name = "out" category = "undocumented" type = "std::ostream*" default = "&std::cout" - includes = ["<iosfwd>"] + includes = ["<iostream>"] [[option]] name = "err" category = "undocumented" type = "std::ostream*" default = "&std::cerr" - includes = ["<iosfwd>"] + includes = ["<iostream>"] [[option]] name = "inputLanguage" diff --git a/src/options/fp_options.toml b/src/options/fp_options.toml index 6fd632a7c..be40b49e2 100644 --- a/src/options/fp_options.toml +++ b/src/options/fp_options.toml @@ -8,3 +8,11 @@ name = "Floating-Point" type = "bool" default = "false" help = "Allow floating-point sorts of all sizes, rather than only Float32 (8/24) or Float64 (11/53) (experimental)" + +[[option]] + name = "fpLazyWb" + category = "experimental" + long = "fp-lazy-wb" + type = "bool" + default = "false" + help = "Enable lazier word-blasting (on preNotifyFact instead of registerTerm)" diff --git a/src/options/mkoptions.py b/src/options/mkoptions.py index 45b1db4d6..aeff832b5 100644 --- a/src/options/mkoptions.py +++ b/src/options/mkoptions.py @@ -27,6 +27,7 @@ Directory <tpl-src> must contain: - options_template.cpp + - options_public_template.cpp - module_template.cpp - module_template.h @@ -183,6 +184,11 @@ def concat_format(s, objs): return '\n'.join([s.format(**o.__dict__) for o in objs]) +def get_module_headers(modules): + """Render includes for module headers""" + return concat_format('#include "{header}"', modules) + + def get_holder_fwd_decls(modules): """Render forward declaration of holder structs""" return concat_format(' struct Holder{id_cap};', modules) @@ -237,6 +243,19 @@ def get_predicates(option): return ['opts.handler().{}("{}", option, value);'.format(x, optname) for x in option.predicates] + +def get_getall(module, option): + """Render snippet to add option to result of getAll()""" + if option.type == 'bool': + return 'res.push_back({{"{}", opts.{}.{} ? "true" : "false"}});'.format( + option.long_name, module.id, option.name) + elif is_numeric_cpp_type(option.type): + return 'res.push_back({{"{}", std::to_string(opts.{}.{})}});'.format( + option.long_name, module.id, option.name) + else: + return '{{ std::stringstream ss; ss << opts.{}.{}; res.push_back({{"{}", ss.str()}}); }}'.format( + module.id, option.name, option.long_name) + class Module(object): """Options module. @@ -705,25 +724,26 @@ def add_getopt_long(long_name, argument_req, getopt_long): 'required' if argument_req else 'no', value)) -def codegen_all_modules(modules, build_dir, dst_dir, tpl_options_h, tpl_options_cpp): +def codegen_all_modules(modules, build_dir, dst_dir, tpl_options_h, + tpl_options_cpp, tpl_options_public): """ - Generate code for all option modules (options.cpp, options_holder.h). + Generate code for all option modules (options.cpp). """ headers_module = [] # generated *_options.h header includes headers_handler = set() # option includes (for handlers, predicates, ...) getopt_short = [] # short options for getopt_long getopt_long = [] # long options for getopt_long - options_smt = [] # all options names accessible via {set,get}-option + options_getall = [] # options for options::getAll() options_getoptions = [] # options for Options::getOptions() options_handler = [] # option handler calls - defaults = [] # default values - custom_handlers = [] # custom handler implementations assign/assignBool help_common = [] # help text for all common options help_others = [] # help text for all non-common options setoption_handlers = [] # handlers for set-option command getoption_handlers = [] # handlers for get-option command + assign_impls = [] + sphinxgen = SphinxGenerator() for module in modules: @@ -809,7 +829,7 @@ def codegen_all_modules(modules, build_dir, dst_dir, tpl_options_h, tpl_options_ cond = ' || '.join( ['key == "{}"'.format(x) for x in sorted(keys)]) - setoption_handlers.append('if({}) {{'.format(cond)) + setoption_handlers.append(' if ({}) {{'.format(cond)) if option.type == 'bool': setoption_handlers.append( TPL_CALL_ASSIGN_BOOL.format( @@ -824,15 +844,15 @@ def codegen_all_modules(modules, build_dir, dst_dir, tpl_options_h, tpl_options_ name=option.name, option='key')) elif option.handler: - h = 'handler->{handler}("{smtname}", key' + h = ' opts.handler().{handler}("{smtname}", key' if argument_req: h += ', optionarg' h += ');' setoption_handlers.append( h.format(handler=option.handler, smtname=option.long_name)) - setoption_handlers.append('return;') - setoption_handlers.append('}') + setoption_handlers.append(' return;') + setoption_handlers.append(' }') if option.name: getoption_handlers.append( @@ -880,80 +900,51 @@ def codegen_all_modules(modules, build_dir, dst_dir, tpl_options_h, tpl_options_ cases.append(' break;') options_handler.extend(cases) - optname = option.long - # collect options available to the SMT-frontend - if optname: - options_smt.append('"{}",'.format(optname)) - if option.name: # Build options for options::getOptions() - if optname: - # collect SMT option names - options_smt.append('"{}",'.format(optname)) - - if option.type == 'bool': - s = 'opts.push_back({{"{}", {}.{} ? "true" : "false"}});'.format( - optname, module.id, option.name) - elif is_numeric_cpp_type(option.type): - s = 'opts.push_back({{"{}", std::to_string({}.{})}});'.format( - optname, module.id, option.name) - else: - s = '{{ std::stringstream ss; ss << {}.{}; opts.push_back({{"{}", ss.str()}}); }}'.format( - module.id, option.name, optname) - options_getoptions.append(s) + if option.long_name: + options_getall.append(get_getall(module, option)) # Define handler assign/assignBool if not mode_handler: if option.type == 'bool': - custom_handlers.append(TPL_ASSIGN_BOOL.format( + assign_impls.append(TPL_ASSIGN_BOOL.format( module=module.id, name=option.name, handler=handler, predicates='\n'.join(predicates) )) elif option.short or option.long: - custom_handlers.append(TPL_ASSIGN.format( + assign_impls.append(TPL_ASSIGN.format( module=module.id, name=option.name, handler=handler, predicates='\n'.join(predicates) )) - # Default option values - default = option.default if option.default else '' - # Prepend enum name - if option.mode and option.type not in default: - default = '{}::{}'.format(option.type, default) - defaults.append('{}({})'.format(option.name, default)) - defaults.append('{}WasSetByUser(false)'.format(option.name)) - - write_file(dst_dir, 'options.h', tpl_options_h.format( - holder_fwd_decls=get_holder_fwd_decls(modules), - holder_mem_decls=get_holder_mem_decls(modules), - holder_ref_decls=get_holder_ref_decls(modules), - )) - - write_file(dst_dir, 'options.cpp', tpl_options_cpp.format( - headers_module='\n'.join(headers_module), - headers_handler='\n'.join(sorted(list(headers_handler))), - holder_mem_copy=get_holder_mem_copy(modules), - holder_mem_inits=get_holder_mem_inits(modules), - holder_ref_inits=get_holder_ref_inits(modules), - custom_handlers='\n'.join(custom_handlers), - module_defaults=',\n '.join(defaults), - help_common='\n'.join(help_common), - help_others='\n'.join(help_others), - cmdline_options='\n '.join(getopt_long), - options_short=''.join(getopt_short), - options_handler='\n '.join(options_handler), - option_value_begin=g_getopt_long_start, - option_value_end=g_getopt_long_start + len(getopt_long), - options_smt='\n '.join(options_smt), - options_getoptions='\n '.join(options_getoptions), - setoption_handlers='\n'.join(setoption_handlers), - getoption_handlers='\n'.join(getoption_handlers) - )) + data = { + 'holder_fwd_decls': get_holder_fwd_decls(modules), + 'holder_mem_decls': get_holder_mem_decls(modules), + 'holder_ref_decls': get_holder_ref_decls(modules), + 'headers_module': get_module_headers(modules), + 'headers_handler': '\n'.join(sorted(list(headers_handler))), + 'holder_mem_inits': get_holder_mem_inits(modules), + 'holder_ref_inits': get_holder_ref_inits(modules), + 'holder_mem_copy': get_holder_mem_copy(modules), + 'cmdline_options': '\n '.join(getopt_long), + 'help_common': '\n'.join(help_common), + 'help_others': '\n'.join(help_others), + 'options_handler': '\n '.join(options_handler), + 'options_short': ''.join(getopt_short), + 'assigns': '\n'.join(assign_impls), + 'options_getall': '\n '.join(options_getall), + 'getoption_handlers': '\n'.join(getoption_handlers), + 'setoption_handlers': '\n'.join(setoption_handlers), + } + write_file(dst_dir, 'options.h', tpl_options_h.format(**data)) + write_file(dst_dir, 'options.cpp', tpl_options_cpp.format(**data)) + write_file(dst_dir, 'options_public.cpp', tpl_options_public.format(**data)) if os.path.isdir('{}/docs/'.format(build_dir)): sphinxgen.render('{}/docs/'.format(build_dir), 'options_generated.rst') @@ -1093,6 +1084,7 @@ def mkoptions_main(): # Read source code template files from source directory. tpl_module_h = read_tpl(src_dir, 'module_template.h') tpl_module_cpp = read_tpl(src_dir, 'module_template.cpp') + tpl_options_public = read_tpl(src_dir, 'options_public_template.cpp') tpl_options_h = read_tpl(src_dir, 'options_template.h') tpl_options_cpp = read_tpl(src_dir, 'options_template.cpp') @@ -1113,8 +1105,7 @@ def mkoptions_main(): codegen_module(module, dst_dir, tpl_module_h, tpl_module_cpp) # Create options.cpp in destination directory - codegen_all_modules(modules, build_dir, dst_dir, tpl_options_h, tpl_options_cpp) - + codegen_all_modules(modules, build_dir, dst_dir, tpl_options_h, tpl_options_cpp, tpl_options_public) if __name__ == "__main__": diff --git a/src/options/options_handler.cpp b/src/options/options_handler.cpp index e423dc149..7a80c4d7a 100644 --- a/src/options/options_handler.cpp +++ b/src/options/options_handler.cpp @@ -155,11 +155,11 @@ void OptionsHandler::checkBvSatSolver(const std::string& option, throw OptionException(ss.str()); } - if (options::bvSolver() != options::BVSolver::BITBLAST + if (d_options->bv.bvSolver != options::BVSolver::BITBLAST && (m == SatSolverMode::CRYPTOMINISAT || m == SatSolverMode::CADICAL || m == SatSolverMode::KISSAT)) { - if (options::bitblastMode() == options::BitblastMode::LAZY + if (d_options->bv.bitblastMode == options::BitblastMode::LAZY && d_options->bv.bitblastModeWasSetByUser) { throwLazyBBUnsupported(m); @@ -178,9 +178,9 @@ void OptionsHandler::checkBitblastMode(const std::string& option, options::bv::setDefaultBitvectorEqualitySolver(*d_options, true); options::bv::setDefaultBitvectorInequalitySolver(*d_options, true); options::bv::setDefaultBitvectorAlgebraicSolver(*d_options, true); - if (options::bvSatSolver() != options::SatSolverMode::MINISAT) + if (d_options->bv.bvSatSolver != options::SatSolverMode::MINISAT) { - throwLazyBBUnsupported(options::bvSatSolver()); + throwLazyBBUnsupported(d_options->bv.bvSatSolver); } } else if (m == BitblastMode::EAGER) @@ -195,7 +195,7 @@ void OptionsHandler::setBitblastAig(const std::string& option, { if(arg) { if (d_options->bv.bitblastModeWasSetByUser) { - if (options::bitblastMode() != options::BitblastMode::EAGER) + if (d_options->bv.bitblastMode != options::BitblastMode::EAGER) { throw OptionException("bitblast-aig must be used with eager bitblaster"); } diff --git a/src/options/options_public.cpp b/src/options/options_public.cpp deleted file mode 100644 index 552058312..000000000 --- a/src/options/options_public.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/****************************************************************************** - * Top contributors (to current version): - * Tim King, Gereon Kremer, Andrew Reynolds - * - * This file is part of the cvc5 project. - * - * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS - * in the top-level source directory and their institutional affiliations. - * All rights reserved. See the file COPYING in the top-level source - * directory for licensing information. - * **************************************************************************** - * - * Definitions of public facing interface functions for Options. - * - * These are all one line wrappers for accessing the internal option data. - */ - -#include "options_public.h" - -#include "options/uf_options.h" - -namespace cvc5::options { - -bool getUfHo(const Options& opts) { return opts.uf.ufHo; } - -} // namespace cvc5::options diff --git a/src/options/options_public.h b/src/options/options_public.h index 60929c96c..03d7ec41a 100644 --- a/src/options/options_public.h +++ b/src/options/options_public.h @@ -10,12 +10,7 @@ * directory for licensing information. * **************************************************************************** * - * Public facing functions for options that need to be accessed from the - * outside. - * - * These are all one line wrappers for accessing the internal option data so - * that external code (including parser/ and main/) does not need to include - * the option modules (*_options.h). + * Global (command-line, set-option, ...) parameters for SMT. */ #include "cvc5_public.h" @@ -23,12 +18,77 @@ #ifndef CVC5__OPTIONS__OPTIONS_PUBLIC_H #define CVC5__OPTIONS__OPTIONS_PUBLIC_H +#include <iosfwd> +#include <string> +#include <vector> + +#include "cvc5_export.h" #include "options/options.h" namespace cvc5::options { bool getUfHo(const Options& opts) CVC5_EXPORT; +/** + * Get a description of the command-line flags accepted by + * parse. The returned string will be escaped so that it is + * suitable as an argument to printf. */ +const std::string& getDescription() CVC5_EXPORT; + +/** + * Print overall command-line option usage message, prefixed by + * "msg"---which could be an error message causing the usage + * output in the first place, e.g. "no such option --foo" + */ +void printUsage(const std::string& msg, std::ostream& os) CVC5_EXPORT; + +/** + * Print command-line option usage message for only the most-commonly + * used options. The message is prefixed by "msg"---which could be + * an error message causing the usage output in the first place, e.g. + * "no such option --foo" + */ +void printShortUsage(const std::string& msg, std::ostream& os) CVC5_EXPORT; + +/** Print help for the --lang command line option */ +void printLanguageHelp(std::ostream& os) CVC5_EXPORT; + +/** + * Initialize the Options object options based on the given + * command-line arguments given in argc and argv. The return value + * is what's left of the command line (that is, the non-option + * arguments). + * + * This function uses getopt_long() and is not thread safe. + * + * Throws OptionException on failures. + * + * Preconditions: options and argv must be non-null. + */ +std::vector<std::string> parse(Options& opts, + int argc, + char* argv[], + std::string& binaryName) CVC5_EXPORT; + +/** + * Retrieve an option value by name (as given in key) from the Options object + * opts as a string. + */ +std::string get(const Options& opts, const std::string& key) CVC5_EXPORT; + +/** + * Update the Options object opts, set the value of the option specified by key + * to the value parsed from optionarg. + */ +void set(Options& opts, + const std::string& key, + const std::string& optionarg) CVC5_EXPORT; + +/** + * Get the setting for all options. + */ +std::vector<std::vector<std::string> > getAll(const Options& opts) CVC5_EXPORT; + } // namespace cvc5::options #endif diff --git a/src/options/options_public_template.cpp b/src/options/options_public_template.cpp new file mode 100644 index 000000000..70c6f420a --- /dev/null +++ b/src/options/options_public_template.cpp @@ -0,0 +1,496 @@ +/****************************************************************************** + * Top contributors (to current version): + * Tim King, Gereon Kremer, Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Global (command-line, set-option, ...) parameters for SMT. + */ + +#include "options/options_public.h" + +#if !defined(_BSD_SOURCE) && defined(__MINGW32__) && !defined(__MINGW64__) +// force use of optreset; mingw32 croaks on argv-switching otherwise +#include "base/cvc5config.h" +#define _BSD_SOURCE +#undef HAVE_DECL_OPTRESET +#define HAVE_DECL_OPTRESET 1 +#define CVC5_IS_NOT_REALLY_BSD +#endif /* !_BSD_SOURCE && __MINGW32__ && !__MINGW64__ */ + +#ifdef __MINGW64__ +extern int optreset; +#endif /* __MINGW64__ */ + +#include <getopt.h> + +// clean up +#ifdef CVC5_IS_NOT_REALLY_BSD +# undef _BSD_SOURCE +#endif /* CVC5_IS_NOT_REALLY_BSD */ + +#include "base/check.h" +#include "base/output.h" +#include "options/didyoumean.h" +#include "options/options_handler.h" +#include "options/options_listener.h" +#include "options/options.h" +#include "options/uf_options.h" +${headers_module}$ +${headers_handler}$ + +#include <cstring> +#include <iostream> +#include <limits> + +namespace cvc5::options { + +bool getUfHo(const Options& opts) { return opts.uf.ufHo; } + +// clang-format off +static const std::string mostCommonOptionsDescription = + "\ +Most commonly-used cvc5 options:\n" +${help_common}$ + ; + +static const std::string optionsDescription = + mostCommonOptionsDescription + "\n\nAdditional cvc5 options:\n" +${help_others}$; + +static const std::string optionsFootnote = "\n\ +[*] Each of these options has a --no-OPTIONNAME variant, which reverses the\n\ + sense of the option.\n\ +"; + +static const std::string languageDescription = + "\ +Languages currently supported as arguments to the -L / --lang option:\n\ + auto attempt to automatically determine language\n\ + cvc | presentation | pl CVC presentation language\n\ + smt | smtlib | smt2 |\n\ + smt2.6 | smtlib2.6 SMT-LIB format 2.6 with support for the strings standard\n\ + tptp TPTP format (cnf, fof and tff)\n\ + sygus | sygus2 SyGuS version 2.0\n\ +\n\ +Languages currently supported as arguments to the --output-lang option:\n\ + auto match output language to input language\n\ + cvc | presentation | pl CVC presentation language\n\ + smt | smtlib | smt2 |\n\ + smt2.6 | smtlib2.6 SMT-LIB format 2.6 with support for the strings standard\n\ + tptp TPTP format\n\ + ast internal format (simple syntax trees)\n\ +"; +// clang-format on + +const std::string& getDescription() +{ + return optionsDescription; +} + +void printUsage(const std::string& msg, std::ostream& os) { + os << msg << optionsDescription << std::endl + << optionsFootnote << std::endl << std::flush; +} + +void printShortUsage(const std::string& msg, std::ostream& os) { + os << msg << mostCommonOptionsDescription << std::endl + << optionsFootnote << std::endl + << "For full usage, please use --help." + << std::endl << std::endl << std::flush; +} + +void printLanguageHelp(std::ostream& os) { + os << languageDescription << std::flush; +} + +/** Set a given Options* as "current" just for a particular scope. */ +class OptionsGuard { + Options** d_field; + Options* d_old; +public: + OptionsGuard(Options** field, Options* opts) : + d_field(field), + d_old(*field) { + *field = opts; + } + ~OptionsGuard() { + *d_field = d_old; + } +};/* class OptionsGuard */ + +/** + * This is a table of long options. By policy, each short option + * should have an equivalent long option (but the reverse isn't the + * case), so this table should thus contain all command-line options. + * + * Each option in this array has four elements: + * + * 1. the long option string + * 2. argument behavior for the option: + * no_argument - no argument permitted + * required_argument - an argument is expected + * optional_argument - an argument is permitted but not required + * 3. this is a pointer to an int which is set to the 4th entry of the + * array if the option is present; or NULL, in which case + * getopt_long() returns the 4th entry + * 4. the return value for getopt_long() when this long option (or the + * value to set the 3rd entry to; see #3) + * + * If you add something here, you should add it in src/main/usage.h + * also, to document it. + * + * If you add something that has a short option equivalent, you should + * add it to the getopt_long() call in parse(). + */ +// clang-format off +static struct option cmdlineOptions[] = { + ${cmdline_options}$ + {nullptr, no_argument, nullptr, '\0'}}; +// clang-format on + +std::string suggestCommandLineOptions(const std::string& optionName) +{ + DidYouMean didYouMean; + + const char* opt; + for(size_t i = 0; (opt = cmdlineOptions[i].name) != nullptr; ++i) { + didYouMean.addWord(std::string("--") + cmdlineOptions[i].name); + } + + return didYouMean.getMatchAsString(optionName.substr(0, optionName.find('='))); +} + +/** + * This is a default handler for options of built-in C++ type. This + * template is really just a helper for the handleOption() template, + * below. Variants of this template handle numeric and non-numeric, + * integral and non-integral, signed and unsigned C++ types. + * handleOption() makes sure to instantiate the right one. + * + * This implements default behavior when e.g. an option is + * unsigned but the user specifies a negative argument; etc. + */ +template <class T, bool is_numeric, bool is_integer> +struct OptionHandler { + static T handle(const std::string& option, const std::string& flag, const std::string& optionarg); +};/* struct OptionHandler<> */ + +/** Variant for integral C++ types */ +template <class T> +struct OptionHandler<T, true, true> { + static bool stringToInt(T& t, const std::string& str) { + std::istringstream ss(str); + ss >> t; + char tmp; + return !(ss.fail() || ss.get(tmp)); + } + + static bool containsMinus(const std::string& str) { + return str.find('-') != std::string::npos; + } + + static T handle(const std::string& option, const std::string& flag, const std::string& optionarg) { + try { + T i; + bool success = stringToInt(i, optionarg); + + if(!success){ + throw OptionException(flag + ": failed to parse "+ optionarg + + " as an integer of the appropriate type."); + } + + // Depending in the platform unsigned numbers with '-' signs may parse. + // Reject these by looking for any minus if it is not signed. + if( (! std::numeric_limits<T>::is_signed) && containsMinus(optionarg) ) { + // unsigned type but user gave negative argument + throw OptionException(flag + " requires a nonnegative argument"); + } else if(i < std::numeric_limits<T>::min()) { + // negative overflow for type + std::stringstream ss; + ss << flag << " requires an argument >= " + << std::numeric_limits<T>::min(); + throw OptionException(ss.str()); + } else if(i > std::numeric_limits<T>::max()) { + // positive overflow for type + std::stringstream ss; + ss << flag << " requires an argument <= " + << std::numeric_limits<T>::max(); + throw OptionException(ss.str()); + } + + return i; + + // if(std::numeric_limits<T>::is_signed) { + // return T(i.getLong()); + // } else { + // return T(i.getUnsignedLong()); + // } + } catch(std::invalid_argument&) { + // user gave something other than an integer + throw OptionException(flag + " requires an integer argument"); + } + } +};/* struct OptionHandler<T, true, true> */ + +/** Variant for numeric but non-integral C++ types */ +template <class T> +struct OptionHandler<T, true, false> { + static T handle(const std::string& option, const std::string& flag, const std::string& optionarg) { + std::stringstream inss(optionarg); + long double r; + inss >> r; + if(! inss.eof()) { + // we didn't consume the whole string (junk at end) + throw OptionException(flag + " requires a numeric argument"); + } + + if(! std::numeric_limits<T>::is_signed && r < 0.0) { + // unsigned type but user gave negative value + throw OptionException(flag + " requires a nonnegative argument"); + } else if(r < -std::numeric_limits<T>::max()) { + // negative overflow for type + std::stringstream ss; + ss << flag << " requires an argument >= " + << -std::numeric_limits<T>::max(); + throw OptionException(ss.str()); + } else if(r > std::numeric_limits<T>::max()) { + // positive overflow for type + std::stringstream ss; + ss << flag << " requires an argument <= " + << std::numeric_limits<T>::max(); + throw OptionException(ss.str()); + } + + return T(r); + } +};/* struct OptionHandler<T, true, false> */ + +/** Variant for non-numeric C++ types */ +template <class T> +struct OptionHandler<T, false, false> { + static T handle(const std::string& option, const std::string& flag, const std::string& optionarg) { + T::unsupported_handleOption_call___please_write_me; + // The above line causes a compiler error if this version of the template + // is ever instantiated (meaning that a specialization is missing). So + // don't worry about the segfault in the next line, the "return" is only + // there to keep the compiler from giving additional, distracting errors + // and warnings. + return *(T*)0; + } +};/* struct OptionHandler<T, false, false> */ + +/** Handle an option of type T in the default way. */ +template <class T> +T handleOption(const std::string& option, const std::string& flag, const std::string& optionarg) { + return OptionHandler<T, std::numeric_limits<T>::is_specialized, std::numeric_limits<T>::is_integer>::handle(option, flag, optionarg); +} + +/** Handle an option of type std::string in the default way. */ +template <> +std::string handleOption<std::string>(const std::string& option, const std::string& flag, const std::string& optionarg) { + return optionarg; +} + +// clang-format off +${assigns}$ +// clang-format off + +void parseInternal(Options& opts, int argc, + char* argv[], + std::vector<std::string>& nonoptions) +{ + Assert(argv != nullptr); + if(Debug.isOn("options")) { + Debug("options") << "starting a new parseInternal with " + << argc << " arguments" << std::endl; + for( int i = 0; i < argc ; i++ ){ + Assert(argv[i] != NULL); + Debug("options") << " argv[" << i << "] = " << argv[i] << std::endl; + } + } + + // Reset getopt(), in the case of multiple calls to parse(). + // This can be = 1 in newer GNU getopt, but older (< 2007) require = 0. + optind = 0; +#if HAVE_DECL_OPTRESET + optreset = 1; // on BSD getopt() (e.g. Mac OS), might need this +#endif /* HAVE_DECL_OPTRESET */ + + // We must parse the binary name, which is manually ignored below. Setting + // this to 1 leads to incorrect behavior on some platforms. + int main_optind = 0; + int old_optind; + + + while(true) { // Repeat Forever + + optopt = 0; + std::string option, optionarg; + + optind = main_optind; + old_optind = main_optind; + + // If we encounter an element that is not at zero and does not start + // with a "-", this is a non-option. We consume this element as a + // non-option. + if (main_optind > 0 && main_optind < argc && + argv[main_optind][0] != '-') { + Debug("options") << "enqueueing " << argv[main_optind] + << " as a non-option." << std::endl; + nonoptions.push_back(argv[main_optind]); + ++main_optind; + continue; + } + + + Debug("options") << "[ before, main_optind == " << main_optind << " ]" + << std::endl; + Debug("options") << "[ before, optind == " << optind << " ]" << std::endl; + Debug("options") << "[ argc == " << argc << ", argv == " << argv << " ]" + << std::endl; + // clang-format off + int c = getopt_long(argc, argv, + "+:${options_short}$", + cmdlineOptions, NULL); + // clang-format on + + main_optind = optind; + + Debug("options") << "[ got " << int(c) << " (" << char(c) << ") ]" + << "[ next option will be at pos: " << optind << " ]" + << std::endl; + + // The initial getopt_long call should always determine that argv[0] + // is not an option and returns -1. We always manually advance beyond + // this element. + if ( old_optind == 0 && c == -1 ) { + Assert(main_optind > 0); + continue; + } + + if ( c == -1 ) { + if(Debug.isOn("options")) { + Debug("options") << "done with option parsing" << std::endl; + for(int index = optind; index < argc; ++index) { + Debug("options") << "remaining " << argv[index] << std::endl; + } + } + break; + } + + option = argv[old_optind == 0 ? 1 : old_optind]; + optionarg = (optarg == NULL) ? "" : optarg; + + Debug("preemptGetopt") << "processing option " << c + << " (`" << char(c) << "'), " << option << std::endl; + + // clang-format off + switch(c) + { +${options_handler}$ + + case ':' : + // This can be a long or short option, and the way to get at the + // name of it is different. + throw OptionException(std::string("option `") + option + + "' missing its required argument"); + + case '?': + default: + throw OptionException(std::string("can't understand option `") + option + + "'" + suggestCommandLineOptions(option)); + } + } + // clang-format on + + Debug("options") << "got " << nonoptions.size() + << " non-option arguments." << std::endl; +} + +/** + * Parse argc/argv and put the result into a cvc5::Options. + * The return value is what's left of the command line (that is, the + * non-option arguments). + * + * Throws OptionException on failures. + */ +std::vector<std::string> parse( + Options & opts, int argc, char* argv[], std::string& binaryName) +{ + Assert(argv != nullptr); + + Options* cur = &Options::current(); + OptionsGuard guard(&cur, &opts); + + const char *progName = argv[0]; + + // To debug options parsing, you may prefer to simply uncomment this + // and recompile. Debug flags have not been parsed yet so these have + // not been set. + //DebugChannel.on("options"); + + Debug("options") << "options::parse == " << &opts << std::endl; + Debug("options") << "argv == " << argv << std::endl; + + // Find the base name of the program. + const char *x = strrchr(progName, '/'); + if(x != nullptr) { + progName = x + 1; + } + binaryName = std::string(progName); + + std::vector<std::string> nonoptions; + parseInternal(opts, argc, argv, nonoptions); + if (Debug.isOn("options")){ + for(std::vector<std::string>::const_iterator i = nonoptions.begin(), + iend = nonoptions.end(); i != iend; ++i){ + Debug("options") << "nonoptions " << *i << std::endl; + } + } + + return nonoptions; +} + +std::string get(const Options& options, const std::string& key) +{ + Trace("options") << "Options::getOption(" << key << ")" << std::endl; + ${getoption_handlers}$ + + throw UnrecognizedOptionException(key); +} + +void setInternal(Options& opts, const std::string& key, + const std::string& optionarg) + { + ${setoption_handlers}$ + throw UnrecognizedOptionException(key); +} + +void set(Options& opts, const std::string& key, const std::string& optionarg) +{ + + Trace("options") << "setOption(" << key << ", " << optionarg << ")" + << std::endl; + // first update this object + setInternal(opts, key, optionarg); + // then, notify the provided listener + opts.notifyListener(key); +} + +std::vector<std::vector<std::string> > getAll(const Options& opts) +{ + std::vector<std::vector<std::string>> res; + +${options_getall}$ + + return res; +} + +} // namespace cvc5::options diff --git a/src/options/options_template.cpp b/src/options/options_template.cpp index 2c22065c2..db58c3a4a 100644 --- a/src/options/options_template.cpp +++ b/src/options/options_template.cpp @@ -13,43 +13,11 @@ * Contains code for handling command-line options. */ -#if !defined(_BSD_SOURCE) && defined(__MINGW32__) && !defined(__MINGW64__) -// force use of optreset; mingw32 croaks on argv-switching otherwise -#include "base/cvc5config.h" -#define _BSD_SOURCE -#undef HAVE_DECL_OPTRESET -#define HAVE_DECL_OPTRESET 1 -#define CVC5_IS_NOT_REALLY_BSD -#endif /* !_BSD_SOURCE && __MINGW32__ && !__MINGW64__ */ - -#ifdef __MINGW64__ -extern int optreset; -#endif /* __MINGW64__ */ - -#include <getopt.h> - -// clean up -#ifdef CVC5_IS_NOT_REALLY_BSD -# undef _BSD_SOURCE -#endif /* CVC5_IS_NOT_REALLY_BSD */ - -#include <unistd.h> -#include <string.h> -#include <time.h> - -#include <cstdio> -#include <cstdlib> -#include <cstring> -#include <iomanip> -#include <new> -#include <string> -#include <sstream> -#include <limits> +#include "options/options.h" #include "base/check.h" #include "base/exception.h" #include "base/output.h" -#include "options/didyoumean.h" #include "options/language.h" #include "options/options_handler.h" #include "options/options_listener.h" @@ -65,175 +33,22 @@ using namespace cvc5; using namespace cvc5::options; // clang-format on -namespace cvc5 { - -thread_local Options* Options::s_current = NULL; - -/** - * This is a default handler for options of built-in C++ type. This - * template is really just a helper for the handleOption() template, - * below. Variants of this template handle numeric and non-numeric, - * integral and non-integral, signed and unsigned C++ types. - * handleOption() makes sure to instantiate the right one. - * - * This implements default behavior when e.g. an option is - * unsigned but the user specifies a negative argument; etc. - */ -template <class T, bool is_numeric, bool is_integer> -struct OptionHandler { - static T handle(const std::string& option, - const std::string& flag, - const std::string& optionarg); -};/* struct OptionHandler<> */ - -/** Variant for integral C++ types */ -template <class T> -struct OptionHandler<T, true, true> { - static bool stringToInt(T& t, const std::string& str) { - std::istringstream ss(str); - ss >> t; - char tmp; - return !(ss.fail() || ss.get(tmp)); - } - - static bool containsMinus(const std::string& str) { - return str.find('-') != std::string::npos; - } - - static T handle(const std::string& option, - const std::string& flag, - const std::string& optionarg) - { - try { - T i; - bool success = stringToInt(i, optionarg); - - if(!success){ - throw OptionException(flag + ": failed to parse " + optionarg - + " as an integer of the appropriate type."); - } - - // Depending in the platform unsigned numbers with '-' signs may parse. - // Reject these by looking for any minus if it is not signed. - if( (! std::numeric_limits<T>::is_signed) && containsMinus(optionarg) ) { - // unsigned type but user gave negative argument - throw OptionException(flag + " requires a nonnegative argument"); - } else if(i < std::numeric_limits<T>::min()) { - // negative overflow for type - std::stringstream ss; - ss << flag - << " requires an argument >= " << std::numeric_limits<T>::min(); - throw OptionException(ss.str()); - } else if(i > std::numeric_limits<T>::max()) { - // positive overflow for type - std::stringstream ss; - ss << flag - << " requires an argument <= " << std::numeric_limits<T>::max(); - throw OptionException(ss.str()); - } - - return i; - - // if(std::numeric_limits<T>::is_signed) { - // return T(i.getLong()); - // } else { - // return T(i.getUnsignedLong()); - // } - } catch(std::invalid_argument&) { - // user gave something other than an integer - throw OptionException(flag + " requires an integer argument"); - } - } -};/* struct OptionHandler<T, true, true> */ - -/** Variant for numeric but non-integral C++ types */ -template <class T> -struct OptionHandler<T, true, false> { - static T handle(const std::string& option, - const std::string& flag, - const std::string& optionarg) - { - std::stringstream inss(optionarg); - long double r; - inss >> r; - if (!inss.eof()) - { - // we didn't consume the whole string (junk at end) - throw OptionException(flag + " requires a numeric argument"); - } - - if(! std::numeric_limits<T>::is_signed && r < 0.0) { - // unsigned type but user gave negative value - throw OptionException(flag + " requires a nonnegative argument"); - } else if(r < -std::numeric_limits<T>::max()) { - // negative overflow for type - std::stringstream ss; - ss << flag - << " requires an argument >= " << -std::numeric_limits<T>::max(); - throw OptionException(ss.str()); - } else if(r > std::numeric_limits<T>::max()) { - // positive overflow for type - std::stringstream ss; - ss << flag - << " requires an argument <= " << std::numeric_limits<T>::max(); - throw OptionException(ss.str()); - } - - return T(r); - } -};/* struct OptionHandler<T, true, false> */ - -/** Variant for non-numeric C++ types */ -template <class T> -struct OptionHandler<T, false, false> { - static T handle(const std::string& option, - const std::string& flag, - const std::string& optionarg) - { - T::unsupported_handleOption_call___please_write_me; - // The above line causes a compiler error if this version of the template - // is ever instantiated (meaning that a specialization is missing). So - // don't worry about the segfault in the next line, the "return" is only - // there to keep the compiler from giving additional, distracting errors - // and warnings. - return *(T*)0; - } -};/* struct OptionHandler<T, false, false> */ - -/** Handle an option of type T in the default way. */ -template <class T> -T handleOption(const std::string& option, - const std::string& flag, - const std::string& optionarg) -{ - return OptionHandler<T, - std::numeric_limits<T>::is_specialized, - std::numeric_limits<T>::is_integer>::handle(option, - flag, - optionarg); -} - -/** Handle an option of type std::string in the default way. */ -template <> -std::string handleOption<std::string>(const std::string& option, - const std::string& flag, - const std::string& optionarg) +namespace cvc5 { - return optionarg; -} + thread_local Options* Options::s_current = nullptr; -Options::Options(OptionsListener* ol) - : d_handler(new options::OptionsHandler(this)), + Options::Options(OptionsListener * ol) + : + d_olisten(ol), // clang-format off ${holder_mem_inits}$ ${holder_ref_inits}$ // clang-format on - d_olisten(ol) -{} + d_handler(std::make_unique<options::OptionsHandler>(this)) + { + } -Options::~Options() { - delete d_handler; -} + Options::~Options() {} void Options::copyValues(const Options& options){ if(this != &options) { @@ -244,333 +59,14 @@ ${holder_mem_copy}$ } void Options::setListener(OptionsListener* ol) { d_olisten = ol; } - -// clang-format off -${custom_handlers}$ -// clang-format on - -static const std::string mostCommonOptionsDescription = - "\ -Most commonly-used cvc5 options:\n" - // clang-format off -${help_common}$ - // clang-format on - ; - -// clang-format off -static const std::string optionsDescription = - mostCommonOptionsDescription + "\n\nAdditional cvc5 options:\n" -${help_others}$; -// clang-format on - -static const std::string optionsFootnote = "\n\ -[*] Each of these options has a --no-OPTIONNAME variant, which reverses the\n\ - sense of the option.\n\ -"; - -static const std::string languageDescription = - "\ -Languages currently supported as arguments to the -L / --lang option:\n\ - auto attempt to automatically determine language\n\ - cvc | presentation | pl CVC presentation language\n\ - smt | smtlib | smt2 |\n\ - smt2.6 | smtlib2.6 SMT-LIB format 2.6 with support for the strings standard\n\ - tptp TPTP format (cnf, fof and tff)\n\ - sygus | sygus2 SyGuS version 2.0\n\ -\n\ -Languages currently supported as arguments to the --output-lang option:\n\ - auto match output language to input language\n\ - cvc | presentation | pl CVC presentation language\n\ - smt | smtlib | smt2 |\n\ - smt2.6 | smtlib2.6 SMT-LIB format 2.6 with support for the strings standard\n\ - tptp TPTP format\n\ - ast internal format (simple syntax trees)\n\ -"; - -std::string Options::getDescription() const { - return optionsDescription; -} - -void Options::printUsage(const std::string msg, std::ostream& out) { - out << msg << optionsDescription << std::endl - << optionsFootnote << std::endl << std::flush; -} - -void Options::printShortUsage(const std::string msg, std::ostream& out) { - out << msg << mostCommonOptionsDescription << std::endl - << optionsFootnote << std::endl - << "For full usage, please use --help." - << std::endl << std::endl << std::flush; -} - -void Options::printLanguageHelp(std::ostream& out) { - out << languageDescription << std::flush; -} - -/** - * This is a table of long options. By policy, each short option - * should have an equivalent long option (but the reverse isn't the - * case), so this table should thus contain all command-line options. - * - * Each option in this array has four elements: - * - * 1. the long option string - * 2. argument behavior for the option: - * no_argument - no argument permitted - * required_argument - an argument is expected - * optional_argument - an argument is permitted but not required - * 3. this is a pointer to an int which is set to the 4th entry of the - * array if the option is present; or NULL, in which case - * getopt_long() returns the 4th entry - * 4. the return value for getopt_long() when this long option (or the - * value to set the 3rd entry to; see #3) - * - * If you add something here, you should add it in src/main/usage.h - * also, to document it. - * - * If you add something that has a short option equivalent, you should - * add it to the getopt_long() call in parseOptions(). - */ -// clang-format off -static struct option cmdlineOptions[] = { - ${cmdline_options}$ - {nullptr, no_argument, nullptr, '\0'}}; -// clang-format on - -namespace options { - -/** Set a given Options* as "current" just for a particular scope. */ -class OptionsGuard { - Options** d_field; - Options* d_old; -public: - OptionsGuard(Options** field, Options* opts) : - d_field(field), - d_old(*field) { - *field = opts; - } - ~OptionsGuard() { - *d_field = d_old; - } -};/* class OptionsGuard */ - -} // namespace options - -/** - * Parse argc/argv and put the result into a cvc5::Options. - * The return value is what's left of the command line (that is, the - * non-option arguments). - * - * Throws OptionException on failures. - */ -std::vector<std::string> Options::parseOptions(Options* options, - int argc, - char* argv[], - std::string& binaryName) -{ - Assert(options != NULL); - Assert(argv != NULL); - - options::OptionsGuard guard(&s_current, options); - - const char *progName = argv[0]; - - // To debug options parsing, you may prefer to simply uncomment this - // and recompile. Debug flags have not been parsed yet so these have - // not been set. - //DebugChannel.on("options"); - - Debug("options") << "Options::parseOptions == " << options << std::endl; - Debug("options") << "argv == " << argv << std::endl; - - // Find the base name of the program. - const char *x = strrchr(progName, '/'); - if(x != NULL) { - progName = x + 1; - } - binaryName = std::string(progName); - - std::vector<std::string> nonoptions; - options->parseOptionsRecursive(argc, argv, &nonoptions); - if(Debug.isOn("options")){ - for(std::vector<std::string>::const_iterator i = nonoptions.begin(), - iend = nonoptions.end(); i != iend; ++i){ - Debug("options") << "nonoptions " << *i << std::endl; - } - } - - return nonoptions; -} - -std::string suggestCommandLineOptions(const std::string& optionName) -{ - DidYouMean didYouMean; - - const char* opt; - for(size_t i = 0; (opt = cmdlineOptions[i].name) != nullptr; ++i) { - didYouMean.addWord(std::string("--") + cmdlineOptions[i].name); - } - - return didYouMean.getMatchAsString(optionName.substr(0, optionName.find('='))); -} - -void Options::parseOptionsRecursive(int argc, - char* argv[], - std::vector<std::string>* nonoptions) -{ - Options& opts = *this; - if(Debug.isOn("options")) { - Debug("options") << "starting a new parseOptionsRecursive with " - << argc << " arguments" << std::endl; - for( int i = 0; i < argc ; i++ ){ - Assert(argv[i] != NULL); - Debug("options") << " argv[" << i << "] = " << argv[i] << std::endl; - } - } - - // Reset getopt(), in the case of multiple calls to parseOptions(). - // This can be = 1 in newer GNU getopt, but older (< 2007) require = 0. - optind = 0; -#if HAVE_DECL_OPTRESET - optreset = 1; // on BSD getopt() (e.g. Mac OS), might need this -#endif /* HAVE_DECL_OPTRESET */ - - // We must parse the binary name, which is manually ignored below. Setting - // this to 1 leads to incorrect behavior on some platforms. - int main_optind = 0; - int old_optind; - - - while(true) { // Repeat Forever - - optopt = 0; - std::string option, optionarg; - - optind = main_optind; - old_optind = main_optind; - - // If we encounter an element that is not at zero and does not start - // with a "-", this is a non-option. We consume this element as a - // non-option. - if (main_optind > 0 && main_optind < argc && - argv[main_optind][0] != '-') { - Debug("options") << "enqueueing " << argv[main_optind] - << " as a non-option." << std::endl; - nonoptions->push_back(argv[main_optind]); - ++main_optind; - continue; - } - - - Debug("options") << "[ before, main_optind == " << main_optind << " ]" - << std::endl; - Debug("options") << "[ before, optind == " << optind << " ]" << std::endl; - Debug("options") << "[ argc == " << argc << ", argv == " << argv << " ]" - << std::endl; - // clang-format off - int c = getopt_long(argc, argv, - "+:${options_short}$", - cmdlineOptions, NULL); - // clang-format on - - main_optind = optind; - - Debug("options") << "[ got " << int(c) << " (" << char(c) << ") ]" - << "[ next option will be at pos: " << optind << " ]" - << std::endl; - - // The initial getopt_long call should always determine that argv[0] - // is not an option and returns -1. We always manually advance beyond - // this element. - if ( old_optind == 0 && c == -1 ) { - Assert(main_optind > 0); - continue; - } - - if ( c == -1 ) { - if(Debug.isOn("options")) { - Debug("options") << "done with option parsing" << std::endl; - for(int index = optind; index < argc; ++index) { - Debug("options") << "remaining " << argv[index] << std::endl; - } - } - break; - } - - option = argv[old_optind == 0 ? 1 : old_optind]; - optionarg = (optarg == NULL) ? "" : optarg; - - Debug("preemptGetopt") << "processing option " << c - << " (`" << char(c) << "'), " << option << std::endl; - - // clang-format off - switch(c) + +void Options::notifyListener(const std::string& key) + { + if (d_olisten != nullptr) { -${options_handler}$ - - case ':' : - // This can be a long or short option, and the way to get at the - // name of it is different. - throw OptionException(std::string("option `") + option - + "' missing its required argument"); - - case '?': - default: - throw OptionException(std::string("can't understand option `") + option - + "'" + suggestCommandLineOptions(option)); + d_olisten->notifySetOption(key); } } - // clang-format on - - Debug("options") << "got " << nonoptions->size() - << " non-option arguments." << std::endl; -} - -// clang-format off -std::vector<std::vector<std::string> > Options::getOptions() const -{ - std::vector< std::vector<std::string> > opts; - - ${options_getoptions}$ - - return opts; -} -// clang-format on - -void Options::setOption(const std::string& key, const std::string& optionarg) -{ - Trace("options") << "setOption(" << key << ", " << optionarg << ")" - << std::endl; - // first update this object - setOptionInternal(key, optionarg); - // then, notify the provided listener - if (d_olisten != nullptr) - { - d_olisten->notifySetOption(key); - } -} - -// clang-format off -void Options::setOptionInternal(const std::string& key, - const std::string& optionarg) -{ - options::OptionsHandler* handler = d_handler; - Options& opts = *this; - ${setoption_handlers}$ - throw UnrecognizedOptionException(key); -} -// clang-format on - -// clang-format off -std::string Options::getOption(const std::string& key) const -{ - Trace("options") << "Options::getOption(" << key << ")" << std::endl; - const Options& options = *this; - ${getoption_handlers}$ - - throw UnrecognizedOptionException(key); -} -// clang-format on } // namespace cvc5 diff --git a/src/options/options_template.h b/src/options/options_template.h index e9a4a6999..a4e595f0d 100644 --- a/src/options/options_template.h +++ b/src/options/options_template.h @@ -25,46 +25,20 @@ #include "base/listener.h" #include "cvc5_export.h" -#include "options/language.h" -#include "options/printer_modes.h" namespace cvc5 { - -namespace api { -class Solver; -} namespace options { - struct OptionsHolder; class OptionsHandler; // clang-format off ${holder_fwd_decls}$ // clang-format on - } // namespace options +} // namespace options class OptionsListener; class CVC5_EXPORT Options { - friend api::Solver; - - /** The handler for the options of the theory. */ - options::OptionsHandler* d_handler; - -// clang-format off -${holder_mem_decls}$ -// clang-format on - public: -// clang-format off -${holder_ref_decls}$ -// clang-format on - - private: - - /** The current Options in effect */ - static thread_local Options* s_current; - - friend class options::OptionsHandler; - + public: /** * Options cannot be copied as they are given an explicit list of * Listeners to respond to. @@ -77,25 +51,25 @@ ${holder_ref_decls}$ */ Options& operator=(const Options& options) = delete; -public: - class OptionsScope - { - private: - Options* d_oldOptions; - public: - OptionsScope(Options* newOptions) : - d_oldOptions(Options::s_current) - { - Options::s_current = newOptions; - } - ~OptionsScope(){ - Options::s_current = d_oldOptions; - } - }; + class OptionsScope + { + private: + Options* d_oldOptions; + public: + OptionsScope(Options* newOptions) : + d_oldOptions(Options::s_current) + { + Options::s_current = newOptions; + } + ~OptionsScope(){ + Options::s_current = d_oldOptions; + } + }; + friend class OptionsScope; /** Return true if current Options are null */ static inline bool isCurrentNull() { - return s_current == NULL; + return s_current == nullptr; } /** Get the current Options in effect */ @@ -117,103 +91,31 @@ public: */ void copyValues(const Options& options); - /** - * Set the value of the given option by key. - * - * Throws OptionException or ModalException on failures. - */ - void setOption(const std::string& key, const std::string& optionarg); - - /** - * Gets the value of the given option by key and returns value as a string. - * - * Throws OptionException on failures, such as key not being the name of an - * option. - */ - std::string getOption(const std::string& key) const; - - // Static accessor functions. - // TODO: Document these. - static std::ostream* currentGetOut(); - - /** - * Get a description of the command-line flags accepted by - * parseOptions. The returned string will be escaped so that it is - * suitable as an argument to printf. */ - std::string getDescription() const; - - /** - * Print overall command-line option usage message, prefixed by - * "msg"---which could be an error message causing the usage - * output in the first place, e.g. "no such option --foo" - */ - static void printUsage(const std::string msg, std::ostream& out); - - /** - * Print command-line option usage message for only the most-commonly - * used options. The message is prefixed by "msg"---which could be - * an error message causing the usage output in the first place, e.g. - * "no such option --foo" - */ - static void printShortUsage(const std::string msg, std::ostream& out); - - /** Print help for the --lang command line option */ - static void printLanguageHelp(std::ostream& out); - - /** - * Initialize the Options object options based on the given - * command-line arguments given in argc and argv. The return value - * is what's left of the command line (that is, the non-option - * arguments). - * - * This function uses getopt_long() and is not thread safe. - * - * Throws OptionException on failures. - * - * Preconditions: options and argv must be non-null. - */ - static std::vector<std::string> parseOptions(Options* options, - int argc, - char* argv[], - std::string& binaryName); - - /** - * Get the setting for all options. - */ - std::vector<std::vector<std::string> > getOptions() const; - /** Set the generic listener associated with this class to ol */ void setListener(OptionsListener* ol); - /** Sends a std::flush to getErr(). */ - void flushErr(); - - /** Sends a std::flush to getOut(). */ - void flushOut(); + void notifyListener(const std::string& key); private: /** Pointer to the options listener, if one exists */ - OptionsListener* d_olisten; - /** - * Helper method for setOption, updates this object for setting the given - * option. - */ - void setOptionInternal(const std::string& key, const std::string& optionarg); - /** - * Internal procedure for implementing the parseOptions function. - * Initializes the options object based on the given command-line - * arguments. The command line arguments are stored in argc/argv. - * Nonoptions are stored into nonoptions. - * - * This is not thread safe. - * - * Throws OptionException on failures. - * - * Preconditions: options, extender and nonoptions are non-null. - */ - void parseOptionsRecursive(int argc, - char* argv[], - std::vector<std::string>* nonoptions); + OptionsListener* d_olisten = nullptr; + +// clang-format off +${holder_mem_decls}$ +// clang-format on + public: +// clang-format off +${holder_ref_decls}$ +// clang-format on + + private: + /** The handler for the options of the theory. */ + std::unique_ptr<options::OptionsHandler> d_handler; + + /** The current Options in effect */ + static thread_local Options* s_current; + + }; /* class Options */ } // namespace cvc5 diff --git a/src/options/smt_options.toml b/src/options/smt_options.toml index 9b5a93486..a97fe572f 100644 --- a/src/options/smt_options.toml +++ b/src/options/smt_options.toml @@ -195,6 +195,14 @@ name = "SMT Layer" help = "Produce unsat cores using solving under assumptions and preprocessing proofs." [[option]] + name = "minimalUnsatCores" + category = "regular" + long = "minimal-unsat-cores" + type = "bool" + default = "false" + help = "if an unsat core is produced, it is reduced to a minimal unsat core" + +[[option]] name = "checkUnsatCores" category = "regular" long = "check-unsat-cores" diff --git a/src/printer/smt2/smt2_printer.cpp b/src/printer/smt2/smt2_printer.cpp index b5a8472e6..bf26b80ee 100644 --- a/src/printer/smt2/smt2_printer.cpp +++ b/src/printer/smt2/smt2_printer.cpp @@ -824,8 +824,8 @@ void Smt2Printer::toStream(std::ostream& out, case kind::PRODUCT: case kind::TRANSPOSE: case kind::TCLOSURE: - out << smtKindString(k, d_variant) << " "; - break; + case kind::IDEN: + case kind::JOIN_IMAGE: out << smtKindString(k, d_variant) << " "; break; case kind::COMPREHENSION: out << smtKindString(k, d_variant) << " "; break; case kind::SINGLETON: { @@ -1237,6 +1237,11 @@ std::string Smt2Printer::smtKindString(Kind k, Variant v) case kind::BITVECTOR_ROTATE_RIGHT: return "rotate_right"; case kind::INT_TO_BITVECTOR: return "int2bv"; + // datatypes theory + case kind::APPLY_TESTER: return "is"; + case kind::APPLY_UPDATER: return "update"; + + // set theory case kind::UNION: return "union"; case kind::INTERSECTION: return "intersection"; case kind::SETMINUS: return "setminus"; @@ -1254,6 +1259,8 @@ std::string Smt2Printer::smtKindString(Kind k, Variant v) case kind::PRODUCT: return "product"; case kind::TRANSPOSE: return "transpose"; case kind::TCLOSURE: return "tclosure"; + case kind::IDEN: return "iden"; + case kind::JOIN_IMAGE: return "join_image"; // bag theory case kind::BAG_TYPE: return "Bag"; diff --git a/src/proof/lfsc/lfsc_util.cpp b/src/proof/lfsc/lfsc_util.cpp new file mode 100644 index 000000000..d8bd8f548 --- /dev/null +++ b/src/proof/lfsc/lfsc_util.cpp @@ -0,0 +1,88 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Yoni Zohar + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Utilities for LFSC proofs. + */ + +#include "proof/lfsc/lfsc_util.h" + +#include "proof/proof_checker.h" +#include "util/rational.h" + +namespace cvc5 { +namespace proof { + +const char* toString(LfscRule id) +{ + switch (id) + { + case LfscRule::SCOPE: return "scope"; + case LfscRule::NEG_SYMM: return "neg_symm"; + case LfscRule::CONG: return "cong"; + case LfscRule::AND_INTRO1: return "and_intro1"; + case LfscRule::AND_INTRO2: return "and_intro2"; + case LfscRule::NOT_AND_REV: return "not_and_rev"; + case LfscRule::PROCESS_SCOPE: return "process_scope"; + case LfscRule::ARITH_SUM_UB: return "arith_sum_ub"; + case LfscRule::INSTANTIATE: return "instantiate"; + case LfscRule::SKOLEMIZE: return "skolemize"; + case LfscRule::LAMBDA: return "\\"; + case LfscRule::PLET: return "plet"; + default: return "?"; + } +} +std::ostream& operator<<(std::ostream& out, LfscRule id) +{ + out << toString(id); + return out; +} + +bool getLfscRule(Node n, LfscRule& lr) +{ + uint32_t id; + if (ProofRuleChecker::getUInt32(n, id)) + { + lr = static_cast<LfscRule>(id); + return true; + } + return false; +} + +LfscRule getLfscRule(Node n) +{ + LfscRule lr = LfscRule::UNKNOWN; + getLfscRule(n, lr); + return lr; +} + +Node mkLfscRuleNode(LfscRule r) +{ + return NodeManager::currentNM()->mkConst(Rational(static_cast<uint32_t>(r))); +} + +bool LfscProofLetifyTraverseCallback::shouldTraverse(const ProofNode* pn) +{ + if (pn->getRule() == PfRule::SCOPE) + { + return false; + } + if (pn->getRule() != PfRule::LFSC_RULE) + { + return true; + } + // do not traverse under LFSC (lambda) scope + LfscRule lr = getLfscRule(pn->getArguments()[0]); + return lr != LfscRule::LAMBDA; +} + +} // namespace proof +} // namespace cvc5 diff --git a/src/proof/lfsc/lfsc_util.h b/src/proof/lfsc/lfsc_util.h new file mode 100644 index 000000000..c97c07489 --- /dev/null +++ b/src/proof/lfsc/lfsc_util.h @@ -0,0 +1,105 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Yoni Zohar + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Utilities for LFSC proofs. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__LFSC__LFSC_UTIL_H +#define CVC5__PROOF__LFSC__LFSC_UTIL_H + +#include <iostream> +#include <map> + +#include "expr/node.h" +#include "proof/proof_letify.h" + +namespace cvc5 { +namespace proof { + +/** + * LFSC rules. The enum below contains all rules that don't correspond to a + * PfRule, e.g. congruence in LFSC does not have the same form as congruence + * in the internal calculus. + */ +enum class LfscRule : uint32_t +{ + //----------- translated rules + + // We defined LFSC versions for rules that either don't exist in the internal + // calculus, or have a different set of arugments/children. + + // scope has a different structure, e.g. uses lambdas + SCOPE, + // must distinguish equalities and disequalities + NEG_SYMM, + // congruence is done via a higher-order variant of congruence + CONG, + // we use unrolled binary versions of and intro + AND_INTRO1, + AND_INTRO2, + // needed as a helper for SCOPE + NOT_AND_REV, + PROCESS_SCOPE, + // arithmetic + ARITH_SUM_UB, + + // form of quantifier rules varies from internal calculus + INSTANTIATE, + SKOLEMIZE, + + // a lambda with argument + LAMBDA, + // a proof-let "plet" + PLET, + //----------- unknown + UNKNOWN, +}; + +/** + * Converts a lfsc rule to a string. Note: This function is also used in + * `safe_print()`. Changing this function name or signature will result in + * `safe_print()` printing "<unsupported>" instead of the proper strings for + * the enum values. + * + * @param id The lfsc rule + * @return The name of the lfsc rule + */ +const char* toString(LfscRule id); + +/** + * Writes a lfsc rule name to a stream. + * + * @param out The stream to write to + * @param id The lfsc rule to write to the stream + * @return The stream + */ +std::ostream& operator<<(std::ostream& out, LfscRule id); +/** Get LFSC rule from a node */ +LfscRule getLfscRule(Node n); +/** Get LFSC rule from a node, return true if success and store in lr */ +bool getLfscRule(Node n, LfscRule& lr); +/** Make node for LFSC rule */ +Node mkLfscRuleNode(LfscRule r); + +/** Helper class used for letifying LFSC proofs. */ +class LfscProofLetifyTraverseCallback : public ProofLetifyTraverseCallback +{ + public: + bool shouldTraverse(const ProofNode* pn) override; +}; + +} // namespace proof +} // namespace cvc5 + +#endif diff --git a/src/proof/print_expr.cpp b/src/proof/print_expr.cpp new file mode 100644 index 000000000..becd9195a --- /dev/null +++ b/src/proof/print_expr.cpp @@ -0,0 +1,58 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Utilities for printing expressions in proofs + */ + +#include "proof/print_expr.h" + +namespace cvc5 { +namespace proof { + +PExprStream::PExprStream(std::vector<PExpr>& stream, Node tt, Node ff) + : d_stream(stream), d_tt(tt), d_ff(ff) +{ +} + +PExprStream& PExprStream::operator<<(const ProofNode* pn) +{ + d_stream.push_back(PExpr(pn)); + return *this; +} + +PExprStream& PExprStream::operator<<(Node n) +{ + d_stream.push_back(PExpr(n)); + return *this; +} + +PExprStream& PExprStream::operator<<(TypeNode tn) +{ + d_stream.push_back(PExpr(tn)); + return *this; +} + +PExprStream& PExprStream::operator<<(bool b) +{ + Assert(!d_tt.isNull() && !d_ff.isNull()); + d_stream.push_back(b ? d_tt : d_ff); + return *this; +} + +PExprStream& PExprStream::operator<<(PExpr p) +{ + d_stream.push_back(p); + return *this; +} + +} // namespace proof +} // namespace cvc5 diff --git a/src/proof/print_expr.h b/src/proof/print_expr.h new file mode 100644 index 000000000..15a8bb6c2 --- /dev/null +++ b/src/proof/print_expr.h @@ -0,0 +1,86 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Utilities for printing expressions in proofs + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__PRINT_EXPR_H +#define CVC5__PROOF__PRINT_EXPR_H + +#include <iostream> +#include <map> + +#include "expr/node.h" +#include "proof/proof_node.h" + +namespace cvc5 { +namespace proof { + +/** + * A term, type or a proof. This class is used for printing combinations of + * proofs terms and types. + */ +class PExpr +{ + public: + PExpr() : d_node(), d_pnode(nullptr), d_typeNode() {} + PExpr(Node n) : d_node(n), d_pnode(nullptr), d_typeNode() {} + PExpr(const ProofNode* pn) : d_node(), d_pnode(pn), d_typeNode() {} + PExpr(TypeNode tn) : d_node(), d_pnode(nullptr), d_typeNode(tn) {} + ~PExpr() {} + /** The node, if this p-exression is a node */ + Node d_node; + /** The proof node, if this p-expression is a proof */ + const ProofNode* d_pnode; + /** The type node, if this p-expression is a type */ + TypeNode d_typeNode; +}; + +/** + * A stream of p-expressions, which appends p-expressions to a reference vector. + * That vector can then be used when printing a proof. + */ +class PExprStream +{ + public: + /** + * Takes a reference to a stream (vector of p-expressions), and the + * representation true/false (tt/ff). + */ + PExprStream(std::vector<PExpr>& stream, + Node tt = Node::null(), + Node ff = Node::null()); + /** Append a proof node */ + PExprStream& operator<<(const ProofNode* pn); + /** Append a node */ + PExprStream& operator<<(Node n); + /** Append a type node */ + PExprStream& operator<<(TypeNode tn); + /** Append a Boolean */ + PExprStream& operator<<(bool b); + /** Append a pexpr */ + PExprStream& operator<<(PExpr p); + + private: + /** Reference to the stream */ + std::vector<PExpr>& d_stream; + /** Builtin nodes for true and false */ + Node d_tt; + Node d_ff; +}; + +} // namespace proof +} // namespace cvc5 + +#endif diff --git a/src/proof/proof_checker.cpp b/src/proof/proof_checker.cpp index 96f5197e3..6a9979f75 100644 --- a/src/proof/proof_checker.cpp +++ b/src/proof/proof_checker.cpp @@ -85,6 +85,11 @@ ProofCheckerStatistics::ProofCheckerStatistics() { } +ProofChecker::ProofChecker(uint32_t pclevel, theory::RewriteDb* rdb) + : d_pclevel(pclevel), d_rdb(rdb) +{ +} + Node ProofChecker::check(ProofNode* pn, Node expected) { return check(pn->getRule(), pn->getChildren(), pn->getArguments(), expected); @@ -303,6 +308,8 @@ ProofRuleChecker* ProofChecker::getCheckerFor(PfRule id) return it->second; } +theory::RewriteDb* ProofChecker::getRewriteDatabase() { return d_rdb; } + uint32_t ProofChecker::getPedanticLevel(PfRule id) const { std::map<PfRule, uint32_t>::const_iterator itp = d_plevel.find(id); diff --git a/src/proof/proof_checker.h b/src/proof/proof_checker.h index ac5531bf4..f1f10eedb 100644 --- a/src/proof/proof_checker.h +++ b/src/proof/proof_checker.h @@ -29,6 +29,10 @@ namespace cvc5 { class ProofChecker; class ProofNode; +namespace theory { +class RewriteDb; +} + /** A virtual base class for checking a proof rule */ class ProofRuleChecker { @@ -101,7 +105,7 @@ class ProofCheckerStatistics class ProofChecker { public: - ProofChecker(uint32_t pclevel = 0) : d_pclevel(pclevel) {} + ProofChecker(uint32_t pclevel = 0, theory::RewriteDb* rdb = nullptr); ~ProofChecker() {} /** * Return the formula that is proven by proof node pn, or null if pn is not @@ -162,7 +166,8 @@ class ProofChecker uint32_t plevel = 10); /** get checker for */ ProofRuleChecker* getCheckerFor(PfRule id); - + /** get the rewrite database */ + theory::RewriteDb* getRewriteDatabase(); /** * Get the pedantic level for id if it has been assigned a pedantic * level via registerTrustedChecker above, or zero otherwise. @@ -186,6 +191,8 @@ class ProofChecker std::map<PfRule, uint32_t> d_plevel; /** The pedantic level of this checker */ uint32_t d_pclevel; + /** Pointer to the rewrite database */ + theory::RewriteDb* d_rdb; /** * Check internal. This is used by check and checkDebug above. It writes * checking errors on out when enableOutput is true. We treat trusted checkers diff --git a/src/proof/proof_letify.cpp b/src/proof/proof_letify.cpp new file mode 100644 index 000000000..c6269631c --- /dev/null +++ b/src/proof/proof_letify.cpp @@ -0,0 +1,124 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Utilities for computing letification of proofs. + */ + +#include "proof/proof_letify.h" + +namespace cvc5 { +namespace proof { + +bool ProofLetifyTraverseCallback::shouldTraverse(const ProofNode* pn) +{ + return pn->getRule() != PfRule::SCOPE; +} + +void ProofLetify::computeProofLet(const ProofNode* pn, + std::vector<const ProofNode*>& pletList, + std::map<const ProofNode*, size_t>& pletMap, + size_t thresh, + ProofLetifyTraverseCallback* pltc) +{ + Assert(pletList.empty() && pletMap.empty()); + if (thresh == 0) + { + // value of 0 means do not introduce let + return; + } + std::vector<const ProofNode*> visitList; + std::map<const ProofNode*, size_t> pcount; + if (pltc == nullptr) + { + // use default callback + ProofLetifyTraverseCallback defaultPltc; + computeProofCounts(pn, visitList, pcount, &defaultPltc); + } + else + { + computeProofCounts(pn, visitList, pcount, pltc); + } + // Now populate the pletList and pletMap + convertProofCountToLet(visitList, pcount, pletList, pletMap, thresh); +} + +void ProofLetify::computeProofCounts(const ProofNode* pn, + std::vector<const ProofNode*>& visitList, + std::map<const ProofNode*, size_t>& pcount, + ProofLetifyTraverseCallback* pltc) +{ + std::map<const ProofNode*, size_t>::iterator it; + std::vector<const ProofNode*> visit; + const ProofNode* cur; + visit.push_back(pn); + do + { + cur = visit.back(); + it = pcount.find(cur); + if (it == pcount.end()) + { + pcount[cur] = 0; + if (!pltc->shouldTraverse(cur)) + { + // callback indicated we should not traverse + continue; + } + const std::vector<std::shared_ptr<ProofNode>>& pc = cur->getChildren(); + for (const std::shared_ptr<ProofNode>& cp : pc) + { + visit.push_back(cp.get()); + } + } + else + { + if (it->second == 0) + { + visitList.push_back(cur); + } + pcount[cur]++; + visit.pop_back(); + } + } while (!visit.empty()); +} + +void ProofLetify::convertProofCountToLet( + const std::vector<const ProofNode*>& visitList, + const std::map<const ProofNode*, size_t>& pcount, + std::vector<const ProofNode*>& pletList, + std::map<const ProofNode*, size_t>& pletMap, + size_t thresh) +{ + Assert(pletList.empty() && pletMap.empty()); + if (thresh == 0) + { + // value of 0 means do not introduce let + return; + } + // Assign ids for those whose count is > 1, traverse in reverse order + // so that deeper proofs are assigned lower identifiers + std::map<const ProofNode*, size_t>::const_iterator itc; + for (const ProofNode* pn : visitList) + { + itc = pcount.find(pn); + Assert(itc != pcount.end()); + if (itc->second >= thresh && pn->getRule() != PfRule::ASSUME) + { + pletList.push_back(pn); + // start with id 1 + size_t id = pletMap.size() + 1; + pletMap[pn] = id; + } + } +} + +} // namespace proof +} // namespace cvc5 diff --git a/src/proof/proof_letify.h b/src/proof/proof_letify.h new file mode 100644 index 000000000..cfe1259e5 --- /dev/null +++ b/src/proof/proof_letify.h @@ -0,0 +1,98 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Utilities for computing letification of proofs. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__PROOF__PROOF_LETIFY_H +#define CVC5__PROOF__PROOF_LETIFY_H + +#include <iostream> +#include <map> + +#include "expr/node.h" +#include "proof/proof_node.h" + +namespace cvc5 { +namespace proof { + +/** + * A callback which asks whether a proof node should be traversed for + * proof letification. For example, this may make it so that SCOPE is not + * traversed. + */ +class ProofLetifyTraverseCallback +{ + public: + virtual ~ProofLetifyTraverseCallback() {} + /** + * Should we traverse proof node pn for letification? If this returns false, + * then pn is being treated as a black box for letification. + */ + virtual bool shouldTraverse(const ProofNode* pn); +}; + +/** + * Utilities for letification. + */ +class ProofLetify +{ + public: + /** + * Stores proofs in map that require letification, mapping them to a unique + * identifier. For each proof node in the domain of pletMap in the list + * pletList such that pletList[i] does not contain subproof pletList[j] for + * j>i. + * + * @param pn The proof node to letify + * @param pletList The list of proofs occurring in pn that should be letified + * @param pletMap Mapping from proofs in pletList to an identifier + * @param thresh The number of times a proof node has to occur to be added + * to pletList + * @param pltc A callback indicating whether to traverse a proof node during + * this call. + */ + static void computeProofLet(const ProofNode* pn, + std::vector<const ProofNode*>& pletList, + std::map<const ProofNode*, size_t>& pletMap, + size_t thresh = 2, + ProofLetifyTraverseCallback* pltc = nullptr); + + private: + /** + * Convert a map from proof nodes to # occurrences (pcount) to a list + * pletList / pletMap as described in the method above, where thresh + * is the minimum number of occurrences to be added to the list. + */ + static void convertProofCountToLet( + const std::vector<const ProofNode*>& visitList, + const std::map<const ProofNode*, size_t>& pcount, + std::vector<const ProofNode*>& pletList, + std::map<const ProofNode*, size_t>& pletMap, + size_t thresh = 2); + /** + * Compute the count of sub proof nodes in pn, store in pcount. Additionally, + * store each proof node in the domain of pcount in an order in visitList + * such that visitList[i] does not contain sub proof visitList[j] for j>i. + */ + static void computeProofCounts(const ProofNode* pn, + std::vector<const ProofNode*>& visitList, + std::map<const ProofNode*, size_t>& pcount, + ProofLetifyTraverseCallback* pltc); +}; + +} // namespace proof +} // namespace cvc5 + +#endif diff --git a/src/proof/proof_rule.cpp b/src/proof/proof_rule.cpp index 7be07f7f5..7e1cdf8d3 100644 --- a/src/proof/proof_rule.cpp +++ b/src/proof/proof_rule.cpp @@ -47,6 +47,7 @@ const char* toString(PfRule id) case PfRule::TRUST_SUBS: return "TRUST_SUBS"; case PfRule::TRUST_SUBS_MAP: return "TRUST_SUBS_MAP"; case PfRule::TRUST_SUBS_EQ: return "TRUST_SUBS_EQ"; + case PfRule::THEORY_INFERENCE: return "THEORY_INFERENCE"; //================================================= Boolean rules case PfRule::RESOLUTION: return "RESOLUTION"; case PfRule::CHAIN_RESOLUTION: return "CHAIN_RESOLUTION"; @@ -119,6 +120,7 @@ const char* toString(PfRule id) case PfRule::ARRAYS_READ_OVER_WRITE_1: return "ARRAYS_READ_OVER_WRITE_1"; case PfRule::ARRAYS_EXT: return "ARRAYS_EXT"; case PfRule::ARRAYS_TRUST: return "ARRAYS_TRUST"; + case PfRule::ARRAYS_EQ_RANGE_EXPAND: return "ARRAYS_EQ_RANGE_EXPAND"; //================================================= Bit-Vector rules case PfRule::BV_BITBLAST: return "BV_BITBLAST"; case PfRule::BV_BITBLAST_STEP: return "BV_BITBLAST_STEP"; @@ -200,6 +202,8 @@ const char* toString(PfRule id) return "ARITH_TRANS_SINE_APPROX_BELOW_POS"; case PfRule::ARITH_NL_CAD_DIRECT: return "ARITH_NL_CAD_DIRECT"; case PfRule::ARITH_NL_CAD_RECURSIVE: return "ARITH_NL_CAD_RECURSIVE"; + //================================================= External rules + case PfRule::LFSC_RULE: return "LFSC_RULE"; //================================================= Unknown rule case PfRule::UNKNOWN: return "UNKNOWN"; default: return "?"; diff --git a/src/proof/proof_rule.h b/src/proof/proof_rule.h index 9625e1d28..78e773bdc 100644 --- a/src/proof/proof_rule.h +++ b/src/proof/proof_rule.h @@ -257,6 +257,8 @@ enum class PfRule : uint32_t // where F is a solved equality of the form (= x t) derived as the solved // form of F', where F' is given as a child. TRUST_SUBS_EQ, + // where F is a fact derived by a theory-specific inference + THEORY_INFERENCE, // ========= SAT Refutation for assumption-based unsat cores // Children: (P1, ..., Pn) // Arguments: none @@ -711,6 +713,15 @@ enum class PfRule : uint32_t // Conclusion: (not (= (select a k) (select b k))) // where k is arrays::SkolemCache::getExtIndexSkolem((not (= a b))). ARRAYS_EXT, + // ======== EQ_RANGE expansion + // Children: none + // Arguments: ((eqrange a b i j)) + // ---------------------------------------- + // Conclusion: (= + // (eqrange a b i j) + // (forall ((x T)) + // (=> (and (<= i x) (<= x j)) (= (select a x) (select b x))))) + ARRAYS_EQ_RANGE_EXPAND, // ======== Array Trust // Children: (P1 ... Pn) // Arguments: (F) @@ -825,10 +836,16 @@ enum class PfRule : uint32_t SKOLEMIZE, // ======== Instantiate // Children: (P:(forall ((x1 T1) ... (xn Tn)) F)) - // Arguments: (t1 ... tn) + // Arguments: (t1 ... tn, (id (t)?)? ) // ---------------------------------------- // Conclusion: F*sigma - // sigma maps x1 ... xn to t1 ... tn. + // where sigma maps x1 ... xn to t1 ... tn. + // + // The optional argument id indicates the inference id that caused the + // instantiation. The term t indicates an additional term (e.g. the trigger) + // associated with the instantiation, which depends on the id. If the id + // has prefix "QUANTIFIERS_INST_E_MATCHING", then t is the trigger that + // generated the instantiation. INSTANTIATE, // ======== (Trusted) quantifiers preprocess // Children: ? @@ -1387,6 +1404,14 @@ enum class PfRule : uint32_t // that extends the Cell and satisfies all assumptions. ARITH_NL_CAD_RECURSIVE, + //================================================ Place holder for Lfsc rules + // ======== Lfsc rule + // Children: (P1 ... Pn) + // Arguments: (id, Q, A1, ..., Am) + // --------------------- + // Conclusion: (Q) + LFSC_RULE, + //================================================= Unknown rule UNKNOWN, }; diff --git a/src/smt/set_defaults.cpp b/src/smt/set_defaults.cpp index bb104e98d..e12d3eb1d 100644 --- a/src/smt/set_defaults.cpp +++ b/src/smt/set_defaults.cpp @@ -69,7 +69,7 @@ void setDefaults(LogicInfo& logic, bool isInternalSubsolver) opts.driver.dumpUnsatCores = true; } if (options::checkUnsatCores() || options::dumpUnsatCores() - || options::unsatAssumptions() + || options::unsatAssumptions() || options::minimalUnsatCores() || options::unsatCoresMode() != options::UnsatCoresMode::OFF) { opts.smt.unsatCores = true; @@ -968,6 +968,18 @@ void setDefaults(LogicInfo& logic, bool isInternalSubsolver) Trace("smt") << "setting decision mode to " << opts.decision.decisionMode << std::endl; } + + // set up of central equality engine + if (opts.arith.arithEqSolver) + { + if (!opts.arith.arithCongManWasSetByUser) + { + // if we are using the arithmetic equality solver, do not use the + // arithmetic congruence manager by default + opts.arith.arithCongMan = false; + } + } + if (options::incrementalSolving()) { // disable modes not supported by incremental diff --git a/src/smt/smt_engine.cpp b/src/smt/smt_engine.cpp index 9056e7c94..bbd65c24f 100644 --- a/src/smt/smt_engine.cpp +++ b/src/smt/smt_engine.cpp @@ -27,6 +27,8 @@ #include "options/language.h" #include "options/main_options.h" #include "options/option_exception.h" +#include "options/options_public.h" +#include "options/parser_options.h" #include "options/printer_options.h" #include "options/proof_options.h" #include "options/smt_options.h" @@ -549,7 +551,7 @@ std::string SmtEngine::getInfo(const std::string& key) const } Assert(key == "all-options"); // get the options, like all-statistics - return toSExpr(Options::current().getOptions()); + return toSExpr(options::getAll(getOptions())); } void SmtEngine::debugCheckFormals(const std::vector<Node>& formals, Node func) @@ -1400,9 +1402,80 @@ UnsatCore SmtEngine::getUnsatCoreInternal() std::shared_ptr<ProofNode> pfn = d_pfManager->getFinalProof(pepf, *d_asserts); std::vector<Node> core; d_ucManager->getUnsatCore(pfn, *d_asserts, core); + if (options::minimalUnsatCores()) + { + core = reduceUnsatCore(core); + } return UnsatCore(core); } +std::vector<Node> SmtEngine::reduceUnsatCore(const std::vector<Node>& core) +{ + Assert(options::unsatCores()) + << "cannot reduce unsat core if unsat cores are turned off"; + + Notice() << "SmtEngine::reduceUnsatCore(): reducing unsat core" << endl; + std::unordered_set<Node> removed; + for (const Node& skip : core) + { + std::unique_ptr<SmtEngine> coreChecker; + initializeSubsolver(coreChecker); + coreChecker->setLogic(getLogicInfo()); + coreChecker->getOptions().smt.checkUnsatCores = false; + // disable all proof options + coreChecker->getOptions().smt.produceProofs = false; + coreChecker->getOptions().smt.checkProofs = false; + coreChecker->getOptions().proof.proofEagerChecking = false; + + for (const Node& ucAssertion : core) + { + if (ucAssertion != skip && removed.find(ucAssertion) == removed.end()) + { + Node assertionAfterExpansion = expandDefinitions(ucAssertion); + coreChecker->assertFormula(assertionAfterExpansion); + } + } + Result r; + try + { + r = coreChecker->checkSat(); + } + catch (...) + { + throw; + } + + if (r.asSatisfiabilityResult().isSat() == Result::UNSAT) + { + removed.insert(skip); + } + else if (r.asSatisfiabilityResult().isUnknown()) + { + Warning() << "SmtEngine::reduceUnsatCore(): could not reduce unsat core " + "due to " + "unknown result."; + } + } + + if (removed.empty()) + { + return core; + } + else + { + std::vector<Node> newUcAssertions; + for (const Node& n : core) + { + if (removed.find(n) == removed.end()) + { + newUcAssertions.push_back(n); + } + } + + return newUcAssertions; + } +} + void SmtEngine::checkUnsatCore() { Assert(d_env->getOptions().smt.unsatCores) << "cannot check unsat core if unsat cores are turned off"; @@ -1907,7 +1980,7 @@ void SmtEngine::setOption(const std::string& key, const std::string& value) } std::string optionarg = value; - getOptions().setOption(key, optionarg); + options::set(getOptions(), key, optionarg); } void SmtEngine::setIsInternalSubsolver() { d_isInternalSubsolver = true; } @@ -1972,7 +2045,7 @@ std::string SmtEngine::getOption(const std::string& key) const return nm->mkNode(Kind::SEXPR, result).toString(); } - std::string atom = getOptions().getOption(key); + std::string atom = options::get(getOptions(), key); if (atom != "true" && atom != "false") { diff --git a/src/smt/smt_engine.h b/src/smt/smt_engine.h index 3128257e6..02e5c6b06 100644 --- a/src/smt/smt_engine.h +++ b/src/smt/smt_engine.h @@ -1,6 +1,6 @@ /****************************************************************************** * Top contributors (to current version): - * Andrew Reynolds, Morgan Deters, Aina Niemetz + * * This file is part of the cvc5 project. * @@ -368,6 +368,11 @@ class CVC5_EXPORT SmtEngine Result assertFormula(const Node& formula, bool inUnsatCore = true); /** + * Reduce an unsatisfiable core to make it minimal. + */ + std::vector<Node> reduceUnsatCore(const std::vector<Node>& core); + + /** * Check if a given (set of) expression(s) is entailed with respect to the * current set of assertions. We check this by asserting the negation of * the (big AND over the) given (set of) expression(s). diff --git a/src/smt/unsat_core_manager.cpp b/src/smt/unsat_core_manager.cpp index ba2c07326..22304f9e8 100644 --- a/src/smt/unsat_core_manager.cpp +++ b/src/smt/unsat_core_manager.cpp @@ -80,7 +80,10 @@ void UnsatCoreManager::getRelevantInstantiations( const std::vector<Node>& instTerms = cur->getArguments(); Assert(cs.size() == 1); Node q = cs[0]->getResult(); - insts[q].push_back({instTerms.begin(), instTerms.end()}); + // the instantiation is a prefix of the arguments up to the number of + // variables + insts[q].push_back( + {instTerms.begin(), instTerms.begin() + q[0].getNumChildren()}); } for (const std::shared_ptr<ProofNode>& cp : cs) { diff --git a/src/theory/arith/congruence_manager.cpp b/src/theory/arith/congruence_manager.cpp index 9e7202f1d..3a35319ed 100644 --- a/src/theory/arith/congruence_manager.cpp +++ b/src/theory/arith/congruence_manager.cpp @@ -71,24 +71,44 @@ ArithCongruenceManager::~ArithCongruenceManager() {} bool ArithCongruenceManager::needsEqualityEngine(EeSetupInfo& esi) { + Assert(!options::arithEqSolver()); esi.d_notify = &d_notify; - esi.d_name = "theory::arith::ArithCongruenceManager"; + esi.d_name = "arithCong::ee"; return true; } -void ArithCongruenceManager::finishInit(eq::EqualityEngine* ee, - eq::ProofEqEngine* pfee) +void ArithCongruenceManager::finishInit(eq::EqualityEngine* ee) { - Assert(ee != nullptr); - d_ee = ee; + if (options::arithEqSolver()) + { + // use our own copy + d_allocEe.reset( + new eq::EqualityEngine(d_notify, d_satContext, "arithCong::ee", true)); + d_ee = d_allocEe.get(); + if (d_pnm != nullptr) + { + // allocate an internal proof equality engine + d_allocPfee.reset( + new eq::ProofEqEngine(d_satContext, d_userContext, *d_ee, d_pnm)); + d_ee->setProofEqualityEngine(d_allocPfee.get()); + } + } + else + { + Assert(ee != nullptr); + // otherwise, we use the official one + d_ee = ee; + } + // set the congruence kinds on the separate equality engine d_ee->addFunctionKind(kind::NONLINEAR_MULT); d_ee->addFunctionKind(kind::EXPONENTIAL); d_ee->addFunctionKind(kind::SINE); d_ee->addFunctionKind(kind::IAND); d_ee->addFunctionKind(kind::POW2); + // the proof equality engine is the one from the equality engine + d_pfee = d_ee->getProofEqualityEngine(); // have proof equality engine only if proofs are enabled - Assert(isProofEnabled() == (pfee != nullptr)); - d_pfee = pfee; + Assert(isProofEnabled() == (d_pfee != nullptr)); } ArithCongruenceManager::Statistics::Statistics() diff --git a/src/theory/arith/congruence_manager.h b/src/theory/arith/congruence_manager.h index 4ab0b313b..2c59a405f 100644 --- a/src/theory/arith/congruence_manager.h +++ b/src/theory/arith/congruence_manager.h @@ -110,6 +110,8 @@ private: /** The equality engine being used by this class */ eq::EqualityEngine* d_ee; + /** The equality engine we allocated */ + std::unique_ptr<eq::EqualityEngine> d_allocEe; /** The sat context */ context::Context* d_satContext; /** The user context */ @@ -143,6 +145,8 @@ private: /** Pointer to the proof equality engine of TheoryArith */ theory::eq::ProofEqEngine* d_pfee; + /** The proof equality engine we allocated */ + std::unique_ptr<eq::ProofEqEngine> d_allocPfee; /** Raise a conflict node `conflict` to the theory of arithmetic. * @@ -240,9 +244,9 @@ private: bool needsEqualityEngine(EeSetupInfo& esi); /** * Finish initialize. This class is instructed by TheoryArithPrivate to use - * the equality engine ee and proof equality engine pfee. + * the equality engine ee. */ - void finishInit(eq::EqualityEngine* ee, eq::ProofEqEngine* pfee); + void finishInit(eq::EqualityEngine* ee); //--------------------------------- end initialization /** diff --git a/src/theory/arith/equality_solver.cpp b/src/theory/arith/equality_solver.cpp index 58793c654..8b4e1b8dd 100644 --- a/src/theory/arith/equality_solver.cpp +++ b/src/theory/arith/equality_solver.cpp @@ -80,6 +80,11 @@ TrustNode EqualitySolver::explain(TNode lit) } bool EqualitySolver::propagateLit(Node lit) { + // if we've already propagated, ignore + if (d_aim.hasPropagated(lit)) + { + return true; + } // notice this is only used when ee-mode=distributed // remember that this was a literal we propagated Trace("arith-eq-solver-debug") << "propagate lit " << lit << std::endl; diff --git a/src/theory/arith/inference_manager.cpp b/src/theory/arith/inference_manager.cpp index 90c17be4a..9cb232908 100644 --- a/src/theory/arith/inference_manager.cpp +++ b/src/theory/arith/inference_manager.cpp @@ -27,7 +27,10 @@ namespace arith { InferenceManager::InferenceManager(TheoryArith& ta, ArithState& astate, ProofNodeManager* pnm) - : InferenceManagerBuffered(ta, astate, pnm, "theory::arith::") + : InferenceManagerBuffered(ta, astate, pnm, "theory::arith::"), + // currently must track propagated literals if using the equality solver + d_trackPropLits(options::arithEqSolver()), + d_propLits(astate.getSatContext()) { } @@ -146,6 +149,21 @@ bool InferenceManager::isEntailedFalse(const SimpleTheoryLemma& lem) return false; } +bool InferenceManager::propagateLit(TNode lit) +{ + if (d_trackPropLits) + { + d_propLits.insert(lit); + } + return TheoryInferenceManager::propagateLit(lit); +} + +bool InferenceManager::hasPropagated(TNode lit) const +{ + Assert(d_trackPropLits); + return d_propLits.find(lit) != d_propLits.end(); +} + } // namespace arith } // namespace theory } // namespace cvc5 diff --git a/src/theory/arith/inference_manager.h b/src/theory/arith/inference_manager.h index 7e103797e..eeb9d096f 100644 --- a/src/theory/arith/inference_manager.h +++ b/src/theory/arith/inference_manager.h @@ -97,6 +97,13 @@ class InferenceManager : public InferenceManagerBuffered /** Checks whether the given lemma is already present in the cache. */ virtual bool hasCachedLemma(TNode lem, LemmaProperty p) override; + /** overrides propagateLit to track which literals have been propagated */ + bool propagateLit(TNode lit) override; + /** + * Return true if we have propagated lit already. This call is only valid if + * d_trackPropLits is true. + */ + bool hasPropagated(TNode lit) const; protected: /** @@ -111,9 +118,12 @@ class InferenceManager : public InferenceManagerBuffered * conflict. */ bool isEntailedFalse(const SimpleTheoryLemma& lem); - /** The waiting lemmas. */ std::vector<std::unique_ptr<SimpleTheoryLemma>> d_waitingLem; + /** Whether we are tracking the set of propagated literals */ + bool d_trackPropLits; + /** The literals we have propagated */ + NodeSet d_propLits; }; } // namespace arith diff --git a/src/theory/arith/theory_arith_private.cpp b/src/theory/arith/theory_arith_private.cpp index fb9de9641..053b91d90 100644 --- a/src/theory/arith/theory_arith_private.cpp +++ b/src/theory/arith/theory_arith_private.cpp @@ -137,7 +137,7 @@ TheoryArithPrivate::TheoryArithPrivate(TheoryArith& containing, d_partialModel, RaiseEqualityEngineConflict(*this), d_pnm), - d_cmEnabled(c, true), + d_cmEnabled(c, options::arithCongMan()), d_dualSimplex( d_linEq, d_errorSet, RaiseConflict(*this), TempVarMalloc(*this)), @@ -181,14 +181,20 @@ TheoryArithPrivate::~TheoryArithPrivate(){ bool TheoryArithPrivate::needsEqualityEngine(EeSetupInfo& esi) { + if (!d_cmEnabled) + { + return false; + } return d_congruenceManager.needsEqualityEngine(esi); } void TheoryArithPrivate::finishInit() { - eq::EqualityEngine* ee = d_containing.getEqualityEngine(); - eq::ProofEqEngine* pfee = d_containing.getProofEqEngine(); - Assert(ee != nullptr); - d_congruenceManager.finishInit(ee, pfee); + if (d_cmEnabled) + { + eq::EqualityEngine* ee = d_containing.getEqualityEngine(); + Assert(ee != nullptr); + d_congruenceManager.finishInit(ee); + } } static bool contains(const ConstraintCPVec& v, ConstraintP con){ diff --git a/src/theory/arrays/proof_checker.cpp b/src/theory/arrays/proof_checker.cpp index f8c5b2669..6d546d746 100644 --- a/src/theory/arrays/proof_checker.cpp +++ b/src/theory/arrays/proof_checker.cpp @@ -14,8 +14,10 @@ */ #include "theory/arrays/proof_checker.h" + #include "expr/skolem_manager.h" #include "theory/arrays/skolem_cache.h" +#include "theory/arrays/theory_arrays_rewriter.h" #include "theory/rewriter.h" namespace cvc5 { @@ -28,6 +30,7 @@ void ArraysProofRuleChecker::registerTo(ProofChecker* pc) pc->registerChecker(PfRule::ARRAYS_READ_OVER_WRITE_CONTRA, this); pc->registerChecker(PfRule::ARRAYS_READ_OVER_WRITE_1, this); pc->registerChecker(PfRule::ARRAYS_EXT, this); + pc->registerChecker(PfRule::ARRAYS_EQ_RANGE_EXPAND, this); // trusted rules pc->registerTrustedChecker(PfRule::ARRAYS_TRUST, this, 2); } @@ -103,6 +106,11 @@ Node ArraysProofRuleChecker::checkInternal(PfRule id, Node bs = nm->mkNode(kind::SELECT, b, k); return as.eqNode(bs).notNode(); } + if (id == PfRule::ARRAYS_EQ_RANGE_EXPAND) + { + Node expandedEqRange = TheoryArraysRewriter::expandEqRange(args[0]); + return args[0].eqNode(expandedEqRange); + } if (id == PfRule::ARRAYS_TRUST) { // "trusted" rules diff --git a/src/theory/arrays/skolem_cache.cpp b/src/theory/arrays/skolem_cache.cpp index 19ab1aaae..7cf192b7f 100644 --- a/src/theory/arrays/skolem_cache.cpp +++ b/src/theory/arrays/skolem_cache.cpp @@ -35,6 +35,14 @@ struct ExtIndexVarAttributeId }; typedef expr::Attribute<ExtIndexVarAttributeId, Node> ExtIndexVarAttribute; +/** + * A bound variable corresponding to the index used in the eqrange expansion. + */ +struct EqRangeVarAttributeId +{ +}; +typedef expr::Attribute<EqRangeVarAttributeId, Node> EqRangeVarAttribute; + SkolemCache::SkolemCache() {} Node SkolemCache::getExtIndexSkolem(Node deq) @@ -66,6 +74,13 @@ Node SkolemCache::getExtIndexSkolem(Node deq) "an extensional lemma index variable from the theory of arrays"); } +Node SkolemCache::getEqRangeVar(TNode eqr) +{ + Assert(eqr.getKind() == kind::EQ_RANGE); + BoundVarManager* bvm = NodeManager::currentNM()->getBoundVarManager(); + return bvm->mkBoundVar<EqRangeVarAttribute>(eqr, eqr[2].getType()); +} + Node SkolemCache::getExtIndexVar(Node deq) { Node a = deq[0][0]; diff --git a/src/theory/arrays/skolem_cache.h b/src/theory/arrays/skolem_cache.h index 12578c01f..17a5c6975 100644 --- a/src/theory/arrays/skolem_cache.h +++ b/src/theory/arrays/skolem_cache.h @@ -43,6 +43,13 @@ class SkolemCache */ static Node getExtIndexSkolem(Node deq); + /** + * Get the bound variable for given EQ_RANGE operator. This bound variable + * is unique for `eqr`. Calling this method will always return the same bound + * variable over the lifetime of `eqr`. + */ + static Node getEqRangeVar(TNode eqr); + private: /** * Get the bound variable x of the witness term above for disequality deq diff --git a/src/theory/arrays/theory_arrays.cpp b/src/theory/arrays/theory_arrays.cpp index be8e1a08e..8fa7e0fd3 100644 --- a/src/theory/arrays/theory_arrays.cpp +++ b/src/theory/arrays/theory_arrays.cpp @@ -85,6 +85,7 @@ TheoryArrays::TheoryArrays(context::Context* c, name + "number of setModelVal conflicts")), d_ppEqualityEngine(u, name + "pp", true), d_ppFacts(u), + d_rewriter(pnm), d_state(c, u, valuation), d_im(*this, d_state, pnm), d_literalsToPropagate(c), diff --git a/src/theory/arrays/theory_arrays_rewriter.cpp b/src/theory/arrays/theory_arrays_rewriter.cpp index 1072ffaf4..dd7a56d33 100644 --- a/src/theory/arrays/theory_arrays_rewriter.cpp +++ b/src/theory/arrays/theory_arrays_rewriter.cpp @@ -20,6 +20,9 @@ #include "expr/array_store_all.h" #include "expr/attribute.h" +#include "proof/conv_proof_generator.h" +#include "proof/eager_proof_generator.h" +#include "theory/arrays/skolem_cache.h" #include "util/cardinality.h" namespace cvc5 { @@ -48,6 +51,11 @@ void setMostFrequentValueCount(TNode store, uint64_t count) { return store.setAttribute(ArrayConstantMostFrequentValueCountAttr(), count); } +TheoryArraysRewriter::TheoryArraysRewriter(ProofNodeManager* pnm) + : d_epg(pnm ? new EagerProofGenerator(pnm) : nullptr) +{ +} + Node TheoryArraysRewriter::normalizeConstant(TNode node) { return normalizeConstant(node, node[1].getType().getCardinality()); @@ -271,6 +279,48 @@ Node TheoryArraysRewriter::normalizeConstant(TNode node, Cardinality indexCard) return n; } +Node TheoryArraysRewriter::expandEqRange(TNode node) +{ + Assert(node.getKind() == kind::EQ_RANGE); + + NodeManager* nm = NodeManager::currentNM(); + TNode a = node[0]; + TNode b = node[1]; + TNode i = node[2]; + TNode j = node[3]; + Node k = SkolemCache::getEqRangeVar(node); + Node bvl = nm->mkNode(kind::BOUND_VAR_LIST, k); + TypeNode type = k.getType(); + + Kind kle; + Node range; + if (type.isBitVector()) + { + kle = kind::BITVECTOR_ULE; + } + else if (type.isFloatingPoint()) + { + kle = kind::FLOATINGPOINT_LEQ; + } + else if (type.isInteger() || type.isReal()) + { + kle = kind::LEQ; + } + else + { + Unimplemented() << "Type " << type << " is not supported for predicate " + << node.getKind(); + } + + range = nm->mkNode(kind::AND, nm->mkNode(kle, i, k), nm->mkNode(kle, k, j)); + + Node eq = nm->mkNode(kind::EQUAL, + nm->mkNode(kind::SELECT, a, k), + nm->mkNode(kind::SELECT, b, k)); + Node implies = nm->mkNode(kind::IMPLIES, range, eq); + return nm->mkNode(kind::FORALL, bvl, implies); +} + RewriteResponse TheoryArraysRewriter::postRewrite(TNode node) { Trace("arrays-postrewrite") @@ -610,57 +660,22 @@ RewriteResponse TheoryArraysRewriter::preRewrite(TNode node) TrustNode TheoryArraysRewriter::expandDefinition(Node node) { - NodeManager* nm = NodeManager::currentNM(); Kind kind = node.getKind(); - /* Expand - * - * (eqrange a b i j) - * - * to - * - * forall k . i <= k <= j => a[k] = b[k] - * - */ if (kind == kind::EQ_RANGE) { - TNode a = node[0]; - TNode b = node[1]; - TNode i = node[2]; - TNode j = node[3]; - Node k = nm->mkBoundVar(i.getType()); - Node bvl = nm->mkNode(kind::BOUND_VAR_LIST, k); - TypeNode type = k.getType(); - - Kind kle; - Node range; - if (type.isBitVector()) - { - kle = kind::BITVECTOR_ULE; - } - else if (type.isFloatingPoint()) + Node expandedEqRange = expandEqRange(node); + if (d_epg) { - kle = kind::FLOATINGPOINT_LEQ; + TrustNode tn = d_epg->mkTrustNode(node.eqNode(expandedEqRange), + PfRule::ARRAYS_EQ_RANGE_EXPAND, + {}, + {node}); + return TrustNode::mkTrustRewrite(node, expandedEqRange, d_epg.get()); } - else if (type.isInteger() || type.isReal()) - { - kle = kind::LEQ; - } - else - { - Unimplemented() << "Type " << type << " is not supported for predicate " - << kind; - } - - range = nm->mkNode(kind::AND, nm->mkNode(kle, i, k), nm->mkNode(kle, k, j)); - - Node eq = nm->mkNode(kind::EQUAL, - nm->mkNode(kind::SELECT, a, k), - nm->mkNode(kind::SELECT, b, k)); - Node implies = nm->mkNode(kind::IMPLIES, range, eq); - Node ret = nm->mkNode(kind::FORALL, bvl, implies); - return TrustNode::mkTrustRewrite(node, ret, nullptr); + return TrustNode::mkTrustRewrite(node, expandedEqRange, nullptr); } + return TrustNode::null(); } diff --git a/src/theory/arrays/theory_arrays_rewriter.h b/src/theory/arrays/theory_arrays_rewriter.h index 498266ce3..95a19004e 100644 --- a/src/theory/arrays/theory_arrays_rewriter.h +++ b/src/theory/arrays/theory_arrays_rewriter.h @@ -29,6 +29,9 @@ #include "theory/type_enumerator.h" namespace cvc5 { + +class EagerProofGenerator; + namespace theory { namespace arrays { @@ -43,16 +46,18 @@ static inline Node mkEqNode(Node a, Node b) { class TheoryArraysRewriter : public TheoryRewriter { - /** - * Puts array constant node into normal form. This is so that array constants - * that are distinct nodes are semantically disequal. - */ - static Node normalizeConstant(TNode node); - public: + TheoryArraysRewriter(ProofNodeManager* pnm); + /** Normalize a constant whose index type has cardinality indexCard */ static Node normalizeConstant(TNode node, Cardinality indexCard); + /* Expands the eqrange predicate (eqrange a b i j) to the quantified formula + * (forall ((x T)) + * (=> (and (<= i x) (<= x j)) (= (select a x) (select b x)))). + */ + static Node expandEqRange(TNode node); + RewriteResponse postRewrite(TNode node) override; RewriteResponse preRewrite(TNode node) override; @@ -62,6 +67,14 @@ class TheoryArraysRewriter : public TheoryRewriter static inline void init() {} static inline void shutdown() {} + private: + /** + * Puts array constant node into normal form. This is so that array constants + * that are distinct nodes are semantically disequal. + */ + static Node normalizeConstant(TNode node); + + std::unique_ptr<EagerProofGenerator> d_epg; }; /* class TheoryArraysRewriter */ } // namespace arrays diff --git a/src/theory/bags/theory_bags.cpp b/src/theory/bags/theory_bags.cpp index c2aa9eb1e..580d26c08 100644 --- a/src/theory/bags/theory_bags.cpp +++ b/src/theory/bags/theory_bags.cpp @@ -35,7 +35,7 @@ TheoryBags::TheoryBags(context::Context* c, ProofNodeManager* pnm) : Theory(THEORY_BAGS, c, u, out, valuation, logicInfo, pnm), d_state(c, u, valuation), - d_im(*this, d_state, nullptr), + d_im(*this, d_state, pnm), d_ig(&d_state, &d_im), d_notify(*this, d_im), d_statistics(), diff --git a/src/theory/builtin/proof_checker.cpp b/src/theory/builtin/proof_checker.cpp index dae3922e6..54d1779ec 100644 --- a/src/theory/builtin/proof_checker.cpp +++ b/src/theory/builtin/proof_checker.cpp @@ -53,6 +53,7 @@ void BuiltinProofRuleChecker::registerTo(ProofChecker* pc) pc->registerTrustedChecker(PfRule::TRUST_SUBS, this, 1); pc->registerTrustedChecker(PfRule::TRUST_SUBS_MAP, this, 1); pc->registerTrustedChecker(PfRule::TRUST_SUBS_EQ, this, 3); + pc->registerTrustedChecker(PfRule::THEORY_INFERENCE, this, 3); } Node BuiltinProofRuleChecker::applySubstitutionRewrite( @@ -400,7 +401,8 @@ Node BuiltinProofRuleChecker::checkInternal(PfRule id, || id == PfRule::THEORY_EXPAND_DEF || id == PfRule::WITNESS_AXIOM || id == PfRule::THEORY_LEMMA || id == PfRule::THEORY_REWRITE || id == PfRule::TRUST_REWRITE || id == PfRule::TRUST_SUBS - || id == PfRule::TRUST_SUBS_MAP || id == PfRule::TRUST_SUBS_EQ) + || id == PfRule::TRUST_SUBS_MAP || id == PfRule::TRUST_SUBS_EQ + || id == PfRule::THEORY_INFERENCE) { // "trusted" rules Assert(!args.empty()); diff --git a/src/theory/bv/bitblast/proof_bitblaster.cpp b/src/theory/bv/bitblast/proof_bitblaster.cpp index f714ffda9..43618974b 100644 --- a/src/theory/bv/bitblast/proof_bitblaster.cpp +++ b/src/theory/bv/bitblast/proof_bitblaster.cpp @@ -172,6 +172,11 @@ Node BBProof::getStoredBBAtom(TNode node) return d_bb->getStoredBBAtom(node); } +void BBProof::getBBTerm(TNode node, Bits& bits) const +{ + d_bb->getBBTerm(node, bits); +} + bool BBProof::collectModelValues(TheoryModel* m, const std::set<Node>& relevantTerms) { diff --git a/src/theory/bv/bitblast/proof_bitblaster.h b/src/theory/bv/bitblast/proof_bitblaster.h index f6aa71f21..bc99d27bf 100644 --- a/src/theory/bv/bitblast/proof_bitblaster.h +++ b/src/theory/bv/bitblast/proof_bitblaster.h @@ -43,6 +43,8 @@ class BBProof bool hasBBTerm(TNode node) const; /** Get bit-blasted node stored for atom. */ Node getStoredBBAtom(TNode node); + /** Get bit-blasted bits stored for node. */ + void getBBTerm(TNode node, Bits& bits) const; /** Collect model values for all relevant terms given in 'relevantTerms'. */ bool collectModelValues(TheoryModel* m, const std::set<Node>& relevantTerms); diff --git a/src/theory/bv/bv_solver.h b/src/theory/bv/bv_solver.h index 6ccc6c7c1..c959fb648 100644 --- a/src/theory/bv/bv_solver.h +++ b/src/theory/bv/bv_solver.h @@ -102,6 +102,14 @@ class BVSolver return EqualityStatus::EQUALITY_UNKNOWN; } + /** + * Get the current value of `node`. + * + * The `initialize` flag indicates whether bits should be zero-initialized + * if they don't have a value yet. + */ + virtual Node getValue(TNode node, bool initialize) { return Node::null(); } + /** Called by abstraction preprocessing pass. */ virtual bool applyAbstraction(const std::vector<Node>& assertions, std::vector<Node>& new_assertions) diff --git a/src/theory/bv/bv_solver_bitblast.cpp b/src/theory/bv/bv_solver_bitblast.cpp index 5b70fb3a2..ecd42e4a0 100644 --- a/src/theory/bv/bv_solver_bitblast.cpp +++ b/src/theory/bv/bv_solver_bitblast.cpp @@ -119,8 +119,6 @@ BVSolverBitblast::BVSolverBitblast(TheoryState* s, d_bbInputFacts(s->getSatContext()), d_assumptions(s->getSatContext()), d_assertions(s->getSatContext()), - d_invalidateModelCache(s->getSatContext(), true), - d_inSatMode(s->getSatContext(), false), d_epg(pnm ? new EagerProofGenerator(pnm, s->getUserContext(), "") : nullptr), d_factLiteralCache(s->getSatContext()), @@ -208,12 +206,9 @@ void BVSolverBitblast::postCheck(Theory::Effort level) d_assumptions.push_back(d_factLiteralCache[fact]); } - d_invalidateModelCache.set(true); std::vector<prop::SatLiteral> assumptions(d_assumptions.begin(), d_assumptions.end()); prop::SatValue val = d_satSolver->solve(assumptions); - d_inSatMode = val == prop::SatValue::SAT_VALUE_TRUE; - Debug("bv-bitblast") << "d_inSatMode: " << d_inSatMode << std::endl; if (val == prop::SatValue::SAT_VALUE_FALSE) { @@ -298,7 +293,7 @@ bool BVSolverBitblast::collectModelValues(TheoryModel* m, continue; } - Node value = getValueFromSatSolver(term, true); + Node value = getValue(term, true); Assert(value.isConst()); if (!m->assertEquality(term, value, true)) { @@ -330,27 +325,6 @@ bool BVSolverBitblast::collectModelValues(TheoryModel* m, return true; } -EqualityStatus BVSolverBitblast::getEqualityStatus(TNode a, TNode b) -{ - Debug("bv-bitblast") << "getEqualityStatus on " << a << " and " << b - << std::endl; - if (!d_inSatMode) - { - Debug("bv-bitblast") << EQUALITY_UNKNOWN << std::endl; - return EQUALITY_UNKNOWN; - } - Node value_a = getValue(a); - Node value_b = getValue(b); - - if (value_a == value_b) - { - Debug("bv-bitblast") << EQUALITY_TRUE_IN_MODEL << std::endl; - return EQUALITY_TRUE_IN_MODEL; - } - Debug("bv-bitblast") << EQUALITY_FALSE_IN_MODEL << std::endl; - return EQUALITY_FALSE_IN_MODEL; -} - void BVSolverBitblast::initSatSolver() { switch (options::bvSatSolver()) @@ -372,7 +346,7 @@ void BVSolverBitblast::initSatSolver() "theory::bv::BVSolverBitblast")); } -Node BVSolverBitblast::getValueFromSatSolver(TNode node, bool initialize) +Node BVSolverBitblast::getValue(TNode node, bool initialize) { if (node.isConst()) { @@ -405,76 +379,6 @@ Node BVSolverBitblast::getValueFromSatSolver(TNode node, bool initialize) return utils::mkConst(bits.size(), value); } -Node BVSolverBitblast::getValue(TNode node) -{ - if (d_invalidateModelCache.get()) - { - d_modelCache.clear(); - } - d_invalidateModelCache.set(false); - - std::vector<TNode> visit; - - TNode cur; - visit.push_back(node); - do - { - cur = visit.back(); - visit.pop_back(); - - auto it = d_modelCache.find(cur); - if (it != d_modelCache.end() && !it->second.isNull()) - { - continue; - } - - if (d_bitblaster->hasBBTerm(cur)) - { - Node value = getValueFromSatSolver(cur, false); - if (value.isConst()) - { - d_modelCache[cur] = value; - continue; - } - } - if (Theory::isLeafOf(cur, theory::THEORY_BV)) - { - Node value = getValueFromSatSolver(cur, true); - d_modelCache[cur] = value; - continue; - } - - if (it == d_modelCache.end()) - { - visit.push_back(cur); - d_modelCache.emplace(cur, Node()); - visit.insert(visit.end(), cur.begin(), cur.end()); - } - else if (it->second.isNull()) - { - NodeBuilder nb(cur.getKind()); - if (cur.getMetaKind() == kind::metakind::PARAMETERIZED) - { - nb << cur.getOperator(); - } - - std::unordered_map<Node, Node>::iterator iit; - for (const TNode& child : cur) - { - iit = d_modelCache.find(child); - Assert(iit != d_modelCache.end()); - Assert(iit->second.isConst()); - nb << iit->second; - } - it->second = Rewriter::rewrite(nb.constructNode()); - } - } while (!visit.empty()); - - auto it = d_modelCache.find(node); - Assert(it != d_modelCache.end()); - return it->second; -} - void BVSolverBitblast::handleEagerAtom(TNode fact, bool assertFact) { Assert(fact.getKind() == kind::BITVECTOR_EAGER_ATOM); diff --git a/src/theory/bv/bv_solver_bitblast.h b/src/theory/bv/bv_solver_bitblast.h index 87f3f25cd..3f4ab5025 100644 --- a/src/theory/bv/bv_solver_bitblast.h +++ b/src/theory/bv/bv_solver_bitblast.h @@ -63,31 +63,22 @@ class BVSolverBitblast : public BVSolver std::string identify() const override { return "BVSolverBitblast"; }; - EqualityStatus getEqualityStatus(TNode a, TNode b) override; - void computeRelevantTerms(std::set<Node>& termSet) override; bool collectModelValues(TheoryModel* m, const std::set<Node>& termSet) override; - private: - /** Initialize SAT solver and CNF stream. */ - void initSatSolver(); - /** - * Get value of `node` from SAT solver. + * Get the current value of `node`. * * The `initialize` flag indicates whether bits should be zero-initialized * if they were not bit-blasted yet. */ - Node getValueFromSatSolver(TNode node, bool initialize); + Node getValue(TNode node, bool initialize) override; - /** - * Get the current value of `node`. - * - * Computes the value if `node` was not yet bit-blasted. - */ - Node getValue(TNode node); + private: + /** Initialize SAT solver and CNF stream. */ + void initSatSolver(); /** * Handle BITVECTOR_EAGER_ATOM atoms and assert/assume to CnfStream. @@ -97,14 +88,6 @@ class BVSolverBitblast : public BVSolver */ void handleEagerAtom(TNode fact, bool assertFact); - /** - * Cache for getValue() calls. - * - * Is cleared at the beginning of a getValue() call if the - * `d_invalidateModelCache` flag is set to true. - */ - std::unordered_map<Node, Node> d_modelCache; - /** Bit-blaster used to bit-blast atoms/terms. */ std::unique_ptr<NodeBitblaster> d_bitblaster; @@ -120,7 +103,7 @@ class BVSolverBitblast : public BVSolver /** * Bit-blast queue for facts sent to this solver. * - * Get populated on preNotifyFact(). + * Gets populated on preNotifyFact(). */ context::CDQueue<Node> d_bbFacts; @@ -137,12 +120,6 @@ class BVSolverBitblast : public BVSolver /** Stores the current input assertions. */ context::CDList<Node> d_assertions; - /** Flag indicating whether `d_modelCache` should be invalidated. */ - context::CDO<bool> d_invalidateModelCache; - - /** Indicates whether the last check() call was satisfiable. */ - context::CDO<bool> d_inSatMode; - /** Proof generator that manages proofs for lemmas generated by this class. */ std::unique_ptr<EagerProofGenerator> d_epg; diff --git a/src/theory/bv/bv_solver_bitblast_internal.cpp b/src/theory/bv/bv_solver_bitblast_internal.cpp index bd47cc45e..ef4f3559b 100644 --- a/src/theory/bv/bv_solver_bitblast_internal.cpp +++ b/src/theory/bv/bv_solver_bitblast_internal.cpp @@ -147,6 +147,40 @@ bool BVSolverBitblastInternal::collectModelValues(TheoryModel* m, return d_bitblaster->collectModelValues(m, termSet); } +Node BVSolverBitblastInternal::getValue(TNode node, bool initialize) +{ + if (node.isConst()) + { + return node; + } + + if (!d_bitblaster->hasBBTerm(node)) + { + return initialize ? utils::mkConst(utils::getSize(node), 0u) : Node(); + } + + Valuation& val = d_state.getValuation(); + + std::vector<Node> bits; + d_bitblaster->getBBTerm(node, bits); + Integer value(0), one(1), zero(0), bit; + for (size_t i = 0, size = bits.size(), j = size - 1; i < size; ++i, --j) + { + bool satValue; + if (val.hasSatValue(bits[j], satValue)) + { + bit = satValue ? one : zero; + } + else + { + if (!initialize) return Node(); + bit = zero; + } + value = value * 2 + bit; + } + return utils::mkConst(bits.size(), value); +} + BVProofRuleChecker* BVSolverBitblastInternal::getProofChecker() { return &d_checker; diff --git a/src/theory/bv/bv_solver_bitblast_internal.h b/src/theory/bv/bv_solver_bitblast_internal.h index 8a1886084..1ec3ec1fe 100644 --- a/src/theory/bv/bv_solver_bitblast_internal.h +++ b/src/theory/bv/bv_solver_bitblast_internal.h @@ -42,6 +42,8 @@ class BVSolverBitblastInternal : public BVSolver ProofNodeManager* pnm); ~BVSolverBitblastInternal() = default; + bool needsEqualityEngine(EeSetupInfo& esi) override { return true; } + void preRegisterTerm(TNode n) override {} bool preNotifyFact(TNode atom, @@ -55,6 +57,8 @@ class BVSolverBitblastInternal : public BVSolver bool collectModelValues(TheoryModel* m, const std::set<Node>& termSet) override; + Node getValue(TNode node, bool initialize) override; + /** get the proof checker of this theory */ BVProofRuleChecker* getProofChecker(); diff --git a/src/theory/bv/theory_bv.cpp b/src/theory/bv/theory_bv.cpp index 37881f9b2..547d24b23 100644 --- a/src/theory/bv/theory_bv.cpp +++ b/src/theory/bv/theory_bv.cpp @@ -43,6 +43,7 @@ TheoryBV::TheoryBV(context::Context* c, d_state(c, u, valuation), d_im(*this, d_state, nullptr, "theory::bv::"), d_notify(d_im), + d_invalidateModelCache(c, true), d_stats("theory::bv::") { switch (options::bvSolver()) @@ -158,7 +159,11 @@ void TheoryBV::preRegisterTerm(TNode node) bool TheoryBV::preCheck(Effort e) { return d_internal->preCheck(e); } -void TheoryBV::postCheck(Effort e) { d_internal->postCheck(e); } +void TheoryBV::postCheck(Effort e) +{ + d_invalidateModelCache = true; + d_internal->postCheck(e); +} bool TheoryBV::preNotifyFact( TNode atom, bool pol, TNode fact, bool isPrereg, bool isInternal) @@ -282,7 +287,27 @@ void TheoryBV::presolve() { d_internal->presolve(); } EqualityStatus TheoryBV::getEqualityStatus(TNode a, TNode b) { - return d_internal->getEqualityStatus(a, b); + EqualityStatus status = d_internal->getEqualityStatus(a, b); + + if (status == EqualityStatus::EQUALITY_UNKNOWN) + { + Node value_a = getValue(a); + Node value_b = getValue(b); + + if (value_a.isNull() || value_b.isNull()) + { + return status; + } + + if (value_a == value_b) + { + Debug("theory-bv") << EQUALITY_TRUE_IN_MODEL << std::endl; + return EQUALITY_TRUE_IN_MODEL; + } + Debug("theory-bv") << EQUALITY_FALSE_IN_MODEL << std::endl; + return EQUALITY_FALSE_IN_MODEL; + } + return status; } TrustNode TheoryBV::explain(TNode node) { return d_internal->explain(node); } @@ -303,6 +328,80 @@ bool TheoryBV::applyAbstraction(const std::vector<Node>& assertions, return d_internal->applyAbstraction(assertions, new_assertions); } +Node TheoryBV::getValue(TNode node) +{ + if (d_invalidateModelCache.get()) + { + d_modelCache.clear(); + } + d_invalidateModelCache.set(false); + + std::vector<TNode> visit; + + TNode cur; + visit.push_back(node); + do + { + cur = visit.back(); + visit.pop_back(); + + auto it = d_modelCache.find(cur); + if (it != d_modelCache.end() && !it->second.isNull()) + { + continue; + } + + if (cur.isConst()) + { + d_modelCache[cur] = cur; + continue; + } + + Node value = d_internal->getValue(cur, false); + if (value.isConst()) + { + d_modelCache[cur] = value; + continue; + } + + if (Theory::isLeafOf(cur, theory::THEORY_BV)) + { + value = d_internal->getValue(cur, true); + d_modelCache[cur] = value; + continue; + } + + if (it == d_modelCache.end()) + { + visit.push_back(cur); + d_modelCache.emplace(cur, Node()); + visit.insert(visit.end(), cur.begin(), cur.end()); + } + else if (it->second.isNull()) + { + NodeBuilder nb(cur.getKind()); + if (cur.getMetaKind() == kind::metakind::PARAMETERIZED) + { + nb << cur.getOperator(); + } + + std::unordered_map<Node, Node>::iterator iit; + for (const TNode& child : cur) + { + iit = d_modelCache.find(child); + Assert(iit != d_modelCache.end()); + Assert(iit->second.isConst()); + nb << iit->second; + } + it->second = Rewriter::rewrite(nb.constructNode()); + } + } while (!visit.empty()); + + auto it = d_modelCache.find(node); + Assert(it != d_modelCache.end()); + return it->second; +} + TheoryBV::Statistics::Statistics(const std::string& name) : d_solveSubstitutions( smtStatisticsRegistry().registerInt(name + "NumSolveSubstitutions")) diff --git a/src/theory/bv/theory_bv.h b/src/theory/bv/theory_bv.h index f2d6bb47e..da44d7022 100644 --- a/src/theory/bv/theory_bv.h +++ b/src/theory/bv/theory_bv.h @@ -109,6 +109,8 @@ class TheoryBV : public Theory private: void notifySharedTerm(TNode t) override; + Node getValue(TNode node); + /** Internal BV solver. */ std::unique_ptr<BVSolver> d_internal; @@ -124,6 +126,17 @@ class TheoryBV : public Theory /** The notify class for equality engine. */ TheoryEqNotifyClass d_notify; + /** Flag indicating whether `d_modelCache` should be invalidated. */ + context::CDO<bool> d_invalidateModelCache; + + /** + * Cache for getValue() calls. + * + * Is cleared at the beginning of a getValue() call if the + * `d_invalidateModelCache` flag is set to true. + */ + std::unordered_map<Node, Node> d_modelCache; + /** TheoryBV statistics. */ struct Statistics { diff --git a/src/theory/datatypes/inference_manager.cpp b/src/theory/datatypes/inference_manager.cpp index 014255a7c..ccd0e6853 100644 --- a/src/theory/datatypes/inference_manager.cpp +++ b/src/theory/datatypes/inference_manager.cpp @@ -69,6 +69,13 @@ void InferenceManager::addPendingInference(Node conc, void InferenceManager::process() { + // if we are in conflict, immediately reset and clear pending + if (d_theoryState.isInConflict()) + { + reset(); + clearPending(); + return; + } // process pending lemmas, used infrequently, only for definitional lemmas doPendingLemmas(); // now process the pending facts diff --git a/src/theory/datatypes/sygus_datatype_utils.cpp b/src/theory/datatypes/sygus_datatype_utils.cpp index 7e5099d55..72ddd7b0e 100644 --- a/src/theory/datatypes/sygus_datatype_utils.cpp +++ b/src/theory/datatypes/sygus_datatype_utils.cpp @@ -718,6 +718,24 @@ TypeNode substituteAndGeneralizeSygusType(TypeNode sdt, return sdtS; } +unsigned getSygusTermSize(Node n) +{ + if (n.getKind() != APPLY_CONSTRUCTOR) + { + return 0; + } + unsigned sum = 0; + for (const Node& nc : n) + { + sum += getSygusTermSize(nc); + } + const DType& dt = datatypeOf(n.getOperator()); + int cindex = indexOf(n.getOperator()); + Assert(cindex >= 0 && static_cast<size_t>(cindex) < dt.getNumConstructors()); + unsigned weight = dt[cindex].getWeight(); + return weight + sum; +} + } // namespace utils } // namespace datatypes } // namespace theory diff --git a/src/theory/datatypes/sygus_datatype_utils.h b/src/theory/datatypes/sygus_datatype_utils.h index 6f3791a4d..35672434c 100644 --- a/src/theory/datatypes/sygus_datatype_utils.h +++ b/src/theory/datatypes/sygus_datatype_utils.h @@ -227,6 +227,11 @@ TypeNode substituteAndGeneralizeSygusType(TypeNode sdt, const std::vector<Node>& syms, const std::vector<Node>& vars); +/** + * Get SyGuS term size, which is based on the weight of constructor applications + * in n. + */ +unsigned getSygusTermSize(Node n); // ------------------------ end sygus utils } // namespace utils diff --git a/src/theory/datatypes/sygus_extension.cpp b/src/theory/datatypes/sygus_extension.cpp index ee96b95d8..63af57592 100644 --- a/src/theory/datatypes/sygus_extension.cpp +++ b/src/theory/datatypes/sygus_extension.cpp @@ -1040,7 +1040,7 @@ Node SygusExtension::registerSearchValue(Node a, Node bvr = d_tds->getExtRewriter()->extendedRewrite(bv); Trace("sygus-sb-debug") << " ......search value rewrites to " << bvr << std::endl; Trace("dt-sygus") << " * DT builtin : " << n << " -> " << bvr << std::endl; - unsigned sz = d_tds->getSygusTermSize( nv ); + unsigned sz = utils::getSygusTermSize(nv); if( d_tds->involvesDivByZero( bvr ) ){ quantifiers::DivByZeroSygusInvarianceTest dbzet; Trace("sygus-sb-mexp-debug") << "Minimize explanation for div-by-zero in " @@ -1143,7 +1143,7 @@ Node SygusExtension::registerSearchValue(Node a, } Trace("sygus-sb-exc") << std::endl; } - Assert(d_tds->getSygusTermSize(bad_val) == sz); + Assert(utils::getSygusTermSize(bad_val) == sz); // generalize the explanation for why the analog of bad_val // is equivalent to bvr @@ -1177,7 +1177,7 @@ void SygusExtension::registerSymBreakLemmaForValue( { TypeNode tn = val.getType(); Node x = getFreeVar(tn); - unsigned sz = d_tds->getSygusTermSize(val); + unsigned sz = utils::getSygusTermSize(val); std::vector<Node> exp; d_tds->getExplain()->getExplanationFor(x, val, exp, et, valr, var_count, sz); Node lem = diff --git a/src/theory/datatypes/theory_datatypes.cpp b/src/theory/datatypes/theory_datatypes.cpp index 53f128286..50c955d48 100644 --- a/src/theory/datatypes/theory_datatypes.cpp +++ b/src/theory/datatypes/theory_datatypes.cpp @@ -168,13 +168,17 @@ TNode TheoryDatatypes::getEqcConstructor( TNode r ) { bool TheoryDatatypes::preCheck(Effort level) { + Trace("datatypes-check") << "TheoryDatatypes::preCheck: " << level + << std::endl; + d_im.process(); d_im.reset(); - d_im.clearPending(); return false; } void TheoryDatatypes::postCheck(Effort level) { + Trace("datatypes-check") << "TheoryDatatypes::postCheck: " << level + << std::endl; // Apply any last pending inferences, which may occur if the last processed // fact was an internal one and triggered further internal inferences. d_im.process(); @@ -182,7 +186,6 @@ void TheoryDatatypes::postCheck(Effort level) { Assert(d_sygusExtension != nullptr); d_sygusExtension->check(); - return; } else if (level == EFFORT_FULL && !d_state.isInConflict() && !d_im.hasSentLemma() && !d_valuation.needCheck()) @@ -532,18 +535,19 @@ void TheoryDatatypes::eqNotifyNewClass(TNode t){ void TheoryDatatypes::eqNotifyMerge(TNode t1, TNode t2) { if( t1.getType().isDatatype() ){ - Trace("datatypes-debug") + Trace("datatypes-merge") << "NotifyMerge : " << t1 << " " << t2 << std::endl; - merge(t1,t2); + merge(t1, t2); } } void TheoryDatatypes::merge( Node t1, Node t2 ){ if (!d_state.isInConflict()) { + Trace("datatypes-merge") << "Merge " << t1 << " " << t2 << std::endl; + Assert(areEqual(t1, t2)); TNode trep1 = t1; TNode trep2 = t2; - Trace("datatypes-debug") << "Merge " << t1 << " " << t2 << std::endl; EqcInfo* eqc2 = getOrMakeEqcInfo( t2 ); if( eqc2 ){ bool checkInst = false; @@ -575,6 +579,7 @@ void TheoryDatatypes::merge( Node t1, Node t2 ){ } else { + Assert(areEqual(cons1, cons2)); //do unification for( int i=0; i<(int)cons1.getNumChildren(); i++ ) { if( !areEqual( cons1[i], cons2[i] ) ){ @@ -1852,38 +1857,16 @@ void TheoryDatatypes::computeRelevantTerms(std::set<Node>& termSet) Trace("dt-cmi") << "Have " << termSet.size() << " relevant terms..." << std::endl; - //also include non-singleton equivalence classes TODO : revisit this + //also include non-singleton dt equivalence classes TODO : revisit this eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(d_equalityEngine); while( !eqcs_i.isFinished() ){ TNode r = (*eqcs_i); - bool addedFirst = false; - Node first; - TypeNode rtn = r.getType(); - if (!rtn.isBoolean()) + if (r.getType().isDatatype()) { eq::EqClassIterator eqc_i = eq::EqClassIterator(r, d_equalityEngine); while (!eqc_i.isFinished()) { - TNode n = (*eqc_i); - if (first.isNull()) - { - first = n; - // always include all datatypes - if (rtn.isDatatype()) - { - addedFirst = true; - termSet.insert(n); - } - } - else - { - if (!addedFirst) - { - addedFirst = true; - termSet.insert(first); - } - termSet.insert(n); - } + termSet.insert(*eqc_i); ++eqc_i; } } diff --git a/src/theory/datatypes/theory_datatypes_utils.h b/src/theory/datatypes/theory_datatypes_utils.h index 898ee3491..705e867b9 100644 --- a/src/theory/datatypes/theory_datatypes_utils.h +++ b/src/theory/datatypes/theory_datatypes_utils.h @@ -15,8 +15,8 @@ #include "cvc5_private.h" -#ifndef CVC5__THEORY__STRINGS__THEORY_DATATYPES_UTILS_H -#define CVC5__THEORY__STRINGS__THEORY_DATATYPES_UTILS_H +#ifndef CVC5__THEORY__DATATYPES__THEORY_DATATYPES_UTILS_H +#define CVC5__THEORY__DATATYPES__THEORY_DATATYPES_UTILS_H #include <vector> diff --git a/src/theory/ee_manager_central.cpp b/src/theory/ee_manager_central.cpp new file mode 100644 index 000000000..f98bce970 --- /dev/null +++ b/src/theory/ee_manager_central.cpp @@ -0,0 +1,306 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Equality engine manager for central equality engine architecture + */ + +#include "theory/ee_manager_central.h" + +#include "theory/quantifiers_engine.h" +#include "theory/shared_solver.h" +#include "theory/theory_engine.h" +#include "theory/theory_state.h" + +namespace cvc5 { +namespace theory { + +EqEngineManagerCentral::EqEngineManagerCentral(TheoryEngine& te, + SharedSolver& shs, + ProofNodeManager* pnm) + : EqEngineManager(te, shs), + d_masterEENotify(nullptr), + d_masterEqualityEngine(nullptr), + d_centralEENotify(*this), + d_centralEqualityEngine( + d_centralEENotify, te.getSatContext(), "central::ee", true) +{ + for (TheoryId theoryId = theory::THEORY_FIRST; + theoryId != theory::THEORY_LAST; + ++theoryId) + { + d_theoryNotify[theoryId] = nullptr; + } + if (pnm != nullptr) + { + d_centralPfee.reset(new eq::ProofEqEngine(d_te.getSatContext(), + d_te.getUserContext(), + d_centralEqualityEngine, + pnm)); + d_centralEqualityEngine.setProofEqualityEngine(d_centralPfee.get()); + } +} + +EqEngineManagerCentral::~EqEngineManagerCentral() {} + +void EqEngineManagerCentral::initializeTheories() +{ + context::Context* c = d_te.getSatContext(); + // initialize the shared solver + EeSetupInfo esis; + if (d_sharedSolver.needsEqualityEngine(esis)) + { + // the shared solver uses central equality engine + d_sharedSolver.setEqualityEngine(&d_centralEqualityEngine); + } + else + { + Unreachable() << "Expected shared solver to use equality engine"; + } + // whether to use master equality engine as central + bool masterEqToCentral = true; + // setup info for each theory + std::map<TheoryId, EeSetupInfo> esiMap; + // set of theories that need equality engines + std::unordered_set<TheoryId> eeTheories; + const LogicInfo& logicInfo = d_te.getLogicInfo(); + for (TheoryId theoryId = theory::THEORY_FIRST; + theoryId != theory::THEORY_LAST; + ++theoryId) + { + Theory* t = d_te.theoryOf(theoryId); + if (t == nullptr) + { + // theory not active, skip + continue; + } + if (!t->needsEqualityEngine(esiMap[theoryId])) + { + // theory said it doesn't need an equality engine, skip + continue; + } + // otherwise add it to the set of equality engine theories + eeTheories.insert(theoryId); + // if the logic has a theory that does not use central equality engine, + // we can't use the central equality engine for the master equality + // engine + if (theoryId != THEORY_QUANTIFIERS && logicInfo.isTheoryEnabled(theoryId) + && !Theory::usesCentralEqualityEngine(theoryId)) + { + Trace("ee-central") << "Must use separate master equality engine due to " + << theoryId << std::endl; + masterEqToCentral = false; + } + } + + // initialize the master equality engine, which may be the central equality + // engine + if (logicInfo.isQuantified()) + { + // construct the master equality engine + Assert(d_masterEqualityEngine == nullptr); + QuantifiersEngine* qe = d_te.getQuantifiersEngine(); + Assert(qe != nullptr); + d_masterEENotify.reset(new quantifiers::MasterNotifyClass(qe)); + if (!masterEqToCentral) + { + d_masterEqualityEngineAlloc.reset(new eq::EqualityEngine( + *d_masterEENotify.get(), d_te.getSatContext(), "master::ee", false)); + d_masterEqualityEngine = d_masterEqualityEngineAlloc.get(); + } + else + { + Trace("ee-central") + << "Master equality engine is the central equality engine" + << std::endl; + d_masterEqualityEngine = &d_centralEqualityEngine; + d_centralEENotify.d_newClassNotify.push_back(d_masterEENotify.get()); + } + } + + // allocate equality engines per theory + for (TheoryId theoryId = theory::THEORY_FIRST; + theoryId != theory::THEORY_LAST; + ++theoryId) + { + Trace("ee-central") << "Setup equality engine for " << theoryId + << std::endl; + // always allocate an object in d_einfo here + EeTheoryInfo& eet = d_einfo[theoryId]; + if (eeTheories.find(theoryId) == eeTheories.end()) + { + Trace("ee-central") << "..." << theoryId << " does not need ee" + << std::endl; + continue; + } + Theory* t = d_te.theoryOf(theoryId); + Assert(t != nullptr); + Assert(esiMap.find(theoryId) != esiMap.end()); + EeSetupInfo& esi = esiMap[theoryId]; + if (esi.d_useMaster) + { + Trace("ee-central") << "...uses master" << std::endl; + // the theory said it wants to use the master equality engine + eet.d_usedEe = d_masterEqualityEngine; + continue; + } + // set the notify + eq::EqualityEngineNotify* notify = esi.d_notify; + d_theoryNotify[theoryId] = notify; + // split on whether integrated, or whether asked for master + if (t->usesCentralEqualityEngine()) + { + Trace("ee-central") << "...uses central" << std::endl; + // the theory uses the central equality engine + eet.d_usedEe = &d_centralEqualityEngine; + if (logicInfo.isTheoryEnabled(theoryId)) + { + // add to vectors for the kinds of notifications + if (esi.needsNotifyNewClass()) + { + d_centralEENotify.d_newClassNotify.push_back(notify); + } + if (esi.needsNotifyMerge()) + { + d_centralEENotify.d_mergeNotify.push_back(notify); + } + if (esi.needsNotifyDisequal()) + { + d_centralEENotify.d_disequalNotify.push_back(notify); + } + } + continue; + } + Trace("ee-central") << "...uses new" << std::endl; + eet.d_allocEe.reset(allocateEqualityEngine(esi, c)); + // the theory uses the equality engine + eet.d_usedEe = eet.d_allocEe.get(); + if (!masterEqToCentral) + { + // set the master equality engine of the theory's equality engine + eet.d_allocEe->setMasterEqualityEngine(d_masterEqualityEngine); + } + } + + // set the master equality engine of the theory's equality engine + if (!masterEqToCentral) + { + d_centralEqualityEngine.setMasterEqualityEngine(d_masterEqualityEngine); + } +} + +void EqEngineManagerCentral::notifyBuildingModel() {} + +EqEngineManagerCentral::CentralNotifyClass::CentralNotifyClass( + EqEngineManagerCentral& eemc) + : d_eemc(eemc), d_mNotify(nullptr), d_quantEngine(nullptr) +{ +} + +bool EqEngineManagerCentral::CentralNotifyClass::eqNotifyTriggerPredicate( + TNode predicate, bool value) +{ + Trace("eem-central") << "eqNotifyTriggerPredicate: " << predicate + << std::endl; + return d_eemc.eqNotifyTriggerPredicate(predicate, value); +} + +bool EqEngineManagerCentral::CentralNotifyClass::eqNotifyTriggerTermEquality( + TheoryId tag, TNode t1, TNode t2, bool value) +{ + Trace("eem-central") << "eqNotifyTriggerTermEquality: " << t1 << " " << t2 + << value << ", tag = " << tag << std::endl; + return d_eemc.eqNotifyTriggerTermEquality(tag, t1, t2, value); +} + +void EqEngineManagerCentral::CentralNotifyClass::eqNotifyConstantTermMerge( + TNode t1, TNode t2) +{ + Trace("eem-central") << "eqNotifyConstantTermMerge: " << t1 << " " << t2 + << std::endl; + d_eemc.eqNotifyConstantTermMerge(t1, t2); +} + +void EqEngineManagerCentral::CentralNotifyClass::eqNotifyNewClass(TNode t) +{ + Trace("eem-central") << "...eqNotifyNewClass " << t << std::endl; + // notify all theories that have new equivalence class notifications + for (eq::EqualityEngineNotify* notify : d_newClassNotify) + { + notify->eqNotifyNewClass(t); + } +} + +void EqEngineManagerCentral::CentralNotifyClass::eqNotifyMerge(TNode t1, + TNode t2) +{ + Trace("eem-central") << "...eqNotifyMerge " << t1 << ", " << t2 << std::endl; + // notify all theories that have merge notifications + for (eq::EqualityEngineNotify* notify : d_mergeNotify) + { + notify->eqNotifyMerge(t1, t2); + } +} + +void EqEngineManagerCentral::CentralNotifyClass::eqNotifyDisequal(TNode t1, + TNode t2, + TNode reason) +{ + Trace("eem-central") << "...eqNotifyDisequal " << t1 << ", " << t2 + << std::endl; + // notify all theories that have disequal notifications + for (eq::EqualityEngineNotify* notify : d_disequalNotify) + { + notify->eqNotifyDisequal(t1, t2, reason); + } +} + +bool EqEngineManagerCentral::eqNotifyTriggerPredicate(TNode predicate, + bool value) +{ + // always propagate with the shared solver + Trace("eem-central") << "...propagate " << predicate << ", " << value + << " with shared solver" << std::endl; + return d_sharedSolver.propagateLit(predicate, value); +} + +bool EqEngineManagerCentral::eqNotifyTriggerTermEquality(TheoryId tag, + TNode a, + TNode b, + bool value) +{ + // propagate to theory engine + bool ok = d_sharedSolver.propagateLit(a.eqNode(b), value); + if (!ok) + { + return false; + } + // no need to propagate shared term equalities to the UF theory + if (tag == THEORY_UF) + { + return true; + } + // propagate shared equality + return d_sharedSolver.propagateSharedEquality(tag, a, b, value); +} + +void EqEngineManagerCentral::eqNotifyConstantTermMerge(TNode t1, TNode t2) +{ + Node lit = t1.eqNode(t2); + Node conflict = d_centralEqualityEngine.mkExplainLit(lit); + Trace("eem-central") << "...explained conflict of " << lit << " ... " + << conflict << std::endl; + d_sharedSolver.sendConflict(TrustNode::mkTrustConflict(conflict)); + return; +} + +} // namespace theory +} // namespace cvc5 diff --git a/src/theory/ee_manager_central.h b/src/theory/ee_manager_central.h new file mode 100644 index 000000000..eb13b7820 --- /dev/null +++ b/src/theory/ee_manager_central.h @@ -0,0 +1,130 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Equality engine manager for central equality engine architecture + */ + +#include "cvc5_private.h" + +#ifndef CVC5__THEORY__EE_MANAGER_CENTRAL__H +#define CVC5__THEORY__EE_MANAGER_CENTRAL__H + +#include <map> +#include <memory> + +#include "theory/ee_manager.h" +#include "theory/quantifiers/master_eq_notify.h" +#include "theory/uf/equality_engine.h" + +namespace cvc5 { +namespace theory { + +/** + * The (central) equality engine manager. This encapsulates an architecture + * in which all applicable theories use a single central equality engine. + * + * This class is not responsible for actually initializing equality engines in + * theories (since this class does not have access to the internals of Theory). + * Instead, it is only responsible for the construction of the equality + * engine objects themselves. TheoryEngine is responsible for querying this + * class during finishInit() to determine the equality engines to pass to each + * theories based on getEeTheoryInfo. + * + * It also may allocate a "master" equality engine, which is intuitively the + * equality engine of the theory of quantifiers. If all theories use the + * central equality engine, then the master equality engine is the same as the + * central equality engine. + * + * The theories that use central equality engine are determined by + * Theory::usesCentralEqualityEngine. + * + * The main idea behind this class is to use a notification class on the + * central equality engine which dispatches *multiple* notifications to the + * theories that use the central equality engine. + */ +class EqEngineManagerCentral : public EqEngineManager +{ + public: + EqEngineManagerCentral(TheoryEngine& te, + SharedSolver& shs, + ProofNodeManager* pnm); + ~EqEngineManagerCentral(); + /** + * Initialize theories. This method allocates unique equality engines + * per theories and connects them to a master equality engine. + */ + void initializeTheories() override; + /** Notify this class that we are building the model. */ + void notifyBuildingModel(); + + private: + /** + * Notify class for central equality engine. This class dispatches + * notifications from the central equality engine to the appropriate + * theory(s). + */ + class CentralNotifyClass : public theory::eq::EqualityEngineNotify + { + public: + CentralNotifyClass(EqEngineManagerCentral& eemc); + bool eqNotifyTriggerPredicate(TNode predicate, bool value) override; + bool eqNotifyTriggerTermEquality(TheoryId tag, + TNode t1, + TNode t2, + bool value) override; + void eqNotifyConstantTermMerge(TNode t1, TNode t2) override; + void eqNotifyNewClass(TNode t) override; + void eqNotifyMerge(TNode t1, TNode t2) override; + void eqNotifyDisequal(TNode t1, TNode t2, TNode reason) override; + /** Parent */ + EqEngineManagerCentral& d_eemc; + /** List of notify classes that need new class notification */ + std::vector<eq::EqualityEngineNotify*> d_newClassNotify; + /** List of notify classes that need merge notification */ + std::vector<eq::EqualityEngineNotify*> d_mergeNotify; + /** List of notify classes that need disequality notification */ + std::vector<eq::EqualityEngineNotify*> d_disequalNotify; + /** The model notify class */ + eq::EqualityEngineNotify* d_mNotify; + /** The quantifiers engine */ + QuantifiersEngine* d_quantEngine; + }; + /** Notification when predicate gets value in central equality engine */ + bool eqNotifyTriggerPredicate(TNode predicate, bool value); + bool eqNotifyTriggerTermEquality(TheoryId tag, + TNode t1, + TNode t2, + bool value); + /** Notification when constants are merged in central equality engine */ + void eqNotifyConstantTermMerge(TNode t1, TNode t2); + /** The master equality engine notify class */ + std::unique_ptr<quantifiers::MasterNotifyClass> d_masterEENotify; + /** The master equality engine. */ + eq::EqualityEngine* d_masterEqualityEngine; + /** The master equality engine, if we allocated it */ + std::unique_ptr<eq::EqualityEngine> d_masterEqualityEngineAlloc; + /** The central equality engine notify class */ + CentralNotifyClass d_centralEENotify; + /** The central equality engine. */ + eq::EqualityEngine d_centralEqualityEngine; + /** The proof equality engine for the central equality engine */ + std::unique_ptr<eq::ProofEqEngine> d_centralPfee; + /** + * A table of from theory IDs to notify classes. + */ + eq::EqualityEngineNotify* d_theoryNotify[theory::THEORY_LAST]; +}; + +} // namespace theory +} // namespace cvc5 + +#endif /* CVC5__THEORY__EE_MANAGER_CENTRAL__H */ diff --git a/src/theory/fp/fp_converter.cpp b/src/theory/fp/fp_converter.cpp index e309ab2e4..f6f1f3bb3 100644 --- a/src/theory/fp/fp_converter.cpp +++ b/src/theory/fp/fp_converter.cpp @@ -1296,15 +1296,18 @@ Node FpConverter::getValue(Valuation &val, TNode var) if (t.isRoundingMode()) { rmMap::const_iterator i(d_rmMap.find(var)); - - Assert(i != d_rmMap.end()) - << "Asking for the value of an unregistered expression"; + if (i == d_rmMap.end()) + { + return Node::null(); + } return rmToNode((*i).second); } - fpMap::const_iterator i(d_fpMap.find(var)); - Assert(i != d_fpMap.end()) - << "Asking for the value of an unregistered expression"; + fpMap::const_iterator i(d_fpMap.find(var)); + if (i == d_fpMap.end()) + { + return Node::null(); + } return ufToNode(fpt(t), (*i).second); } diff --git a/src/theory/fp/fp_converter.h b/src/theory/fp/fp_converter.h index 9900c2987..d589eff60 100644 --- a/src/theory/fp/fp_converter.h +++ b/src/theory/fp/fp_converter.h @@ -300,7 +300,10 @@ class FpConverter /** Adds a node to the conversion, returns the converted node */ Node convert(TNode); - /** Gives the node representing the value of a given variable */ + /** + * Gives the node representing the value of a word-blasted variable. + * Returns a null node if it has not been word-blasted. + */ Node getValue(Valuation&, TNode); context::CDList<Node> d_additionalAssertions; diff --git a/src/theory/fp/theory_fp.cpp b/src/theory/fp/theory_fp.cpp index 1c8f10f77..9b5ac66d3 100644 --- a/src/theory/fp/theory_fp.cpp +++ b/src/theory/fp/theory_fp.cpp @@ -76,12 +76,13 @@ TheoryFp::TheoryFp(context::Context* c, d_abstractionMap(u), d_rewriter(u), d_state(c, u, valuation), - d_im(*this, d_state, pnm, "theory::fp::", false) + d_im(*this, d_state, pnm, "theory::fp::", false), + d_wbFactsCache(u) { // indicate we are using the default theory state and inference manager d_theoryState = &d_state; d_inferManager = &d_im; -} /* TheoryFp::TheoryFp() */ +} TheoryRewriter* TheoryFp::getTheoryRewriter() { return &d_rewriter; } @@ -304,6 +305,9 @@ bool TheoryFp::refineAbstraction(TheoryModel *m, TNode abstract, TNode concrete) Node floatValue = m->getValue(concrete[0]); Node undefValue = m->getValue(concrete[1]); + Assert(!abstractValue.isNull()); + Assert(!floatValue.isNull()); + Assert(!undefValue.isNull()); Assert(abstractValue.isConst()); Assert(floatValue.isConst()); Assert(undefValue.isConst()); @@ -413,6 +417,9 @@ bool TheoryFp::refineAbstraction(TheoryModel *m, TNode abstract, TNode concrete) Node rmValue = m->getValue(concrete[0]); Node realValue = m->getValue(concrete[1]); + Assert(!abstractValue.isNull()); + Assert(!rmValue.isNull()); + Assert(!realValue.isNull()); Assert(abstractValue.isConst()); Assert(rmValue.isConst()); Assert(realValue.isConst()); @@ -509,12 +516,16 @@ bool TheoryFp::refineAbstraction(TheoryModel *m, TNode abstract, TNode concrete) return false; } -void TheoryFp::convertAndEquateTerm(TNode node) { +void TheoryFp::convertAndEquateTerm(TNode node) +{ Trace("fp-convertTerm") << "TheoryFp::convertTerm(): " << node << std::endl; - size_t oldAdditionalAssertions = d_conv->d_additionalAssertions.size(); + + size_t oldSize = d_conv->d_additionalAssertions.size(); Node converted(d_conv->convert(node)); + size_t newSize = d_conv->d_additionalAssertions.size(); + if (converted != node) { Debug("fp-convertTerm") << "TheoryFp::convertTerm(): before " << node << std::endl; @@ -522,12 +533,11 @@ void TheoryFp::convertAndEquateTerm(TNode node) { << "TheoryFp::convertTerm(): after " << converted << std::endl; } - size_t newAdditionalAssertions = d_conv->d_additionalAssertions.size(); - Assert(oldAdditionalAssertions <= newAdditionalAssertions); + Assert(oldSize <= newSize); - while (oldAdditionalAssertions < newAdditionalAssertions) + while (oldSize < newSize) { - Node addA = d_conv->d_additionalAssertions[oldAdditionalAssertions]; + Node addA = d_conv->d_additionalAssertions[oldSize]; Debug("fp-convertTerm") << "TheoryFp::convertTerm(): additional assertion " << addA << std::endl; @@ -538,7 +548,7 @@ void TheoryFp::convertAndEquateTerm(TNode node) { nm->mkNode(kind::EQUAL, addA, nm->mkConst(::cvc5::BitVector(1U, 1U))), InferenceId::FP_EQUATE_TERM); - ++oldAdditionalAssertions; + ++oldSize; } // Equate the floating-point atom and the converted one. @@ -578,10 +588,12 @@ void TheoryFp::convertAndEquateTerm(TNode node) { return; } -void TheoryFp::registerTerm(TNode node) { +void TheoryFp::registerTerm(TNode node) +{ Trace("fp-registerTerm") << "TheoryFp::registerTerm(): " << node << std::endl; - if (!isRegistered(node)) { + if (!isRegistered(node)) + { Kind k = node.getKind(); Assert(k != kind::FLOATINGPOINT_TO_FP_GENERIC && k != kind::FLOATINGPOINT_SUB && k != kind::FLOATINGPOINT_EQ @@ -646,14 +658,19 @@ void TheoryFp::registerTerm(TNode node) { InferenceId::FP_REGISTER_TERM); } - // Use symfpu to produce an equivalent bit-vector statement - convertAndEquateTerm(node); + /* When not word-blasting lazier, we word-blast every term on + * registration. */ + if (!options::fpLazyWb()) + { + convertAndEquateTerm(node); + } } return; } -bool TheoryFp::isRegistered(TNode node) { - return !(d_registeredTerms.find(node) == d_registeredTerms.end()); +bool TheoryFp::isRegistered(TNode node) +{ + return d_registeredTerms.find(node) != d_registeredTerms.end(); } void TheoryFp::preRegisterTerm(TNode node) @@ -714,7 +731,7 @@ bool TheoryFp::needsCheckLastEffort() void TheoryFp::postCheck(Effort level) { - // Resolve the abstractions for the conversion lemmas + /* Resolve the abstractions for the conversion lemmas */ if (level == EFFORT_LAST_CALL) { Trace("fp") << "TheoryFp::check(): checking abstractions" << std::endl; @@ -739,6 +756,13 @@ void TheoryFp::postCheck(Effort level) bool TheoryFp::preNotifyFact( TNode atom, bool pol, TNode fact, bool isPrereg, bool isInternal) { + /* Word-blast lazier if configured. */ + if (options::fpLazyWb() && d_wbFactsCache.find(atom) == d_wbFactsCache.end()) + { + d_wbFactsCache.insert(atom); + convertAndEquateTerm(atom); + } + if (atom.getKind() == kind::EQUAL) { Assert(!(atom[0].getType().isFloatingPoint() @@ -763,6 +787,16 @@ bool TheoryFp::preNotifyFact( return false; } +void TheoryFp::notifySharedTerm(TNode n) +{ + /* Word-blast lazier if configured. */ + if (options::fpLazyWb() && d_wbFactsCache.find(n) == d_wbFactsCache.end()) + { + d_wbFactsCache.insert(n); + convertAndEquateTerm(n); + } +} + TrustNode TheoryFp::explain(TNode n) { Trace("fp") << "TheoryFp::explain(): explain " << n << std::endl; @@ -846,7 +880,10 @@ bool TheoryFp::collectModelValues(TheoryModel* m, << "TheoryFp::collectModelInfo(): relevantVariable " << node << std::endl; - if (!m->assertEquality(node, d_conv->getValue(d_valuation, node), true)) + Node converted = d_conv->getValue(d_valuation, node); + // We only assign the value if the FpConverter actually has one, that is, + // if FpConverter::getValue() does not return a null node. + if (!converted.isNull() && !m->assertEquality(node, converted, true)) { return false; } diff --git a/src/theory/fp/theory_fp.h b/src/theory/fp/theory_fp.h index ada9b39d0..14779cc3d 100644 --- a/src/theory/fp/theory_fp.h +++ b/src/theory/fp/theory_fp.h @@ -114,6 +114,8 @@ class TheoryFp : public Theory }; friend NotifyClass; + void notifySharedTerm(TNode n) override; + NotifyClass d_notification; /** General utility. */ @@ -148,18 +150,19 @@ class TheoryFp : public Theory Node abstractFloatToReal(Node); private: - ConversionAbstractionMap d_realToFloatMap; ConversionAbstractionMap d_floatToRealMap; AbstractionMap d_abstractionMap; // abstract -> original /** The theory rewriter for this theory. */ TheoryFpRewriter d_rewriter; - /** A (default) theory state object */ + /** A (default) theory state object. */ TheoryState d_state; - /** A (default) inference manager */ + /** A (default) inference manager. */ TheoryInferenceManager d_im; -}; /* class TheoryFp */ + /** Cache of word-blasted facts. */ + context::CDHashSet<Node> d_wbFactsCache; +}; } // namespace fp } // namespace theory diff --git a/src/theory/inference_id.cpp b/src/theory/inference_id.cpp index a26147f09..c0700c06e 100644 --- a/src/theory/inference_id.cpp +++ b/src/theory/inference_id.cpp @@ -16,6 +16,7 @@ #include "theory/inference_id.h" #include <iostream> +#include "util/rational.h" namespace cvc5 { namespace theory { @@ -243,9 +244,11 @@ const char* toString(InferenceId i) case InferenceId::SEP_DISTINCT_REF: return "SEP_DISTINCT_REF"; case InferenceId::SEP_REF_BOUND: return "SEP_REF_BOUND"; + case InferenceId::SETS_CG_SPLIT: return "SETS_CG_SPLIT"; case InferenceId::SETS_COMPREHENSION: return "SETS_COMPREHENSION"; case InferenceId::SETS_DEQ: return "SETS_DEQ"; case InferenceId::SETS_DOWN_CLOSURE: return "SETS_DOWN_CLOSURE"; + case InferenceId::SETS_EQ_CONFLICT: return "SETS_EQ_CONFLICT"; case InferenceId::SETS_EQ_MEM: return "SETS_EQ_MEM"; case InferenceId::SETS_EQ_MEM_CONFLICT: return "SETS_EQ_MEM_CONFLICT"; case InferenceId::SETS_MEM_EQ: return "SETS_MEM_EQ"; @@ -393,5 +396,10 @@ std::ostream& operator<<(std::ostream& out, InferenceId i) return out; } +Node mkInferenceIdNode(InferenceId i) +{ + return NodeManager::currentNM()->mkConst(Rational(static_cast<uint32_t>(i))); +} + } // namespace theory } // namespace cvc5 diff --git a/src/theory/inference_id.h b/src/theory/inference_id.h index f32c80b68..b3be59c5c 100644 --- a/src/theory/inference_id.h +++ b/src/theory/inference_id.h @@ -20,6 +20,8 @@ #include <iosfwd> +#include "expr/node.h" + namespace cvc5 { namespace theory { @@ -378,9 +380,13 @@ enum class InferenceId // ---------------------------------- sets theory //-------------------- sets core solver + // split when computing care graph + SETS_CG_SPLIT, SETS_COMPREHENSION, SETS_DEQ, SETS_DOWN_CLOSURE, + // conflict when two singleton/emptyset terms merge + SETS_EQ_CONFLICT, SETS_EQ_MEM, SETS_EQ_MEM_CONFLICT, SETS_MEM_EQ, @@ -782,6 +788,9 @@ const char* toString(InferenceId i); */ std::ostream& operator<<(std::ostream& out, InferenceId i); +/** Make node from inference id */ +Node mkInferenceIdNode(InferenceId i); + } // namespace theory } // namespace cvc5 diff --git a/src/theory/quantifiers/candidate_rewrite_database.cpp b/src/theory/quantifiers/candidate_rewrite_database.cpp index 789a723b9..c2ee563e3 100644 --- a/src/theory/quantifiers/candidate_rewrite_database.cpp +++ b/src/theory/quantifiers/candidate_rewrite_database.cpp @@ -20,6 +20,7 @@ #include "smt/smt_engine.h" #include "smt/smt_engine_scope.h" #include "smt/smt_statistics_registry.h" +#include "theory/datatypes/sygus_datatype_utils.h" #include "theory/quantifiers/sygus/term_database_sygus.h" #include "theory/quantifiers/term_util.h" #include "theory/rewriter.h" @@ -244,8 +245,8 @@ Node CandidateRewriteDatabase::addTerm(Node sol, // wish to enumerate any term that contains sol (resp. eq_sol) // as a subterm. Node exc_sol = sol; - unsigned sz = d_tds->getSygusTermSize(sol); - unsigned eqsz = d_tds->getSygusTermSize(eq_sol); + unsigned sz = datatypes::utils::getSygusTermSize(sol); + unsigned eqsz = datatypes::utils::getSygusTermSize(eq_sol); if (eqsz > sz) { sz = eqsz; diff --git a/src/theory/quantifiers/cegqi/inst_strategy_cegqi.cpp b/src/theory/quantifiers/cegqi/inst_strategy_cegqi.cpp index f059767a6..f65828d2f 100644 --- a/src/theory/quantifiers/cegqi/inst_strategy_cegqi.cpp +++ b/src/theory/quantifiers/cegqi/inst_strategy_cegqi.cpp @@ -491,7 +491,7 @@ bool InstStrategyCegqi::doAddInstantiation( std::vector< Node >& subs ) { else if (inst->addInstantiation(d_curr_quant, subs, InferenceId::QUANTIFIERS_INST_CEGQI, - false, + Node::null(), false, usedVts)) { diff --git a/src/theory/quantifiers/ematching/trigger.cpp b/src/theory/quantifiers/ematching/trigger.cpp index 62558e2c6..529125978 100644 --- a/src/theory/quantifiers/ematching/trigger.cpp +++ b/src/theory/quantifiers/ematching/trigger.cpp @@ -59,6 +59,7 @@ Trigger::Trigger(QuantifiersState& qs, Node np = ensureGroundTermPreprocessed(val, n, d_groundTerms); d_nodes.push_back(np); } + d_trNode = NodeManager::currentNM()->mkNode(SEXPR, d_nodes); if (Trace.isOn("trigger")) { QuantAttributes& qa = d_qreg.getQuantAttributes(); @@ -163,7 +164,7 @@ uint64_t Trigger::addInstantiations() bool Trigger::sendInstantiation(std::vector<Node>& m, InferenceId id) { - return d_qim.getInstantiate()->addInstantiation(d_quant, m, id); + return d_qim.getInstantiate()->addInstantiation(d_quant, m, id, d_trNode); } bool Trigger::sendInstantiation(InstMatch& m, InferenceId id) diff --git a/src/theory/quantifiers/ematching/trigger.h b/src/theory/quantifiers/ematching/trigger.h index 172e93c12..944a082c0 100644 --- a/src/theory/quantifiers/ematching/trigger.h +++ b/src/theory/quantifiers/ematching/trigger.h @@ -181,6 +181,8 @@ class Trigger { std::vector<Node>& gts); /** The nodes comprising this trigger. */ std::vector<Node> d_nodes; + /** The nodes as a single s-expression */ + Node d_trNode; /** * The preprocessed ground terms in the nodes of the trigger, which as an * optimization omits variables and constant subterms. These terms are diff --git a/src/theory/quantifiers/extended_rewrite.cpp b/src/theory/quantifiers/extended_rewrite.cpp index aa7e183bb..58a78b4aa 100644 --- a/src/theory/quantifiers/extended_rewrite.cpp +++ b/src/theory/quantifiers/extended_rewrite.cpp @@ -48,7 +48,7 @@ ExtendedRewriter::ExtendedRewriter(bool aggr) : d_aggr(aggr) d_false = NodeManager::currentNM()->mkConst(false); } -void ExtendedRewriter::setCache(Node n, Node ret) +void ExtendedRewriter::setCache(Node n, Node ret) const { if (d_aggr) { @@ -62,7 +62,7 @@ void ExtendedRewriter::setCache(Node n, Node ret) } } -Node ExtendedRewriter::getCache(Node n) +Node ExtendedRewriter::getCache(Node n) const { if (d_aggr) { @@ -83,7 +83,7 @@ Node ExtendedRewriter::getCache(Node n) bool ExtendedRewriter::addToChildren(Node nc, std::vector<Node>& children, - bool dropDup) + bool dropDup) const { // If the operator is non-additive, do not consider duplicates if (dropDup @@ -95,7 +95,7 @@ bool ExtendedRewriter::addToChildren(Node nc, return true; } -Node ExtendedRewriter::extendedRewrite(Node n) +Node ExtendedRewriter::extendedRewrite(Node n) const { n = Rewriter::rewrite(n); @@ -280,7 +280,7 @@ Node ExtendedRewriter::extendedRewrite(Node n) return ret; } -Node ExtendedRewriter::extendedRewriteAggr(Node n) +Node ExtendedRewriter::extendedRewriteAggr(Node n) const { Node new_ret; Trace("q-ext-rewrite-debug2") @@ -341,7 +341,7 @@ Node ExtendedRewriter::extendedRewriteAggr(Node n) return new_ret; } -Node ExtendedRewriter::extendedRewriteIte(Kind itek, Node n, bool full) +Node ExtendedRewriter::extendedRewriteIte(Kind itek, Node n, bool full) const { Assert(n.getKind() == itek); Assert(n[1] != n[2]); @@ -561,7 +561,7 @@ Node ExtendedRewriter::extendedRewriteIte(Kind itek, Node n, bool full) return new_ret; } -Node ExtendedRewriter::extendedRewriteAndOr(Node n) +Node ExtendedRewriter::extendedRewriteAndOr(Node n) const { // all the below rewrites are aggressive if (!d_aggr) @@ -592,7 +592,7 @@ Node ExtendedRewriter::extendedRewriteAndOr(Node n) return new_ret; } -Node ExtendedRewriter::extendedRewritePullIte(Kind itek, Node n) +Node ExtendedRewriter::extendedRewritePullIte(Kind itek, Node n) const { Assert(n.getKind() != ITE); if (n.isClosure()) @@ -715,7 +715,7 @@ Node ExtendedRewriter::extendedRewritePullIte(Kind itek, Node n) return Node::null(); } -Node ExtendedRewriter::extendedRewriteNnf(Node ret) +Node ExtendedRewriter::extendedRewriteNnf(Node ret) const { Assert(ret.getKind() == NOT); @@ -761,8 +761,11 @@ Node ExtendedRewriter::extendedRewriteNnf(Node ret) return NodeManager::currentNM()->mkNode(nk, new_children); } -Node ExtendedRewriter::extendedRewriteBcp( - Kind andk, Kind ork, Kind notk, std::map<Kind, bool>& bcp_kinds, Node ret) +Node ExtendedRewriter::extendedRewriteBcp(Kind andk, + Kind ork, + Kind notk, + std::map<Kind, bool>& bcp_kinds, + Node ret) const { Kind k = ret.getKind(); Assert(k == andk || k == ork); @@ -926,7 +929,7 @@ Node ExtendedRewriter::extendedRewriteBcp( Node ExtendedRewriter::extendedRewriteFactoring(Kind andk, Kind ork, Kind notk, - Node n) + Node n) const { Trace("ext-rew-factoring") << "Factoring: *** INPUT: " << n << std::endl; NodeManager* nm = NodeManager::currentNM(); @@ -1019,7 +1022,7 @@ Node ExtendedRewriter::extendedRewriteEqRes(Kind andk, Kind notk, std::map<Kind, bool>& bcp_kinds, Node n, - bool isXor) + bool isXor) const { Assert(n.getKind() == andk || n.getKind() == ork); Trace("ext-rew-eqres") << "Eq res: **** INPUT: " << n << std::endl; @@ -1166,7 +1169,7 @@ class SimpSubsumeTrie }; Node ExtendedRewriter::extendedRewriteEqChain( - Kind eqk, Kind andk, Kind ork, Kind notk, Node ret, bool isXor) + Kind eqk, Kind andk, Kind ork, Kind notk, Node ret, bool isXor) const { Assert(ret.getKind() == eqk); @@ -1527,9 +1530,10 @@ Node ExtendedRewriter::extendedRewriteEqChain( return Node::null(); } -Node ExtendedRewriter::partialSubstitute(Node n, - const std::map<Node, Node>& assign, - const std::map<Kind, bool>& rkinds) +Node ExtendedRewriter::partialSubstitute( + Node n, + const std::map<Node, Node>& assign, + const std::map<Kind, bool>& rkinds) const { std::unordered_map<TNode, Node> visited; std::unordered_map<TNode, Node>::iterator it; @@ -1601,10 +1605,11 @@ Node ExtendedRewriter::partialSubstitute(Node n, return visited[n]; } -Node ExtendedRewriter::partialSubstitute(Node n, - const std::vector<Node>& vars, - const std::vector<Node>& subs, - const std::map<Kind, bool>& rkinds) +Node ExtendedRewriter::partialSubstitute( + Node n, + const std::vector<Node>& vars, + const std::vector<Node>& subs, + const std::map<Kind, bool>& rkinds) const { Assert(vars.size() == subs.size()); std::map<Node, Node> assign; @@ -1615,7 +1620,7 @@ Node ExtendedRewriter::partialSubstitute(Node n, return partialSubstitute(n, assign, rkinds); } -Node ExtendedRewriter::solveEquality(Node n) +Node ExtendedRewriter::solveEquality(Node n) const { // TODO (#1706) : implement Assert(n.getKind() == EQUAL); @@ -1626,7 +1631,7 @@ Node ExtendedRewriter::solveEquality(Node n) bool ExtendedRewriter::inferSubstitution(Node n, std::vector<Node>& vars, std::vector<Node>& subs, - bool usePred) + bool usePred) const { if (n.getKind() == AND) { @@ -1696,7 +1701,7 @@ bool ExtendedRewriter::inferSubstitution(Node n, return false; } -Node ExtendedRewriter::extendedRewriteStrings(Node ret) +Node ExtendedRewriter::extendedRewriteStrings(Node ret) const { Node new_ret; Trace("q-ext-rewrite-debug") diff --git a/src/theory/quantifiers/extended_rewrite.h b/src/theory/quantifiers/extended_rewrite.h index 047318e86..8996fc441 100644 --- a/src/theory/quantifiers/extended_rewrite.h +++ b/src/theory/quantifiers/extended_rewrite.h @@ -51,7 +51,7 @@ class ExtendedRewriter ExtendedRewriter(bool aggr = true); ~ExtendedRewriter() {} /** return the extended rewritten form of n */ - Node extendedRewrite(Node n); + Node extendedRewrite(Node n) const; private: /** @@ -69,16 +69,16 @@ class ExtendedRewriter Node d_true; Node d_false; /** cache that the extended rewritten form of n is ret */ - void setCache(Node n, Node ret); + void setCache(Node n, Node ret) const; /** get the cache for n */ - Node getCache(Node n); + Node getCache(Node n) const; /** add to children * * Adds nc to the vector of children, if dropDup is true, we do not add * nc if it already occurs in children. This method returns false in this * case, otherwise it returns true. */ - bool addToChildren(Node nc, std::vector<Node>& children, bool dropDup); + bool addToChildren(Node nc, std::vector<Node>& children, bool dropDup) const; //--------------------------------------generic utilities /** Rewrite ITE, for example: @@ -92,13 +92,13 @@ class ExtendedRewriter * take. If full is false, then we do only perform rewrites that * strictly decrease the term size of n. */ - Node extendedRewriteIte(Kind itek, Node n, bool full = true); + Node extendedRewriteIte(Kind itek, Node n, bool full = true) const; /** Rewrite AND/OR * * This implements BCP, factoring, and equality resolution for the Boolean * term n whose top symbolic is AND/OR. */ - Node extendedRewriteAndOr(Node n); + Node extendedRewriteAndOr(Node n) const; /** Pull ITE, for example: * * D=C2 ---> false @@ -111,7 +111,7 @@ class ExtendedRewriter * * If this function returns a non-null node ret, then n ---> ret. */ - Node extendedRewritePullIte(Kind itek, Node n); + Node extendedRewritePullIte(Kind itek, Node n) const; /** Negation Normal Form (NNF), for example: * * ~( A & B ) ---> ( ~ A | ~B ) @@ -119,7 +119,7 @@ class ExtendedRewriter * * If this function returns a non-null node ret, then n ---> ret. */ - Node extendedRewriteNnf(Node n); + Node extendedRewriteNnf(Node n) const; /** (type-independent) Boolean constraint propagation, for example: * * ~A & ( B V A ) ---> ~A & B @@ -137,8 +137,11 @@ class ExtendedRewriter * * If this function returns a non-null node ret, then n ---> ret. */ - Node extendedRewriteBcp( - Kind andk, Kind ork, Kind notk, std::map<Kind, bool>& bcp_kinds, Node n); + Node extendedRewriteBcp(Kind andk, + Kind ork, + Kind notk, + std::map<Kind, bool>& bcp_kinds, + Node n) const; /** (type-independent) factoring, for example: * * ( A V B ) ^ ( A V C ) ----> A V ( B ^ C ) @@ -147,7 +150,7 @@ class ExtendedRewriter * This function takes as arguments the kinds that specify AND, OR, NOT. * We assume that the children of n do not contain duplicates. */ - Node extendedRewriteFactoring(Kind andk, Kind ork, Kind notk, Node n); + Node extendedRewriteFactoring(Kind andk, Kind ork, Kind notk, Node n) const; /** (type-independent) equality resolution, for example: * * ( A V C ) & ( A = B ) ---> ( B V C ) & ( A = B ) @@ -167,7 +170,7 @@ class ExtendedRewriter Kind notk, std::map<Kind, bool>& bcp_kinds, Node n, - bool isXor = false); + bool isXor = false) const; /** (type-independent) Equality chain rewriting, for example: * * A = ( A = B ) ---> B @@ -178,26 +181,32 @@ class ExtendedRewriter * This function takes as arguments the kinds that specify EQUAL, AND, OR, * and NOT. If the flag isXor is true, the eqk is treated as XOR. */ - Node extendedRewriteEqChain( - Kind eqk, Kind andk, Kind ork, Kind notk, Node n, bool isXor = false); + Node extendedRewriteEqChain(Kind eqk, + Kind andk, + Kind ork, + Kind notk, + Node n, + bool isXor = false) const; /** extended rewrite aggressive * * All aggressive rewriting techniques (those that should be prioritized * at a lower level) go in this function. */ - Node extendedRewriteAggr(Node n); + Node extendedRewriteAggr(Node n) const; /** Decompose right associative chain * * For term f( ... f( f( base, tn ), t{n-1} ) ... t1 ), returns term base, and * appends t1...tn to children. */ - Node decomposeRightAssocChain(Kind k, Node n, std::vector<Node>& children); + Node decomposeRightAssocChain(Kind k, + Node n, + std::vector<Node>& children) const; /** Make right associative chain * * Sorts children to obtain list { tn...t1 }, and returns the term * f( ... f( f( base, tn ), t{n-1} ) ... t1 ). */ - Node mkRightAssocChain(Kind k, Node base, std::vector<Node>& children); + Node mkRightAssocChain(Kind k, Node base, std::vector<Node>& children) const; /** Partial substitute * * Applies the substitution specified by assign to n, recursing only beneath @@ -206,18 +215,18 @@ class ExtendedRewriter */ Node partialSubstitute(Node n, const std::map<Node, Node>& assign, - const std::map<Kind, bool>& rkinds); + const std::map<Kind, bool>& rkinds) const; /** same as above, with vectors */ Node partialSubstitute(Node n, const std::vector<Node>& vars, const std::vector<Node>& subs, - const std::map<Kind, bool>& rkinds); + const std::map<Kind, bool>& rkinds) const; /** solve equality * * If this function returns a non-null node n', then n' is equivalent to n * and is of the form that can be used by inferSubstitution below. */ - Node solveEquality(Node n); + Node solveEquality(Node n) const; /** infer substitution * * If n is an equality of the form x = t, where t is either: @@ -231,12 +240,12 @@ class ExtendedRewriter bool inferSubstitution(Node n, std::vector<Node>& vars, std::vector<Node>& subs, - bool usePred = false); + bool usePred = false) const; /** extended rewrite * * Prints debug information, indicating the rewrite n ---> ret was found. */ - inline void debugExtendedRewrite(Node n, Node ret, const char* c) const; + void debugExtendedRewrite(Node n, Node ret, const char* c) const; //--------------------------------------end generic utilities //--------------------------------------theory-specific top-level calls @@ -245,7 +254,7 @@ class ExtendedRewriter * If this method returns a non-null node ret', then ret is equivalent to * ret'. */ - Node extendedRewriteStrings(Node ret); + Node extendedRewriteStrings(Node ret) const; //--------------------------------------end theory-specific top-level calls }; diff --git a/src/theory/quantifiers/fmf/full_model_check.cpp b/src/theory/quantifiers/fmf/full_model_check.cpp index c3fa664d9..c4f83191b 100644 --- a/src/theory/quantifiers/fmf/full_model_check.cpp +++ b/src/theory/quantifiers/fmf/full_model_check.cpp @@ -725,8 +725,11 @@ int FullModelChecker::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, i } // just add the instance d_triedLemmas++; - if (instq->addInstantiation( - f, inst, InferenceId::QUANTIFIERS_INST_FMF_FMC, true)) + if (instq->addInstantiation(f, + inst, + InferenceId::QUANTIFIERS_INST_FMF_FMC, + Node::null(), + true)) { Trace("fmc-debug-inst") << "** Added instantiation." << std::endl; d_addedLemmas++; @@ -875,8 +878,11 @@ bool FullModelChecker::exhaustiveInstantiate(FirstOrderModelFmc* fm, if (ev!=d_true) { Trace("fmc-exh-debug") << ", add!"; //add as instantiation - if (ie->addInstantiation( - f, inst, InferenceId::QUANTIFIERS_INST_FMF_FMC_EXH, true)) + if (ie->addInstantiation(f, + inst, + InferenceId::QUANTIFIERS_INST_FMF_FMC_EXH, + Node::null(), + true)) { Trace("fmc-exh-debug") << " ...success."; addedLemmas++; diff --git a/src/theory/quantifiers/fmf/model_builder.h b/src/theory/quantifiers/fmf/model_builder.h index cfccd4d93..a767af47a 100644 --- a/src/theory/quantifiers/fmf/model_builder.h +++ b/src/theory/quantifiers/fmf/model_builder.h @@ -56,8 +56,6 @@ class QModelBuilder : public TheoryEngineModelBuilder virtual int doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ) { return false; } //whether to construct model virtual bool optUseModel(); - /** exist instantiation ? */ - virtual bool existsInstantiation( Node f, InstMatch& m, bool modEq = true, bool modInst = false ) { return false; } //debug model void debugModel(TheoryModel* m) override; //statistics diff --git a/src/theory/quantifiers/fmf/model_engine.cpp b/src/theory/quantifiers/fmf/model_engine.cpp index 747b0621f..e58f66d0b 100644 --- a/src/theory/quantifiers/fmf/model_engine.cpp +++ b/src/theory/quantifiers/fmf/model_engine.cpp @@ -301,8 +301,11 @@ void ModelEngine::exhaustiveInstantiate( Node f, int effort ){ Debug("fmf-model-eval") << "* Add instantiation " << m << std::endl; triedLemmas++; //add as instantiation - if (inst->addInstantiation( - f, m.d_vals, InferenceId::QUANTIFIERS_INST_FMF_EXH, true)) + if (inst->addInstantiation(f, + m.d_vals, + InferenceId::QUANTIFIERS_INST_FMF_EXH, + Node::null(), + true)) { addedLemmas++; if (d_qstate.isInConflict()) diff --git a/src/theory/quantifiers/instantiate.cpp b/src/theory/quantifiers/instantiate.cpp index 268d1371f..05361eaa1 100644 --- a/src/theory/quantifiers/instantiate.cpp +++ b/src/theory/quantifiers/instantiate.cpp @@ -101,8 +101,8 @@ void Instantiate::addRewriter(InstantiationRewriter* ir) bool Instantiate::addInstantiation(Node q, std::vector<Node>& terms, InferenceId id, + Node pfArg, bool mkRep, - bool modEq, bool doVts) { // For resource-limiting (also does a time check). @@ -229,7 +229,7 @@ bool Instantiate::addInstantiation(Node q, } // record the instantiation - bool recorded = recordInstantiationInternal(q, terms, modEq); + bool recorded = recordInstantiationInternal(q, terms); if (!recorded) { Trace("inst-add-debug") << " --> Already exists (no record)." << std::endl; @@ -250,7 +250,8 @@ bool Instantiate::addInstantiation(Node q, Trace("inst-add-debug") << "Constructing instantiation..." << std::endl; Assert(d_qreg.d_vars[q].size() == terms.size()); // get the instantiation - Node body = getInstantiation(q, d_qreg.d_vars[q], terms, doVts, pfTmp.get()); + Node body = getInstantiation( + q, d_qreg.d_vars[q], terms, id, pfArg, doVts, pfTmp.get()); Node orig_body = body; // now preprocess, storing the trust node for the rewrite TrustNode tpBody = QuantifiersRewriter::preprocess(body, true); @@ -394,12 +395,12 @@ bool Instantiate::addInstantiationExpFail(Node q, std::vector<Node>& terms, std::vector<bool>& failMask, InferenceId id, + Node pfArg, bool mkRep, - bool modEq, bool doVts, bool expFull) { - if (addInstantiation(q, terms, id, mkRep, modEq, doVts)) + if (addInstantiation(q, terms, id, pfArg, mkRep, doVts)) { return true; } @@ -421,7 +422,9 @@ bool Instantiate::addInstantiationExpFail(Node q, subs[vars[i]] = terms[i]; } // get the instantiation body - Node ibody = getInstantiation(q, vars, terms, doVts); + InferenceId idNone = InferenceId::UNKNOWN; + Node nulln; + Node ibody = getInstantiation(q, vars, terms, idNone, nulln, doVts); ibody = Rewriter::rewrite(ibody); for (size_t i = 0; i < tsize; i++) { @@ -450,7 +453,7 @@ bool Instantiate::addInstantiationExpFail(Node q, // check whether the instantiation rewrites to the same thing if (!success) { - Node ibodyc = getInstantiation(q, vars, terms, doVts); + Node ibodyc = getInstantiation(q, vars, terms, idNone, nulln, doVts); ibodyc = Rewriter::rewrite(ibodyc); success = (ibodyc == ibody); Trace("inst-exp-fail") << " rewrite invariant: " << success << std::endl; @@ -521,6 +524,8 @@ bool Instantiate::existsInstantiation(Node q, Node Instantiate::getInstantiation(Node q, std::vector<Node>& vars, std::vector<Node>& terms, + InferenceId id, + Node pfArg, bool doVts, LazyCDProof* pf) { @@ -534,7 +539,19 @@ Node Instantiate::getInstantiation(Node q, // store the proof of the instantiated body, with (open) assumption q if (pf != nullptr) { - pf->addStep(body, PfRule::INSTANTIATE, {q}, terms); + // additional arguments: if the inference id is not unknown, include it, + // followed by the proof argument if non-null. The latter is used e.g. + // to track which trigger caused an instantiation. + std::vector<Node> pfTerms = terms; + if (id != InferenceId::UNKNOWN) + { + pfTerms.push_back(mkInferenceIdNode(id)); + if (!pfArg.isNull()) + { + pfTerms.push_back(pfArg); + } + } + pf->addStep(body, PfRule::INSTANTIATE, {q}, pfTerms); } // run rewriters to rewrite the instantiation in sequence. @@ -564,18 +581,16 @@ Node Instantiate::getInstantiation(Node q, Node Instantiate::getInstantiation(Node q, std::vector<Node>& terms, bool doVts) { Assert(d_qreg.d_vars.find(q) != d_qreg.d_vars.end()); - return getInstantiation(q, d_qreg.d_vars[q], terms, doVts); + return getInstantiation( + q, d_qreg.d_vars[q], terms, InferenceId::UNKNOWN, Node::null(), doVts); } -bool Instantiate::recordInstantiationInternal(Node q, - std::vector<Node>& terms, - bool modEq) +bool Instantiate::recordInstantiationInternal(Node q, std::vector<Node>& terms) { if (options::incrementalSolving()) { Trace("inst-add-debug") - << "Adding into context-dependent inst trie, modEq = " << modEq - << std::endl; + << "Adding into context-dependent inst trie" << std::endl; CDInstMatchTrie* imt; std::map<Node, CDInstMatchTrie*>::iterator it = d_c_inst_match_trie.find(q); if (it != d_c_inst_match_trie.end()) @@ -588,10 +603,10 @@ bool Instantiate::recordInstantiationInternal(Node q, d_c_inst_match_trie[q] = imt; } d_c_inst_match_trie_dom.insert(q); - return imt->addInstMatch(d_qstate, q, terms, modEq); + return imt->addInstMatch(d_qstate, q, terms); } Trace("inst-add-debug") << "Adding into inst trie" << std::endl; - return d_inst_match_trie[q].addInstMatch(d_qstate, q, terms, modEq); + return d_inst_match_trie[q].addInstMatch(d_qstate, q, terms); } bool Instantiate::removeInstantiationInternal(Node q, std::vector<Node>& terms) diff --git a/src/theory/quantifiers/instantiate.h b/src/theory/quantifiers/instantiate.h index eddc7470b..1f380350f 100644 --- a/src/theory/quantifiers/instantiate.h +++ b/src/theory/quantifiers/instantiate.h @@ -139,10 +139,10 @@ class Instantiate : public QuantifiersUtil * @param terms the terms to instantiate with * @param id the identifier of the instantiation lemma sent via the inference * manager + * @param pfArg an additional node to add to the arguments of the INSTANTIATE + * step * @param mkRep whether to take the representatives of the terms in the * range of the substitution m, - * @param modEq whether to check for duplication modulo equality in - * instantiation tries (for performance), * @param doVts whether we must apply virtual term substitution to the * instantiation lemma. * @@ -161,8 +161,8 @@ class Instantiate : public QuantifiersUtil bool addInstantiation(Node q, std::vector<Node>& terms, InferenceId id, + Node pfArg = Node::null(), bool mkRep = false, - bool modEq = false, bool doVts = false); /** * Same as above, but we also compute a vector failMask indicating which @@ -191,8 +191,8 @@ class Instantiate : public QuantifiersUtil std::vector<Node>& terms, std::vector<bool>& failMask, InferenceId id, + Node pfArg = Node::null(), bool mkRep = false, - bool modEq = false, bool doVts = false, bool expFull = true); /** record instantiation @@ -226,6 +226,8 @@ class Instantiate : public QuantifiersUtil Node getInstantiation(Node q, std::vector<Node>& vars, std::vector<Node>& terms, + InferenceId id = InferenceId::UNKNOWN, + Node pfArg = Node::null(), bool doVts = false, LazyCDProof* pf = nullptr); /** get instantiation @@ -293,14 +295,8 @@ class Instantiate : public QuantifiersUtil Statistics d_statistics; private: - /** record instantiation, return true if it was not a duplicate - * - * modEq : whether to check for duplication modulo equality in instantiation - * tries (for performance), - */ - bool recordInstantiationInternal(Node q, - std::vector<Node>& terms, - bool modEq = false); + /** record instantiation, return true if it was not a duplicate */ + bool recordInstantiationInternal(Node q, std::vector<Node>& terms); /** remove instantiation from the cache */ bool removeInstantiationInternal(Node q, std::vector<Node>& terms); /** diff --git a/src/theory/quantifiers/proof_checker.cpp b/src/theory/quantifiers/proof_checker.cpp index f44f2f291..5e02e16a5 100644 --- a/src/theory/quantifiers/proof_checker.cpp +++ b/src/theory/quantifiers/proof_checker.cpp @@ -102,15 +102,16 @@ Node QuantifiersProofRuleChecker::checkInternal( else if (id == PfRule::INSTANTIATE) { Assert(children.size() == 1); + // note we may have more arguments than just the term vector if (children[0].getKind() != FORALL - || args.size() != children[0][0].getNumChildren()) + || args.size() < children[0][0].getNumChildren()) { return Node::null(); } Node body = children[0][1]; std::vector<Node> vars; std::vector<Node> subs; - for (unsigned i = 0, nargs = args.size(); i < nargs; i++) + for (size_t i = 0, nc = children[0][0].getNumChildren(); i < nc; i++) { vars.push_back(children[0][0][i]); subs.push_back(args[i]); diff --git a/src/theory/quantifiers/sygus/cegis_unif.cpp b/src/theory/quantifiers/sygus/cegis_unif.cpp index 28788a5ea..544bdcc5c 100644 --- a/src/theory/quantifiers/sygus/cegis_unif.cpp +++ b/src/theory/quantifiers/sygus/cegis_unif.cpp @@ -19,6 +19,7 @@ #include "expr/sygus_datatype.h" #include "options/quantifiers_options.h" #include "printer/printer.h" +#include "theory/datatypes/sygus_datatype_utils.h" #include "theory/quantifiers/sygus/sygus_unif_rl.h" #include "theory/quantifiers/sygus/synth_conjecture.h" #include "theory/quantifiers/sygus/term_database_sygus.h" @@ -205,8 +206,8 @@ bool CegisUnif::getEnumValues(const std::vector<Node>& enums, if (curr_val < prev_val) { // must have the same size - unsigned prev_size = d_tds->getSygusTermSize(prev_val); - unsigned curr_size = d_tds->getSygusTermSize(curr_val); + unsigned prev_size = datatypes::utils::getSygusTermSize(prev_val); + unsigned curr_size = datatypes::utils::getSygusTermSize(curr_val); Assert(prev_size <= curr_size); if (curr_size == prev_size) { diff --git a/src/theory/quantifiers/sygus/enum_stream_substitution.cpp b/src/theory/quantifiers/sygus/enum_stream_substitution.cpp index 3ae34d82c..f853ac8e8 100644 --- a/src/theory/quantifiers/sygus/enum_stream_substitution.cpp +++ b/src/theory/quantifiers/sygus/enum_stream_substitution.cpp @@ -24,6 +24,7 @@ #include "theory/quantifiers/sygus/term_database_sygus.h" #include <numeric> // for std::iota +#include <sstream> using namespace cvc5::kind; diff --git a/src/theory/quantifiers/sygus/enum_stream_substitution.h b/src/theory/quantifiers/sygus/enum_stream_substitution.h index ea028991b..05c693ace 100644 --- a/src/theory/quantifiers/sygus/enum_stream_substitution.h +++ b/src/theory/quantifiers/sygus/enum_stream_substitution.h @@ -19,12 +19,14 @@ #define CVC5__THEORY__QUANTIFIERS__SYGUS__ENUM_STREAM_SUBSTITUTION_H #include "expr/node.h" -#include "theory/quantifiers/sygus/synth_conjecture.h" +#include "theory/quantifiers/sygus/enum_val_generator.h" namespace cvc5 { namespace theory { namespace quantifiers { +class TermDbSygus; + /** Streamer of different values according to variable permutations * * Generates a new value (modulo rewriting) when queried in which its variables @@ -33,7 +35,7 @@ namespace quantifiers { class EnumStreamPermutation { public: - EnumStreamPermutation(quantifiers::TermDbSygus* tds); + EnumStreamPermutation(TermDbSygus* tds); ~EnumStreamPermutation() {} /** resets utility * @@ -70,7 +72,7 @@ class EnumStreamPermutation private: /** sygus term database of current quantifiers engine */ - quantifiers::TermDbSygus* d_tds; + TermDbSygus* d_tds; /** maps subclass ids to subset of d_vars with that subclass id */ std::map<unsigned, std::vector<Node>> d_var_classes; /** maps variables to subfield types with constructors for @@ -165,7 +167,7 @@ class EnumStreamPermutation class EnumStreamSubstitution { public: - EnumStreamSubstitution(quantifiers::TermDbSygus* tds); + EnumStreamSubstitution(TermDbSygus* tds); ~EnumStreamSubstitution() {} /** initializes utility * @@ -211,7 +213,7 @@ class EnumStreamSubstitution private: /** sygus term database of current quantifiers engine */ - quantifiers::TermDbSygus* d_tds; + TermDbSygus* d_tds; /** type this utility has been initialized for */ TypeNode d_tn; /** current value */ @@ -281,7 +283,7 @@ class EnumStreamSubstitution class EnumStreamConcrete : public EnumValGenerator { public: - EnumStreamConcrete(quantifiers::TermDbSygus* tds) : d_ess(tds) {} + EnumStreamConcrete(TermDbSygus* tds) : d_ess(tds) {} /** initialize this class with enumerator e */ void initialize(Node e) override; /** get that value v was enumerated */ diff --git a/src/theory/quantifiers/sygus/enum_val_generator.h b/src/theory/quantifiers/sygus/enum_val_generator.h new file mode 100644 index 000000000..64c069087 --- /dev/null +++ b/src/theory/quantifiers/sygus/enum_val_generator.h @@ -0,0 +1,62 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Base class for sygus enumerators + */ + +#include "cvc5_private.h" + +#ifndef CVC5__THEORY__QUANTIFIERS__SYGUS__ENUM_VAL_GENERATOR_H +#define CVC5__THEORY__QUANTIFIERS__SYGUS__ENUM_VAL_GENERATOR_H + +#include "expr/node.h" + +namespace cvc5 { +namespace theory { +namespace quantifiers { + +/** + * A base class for generating values for actively-generated enumerators. + * At a high level, the job of this class is to accept a stream of "abstract + * values" a1, ..., an, ..., and generate a (possibly larger) stream of + * "concrete values" c11, ..., c1{m_1}, ..., cn1, ... cn{m_n}, .... + */ +class EnumValGenerator +{ + public: + virtual ~EnumValGenerator() {} + /** initialize this class with enumerator e */ + virtual void initialize(Node e) = 0; + /** Inform this generator that abstract value v was enumerated. */ + virtual void addValue(Node v) = 0; + /** + * Increment this value generator. If this returns false, then we are out of + * values. If this returns true, getCurrent(), if non-null, returns the + * current term. + * + * Notice that increment() may return true and afterwards it may be the case + * getCurrent() is null. We do this so that increment() does not take too + * much time per call, which can be the case for grammars where it is + * difficult to find the next (non-redundant) term. Returning true with + * a null current term gives the caller the chance to interleave other + * reasoning. + */ + virtual bool increment() = 0; + /** Get the current concrete value generated by this class. */ + virtual Node getCurrent() = 0; +}; + +} // namespace quantifiers +} // namespace theory +} // namespace cvc5 + +#endif diff --git a/src/theory/quantifiers/sygus/rcons_type_info.cpp b/src/theory/quantifiers/sygus/rcons_type_info.cpp index 1c62f030d..a1ae53ad1 100644 --- a/src/theory/quantifiers/sygus/rcons_type_info.cpp +++ b/src/theory/quantifiers/sygus/rcons_type_info.cpp @@ -31,7 +31,7 @@ void RConsTypeInfo::initialize(TermDbSygus* tds, NodeManager* nm = NodeManager::currentNM(); SkolemManager* sm = nm->getSkolemManager(); - d_enumerator.reset(new SygusEnumerator(tds, nullptr, s, true)); + d_enumerator.reset(new SygusEnumerator(tds, nullptr, &s, true)); d_enumerator->initialize(sm->mkDummySkolem("sygus_rcons", stn)); d_crd.reset(new CandidateRewriteDatabase(true, false, true, false)); // since initial samples are not always useful for equivalence checks, set diff --git a/src/theory/quantifiers/sygus/sygus_enumerator.cpp b/src/theory/quantifiers/sygus/sygus_enumerator.cpp index 0cf92b373..2dfd41fb4 100644 --- a/src/theory/quantifiers/sygus/sygus_enumerator.cpp +++ b/src/theory/quantifiers/sygus/sygus_enumerator.cpp @@ -20,6 +20,7 @@ #include "options/datatypes_options.h" #include "options/quantifiers_options.h" #include "smt/logic_exception.h" +#include "theory/datatypes/sygus_datatype_utils.h" #include "theory/datatypes/theory_datatypes_utils.h" #include "theory/quantifiers/sygus/synth_engine.h" #include "theory/quantifiers/sygus/type_node_id_trie.h" @@ -33,12 +34,14 @@ namespace quantifiers { SygusEnumerator::SygusEnumerator(TermDbSygus* tds, SynthConjecture* p, - SygusStatistics& s, - bool enumShapes) + SygusStatistics* s, + bool enumShapes, + bool enumAnyConstHoles) : d_tds(tds), d_parent(p), d_stats(s), d_enumShapes(enumShapes), + d_enumAnyConstHoles(enumAnyConstHoles), d_tlEnum(nullptr), d_abortSize(-1) { @@ -54,6 +57,12 @@ void SygusEnumerator::initialize(Node e) d_tlEnum = getMasterEnumForType(d_etype); d_abortSize = options::sygusAbortSize(); + // if we don't have a term database, we don't register symmetry breaking + // lemmas + if (!d_tds) + { + return; + } // Get the statically registered symmetry breaking clauses for e, see if they // can be used for speeding up the enumeration. NodeManager* nm = NodeManager::currentNM(); @@ -141,7 +150,8 @@ Node SygusEnumerator::getCurrent() if (d_sbExcTlCons.find(ret.getOperator()) != d_sbExcTlCons.end()) { Trace("sygus-enum-exc") - << "Exclude (external) : " << d_tds->sygusToBuiltin(ret) << std::endl; + << "Exclude (external) : " << datatypes::utils::sygusToBuiltin(ret) + << std::endl; ret = Node::null(); } } @@ -330,9 +340,12 @@ bool SygusEnumerator::TermCache::addTerm(Node n) Assert(!n.isNull()); if (options::sygusSymBreakDynamic()) { - Node bn = d_tds->sygusToBuiltin(n); - Node bnr = d_tds->getExtRewriter()->extendedRewrite(bn); - ++(d_stats->d_enumTermsRewrite); + Node bn = datatypes::utils::sygusToBuiltin(n); + Node bnr = d_extr.extendedRewrite(bn); + if (d_stats != nullptr) + { + ++(d_stats->d_enumTermsRewrite); + } if (options::sygusRewVerify()) { if (bn != bnr) @@ -358,7 +371,10 @@ bool SygusEnumerator::TermCache::addTerm(Node n) // if we are doing PBE symmetry breaking if (d_eec != nullptr) { - ++(d_stats->d_enumTermsExampleEval); + if (d_stats != nullptr) + { + ++(d_stats->d_enumTermsExampleEval); + } // Is it equivalent under examples? Node bne = d_eec->addSearchVal(d_tn, bnr); if (!bne.isNull()) @@ -374,7 +390,10 @@ bool SygusEnumerator::TermCache::addTerm(Node n) } Trace("sygus-enum-terms") << "tc(" << d_tn << "): term " << bn << std::endl; } - ++(d_stats->d_enumTerms); + if (d_stats != nullptr) + { + ++(d_stats->d_enumTerms); + } d_terms.push_back(n); return true; } @@ -474,8 +493,8 @@ Node SygusEnumerator::TermEnumSlave::getCurrent() Node curr = tc.getTerm(d_index); Trace("sygus-enum-debug2") << "slave(" << d_tn - << "): current : " << d_se->d_tds->sygusToBuiltin(curr) - << ", sizes = " << d_se->d_tds->getSygusTermSize(curr) << " " + << "): current : " << datatypes::utils::sygusToBuiltin(curr) + << ", sizes = " << datatypes::utils::getSygusTermSize(curr) << " " << getCurrentSize() << std::endl; Trace("sygus-enum-debug2") << "slave(" << d_tn << "): indices : " << d_hasIndexNextEnd << " " @@ -560,7 +579,7 @@ void SygusEnumerator::initializeTermCache(TypeNode tn) { eec = d_parent->getExampleEvalCache(d_enum); } - d_tcache[tn].initialize(&d_stats, d_enum, tn, d_tds, eec); + d_tcache[tn].initialize(d_stats, d_enum, tn, d_tds, eec); } SygusEnumerator::TermEnum* SygusEnumerator::getMasterEnumForType(TypeNode tn) @@ -578,7 +597,7 @@ SygusEnumerator::TermEnum* SygusEnumerator::getMasterEnumForType(TypeNode tn) AlwaysAssert(ret); return &d_masterEnum[tn]; } - if (options::sygusRepairConst()) + if (d_enumAnyConstHoles) { std::map<TypeNode, TermEnumMasterFv>::iterator it = d_masterEnumFv.find(tn); if (it != d_masterEnumFv.end()) @@ -720,6 +739,7 @@ bool SygusEnumerator::TermEnumMaster::incrementInternal() // If we are enumerating shapes, the first enumerated term is a free variable. if (d_enumShapes && !d_enumShapesInit) { + Assert(d_tds != nullptr); Node fv = d_tds->getFreeVar(d_tn, 0); d_enumShapesInit = true; d_currTermSet = true; @@ -1083,6 +1103,7 @@ void SygusEnumerator::TermEnumMaster::childrenToShape( Node SygusEnumerator::TermEnumMaster::convertShape( Node n, std::map<TypeNode, int>& vcounter) { + Assert(d_tds != nullptr); NodeManager* nm = NodeManager::currentNM(); std::unordered_map<TNode, Node> visited; std::unordered_map<TNode, Node>::iterator it; @@ -1195,6 +1216,7 @@ bool SygusEnumerator::TermEnumMasterFv::initialize(SygusEnumerator* se, Node SygusEnumerator::TermEnumMasterFv::getCurrent() { + Assert(d_se->d_tds != nullptr); Node ret = d_se->d_tds->getFreeVar(d_tn, d_currSize); Trace("sygus-enum-debug2") << "master_fv(" << d_tn << "): mk " << ret << std::endl; diff --git a/src/theory/quantifiers/sygus/sygus_enumerator.h b/src/theory/quantifiers/sygus/sygus_enumerator.h index 355108957..39e58d5f3 100644 --- a/src/theory/quantifiers/sygus/sygus_enumerator.h +++ b/src/theory/quantifiers/sygus/sygus_enumerator.h @@ -22,6 +22,7 @@ #include <unordered_set> #include "expr/node.h" #include "expr/type_node.h" +#include "theory/quantifiers/sygus/enum_val_generator.h" #include "theory/quantifiers/sygus/synth_conjecture.h" #include "theory/quantifiers/sygus/term_database_sygus.h" @@ -56,10 +57,23 @@ class SygusPbe; class SygusEnumerator : public EnumValGenerator { public: - SygusEnumerator(TermDbSygus* tds, - SynthConjecture* p, - SygusStatistics& s, - bool enumShapes = false); + /** + * @param tds Pointer to the term database, required if enumShapes or + * enumAnyConstHoles is true, or if we want to include symmetry breaking from + * lemmas stored in the sygus term database, + * @param p Pointer to the conjecture, required if we wish to do + * conjecture-specific symmetry breaking + * @param s Pointer to the statistics + * @param enumShapes If true, this enumerator will generate terms having any + * number of free variables + * @param enumAnyConstHoles If true, this enumerator will generate terms where + * free variables are the arguments to any-constant constructors. + */ + SygusEnumerator(TermDbSygus* tds = nullptr, + SynthConjecture* p = nullptr, + SygusStatistics* s = nullptr, + bool enumShapes = false, + bool enumAnyConstHoles = false); ~SygusEnumerator() {} /** initialize this class with enumerator e */ void initialize(Node e) override; @@ -77,10 +91,13 @@ class SygusEnumerator : public EnumValGenerator TermDbSygus* d_tds; /** pointer to the synth conjecture that owns this enumerator */ SynthConjecture* d_parent; - /** reference to the statistics of parent */ - SygusStatistics& d_stats; + /** pointer to the statistics */ + SygusStatistics* d_stats; /** Whether we are enumerating shapes */ bool d_enumShapes; + /** Whether we are enumerating free variables as arguments to any-constant + * constructors */ + bool d_enumAnyConstHoles; /** Term cache * * This stores a list of terms for a given sygus type. The key features of @@ -171,6 +188,8 @@ class SygusEnumerator : public EnumValGenerator TypeNode d_tn; /** pointer to term database sygus */ TermDbSygus* d_tds; + /** extended rewriter */ + ExtendedRewriter d_extr; /** * Pointer to the example evaluation cache utility (used for symmetry * breaking). diff --git a/src/theory/quantifiers/sygus/sygus_enumerator_basic.h b/src/theory/quantifiers/sygus/sygus_enumerator_basic.h index bae6f6327..42bce471d 100644 --- a/src/theory/quantifiers/sygus/sygus_enumerator_basic.h +++ b/src/theory/quantifiers/sygus/sygus_enumerator_basic.h @@ -22,7 +22,7 @@ #include <unordered_set> #include "expr/node.h" #include "expr/type_node.h" -#include "theory/quantifiers/sygus/synth_conjecture.h" +#include "theory/quantifiers/sygus/enum_val_generator.h" #include "theory/quantifiers/sygus/term_database_sygus.h" #include "theory/type_enumerator.h" diff --git a/src/theory/quantifiers/sygus/sygus_enumerator_callback.cpp b/src/theory/quantifiers/sygus/sygus_enumerator_callback.cpp new file mode 100644 index 000000000..7b3236832 --- /dev/null +++ b/src/theory/quantifiers/sygus/sygus_enumerator_callback.cpp @@ -0,0 +1,107 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Mathias Preiner + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * sygus_enumerator + */ + +#include "theory/quantifiers/sygus/sygus_enumerator_callback.h" + +#include "theory/datatypes/sygus_datatype_utils.h" +#include "theory/quantifiers/sygus/example_eval_cache.h" +#include "theory/quantifiers/sygus/sygus_stats.h" +#include "theory/quantifiers/sygus_sampler.h" + +namespace cvc5 { +namespace theory { +namespace quantifiers { + +SygusEnumeratorCallback::SygusEnumeratorCallback(Node e, SygusStatistics* s) + : d_enum(e), d_stats(s) +{ + d_tn = e.getType(); +} + +bool SygusEnumeratorCallback::addTerm(Node n, std::unordered_set<Node>& bterms) +{ + Node bn = datatypes::utils::sygusToBuiltin(n); + Node bnr = d_extr.extendedRewrite(bn); + if (d_stats != nullptr) + { + ++(d_stats->d_enumTermsRewrite); + } + // call the solver-specific notify term + notifyTermInternal(n, bn, bnr); + // check whether we should keep the term, which is based on the callback, + // and the builtin terms + // First, must be unique up to rewriting + if (bterms.find(bnr) != bterms.end()) + { + Trace("sygus-enum-exc") << "Exclude: " << bn << std::endl; + return false; + } + // insert to builtin term cache, regardless of whether it is redundant + // based on the callback + bterms.insert(bnr); + // callback-specific add term + if (!addTermInternal(n, bn, bnr)) + { + Trace("sygus-enum-exc") + << "Exclude: " << bn << " due to callback" << std::endl; + return false; + } + Trace("sygus-enum-terms") << "tc(" << d_tn << "): term " << bn << std::endl; + return true; +} + +SygusEnumeratorCallbackDefault::SygusEnumeratorCallbackDefault( + Node e, SygusStatistics* s, ExampleEvalCache* eec, SygusSampler* ssrv) + : SygusEnumeratorCallback(e, s), d_eec(eec), d_samplerRrV(ssrv) +{ +} +void SygusEnumeratorCallbackDefault::notifyTermInternal(Node n, + Node bn, + Node bnr) +{ + if (d_samplerRrV != nullptr) + { + d_samplerRrV->checkEquivalent(bn, bnr); + } +} + +bool SygusEnumeratorCallbackDefault::addTermInternal(Node n, Node bn, Node bnr) +{ + // if we are doing PBE symmetry breaking + if (d_eec != nullptr) + { + if (d_stats != nullptr) + { + ++(d_stats->d_enumTermsExampleEval); + } + // Is it equivalent under examples? + Node bne = d_eec->addSearchVal(d_tn, bnr); + if (!bne.isNull()) + { + if (bnr != bne) + { + Trace("sygus-enum-exc") + << "Exclude (by examples): " << bn << ", since we already have " + << bne << std::endl; + return false; + } + } + } + return true; +} + +} // namespace quantifiers +} // namespace theory +} // namespace cvc5 diff --git a/src/theory/quantifiers/sygus/sygus_enumerator_callback.h b/src/theory/quantifiers/sygus/sygus_enumerator_callback.h new file mode 100644 index 000000000..545440eef --- /dev/null +++ b/src/theory/quantifiers/sygus/sygus_enumerator_callback.h @@ -0,0 +1,110 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds, Mathias Preiner + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * sygus_enumerator + */ + +#include "cvc5_private.h" + +#ifndef CVC5__THEORY__QUANTIFIERS__SYGUS__SYGUS_ENUMERATOR_CALLBACK_H +#define CVC5__THEORY__QUANTIFIERS__SYGUS__SYGUS_ENUMERATOR_CALLBACK_H + +#include <unordered_set> + +#include "expr/node.h" +#include "theory/quantifiers/extended_rewrite.h" + +namespace cvc5 { +namespace theory { +namespace quantifiers { + +class ExampleEvalCache; +class SygusStatistics; +class SygusSampler; + +/** + * Base class for callbacks in the fast enumerator. This allows a user to + * provide custom criteria for whether or not enumerated values should be + * considered. + */ +class SygusEnumeratorCallback +{ + public: + SygusEnumeratorCallback(Node e, SygusStatistics* s = nullptr); + virtual ~SygusEnumeratorCallback() {} + /** + * Add term, return true if the term should be considered in the enumeration. + * Notice that returning false indicates that n should not be considered as a + * subterm of any other term in the enumeration. + * + * @param n The SyGuS term + * @param bterms The (rewritten, builtin) terms we have already enumerated + * @return true if n should be considered in the enumeration. + */ + virtual bool addTerm(Node n, std::unordered_set<Node>& bterms) = 0; + + protected: + /** + * Callback-specific notification of the above + * + * @param n The SyGuS term + * @param bn The builtin version of the enumerated term + * @param bnr The (extended) rewritten form of bn + */ + virtual void notifyTermInternal(Node n, Node bn, Node bnr) = 0; + /** + * Callback-specific add term + * + * @param n The SyGuS term + * @param bn The builtin version of the enumerated term + * @param bnr The (extended) rewritten form of bn + * @return true if the term should be considered in the enumeration. + */ + virtual bool addTermInternal(Node n, Node bn, Node bnr) = 0; + /** The enumerator */ + Node d_enum; + /** The type of enum */ + TypeNode d_tn; + /** extended rewriter */ + ExtendedRewriter d_extr; + /** pointer to the statistics */ + SygusStatistics* d_stats; +}; + +class SygusEnumeratorCallbackDefault : public SygusEnumeratorCallback +{ + public: + SygusEnumeratorCallbackDefault(Node e, + SygusStatistics* s = nullptr, + ExampleEvalCache* eec = nullptr, + SygusSampler* ssrv = nullptr); + virtual ~SygusEnumeratorCallbackDefault() {} + + protected: + /** Notify that bn / bnr is an enumerated builtin, rewritten form of a term */ + void notifyTermInternal(Node n, Node bn, Node bnr) override; + /** Add term, return true if n should be considered in the enumeration */ + bool addTermInternal(Node n, Node bn, Node bnr) override; + /** + * Pointer to the example evaluation cache utility (used for symmetry + * breaking). + */ + ExampleEvalCache* d_eec; + /** sampler (for --sygus-rr-verify) */ + SygusSampler* d_samplerRrV; +}; + +} // namespace quantifiers +} // namespace theory +} // namespace cvc5 + +#endif /* CVC5__THEORY__QUANTIFIERS__SYGUS__SYGUS_ENUMERATOR_CALLBACK_H */ diff --git a/src/theory/quantifiers/sygus/sygus_explain.cpp b/src/theory/quantifiers/sygus/sygus_explain.cpp index 395f16beb..23c315f42 100644 --- a/src/theory/quantifiers/sygus/sygus_explain.cpp +++ b/src/theory/quantifiers/sygus/sygus_explain.cpp @@ -18,6 +18,7 @@ #include "expr/dtype.h" #include "expr/dtype_cons.h" #include "smt/logic_exception.h" +#include "theory/datatypes/sygus_datatype_utils.h" #include "theory/datatypes/theory_datatypes_utils.h" #include "theory/quantifiers/sygus/sygus_invariance.h" #include "theory/quantifiers/sygus/term_database_sygus.h" @@ -220,7 +221,7 @@ void SygusExplain::getExplanationFor(TermRecBuild& trb, // we are tracking term size if positive if (sz >= 0) { - int s = d_tdb->getSygusTermSize(vn[i]); + int s = datatypes::utils::getSygusTermSize(vn[i]); sz = sz - s; } } diff --git a/src/theory/quantifiers/sygus/sygus_pbe.cpp b/src/theory/quantifiers/sygus/sygus_pbe.cpp index 86d0bbc8e..892ee6dd4 100644 --- a/src/theory/quantifiers/sygus/sygus_pbe.cpp +++ b/src/theory/quantifiers/sygus/sygus_pbe.cpp @@ -15,6 +15,7 @@ #include "theory/quantifiers/sygus/sygus_pbe.h" #include "options/quantifiers_options.h" +#include "theory/datatypes/sygus_datatype_utils.h" #include "theory/quantifiers/sygus/example_infer.h" #include "theory/quantifiers/sygus/sygus_unif_io.h" #include "theory/quantifiers/sygus/synth_conjecture.h" @@ -180,7 +181,7 @@ bool SygusPbe::constructCandidates(const std::vector<Node>& enums, Trace("sygus-pbe-enum") << std::endl; if (!enum_values[i].isNull()) { - unsigned sz = d_tds->getSygusTermSize(enum_values[i]); + unsigned sz = datatypes::utils::getSygusTermSize(enum_values[i]); szs.push_back(sz); if (i == 0 || sz < min_term_size) { diff --git a/src/theory/quantifiers/sygus/sygus_unif.cpp b/src/theory/quantifiers/sygus/sygus_unif.cpp index 16ca1f4e6..00370ffa2 100644 --- a/src/theory/quantifiers/sygus/sygus_unif.cpp +++ b/src/theory/quantifiers/sygus/sygus_unif.cpp @@ -15,6 +15,7 @@ #include "theory/quantifiers/sygus/sygus_unif.h" +#include "theory/datatypes/sygus_datatype_utils.h" #include "theory/quantifiers/sygus/term_database_sygus.h" #include "theory/quantifiers/term_util.h" #include "util/random.h" @@ -52,7 +53,7 @@ Node SygusUnif::getMinimalTerm(const std::vector<Node>& terms) unsigned ssize = 0; if (it == d_termToSize.end()) { - ssize = d_tds->getSygusTermSize(n); + ssize = datatypes::utils::getSygusTermSize(n); d_termToSize[n] = ssize; } else diff --git a/src/theory/quantifiers/sygus/sygus_unif_io.cpp b/src/theory/quantifiers/sygus/sygus_unif_io.cpp index 8c8f5ccd4..8207a07f2 100644 --- a/src/theory/quantifiers/sygus/sygus_unif_io.cpp +++ b/src/theory/quantifiers/sygus/sygus_unif_io.cpp @@ -16,6 +16,7 @@ #include "theory/quantifiers/sygus/sygus_unif_io.h" #include "options/quantifiers_options.h" +#include "theory/datatypes/sygus_datatype_utils.h" #include "theory/evaluator.h" #include "theory/quantifiers/sygus/example_infer.h" #include "theory/quantifiers/sygus/synth_conjecture.h" @@ -835,7 +836,8 @@ Node SygusUnifIo::constructSolutionNode(std::vector<Node>& lemmas) if (!vcc.isNull() && (d_solution.isNull() || (!d_solution.isNull() - && d_tds->getSygusTermSize(vcc) < d_sol_term_size))) + && datatypes::utils::getSygusTermSize(vcc) + < d_sol_term_size))) { if (Trace.isOn("sygus-pbe")) { @@ -846,7 +848,7 @@ Node SygusUnifIo::constructSolutionNode(std::vector<Node>& lemmas) } d_solution = vcc; newSolution = vcc; - d_sol_term_size = d_tds->getSygusTermSize(vcc); + d_sol_term_size = datatypes::utils::getSygusTermSize(vcc); Trace("sygus-pbe-sol") << "PBE solution size: " << d_sol_term_size << std::endl; // We've determined its feasible, now, enable information gain and diff --git a/src/theory/quantifiers/sygus/synth_conjecture.cpp b/src/theory/quantifiers/sygus/synth_conjecture.cpp index 1ddc2fa22..73bd6b8a4 100644 --- a/src/theory/quantifiers/sygus/synth_conjecture.cpp +++ b/src/theory/quantifiers/sygus/synth_conjecture.cpp @@ -827,7 +827,10 @@ Node SynthConjecture::getEnumeratedValue(Node e, bool& activeIncomplete) == options::SygusActiveGenMode::ENUM || options::sygusActiveGenMode() == options::SygusActiveGenMode::AUTO); - d_evg[e].reset(new SygusEnumerator(d_tds, this, d_stats)); + // if sygus repair const is enabled, we enumerate terms with free + // variables as arguments to any-constant constructors + d_evg[e].reset(new SygusEnumerator( + d_tds, this, &d_stats, false, options::sygusRepairConst())); } } Trace("sygus-active-gen") diff --git a/src/theory/quantifiers/sygus/synth_conjecture.h b/src/theory/quantifiers/sygus/synth_conjecture.h index e6645ddf2..04999da0d 100644 --- a/src/theory/quantifiers/sygus/synth_conjecture.h +++ b/src/theory/quantifiers/sygus/synth_conjecture.h @@ -26,6 +26,7 @@ #include "theory/quantifiers/sygus/cegis.h" #include "theory/quantifiers/sygus/cegis_core_connective.h" #include "theory/quantifiers/sygus/cegis_unif.h" +#include "theory/quantifiers/sygus/enum_val_generator.h" #include "theory/quantifiers/sygus/example_eval_cache.h" #include "theory/quantifiers/sygus/example_infer.h" #include "theory/quantifiers/sygus/sygus_process_conj.h" @@ -42,37 +43,6 @@ class CegGrammarConstructor; class SygusPbe; class SygusStatistics; -/** - * A base class for generating values for actively-generated enumerators. - * At a high level, the job of this class is to accept a stream of "abstract - * values" a1, ..., an, ..., and generate a (possibly larger) stream of - * "concrete values" c11, ..., c1{m_1}, ..., cn1, ... cn{m_n}, .... - */ -class EnumValGenerator -{ - public: - virtual ~EnumValGenerator() {} - /** initialize this class with enumerator e */ - virtual void initialize(Node e) = 0; - /** Inform this generator that abstract value v was enumerated. */ - virtual void addValue(Node v) = 0; - /** - * Increment this value generator. If this returns false, then we are out of - * values. If this returns true, getCurrent(), if non-null, returns the - * current term. - * - * Notice that increment() may return true and afterwards it may be the case - * getCurrent() is null. We do this so that increment() does not take too - * much time per call, which can be the case for grammars where it is - * difficult to find the next (non-redundant) term. Returning true with - * a null current term gives the caller the chance to interleave other - * reasoning. - */ - virtual bool increment() = 0; - /** Get the current concrete value generated by this class. */ - virtual Node getCurrent() = 0; -}; - /** a synthesis conjecture * This class implements approaches for a synthesis conjecture, given by data * member d_quant. diff --git a/src/theory/quantifiers/sygus/term_database_sygus.cpp b/src/theory/quantifiers/sygus/term_database_sygus.cpp index 826563401..3b0ea3312 100644 --- a/src/theory/quantifiers/sygus/term_database_sygus.cpp +++ b/src/theory/quantifiers/sygus/term_database_sygus.cpp @@ -359,23 +359,6 @@ Node TermDbSygus::sygusToBuiltin(Node n, TypeNode tn) return ret; } -unsigned TermDbSygus::getSygusTermSize( Node n ){ - if (n.getKind() != APPLY_CONSTRUCTOR) - { - return 0; - } - unsigned sum = 0; - for (unsigned i = 0; i < n.getNumChildren(); i++) - { - sum += getSygusTermSize(n[i]); - } - const DType& dt = datatypes::utils::datatypeOf(n.getOperator()); - int cindex = datatypes::utils::indexOf(n.getOperator()); - Assert(cindex >= 0 && cindex < (int)dt.getNumConstructors()); - unsigned weight = dt[cindex].getWeight(); - return weight + sum; -} - bool TermDbSygus::registerSygusType(TypeNode tn) { std::map<TypeNode, bool>::iterator it = d_registerStatus.find(tn); diff --git a/src/theory/quantifiers/sygus/term_database_sygus.h b/src/theory/quantifiers/sygus/term_database_sygus.h index e0a812069..80411b258 100644 --- a/src/theory/quantifiers/sygus/term_database_sygus.h +++ b/src/theory/quantifiers/sygus/term_database_sygus.h @@ -456,7 +456,6 @@ class TermDbSygus { Node getSygusNormalized( Node n, std::map< TypeNode, int >& var_count, std::map< Node, Node >& subs ); Node getNormalized(TypeNode t, Node prog); - unsigned getSygusTermSize( Node n ); /** involves div-by-zero */ bool involvesDivByZero( Node n ); /** get anchor */ diff --git a/src/theory/quantifiers/term_database.cpp b/src/theory/quantifiers/term_database.cpp index bedab16f1..523b84e65 100644 --- a/src/theory/quantifiers/term_database.cpp +++ b/src/theory/quantifiers/term_database.cpp @@ -480,9 +480,8 @@ void TermDb::addTermHo(Node n) Node psk; if (itp == d_ho_fun_op_purify.end()) { - psk = sm->mkDummySkolem("pfun", - curr.getType(), - "purify for function operator term indexing"); + psk = sm->mkPurifySkolem( + curr, "pfun", "purify for function operator term indexing"); d_ho_fun_op_purify[curr] = psk; // we do not add it to d_ops since it is an internal operator } @@ -1034,7 +1033,10 @@ bool TermDb::reset( Theory::Effort effort ){ eq = itpe->second; } Trace("quant-ho") << "- assert purify equality : " << eq << std::endl; - ee->assertEquality(eq, true, eq); + // Note that ee may be the central equality engine, in which case this + // equality is explained trivially with "true", since both sides of + // eq are HOL and FOL encodings of the same thing. + ee->assertEquality(eq, true, d_true); if (!ee->consistent()) { // In some rare cases, purification functions (in the domain of diff --git a/src/theory/sep/theory_sep.cpp b/src/theory/sep/theory_sep.cpp index efede77ba..98130beb5 100644 --- a/src/theory/sep/theory_sep.cpp +++ b/src/theory/sep/theory_sep.cpp @@ -52,7 +52,7 @@ TheorySep::TheorySep(context::Context* c, d_lemmas_produced_c(u), d_bounds_init(false), d_state(c, u, valuation), - d_im(*this, d_state, nullptr, "theory::sep::"), + d_im(*this, d_state, pnm, "theory::sep::"), d_notify(*this), d_reduce(u), d_spatial_assertions(c) @@ -1785,11 +1785,12 @@ void TheorySep::sendLemma( std::vector< Node >& ant, Node conc, InferenceId id, if( conc==d_false ){ Trace("sep-lemma") << "Sep::Conflict: " << ant << " by " << id << std::endl; - d_im.conflictExp(id, ant, nullptr); + d_im.conflictExp(id, PfRule::THEORY_INFERENCE, ant, {conc}); }else{ Trace("sep-lemma") << "Sep::Lemma: " << conc << " from " << ant << " by " << id << std::endl; - TrustNode trn = d_im.mkLemmaExp(conc, ant, {}); + TrustNode trn = + d_im.mkLemmaExp(conc, PfRule::THEORY_INFERENCE, ant, {}, {conc}); d_im.addPendingLemma( trn.getNode(), id, LemmaProperty::NONE, trn.getGenerator()); } diff --git a/src/theory/sets/inference_manager.cpp b/src/theory/sets/inference_manager.cpp index 73c6db35f..d0dc21839 100644 --- a/src/theory/sets/inference_manager.cpp +++ b/src/theory/sets/inference_manager.cpp @@ -92,7 +92,7 @@ bool InferenceManager::assertFactRec(Node fact, InferenceId id, Node exp, int in || (atom.getKind() == EQUAL && atom[0].getType().isSet())) { // send to equality engine - if (assertInternalFact(atom, polarity, id, exp)) + if (assertSetsFact(atom, polarity, id, exp)) { // return true if this wasn't redundant return true; @@ -111,6 +111,17 @@ bool InferenceManager::assertFactRec(Node fact, InferenceId id, Node exp, int in } return false; } + +bool InferenceManager::assertSetsFact(Node atom, + bool polarity, + InferenceId id, + Node exp) +{ + Node conc = polarity ? atom : atom.notNode(); + return assertInternalFact( + atom, polarity, id, PfRule::THEORY_INFERENCE, {exp}, {conc}); +} + void InferenceManager::assertInference(Node fact, InferenceId id, Node exp, diff --git a/src/theory/sets/inference_manager.h b/src/theory/sets/inference_manager.h index 7a64b10c7..0a7c42e11 100644 --- a/src/theory/sets/inference_manager.h +++ b/src/theory/sets/inference_manager.h @@ -71,6 +71,10 @@ class InferenceManager : public InferenceManagerBuffered InferenceId id, std::vector<Node>& exp, int inferType = 0); + /** + * Immediately assert an internal fact with the default handling of proofs. + */ + bool assertSetsFact(Node atom, bool polarity, InferenceId id, Node exp); /** flush the splitting lemma ( n OR (NOT n) ) * diff --git a/src/theory/sets/theory_sets.cpp b/src/theory/sets/theory_sets.cpp index 439e9443d..718077888 100644 --- a/src/theory/sets/theory_sets.cpp +++ b/src/theory/sets/theory_sets.cpp @@ -36,7 +36,7 @@ TheorySets::TheorySets(context::Context* c, : Theory(THEORY_SETS, c, u, out, valuation, logicInfo, pnm), d_skCache(), d_state(c, u, valuation, d_skCache), - d_im(*this, d_state, nullptr), + d_im(*this, d_state, pnm), d_internal(new TheorySetsPrivate(*this, d_state, d_im, d_skCache, pnm)), d_notify(*d_internal.get(), d_im) { diff --git a/src/theory/sets/theory_sets_private.cpp b/src/theory/sets/theory_sets_private.cpp index 42e317402..3817079a3 100644 --- a/src/theory/sets/theory_sets_private.cpp +++ b/src/theory/sets/theory_sets_private.cpp @@ -85,7 +85,7 @@ void TheorySetsPrivate::eqNotifyNewClass(TNode t) void TheorySetsPrivate::eqNotifyMerge(TNode t1, TNode t2) { - if (!d_state.isInConflict()) + if (!d_state.isInConflict() && t1.getType().isSet()) { Trace("sets-prop-debug") << "Merge " << t1 << " and " << t2 << "..." << std::endl; @@ -108,14 +108,15 @@ void TheorySetsPrivate::eqNotifyMerge(TNode t1, TNode t2) // infer equality between elements of singleton Node exp = s1.eqNode(s2); Node eq = s1[0].eqNode(s2[0]); - d_im.assertInternalFact(eq, true, InferenceId::SETS_SINGLETON_EQ, exp); + d_im.assertSetsFact(eq, true, InferenceId::SETS_SINGLETON_EQ, exp); } else { // singleton equal to emptyset, conflict Trace("sets-prop") << "Propagate conflict : " << s1 << " == " << s2 << std::endl; - d_im.conflictEqConstantMerge(s1, s2); + Node eqs = s1.eqNode(s2); + d_im.conflict(eqs, InferenceId::SETS_EQ_CONFLICT); return; } } @@ -149,7 +150,7 @@ void TheorySetsPrivate::eqNotifyMerge(TNode t1, TNode t2) Assert(f.getKind() == kind::IMPLIES); Trace("sets-prop") << "Propagate eq-mem eq inference : " << f[0] << " => " << f[1] << std::endl; - d_im.assertInternalFact(f[1], true, InferenceId::SETS_EQ_MEM, f[0]); + d_im.assertSetsFact(f[1], true, InferenceId::SETS_EQ_MEM, f[0]); } } } @@ -829,7 +830,7 @@ void TheorySetsPrivate::notifyFact(TNode atom, bool polarity, TNode fact) Trace("sets-prop") << "Propagate mem-eq : " << pexp << std::endl; Node eq = s[0].eqNode(atom[0]); // triggers an internal inference - d_im.assertInternalFact(eq, true, InferenceId::SETS_MEM_EQ, pexp); + d_im.assertSetsFact(eq, true, InferenceId::SETS_MEM_EQ, pexp); } } else @@ -907,7 +908,7 @@ void TheorySetsPrivate::addCarePairs(TNodeTrie* t1, { Trace("sets-cg-lemma") << "Should split on : " << x << "==" << y << std::endl; - d_im.split(x.eqNode(y), InferenceId::UNKNOWN); + d_im.split(x.eqNode(y), InferenceId::SETS_CG_SPLIT); } } } diff --git a/src/theory/shared_solver.cpp b/src/theory/shared_solver.cpp index f2266c236..b020a3938 100644 --- a/src/theory/shared_solver.cpp +++ b/src/theory/shared_solver.cpp @@ -57,13 +57,17 @@ void SharedSolver::preRegister(TNode atom) // See term_registration_visitor.h for more details. if (d_logicInfo.isSharingEnabled()) { - // register it with the shared terms database if sharing is enabled - preRegisterSharedInternal(atom); // Collect the shared terms in atom, as well as calling preregister on the // appropriate theories in atom. // This calls Theory::preRegisterTerm and Theory::addSharedTerm, possibly // multiple times. NodeVisitor<SharedTermsVisitor>::run(d_sharedTermsVisitor, atom); + // Register it with the shared terms database if sharing is enabled. + // Notice that this must come *after* the above call, since we must ensure + // that all subterms of atom have already been added to the central + // equality engine before atom is added. This avoids spurious notifications + // from the equality engine. + preRegisterSharedInternal(atom); } else { diff --git a/src/theory/theory.cpp b/src/theory/theory.cpp index 10c31edb7..891a3ca08 100644 --- a/src/theory/theory.cpp +++ b/src/theory/theory.cpp @@ -598,5 +598,26 @@ eq::EqualityEngine* Theory::getEqualityEngine() return d_equalityEngine; } +bool Theory::usesCentralEqualityEngine() const +{ + return usesCentralEqualityEngine(d_id); +} + +bool Theory::usesCentralEqualityEngine(TheoryId id) +{ + if (id == THEORY_BUILTIN) + { + return true; + } + if (options::eeMode() == options::EqEngineMode::DISTRIBUTED) + { + return false; + } + // Below are all theories with an equality engine except id ==THEORY_ARITH + return id == THEORY_UF || id == THEORY_DATATYPES || id == THEORY_BAGS + || id == THEORY_FP || id == THEORY_SETS || id == THEORY_STRINGS + || id == THEORY_SEP || id == THEORY_ARRAYS || id == THEORY_BV; +} + } // namespace theory } // namespace cvc5 diff --git a/src/theory/theory.h b/src/theory/theory.h index 4dbb7a436..7149d8e3f 100644 --- a/src/theory/theory.h +++ b/src/theory/theory.h @@ -307,13 +307,13 @@ class Theory { * class (see addSharedTerm). */ virtual void notifySharedTerm(TNode n); - /** * Notify in conflict, called when a conflict clause is added to TheoryEngine * by any theory (not necessarily this one). This signals that the theory * should suspend what it is currently doing and wait for backtracking. */ virtual void notifyInConflict(); + public: //--------------------------------- initialization /** @@ -876,6 +876,11 @@ class Theory { * E |= lit in the theory. */ virtual std::pair<bool, Node> entailmentCheck(TNode lit); + + /** uses central equality engine */ + bool usesCentralEqualityEngine() const; + /** uses central equality engine (static) */ + static bool usesCentralEqualityEngine(TheoryId id); };/* class Theory */ std::ostream& operator<<(std::ostream& os, theory::Theory::Effort level); diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp index bf196273e..f98f1cb6c 100644 --- a/src/theory/theory_engine.cpp +++ b/src/theory/theory_engine.cpp @@ -899,7 +899,7 @@ void TheoryEngine::assertToTheory(TNode assertion, TNode originalAssertion, theo Trace("theory::propagate") << "TheoryEngine::assertToTheory(" << assertion << ", " << toTheoryId << ", " << fromTheoryId << "): conflict (no sharing)" << endl; Trace("dtview::conflict") << ":THEORY-CONFLICT: " << assertion << std::endl; - d_inConflict = true; + markInConflict(); } else { return; } @@ -954,7 +954,7 @@ void TheoryEngine::assertToTheory(TNode assertion, TNode originalAssertion, theo << endl; Trace("dtview::conflict") << ":THEORY-CONFLICT: " << assertion << std::endl; - d_inConflict = true; + markInConflict(); } } return; diff --git a/src/theory/theory_inference_manager.cpp b/src/theory/theory_inference_manager.cpp index bfd23fb23..bad90f061 100644 --- a/src/theory/theory_inference_manager.cpp +++ b/src/theory/theory_inference_manager.cpp @@ -393,7 +393,7 @@ bool TheoryInferenceManager::processInternalFact(TNode atom, Assert(d_ee != nullptr); Trace("infer-manager") << "TheoryInferenceManager::assertInternalFact: " << (pol ? Node(atom) : atom.notNode()) << " from " - << expn << std::endl; + << expn << " / " << iid << " " << id << std::endl; if (Configuration::isAssertionBuild()) { // check that all facts hold in the equality engine, to ensure that we @@ -431,10 +431,10 @@ bool TheoryInferenceManager::processInternalFact(TNode atom, } d_numCurrentFacts++; // Now, assert the fact. How to do so depends on whether proofs are enabled. - // If no proof production, or no proof rule was given bool ret = false; - if (d_pfee == nullptr || id == PfRule::UNKNOWN) + if (d_pfee == nullptr) { + Trace("infer-manager") << "...assert without proofs..." << std::endl; if (atom.getKind() == kind::EQUAL) { ret = d_ee->assertEquality(atom, pol, expn); @@ -453,6 +453,8 @@ bool TheoryInferenceManager::processInternalFact(TNode atom, } else { + Assert(id != PfRule::UNKNOWN); + Trace("infer-manager") << "...assert with proofs..." << std::endl; // Note that we reconstruct the original literal lit here, since both the // original literal is needed for bookkeeping proofs. It is possible to // optimize this so that a few less nodes are created, but at the cost @@ -472,7 +474,8 @@ bool TheoryInferenceManager::processInternalFact(TNode atom, // call the notify fact method with isInternal = true d_theory.notifyFact(atom, pol, expn, true); Trace("infer-manager") - << "TheoryInferenceManager::finished assertInternalFact" << std::endl; + << "TheoryInferenceManager::finished assertInternalFact, ret=" << ret + << std::endl; return ret; } diff --git a/src/theory/theory_inference_manager.h b/src/theory/theory_inference_manager.h index c61f4311b..cea0e698b 100644 --- a/src/theory/theory_inference_manager.h +++ b/src/theory/theory_inference_manager.h @@ -132,7 +132,7 @@ class TheoryInferenceManager * EqualityEngineNotify::eqNotifyTriggerPredicate and * EqualityEngineNotify::eqNotifyTriggerTermEquality. */ - bool propagateLit(TNode lit); + virtual bool propagateLit(TNode lit); /** * Return an explanation for the literal represented by parameter lit * (which was previously propagated by this theory). By default, this @@ -300,6 +300,8 @@ class TheoryInferenceManager * Theory's preNotifyFact and notifyFact method have been called with * isInternal = true. * + * Note this method should never be used when proofs are enabled. + * * @param atom The atom of the fact to assert * @param pol Its polarity * @param exp Its explanation, i.e. ( exp => (~) atom ) is valid. diff --git a/src/theory/uf/eq_proof.cpp b/src/theory/uf/eq_proof.cpp index 6b2fae389..27b61e87d 100644 --- a/src/theory/uf/eq_proof.cpp +++ b/src/theory/uf/eq_proof.cpp @@ -1049,6 +1049,13 @@ Node EqProof::addToProof(CDProof* p, } // build constant application (f c1 ... cn) and equality (= (f c1 ... cn) c) Kind k = d_node[0].getKind(); + std::vector<Node> cargs; + cargs.push_back(ProofRuleChecker::mkKindNode(k)); + if (d_node[0].getMetaKind() == kind::metakind::PARAMETERIZED) + { + constChildren.insert(constChildren.begin(), d_node[0].getOperator()); + cargs.push_back(d_node[0].getOperator()); + } Node constApp = NodeManager::currentNM()->mkNode(k, constChildren); Node constEquality = constApp.eqNode(d_node[1]); Trace("eqproof-conv") << "EqProof::addToProof: adding " @@ -1060,11 +1067,7 @@ Node EqProof::addToProof(CDProof* p, Trace("eqproof-conv") << "EqProof::addToProof: adding " << PfRule::CONG << " step for " << congConclusion << " from " << subChildren << "\n"; - p->addStep(congConclusion, - PfRule::CONG, - {subChildren}, - {ProofRuleChecker::mkKindNode(k)}, - true); + p->addStep(congConclusion, PfRule::CONG, {subChildren}, cargs, true); Trace("eqproof-conv") << "EqProof::addToProof: adding " << PfRule::TRANS << " step for original conclusion " << d_node << "\n"; std::vector<Node> transitivityChildren{congConclusion, constEquality}; diff --git a/src/util/iand.h b/src/util/iand.h index 117ab6f02..6c29c38b4 100644 --- a/src/util/iand.h +++ b/src/util/iand.h @@ -39,7 +39,7 @@ struct IntAnd inline std::ostream& operator<<(std::ostream& os, const IntAnd& ia); inline std::ostream& operator<<(std::ostream& os, const IntAnd& ia) { - return os << "[" << ia.d_size << "]"; + return os << "(_ iand " << ia.d_size << ")"; } } // namespace cvc5 diff --git a/test/regress/CMakeLists.txt b/test/regress/CMakeLists.txt index db6a7ae48..151e01088 100644 --- a/test/regress/CMakeLists.txt +++ b/test/regress/CMakeLists.txt @@ -604,6 +604,7 @@ set(regress_0_tests regress0/fp/issue6164.smt2 regress0/fp/rti_3_5_bug.smt2 regress0/fp/simple.smt2 + regress0/fp/word-blast.smt2 regress0/fp/wrong-model.smt2 regress0/fuzz_1.smtv1.smt2 regress0/fuzz_3.smtv1.smt2 @@ -1649,6 +1650,7 @@ set(regress_1_tests regress1/issue5739-rtf-processed.smt2 regress1/lemmas/clocksynchro_5clocks.main_invar.base.smtv1.smt2 regress1/lemmas/pursuit-safety-8.smtv1.smt2 + regress1/minimal_unsat_core.smt2 regress1/model-blocker-simple.smt2 regress1/model-blocker-values.smt2 regress1/nl/approx-sqrt.smt2 @@ -2504,6 +2506,7 @@ set(regress_2_tests regress2/strings/issue6057-replace-re-all-simplified.smt2 regress2/strings/issue6636-replace-re-all.smt2 regress2/strings/issue6637-replace-re-all.smt2 + regress2/strings/issue6639-replace-re-all.smt2 regress2/strings/issue918.smt2 regress2/strings/non_termination_regular_expression6.smt2 regress2/strings/range-perf.smt2 diff --git a/test/regress/regress0/fp/word-blast.smt2 b/test/regress/regress0/fp/word-blast.smt2 new file mode 100644 index 000000000..65290a3b3 --- /dev/null +++ b/test/regress/regress0/fp/word-blast.smt2 @@ -0,0 +1,15 @@ +; COMMAND-LINE: --fp-lazy-wb +; EXPECT: unsat +(set-logic QF_BVFP) +(declare-fun meters_ackermann!0 () (_ BitVec 64)) +(assert + (let ((?x8 ((_ to_fp 11 53) meters_ackermann!0))) + (fp.geq ?x8 ((_ to_fp 11 53) (_ bv0 64))))) +(assert + (let ((?x8 ((_ to_fp 11 53) meters_ackermann!0))) + (fp.leq ?x8 ((_ to_fp 11 53) (_ bv4652007308841189376 64))))) +(assert + (let ((?x19 ((_ sign_extend 32) ((_ fp.to_sbv 32) roundTowardZero (fp.abs ((_ to_fp 11 53) meters_ackermann!0)))))) +(not (not (bvult (_ bv9223372036854775807 64) ?x19))))) +(check-sat) +(exit) diff --git a/test/regress/regress1/minimal_unsat_core.smt2 b/test/regress/regress1/minimal_unsat_core.smt2 new file mode 100644 index 000000000..ef1d3ceef --- /dev/null +++ b/test/regress/regress1/minimal_unsat_core.smt2 @@ -0,0 +1,12 @@ +; COMMAND-LINE: --minimal-unsat-cores +(set-logic QF_NIA) +(set-info :status unsat) +(declare-fun n () Int) + +(assert (= n 0)) +(assert (= (div (div n n) n) + (div (div (div n n) n) n))) +(assert (distinct (div (div n n) n) + (div (div (div (div (div n n) n) n) n) n))) + +(check-sat) diff --git a/test/regress/regress1/quantifiers/symmetric_unsat_7.smt2 b/test/regress/regress1/quantifiers/symmetric_unsat_7.smt2 index 68748d4a5..7249e87aa 100644 --- a/test/regress/regress1/quantifiers/symmetric_unsat_7.smt2 +++ b/test/regress/regress1/quantifiers/symmetric_unsat_7.smt2 @@ -1,4 +1,8 @@ ; COMMAND-LINE: --no-check-unsat-cores --no-produce-proofs +; EXPECT: unsat + +; Note we require disabling proofs/unsat cores due to timeouts in nightlies + (set-logic AUFLIRA) (set-info :source | Example extracted from Peter Baumgartner's talk at CADE-21: Logical Engineering with Instance-Based Methods. diff --git a/test/regress/regress1/strings/stoi-400million.smt2 b/test/regress/regress1/strings/stoi-400million.smt2 index 1b1fca2ac..42dfb1bfc 100644 --- a/test/regress/regress1/strings/stoi-400million.smt2 +++ b/test/regress/regress1/strings/stoi-400million.smt2 @@ -1,4 +1,4 @@ -; COMMAND-LINE: --strings-exp --no-produce-proofs --no-jh-rlv-order +; COMMAND-LINE: --strings-exp --no-jh-rlv-order ; EXPECT: sat (set-info :smt-lib-version 2.6) (set-logic ALL) diff --git a/test/regress/regress2/strings/issue6639-replace-re-all.smt2 b/test/regress/regress2/strings/issue6639-replace-re-all.smt2 new file mode 100644 index 000000000..2384f82ef --- /dev/null +++ b/test/regress/regress2/strings/issue6639-replace-re-all.smt2 @@ -0,0 +1,6 @@ +(set-logic QF_SLIA) +(declare-fun a () String) +(assert (= a "")) +(assert (not (str.in_re (str.replace_re_all "b" (re.comp (str.to_re a)) "a") (str.to_re "a")))) +(set-info :status unsat) +(check-sat) diff --git a/test/unit/api/op_black.cpp b/test/unit/api/op_black.cpp index 8c4e31c22..fd45b1c22 100644 --- a/test/unit/api/op_black.cpp +++ b/test/unit/api/op_black.cpp @@ -92,6 +92,52 @@ TEST_F(TestApiBlackOp, getNumIndices) ASSERT_EQ(2, regexp_loop.getNumIndices()); } +TEST_F(TestApiBlackOp, subscriptOperator) +{ + Op plus = d_solver.mkOp(PLUS); + Op divisible = d_solver.mkOp(DIVISIBLE, 4); + Op bitvector_repeat = d_solver.mkOp(BITVECTOR_REPEAT, 4); + Op bitvector_zero_extend = d_solver.mkOp(BITVECTOR_ZERO_EXTEND, 4); + Op bitvector_sign_extend = d_solver.mkOp(BITVECTOR_SIGN_EXTEND, 4); + Op bitvector_rotate_left = d_solver.mkOp(BITVECTOR_ROTATE_LEFT, 4); + Op bitvector_rotate_right = d_solver.mkOp(BITVECTOR_ROTATE_RIGHT, 4); + Op int_to_bitvector = d_solver.mkOp(INT_TO_BITVECTOR, 4); + Op iand = d_solver.mkOp(IAND, 4); + Op floatingpoint_to_ubv = d_solver.mkOp(FLOATINGPOINT_TO_UBV, 4); + Op floatingopint_to_sbv = d_solver.mkOp(FLOATINGPOINT_TO_SBV, 4); + Op floatingpoint_to_fp_ieee_bitvector = + d_solver.mkOp(FLOATINGPOINT_TO_FP_IEEE_BITVECTOR, 4, 5); + Op floatingpoint_to_fp_floatingpoint = + d_solver.mkOp(FLOATINGPOINT_TO_FP_FLOATINGPOINT, 4, 5); + Op floatingpoint_to_fp_real = d_solver.mkOp(FLOATINGPOINT_TO_FP_REAL, 4, 5); + Op floatingpoint_to_fp_signed_bitvector = + d_solver.mkOp(FLOATINGPOINT_TO_FP_SIGNED_BITVECTOR, 4, 5); + Op floatingpoint_to_fp_unsigned_bitvector = + d_solver.mkOp(FLOATINGPOINT_TO_FP_UNSIGNED_BITVECTOR, 4, 5); + Op floatingpoint_to_fp_generic = + d_solver.mkOp(FLOATINGPOINT_TO_FP_GENERIC, 4, 5); + Op regexp_loop = d_solver.mkOp(REGEXP_LOOP, 4, 5); + + ASSERT_THROW(plus[0], CVC5ApiException); + ASSERT_EQ(4, divisible[0].getUInt32Value()); + ASSERT_EQ(4, bitvector_repeat[0].getUInt32Value()); + ASSERT_EQ(4, bitvector_zero_extend[0].getUInt32Value()); + ASSERT_EQ(4, bitvector_sign_extend[0].getUInt32Value()); + ASSERT_EQ(4, bitvector_rotate_left[0].getUInt32Value()); + ASSERT_EQ(4, bitvector_rotate_right[0].getUInt32Value()); + ASSERT_EQ(4, int_to_bitvector[0].getUInt32Value()); + ASSERT_EQ(4, iand[0].getUInt32Value()); + ASSERT_EQ(4, floatingpoint_to_ubv[0].getUInt32Value()); + ASSERT_EQ(4, floatingopint_to_sbv[0].getUInt32Value()); + ASSERT_EQ(4, floatingpoint_to_fp_ieee_bitvector[0].getUInt32Value()); + ASSERT_EQ(4, floatingpoint_to_fp_floatingpoint[0].getUInt32Value()); + ASSERT_EQ(4, floatingpoint_to_fp_real[0].getUInt32Value()); + ASSERT_EQ(4, floatingpoint_to_fp_signed_bitvector[0].getUInt32Value()); + ASSERT_EQ(4, floatingpoint_to_fp_unsigned_bitvector[0].getUInt32Value()); + ASSERT_EQ(4, floatingpoint_to_fp_generic[0].getUInt32Value()); + ASSERT_EQ(4, regexp_loop[0].getUInt32Value()); +} + TEST_F(TestApiBlackOp, getIndicesString) { Op x; @@ -207,6 +253,22 @@ TEST_F(TestApiBlackOp, getIndicesPairUint) CVC5ApiException); } +TEST_F(TestApiBlackOp, getIndicesVector) +{ + std::vector<uint32_t> indices = {0, 3, 2, 0, 1, 2}; + Op tuple_project_op = d_solver.mkOp(TUPLE_PROJECT, indices); + + ASSERT_TRUE(tuple_project_op.isIndexed()); + std::vector<Term> tuple_project_extract_indices = + tuple_project_op.getIndices<std::vector<Term>>(); + ASSERT_THROW(tuple_project_op.getIndices<std::string>(), CVC5ApiException); + for (size_t i = 0; i < indices.size(); i++) + { + ASSERT_EQ(indices[i], tuple_project_extract_indices[i].getUInt32Value()); + ASSERT_EQ(indices[i], tuple_project_op[i].getUInt32Value()); + } +} + TEST_F(TestApiBlackOp, opScopingToString) { Op bitvector_repeat_ot = d_solver.mkOp(BITVECTOR_REPEAT, 5); diff --git a/test/unit/node/node_black.cpp b/test/unit/node/node_black.cpp index 522270de4..787dd77ca 100644 --- a/test/unit/node/node_black.cpp +++ b/test/unit/node/node_black.cpp @@ -27,6 +27,7 @@ #include "expr/node_manager.h" #include "expr/node_value.h" #include "expr/skolem_manager.h" +#include "options/options_public.h" #include "smt/smt_engine.h" #include "test_node.h" #include "theory/rewriter.h" @@ -68,7 +69,7 @@ class TestNodeBlackNode : public TestNode argv[0] = strdup(""); argv[1] = strdup("--output-lang=ast"); std::string progName; - Options::parseOptions(&opts, 2, argv, progName); + options::parse(opts, 2, argv, progName); free(argv[0]); free(argv[1]); d_smt.reset(new SmtEngine(d_nodeManager.get(), &opts)); diff --git a/test/unit/theory/theory_black.cpp b/test/unit/theory/theory_black.cpp index 766d696a1..d8ae8e468 100644 --- a/test/unit/theory/theory_black.cpp +++ b/test/unit/theory/theory_black.cpp @@ -53,13 +53,12 @@ TEST_F(TestTheoryBlack, array_const) arr = d_nodeManager->mkNode(STORE, storeAll, zero, one); ASSERT_TRUE(arr.isConst()); Node arr2 = d_nodeManager->mkNode(STORE, arr, one, zero); - ASSERT_FALSE(arr2.isConst()); arr2 = Rewriter::rewrite(arr2); ASSERT_TRUE(arr2.isConst()); arr2 = d_nodeManager->mkNode(STORE, arr, one, one); + arr2 = Rewriter::rewrite(arr2); ASSERT_TRUE(arr2.isConst()); arr2 = d_nodeManager->mkNode(STORE, arr, zero, one); - ASSERT_FALSE(arr2.isConst()); arr2 = Rewriter::rewrite(arr2); ASSERT_TRUE(arr2.isConst()); |