diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 73a2bd2e4ab..7190936a7af 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,7 +63,7 @@ jobs: - name: win64:production os: windows-latest - config: production --auto-download + config: production --auto-download --java-bindings cache-key: production-win64-native strip-bin: strip windows-build: true diff --git a/AUTHORS b/AUTHORS index bb498504588..499c84ed0cc 100644 --- a/AUTHORS +++ b/AUTHORS @@ -10,18 +10,15 @@ Current: Leni Aniva, Stanford University Haniel Barbosa, The University of Iowa, Universidade Federal de Minas Gerais Clark Barrett, New York University, Google, Stanford University - Martin Brain, University of Oxford - Vinicius Camillo, Universidade Federal de Minas Gerais - Gereon Kremer, Stanford University Hanna Lachnitt, Stanford University + Daniel Larraz, The University of Iowa Abdalrhman Mohamed, The University of Iowa Mudathir Mohamed, The University of Iowa Aina Niemetz, Stanford University - Andres Noetzli, Stanford University Alex Ozdemir, Stanford University Mathias Preiner, Stanford University Andrew Reynolds, The University of Iowa, EPFL - Ying Sheng, Stanford University + Hans-Jörg Schurr, The University of Iowa Cesare Tinelli, The University of Iowa Amalee Wilson, Stanford University Yoni Zohar, Stanford University @@ -29,16 +26,21 @@ Current: Alumni: Kshitij Bansal, New York University, Google Francois Bobot, The University of Iowa, Commissariat a l'Energie Atomique + Martin Brain, University of Oxford + Vinicius Camillo, Universidade Federal de Minas Gerais Christopher Conway, New York University, Google Morgan Deters, New York University Liana Hadarean, New York University, Mentor Graphics Corporation Ahmed Irfan, Stanford University Dejan Jovanovic, New York University, SRI International Guy Katz, New York University, Stanford University - Makai Mann, Stanford University Tim King, New York University, Universite Joseph Fourier, Google + Gereon Kremer, Stanford University Tianyi Liang, The University of Iowa + Makai Mann, Stanford University Paul Meng, The University of Iowa + Andres Noetzli, Stanford University + Ying Sheng, Stanford University Other contributors to the cvc5 codebase are listed in the THANKS file. diff --git a/CMakeLists.txt b/CMakeLists.txt index ee7f8daef68..eb7a345512f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,7 +99,11 @@ cvc5_option(ENABLE_TRACING "Enable tracing") cvc5_option(ENABLE_UNIT_TESTING "Enable unit testing") cvc5_option(ENABLE_VALGRIND "Enable valgrind instrumentation") cvc5_option(ENABLE_AUTO_DOWNLOAD "Enable automatic download of dependencies") +cvc5_option(USE_PYTHON_VENV "Install Python dependencies in virtual environment") cvc5_option(ENABLE_IPO "Enable interprocedural optimization") + +cvc5_set_option(USE_PYTHON_VENV ON) + # >> 2-valued: ON OFF # > for options where we don't need to detect if set by user (default: OFF) option(ENABLE_COVERAGE "Enable support for gcov coverage testing") @@ -364,37 +368,52 @@ else() endif() endif() -if(DEFINED BUILD_BINDINGS_PYTHON_VERSION) - find_package(Python ${BUILD_BINDINGS_PYTHON_VERSION} EXACT COMPONENTS Interpreter REQUIRED) +# Find a Python interpreter on the system. It serves two exclusive purposes: +# either to be used when the '--auto-download' option is not specified, or +# to create a virtual environment when the '--auto-download' option is given. +# In the latter case, it is also used to install the Python bindings (if built). +if(DEFINED BUILD_BINDINGS_PYTHON_VERSION) + find_package(Python ${BUILD_BINDINGS_PYTHON_VERSION} + EXACT COMPONENTS Interpreter REQUIRED + ) else() find_package(Python COMPONENTS Interpreter REQUIRED) endif() # Keep the PATH to the Python binary before setting venv. +# Use a CACHE variable to remember the original value. # We use this path in the installation step of the Python bindings. -set(Python_BASE_EXECUTABLE "${Python_EXECUTABLE}") - -# Set up Python venv if one doesn't exist already -set(VENV_PATH "${CMAKE_BINARY_DIR}/venv-${Python_VERSION}") -if(NOT EXISTS ${VENV_PATH}) - message(STATUS "Creating Python virtual environment") - # Use packages installed in the base installation if they're already present - execute_process (COMMAND - "${Python_EXECUTABLE}" -m venv --system-site-packages "${VENV_PATH}" - ) -endif() +set(Python_BASE_EXECUTABLE "${Python_EXECUTABLE}" + CACHE STRING "Base Python executable") + +if(ENABLE_AUTO_DOWNLOAD AND USE_PYTHON_VENV) + # Set up Python venv if one doesn't exist already + set(VENV_PATH "${CMAKE_BINARY_DIR}/venv-${Python_VERSION}") + if(NOT EXISTS ${VENV_PATH}) + message(STATUS "Creating Python virtual environment") + # Use packages installed in the base installation if they're already present + execute_process ( + COMMAND + "${Python_EXECUTABLE}" -m venv --system-site-packages "${VENV_PATH}" + RESULT_VARIABLE VENV_CMD_EXIT_CODE + ) + if(VENV_CMD_EXIT_CODE) + message(FATAL_ERROR "Could not create Python virtual environment") + endif() + endif() -# Find (and set up) the Python interpreter within the virtual environment -set(ENV{VIRTUAL_ENV} "${VENV_PATH}") -set(Python_FIND_VIRTUALENV FIRST) -unset(Python_EXECUTABLE) -unset(Python_EXECUTABLE CACHE) + # Set up variables to find Python within the virtual environment + set(ENV{VIRTUAL_ENV} "${VENV_PATH}") + unset(Python_EXECUTABLE CACHE) + if(EXISTS ${VENV_PATH}/bin/python3) + set(Python_EXECUTABLE ${VENV_PATH}/bin/python3) + elseif(EXISTS ${VENV_PATH}/Scripts/python.exe) + set(Python_EXECUTABLE ${VENV_PATH}/Scripts/python.exe) + else() + message(FATAL_ERROR "Could not find Python interpreter in venv") + endif() -if(BUILD_BINDINGS_PYTHON) - # If the component Development is requested, CMake strongly recommends to - # also include the component Interpreter to get expected result. - find_package(Python COMPONENTS Interpreter Development REQUIRED) -else() + # Find the Python interpreter within the virtual environment find_package(Python COMPONENTS Interpreter REQUIRED) endif() diff --git a/INSTALL.rst b/INSTALL.rst index 6c1208218b8..4eba0a51bab 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -127,8 +127,9 @@ versions; more recent versions should be compatible. - `CaDiCaL >= 1.6.0 (SAT solver) `_ - `SymFPU `_ -The Python modules will be installed automatically in -a virtual environment if they are missing. +If ``--auto-download`` is given, the Python modules will be installed automatically in +a virtual environment if they are missing. To install the modules globally and skip +the creation of the virtual environment, configure cvc5 with ``./configure.sh --no-pyvenv``. CaDiCaL (SAT solver) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -271,8 +272,9 @@ Dependencies for Language Bindings - `pytest `_ - The source for the `pythonic API <(https://github.com/cvc5/cvc5_pythonic_api)>`. -The Python modules will be installed automatically in -a virtual environment if they are missing. +If ``--auto-download`` is given, the Python modules will be installed automatically in +a virtual environment if they are missing. To install the modules globally and skip +the creation of the virtual environment, configure cvc5 with ``./configure.sh --no-pyvenv``. If configured with ``--pythonic-path=PATH``, the build system will expect the Pythonic API's source to be at ``PATH``. Otherwise, if configured with ``--auto-download``, the build system will download it. diff --git a/THANKS b/THANKS index 1e4c092081a..0bbd7aab2d4 100644 --- a/THANKS +++ b/THANKS @@ -15,6 +15,8 @@ Thanks to: - Simon Dierl for fixing the ENABLE_BEST option in the build system in 2019. +- Florian Frohn for fixing the compilation of cvc5 with musl-libc in 2023. + - Finn Haedicke of University of Bremen, Germany for fixing namespace specifiers in CVC4's version of minisat in 2015. @@ -23,6 +25,8 @@ Thanks to: - Thomas Hunger for some important patches to CVC4's SWIG interfaces in March 2014. +- Jerry James for multiple bugfixes in 2023. + - Andrew V. Jones (now Teylu) for several fixes, refactoring the GLPK-based approximating Simplex solver, and working towards a native Windows build since 2019. @@ -34,20 +38,38 @@ Thanks to: - Cristian Mattarei of Stanford University for fixing an issue with parsing floating point numbers in 2017. +- José Neto for improvements to build system and web version of cvc5 in + 2023 and 2024. + +- Tobias Nießen for a correction to the API documentation in 2023. + - Jordy Ruiz of University of Toulouse for fixing throw specifiers on the theory output channels in 2015. +- Áron Ricardo Perez-Lopez for solving an issue with static compilation in 2023. + - Clement Pit-Claudel of MIT for improving the signal handling support for Windows builds in 2017. +- Sorawee Porncharoenwase for fixing an issue related to compiling cvc5 with + CoCoA on M1/M2 CPUs. + +- sarkoxed for extending the finite fields API to allow custom bases for string + representations in 2023. + - Florian Schanda for improving the readability of output of get-model in 2018. - Tom Smeding for a fix in the contrib/get-antlr-3.4 script in 2018. +- Scott Talbert for improvements to the build systems and correcting spelling + errors in the documentation in 2023 and 2024. + - Piotr Troja for several fixes in 2019. - Arjun Viswanathan for improvements in the CVC and the SMT2 parser. +- Anjiang-Wei for fixing a typo in the Python API documentation in 2023. + - Fabian Wolff in 2016 for fixing several spelling mistakes. - Justin Xu for contributing to refactoring CVC4's preprocessing infrastructure. diff --git a/cmake/FindCLN.cmake b/cmake/FindCLN.cmake index c0484c34531..90b07e36bd9 100644 --- a/cmake/FindCLN.cmake +++ b/cmake/FindCLN.cmake @@ -47,40 +47,87 @@ if(NOT CLN_FOUND_SYSTEM) include(ExternalProject) fail_if_cross_compiling("Windows" "" "CLN" "autoconf fails") - fail_if_cross_compiling("" "arm" "CLN" "syntax error in configure") + + if("${CMAKE_GENERATOR}" STREQUAL "MSYS Makefiles") + message(FATAL_ERROR + "Compilation of CLN in the MSYS2 environment is not supported." + ) + endif() set(CLN_VERSION "1.3.7") + set(CLN_SO_MAJOR_VER "6") + set(CLN_SO_MINOR_VER "0") + set(CLN_SO_PATCH_VER "7") + set(CLN_SO_VERSION + "${CLN_SO_MAJOR_VER}.${CLN_SO_MINOR_VER}.${CLN_SO_PATCH_VER}" + ) string(REPLACE "." "-" CLN_TAG ${CLN_VERSION}) find_program(AUTORECONF autoreconf) if(NOT AUTORECONF) - message(SEND_ERROR "Can not build CLN, missing binary for autoreconf") + message(FATAL_ERROR "Can not build CLN, missing binary for autoreconf") + endif() + + set(CLN_INCLUDE_DIR "${DEPS_BASE}/include/") + if(BUILD_SHARED_LIBS) + set(LINK_OPTS --enable-shared --disable-static) + set(CLN_LIBRARIES "${DEPS_BASE}/${CMAKE_INSTALL_LIBDIR}/libcln${CMAKE_SHARED_LIBRARY_SUFFIX}") + if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(CLN_BYPRODUCTS + /${CMAKE_INSTALL_LIBDIR}/libcln${CMAKE_SHARED_LIBRARY_SUFFIX} + /${CMAKE_INSTALL_LIBDIR}/libcln.${CLN_SO_MAJOR_VER}${CMAKE_SHARED_LIBRARY_SUFFIX} + ) + else() + set(CLN_BYPRODUCTS + /${CMAKE_INSTALL_LIBDIR}/libcln${CMAKE_SHARED_LIBRARY_SUFFIX} + /${CMAKE_INSTALL_LIBDIR}/libcln${CMAKE_SHARED_LIBRARY_SUFFIX}.${CLN_SO_MAJOR_VER} + /${CMAKE_INSTALL_LIBDIR}/libcln${CMAKE_SHARED_LIBRARY_SUFFIX}.${CLN_SO_VERSION} + ) + endif() + else() + set(LINK_OPTS --enable-static --disable-shared) + set(CLN_LIBRARIES "${DEPS_BASE}/${CMAKE_INSTALL_LIBDIR}/libcln${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(CLN_BYPRODUCTS /${CMAKE_INSTALL_LIBDIR}/libcln${CMAKE_STATIC_LIBRARY_SUFFIX}) + endif() + + set(CONFIGURE_OPTS "") + # CLN yields the following message at the end of the build process. + # WARNING: `makeinfo' is missing on your system. + # This is a specific issue to Github CI on linux environments. + # Since makeinfo just builds the documentation for CLN, + # it is possible to get around this issue by just disabling it: + set(CONFIGURE_ENV env "MAKEINFO=true") + + if(CMAKE_CROSSCOMPILING OR CMAKE_CROSSCOMPILING_MACOS) + set(CONFIGURE_OPTS + --host=${TOOLCHAIN_PREFIX} + --build=${CMAKE_HOST_SYSTEM_PROCESSOR}) + + set(CONFIGURE_ENV ${CONFIGURE_ENV} ${CMAKE_COMMAND} -E + env "CC_FOR_BUILD=cc") + if (CMAKE_CROSSCOMPILING_MACOS) + set(CONFIGURE_ENV + ${CONFIGURE_ENV} + env "CFLAGS=--target=${TOOLCHAIN_PREFIX}" + env "LDFLAGS=-arch ${CMAKE_OSX_ARCHITECTURES}") + endif() endif() ExternalProject_Add( CLN-EP ${COMMON_EP_CONFIG} - URL "https://www.ginac.de/CLN/cln.git/?p=cln.git\\\;a=snapshot\\\;h=cln_${CLN_TAG}\\\;sf=tgz" - URL_HASH SHA1=bd6dec17cf1088bdd592794d9239d47c752cf3da - DOWNLOAD_NAME cln.tgz + URL "https://www.ginac.de/CLN/cln-1.3.7.tar.bz2" + URL_HASH SHA1=17cf2c60e262e30f57caae39692fce7917e11d95 + DOWNLOAD_NAME cln.tar.bz2 CONFIGURE_COMMAND - ${CMAKE_COMMAND} -E chdir ./autogen.sh - COMMAND - ${CMAKE_COMMAND} -E chdir autoreconf -iv - COMMAND ${SHELL} /configure --prefix= --enable-shared - --enable-static --with-pic - BUILD_BYPRODUCTS /${CMAKE_INSTALL_LIBDIR}/libcln.a - /${CMAKE_INSTALL_LIBDIR}/libcln${CMAKE_SHARED_LIBRARY_SUFFIX} + ${CONFIGURE_ENV} ${SHELL} /configure + --prefix= ${LINK_OPTS} --with-pic + ${CONFIGURE_OPTS} + BUILD_BYPRODUCTS ${CLN_BYPRODUCTS} ) add_dependencies(CLN-EP GMP) - set(CLN_INCLUDE_DIR "${DEPS_BASE}/include/") - if(BUILD_SHARED_LIBS) - set(CLN_LIBRARIES "${DEPS_BASE}/${CMAKE_INSTALL_LIBDIR}/libcln${CMAKE_SHARED_LIBRARY_SUFFIX}") - else() - set(CLN_LIBRARIES "${DEPS_BASE}/${CMAKE_INSTALL_LIBDIR}/libcln.a") - endif() endif() set(CLN_FOUND TRUE) @@ -107,4 +154,12 @@ if(CLN_FOUND_SYSTEM) else() message(STATUS "Building CLN ${CLN_VERSION}: ${CLN_LIBRARIES}") add_dependencies(CLN CLN-EP) + + ExternalProject_Get_Property(CLN-EP BUILD_BYPRODUCTS INSTALL_DIR) + string(REPLACE "" "${INSTALL_DIR}" BUILD_BYPRODUCTS "${BUILD_BYPRODUCTS}") + + # Static builds install the CLN static libraries. + # These libraries are required to compile a program that + # uses the cvc5 static library. + install(FILES ${BUILD_BYPRODUCTS} TYPE ${LIB_BUILD_TYPE}) endif() diff --git a/cmake/FindCoCoA.cmake b/cmake/FindCoCoA.cmake index 5978f8cc4e6..0de3e06c1d0 100644 --- a/cmake/FindCoCoA.cmake +++ b/cmake/FindCoCoA.cmake @@ -100,4 +100,8 @@ if(CoCoA_FOUND_SYSTEM) else() message(STATUS "Building CoCoA ${CoCoA_VERSION}: ${CoCoA_LIBRARIES}") add_dependencies(CoCoA CoCoA-EP) + # Install static library only if it is a static build. + if(NOT BUILD_SHARED_LIBS) + install(FILES ${CoCoA_LIBRARIES} TYPE ${LIB_BUILD_TYPE}) + endif() endif() diff --git a/cmake/FindCryptoMiniSat.cmake b/cmake/FindCryptoMiniSat.cmake index 606d2093fc8..00ab36537f0 100644 --- a/cmake/FindCryptoMiniSat.cmake +++ b/cmake/FindCryptoMiniSat.cmake @@ -110,4 +110,8 @@ else() "Building CryptoMiniSat ${CryptoMiniSat_VERSION}: ${CryptoMiniSat_LIBRARIES}" ) add_dependencies(CryptoMiniSat CryptoMiniSat-EP) + # Install static library only if it is a static build. + if(NOT BUILD_SHARED_LIBS) + install(FILES ${CryptoMiniSat_LIBRARIES} TYPE ${LIB_BUILD_TYPE}) + endif() endif() diff --git a/cmake/FindCython.cmake b/cmake/FindCython.cmake index 20d8b3b221c..b2978136e6a 100644 --- a/cmake/FindCython.cmake +++ b/cmake/FindCython.cmake @@ -14,41 +14,93 @@ # Cython_FOUND - found Cython Python module ## -execute_process( - COMMAND "${Python_EXECUTABLE}" -c "import Cython; print(Cython.__version__)" - RESULT_VARIABLE Cython_VERSION_CHECK_RESULT - OUTPUT_VARIABLE Cython_VERSION - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE -) +macro(get_cython_version) + execute_process( + COMMAND "${Python_EXECUTABLE}" -c "import Cython; print(Cython.__version__)" + RESULT_VARIABLE Cython_VERSION_CHECK_RESULT + OUTPUT_VARIABLE Cython_VERSION + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +endmacro() -set(Cython_FOUND FALSE) +get_cython_version() + +if (Cython_FIND_REQUIRED) + set(Cython_FIND_MODE FATAL_ERROR) +else() + set(Cython_FIND_MODE STATUS) +endif() if (Cython_VERSION_CHECK_RESULT EQUAL 0) + set(Cython_FOUND TRUE) message(STATUS "Found Cython version: ${Cython_VERSION}") if (DEFINED Cython_FIND_VERSION) if(Cython_FIND_VERSION_EXACT) if (NOT (Cython_VERSION VERSION_EQUAL ${Cython_FIND_VERSION})) - message(STATUS - "Installing module Cython==${Cython_FIND_VERSION} in Python venv" - ) - execute_process( - COMMAND - ${Python_EXECUTABLE} -m pip install Cython==${Cython_FIND_VERSION} - ) + if(ENABLE_AUTO_DOWNLOAD) + message(STATUS + "Installing module Cython==${Cython_FIND_VERSION}" + ) + execute_process( + COMMAND + ${Python_EXECUTABLE} -m pip install Cython==${Cython_FIND_VERSION} + RESULT_VARIABLE CYTHON_INSTALL_CMD_EXIT_CODE + ) + if(CYTHON_INSTALL_CMD_EXIT_CODE) + message(${Cython_FIND_MODE} + "Could not install Cython==${Cython_FIND_VERSION}") + else() + get_cython_version() + endif() + else() + message(${Cython_FIND_MODE} + "Cython version == ${Cython_FIND_VERSION} is required, " + "but found version ${Cython_VERSION}") + endif() endif() else() if (Cython_VERSION VERSION_LESS ${Cython_FIND_VERSION}) - message(STATUS "Upgrading module Cython in Python venv") - execute_process(COMMAND - ${Python_EXECUTABLE} -m pip install Cython -U - ) + if(ENABLE_AUTO_DOWNLOAD) + message(STATUS "Upgrading module Cython") + execute_process(COMMAND + ${Python_EXECUTABLE} -m pip install Cython -U + RESULT_VARIABLE CYTHON_INSTALL_CMD_EXIT_CODE + ) + if(CYTHON_INSTALL_CMD_EXIT_CODE) + message(${Cython_FIND_MODE} + "Could not install Cython >= ${Cython_FIND_VERSION}") + else() + get_cython_version() + endif() + else() + message(${Cython_FIND_MODE} + "Cython version >= ${Cython_FIND_VERSION} is required, " + "but found version ${Cython_VERSION}") + endif() endif() endif() endif() else() - message(STATUS "Installing module Cython in Python venv") - execute_process(COMMAND ${Python_EXECUTABLE} -m pip install Cython) + set(Cython_FOUND FALSE) + if(ENABLE_AUTO_DOWNLOAD) + message(STATUS "Installing module Cython") + execute_process( + COMMAND ${Python_EXECUTABLE} -m pip install Cython + RESULT_VARIABLE CYTHON_INSTALL_CMD_EXIT_CODE) + if(CYTHON_INSTALL_CMD_EXIT_CODE) + message(${Cython_FIND_MODE} "Could not install module Cython") + else() + set(Cython_FOUND TRUE) + get_cython_version() + endif() + else() + message(${Cython_FIND_MODE} + "Could not find module Cython for Python " + "version ${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}. " + "Make sure to install Cython for this Python version " + "via \n`${Python_EXECUTABLE} -m pip install Cython'.\n" + "or use --auto-download to let us install it for you.\n" + "Note: You need to have pip installed for this Python version.") + endif() endif() - -set(Cython_FOUND TRUE) diff --git a/cmake/FindKissat.cmake b/cmake/FindKissat.cmake index 9cae199594f..772e588b5e0 100644 --- a/cmake/FindKissat.cmake +++ b/cmake/FindKissat.cmake @@ -88,4 +88,8 @@ if(Kissat_FOUND_SYSTEM) else() message(STATUS "Building Kissat ${Kissat_VERSION}: ${Kissat_LIBRARIES}") add_dependencies(Kissat Kissat-EP) + # Install static library only if it is a static build. + if(NOT BUILD_SHARED_LIBS) + install(FILES ${Kissat_LIBRARIES} TYPE ${LIB_BUILD_TYPE}) + endif() endif() diff --git a/cmake/Helpers.cmake b/cmake/Helpers.cmake index 1cb5c28a340..4fe21941116 100644 --- a/cmake/Helpers.cmake +++ b/cmake/Helpers.cmake @@ -247,11 +247,25 @@ function(check_python_module module) endif() if(RET_MODULE_TEST) - message(STATUS "Installing module ${module} in Python venv") - execute_process( - COMMAND - ${Python_EXECUTABLE} -m pip install ${module} - ) + if(ENABLE_AUTO_DOWNLOAD) + message(STATUS "Installing Python module ${module_name}") + execute_process( + COMMAND + ${Python_EXECUTABLE} -m pip install ${module_name} + RESULT_VARIABLE PYTHON_MODULE_INSTALL_CMD_EXIT_CODE + ) + if(PYTHON_MODULE_INSTALL_CMD_EXIT_CODE) + message(FATAL_ERROR "Could not install Python module ${module_name}") + endif() + else() + message(FATAL_ERROR + "Could not find module ${module_name} for Python " + "version ${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}. " + "Make sure to install ${module_name} for this Python version " + "via \n`${Python_EXECUTABLE} -m pip install ${module_name}',\n" + "or use --auto-download to let us install it for you.\n" + "Note: You need to have pip installed for this Python version.") + endif() endif() endfunction() diff --git a/configure.sh b/configure.sh index 68e62fb928c..784c6569dda 100755 --- a/configure.sh +++ b/configure.sh @@ -125,6 +125,7 @@ ninja=default profiling=default python_bindings=default python_only_src=default +pyvenv=default java_bindings=default editline=default build_shared=ON @@ -264,6 +265,9 @@ do --auto-download) auto_download=ON;; --no-auto-download) auto_download=OFF;; + --pyvenv) pyvenv=ON;; + --no-pyvenv) pyvenv=OFF;; + --statistics) statistics=ON;; --no-statistics) statistics=OFF;; @@ -348,6 +352,8 @@ fi && cmake_opts="$cmake_opts -DENABLE_ASAN=$asan" [ $auto_download != default ] \ && cmake_opts="$cmake_opts -DENABLE_AUTO_DOWNLOAD=$auto_download" +[ $pyvenv != default ] \ + && cmake_opts="$cmake_opts -DUSE_PYTHON_VENV=$pyvenv" [ $ubsan != default ] \ && cmake_opts="$cmake_opts -DENABLE_UBSAN=$ubsan" [ $tsan != default ] \ diff --git a/contrib/competitions/smt-comp/run-script-smtcomp-current b/contrib/competitions/smt-comp/run-script-smtcomp-current index c0af8efc476..553992305e3 100755 --- a/contrib/competitions/smt-comp/run-script-smtcomp-current +++ b/contrib/competitions/smt-comp/run-script-smtcomp-current @@ -74,7 +74,7 @@ QF_NRA) finishwith --decision=internal --nl-ext=none ;; # all logics with UF + quantifiers should either fall under this or special cases below -ALIA|AUFLIA|AUFLIRA|AUFNIRA|UF|UFBVLIA|UFIDL|UFLIA|UFLRA|UFNIA|UFDT|UFDTLIA|UFDTLIRA|AUFDTLIA|AUFDTLIRA|AUFBV|AUFBVDTLIA|AUFBVFP|AUFNIA|UFFPDTLIRA|UFFPDTNIRA) +ALIA|AUFLIA|AUFLIRA|AUFNIRA|UF|UFBVLIA|UFBVFP|UFIDL|UFLIA|UFLRA|UFNIA|UFDT|UFDTLIA|UFDTLIRA|AUFDTLIA|AUFDTLIRA|AUFBV|AUFBVDTLIA|AUFBVFP|AUFNIA|UFFPDTLIRA|UFFPDTNIRA) # initial runs 1 min trywith 30 --simplification=none --enum-inst trywith 30 --no-e-matching --enum-inst @@ -86,7 +86,7 @@ ALIA|AUFLIA|AUFLIRA|AUFNIRA|UF|UFBVLIA|UFIDL|UFLIA|UFLRA|UFNIA|UFDT|UFDTLIA|UFDT trywith 30 --multi-trigger-when-single --multi-trigger-priority --enum-inst trywith 30 --multi-trigger-cache --enum-inst trywith 30 --no-multi-trigger-linear --enum-inst - # other 4 min + # other 4 min 30 sec trywith 30 --pre-skolem-quant=on --enum-inst trywith 30 --inst-when=full --enum-inst trywith 30 --no-e-matching --no-cbqi --enum-inst @@ -95,13 +95,15 @@ ALIA|AUFLIA|AUFLIRA|AUFNIRA|UF|UFBVLIA|UFIDL|UFLIA|UFLRA|UFNIA|UFDT|UFDTLIA|UFDT trywith 30 --decision=internal --enum-inst --enum-inst-sum trywith 30 --term-db-mode=relevant --enum-inst trywith 30 --enum-inst-interleave --enum-inst - # finite model find 3 min + trywith 30 --preregister-mode=lazy --enum-inst + # finite model find and mbqi 3 min 30 sec trywith 30 --finite-model-find --fmf-mbqi=none trywith 30 --finite-model-find --decision=internal trywith 30 --finite-model-find --macros-quant --macros-quant-mode=all trywith 60 --finite-model-find --e-matching - # long runs 4 min - trywith 240 --finite-model-find --decision=internal + trywith 60 --mbqi + # long runs 3 min + trywith 180 --finite-model-find --decision=internal finishwith --enum-inst ;; UFBV) @@ -109,6 +111,7 @@ UFBV) trywith 150 --sygus-inst trywith 150 --mbqi --no-cegqi --no-sygus-inst trywith 300 --enum-inst --cegqi-nested-qe --decision=internal + trywith 300 --mbqi-fast-sygus --no-cegqi --no-sygus-inst trywith 30 --enum-inst --no-cegqi-innermost --global-negate finishwith --finite-model-find ;; @@ -116,13 +119,15 @@ ABV|BV) trywith 80 --enum-inst trywith 80 --sygus-inst trywith 80 --mbqi --no-cegqi --no-sygus-inst + trywith 300 --mbqi-fast-sygus --no-cegqi --no-sygus-inst trywith 300 --enum-inst --cegqi-nested-qe --decision=internal trywith 30 --enum-inst --no-cegqi-bv trywith 30 --enum-inst --cegqi-bv-ineq=eq-slack # finish 10min finishwith --enum-inst --no-cegqi-innermost --global-negate ;; -ABVFP|ABVFPLRA|BVFP|FP|NIA|NRA) +ABVFP|ABVFPLRA|BVFP|FP|NIA|NRA|BVFPLRA) + trywith 300 --mbqi-fast-sygus --no-cegqi --no-sygus-inst trywith 300 --enum-inst --nl-ext-tplanes trywith 60 --mbqi --no-cegqi --no-sygus-inst finishwith --sygus-inst @@ -130,7 +135,8 @@ ABVFP|ABVFPLRA|BVFP|FP|NIA|NRA) LIA|LRA) trywith 30 --enum-inst trywith 300 --enum-inst --cegqi-nested-qe - trywith 60 --mbqi --no-cegqi --no-sygus-inst + trywith 30 --mbqi --no-cegqi --no-sygus-inst + trywith 30 --mbqi-fast-sygus --no-cegqi --no-sygus-inst finishwith --enum-inst --cegqi-nested-qe --decision=internal ;; QF_AUFBV) diff --git a/contrib/get-alf-checker b/contrib/get-alf-checker index 20672cde347..4f9bc8a3ac5 100755 --- a/contrib/get-alf-checker +++ b/contrib/get-alf-checker @@ -24,7 +24,7 @@ ALFC_DIR="$BASE_DIR/alf-checker" mkdir -p $ALFC_DIR # download and unpack ALFC -ALF_VERSION="980000f86cd2b47d29636667b3c52d62c3de0108" +ALF_VERSION="3c122c02bdb49d239223e683740da59f74816ba7" download "https://github.com/cvc5/alfc/archive/$ALF_VERSION.tar.gz" $BASE_DIR/tmp/alfc.tgz tar --strip 1 -xzf $BASE_DIR/tmp/alfc.tgz -C $ALFC_DIR diff --git a/contrib/get-glpk-cut-log b/contrib/get-glpk-cut-log index cecaa6d6a19..0a57475899c 100755 --- a/contrib/get-glpk-cut-log +++ b/contrib/get-glpk-cut-log @@ -10,7 +10,7 @@ contrib_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd -P) patch_file=${contrib_dir}/glpk-cut-log.patch GLPK_DIR="$DEPS_DIR/glpk-cut-log" -version="5.0" +version="4.52" setup_dep \ "https://ftp.gnu.org/gnu/glpk/glpk-${version}.tar.gz" \ diff --git a/contrib/glpk-cut-log.patch b/contrib/glpk-cut-log.patch index c8409f7bb37..4919b19ebf7 100644 --- a/contrib/glpk-cut-log.patch +++ b/contrib/glpk-cut-log.patch @@ -1,11 +1,11 @@ -%% This patch is taken from https://github.com/timothy-king/glpk-cut-log and -%% has the following license: -%% -%% GLPK is free software: you can redistribute it and/or modify it under the -%% terms of the GNU General Public License as published by the Free Software -%% Foundation, either version 3 of the License, or (at your option) any later -%% version. -%% +@@ This patch is taken from https://github.com/timothy-king/glpk-cut-log and +@@ has the following license: +@@ +@@ GLPK is free software: you can redistribute it and/or modify it under the +@@ terms of the GNU General Public License as published by the Free Software +@@ Foundation, either version 3 of the License, or (at your option) any later +@@ version. +@@ From cf84e3855d8c5676557daaca434b42b2fc17dc29 Mon Sep 17 00:00:00 2001 From: Tim King Date: Sat, 14 Dec 2013 16:03:35 -0500 @@ -73,10 +73,10 @@ Updating the installation instructions. --- diff --git a/configure.ac b/configure.ac -index cbdb724..ba642ba 100644 +index bbc8929..0bc64d1 100644 --- a/configure.ac +++ b/configure.ac -@@ -6,7 +6,7 @@ AC_CONFIG_SRCDIR([src/glpk.h]) +@@ -5,7 +5,7 @@ AC_CONFIG_SRCDIR([src/glpk.h]) AC_CONFIG_MACRO_DIR([m4]) @@ -86,30 +86,39 @@ index cbdb724..ba642ba 100644 AC_CONFIG_HEADERS([config.h]) diff --git a/src/Makefile.am b/src/Makefile.am -index 48dff15..d04b547 100644 +index b39efa6..2a025bc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am -@@ -27,6 +27,7 @@ libglpk_la_LDFLAGS = \ - ${NOUNDEFINED} +@@ -20,7 +20,10 @@ libglpk_la_LDFLAGS = \ + -version-info 36:0:1 \ + -export-symbols-regex '^glp_*' ++ ++# all of the cut log sources are listed first libglpk_la_SOURCES = \ +cutlog01.c \ - amd/amd_1.c \ - amd/amd_2.c \ - amd/amd_aat.c \ + glpapi01.c \ + glpapi02.c \ + glpapi03.c \ diff --git a/src/cutlog01.c b/src/cutlog01.c new file mode 100644 -index 0000000..c8818df +index 0000000..9a85bb7 --- /dev/null +++ b/src/cutlog01.c -@@ -0,0 +1,446 @@ +@@ -0,0 +1,452 @@ +/* cutlog01.c (api extension routines) */ + -+#include "env.h" -+#include "ios.h" -+#include "npp.h" -+#include "glpk.h" -+#include "ios.h" ++ ++#include "glpapi.h" ++#include "glpios.h" ++ ++int glp_get_it_cnt(glp_prob *P){ ++ if(P == NULL){ ++ return 0; ++ }else{ ++ return P->it_cnt; ++ } ++} + +int glp_ios_get_cut(glp_tree *T, int i, int* ind, double* val, int* klass, int* type, double* rhs){ + xassert(T != NULL); @@ -549,23 +558,23 @@ index 0000000..c8818df + } + return tree->num_deleting_rows; +} -diff --git a/src/draft/glpapi06.c b/src/draft/glpapi06.c -index 635662e..ad6d55a 100644 ---- a/src/draft/glpapi06.c -+++ b/src/draft/glpapi06.c -@@ -524,6 +524,7 @@ void glp_init_smcp(glp_smcp *parm) - parm->shift = GLP_ON; - parm->aorn = GLP_USE_NT; - #endif +diff --git a/src/glpapi06.c b/src/glpapi06.c +index 743d6be..05d0498 100644 +--- a/src/glpapi06.c ++++ b/src/glpapi06.c +@@ -488,6 +488,7 @@ void glp_init_smcp(glp_smcp *parm) + parm->out_frq = 500; + parm->out_dly = 0; + parm->presolve = GLP_OFF; + parm->stability_lmt = 200; return; } -diff --git a/src/draft/glpapi13.c b/src/draft/glpapi13.c -index 0ca2adf..c5d4af6 100644 ---- a/src/draft/glpapi13.c -+++ b/src/draft/glpapi13.c -@@ -451,8 +451,14 @@ void glp_ios_row_attr(glp_tree *tree, int i, glp_attr *attr) +diff --git a/src/glpapi13.c b/src/glpapi13.c +index 926dda4..55adf44 100644 +--- a/src/glpapi13.c ++++ b/src/glpapi13.c +@@ -453,8 +453,14 @@ void glp_ios_row_attr(glp_tree *tree, int i, glp_attr *attr) int glp_ios_pool_size(glp_tree *tree) { /* determine current size of the cut pool */ @@ -580,24 +589,15 @@ index 0ca2adf..c5d4af6 100644 + xerror("glp_ios_pool_size: operation not allowed\n"); + } xassert(tree->local != NULL); - #ifdef NEW_LOCAL /* 02/II-2018 */ - return tree->local->m; -diff --git a/src/draft/ios.h b/src/draft/ios.h -index e2b47c6..2e61fdc 100644 ---- a/src/draft/ios.h -+++ b/src/draft/ios.h -@@ -24,7 +24,7 @@ - - #include "prob.h" - --#if 1 /* 02/II-2018 */ -+#if 0 /* 02/II-2018 */ /* cvc5; NEW_LOCAL is not compatible with cut logging */ - #define NEW_LOCAL 1 - #endif - -@@ -46,6 +46,9 @@ typedef struct IOSPOOL IOSPOOL; + return tree->local->size; + } +diff --git a/src/glpios.h b/src/glpios.h +index 9e2d6b2..3b31901 100644 +--- a/src/glpios.h ++++ b/src/glpios.h +@@ -36,6 +36,9 @@ typedef struct IOSAIJ IOSAIJ; + typedef struct IOSPOOL IOSPOOL; typedef struct IOSCUT IOSCUT; - #endif + +typedef struct IOSAUX IOSAUX; @@ -605,7 +605,7 @@ index e2b47c6..2e61fdc 100644 struct glp_tree { /* branch-and-bound tree */ int magic; -@@ -223,6 +226,19 @@ struct glp_tree +@@ -217,6 +220,19 @@ struct glp_tree GLP_NO_BRNCH - use general selection technique */ int child; /* subproblem reference number corresponding to br_sel */ @@ -625,7 +625,7 @@ index e2b47c6..2e61fdc 100644 }; struct IOSLOT -@@ -235,6 +251,8 @@ struct IOSLOT +@@ -229,6 +245,8 @@ struct IOSLOT struct IOSNPD { /* node subproblem descriptor */ @@ -634,15 +634,19 @@ index e2b47c6..2e61fdc 100644 int p; /* subproblem reference number (it is the index to corresponding slot, i.e. slot[p] points to this descriptor) */ -@@ -406,9 +424,47 @@ struct IOSCUT - /* pointer to previous cut */ - IOSCUT *next; - /* pointer to next cut */ +@@ -393,12 +411,51 @@ struct IOSCUT + GLP_FX: sum a[j] * x[j] = b */ + double rhs; + /* cut right-hand side */ + + IOSAUX *aux; + /* cut auxillary source information */ ++ + IOSCUT *prev; + /* pointer to previous cut */ + IOSCUT *next; + /* pointer to next cut */ }; - #endif +struct IOSAUX +{ @@ -682,7 +686,7 @@ index e2b47c6..2e61fdc 100644 #define ios_create_tree _glp_ios_create_tree glp_tree *ios_create_tree(glp_prob *mip, const glp_iocp *parm); /* create branch-and-bound tree */ -@@ -539,6 +595,31 @@ int ios_choose_node(glp_tree *T); +@@ -615,6 +672,31 @@ int ios_choose_node(glp_tree *T); int ios_choose_var(glp_tree *T, int *next); /* select variable to branch on */ @@ -714,11 +718,11 @@ index e2b47c6..2e61fdc 100644 #endif /* eof */ -diff --git a/src/draft/glpios01.c b/src/draft/glpios01.c -index f1ee43a..f32867d 100644 ---- a/src/draft/glpios01.c -+++ b/src/draft/glpios01.c -@@ -175,6 +175,16 @@ glp_tree *ios_create_tree(glp_prob *mip, const glp_iocp *parm) +diff --git a/src/glpios01.c b/src/glpios01.c +index 70798fd..d9e5703 100644 +--- a/src/glpios01.c ++++ b/src/glpios01.c +@@ -159,6 +159,16 @@ glp_tree *ios_create_tree(glp_prob *mip, const glp_iocp *parm) tree->stop = 0; /* create the root subproblem, which initially is identical to the original MIP */ @@ -735,7 +739,7 @@ index f1ee43a..f32867d 100644 new_node(tree, NULL); return tree; } -@@ -294,6 +304,7 @@ void ios_revive_node(glp_tree *tree, int p) +@@ -278,6 +288,7 @@ void ios_revive_node(glp_tree *tree, int p) double *val; ind = xcalloc(1+n, sizeof(int)); val = xcalloc(1+n, sizeof(double)); @@ -743,7 +747,7 @@ index f1ee43a..f32867d 100644 for (r = node->r_ptr; r != NULL; r = r->next) { i = glp_add_rows(mip, 1); glp_set_row_name(mip, i, r->name); -@@ -473,6 +484,13 @@ void ios_freeze_node(glp_tree *tree) +@@ -457,6 +468,13 @@ void ios_freeze_node(glp_tree *tree) double *val; ind = xcalloc(1+n, sizeof(int)); val = xcalloc(1+n, sizeof(double)); @@ -757,7 +761,7 @@ index f1ee43a..f32867d 100644 for (i = m; i > pred_m; i--) { GLPROW *row = mip->row[i]; IOSROW *r; -@@ -517,6 +535,8 @@ void ios_freeze_node(glp_tree *tree) +@@ -501,6 +519,8 @@ void ios_freeze_node(glp_tree *tree) xassert(nrs > 0); num = xcalloc(1+nrs, sizeof(int)); for (i = 1; i <= nrs; i++) num[i] = root_m + i; @@ -766,7 +770,7 @@ index f1ee43a..f32867d 100644 glp_del_rows(mip, nrs, num); xfree(num); } -@@ -652,6 +672,9 @@ static IOSNPD *new_node(glp_tree *tree, IOSNPD *parent) +@@ -636,6 +656,9 @@ static IOSNPD *new_node(glp_tree *tree, IOSNPD *parent) tree->a_cnt++; tree->n_cnt++; tree->t_cnt++; @@ -776,7 +780,7 @@ index f1ee43a..f32867d 100644 /* increase the number of child subproblems */ if (parent == NULL) xassert(p == 1); -@@ -834,6 +857,8 @@ void ios_delete_tree(glp_tree *tree) +@@ -818,6 +841,8 @@ void ios_delete_tree(glp_tree *tree) xassert(nrs > 0); num = xcalloc(1+nrs, sizeof(int)); for (i = 1; i <= nrs; i++) num[i] = tree->orig_m + i; @@ -785,7 +789,7 @@ index f1ee43a..f32867d 100644 glp_del_rows(mip, nrs, num); xfree(num); } -@@ -1477,6 +1502,7 @@ int ios_add_row(glp_tree *tree, IOSPOOL *pool, +@@ -1417,6 +1442,7 @@ int ios_add_row(glp_tree *tree, IOSPOOL *pool, cut->rhs = rhs; cut->prev = pool->tail; cut->next = NULL; @@ -793,7 +797,7 @@ index f1ee43a..f32867d 100644 if (cut->prev == NULL) pool->head = cut; else -@@ -1591,6 +1617,11 @@ void ios_del_row(glp_tree *tree, IOSPOOL *pool, int i) +@@ -1517,6 +1543,11 @@ void ios_del_row(glp_tree *tree, IOSPOOL *pool, int i) cut->ptr = aij->next; dmp_free_atom(tree->pool, aij, sizeof(IOSAIJ)); } @@ -805,7 +809,7 @@ index f1ee43a..f32867d 100644 dmp_free_atom(tree->pool, cut, sizeof(IOSCUT)); pool->size--; return; -@@ -1624,6 +1655,10 @@ void ios_clear_pool(glp_tree *tree, IOSPOOL *pool) +@@ -1535,6 +1566,10 @@ void ios_clear_pool(glp_tree *tree, IOSPOOL *pool) cut->ptr = aij->next; dmp_free_atom(tree->pool, aij, sizeof(IOSAIJ)); } @@ -816,16 +820,11 @@ index f1ee43a..f32867d 100644 dmp_free_atom(tree->pool, cut, sizeof(IOSCUT)); } pool->size = 0; -@@ -@@ glpios08 was split into src/cglib/{cfg2.c, clqcut.c} in GLPK 4.59 -@@ -@@ This code applies against glpios03 around GLPK 4.65. -@@ -diff --git a/src/draft/glpios03.c b/src/draft/glpios03.c -index 2c1180f..ec1da44 100644 ---- a/src/draft/glpios03.c -+++ b/src/draft/glpios03.c -@@ -359,12 +394,12 @@ static void fix_by_red_cost(glp_tree *T) +diff --git a/src/glpios03.c b/src/glpios03.c +index 80d701b..03e9208 100644 +--- a/src/glpios03.c ++++ b/src/glpios03.c +@@ -358,12 +358,12 @@ static void fix_by_red_cost(glp_tree *T) * 2 - both branches are hopeless and have been pruned; new subproblem * selection is needed to continue the search. */ @@ -840,7 +839,7 @@ index 2c1180f..ec1da44 100644 double lb, ub, beta, new_ub, new_lb, dn_lp, up_lp, dn_bnd, up_bnd; /* determine bounds and value of x[j] in optimal solution to LP relaxation of the current subproblem */ -@@ -432,6 +467,7 @@ static int branch_on(glp_tree *T, int j, int next) +@@ -431,6 +431,7 @@ static int branch_on(glp_tree *T, int j, int next) else xassert(mip != mip); ret = 1; @@ -848,7 +847,7 @@ index 2c1180f..ec1da44 100644 goto done; } else if (dn_bad) -@@ -450,6 +486,7 @@ static int branch_on(glp_tree *T, int j, int next) +@@ -449,6 +450,7 @@ static int branch_on(glp_tree *T, int j, int next) else xassert(mip != mip); ret = 1; @@ -856,40 +855,7 @@ index 2c1180f..ec1da44 100644 goto done; } /* both down- and up-branches seem to be hopeful */ -@@ -650,8 +687,16 @@ static void gmi_gen(glp_tree *T) - val = xcalloc(1+P->n, sizeof(double)); - for (i = 1; i <= pool->m; i++) - { len = glp_get_mat_row(pool, i, ind, val); - glp_ios_add_row(T, NULL, GLP_RF_GMI, 0, len, ind, val, - GLP_LO, pool->row[i]->lb); -+ -+ /** callback for a cut being added to the cut pool */ -+ if (T->parm->cb_func != NULL) -+ { xassert(T->reason == GLP_ICUTGEN); -+ T->reason = GLP_ICUTADDED; -+ T->parm->cb_func(T, T->parm->cb_info); -+ T->reason = GLP_ICUTGEN; -+ } - } - xfree(ind); - xfree(val); -@@ -728,6 +773,15 @@ static void clq_gen(glp_tree *T, glp_cfg *G) - if (len > 0) - glp_ios_add_row(T, NULL, GLP_RF_CLQ, 0, len, ind, val, GLP_UP, - val[0]); -+ -+ /** callback for a cut being added to the cut pool */ -+ if (T->parm->cb_func != NULL) -+ { xassert(T->reason == GLP_ICUTGEN); -+ T->reason = GLP_ICUTADDED; -+ T->parm->cb_func(T, T->parm->cb_info); -+ T->reason = GLP_ICUTGEN; -+ } -+ - tfree(ind); - tfree(val); - return; -@@ -814,7 +868,8 @@ static void remove_cuts(glp_tree *T) +@@ -702,7 +704,8 @@ static void remove_cuts(glp_tree *T) } } if (cnt > 0) @@ -899,7 +865,7 @@ index 2c1180f..ec1da44 100644 #if 0 xprintf("%d inactive cut(s) removed\n", cnt); #endif -@@ -896,6 +951,7 @@ static void display_cut_info(glp_tree *T) +@@ -784,6 +787,7 @@ static void display_cut_info(glp_tree *T) int ios_driver(glp_tree *T) { int p, curr_p, p_stat, d_stat, ret; @@ -907,7 +873,7 @@ index 2c1180f..ec1da44 100644 #if 1 /* carry out to glp_tree */ int pred_p = 0; /* if the current subproblem has been just created due to -@@ -1164,6 +1220,12 @@ more: /* minor loop starts here */ +@@ -1010,6 +1014,12 @@ more: /* minor loop starts here */ if (T->parm->msg_lev >= GLP_MSG_DBG) xprintf("LP relaxation has no feasible solution\n"); /* prune the branch */ @@ -920,7 +886,7 @@ index 2c1180f..ec1da44 100644 goto fath; } else -@@ -1398,6 +1460,14 @@ more: /* minor loop starts here */ +@@ -1219,6 +1229,14 @@ more: /* minor loop starts here */ ios_process_cuts(T); T->reason = 0; } @@ -935,7 +901,7 @@ index 2c1180f..ec1da44 100644 /* clear the local cut pool */ ios_clear_pool(T, T->local); /* perform re-optimization, if necessary */ -@@ -1442,7 +1512,34 @@ more: /* minor loop starts here */ +@@ -1255,7 +1273,34 @@ more: /* minor loop starts here */ T->br_var = ios_choose_var(T, &T->br_sel); /* perform actual branching */ curr_p = T->curr->p; @@ -971,57 +937,37 @@ index 2c1180f..ec1da44 100644 T->br_var = T->br_sel = 0; if (ret == 0) { /* both branches have been created */ -diff --git a/src/intopt/covgen.c b/src/intopt/covgen.c -index 986cf88..bfe708c 100644 ---- a/src/intopt/covgen.c -+++ b/src/intopt/covgen.c -@@ -572,14 +572,14 @@ glp_cov *glp_cov_init(glp_prob *P) - /* the set of "0-1 knapsack" inequalities has been built */ - if (csa.set->m == 0) - { /* the set is empty */ -- xprintf("No 0-1 knapsack inequalities detected\n"); -+ /* xprintf("No 0-1 knapsack inequalities detected\n"); */ - cov = NULL; - glp_delete_prob(csa.set); - } - else - { /* create the cover cut generator working area */ -- xprintf("Number of 0-1 knapsack inequalities = %d\n", -- csa.set->m); -+ /* xprintf("Number of 0-1 knapsack inequalities = %d\n", -+ csa.set->m); */ - cov = talloc(1, glp_cov); - cov->n = P->n; - cov->set = csa.set; -%% -%% glpios05 was split into src/cglib/{gmicut.c, gmigen.c} -%% in GLPK 4.59 -%% -%% src/cglib was renamed into src/intopt in GLPK 4.65 -%% -diff --git a/src/intopt/gmicut.c b/src/intopt/gmicut.c -index ae75a8c..61b2fda 100644 ---- a/src/intopt/gmicut.c -+++ b/src/intopt/gmicut.c -@@ -21,6 +21,7 @@ - - #include "env.h" - #include "prob.h" -+#include "ios.h" - - /*********************************************************************** - * NAME -@@ -104,6 +105,9 @@ int glp_gmi_cut(glp_prob *P, int j, - GLPAIJ *aij; +diff --git a/src/glpios05.c b/src/glpios05.c +index b9322b9..caf69d7 100644 +--- a/src/glpios05.c ++++ b/src/glpios05.c +@@ -65,6 +65,9 @@ static void gen_cut(glp_tree *tree, struct worka *worka, int j) + double *phi = worka->phi; int i, k, len, kind, stat; double lb, ub, alfa, beta, ksi, phi1, rhs; + int input_j; + input_j = j; + - /* sanity checks */ - if (!(P->m == 0 || P->valid)) - { /* current basis factorization is not valid */ -@@ -202,6 +206,7 @@ int glp_gmi_cut(glp_prob *P, int j, + /* compute row of the simplex tableau, which (row) corresponds + to specified basic variable xB[i] = x[m+j]; see (23) */ + len = glp_eval_tab_row(mip, m+j, ind, val); +@@ -73,6 +76,7 @@ static void gen_cut(glp_tree *tree, struct worka *worka, int j) + if it would be computed with formula (27); it is assumed that + beta[i] is fractional enough */ + beta = mip->col[j]->prim; ++ + /* compute cut coefficients phi and right-hand side rho, which + correspond to formula (30); dense format is used, because rows + of the simplex tableau is usually dense */ +@@ -104,6 +108,7 @@ static void gen_cut(glp_tree *tree, struct worka *worka, int j) + xassert(stat != GLP_BS); + /* determine row coefficient ksi[i,j] at xN[j]; see (23) */ + ksi = val[j]; ++ /* printf("%d %4.15f ", k, ksi); */ + /* if ksi[i,j] is too large in the magnitude, do not generate + the cut */ + if (fabs(ksi) > 1e+05) goto fini; +@@ -135,6 +140,7 @@ static void gen_cut(glp_tree *tree, struct worka *worka, int j) /* y[j] is integer */ if (fabs(alfa - floor(alfa + 0.5)) < 1e-10) { /* alfa[i,j] is close to nearest integer; skip it */ @@ -1029,7 +975,7 @@ index ae75a8c..61b2fda 100644 goto skip; } else if (f(alfa) <= f(beta)) -@@ -237,6 +242,13 @@ int glp_gmi_cut(glp_prob *P, int j, +@@ -170,6 +176,13 @@ static void gen_cut(glp_tree *tree, struct worka *worka, int j) } skip: ; } @@ -1041,14 +987,19 @@ index ae75a8c..61b2fda 100644 + /* printf("\n"); */ + /* now the cut has the form sum_k phi[k] * x[k] >= rho, where cut - * coefficients are stored in the array phi in dense format; - * x[1,...,m] are auxiliary variables, x[m+1,...,m+n] are struc- -@@ -276,6 +288,17 @@ skip: ; - rhs = 0.0; - ind[0] = 0, val[0] = rhs; - /* the cut has been successfully generated */ -+ -+ glp_tree *tree = P->tree; + coefficients are stored in the array phi in dense format; + x[1,...,m] are auxiliary variables, x[m+1,...,m+n] are struc- +@@ -218,8 +231,20 @@ skip: ; + ios_add_cut_row(tree, pool, GLP_RF_GMI, len, ind, val, GLP_LO, + rhs); + #else +- glp_ios_add_row(tree, NULL, GLP_RF_GMI, 0, len, ind, val, GLP_LO, +- rhs); ++ int ord; ++ ord = glp_ios_add_row(tree, NULL, GLP_RF_GMI, 0, len, ind, val, ++ GLP_LO, rhs); ++ ios_cut_set_single_aux(tree, ord, input_j); ++ + /* printf("ord: % d beta %f\n", ord, beta); */ + + /** callback for a cut being added to the cut pool */ @@ -1058,14 +1009,14 @@ index ae75a8c..61b2fda 100644 + tree->parm->cb_func(tree, tree->parm->cb_info); + tree->reason = GLP_ICUTGEN; + } - return len; + #endif + fini: return; } - -diff --git a/src/intopt/mirgen.c b/src/intopt/mirgen.c -index 4467189..4b03dcb 100644 ---- a/src/intopt/mirgen.c -+++ b/src/intopt/mirgen.c -@@ -123,6 +123,29 @@ struct glp_mir +diff --git a/src/glpios06.c b/src/glpios06.c +index 2bd0453..4bd3cf5 100644 +--- a/src/glpios06.c ++++ b/src/glpios06.c +@@ -100,6 +100,29 @@ struct MIR /* sparse vector of cutting plane coefficients, alpha[k] */ double cut_rhs; /* right-hand size of the cutting plane, beta */ @@ -1095,7 +1046,7 @@ index 4467189..4b03dcb 100644 }; /*********************************************************************** -@@ -166,6 +189,7 @@ static void set_row_attrib(glp_prob *mip, glp_mir *mir) +@@ -146,6 +169,7 @@ static void set_row_attrib(glp_tree *tree, struct MIR *mir) xassert(row != row); } mir->vlb[k] = mir->vub[k] = 0; @@ -1103,7 +1054,7 @@ index 4467189..4b03dcb 100644 } return; } -@@ -200,6 +224,7 @@ static void set_col_attrib(glp_prob *mip, glp_mir *mir) +@@ -181,6 +205,7 @@ static void set_col_attrib(glp_tree *tree, struct MIR *mir) xassert(col != col); } mir->vlb[k] = mir->vub[k] = 0; @@ -1111,7 +1062,7 @@ index 4467189..4b03dcb 100644 } return; } -@@ -248,6 +273,7 @@ static void set_var_bounds(glp_prob *mip, glp_mir *mir) +@@ -230,6 +255,7 @@ static void set_var_bounds(glp_tree *tree, struct MIR *mir) { /* set variable lower bound for x1 */ mir->lb[k1] = - a2 / a1; mir->vlb[k1] = k2; @@ -1119,7 +1070,7 @@ index 4467189..4b03dcb 100644 /* the row should not be used */ mir->skip[i] = 1; } -@@ -258,6 +284,7 @@ static void set_var_bounds(glp_prob *mip, glp_mir *mir) +@@ -240,6 +266,7 @@ static void set_var_bounds(glp_tree *tree, struct MIR *mir) { /* set variable upper bound for x1 */ mir->ub[k1] = - a2 / a1; mir->vub[k1] = k2; @@ -1127,10 +1078,10 @@ index 4467189..4b03dcb 100644 /* the row should not be used */ mir->skip[i] = 1; } -@@ -329,6 +356,13 @@ glp_mir *glp_mir_init(glp_prob *mip) +@@ -313,6 +340,13 @@ void *ios_mir_init(glp_tree *tree) mir->subst = xcalloc(1+m+n, sizeof(char)); - mir->mod_vec = spv_create_vec(m+n); - mir->cut_vec = spv_create_vec(m+n); + mir->mod_vec = ios_create_vec(m+n); + mir->cut_vec = ios_create_vec(m+n); + + /* added */ + mir->cut_cset = xcalloc(1+m+n, sizeof(char)); @@ -1139,9 +1090,9 @@ index 4467189..4b03dcb 100644 + mir->agg_coeffs = xcalloc(1+MAXAGGR, sizeof(double)); + /* set global row attributes */ - set_row_attrib(mip, mir); + set_row_attrib(tree, mir); /* set global column attributes */ -@@ -426,6 +460,9 @@ static void initial_agg_row(glp_prob *mip, glp_mir *mir, int i) +@@ -405,6 +439,9 @@ static void initial_agg_row(glp_tree *tree, struct MIR *mir, int i) mir->skip[i] = 2; mir->agg_cnt = 1; mir->agg_row[1] = i; @@ -1150,8 +1101,8 @@ index 4467189..4b03dcb 100644 + /* use x[i] - sum a[i,j] * x[m+j] = 0, where x[i] is auxiliary variable of row i, x[m+j] are structural variables */ - spv_clear_vec(mir->agg_vec); -@@ -805,19 +842,19 @@ static int CDECL cmir_cmp(const void *p1, const void *p2) + ios_clear_vec(mir->agg_vec); +@@ -784,19 +821,19 @@ static int cmir_cmp(const void *p1, const void *p2) static double cmir_sep(const int n, const double a[], const double b, const double u[], const double x[], const double s, @@ -1176,7 +1127,7 @@ index 4467189..4b03dcb 100644 for (j = 1; j <= n; j++) { xassert(a[j] != 0.0); /* if x[j] is close to its bounds, skip it */ -@@ -830,16 +867,16 @@ static double cmir_sep(const int n, const double a[], const double b, +@@ -809,16 +846,16 @@ static double cmir_sep(const int n, const double a[], const double b, /* compute violation */ r = - (*beta) - (*gamma) * s; for (k = 1; k <= n; k++) r += alpha[k] * x[k]; @@ -1198,7 +1149,7 @@ index 4467189..4b03dcb 100644 for (j = 1; j <= 3; j++) { /* construct c-MIR inequality */ fail = cmir_ineq(n, a, b, u, cset, d_try[j], alpha, beta, -@@ -848,7 +885,7 @@ static double cmir_sep(const int n, const double a[], const double b, +@@ -827,7 +864,7 @@ static double cmir_sep(const int n, const double a[], const double b, /* compute violation */ r = - (*beta) - (*gamma) * s; for (k = 1; k <= n; k++) r += alpha[k] * x[k]; @@ -1207,7 +1158,7 @@ index 4467189..4b03dcb 100644 } /* build subset of variables lying strictly between their bounds and order it by nondecreasing values of |x[j] - u[j]/2| */ -@@ -870,7 +907,7 @@ static double cmir_sep(const int n, const double a[], const double b, +@@ -849,7 +886,7 @@ static double cmir_sep(const int n, const double a[], const double b, /* replace x[j] by its complement or vice versa */ cset[j] = (char)!cset[j]; /* construct c-MIR inequality */ @@ -1216,7 +1167,7 @@ index 4467189..4b03dcb 100644 /* restore the variable */ cset[j] = (char)!cset[j]; /* do not replace the variable in case of failure */ -@@ -881,10 +918,11 @@ static double cmir_sep(const int n, const double a[], const double b, +@@ -860,10 +897,11 @@ static double cmir_sep(const int n, const double a[], const double b, if (r_best < r) r_best = r, cset[j] = (char)!cset[j]; } /* construct the best c-MIR inequality chosen */ @@ -1230,17 +1181,17 @@ index 4467189..4b03dcb 100644 xfree(vset); /* return to the calling routine */ return r_best; -@@ -895,7 +933,8 @@ static double generate(glp_mir *mir) +@@ -874,7 +912,8 @@ static double generate(struct MIR *mir) int m = mir->m; int n = mir->n; int j, k, kk, nint; - double s, *u, *x, *alpha, r_best = 0.0, b, beta, gamma; + double s, *u, *x, *alpha, r_best = 0.0, b, beta, gamma, delta; + char *cset; - spv_copy_vec(mir->cut_vec, mir->mod_vec); + ios_copy_vec(mir->cut_vec, mir->mod_vec); mir->cut_rhs = mir->mod_rhs; /* remove small terms, which can appear due to substitution of -@@ -944,6 +983,7 @@ static double generate(glp_mir *mir) +@@ -923,6 +962,7 @@ static double generate(struct MIR *mir) u = xcalloc(1+nint, sizeof(double)); x = xcalloc(1+nint, sizeof(double)); alpha = xcalloc(1+nint, sizeof(double)); @@ -1248,7 +1199,23 @@ index 4467189..4b03dcb 100644 /* determine u and x */ for (j = 1; j <= nint; j++) { k = mir->cut_vec->ind[j]; -@@ -1010,7 +1050,7 @@ static double generate(glp_mir *mir) +@@ -936,6 +976,7 @@ static double generate(struct MIR *mir) + x[j] = mir->ub[k] - mir->x[k]; + else + xassert(k != k); ++ if(!(x[j] >= -0.001)) { goto skip; } + xassert(x[j] >= -0.001); + if (x[j] < 0.0) x[j] = 0.0; + } +@@ -965,6 +1006,7 @@ static double generate(struct MIR *mir) + } + else + xassert(k != k); ++ if(!(x >= -0.001)) { goto skip; } + xassert(x >= -0.001); + if (x < 0.0) x = 0.0; + s -= mir->cut_vec->val[j] * x; +@@ -973,7 +1015,7 @@ static double generate(struct MIR *mir) /* apply heuristic to obtain most violated c-MIR inequality */ b = mir->cut_rhs; r_best = cmir_sep(nint, mir->cut_vec->val, b, u, x, s, alpha, @@ -1257,9 +1224,9 @@ index 4467189..4b03dcb 100644 if (r_best == 0.0) goto skip; xassert(r_best > 0.0); /* convert to raw cut */ -@@ -1025,10 +1065,22 @@ static double generate(glp_mir *mir) - #if MIR_DEBUG - spv_check_vec(mir->cut_vec); +@@ -988,10 +1030,22 @@ static double generate(struct MIR *mir) + #if _MIR_DEBUG + ios_check_vec(mir->cut_vec); #endif + /* added */ + mir->cut_delta = delta; @@ -1280,7 +1247,7 @@ index 4467189..4b03dcb 100644 done: return r_best; } -@@ -1236,8 +1288,25 @@ static void add_cut(glp_mir *mir, glp_prob *pool) +@@ -1199,8 +1253,25 @@ static void add_cut(glp_tree *tree, struct MIR *mir) ios_add_cut_row(tree, pool, GLP_RF_MIR, len, ind, val, GLP_UP, mir->cut_rhs); #else @@ -1305,32 +1272,32 @@ index 4467189..4b03dcb 100644 + tree->reason = GLP_ICUTGEN; + } #endif - #else - { int i; -@@ -1265,6 +1334,7 @@ static int aggregate_row(glp_prob *mip, glp_mir *mir, SPV *v) - #endif + xfree(ind); + xfree(val); +@@ -1216,6 +1287,7 @@ static int aggregate_row(glp_tree *tree, struct MIR *mir) + IOSVEC *v; int ii, j, jj, k, kk, kappa = 0, ret = 0; double d1, d2, d, d_max = 0.0; + double guass_coeff; /* choose appropriate structural variable in the aggregated row to be substituted */ for (j = 1; j <= mir->agg_vec->nnz; j++) -@@ -1369,8 +1439,9 @@ static int aggregate_row(glp_prob *mip, glp_mir *mir, SPV *v) +@@ -1300,8 +1372,9 @@ static int aggregate_row(glp_tree *tree, struct MIR *mir) xassert(j != 0); jj = v->pos[kappa]; xassert(jj != 0); -- spv_linear_comb(mir->agg_vec, +- ios_linear_comb(mir->agg_vec, - - mir->agg_vec->val[j] / v->val[jj], v); + guass_coeff = - mir->agg_vec->val[j] / v->val[jj]; + mir->agg_coeffs[mir->agg_cnt] = guass_coeff; -+ spv_linear_comb(mir->agg_vec, guass_coeff, v); - #if 0 /* 29/II-2016 by Chris */ ++ ios_linear_comb(mir->agg_vec, guass_coeff, v); ios_delete_vec(v); - #endif -@@ -1520,6 +1591,13 @@ void glp_mir_free(glp_mir *mir) + ios_set_vj(mir->agg_vec, kappa, 0.0); + #if _MIR_DEBUG +@@ -1440,6 +1513,13 @@ void ios_mir_term(void *gen) xfree(mir->subst); - spv_delete_vec(mir->mod_vec); - spv_delete_vec(mir->cut_vec); + ios_delete_vec(mir->mod_vec); + ios_delete_vec(mir->cut_vec); + + /* added */ + xfree(mir->cut_cset); @@ -1341,11 +1308,11 @@ index 4467189..4b03dcb 100644 xfree(mir); return; } -diff --git a/src/draft/glpios07.c b/src/draft/glpios07.c -index b8e700e..a246634 100644 ---- a/src/draft/glpios07.c -+++ b/src/draft/glpios07.c -@@ -537,6 +537,14 @@ void ios_cov_gen(glp_tree *tree) +diff --git a/src/glpios07.c b/src/glpios07.c +index 0892550..9f742e6 100644 +--- a/src/glpios07.c ++++ b/src/glpios07.c +@@ -543,6 +543,14 @@ void ios_cov_gen(glp_tree *tree) /* add the cut to the cut pool */ glp_ios_add_row(tree, NULL, GLP_RF_COV, 0, len, ind, val, GLP_UP, val[0]); @@ -1360,11 +1327,31 @@ index b8e700e..a246634 100644 } /* free working arrays */ xfree(ind); -diff --git a/src/draft/glpios11.c b/src/draft/glpios11.c -index e23b97f..13978d1 100644 ---- a/src/draft/glpios11.c -+++ b/src/draft/glpios11.c -@@ -324,6 +324,9 @@ void ios_process_cuts(glp_tree *T) +diff --git a/src/glpios08.c b/src/glpios08.c +index 2d2fcd5..3aad808 100644 +--- a/src/glpios08.c ++++ b/src/glpios08.c +@@ -129,6 +129,15 @@ void ios_clq_gen(glp_tree *T, void *G_) + /* add cut inequality to local cut pool */ + glp_ios_add_row(T, NULL, GLP_RF_CLQ, 0, len, ind, val, GLP_UP, + rhs); ++ ++ /** callback for a cut being added to the cut pool */ ++ if (T->parm->cb_func != NULL) ++ { xassert(T->reason == GLP_ICUTGEN); ++ T->reason = GLP_ICUTADDED; ++ T->parm->cb_func(T, T->parm->cb_info); ++ T->reason = GLP_ICUTGEN; ++ } ++ + skip: /* free working arrays */ + tfree(ind); + tfree(val); +diff --git a/src/glpios11.c b/src/glpios11.c +index c40e9a5..82d5698 100644 +--- a/src/glpios11.c ++++ b/src/glpios11.c +@@ -193,6 +193,9 @@ void ios_process_cuts(glp_tree *T) glp_set_mat_row(T->mip, i, len, ind, val); xassert(cut->type == GLP_LO || cut->type == GLP_UP); glp_set_row_bnds(T->mip, i, cut->type, cut->rhs, cut->rhs); @@ -1375,18 +1362,18 @@ index e23b97f..13978d1 100644 /* free working arrays */ xfree(info); diff --git a/src/glpk.h b/src/glpk.h -index ea79ec2..5034199 100644 +index 75c292c..e117782 100644 --- a/src/glpk.h +++ b/src/glpk.h -@@ -140,6 +140,7 @@ typedef struct - int aorn; /* option to use A or N: */ - #define GLP_USE_AT 1 /* use A matrix in row-wise format */ - #define GLP_USE_NT 2 /* use N matrix in row-wise format */ +@@ -130,6 +130,7 @@ typedef struct + int out_frq; /* spx.out_frq */ + int out_dly; /* spx.out_dly (milliseconds) */ + int presolve; /* enable/disable using LP presolver */ + int stability_lmt; /* maximum number of check stability failures before stopping */ - double foo_bar[33]; /* (reserved) */ - #endif + double foo_bar[36]; /* (reserved) */ } glp_smcp; -@@ -238,6 +239,11 @@ typedef struct + +@@ -229,6 +230,11 @@ typedef struct #define GLP_IBRANCH 0x05 /* request for branching */ #define GLP_ISELECT 0x06 /* request for subproblem selection */ #define GLP_IPREPRO 0x07 /* request for preprocessing */ @@ -1398,7 +1385,7 @@ index ea79ec2..5034199 100644 /* branch selection indicator: */ #define GLP_NO_BRNCH 0 /* select no branch */ -@@ -1164,6 +1170,54 @@ int glp_top_sort(glp_graph *G, int v_num); +@@ -1057,6 +1063,54 @@ int glp_top_sort(glp_graph *G, int v_num); int glp_wclique_exact(glp_graph *G, int v_wgt, double *sol, int v_set); /* find maximum weight clique with exact algorithm */ @@ -1453,81 +1440,89 @@ index ea79ec2..5034199 100644 #ifdef __cplusplus } #endif -%% -%% Primal simplex method was reimplemented in 4.56 -%% -diff --git a/src/simplex/spxprim.c b/src/simplex/spxprim.c -index d004197..31a1c7b 100644 ---- a/src/simplex/spxprim.c -+++ b/src/simplex/spxprim.c -@@ -182,6 +182,10 @@ struct csa - int ns_cnt, ls_cnt; - /* normal and long-step iteration counts */ - #endif +diff --git a/src/glpspx01.c b/src/glpspx01.c +index 5c17114..caaab27 100644 +--- a/src/glpspx01.c ++++ b/src/glpspx01.c +@@ -238,6 +238,9 @@ struct csa + double *work2; /* double work2[1+m]; */ + double *work3; /* double work3[1+m]; */ + double *work4; /* double work4[1+m]; */ + + /** Things Tim has added. */ + int stability_failures; -+ int stability_lmt; }; - /*********************************************************************** -@@ -1213,6 +1217,11 @@ loop: /* main loop starts here */ - if (perturb <= 0) - { if (check_feas(csa, csa->phase, tol_bnd, tol_bnd1)) - { /* excessive bound violations due to round-off errors */ -+ csa->stability_failures++; -+ if (csa->stability_failures >= csa->stability_lmt){ -+ ret = GLP_EINSTAB; -+ goto fini; -+ } - #if 1 /* 01/VII-2017 */ - if (perturb < 0) - { if (msg_lev >= GLP_MSG_ALL) -@@ -1760,6 +1769,8 @@ int spx_primal(glp_prob *P, const glp_smcp *parm) - #if 1 /* 23/VI-2017 */ - csa->ns_cnt = csa->ls_cnt = 0; - #endif -+ csa->stability_failures = 0; -+ csa->stability_lmt = parm->stability_lmt; - /* try to solve working LP */ - ret = primal_simplex(csa); - /* return basis factorization back to problem object */ -%% -%% Dual simplex method was reimplemented in 4.57 -%% -diff --git a/src/simplex/spydual.c b/src/simplex/spydual.c -index 770b621..f04afa7 100644 ---- a/src/simplex/spydual.c -+++ b/src/simplex/spydual.c -@@ -197,6 +197,10 @@ struct csa - int ns_cnt, ls_cnt; - /* normal and long-step iteration count */ - #endif + static const double kappa = 0.10; +@@ -400,6 +403,8 @@ static void init_csa(struct csa *csa, glp_prob *lp) + csa->refct = 0; + memset(&refsp[1], 0, (m+n) * sizeof(char)); + for (j = 1; j <= n; j++) gamma[j] = 1.0; + -+ /** Things Tim has added. */ ++ csa->stability_failures = 0; + return; + } + +@@ -2647,6 +2652,11 @@ loop: /* main loop starts here */ + if (check_stab(csa, parm->tol_bnd)) + { /* there are excessive bound violations due to round-off + errors */ ++ csa->stability_failures++; ++ if (csa->stability_failures >= parm->stability_lmt){ ++ ret = GLP_EINSTAB; ++ goto done; ++ } + if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("Warning: numerical instability (primal simplex," + " phase %s)\n", csa->phase == 1 ? "I" : "II"); +diff --git a/src/glpspx02.c b/src/glpspx02.c +index 19152a6..de3d677 100644 +--- a/src/glpspx02.c ++++ b/src/glpspx02.c +@@ -288,6 +288,9 @@ struct csa + double *work2; /* double work2[1+m]; */ + double *work3; /* double work3[1+m]; */ + double *work4; /* double work4[1+m]; */ ++ ++ /* count the number of stability failures */ + int stability_failures; -+ int stability_lmt; }; - /*********************************************************************** -@@ -1329,6 +1333,11 @@ loop: /* main loop starts here */ - { if (check_feas(csa, tol_dj, tol_dj1, 0)) - { /* dual feasibility is broken due to excessive round-off - * errors */ -+ csa->stability_failures++; -+ if (csa->stability_failures >= csa->stability_lmt){ -+ ret = GLP_EINSTAB; -+ goto fini; -+ } - if (perturb < 0) - { if (msg_lev >= GLP_MSG_ALL) - xprintf("Perturbing LP to avoid instability [%d].." -@@ -2004,6 +2013,8 @@ int spy_dual(glp_prob *P, const glp_smcp *parm) - #if 1 /* 23/III-2016 */ - csa->ns_cnt = csa->ls_cnt = 0; - #endif + static const double kappa = 0.10; +@@ -501,6 +504,8 @@ static void init_csa(struct csa *csa, glp_prob *lp) + csa->refct = 0; + memset(&refsp[1], 0, (m+n) * sizeof(char)); + for (i = 1; i <= m; i++) gamma[i] = 1.0; ++ + csa->stability_failures = 0; -+ csa->stability_lmt = parm->stability_lmt; - /* try to solve working LP */ - ret = dual_simplex(csa); - /* return basis factorization back to problem object */ + return; + } + +@@ -2741,7 +2746,13 @@ loop: /* main loop starts here */ + /* make sure that the current basic solution remains dual + feasible */ + if (check_stab(csa, parm->tol_dj) != 0) +- { if (parm->msg_lev >= GLP_MSG_ERR) ++ { ++ csa->stability_failures++; ++ if (csa->stability_failures >= parm->stability_lmt){ ++ ret = GLP_EINSTAB; ++ goto done; ++ } ++ if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("Warning: numerical instability (dual simplex, p" + "hase %s)\n", csa->phase == 1 ? "I" : "II"); + #if 1 +@@ -3023,6 +3034,10 @@ loop: /* main loop starts here */ + /* accuracy check based on the pivot element */ + { double piv1 = csa->tcol_vec[csa->p]; /* more accurate */ + double piv2 = csa->trow_vec[csa->q]; /* less accurate */ ++ if(piv1 == 0.0){ ++ ret = GLP_EFAIL; ++ goto done; ++ } + xassert(piv1 != 0.0); + if (fabs(piv1 - piv2) > 1e-8 * (1.0 + fabs(piv1)) || + !(piv1 > 0.0 && piv2 > 0.0 || piv1 < 0.0 && piv2 < 0.0)) +-- +2.26.2 diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index fce444123b2..47adf5bebe7 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -85,12 +85,18 @@ if(TARGET cvc5::cvc5jar) get_target_property(CVC5_JAR cvc5::cvc5jar JAR_FILE) message(STATUS "cvc5jar: ${CVC5_JAR}") + if(WIN32) + set(PATH_SEP ";") + else() + set(PATH_SEP ":") + endif() + add_jar(SimpleVC SimpleVC.java INCLUDE_JARS "${CVC5_JAR}") add_test( NAME java/SimpleVC COMMAND ${Java_JAVA_EXECUTABLE} - -cp "${CVC5_JAR}:${CMAKE_BINARY_DIR}/SimpleVC.jar" + -cp "${CVC5_JAR}${PATH_SEP}${CMAKE_BINARY_DIR}/SimpleVC.jar" -Djava.library.path=${CVC5_JNI_PATH} SimpleVC ) diff --git a/examples/api/java/CMakeLists.txt b/examples/api/java/CMakeLists.txt index f4c1ea015b2..57f5fb1a980 100644 --- a/examples/api/java/CMakeLists.txt +++ b/examples/api/java/CMakeLists.txt @@ -43,11 +43,17 @@ function(add_java_example example) set(EXAMPLE_TEST_NAME api/java/${example}) + if(WIN32) + set(PATH_SEP ";") + else() + set(PATH_SEP ":") + endif() + add_test( NAME ${EXAMPLE_TEST_NAME} COMMAND ${Java_JAVA_EXECUTABLE} - -cp "${CVC5_JAR}:${CMAKE_BINARY_DIR}/bin/api/java/${example}.jar" + -cp "${CVC5_JAR}${PATH_SEP}${CMAKE_BINARY_DIR}/bin/api/java/${example}.jar" -Djava.library.path=${CVC5_JNI_PATH} ${example} ) diff --git a/include/cvc5/c/cvc5.h b/include/cvc5/c/cvc5.h index b984b39f7a9..0aadb088804 100644 --- a/include/cvc5/c/cvc5.h +++ b/include/cvc5/c/cvc5.h @@ -67,12 +67,20 @@ typedef struct cvc5_term_t* Cvc5Term; typedef struct cvc5_op_t* Cvc5Op; /** A cvc5 datatype. */ -typedef struct Cvc5Datatype Cvc5Datatype; +typedef struct cvc5_dt_t* Cvc5Datatype; +/** + * A cvc5 datatype selector. + */ +typedef struct cvc5_dt_sel_t* Cvc5DatatypeSelector; +/** + * A cvc5 datatype constructor. + */ +typedef struct cvc5_dt_cons_t* Cvc5DatatypeConstructor; /** * A cvc5 datatype constructor declaration. A datatype constructor declaration * is a specification used for creating a datatype constructor. */ -typedef struct Cvc5DatatypeConstructorDecl Cvc5DatatypeConstructorDecl; +typedef struct cvc5_dt_cons_decl_t* Cvc5DatatypeConstructorDecl; /** * A cvc5 datatype declaration. A datatype declaration is not itself a datatype * (see Datatype), but a specification for creating a datatype sort. @@ -85,15 +93,7 @@ typedef struct Cvc5DatatypeConstructorDecl Cvc5DatatypeConstructorDecl; * - cvc5_mk_datatype_sort() * - cvc5_mk_datatype_sorts() */ -typedef struct Cvc5DatatypeDecl Cvc5DatatypeDecl; -/** - * A cvc5 datatype selector. - */ -typedef struct Cvc5DatatypeSelector Cvc5DatatypeSelector; -/** - * A cvc5 datatype constructor. - */ -typedef struct Cvc5DatatypeConstructor Cvc5DatatypeConstructor; +typedef struct cvc5_dt_decl_t* Cvc5DatatypeDecl; /** * A Sygus Grammar. This can be used to define a context-free grammar @@ -539,11 +539,14 @@ Cvc5Datatype* cvc5_sort_get_datatype(Cvc5Sort sort); * * Create sort parameters with cvc5_mk_param_sort(). * - * @param sort The sort to instantiate. + * @param sort The sort to instantiate. + * @param size The number of sort parameters to instantiate with. * @param params The list of sort parameters to instantiate with. * @return The instantiated sort. */ -Cvc5Sort cvc5_sort_instantiate(Cvc5Sort sort, Cvc5Sort params[]); +Cvc5Sort cvc5_sort_instantiate(Cvc5Sort sort, + size_t size, + const Cvc5Sort params[]); /** * Get the sorts used to instantiate the sort parameters of a given parametric @@ -593,8 +596,8 @@ Cvc5Sort cvc5_sort_substitute(Cvc5Sort sort, Cvc5Sort s, Cvc5Sort replacement); */ Cvc5Sort cvc5_sort_substitute_sorts(Cvc5Sort sort, size_t size, - const Cvc5Sort* sorts, - const Cvc5Sort* replacements); + const Cvc5Sort sorts[], + const Cvc5Sort replacements[]); /** * Get a string representation of a given sort. @@ -1002,8 +1005,8 @@ Cvc5Term cvc5_term_substitute_term(Cvc5Term term, */ Cvc5Term cvc5_term_substitute_terms(Cvc5Term term, size_t size, - const Cvc5Term* terms, - const Cvc5Term* replacements); + const Cvc5Term terms[], + const Cvc5Term replacements[]); /** * Determine if a given term has an operator. @@ -1375,7 +1378,7 @@ bool cvc5_term_is_fp_value(Cvc5Term term); * @param term The term. * @param ew The resulting exponent width. * @param sw The resulting significand width. - * @param val The bit-vector value term. + * @param val The resulting bit-vector value term. */ void cvc5_term_get_fp_value(Cvc5Term term, uint32_t* ew, @@ -1561,17 +1564,17 @@ size_t cvc5_term_hash(Cvc5Term term); * @param name The name of the datatype selector declaration to add. * @param sort The codomain sort of the datatype selector declaration to add. */ -void cvc5_dt_consdecl_add_selector(Cvc5DatatypeConstructorDecl* decl, - const char* name, - Cvc5Sort sort); +void cvc5_dt_cons_decl_add_selector(Cvc5DatatypeConstructorDecl decl, + const char* name, + Cvc5Sort sort); /** * Add datatype selector declaration whose codomain type is the datatype * itself to a given constructor declaration. * @param decl The datatype constructor declaration. * @param name The name of the datatype selector declaration to add. */ -void cvc5_dt_consdecl_add_selector_self(Cvc5DatatypeConstructorDecl* decl, - const char* name); +void cvc5_dt_cons_decl_add_selector_self(Cvc5DatatypeConstructorDecl decl, + const char* name); /** * Add datatype selector declaration whose codomain sort is an unresolved @@ -1581,9 +1584,9 @@ void cvc5_dt_consdecl_add_selector_self(Cvc5DatatypeConstructorDecl* decl, * @param unres_name The name of the unresolved datatype. The codomain of the * selector will be the resolved datatype with the given name. */ -void cvc5_dt_consdecl_add_selector_unresolved(Cvc5DatatypeConstructorDecl* decl, - const char* name, - const char* unres_name); +void cvc5_dt_cons_decl_add_selector_unresolved(Cvc5DatatypeConstructorDecl decl, + const char* name, + const char* unres_name); /** * Get a string representation of a given constructor declaration. * @param decl The datatype constructor declaration. @@ -1591,7 +1594,7 @@ void cvc5_dt_consdecl_add_selector_unresolved(Cvc5DatatypeConstructorDecl* decl, * @note The returned char* pointer is only valid until the next call to this * function. */ -const char* cvc5_dt_consdecl_to_string(Cvc5DatatypeConstructorDecl* decl); +const char* cvc5_dt_cons_decl_to_string(Cvc5DatatypeConstructorDecl decl); /* -------------------------------------------------------------------------- */ /* Cvc5DatatypeDecl */ @@ -1602,15 +1605,15 @@ const char* cvc5_dt_consdecl_to_string(Cvc5DatatypeConstructorDecl* decl); * @param decl The datatype declaration. * @param ctor The datatype constructor declaration to add. */ -void cvc5_dt_decl_add_constructor(Cvc5DatatypeDecl* decl, - const Cvc5DatatypeConstructorDecl* ctor); +void cvc5_dt_decl_add_constructor(Cvc5DatatypeDecl decl, + Cvc5DatatypeConstructorDecl ctor); /** * Get the number of constructors for a given Datatype declaration. * @param decl The datatype declaration. * @return The number of constructors. */ -size_t cvc5_dt_decl_get_num_constructors(const Cvc5DatatypeDecl* decl); +size_t cvc5_dt_decl_get_num_constructors(Cvc5DatatypeDecl decl); /** * Determine if a given Datatype declaration is parametric. @@ -1618,7 +1621,7 @@ size_t cvc5_dt_decl_get_num_constructors(const Cvc5DatatypeDecl* decl); * @param decl The datatype declaration. * @return True if the datatype declaration is parametric. */ -bool cvc5_dt_decl_is_parametric(const Cvc5DatatypeDecl* decl); +bool cvc5_dt_decl_is_parametric(Cvc5DatatypeDecl decl); /** * Determine if a given datatype declaration is resolved (has already been @@ -1626,7 +1629,7 @@ bool cvc5_dt_decl_is_parametric(const Cvc5DatatypeDecl* decl); * @param decl The datatype declaration. * @return True if the datatype declaration is resolved. */ -bool cvc5_dt_decl_is_resolved(const Cvc5DatatypeDecl* decl); +bool cvc5_dt_decl_is_resolved(Cvc5DatatypeDecl decl); /** * Get a string representation of a given datatype declaration. @@ -1635,7 +1638,7 @@ bool cvc5_dt_decl_is_resolved(const Cvc5DatatypeDecl* decl); * @note The returned char* pointer is only valid until the next call to this * function. */ -const char* cvc5_dt_decl_to_string(const Cvc5DatatypeDecl* decl); +const char* cvc5_dt_decl_to_string(Cvc5DatatypeDecl decl); /** * Get the name of a given datatype declaration. @@ -1644,7 +1647,7 @@ const char* cvc5_dt_decl_to_string(const Cvc5DatatypeDecl* decl); * @note The returned char* pointer is only valid until the next call to this * function. */ -const char* cvc5_dt_decl_get_name(const Cvc5DatatypeDecl* decl); +const char* cvc5_dt_decl_get_name(Cvc5DatatypeDecl decl); /* -------------------------------------------------------------------------- */ /* Cvc5DatatypeSelector */ @@ -1657,7 +1660,7 @@ const char* cvc5_dt_decl_get_name(const Cvc5DatatypeDecl* decl); * @note The returned char* pointer is only valid until the next call to this * function. */ -const char* cvc5_dt_del_get_name(const Cvc5DatatypeSelector* sel); +const char* cvc5_dt_del_get_name(Cvc5DatatypeSelector sel); /** * Get the selector term of a given datatype selector. @@ -1669,7 +1672,7 @@ const char* cvc5_dt_del_get_name(const Cvc5DatatypeSelector* sel); * @param sel The datatype selector. * @return The selector term. */ -Cvc5Term cvc5_dt_sel_get_term(const Cvc5DatatypeSelector* sel); +Cvc5Term cvc5_dt_sel_get_term(Cvc5DatatypeSelector sel); /** * Get the updater term of a given datatype selector. @@ -1681,14 +1684,14 @@ Cvc5Term cvc5_dt_sel_get_term(const Cvc5DatatypeSelector* sel); * @param sel The datatype selector. * @return The updater term. */ -Cvc5Term cvc5_dt_sel_get_updater_term(const Cvc5DatatypeSelector* sel); +Cvc5Term cvc5_dt_sel_get_updater_term(Cvc5DatatypeSelector sel); /** * Get the codomain sort of a given selector. * @param sel The datatype selector. * @return The codomain sort of the selector. */ -Cvc5Sort cvc5_dt_sel_get_codomain_sort(const Cvc5DatatypeSelector* sel); +Cvc5Sort cvc5_dt_sel_get_codomain_sort(Cvc5DatatypeSelector sel); /** * Get the string representation of a given datatype selector. @@ -1697,7 +1700,7 @@ Cvc5Sort cvc5_dt_sel_get_codomain_sort(const Cvc5DatatypeSelector* sel); * @note The returned char* pointer is only valid until the next call to this * function. */ -const char* cvc5_dt_sel_to_string(const Cvc5DatatypeSelector* sel); +const char* cvc5_dt_sel_to_string(Cvc5DatatypeSelector sel); /* -------------------------------------------------------------------------- */ /* Cvc5DatatypeConstructor */ @@ -1710,7 +1713,7 @@ const char* cvc5_dt_sel_to_string(const Cvc5DatatypeSelector* sel); * @note The returned char* pointer is only valid until the next call to this * function. */ -const char* cvc5_dt_cons_get_name(const Cvc5DatatypeConstructor* cons); +const char* cvc5_dt_cons_get_name(Cvc5DatatypeConstructor cons); /** * Get the constructor term of a given datatype constructor. @@ -1729,7 +1732,7 @@ const char* cvc5_dt_cons_get_name(const Cvc5DatatypeConstructor* cons); * @param cons The datatype constructor. * @return The constructor term. */ -Cvc5Term cvc5_dt_cons_get_term(const Cvc5DatatypeConstructor* cons); +Cvc5Term cvc5_dt_cons_get_term(Cvc5DatatypeConstructor cons); /** * Get the constructor term of this datatype constructor whose return @@ -1772,7 +1775,7 @@ Cvc5Term cvc5_dt_cons_get_term(const Cvc5DatatypeConstructor* cons); * @param sort The desired return sort of the constructor. * @return The constructor term. */ -Cvc5Term cvc5_dt_cons_get_instantiated_term(const Cvc5DatatypeConstructor* cons, +Cvc5Term cvc5_dt_cons_get_instantiated_term(Cvc5DatatypeConstructor cons, Cvc5Sort sort); /** @@ -1785,22 +1788,22 @@ Cvc5Term cvc5_dt_cons_get_instantiated_term(const Cvc5DatatypeConstructor* cons, * @param cons The datatype constructor. * @return The tester term. */ -Cvc5Term cvc5_dt_cons_get_tester_term(const Cvc5DatatypeConstructor* cons); +Cvc5Term cvc5_dt_cons_get_tester_term(Cvc5DatatypeConstructor cons); /** * Get the number of selectors of a given datatype constructor. * @param cons The datatype constructor. * @return The number of selectors. */ -size_t cvc5_dt_cons_get_num_selectors(const Cvc5DatatypeConstructor* cons); +size_t cvc5_dt_cons_get_num_selectors(Cvc5DatatypeConstructor cons); /** * Get the selector at index `i` of a given datatype constructor. * @param cons The datatype constructor. * @return The i^th DatatypeSelector. */ -Cvc5DatatypeSelector* cvc5_dt_cons_get_selector( - const Cvc5DatatypeConstructor* cons, size_t index); +Cvc5DatatypeSelector cvc5_dt_cons_get_selector(Cvc5DatatypeConstructor cons, + size_t index); /** * Get the datatype selector with the given name. * @note This is a linear search through the selectors, so in case of @@ -1809,8 +1812,8 @@ Cvc5DatatypeSelector* cvc5_dt_cons_get_selector( * @param name The name of the datatype selector. * @return The first datatype selector with the given name. */ -Cvc5DatatypeSelector* cvc5_dt_cons_get_selector_by_name( - const Cvc5DatatypeConstructor* cons, const char* name); +Cvc5DatatypeSelector cvc5_dt_cons_get_selector_by_name( + Cvc5DatatypeConstructor cons, const char* name); /** * Get a string representation of a given datatype constructor. @@ -1819,7 +1822,7 @@ Cvc5DatatypeSelector* cvc5_dt_cons_get_selector_by_name( * @note The returned char* pointer is only valid until the next call to this * function. */ -const char* cvc5_dt_cons_to_string(const Cvc5DatatypeConstructor* cons); +const char* cvc5_dt_cons_to_string(Cvc5DatatypeConstructor cons); /* -------------------------------------------------------------------------- */ /* Cvc5Datatype */ @@ -1831,8 +1834,7 @@ const char* cvc5_dt_cons_to_string(const Cvc5DatatypeConstructor* cons); * @param idx The index of the datatype constructor to return. * @return The datatype constructor with the given index. */ -Cvc5DatatypeConstructor* cvc5_dt_get_constructor(const Cvc5Datatype* dt, - size_t idx); +Cvc5DatatypeConstructor cvc5_dt_get_constructor(Cvc5Datatype dt, size_t idx); /** * Get the datatype constructor of a given datatype with the given name. @@ -1842,8 +1844,8 @@ Cvc5DatatypeConstructor* cvc5_dt_get_constructor(const Cvc5Datatype* dt, * @param name The name of the datatype constructor. * @return The datatype constructor with the given name. */ -Cvc5DatatypeConstructor* cvc5_dt_get_constructor_by_name(const Cvc5Datatype* dt, - const char* name); +Cvc5DatatypeConstructor cvc5_dt_get_constructor_by_name(Cvc5Datatype dt, + const char* name); /** * Get the datatype selector of a given datatype with the given name. @@ -1854,8 +1856,7 @@ Cvc5DatatypeConstructor* cvc5_dt_get_constructor_by_name(const Cvc5Datatype* dt, * @param name The name of the datatype selector. * @return The datatype selector with the given name. */ -Cvc5DatatypeSelector* cvc5_dt_get_selector(const Cvc5Datatype* dt, - const char* name); +Cvc5DatatypeSelector cvc5_dt_get_selector(Cvc5Datatype dt, const char* name); /** * Get the name of a given datatype. @@ -1864,14 +1865,14 @@ Cvc5DatatypeSelector* cvc5_dt_get_selector(const Cvc5Datatype* dt, * @note The returned char* pointer is only valid until the next call to this * function. */ -const char* cvc5_dt_get_name(const Cvc5Datatype* dt); +const char* cvc5_dt_get_name(Cvc5Datatype dt); /** * Get the number of constructors of a given datatype. * @param dt The datatype. * @return The number of constructors. */ -size_t cvc5_dt_get_num_constructors(const Cvc5Datatype* dt); +size_t cvc5_dt_get_num_constructors(Cvc5Datatype dt); /** * Get the parameters of a given datatype, if it is parametric. @@ -1881,7 +1882,7 @@ size_t cvc5_dt_get_num_constructors(const Cvc5Datatype* dt); * @param size The size of the resulting array. * @return The parameters of this datatype. */ -const Cvc5Sort* cvc5_dt_get_parameters(const Cvc5Datatype* dt, size_t* size); +const Cvc5Sort* cvc5_dt_get_parameters(Cvc5Datatype dt, size_t* size); /** * Determine if a given datatype is parametric. @@ -1889,21 +1890,21 @@ const Cvc5Sort* cvc5_dt_get_parameters(const Cvc5Datatype* dt, size_t* size); * @param dt The datatype. * @return True if the datatype is parametric. */ -bool cvc5_dt_is_parametric(const Cvc5Datatype* dt); +bool cvc5_dt_is_parametric(Cvc5Datatype dt); /** * Determine if a given datatype corresponds to a co-datatype. * @param dt The datatype. * @return True if the datatype corresponds to a co-datatype. */ -bool cvc5_dt_is_codatatype(const Cvc5Datatype* dt); +bool cvc5_dt_is_codatatype(Cvc5Datatype dt); /** * Determine if a given datatype corresponds to a tuple. * @param dt The datatype. * @return True if this datatype corresponds to a tuple. */ -bool cvc5_dt_is_tuple(const Cvc5Datatype* dt); +bool cvc5_dt_is_tuple(Cvc5Datatype dt); /** * Determine if a given datatype corresponds to a record. @@ -1911,14 +1912,14 @@ bool cvc5_dt_is_tuple(const Cvc5Datatype* dt); * @param dt The datatype. * @return True if the datatype corresponds to a record. */ -bool cvc5_dt_is_record(const Cvc5Datatype* dt); +bool cvc5_dt_is_record(Cvc5Datatype dt); /** * Determine if a given datatype is finite. * @param dt The datatype. * @return True if the datatype is finite. */ -bool cvc5_dt_is_finite(const Cvc5Datatype* dt); +bool cvc5_dt_is_finite(Cvc5Datatype dt); /** * Determine if a given datatype is well-founded. @@ -1929,7 +1930,7 @@ bool cvc5_dt_is_finite(const Cvc5Datatype* dt); * @param dt The datatype. * @return True if the datatype is well-founded. */ -bool cvc5_dt_is_well_founded(const Cvc5Datatype* dt); +bool cvc5_dt_is_well_founded(Cvc5Datatype dt); /** * Get a string representation of a given datatype. @@ -1937,7 +1938,7 @@ bool cvc5_dt_is_well_founded(const Cvc5Datatype* dt); * @note The returned char* pointer is only valid until the next call to this * function. */ -const char* cvc5_dt_to_string(const Cvc5Datatype* dt); +const char* cvc5_dt_to_string(Cvc5Datatype dt); /* -------------------------------------------------------------------------- */ /* Cvc5Grammar */ @@ -1963,7 +1964,7 @@ void cvc5_grammar_add_rule(Cvc5Grammar* grammar, void cvc5_grammar_add_rules(Cvc5Grammar* grammar, Cvc5Term symbol, size_t size, - const Cvc5Term* rules); + const Cvc5Term rules[]); /** * Allow `symbol` to be an arbitrary constant of a given grammar. @@ -2005,6 +2006,18 @@ Cvc5TermManager* cvc5_term_manager_new(); */ void cvc5_term_manager_delete(Cvc5TermManager* tm); +/** + * Release all managed references. + * + * This will free all memory used by any managed objects allocated by the + * term manager. + * + * @note This invalidates all managed objects created by the term manager. + * + * @param tm The term manager instance. + */ +void cvc5_term_manager_release(Cvc5TermManager* tm); + /* .................................................................... */ /* Sorts Handling */ /* .................................................................... */ @@ -2088,23 +2101,23 @@ Cvc5Sort cvc5_mk_ff_sort(Cvc5TermManager* tm, const char* size, uint32_t base); /** * Create a datatype sort. - * @param tm The term manager instance. - * @param dtypedecl The datatype declaration from which the sort is created. + * @param tm The term manager instance. + * @param decl The datatype declaration from which the sort is created. * @return The datatype sort. */ -Cvc5Sort cvc5_mk_dt_sort(Cvc5TermManager* tm, const Cvc5DatatypeDecl& dtypedecl); +Cvc5Sort cvc5_mk_dt_sort(Cvc5TermManager* tm, Cvc5DatatypeDecl decl); /** * Create a vector of datatype sorts. * @note The names of the datatype declarations must be distinct. - * @param tm The term manager instance. - * @param size The number of datatype declarations. - * @param dtypedecls The datatype declarations from which the sort is created. + * @param tm The term manager instance. + * @param size The number of datatype declarations. + * @param decls The datatype declarations from which the sort is created. * @return The datatype sorts. */ const Cvc5Sort* cvc5_mk_dt_sorts(Cvc5TermManager* tm, size_t size, - const Cvc5DatatypeDecl* dtypedecls); + const Cvc5DatatypeDecl decls[]); /** * Create function sort. * @param tm The term manager instance. @@ -2115,7 +2128,7 @@ const Cvc5Sort* cvc5_mk_dt_sorts(Cvc5TermManager* tm, */ Cvc5Sort cvc5_mk_fun_sort(Cvc5TermManager* tm, size_t size, - const Cvc5Sort* sorts, + const Cvc5Sort sorts[], Cvc5Sort codomain); /** @@ -2138,7 +2151,7 @@ Cvc5Sort cvc5_mk_param_sort(Cvc5TermManager* tm, const char* symbol); */ Cvc5Sort cvc5_mk_predicate_sort(Cvc5TermManager* tm, size_t size, - const Cvc5Sort* sorts); + const Cvc5Sort sorts[]); /** * Create a record sort @@ -2150,8 +2163,8 @@ Cvc5Sort cvc5_mk_predicate_sort(Cvc5TermManager* tm, */ Cvc5Sort cvc5_mk_record_sort(Cvc5TermManager* tm, size_t size, - const char** names, - const Cvc5Sort* sorts); + const char* names[], + const Cvc5Sort sorts[]); /** * Create a set sort. * @param tm The term manager instance. @@ -2251,7 +2264,15 @@ Cvc5Sort cvc5_mk_uninterpreted_sort_constructor_sort(Cvc5TermManager* tm, */ Cvc5Sort cvc5_mk_tuple_sort(Cvc5TermManager* tm, size_t size, - const Cvc5Sort* sorts); + const Cvc5Sort sorts[]); + +/** + * Create a nullable sort. + * @param tm The term manager instance. + * @param sort The sort of the element of the nullable. + * @return The nullable sort. + */ +Cvc5Sort cvc5_mk_nullable_sort(Cvc5TermManager* tm, Cvc5Sort sort); /* .................................................................... */ /* Create Terms */ @@ -2267,7 +2288,7 @@ Cvc5Sort cvc5_mk_tuple_sort(Cvc5TermManager* tm, Cvc5Term cvc5_mk_term(Cvc5TermManager* tm, Cvc5Kind kind, size_t size, - const Cvc5Term* children); + const Cvc5Term children[]); /** * Create n-ary term of given kind from a given operator. @@ -2281,7 +2302,7 @@ Cvc5Term cvc5_mk_term(Cvc5TermManager* tm, Cvc5Term cvc5_mk_term_from_op(Cvc5TermManager* tm, Cvc5Op op, size_t size, - const Cvc5Term* children); + const Cvc5Term children[]); /** * Create a tuple term. @@ -2290,7 +2311,9 @@ Cvc5Term cvc5_mk_term_from_op(Cvc5TermManager* tm, * @param terms The elements. * @return The tuple Term. */ -Cvc5Term cvc5_mk_tuple(Cvc5TermManager* tm, size_t size, const Cvc5Term* terms); +Cvc5Term cvc5_mk_tuple(Cvc5TermManager* tm, + size_t size, + const Cvc5Term terms[]); /** * Create a nullable some term. @@ -2341,7 +2364,9 @@ Cvc5Term cvc5_mk_nullable_null(Cvc5Sort sort); * is a lambda expression, and the remaining children are * the original arguments. */ -Cvc5Term cvc5_mk_nullable_lift(Cvc5Kind kind, size_t nargs, Cvc5Term* args); +Cvc5Term cvc5_mk_nullable_lift(Cvc5Kind kind, + size_t nargs, + const Cvc5Term args[]); /* .................................................................... */ /* Create Operators */ @@ -2379,7 +2404,7 @@ Cvc5Term cvc5_mk_nullable_lift(Cvc5Kind kind, size_t nargs, Cvc5Term* args); Cvc5Op cvc5_mk_op(Cvc5TermManager* tm, Cvc5Kind kind, size_t size, - const uint32_t* idxs); + const uint32_t idxs[]); /** * Create operator of kind: @@ -2760,7 +2785,7 @@ Cvc5Term cvc5_mk_var(Cvc5TermManager* tm, Cvc5Sort sort, const char* symbol); * @param name The name of the datatype constructor. * @return The DatatypeConstructorDecl. */ -Cvc5DatatypeConstructorDecl* cvc5_mk_dt_consdecl(Cvc5TermManager* tm, +Cvc5DatatypeConstructorDecl cvc5_mk_dt_cons_decl(Cvc5TermManager* tm, const char* name); /* .................................................................... */ @@ -2774,9 +2799,9 @@ Cvc5DatatypeConstructorDecl* cvc5_mk_dt_consdecl(Cvc5TermManager* tm, * @param is_codt True if a codatatype is to be constructed. * @return The Cvc5DatatypeDecl. */ -Cvc5DatatypeDecl* cvc5_mk_dt_decl(Cvc5TermManager* tm, - const char* name, - bool is_codt); +Cvc5DatatypeDecl cvc5_mk_dt_decl(Cvc5TermManager* tm, + const char* name, + bool is_codt); /** * Create a datatype declaration. @@ -2791,11 +2816,11 @@ Cvc5DatatypeDecl* cvc5_mk_dt_decl(Cvc5TermManager* tm, * @param is_codt True if a codatatype is to be constructed. * @return The Cvc5DatatypeDecl. */ -Cvc5DatatypeDecl* cvc5_mk_dt_decl_with_params(Cvc5TermManager* tm, - const char* name, - size_t size, - const Cvc5Sort* params, - bool is_codt); +Cvc5DatatypeDecl cvc5_mk_dt_decl_with_params(Cvc5TermManager* tm, + const char* name, + size_t size, + const Cvc5Sort params[], + bool is_codt); /* .................................................................... */ /* SMT-LIB-style Term/Sort Creation */ @@ -2821,7 +2846,7 @@ Cvc5DatatypeDecl* cvc5_mk_dt_decl_with_params(Cvc5TermManager* tm, Cvc5Sort cvc5_declare_dt(Cvc5* solver, const char* symbol, size_t size, - const Cvc5DatatypeConstructorDecl* ctors); + const Cvc5DatatypeConstructorDecl ctors[]); /** * Declare n-ary function symbol. @@ -2847,7 +2872,7 @@ Cvc5Sort cvc5_declare_dt(Cvc5* solver, Cvc5Term cvc5_declare_fun(Cvc5* solver, const char* symbol, size_t size, - const Cvc5Sort* sorts, + const Cvc5Sort sorts[], Cvc5Sort sort, bool fresh); @@ -2944,8 +2969,8 @@ const Cvc5Term* cvc5_proof_get_arguments(Cvc5Proof* proof); const char* cvc5_proof_to_string(Cvc5Proof* proof, Cvc5ProofFormat format, size_t assertions_size, - Cvc5Term* assertions, - const char** assertion_names); + const Cvc5Term assertions[], + const char* assertion_names[]); /* -------------------------------------------------------------------------- */ /* Cvc5 */ @@ -2953,9 +2978,10 @@ const char* cvc5_proof_to_string(Cvc5Proof* proof, /** * Construct a new instance of a cvc5 solver. + * @param tm The associated term manager instance. * @return The cvc5 solver instance. */ -Cvc5* cvc5_new(); +Cvc5* cvc5_new(Cvc5TermManager* tm); /** * Delete a cvc5 solver instance. @@ -2991,7 +3017,7 @@ void cvc5_delete(Cvc5* cvc5); Cvc5Term cvc5_define_fun(Cvc5* cvc5, const char* symbol, size_t nbound_vars, - const Cvc5Term* bound_vars, + const Cvc5Term bound_vars[], const Cvc5Sort sort, const Cvc5Term term, bool global); @@ -3020,7 +3046,7 @@ Cvc5Term cvc5_define_fun(Cvc5* cvc5, Cvc5Term cvc5_define_fun_rec(Cvc5* cvc5, const char* symbol, size_t nbound_vars, - const Cvc5Term* bound_vars, + const Cvc5Term bound_vars[], const Cvc5Sort sort, const Cvc5Term term, bool global); @@ -3049,7 +3075,7 @@ Cvc5Term cvc5_define_fun_rec(Cvc5* cvc5, Cvc5Term cvc5_define_fun_rec_from_const(Cvc5* cvc5, Cvc5Term fun, size_t nbound_vars, - const Cvc5Term* bound_vars, + const Cvc5Term bound_vars[], const Cvc5Term term, bool global); /** @@ -3079,10 +3105,10 @@ Cvc5Term cvc5_define_fun_rec_from_const(Cvc5* cvc5, */ void cvc5_define_funs_rec(Cvc5* cvc5, size_t nfuns, - const Cvc5Term* funs, - size_t* nbound_vars, - const Cvc5Term** bound_vars, - const Cvc5Term* terms, + const Cvc5Term funs[], + size_t nbound_vars[], + const Cvc5Term* bound_vars[], + const Cvc5Term terms[], bool global); /** * Simplify a formula without doing "much" work. @@ -3149,7 +3175,7 @@ Cvc5Result* cvc5_check_sat(Cvc5* cvc5); */ Cvc5Result* cvc5_check_sat_assuming(Cvc5* cvc5, size_t size, - const Cvc5Term* assumptions); + const Cvc5Term assumptions[]); /** * Get the list of asserted formulas. * @@ -3284,7 +3310,10 @@ const Cvc5Term* cvc5_get_unsat_core(Cvc5* cvc5, size_t* size); * @param inputs The resulting inputs that are mapped to the resulting `values`. * @param values The resulting real values. */ -void cvc5_get_difficulty(Cvc5* cvc5, size_t* size, Cvc5Term* inputs, Cvc5Term* values); +void cvc5_get_difficulty(Cvc5* cvc5, + size_t* size, + const Cvc5Term inputs[], + const Cvc5Term values[]); /** * Get a timeout core, which computes a subset of the current assertions that @@ -3396,7 +3425,9 @@ Cvc5Term cvc5_get_value(Cvc5* cvc5, Cvc5Term term); * @param terms The terms. * @return The values of the given terms. */ -const Cvc5Term* cvc5_get_values(Cvc5* cvc5, size_t size, const Cvc5Term* terms); +const Cvc5Term* cvc5_get_values(Cvc5* cvc5, + size_t size, + const Cvc5Term terms[]); /** * Get the domain elements of uninterpreted sort s in the current model. The @@ -3459,9 +3490,9 @@ bool cvc5_is_model_core_symbol(Cvc5* cvc5, Cvc5Term v); */ const char* cvc5_get_model(Cvc5* cvc5, size_t nsorts, - const Cvc5Sort* sorts, + const Cvc5Sort sorts[], size_t nconsts, - const Cvc5Term* consts); + const Cvc5Term consts[]); /** * Do quantifier elimination. @@ -3603,7 +3634,7 @@ Cvc5Term cvc5_declare_pool(Cvc5* cvc5, const char* symbol, Cvc5Sort sort, size_t size, - const Cvc5Term* init_value); + const Cvc5Term init_value[]); /** * Declare an oracle function with reference to an implementation. * @@ -3635,7 +3666,7 @@ Cvc5Term cvc5_declare_pool(Cvc5* cvc5, Cvc5Term cvc5_declare_oracle_fun(Cvc5* cvc5, const char* symbol, size_t nsorts, - const Cvc5Sort* sorts, + const Cvc5Sort sorts[], Cvc5Sort sort, Cvc5Term (*fun)(const Cvc5Term*)); /** @@ -3857,7 +3888,7 @@ void cvc5_block_model(Cvc5BlockModelsMode mode); * @param size The number of values to block. * @param terms The values to block. */ -void cvc5_block_model_values(Cvc5* cvc5, size_t size, const Cvc5Term* terms); +void cvc5_block_model_values(Cvc5* cvc5, size_t size, const Cvc5Term terms[]); /** * @warning This function is experimental and may change in future versions. @@ -3981,7 +4012,7 @@ Cvc5Term cvc5_declare_sygus_var(Cvc5* cvc5, const char* symbol, Cvc5Sort sort); */ Cvc5Grammar* cvc5_mk_grammar(Cvc5* cvc5, size_t size, - const Cvc5Term* bound_vars, + const Cvc5Term bound_vars[], Cvc5Term symbols); /** @@ -4005,7 +4036,7 @@ Cvc5Grammar* cvc5_mk_grammar(Cvc5* cvc5, Cvc5Term cvc5_synth_fun(Cvc5* cvc5, const char* symbol, size_t size, - const Cvc5Term* bound_vars, + const Cvc5Term bound_vars[], Cvc5Sort sort); /** @@ -4030,7 +4061,7 @@ Cvc5Term cvc5_synth_fun(Cvc5* cvc5, Cvc5Term cvc5_synth_fun_with_grammar(Cvc5* cvc5, const char* symbol, size_t size, - const Cvc5Term* bound_vars, + const Cvc5Term bound_vars[], Cvc5Sort sort, Cvc5Grammar* grammar); @@ -4166,7 +4197,7 @@ Cvc5Term cvc5_get_synth_solution(Cvc5Term term); * @return The synthesis solutions of the given terms. */ const Cvc5Term* cvc5_get_synth_solutions(Cvc5* cvc5, - const Cvc5Term* terms, + const Cvc5Term terms[], size_t* size); /** diff --git a/include/cvc5/cvc5.h b/include/cvc5/cvc5.h index ad2ef7e80dd..e4bca88e0f5 100644 --- a/include/cvc5/cvc5.h +++ b/include/cvc5/cvc5.h @@ -5459,18 +5459,19 @@ class CVC5_EXPORT Solver /* .................................................................... */ /** - * Simplify a formula without doing "much" work. + * Simplify a term or formula based on rewriting and (optionally) applying + * substitutions for solved variables. * - * Does not involve the SAT Engine in the simplification, but uses the - * current definitions, and assertions. It also involves theory - * normalization. + * If applySubs is true, then for example, if `(= x 0)` was asserted to this + * solver, this method may replace occurrences of `x` with `0`. * * @warning This function is experimental and may change in future versions. * - * @param t The formula to simplify. - * @return The simplified formula. + * @param t The term to simplify. + * @param applySubs Whether to apply substitutions for solved variables. + * @return The simplified term. */ - Term simplify(const Term& t); + Term simplify(const Term& t, bool applySubs = false); /** * Assert a formula. diff --git a/include/cvc5/cvc5_proof_rule.h b/include/cvc5/cvc5_proof_rule.h index a0694a49def..2218a95a4d3 100644 --- a/include/cvc5/cvc5_proof_rule.h +++ b/include/cvc5/cvc5_proof_rule.h @@ -311,7 +311,7 @@ enum ENUM(ProofRule) : uint32_t * **Other theory rewrite rules** * * .. math:: - * \inferrule{- \mid id, t}{t = t'} + * \inferrule{- \mid id, t = t'}{t = t'} * * where `id` is the :cpp:enum:`ProofRewriteRule` of the theory rewrite * rule which transforms :math:`t` to :math:`t'`. @@ -1253,20 +1253,6 @@ enum ENUM(ProofRule) : uint32_t * \endverbatim */ EVALUE(DT_UNIF), - /** - * \verbatim embed:rst:leading-asterisk - * **Datatypes -- Instantiation** - * - * .. math:: - * - * \inferrule{-\mid t,n}{\mathit{is}_C(t) = - * (t = C(\mathit{sel}_1(t),\dots,\mathit{sel}_n(t)))} - * - * where :math:`C` is the :math:`n^{\mathit{th}}` constructor of the type of - * t, and :math:`\mathit{is}_C` is the discriminator (tester) for :math:`C`. - * \endverbatim - */ - EVALUE(DT_INST), /** * \verbatim embed:rst:leading-asterisk * **Datatypes -- Split** @@ -1682,9 +1668,17 @@ enum ENUM(ProofRule) : uint32_t * * .. math:: * - * \inferrule{t\not\in R\mid -}{\texttt{RegExpOpr::reduceRegExpNeg}(t\not\in R)} + * \inferrule{t \not \in \mathit{re}.\text{*}(R) \mid -}{t \neq \ '' \ \wedge \forall L. L \leq 0 \vee \mathit{str.len}(t) < L \vee \mathit{pre}(t, L) \not \in R \vee \mathit{suf}(t, L) \not \in \mathit{re}.\text{*}(R)} + * + * Or alternatively for regular expression concatenation: + * + * .. math:: + * + * \inferrule{t \not \in \mathit{re}.\text{++}(R_1, \ldots, R_n)\mid -}{\forall L. L < 0 \vee \mathit{str.len}(t) < L \vee \mathit{pre}(t, L) \not \in R_1 \vee \mathit{suf}(t, L) \not \in \mathit{re}.\text{++}(R_2, \ldots, R_n)} + * + * Note that in either case the varaible :math:`L` has type :math:`Int` and + * name `"@var.str_index"`. * - * corresponding to the one-step unfolding of the premise. * \endverbatim */ EVALUE(RE_UNFOLD_NEG), @@ -1694,13 +1688,22 @@ enum ENUM(ProofRule) : uint32_t * * .. math:: * - * \inferrule{t\not\in R\mid - * -}{\texttt{RegExpOpr::reduceRegExpNegConcatFixed}(t\not\in R,L,i)} + * \inferrule{t\not\in \mathit{re}.\text{re.++}(r_1, \ldots, r_n) \mid \bot}{ + * \mathit{pre}(t, L) \not \in r_1 \vee \mathit{suf}(t, L) \not \in \mathit{re}.\text{re.++}(r_2, \ldots, r_n)} + * + * where :math:`r_1` has fixed length :math:`L`. + * + * or alternatively for the reverse: + * * - * where :math:`\texttt{RegExpOpr::getRegExpConcatFixed}(t\not\in R, i) = L`, - * corresponding to the one-step unfolding of the premise, optimized for fixed - * length of component :math:`i` of the regular expression concatenation - * :math:`R`. + * .. math:: + * + * \inferrule{t \not \in \mathit{re}.\text{re.++}(r_1, \ldots, r_n) \mid \top}{ + * \mathit{suf}(t, str.len(t) - L) \not \in r_n \vee + * \mathit{pre}(t, str.len(t) - L) \not \in \mathit{re}.\text{re.++}(r_1, \ldots, r_{n-1})} + * + * where :math:`r_n` has fixed length :math:`L`. + * * \endverbatim */ EVALUE(RE_UNFOLD_NEG_CONCAT_FIXED), @@ -2300,12 +2303,27 @@ enum ENUM(ProofRewriteRule) : uint32_t * \endverbatim */ EVALUE(DISTINCT_ELIM), + /** + * \verbatim embed:rst:leading-asterisk + * **Booleans -- Negation Normal Form with normalization** + * + * .. math:: + * F = G + * + * where :math:`G` is the result of applying negation normal form to + * :math:`F` with additional normalizations, see + * TheoryBoolRewriter::computeNnfNorm. + * + * \endverbatim + */ + EVALUE(MACRO_BOOL_NNF_NORM), /** * \verbatim embed:rst:leading-asterisk * **Equality -- Beta reduction** * * .. math:: - * ((\lambda x_1 \dots x_n.\> t) t_1 \ldots t_n) = t\{x_1 \mapsto t_1, \dots, x_n \mapsto t_n\} + * ((\lambda x_1 \ldots x_n.\> t) \ t_1 \ldots t_n) = t\{x_1 \mapsto t_1, + * \ldots, x_n \mapsto t_n\} * * The right hand side of the equality in the conclusion is computed using * standard substitution via ``Node::substitute``. @@ -2333,6 +2351,70 @@ enum ENUM(ProofRewriteRule) : uint32_t * \endverbatim */ EVALUE(EXISTS_ELIM), + /** + * \verbatim embed:rst:leading-asterisk + * **Quantifiers -- Unused variables** + * + * .. math:: + * \forall X.\> F = \forall X_1.\> F + * + * where :math:`X_1` is the subset of :math:`X` that appear free in :math:`F`. + * + * \endverbatim + */ + EVALUE(QUANT_UNUSED_VARS), + /** + * \verbatim embed:rst:leading-asterisk + * **Quantifiers -- Merge prenex** + * + * .. math:: + * \forall X_1.\> \ldots \forall X_n.\> F = \forall X_1 \ldots X_n.\> F + * + * where :math:`X_1 \ldots X_n` are lists of variables. + * + * \endverbatim + */ + EVALUE(QUANT_MERGE_PRENEX), + /** + * \verbatim embed:rst:leading-asterisk + * **Quantifiers -- Miniscoping** + * + * .. math:: + * \forall X.\> F_1 \wedge \ldots \wedge F_n = + * (\forall X.\> F_1) \wedge \ldots \wedge (\forall X.\> F_n) + * + * \endverbatim + */ + EVALUE(QUANT_MINISCOPE), + /** + * \verbatim embed:rst:leading-asterisk + * **Quantifiers -- Macro connected free variable partitioning** + * + * .. math:: + * \forall X.\> F_1 \vee \ldots \vee F_n = + * (\forall X_1.\> F_{1,1} \vee \ldots \vee F_{1,k_1}) \vee \ldots \vee + * (\forall X_m.\> F_{m,1} \vee \ldots \vee F_{m,k_m}) + * + * where :math:`X_1, \ldots, X_m` is a partition of :math:`X`. This is + * determined by computing the connected components when considering two + * variables in :math:`X` to be connected if they occur in the same + * :math:`F_i`. + * \endverbatim + */ + EVALUE(MACRO_QUANT_PARTITION_CONNECTED_FV), + /** + * \verbatim embed:rst:leading-asterisk + * **Datatypes -- Instantiation** + * + * .. math:: + * \mathit{is}_C(t) = (t = C(\mathit{sel}_1(t),\dots,\mathit{sel}_n(t))) + * + * where :math:`C` is the :math:`n^{\mathit{th}}` constructor of the type of + * :math:`t`, and :math:`\mathit{is}_C` is the discriminator (tester) for + * :math:`C`. + * \endverbatim + */ + EVALUE(DT_INST), /** * \verbatim embed:rst:leading-asterisk * **Datatypes - collapse selector** @@ -2350,18 +2432,30 @@ enum ENUM(ProofRewriteRule) : uint32_t * **Datatypes - collapse tester** * * .. math:: - * is\text{-}c(c(t_1, \ldots, t_n)) = true + * \mathit{is}_c(c(t_1, \ldots, t_n)) = true * * or alternatively * * .. math:: - * is\text{-}c(d(t_1, \ldots, t_n)) = false + * \mathit{is}_c(d(t_1, \ldots, t_n)) = false * * where :math:`c` and :math:`d` are distinct constructors. * * \endverbatim */ EVALUE(DT_COLLAPSE_TESTER), + /** + * \verbatim embed:rst:leading-asterisk + * **Datatypes - collapse tester** + * + * .. math:: + * \mathit{is}_c(t) = true + * + * where :math:`c` is the only constructor of its associated datatype. + * + * \endverbatim + */ + EVALUE(DT_COLLAPSE_TESTER_SINGLETON), /** * \verbatim embed:rst:leading-asterisk * **Datatypes - constructor equality** @@ -2392,6 +2486,24 @@ enum ENUM(ProofRewriteRule) : uint32_t * \endverbatim */ EVALUE(RE_LOOP_ELIM), + /** + * \verbatim embed:rst:leading-asterisk + * **Sets - empty tester evaluation** + * + * .. math:: + * \mathit{sets.is\_empty}(as \ \mathit{set.empty} \ (\mathit{Set} \ T)) = + * \top + * + * or alternatively: + * + * .. math:: + * \mathit{sets.is\_empty}(c) = \bot + * + * where :math:`c` is a constant set that is not the empty set. + * + * \endverbatim + */ + EVALUE(SETS_IS_EMPTY_EVAL), // RARE rules // ${rules}$ /** Auto-generated from RARE rule arith-plus-zero */ @@ -2448,6 +2560,10 @@ enum ENUM(ProofRewriteRule) : uint32_t EVALUE(ARRAY_STORE_SELF), /** Auto-generated from RARE rule bool-double-not-elim */ EVALUE(BOOL_DOUBLE_NOT_ELIM), + /** Auto-generated from RARE rule bool-not-true */ + EVALUE(BOOL_NOT_TRUE), + /** Auto-generated from RARE rule bool-not-false */ + EVALUE(BOOL_NOT_FALSE), /** Auto-generated from RARE rule bool-eq-true */ EVALUE(BOOL_EQ_TRUE), /** Auto-generated from RARE rule bool-eq-false */ @@ -2484,6 +2600,12 @@ enum ENUM(ProofRewriteRule) : uint32_t EVALUE(BOOL_AND_CONF), /** Auto-generated from RARE rule bool-or-taut */ EVALUE(BOOL_OR_TAUT), + /** Auto-generated from RARE rule bool-or-de-morgan */ + EVALUE(BOOL_OR_DE_MORGAN), + /** Auto-generated from RARE rule bool-implies-de-morgan */ + EVALUE(BOOL_IMPLIES_DE_MORGAN), + /** Auto-generated from RARE rule bool-and-de-morgan */ + EVALUE(BOOL_AND_DE_MORGAN), /** Auto-generated from RARE rule bool-xor-refl */ EVALUE(BOOL_XOR_REFL), /** Auto-generated from RARE rule bool-xor-nrefl */ @@ -2496,6 +2618,10 @@ enum ENUM(ProofRewriteRule) : uint32_t EVALUE(BOOL_XOR_COMM), /** Auto-generated from RARE rule bool-xor-elim */ EVALUE(BOOL_XOR_ELIM), + /** Auto-generated from RARE rule bool-not-xor-elim */ + EVALUE(BOOL_NOT_XOR_ELIM), + /** Auto-generated from RARE rule bool-not-eq-elim */ + EVALUE(BOOL_NOT_EQ_ELIM), /** Auto-generated from RARE rule ite-neg-branch */ EVALUE(ITE_NEG_BRANCH), /** Auto-generated from RARE rule ite-then-true */ @@ -2510,6 +2636,8 @@ enum ENUM(ProofRewriteRule) : uint32_t EVALUE(ITE_THEN_LOOKAHEAD_SELF), /** Auto-generated from RARE rule ite-else-lookahead-self */ EVALUE(ITE_ELSE_LOOKAHEAD_SELF), + /** Auto-generated from RARE rule bool-not-ite-elim */ + EVALUE(BOOL_NOT_ITE_ELIM), /** Auto-generated from RARE rule ite-true-cond */ EVALUE(ITE_TRUE_COND), /** Auto-generated from RARE rule ite-false-cond */ @@ -2852,20 +2980,46 @@ enum ENUM(ProofRewriteRule) : uint32_t EVALUE(BV_SIGN_EXTEND_ULT_CONST_3), /** Auto-generated from RARE rule bv-sign-extend-ult-const-4 */ EVALUE(BV_SIGN_EXTEND_ULT_CONST_4), + /** Auto-generated from RARE rule sets-eq-singleton-emp */ + EVALUE(SETS_EQ_SINGLETON_EMP), /** Auto-generated from RARE rule sets-member-singleton */ EVALUE(SETS_MEMBER_SINGLETON), + /** Auto-generated from RARE rule sets-member-emp */ + EVALUE(SETS_MEMBER_EMP), /** Auto-generated from RARE rule sets-subset-elim */ EVALUE(SETS_SUBSET_ELIM), /** Auto-generated from RARE rule sets-union-comm */ EVALUE(SETS_UNION_COMM), /** Auto-generated from RARE rule sets-inter-comm */ EVALUE(SETS_INTER_COMM), + /** Auto-generated from RARE rule sets-inter-emp1 */ + EVALUE(SETS_INTER_EMP1), + /** Auto-generated from RARE rule sets-inter-emp2 */ + EVALUE(SETS_INTER_EMP2), + /** Auto-generated from RARE rule sets-minus-emp1 */ + EVALUE(SETS_MINUS_EMP1), + /** Auto-generated from RARE rule sets-minus-emp2 */ + EVALUE(SETS_MINUS_EMP2), + /** Auto-generated from RARE rule sets-union-emp1 */ + EVALUE(SETS_UNION_EMP1), + /** Auto-generated from RARE rule sets-union-emp2 */ + EVALUE(SETS_UNION_EMP2), /** Auto-generated from RARE rule sets-inter-member */ EVALUE(SETS_INTER_MEMBER), /** Auto-generated from RARE rule sets-minus-member */ EVALUE(SETS_MINUS_MEMBER), /** Auto-generated from RARE rule sets-union-member */ EVALUE(SETS_UNION_MEMBER), + /** Auto-generated from RARE rule sets-choose-singleton */ + EVALUE(SETS_CHOOSE_SINGLETON), + /** Auto-generated from RARE rule sets-card-singleton */ + EVALUE(SETS_CARD_SINGLETON), + /** Auto-generated from RARE rule sets-card-union */ + EVALUE(SETS_CARD_UNION), + /** Auto-generated from RARE rule sets-card-minus */ + EVALUE(SETS_CARD_MINUS), + /** Auto-generated from RARE rule sets-card-emp */ + EVALUE(SETS_CARD_EMP), /** Auto-generated from RARE rule str-eq-ctn-false */ EVALUE(STR_EQ_CTN_FALSE), /** Auto-generated from RARE rule str-concat-flatten */ diff --git a/proofs/alf/cvc5/rules/Strings.smt3 b/proofs/alf/cvc5/rules/Strings.smt3 index 24a0987c2b3..bd974107e8e 100644 --- a/proofs/alf/cvc5/rules/Strings.smt3 +++ b/proofs/alf/cvc5/rules/Strings.smt3 @@ -1,4 +1,5 @@ (include "../theories/Strings.smt3") +(include "../theories/Quantifiers.smt3") (include "../programs/Strings.smt3") ;;-------------------- Core @@ -219,6 +220,47 @@ )) ) +; ProofRule::RE_UNFOLD_NEG_CONCAT_FIXED +(declare-rule re_unfold_neg_concat_fixed ((s String) (r RegLan) (rev Bool)) + :premises ((not (str.in_re s r))) + :args (rev) + :conclusion + (alf.match ((r1 RegLan) (r2 RegLan :list)) + ($str_rev rev r) + ( + ((re.++ r1 r2) (let ((n ($str_fixed_len_re r1))) + (alf.ite rev + (or (not (str.in_re ($str_suffix_len s n) r1)) + (not (str.in_re (skolem_prefix s (- (str.len s) n)) ($singleton_elim ($str_rev rev r2))))) + (or (not (str.in_re (skolem_prefix s n) r1)) + (not (str.in_re (skolem_suffix_rem s n) ($singleton_elim r2))))))) + )) +) + +; ProofRule::RE_UNFOLD_NEG +(declare-rule re_unfold_neg ((t String) (r RegLan)) + :premises ((not (str.in_re t r))) + :conclusion + (alf.match ((r1 RegLan) (r2 RegLan :list)) + r + ( + ((re.* r1) + (and + (not (= t "")) + (forall ((@var.str_index Int)) + (or (<= @var.str_index 0) + (< (str.len t) @var.str_index) + (not (str.in_re (skolem_prefix t @var.str_index) r1)) + (not (str.in_re (skolem_suffix_rem t @var.str_index) r)))))) + ((re.++ r1 r2) + (forall ((@var.str_index Int)) + (or (< @var.str_index 0) + (< (str.len t) @var.str_index) + (not (str.in_re (skolem_prefix t @var.str_index) r1)) + (not (str.in_re (skolem_suffix_rem t @var.str_index) ($singleton_elim r2)))))) + )) +) + ;;-------------------- Extended functions (declare-rule string_reduction ((U Type) (s U)) @@ -233,14 +275,15 @@ ;;-------------------- Instances of THEORY_REWRITE -(declare-rule str-in-re-eval ((s String) (r RegLan)) - :args ((str.in_re s r)) - :conclusion (= (str.in_re s r) ($str_eval_str_in_re s r)) +(declare-rule str-in-re-eval ((s String) (r RegLan) (b Bool)) + :args ((= (str.in_re s r) b)) + :requires ((($str_eval_str_in_re s r) b)) + :conclusion (= (str.in_re s r) b) ) -(declare-rule re-loop-elim ((l Int) (u Int) (r RegLan)) - :args ((re.loop l u r)) - :conclusion (let ((diff (alf.add (alf.neg l) u))) - (alf.requires (alf.is_neg diff) false - (= (re.loop l u r) ($str_mk_re_loop_elim l diff r)))) +(declare-rule re-loop-elim ((l Int) (u Int) (r1 RegLan) (r2 RegLan)) + :args ((= (re.loop l u r1) r2)) + :requires (((alf.is_neg (alf.add (alf.neg l) u)) false) + (($str_mk_re_loop_elim l (alf.add (alf.neg l) u) r1) r2)) + :conclusion (= (re.loop l u r1) r2) ) diff --git a/proofs/alf/cvc5/rules/Uf.smt3 b/proofs/alf/cvc5/rules/Uf.smt3 index 3476fb5c501..f6b8da226d3 100644 --- a/proofs/alf/cvc5/rules/Uf.smt3 +++ b/proofs/alf/cvc5/rules/Uf.smt3 @@ -129,3 +129,22 @@ ; HO_APP_ENCODE ; BETA_REDUCE + +;;-------------------- Instances of THEORY_REWRITE + +; Note that distinct is already treated as pairwise, thus we only need to convert from +; binary distinct to disequalities. +(program $mk_distinct-elim ((T Type) (x T) (y T) (b Bool :list)) + (Bool) Bool + ( + (($mk_distinct-elim (and (distinct x y) b)) (alf.cons and (not (= x y)) ($mk_distinct-elim b))) + (($mk_distinct-elim true) true) + (($mk_distinct-elim (distinct x y)) (alf.cons and (not (= x y)) true)) + ) +) + +(declare-rule distinct-elim ((b1 Bool) (b2 Bool)) + :args ((= b1 b2)) + :requires ((($singleton_elim ($mk_distinct-elim b1)) b2)) + :conclusion (= b1 b2) +) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 322b168a809..900f5666586 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -901,6 +901,8 @@ libcvc5_add_sources( theory/quantifiers/lazy_trie.h theory/quantifiers/master_eq_notify.cpp theory/quantifiers/master_eq_notify.h + theory/quantifiers/mbqi_fast_sygus.cpp + theory/quantifiers/mbqi_fast_sygus.h theory/quantifiers/oracle_checker.cpp theory/quantifiers/oracle_checker.h theory/quantifiers/oracle_engine.cpp @@ -1500,12 +1502,12 @@ endif() if(USE_CRYPTOMINISAT) add_dependencies(cvc5-obj CryptoMiniSat) target_include_directories(cvc5-obj SYSTEM PRIVATE ${CryptoMiniSat_INCLUDE_DIR}) - target_link_libraries(cvc5 PRIVATE CryptoMiniSat) + target_link_libraries(cvc5 PRIVATE $ $) endif() if(USE_KISSAT) add_dependencies(cvc5-obj Kissat) target_include_directories(cvc5-obj SYSTEM PRIVATE ${Kissat_INCLUDE_DIR}) - target_link_libraries(cvc5 PRIVATE Kissat) + target_link_libraries(cvc5 PRIVATE $ $) endif() if(USE_GLPK) target_include_directories(cvc5-obj SYSTEM PRIVATE ${GLPK_INCLUDE_DIR}) @@ -1525,7 +1527,7 @@ endif() if(USE_COCOA) add_dependencies(cvc5-obj CoCoA) target_include_directories(cvc5-obj SYSTEM PRIVATE ${CoCoA_INCLUDE_DIR}) - target_link_libraries(cvc5 PRIVATE CoCoA) + target_link_libraries(cvc5 PRIVATE $ $) endif() add_dependencies(cvc5-obj SymFPU) diff --git a/src/api/c/cvc5.cpp b/src/api/c/cvc5.cpp index 819b062e748..3d7a1cf2d45 100644 --- a/src/api/c/cvc5.cpp +++ b/src/api/c/cvc5.cpp @@ -173,3 +173,1339 @@ const char* cvc5_modes_input_language_to_string(Cvc5InputLanguage lang) CVC5_CAPI_TRY_CATCH_END; return str.c_str(); } + +/* -------------------------------------------------------------------------- */ +/* Wrapper structs */ +/* -------------------------------------------------------------------------- */ + +/** Wrapper for cvc5 C++ terms. */ +struct cvc5_term_t +{ + /** + * Constructor. + * @param term The wrapped C++ term. + * @param tm The associated term manager. + */ + cvc5_term_t(Cvc5TermManager* tm, const cvc5::Term& term) + : d_term(term), d_tm(tm) + { + } + /** The wrapped C++ term. */ + cvc5::Term d_term; + /** External refs count. */ + uint32_t d_refs = 1; + /** The associated term manager. */ + Cvc5TermManager* d_tm = nullptr; +}; + +/** Wrapper for cvc5 C++ sorts. */ +struct cvc5_sort_t +{ + /** + * Constructor. + * @param sort The wrapped C++ sort. + * @param tm The associated term manager. + */ + cvc5_sort_t(Cvc5TermManager* tm, const cvc5::Sort& sort) + : d_sort(sort), d_tm(tm) + { + } + /** The wrapped C++ sort. */ + cvc5::Sort d_sort; + /** External refs count. */ + uint32_t d_refs = 1; + /** The associated term manager. */ + Cvc5TermManager* d_tm = nullptr; +}; + +/** Wrapper for cvc5 C++ datatypes. */ +struct cvc5_dt_t +{ + /** + * Constructor. + * @param tm The associated term manager. + * @param dt The wrapped C++ datatype. + */ + cvc5_dt_t(Cvc5TermManager* tm, const cvc5::Datatype& dt) : d_dt(dt), d_tm(tm) + { + } + /** The wrapped C++ datatype. */ + cvc5::Datatype d_dt; + /** External refs count. */ + uint32_t d_refs = 1; + /** The associated term manager. */ + Cvc5TermManager* d_tm = nullptr; +}; + +/** Wrapper for cvc5 C++ datatype constructors. */ +struct cvc5_dt_cons_t +{ + /** + * Constructor. + * @param tm The associated term manager. + * @param dt The wrapped C++ datatype constructor. + */ + cvc5_dt_cons_t(Cvc5TermManager* tm, const cvc5::DatatypeConstructor& cons) + : d_dt_cons(cons), d_tm(tm) + { + } + /** The wrapped C++ datatype constructor. */ + cvc5::DatatypeConstructor d_dt_cons; + /** External refs count. */ + uint32_t d_refs = 1; + /** The associated term manager. */ + Cvc5TermManager* d_tm = nullptr; +}; + +/** Wrapper for cvc5 C++ datatype selectors. */ +struct cvc5_dt_sel_t +{ + /** + * Constructor. + * @param tm The associated term manager. + * @param dt The wrapped C++ datatype selector. + */ + cvc5_dt_sel_t(Cvc5TermManager* tm, const cvc5::DatatypeSelector& sel) + : d_dt_sel(sel), d_tm(tm) + { + } + /** The wrapped C++ datatype selector. */ + cvc5::DatatypeSelector d_dt_sel; + /** External refs count. */ + uint32_t d_refs = 1; + /** The associated term manager. */ + Cvc5TermManager* d_tm = nullptr; +}; + +/** Wrapper for cvc5 C++ datatype declarations. */ +struct cvc5_dt_decl_t +{ + /** + * Constructor. + * @param decl The wrapped C++ datatype declaration. + * @param tm The associated term manager. + */ + cvc5_dt_decl_t(Cvc5TermManager* tm, const cvc5::DatatypeDecl& decl) + : d_decl(decl), d_tm(tm) + { + } + /** The wrapped C++ datatype declaration. */ + cvc5::DatatypeDecl d_decl; + /** External refs count. */ + uint32_t d_refs = 1; + /** The associated term manager. */ + Cvc5TermManager* d_tm = nullptr; +}; + +/** Wrapper for cvc5 C++ datatype constructor declarations. */ +struct cvc5_dt_cons_decl_t +{ + /** + * Constructor. + * @param decl The wrapped C++ datatype constructor declaration. + * @param tm The associated term manager. + */ + cvc5_dt_cons_decl_t(Cvc5TermManager* tm, + const cvc5::DatatypeConstructorDecl& decl) + : d_decl(decl), d_tm(tm) + { + } + /** The wrapped C++ datatype constructor declaration. */ + cvc5::DatatypeConstructorDecl d_decl; + /** External refs count. */ + uint32_t d_refs = 1; + /** The associated term manager. */ + Cvc5TermManager* d_tm = nullptr; +}; + +/* -------------------------------------------------------------------------- */ +/* Cvc5TermManager struct */ +/* -------------------------------------------------------------------------- */ + +/** Wrapper for cvc5 C++ term manager. */ +struct Cvc5TermManager +{ + /** + * Export C++ sort to C API. + * @param sort The sort to export. + */ + Cvc5Sort export_sort(const cvc5::Sort& sort); + /** + * Export C++ term to C API. + * @param term The term to export. + */ + Cvc5Term export_term(const cvc5::Term& term); + /** + * Export C++ datatype to C API. + * @param dt The datatype to export. + */ + Cvc5Datatype export_dt(const cvc5::Datatype& dt); + /** + * Export C++ datatype constructor to C API. + * @param cons The datatype constructor to export. + */ + Cvc5DatatypeConstructor export_dt_cons(const cvc5::DatatypeConstructor& cons); + /** + * Export C++ datatype selector to C API. + * @param sel The datatype selector to export. + */ + Cvc5DatatypeSelector export_dt_sel(const cvc5::DatatypeSelector& sel); + /** + * Export C++ datatype declaration to C API. + * @param decl The datatype declaration to export. + */ + Cvc5DatatypeDecl export_dt_decl(const cvc5::DatatypeDecl& decl); + /** + * Export C++ datatype constructor declaration to C API. + * @param decl The datatype constructor declaration to export. + */ + Cvc5DatatypeConstructorDecl export_dt_cons_decl( + const cvc5::DatatypeConstructorDecl& decl); + + /* Manual memory management for sorts and terms. ------ */ + + /** + * Decrement the external ref count of a term. If the ref count reaches zero, + * the term is release (freed). + * @param term The term to release. + */ + void release(cvc5_term_t* term); + /** + * Increment the external ref count of a term. + * @param term The term to copy. + * @return The copied term. + */ + cvc5_term_t* copy(cvc5_term_t* term); + /** + * Decrement the external ref count of a sort. If the ref count reaches zero, + * the sort is release (freed). + * @param sort The sort to release. + */ + void release(cvc5_sort_t* sort); + /** + * Increment the external ref count of a sort. + * @param sort The sort to copy. + * @return The copied sort. + */ + cvc5_sort_t* copy(cvc5_sort_t* sort); + /** + * Decrement the external ref count of a datatype. If the ref count reaches + * zero, the datatype is release (freed). + * @param dt The datatype to release. + */ + void release(cvc5_dt_t* dt); + /** + * Increment the external ref count of a datatype. + * @param dt The datatype to copy. + * @return The copied datatype. + */ + cvc5_dt_t* copy(cvc5_dt_t* dt); + /** + * Decrement the external ref count of a datatype constructor. If the ref + * count reaches zero, the datatype constructor is release (freed). + * @param cons The datatype constructor to release. + */ + void release(cvc5_dt_cons_t* cons); + /** + * Increment the external ref count of a datatype constructor. + * @param cons The datatype constructor to copy. + * @return The copied datatype constructor. + */ + cvc5_dt_cons_t* copy(cvc5_dt_cons_t* cons); + /** + * Decrement the external ref count of a datatype selector. If the ref + * count reaches zero, the datatype selector is release (freed). + * @param cons The datatype selector to release. + */ + void release(cvc5_dt_sel_t* sel); + /** + * Increment the external ref count of a datatype selector. + * @param cons The datatype selector to copy. + * @return The copied datatype selector. + */ + cvc5_dt_sel_t* copy(cvc5_dt_sel_t* sel); + /** + * Decrement the external ref count of a datatype declaration. If the ref + * count reaches zero, the datatype declaration is release (freed). + * @param decl The datatype declaration to release. + */ + void release(cvc5_dt_decl_t* decl); + /** + * Increment the external ref count of a datatype declaration. + * @param decl The datatype declaration to copy. + * @return The copied datatype declaration. + */ + cvc5_dt_decl_t* copy(cvc5_dt_decl_t* decl); + /** + * Decrement the external ref count of a datatype constructor declaration. If + * the ref count reaches zero, the datatype constructor declaration is + * release (freed). + * @param decl The datatype constructor declaration to release. + */ + void release(cvc5_dt_cons_decl_t* decl); + /** + * Increment the external ref count of a datatype constructor declaration. + * @param decl The datatype constructor declaration to copy. + * @return The copied datatype constructor declaration. + */ + cvc5_dt_cons_decl_t* copy(cvc5_dt_cons_decl_t* decl); + + /** Release all managed objects. */ + void release(); + + /* ---------------------------------------------------- */ + + /** The associated term manager instance. */ + cvc5::TermManager d_tm; + + private: + std::unordered_map d_alloc_sorts; + std::unordered_map d_alloc_terms; + std::unordered_map d_alloc_dts; + std::unordered_map + d_alloc_dt_conss; + std::unordered_map d_alloc_dt_sels; + std::unordered_map d_alloc_dt_decls; + std::unordered_map + d_alloc_dt_cons_decls; +}; + +Cvc5Sort Cvc5TermManager::export_sort(const cvc5::Sort& sort) +{ + Assert(!sort.isNull()); + auto [it, inserted] = d_alloc_sorts.try_emplace(sort, this, sort); + if (!inserted) + { + copy(&it->second); + } + return &it->second; +} + +Cvc5Term Cvc5TermManager::export_term(const cvc5::Term& term) +{ + Assert(!term.isNull()); + auto [it, inserted] = d_alloc_terms.try_emplace(term, this, term); + if (!inserted) + { + copy(&it->second); + } + return &it->second; +} + +Cvc5Datatype Cvc5TermManager::export_dt(const cvc5::Datatype& dt) +{ + Assert(!dt.isNull()); + auto [it, inserted] = d_alloc_dts.try_emplace(dt, this, dt); + if (!inserted) + { + copy(&it->second); + } + return &it->second; +} + +Cvc5DatatypeConstructor Cvc5TermManager::export_dt_cons( + const cvc5::DatatypeConstructor& cons) +{ + Assert(!cons.isNull()); + auto [it, inserted] = d_alloc_dt_conss.try_emplace(cons, this, cons); + if (!inserted) + { + copy(&it->second); + } + return &it->second; +} + +Cvc5DatatypeSelector Cvc5TermManager::export_dt_sel( + const cvc5::DatatypeSelector& sel) +{ + Assert(!sel.isNull()); + auto [it, inserted] = d_alloc_dt_sels.try_emplace(sel, this, sel); + if (!inserted) + { + copy(&it->second); + } + return &it->second; +} + +Cvc5DatatypeDecl Cvc5TermManager::export_dt_decl(const cvc5::DatatypeDecl& decl) +{ + Assert(!decl.isNull()); + auto [it, inserted] = d_alloc_dt_decls.try_emplace(decl, this, decl); + if (!inserted) + { + copy(&it->second); + } + return &it->second; +} + +Cvc5DatatypeConstructorDecl Cvc5TermManager::export_dt_cons_decl( + const cvc5::DatatypeConstructorDecl& decl) +{ + Assert(!decl.isNull()); + auto [it, inserted] = d_alloc_dt_cons_decls.try_emplace(decl, this, decl); + if (!inserted) + { + copy(&it->second); + } + return &it->second; +} + +void Cvc5TermManager::release(cvc5_term_t* term) +{ + term->d_refs -= 1; + if (term->d_refs == 0) + { + Assert(d_alloc_terms.find(term->d_term) != d_alloc_terms.end()); + d_alloc_terms.erase(term->d_term); + } +} + +cvc5_term_t* Cvc5TermManager::copy(cvc5_term_t* term) +{ + term->d_refs += 1; + return term; +} + +void Cvc5TermManager::release(cvc5_sort_t* sort) +{ + sort->d_refs -= 1; + if (sort->d_refs == 0) + { + Assert(d_alloc_sorts.find(sort->d_sort) != d_alloc_sorts.end()); + d_alloc_sorts.erase(sort->d_sort); + } +} + +cvc5_sort_t* Cvc5TermManager::copy(cvc5_sort_t* sort) +{ + sort->d_refs += 1; + return sort; +} + +void Cvc5TermManager::release(cvc5_dt_t* dt) +{ + dt->d_refs -= 1; + if (dt->d_refs == 0) + { + Assert(d_alloc_dts.find(dt->d_dt) != d_alloc_dts.end()); + d_alloc_dts.erase(dt->d_dt); + } +} + +cvc5_dt_t* Cvc5TermManager::copy(cvc5_dt_t* dt) +{ + dt->d_refs += 1; + return dt; +} + +void Cvc5TermManager::release(cvc5_dt_cons_t* cons) +{ + cons->d_refs -= 1; + if (cons->d_refs == 0) + { + Assert(d_alloc_dt_conss.find(cons->d_dt_cons) != d_alloc_dt_conss.end()); + d_alloc_dt_conss.erase(cons->d_dt_cons); + } +} + +cvc5_dt_cons_t* Cvc5TermManager::copy(cvc5_dt_cons_t* cons) +{ + cons->d_refs += 1; + return cons; +} + +void Cvc5TermManager::release(cvc5_dt_sel_t* sel) +{ + sel->d_refs -= 1; + if (sel->d_refs == 0) + { + Assert(d_alloc_dt_sels.find(sel->d_dt_sel) != d_alloc_dt_sels.end()); + d_alloc_dt_sels.erase(sel->d_dt_sel); + } +} + +cvc5_dt_sel_t* Cvc5TermManager::copy(cvc5_dt_sel_t* sel) +{ + sel->d_refs += 1; + return sel; +} + +void Cvc5TermManager::release(cvc5_dt_decl_t* decl) +{ + decl->d_refs -= 1; + if (decl->d_refs == 0) + { + Assert(d_alloc_dt_decls.find(decl->d_decl) != d_alloc_dt_decls.end()); + d_alloc_dt_decls.erase(decl->d_decl); + } +} + +cvc5_dt_decl_t* Cvc5TermManager::copy(cvc5_dt_decl_t* decl) +{ + decl->d_refs += 1; + return decl; +} + +void Cvc5TermManager::release(cvc5_dt_cons_decl_t* decl) +{ + decl->d_refs -= 1; + if (decl->d_refs == 0) + { + Assert(d_alloc_dt_cons_decls.find(decl->d_decl) + != d_alloc_dt_cons_decls.end()); + d_alloc_dt_cons_decls.erase(decl->d_decl); + } +} + +cvc5_dt_cons_decl_t* Cvc5TermManager::copy(cvc5_dt_cons_decl_t* decl) +{ + decl->d_refs += 1; + return decl; +} + +void Cvc5TermManager::release() +{ + d_alloc_sorts.clear(); + d_alloc_terms.clear(); + d_alloc_dts.clear(); + d_alloc_dt_conss.clear(); + d_alloc_dt_sels.clear(); + d_alloc_dt_decls.clear(); + d_alloc_dt_cons_decls.clear(); +} + +/* -------------------------------------------------------------------------- */ +/* Cvc5Datatype */ +/* -------------------------------------------------------------------------- */ + +Cvc5Sort cvc5_sort_instantiate(Cvc5Sort sort, + size_t size, + const Cvc5Sort params[]) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_SORT(sort); + CVC5_CAPI_CHECK_NOT_NULL(params); + std::vector cparams; + for (uint32_t i = 0; i < size; ++i) + { + CVC5_CAPI_CHECK_SORT_AT_IDX(params, i); + cparams.push_back(params[i]->d_sort); + } + res = sort->d_tm->export_sort(sort->d_sort.instantiate(cparams)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +/* -------------------------------------------------------------------------- */ +/* Cvc5Datatype */ +/* -------------------------------------------------------------------------- */ + +/* Cvc5DatatypeConstructorDecl ----------------------------------------- */ + +void cvc5_dt_cons_decl_add_selector(Cvc5DatatypeConstructorDecl decl, + const char* name, + Cvc5Sort sort) +{ + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_CONS_DECL(decl); + CVC5_CAPI_CHECK_NOT_NULL(name); + CVC5_CAPI_CHECK_SORT(sort); + decl->d_decl.addSelector(name, sort->d_sort); + CVC5_CAPI_TRY_CATCH_END; +} + +void cvc5_dt_cons_decl_add_selector_self(Cvc5DatatypeConstructorDecl decl, + const char* name) +{ + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_CONS_DECL(decl); + CVC5_CAPI_CHECK_NOT_NULL(name); + decl->d_decl.addSelectorSelf(name); + CVC5_CAPI_TRY_CATCH_END; +} + +void cvc5_dt_cons_decl_add_selector_unresolved(Cvc5DatatypeConstructorDecl decl, + const char* name, + const char* unres_name) +{ + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_CONS_DECL(decl); + CVC5_CAPI_CHECK_NOT_NULL(name); + CVC5_CAPI_CHECK_NOT_NULL(unres_name); + decl->d_decl.addSelectorUnresolved(name, unres_name); + CVC5_CAPI_TRY_CATCH_END; +} + +const char* cvc5_dt_cons_decl_to_string(Cvc5DatatypeConstructorDecl decl) +{ + static thread_local std::string str; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_CONS_DECL(decl); + str = decl->d_decl.toString(); + CVC5_CAPI_TRY_CATCH_END; + return str.c_str(); +} + +/* Cvc5DatatypeDecl ---------------------------------------------------- */ + +void cvc5_dt_decl_add_constructor(Cvc5DatatypeDecl decl, + Cvc5DatatypeConstructorDecl cdecl) +{ + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_DECL(decl); + CVC5_CAPI_CHECK_NOT_NULL(cdecl); + decl->d_decl.addConstructor(cdecl->d_decl); + CVC5_CAPI_TRY_CATCH_END; +} + +size_t cvc5_dt_decl_get_num_constructors(Cvc5DatatypeDecl decl) +{ + size_t res = 0; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_DECL(decl); + res = decl->d_decl.getNumConstructors(); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +bool cvc5_dt_decl_is_parametric(Cvc5DatatypeDecl decl) +{ + size_t res = 0; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_DECL(decl); + res = decl->d_decl.getNumConstructors(); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +bool cvc5_dt_decl_is_resolved(Cvc5DatatypeDecl decl) +{ + bool res = false; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_DECL(decl); + res = decl->d_decl.isResolved(); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +const char* cvc5_dt_decl_to_string(Cvc5DatatypeDecl decl) +{ + static thread_local std::string str; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_DECL(decl); + str = decl->d_decl.toString(); + CVC5_CAPI_TRY_CATCH_END; + return str.c_str(); +} + +const char* cvc5_dt_decl_get_name(Cvc5DatatypeDecl decl) +{ + static thread_local std::string str; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_DECL(decl); + str = decl->d_decl.getName(); + CVC5_CAPI_TRY_CATCH_END; + return str.c_str(); +} + +/* Cvc5DatatypeSelector ------------------------------------------------ */ + +const char* cvc5_dt_del_get_name(Cvc5DatatypeSelector sel) +{ + static thread_local std::string str; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_SEL(sel); + str = sel->d_dt_sel.getName(); + CVC5_CAPI_TRY_CATCH_END; + return str.c_str(); +} + +Cvc5Term cvc5_dt_sel_get_term(Cvc5DatatypeSelector sel) +{ + Cvc5Term res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_SEL(sel); + res = sel->d_tm->export_term(sel->d_dt_sel.getTerm()); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Term cvc5_dt_sel_get_updater_term(Cvc5DatatypeSelector sel) +{ + Cvc5Term res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_SEL(sel); + res = sel->d_tm->export_term(sel->d_dt_sel.getTerm()); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_dt_sel_get_codomain_sort(Cvc5DatatypeSelector sel) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_SEL(sel); + res = sel->d_tm->export_sort(sel->d_dt_sel.getCodomainSort()); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +const char* cvc5_dt_sel_to_string(Cvc5DatatypeSelector sel) +{ + static thread_local std::string str; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_SEL(sel); + str = sel->d_dt_sel.toString(); + CVC5_CAPI_TRY_CATCH_END; + return str.c_str(); +} + +/* Cvc5DatatypeConstructor --------------------------------------------- */ + +const char* cvc5_dt_cons_get_name(Cvc5DatatypeConstructor cons) +{ + static thread_local std::string str; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_CONS(cons); + str = cons->d_dt_cons.getName(); + CVC5_CAPI_TRY_CATCH_END; + return str.c_str(); +} + +Cvc5Term cvc5_dt_cons_get_term(Cvc5DatatypeConstructor cons) +{ + Cvc5Term res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_CONS(cons); + res = cons->d_tm->export_term(cons->d_dt_cons.getTerm()); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Term cvc5_dt_cons_get_instantiated_term(Cvc5DatatypeConstructor cons, + Cvc5Sort sort) +{ + Cvc5Term res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_CONS(cons); + CVC5_CAPI_CHECK_SORT(sort); + res = cons->d_tm->export_term( + cons->d_dt_cons.getInstantiatedTerm(sort->d_sort)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Term cvc5_dt_cons_get_tester_term(Cvc5DatatypeConstructor cons) +{ + Cvc5Term res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_CONS(cons); + res = cons->d_tm->export_term(cons->d_dt_cons.getTesterTerm()); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +size_t cvc5_dt_cons_get_num_selectors(Cvc5DatatypeConstructor cons) +{ + size_t res = 0; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_CONS(cons); + res = cons->d_dt_cons.getNumSelectors(); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5DatatypeSelector cvc5_dt_cons_get_selector(Cvc5DatatypeConstructor cons, + size_t index) +{ + Cvc5DatatypeSelector res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_CONS(cons); + res = cons->d_tm->export_dt_sel(cons->d_dt_cons[index]); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5DatatypeSelector cvc5_dt_cons_get_selector_by_name( + Cvc5DatatypeConstructor cons, const char* name) +{ + Cvc5DatatypeSelector res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_CONS(cons); + CVC5_CAPI_CHECK_NOT_NULL(name); + res = cons->d_tm->export_dt_sel(cons->d_dt_cons.getSelector(name)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +const char* cvc5_dt_cons_to_string(Cvc5DatatypeConstructor cons) +{ + static thread_local std::string str; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT_CONS(cons); + str = cons->d_dt_cons.toString(); + CVC5_CAPI_TRY_CATCH_END; + return str.c_str(); +} + +/* Cvc5Datatype -------------------------------------------------------- */ + +Cvc5DatatypeConstructor cvc5_dt_get_constructor(Cvc5Datatype dt, size_t idx) +{ + Cvc5DatatypeConstructor res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT(dt); + res = dt->d_tm->export_dt_cons(dt->d_dt[idx]); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5DatatypeConstructor cvc5_dt_get_constructor_by_name(Cvc5Datatype dt, + const char* name) +{ + Cvc5DatatypeConstructor res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT(dt); + CVC5_CAPI_CHECK_NOT_NULL(name); + res = dt->d_tm->export_dt_cons(dt->d_dt.getConstructor(name)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5DatatypeSelector cvc5_dt_get_selector(Cvc5Datatype dt, const char* name) +{ + Cvc5DatatypeSelector res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT(dt); + CVC5_CAPI_CHECK_NOT_NULL(name); + res = dt->d_tm->export_dt_sel(dt->d_dt.getSelector(name)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +const char* cvc5_dt_get_name(Cvc5Datatype dt) +{ + static thread_local std::string str; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT(dt); + str = dt->d_dt.getName(); + CVC5_CAPI_TRY_CATCH_END; + return str.c_str(); +} + +size_t cvc5_dt_get_num_constructors(Cvc5Datatype dt) +{ + size_t res = 0; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT(dt); + res = dt->d_dt.getNumConstructors(); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +const Cvc5Sort* cvc5_dt_get_parameters(Cvc5Datatype dt, size_t* size) +{ + static thread_local std::vector res; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT(dt); + CVC5_CAPI_CHECK_NOT_NULL(size); + auto sorts = dt->d_dt.getParameters(); + auto tm = dt->d_tm; + for (auto& s : sorts) + { + res.push_back(tm->export_sort(s)); + } + *size = res.size(); + CVC5_CAPI_TRY_CATCH_END; + return *size > 0 ? res.data() : nullptr; +} + +bool cvc5_dt_is_parametric(Cvc5Datatype dt) +{ + bool res = false; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT(dt); + res = dt->d_dt.isParametric(); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +bool cvc5_dt_is_codatatype(Cvc5Datatype dt) +{ + bool res = false; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT(dt); + res = dt->d_dt.isCodatatype(); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +bool cvc5_dt_is_tuple(Cvc5Datatype dt) +{ + bool res = false; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT(dt); + res = dt->d_dt.isTuple(); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +bool cvc5_dt_is_record(Cvc5Datatype dt) +{ + bool res = false; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT(dt); + res = dt->d_dt.isRecord(); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +bool cvc5_dt_is_finite(Cvc5Datatype dt) +{ + bool res = false; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT(dt); + res = dt->d_dt.isFinite(); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +bool cvc5_dt_is_well_founded(Cvc5Datatype dt) +{ + bool res = false; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT(dt); + res = dt->d_dt.isWellFounded(); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +const char* cvc5_dt_to_string(Cvc5Datatype dt) +{ + static thread_local std::string str; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_DT(dt); + str = dt->d_dt.toString(); + CVC5_CAPI_TRY_CATCH_END; + return str.c_str(); +} + +/* -------------------------------------------------------------------------- */ +/* Cvc5TermManager */ +/* -------------------------------------------------------------------------- */ + +Cvc5TermManager* cvc5_term_manager_new() +{ + Cvc5TermManager* res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + res = new Cvc5TermManager(); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +void cvc5_term_manager_delete(Cvc5TermManager* tm) +{ + CVC5_CAPI_TRY_CATCH_BEGIN; + delete tm; + CVC5_CAPI_TRY_CATCH_END; +} + +void cvc5_term_manager_release(Cvc5TermManager* tm) +{ + CVC5_CAPI_TRY_CATCH_BEGIN; + tm->release(); + CVC5_CAPI_TRY_CATCH_END; +} + +/* Sorts Handling ----------------------------------------------------------- */ + +Cvc5Sort cvc5_get_boolean_sort(Cvc5TermManager* tm) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + res = tm->export_sort(tm->d_tm.getBooleanSort()); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_get_integer_sort(Cvc5TermManager* tm) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + res = tm->export_sort(tm->d_tm.getIntegerSort()); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_get_real_sort(Cvc5TermManager* tm) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + res = tm->export_sort(tm->d_tm.getRealSort()); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_get_regexp_sort(Cvc5TermManager* tm) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + res = tm->export_sort(tm->d_tm.getRegExpSort()); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_get_rm_sort(Cvc5TermManager* tm) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + res = tm->export_sort(tm->d_tm.getRoundingModeSort()); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_get_string_sort(Cvc5TermManager* tm) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + res = tm->export_sort(tm->d_tm.getStringSort()); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_mk_array_sort(Cvc5TermManager* tm, Cvc5Sort index, Cvc5Sort elem) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + CVC5_CAPI_CHECK_SORT(index); + CVC5_CAPI_CHECK_SORT(elem); + res = tm->export_sort(tm->d_tm.mkArraySort(index->d_sort, elem->d_sort)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_mk_bv_sort(Cvc5TermManager* tm, uint32_t size) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + res = tm->export_sort(tm->d_tm.mkBitVectorSort(size)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_mk_fp_sort(Cvc5TermManager* tm, uint32_t exp, uint32_t sig) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + res = tm->export_sort(tm->d_tm.mkFloatingPointSort(exp, sig)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_mk_ff_sort(Cvc5TermManager* tm, const char* size, uint32_t base) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + CVC5_CAPI_CHECK_NOT_NULL(size); + res = tm->export_sort(tm->d_tm.mkFiniteFieldSort(size, base)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_mk_dt_sort(Cvc5TermManager* tm, Cvc5DatatypeDecl decl) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + res = tm->export_sort(tm->d_tm.mkDatatypeSort(decl->d_decl)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +const Cvc5Sort* cvc5_mk_dt_sorts(Cvc5TermManager* tm, + size_t size, + const Cvc5DatatypeDecl decls[]) +{ + static thread_local std::vector res; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + CVC5_CAPI_CHECK_NOT_NULL(decls); + std::vector cdecls; + for (size_t i = 0; i < size; ++i) + { + CVC5_CAPI_CHECK_DT_DECL_AT_IDX(decls, i); + cdecls.push_back(decls[i]->d_decl); + } + auto sorts = tm->d_tm.mkDatatypeSorts(cdecls); + for (auto& s : sorts) + { + res.push_back(tm->export_sort(s)); + } + CVC5_CAPI_TRY_CATCH_END; + return res.data(); +} + +Cvc5Sort cvc5_mk_fun_sort(Cvc5TermManager* tm, + size_t size, + const Cvc5Sort sorts[], + Cvc5Sort codomain) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + CVC5_CAPI_CHECK_NOT_NULL(sorts); + std::vector csorts; + for (size_t i = 0; i < size; ++i) + { + CVC5_CAPI_CHECK_SORT_AT_IDX(sorts, i); + csorts.push_back(sorts[i]->d_sort); + } + res = tm->export_sort(tm->d_tm.mkFunctionSort(csorts, codomain->d_sort)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_mk_param_sort(Cvc5TermManager* tm, const char* symbol) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + CVC5_CAPI_CHECK_NOT_NULL(symbol); + res = tm->export_sort(tm->d_tm.mkParamSort(symbol)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_mk_predicate_sort(Cvc5TermManager* tm, + size_t size, + const Cvc5Sort sorts[]) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + CVC5_CAPI_CHECK_NOT_NULL(sorts); + std::vector csorts; + for (size_t i = 0; i < size; ++i) + { + CVC5_CAPI_CHECK_SORT_AT_IDX(sorts, i); + csorts.push_back(sorts[i]->d_sort); + } + res = tm->export_sort(tm->d_tm.mkPredicateSort(csorts)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_mk_record_sort(Cvc5TermManager* tm, + size_t size, + const char* names[], + const Cvc5Sort sorts[]) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + if (names != NULL) + { + CVC5_CAPI_CHECK_NOT_NULL(sorts); + std::vector> cfields; + for (size_t i = 0; i < size; ++i) + { + CVC5_CAPI_CHECK_NOT_NULL_AT_IDX(names, i); + CVC5_CAPI_CHECK_SORT_AT_IDX(sorts, i); + cfields.emplace_back(names[i], sorts[i]->d_sort); + } + res = tm->export_sort(tm->d_tm.mkRecordSort(cfields)); + } + else + { + res = tm->export_sort(tm->d_tm.mkRecordSort({})); + } + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_mk_set_sort(Cvc5TermManager* tm, Cvc5Sort sort) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + CVC5_CAPI_CHECK_SORT(sort); + res = tm->export_sort(tm->d_tm.mkSetSort(sort->d_sort)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_mk_bag_sort(Cvc5TermManager* tm, Cvc5Sort sort) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + CVC5_CAPI_CHECK_SORT(sort); + res = tm->export_sort(tm->d_tm.mkBagSort(sort->d_sort)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_mk_sequence_sort(Cvc5TermManager* tm, Cvc5Sort sort) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + CVC5_CAPI_CHECK_SORT(sort); + res = tm->export_sort(tm->d_tm.mkSequenceSort(sort->d_sort)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_mk_abstract_sort(Cvc5TermManager* tm, Cvc5SortKind k) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + res = + tm->export_sort(tm->d_tm.mkAbstractSort(static_cast(k))); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_mk_uninterpreted_sort(Cvc5TermManager* tm, const char* symbol) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + CVC5_CAPI_CHECK_NOT_NULL(symbol); + if (symbol) + { + res = tm->export_sort(tm->d_tm.mkUninterpretedSort(symbol)); + } + else + { + res = tm->export_sort(tm->d_tm.mkUninterpretedSort()); + } + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_mk_unresolved_dt_sort(Cvc5TermManager* tm, + const char* symbol, + size_t arity) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + CVC5_CAPI_CHECK_NOT_NULL(symbol); + res = tm->export_sort(tm->d_tm.mkUnresolvedDatatypeSort(symbol, arity)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_mk_uninterpreted_sort_constructor_sort(Cvc5TermManager* tm, + size_t arity, + const char* symbol) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + if (symbol) + { + res = tm->export_sort( + tm->d_tm.mkUninterpretedSortConstructorSort(arity, symbol)); + } + else + { + res = tm->export_sort(tm->d_tm.mkUninterpretedSortConstructorSort(arity)); + } + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_mk_tuple_sort(Cvc5TermManager* tm, + size_t size, + const Cvc5Sort sorts[]) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + CVC5_CAPI_CHECK_NOT_NULL(sorts); + std::vector csorts; + for (size_t i = 0; i < size; ++i) + { + CVC5_CAPI_CHECK_SORT_AT_IDX(sorts, i); + csorts.push_back(sorts[i]->d_sort); + } + res = tm->export_sort(tm->d_tm.mkTupleSort(csorts)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5Sort cvc5_mk_nullable_sort(Cvc5TermManager* tm, Cvc5Sort sort) +{ + Cvc5Sort res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + CVC5_CAPI_CHECK_SORT(sort); + res = tm->export_sort(tm->d_tm.mkNullableSort(sort->d_sort)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +/* Datatype constructor declaration ------------------------------------ */ + +Cvc5DatatypeConstructorDecl cvc5_mk_dt_cons_decl(Cvc5TermManager* tm, + const char* name) +{ + Cvc5DatatypeConstructorDecl res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + CVC5_CAPI_CHECK_NOT_NULL(name); + res = tm->export_dt_cons_decl(tm->d_tm.mkDatatypeConstructorDecl(name)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +/* Datatype declaration ------------------------------------------------ */ + +Cvc5DatatypeDecl cvc5_mk_dt_decl(Cvc5TermManager* tm, + const char* name, + bool is_codt) +{ + Cvc5DatatypeDecl res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + CVC5_CAPI_CHECK_NOT_NULL(name); + res = tm->export_dt_decl(tm->d_tm.mkDatatypeDecl(name, is_codt)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} + +Cvc5DatatypeDecl cvc5_mk_dt_decl_with_params(Cvc5TermManager* tm, + const char* name, + size_t size, + const Cvc5Sort* params, + bool is_codt) +{ + Cvc5DatatypeDecl res = nullptr; + CVC5_CAPI_TRY_CATCH_BEGIN; + CVC5_CAPI_CHECK_NOT_NULL(tm); + CVC5_CAPI_CHECK_NOT_NULL(name); + CVC5_CAPI_CHECK_NOT_NULL(params); + std::vector cparams; + for (size_t i = 0; i < size; ++i) + { + CVC5_CAPI_CHECK_SORT_AT_IDX(params, i); + cparams.push_back(params[i]->d_sort); + } + res = tm->export_dt_decl(tm->d_tm.mkDatatypeDecl(name, cparams, is_codt)); + CVC5_CAPI_TRY_CATCH_END; + return res; +} diff --git a/src/api/c/cvc5_checks.h b/src/api/c/cvc5_checks.h index cd8f3f148b1..938e2831d69 100644 --- a/src/api/c/cvc5_checks.h +++ b/src/api/c/cvc5_checks.h @@ -69,48 +69,85 @@ class Cvc5CApiAbortStream #endif +#define CVC5_CAPI_CHECK_NOT_NULL(arg) \ + CVC5_API_CHECK(arg != nullptr) << "Invalid call to '" << __PRETTY_FUNCTION__ \ + << "', unexpected NULL argument" + +#define CVC5_CAPI_CHECK_NOT_NULL_AT_IDX(arg, i) \ + CVC5_API_CHECK(arg[i] != nullptr) \ + << "Invalid call to '" << __PRETTY_FUNCTION__ \ + << "', unexpected NULL argument at index " << i + /* -------------------------------------------------------------------------- */ #define CVC5_CAPI_CHECK_KIND(kind) \ CVC5_API_CHECK((kind) >= CVC5_KIND_INTERNAL_KIND \ && (kind) < CVC5_KIND_LAST_KIND) \ - << "invalid term kind"; + << "invalid term kind" #define CVC5_CAPI_CHECK_SORT_KIND(kind) \ CVC5_API_CHECK((kind) >= CVC5_SORT_KIND_INTERNAL_SORT_KIND \ && (kind) < CVC5_SORT_KIND_LAST_SORT_KIND) \ - << "invalid sort kind"; + << "invalid sort kind" #define CVC5_CAPI_CHECK_RM(rm) \ - CVC5_API_CHECK((rm) >= 0 && (rm) < CVC5_RM_LAST) << "invalid rounding mode"; + CVC5_API_CHECK((rm) >= 0 && (rm) < CVC5_RM_LAST) << "invalid rounding mode" #define CVC5_CAPI_CHECK_UNKNOWN_EXPLANATION(e) \ CVC5_API_CHECK((e) >= 0 && (e) < CVC5_UNKNOWN_EXPLANATION_LAST) \ - << "invalid unknown explanation kind"; + << "invalid unknown explanation kind" #define CVC5_CAPI_CHECK_BLOCK_MODELS_MODE(mode) \ CVC5_API_CHECK((mode) >= 0 && (mode) < CVC5_BLOCK_MODELS_MODE_LAST) \ - << "invalid block models mode"; + << "invalid block models mode" #define CVC5_CAPI_CHECK_FIND_SYNTH_TARGET(target) \ CVC5_API_CHECK((target) >= 0 && (target) < CVC5_FIND_SYNTH_TARGET_LAST) \ - << "invalid find synthesis target"; + << "invalid find synthesis target" #define CVC5_CAPI_CHECK_INPUT_LANGUAGE(lang) \ CVC5_API_CHECK((lang) >= 0 && (lang) < CVC5_INPUT_LANGUAGE_LAST) \ - << "invalid input language"; + << "invalid input language" #define CVC5_CAPI_CHECK_LEARNED_LIT_TYPE(type) \ CVC5_API_CHECK((type) >= 0 && (type) < CVC5_LEARNED_LIT_TYPE_LAST) \ - << "invalid learned literal type"; + << "invalid learned literal type" #define CVC5_CAPI_CHECK_PROOF_COMPONENT(pc) \ CVC5_API_CHECK((pc) >= 0 && (pc) < CVC5_PROOF_COMPONENT_LAST) \ - << "invalid proof component kind"; + << "invalid proof component kind" #define CVC5_CAPI_CHECK_PROOF_FORMAT(format) \ CVC5_API_CHECK((format) >= 0 && (format) < CVC5_PROOF_FORMAT_LAST) \ - << "invalid proof format"; + << "invalid proof format" + +/* -------------------------------------------------------------------------- */ + +#define CVC5_CAPI_CHECK_SORT(sort) \ + CVC5_API_CHECK(sort != nullptr) << "invalid sort" + +#define CVC5_CAPI_CHECK_SORT_AT_IDX(sorts, i) \ + CVC5_API_CHECK(sorts[i] != nullptr) << "invalid sort at index " << i + +#define CVC5_CAPI_CHECK_DT_DECL(decl) \ + CVC5_API_CHECK(decl != nullptr) << "invalid datatype declaration" + +#define CVC5_CAPI_CHECK_DT_DECL_AT_IDX(decls, i) \ + CVC5_API_CHECK(decls[i] != nullptr) \ + << "invalid datatype declaration at index " << i + +#define CVC5_CAPI_CHECK_DT_CONS_DECL(decl) \ + CVC5_API_CHECK(decl != nullptr) << "invalid datatype constructor " \ + "declaration" + +#define CVC5_CAPI_CHECK_DT_SEL(sel) \ + CVC5_API_CHECK(sel != nullptr) << "invalid datatype selector" + +#define CVC5_CAPI_CHECK_DT_CONS(cons) \ + CVC5_API_CHECK(cons != nullptr) << "invalid datatype constructor" + +#define CVC5_CAPI_CHECK_DT(dt) \ + CVC5_API_CHECK(dt != nullptr) << "invalid datatype" /* -------------------------------------------------------------------------- */ } diff --git a/src/api/cpp/cvc5.cpp b/src/api/cpp/cvc5.cpp index 9e69a96d754..c755af5de96 100644 --- a/src/api/cpp/cvc5.cpp +++ b/src/api/cpp/cvc5.cpp @@ -7094,12 +7094,12 @@ Op Solver::mkOp(Kind kind, const std::string& arg) const /* Non-SMT-LIB commands */ /* -------------------------------------------------------------------------- */ -Term Solver::simplify(const Term& term) +Term Solver::simplify(const Term& term, bool applySubs) { CVC5_API_TRY_CATCH_BEGIN; CVC5_API_SOLVER_CHECK_TERM(term); //////// all checks before this line - Term res = Term(&d_tm, d_slv->simplify(*term.d_node)); + Term res = Term(&d_tm, d_slv->simplify(*term.d_node, applySubs)); Assert(*res.getSort().d_type == *term.getSort().d_type); return res; //////// diff --git a/src/api/cpp/cvc5_proof_rule_template.cpp b/src/api/cpp/cvc5_proof_rule_template.cpp index e20ea7b8edf..faf5604c8cf 100644 --- a/src/api/cpp/cvc5_proof_rule_template.cpp +++ b/src/api/cpp/cvc5_proof_rule_template.cpp @@ -122,7 +122,6 @@ const char* toString(ProofRule rule) case ProofRule::BV_EAGER_ATOM: return "BV_EAGER_ATOM"; //================================================= Datatype rules case ProofRule::DT_UNIF: return "DT_UNIF"; - case ProofRule::DT_INST: return "DT_INST"; case ProofRule::DT_SPLIT: return "DT_SPLIT"; case ProofRule::DT_CLASH: return "DT_CLASH"; //================================================= Quantifiers rules @@ -222,15 +221,26 @@ const char* toString(cvc5::ProofRewriteRule rule) case ProofRewriteRule::NONE: return "NONE"; //================================================= ad-hoc rules case ProofRewriteRule::DISTINCT_ELIM: return "distinct-elim"; + case ProofRewriteRule::MACRO_BOOL_NNF_NORM: return "macro-bool-nnf-norm"; case ProofRewriteRule::BETA_REDUCE: return "beta-reduce"; case ProofRewriteRule::ARRAYS_EQ_RANGE_EXPAND: return "arrays-eq-range-expand"; case ProofRewriteRule::EXISTS_ELIM: return "exists-elim"; + case ProofRewriteRule::QUANT_UNUSED_VARS: return "quant-unused-vars"; + case ProofRewriteRule::QUANT_MERGE_PRENEX: return "quant-merge-prenex"; + case ProofRewriteRule::QUANT_MINISCOPE: return "quant-miniscope"; + case ProofRewriteRule::MACRO_QUANT_PARTITION_CONNECTED_FV: + return "macro-quant-partition-connected-fv"; + case ProofRewriteRule::DT_INST: return "dt-inst"; case ProofRewriteRule::DT_COLLAPSE_SELECTOR: return "dt-collapse-selector"; case ProofRewriteRule::DT_COLLAPSE_TESTER: return "dt-collapse-tester"; + case ProofRewriteRule::DT_COLLAPSE_TESTER_SINGLETON: + return "dt-collapse-tester-singleton"; case ProofRewriteRule::DT_CONS_EQ: return "dt-cons-eq"; case ProofRewriteRule::RE_LOOP_ELIM: return "re-loop-elim"; + case ProofRewriteRule::SETS_IS_EMPTY_EVAL: + return "sets-is-empty-eval"; //================================================= RARE rules // clang-format off ${printer}$ diff --git a/src/api/java/CMakeLists.txt b/src/api/java/CMakeLists.txt index 8e66eefeec1..6b181869c07 100644 --- a/src/api/java/CMakeLists.txt +++ b/src/api/java/CMakeLists.txt @@ -227,23 +227,39 @@ target_include_directories(cvc5jni PUBLIC ${CMAKE_BINARY_DIR}/src/) target_include_directories(cvc5jni PUBLIC ${JNI_DIR}) target_link_libraries(cvc5jni PRIVATE cvc5 cvc5parser) +# Java on Windows expects the JNI libraries without the prefix +if (WIN32) + set_target_properties(cvc5jni PROPERTIES PREFIX "") + set_target_properties(cvc5jni PROPERTIES IMPORT_PREFIX "") +endif() + set(CVC5_JAR "cvc5-${CVC5_VERSION}.jar") -# create cvc5.jar file -add_jar(cvc5jar - SOURCES - ${JAVA_FILES} - VERSION ${CVC5_VERSION} - OUTPUT_NAME cvc5 -) +# Create cvc5.jar file +if (WIN32) + # CMake creates a symbolic link pointing to the jar with + # the version info when the VERSION attribute is provided. + # This fails on Windows so we don't provide it. + add_jar(cvc5jar + SOURCES + ${JAVA_FILES} + OUTPUT_NAME cvc5 + ) +else() + add_jar(cvc5jar + SOURCES + ${JAVA_FILES} + VERSION ${CVC5_VERSION} + OUTPUT_NAME cvc5 + ) +endif() add_dependencies(cvc5jar generate-java-kinds cvc5jni cvc5 cvc5parser) -# install in the same directory of cvc5-targets -install(TARGETS cvc5jni - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} -) +# Install in the same directory of cvc5-targets. +# On Windows, CMake's default install action places +# DLLs into the runtime path (by default "bin") +install(TARGETS cvc5jni) install_jar(cvc5jar DESTINATION share/java) diff --git a/src/api/java/io/github/cvc5/Solver.java b/src/api/java/io/github/cvc5/Solver.java index 0489803092b..419fa9c31d5 100644 --- a/src/api/java/io/github/cvc5/Solver.java +++ b/src/api/java/io/github/cvc5/Solver.java @@ -1975,16 +1975,12 @@ public DatatypeDecl mkDatatypeDecl(String name, Sort[] params, boolean isCoDatat /* .................................................................... */ /** - * Simplify a formula without doing "much" work. + * Simplify a term or formula based on rewriting. * - * Does not involve the SAT Engine in the simplification, but uses the - * current definitions, assertions, and the current partial model, if one has - * been constructed. It also involves theory normalization. + * @api.note This function is experimental and may change in future versions. * - * @api.note This method is experimental and may change in future versions. - * - * @param t The formula to simplify. - * @return The simplified formula. + * @param t The term to simplify. + * @return The simplified term. */ public Term simplify(Term t) { @@ -1994,6 +1990,26 @@ public Term simplify(Term t) private native long simplify(long pointer, long termPointer); + /** + * Simplify a term or formula based on rewriting and (optionally) applying + * substitutions for solved variables. + * + * If applySubs is true, then for example, if `(= x 0)` was asserted to this + * solver, this method may replace occurrences of `x` with `0`. + * + * @api.note This function is experimental and may change in future versions. + * + * @param t The term to simplify. + * @param applySubs Whether to apply substitutions for solved variables. + * @return The simplified term. + */ + public Term simplify(Term t, boolean applySubs) + { + long termPointer = simplify(pointer, t.getPointer(), applySubs); + return new Term(termPointer); + } + + private native long simplify(long pointer, long termPointer, boolean applySubs); /** * Assert a formula. * SMT-LIB: diff --git a/src/api/java/jni/solver.cpp b/src/api/java/jni/solver.cpp index dfacfcee100..d13c36fb882 100644 --- a/src/api/java/jni/solver.cpp +++ b/src/api/java/jni/solver.cpp @@ -76,10 +76,8 @@ JNIEXPORT jlong JNICALL Java_io_github_cvc5_Solver_getTermManager(JNIEnv* env, * Method: simplify * Signature: (JJ)J */ -JNIEXPORT jlong JNICALL Java_io_github_cvc5_Solver_simplify(JNIEnv* env, - jobject, - jlong pointer, - jlong termPointer) +JNIEXPORT jlong JNICALL Java_io_github_cvc5_Solver_simplify__JJ( + JNIEnv* env, jobject, jlong pointer, jlong termPointer) { CVC5_JAVA_API_TRY_CATCH_BEGIN; Solver* solver = reinterpret_cast(pointer); @@ -89,6 +87,22 @@ JNIEXPORT jlong JNICALL Java_io_github_cvc5_Solver_simplify(JNIEnv* env, CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, 0); } +/* + * Class: io_github_cvc5_Solver + * Method: simplify + * Signature: (JJZ)J + */ +JNIEXPORT jlong JNICALL Java_io_github_cvc5_Solver_simplify__JJZ( + JNIEnv* env, jobject, jlong pointer, jlong termPointer, jboolean applySubs) +{ + CVC5_JAVA_API_TRY_CATCH_BEGIN; + Solver* solver = reinterpret_cast(pointer); + Term* term = reinterpret_cast(termPointer); + Term* retPointer = new Term(solver->simplify(*term, (bool)applySubs)); + return reinterpret_cast(retPointer); + CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, 0); +} + /* * Class: io_github_cvc5_Solver * Method: assertFormula diff --git a/src/api/python/cvc5.pxd b/src/api/python/cvc5.pxd index 0d35249a00c..d43f1cb9e1c 100644 --- a/src/api/python/cvc5.pxd +++ b/src/api/python/cvc5.pxd @@ -412,7 +412,7 @@ cdef extern from "" namespace "cvc5": DatatypeDecl mkDatatypeDecl(const string& name, vector[Sort]& params, bint isCoDatatype) except + # default value for symbol defined in cpp/cvc5.h Term mkVar(Sort sort) except + - Term simplify(const Term& t) except + + Term simplify(const Term& t, bint applySubs) except + void assertFormula(Term term) except + Result checkSat() except + Result checkSatAssuming(const vector[Term]& assumptions) except + diff --git a/src/api/python/cvc5.pxi b/src/api/python/cvc5.pxi index a12468eb377..34e7ea6046d 100644 --- a/src/api/python/cvc5.pxi +++ b/src/api/python/cvc5.pxi @@ -2929,20 +2929,23 @@ cdef class Solver: """ return self.tm.mkDatatypeDecl(name, sorts_or_bool, isCoDatatype) - def simplify(self, Term t): + def simplify(self, Term t, applySubs=False): """ - Simplify a formula without doing "much" work. Does not involve the - SAT Engine in the simplification, but uses the current definitions, - assertions, and the current partial model, if one has been - constructed. It also involves theory normalization. + Simplify a term or formula based on rewriting and (optionally) + applying substitutions for solved variables. + + If applySubs is true, then for example, if `(= x 0)` was asserted to + this solver, this method may replace occurrences of `x` with `0`. .. warning:: This function is experimental and may change in future versions. - :param t: The formula to simplify. - :return: The simplified formula. + :param t: The term to simplify. + :param applySubs: Whether to apply substitutions for solved + variables. + :return: The simplified term. """ - return _term(self.tm, self.csolver.simplify(t.cterm)) + return _term(self.tm, self.csolver.simplify(t.cterm, applySubs)) def assertFormula(self, Term term): """ diff --git a/src/expr/node_algorithm.cpp b/src/expr/node_algorithm.cpp index 58b8f53f975..c087f6e4831 100644 --- a/src/expr/node_algorithm.cpp +++ b/src/expr/node_algorithm.cpp @@ -482,14 +482,14 @@ bool hasFreeVariablesScope(TNode n, std::unordered_set& scope) return getVariablesInternal(n, fvs, scope, false); } -bool getVariables(TNode n, std::unordered_set& vs) +bool getVariables(TNode n, std::unordered_set& vs) { std::unordered_set visited; return getVariables(n, vs, visited); } bool getVariables(TNode n, - std::unordered_set& vs, + std::unordered_set& vs, std::unordered_set& visited) { std::vector visit; @@ -508,6 +508,10 @@ bool getVariables(TNode n, } else { + if (cur.hasOperator()) + { + visit.push_back(cur.getOperator()); + } visit.insert(visit.end(), cur.begin(), cur.end()); } visited.insert(cur); diff --git a/src/expr/node_algorithm.h b/src/expr/node_algorithm.h index caab553fa8f..6a9050d945c 100644 --- a/src/expr/node_algorithm.h +++ b/src/expr/node_algorithm.h @@ -145,7 +145,7 @@ bool hasFreeVariablesScope(TNode n, std::unordered_set& scope); * @param vs The set which free variables are added to * @return true iff this node contains a free variable. */ -bool getVariables(TNode n, std::unordered_set& vs); +bool getVariables(TNode n, std::unordered_set& vs); /** * Get all variables in n. * @param n The node under investigation @@ -154,7 +154,7 @@ bool getVariables(TNode n, std::unordered_set& vs); * @return true iff this node contains a free variable. */ bool getVariables(TNode n, - std::unordered_set& vs, + std::unordered_set& vs, std::unordered_set& visited); /** diff --git a/src/options/base_options.toml b/src/options/base_options.toml index 2941b398e1b..70821815f32 100644 --- a/src/options/base_options.toml +++ b/src/options/base_options.toml @@ -265,6 +265,10 @@ name = "Base" help = "prints the options set during automatic configuration." description = "With ``-o options-auto``, cvc5 prints the options set during automatic configuration." example-file = "regress0/printer/print_options_auto.smt2" +[[option.mode.RARE_DB]] + name = "rare-db" + help = "upon initialization, print the entire set of RARE rewrite rules as they are defined in the proof signature." + description = "With ``-o rare-db``, upon initialization, cvc5 prints the entire set of RARE rewrite rules as they are defined in the proof signature." # Stores then enabled output tags. [[option]] diff --git a/src/options/ff_options.toml b/src/options/ff_options.toml index 4bee89b470b..f420def9e58 100644 --- a/src/options/ff_options.toml +++ b/src/options/ff_options.toml @@ -18,12 +18,12 @@ name = "Finite Field Theory" help = "include field polynomials in Groebner basis computation; don't do this" [[option]] - name = "ffDisjunctiveBit" + name = "ffElimDisjunctiveBit" category = "expert" - long = "ff-disjunctive-bit" + long = "ff-elim-disjunctive-bit" type = "bool" - default = "false" - help = "leave disjunctive bit constraints (or (= x 1) (= x 0)) alone; otherwise, preprocess to (= (* x x) x)" + default = "true" + help = "rewrite disjunctive bit constraints (or (= x 1) (= x 0)) as (= (* x x) x)" [[option]] name = "ffBitsum" diff --git a/src/options/proof_options.toml b/src/options/proof_options.toml index 6ad715ed7d9..5bdeb8b9ac5 100644 --- a/src/options/proof_options.toml +++ b/src/options/proof_options.toml @@ -108,13 +108,16 @@ name = "Proof" help = "Allow macros. Do not improve the granularity of proofs." [[option.mode.REWRITE]] name = "rewrite" - help = "Allow rewrite or substitution steps, expand macros." + help = "Allow trusted rewrite or substitution steps, expand macros." [[option.mode.THEORY_REWRITE]] name = "theory-rewrite" - help = "Allow theory rewrite steps, expand macros, rewrite and substitution steps." + help = "Allow trusted theory rewrite steps, expand macros, rewrite and substitution steps." [[option.mode.DSL_REWRITE]] name = "dsl-rewrite" - help = "Allow DSL rewrites and evaluation steps, expand macros, rewrite, substitution, and theory rewrite steps." + help = "Allow theory and DSL rewrite steps, expand macros, rewrite, substitution, and theory rewrite steps." +[[option.mode.DSL_REWRITE_STRICT]] + name = "dsl-rewrite-strict" + help = "Allow theory and DSL rewrite steps giving preference to DSL rewrite steps." [[option]] name = "proofRewriteRconsRecLimit" @@ -129,7 +132,7 @@ name = "Proof" category = "regular" long = "proof-rewrite-rcons-step-limit=N" type = "uint64_t" - default = "200" + default = "1000" help = "the limit of steps considered for reconstructing proofs of theory rewrites" [[option]] diff --git a/src/options/quantifiers_options.toml b/src/options/quantifiers_options.toml index 73659563c84..237e48b7e3e 100644 --- a/src/options/quantifiers_options.toml +++ b/src/options/quantifiers_options.toml @@ -200,6 +200,14 @@ name = "Quantifiers" default = "false" help = "use model-based quantifier instantiation" +[[option]] + name = "mbqiFastSygus" + category = "expert" + long = "mbqi-fast-sygus" + type = "bool" + default = "false" + help = "use fast enumeration to augment instantiations from MBQI" + #### E-matching options [[option]] diff --git a/src/preprocessing/passes/ackermann.cpp b/src/preprocessing/passes/ackermann.cpp index 073238b7c6c..85a473eb71e 100644 --- a/src/preprocessing/passes/ackermann.cpp +++ b/src/preprocessing/passes/ackermann.cpp @@ -232,10 +232,10 @@ std::unordered_set getVarsWithUSorts(AssertionPipeline* assertions) for (const Node& assertion : assertions->ref()) { - std::unordered_set vars; + std::unordered_set vars; expr::getVariables(assertion, vars); - for (const TNode& var : vars) + for (const Node& var : vars) { if (var.getType().isUninterpretedSort()) { diff --git a/src/preprocessing/passes/real_to_int.cpp b/src/preprocessing/passes/real_to_int.cpp index f1a94c87208..e438ce5f5fc 100644 --- a/src/preprocessing/passes/real_to_int.cpp +++ b/src/preprocessing/passes/real_to_int.cpp @@ -148,9 +148,14 @@ Node RealToInt::realToIntInternal(TNode n, NodeMap& cache, std::vector& va bool childChanged = false; std::vector children; Kind k = n.getKind(); - // we change Real equalities to Int equalities - bool preserveTypes = - k != Kind::EQUAL && (kindToTheoryId(k) != THEORY_ARITH); + bool preserveTypes = true; + // We change Real equalities to Int equalities, we handle other kinds + // here as well. + if (k==Kind::EQUAL || k==Kind::MULT || k==Kind::NONLINEAR_MULT || + k==Kind::ADD || k==Kind::SUB || k==Kind::NEG) + { + preserveTypes = false; + } for (size_t i = 0; i < n.getNumChildren(); i++) { Node nc = realToIntInternal(n[i], cache, var_eq); @@ -178,7 +183,7 @@ Node RealToInt::realToIntInternal(TNode n, NodeMap& cache, std::vector& va else { TypeNode tn = n.getType(); - if (tn.isReal() && !tn.isInteger()) + if (tn.isReal()) { if (n.getKind() == Kind::BOUND_VARIABLE) { @@ -218,10 +223,10 @@ PreprocessingPassResult RealToInt::applyInternal( std::vector var_eq; for (unsigned i = 0, size = assertionsToPreprocess->size(); i < size; ++i) { - assertionsToPreprocess->replace( - i, - rewrite( - realToIntInternal((*assertionsToPreprocess)[i], d_cache, var_eq))); + Node a = (*assertionsToPreprocess)[i]; + Node ac = realToIntInternal(a, d_cache, var_eq); + Trace("real-to-int") << "Converted " << a << " to " << ac << std::endl; + assertionsToPreprocess->replace(i, rewrite(ac)); if (assertionsToPreprocess->isInConflict()) { return PreprocessingPassResult::CONFLICT; diff --git a/src/proof/alf/alf_dependent_type_converter.h b/src/proof/alf/alf_dependent_type_converter.h index d9cc2793c54..faf672bd128 100644 --- a/src/proof/alf/alf_dependent_type_converter.h +++ b/src/proof/alf/alf_dependent_type_converter.h @@ -36,10 +36,12 @@ namespace proof { * * becomes the ALF rule: * - * (declare-rule dsl.bv-extract-concat-1 - * ((@n0 Int) (@n1 Int) (@n2 Int) (x (BitVec @n0)) (xs (BitVec @n1) :list) (y - * (BitVec @n2)) (i Int) (j Int)) :premises ((= (<= j (@bvsize x)) true)) :args - * (x xs y i j) :conclusion (= (extract j i (concat xs y x)) (extract j i x)) + * (declare-rule bv-extract-concat-1 + * ((@n0 Int) (@n1 Int) (@n2 Int) (x (BitVec @n0)) (xs (BitVec @n1) :list) + * (y (BitVec @n2)) (i Int) (j Int)) + * :premises ((= (<= j (@bvsize x)) true)) + * :args (x xs y i j) + * :conclusion (= (extract j i (concat xs y x)) (extract j i x)) * ) * * This converter is responsible for converting an approximate type into a Node diff --git a/src/proof/alf/alf_list_node_converter.h b/src/proof/alf/alf_list_node_converter.h index f1f0b4283e7..3d7cb93c308 100644 --- a/src/proof/alf/alf_list_node_converter.h +++ b/src/proof/alf/alf_list_node_converter.h @@ -35,7 +35,7 @@ namespace proof { * * becomes the ALF rule: * - * (declare-rule dsl.bool-or-false ((xs Bool :list) (ys Bool :list)) + * (declare-rule bool-or-false ((xs Bool :list) (ys Bool :list)) * :args (xs ys) * :conclusion (= (or xs false ys)) ($singleton_elim (or xs ys))) * ) diff --git a/src/proof/alf/alf_print_channel.cpp b/src/proof/alf/alf_print_channel.cpp index a44df9153da..6a73256e32b 100644 --- a/src/proof/alf/alf_print_channel.cpp +++ b/src/proof/alf/alf_print_channel.cpp @@ -138,6 +138,14 @@ void AlfPrintChannelOut::printTrustStep(ProofRule r, d_out << " " << di; } } + else if (r == ProofRule::THEORY_REWRITE) + { + ProofRewriteRule di; + if (rewriter::getRewriteRule(args[0], di)) + { + d_out << " " << di; + } + } d_out << std::endl; // trust takes a premise-list which must be specified even if empty printStepInternal("trust", n, i, premises, {nc}, false, true); @@ -215,7 +223,7 @@ void AlfPrintChannelPre::processInternal(const Node& n) expr::getVariables(n, d_vars, d_varsVisited); } -const std::unordered_set& AlfPrintChannelPre::getVariables() const +const std::unordered_set& AlfPrintChannelPre::getVariables() const { return d_vars; } diff --git a/src/proof/alf/alf_print_channel.h b/src/proof/alf/alf_print_channel.h index 530bec34cd8..8fb30e82a9b 100644 --- a/src/proof/alf/alf_print_channel.h +++ b/src/proof/alf/alf_print_channel.h @@ -155,7 +155,7 @@ class AlfPrintChannelPre : public AlfPrintChannel TNode conc) override; /** Get variables we encountered in printing */ - const std::unordered_set& getVariables() const; + const std::unordered_set& getVariables() const; private: /** The let binding */ @@ -163,7 +163,7 @@ class AlfPrintChannelPre : public AlfPrintChannel /** For computing free variables */ std::unordered_set d_keep; /** The set of variables we have encountered */ - std::unordered_set d_vars; + std::unordered_set d_vars; /** The visited cache for computing variables */ std::unordered_set d_varsVisited; /** Process that we will print node n in the final proof */ diff --git a/src/proof/alf/alf_printer.cpp b/src/proof/alf/alf_printer.cpp index a0c52737900..4b80eb59ea6 100644 --- a/src/proof/alf/alf_printer.cpp +++ b/src/proof/alf/alf_printer.cpp @@ -137,6 +137,8 @@ bool AlfPrinter::isHandled(const ProofNode* pfn) const case ProofRule::STRING_LENGTH_NON_EMPTY: case ProofRule::RE_INTER: case ProofRule::RE_UNFOLD_POS: + case ProofRule::RE_UNFOLD_NEG_CONCAT_FIXED: + case ProofRule::RE_UNFOLD_NEG: case ProofRule::ITE_EQ: case ProofRule::INSTANTIATE: case ProofRule::SKOLEMIZE: @@ -144,6 +146,13 @@ bool AlfPrinter::isHandled(const ProofNode* pfn) const case ProofRule::ENCODE_PRED_TRANSFORM: case ProofRule::ACI_NORM: case ProofRule::DSL_REWRITE: return true; + case ProofRule::THEORY_REWRITE: + { + ProofRewriteRule id; + rewriter::getRewriteRule(pfn->getArguments()[0], id); + return isHandledTheoryRewrite(id, pfn->getArguments()[1]); + } + break; case ProofRule::ARITH_POLY_NORM: { // we don't support bitvectors yet @@ -188,6 +197,18 @@ bool AlfPrinter::isHandled(const ProofNode* pfn) const return false; } +bool AlfPrinter::isHandledTheoryRewrite(ProofRewriteRule id, + const Node& n) const +{ + switch (id) + { + case ProofRewriteRule::DISTINCT_ELIM: + case ProofRewriteRule::RE_LOOP_ELIM: return true; + default: break; + } + return false; +} + bool AlfPrinter::canEvaluate(Node n) const { std::unordered_set visited; @@ -238,6 +259,8 @@ bool AlfPrinter::canEvaluate(Node n) const case Kind::STRING_INDEXOF: case Kind::STRING_TO_CODE: case Kind::STRING_FROM_CODE: + case Kind::BITVECTOR_EXTRACT: + case Kind::BITVECTOR_CONCAT: case Kind::BITVECTOR_ADD: case Kind::BITVECTOR_SUB: case Kind::BITVECTOR_NEG: @@ -337,7 +360,15 @@ std::string AlfPrinter::getRuleName(const ProofNode* pfn) const ProofRewriteRule dr; rewriter::getRewriteRule(pfn->getArguments()[0], dr); std::stringstream ss; - ss << "dsl." << dr; + ss << dr; + return ss.str(); + } + else if (r == ProofRule::THEORY_REWRITE) + { + ProofRewriteRule id; + rewriter::getRewriteRule(pfn->getArguments()[0], id); + std::stringstream ss; + ss << id; return ss.str(); } std::string name = toString(r); @@ -359,7 +390,7 @@ void AlfPrinter::printDslRule(std::ostream& out, ProofRewriteRule r) // BOUND_VARIABLE of this rule as user provided variables. The substitution // su stores this mapping. Subs su; - out << "(declare-rule dsl." << r << " ("; + out << "(declare-rule " << r << " ("; AlfDependentTypeConverter adtc(nodeManager(), d_tproc); std::stringstream ssExplicit; for (size_t i = 0, nvars = uvarList.size(); i < nvars; i++) @@ -486,8 +517,8 @@ void AlfPrinter::print(std::ostream& out, std::shared_ptr pfn) if (i == 1) { std::stringstream outVars; - const std::unordered_set& vars = aletify.getVariables(); - for (TNode v : vars) + const std::unordered_set& vars = aletify.getVariables(); + for (const Node& v : vars) { if (v.getKind() == Kind::BOUND_VARIABLE) { @@ -720,6 +751,14 @@ void AlfPrinter::getArgsFromProofRule(const ProofNode* pn, } return; } + case ProofRule::THEORY_REWRITE: + { + // ignore the identifier + Assert(pargs.size() == 2); + args.push_back(d_tproc.convert(pargs[1])); + return; + } + break; default: break; } for (size_t i = 0, nargs = pargs.size(); i < nargs; i++) diff --git a/src/proof/alf/alf_printer.h b/src/proof/alf/alf_printer.h index e36f07fe2c5..0c1e779097e 100644 --- a/src/proof/alf/alf_printer.h +++ b/src/proof/alf/alf_printer.h @@ -43,12 +43,24 @@ class AlfPrinter : protected EnvObj /** * Print the full proof of assertions => false by pfn. + * @param out The output stream. + * @param pfn The proof node. */ void print(std::ostream& out, std::shared_ptr pfn); + /** + * Print proof rewrite rule name r to output stream out + * @param out The output stream. + * @param r The proof rewrite rule. This should be one of the proof rewrite + * rules that corresponds to a RARE rewrite. + */ + void printDslRule(std::ostream& out, ProofRewriteRule r); + private: /** Return true if it is possible to trust the topmost application in pfn */ bool isHandled(const ProofNode* pfn) const; + /** Return true if id is handled as a theory rewrite for term n */ + bool isHandledTheoryRewrite(ProofRewriteRule id, const Node& n) const; /** * Return true if it is possible to evaluate n using the evaluation side * condition in the ALF signature. Notice this requires that all subterms of n @@ -106,8 +118,6 @@ class AlfPrinter : protected EnvObj * Allocate (if necessary) the identifier for step */ size_t allocateProofId(const ProofNode* pn, bool& wasAlloc); - /** Print DSL rule name r to output stream out */ - void printDslRule(std::ostream& out, ProofRewriteRule r); /** Print let list to output stream out */ void printLetList(std::ostream& out, LetBinding& lbind); /** Reference to the term processor */ diff --git a/src/proof/eager_proof_generator.cpp b/src/proof/eager_proof_generator.cpp index 45354e861e2..f474703d265 100644 --- a/src/proof/eager_proof_generator.cpp +++ b/src/proof/eager_proof_generator.cpp @@ -132,7 +132,7 @@ TrustNode EagerProofGenerator::mkTrustNodeRewrite(const Node& a, { std::vector args; args.push_back(rewriter::mkRewriteRuleNode(id)); - args.push_back(a); + args.push_back(a.eqNode(b)); return mkTrustedRewrite(a, b, ProofRule::THEORY_REWRITE, args); } diff --git a/src/proof/lfsc/lfsc_post_processor.cpp b/src/proof/lfsc/lfsc_post_processor.cpp index 7759a8cb840..8b570e100e4 100644 --- a/src/proof/lfsc/lfsc_post_processor.cpp +++ b/src/proof/lfsc/lfsc_post_processor.cpp @@ -442,7 +442,7 @@ bool LfscProofPostprocessCallback::update(Node res, if (idr == ProofRewriteRule::BETA_REDUCE) { // get the term to beta-reduce - Node termToReduce = nm->mkNode(Kind::APPLY_UF, args[1]); + Node termToReduce = nm->mkNode(Kind::APPLY_UF, args[1][0]); addLfscRule(cdp, res, {}, LfscRule::BETA_REDUCE, {termToReduce}); } else diff --git a/src/proof/proof.cpp b/src/proof/proof.cpp index 888f22fca20..d9027d2ad88 100644 --- a/src/proof/proof.cpp +++ b/src/proof/proof.cpp @@ -285,7 +285,7 @@ bool CDProof::addTheoryRewriteStep(Node expected, } std::vector sargs; sargs.push_back(rewriter::mkRewriteRuleNode(id)); - sargs.push_back(expected[0]); + sargs.push_back(expected); return addStep( expected, ProofRule::THEORY_REWRITE, {}, sargs, ensureChildren, opolicy); } diff --git a/src/proof/proof_node_algorithm.cpp b/src/proof/proof_node_algorithm.cpp index 172b4ff3caf..48d0e81c785 100644 --- a/src/proof/proof_node_algorithm.cpp +++ b/src/proof/proof_node_algorithm.cpp @@ -87,6 +87,38 @@ void getFreeAssumptionsMap( } while (!visit.empty()); } +void getSubproofRule(std::shared_ptr pn, + ProofRule r, + std::vector>& pfs) +{ + // proof should not be cyclic + std::unordered_set visited; + std::unordered_set::iterator it; + std::vector> visit; + std::shared_ptr cur; + visit.push_back(pn); + do + { + cur = visit.back(); + visit.pop_back(); + it = visited.find(cur.get()); + if (it == visited.end()) + { + visited.insert(cur.get()); + if (cur->getRule() == r) + { + pfs.push_back(cur); + } + else + { + const std::vector>& cs = cur->getChildren(); + // traverse on children + visit.insert(visit.end(), cs.begin(), cs.end()); + } + } + } while (!visit.empty()); +} + bool containsAssumption(const ProofNode* pn, std::unordered_map& caMap, const std::unordered_set& allowed) diff --git a/src/proof/proof_node_algorithm.h b/src/proof/proof_node_algorithm.h index c00d1cb26ea..8ea8eef26df 100644 --- a/src/proof/proof_node_algorithm.h +++ b/src/proof/proof_node_algorithm.h @@ -57,6 +57,16 @@ void getFreeAssumptionsMap( std::shared_ptr pn, std::map>>& amap); +/** + * Get the subproofs of pn that have rule r. + * @param pn The proof node. + * @param r The rule to find. + * @param pfs The list of subproofs of pn that have rule r. + */ +void getSubproofRule(std::shared_ptr pn, + ProofRule r, + std::vector>& pfs); + /** * Return true if pn contains a subproof whose rule is ASSUME. Notice that we * do *not* distinguish between free vs. non-free assumptions in this call. diff --git a/src/proof/proof_node_to_sexpr.cpp b/src/proof/proof_node_to_sexpr.cpp index 76e722a6c3d..153ffe8cdfe 100644 --- a/src/proof/proof_node_to_sexpr.cpp +++ b/src/proof/proof_node_to_sexpr.cpp @@ -333,6 +333,7 @@ ProofNodeToSExpr::ArgFormat ProofNodeToSExpr::getArgumentFormat( } break; case ProofRule::DSL_REWRITE: + case ProofRule::THEORY_REWRITE: if (i == 0) { return ArgFormat::DSL_REWRITE_ID; diff --git a/src/proof/proof_node_updater.cpp b/src/proof/proof_node_updater.cpp index 223ea52ab40..f44e151565c 100644 --- a/src/proof/proof_node_updater.cpp +++ b/src/proof/proof_node_updater.cpp @@ -51,6 +51,12 @@ bool ProofNodeUpdaterCallback::updatePost(Node res, return false; } +bool ProofNodeUpdaterCallback::canMerge(std::shared_ptr pn) +{ + // by default, no restriction on what proofs can merge + return true; +} + ProofNodeUpdater::ProofNodeUpdater(Env& env, ProofNodeUpdaterCallback& cb, bool mergeSubproofs, @@ -294,6 +300,11 @@ void ProofNodeUpdater::runFinalize( } if (d_mergeSubproofs) { + // if we cannot merge this proof, skip it + if (!d_cb.canMerge(cur)) + { + return; + } Node res = cur->getResult(); // cache the result if we don't contain an assumption if (!expr::containsAssumption(cur.get(), cfaMap, cfaAllowed)) diff --git a/src/proof/proof_node_updater.h b/src/proof/proof_node_updater.h index b04503c1ac8..618cd3e69d5 100644 --- a/src/proof/proof_node_updater.h +++ b/src/proof/proof_node_updater.h @@ -83,6 +83,13 @@ class ProofNodeUpdaterCallback const std::vector& children, const std::vector& args, CDProof* cdp); + /** + * Can we merge pn into other proofs? This method is only called if we are + * merging subproofs with a proof node updater (mergeSubproofs=true). + * If we return false for pn, then its contents will never be copied into + * another proof, nor will its contents be replaced. + */ + virtual bool canMerge(std::shared_ptr pn); }; /** diff --git a/src/proof/trust_id.cpp b/src/proof/trust_id.cpp index 0dd82529a32..b6f5eaf0dc6 100644 --- a/src/proof/trust_id.cpp +++ b/src/proof/trust_id.cpp @@ -45,6 +45,9 @@ const char* toString(TrustId id) case TrustId::ARITH_PRED_CAST_TYPE: return "ARITH_PRED_CAST_TYPE"; case TrustId::QUANTIFIERS_PREPROCESS: return "QUANTIFIERS_PREPROCESS"; case TrustId::SUBTYPE_ELIMINATION: return "SUBTYPE_ELIMINATION"; + case TrustId::MACRO_THEORY_REWRITE_RCONS: + return "MACRO_THEORY_REWRITE_RCONS"; + case TrustId::MACRO_BOOL_NNF_NORM_RCONS: return "MACRO_BOOL_NNF_NORM_RCONS"; default: return "TrustId::Unknown"; }; } diff --git a/src/proof/trust_id.h b/src/proof/trust_id.h index 27469655608..e12dd5d3f68 100644 --- a/src/proof/trust_id.h +++ b/src/proof/trust_id.h @@ -66,6 +66,10 @@ enum class TrustId : uint32_t QUANTIFIERS_PREPROCESS, /** A subtype elimination step that could not be processed */ SUBTYPE_ELIMINATION, + /** A rewrite required for showing a macro theory rewrite */ + MACRO_THEORY_REWRITE_RCONS, + /** A rewrite required for the macro Bool NNF theory rewrite */ + MACRO_BOOL_NNF_NORM_RCONS, }; /** Converts a trust id to a string. */ const char* toString(TrustId id); diff --git a/src/prop/zero_level_learner.cpp b/src/prop/zero_level_learner.cpp index 64c59b8b0fe..02eaf5718c3 100644 --- a/src/prop/zero_level_learner.cpp +++ b/src/prop/zero_level_learner.cpp @@ -239,13 +239,13 @@ modes::LearnedLitType ZeroLevelLearner::computeLearnedLiteralType( const Node& input) { // literal was learned, determine its type - // apply substitutions first - Node lit = d_tsmap.apply(input, d_env.getRewriter()); - TNode aatom = lit.getKind() == Kind::NOT ? lit[0] : lit; + // compute whether internal prior to substitution + TNode aatom = input.getKind() == Kind::NOT ? input[0] : input; bool internal = d_ppnAtoms.find(aatom) == d_ppnAtoms.end(); + // apply substitutions now + Node lit = d_tsmap.apply(input, d_env.getRewriter()); modes::LearnedLitType ltype = internal ? modes::LearnedLitType::INTERNAL : modes::LearnedLitType::INPUT; - // compute if solvable if (internal || d_trackSimplifications) { Subs ss; @@ -279,7 +279,7 @@ modes::LearnedLitType ZeroLevelLearner::computeLearnedLiteralType( processed = true; Trace("lemma-inprocess-subs") << "Add subs: " << v << " -> " << ss.d_subs[i] << std::endl; - d_tsmap.addSubstitution(v, ss.d_subs[i]); + addSimplification(v, ss.d_subs[i]); } } } @@ -305,7 +305,7 @@ modes::LearnedLitType ZeroLevelLearner::computeLearnedLiteralType( { Trace("lemma-inprocess-subs") << "Add cp: " << lit[1 - i] << " -> " << lit[i] << std::endl; - d_tsmap.addSubstitution(lit[1 - i], lit[i]); + addSimplification(lit[1 - i], lit[i]); processed = true; } break; @@ -315,7 +315,7 @@ modes::LearnedLitType ZeroLevelLearner::computeLearnedLiteralType( { Trace("lemma-inprocess-subs") << "Add cp subterm: " << lit[1 - i] << " -> " << lit[i] << std::endl; - d_tsmap.addSubstitution(lit[1 - i], lit[i]); + addSimplification(lit[1 - i], lit[i]); processed = true; break; } @@ -339,6 +339,20 @@ theory::TrustSubstitutionMap& ZeroLevelLearner::getSimplifications() return d_tsmap; } +void ZeroLevelLearner::addSimplification(const Node& t, const Node& s) +{ + // in rare cases we may already have a substitution for v, e.g. + // if x -> 0, (f y) ---> a, and we learn (f (+ x y)) = b, we + // would substitute+rewrite to get (f y) --> b despite already + // having a substitution for (f y). We could avoid this by applying + // substitution+rewriting until fixed point at the beginning of + // computeLearnedLiteralType, but this may be expensive. + if (!d_tsmap.get().hasSubstitution(t)) + { + d_tsmap.addSubstitution(t, s); + } +} + void ZeroLevelLearner::processLearnedLiteral(const Node& lit, modes::LearnedLitType ltype) { diff --git a/src/prop/zero_level_learner.h b/src/prop/zero_level_learner.h index 11c2a2d69cf..eb37fb1ff0d 100644 --- a/src/prop/zero_level_learner.h +++ b/src/prop/zero_level_learner.h @@ -89,6 +89,13 @@ class ZeroLevelLearner : protected EnvObj bool getSolved(const Node& lit, Subs& subs); /** has learned literal */ bool hasLearnedLiteralForRestart() const; + /** + * Adds a substitution to d_tsmap. This occurs when we learn a literal at + * decision level zero that is equivalent to (= t s) + * @param t The term to substitute. + * @param s The value to substitute t to. + */ + void addSimplification(const Node& t, const Node& s); /** The theory engine we are using */ TheoryEngine* d_theoryEngine; diff --git a/src/rewriter/basic_rewrite_rcons.cpp b/src/rewriter/basic_rewrite_rcons.cpp index d32ede86b2f..d76b2f6d369 100644 --- a/src/rewriter/basic_rewrite_rcons.cpp +++ b/src/rewriter/basic_rewrite_rcons.cpp @@ -16,21 +16,37 @@ #include "rewriter/basic_rewrite_rcons.h" +#include "proof/conv_proof_generator.h" #include "proof/proof_checker.h" +#include "proof/proof_node_algorithm.h" +#include "rewriter/rewrite_db_term_process.h" #include "rewriter/rewrites.h" #include "smt/env.h" +#include "theory/arith/arith_poly_norm.h" +#include "theory/arith/arith_proof_utilities.h" +#include "theory/booleans/theory_bool_rewriter.h" #include "theory/bv/theory_bv_rewrite_rules.h" #include "theory/rewriter.h" +#include "theory/strings/arith_entail.h" +#include "util/rational.h" using namespace cvc5::internal::kind; namespace cvc5::internal { namespace rewriter { -BasicRewriteRCons::BasicRewriteRCons(Env& env) : EnvObj(env) {} +BasicRewriteRCons::BasicRewriteRCons(Env& env) : EnvObj(env) +{ + d_isDslStrict = (options().proof.proofGranularityMode + == options::ProofGranularityMode::DSL_REWRITE_STRICT); +} -bool BasicRewriteRCons::prove( - CDProof* cdp, Node a, Node b, theory::TheoryId tid, MethodId mid) +bool BasicRewriteRCons::prove(CDProof* cdp, + Node a, + Node b, + theory::TheoryId tid, + MethodId mid, + std::vector>& subgoals) { Node eq = a.eqNode(b); Trace("trewrite-rcons") << "Reconstruct " << eq << " (from " << tid << ", " @@ -52,15 +68,12 @@ bool BasicRewriteRCons::prove( } // try theory rewrite (pre-rare) - ProofRewriteRule prid = - d_env.getRewriter()->findRule(a, b, theory::TheoryRewriteCtx::PRE_DSL); - if (prid != ProofRewriteRule::NONE) + if (!d_isDslStrict) { - if (tryRule( - cdp, eq, ProofRule::THEORY_REWRITE, {mkRewriteRuleNode(prid), a})) + if (tryTheoryRewrite(cdp, eq, theory::TheoryRewriteCtx::PRE_DSL, subgoals)) { - Trace("trewrite-rcons") << "Reconstruct " << eq << " (from " << prid - << ", " << mid << ")" << std::endl; + Trace("trewrite-rcons") + << "Reconstruct (pre) " << eq << " via theory rewrite" << std::endl; return true; } } @@ -69,32 +82,47 @@ bool BasicRewriteRCons::prove( } bool BasicRewriteRCons::postProve( - CDProof* cdp, Node a, Node b, theory::TheoryId tid, MethodId mid) + CDProof* cdp, + Node a, + Node b, + theory::TheoryId tid, + MethodId mid, + std::vector>& subgoals) { Node eq = a.eqNode(b); - - // try theory rewrite (post-rare) - ProofRewriteRule prid = - d_env.getRewriter()->findRule(a, b, theory::TheoryRewriteCtx::POST_DSL); - if (prid != ProofRewriteRule::NONE) + // try theory rewrite (post-rare), which may try both pre and post if + // the proof-granularity mode is dsl-rewrite-strict. + bool success = false; + if (d_isDslStrict) { - if (tryRule( - cdp, eq, ProofRule::THEORY_REWRITE, {mkRewriteRuleNode(prid), a})) + if (tryTheoryRewrite(cdp, eq, theory::TheoryRewriteCtx::PRE_DSL, subgoals)) { - Trace("trewrite-rcons") << "Reconstruct (post) " << eq << " (from " - << prid << ", " << mid << ")" << std::endl; - return true; + success = true; } } - - Trace("trewrite-rcons") << "...(fail)" << std::endl; - return false; + if (!success + && tryTheoryRewrite( + cdp, eq, theory::TheoryRewriteCtx::POST_DSL, subgoals)) + { + success = true; + } + if (success) + { + Trace("trewrite-rcons") + << "Reconstruct (post) " << eq << " via theory rewrite" << std::endl; + } + else + { + Trace("trewrite-rcons") << "...(fail)" << std::endl; + } + return success; } bool BasicRewriteRCons::tryRule(CDProof* cdp, Node eq, ProofRule r, - const std::vector& args) + const std::vector& args, + bool addStep) { Trace("trewrite-rcons-debug") << "Try " << r << std::endl; ProofChecker* pc = d_env.getProofNodeManager()->getChecker(); @@ -103,11 +131,84 @@ bool BasicRewriteRCons::tryRule(CDProof* cdp, Node res = pc->checkDebug(r, {}, args, Node::null(), "trewrite-rcons"); if (!res.isNull() && res == eq) { - cdp->addStep(eq, r, {}, args); + if (addStep) + { + cdp->addStep(eq, r, {}, args); + } return true; } return false; } +void BasicRewriteRCons::ensureProofForTheoryRewrite( + CDProof* cdp, + ProofRewriteRule id, + const Node& eq, + std::vector>& subgoals) +{ + switch (id) + { + case ProofRewriteRule::MACRO_BOOL_NNF_NORM: + if (ensureProofMacroBoolNnfNorm(cdp, eq, subgoals)) + { + return; + } + break; + default: break; + } + // default, just add the rewrite + std::vector args; + args.push_back( + nodeManager()->mkConstInt(Rational(static_cast(id)))); + args.push_back(eq); + cdp->addStep(eq, ProofRule::THEORY_REWRITE, {}, args); +} + +bool BasicRewriteRCons::ensureProofMacroBoolNnfNorm( + CDProof* cdp, + const Node& eq, + std::vector>& subgoals) +{ + Trace("brc-macro") << "Expand Bool NNF norm " << eq[0] << " == " << eq[1] + << std::endl; + // Call the utility again with proof tracking and construct the term + // conversion proof. This proof itself may have trust steps in it. + TConvProofGenerator tcpg(d_env, nullptr); + Node nr = theory::booleans::TheoryBoolRewriter::computeNnfNorm( + nodeManager(), eq[0], &tcpg); + std::shared_ptr pfn = tcpg.getProofFor(eq); + Trace("brc-macro") << "...proof is " << *pfn.get() << std::endl; + cdp->addProof(pfn); + // the small steps are trust steps, record them here + expr::getSubproofRule(pfn, ProofRule::TRUST, subgoals); + return true; +} + +bool BasicRewriteRCons::tryTheoryRewrite( + CDProof* cdp, + const Node& eq, + theory::TheoryRewriteCtx ctx, + std::vector>& subgoals) +{ + Assert(eq.getKind() == Kind::EQUAL); + ProofRewriteRule prid = d_env.getRewriter()->findRule(eq[0], eq[1], ctx); + if (prid != ProofRewriteRule::NONE) + { + // Do not add the step in the call to tryStep, instead we add it via + // ensureProofForTheoryRewrite. + if (tryRule(cdp, + eq, + ProofRule::THEORY_REWRITE, + {mkRewriteRuleNode(prid), eq}, + false)) + { + // Theory rewrites may require macro expansion + ensureProofForTheoryRewrite(cdp, prid, eq, subgoals); + return true; + } + } + return false; +} + } // namespace rewriter } // namespace cvc5::internal diff --git a/src/rewriter/basic_rewrite_rcons.h b/src/rewriter/basic_rewrite_rcons.h index f92cade2c34..95907357058 100644 --- a/src/rewriter/basic_rewrite_rcons.h +++ b/src/rewriter/basic_rewrite_rcons.h @@ -26,6 +26,7 @@ #include "proof/proof_node_manager.h" #include "smt/env_obj.h" #include "theory/builtin/proof_checker.h" +#include "theory/rewriter.h" namespace cvc5::internal { namespace rewriter { @@ -44,26 +45,96 @@ class BasicRewriteRCons : protected EnvObj * Try to prove (= a b), where a ---> b was a theory rewrite from theory * tid with the given method. If this method returns true, then a proof * of (= a b) was added to cdp. + * @param cdp The proof to add to. + * @param a The left hand side of the equality. + * @param b The left hand side of the equality. + * @param tid The theory that was the source of the rewrite (if any). + * @param tid The method that was the source of the rewrite (if any). + * @param subgoals The list of proofs introduced when proving eq that + * are trusted steps. + * @return true if we successfully added a proof of (= a b) to cdp. */ - bool prove(CDProof* cdp, Node a, Node b, theory::TheoryId tid, MethodId mid); + bool prove(CDProof* cdp, + Node a, + Node b, + theory::TheoryId tid, + MethodId mid, + std::vector>& subgoals); /** * There are theory rewrites which cannot be expressed in RARE rules. In this * case we need to use proof rules which are not written in RARE. It is only * used as a last resort method so this is executed only when other rules * fail. + * @param cdp The proof to add to. + * @param a The left hand side of the equality. + * @param b The left hand side of the equality. + * @param tid The theory that was the source of the rewrite (if any). + * @param tid The method that was the source of the rewrite (if any). + * @param subgoals The list of proofs introduced when proving eq that + * are trusted steps. + * @return true if we successfully added a proof of (= a b) to cdp. */ - bool postProve( - CDProof* cdp, Node a, Node b, theory::TheoryId tid, MethodId mid); + bool postProve(CDProof* cdp, + Node a, + Node b, + theory::TheoryId tid, + MethodId mid, + std::vector>& subgoals); + /** + * Ensure we have a proof for theory rewrite id of eq in cdp. This typically + * adds a single THEORY_REWRITE step to cdp. However, for rules with prefix + * MACRO_, we perform elaboration. + * @param cdp The proof to add to. + * @param id The theory rewrite that proves eq. + * @param eq The conclusion of the theory rewrite. + * @param subgoals The list of proofs introduced when proving eq that + * are trusted steps. + */ + void ensureProofForTheoryRewrite( + CDProof* cdp, + ProofRewriteRule id, + const Node& eq, + std::vector>& subgoals); private: + /** + * Is proof-granularity set to dsl-rewrite-strict? This impacts when + * THEORY_REWRITE are tried. + */ + bool d_isDslStrict; /** * Try rule r, return true if eq could be proven by r with arguments args. * If this method returns true, a proof of eq was added to cdp. + * If addStep is true, we add the proof to cdp. Otherwise, the caller is + * responsible for adding the proof. */ bool tryRule(CDProof* cdp, Node eq, ProofRule r, - const std::vector& args); + const std::vector& args, + bool addStep = true); + /** + * Elaborate a rewrite eq that was proven by + * ProofRewriteRule::MACRO_BOOL_NNF_NORM. + * + * @param cdp The proof to add to. + * @param eq The rewrite proven by ProofRewriteRule::MACRO_BOOL_NNF_NORM. + * @param subgoals The list of proofs introduced when proving eq that + * are trusted steps. These are small step rewrites corresponding to NNF + * flattening of operators, and other simple inferences. + * @return true if added a closed proof of eq to cdp. + */ + bool ensureProofMacroBoolNnfNorm( + CDProof* cdp, + const Node& eq, + std::vector>& subgoals); + /** + * Try THEORY_REWRITE with theory::TheoryRewriteCtx ctx. + */ + bool tryTheoryRewrite(CDProof* cdp, + const Node& eq, + theory::TheoryRewriteCtx ctx, + std::vector>& subgoals); }; } // namespace rewriter diff --git a/src/rewriter/mkrewrites.py b/src/rewriter/mkrewrites.py index 082f7af63ec..244d0fc4f21 100644 --- a/src/rewriter/mkrewrites.py +++ b/src/rewriter/mkrewrites.py @@ -103,8 +103,13 @@ def gen_mk_node(defns, expr): def gen_rewrite_db_rule(defns, rule): fvs_list = ', '.join(bvar.name for bvar in rule.bvars) - fixed_point_arg = gen_mk_node(defns, rule.rhs_context) \ - if rule.rhs_context else 'Node::null()' + + if rule.rhs_context: + assert rule.is_fixed_point + fixed_point_arg = gen_mk_node(defns, rule.rhs_context) + else: + assert not rule.is_fixed_point + fixed_point_arg = 'Node::null()' return f'db.addRule(ProofRewriteRule::{rule.get_enum()}, {{ {fvs_list} }}, ' \ f'{gen_mk_node(defns, rule.lhs)}, {gen_mk_node(defns, rule.rhs)}, '\ f'{gen_mk_node(defns, rule.cond)}, {fixed_point_arg});' diff --git a/src/rewriter/node.py b/src/rewriter/node.py index 1c77bafb1e4..a99748c3bea 100644 --- a/src/rewriter/node.py +++ b/src/rewriter/node.py @@ -202,6 +202,11 @@ def __new__(cls, symbol, kind): SET_SUBSET = ('set.subset', 'SET_SUBSET') SET_MEMBER = ('set.member', 'SET_MEMBER') SET_SINGLETON = ('set.singleton', 'SET_SINGLETON') + SET_CHOOSE = ('set.choose', 'SET_CHOOSE') + SET_CARD = ('set.card', 'SET_CARD') + SET_IS_EMPTY = ('set.is_empty', 'SET_IS_EMPTY') + SET_IS_SINGLETON = ('set.is_singleton', 'SET_IS_SINGLETON') + class BaseSort(Enum): Bool = auto() diff --git a/src/rewriter/rewrite_db.cpp b/src/rewriter/rewrite_db.cpp index 0167dd25748..23ff38a6889 100644 --- a/src/rewriter/rewrite_db.cpp +++ b/src/rewriter/rewrite_db.cpp @@ -58,16 +58,26 @@ void RewriteDb::addRule(ProofRewriteRule id, Node eq = a.eqNode(b); // we canonize left-to-right, hence we should traverse in the opposite // order, since we index based on conclusion, we make a dummy node here - Node tmp = nm->mkNode(Kind::IMPLIES, eq, cond); + std::vector tmpArgs; + tmpArgs.push_back(eq); + tmpArgs.push_back(cond); + if (!context.isNull()) + { + tmpArgs.push_back(context); + } + Node tmp = nm->mkNode(Kind::SEXPR, tmpArgs); // must canonize Trace("rewrite-db") << "Add rule " << id << ": " << cond << " => " << a << " == " << b << std::endl; Assert(a.getType().isComparableTo(b.getType())); - Node cr = d_canon.getCanonicalTerm(tmp, false, false); - context = d_canon.getCanonicalTerm(context, false, false); + Node ctmp = d_canon.getCanonicalTerm(tmp, false, false); + if (!context.isNull()) + { + context = ctmp[2]; + } - Node condC = cr[1]; + Node condC = ctmp[1]; std::vector conds; if (condC.getKind() == Kind::AND) { @@ -102,7 +112,7 @@ void RewriteDb::addRule(ProofRewriteRule id, } // register side conditions? - Node eqC = cr[0]; + Node eqC = ctmp[0]; Assert(eqC.getKind() == Kind::EQUAL); // add to discrimination tree @@ -187,5 +197,11 @@ const std::unordered_set& RewriteDb::getAllFreeVariables() const return d_allFv; } +const std::map& RewriteDb::getAllRules() + const +{ + return d_rewDbRule; +} + } // namespace rewriter } // namespace cvc5::internal diff --git a/src/rewriter/rewrite_db.h b/src/rewriter/rewrite_db.h index 9546a2abf20..33e40cb7a04 100644 --- a/src/rewriter/rewrite_db.h +++ b/src/rewriter/rewrite_db.h @@ -91,6 +91,8 @@ class RewriteDb const std::vector& getRuleIdsForHead(const Node& h) const; /** Return the union of free variables in all rules */ const std::unordered_set& getAllFreeVariables() const; + /** Return all rewrite rules */ + const std::map& getAllRules() const; private: /** common constants */ diff --git a/src/rewriter/rewrite_db_proof_cons.cpp b/src/rewriter/rewrite_db_proof_cons.cpp index 8040e130f2c..b49a56e69b3 100644 --- a/src/rewriter/rewrite_db_proof_cons.cpp +++ b/src/rewriter/rewrite_db_proof_cons.cpp @@ -54,13 +54,15 @@ RewriteDbProofCons::RewriteDbProofCons(Env& env, RewriteDb* db) d_false = nm->mkConst(false); } -bool RewriteDbProofCons::prove(CDProof* cdp, - const Node& a, - const Node& b, - theory::TheoryId tid, - MethodId mid, - int64_t recLimit, - int64_t stepLimit) +bool RewriteDbProofCons::prove( + CDProof* cdp, + const Node& a, + const Node& b, + theory::TheoryId tid, + MethodId mid, + int64_t recLimit, + int64_t stepLimit, + std::vector>& subgoals) { // clear the proof caches d_pcache.clear(); @@ -92,37 +94,40 @@ bool RewriteDbProofCons::prove(CDProof* cdp, << std::endl; Trace("rpc-debug") << "- prove basic" << std::endl; // first, try with the basic utility - if (d_trrc.prove(cdp, eq[0], eq[1], tid, mid)) + bool success = false; + if (d_trrc.prove(cdp, eq[0], eq[1], tid, mid, subgoals)) { Trace("rpc") << "...success (basic)" << std::endl; - return true; + success = true; } - bool success = false; - ++d_statTotalInputs; - Trace("rpc-debug") << "- convert to internal" << std::endl; - // prove the equality - for (int64_t i = 0; i <= recLimit; i++) + else { - Trace("rpc-debug") << "* Try recursion depth " << i << std::endl; - if (proveEq(cdp, eq, eq, i, stepLimit)) + ++d_statTotalInputs; + Trace("rpc-debug") << "- convert to internal" << std::endl; + // prove the equality + for (int64_t i = 0; i <= recLimit; i++) { - success = true; - break; + Trace("rpc-debug") << "* Try recursion depth " << i << std::endl; + if (proveEq(cdp, eq, eq, i, stepLimit, subgoals)) + { + success = true; + break; + } } - } - if (!success) - { - Node eqi = d_rdnc.convert(eq); - // if converter didn't make a difference, don't try to prove again - if (eqi != eq) + if (!success) { - for (int64_t i = 0; i <= recLimit; i++) + Node eqi = d_rdnc.convert(eq); + // if converter didn't make a difference, don't try to prove again + if (eqi != eq) { - Trace("rpc-debug") << "* Try recursion depth " << i << std::endl; - if (proveEq(cdp, eq, eqi, i, stepLimit)) + for (int64_t i = 0; i <= recLimit; i++) { - success = true; - break; + Trace("rpc-debug") << "* Try recursion depth " << i << std::endl; + if (proveEq(cdp, eq, eqi, i, stepLimit, subgoals)) + { + success = true; + break; + } } } } @@ -130,21 +135,30 @@ bool RewriteDbProofCons::prove(CDProof* cdp, if (!success) { // now try the "post-prove" method as a last resort - if (d_trrc.postProve(cdp, eq[0], eq[1], tid, mid)) + if (d_trrc.postProve(cdp, eq[0], eq[1], tid, mid, subgoals)) { Trace("rpc") << "...success (post-prove basic)" << std::endl; - return true; + success = true; + } + else + { + Trace("rpc") << "...fail" << std::endl; } } - Trace("rpc") << "..." << (success ? "success" : "fail") << std::endl; + else + { + Trace("rpc") << "...success" << std::endl; + } return success; } -bool RewriteDbProofCons::proveEq(CDProof* cdp, - const Node& eq, - const Node& eqi, - int64_t recLimit, - int64_t stepLimit) +bool RewriteDbProofCons::proveEq( + CDProof* cdp, + const Node& eq, + const Node& eqi, + int64_t recLimit, + int64_t stepLimit, + std::vector>& subgoals) { // add one to recursion limit, since it is decremented whenever we // initiate the getMatches routine. @@ -169,7 +183,7 @@ bool RewriteDbProofCons::proveEq(CDProof* cdp, { cdp->addStep(eq, ProofRule::ENCODE_PRED_TRANSFORM, {eqi}, {eq}); } - ensureProofInternal(cdp, eqi); + ensureProofInternal(cdp, eqi, subgoals); AlwaysAssert(cdp->hasStep(eqi)) << eqi; Trace("rpc-debug") << "- finish ensure proof" << std::endl; return true; @@ -221,6 +235,13 @@ RewriteProofStatus RewriteDbProofCons::proveInternalViaStrategy(const Node& eqi) { return RewriteProofStatus::ARITH_POLY_NORM; } + // Maybe holds via a THEORY_REWRITE that has been marked with + // TheoryRewriteCtx::DSL_SUBCALL. + if (proveWithRule( + RewriteProofStatus::THEORY_REWRITE, eqi, {}, {}, false, false, true)) + { + return RewriteProofStatus::THEORY_REWRITE; + } Trace("rpc-debug2") << "...not proved via builtin tactic" << std::endl; d_currRecLimit--; Node prevTarget = d_target; @@ -286,6 +307,14 @@ bool RewriteDbProofCons::notifyMatch(const Node& s, // apply substitution, which may notice vars may be out of order wrt rule // var list target = expr::narySubstitute(target, vars, subs); + // it may be impossible to construct the conclusion due to null terminators + // for approximate types, return false in this case + if (target.isNull()) + { + // note that we return false here, indicating that we don't want any + // more matches, since we have failed for the current fixed point rule. + return false; + } // We now prove with the given rule. this should only fail if there are // conditions on the rule which fail. Notice we never allow recursion here. // We also don't permit inflection matching (which regardless should not @@ -346,7 +375,8 @@ bool RewriteDbProofCons::proveWithRule(RewriteProofStatus id, bool doRecurse, ProofRewriteRule r) { - Assert(!target.isNull() && target.getKind() == Kind::EQUAL); + Assert(!target.isNull() && target.getKind() == Kind::EQUAL) + << "Unknown " << target << " with rule " << id << " " << r << std::endl; Trace("rpc-debug2") << "Check rule " << (id == RewriteProofStatus::DSL ? toString(r) : toString(id)) @@ -471,8 +501,20 @@ bool RewriteDbProofCons::proveWithRule(RewriteProofStatus id, } pic.d_id = id; } + else if (id == RewriteProofStatus::THEORY_REWRITE) + { + ProofRewriteRule prid = d_env.getRewriter()->findRule( + target[0], target[1], theory::TheoryRewriteCtx::DSL_SUBCALL); + if (prid == ProofRewriteRule::NONE) + { + return false; + } + pic.d_id = id; + pic.d_dslId = prid; + } else { + Assert(id == RewriteProofStatus::DSL); const RewriteProofRule& rpr = d_db->getRule(r); // does it conclusion match what we are trying to show? Node conc = rpr.getConclusion(); @@ -744,7 +786,10 @@ bool RewriteDbProofCons::proveInternalBase(const Node& eqi, return false; } -bool RewriteDbProofCons::ensureProofInternal(CDProof* cdp, const Node& eqi) +bool RewriteDbProofCons::ensureProofInternal( + CDProof* cdp, + const Node& eqi, + std::vector>& subgoals) { // note we could use single internal cdp to improve subproof sharing NodeManager* nm = nodeManager(); @@ -821,43 +866,43 @@ bool RewriteDbProofCons::ensureProofInternal(CDProof* cdp, const Node& eqi) { // premises are the steps, stored in d_vars ps.insert(ps.end(), pcur.d_vars.begin(), pcur.d_vars.end()); - if (pcur.d_id == RewriteProofStatus::CONG - || pcur.d_id == RewriteProofStatus::CONG_EVAL) - { - pfac.push_back(ProofRuleChecker::mkKindNode(cur[0].getKind())); - if (cur[0].getMetaKind() == kind::metakind::PARAMETERIZED) - { - pfac.push_back(cur[0].getOperator()); - } - } } else { + Assert(cur.getKind() == Kind::EQUAL); Assert(pcur.d_dslId != ProofRewriteRule::NONE); - const RewriteProofRule& rpr = d_db->getRule(pcur.d_dslId); // add the DSL proof rule we used pfac.push_back( nm->mkConstInt(Rational(static_cast(pcur.d_dslId)))); - // compute premises based on the used substitution - // build the substitution context - const std::vector& vs = rpr.getVarList(); - Assert(pcur.d_vars.size() == vs.size()); - std::vector rsubs; - // must order the variables to match order of rewrite rule - for (const Node& v : vs) + if (pcur.d_id == RewriteProofStatus::DSL) { - itv = std::find(pcur.d_vars.begin(), pcur.d_vars.end(), v); - size_t d = std::distance(pcur.d_vars.begin(), itv); - Assert(d < pcur.d_subs.size()); - rsubs.push_back(pcur.d_subs[d]); + const RewriteProofRule& rpr = d_db->getRule(pcur.d_dslId); + // compute premises based on the used substitution + // build the substitution context + const std::vector& vs = rpr.getVarList(); + Assert(pcur.d_vars.size() == vs.size()); + std::vector rsubs; + // must order the variables to match order of rewrite rule + for (const Node& v : vs) + { + itv = std::find(pcur.d_vars.begin(), pcur.d_vars.end(), v); + size_t d = std::distance(pcur.d_vars.begin(), itv); + Assert(d < pcur.d_subs.size()); + rsubs.push_back(pcur.d_subs[d]); + } + // get the conditions, store into premises of cur. + if (!rpr.getObligations(vs, rsubs, ps)) + { + Assert(false) << "failed a side condition?"; + return false; + } + pfac.insert(pfac.end(), rsubs.begin(), rsubs.end()); } - // get the conditions, store into premises of cur. - if (!rpr.getObligations(vs, rsubs, ps)) + else { - Assert(false) << "failed a side condition?"; - return false; + Assert(pcur.d_id == RewriteProofStatus::THEORY_REWRITE); + pfac.push_back(cur); } - pfac.insert(pfac.end(), rsubs.begin(), rsubs.end()); } // recurse on premises visit.insert(visit.end(), ps.begin(), ps.end()); @@ -941,18 +986,30 @@ bool RewriteDbProofCons::ensureProofInternal(CDProof* cdp, const Node& eqi) { cdp->addStep(cur, ProofRule::ARITH_POLY_NORM, {}, {cur}); } - else + else if (pcur.d_id == RewriteProofStatus::DSL + || pcur.d_id == RewriteProofStatus::THEORY_REWRITE) { Assert(pfArgs.find(cur) != pfArgs.end()); Assert(pcur.d_dslId != ProofRewriteRule::NONE); - const RewriteProofRule& rpr = d_db->getRule(pcur.d_dslId); const std::vector& args = pfArgs[cur]; - std::vector subs(args.begin() + 1, args.end()); - conc = rpr.getConclusionFor(subs); - Trace("rpc-debug") << "Finalize proof for " << cur << std::endl; - Trace("rpc-debug") << "Proved: " << cur << std::endl; - Trace("rpc-debug") << "From: " << conc << std::endl; - cdp->addStep(conc, ProofRule::DSL_REWRITE, ps, args); + ProofRule pfr; + if (pcur.d_id == RewriteProofStatus::DSL) + { + std::vector subs(args.begin() + 1, args.end()); + const RewriteProofRule& rpr = d_db->getRule(pcur.d_dslId); + conc = rpr.getConclusionFor(subs); + Trace("rpc-debug") << "Finalize proof for " << cur << std::endl; + Trace("rpc-debug") << "Proved: " << cur << std::endl; + Trace("rpc-debug") << "From: " << conc << std::endl; + pfr = ProofRule::DSL_REWRITE; + cdp->addStep(conc, pfr, ps, args); + } + else + { + Assert(pcur.d_id == RewriteProofStatus::THEORY_REWRITE); + // use the utility, possibly to do macro expansion + d_trrc.ensureProofForTheoryRewrite(cdp, pcur.d_dslId, cur, subgoals); + } } } } while (!visit.empty()); @@ -1055,6 +1112,7 @@ Node RewriteDbProofCons::getRuleConclusion(const RewriteProofRule& rpr, ProvenInfo& dpi = d_pcache[source.eqNode(target)]; dpi.d_id = pi.d_id; + dpi.d_dslId = pi.d_dslId; dpi.d_vars = vars; dpi.d_subs = stepSubs; diff --git a/src/rewriter/rewrite_db_proof_cons.h b/src/rewriter/rewrite_db_proof_cons.h index 80be3ecad3e..5c79f52f69d 100644 --- a/src/rewriter/rewrite_db_proof_cons.h +++ b/src/rewriter/rewrite_db_proof_cons.h @@ -49,14 +49,17 @@ class RewriteDbProofCons : protected EnvObj * Prove (= a b) with recursion limit recLimit and step limit stepLimit. * If cdp is provided, we add a proof for this fact on it. * - * @param cdp The object to add the proof of (= a b) to - * @param a The left hand side of the equality - * @param b The right hand side of the equality + * @param cdp The object to add the proof of (= a b) to. + * @param a The left hand side of the equality. + * @param b The right hand side of the equality. * @param tid The theory identifier responsible for the rewrite, if one * exists. * @param mid The method id (the kind of rewrite) - * @param recLimit The recursion limit for this call + * @param recLimit The recursion limit for this call. * @param stepLimit The step limit for this call. + * @param subgoals The list of proofs introduced when proving (= a b) that + * are trusted steps, and thus would require further elaboration from the + * caller of this method. * @return true if we successfully added a proof of (= a b) to cdp */ bool prove(CDProof* cdp, @@ -65,7 +68,8 @@ class RewriteDbProofCons : protected EnvObj theory::TheoryId tid, MethodId mid, int64_t recLimit, - int64_t stepLimit); + int64_t stepLimit, + std::vector>& subgoals); private: /** @@ -117,17 +121,31 @@ class RewriteDbProofCons : protected EnvObj /** * Is internal rule? these rules store children (if any) in d_vars. */ - bool isInternalRule() const { return d_id != RewriteProofStatus::DSL; } + bool isInternalRule() const + { + return d_id != RewriteProofStatus::DSL + && d_id != RewriteProofStatus::THEORY_REWRITE; + } }; /** * Prove and store the proof of eq with internal form eqi in cdp if possible, * return true if successful. + * + * @param cdp The object to add the proof of eq to. + * @param eq The equality we are trying to prove. + * @param eqi The internal version of the equality that may have been + * converted from eq using d_rdnc. + * @param recLimit The recursion limit for this call. + * @param stepLimit The step limit for this call. + * @param subgoals The list of proofs introduced when proving eq that + * are trusted steps. */ bool proveEq(CDProof* cdp, const Node& eq, const Node& eqi, int64_t recLimit, - int64_t stepLimit); + int64_t stepLimit, + std::vector>& subgoals); /** * Prove internal, which is the main entry point for proven an equality eqi. * Returns the proof rule that was used to prove eqi, or @@ -166,8 +184,12 @@ class RewriteDbProofCons : protected EnvObj * * @param cdp The proof to add the proof of eqi to * @param eqi The proven equality + * @param subgoals The list of proofs introduced when proving eq that + * are trusted steps. */ - bool ensureProofInternal(CDProof* cdp, const Node& eqi); + bool ensureProofInternal(CDProof* cdp, + const Node& eqi, + std::vector>& subgoals); /** Return the evaluation of n, which uses local caching. */ Node doEvaluate(const Node& n); /** diff --git a/src/rewriter/rewrite_db_term_process.cpp b/src/rewriter/rewrite_db_term_process.cpp index 3e2e56e4763..47cc0875f55 100644 --- a/src/rewriter/rewrite_db_term_process.cpp +++ b/src/rewriter/rewrite_db_term_process.cpp @@ -80,7 +80,7 @@ Node RewriteDbNodeConverter::postConvert(Node n) } } // convert indexed operators to symbolic - if (GenericOp::isIndexedOperatorKind(k)) + if (GenericOp::isNumeralIndexedOperatorKind(k)) { NodeManager* nm = NodeManager::currentNM(); std::vector indices = diff --git a/src/rewriter/rewrite_proof_status.cpp b/src/rewriter/rewrite_proof_status.cpp index ec163317a16..f3482ae9d43 100644 --- a/src/rewriter/rewrite_proof_status.cpp +++ b/src/rewriter/rewrite_proof_status.cpp @@ -33,6 +33,7 @@ const char* toString(RewriteProofStatus s) case RewriteProofStatus::ARITH_POLY_NORM: return "ARITH_POLY_NORM"; case RewriteProofStatus::ACI_NORM: return "ACI_NORM"; case RewriteProofStatus::DSL: return "DSL"; + case RewriteProofStatus::THEORY_REWRITE: return "THEORY_REWRITE"; default: Unreachable(); } } diff --git a/src/rewriter/rewrite_proof_status.h b/src/rewriter/rewrite_proof_status.h index ec5a41ed609..c66d241caf0 100644 --- a/src/rewriter/rewrite_proof_status.h +++ b/src/rewriter/rewrite_proof_status.h @@ -42,6 +42,8 @@ enum class RewriteProofStatus : uint32_t ACI_NORM, // we have a DSL proof rule that proves this goal. DSL, + // we have a THEORY_REWRITE that proves this goal. + THEORY_REWRITE }; /** diff --git a/src/rewriter/rw_parser.py b/src/rewriter/rw_parser.py index 7ef0b81fd76..dd07ebe88cb 100644 --- a/src/rewriter/rw_parser.py +++ b/src/rewriter/rw_parser.py @@ -194,8 +194,8 @@ def rule_action(s, l, t): def fixed_rule_action(s, l, t): # t = [key, args, match, target, (cond)] assert len(t) == 4 or len(t) == 5 - keys, args, match, target, cond = list(t) + [None] \ - if len(t) == 4 else t + keys, args, match, target = t[:4] + cond = Placeholder() if len(t) == 4 else t[4] return self.rule_action(args, CBool(True), match, target, True, cond) fixed_rule = ( pp.Suppress('(') + diff --git a/src/smt/process_assertions.cpp b/src/smt/process_assertions.cpp index a900d4ef8d2..0d8df75de6d 100644 --- a/src/smt/process_assertions.cpp +++ b/src/smt/process_assertions.cpp @@ -316,7 +316,7 @@ bool ProcessAssertions::apply(AssertionPipeline& ap) Trace("smt") << " assertions : " << ap.size() << endl; // ff - if (options().ff.ffDisjunctiveBit) + if (options().ff.ffElimDisjunctiveBit) { applyPass("ff-disjunctive-bit", ap); } diff --git a/src/smt/proof_manager.cpp b/src/smt/proof_manager.cpp index cd3c9020f0a..7f2686a89bc 100644 --- a/src/smt/proof_manager.cpp +++ b/src/smt/proof_manager.cpp @@ -36,6 +36,7 @@ #include "smt/proof_post_processor.h" #include "smt/smt_solver.h" +using namespace cvc5::internal::rewriter; namespace cvc5::internal { namespace smt { @@ -48,9 +49,31 @@ PfManager::PfManager(Env& env) { // construct the rewrite db only if DSL rewrites are enabled if (options().proof.proofGranularityMode - == options::ProofGranularityMode::DSL_REWRITE) + == options::ProofGranularityMode::DSL_REWRITE + || options().proof.proofGranularityMode + == options::ProofGranularityMode::DSL_REWRITE_STRICT) { - d_rewriteDb.reset(new rewriter::RewriteDb); + d_rewriteDb.reset(new RewriteDb); + if (isOutputOn(OutputTag::RARE_DB)) + { + if (options().proof.proofFormatMode != options::ProofFormatMode::ALF) + { + Warning() + << "WARNING: Assuming --proof-format=alf when printing the RARE " + "database with -o rare-db" + << std::endl; + } + proof::AlfNodeConverter atp(nodeManager()); + proof::AlfPrinter alfp(d_env, atp, d_rewriteDb.get()); + const std::map& rules = + d_rewriteDb->getAllRules(); + std::stringstream ss; + for (const std::pair& r : rules) + { + alfp.printDslRule(ss, r.first); + } + output(OutputTag::RARE_DB) << ss.str(); + } } // enable the proof checker and the proof node manager d_pchecker.reset( @@ -98,6 +121,7 @@ PfManager::PfManager(Env& env) { d_pfpp->setEliminateRule(ProofRule::SUBS); d_pfpp->setEliminateRule(ProofRule::MACRO_REWRITE); + // if in a DSL rewrite mode if (options().proof.proofGranularityMode != options::ProofGranularityMode::THEORY_REWRITE) { diff --git a/src/smt/proof_post_processor.cpp b/src/smt/proof_post_processor.cpp index 925add7a26b..e1c9c701f3a 100644 --- a/src/smt/proof_post_processor.cpp +++ b/src/smt/proof_post_processor.cpp @@ -22,6 +22,7 @@ #include "proof/proof_node_manager.h" #include "proof/resolution_proofs_util.h" #include "proof/subtype_elim_proof_converter.h" +#include "theory/arith/arith_proof_utilities.h" #include "theory/arith/arith_utilities.h" #include "theory/builtin/proof_checker.h" #include "theory/bv/bitblast/bitblast_proof_generator.h" @@ -175,6 +176,17 @@ bool ProofPostprocessCallback::update(Node res, return !ret.isNull(); } +bool ProofPostprocessCallback::canMerge(std::shared_ptr pn) +{ + if (d_collectAllTrusted) + { + ProofRule id = pn->getRule(); + return (id != ProofRule::TRUST_THEORY_REWRITE && id != ProofRule::TRUST); + } + // otherwise we can merge + return true; +} + bool ProofPostprocessCallback::updateInternal(Node res, ProofRule id, const std::vector& children, @@ -943,57 +955,9 @@ Node ProofPostprocessCallback::expandMacros(ProofRule id, } else if (id == ProofRule::MACRO_ARITH_SCALE_SUM_UB) { - Trace("macro::arith") << "Expand MACRO_ARITH_SCALE_SUM_UB" << std::endl; - if (TraceIsOn("macro::arith")) - { - for (const auto& child : children) - { - Trace("macro::arith") << " child: " << child << std::endl; - } - Trace("macro::arith") << " args: " << args << std::endl; - } - Assert(args.size() == children.size()); - NodeManager* nm = nodeManager(); - ProofStepBuffer steps{d_pc}; - - // Scale all children, accumulating - std::vector scaledRels; - for (size_t i = 0; i < children.size(); ++i) - { - TNode child = children[i]; - TNode scalar = args[i]; - bool isPos = scalar.getConst() > 0; - Node scalarCmp = - nm->mkNode(isPos ? Kind::GT : Kind::LT, - scalar, - nm->mkConstRealOrInt(scalar.getType(), Rational(0))); - // (= scalarCmp true) - Node scalarCmpOrTrue = - steps.tryStep(ProofRule::EVALUATE, {}, {scalarCmp}); - Assert(!scalarCmpOrTrue.isNull()); - // scalarCmp - steps.addStep(ProofRule::TRUE_ELIM, {scalarCmpOrTrue}, {}, scalarCmp); - // (and scalarCmp relation) - Node scalarCmpAndRel = - steps.tryStep(ProofRule::AND_INTRO, {scalarCmp, child}, {}); - Assert(!scalarCmpAndRel.isNull()); - // (=> (and scalarCmp relation) scaled) - Node impl = steps.tryStep( - isPos ? ProofRule::ARITH_MULT_POS : ProofRule::ARITH_MULT_NEG, - {}, - {scalar, child}); - Assert(!impl.isNull()); - // scaled - Node scaled = - steps.tryStep(ProofRule::MODUS_PONENS, {scalarCmpAndRel, impl}, {}); - Assert(!scaled.isNull()); - scaledRels.emplace_back(scaled); - } - - Node sumBounds = steps.tryStep(ProofRule::ARITH_SUM_UB, scaledRels, {}); - cdp->addSteps(steps); - Trace("macro::arith") << "Expansion done. Proved: " << sumBounds - << std::endl; + Node sumBounds = theory::arith::expandMacroSumUb(children, args, cdp); + Assert(!sumBounds.isNull()); + Assert(res.isNull() || sumBounds == res); return sumBounds; } else if (id == ProofRule::MACRO_STRING_INFERENCE) diff --git a/src/smt/proof_post_processor.h b/src/smt/proof_post_processor.h index 7ab4cd9affd..c71945f8610 100644 --- a/src/smt/proof_post_processor.h +++ b/src/smt/proof_post_processor.h @@ -87,6 +87,13 @@ class ProofPostprocessCallback : public ProofNodeUpdaterCallback, protected EnvO const std::vector& args, CDProof* cdp, bool& continueUpdate) override; + /** + * Can merge. This returns false if pn is a trusted proof, since we do not + * want the proof node updater to merge its contents into another proof, + * which we otherwise would not be informed of and would lead to trusted + * proofs that are not recorded in d_trustedPfs. + */ + bool canMerge(std::shared_ptr pn) override; private: /** Common constants */ diff --git a/src/smt/proof_post_processor_dsl.cpp b/src/smt/proof_post_processor_dsl.cpp index 12a44aecede..e2c73668b53 100644 --- a/src/smt/proof_post_processor_dsl.cpp +++ b/src/smt/proof_post_processor_dsl.cpp @@ -17,6 +17,7 @@ #include "options/base_options.h" #include "options/smt_options.h" +#include "proof/proof_ensure_closed.h" using namespace cvc5::internal::theory; @@ -32,12 +33,36 @@ ProofPostprocessDsl::ProofPostprocessDsl(Env& env, rewriter::RewriteDb* rdb) void ProofPostprocessDsl::reconstruct( std::unordered_set>& pfs) { + Trace("pp-dsl") << "Reconstruct proofs for " << pfs.size() + << " trusted steps..." << std::endl; // run an updater for this callback ProofNodeUpdater pnu(d_env, *this, false); for (std::shared_ptr p : pfs) { pnu.process(p); } + if (!d_subgoals.empty()) + { + std::vector> sgs = d_subgoals; + Trace("pp-dsl") << "Also reconstruct proofs for " << sgs.size() + << " subgoals..." << std::endl; + d_subgoals.clear(); + for (std::shared_ptr p : sgs) + { + pnu.process(p); + } + } + // should never construct a subgoal for a step from a subgoal + if (!d_subgoals.empty()) + { + Trace("pp-dsl") << "REM SUBGOALS: " << std::endl; + for (std::shared_ptr p : d_subgoals) + { + Warning() << "WARNING: unproven subgoal " << p->getResult() << std::endl; + Trace("pp-dsl") << " " << p->getResult() << std::endl; + } + d_subgoals.clear(); + } } bool ProofPostprocessDsl::shouldUpdate(std::shared_ptr pn, @@ -80,9 +105,11 @@ bool ProofPostprocessDsl::update(Node res, } int64_t recLimit = options().proof.proofRewriteRconsRecLimit; int64_t stepLimit = options().proof.proofRewriteRconsStepLimit; - // attempt to reconstruct the proof of the equality into cdp using the - // rewrite database proof reconstructor - if (d_rdbPc.prove(cdp, res[0], res[1], tid, mid, recLimit, stepLimit)) + // Attempt to reconstruct the proof of the equality into cdp using the + // rewrite database proof reconstructor. + // We record the subgoals in d_subgoals. + if (d_rdbPc.prove( + cdp, res[0], res[1], tid, mid, recLimit, stepLimit, d_subgoals)) { // If we made (= res true) above, conclude the original res. if (reqTrueElim) @@ -90,6 +117,7 @@ bool ProofPostprocessDsl::update(Node res, cdp->addStep(res[0], ProofRule::TRUE_ELIM, {res}, {}); res = res[0]; } + pfgEnsureClosed(options(), res, cdp, "check-dsl", "check dsl"); // if successful, we update the proof return true; } diff --git a/src/smt/proof_post_processor_dsl.h b/src/smt/proof_post_processor_dsl.h index cb2171de230..cf01f20712a 100644 --- a/src/smt/proof_post_processor_dsl.h +++ b/src/smt/proof_post_processor_dsl.h @@ -62,6 +62,8 @@ class ProofPostprocessDsl : protected EnvObj, public ProofNodeUpdaterCallback Node d_true; /** The rewrite database proof generator */ rewriter::RewriteDbProofCons d_rdbPc; + /** The accumulated subgoals from calls to d_rdbPc */ + std::vector> d_subgoals; }; } // namespace smt diff --git a/src/smt/set_defaults.cpp b/src/smt/set_defaults.cpp index a5b16292c25..1bf1233da88 100644 --- a/src/smt/set_defaults.cpp +++ b/src/smt/set_defaults.cpp @@ -1391,6 +1391,11 @@ void SetDefaults::setDefaultsQuantifiers(const LogicInfo& logic, { SET_AND_NOTIFY(quantifiers, cegqi, false, "instMaxLevel"); } + // enable MBQI if --mbqi-fast-sygus is provided + if (opts.quantifiers.mbqiFastSygus) + { + SET_AND_NOTIFY_IF_NOT_USER(quantifiers, mbqi, true, "mbqiFastSygus"); + } if (opts.quantifiers.mbqi) { // MBQI is an alternative to CEGQI/SyQI diff --git a/src/smt/solver_engine.cpp b/src/smt/solver_engine.cpp index 0d08e6b0f12..0cbe718f165 100644 --- a/src/smt/solver_engine.cpp +++ b/src/smt/solver_engine.cpp @@ -192,6 +192,16 @@ void SolverEngine::finishInit() *d_env.get(), *d_smtSolver.get(), *d_pfManager.get())); pnm = d_pfManager->getProofNodeManager(); } + if (d_env->isOutputOn(OutputTag::RARE_DB)) + { + if (!d_env->getOptions().smt.produceProofs + || options().proof.proofGranularityMode + != options::ProofGranularityMode::DSL_REWRITE) + { + Warning() << "WARNING: -o rare-db requires --produce-proofs and " + "--proof-granularity=dsl-rewrite" << std::endl; + } + } // enable proof support in the environment/rewriter d_env->finishInit(pnm); @@ -1106,13 +1116,18 @@ void SolverEngine::addPlugin(Plugin* p) d_env->addPlugin(p); } -Node SolverEngine::simplify(const Node& t) +Node SolverEngine::simplify(const Node& t, bool applySubs) { beginCall(true); - // ensure we've processed assertions - d_smtDriver->refreshAssertions(); - // apply substitutions - Node tt = d_smtSolver->getPreprocessor()->applySubstitutions(t); + Node tt = t; + // if we are applying substitutions + if (applySubs) + { + // ensure we've processed assertions + d_smtDriver->refreshAssertions(); + // apply substitutions + tt = d_smtSolver->getPreprocessor()->applySubstitutions(tt); + } // now rewrite Node ret = d_env->getRewriter()->rewrite(tt); // make so that the returned term does not involve arithmetic subtyping diff --git a/src/smt/solver_engine.h b/src/smt/solver_engine.h index 0fd670f2d6a..59e32dd5d45 100644 --- a/src/smt/solver_engine.h +++ b/src/smt/solver_engine.h @@ -491,17 +491,17 @@ class CVC5_EXPORT SolverEngine */ void addPlugin(Plugin* p); /** - * Simplify a formula without doing "much" work. Does not involve - * the SAT Engine in the simplification, but uses the current - * definitions, assertions, and the current partial model, if one - * has been constructed. It also involves theory normalization. + * Simplify a term or formula based on rewriting and (optionally) applying + * substitutions for solved variables. * - * @throw TypeCheckingException, LogicException + * If applySubs is true, then for example, if `(= x 0)` was asserted to this + * solver, this method may replace occurrences of `x` with `0`. * - * @todo (design) is this meant to give an equivalent or an - * equisatisfiable formula? + * @param t The term to simplify. + * @param applySubs Whether to apply substitutions for solved variables. + * @return The simplified term. */ - Node simplify(const Node& e); + Node simplify(const Node& e, bool applySubs); /** * Get the assigned value of an expr (only if immediately preceded by a SAT diff --git a/src/smt/sygus_solver.cpp b/src/smt/sygus_solver.cpp index 42d371f91c6..f0e010190c5 100644 --- a/src/smt/sygus_solver.cpp +++ b/src/smt/sygus_solver.cpp @@ -242,20 +242,82 @@ SynthResult SygusSolver::checkSynth(bool isNext) body = nm->mkNode(Kind::IMPLIES, bodyAssump, body); } body = body.notNode(); - Trace("smt") << "...constructed sygus constraint " << body << std::endl; + Trace("smt-debug") << "...constructed sygus constraint " << body + << std::endl; if (!d_sygusVars.empty()) { Node boundVars = nm->mkNode(Kind::BOUND_VAR_LIST, listToVector(d_sygusVars)); body = nm->mkNode(Kind::EXISTS, boundVars, body); - Trace("smt") << "...constructed exists " << body << std::endl; + Trace("smt-debug") << "...constructed exists " << body << std::endl; } - if (!d_sygusFunSymbols.empty()) + bool inferTrivial = true; + // cannot omit unused functions if in incremental or sygus-stream + if (options().quantifiers.sygusStream || options().base.incrementalSolving) { - body = quantifiers::SygusUtils::mkSygusConjecture( - listToVector(d_sygusFunSymbols), body); + inferTrivial = false; } - Trace("smt") << "...constructed forall " << body << std::endl; + // Mark functions that do not occur in the conjecture as trivial, + // and do not solve for them. + std::vector ntrivSynthFuns; + if (inferTrivial) + { + // must expand definitions first + Node ppBody = d_smtSolver.getPreprocessor()->applySubstitutions(body); + ppBody = rewrite(ppBody); + std::unordered_set vs; + expr::getVariables(ppBody, vs); + for (size_t i = 0; i < 2; i++) + { + d_trivialFuns.clear(); + for (const Node& f : d_sygusFunSymbols) + { + if (vs.find(f) != vs.end()) + { + ntrivSynthFuns.push_back(f); + } + else + { + Trace("smt-debug") << "...trivial function: " << f << std::endl; + d_trivialFuns.push_back(f); + } + } + // we could have dependencies from the grammars of + // functions-to-synthesize to trivial functions, account for this as + // well + if (i == 0 && !d_trivialFuns.empty()) + { + size_t prevSize = vs.size(); + for (const Node& f : ntrivSynthFuns) + { + TypeNode tnp = quantifiers::SygusUtils::getSygusType(f); + if (tnp.isNull()) + { + continue; + } + theory::datatypes::utils::getFreeVariablesSygusType(tnp, vs); + } + if (vs.size() == prevSize) + { + // no new symbols found + break; + } + } + else + { + break; + } + } + } + else + { + ntrivSynthFuns = listToVector(d_sygusFunSymbols); + } + if (!ntrivSynthFuns.empty()) + { + body = quantifiers::SygusUtils::mkSygusConjecture(ntrivSynthFuns, body); + } + Trace("smt-debug") << "...constructed forall " << body << std::endl; Trace("smt") << "Check synthesis conjecture: " << body << std::endl; @@ -347,14 +409,30 @@ SynthResult SygusSolver::checkSynth(bool isNext) bool SygusSolver::getSynthSolutions(std::map& solMap) { + bool ret = false; Trace("smt") << "SygusSolver::getSynthSolutions" << std::endl; if (usingSygusSubsolver()) { // use the call to get the synth solutions from the subsolver - return d_subsolver ? d_subsolver->getSubsolverSynthSolutions(solMap) - : false; + if (d_subsolver) + { + ret = d_subsolver->getSubsolverSynthSolutions(solMap); + } + } + else + { + ret = getSubsolverSynthSolutions(solMap); + } + // also get solutions for trivial functions to synthesize + for (const Node& f : d_trivialFuns) + { + Node sf = quantifiers::SygusUtils::mkSygusTermFor(f); + Trace("smt-debug") << "Got " << sf << " for trivial function " << f + << std::endl; + Assert(f.getType() == sf.getType()); + solMap[f] = sf; } - return getSubsolverSynthSolutions(solMap); + return ret; } bool SygusSolver::getSubsolverSynthSolutions(std::map& solMap) @@ -437,8 +515,11 @@ void SygusSolver::checkSynthSolution(Assertions& as, initializeSygusSubsolver(solChecker, as); solChecker->getOptions().write_smt().checkSynthSol = false; solChecker->getOptions().write_quantifiers().sygusRecFun = false; - Assert(conj.getKind() == Kind::FORALL); - Node conjBody = conj[1]; + Node conjBody = conj; + if (conj.getKind() == Kind::FORALL) + { + conjBody = conjBody[1]; + } // we must apply substitutions here, since define-fun may contain the // function-to-synthesize, which needs to be substituted. conjBody = d_smtSolver.getPreprocessor()->applySubstitutions(conjBody); diff --git a/src/smt/sygus_solver.h b/src/smt/sygus_solver.h index 42d4e3fc757..962aa5e1480 100644 --- a/src/smt/sygus_solver.h +++ b/src/smt/sygus_solver.h @@ -230,6 +230,11 @@ class SygusSolver : protected EnvObj NodeList d_sygusFunSymbols; /** The current sygus conjecture */ Node d_conj; + /** + * The list of synthesis functions that were trivial (not contained in the + * conjecture but were specified via synth-fun). + */ + std::vector d_trivialFuns; /** * Whether we need to reconstruct the sygus conjecture. * diff --git a/src/theory/arith/arith_poly_norm.cpp b/src/theory/arith/arith_poly_norm.cpp index f65e6f7f5a5..20f2132556f 100644 --- a/src/theory/arith/arith_poly_norm.cpp +++ b/src/theory/arith/arith_poly_norm.cpp @@ -15,6 +15,8 @@ #include "theory/arith/arith_poly_norm.h" +#include "expr/attribute.h" +#include "theory/bv/theory_bv_utils.h" #include "util/bitvector.h" using namespace cvc5::internal::kind; @@ -161,6 +163,24 @@ bool PolyNorm::isEqual(const PolyNorm& p) const return true; } +bool PolyNorm::isConstant(Rational& c) const +{ + if (d_polyNorm.size() == 0) + { + c = Rational(0); + return true; + } + if (d_polyNorm.size() == 1) + { + if (d_polyNorm.begin()->first.isNull()) + { + c = d_polyNorm.begin()->second; + return true; + } + } + return false; +} + bool PolyNorm::isEqualMod(const PolyNorm& p, Rational& c) const { if (d_polyNorm.size() != p.d_polyNorm.size()) @@ -191,6 +211,79 @@ bool PolyNorm::isEqualMod(const PolyNorm& p, Rational& c) const return true; } +Node PolyNorm::toNode(const TypeNode& tn) const +{ + std::vector sum; + NodeManager* nm = NodeManager::currentNM(); + bool isArith = (tn.isInteger() || tn.isReal()); + bool isBv = tn.isBitVector(); + Kind multKind; + Kind addKind; + Node one; + if (isArith) + { + multKind = Kind::MULT; + addKind = Kind::ADD; + one = nm->mkConstRealOrInt(tn, Rational(1)); + } + else if (isBv) + { + multKind = Kind::BITVECTOR_MULT; + addKind = Kind::BITVECTOR_ADD; + one = bv::utils::mkOne(tn.getBitVectorSize()); + } + else + { + return Node::null(); + } + for (const std::pair& m : d_polyNorm) + { + Node coeff; + if (isArith) + { + coeff = nm->mkConstRealOrInt(tn, m.second); + } + else + { + Assert(isBv); + coeff = nm->mkConst( + BitVector(tn.getBitVectorSize(), m.second.getNumerator())); + } + if (m.first.isNull()) + { + sum.push_back(coeff); + } + else if (coeff == one) + { + sum.push_back(m.first); + } + else + { + Assert(m.first.getType().isComparableTo(tn)); + sum.push_back(nm->mkNode(multKind, {coeff, m.first})); + } + } + if (sum.size() == 1) + { + return sum[0]; + } + if (sum.empty()) + { + if (isArith) + { + return nm->mkConstRealOrInt(tn, Rational(0)); + } + else + { + Assert(isBv); + return bv::utils::mkZero(tn.getBitVectorSize()); + } + } + // must sort to ensure this method is idempotent + std::sort(sum.begin(), sum.end()); + return nm->mkNode(addKind, sum); +} + Node PolyNorm::multMonoVar(TNode m1, TNode m2) { std::vector vars = getMonoVars(m1); @@ -425,6 +518,30 @@ PolyNorm PolyNorm::mkDiff(TNode a, TNode b) return pa; } +struct ArithPolyNormTag +{ +}; +/** Cache for PolyNorm::getPolyNorm */ +typedef expr::Attribute ArithPolyNormAttr; + +Node PolyNorm::getPolyNorm(Node a) +{ + ArithPolyNormAttr apna; + Node an = a.getAttribute(apna); + if (an.isNull()) + { + PolyNorm pa = arith::PolyNorm::mkPolyNorm(a); + an = pa.toNode(a.getType()); + a.setAttribute(apna, an); + // as an optimization, assume idempotent + if (a != an) + { + an.setAttribute(apna, an); + } + } + return an; +} + } // namespace arith } // namespace theory } // namespace cvc5::internal diff --git a/src/theory/arith/arith_poly_norm.h b/src/theory/arith/arith_poly_norm.h index 3c9c054a5d7..2e709d5f3a7 100644 --- a/src/theory/arith/arith_poly_norm.h +++ b/src/theory/arith/arith_poly_norm.h @@ -61,11 +61,17 @@ class PolyNorm bool empty() const; /** Is this polynomial equal to polynomial p? */ bool isEqual(const PolyNorm& p) const; + /** Is this polynomial a constant? If so, store it in c */ + bool isConstant(Rational& c) const; /** * Is this polynomial equal to polynomial p*c for some c? If so, return * true and store in c. */ bool isEqualMod(const PolyNorm& p, Rational& c) const; + /** + * Convert the polynomial representation to a node + */ + Node toNode(const TypeNode& tn) const; /** * Make polynomial from real term n. This method normalizes applications * of operators ADD, SUB, NEG, MULT, NONLINEAR_MULT, bitvector equivalent @@ -89,6 +95,12 @@ class PolyNorm */ static bool isArithPolyNormAtom(TNode a, TNode b); + /** + * Return the normalized form of (arithmetic) term a based on the techniques + * from this class. + */ + static Node getPolyNorm(Node a); + private: /** * Make the difference of two nodes a and b, independent of their type. diff --git a/src/theory/arith/arith_proof_utilities.cpp b/src/theory/arith/arith_proof_utilities.cpp index b072bce442f..13c1261a020 100644 --- a/src/theory/arith/arith_proof_utilities.cpp +++ b/src/theory/arith/arith_proof_utilities.cpp @@ -15,6 +15,7 @@ #include "theory/arith/arith_proof_utilities.h" +#include "proof/proof_node_manager.h" #include "util/rational.h" namespace cvc5::internal { @@ -44,6 +45,63 @@ std::vector getMacroSumUbCoeff(const std::vector& pfs, return ret; } +Node expandMacroSumUb(const std::vector& children, + const std::vector& args, + CDProof* cdp) +{ + if (TraceIsOn("macro::arith")) + { + Trace("macro::arith") << "Expand MACRO_ARITH_SCALE_SUM_UB" << std::endl; + for (const auto& child : children) + { + Trace("macro::arith") << " child: " << child << std::endl; + } + Trace("macro::arith") << " args: " << args << std::endl; + } + Assert(args.size() == children.size()); + NodeManager* nm = NodeManager::currentNM(); + ProofStepBuffer steps{cdp->getManager()->getChecker()}; + + // Scale all children, accumulating + std::vector scaledRels; + Node one = nm->mkConstInt(Rational(1)); + for (size_t i = 0; i < children.size(); ++i) + { + TNode child = children[i]; + TNode scalar = args[i]; + bool isPos = scalar.getConst() > 0; + Node scalarCmp = + nm->mkNode(isPos ? Kind::GT : Kind::LT, + scalar, + nm->mkConstRealOrInt(scalar.getType(), Rational(0))); + // (= scalarCmp true) + Node scalarCmpOrTrue = steps.tryStep(ProofRule::EVALUATE, {}, {scalarCmp}); + Assert(!scalarCmpOrTrue.isNull()); + // scalarCmp + steps.addStep(ProofRule::TRUE_ELIM, {scalarCmpOrTrue}, {}, scalarCmp); + // (and scalarCmp relation) + Node scalarCmpAndRel = + steps.tryStep(ProofRule::AND_INTRO, {scalarCmp, child}, {}); + Assert(!scalarCmpAndRel.isNull()); + // (=> (and scalarCmp relation) scaled) + Node impl = steps.tryStep( + isPos ? ProofRule::ARITH_MULT_POS : ProofRule::ARITH_MULT_NEG, + {}, + {scalar, child}); + Assert(!impl.isNull()); + // scaled + Node scaled = + steps.tryStep(ProofRule::MODUS_PONENS, {scalarCmpAndRel, impl}, {}); + Assert(!scaled.isNull()); + scaledRels.emplace_back(scaled); + } + + Node sumBounds = steps.tryStep(ProofRule::ARITH_SUM_UB, scaledRels, {}); + cdp->addSteps(steps); + Trace("macro::arith") << "Expansion done. Proved: " << sumBounds << std::endl; + return sumBounds; +} + } // namespace arith } // namespace theory } // namespace cvc5::internal diff --git a/src/theory/arith/arith_proof_utilities.h b/src/theory/arith/arith_proof_utilities.h index cf7a010180e..e1d4b20dee7 100644 --- a/src/theory/arith/arith_proof_utilities.h +++ b/src/theory/arith/arith_proof_utilities.h @@ -23,6 +23,7 @@ #include #include "expr/node.h" +#include "proof/proof.h" #include "proof/proof_node.h" namespace cvc5::internal { @@ -44,6 +45,23 @@ namespace arith { std::vector getMacroSumUbCoeff(const std::vector& pfs, const std::vector& coeffs); +/** + * Expand an instance of ProofRule::MACRO_ARITH_SCALE_SUM_UB. + * This adds steps to cdp that prove the same as an application of this rule, + * assuming that children are free assumptions. + * + * This method assumes that children and args are valid parameters to + * MACRO_ARITH_SCALE_SUM_UB. + * + * @param children The children of MACRO_ARITH_SCALE_SUM_UB. + * @param args The arguments of MACRO_ARITH_SCALE_SUM_UB. + * @param cdp The proof to add steps to. + * @return The conclusion of the proof rule. + */ +Node expandMacroSumUb(const std::vector& children, + const std::vector& args, + CDProof* cdp); + } // namespace arith } // namespace theory } // namespace cvc5::internal diff --git a/src/theory/arith/nl/icp/icp_solver.cpp b/src/theory/arith/nl/icp/icp_solver.cpp index 9c721c42fdf..4fdc24a0f81 100644 --- a/src/theory/arith/nl/icp/icp_solver.cpp +++ b/src/theory/arith/nl/icp/icp_solver.cpp @@ -72,7 +72,7 @@ ICPSolver::ICPSolver(Env& env, InferenceManager& im) std::vector ICPSolver::collectVariables(const Node& n) const { - std::unordered_set tmp; + std::unordered_set tmp; expr::getVariables(n, tmp); std::vector res; for (const auto& t : tmp) @@ -98,7 +98,7 @@ std::vector ICPSolver::constructCandidates(const Node& n) auto poly = std::get<0>(comp); std::vector result; - std::unordered_set vars; + std::unordered_set vars; expr::getVariables(n, vars); for (const auto& v : vars) { diff --git a/src/theory/booleans/rewrites b/src/theory/booleans/rewrites index 828afa440f4..a2a8aa1e1d1 100644 --- a/src/theory/booleans/rewrites +++ b/src/theory/booleans/rewrites @@ -1,4 +1,8 @@ (define-rule bool-double-not-elim ((t Bool)) (not (not t)) t) +(define-cond-rule bool-not-true ((t Bool)) + (= t false) (not t) true) +(define-cond-rule bool-not-false ((t Bool)) + (= t true) (not t) false) (define-rule bool-eq-true ((t Bool)) (= t true) t) (define-rule bool-eq-false ((t Bool)) (= t false) (not t)) @@ -23,12 +27,27 @@ (define-rule bool-and-conf ((xs Bool :list) (w Bool) (ys Bool :list) (zs Bool :list)) (and xs w ys (not w) zs) false) (define-rule bool-or-taut ((xs Bool :list) (w Bool) (ys Bool :list) (zs Bool :list)) (or xs w ys (not w) zs) true) +(define-rule* bool-or-de-morgan ((x Bool) (y Bool) (zs Bool :list)) + (not (or x y zs)) + (not (or y zs)) + (and (not x) _)) +(define-rule bool-implies-de-morgan ((x Bool) (y Bool)) + (not (=> x y)) + (and x (not y))) +(define-rule* bool-and-de-morgan ((x Bool) (y Bool) (zs Bool :list)) + (not (and x y zs)) + (not (and y zs)) + (or (not x) _)) + (define-rule bool-xor-refl ((x Bool)) (xor x x) false) (define-rule bool-xor-nrefl ((x Bool)) (xor x (not x)) true) (define-rule bool-xor-false ((x Bool)) (xor x false) x) (define-rule bool-xor-true ((x Bool)) (xor x true) (not x)) (define-rule bool-xor-comm ((x Bool) (y Bool)) (xor x y) (xor y x)) -(define-rule bool-xor-elim ((x Bool) (y Bool)) (xor x y) (not (= x y))) +(define-rule bool-xor-elim ((x Bool) (y Bool)) (xor x y) (= (not x) y)) +(define-rule bool-not-xor-elim ((x Bool) (y Bool)) (not (xor x y)) (= x y)) + +(define-rule bool-not-eq-elim ((x Bool) (y Bool)) (not (= x y)) (= (not x) y)) (define-cond-rule ite-neg-branch ((c Bool) (x Bool) (y Bool)) (= (not y) x) (ite c x y) (= c x)) @@ -39,3 +58,5 @@ (define-rule ite-then-lookahead-self ((c Bool) (x Bool)) (ite c c x) (ite c true x)) (define-rule ite-else-lookahead-self ((c Bool) (x Bool)) (ite c x c) (ite c x false)) + +(define-rule bool-not-ite-elim ((c Bool) (x Bool) (y Bool)) (not (ite c x y)) (ite c (not x) (not y))) diff --git a/src/theory/booleans/theory_bool_rewriter.cpp b/src/theory/booleans/theory_bool_rewriter.cpp index bc328d4d574..bf08cf5a42f 100644 --- a/src/theory/booleans/theory_bool_rewriter.cpp +++ b/src/theory/booleans/theory_bool_rewriter.cpp @@ -21,7 +21,9 @@ #include #include +#include "expr/algorithm/flatten.h" #include "expr/node_value.h" +#include "proof/conv_proof_generator.h" #include "util/cardinality.h" namespace cvc5::internal { @@ -32,6 +34,236 @@ TheoryBoolRewriter::TheoryBoolRewriter(NodeManager* nm) : TheoryRewriter(nm) { d_true = nm->mkConst(true); d_false = nm->mkConst(false); + registerProofRewriteRule(ProofRewriteRule::MACRO_BOOL_NNF_NORM, + TheoryRewriteCtx::POST_DSL); +} + +Node TheoryBoolRewriter::rewriteViaRule(ProofRewriteRule id, const Node& n) +{ + switch (id) + { + case ProofRewriteRule::MACRO_BOOL_NNF_NORM: + { + Node nn = computeNnfNorm(nodeManager(), n); + if (nn != n) + { + return nn; + } + } + break; + default: break; + } + return Node::null(); +} + +bool TheoryBoolRewriter::addNnfNormChild(std::vector& children, + Node c, + Kind k, + std::map& lit_pol, + bool& childrenChanged) +{ + if (k == Kind::OR || k == Kind::AND) + { + Node lit = c.getKind() == Kind::NOT ? c[0] : c; + bool pol = c.getKind() != Kind::NOT; + std::map::iterator it = lit_pol.find(lit); + if (it == lit_pol.end()) + { + lit_pol[lit] = pol; + children.push_back(c); + } + else + { + childrenChanged = true; + if (it->second != pol) + { + return false; + } + } + } + else + { + children.push_back(c); + } + return true; +} + +Node TheoryBoolRewriter::computeNnfNorm(NodeManager* nm, + const Node& n, + TConvProofGenerator* pg) +{ + // at pre-order traversal, we store preKind and preChildren, which + // determine the Kind and the children for the node to reconstruct. + std::unordered_map preKind; + std::unordered_map> preChildren; + std::unordered_map visited; + std::unordered_map::iterator it; + std::vector visit; + TNode cur; + visit.push_back(n); + do + { + cur = visit.back(); + visit.pop_back(); + it = visited.find(cur); + if (it == visited.end()) + { + Kind k = cur.getKind(); + bool negAllCh = false; + bool negCh1 = false; + // the new formula we should traverse + TNode ncur = cur; + if (k == Kind::IMPLIES) + { + k = Kind::OR; + negCh1 = true; + } + else if (k == Kind::XOR) + { + k = Kind::EQUAL; + negCh1 = true; + } + else if (k == Kind::NOT) + { + // double negation should already be eliminated + Assert(cur[0].getKind() != Kind::NOT); + if (cur[0].getKind() == Kind::OR || cur[0].getKind() == Kind::IMPLIES) + { + k = Kind::AND; + negAllCh = true; + negCh1 = cur[0].getKind() == Kind::IMPLIES; + } + else if (cur[0].getKind() == Kind::AND) + { + k = Kind::OR; + negAllCh = true; + } + else if (cur[0].getKind() == Kind::XOR + || (cur[0].getKind() == Kind::EQUAL + && cur[0][0].getType().isBoolean())) + { + k = Kind::EQUAL; + negCh1 = cur[0].getKind() == Kind::EQUAL; + } + else if (cur[0].getKind() == Kind::ITE) + { + k = cur[0].getKind(); + negAllCh = true; + negCh1 = true; + } + else + { + visited[cur] = cur; + continue; + } + ncur = cur[0]; + } + else if ((k != Kind::EQUAL || !cur[0].getType().isBoolean()) + && k != Kind::ITE && k != Kind::AND && k != Kind::OR) + { + // a literal + visited[cur] = cur; + continue; + } + preKind[cur] = k; + visited[cur] = Node::null(); + visit.push_back(cur); + std::vector& pc = preChildren[cur]; + for (size_t i = 0, nchild = ncur.getNumChildren(); i < nchild; ++i) + { + Node c = + (i == 0 && negCh1) != negAllCh ? ncur[i].negate() : Node(ncur[i]); + pc.push_back(c); + visit.push_back(c); + } + // if proof producing, possibly add a pre-rewrite step + if (pg != nullptr) + { + Node preCur = nm->mkNode(k, pc); + if (preCur != cur) + { + pg->addRewriteStep( + cur, preCur, nullptr, true, TrustId::MACRO_BOOL_NNF_NORM_RCONS); + } + } + } + else if (it->second.isNull()) + { + Kind ok = cur.getKind(); + Assert(preKind.find(cur) != preKind.end()); + Kind k = preKind[cur]; + Assert(cur.getMetaKind() != kind::metakind::PARAMETERIZED); + bool childChanged = false; + std::vector children; + std::vector& pc = preChildren[cur]; + std::map lit_pol; + bool success = true; + for (const Node& cn : pc) + { + it = visited.find(cn); + Assert(it != visited.end()); + Assert(!it->second.isNull()); + Node c = it->second; + if (c.getKind() == k && (k == Kind::OR || k == Kind::AND)) + { + // flatten + childChanged = true; + for (const Node& cc : c) + { + if (!addNnfNormChild(children, cc, k, lit_pol, childChanged)) + { + success = false; + break; + } + } + } + else + { + success = addNnfNormChild(children, c, k, lit_pol, childChanged); + } + if (!success) + { + // tautology + break; + } + childChanged = childChanged || c != cn; + } + Node ret = cur; + if (!success) + { + Assert(k == Kind::OR || k == Kind::AND); + ret = nm->mkConst(k == Kind::OR); + } + else if (childChanged || k != ok) + { + ret = (children.size() == 1 && k != Kind::NOT) + ? children[0] + : nm->mkNode(k, children); + } + // if proof producing, possibly add a post-rewrite step + if (pg != nullptr) + { + std::vector pcc; + for (const Node& cn : pc) + { + it = visited.find(cn); + Assert(it != visited.end()); + Assert(!it->second.isNull()); + pcc.push_back(it->second); + } + Node pcpc = nm->mkNode(k, pcc); + if (pcpc != ret) + { + pg->addRewriteStep( + pcpc, ret, nullptr, false, TrustId::MACRO_BOOL_NNF_NORM_RCONS); + } + } + visited[cur] = ret; + } + } while (!visit.empty()); + Assert(visited.find(n) != visited.end()); + Assert(!visited.find(n)->second.isNull()); + return visited[n]; } RewriteResponse TheoryBoolRewriter::postRewrite(TNode node) { diff --git a/src/theory/booleans/theory_bool_rewriter.h b/src/theory/booleans/theory_bool_rewriter.h index e9fbe339a32..c845174e5bd 100644 --- a/src/theory/booleans/theory_bool_rewriter.h +++ b/src/theory/booleans/theory_bool_rewriter.h @@ -24,6 +24,9 @@ #include "theory/theory_rewriter.h" namespace cvc5::internal { + +class TConvProofGenerator; + namespace theory { namespace booleans { @@ -34,7 +37,37 @@ class TheoryBoolRewriter : public TheoryRewriter RewriteResponse preRewrite(TNode node) override; RewriteResponse postRewrite(TNode node) override; + /** + * Rewrite n based on the proof rewrite rule id. + * @param id The rewrite rule. + * @param n The node to rewrite. + * @return The rewritten version of n based on id, or Node::null() if n + * cannot be rewritten. + */ + Node rewriteViaRule(ProofRewriteRule id, const Node& n) override; + /** + * Eliminates IMPLIES/XOR, removes duplicates/infers tautologies of AND/OR, + * and computes NNF. + * + * @param nm Pointer to node manager. + * @param n The node to rewrite. + * @param pg If non-null, this stores rewrite rules that are capable of + * proving that n is equal to its normalized form. + * @return The normalized form of n. + */ + static Node computeNnfNorm(NodeManager* nm, + const Node& n, + TConvProofGenerator* pg = nullptr); + protected: + /** + * Helper method for computeNnfNorm. + */ + static bool addNnfNormChild(std::vector& children, + Node c, + Kind k, + std::map& lit_pol, + bool& childrenChanged); /** * Helper method which performs flattening. * diff --git a/src/theory/builtin/generic_op.h b/src/theory/builtin/generic_op.h index b68f153bd8c..bd596aca32a 100644 --- a/src/theory/builtin/generic_op.h +++ b/src/theory/builtin/generic_op.h @@ -42,6 +42,8 @@ class GenericOp /** Is k a kind that is an indexed operator? */ static bool isIndexedOperatorKind(Kind k); + /** Is k a kind that is an indexed operator? */ + static bool isNumeralIndexedOperatorKind(Kind k); /** * Return the list of nodes corresponding to the indices of n, which is * an operator for an application of kind k. @@ -62,8 +64,6 @@ class GenericOp GenericOp(); /** The kind of indexed operator this operator represents */ Kind d_kind; - /** Is k a kind that is an indexed operator? */ - static bool isNumeralIndexedOperatorKind(Kind k); }; std::ostream& operator<<(std::ostream& out, const GenericOp& op); diff --git a/src/theory/builtin/proof_checker.cpp b/src/theory/builtin/proof_checker.cpp index d52e0efa321..d1f72b93ad1 100644 --- a/src/theory/builtin/proof_checker.cpp +++ b/src/theory/builtin/proof_checker.cpp @@ -477,12 +477,16 @@ Node BuiltinProofRuleChecker::checkInternal(ProofRule id, { return Node::null(); } - Node rhs = d_rewriter->rewriteViaRule(di, args[1]); - if (rhs.isNull()) + if (args[1].getKind() != Kind::EQUAL) { return Node::null(); } - return args[1].eqNode(rhs); + Node rhs = d_rewriter->rewriteViaRule(di, args[1][0]); + if (rhs.isNull() || rhs != args[1][1]) + { + return Node::null(); + } + return args[1]; } // no rule return Node::null(); diff --git a/src/theory/datatypes/datatypes_rewriter.cpp b/src/theory/datatypes/datatypes_rewriter.cpp index 6291ccc97fa..072f17a859f 100644 --- a/src/theory/datatypes/datatypes_rewriter.cpp +++ b/src/theory/datatypes/datatypes_rewriter.cpp @@ -41,10 +41,14 @@ DatatypesRewriter::DatatypesRewriter(NodeManager* nm, const Options& opts) : TheoryRewriter(nm), d_sygusEval(sygusEval), d_opts(opts) { + registerProofRewriteRule(ProofRewriteRule::DT_INST, + TheoryRewriteCtx::PRE_DSL); registerProofRewriteRule(ProofRewriteRule::DT_COLLAPSE_SELECTOR, TheoryRewriteCtx::PRE_DSL); registerProofRewriteRule(ProofRewriteRule::DT_COLLAPSE_TESTER, TheoryRewriteCtx::PRE_DSL); + registerProofRewriteRule(ProofRewriteRule::DT_COLLAPSE_TESTER_SINGLETON, + TheoryRewriteCtx::PRE_DSL); registerProofRewriteRule(ProofRewriteRule::DT_CONS_EQ, TheoryRewriteCtx::PRE_DSL); } @@ -53,6 +57,21 @@ Node DatatypesRewriter::rewriteViaRule(ProofRewriteRule id, const Node& n) { switch (id) { + case ProofRewriteRule::DT_INST: + { + if (n.getKind() != Kind::APPLY_TESTER) + { + return Node::null(); + } + Node t = n[0]; + TypeNode tn = t.getType(); + Assert(tn.isDatatype()); + const DType& dt = tn.getDType(); + size_t i = utils::indexOf(n.getOperator()); + bool sharedSel = d_opts.datatypes.dtSharedSelectors; + Node ticons = utils::getInstCons(t, dt, i, sharedSel); + return t.eqNode(ticons); + } case ProofRewriteRule::DT_COLLAPSE_SELECTOR: { if (n.getKind() != Kind::APPLY_SELECTOR @@ -85,6 +104,20 @@ Node DatatypesRewriter::rewriteViaRule(ProofRewriteRule id, const Node& n) return nm->mkConst(result); } break; + case ProofRewriteRule::DT_COLLAPSE_TESTER_SINGLETON: + { + if (n.getKind() != Kind::APPLY_TESTER) + { + return Node::null(); + } + const DType& dt = n[0].getType().getDType(); + if (dt.getNumConstructors() == 1) + { + NodeManager* nm = nodeManager(); + return nm->mkConst(true); + } + } + break; case ProofRewriteRule::DT_CONS_EQ: { if (n.getKind() == Kind::EQUAL) diff --git a/src/theory/datatypes/infer_proof_cons.cpp b/src/theory/datatypes/infer_proof_cons.cpp index 4faa8530f66..6d66ae02078 100644 --- a/src/theory/datatypes/infer_proof_cons.cpp +++ b/src/theory/datatypes/infer_proof_cons.cpp @@ -146,7 +146,7 @@ void InferProofCons::convert(InferenceId infer, TNode conc, TNode exp, CDProof* Node t = exp[0]; Node nn = nm->mkConstInt(Rational(n)); Node eq = exp.eqNode(conc); - cdp->addStep(eq, ProofRule::DT_INST, {}, {t, nn}); + cdp->addTheoryRewriteStep(eq, ProofRewriteRule::DT_INST); cdp->addStep(conc, ProofRule::EQ_RESOLVE, {exp, eq}, {}); success = true; } diff --git a/src/theory/datatypes/proof_checker.cpp b/src/theory/datatypes/proof_checker.cpp index fe93f8e986a..58a3a4c41a7 100644 --- a/src/theory/datatypes/proof_checker.cpp +++ b/src/theory/datatypes/proof_checker.cpp @@ -32,7 +32,6 @@ DatatypesProofRuleChecker::DatatypesProofRuleChecker(NodeManager* nm, void DatatypesProofRuleChecker::registerTo(ProofChecker* pc) { pc->registerChecker(ProofRule::DT_UNIF, this); - pc->registerChecker(ProofRule::DT_INST, this); pc->registerChecker(ProofRule::DT_SPLIT, this); pc->registerChecker(ProofRule::DT_CLASH, this); } @@ -62,26 +61,6 @@ Node DatatypesProofRuleChecker::checkInternal(ProofRule id, Assert(children[0][0].getNumChildren() == children[0][1].getNumChildren()); return children[0][0][i].eqNode(children[0][1][i]); } - else if (id == ProofRule::DT_INST) - { - Assert(children.empty()); - Assert(args.size() == 2); - Node t = args[0]; - TypeNode tn = t.getType(); - uint32_t i; - if (!tn.isDatatype() || !getUInt32(args[1], i)) - { - return Node::null(); - } - const DType& dt = tn.getDType(); - if (i >= dt.getNumConstructors()) - { - return Node::null(); - } - Node tester = utils::mkTester(t, i, dt); - Node ticons = utils::getInstCons(t, dt, i, d_sharedSel); - return tester.eqNode(t.eqNode(ticons)); - } else if (id == ProofRule::DT_SPLIT) { Assert(children.empty()); diff --git a/src/theory/datatypes/sygus_datatype_utils.cpp b/src/theory/datatypes/sygus_datatype_utils.cpp index cc5bd8c7ab1..7daaf044e9d 100644 --- a/src/theory/datatypes/sygus_datatype_utils.cpp +++ b/src/theory/datatypes/sygus_datatype_utils.cpp @@ -354,7 +354,16 @@ Node builtinVarToSygus(Node v) return Node::null(); } -void getFreeSymbolsSygusType(TypeNode sdt, std::unordered_set& syms) +/** + * Get free symbols or variables in a sygus datatype type. + * @param sdt The sygus datatype. + * @param sym The symbols to add to. + * @param isVar If we are looking for variables (if not, we are looking for + * symbols). + */ +void getFreeSymbolsSygusTypeInternal(TypeNode sdt, + std::unordered_set& syms, + bool isVar) { // datatype types we need to process std::vector typeToProcess; @@ -372,7 +381,14 @@ void getFreeSymbolsSygusType(TypeNode sdt, std::unordered_set& syms) { // collect the symbols from the operator Node op = dtc[j].getSygusOp(); - expr::getSymbols(op, syms); + if (isVar) + { + expr::getVariables(op, syms); + } + else + { + expr::getSymbols(op, syms); + } // traverse the argument types for (unsigned k = 0, nargs = dtc[j].getNumArgs(); k < nargs; k++) { @@ -397,6 +413,16 @@ void getFreeSymbolsSygusType(TypeNode sdt, std::unordered_set& syms) } } +void getFreeSymbolsSygusType(TypeNode sdt, std::unordered_set& syms) +{ + getFreeSymbolsSygusTypeInternal(sdt, syms, false); +} + +void getFreeVariablesSygusType(TypeNode sdt, std::unordered_set& syms) +{ + getFreeSymbolsSygusTypeInternal(sdt, syms, true); +} + TypeNode substituteAndGeneralizeSygusType(TypeNode sdt, const std::vector& syms, const std::vector& vars) diff --git a/src/theory/datatypes/sygus_datatype_utils.h b/src/theory/datatypes/sygus_datatype_utils.h index f4531e03b03..7b5319f81fa 100644 --- a/src/theory/datatypes/sygus_datatype_utils.h +++ b/src/theory/datatypes/sygus_datatype_utils.h @@ -181,6 +181,16 @@ Node builtinVarToSygus(Node v); */ void getFreeSymbolsSygusType(TypeNode sdt, std::unordered_set& syms); +/** + * Get variables in a sygus datatype type, analogous to the above but for + * variables. + * + * Add the variables (expr::getVariables) in terms that can be generated by + * sygus datatype sdt to the set syms. Notice that expr::getVariables + * only includes variables whose kind is BOUND_VARIABLE. + */ +void getFreeVariablesSygusType(TypeNode sdt, std::unordered_set& syms); + /** Substitute and generalize a sygus datatype type * * This transforms a sygus datatype sdt into another one sdt' that generates diff --git a/src/theory/evaluator.cpp b/src/theory/evaluator.cpp index ccd21e2d0f8..359a3beb6d3 100644 --- a/src/theory/evaluator.cpp +++ b/src/theory/evaluator.cpp @@ -856,7 +856,52 @@ EvalResult Evaluator::evalInternal( } break; } - + case Kind::STRING_REV: + { + const String& s = results[currNode[0]].d_str; + std::vector nvec = s.getVec(); + std::reverse(nvec.begin(), nvec.end()); + results[currNode] = EvalResult(String(nvec)); + break; + } + case Kind::STRING_TO_LOWER: + case Kind::STRING_TO_UPPER: + { + const String& s = results[currNode[0]].d_str; + std::vector nvec = s.getVec(); + Kind k = currNodeVal.getKind(); + for (unsigned i = 0, nvsize = nvec.size(); i < nvsize; i++) + { + unsigned newChar = nvec[i]; + // transform it + // upper 65 ... 90 + // lower 97 ... 122 + if (k == Kind::STRING_TO_UPPER) + { + if (newChar >= 97 && newChar <= 122) + { + newChar = newChar - 32; + } + } + else if (k == Kind::STRING_TO_LOWER) + { + if (newChar >= 65 && newChar <= 90) + { + newChar = newChar + 32; + } + } + nvec[i] = newChar; + } + results[currNode] = EvalResult(String(nvec)); + break; + } + case Kind::STRING_LEQ: + { + const String& s1 = results[currNode[0]].d_str; + const String& s2 = results[currNode[1]].d_str; + results[currNode] = EvalResult(s1.isLeq(s2)); + break; + } case Kind::CONST_BITVECTOR: results[currNode] = EvalResult(currNodeVal.getConst()); break; diff --git a/src/theory/quantifiers/inst_strategy_mbqi.cpp b/src/theory/quantifiers/inst_strategy_mbqi.cpp index 446ce1e9881..4e95a873675 100644 --- a/src/theory/quantifiers/inst_strategy_mbqi.cpp +++ b/src/theory/quantifiers/inst_strategy_mbqi.cpp @@ -18,6 +18,7 @@ #include "expr/node_algorithm.h" #include "expr/skolem_manager.h" #include "expr/subs.h" +#include "theory/quantifiers/mbqi_fast_sygus.h" #include "theory/quantifiers/first_order_model.h" #include "theory/quantifiers/instantiate.h" #include "theory/quantifiers/quantifiers_rewriter.h" @@ -45,6 +46,11 @@ InstStrategyMbqi::InstStrategyMbqi(Env& env, d_nonClosedKinds.insert(Kind::STORE_ALL); d_nonClosedKinds.insert(Kind::CODATATYPE_BOUND_VARIABLE); d_nonClosedKinds.insert(Kind::UNINTERPRETED_SORT_VALUE); + + if (options().quantifiers.mbqiFastSygus) + { + d_msenum.reset(new MbqiFastSygus(env, *this)); + } } void InstStrategyMbqi::reset_round(Theory::Effort e) { d_quantChecked.clear(); } @@ -237,7 +243,8 @@ void InstStrategyMbqi::process(Node q) // get the model values for skolems std::vector terms; - getModelFromSubsolver(*mbqiChecker.get(), skolems.d_subs, terms); + modelValueFromQuery( + q, query, *mbqiChecker.get(), skolems.d_subs, terms, mvToFreshVar); Assert(skolems.size() == terms.size()); if (TraceIsOn("mbqi")) { @@ -442,6 +449,26 @@ Node InstStrategyMbqi::convertToQuery( return cmap[cur]; } + +void InstStrategyMbqi::modelValueFromQuery( + const Node& q, + const Node& query, + SolverEngine& smt, + const std::vector& vars, + std::vector& mvs, + const std::map& mvToFreshVar) +{ + getModelFromSubsolver(smt, vars, mvs); + if (options().quantifiers.mbqiFastSygus) + { + std::vector smvs(mvs); + if (d_msenum->constructInstantiation(q, query, vars, smvs, mvToFreshVar)) + { + mvs = smvs; + } + } +} + Node InstStrategyMbqi::convertFromModel( Node t, std::unordered_map& cmap, diff --git a/src/theory/quantifiers/inst_strategy_mbqi.h b/src/theory/quantifiers/inst_strategy_mbqi.h index a96181fda51..b46823651a8 100644 --- a/src/theory/quantifiers/inst_strategy_mbqi.h +++ b/src/theory/quantifiers/inst_strategy_mbqi.h @@ -24,9 +24,14 @@ #include "theory/quantifiers/quant_module.h" namespace cvc5::internal { + +class SolverEngine; + namespace theory { namespace quantifiers { +class MbqiFastSygus; + /** * InstStrategyMbqi * @@ -40,6 +45,7 @@ namespace quantifiers { */ class InstStrategyMbqi : public QuantifiersModule { + friend class MbqiFastSygus; public: InstStrategyMbqi(Env& env, QuantifiersState& qs, @@ -103,10 +109,29 @@ class InstStrategyMbqi : public QuantifiersModule * which can lead to logic exceptions in subsolvers. */ Node mkMbqiSkolem(const Node& t); + /** + * Return the model value for term t from the solver, possibly post-processing + * it with modules maintained by this class (e.g. d_msenum). + * @param q The quantified formula we are instantiating. + * @param query The query used to find the model-based instantiation. + * @param smt The subsolver the query was made on. + * @param vars The variables we are instantiating. + * @param mvs The model values found for vars by the subsolver. This vector + * may be modified based on modules maintained by this class. + * @param mvToFreshVar Used for representing values for uninterpreted sorts. + */ + void modelValueFromQuery(const Node& q, + const Node& query, + SolverEngine& smt, + const std::vector& vars, + std::vector& mvs, + const std::map& mvToFreshVar); /** The quantified formulas that we succeeded in checking */ std::unordered_set d_quantChecked; /** Kinds that cannot appear in queries */ std::unordered_set d_nonClosedKinds; + /** Submodule for sygus enum */ + std::unique_ptr d_msenum; }; } // namespace quantifiers diff --git a/src/theory/quantifiers/mbqi_fast_sygus.cpp b/src/theory/quantifiers/mbqi_fast_sygus.cpp new file mode 100644 index 00000000000..7007cc5308a --- /dev/null +++ b/src/theory/quantifiers/mbqi_fast_sygus.cpp @@ -0,0 +1,313 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2024 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. + * **************************************************************************** + * + * A class for augmenting model-based instantiations via fast sygus enumeration. + */ + +#include "theory/quantifiers/mbqi_fast_sygus.h" + +#include "expr/node_algorithm.h" +#include "expr/skolem_manager.h" +#include "printer/smt2/smt2_printer.h" +#include "theory/datatypes/sygus_datatype_utils.h" +#include "theory/quantifiers/inst_strategy_mbqi.h" +#include "theory/quantifiers/sygus/sygus_enumerator.h" +#include "theory/quantifiers/sygus/sygus_grammar_cons.h" +#include "theory/smt_engine_subsolver.h" +#include "util/random.h" + +namespace cvc5::internal { +namespace theory { +namespace quantifiers { + +void MVarInfo::initialize(Env& env, + const Node& q, + const Node& v, + const std::vector& etrules) +{ + NodeManager* nm = NodeManager::currentNM(); + TypeNode tn = v.getType(); + Assert(MQuantInfo::shouldEnumerate(tn)); + TypeNode retType = tn; + std::vector trules; + if (tn.isFunction()) + { + std::vector argTypes = tn.getArgTypes(); + retType = tn.getRangeType(); + std::vector vs; + for (const TypeNode& tnc : argTypes) + { + Node vc = nm->mkBoundVar(tnc); + vs.push_back(vc); + } + d_lamVars = nm->mkNode(Kind::BOUND_VAR_LIST, vs); + trules.insert(trules.end(), vs.begin(), vs.end()); + } + // include free symbols from body of quantified formula if applicable + std::unordered_set syms; + expr::getSymbols(q[1], syms); + trules.insert(trules.end(), syms.begin(), syms.end()); + // include the external terminal rules + for (const Node& symbol : etrules) + { + if (std::find(trules.begin(), trules.end(), symbol) == trules.end()) + { + trules.push_back(symbol); + } + } + SygusGrammarCons sgc; + Node bvl; + TypeNode tng = sgc.mkDefaultSygusType(env, retType, bvl, trules); + if (TraceIsOn("mbqi-model-enum")) + { + Trace("mbqi-model-enum") << "Enumerate terms for " << retType; + if (!d_lamVars.isNull()) + { + Trace("mbqi-model-enum") << ", variable list " << d_lamVars; + } + Trace("mbqi-model-enum") << std::endl; + Trace("mbqi-model-enum") << "Based on grammar:" << std::endl; + Trace("mbqi-model-enum") + << printer::smt2::Smt2Printer::sygusGrammarString(tng) << std::endl; + } + d_senum.reset(new SygusTermEnumerator(env, tng)); +} + +Node MVarInfo::getEnumeratedTerm(size_t i) +{ + NodeManager* nm = NodeManager::currentNM(); + size_t nullCount = 0; + while (i >= d_enum.size()) + { + Node curr = d_senum->getCurrent(); + Trace("mbqi-sygus-enum") << "Enumerate: " << curr << std::endl; + if (!curr.isNull()) + { + if (!d_lamVars.isNull()) + { + curr = nm->mkNode(Kind::LAMBDA, d_lamVars, curr); + } + d_enum.push_back(curr); + nullCount = 0; + } + else + { + nullCount++; + if (nullCount > 100) + { + // break if we aren't making progress + break; + } + } + if (!d_senum->incrementPartial()) + { + // enumeration is finished + break; + } + } + if (i >= d_enum.size()) + { + return Node::null(); + } + return d_enum[i]; +} + +void MQuantInfo::initialize(Env& env, InstStrategyMbqi& parent, const Node& q) +{ + // The externally provided terminal rules. This set is shared between + // all variables we instantiate. + std::vector etrules; + for (const Node& v : q[0]) + { + size_t index = d_vinfo.size(); + d_vinfo.emplace_back(); + TypeNode vtn = v.getType(); + // if enumerated, add to list + if (shouldEnumerate(vtn)) + { + d_indices.push_back(index); + } + else + { + d_nindices.push_back(index); + // Include variables defined in terms of others we are not enumerating. + etrules.push_back(v); + } + } + // Get free symbols from body of quantified formula here + std::unordered_set syms; + expr::getSymbols(q[1], syms); + for (const Node& symbol : syms) + { + if (std::find(etrules.begin(), etrules.end(), symbol) == etrules.end()) + { + etrules.push_back(symbol); + } + } + Trace("mbqi-model-enum") << "Terminals: " << etrules << std::endl; + // initialize the variables we are instantiating + for (size_t index : d_indices) + { + d_vinfo[index].initialize(env, q, q[0][index], etrules); + } +} + +MVarInfo& MQuantInfo::getVarInfo(size_t index) +{ + Assert(index < d_vinfo.size()); + return d_vinfo[index]; +} + +std::vector MQuantInfo::getInstIndices() { return d_indices; } +std::vector MQuantInfo::getNoInstIndices() { return d_nindices; } + +bool MQuantInfo::shouldEnumerate(const TypeNode& tn) +{ + if (tn.isUninterpretedSort()) + { + return false; + } + return true; +} + +MbqiFastSygus::MbqiFastSygus(Env& env, InstStrategyMbqi& parent) + : EnvObj(env), d_parent(parent) +{ +} + +MQuantInfo& MbqiFastSygus::getOrMkQuantInfo(const Node& q) +{ + auto [it, inserted] = d_qinfo.try_emplace(q); + if (inserted) + { + it->second.initialize(d_env, d_parent, q); + } + return it->second; +} + +bool MbqiFastSygus::constructInstantiation( + const Node& q, + const Node& query, + const std::vector& vars, + std::vector& mvs, + const std::map& mvFreshVar) +{ + Assert(q[0].getNumChildren() == vars.size()); + Assert(vars.size() == mvs.size()); + if (TraceIsOn("mbqi-model-enum")) + { + Trace("mbqi-model-enum") << "Instantiate " << q << std::endl; + for (size_t i = 0, nvars = vars.size(); i < nvars; i++) + { + Trace("mbqi-model-enum") + << " " << q[0][i] << " -> " << mvs[i] << std::endl; + } + } + MQuantInfo& qi = getOrMkQuantInfo(q); + std::vector indices = qi.getInstIndices(); + std::vector nindices = qi.getNoInstIndices(); + Subs inst; + Subs vinst; + std::unordered_map tmpCMap; + for (size_t i : nindices) + { + Node v = mvs[i]; + v = d_parent.convertFromModel(v, tmpCMap, mvFreshVar); + if (v.isNull()) + { + return false; + } + Trace("mbqi-model-enum") + << "* Assume: " << q[0][i] << " -> " << v << std::endl; + // if we don't enumerate it, we are already considering this instantiation + inst.add(vars[i], v); + vinst.add(q[0][i], v); + } + Node queryCurr = query; + Trace("mbqi-model-enum") << "...query is " << queryCurr << std::endl; + queryCurr = rewrite(inst.apply(queryCurr)); + Trace("mbqi-model-enum") << "...processed is " << queryCurr << std::endl; + // consider variables in random order, for diversity of instantiations + std::shuffle(indices.begin(), indices.end(), Random::getRandom()); + for (size_t i = 0, isize = indices.size(); i < isize; i++) + { + size_t ii = indices[i]; + TNode v = vars[ii]; + MVarInfo& vi = qi.getVarInfo(ii); + size_t cindex = 0; + bool success = false; + bool successEnum; + do + { + Node ret = vi.getEnumeratedTerm(cindex); + cindex++; + Node retc; + if (!ret.isNull()) + { + Trace("mbqi-model-enum") << "- Try candidate: " << ret << std::endl; + // apply current substitution (to account for cases where ret has + // other variables in its grammar). + ret = vinst.apply(ret); + retc = ret; + successEnum = true; + // now convert the value + std::unordered_map tmpConvertMap; + std::map > freshVarType; + retc = d_parent.convertToQuery(retc, tmpConvertMap, freshVarType); + } + else + { + Trace("mbqi-model-enum") + << "- Failed to enumerate candidate" << std::endl; + // if we failed to enumerate, just try the original + Node mc = d_parent.convertFromModel(mvs[ii], tmpCMap, mvFreshVar); + if (mc.isNull()) + { + // if failed to convert, we fail + return false; + } + ret = mc; + retc = mc; + successEnum = false; + } + Trace("mbqi-model-enum") + << "- Converted candidate: " << v << " -> " << retc << std::endl; + // see if it is still satisfiable, if still SAT, we replace + Node queryCheck = queryCurr.substitute(v, TNode(retc)); + queryCheck = rewrite(queryCheck); + Trace("mbqi-model-enum") << "...check " << queryCheck << std::endl; + SubsolverSetupInfo ssi(d_env); + Result r = checkWithSubsolver(queryCheck, ssi); + if (r == Result::SAT) + { + // remember the updated query + queryCurr = queryCheck; + Trace("mbqi-model-enum") << "...success" << std::endl; + Trace("mbqi-model-enum") + << "* Enumerated " << q[0][ii] << " -> " << ret << std::endl; + mvs[ii] = ret; + vinst.add(q[0][ii], ret); + success = true; + } + else if (!successEnum) + { + // we did not enumerate a candidate, and tried the original, which + // failed. + return false; + } + } while (!success); + } + return true; +} +} // namespace quantifiers +} // namespace theory +} // namespace cvc5::internal diff --git a/src/theory/quantifiers/mbqi_fast_sygus.h b/src/theory/quantifiers/mbqi_fast_sygus.h new file mode 100644 index 00000000000..7bafd769952 --- /dev/null +++ b/src/theory/quantifiers/mbqi_fast_sygus.h @@ -0,0 +1,161 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2023 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. + * **************************************************************************** + * + * A class for augmenting model-based instantiations via fast sygus enumeration. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__THEORY__QUANTIFIERS__MBQI_FAST_SYGUS_H +#define CVC5__THEORY__QUANTIFIERS__MBQI_FAST_SYGUS_H + +#include +#include + +#include "expr/sygus_term_enumerator.h" +#include "smt/env_obj.h" +#include "theory/quantifiers/sygus/sygus_enumerator.h" + +namespace cvc5::internal { +namespace theory { +namespace quantifiers { + +class InstStrategyMbqi; + +/** + * Maintains a sygus enumeration for a single quantified variable in our + * strategy. + */ +class MVarInfo +{ + public: + /** + * Initialize this class for variable v of quantified formula q. + * + * @param env Reference to the environment. + * @param q The quantified formula. + * @param v The variable from q we are enumerating terms for. + * @param etrules A list of terms which to consider terminals in the grammar + * we enumerate. These terms may be of any sort. + */ + void initialize(Env& env, + const Node& q, + const Node& v, + const std::vector& etrules); + /** + * Get the i^th term in the enumeration maintained by this class. Will + * continue the sygus enumeration if i is greater than the number of terms + * enumerated so far. + */ + Node getEnumeratedTerm(size_t i); + + private: + /** The underlying sygus enumerator utility */ + std::unique_ptr d_senum; + /** A cache of all enumerated terms so far */ + std::vector d_enum; + /** + * If we are enumerating function values, this is a BOUND_VAR_LIST node. + * The terms we enumerate are t_1, ..., which are transformed to + * (lambda t_1) ... for this variable list. + */ + Node d_lamVars; +}; + +/** + * Maintains information about a quantified formula in our strategy, including + * which variables are processed/unprocessed, and the sygus enumeration for + * each of them (in MVarInfo). + */ +class MQuantInfo +{ + public: + /** + * Intialize the instantiation strategy for quantified formula q. + * + * @param env Reference to the environment. + * @param parent Reference to the parent instantiation strategy. + * @param q The quantified formula. + */ + void initialize(Env& env, InstStrategyMbqi& parent, const Node& q); + /** Get indices of variables to instantiate */ + std::vector getInstIndices(); + /** Get indices of variables not to instantiate */ + std::vector getNoInstIndices(); + /** Get variable info for the index^th variable of the quantified formula */ + MVarInfo& getVarInfo(size_t index); + /** Should we enumerate terms for type tn? */ + static bool shouldEnumerate(const TypeNode& tn); + + private: + /** The quantified formula */ + Node d_quant; + /** A list of variable informations for each of the variables of q */ + std::vector d_vinfo; + /** The indices of variables we are enumerating */ + std::vector d_indices; + /** The indices of variables we are not enumerating */ + std::vector d_nindices; +}; + +/** + * MbqiFastSygus, which postprocesses an instantiation from MBQI based on + * sygus enumeration. + */ +class MbqiFastSygus : protected EnvObj +{ + public: + MbqiFastSygus(Env& env, InstStrategyMbqi& parent); + ~MbqiFastSygus() {} + + /** + * Updates mvs to the desired instantiation of q. Returns true if successful. + * + * In detail, this method maintains the invariant that + * query[ mvs / vars ] is satisfiable. + * This is initially guaranteed since mvs is a model for vars in query + * due to MBQI. This method iterates over the variables vars[i] and replaces + * mvs[i] with the first term in the SyGuS enumeration such that the updated + * mvs still satisfies the query. Checking whether the invariant holds is + * confirmed via a subsolver call for each replacement. + * + * @param q The quantified formula to instantiate. + * @param query The query that was made to a subsolver for MBQI. + * @param vars The input variables that were used in the query, which + * correspond 1-to-1 with the variables of the quantified formula. + * @param mvs The model values of vars found in the subsolver for MBQI. + * @param mvFreshVar Maps model values to variables, for the purposes + * of representing term models for uninterpreted sorts. + * @return true if we successfully modified the instantiation. + */ + bool constructInstantiation(const Node& q, + const Node& query, + const std::vector& vars, + std::vector& mvs, + const std::map& mvFreshVar); + + private: + /** + * @return The information for quantified formula q. + */ + MQuantInfo& getOrMkQuantInfo(const Node& q); + /** Map from quantified formulas to information above */ + std::map d_qinfo; + /** Reference to the parent instantiation strategy */ + InstStrategyMbqi& d_parent; +}; + +} // namespace quantifiers +} // namespace theory +} // namespace cvc5::internal + +#endif /* CVC5__THEORY__QUANTIFIERS__MBQI_FAST_SYGUS_H */ diff --git a/src/theory/quantifiers/quantifiers_modules.h b/src/theory/quantifiers/quantifiers_modules.h index 95b580487c0..e7fb5e17b8c 100644 --- a/src/theory/quantifiers/quantifiers_modules.h +++ b/src/theory/quantifiers/quantifiers_modules.h @@ -29,6 +29,7 @@ #include "theory/quantifiers/inst_strategy_mbqi.h" #include "theory/quantifiers/inst_strategy_pool.h" #include "theory/quantifiers/inst_strategy_sub_conflict.h" +#include "theory/quantifiers/mbqi_fast_sygus.h" #include "theory/quantifiers/oracle_engine.h" #include "theory/quantifiers/quant_conflict_find.h" #include "theory/quantifiers/quant_split.h" diff --git a/src/theory/quantifiers/quantifiers_rewriter.cpp b/src/theory/quantifiers/quantifiers_rewriter.cpp index 4ec3f96d408..4c39eb5c8ae 100644 --- a/src/theory/quantifiers/quantifiers_rewriter.cpp +++ b/src/theory/quantifiers/quantifiers_rewriter.cpp @@ -24,6 +24,7 @@ #include "expr/skolem_manager.h" #include "options/quantifiers_options.h" #include "theory/arith/arith_msum.h" +#include "theory/booleans/theory_bool_rewriter.h" #include "theory/datatypes/theory_datatypes_utils.h" #include "theory/quantifiers/bv_inverter.h" #include "theory/quantifiers/ematching/trigger.h" @@ -99,6 +100,14 @@ QuantifiersRewriter::QuantifiersRewriter(NodeManager* nm, { registerProofRewriteRule(ProofRewriteRule::EXISTS_ELIM, TheoryRewriteCtx::PRE_DSL); + registerProofRewriteRule(ProofRewriteRule::QUANT_UNUSED_VARS, + TheoryRewriteCtx::PRE_DSL); + registerProofRewriteRule(ProofRewriteRule::QUANT_MERGE_PRENEX, + TheoryRewriteCtx::PRE_DSL); + registerProofRewriteRule(ProofRewriteRule::QUANT_MINISCOPE, + TheoryRewriteCtx::PRE_DSL); + registerProofRewriteRule(ProofRewriteRule::MACRO_QUANT_PARTITION_CONNECTED_FV, + TheoryRewriteCtx::PRE_DSL); } Node QuantifiersRewriter::rewriteViaRule(ProofRewriteRule id, const Node& n) @@ -120,6 +129,74 @@ Node QuantifiersRewriter::rewriteViaRule(ProofRewriteRule id, const Node& n) } return d_nm->mkNode(Kind::NOT, d_nm->mkNode(Kind::FORALL, fchildren)); } + case ProofRewriteRule::QUANT_UNUSED_VARS: + { + if (!n.isClosure()) + { + return Node::null(); + } + std::vector vars(n[0].begin(), n[0].end()); + std::vector activeVars; + computeArgVec(vars, activeVars, n[1]); + if (activeVars.size() < vars.size()) + { + if (activeVars.empty()) + { + return n[1]; + } + return d_nm->mkNode( + n.getKind(), d_nm->mkNode(Kind::BOUND_VAR_LIST, activeVars), n[1]); + } + } + break; + case ProofRewriteRule::QUANT_MERGE_PRENEX: + { + if (!n.isClosure()) + { + return Node::null(); + } + // Don't check standard here, which can't be replicated in a proof checker + // without modelling the patterns. + Node q = mergePrenex(n, false); + if (q != n) + { + return q; + } + } + break; + case ProofRewriteRule::QUANT_MINISCOPE: + { + if (n.getKind() != Kind::FORALL || n[1].getKind() != Kind::AND) + { + return Node::null(); + } + // note that qa is not needed; moreover external proofs should be agnostic + // to it + QAttributes qa; + QuantAttributes::computeQuantAttributes(n, qa); + Node nret = computeMiniscoping(n, qa, true, false); + Assert(nret != n); + return nret; + } + break; + case ProofRewriteRule::MACRO_QUANT_PARTITION_CONNECTED_FV: + { + if (n.getKind() != Kind::FORALL || n[1].getKind() != Kind::OR) + { + return Node::null(); + } + // note that qa is not needed; moreover external proofs should be agnostic + // to it + QAttributes qa; + QuantAttributes::computeQuantAttributes(n, qa); + std::vector vars(n[0].begin(), n[0].end()); + Node body = n[1]; + Node nret = computeSplit(vars, body, qa); + if (nret != n) + { + return nret; + } + } break; default: break; } @@ -241,7 +318,7 @@ RewriteResponse QuantifiersRewriter::preRewrite(TNode q) // eagerly here, where after we would drop y to obtain: // (forall ((x Int)) (! (P x) :pattern ((f x)))) // See issue #10303. - Node qm = mergePrenex(q); + Node qm = mergePrenex(q, true); if (q != qm) { return RewriteResponse(REWRITE_AGAIN_FULL, qm); @@ -273,7 +350,7 @@ RewriteResponse QuantifiersRewriter::postRewrite(TNode in) else if (in.getKind() == Kind::FORALL) { // do prenex merging - ret = mergePrenex(in); + ret = mergePrenex(in, true); if (ret != in) { status = REWRITE_AGAIN_FULL; @@ -310,7 +387,7 @@ RewriteResponse QuantifiersRewriter::postRewrite(TNode in) return RewriteResponse( status, ret ); } -Node QuantifiersRewriter::mergePrenex(const Node& q) +Node QuantifiersRewriter::mergePrenex(const Node& q, bool checkStd) { Assert(q.getKind() == Kind::FORALL || q.getKind() == Kind::EXISTS); Kind k = q.getKind(); @@ -335,17 +412,22 @@ Node QuantifiersRewriter::mergePrenex(const Node& q) continueCombine = false; if (body.getNumChildren() == 2 && body[1].getKind() == k) { - QAttributes qa; - QuantAttributes::computeQuantAttributes(body[1], qa); - // Should never combine a quantified formula with a pool or - // non-standard quantified formula here. - // Note that we technically should check - // doOperation(body[1], COMPUTE_PRENEX, qa) here, although this - // is too restrictive, as sometimes nested patterns should just be - // applied to the top level, for example: - // (forall ((x Int)) (forall ((y Int)) (! P :pattern ((f x y))))) - // should be a pattern for the top-level quantifier here. - if (qa.isStandard() && !qa.d_hasPool) + bool process = true; + if (checkStd) + { + // Should never combine a quantified formula with a pool or + // non-standard quantified formula here. + // Note that we technically should check + // doOperation(body[1], COMPUTE_PRENEX, qa) here, although this + // is too restrictive, as sometimes nested patterns should just be + // applied to the top level, for example: + // (forall ((x Int)) (forall ((y Int)) (! P :pattern ((f x y))))) + // should be a pattern for the top-level quantifier here. + QAttributes qa; + QuantAttributes::computeQuantAttributes(body[1], qa); + process = qa.isStandard(); + } + if (process) { body = body[1]; continueCombine = true; @@ -368,183 +450,6 @@ Node QuantifiersRewriter::mergePrenex(const Node& q) return q; } -bool QuantifiersRewriter::addCheckElimChild(std::vector& children, - Node c, - Kind k, - std::map& lit_pol, - bool& childrenChanged) const -{ - if ((k == Kind::OR || k == Kind::AND) && d_opts.quantifiers.elimTautQuant) - { - Node lit = c.getKind() == Kind::NOT ? c[0] : c; - bool pol = c.getKind() != Kind::NOT; - std::map< Node, bool >::iterator it = lit_pol.find( lit ); - if( it==lit_pol.end() ){ - lit_pol[lit] = pol; - children.push_back( c ); - }else{ - childrenChanged = true; - if( it->second!=pol ){ - return false; - } - } - } - else - { - children.push_back( c ); - } - return true; -} - -Node QuantifiersRewriter::computeElimSymbols(Node body) const -{ - // at pre-order traversal, we store preKind and preChildren, which - // determine the Kind and the children for the node to reconstruct. - std::unordered_map preKind; - std::unordered_map> preChildren; - NodeManager* nm = nodeManager(); - std::unordered_map visited; - std::unordered_map::iterator it; - std::vector visit; - TNode cur; - visit.push_back(body); - do - { - cur = visit.back(); - visit.pop_back(); - it = visited.find(cur); - if (it == visited.end()) - { - Kind k = cur.getKind(); - bool negAllCh = false; - bool negCh1 = false; - // the new formula we should traverse - TNode ncur = cur; - if (k == Kind::IMPLIES) - { - k = Kind::OR; - negCh1 = true; - } - else if (k == Kind::XOR) - { - k = Kind::EQUAL; - negCh1 = true; - } - else if (k == Kind::NOT) - { - // double negation should already be eliminated - Assert(cur[0].getKind() != Kind::NOT); - if (cur[0].getKind() == Kind::OR || cur[0].getKind() == Kind::IMPLIES) - { - k = Kind::AND; - negAllCh = true; - negCh1 = cur[0].getKind() == Kind::IMPLIES; - } - else if (cur[0].getKind() == Kind::AND) - { - k = Kind::OR; - negAllCh = true; - } - else if (cur[0].getKind() == Kind::XOR - || (cur[0].getKind() == Kind::EQUAL - && cur[0][0].getType().isBoolean())) - { - k = Kind::EQUAL; - negCh1 = (cur[0].getKind() == Kind::EQUAL); - } - else if (cur[0].getKind() == Kind::ITE) - { - k = cur[0].getKind(); - negAllCh = true; - negCh1 = true; - } - else - { - visited[cur] = cur; - continue; - } - ncur = cur[0]; - } - else if ((k != Kind::EQUAL || !body[0].getType().isBoolean()) - && k != Kind::ITE && k != Kind::AND && k != Kind::OR) - { - // a literal - visited[cur] = cur; - continue; - } - preKind[cur] = k; - visited[cur] = Node::null(); - visit.push_back(cur); - std::vector& pc = preChildren[cur]; - for (size_t i = 0, nchild = ncur.getNumChildren(); i < nchild; ++i) - { - Node c = - (i == 0 && negCh1) != negAllCh ? ncur[i].negate() : Node(ncur[i]); - pc.push_back(c); - visit.push_back(c); - } - } - else if (it->second.isNull()) - { - Kind ok = cur.getKind(); - Assert(preKind.find(cur) != preKind.end()); - Kind k = preKind[cur]; - Assert(cur.getMetaKind() != kind::metakind::PARAMETERIZED); - bool childChanged = false; - std::vector children; - std::vector& pc = preChildren[cur]; - std::map lit_pol; - bool success = true; - for (const Node& cn : pc) - { - it = visited.find(cn); - Assert(it != visited.end()); - Assert(!it->second.isNull()); - Node c = it->second; - if (c.getKind() == k && (k == Kind::OR || k == Kind::AND)) - { - // flatten - childChanged = true; - for (const Node& cc : c) - { - if (!addCheckElimChild(children, cc, k, lit_pol, childChanged)) - { - success = false; - break; - } - } - } - else - { - success = addCheckElimChild(children, c, k, lit_pol, childChanged); - } - if (!success) - { - // tautology - break; - } - childChanged = childChanged || c != cn; - } - Node ret = cur; - if (!success) - { - Assert(k == Kind::OR || k == Kind::AND); - ret = nm->mkConst(k == Kind::OR); - } - else if (childChanged || k != ok) - { - ret = (children.size() == 1 && k != Kind::NOT) - ? children[0] - : nm->mkNode(k, children); - } - visited[cur] = ret; - } - } while (!visit.empty()); - Assert(visited.find(body) != visited.end()); - Assert(!visited.find(body)->second.isNull()); - return visited[body]; -} - void QuantifiersRewriter::computeDtTesterIteSplit( Node n, std::map& pcons, @@ -1751,24 +1656,35 @@ Node QuantifiersRewriter::computeSplit(std::vector& args, } if ( eqc_active>1 || !lits.empty() || var_to_eqc.size()!=args.size() ){ NodeManager* nm = nodeManager(); - Trace("clause-split-debug") << "Split quantified formula with body " << body << std::endl; - Trace("clause-split-debug") << " Ground literals: " << std::endl; - for( size_t i=0; i >::iterator it = eqc_to_lit.begin(); it != eqc_to_lit.end(); ++it ){ - Trace("clause-split-debug") << " Literals: " << std::endl; - for (size_t i=0; isecond.size(); i++) { - Trace("clause-split-debug") << " " << it->second[i] << std::endl; - } int eqc = it->first; - Trace("clause-split-debug") << " Variables: " << std::endl; - for (size_t i=0; isecond.size(); i++) + { + Trace("clause-split-debug") << " " << it->second[i] << std::endl; + } + Trace("clause-split-debug") << " Variables: " << std::endl; + for (size_t i = 0; i < eqc_to_var[eqc].size(); i++) + { + Trace("clause-split-debug") + << " " << eqc_to_var[eqc][i] << std::endl; + } + Trace("clause-split-debug") << std::endl; } - Trace("clause-split-debug") << std::endl; Node bvl = nodeManager()->mkNode(Kind::BOUND_VAR_LIST, eqc_to_var[eqc]); Node bd = it->second.size() == 1 ? it->second[0] : nm->mkNode(Kind::OR, it->second); @@ -1780,9 +1696,8 @@ Node QuantifiersRewriter::computeSplit(std::vector& args, lits.size() == 1 ? lits[0] : nodeManager()->mkNode(Kind::OR, lits); Trace("clause-split-debug") << "Made node : " << nf << std::endl; return nf; - }else{ - return mkForAll( args, body, qa ); } + return mkForAll(args, body, qa); } Node QuantifiersRewriter::mkForAll(const std::vector& args, @@ -2151,8 +2066,11 @@ Node QuantifiersRewriter::computeOperation(Node f, Node n = f[1]; if (computeOption == COMPUTE_ELIM_SYMBOLS) { - n = computeElimSymbols(n); - }else if( computeOption==COMPUTE_AGGRESSIVE_MINISCOPING ){ + // relies on external utility + n = booleans::TheoryBoolRewriter::computeNnfNorm(nodeManager(), n); + } + else if (computeOption == COMPUTE_AGGRESSIVE_MINISCOPING) + { return computeAggressiveMiniscoping( args, n ); } else if (computeOption == COMPUTE_EXT_REWRITE) diff --git a/src/theory/quantifiers/quantifiers_rewriter.h b/src/theory/quantifiers/quantifiers_rewriter.h index 89abbde61f5..f11b7448843 100644 --- a/src/theory/quantifiers/quantifiers_rewriter.h +++ b/src/theory/quantifiers/quantifiers_rewriter.h @@ -169,11 +169,6 @@ class QuantifiersRewriter : public TheoryRewriter std::vector& subs, QAttributes& qa) const; //-------------------------------------end variable elimination utilities - /** - * Eliminates IMPLIES/XOR, removes duplicates/infers tautologies of AND/OR, - * and computes NNF. - */ - Node computeElimSymbols(Node body) const; /** * Compute miniscoping in quantified formula q with attributes in qa. */ @@ -231,8 +226,13 @@ class QuantifiersRewriter : public TheoryRewriter * (forall ((x Int)) (forall ((y Int)) (P x y))) ---> * (forall ((x Int) (y Int)) (P x y)). * This is done until fixed point. + * + * @param q The quantified formula to prenex. + * @param checkStd If true, we do not merge prenex for any non-standard + * quantified formula + * @return The result of merging prenex in q. */ - Node mergePrenex(const Node& q); + Node mergePrenex(const Node& q, bool checkStd); /** * Helper method for getVarElim, called when n has polarity pol in body. */ @@ -242,11 +242,6 @@ class QuantifiersRewriter : public TheoryRewriter std::vector& args, std::vector& vars, std::vector& subs) const; - bool addCheckElimChild(std::vector& children, - Node c, - Kind k, - std::map& lit_pol, - bool& childrenChanged) const; static void computeArgs(const std::vector& args, std::map& activeMap, Node n, diff --git a/src/theory/quantifiers/sygus/rcons_obligation.cpp b/src/theory/quantifiers/sygus/rcons_obligation.cpp index e40a10a2de3..e39a99f2856 100644 --- a/src/theory/quantifiers/sygus/rcons_obligation.cpp +++ b/src/theory/quantifiers/sygus/rcons_obligation.cpp @@ -86,9 +86,9 @@ void RConsObligation::printCandSols( Trace("sygus-rcons") << " " << datatypes::utils::sygusToBuiltin(candSol) << std::endl; - std::unordered_set vars; + std::unordered_set vars; expr::getVariables(candSol, vars); - for (TNode var : vars) + for (const Node& var : vars) { if (visited.find(var) == visited.cend()) for (const std::unique_ptr& ob : obs) diff --git a/src/theory/quantifiers/sygus/sygus_reconstruct.cpp b/src/theory/quantifiers/sygus/sygus_reconstruct.cpp index ac1b428f5c9..018622e983d 100644 --- a/src/theory/quantifiers/sygus/sygus_reconstruct.cpp +++ b/src/theory/quantifiers/sygus/sygus_reconstruct.cpp @@ -553,14 +553,14 @@ void SygusReconstruct::removeReconstructedTerms( Node SygusReconstruct::mkGround(Node n) const { // get the set of bound variables in n - std::unordered_set vars; + std::unordered_set vars; expr::getVariables(n, vars); std::unordered_map subs; // generate a ground value for each one of those variables NodeManager* nm = nodeManager(); - for (const TNode& var : vars) + for (const Node& var : vars) { subs.emplace(var, nm->mkGroundValue(var.getType())); } diff --git a/src/theory/quantifiers/sygus/sygus_utils.cpp b/src/theory/quantifiers/sygus/sygus_utils.cpp index 502d4ef2658..2c34c593565 100644 --- a/src/theory/quantifiers/sygus/sygus_utils.cpp +++ b/src/theory/quantifiers/sygus/sygus_utils.cpp @@ -19,6 +19,7 @@ #include "expr/node_algorithm.h" #include "expr/skolem_manager.h" +#include "theory/datatypes/sygus_datatype_utils.h" #include "theory/quantifiers/quantifiers_attributes.h" using namespace cvc5::internal::kind; @@ -219,6 +220,37 @@ TypeNode SygusUtils::getSygusType(const Node& f) return TypeNode::null(); } +Node SygusUtils::mkSygusTermFor(const Node& f) +{ + NodeManager* nm = NodeManager::currentNM(); + TypeNode tn = getSygusType(f); + Node bvl = getOrMkSygusArgumentList(f); + if (tn.isNull()) + { + Node ret; + if (f.getType().isFunction()) + { + Assert(!bvl.isNull()); + ret = nm->mkGroundValue(f.getType().getRangeType()); + // give the appropriate variable list + ret = nm->mkNode(Kind::LAMBDA, bvl, ret); + } + else + { + ret = nm->mkGroundValue(f.getType()); + } + return ret; + } + Node ret = nm->mkGroundValue(tn); + // use external=true + ret = datatypes::utils::sygusToBuiltin(ret, true); + if (!bvl.isNull()) + { + ret = nm->mkNode(Kind::LAMBDA, bvl, ret); + } + return ret; +} + } // namespace quantifiers } // namespace theory } // namespace cvc5::internal diff --git a/src/theory/quantifiers/sygus/sygus_utils.h b/src/theory/quantifiers/sygus/sygus_utils.h index 1a4aaa0c993..911b0817539 100644 --- a/src/theory/quantifiers/sygus/sygus_utils.h +++ b/src/theory/quantifiers/sygus/sygus_utils.h @@ -115,6 +115,11 @@ class SygusUtils * function-to-synthesize f. */ static TypeNode getSygusType(const Node& f); + /** + * Makes an arbitrary term as a solution for f, possibly using its sygus + * type if it has one. + */ + static Node mkSygusTermFor(const Node& f); }; } // namespace quantifiers diff --git a/src/theory/sets/rewrites b/src/theory/sets/rewrites index fd7da818a68..415db21a690 100644 --- a/src/theory/sets/rewrites +++ b/src/theory/sets/rewrites @@ -1,8 +1,18 @@ ; Equality +(define-cond-rule sets-eq-singleton-emp ((x ?Set) (y ?)) + (set.is_empty x) + (= x (set.singleton y)) + false) + (define-rule sets-member-singleton ((x ?) (y ?)) (set.member x (set.singleton y)) (= x y)) + +(define-cond-rule sets-member-emp ((x ?) (y ?Set)) + (set.is_empty y) + (set.member x y) + false) (define-rule sets-subset-elim ((x ?Set) (y ?Set)) (set.subset x y) @@ -15,6 +25,30 @@ (set.inter x y) (set.inter y x)) +(define-cond-rule sets-inter-emp1 ((x ?Set) (y ?Set)) + (set.is_empty x) + (set.inter x y) + x) +(define-cond-rule sets-inter-emp2 ((x ?Set) (y ?Set)) + (set.is_empty y) + (set.inter x y) + y) +(define-cond-rule sets-minus-emp1 ((x ?Set) (y ?Set)) + (set.is_empty x) + (set.minus x y) + x) +(define-cond-rule sets-minus-emp2 ((x ?Set) (y ?Set)) + (set.is_empty y) + (set.minus x y) + x) +(define-cond-rule sets-union-emp1 ((x ?Set) (y ?Set)) + (set.is_empty x) + (set.union x y) + y) +(define-cond-rule sets-union-emp2 ((x ?Set) (y ?Set)) + (set.is_empty y) + (set.union x y) + x) (define-rule sets-inter-member ((x ?) (y ?Set) (z ?Set)) (set.member x (set.inter y z)) @@ -26,5 +60,25 @@ (set.member x (set.union y z)) (or (set.member x y) (set.member x z))) -; (set.card (set.union s t)) ---> (- (+ (set.card s) (set.card t)) (set.card (set.inter s t))) +(define-rule sets-choose-singleton ((x ?)) + (set.choose (set.singleton x)) + x) + +(define-rule sets-card-singleton ((x ?)) + (set.card (set.singleton x)) + 1) + +(define-rule sets-card-union ((s ?Set) (t ?Set)) + (set.card (set.union s t)) + (- (+ (set.card s) (set.card t)) (set.card (set.inter s t)))) + +(define-rule sets-card-minus ((s ?Set) (t ?Set)) + (set.card (set.minus s t)) + (- (set.card s) (set.card (set.inter s t)))) + +(define-cond-rule sets-card-emp ((x ?Set)) + (set.is_empty x) + (set.card x) + 0) + ; (set.complement S) ---> (set.minus (as set.universe (Set Int)) S) diff --git a/src/theory/sets/theory_sets_rewriter.cpp b/src/theory/sets/theory_sets_rewriter.cpp index 3952d55d8e3..dd4b3c70cf1 100644 --- a/src/theory/sets/theory_sets_rewriter.cpp +++ b/src/theory/sets/theory_sets_rewriter.cpp @@ -34,7 +34,30 @@ namespace cvc5::internal { namespace theory { namespace sets { -TheorySetsRewriter::TheorySetsRewriter(NodeManager* nm) : TheoryRewriter(nm) {} +TheorySetsRewriter::TheorySetsRewriter(NodeManager* nm) : TheoryRewriter(nm) +{ + // Needs to be a subcall in DSL reconstruction since set.is_empty is used + // as a premise to test emptiness of a set. + registerProofRewriteRule(ProofRewriteRule::SETS_IS_EMPTY_EVAL, + TheoryRewriteCtx::DSL_SUBCALL); +} + +Node TheorySetsRewriter::rewriteViaRule(ProofRewriteRule id, const Node& n) +{ + switch (id) + { + case ProofRewriteRule::SETS_IS_EMPTY_EVAL: + { + if (n.getKind() == Kind::SET_IS_EMPTY && n[0].isConst()) + { + return nodeManager()->mkConst(n[0].getKind() == Kind::SET_EMPTY); + } + } + break; + default: break; + } + return Node::null(); +} bool TheorySetsRewriter::checkConstantMembership(TNode elementTerm, TNode setTerm) { diff --git a/src/theory/sets/theory_sets_rewriter.h b/src/theory/sets/theory_sets_rewriter.h index 658d8e08ce0..1c23b1128f3 100644 --- a/src/theory/sets/theory_sets_rewriter.h +++ b/src/theory/sets/theory_sets_rewriter.h @@ -28,6 +28,16 @@ class TheorySetsRewriter : public TheoryRewriter { public: TheorySetsRewriter(NodeManager* nm); + + /** + * Rewrite n based on the proof rewrite rule id. + * @param id The rewrite rule. + * @param n The node to rewrite. + * @return The rewritten version of n based on id, or Node::null() if n + * cannot be rewritten. + */ + Node rewriteViaRule(ProofRewriteRule id, const Node& n) override; + /** * Rewrite a node into the normal form for the theory of sets. * Called in post-order (really reverse-topological order) when diff --git a/src/theory/strings/base_solver.cpp b/src/theory/strings/base_solver.cpp index a93f8e8112e..a6e3c827177 100644 --- a/src/theory/strings/base_solver.cpp +++ b/src/theory/strings/base_solver.cpp @@ -22,6 +22,7 @@ #include "theory/rewriter.h" #include "theory/strings/theory_strings_utils.h" #include "theory/strings/word.h" +#include "util/cardinality.h" #include "util/rational.h" #include "util/string.h" diff --git a/src/theory/strings/infer_proof_cons.cpp b/src/theory/strings/infer_proof_cons.cpp index 2a028d90b19..33d555da7ce 100644 --- a/src/theory/strings/infer_proof_cons.cpp +++ b/src/theory/strings/infer_proof_cons.cpp @@ -700,6 +700,7 @@ void InferProofCons::convert(InferenceId infer, useBuffer = true; } ProofRule r = ProofRule::UNKNOWN; + std::vector args; if (mem.isNull()) { // failed to eliminate above @@ -718,25 +719,27 @@ void InferProofCons::convert(InferenceId infer, && mem[0].getKind() == Kind::STRING_IN_REGEXP); if (mem[0][1].getKind() == Kind::REGEXP_CONCAT) { - size_t index; - Node reLen = RegExpOpr::getRegExpConcatFixed(mem[0][1], index); + bool isCRev; + Node reLen = RegExpOpr::getRegExpConcatFixed(mem[0][1], isCRev); // if we can find a fixed length for a component, use the optimized // version if (!reLen.isNull()) { r = ProofRule::RE_UNFOLD_NEG_CONCAT_FIXED; + args.push_back(nm->mkConst(isCRev)); } } } if (useBuffer) { - mem = psb.tryStep(r, {mem}, {}); + mem = psb.tryStep(r, {mem}, args); // should match the conclusion useBuffer = (mem==conc); } else { ps.d_rule = r; + ps.d_args = args; } } break; diff --git a/src/theory/strings/proof_checker.cpp b/src/theory/strings/proof_checker.cpp index 261251ba6a5..03acb956a70 100644 --- a/src/theory/strings/proof_checker.cpp +++ b/src/theory/strings/proof_checker.cpp @@ -20,6 +20,7 @@ #include "theory/rewriter.h" #include "theory/strings/core_solver.h" #include "theory/strings/regexp_elim.h" +#include "theory/strings/regexp_entail.h" #include "theory/strings/regexp_operation.h" #include "theory/strings/term_registry.h" #include "theory/strings/theory_strings_preprocess.h" @@ -420,7 +421,6 @@ Node StringProofRuleChecker::checkInternal(ProofRule id, || id == ProofRule::RE_UNFOLD_NEG_CONCAT_FIXED) { Assert(children.size() == 1); - Assert(args.empty()); Node skChild = children[0]; if (id == ProofRule::RE_UNFOLD_NEG || id == ProofRule::RE_UNFOLD_NEG_CONCAT_FIXED) @@ -440,29 +440,38 @@ Node StringProofRuleChecker::checkInternal(ProofRule id, Node conc; if (id == ProofRule::RE_UNFOLD_POS) { + Assert(args.empty()); std::vector newSkolems; SkolemCache skc(nullptr); conc = RegExpOpr::reduceRegExpPos(skChild, &skc, newSkolems); } else if (id == ProofRule::RE_UNFOLD_NEG) { + Assert(args.empty()); conc = RegExpOpr::reduceRegExpNeg(skChild); } else if (id == ProofRule::RE_UNFOLD_NEG_CONCAT_FIXED) { - if (skChild[0][1].getKind() != Kind::REGEXP_CONCAT) + Assert(args.size() == 1); + bool isRev; + if (!getBool(args[0], isRev)) + { + return Node::null(); + } + Node r = skChild[0][1]; + if (r.getKind() != Kind::REGEXP_CONCAT) { Trace("strings-pfcheck") << "...fail, no concat regexp" << std::endl; return Node::null(); } - size_t index; - Node reLen = RegExpOpr::getRegExpConcatFixed(skChild[0][1], index); + size_t index = isRev ? r.getNumChildren() - 1 : 0; + Node reLen = RegExpEntail::getFixedLengthForRegexp(r[index]); if (reLen.isNull()) { Trace("strings-pfcheck") << "...fail, non-fixed lengths" << std::endl; return Node::null(); } - conc = RegExpOpr::reduceRegExpNegConcatFixed(skChild, reLen, index); + conc = RegExpOpr::reduceRegExpNegConcatFixed(skChild, reLen, isRev); } return conc; } diff --git a/src/theory/strings/regexp_entail.cpp b/src/theory/strings/regexp_entail.cpp index e1d168ad66b..9b919645d6d 100644 --- a/src/theory/strings/regexp_entail.cpp +++ b/src/theory/strings/regexp_entail.cpp @@ -41,9 +41,6 @@ Node RegExpEntail::simpleRegexpConsume(std::vector& mchildren, { Trace("regexp-ext-rewrite-debug") << "Simple reg exp consume, dir=" << dir << ":" << std::endl; - Trace("regexp-ext-rewrite-debug") - << " mchildren : " << mchildren << std::endl; - Trace("regexp-ext-rewrite-debug") << " children : " << children << std::endl; NodeManager* nm = NodeManager::currentNM(); unsigned tmin = dir < 0 ? 0 : dir; unsigned tmax = dir < 0 ? 1 : dir; @@ -52,6 +49,12 @@ Node RegExpEntail::simpleRegexpConsume(std::vector& mchildren, { if (tmin <= t && t <= tmax) { + Trace("regexp-ext-rewrite-debug") + << "Run consume, direction is " << t << " with:" << std::endl; + Trace("regexp-ext-rewrite-debug") + << " mchildren : " << mchildren << std::endl; + Trace("regexp-ext-rewrite-debug") + << " children : " << children << std::endl; bool do_next = true; while (!children.empty() && !mchildren.empty() && do_next) { @@ -64,30 +67,33 @@ Node RegExpEntail::simpleRegexpConsume(std::vector& mchildren, Assert(xc.getKind() != Kind::STRING_CONCAT); if (rc.getKind() == Kind::STRING_TO_REGEXP) { - if (xc == rc[0]) + std::vector childrenc; + utils::getConcat(rc[0], childrenc); + size_t cindex = t == 1 ? 0 : childrenc.size() - 1; + Node rcc = childrenc[cindex]; + Node remStr; + if (xc == rcc) { - children.pop_back(); mchildren.pop_back(); do_next = true; Trace("regexp-ext-rewrite-debug") << "- strip equal" << std::endl; } - else if (rc[0].isConst() && Word::isEmpty(rc[0])) + else if (rcc.isConst() && Word::isEmpty(rcc)) { Trace("regexp-ext-rewrite-debug") << "- ignore empty RE" << std::endl; // ignore and continue - children.pop_back(); do_next = true; } - else if (xc.isConst() && rc[0].isConst()) + else if (xc.isConst() && rcc.isConst()) { // split the constant size_t index; - Node s = Word::splitConstant(xc, rc[0], index, t == 0); + remStr = Word::splitConstant(xc, rcc, index, t == 0); Trace("regexp-ext-rewrite-debug") - << "- CRE: Regexp const split : " << xc << " " << rc[0] - << " -> " << s << " " << index << " " << t << std::endl; - if (s.isNull()) + << "- CRE: Regexp const split : " << xc << " " << rcc << " -> " + << remStr << " " << index << " " << t << std::endl; + if (remStr.isNull()) { Trace("regexp-ext-rewrite-debug") << "...return false" << std::endl; @@ -97,20 +103,44 @@ Node RegExpEntail::simpleRegexpConsume(std::vector& mchildren, { Trace("regexp-ext-rewrite-debug") << "- strip equal const" << std::endl; - children.pop_back(); mchildren.pop_back(); if (index == 0) { - mchildren.push_back(s); - } - else - { - children.push_back(nm->mkNode(Kind::STRING_TO_REGEXP, s)); + mchildren.push_back(remStr); + // we've processed the remainder as leftover for the LHS + // string, clear it now + remStr = Node::null(); } + // otherwise remStr is processed below } Trace("regexp-ext-rewrite-debug") << "- split const" << std::endl; do_next = true; } + if (do_next) + { + if (remStr.isNull()) + { + // we have fully processed the component + childrenc.erase(childrenc.begin() + cindex); + } + else + { + // we have a remainder + childrenc[cindex] = remStr; + } + if (childrenc.empty()) + { + // if childrenc is empty, we are done with the current str.to_re + children.pop_back(); + } + else + { + // otherwise we reconstruct it + TypeNode stype = nm->stringType(); + children[children.size() - 1] = nm->mkNode( + Kind::STRING_TO_REGEXP, utils::mkConcat(childrenc, stype)); + } + } } else if (xc.isConst()) { diff --git a/src/theory/strings/regexp_operation.cpp b/src/theory/strings/regexp_operation.cpp index b353250c02c..586bea316e5 100644 --- a/src/theory/strings/regexp_operation.cpp +++ b/src/theory/strings/regexp_operation.cpp @@ -927,15 +927,15 @@ Node RegExpOpr::simplify(Node t, bool polarity) if (r.getKind() == Kind::REGEXP_CONCAT) { // the index we are removing from the RE concatenation - size_t index = 0; + bool isRev; // As an optimization to the reduction, if we can determine that // all strings in the language of R1 have the same length, say n, // then the conclusion of the reduction is quantifier-free: - // ~( substr(s,0,n) in R1 ) OR ~( substr(s,n,len(s)-n) in R2) - Node reLen = getRegExpConcatFixed(r, index); + // ~( substr(s,0,n) in R1 ) OR ~( substr(s,len(s)-n,n) in R2) + Node reLen = getRegExpConcatFixed(r, isRev); if (!reLen.isNull()) { - conc = reduceRegExpNegConcatFixed(tlit, reLen, index); + conc = reduceRegExpNegConcatFixed(tlit, reLen, isRev); } } if (conc.isNull()) @@ -949,10 +949,10 @@ Node RegExpOpr::simplify(Node t, bool polarity) return conc; } -Node RegExpOpr::getRegExpConcatFixed(Node r, size_t& index) +Node RegExpOpr::getRegExpConcatFixed(Node r, bool& isRev) { Assert(r.getKind() == Kind::REGEXP_CONCAT); - index = 0; + isRev = false; Node reLen = RegExpEntail::getFixedLengthForRegexp(r[0]); if (!reLen.isNull()) { @@ -963,7 +963,7 @@ Node RegExpOpr::getRegExpConcatFixed(Node r, size_t& index) reLen = RegExpEntail::getFixedLengthForRegexp(r[indexE]); if (!reLen.isNull()) { - index = indexE; + isRev = true; return reLen; } return Node::null(); @@ -983,22 +983,20 @@ Node RegExpOpr::reduceRegExpNeg(Node mem) { // do not use length entailment, call regular expression concat Node reLen; - size_t i = 0; - conc = reduceRegExpNegConcatFixed(mem, reLen, i); + conc = reduceRegExpNegConcatFixed(mem, reLen, false); } else if (k == Kind::REGEXP_STAR) { Node emp = Word::mkEmptyWord(s.getType()); Node lens = nm->mkNode(Kind::STRING_LENGTH, s); Node sne = s.eqNode(emp).negate(); - Node b1 = nm->mkBoundVar(nm->integerType()); + Node b1 = SkolemCache::mkIndexVar(mem); Node b1v = nm->mkNode(Kind::BOUND_VAR_LIST, b1); - Node g11n = nm->mkNode(Kind::GT, b1, zero).notNode(); - Node g12n = nm->mkNode(Kind::GEQ, lens, b1).notNode(); + Node g11n = nm->mkNode(Kind::LEQ, b1, zero); + Node g12n = nm->mkNode(Kind::LT, lens, b1); // internal - Node s1 = nm->mkNode(Kind::STRING_SUBSTR, s, zero, b1); - Node s2 = - nm->mkNode(Kind::STRING_SUBSTR, s, b1, nm->mkNode(Kind::SUB, lens, b1)); + Node s1 = utils::mkPrefix(s, b1); + Node s2 = utils::mkSuffix(s, b1); Node s1r1 = nm->mkNode(Kind::STRING_IN_REGEXP, s1, r[0]).negate(); Node s2r2 = nm->mkNode(Kind::STRING_IN_REGEXP, s2, r).negate(); @@ -1014,7 +1012,7 @@ Node RegExpOpr::reduceRegExpNeg(Node mem) return conc; } -Node RegExpOpr::reduceRegExpNegConcatFixed(Node mem, Node reLen, size_t index) +Node RegExpOpr::reduceRegExpNegConcatFixed(Node mem, Node reLen, bool isRev) { Assert(mem.getKind() == Kind::NOT && mem[0].getKind() == Kind::STRING_IN_REGEXP); @@ -1031,7 +1029,6 @@ Node RegExpOpr::reduceRegExpNegConcatFixed(Node mem, Node reLen, size_t index) // ~(substr(s,0,x) in R1) OR ~(substr(s,x,len(s)-x) in R2 ++ ... ++ Rn) // Index is the child index of r that we are stripping off, which is either // from the beginning or the end. - Assert(index == 0 || index == r.getNumChildren() - 1); Node lens = nm->mkNode(Kind::STRING_LENGTH, s); Node b1; Node b1v; @@ -1040,9 +1037,8 @@ Node RegExpOpr::reduceRegExpNegConcatFixed(Node mem, Node reLen, size_t index) { b1 = SkolemCache::mkIndexVar(mem); b1v = nm->mkNode(Kind::BOUND_VAR_LIST, b1); - guard1n = nm->mkNode(Kind::GEQ, b1, zero).notNode(); - guard2n = - nm->mkNode(Kind::GEQ, nm->mkNode(Kind::STRING_LENGTH, s), b1).notNode(); + guard1n = nm->mkNode(Kind::LT, b1, zero); + guard2n = nm->mkNode(Kind::LT, nm->mkNode(Kind::STRING_LENGTH, s), b1); } else { @@ -1050,19 +1046,17 @@ Node RegExpOpr::reduceRegExpNegConcatFixed(Node mem, Node reLen, size_t index) } Node s1; Node s2; - if (index == 0) + if (!isRev) { - s1 = nm->mkNode(Kind::STRING_SUBSTR, s, zero, b1); - s2 = - nm->mkNode(Kind::STRING_SUBSTR, s, b1, nm->mkNode(Kind::SUB, lens, b1)); + s1 = utils::mkPrefix(s, b1); + s2 = utils::mkSuffix(s, b1); } else { - s1 = - nm->mkNode(Kind::STRING_SUBSTR, s, nm->mkNode(Kind::SUB, lens, b1), b1); - s2 = nm->mkNode( - Kind::STRING_SUBSTR, s, zero, nm->mkNode(Kind::SUB, lens, b1)); + s1 = utils::mkSuffixOfLen(s, b1); + s2 = utils::mkPrefix(s, nm->mkNode(Kind::SUB, lens, b1)); } + size_t index = isRev ? r.getNumChildren() - 1 : 0; Node s1r1 = nm->mkNode(Kind::STRING_IN_REGEXP, s1, r[index]).negate(); std::vector nvec; for (unsigned i = 0, nchild = r.getNumChildren(); i < nchild; i++) diff --git a/src/theory/strings/regexp_operation.h b/src/theory/strings/regexp_operation.h index ee36bbc8488..0ffdc29dede 100644 --- a/src/theory/strings/regexp_operation.h +++ b/src/theory/strings/regexp_operation.h @@ -130,11 +130,11 @@ class RegExpOpr : protected EnvObj /** * Given regular expression of the form * (re.++ r_0 ... r_{n-1}) - * This returns a non-null node reLen and updates index such that + * This returns a non-null node reLen and updates isRev such that * RegExpEntail::getFixedLengthForRegexp(r_index) = reLen - * where index is set to either 0 or n-1. + * where index is either 0 or n-1 when isRev is false or true respectively. */ - static Node getRegExpConcatFixed(Node r, size_t& index); + static Node getRegExpConcatFixed(Node r, bool& isRev); //------------------------ trusted reductions /** * Return the unfolded form of mem of the form (str.in_re s r). @@ -150,12 +150,12 @@ class RegExpOpr : protected EnvObj * Return the unfolded form of mem of the form * (not (str.in_re s (re.++ r_0 ... r_{n-1}))) * Called when RegExpEntail::getFixedLengthForRegexp(r_index) = reLen - * where index is either 0 or n-1. + * where index is either 0 or n-1 where isRev is false or true respectively. * * This uses reLen as an optimization to improve the reduction. If reLen * is null, then this optimization is not applied. */ - static Node reduceRegExpNegConcatFixed(Node mem, Node reLen, size_t index); + static Node reduceRegExpNegConcatFixed(Node mem, Node reLen, bool isRev); //------------------------ end trusted reductions /** * This method returns 1 if the empty string is in r, 2 if the empty string diff --git a/src/theory/strings/skolem_cache.cpp b/src/theory/strings/skolem_cache.cpp index 68bdd896345..8b42babd231 100644 --- a/src/theory/strings/skolem_cache.cpp +++ b/src/theory/strings/skolem_cache.cpp @@ -290,7 +290,8 @@ Node SkolemCache::mkIndexVar(Node t) NodeManager* nm = NodeManager::currentNM(); TypeNode intType = nm->integerType(); BoundVarManager* bvm = nm->getBoundVarManager(); - return bvm->mkBoundVar(t, intType); + // Note that proof rules may depend on the name of this variable. + return bvm->mkBoundVar(t, "@var.str_index", intType); } Node SkolemCache::mkLengthVar(Node t) @@ -298,7 +299,7 @@ Node SkolemCache::mkLengthVar(Node t) NodeManager* nm = NodeManager::currentNM(); TypeNode intType = nm->integerType(); BoundVarManager* bvm = nm->getBoundVarManager(); - return bvm->mkBoundVar(t, intType); + return bvm->mkBoundVar(t, "@var.str_length", intType); } Node SkolemCache::mkSkolemFun(SkolemId id, Node a, Node b) diff --git a/src/theory/theory_model.cpp b/src/theory/theory_model.cpp index e52031ad54b..a3a81a1d6ab 100644 --- a/src/theory/theory_model.cpp +++ b/src/theory/theory_model.cpp @@ -174,25 +174,20 @@ bool TheoryModel::isModelCoreSymbol(Node s) const return d_model_core.find(s) != d_model_core.end(); } -Cardinality TheoryModel::getCardinality(TypeNode tn) const +size_t TheoryModel::getCardinality(const TypeNode& tn) const { //for now, we only handle cardinalities for uninterpreted sorts - if (!tn.isUninterpretedSort()) - { - Trace("model-getvalue-debug") - << "Get cardinality other sort, unknown." << std::endl; - return Cardinality( CardinalityUnknown() ); - } + Assert(tn.isUninterpretedSort()); if (d_rep_set.hasType(tn)) { Trace("model-getvalue-debug") << "Get cardinality sort, #rep : " << d_rep_set.getNumRepresentatives(tn) << std::endl; - return Cardinality(d_rep_set.getNumRepresentatives(tn)); + return d_rep_set.getNumRepresentatives(tn); } Trace("model-getvalue-debug") << "Get cardinality sort, unconstrained, return 1." << std::endl; - return Cardinality(1); + return 1; } Node TheoryModel::getModelValue(TNode n) const @@ -261,8 +256,9 @@ Node TheoryModel::getModelValue(TNode n) const ret.getOperator().getConst(); Trace("model-getvalue-debug") << "get cardinality constraint " << cc.getType() << std::endl; - ret = nm->mkConst(getCardinality(cc.getType()).getFiniteCardinality() - <= cc.getUpperBound()); + size_t cval = getCardinality(cc.getType()); + Assert(cval > 0); + ret = nm->mkConst(Integer(cval) <= cc.getUpperBound()); } // if the value was constant, we return it. If it was non-constant, // we only return it if we are an evaluated kind. This can occur if the diff --git a/src/theory/theory_model.h b/src/theory/theory_model.h index 5ae23c3b88d..adc9a296134 100644 --- a/src/theory/theory_model.h +++ b/src/theory/theory_model.h @@ -27,7 +27,6 @@ #include "theory/type_enumerator.h" #include "theory/type_set.h" #include "theory/uf/equality_engine.h" -#include "util/cardinality.h" namespace cvc5::internal { @@ -294,9 +293,6 @@ class TheoryModel : protected EnvObj bool isModelCoreSymbol(Node sym) const; //---------------------------- end model cores - /** get cardinality for sort */ - Cardinality getCardinality(TypeNode t) const; - //---------------------------- function values /** Does this model have terms for the given uninterpreted function? */ bool hasUfTerms(Node f) const; @@ -337,6 +333,13 @@ class TheoryModel : protected EnvObj bool isValue(TNode node) const; protected: + /** + * Get cardinality for sort, where t is an uninterpreted sort. + * @param t The sort. + * @return the cardinality of the sort, which is the number of representatives + * for that sort, or 1 if none exist. + */ + size_t getCardinality(const TypeNode& t) const; /** * Assign that n is the representative of the equivalence class r. * @param r The equivalence class diff --git a/src/theory/theory_rewriter.cpp b/src/theory/theory_rewriter.cpp index 86146c4903b..df50f042089 100644 --- a/src/theory/theory_rewriter.cpp +++ b/src/theory/theory_rewriter.cpp @@ -103,6 +103,10 @@ void TheoryRewriter::registerProofRewriteRule(ProofRewriteRule id, { std::unordered_set& rules = d_pfTheoryRewrites[ctx]; rules.insert(id); + if (ctx == TheoryRewriteCtx::DSL_SUBCALL) + { + d_pfTheoryRewrites[TheoryRewriteCtx::PRE_DSL].insert(id); + } } NodeManager* TheoryRewriter::nodeManager() const { return d_nm; } diff --git a/src/theory/theory_rewriter.h b/src/theory/theory_rewriter.h index 7c0a77d02f9..2603a515cd5 100644 --- a/src/theory/theory_rewriter.h +++ b/src/theory/theory_rewriter.h @@ -37,6 +37,9 @@ enum class TheoryRewriteCtx { // Attempt to use the theory rewrite prior to DSL rewrite reconstruction. PRE_DSL, + // Attempt to use the theory rewrite during subcalls in DSL rewrite + // reconstruction. + DSL_SUBCALL, // Attempt to use the theory rewrite only after DSL rewrite reconstruction // fails. POST_DSL, diff --git a/test/regress/cli/CMakeLists.txt b/test/regress/cli/CMakeLists.txt index 3bb47538b6d..fa6040804e4 100644 --- a/test/regress/cli/CMakeLists.txt +++ b/test/regress/cli/CMakeLists.txt @@ -893,6 +893,7 @@ set(regress_0_tests regress0/ho/cong-full-apply.smt2 regress0/ho/cong.smt2 regress0/ho/datatype-field-ho.smt2 + regress0/ho/dd.lcl720.smt2 regress0/ho/dd.shadowing-defs.smt2 regress0/ho/dd.syo009.smt2 regress0/ho/dd.syo372-finite-ho-ext.smt2 @@ -930,6 +931,9 @@ set(regress_0_tests regress0/ho/lambda-test-deps.smt2 regress0/ho/lambda-test-deps-simple.smt2 regress0/ho/lazy-lambda-model.smt2 + regress0/ho/m-enum-bug1.smt2 + regress0/ho/m-enum-bug2.smt2 + regress0/ho/m-enum-bug3.smt2 regress0/ho/match-middle.smt2 regress0/ho/model-lam-simple.smt2 regress0/ho/modulo-func-equality.smt2 @@ -1327,6 +1331,7 @@ set(regress_0_tests regress0/proofs/proj-issue696.smt2 regress0/proofs/proj-issue698.smt2 regress0/proofs/proj-issue711-open-sat-proof.smt2 + regress0/proofs/proj-issue723-rdb-step.smt2 regress0/proofs/proof-components.smt2 regress0/proofs/qgu-fuzz-1-bool-sat.smt2 regress0/proofs/qgu-fuzz-2-bool-chainres-checking.smt2 @@ -1821,6 +1826,10 @@ set(regress_0_tests regress0/strings/proj-issue595-max-model-option.smt2 regress0/strings/quad-028-2-2-unsat.smt2 regress0/strings/quad-138-4-2-unsat.smt2 + regress0/strings/re-consume-bi-dir.smt2 + regress0/strings/re-consume-inter.smt2 + regress0/strings/re-consume-sym.smt2 + regress0/strings/re-consume-sym-sigma.smt2 regress0/strings/re-include-union.smt2 regress0/strings/re_diff.smt2 regress0/strings/re-in-rewrite.smt2 @@ -2393,6 +2402,7 @@ set(regress_1_tests regress1/gensys_brn001.smt2 regress1/get-learned-literals.smt2 regress1/get-learned-literals-types.smt2 + regress1/quantifiers/ho-grammar.smt2 regress1/ho/bug_freeVar_BDD_General_data_270.smt2 regress1/ho/bound_var_bug.smt2 regress1/ho/dd.seu-mbqi.smt2 @@ -2418,6 +2428,7 @@ set(regress_1_tests regress1/hole6.cvc.smt2 regress1/interpolant-unk-570.smt2 regress1/ite5.smt2 + regress1/issue10750-zll-repeat.smt2 regress1/issue3970-nl-ext-purify.smt2 regress1/issue3990-sort-inference.smt2 regress1/issue4273-ext-rew-cache.smt2 @@ -2466,6 +2477,7 @@ set(regress_1_tests regress1/nl/issue10000.smt2 regress1/nl/issue10003-nl-ext-rr.smt2 regress1/nl/issue10139-iand.smt2 + regress1/nl/issue10724-real-as-int-types.smt2 regress1/nl/issue3300-approx-sqrt-witness.smt2 regress1/nl/issue3441.smt2 regress1/nl/issue3617.smt2 @@ -2569,6 +2581,7 @@ set(regress_1_tests regress1/proj-issue619-nconst-nl-mv.smt2 regress1/proj-issue623-bv2nat-static.smt2 regress1/proj-issue634-diff-mc.smt2 + regress1/proj-issue719-zll-repeat.smt2 regress1/proof00.smt2 regress1/proofs/add_two_base.smt2 regress1/proofs/base-lfsc-treesize.smt2 @@ -2766,8 +2779,10 @@ set(regress_1_tests regress1/quantifiers/issue9989-syqi-nwf.smt2 regress1/quantifiers/javafe.ast.StmtVec.009.smt2 regress1/quantifiers/lia-witness-div-pp.smt2 + regress1/quantifiers/lcl579-2-ho-mbqi.smt2 regress1/quantifiers/lra-vts-inf.smt2 regress1/quantifiers/macro-geo-small-3.smt2 + regress1/quantifiers/mbqi_fast_sy_check_ic.smt2 regress1/quantifiers/min-ppgt-em-incomplete.smt2 regress1/quantifiers/min-ppgt-em-incomplete2.smt2 regress1/quantifiers/mix-coeff.smt2 @@ -3325,6 +3340,7 @@ set(regress_1_tests regress1/sygus/interpol_from_pono_1.smt2 regress1/sygus/interpol_from_pono_2.smt2 regress1/sygus/interpolant-conj-simple.smt2 + regress1/sygus/issue10708-unconstrained-int.sy regress1/sygus/issue2914.sy regress1/sygus/issue2935.sy regress1/sygus/issue3109-share-sel.sy diff --git a/test/regress/cli/regress0/ho/dd.lcl720.smt2 b/test/regress/cli/regress0/ho/dd.lcl720.smt2 new file mode 100644 index 00000000000..1d4234ddf3c --- /dev/null +++ b/test/regress/cli/regress0/ho/dd.lcl720.smt2 @@ -0,0 +1,7 @@ +; COMMAND-LINE: --mbqi --mbqi-fast-sygus +; EXPECT: sat +(set-logic HO_ALL) +(declare-const P (-> Int Bool)) +(declare-const f (-> (-> Int Bool) Int)) +(assert (forall ((x (-> Int Bool))) (P (f x)))) +(check-sat) diff --git a/test/regress/cli/regress0/ho/m-enum-bug1.smt2 b/test/regress/cli/regress0/ho/m-enum-bug1.smt2 new file mode 100644 index 00000000000..9f8dcc40e09 --- /dev/null +++ b/test/regress/cli/regress0/ho/m-enum-bug1.smt2 @@ -0,0 +1,17 @@ +; COMMAND-LINE: --mbqi --mbqi-fast-sygus +; EXPECT: unsat +(set-logic HO_ALL) +(declare-sort a 0) +(declare-fun y () a) +(assert + (not + (exists ((F (-> a a)) (X a)) + (= + (F X) + y + ) + ) + ) +) +(set-info :filename SEU882^5) +(check-sat-assuming ( true )) diff --git a/test/regress/cli/regress0/ho/m-enum-bug2.smt2 b/test/regress/cli/regress0/ho/m-enum-bug2.smt2 new file mode 100644 index 00000000000..c70b3833eb2 --- /dev/null +++ b/test/regress/cli/regress0/ho/m-enum-bug2.smt2 @@ -0,0 +1,9 @@ +; COMMAND-LINE: --mbqi --mbqi-fast-sygus +; EXPECT: unsat +(set-logic HO_ALL) +(declare-sort a 0) +(declare-fun f (a) a) +(declare-fun g (a) a) +(assert (not (=> (forall ((X a) (P (-> a Bool))) (=> (P (g X)) (P (f X)))) (= g f)))) +(set-info :filename SYO233^5) +(check-sat-assuming ( true )) diff --git a/test/regress/cli/regress0/ho/m-enum-bug3.smt2 b/test/regress/cli/regress0/ho/m-enum-bug3.smt2 new file mode 100644 index 00000000000..63a8540aa1a --- /dev/null +++ b/test/regress/cli/regress0/ho/m-enum-bug3.smt2 @@ -0,0 +1,21 @@ +; COMMAND-LINE: --mbqi --mbqi-fast-sygus +; EXPECT: unsat +(set-logic HO_ALL) +(declare-sort a 0) +(declare-fun p (a) Bool) +(declare-fun y () a) +(assert + (forall ((M (-> (-> a a) Bool))) + (exists ((G (-> a a))) + (and + (M G) + (and + (p y) + (not (p (G y))) + ) + ) + ) + ) +) +(set-info :filename SEU926^5) +(check-sat-assuming ( true )) diff --git a/test/regress/cli/regress0/proofs/proj-issue723-rdb-step.smt2 b/test/regress/cli/regress0/proofs/proj-issue723-rdb-step.smt2 new file mode 100644 index 00000000000..b2e0002e5cd --- /dev/null +++ b/test/regress/cli/regress0/proofs/proj-issue723-rdb-step.smt2 @@ -0,0 +1,6 @@ +; EXPECT: unsat +(set-logic ALL) +(declare-const x (_ BitVec 1)) +(set-option :check-proof-steps true) +(assert (bvult (_ bv0 10) (bvxor ((_ zero_extend 9) x) ((_ zero_extend 9) x)))) +(check-sat) diff --git a/test/regress/cli/regress0/strings/re-consume-bi-dir.smt2 b/test/regress/cli/regress0/strings/re-consume-bi-dir.smt2 new file mode 100644 index 00000000000..d2c5963e39f --- /dev/null +++ b/test/regress/cli/regress0/strings/re-consume-bi-dir.smt2 @@ -0,0 +1,8 @@ +; EXPECT: unsat +(set-logic ALL) +(declare-fun k () String) +(assert (not (str.in_re +(str.++ "d.1.a" k "3") +(re.++ (str.to_re "d.") (re.* (re.range "0" "9")) (str.to_re ".") (re.* re.allchar)) +))) +(check-sat) diff --git a/test/regress/cli/regress0/strings/re-consume-inter.smt2 b/test/regress/cli/regress0/strings/re-consume-inter.smt2 new file mode 100644 index 00000000000..6c52f515d4f --- /dev/null +++ b/test/regress/cli/regress0/strings/re-consume-inter.smt2 @@ -0,0 +1,9 @@ +; EXPECT: unsat +(set-logic ALL) +(declare-fun k () String) +(assert (str.in_re +(str.++ "a.13" k ".b") +(re.++ (str.to_re "a.") (re.inter (re.* (re.range "0" "9")) (re.++ (re.* re.allchar) (str.to_re "3"))) (str.to_re ".b")) +)) +(assert (or (= k "a") (= k "b"))) +(check-sat) diff --git a/test/regress/cli/regress0/strings/re-consume-sym-sigma.smt2 b/test/regress/cli/regress0/strings/re-consume-sym-sigma.smt2 new file mode 100644 index 00000000000..1fc4195b8fe --- /dev/null +++ b/test/regress/cli/regress0/strings/re-consume-sym-sigma.smt2 @@ -0,0 +1,6 @@ +; EXPECT: unsat +(set-logic ALL) +(declare-fun x () String) +(declare-fun y () String) +(assert (not (str.in_re (str.++ "abc" x "." y) (re.++ (str.to_re (str.++ "abc" x)) (re.* re.allchar) (str.to_re ".") (str.to_re y))))) +(check-sat) diff --git a/test/regress/cli/regress0/strings/re-consume-sym.smt2 b/test/regress/cli/regress0/strings/re-consume-sym.smt2 new file mode 100644 index 00000000000..90643e9ac63 --- /dev/null +++ b/test/regress/cli/regress0/strings/re-consume-sym.smt2 @@ -0,0 +1,6 @@ +; EXPECT: unsat +(set-logic ALL) +(declare-fun x () String) +(declare-fun y () String) +(assert (not (str.in_re (str.++ "abc" x "." y) (re.++ (str.to_re (str.++ "abc" x)) re.allchar (str.to_re y))))) +(check-sat) diff --git a/test/regress/cli/regress0/sygus/print-enumerator.sy b/test/regress/cli/regress0/sygus/print-enumerator.sy index f69ae1917e1..27889e69303 100644 --- a/test/regress/cli/regress0/sygus/print-enumerator.sy +++ b/test/regress/cli/regress0/sygus/print-enumerator.sy @@ -9,4 +9,6 @@ (synth-fun f ((x Int) (y Int)) Int ((Start Int)) ((Start Int (x)))) +(declare-var x Int) +(constraint (= (f x x) x)) (check-synth) diff --git a/test/regress/cli/regress0/sygus/print-grammar.sy b/test/regress/cli/regress0/sygus/print-grammar.sy index 28b52d3ce80..eb6819bedc6 100644 --- a/test/regress/cli/regress0/sygus/print-grammar.sy +++ b/test/regress/cli/regress0/sygus/print-grammar.sy @@ -1,6 +1,6 @@ ; REQUIRES: no-competition ; COMMAND-LINE: -o sygus-grammar -; EXPECT: (sygus-grammar f ((A_Int Int) (A_Bool Bool) )((A_Int Int (x y 0 1 (+ A_Int A_Int) (- A_Int A_Int) (ite A_Bool A_Int A_Int)))(A_Bool Bool (false true (= A_Int A_Int) (<= A_Int A_Int) (not A_Bool) (and A_Bool A_Bool) (or A_Bool A_Bool))))) +; EXPECT: (sygus-grammar f ((A_Int Int) (A_Bool Bool) )((A_Int Int (x y 0 1 (+ A_Int A_Int) (- A_Int A_Int) (ite A_Bool A_Int A_Int)))(A_Bool Bool (true false (= A_Int A_Int) (<= A_Int A_Int) (not A_Bool) (and A_Bool A_Bool) (or A_Bool A_Bool))))) ; EXPECT: ( ; EXPECT: (define-fun f ((x Int) (y Int)) Int 0) ; EXPECT: ) @@ -9,4 +9,7 @@ (synth-fun f ((x Int) (y Int)) Int) +(declare-var x Int) +(declare-var y Int) +(constraint (= (f x y) 0)) (check-synth) diff --git a/test/regress/cli/regress1/issue10750-zll-repeat.smt2 b/test/regress/cli/regress1/issue10750-zll-repeat.smt2 new file mode 100644 index 00000000000..a54a99e1e04 --- /dev/null +++ b/test/regress/cli/regress1/issue10750-zll-repeat.smt2 @@ -0,0 +1,7 @@ +; COMMAND-LINE: --produce-learned-literals +; EXPECT: sat +(set-logic QF_ABV) +(declare-fun m () (_ BitVec 6)) +(assert (not (bvsle (_ bv1 32) ((_ zero_extend 26) (bvsmod (_ bv0 6) (bvadd m (bvsmod (_ bv0 6) m))))))) +(assert (bvsge (_ bv63 6) m)) +(check-sat) diff --git a/test/regress/cli/regress1/nl/issue10724-real-as-int-types.smt2 b/test/regress/cli/regress1/nl/issue10724-real-as-int-types.smt2 new file mode 100644 index 00000000000..00c4f944cd9 --- /dev/null +++ b/test/regress/cli/regress1/nl/issue10724-real-as-int-types.smt2 @@ -0,0 +1,6 @@ +; COMMAND-LINE: --solve-real-as-int -q +; EXPECT: sat +(set-logic ALL) +(declare-fun v () Real) +(assert (and (forall ((a Real)) ((_ divisible 3) (set.choose (set.singleton (to_int (arcsin v)))))))) +(check-sat) diff --git a/test/regress/cli/regress1/proj-issue719-zll-repeat.smt2 b/test/regress/cli/regress1/proj-issue719-zll-repeat.smt2 new file mode 100644 index 00000000000..24f000f4a35 --- /dev/null +++ b/test/regress/cli/regress1/proj-issue719-zll-repeat.smt2 @@ -0,0 +1,6 @@ +; EXPECT: sat +(set-logic ALL) +(declare-const x (_ BitVec 63)) +(set-option :lemma-inprocess full) +(assert (fp.isNaN ((_ to_fp 11 53) ((_ zero_extend 1) x)))) +(check-sat) diff --git a/test/regress/cli/regress1/quantifiers/ho-grammar.smt2 b/test/regress/cli/regress1/quantifiers/ho-grammar.smt2 new file mode 100644 index 00000000000..a55a4d8f530 --- /dev/null +++ b/test/regress/cli/regress1/quantifiers/ho-grammar.smt2 @@ -0,0 +1,9 @@ +; COMMAND-LINE: --mbqi --mbqi-fast-sygus +; EXPECT: unsat +; DISABLE-TESTER: unsat-core +(set-logic HO_ALL) +(declare-fun g (Int Int) Int) +(declare-fun x () Int) +(declare-fun y () Int) +(assert (forall ((h (-> Int Int Int))) (not (= (h x y) (g y x))))) +(check-sat) diff --git a/test/regress/cli/regress1/quantifiers/lcl579-2-ho-mbqi.smt2 b/test/regress/cli/regress1/quantifiers/lcl579-2-ho-mbqi.smt2 new file mode 100644 index 00000000000..831f9b262af --- /dev/null +++ b/test/regress/cli/regress1/quantifiers/lcl579-2-ho-mbqi.smt2 @@ -0,0 +1,26 @@ +; COMMAND-LINE: --mbqi --mbqi-fast-sygus +; EXPECT: unsat +(set-logic HO_ALL) +(declare-sort a 0) +(declare-fun f () a) +(declare-fun g () a) +(assert + (not + (=> + (forall ((P (-> a Bool))) + (=> + (P f) + (P g) + ) + ) + (forall ((R (-> a Bool))) + (=> + (R g) + (R f) + ) + ) + ) + ) +) +(set-info :filename LCL579^2) +(check-sat-assuming ( true )) diff --git a/test/regress/cli/regress1/quantifiers/mbqi_fast_sy_check_ic.smt2 b/test/regress/cli/regress1/quantifiers/mbqi_fast_sy_check_ic.smt2 new file mode 100644 index 00000000000..323a2109ded --- /dev/null +++ b/test/regress/cli/regress1/quantifiers/mbqi_fast_sy_check_ic.smt2 @@ -0,0 +1,11 @@ +; COMMAND-LINE: --mbqi-fast-sygus --no-cegqi +; EXPECT: unsat +(set-logic BV) +(declare-fun t () (_ BitVec 4)) +(declare-fun s () (_ BitVec 4)) +(declare-fun xhi () (_ BitVec 4)) +(declare-fun xlo () (_ BitVec 4)) +(assert (not (= (not (forall ((x (_ BitVec 4))) (or (not (= (bvshl x s) t)) (not (= (bvand xhi x) x)) (not (= (bvor xlo x) x))))) (and (= (bvshl (bvlshr t s) s) t) (= (bvand t (bvshl xhi s)) t) (= (bvor t (bvshl xlo s)) t))))) +(assert true) +(assert (= (bvor xhi (bvnot xlo)) #b1111)) +(check-sat) diff --git a/test/regress/cli/regress1/sygus/issue10708-unconstrained-int.sy b/test/regress/cli/regress1/sygus/issue10708-unconstrained-int.sy new file mode 100644 index 00000000000..23af6c402f1 --- /dev/null +++ b/test/regress/cli/regress1/sygus/issue10708-unconstrained-int.sy @@ -0,0 +1,28 @@ +; COMMAND-LINE: --sygus-out=status +; EXPECT: feasible +(set-logic ALL) + +(define-sort |T@T| () Int) +(synth-fun aType ((x Int)) T@T) +(synth-fun bType ((x Int)) T@T ((StartT T@T)) ( (StartT T@T ((Constant T@T))))) +(synth-fun cType () T@T) +(synth-fun dType () T@T ((StartT T@T)) ( (StartT T@T ((Constant T@T))))) + + +(synth-fun X ((x Int) (y Int)) Int + ((Start Int) (StartBool Bool)) + ((Start Int (0 1 2 3 4 5 6 x y + (+ Start Start) + (- Start Start) + (ite StartBool Start Start))) + (StartBool Bool ((and StartBool StartBool) + (not StartBool) + (= Start Start) + (<= Start Start)))) +) + +(constraint (= (X 2 0) 1)) +(constraint (and (= (X 0 1) 5) (= (X 0 0) 6))) + + +(check-synth) diff --git a/test/unit/api/c/CMakeLists.txt b/test/unit/api/c/CMakeLists.txt index fb9b3ff667b..03ba668f744 100644 --- a/test/unit/api/c/CMakeLists.txt +++ b/test/unit/api/c/CMakeLists.txt @@ -16,4 +16,5 @@ # Generate and add unit test. cvc5_add_unit_test_black(capi_kind_black api/c) cvc5_add_unit_test_black(capi_sort_kind_black api/c) +cvc5_add_unit_test_black(capi_term_manager_black api/c) cvc5_add_unit_test_black(capi_types_black api/c) diff --git a/test/unit/api/c/capi_term_manager_black.cpp b/test/unit/api/c/capi_term_manager_black.cpp new file mode 100644 index 00000000000..f8a8d3eb89d --- /dev/null +++ b/test/unit/api/c/capi_term_manager_black.cpp @@ -0,0 +1,562 @@ +/****************************************************************************** + * Top contributors (to current version): + * Aina Niemetz + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2024 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. + * **************************************************************************** + * + * Black box testing of functions manipulating sorts of the C API. + */ + +extern "C" { +#include +} + +#include "base/output.h" +#include "gtest/gtest.h" + +namespace cvc5::internal::test { + +class TestCApiBlackTermManager : public ::testing::Test +{ + protected: + void SetUp() override + { + d_tm = cvc5_term_manager_new(); + // d_bool = cvc5_get_boolean_sort(d_tm); + // d_int = cvc5_get_integer_sort(d_tm); + // d_real = cvc5_get_real_sort(d_tm); + } + void TearDown() override { cvc5_term_manager_delete(d_tm); } + + Cvc5TermManager* d_tm; + // Cvc5Sort d_bool; + // Cvc5Sort d_int; + // Cvc5Sort d_real; +}; + +TEST_F(TestCApiBlackTermManager, new) {} + +TEST_F(TestCApiBlackTermManager, get_boolean_sort) +{ + (void)cvc5_get_boolean_sort(d_tm); + ASSERT_DEATH(cvc5_get_boolean_sort(nullptr), "unexpected NULL argument"); +} + +TEST_F(TestCApiBlackTermManager, get_integer_sort) +{ + (void)cvc5_get_integer_sort(d_tm); + ASSERT_DEATH(cvc5_get_integer_sort(nullptr), "unexpected NULL argument"); +} + +TEST_F(TestCApiBlackTermManager, get_real_sort) +{ + (void)cvc5_get_real_sort(d_tm); + ASSERT_DEATH(cvc5_get_real_sort(nullptr), "unexpected NULL argument"); +} + +TEST_F(TestCApiBlackTermManager, get_regexp_sort) +{ + (void)cvc5_get_regexp_sort(d_tm); + ASSERT_DEATH(cvc5_get_regexp_sort(nullptr), "unexpected NULL argument"); +} + +TEST_F(TestCApiBlackTermManager, get_string_sort) +{ + (void)cvc5_get_string_sort(d_tm); + ASSERT_DEATH(cvc5_get_string_sort(nullptr), "unexpected NULL argument"); +} + +TEST_F(TestCApiBlackTermManager, get_rm_sort) +{ + (void)cvc5_get_rm_sort(d_tm); + ASSERT_DEATH(cvc5_get_rm_sort(nullptr), "unexpected NULL argument"); +} + +TEST_F(TestCApiBlackTermManager, mk_array_sort) +{ + Cvc5Sort bsort = cvc5_get_boolean_sort(d_tm); + Cvc5Sort isort = cvc5_get_integer_sort(d_tm); + Cvc5Sort rsort = cvc5_get_real_sort(d_tm); + Cvc5Sort bvsort = cvc5_mk_bv_sort(d_tm, 32); + (void)cvc5_mk_array_sort(d_tm, bsort, bsort); + (void)cvc5_mk_array_sort(d_tm, isort, isort); + (void)cvc5_mk_array_sort(d_tm, rsort, rsort); + (void)cvc5_mk_array_sort(d_tm, bvsort, bvsort); + (void)cvc5_mk_array_sort(d_tm, bsort, isort); + (void)cvc5_mk_array_sort(d_tm, rsort, bvsort); + + Cvc5Sort fpsort = cvc5_mk_fp_sort(d_tm, 3, 5); + (void)cvc5_mk_array_sort(d_tm, fpsort, fpsort); + (void)cvc5_mk_array_sort(d_tm, bvsort, fpsort); + + ASSERT_DEATH(cvc5_mk_array_sort(nullptr, bvsort, fpsort), + "unexpected NULL argument"); + ASSERT_DEATH(cvc5_mk_array_sort(d_tm, nullptr, fpsort), "invalid sort"); + ASSERT_DEATH(cvc5_mk_array_sort(d_tm, bvsort, nullptr), "invalid sort"); + + Cvc5TermManager* tm = cvc5_term_manager_new(); + // this will throw when NodeManager is not a singleton anymore + (void)cvc5_mk_array_sort( + d_tm, cvc5_get_boolean_sort(tm), cvc5_get_integer_sort(tm)); + cvc5_term_manager_delete(tm); +} + +TEST_F(TestCApiBlackTermManager, mk_bv_sort) +{ + (void)cvc5_mk_bv_sort(d_tm, 32); + ASSERT_DEATH(cvc5_mk_bv_sort(nullptr, 4), "unexpected NULL argument"); + ASSERT_DEATH(cvc5_mk_bv_sort(d_tm, 0), "expected size > 0"); +} + +TEST_F(TestCApiBlackTermManager, mk_ff_sort) +{ + (void)cvc5_mk_ff_sort(d_tm, "31", 10); + ASSERT_DEATH(cvc5_mk_ff_sort(nullptr, "31", 10), "unexpected NULL argument"); + ASSERT_DEATH(cvc5_mk_ff_sort(d_tm, nullptr, 10), "unexpected NULL argument"); + ASSERT_DEATH(cvc5_mk_ff_sort(d_tm, "6", 10), "expected modulus is prime"); + + ASSERT_DEATH(cvc5_mk_ff_sort(d_tm, "b", 10), "mpz_set_str"); + + (void)cvc5_mk_ff_sort(d_tm, "1100101", 2); + (void)cvc5_mk_ff_sort(d_tm, "10202", 3); + (void)cvc5_mk_ff_sort(d_tm, "401", 5); + (void)cvc5_mk_ff_sort(d_tm, "791a", 11); + (void)cvc5_mk_ff_sort(d_tm, "970f", 16); + (void)cvc5_mk_ff_sort(d_tm, "8CC5", 16); + + ASSERT_DEATH(cvc5_mk_ff_sort(d_tm, "1100100", 2), + "expected modulus is prime"); + ASSERT_DEATH(cvc5_mk_ff_sort(d_tm, "10201", 3), "expected modulus is prime"); + ASSERT_DEATH(cvc5_mk_ff_sort(d_tm, "400", 5), "expected modulus is prime"); + ASSERT_DEATH(cvc5_mk_ff_sort(d_tm, "7919", 11), "expected modulus is prime"); + ASSERT_DEATH(cvc5_mk_ff_sort(d_tm, "970e", 16), "expected modulus is prime"); + ASSERT_DEATH(cvc5_mk_ff_sort(d_tm, "8CC4", 16), "expected modulus is prime"); +} + +TEST_F(TestCApiBlackTermManager, mk_fp_sort) +{ + (void)cvc5_mk_fp_sort(d_tm, 4, 8); + ASSERT_DEATH(cvc5_mk_fp_sort(nullptr, 4, 8), "unexpected NULL argument"); + ASSERT_DEATH(cvc5_mk_fp_sort(d_tm, 0, 8), "expected exponent size > 1"); + ASSERT_DEATH(cvc5_mk_fp_sort(d_tm, 4, 0), "expected significand size > 1"); + ASSERT_DEATH(cvc5_mk_fp_sort(d_tm, 1, 8), "expected exponent size > 1"); + ASSERT_DEATH(cvc5_mk_fp_sort(d_tm, 4, 1), "expected significand size > 1"); +} + +TEST_F(TestCApiBlackTermManager, mk_dt_sort) +{ + { + Cvc5DatatypeDecl decl = cvc5_mk_dt_decl(d_tm, "list", false); + Cvc5DatatypeConstructorDecl cons = cvc5_mk_dt_cons_decl(d_tm, "cons"); + cvc5_dt_cons_decl_add_selector(cons, "head", cvc5_get_integer_sort(d_tm)); + cvc5_dt_decl_add_constructor(decl, cons); + Cvc5DatatypeConstructorDecl nil = cvc5_mk_dt_cons_decl(d_tm, "nil"); + cvc5_dt_decl_add_constructor(decl, nil); + (void)cvc5_mk_dt_sort(d_tm, decl); + ASSERT_DEATH(cvc5_mk_dt_sort(nullptr, decl), "unexpected NULL argument"); + ASSERT_DEATH(cvc5_mk_dt_sort(d_tm, decl), "is already resolved"); + } + + { + Cvc5DatatypeDecl decl = cvc5_mk_dt_decl(d_tm, "list", false); + ASSERT_DEATH(cvc5_mk_dt_sort(d_tm, decl), "at least one constructor"); + } + + { + Cvc5TermManager* tm = cvc5_term_manager_new(); + ; + Cvc5DatatypeDecl decl = cvc5_mk_dt_decl(tm, "list", false); + Cvc5DatatypeConstructorDecl cons = cvc5_mk_dt_cons_decl(tm, "cons"); + cvc5_dt_cons_decl_add_selector(cons, "head", cvc5_get_integer_sort(tm)); + cvc5_dt_decl_add_constructor(decl, cons); + Cvc5DatatypeConstructorDecl nil = cvc5_mk_dt_cons_decl(tm, "nil"); + cvc5_dt_decl_add_constructor(decl, nil); + // this will throw when NodeManager is not a singleton anymore + (void)cvc5_mk_dt_sort(d_tm, decl); + cvc5_term_manager_delete(tm); + } +} + +TEST_F(TestCApiBlackTermManager, mk_dt_sorts) +{ + { + Cvc5DatatypeDecl decl1 = cvc5_mk_dt_decl(d_tm, "list1", false); + Cvc5DatatypeConstructorDecl cons1 = cvc5_mk_dt_cons_decl(d_tm, "cons1"); + cvc5_dt_cons_decl_add_selector(cons1, "head1", cvc5_get_integer_sort(d_tm)); + cvc5_dt_decl_add_constructor(decl1, cons1); + Cvc5DatatypeConstructorDecl nil1 = cvc5_mk_dt_cons_decl(d_tm, "nil1"); + cvc5_dt_decl_add_constructor(decl1, nil1); + Cvc5DatatypeDecl decl2 = cvc5_mk_dt_decl(d_tm, "list2", false); + Cvc5DatatypeConstructorDecl cons2 = cvc5_mk_dt_cons_decl(d_tm, "cons2"); + cvc5_dt_cons_decl_add_selector(cons2, "head2", cvc5_get_integer_sort(d_tm)); + cvc5_dt_decl_add_constructor(decl2, cons2); + Cvc5DatatypeConstructorDecl nil2 = cvc5_mk_dt_cons_decl(d_tm, "nil2"); + cvc5_dt_decl_add_constructor(decl2, nil2); + std::vector decls = {decl1, decl2}; + (void)cvc5_mk_dt_sorts(d_tm, decls.size(), decls.data()); + ASSERT_DEATH(cvc5_mk_dt_sorts(nullptr, decls.size(), decls.data()), + "unexpected NULL argument"); + ASSERT_DEATH(cvc5_mk_dt_sorts(d_tm, decls.size(), nullptr), + "unexpected NULL argument"); + + ASSERT_DEATH(cvc5_mk_dt_sorts(d_tm, decls.size(), decls.data()), + "is already resolved"); + } + + { + std::vector decls = { + cvc5_mk_dt_decl(d_tm, "list", "false")}; + ASSERT_DEATH(cvc5_mk_dt_sorts(d_tm, decls.size(), decls.data()), + "at least one constructor"); + } + + { + /* with unresolved sorts */ + Cvc5Sort unresList = cvc5_mk_unresolved_dt_sort(d_tm, "ulist", 0); + Cvc5DatatypeDecl ulist = cvc5_mk_dt_decl(d_tm, "ulist", false); + Cvc5DatatypeConstructorDecl ucons = cvc5_mk_dt_cons_decl(d_tm, "ucons"); + cvc5_dt_cons_decl_add_selector(ucons, "car", unresList); + cvc5_dt_cons_decl_add_selector(ucons, "cdr", unresList); + cvc5_dt_decl_add_constructor(ulist, ucons); + Cvc5DatatypeConstructorDecl unil = cvc5_mk_dt_cons_decl(d_tm, "unil"); + cvc5_dt_decl_add_constructor(ulist, unil); + std::vector udecls = {ulist}; + (void)cvc5_mk_dt_sorts(d_tm, udecls.size(), udecls.data()); + + ASSERT_DEATH(cvc5_mk_dt_sorts(d_tm, udecls.size(), udecls.data()), + "has already been used"); + } + + { + /* mutually recursive with unresolved parameterized sorts */ + Cvc5Sort p0 = cvc5_mk_param_sort(d_tm, "p0"); + Cvc5Sort p1 = cvc5_mk_param_sort(d_tm, "p1"); + Cvc5Sort u0 = cvc5_mk_unresolved_dt_sort(d_tm, "dt0", 1); + Cvc5Sort u1 = cvc5_mk_unresolved_dt_sort(d_tm, "dt1", 1); + std::vector sorts0 = {p0}; + std::vector sorts1 = {p1}; + Cvc5DatatypeDecl decl0 = cvc5_mk_dt_decl(d_tm, "dt0", sorts0.data()); + Cvc5DatatypeDecl decl1 = cvc5_mk_dt_decl(d_tm, "dt1", sorts1.data()); + Cvc5DatatypeConstructorDecl cons0 = cvc5_mk_dt_cons_decl(d_tm, "c0"); + cvc5_dt_cons_decl_add_selector( + cons0, "s0", cvc5_sort_instantiate(u1, sorts0.size(), sorts0.data())); + Cvc5DatatypeConstructorDecl cons1 = cvc5_mk_dt_cons_decl(d_tm, "c1"); + cvc5_dt_cons_decl_add_selector( + cons1, "s1", cvc5_sort_instantiate(u0, sorts1.size(), sorts1.data())); + cvc5_dt_decl_add_constructor(decl0, cons0); + cvc5_dt_decl_add_constructor(decl1, cons1); + cvc5_dt_decl_add_constructor(decl1, cvc5_mk_dt_cons_decl(d_tm, "nil")); + std::vector decls = {decl0, decl1}; + // const Cvc5Sort* dtsorts = + // cvc5_mk_dt_sorts(d_tm, decls.size(), decls.data()); + // std::vector iargs = {cvc5_get_boolean_sort(d_tm)}; + // Cvc5Sort isort = + // cvc5_sort_instantiate(dtsorts[1], iargs.size(), iargs.data()); + // Cvc5Term t1 = cvc5_mk_const(d_tm, isort, "t"); + // std::vector children = { + // cvc5_dt_sel_get_term(cvc5_dt_get_selector( + // cvc5_sort_get_datatype(cvc5_term_get_sort(t1)), "s1")), + // t1}; + // Cvc5Term t0 = cvc5_mk_term( + // CVC5_KIND_APPLY_SELECTOR, children.size(), children.data()); + // iargs = {cvc5_get_boolean_sort(d_tm)}; + // ASSERT_EQ(cvc5_sort_instantiate(dtsorts[0], iargs.size(), iargs.data()), + // cvc5_term_get_sort(t0)); + } + + { + Cvc5TermManager* tm = cvc5_term_manager_new(); + Cvc5DatatypeDecl decl1 = cvc5_mk_dt_decl(tm, "list1", false); + Cvc5DatatypeConstructorDecl cons1 = cvc5_mk_dt_cons_decl(tm, "cons1"); + cvc5_dt_cons_decl_add_selector(cons1, "head1", cvc5_get_integer_sort(tm)); + cvc5_dt_decl_add_constructor(decl1, cons1); + Cvc5DatatypeConstructorDecl nil1 = cvc5_mk_dt_cons_decl(tm, "nil1"); + cvc5_dt_decl_add_constructor(decl1, nil1); + Cvc5DatatypeDecl decl2 = cvc5_mk_dt_decl(tm, "list2", false); + Cvc5DatatypeConstructorDecl cons2 = cvc5_mk_dt_cons_decl(tm, "cons2"); + cvc5_dt_cons_decl_add_selector(cons2, "head2", cvc5_get_integer_sort(tm)); + cvc5_dt_decl_add_constructor(decl2, cons2); + Cvc5DatatypeConstructorDecl nil2 = cvc5_mk_dt_cons_decl(tm, "nil2"); + cvc5_dt_decl_add_constructor(decl2, nil2); + std::vector decls = {decl1, decl2}; + // this will throw when NodeManager is not a singleton anymore + (void)cvc5_mk_dt_sorts(d_tm, decls.size(), decls.data()); + cvc5_term_manager_delete(tm); + } +} + +TEST_F(TestCApiBlackTermManager, mk_fun_sort) +{ + Cvc5Sort unsort = cvc5_mk_uninterpreted_sort(d_tm, "u"); + std::vector domain = {unsort}; + Cvc5Sort intsort = cvc5_get_integer_sort(d_tm); + Cvc5Sort funsort = + cvc5_mk_fun_sort(d_tm, domain.size(), domain.data(), intsort); + + // function arguments are allowed + domain = {funsort}; + (void)cvc5_mk_fun_sort(d_tm, domain.size(), domain.data(), intsort); + ASSERT_DEATH(cvc5_mk_fun_sort(nullptr, domain.size(), domain.data(), intsort), + "unexpected NULL argument"); + ASSERT_DEATH(cvc5_mk_fun_sort(d_tm, domain.size(), nullptr, intsort), + "unexpected NULL argument"); + domain = {nullptr}; + ASSERT_DEATH(cvc5_mk_fun_sort(d_tm, domain.size(), domain.data(), intsort), + "invalid sort at index 0"); + // non-first-class arguments are not allowed + Cvc5Sort regexpsort = cvc5_get_regexp_sort(d_tm); + domain = {regexpsort}; + ASSERT_DEATH(cvc5_mk_fun_sort(d_tm, domain.size(), domain.data(), intsort), + "expected first-class sort as domain sort"); + domain = {intsort}; + ASSERT_DEATH(cvc5_mk_fun_sort(d_tm, domain.size(), domain.data(), funsort), + "expected non-function sort as codomain sort"); + + domain = {unsort, intsort}; + (void)cvc5_mk_fun_sort(d_tm, domain.size(), domain.data(), intsort); + + domain = {unsort}; + funsort = cvc5_mk_fun_sort(d_tm, domain.size(), domain.data(), intsort); + + // function arguments are allowed + domain = {funsort, unsort}; + (void)cvc5_mk_fun_sort(d_tm, domain.size(), domain.data(), intsort); + + domain = {intsort, unsort}; + ASSERT_DEATH(cvc5_mk_fun_sort(d_tm, domain.size(), domain.data(), funsort), + "expected non-function sort as codomain sort"); + + domain = {cvc5_get_boolean_sort(d_tm), intsort, intsort}; + (void)cvc5_mk_fun_sort(d_tm, domain.size(), domain.data(), intsort); + domain = {cvc5_get_boolean_sort(d_tm), intsort}; + (void)cvc5_mk_fun_sort(d_tm, domain.size(), domain.data(), intsort); + + Cvc5TermManager* tm = cvc5_term_manager_new(); + // this will throw when NodeManager is not a singleton anymore + (void)cvc5_mk_fun_sort( + tm, domain.size(), domain.data(), cvc5_get_integer_sort(tm)); + domain = {cvc5_get_boolean_sort(tm), cvc5_get_integer_sort(tm)}; + (void)cvc5_mk_fun_sort(tm, domain.size(), domain.data(), intsort); + cvc5_term_manager_delete(tm); +} + +TEST_F(TestCApiBlackTermManager, mk_param_sort) +{ + (void)cvc5_mk_param_sort(d_tm, "T"); + (void)cvc5_mk_param_sort(d_tm, ""); + ASSERT_DEATH(cvc5_mk_param_sort(nullptr, ""), "unexpected NULL argument"); + ASSERT_DEATH(cvc5_mk_param_sort(d_tm, nullptr), "unexpected NULL argument"); +} + +TEST_F(TestCApiBlackTermManager, mk_predicate_sort) +{ + Cvc5Sort intsort = cvc5_get_integer_sort(d_tm); + std::vector sorts = {intsort}; + (void)cvc5_mk_predicate_sort(d_tm, sorts.size(), sorts.data()); + ASSERT_DEATH(cvc5_mk_predicate_sort(nullptr, sorts.size(), sorts.data()), + "unexpected NULL argument"); + ASSERT_DEATH(cvc5_mk_predicate_sort(d_tm, sorts.size(), nullptr), + "unexpected NULL argument"); + sorts = {nullptr}; + ASSERT_DEATH(cvc5_mk_predicate_sort(d_tm, sorts.size(), sorts.data()), + "invalid sort at index 0"); + + sorts = {}; + ASSERT_DEATH(cvc5_mk_predicate_sort(d_tm, sorts.size(), sorts.data()), + "expected at least one parameter sort"); + + sorts = {nullptr}; + ASSERT_DEATH(cvc5_mk_predicate_sort(d_tm, sorts.size(), sorts.data()), + "invalid sort at index 0"); + + sorts = {cvc5_mk_uninterpreted_sort(d_tm, "u"), intsort}; + Cvc5Sort funsort = + cvc5_mk_fun_sort(d_tm, sorts.size(), sorts.data(), intsort); + // functions as arguments are allowed + sorts = {funsort, intsort}; + (void)cvc5_mk_predicate_sort(d_tm, sorts.size(), sorts.data()); + + Cvc5TermManager* tm = cvc5_term_manager_new(); + // this will throw when NodeManager is not a singleton anymore + sorts = {intsort}; + (void)cvc5_mk_predicate_sort(tm, sorts.size(), sorts.data()); + cvc5_term_manager_delete(tm); +} + +TEST_F(TestCApiBlackTermManager, mk_record_sort) +{ + std::vector names = {}; + std::vector sorts = {}; + (void)cvc5_mk_record_sort(d_tm, names.size(), names.data(), sorts.data()); + ASSERT_DEATH( + cvc5_mk_record_sort(nullptr, names.size(), names.data(), sorts.data()), + "unexpected NULL argument"); + + names = {"b", nullptr, "i"}; + ASSERT_DEATH( + cvc5_mk_record_sort(d_tm, names.size(), names.data(), sorts.data()), + "unexpected NULL argument"); + + names = {"b", "bv", "i"}; + + sorts = {nullptr, cvc5_mk_bv_sort(d_tm, 8), cvc5_get_integer_sort(d_tm)}; + ASSERT_DEATH( + cvc5_mk_record_sort(d_tm, names.size(), names.data(), sorts.data()), + "invalid sort at index 0"); + + sorts = {cvc5_get_boolean_sort(d_tm), + cvc5_mk_bv_sort(d_tm, 8), + cvc5_get_integer_sort(d_tm)}; + // Cvc5Sort recsort = + cvc5_mk_record_sort(d_tm, names.size(), names.data(), sorts.data()); + + //(void)cvc5_sort_get_datatype(recsort); + (void)cvc5_mk_record_sort(d_tm, names.size(), names.data(), sorts.data()); + + Cvc5TermManager* tm = cvc5_term_manager_new(); + // this will throw when NodeManager is not a singleton anymore + sorts = {cvc5_get_boolean_sort(tm), + cvc5_mk_bv_sort(d_tm, 8), + cvc5_get_integer_sort(tm)}; + (void)cvc5_mk_record_sort(d_tm, names.size(), names.data(), sorts.data()); + cvc5_term_manager_delete(tm); +} + +TEST_F(TestCApiBlackTermManager, mk_set_sort) +{ + (void)cvc5_mk_set_sort(d_tm, cvc5_get_boolean_sort(d_tm)); + (void)cvc5_mk_set_sort(d_tm, cvc5_get_integer_sort(d_tm)); + (void)cvc5_mk_set_sort(d_tm, cvc5_mk_bv_sort(d_tm, 4)); + (void)cvc5_mk_set_sort(d_tm, cvc5_mk_bv_sort(d_tm, 4)); + ASSERT_DEATH(cvc5_mk_set_sort(nullptr, cvc5_get_boolean_sort(d_tm)), + "unexpected NULL argument"); + ASSERT_DEATH(cvc5_mk_set_sort(d_tm, nullptr), "invalid sort"); + Cvc5TermManager* tm = cvc5_term_manager_new(); + // this will throw when NodeManager is not a singleton anymore + (void)cvc5_mk_set_sort(d_tm, cvc5_get_boolean_sort(tm)); + cvc5_term_manager_delete(tm); +} + +TEST_F(TestCApiBlackTermManager, mk_bag_sort) +{ + (void)cvc5_mk_bag_sort(d_tm, cvc5_get_boolean_sort(d_tm)); + (void)cvc5_mk_bag_sort(d_tm, cvc5_get_integer_sort(d_tm)); + (void)cvc5_mk_bag_sort(d_tm, cvc5_mk_bv_sort(d_tm, 4)); + (void)cvc5_mk_bag_sort(d_tm, cvc5_mk_bv_sort(d_tm, 4)); + ASSERT_DEATH(cvc5_mk_bag_sort(nullptr, cvc5_get_boolean_sort(d_tm)), + "unexpected NULL argument"); + ASSERT_DEATH(cvc5_mk_bag_sort(d_tm, nullptr), "invalid sort"); + Cvc5TermManager* tm = cvc5_term_manager_new(); + // this will throw when NodeManager is not a singleton anymore + (void)cvc5_mk_bag_sort(d_tm, cvc5_get_boolean_sort(tm)); + cvc5_term_manager_delete(tm); +} + +TEST_F(TestCApiBlackTermManager, mk_sequence_sort) +{ + (void)cvc5_mk_sequence_sort(d_tm, cvc5_get_boolean_sort(d_tm)); + (void)cvc5_mk_sequence_sort(d_tm, cvc5_get_integer_sort(d_tm)); + (void)cvc5_mk_sequence_sort(d_tm, cvc5_mk_bv_sort(d_tm, 4)); + (void)cvc5_mk_sequence_sort(d_tm, cvc5_mk_bv_sort(d_tm, 4)); + ASSERT_DEATH(cvc5_mk_sequence_sort(nullptr, cvc5_get_boolean_sort(d_tm)), + "unexpected NULL argument"); + ASSERT_DEATH(cvc5_mk_sequence_sort(d_tm, nullptr), "invalid sort"); + Cvc5TermManager* tm = cvc5_term_manager_new(); + // this will throw when NodeManager is not a singleton anymore + (void)cvc5_mk_sequence_sort(d_tm, cvc5_get_boolean_sort(tm)); + cvc5_term_manager_delete(tm); +} + +TEST_F(TestCApiBlackTermManager, mk_abstract_sort) +{ + (void)cvc5_mk_abstract_sort(d_tm, CVC5_SORT_KIND_ARRAY_SORT); + (void)cvc5_mk_abstract_sort(d_tm, CVC5_SORT_KIND_BITVECTOR_SORT); + (void)cvc5_mk_abstract_sort(d_tm, CVC5_SORT_KIND_TUPLE_SORT); + (void)cvc5_mk_abstract_sort(d_tm, CVC5_SORT_KIND_SET_SORT); + ASSERT_DEATH(cvc5_mk_abstract_sort(nullptr, CVC5_SORT_KIND_SET_SORT), + "unexpected NULL argument"); + ASSERT_DEATH(cvc5_mk_abstract_sort(d_tm, CVC5_SORT_KIND_BOOLEAN_SORT), + "Cannot construct abstract type"); + ASSERT_DEATH(cvc5_mk_abstract_sort(d_tm, static_cast(-1)), + "Cannot construct abstract type"); +} + +TEST_F(TestCApiBlackTermManager, mk_uninterpreted_sort) +{ + (void)cvc5_mk_uninterpreted_sort(d_tm, "u"); + (void)cvc5_mk_uninterpreted_sort(d_tm, ""); + ASSERT_DEATH(cvc5_mk_uninterpreted_sort(nullptr, ""), + "unexpected NULL argument"); +} + +TEST_F(TestCApiBlackTermManager, mk_unresolved_dt_sort) +{ + (void)cvc5_mk_unresolved_dt_sort(d_tm, "u", 0); + (void)cvc5_mk_unresolved_dt_sort(d_tm, "u", 1); + (void)cvc5_mk_unresolved_dt_sort(d_tm, "", 0); + (void)cvc5_mk_unresolved_dt_sort(d_tm, "", 1); + ASSERT_DEATH(cvc5_mk_unresolved_dt_sort(nullptr, "", 1), + "unexpected NULL argument"); + ASSERT_DEATH(cvc5_mk_unresolved_dt_sort(d_tm, nullptr, 1), + "unexpected NULL argument"); +} + +TEST_F(TestCApiBlackTermManager, mk_uninterpreted_sort_constructor_sort) +{ + (void)cvc5_mk_uninterpreted_sort_constructor_sort(d_tm, 2, "s"); + (void)cvc5_mk_uninterpreted_sort_constructor_sort(d_tm, 2, ""); + (void)cvc5_mk_uninterpreted_sort_constructor_sort(d_tm, 2, nullptr); + ASSERT_DEATH(cvc5_mk_uninterpreted_sort_constructor_sort(nullptr, 2, "s"), + "unexpected NULL argument"); + (void)cvc5_mk_uninterpreted_sort_constructor_sort(d_tm, 2, nullptr); + ASSERT_DEATH(cvc5_mk_uninterpreted_sort_constructor_sort(d_tm, 0, ""), + "expected an arity > 0"); +} + +TEST_F(TestCApiBlackTermManager, mk_tuple_sort) +{ + Cvc5Sort intsort = cvc5_get_integer_sort(d_tm); + std::vector sorts = {intsort}; + (void)cvc5_mk_tuple_sort(d_tm, sorts.size(), sorts.data()); + ASSERT_DEATH(cvc5_mk_tuple_sort(nullptr, sorts.size(), sorts.data()), + "unexpected NULL argument"); + ASSERT_DEATH(cvc5_mk_tuple_sort(nullptr, sorts.size(), nullptr), + "unexpected NULL argument"); + sorts = {intsort, nullptr}; + ASSERT_DEATH(cvc5_mk_tuple_sort(d_tm, sorts.size(), sorts.data()), + "invalid sort at index 1"); + sorts = {cvc5_mk_uninterpreted_sort(d_tm, "u"), intsort}; + Cvc5Sort funsort = cvc5_mk_tuple_sort(d_tm, sorts.size(), sorts.data()); + sorts = {intsort, funsort}; + (void)cvc5_mk_tuple_sort(d_tm, sorts.size(), sorts.data()); + sorts = {intsort}; + (void)cvc5_mk_tuple_sort(d_tm, sorts.size(), sorts.data()); + + Cvc5TermManager* tm = cvc5_term_manager_new(); + // this will throw when NodeManager is not a singleton anymore + sorts = {cvc5_get_boolean_sort(tm)}; + (void)cvc5_mk_tuple_sort(d_tm, sorts.size(), sorts.data()); + cvc5_term_manager_delete(tm); +} + +TEST_F(TestCApiBlackTermManager, mk_nullable_sort) +{ + Cvc5Sort intsort = cvc5_get_integer_sort(d_tm); + (void)cvc5_mk_nullable_sort(d_tm, intsort); + (void)cvc5_mk_nullable_sort(d_tm, intsort); + ASSERT_DEATH(cvc5_mk_nullable_sort(nullptr, intsort), + "unexpected NULL argument"); + ASSERT_DEATH(cvc5_mk_nullable_sort(d_tm, nullptr), "invalid sort"); + Cvc5TermManager* tm = cvc5_term_manager_new(); + // this will throw when NodeManager is not a singleton anymore + (void)cvc5_mk_nullable_sort(tm, intsort); + cvc5_term_manager_delete(tm); +} + +} // namespace cvc5::internal::test diff --git a/test/unit/api/cpp/api_term_manager_black.cpp b/test/unit/api/cpp/api_term_manager_black.cpp index c8744a2dc88..bffad2ecda1 100644 --- a/test/unit/api/cpp/api_term_manager_black.cpp +++ b/test/unit/api/cpp/api_term_manager_black.cpp @@ -258,10 +258,6 @@ TEST_F(TestApiBlackTermManager, mkFunctionSort) {d_tm.getIntegerSort(), d_tm.mkUninterpretedSort("u")}, funSort2), CVC5ApiException); - ASSERT_NO_THROW(d_tm.mkFunctionSort({d_tm.mkUninterpretedSort("u")}, - d_tm.getIntegerSort())); - ASSERT_NO_THROW(d_tm.mkFunctionSort({d_tm.mkUninterpretedSort("u")}, - d_tm.getIntegerSort())); std::vector sorts1 = { d_tm.getBooleanSort(), d_tm.getIntegerSort(), d_tm.getIntegerSort()}; std::vector sorts2 = {d_tm.getBooleanSort(), d_tm.getIntegerSort()}; @@ -399,7 +395,7 @@ TEST_F(TestApiBlackTermManager, mkNullableSort) ASSERT_NO_THROW(d_tm.mkNullableSort(d_tm.getIntegerSort())); TermManager tm; // this will throw when NodeManager is not a singleton anymore - ASSERT_NO_THROW(d_tm.mkNullableSort({tm.getIntegerSort()})); + ASSERT_NO_THROW(d_tm.mkNullableSort(tm.getIntegerSort())); } TEST_F(TestApiBlackTermManager, mkBitVector) diff --git a/test/unit/api/cpp/solver_black.cpp b/test/unit/api/cpp/solver_black.cpp index 855f6164a81..030b525b38e 100644 --- a/test/unit/api/cpp/solver_black.cpp +++ b/test/unit/api/cpp/solver_black.cpp @@ -50,7 +50,7 @@ TEST_F(TestApiBlackSolver, pow2Large1) Term t180 = d_tm.mkTerm(Kind::POW2, {t10}); Term t258 = d_tm.mkTerm(Kind::GEQ, {t74, t180}); d_solver->assertFormula(t258); - ASSERT_THROW(d_solver->simplify(t82), CVC5ApiException); + ASSERT_THROW(d_solver->simplify(t82, true), CVC5ApiException); } TEST_F(TestApiBlackSolver, pow2Large2) @@ -1821,6 +1821,20 @@ TEST_F(TestApiBlackSolver, simplify) ASSERT_NO_THROW(slv.simplify(x)); } +TEST_F(TestApiBlackSolver, simplifyApplySubs) +{ + d_solver->setOption("incremental", "true"); + Sort intSort = d_tm.getIntegerSort(); + Term x = d_tm.mkConst(intSort, "x"); + Term zero = d_tm.mkInteger(0); + Term eq = d_tm.mkTerm(Kind::EQUAL, {x, zero}); + d_solver->assertFormula(eq); + ASSERT_NO_THROW(d_solver->checkSat()); + + ASSERT_EQ(d_solver->simplify(x, false), x); + ASSERT_EQ(d_solver->simplify(x, true), zero); +} + TEST_F(TestApiBlackSolver, assertFormula) { ASSERT_NO_THROW(d_solver->assertFormula(d_tm.mkTrue())); diff --git a/test/unit/api/java/SolverTest.java b/test/unit/api/java/SolverTest.java index a6202c710ff..914b3b5896d 100644 --- a/test/unit/api/java/SolverTest.java +++ b/test/unit/api/java/SolverTest.java @@ -1594,6 +1594,21 @@ void setInfo() throws CVC5ApiException assertThrows(CVC5ApiException.class, () -> d_solver.setInfo("status", "asdf")); } + @Test + void simplifyApplySubs() throws CVC5ApiException + { + d_solver.setOption("incremental", "true"); + Sort intSort = d_tm.getIntegerSort(); + Term x = d_solver.mkConst(intSort, "x"); + Term zero = d_solver.mkInteger(0); + Term eq = d_solver.mkTerm(EQUAL, x, zero); + d_solver.assertFormula(eq); + assertDoesNotThrow(() -> d_solver.checkSat()); + + assertEquals(d_solver.simplify(x, false), x); + assertEquals(d_solver.simplify(x, true), zero); + } + @Test void simplify() throws CVC5ApiException { diff --git a/test/unit/api/python/test_solver.py b/test/unit/api/python/test_solver.py index 4e78bc768e9..acb89749682 100644 --- a/test/unit/api/python/test_solver.py +++ b/test/unit/api/python/test_solver.py @@ -1159,6 +1159,17 @@ def test_simplify(tm, solver): #with pytest.raises(RuntimeError): slv.simplify(x) +def test_simplify_apply_subs(tm, solver): + solver.setOption("incremental", "true") + intSort = tm.getIntegerSort() + x = tm.mkConst(intSort, "x") + zero = tm.mkInteger(0) + eq = tm.mkTerm(Kind.EQUAL, x, zero) + solver.assertFormula(eq) + solver.checkSat() + + assert solver.simplify(x, False) == x + assert solver.simplify(x, True) == zero def test_assert_formula(tm, solver): solver.assertFormula(tm.mkTrue())