diff options
200 files changed, 5232 insertions, 1578 deletions
diff --git a/.github/actions/run-tests/action.yml b/.github/actions/run-tests/action.yml index e5557bb2d..2b2326699 100644 --- a/.github/actions/run-tests/action.yml +++ b/.github/actions/run-tests/action.yml @@ -7,6 +7,10 @@ inputs: default: false check-unit-tests: default: true + regressions-args: + default: "--no-check-unsat-cores --no-check-proofs" + regressions-exclude: + default: "3-4" runs: using: composite steps: @@ -15,9 +19,9 @@ runs: run: | make -j${{ env.num_proc }} check env: - ARGS: --output-on-failure -LE regress[${{ matrix.exclude_regress }}] + ARGS: --output-on-failure -LE regress[${{ inputs.regressions-exclude }}] CVC5_REGRESSION_ARGS: --no-early-exit - RUN_REGRESSION_ARGS: ${{ matrix.run_regression_args }} + RUN_REGRESSION_ARGS: ${{ inputs.regressions-args }} working-directory: build - name: Run Unit Tests diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d88cad49..89c108b93 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: check-examples: true store-to-release: true exclude_regress: 3-4 - run_regression_args: --no-check-unsat-cores --no-check-proofs + run_regression_args: --tester base --tester model --tester synth --tester abduct - name: macos:production os: macos-11 @@ -25,7 +25,7 @@ jobs: check-examples: true store-to-release: true exclude_regress: 3-4 - run_regression_args: --no-check-unsat-cores --no-check-proofs + run_regression_args: --tester base --tester model --tester synth --tester abduct - name: ubuntu:production-clang os: ubuntu-18.04 @@ -34,7 +34,7 @@ jobs: cache-key: productionclang check-examples: true exclude_regress: 3-4 - run_regression_args: --no-check-unsat-cores --no-check-proofs + run_regression_args: --tester base --tester model --tester synth --tester abduct - name: ubuntu:production-dbg os: ubuntu-18.04 @@ -42,7 +42,7 @@ jobs: cache-key: dbg check-units: true exclude_regress: 3-4 - run_regression_args: --no-check-unsat-cores + run_regression_args: --tester base --tester model --tester synth --tester abduct --tester proof - name: ubuntu:production-dbg-clang os: ubuntu-latest @@ -50,7 +50,7 @@ jobs: config: production --auto-download --assertions --tracing --cln --gpl cache-key: dbgclang exclude_regress: 3-4 - run_regression_args: --no-check-proofs + run_regression_args: --tester base --tester model --tester synth --tester abduct --tester unsat-core name: ${{ matrix.name }} runs-on: ${{ matrix.os }} @@ -89,6 +89,8 @@ jobs: check-examples: ${{ matrix.check-examples }} check-python-bindings: ${{ matrix.python-bindings }} check-unit-tests: ${{ matrix.check-units }} + regressions-args: ${{ matrix.run_regression_args }} + regressions-exclude: ${{ matrix.exclude_regress }} - name: Build documentation if: matrix.build-documentation diff --git a/.github/workflows/cmake-version.yml b/.github/workflows/cmake-version.yml index d61c59088..24d87ba3b 100644 --- a/.github/workflows/cmake-version.yml +++ b/.github/workflows/cmake-version.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: cmake_version: [ - "3.12", "3.13", "3.14", "3.15", "3.16", + "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.15", "3.16", "3.17", "3.18", "3.19", "3.20", "3.21" ] @@ -21,6 +21,10 @@ jobs: - uses: actions/checkout@v2 + - name: Adapt cmake version checks + run: | + sed -i'orig' -E 's/cmake_minimum_required\(VERSION [0-9.]+\)/cmake_minimum_required(VERSION ${{ matrix.cmake_version }})/' CMakeLists.txt + - name: Install dependencies uses: ./.github/actions/install-dependencies with: diff --git a/.github/workflows/docs_upload.yml b/.github/workflows/docs_upload.yml index d9e81b05e..7bdd186a1 100644 --- a/.github/workflows/docs_upload.yml +++ b/.github/workflows/docs_upload.yml @@ -55,7 +55,11 @@ jobs: - name: Setup Context run: | HASH=${{ github.event.workflow_run.head_commit.id }} - if [ "${{ github.event.workflow_run.event }}" == "push" ] ; then + ISTAG=${{ startsWith(github.ref, 'refs/tags/') }} + if [ "$ISTAG" = true ] ; then + NAME=${{ github.event.workflow_run.head_branch }} + echo "Identified tag $NAME" + elif [ "${{ github.event.workflow_run.event }}" == "push" ] ; then NAME=${{ github.event.workflow_run.head_branch }} echo "Identified branch $NAME" elif [ "${{ github.event.workflow_run.event }}" == "pull_request" ] ; then @@ -66,6 +70,7 @@ jobs: fi echo "NAME=$NAME" >> $GITHUB_ENV echo "HASH=$HASH" >> $GITHUB_ENV + echo "ISTAG=$ISTAG" >> $GITHUB_ENV - name: Update docs continue-on-error: true @@ -76,7 +81,9 @@ jobs: mv docs-new target/docs-$NAME-$HASH cd target/ - if diff -r -x "*.zip" docs-master/ docs-$NAME-$HASH + isdiff=$(diff -r -x "*.zip" docs-master/ docs-$NAME-$HASH >&2; echo $?; exit 0) + + if [[ ("$ISTAG" != true) && ($isdiff = 0) ]] then echo "Ignored run, documentation is the same as for current master" else diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d173c845..2f6fa434e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -488,6 +488,8 @@ endif() add_subdirectory(test) +include(target-graphs) + #-----------------------------------------------------------------------------# # Package configuration # diff --git a/cmake/CMakeGraphVizOptions.cmake.in b/cmake/CMakeGraphVizOptions.cmake.in new file mode 100644 index 000000000..d3be598b3 --- /dev/null +++ b/cmake/CMakeGraphVizOptions.cmake.in @@ -0,0 +1,28 @@ +############################################################################### +# Top contributors (to current version): +# Gereon Kremer +# +# This file is part of the cvc5 project. +# +# Copyright (c) 2009-2021 by the authors listed in the file AUTHORS +# in the top-level source directory and their institutional affiliations. +# All rights reserved. See the file COPYING in the top-level source +# directory for licensing information. +# ############################################################################# +# Configuration file for the generation of cmake dependency graphs of cmake +# targets. +## + +set(CTX "@CMAKE_BINARY_DIR@/src/context/CMakeFiles/cvc5base.dir/") +set(BASE "@CMAKE_BINARY_DIR@/src/base/CMakeFiles/cvc5base.dir/") + +# ignore targets that do not actually help the understanding or are (usually) +# not interesting: tests and object files. +set(GRAPHVIZ_IGNORE_TARGETS + main-test + @APITESTS@ + ${CTX}context.cpp.o ${CTX}context_mm.cpp.o + ${BASE}check.cpp.o ${BASE}configuration.cpp.o ${BASE}exception.cpp.o + ${BASE}git_versioninfo.cpp.o ${BASE}listener.cpp.o ${BASE}output.cpp.o +) +set(GRAPHVIZ_GENERATE_DEPENDERS FALSE)
\ No newline at end of file diff --git a/cmake/ConfigureCvc5.cmake b/cmake/ConfigureCvc5.cmake index 009e9969b..7de13f046 100644 --- a/cmake/ConfigureCvc5.cmake +++ b/cmake/ConfigureCvc5.cmake @@ -59,7 +59,7 @@ check_include_file_cxx(ext/stdio_filebuf.h HAVE_EXT_STDIO_FILEBUF_H) # For Windows builds check if clock_gettime is available via -lpthread # (pthread_time.h). -if(CVC5_WINDOWS_BUILD) +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") set(CMAKE_REQUIRED_FLAGS -pthread) check_symbol_exists(clock_gettime "time.h" HAVE_CLOCK_GETTIME) unset(CMAKE_REQUIRED_FLAGS) diff --git a/cmake/FindANTLR3.cmake b/cmake/FindANTLR3.cmake index 60622ab92..772f5f43e 100644 --- a/cmake/FindANTLR3.cmake +++ b/cmake/FindANTLR3.cmake @@ -128,7 +128,7 @@ if(NOT ANTLR3_FOUND_SYSTEM) ${64bit} --host=${TOOLCHAIN_PREFIX} BUILD_BYPRODUCTS <INSTALL_DIR>/${CMAKE_INSTALL_LIBDIR}/libantlr3c.a - <INSTALL_DIR>/${CMAKE_INSTALL_LIBDIR}/libantlr3c.so + <INSTALL_DIR>/${CMAKE_INSTALL_LIBDIR}/libantlr3c${CMAKE_SHARED_LIBRARY_SUFFIX} ) set(ANTLR3_JAR "${DEPS_BASE}/share/java/antlr-3.4-complete.jar") @@ -151,6 +151,9 @@ set_target_properties(ANTLR3_SHARED PROPERTIES IMPORTED_LOCATION "${ANTLR3_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${ANTLR3_INCLUDE_DIR}" ) +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set_target_properties(ANTLR3_SHARED PROPERTIES IMPORTED_IMPLIB "${ANTLR3_LIBRARIES}") +endif() if(ENABLE_STATIC_LIBRARY) add_library(ANTLR3_STATIC STATIC IMPORTED GLOBAL) diff --git a/cmake/FindCryptoMiniSat.cmake b/cmake/FindCryptoMiniSat.cmake index 5b543aedc..b88eaa33e 100644 --- a/cmake/FindCryptoMiniSat.cmake +++ b/cmake/FindCryptoMiniSat.cmake @@ -44,6 +44,12 @@ if(NOT CryptoMiniSat_FOUND_SYSTEM) include(ExternalProject) + if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(LIBFILENAME "libcryptominisat5win") + else() + set(LIBFILENAME "libcryptominisat5") + endif() + ExternalProject_Add( CryptoMiniSat-EP ${COMMON_EP_CONFIG} @@ -77,7 +83,7 @@ if(NOT CryptoMiniSat_FOUND_SYSTEM) ) set(CryptoMiniSat_INCLUDE_DIR "${DEPS_BASE}/include/") - set(CryptoMiniSat_LIBRARIES "${DEPS_BASE}/${CMAKE_INSTALL_LIBDIR}/libcryptominisat5.a") + set(CryptoMiniSat_LIBRARIES "${DEPS_BASE}/${CMAKE_INSTALL_LIBDIR}/${LIBFILENAME}.a") add_library(CryptoMiniSat STATIC IMPORTED GLOBAL) set_target_properties( diff --git a/cmake/FindGMP.cmake b/cmake/FindGMP.cmake index bd51e8686..6dad15954 100644 --- a/cmake/FindGMP.cmake +++ b/cmake/FindGMP.cmake @@ -48,6 +48,7 @@ if(ENABLE_STATIC_LIBRARY AND GMP_FOUND_SYSTEM) if(NOT GMP_STATIC_LIBRARIES) set(GMP_FOUND_SYSTEM FALSE) endif() + set(GMP_STATIC_INCLUDE_DIR "${GMP_INCLUDE_DIR}") reset_force_static_library() endif() @@ -61,21 +62,63 @@ if(NOT GMP_FOUND_SYSTEM) set(GMP_VERSION "6.2.1") - ExternalProject_Add( - GMP-EP - ${COMMON_EP_CONFIG} - URL https://gmplib.org/download/gmp/gmp-${GMP_VERSION}.tar.bz2 - URL_HASH SHA1=2dcf34d4a432dbe6cce1475a835d20fe44f75822 - CONFIGURE_COMMAND - <SOURCE_DIR>/configure --prefix=<INSTALL_DIR> --enable-cxx --with-pic - --enable-shared --enable-static --host=${TOOLCHAIN_PREFIX} - BUILD_BYPRODUCTS <INSTALL_DIR>/lib/libgmp.a - <INSTALL_DIR>/lib/libgmp${CMAKE_SHARED_LIBRARY_SUFFIX} - ) + if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + # on windows, the gmp.h is different for shared and static builds. + # we thus need two separate builds. as the configure script taints the + # source folder, we even need two source folders. + ExternalProject_Add( + GMP-EP-shared + ${COMMON_EP_CONFIG} + URL https://gmplib.org/download/gmp/gmp-${GMP_VERSION}.tar.bz2 + URL_HASH SHA1=2dcf34d4a432dbe6cce1475a835d20fe44f75822 + DOWNLOAD_NAME gmp-${GMP_VERSION}-shared.tar.bz2 + CONFIGURE_COMMAND + <SOURCE_DIR>/configure --enable-shared --disable-static + --prefix=<INSTALL_DIR>/gmp-shared + --enable-cxx --with-pic --host=${TOOLCHAIN_PREFIX} + BUILD_BYPRODUCTS <INSTALL_DIR>/lib/libgmp${CMAKE_SHARED_LIBRARY_SUFFIX} + ) + ExternalProject_Add( + GMP-EP-static + ${COMMON_EP_CONFIG} + URL https://gmplib.org/download/gmp/gmp-${GMP_VERSION}.tar.bz2 + URL_HASH SHA1=2dcf34d4a432dbe6cce1475a835d20fe44f75822 + DOWNLOAD_NAME gmp-${GMP_VERSION}-static.tar.bz2 + CONFIGURE_COMMAND + <SOURCE_DIR>/configure --disable-shared --enable-static + --prefix=<INSTALL_DIR>/gmp-static + --enable-cxx --with-pic --host=${TOOLCHAIN_PREFIX} + BUILD_BYPRODUCTS <INSTALL_DIR>/lib/libgmp.a + ) - set(GMP_INCLUDE_DIR "${DEPS_BASE}/include/") - set(GMP_LIBRARIES "${DEPS_BASE}/lib/libgmp${CMAKE_SHARED_LIBRARY_SUFFIX}") - set(GMP_STATIC_LIBRARIES "${DEPS_BASE}/lib/libgmp.a") + add_custom_target(GMP-EP DEPENDS GMP-EP-shared GMP-EP-static) + + set(GMP_INCLUDE_DIR "${DEPS_BASE}/gmp-shared/include/") + set(GMP_STATIC_INCLUDE_DIR "${DEPS_BASE}/gmp-static/include/") + set(GMP_LIBRARIES "${DEPS_BASE}/gmp-shared/bin/libgmp-10${CMAKE_SHARED_LIBRARY_SUFFIX}") + set(GMP_STATIC_LIBRARIES "${DEPS_BASE}/gmp-static/lib/libgmp.a") + + file(MAKE_DIRECTORY "${GMP_INCLUDE_DIR}") + file(MAKE_DIRECTORY "${GMP_STATIC_INCLUDE_DIR}") + else() + ExternalProject_Add( + GMP-EP + ${COMMON_EP_CONFIG} + URL https://gmplib.org/download/gmp/gmp-${GMP_VERSION}.tar.bz2 + URL_HASH SHA1=2dcf34d4a432dbe6cce1475a835d20fe44f75822 + CONFIGURE_COMMAND + <SOURCE_DIR>/configure --enable-shared --enable-static + --prefix=<INSTALL_DIR> + --enable-cxx --with-pic --host=${TOOLCHAIN_PREFIX} + BUILD_BYPRODUCTS <INSTALL_DIR>/lib/libgmp.a + <INSTALL_DIR>/lib/libgmp${CMAKE_SHARED_LIBRARY_SUFFIX} + ) + + set(GMP_INCLUDE_DIR "${DEPS_BASE}/include/") + set(GMP_STATIC_INCLUDE_DIR "${DEPS_BASE}/include/") + set(GMP_LIBRARIES "${DEPS_BASE}/lib/libgmp${CMAKE_SHARED_LIBRARY_SUFFIX}") + set(GMP_STATIC_LIBRARIES "${DEPS_BASE}/lib/libgmp.a") + endif() endif() set(GMP_FOUND TRUE) @@ -85,12 +128,15 @@ set_target_properties(GMP_SHARED PROPERTIES IMPORTED_LOCATION "${GMP_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${GMP_INCLUDE_DIR}" ) +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set_target_properties(GMP_SHARED PROPERTIES IMPORTED_IMPLIB "${GMP_LIBRARIES}") +endif() if(ENABLE_STATIC_LIBRARY) add_library(GMP_STATIC STATIC IMPORTED GLOBAL) set_target_properties(GMP_STATIC PROPERTIES IMPORTED_LOCATION "${GMP_STATIC_LIBRARIES}" - INTERFACE_INCLUDE_DIRECTORIES "${GMP_INCLUDE_DIR}" + INTERFACE_INCLUDE_DIRECTORIES "${GMP_STATIC_INCLUDE_DIR}" ) endif() diff --git a/cmake/FindPoly.cmake b/cmake/FindPoly.cmake index ae236c6d9..edba3663e 100644 --- a/cmake/FindPoly.cmake +++ b/cmake/FindPoly.cmake @@ -62,15 +62,18 @@ if(NOT Poly_FOUND_SYSTEM) # Roughly following https://stackoverflow.com/a/44383330/2375725 set(patchcmd # Avoid %z and %llu format specifiers - COMMAND find <SOURCE_DIR>/ -type f -exec - sed -i.orig "s/%z[diu]/%\" PRIu64 \"/g" {} + - COMMAND find <SOURCE_DIR>/ -type f -exec - sed -i.orig "s/%ll[du]/%\" PRIu64 \"/g" {} + + COMMAND find <SOURCE_DIR>/ -type f ! -name "*.orig" -exec + sed -i.orig "s/%z[diu]/%\\\" PRIu64 \\\"/g" {} + + COMMAND find <SOURCE_DIR>/ -type f ! -name "*.orig" -exec + sed -i.orig "s/%ll[du]/%\\\" PRIu64 \\\"/g" {} + # Make sure the new macros are available - COMMAND find <SOURCE_DIR>/ -type f -exec - sed -i.orig "s/#include <stdio.h>/#include <stdio.h>\\n#include <inttypes.h>/" {} + - COMMAND find <SOURCE_DIR>/ -type f -exec - sed -i.orig "s/#include <cstdio>/#include <cstdio>\\n#include <inttypes.h>/" {} + + COMMAND find <SOURCE_DIR>/ -type f ! -name "*.orig" -exec + sed -i.orig "s/#include <stdio.h>/#include <stdio.h>\\\\n#include <inttypes.h>/" {} + + COMMAND find <SOURCE_DIR>/ -type f ! -name "*.orig" -exec + sed -i.orig "s/#include <cstdio>/#include <cstdio>\\\\n#include <inttypes.h>/" {} + + # Help with finding GMP + COMMAND sed -i.orig "s/find_library(GMP_LIBRARY gmp)/find_library(GMP_LIBRARY gmp gmp-10)/" + <SOURCE_DIR>/CMakeLists.txt ) else() unset(patchcmd) @@ -80,6 +83,28 @@ if(NOT Poly_FOUND_SYSTEM) get_target_property(GMP_LIBRARY GMP_SHARED IMPORTED_LOCATION) get_filename_component(GMP_LIB_PATH "${GMP_LIBRARY}" DIRECTORY) + set(POLY_BYPRODUCTS + <INSTALL_DIR>/lib/libpicpoly.a + <INSTALL_DIR>/lib/libpicpolyxx.a + <INSTALL_DIR>/lib/libpoly${CMAKE_SHARED_LIBRARY_SUFFIX} + <INSTALL_DIR>/lib/libpolyxx${CMAKE_SHARED_LIBRARY_SUFFIX} + ) + if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + list(APPEND POLY_BYPRODUCTS + <INSTALL_DIR>/lib/libpoly.0${CMAKE_SHARED_LIBRARY_SUFFIX} + <INSTALL_DIR>/lib/libpoly.0.1.9${CMAKE_SHARED_LIBRARY_SUFFIX} + <INSTALL_DIR>/lib/libpolyxx.0${CMAKE_SHARED_LIBRARY_SUFFIX} + <INSTALL_DIR>/lib/libpolyxx.0.1.9${CMAKE_SHARED_LIBRARY_SUFFIX} + ) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + list(APPEND POLY_BYPRODUCTS + <INSTALL_DIR>/lib/libpoly${CMAKE_SHARED_LIBRARY_SUFFIX}.0 + <INSTALL_DIR>/lib/libpoly${CMAKE_SHARED_LIBRARY_SUFFIX}.0.1.9 + <INSTALL_DIR>/lib/libpolyxx${CMAKE_SHARED_LIBRARY_SUFFIX}.0 + <INSTALL_DIR>/lib/libpolyxx${CMAKE_SHARED_LIBRARY_SUFFIX}.0.1.9 + ) + endif() + ExternalProject_Add( Poly-EP ${COMMON_EP_CONFIG} @@ -103,10 +128,7 @@ if(NOT Poly_FOUND_SYSTEM) <INSTALL_DIR>/lib/libpicpoly.a COMMAND ${CMAKE_COMMAND} -E copy src/libpicpolyxx.a <INSTALL_DIR>/lib/libpicpolyxx.a - BUILD_BYPRODUCTS <INSTALL_DIR>/lib/libpicpoly.a - <INSTALL_DIR>/lib/libpicpolyxx.a - <INSTALL_DIR>/lib/libpoly${CMAKE_SHARED_LIBRARY_SUFFIX} - <INSTALL_DIR>/lib/libpolyxx${CMAKE_SHARED_LIBRARY_SUFFIX} + BUILD_BYPRODUCTS ${POLY_BYPRODUCTS} ) ExternalProject_Add_Step( Poly-EP cleanup @@ -120,6 +142,11 @@ if(NOT Poly_FOUND_SYSTEM) set(PolyXX_LIBRARIES "${DEPS_BASE}/lib/libpolyxx${CMAKE_SHARED_LIBRARY_SUFFIX}") set(Poly_STATIC_LIBRARIES "${DEPS_BASE}/lib/libpicpoly.a") set(PolyXX_STATIC_LIBRARIES "${DEPS_BASE}/lib/libpicpolyxx.a") + + if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(Poly_LIBRARIES "${DEPS_BASE}/bin/libpoly${CMAKE_SHARED_LIBRARY_SUFFIX}") + set(PolyXX_LIBRARIES "${DEPS_BASE}/bin/libpolyxx${CMAKE_SHARED_LIBRARY_SUFFIX}") + endif() endif() set(Poly_FOUND TRUE) @@ -129,6 +156,9 @@ set_target_properties(Poly_SHARED PROPERTIES IMPORTED_LOCATION "${Poly_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${Poly_INCLUDE_DIR}" ) +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set_target_properties(Poly_SHARED PROPERTIES IMPORTED_IMPLIB "${Poly_LIBRARIES}") +endif() target_link_libraries(Poly_SHARED INTERFACE GMP_SHARED) add_library(Polyxx_SHARED SHARED IMPORTED GLOBAL) @@ -137,6 +167,9 @@ set_target_properties(Polyxx_SHARED PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${Poly_INCLUDE_DIR}" INTERFACE_LINK_LIBRARIES Poly_SHARED ) +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set_target_properties(Polyxx_SHARED PROPERTIES IMPORTED_IMPLIB "${PolyXX_LIBRARIES}") +endif() if(ENABLE_STATIC_LIBRARY) add_library(Poly_STATIC STATIC IMPORTED GLOBAL) @@ -167,8 +200,10 @@ else() add_dependencies(Poly_SHARED Poly-EP) add_dependencies(Polyxx_SHARED Poly-EP) + ExternalProject_Get_Property(Poly-EP BUILD_BYPRODUCTS INSTALL_DIR) + string(REPLACE "<INSTALL_DIR>" "${INSTALL_DIR}" BUILD_BYPRODUCTS "${BUILD_BYPRODUCTS}") install(FILES - $<TARGET_SONAME_FILE:Poly_SHARED> $<TARGET_SONAME_FILE:Polyxx_SHARED> + ${BUILD_BYPRODUCTS} DESTINATION ${CMAKE_INSTALL_LIBDIR} ) diff --git a/cmake/Toolchain-mingw64.cmake b/cmake/Toolchain-mingw64.cmake index 5196b7c34..f207875af 100644 --- a/cmake/Toolchain-mingw64.cmake +++ b/cmake/Toolchain-mingw64.cmake @@ -33,4 +33,3 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) -set(CVC5_WINDOWS_BUILD TRUE) diff --git a/cmake/target-graphs.cmake b/cmake/target-graphs.cmake new file mode 100644 index 000000000..ad1023cd7 --- /dev/null +++ b/cmake/target-graphs.cmake @@ -0,0 +1,32 @@ +############################################################################### +# Top contributors (to current version): +# Gereon Kremer +# +# This file is part of the cvc5 project. +# +# Copyright (c) 2009-2021 by the authors listed in the file AUTHORS +# in the top-level source directory and their institutional affiliations. +# All rights reserved. See the file COPYING in the top-level source +# directory for licensing information. +# ############################################################################# +# Provides cmake target target-graphs with generates (png) dependency graphs +# to visualize the interdependencies of all cmake targets. +## + +get_target_property(APITESTS build-apitests MANUALLY_ADDED_DEPENDENCIES) +string(REPLACE ";" " " APITESTS "${APITESTS}") + +configure_file( + cmake/CMakeGraphVizOptions.cmake.in + ${CMAKE_BINARY_DIR}/CMakeGraphVizOptions.cmake + @ONLY +) + +add_custom_target(target-graphs + COMMAND + ${CMAKE_COMMAND} --graphviz=target-graphs/graph.dot ${CMAKE_SOURCE_DIR} + COMMAND + find target-graphs/ -iname "graph.dot*" -and \! -iname "*.png" + -exec dot -Tpng -O {} + + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} +) diff --git a/contrib/competitions/smt-comp/run-script-smtcomp2021 b/contrib/competitions/smt-comp/run-script-smtcomp2021 index 9b4f534b0..faa368670 100755 --- a/contrib/competitions/smt-comp/run-script-smtcomp2021 +++ b/contrib/competitions/smt-comp/run-script-smtcomp2021 @@ -56,7 +56,7 @@ QF_LIA) finishwith --miplib-trick --miplib-trick-subs=4 --use-approx --lemmas-on-replay-failure --replay-early-close-depth=4 --replay-lemma-reject-cut=128 --replay-reject-cut=512 --unconstrained-simp --use-soi --pb-rewrites --ite-simp --simp-ite-compress ;; QF_NIA) - trywith 420 --nl-ext-tplanes --decision=justification + trywith 420 --nl-ext-tplanes --decision=justification --jh-rlv-order trywith 60 --nl-ext-tplanes --decision=internal trywith 60 --nl-ext-tplanes --decision=justification-old trywith 60 --no-nl-ext-tplanes --decision=internal @@ -132,7 +132,7 @@ LIA|LRA) ;; QF_AUFBV) trywith 600 - finishwith --decision=justification-stoponly + finishwith --decision=stoponly ;; QF_ABV) trywith 50 --ite-simp --simp-with-care --repeat-simp --arrays-weak-equiv @@ -153,7 +153,7 @@ QF_AUFNIA) ;; QF_ALIA) trywith 140 --decision=justification --arrays-weak-equiv - finishwith --decision=justification-stoponly --no-arrays-eager-index --arrays-eager-lemmas + finishwith --decision=stoponly --no-arrays-eager-index --arrays-eager-lemmas ;; QF_S|QF_SLIA) trywith 300 --strings-exp --strings-fmf --no-jh-rlv-order diff --git a/docs/_static/custom.css b/docs/_static/custom.css index 6934d7017..9039d15a1 100644 --- a/docs/_static/custom.css +++ b/docs/_static/custom.css @@ -31,10 +31,6 @@ code.xref { border-color: #0077b3; } -.wy-body-for-nav { - background: #343131; -} - .wy-menu-vertical a { color: #4dc3ff; } diff --git a/docs/api/cpp/cpp.rst b/docs/api/cpp/cpp.rst index c10ae7517..04f731203 100644 --- a/docs/api/cpp/cpp.rst +++ b/docs/api/cpp/cpp.rst @@ -30,5 +30,6 @@ C++ API Documentation sort statistics term + unknownexplanation diff --git a/docs/api/cpp/unknownexplanation.rst b/docs/api/cpp/unknownexplanation.rst new file mode 100644 index 000000000..9a64ec4aa --- /dev/null +++ b/docs/api/cpp/unknownexplanation.rst @@ -0,0 +1,6 @@ +UnknownExplanation +============ + +.. doxygenenum:: cvc5::api::Result::UnknownExplanation + :project: cvc5 + diff --git a/docs/api/python/grammar.rst b/docs/api/python/grammar.rst new file mode 100644 index 000000000..a2059fa93 --- /dev/null +++ b/docs/api/python/grammar.rst @@ -0,0 +1,6 @@ +Grammar +================ + +.. autoclass:: pycvc5.Grammar + :members: + :undoc-members: diff --git a/docs/api/python/op.rst b/docs/api/python/op.rst new file mode 100644 index 000000000..7769b33f0 --- /dev/null +++ b/docs/api/python/op.rst @@ -0,0 +1,6 @@ +Op +================ + +.. autoclass:: pycvc5.Op + :members: + :undoc-members: diff --git a/docs/api/python/python.rst b/docs/api/python/python.rst index d815f837a..a6aca2cf9 100644 --- a/docs/api/python/python.rst +++ b/docs/api/python/python.rst @@ -19,3 +19,8 @@ Python API Documentation datatypeconstructordecl datatypedecl datatypeselector + grammar + op + result + roundingmode + unknownexplanation diff --git a/docs/api/python/result.rst b/docs/api/python/result.rst new file mode 100644 index 000000000..9edb12b92 --- /dev/null +++ b/docs/api/python/result.rst @@ -0,0 +1,6 @@ +Result +================ + +.. autoclass:: pycvc5.Result + :members: + :undoc-members: diff --git a/docs/api/python/roundingmode.rst b/docs/api/python/roundingmode.rst new file mode 100644 index 000000000..0c226082e --- /dev/null +++ b/docs/api/python/roundingmode.rst @@ -0,0 +1,6 @@ +RoundingMode +================ + +.. autoclass:: pycvc5.RoundingMode + :members: + :undoc-members: diff --git a/docs/api/python/unknownexplanation.rst b/docs/api/python/unknownexplanation.rst new file mode 100644 index 000000000..54c37665b --- /dev/null +++ b/docs/api/python/unknownexplanation.rst @@ -0,0 +1,6 @@ +UnknownExplanation +================ + +.. autoclass:: pycvc5.UnknownExplanation + :members: + :undoc-members: diff --git a/proofs/lfsc/signatures/nary_programs.plf b/proofs/lfsc/signatures/nary_programs.plf new file mode 100644 index 000000000..f04e6ba40 --- /dev/null +++ b/proofs/lfsc/signatures/nary_programs.plf @@ -0,0 +1,179 @@ +; depends: core_defs.plf util_defs.plf + +; This file defines a library of methods for reasoning about n-ary functions. +; Notice that since LFSC does not have support for n-ary function symbols, we +; represent n-ary function applications in a null-terminated, Curried form. +; In particular, the term `(or A B C)` is represented in LFSC as: +; `(apply (apply f_or A) (apply (apply f_or B) (apply (apply f_or C) false)))` +; i.e. using the macro definitions in core_defs: +; `(or A (or B (or C false)))` +; where notice that `false` is the null-terminator for `or`. Many operators +; are treated in this way, including Boolean connectives `and` and `or`, +; arithmetic plus and multiplication, string concatenation, and so on. +; We call the term like the one above "an f_or-application in n-ary form". +; When applicable, we simply write "f-application" to refer to a term that is +; in n-ary form. We call A, B, C the "elements" of the f-application above. +; We say that an f-application is a "singleton" if it consists of a single +; element, e.g. `(or A false)`. +; +; This file defines a set of side conditions that are useful in defining +; theory-specific proof rules involving n-ary functions and are agnostic to the +; function in question. When applicable, the side conditions take the n-ary +; function `f` as an argument, or its null terminator `null`. + +; Head and tail for n-ary operators with null terminators. These methods +; fail if t is not an f-application. +(program nary_head ((f term) (t term)) term + (match t ((apply t1 t2) (getarg f t1)))) +(program nary_tail ((f term) (t term)) term + (match t ((apply t1 t2) (let t12 (getarg f t1) t2)))) + +; Remove the first occurrence of term l from t. This side condition fails if t +; is not an f-application, or if t does not contain l. +(program nary_rm_first ((f term) (t term) (l term)) term + (match t + ((apply t1 t2) ; otherwise at end, l not found + (let t12 (getarg f t1) + (ifequal t12 l t2 (apply t1 (nary_rm_first f t2 l)))))) + ; otherwise not an f-app in n-ary form +) + +; Same as `nary_rm_first`, but also checks whether t is l itself and returns null if so, +; where null is a null terminator. +(program nary_rm_first_or_self ((f term) (t term) (l term) (null term)) term + (ifequal t l null (nary_rm_first f t l)) +) + +; Does t contain l? This side condition may fail if t is not an f-application. +(program nary_ctn ((f term) (t term) (l term)) flag + (match t + ((apply t1 t2) + (let t12 (getarg f t1) + (ifequal t12 l tt (nary_ctn f t2 l)))) + ; otherwise not an f-app in n-ary form + (default ff)) +) + +; Same as `nary_ctn`, but also checks if t is l, returning true if so. +(program nary_ctn_or_self ((f term) (t term) (l term)) flag + (ifequal t l tt (nary_ctn f t l)) +) + +; Returns true if t is a subset of s, when these terms were interpreted as sets. +; This side condition may fail if t or s is not an f-application. +(program nary_is_subset ((f term) (t term) (s term)) flag + (match t + ((apply t1 t2) + (let t12 (getarg f t1) + (ifequal (nary_ctn_or_self f s t12) tt (nary_is_subset f t2 s) ff))) + ; otherwise not in n-ary form + (default tt)) +) + +; Concatenates terms t1 and t2 that are f-applications in n-ary form. +(program nary_concat ((f term) (t1 term) (t2 term) (null term)) term + (match t1 + ((apply t11 t12) + (let t112 (getarg f t11) + (apply t11 (nary_concat f t12 t2 null)))) + (default t2)) ; any non-application term is interpreted as null +) + +; Insert element elem at the beginning of the f-application t. +(program nary_insert ((f term) (elem term) (t term) (null term)) term + (ifequal elem null t (apply (apply f elem) t))) + +; Dual to `nary_insert`. Returns a term pair ( elem, t' ) where elem is the +; head of t, and t' is the tail of t. +(program nary_decompose ((f term) (t term) (null term)) termPair + (match t + ((apply t1 t2) + (match t1 + ((apply t11 t12) (ifequal t11 f (pair t12 t2) (pair null t))) + (default (pair null t)))) + (default (pair null t))) +) + +; N-ary elimination. This replaces a singleton f-application with its element. +; For example, this replaces e.g. (or t false) with t. +(program nary_elim ((f term) (t term) (null term)) term + (match t + ((apply t1 t2) + ; if null terminated, then we return the head, otherwise not in n-ary form + (ifequal t2 null (getarg f t1) t)) + (default (ifequal t null t (fail term)))) +) + +; N-ary introduction, which is the inverse of nary_elim. This turns a term t into +; a (singleton) f-application if it is not already in n-ary form. For example, +; this replaces t with (or t false) if t is not already in n-ary form. +(program nary_intro ((f term) (t term) (null term)) term + (match t + ((apply t1 t2) + (match t1 + ((apply t11 t12) (ifequal t11 f t (apply (apply f t) null))) + (default (apply (apply f t) null)))) + (default (ifequal t null t (apply (apply f t) null)))) +) + +; Extract the n^th element of f-application t. This side condition fails if +; t is not an f-application, or contains fewer than n elements. +(program nary_extract ((f term) (t term) (n mpz)) term + (match t + ((apply t1 t2) + (mp_ifzero n + (getarg f t1) + (nary_extract f t2 (mp_add n (mp_neg 1)))))) +) + +; Helper for dropping duplicates from an f-application t. Elements occur in +; the returned f-application in the order they first appear in t. +; This side condition takes an accumulator tacc which tracks which terms it is +; adding to the return value. We require an explicit accumulator to ensure +; propering ordering for cases like (or A B B A), where (or A B) should be +; returned instead of (or B A). +; This method fails if t is not an f-application. +(program nary_drop_dups_rec ((f term) (t term) (tacc term)) term + (match t + ((apply t1 t2) + (let t12 (getarg f t1) + (let c (nary_ctn f tacc t12) + (let t2d (nary_drop_dups_rec f t2 (ifequal c tt tacc (apply t1 tacc))) + (ifequal c tt t2d (apply t1 t2d)))))) + (default t)) +) + +; Drop all duplicates in an f-application t using the helper method +; `nary_drop_dups_rec`. +(program nary_drop_dups ((f term) (t term) (null term)) term + (nary_elim f (nary_drop_dups_rec f t null) null) +) + +; Truncate, which returns the f-application in which the subterm c has been +; replaced by the null terminator, where c is an f-application that is a +; suffix of t. For example, (or t1 ... tn c) ---> (or t1 ... tn false). +; This side condition fails if c is not a suffix of f-application t. +(program nary_truncate ((f term) (t term) (c term) (null term)) term + (ifequal t c null + (match t + ((apply t1 t2) + (let t12 (getarg f t1) + (apply t1 (nary_truncate f t2 c null)))))) +) + +; Helper for reverse, which takes an accumulator of terms we have added in +; reverse order to the value we will return. This side condition fails if +; t is not an f-application. +(program nary_reverse_rec ((f term) (t term) (acc term) (null term)) term + (match t + ((apply t1 t2) + (let t12 (getarg f t1) + (nary_reverse_rec f t2 (apply t1 acc) null))) + (default (ifequal t null acc (fail term)))) +) + +; Reverse the elements in f-application t using the helper method +; `nary_reverse_rec`. +(program nary_reverse ((f term) (t term) (null term)) term + (nary_reverse_rec f t null null) +) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 733186d16..5bde94a5d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -795,6 +795,8 @@ libcvc5_add_sources( theory/quantifiers/ematching/instantiation_engine.h theory/quantifiers/ematching/pattern_term_selector.cpp theory/quantifiers/ematching/pattern_term_selector.h + theory/quantifiers/ematching/relational_match_generator.cpp + theory/quantifiers/ematching/relational_match_generator.h theory/quantifiers/ematching/trigger.cpp theory/quantifiers/ematching/trigger.h theory/quantifiers/ematching/trigger_database.cpp @@ -805,6 +807,8 @@ libcvc5_add_sources( theory/quantifiers/ematching/trigger_trie.h theory/quantifiers/ematching/var_match_generator.cpp theory/quantifiers/ematching/var_match_generator.h + theory/quantifiers/entailment_check.cpp + theory/quantifiers/entailment_check.h theory/quantifiers/equality_query.cpp theory/quantifiers/equality_query.h theory/quantifiers/expr_miner.cpp @@ -1079,6 +1083,8 @@ libcvc5_add_sources( theory/strings/normal_form.h theory/strings/proof_checker.cpp theory/strings/proof_checker.h + theory/strings/regexp_enumerator.cpp + theory/strings/regexp_enumerator.h theory/strings/regexp_elim.cpp theory/strings/regexp_elim.h theory/strings/regexp_entail.cpp @@ -1302,7 +1308,7 @@ set_source_files_properties(${LIBCVC5_GEN_SRCS} PROPERTIES GENERATED TRUE) # First build the object library add_library(cvc5-obj OBJECT ${LIBCVC5_SRCS} ${LIBCVC5_GEN_SRCS}) -target_compile_definitions(cvc5-obj PRIVATE -D__BUILDING_CVC5LIB) +target_compile_definitions(cvc5-obj PRIVATE -D__BUILDING_CVC5LIB -Dcvc5_obj_EXPORTS) set_target_properties(cvc5-obj PROPERTIES POSITION_INDEPENDENT_CODE ON) # add_dependencies() is necessary for cmake versions before 3.21 add_dependencies(cvc5-obj cvc5base cvc5context) @@ -1402,7 +1408,6 @@ if(USE_GLPK) if(ENABLE_STATIC_LIBRARY) target_link_libraries(cvc5-static PUBLIC ${GLPK_LIBRARIES}) endif() - target_link_libraries(cvc5-obj PUBLIC ${GLPK_LIBRARIES}) endif() if(USE_POLY) add_dependencies(cvc5-obj Polyxx_SHARED) diff --git a/src/api/cpp/cvc5.cpp b/src/api/cpp/cvc5.cpp index 778f700c0..280b07bb2 100644 --- a/src/api/cpp/cvc5.cpp +++ b/src/api/cpp/cvc5.cpp @@ -41,6 +41,7 @@ #include "base/modal_exception.h" #include "expr/array_store_all.h" #include "expr/ascription_type.h" +#include "expr/cardinality_constraint.h" #include "expr/dtype.h" #include "expr/dtype_cons.h" #include "expr/dtype_selector.h" @@ -131,7 +132,6 @@ const static std::unordered_map<Kind, cvc5::Kind> s_kinds{ /* UF ------------------------------------------------------------------ */ {APPLY_UF, cvc5::Kind::APPLY_UF}, {CARDINALITY_CONSTRAINT, cvc5::Kind::CARDINALITY_CONSTRAINT}, - {CARDINALITY_VALUE, cvc5::Kind::CARDINALITY_VALUE}, {HO_APPLY, cvc5::Kind::HO_APPLY}, /* Arithmetic ---------------------------------------------------------- */ {PLUS, cvc5::Kind::PLUS}, @@ -410,7 +410,6 @@ const static std::unordered_map<cvc5::Kind, Kind, cvc5::kind::KindHashFunction> /* UF -------------------------------------------------------------- */ {cvc5::Kind::APPLY_UF, APPLY_UF}, {cvc5::Kind::CARDINALITY_CONSTRAINT, CARDINALITY_CONSTRAINT}, - {cvc5::Kind::CARDINALITY_VALUE, CARDINALITY_VALUE}, {cvc5::Kind::HO_APPLY, HO_APPLY}, /* Arithmetic ------------------------------------------------------ */ {cvc5::Kind::PLUS, PLUS}, @@ -1682,7 +1681,7 @@ size_t Sort::getSortConstructorArity() const /* Bit-vector sort ----------------------------------------------------- */ -uint32_t Sort::getBVSize() const +uint32_t Sort::getBitVectorSize() const { CVC5_API_TRY_CATCH_BEGIN; CVC5_API_CHECK_NOT_NULL; @@ -1695,7 +1694,7 @@ uint32_t Sort::getBVSize() const /* Floating-point sort ------------------------------------------------- */ -uint32_t Sort::getFPExponentSize() const +uint32_t Sort::getFloatingPointExponentSize() const { CVC5_API_TRY_CATCH_BEGIN; CVC5_API_CHECK_NOT_NULL; @@ -1706,7 +1705,7 @@ uint32_t Sort::getFPExponentSize() const CVC5_API_TRY_CATCH_END; } -uint32_t Sort::getFPSignificandSize() const +uint32_t Sort::getFloatingPointSignificandSize() const { CVC5_API_TRY_CATCH_BEGIN; CVC5_API_CHECK_NOT_NULL; @@ -5082,7 +5081,7 @@ Term Solver::mkTermFromKind(Kind kind) const CVC5_API_KIND_CHECK_EXPECTED(kind == PI || kind == REGEXP_EMPTY || kind == REGEXP_SIGMA || kind == SEP_EMP, kind) - << "PI or REGEXP_EMPTY or REGEXP_SIGMA"; + << "PI, REGEXP_EMPTY, REGEXP_SIGMA or SEP_EMP"; //////// all checks before this line Node res; cvc5::Kind k = extToIntKind(kind); @@ -5822,6 +5821,18 @@ Term Solver::mkEmptyBag(const Sort& sort) const CVC5_API_TRY_CATCH_END; } +Term Solver::mkSepEmp() const +{ + CVC5_API_TRY_CATCH_BEGIN; + //////// all checks before this line + Node res = getNodeManager()->mkNullaryOperator(d_nodeMgr->booleanType(), + cvc5::Kind::SEP_EMP); + (void)res.getType(true); /* kick off type checking */ + return Term(this, res); + //////// + CVC5_API_TRY_CATCH_END; +} + Term Solver::mkSepNil(const Sort& sort) const { CVC5_API_TRY_CATCH_BEGIN; @@ -6030,10 +6041,11 @@ Term Solver::mkFloatingPoint(uint32_t exp, uint32_t sig, Term val) const CVC5_API_ARG_CHECK_EXPECTED(exp > 0, exp) << "a value > 0"; CVC5_API_ARG_CHECK_EXPECTED(sig > 0, sig) << "a value > 0"; uint32_t bw = exp + sig; - CVC5_API_ARG_CHECK_EXPECTED(bw == val.getSort().getBVSize(), val) + CVC5_API_ARG_CHECK_EXPECTED(bw == val.d_node->getType().getBitVectorSize(), + val) << "a bit-vector constant with bit-width '" << bw << "'"; CVC5_API_ARG_CHECK_EXPECTED( - val.getSort().isBitVector() && val.d_node->isConst(), val) + val.d_node->getType().isBitVector() && val.d_node->isConst(), val) << "bit-vector constant"; //////// all checks before this line return mkValHelper<cvc5::FloatingPoint>( @@ -6042,6 +6054,22 @@ Term Solver::mkFloatingPoint(uint32_t exp, uint32_t sig, Term val) const CVC5_API_TRY_CATCH_END; } +Term Solver::mkCardinalityConstraint(const Sort& sort, uint32_t ubound) const +{ + CVC5_API_TRY_CATCH_BEGIN; + CVC5_API_SOLVER_CHECK_SORT(sort); + CVC5_API_ARG_CHECK_EXPECTED(sort.isUninterpretedSort(), sort) + << "an uninterpreted sort"; + CVC5_API_ARG_CHECK_EXPECTED(ubound > 0, ubound) << "a value > 0"; + //////// all checks before this line + Node cco = + d_nodeMgr->mkConst(cvc5::CardinalityConstraint(*sort.d_type, ubound)); + Node cc = d_nodeMgr->mkNode(cvc5::Kind::CARDINALITY_CONSTRAINT, cco); + return Term(this, cc); + //////// + CVC5_API_TRY_CATCH_END; +} + /* Create constants */ /* -------------------------------------------------------------------------- */ @@ -7204,7 +7232,7 @@ std::string Solver::getProof(void) const { CVC5_API_TRY_CATCH_BEGIN; CVC5_API_CHECK(d_slv->getOptions().smt.produceProofs) - << "Cannot get proof explicitly enabled (try --produce-proofs)"; + << "Cannot get proof unless proofs are enabled (try --produce-proofs)"; CVC5_API_RECOVERABLE_CHECK(d_slv->getSmtMode() == SmtMode::UNSAT) << "Cannot get proof unless in unsat mode."; return d_slv->getProof(); @@ -7455,6 +7483,8 @@ bool Solver::getAbduct(const Term& conj, Term& output) const { CVC5_API_TRY_CATCH_BEGIN; CVC5_API_SOLVER_CHECK_TERM(conj); + CVC5_API_CHECK(d_slv->getOptions().smt.produceAbducts) + << "Cannot get abduct unless abducts are enabled (try --produce-abducts)"; //////// all checks before this line Node result; bool success = d_slv->getAbduct(*conj.d_node, result); @@ -7471,6 +7501,8 @@ bool Solver::getAbduct(const Term& conj, Grammar& grammar, Term& output) const { CVC5_API_TRY_CATCH_BEGIN; CVC5_API_SOLVER_CHECK_TERM(conj); + CVC5_API_CHECK(d_slv->getOptions().smt.produceAbducts) + << "Cannot get abduct unless abducts are enabled (try --produce-abducts)"; //////// all checks before this line Node result; bool success = diff --git a/src/api/cpp/cvc5.h b/src/api/cpp/cvc5.h index 3db581db6..57c3f311d 100644 --- a/src/api/cpp/cvc5.h +++ b/src/api/cpp/cvc5.h @@ -700,19 +700,19 @@ class CVC5_EXPORT Sort /** * @return the bit-width of the bit-vector sort */ - uint32_t getBVSize() const; + uint32_t getBitVectorSize() const; /* Floating-point sort ------------------------------------------------- */ /** * @return the bit-width of the exponent of the floating-point sort */ - uint32_t getFPExponentSize() const; + uint32_t getFloatingPointExponentSize() const; /** * @return the width of the significand of the floating-point sort */ - uint32_t getFPSignificandSize() const; + uint32_t getFloatingPointSignificandSize() const; /* Datatype sort ------------------------------------------------------- */ @@ -1167,7 +1167,7 @@ class CVC5_EXPORT Term * for example, the term f(x, y) will have Kind APPLY_UF and three * children: f, x, and y */ - class const_iterator + class CVC5_EXPORT const_iterator { friend class Term; @@ -1353,6 +1353,8 @@ class CVC5_EXPORT Term std::pair<int64_t, uint64_t> getReal64Value() const; /** * @return true if the term is a rational value. + * + * Note that a term of kind PI is not considered to be a real value. */ bool isRealValue() const; /** @@ -1448,6 +1450,17 @@ class CVC5_EXPORT Term /** * @return true if the term is a set value. + * + * A term is a set value if it is considered to be a (canonical) constant set + * value. A canonical set value is one whose AST is: + * ``` + * (union (singleton c1) ... (union (singleton c_{n-1}) (singleton c_n)))) + * ``` + * where `c1 ... cn` are values ordered by id such that `c1 > ... > cn` (see + * also @ref Term::operator>(const Term&) const). + * + * Note that a universe set term (kind UNIVERSE_SET) is not considered to be + * a set value. */ bool isSetValue() const; /** @@ -2464,7 +2477,7 @@ class CVC5_EXPORT Grammar /** * Allow \p ntSymbol to be any input variable to corresponding * synth-fun/synth-inv with the same sort as \p ntSymbol. - * @param ntSymbol the non-terminal allowed to be any input constant + * @param ntSymbol the non-terminal allowed to be any input variable */ void addAnyVariable(const Term& ntSymbol); @@ -2599,7 +2612,7 @@ std::ostream& operator<<(std::ostream& out, const Grammar& g) CVC5_EXPORT; * Standard 754. * \endverbatim */ -enum CVC5_EXPORT RoundingMode +enum RoundingMode { /** * Round to the nearest even number. @@ -2679,7 +2692,7 @@ class CVC5_EXPORT DriverOptions /** * Holds some description about a particular option, including its name, its - * aliases, whether the option was explcitly set by the user, and information + * aliases, whether the option was explicitly set by the user, and information * concerning its value. The `valueInfo` member holds any of the following * alternatives: * - VoidInfo if the option holds no value (or the value has no native type) @@ -2874,7 +2887,7 @@ class CVC5_EXPORT Statistics using BaseType = std::map<std::string, Stat>; /** Custom iterator to hide certain statistics from regular iteration */ - class iterator + class CVC5_EXPORT iterator { public: friend class Statistics; @@ -3431,6 +3444,12 @@ class CVC5_EXPORT Solver Term mkEmptyBag(const Sort& sort) const; /** + * Create a separation logic empty term. + * @return the separation logic empty term + */ + Term mkSepEmp() const; + + /** * Create a separation logic nil term. * @param sort the sort of the nil term * @return the separation logic nil term @@ -3578,6 +3597,14 @@ class CVC5_EXPORT Solver */ Term mkFloatingPoint(uint32_t exp, uint32_t sig, Term val) const; + /** + * Create a cardinality constraint for an uninterpreted sort. + * @param sort the sort the cardinality constraint is for + * @param val the upper bound on the cardinality of the sort + * @return the cardinality constraint + */ + Term mkCardinalityConstraint(const Sort& sort, uint32_t ubound) const; + /* .................................................................... */ /* Create Variables */ /* .................................................................... */ diff --git a/src/api/cpp/cvc5_kind.h b/src/api/cpp/cvc5_kind.h index 0ff05022f..a01f84c3f 100644 --- a/src/api/cpp/cvc5_kind.h +++ b/src/api/cpp/cvc5_kind.h @@ -43,7 +43,7 @@ namespace api { * depends on the size of `cvc5::Kind` (`NodeValue::NBITS_KIND`, currently 10 * bits, see expr/node_value.h). */ -enum CVC5_EXPORT Kind : int32_t +enum Kind : int32_t { /** * Internal kind. @@ -331,22 +331,7 @@ enum CVC5_EXPORT Kind : int32_t * - `Solver::mkTerm(Kind kind, const std::vector<Term>& children) const` */ CARDINALITY_CONSTRAINT, - /** - * Cardinality value for uninterpreted sort S. - * An operator that returns an integer indicating the value of the cardinality - * of sort S. - * - * Parameters: - * - 1: Term of sort S - * - * Create with: - * - `Solver::mkTerm(Kind kind, const Term& child1) const` - * - `Solver::mkTerm(Kind kind, const std::vector<Term>& children) const` - */ - CARDINALITY_VALUE, #if 0 - /* Combined cardinality constraint. */ - COMBINED_CARDINALITY_CONSTRAINT, /* Partial uninterpreted function application. */ PARTIAL_APPLY_UF, #endif @@ -766,6 +751,9 @@ enum CVC5_EXPORT Kind : int32_t /** * Pi constant. * + * Note that PI is considered a special symbol of sort Real, but is not + * a real value, i.e., `Term::isRealValue() const` will return false. + * * Create with: * - `Solver::mkPi() const` * - `Solver::mkTerm(Kind kind) const` @@ -2228,6 +2216,10 @@ enum CVC5_EXPORT Kind : int32_t * Finite universe set. * All set variables must be interpreted as subsets of it. * + * Note that UNIVERSE_SET is considered a special symbol of the theory of + * sets and is not considered as a set value, + * i.e., `Term::isSetValue() const` will return false. + * * Create with: * - `Solver::mkUniverseSet(const Sort& sort) const` */ diff --git a/src/api/java/CMakeLists.txt b/src/api/java/CMakeLists.txt index d6759ba6e..df35390ea 100644 --- a/src/api/java/CMakeLists.txt +++ b/src/api/java/CMakeLists.txt @@ -50,6 +50,7 @@ include(UseJava) set(JAVA_FILES ${CMAKE_CURRENT_LIST_DIR}/cvc5/AbstractPointer.java ${CMAKE_CURRENT_LIST_DIR}/cvc5/CVC5ApiException.java + ${CMAKE_CURRENT_LIST_DIR}/cvc5/CVC5ApiOptionException.java ${CMAKE_CURRENT_LIST_DIR}/cvc5/CVC5ApiRecoverableException.java ${CMAKE_CURRENT_LIST_DIR}/cvc5/Datatype.java ${CMAKE_CURRENT_LIST_DIR}/cvc5/DatatypeConstructor.java @@ -59,6 +60,7 @@ set(JAVA_FILES ${CMAKE_CURRENT_LIST_DIR}/cvc5/Grammar.java ${CMAKE_CURRENT_LIST_DIR}/cvc5/IPointer.java ${CMAKE_CURRENT_LIST_DIR}/cvc5/Op.java + ${CMAKE_CURRENT_LIST_DIR}/cvc5/OptionInfo.java ${CMAKE_CURRENT_LIST_DIR}/cvc5/Pair.java ${CMAKE_CURRENT_LIST_DIR}/cvc5/Result.java ${CMAKE_CURRENT_LIST_DIR}/cvc5/RoundingMode.java @@ -115,12 +117,15 @@ add_library(cvc5jni SHARED jni/cvc5_DatatypeSelector.cpp jni/cvc5_Grammar.cpp jni/cvc5_Op.cpp + jni/cvc5_OptionInfo.cpp jni/cvc5_Result.cpp jni/cvc5_Solver.cpp jni/cvc5_Sort.cpp jni/cvc5_Stat.cpp jni/cvc5_Statistics.cpp - jni/cvc5_Term.cpp) + jni/cvc5_Term.cpp + jni/cvc5JavaApi.cpp) + add_dependencies(cvc5jni generate-jni-headers) target_include_directories(cvc5jni PUBLIC ${JNI_INCLUDE_DIRS}) diff --git a/src/api/java/cvc5/CVC5ApiOptionException.java b/src/api/java/cvc5/CVC5ApiOptionException.java new file mode 100644 index 000000000..e792091e1 --- /dev/null +++ b/src/api/java/cvc5/CVC5ApiOptionException.java @@ -0,0 +1,24 @@ +/****************************************************************************** + * Top contributors (to current version): + * Gereon Kremer, Mudathir Mohamed + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * The cvc5 java API. + */ + +package cvc5; + +public class CVC5ApiOptionException extends CVC5ApiRecoverableException +{ + public CVC5ApiOptionException(String message) + { + super(message); + } +} diff --git a/src/api/java/cvc5/DatatypeSelector.java b/src/api/java/cvc5/DatatypeSelector.java index 77136173e..014c35de0 100644 --- a/src/api/java/cvc5/DatatypeSelector.java +++ b/src/api/java/cvc5/DatatypeSelector.java @@ -69,7 +69,7 @@ public class DatatypeSelector extends AbstractPointer private native long getUpdaterTerm(long pointer); - /** @return the range sort of this argument. */ + /** @return the range sort of this selector. */ Sort getRangeSort() { long sortPointer = getRangeSort(pointer); diff --git a/src/api/java/cvc5/OptionInfo.java b/src/api/java/cvc5/OptionInfo.java new file mode 100644 index 000000000..7690a1bc1 --- /dev/null +++ b/src/api/java/cvc5/OptionInfo.java @@ -0,0 +1,209 @@ +/****************************************************************************** + * Top contributors (to current version): + * Gereon Kremer, Mudathir Mohamed + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * The cvc5 java API. + */ + +package cvc5; + +import java.math.BigInteger; + +/** + * Holds some description about a particular option, including its name, its + * aliases, whether the option was explicitly set by the user, and information + * concerning its value. The `valueInfo` member holds any of the following + * alternatives: + * - VoidInfo if the option holds no value (or the value has no native type) + * - ValueInfo if the option is of type boolean or String, holds the + * current value and the default value. + * - NumberInfo if the option is of type BigInteger or double, holds + * the current and default value, as well as the minimum and maximum. + * - ModeInfo if the option is a mode option, holds the current and default + * values, as well as a list of valid modes. + * Additionally, this class provides convenience functions to obtain the + * current value of an option in a type-safe manner using boolValue(), + * stringValue(), intValue(), and doubleValue(). They assert that + * the option has the respective type and return the current value. + */ +public class OptionInfo extends AbstractPointer +{ + // region construction and destruction + OptionInfo(Solver solver, long pointer) + { + super(solver, pointer); + this.name = getName(pointer); + this.aliases = getAliases(pointer); + this.setByUser = getSetByUser(pointer); + this.baseInfo = getBaseInfo(pointer); + } + + protected static native void deletePointer(long pointer); + + public long getPointer() + { + return pointer; + } + + @Override public void finalize() + { + deletePointer(pointer); + } + + /** + * @return a string representation of this optionInfo. + */ + protected native String toString(long pointer); + + // endregion + + /** Abstract class for OptionInfo values */ + abstract class BaseInfo + { + } + + /** Has the current and the default value */ + public abstract class ValueInfo<T> extends BaseInfo + { + private final T defaultValue; + private final T currentValue; + public ValueInfo(T defaultValue, T currentValue) + { + this.defaultValue = defaultValue; + this.currentValue = currentValue; + } + public T getDefaultValue() + { + return defaultValue; + } + public T getCurrentValue() + { + return currentValue; + } + } + + public class ModeInfo extends ValueInfo<String> + { + private final String[] modes; + + public ModeInfo(String defaultValue, String currentValue, String[] modes) + { + super(defaultValue, currentValue); + this.modes = modes; + } + public String[] getModes() + { + return modes; + } + } + + /** Has no value information */ + public class VoidInfo extends BaseInfo + { + } + + /** Default value, current value, minimum and maximum of a numeric value */ + public class NumberInfo<T> extends ValueInfo<T> + { + private final T minimum; + private final T maximum; + public NumberInfo(T defaultValue, T currentValue, T minimum, T maximum) + { + super(defaultValue, currentValue); + this.minimum = minimum; + this.maximum = maximum; + } + public T getMinimum() + { + return minimum; + } + public T getMaximum() + { + return maximum; + } + } + + private native String getName(long pointer); + + private native String[] getAliases(long pointer); + + private native boolean getSetByUser(long pointer); + + private native BaseInfo getBaseInfo(long pointer); + + /** The option name */ + private final String name; + public String getName() + { + return name; + } + /** The option name aliases */ + private final String[] aliases; + public String[] getAliases() + { + return aliases; + } + /** Whether the option was explicitly set by the user */ + private final boolean setByUser; + public boolean getSetByUser() + { + return setByUser; + } + + /** The option variant information */ + private final BaseInfo baseInfo; + public BaseInfo getBaseInfo() + { + return baseInfo; + } + + /** + * Obtain the current value as a boolean. Asserts that valueInfo holds a boolean. + */ + public boolean booleanValue() + { + return booleanValue(pointer); + } + + private native boolean booleanValue(long pointer); + + /** + * Obtain the current value as a string. Asserts that valueInfo holds a + * string. + */ + public String stringValue() + { + return stringValue(pointer); + } + + private native String stringValue(long pointer); + + /** + * Obtain the current value as as int. Asserts that valueInfo holds an int. + */ + public BigInteger intValue() + { + return intValue(pointer); + } + + private native BigInteger intValue(long pointer); + + /** + * Obtain the current value as a double. Asserts that valueInfo holds a + * double. + */ + public double doubleValue() + { + return doubleValue(pointer); + } + + private native double doubleValue(long pointer); +}; diff --git a/src/api/java/cvc5/Solver.java b/src/api/java/cvc5/Solver.java index b0bee500c..cc5797570 100644 --- a/src/api/java/cvc5/Solver.java +++ b/src/api/java/cvc5/Solver.java @@ -882,6 +882,18 @@ public class Solver implements IPointer private native long mkEmptyBag(long pointer, long sortPointer); /** + * Create a separation logic empty term. + * @return the separation logic empty term + */ + public Term mkSepEmp() + { + long termPointer = mkSepEmp(pointer); + return new Term(this, termPointer); + } + + private native long mkSepEmp(long pointer); + + /** * Create a separation logic nil term. * @param sort the sort of the nil term * @return the separation logic nil term @@ -1794,6 +1806,31 @@ public class Solver implements IPointer private native String getOption(long pointer, String option); /** + * Get all option names that can be used with `setOption`, `getOption` and + * `getOptionInfo`. + * @return all option names + */ + public String[] getOptionNames() + { + return getOptionNames(pointer); + } + + private native String[] getOptionNames(long pointer); + + /** + * Get some information about the given option. Check the `OptionInfo` class + * for more details on which information is available. + * @return information about the given option + */ + public OptionInfo getOptionInfo(String option) + { + long optionPointer = getOptionInfo(pointer, option); + return new OptionInfo(this, optionPointer); + } + + private native long getOptionInfo(long pointer, String option); + + /** * Get the set of unsat ("failed") assumptions. * SMT-LIB: * {@code @@ -1828,7 +1865,47 @@ public class Solver implements IPointer private native long[] getUnsatCore(long pointer); /** - * Get the value of the given term. + * Get a difficulty estimate for an asserted formula. This method is + * intended to be called immediately after any response to a checkSat. + * + * @return a map from (a subset of) the input assertions to a real value that + * is an estimate of how difficult each assertion was to solve. Unmentioned + * assertions can be assumed to have zero difficulty. + */ + public Map<Term, Term> getDifficulty() + { + Map<Long, Long> map = getDifficulty(pointer); + Map<Term, Term> ret = new HashMap<>(); + for (Map.Entry<Long, Long> entry : map.entrySet()) + { + Term key = new Term(this, entry.getKey()); + Term value = new Term(this, entry.getValue()); + ret.put(key, value); + } + return ret; + } + + private native Map<Long, Long> getDifficulty(long pointer); + + /** + * Get the refutation proof + * SMT-LIB: + * {@code + * ( get-proof ) + * } + * Requires to enable option 'produce-proofs'. + * @return a string representing the proof, according to the value of + * proof-format-mode. + */ + public String getProof() + { + return getProof(pointer); + } + + private native String getProof(long pointer); + + /** + * Get the value of the given term in the current model. * SMT-LIB: * {@code * ( get-value ( <term> ) ) @@ -1845,7 +1922,7 @@ public class Solver implements IPointer private native long getValue(long pointer, long termPointer); /** - * Get the values of the given terms. + * Get the values of the given terms in the current model. * SMT-LIB: * {@code * ( get-value ( <term>+ ) ) @@ -1863,6 +1940,22 @@ public class Solver implements IPointer private native long[] getValue(long pointer, long[] termPointers); /** + * Get the domain elements of uninterpreted sort s in the current model. The + * current model interprets s as the finite sort whose domain elements are + * given in the return value of this method. + * + * @param s The uninterpreted sort in question + * @return the domain elements of s in the current model + */ + public Term[] getModelDomainElements(Sort s) + { + long[] pointers = getModelDomainElements(pointer, s.getPointer()); + return Utils.getTerms(this, pointers); + } + + private native long[] getModelDomainElements(long pointer, long sortPointer); + + /** * This returns false if the model value of free constant v was not essential * for showing the satisfiability of the last call to checkSat using the * current model. This method will only return false (for any v) if @@ -2000,6 +2093,26 @@ public class Solver implements IPointer private native long getSeparationNilTerm(long pointer); /** + * Declare a symbolic pool of terms with the given initial value. + * SMT-LIB: + * {@code + * ( declare-pool <symbol> <sort> ( <term>* ) ) + * } + * @param symbol The name of the pool + * @param sort The sort of the elements of the pool. + * @param initValue The initial value of the pool + */ + public Term declarePool(String symbol, Sort sort, Term[] initValue) + { + long[] termPointers = Utils.getPointers(initValue); + long termPointer = declarePool(pointer, symbol, sort.getPointer(), termPointers); + return new Term(this, termPointer); + } + + private native long declarePool( + long pointer, String symbol, long sortPointer, long[] termPointers); + + /** * Pop a level from the assertion stack. * SMT-LIB: * {@code @@ -2398,6 +2511,22 @@ public class Solver implements IPointer } private native void addSygusConstraint(long pointer, long termPointer); + + /** + * Add a forumla to the set of Sygus assumptions. + * SyGuS v2: + * {@code + * ( assume <term> ) + * } + * @param term the formula to add as an assumption + */ + public void addSygusAssume(Term term) + { + addSygusAssume(pointer, term.getPointer()); + } + + private native void addSygusAssume(long pointer, long termPointer); + /** * Add a set of Sygus constraints to the current state that correspond to an * invariant synthesis problem. diff --git a/src/api/java/cvc5/Sort.java b/src/api/java/cvc5/Sort.java index f1f541e35..434c07424 100644 --- a/src/api/java/cvc5/Sort.java +++ b/src/api/java/cvc5/Sort.java @@ -725,34 +725,34 @@ public class Sort extends AbstractPointer implements Comparable<Sort> /** * @return the bit-width of the bit-vector sort */ - public int getBVSize() + public int getBitVectorSize() { - return getBVSize(pointer); + return getBitVectorSize(pointer); } - private native int getBVSize(long pointer); + private native int getBitVectorSize(long pointer); /* Floating-point sort ------------------------------------------------- */ /** * @return the bit-width of the exponent of the floating-point sort */ - public int getFPExponentSize() + public int getFloatingPointExponentSize() { - return getFPExponentSize(pointer); + return getFloatingPointExponentSize(pointer); } - private native int getFPExponentSize(long pointer); + private native int getFloatingPointExponentSize(long pointer); /** * @return the width of the significand of the floating-point sort */ - public int getFPSignificandSize() + public int getFloatingPointSignificandSize() { - return getFPSignificandSize(pointer); + return getFloatingPointSignificandSize(pointer); } - private native int getFPSignificandSize(long pointer); + private native int getFloatingPointSignificandSize(long pointer); /* Datatype sort ------------------------------------------------------- */ diff --git a/src/api/java/jni/cvc5JavaApi.cpp b/src/api/java/jni/cvc5JavaApi.cpp new file mode 100644 index 000000000..378fa0948 --- /dev/null +++ b/src/api/java/jni/cvc5JavaApi.cpp @@ -0,0 +1,52 @@ +/****************************************************************************** + * Top contributors (to current version): + * Mudathir Mohamed + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * The cvc5 Java API. + */ + +#include "cvc5JavaApi.h" + +#include <string> +#include <vector> + +jobjectArray getStringArrayFromStringVector( + JNIEnv* env, const std::vector<std::string>& cStrings) +{ + jclass stringClass = env->FindClass("java/lang/String"); + jobjectArray ret = + env->NewObjectArray(cStrings.size(), stringClass, env->NewStringUTF("")); + for (size_t i = 0; i < cStrings.size(); i++) + { + jstring jString = env->NewStringUTF(cStrings[i].c_str()); + env->SetObjectArrayElement(ret, i, jString); + } + return ret; +} + +jobject getDoubleObject(JNIEnv* env, double cValue) +{ + jdouble jValue = static_cast<jdouble>(cValue); + jclass doubleClass = env->FindClass("java/lang/Double"); + jmethodID methodId = env->GetMethodID(doubleClass, "<init>", "(D)V"); + jobject ret = env->NewObject(doubleClass, methodId, jValue); + return ret; +} + +jobject getBooleanObject(JNIEnv* env, bool cValue) +{ + jboolean jValue = static_cast<jboolean>(cValue); + jclass booleanClass = env->FindClass("Ljava/lang/Boolean;"); + jmethodID booleanConstructor = + env->GetMethodID(booleanClass, "<init>", "(Z)V"); + jobject ret = env->NewObject(booleanClass, booleanConstructor, jValue); + return ret; +}
\ No newline at end of file diff --git a/src/api/java/jni/cvc5JavaApi.h b/src/api/java/jni/cvc5JavaApi.h index 7b550ef28..73f2fb396 100644 --- a/src/api/java/jni/cvc5JavaApi.h +++ b/src/api/java/jni/cvc5JavaApi.h @@ -13,31 +13,38 @@ * The cvc5 Java API. */ -#include <jni.h> - #ifndef CVC5__JAVA_API_H #define CVC5__JAVA_API_H +#include <jni.h> + +#include <string> +#include <vector> + #define CVC5_JAVA_API_TRY_CATCH_BEGIN \ try \ { -#define CVC5_JAVA_API_TRY_CATCH_END(env) \ - } \ - catch (const CVC5ApiRecoverableException& e) \ - { \ - jclass exceptionClass = \ - env->FindClass("cvc5/CVC5ApiRecoverableException"); \ - env->ThrowNew(exceptionClass, e.what()); \ - } \ - catch (const CVC5ApiException& e) \ - { \ - jclass exceptionClass = env->FindClass("cvc5/CVC5ApiException"); \ - env->ThrowNew(exceptionClass, e.what()); \ +#define CVC5_JAVA_API_TRY_CATCH_END(env) \ + } \ + catch (const CVC5ApiOptionException& e) \ + { \ + jclass exceptionClass = env->FindClass("cvc5/CVC5ApiOptionException"); \ + env->ThrowNew(exceptionClass, e.what()); \ + } \ + catch (const CVC5ApiRecoverableException& e) \ + { \ + jclass exceptionClass = \ + env->FindClass("cvc5/CVC5ApiRecoverableException"); \ + env->ThrowNew(exceptionClass, e.what()); \ + } \ + catch (const CVC5ApiException& e) \ + { \ + jclass exceptionClass = env->FindClass("cvc5/CVC5ApiException"); \ + env->ThrowNew(exceptionClass, e.what()); \ } #define CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, returnValue) \ CVC5_JAVA_API_TRY_CATCH_END(env) \ return returnValue; -#endif // CVC5__JAVA_API_H /** * Convert pointers coming from Java to cvc5 objects @@ -83,4 +90,51 @@ jlongArray getPointersFromObjects(JNIEnv* env, const std::vector<T>& objects) jlongArray ret = env->NewLongArray(objects.size()); env->SetLongArrayRegion(ret, 0, objects.size(), pointers.data()); return ret; -}
\ No newline at end of file +} + +/** + * Convert a cpp signed (unsigned) integer to an object of BigInteger class + * @tparam T cpp types (int64_t, uint64_t, int32_t, int32_t, etc) + * @param env jni environment + * @param value cpp integer value + * @return an object of java BigInteger + */ +template <class T> +jobject getBigIntegerObject(JNIEnv* env, T value) +{ + std::string s = std::to_string(value); + jstring javaString = env->NewStringUTF(s.c_str()); + jclass bigIntegerClass = env->FindClass("java/math/BigInteger"); + jmethodID bigIntegerConstructor = + env->GetMethodID(bigIntegerClass, "<init>", "(Ljava/lang/String;)V"); + jobject ret = + env->NewObject(bigIntegerClass, bigIntegerConstructor, javaString); + return ret; +} + +/** + * Generate an array of java strings from a vector of cpp strings + * @param env jni environment + * @param cStrings a vector of strings + * @return an array of java strings + */ +jobjectArray getStringArrayFromStringVector( + JNIEnv* env, const std::vector<std::string>& cStrings); + +/** + * Generate a Double object from cpp double value + * @param env jni environment + * @param value + * @return a Double object + */ +jobject getDoubleObject(JNIEnv* env, double value); + +/** + * Generate a Boolean object from cpp bool value + * @param env jni environment + * @param value + * @return a Boolean object + */ +jobject getBooleanObject(JNIEnv* env, bool value); + +#endif // CVC5__JAVA_API_H
\ No newline at end of file diff --git a/src/api/java/jni/cvc5_OptionInfo.cpp b/src/api/java/jni/cvc5_OptionInfo.cpp new file mode 100644 index 000000000..c4bcc2e04 --- /dev/null +++ b/src/api/java/jni/cvc5_OptionInfo.cpp @@ -0,0 +1,351 @@ +/****************************************************************************** + * Top contributors (to current version): + * Mudathir Mohamed + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * The cvc5 Java API. + */ + +#include "cvc5_OptionInfo.h" + +#include "api/cpp/cvc5.h" +#include "cvc5JavaApi.h" + +using namespace cvc5::api; + +/* + * Class: cvc5_OptionInfo + * Method: deletePointer + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_cvc5_OptionInfo_deletePointer(JNIEnv*, + jclass, + jlong pointer) +{ + delete reinterpret_cast<OptionInfo*>(pointer); +} + +/* + * Class: cvc5_OptionInfo + * Method: toString + * Signature: (J)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_cvc5_OptionInfo_toString(JNIEnv* env, + jobject, + jlong pointer) +{ + CVC5_JAVA_API_TRY_CATCH_BEGIN; + OptionInfo* current = reinterpret_cast<OptionInfo*>(pointer); + std::stringstream ss; + ss << *current; + return env->NewStringUTF(ss.str().c_str()); + CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, nullptr); +} + +/* + * Class: cvc5_OptionInfo + * Method: getName + * Signature: (J)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_cvc5_OptionInfo_getName(JNIEnv* env, + jobject, + jlong pointer) +{ + CVC5_JAVA_API_TRY_CATCH_BEGIN; + OptionInfo* current = reinterpret_cast<OptionInfo*>(pointer); + return env->NewStringUTF(current->name.c_str()); + CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, nullptr); +} + +/* + * Class: cvc5_OptionInfo + * Method: getAliases + * Signature: (J)[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_cvc5_OptionInfo_getAliases(JNIEnv* env, + jobject, + jlong pointer) +{ + CVC5_JAVA_API_TRY_CATCH_BEGIN; + OptionInfo* current = reinterpret_cast<OptionInfo*>(pointer); + jobjectArray ret = getStringArrayFromStringVector(env, current->aliases); + return ret; + CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, nullptr); +} + +/* + * Class: cvc5_OptionInfo + * Method: getSetByUser + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL Java_cvc5_OptionInfo_getSetByUser(JNIEnv* env, + jobject, + jlong pointer) +{ + CVC5_JAVA_API_TRY_CATCH_BEGIN; + OptionInfo* current = reinterpret_cast<OptionInfo*>(pointer); + return static_cast<jboolean>(current->setByUser); + CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, static_cast<jboolean>(false)); +} + +/** + * Convert OptionInfo::NumberInfo cpp object to OptionInfo.NumberInfo java + * object + * @tparam T cpp integer types int64_t, uint64_t, etc + * @param env jni environment + * @param optionInfo a java object for this OptionInfo + * @param numberInfoClass the java class for OptionInfo.NumberInfo + * @param methodId a constructor for OptionInfo.NumberInfo + * @param info the cpp OptionInfo::NumberInfo object + * @return a java object of class OptionInfo.NumberInfo<BigInteger> + */ +template <typename T> +jobject getNumberInfoFromInteger(JNIEnv* env, + const _jobject* optionInfo, + jclass numberInfoClass, + jmethodID methodId, + const OptionInfo::NumberInfo<T>& info) +{ + jobject defaultValue = getBigIntegerObject<T>(env, info.defaultValue); + jobject currentValue = getBigIntegerObject<T>(env, info.currentValue); + jobject minimum = nullptr; + if (info.minimum) + { + minimum = getBigIntegerObject<T>(env, *info.minimum); + } + jobject maximum = nullptr; + if (info.maximum) + { + maximum = getBigIntegerObject<T>(env, *info.maximum); + } + jobject ret = env->NewObject(numberInfoClass, + methodId, + optionInfo, + defaultValue, + currentValue, + minimum, + maximum); + + return ret; +} + +template <typename T> +jobject getNumberInfoFromInteger(JNIEnv* env, + const _jobject* optionInfo, + jclass numberInfoClass, + jmethodID methodId, + const OptionInfo::NumberInfo<int64_t>& info); + +template <typename T> +jobject getNumberInfoFromInteger(JNIEnv* env, + const _jobject* optionInfo, + jclass numberInfoClass, + jmethodID methodId, + const OptionInfo::NumberInfo<uint64_t>& info); + +/* + * Class: cvc5_OptionInfo + * Method: getBaseInfo + * Signature: (J)Lcvc5/BaseInfo; + */ +JNIEXPORT jobject JNICALL Java_cvc5_OptionInfo_getBaseInfo(JNIEnv* env, + jobject optionInfo, + jlong pointer) +{ + CVC5_JAVA_API_TRY_CATCH_BEGIN; + OptionInfo* current = reinterpret_cast<OptionInfo*>(pointer); + std::variant<OptionInfo::VoidInfo, + OptionInfo::ValueInfo<bool>, + OptionInfo::ValueInfo<std::string>, + OptionInfo::NumberInfo<int64_t>, + OptionInfo::NumberInfo<uint64_t>, + OptionInfo::NumberInfo<double>, + OptionInfo::ModeInfo> + v = current->valueInfo; + + if (std::holds_alternative<OptionInfo::VoidInfo>(v)) + { + jclass voidInfoClass = env->FindClass("cvc5/OptionInfo$VoidInfo"); + jmethodID methodId = + env->GetMethodID(voidInfoClass, "<init>", "(Lcvc5/OptionInfo;)V"); + jobject ret = env->NewObject(voidInfoClass, methodId, optionInfo); + return ret; + } + + if (std::holds_alternative<OptionInfo::ValueInfo<bool>>(v) + || std::holds_alternative<OptionInfo::ValueInfo<std::string>>(v)) + { + jclass valueInfoClass = env->FindClass("cvc5/OptionInfo$ValueInfo"); + jmethodID methodId = env->GetMethodID( + valueInfoClass, + "<init>", + "(Lcvc5/OptionInfo;Ljava/lang/Object;Ljava/lang/Object;)V"); + + if (std::holds_alternative<OptionInfo::ValueInfo<bool>>(v)) + { + auto info = std::get<OptionInfo::ValueInfo<bool>>(v); + jobject currentValue = getBooleanObject(env, info.currentValue); + jobject defaultValue = getBooleanObject(env, info.defaultValue); + jobject ret = env->NewObject( + valueInfoClass, methodId, optionInfo, defaultValue, currentValue); + return ret; + } + + if (std::holds_alternative<OptionInfo::ValueInfo<std::string>>(v)) + { + auto info = std::get<OptionInfo::ValueInfo<std::string>>(v); + jstring defaultValue = env->NewStringUTF(info.defaultValue.c_str()); + jstring currentValue = env->NewStringUTF(info.currentValue.c_str()); + jobject ret = env->NewObject( + valueInfoClass, methodId, optionInfo, defaultValue, currentValue); + return ret; + } + } + + if (std::holds_alternative<OptionInfo::NumberInfo<int64_t>>(v) + || std::holds_alternative<OptionInfo::NumberInfo<uint64_t>>(v) + || std::holds_alternative<OptionInfo::NumberInfo<double>>(v)) + { + jclass numberInfoClass = env->FindClass("cvc5/OptionInfo$NumberInfo"); + jmethodID methodId = + env->GetMethodID(numberInfoClass, + "<init>", + "(Lcvc5/OptionInfo;Ljava/lang/Object;Ljava/lang/" + "Object;Ljava/lang/Object;Ljava/lang/Object;)V"); + + if (std::holds_alternative<OptionInfo::NumberInfo<int64_t>>(v)) + { + auto info = std::get<OptionInfo::NumberInfo<int64_t>>(v); + return getNumberInfoFromInteger( + env, optionInfo, numberInfoClass, methodId, info); + } + + if (std::holds_alternative<OptionInfo::NumberInfo<uint64_t>>(v)) + { + auto info = std::get<OptionInfo::NumberInfo<uint64_t>>(v); + return getNumberInfoFromInteger( + env, optionInfo, numberInfoClass, methodId, info); + } + + if (std::holds_alternative<OptionInfo::NumberInfo<double>>(v)) + { + auto info = std::get<OptionInfo::NumberInfo<double>>(v); + jobject defaultValue = getDoubleObject(env, info.defaultValue); + jobject currentValue = getDoubleObject(env, info.currentValue); + jobject minimum = nullptr; + if (info.minimum) + { + minimum = getDoubleObject(env, *info.minimum); + } + jobject maximum = nullptr; + if (info.maximum) + { + maximum = getDoubleObject(env, *info.maximum); + } + jobject ret = env->NewObject(numberInfoClass, + methodId, + optionInfo, + defaultValue, + currentValue, + minimum, + maximum); + return ret; + } + } + + if (std::holds_alternative<OptionInfo::ModeInfo>(v)) + { + jclass modeInfoClass = env->FindClass("cvc5/OptionInfo$ModeInfo"); + jmethodID methodId = + env->GetMethodID(modeInfoClass, + "<init>", + "(Lcvc5/OptionInfo;Ljava/lang/String;Ljava/lang/" + "String;[Ljava/lang/String;)V"); + + auto info = std::get<OptionInfo::ModeInfo>(v); + jstring defaultValue = env->NewStringUTF(info.defaultValue.c_str()); + jstring currentValue = env->NewStringUTF(info.currentValue.c_str()); + jobject stringArray = getStringArrayFromStringVector(env, info.modes); + jobject ret = env->NewObject(modeInfoClass, + methodId, + optionInfo, + defaultValue, + currentValue, + stringArray); + return ret; + } + + return nullptr; + CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, nullptr); +} + +/* + * Class: cvc5_OptionInfo + * Method: booleanValue + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL Java_cvc5_OptionInfo_booleanValue(JNIEnv* env, + jobject, + jlong pointer) +{ + CVC5_JAVA_API_TRY_CATCH_BEGIN; + OptionInfo* current = reinterpret_cast<OptionInfo*>(pointer); + return static_cast<jboolean>(current->boolValue()); + CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, static_cast<jboolean>(false)); +} + +/* + * Class: cvc5_OptionInfo + * Method: stringValue + * Signature: (J)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_cvc5_OptionInfo_stringValue(JNIEnv* env, + jobject, + jlong pointer) +{ + CVC5_JAVA_API_TRY_CATCH_BEGIN; + OptionInfo* current = reinterpret_cast<OptionInfo*>(pointer); + std::string ret = current->stringValue(); + return env->NewStringUTF(ret.c_str()); + CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, nullptr); +} + +/* + * Class: cvc5_OptionInfo + * Method: intValue + * Signature: (J)Ljava/math/BigInteger; + */ +JNIEXPORT jobject JNICALL Java_cvc5_OptionInfo_intValue(JNIEnv* env, + jobject, + jlong pointer) +{ + CVC5_JAVA_API_TRY_CATCH_BEGIN; + OptionInfo* current = reinterpret_cast<OptionInfo*>(pointer); + std::int64_t value = current->intValue(); + jobject ret = getBigIntegerObject<std::int64_t>(env, value); + return ret; + CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, nullptr); +} + +/* + * Class: cvc5_OptionInfo + * Method: doubleValue + * Signature: (J)D + */ +JNIEXPORT jdouble JNICALL Java_cvc5_OptionInfo_doubleValue(JNIEnv* env, + jobject, + jlong pointer) +{ + CVC5_JAVA_API_TRY_CATCH_BEGIN; + OptionInfo* current = reinterpret_cast<OptionInfo*>(pointer); + double ret = current->doubleValue(); + return static_cast<jdouble>(ret); + CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, static_cast<jdouble>(0.0)); +} diff --git a/src/api/java/jni/cvc5_Solver.cpp b/src/api/java/jni/cvc5_Solver.cpp index c28ea412f..cdbb48db0 100644 --- a/src/api/java/jni/cvc5_Solver.cpp +++ b/src/api/java/jni/cvc5_Solver.cpp @@ -1061,6 +1061,22 @@ JNIEXPORT jlong JNICALL Java_cvc5_Solver_mkEmptyBag(JNIEnv* env, /* * Class: cvc5_Solver + * Method: mkSepEmp + * Signature: (JJ)J + */ +JNIEXPORT jlong JNICALL Java_cvc5_Solver_mkSepEmp(JNIEnv* env, + jobject, + jlong pointer) +{ + CVC5_JAVA_API_TRY_CATCH_BEGIN; + Solver* solver = reinterpret_cast<Solver*>(pointer); + Term* retPointer = new Term(solver->mkSepEmp()); + return reinterpret_cast<jlong>(retPointer); + CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, 0); +} + +/* + * Class: cvc5_Solver * Method: mkSepNil * Signature: (JJ)J */ @@ -1871,6 +1887,57 @@ JNIEXPORT jstring JNICALL Java_cvc5_Solver_getOption(JNIEnv* env, /* * Class: cvc5_Solver + * Method: getOptionNames + * Signature: (J)[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_cvc5_Solver_getOptionNames(JNIEnv* env, + jobject, + jlong pointer) +{ + CVC5_JAVA_API_TRY_CATCH_BEGIN; + Solver* solver = reinterpret_cast<Solver*>(pointer); + std::vector<std::string> options = solver->getOptionNames(); + jobjectArray ret = getStringArrayFromStringVector(env, options); + return ret; + CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, nullptr); +} + +/* + * Class: cvc5_Solver + * Method: getOptionInfo + * Signature: (JLjava/lang/String;)J + */ +JNIEXPORT jlong JNICALL Java_cvc5_Solver_getOptionInfo(JNIEnv* env, + jobject, + jlong pointer, + jstring jOption) +{ + CVC5_JAVA_API_TRY_CATCH_BEGIN; + Solver* solver = reinterpret_cast<Solver*>(pointer); + std::string cOption(env->GetStringUTFChars(jOption, nullptr)); + OptionInfo* ret = new OptionInfo(solver->getOptionInfo(cOption)); + return reinterpret_cast<jlong>(ret); + CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, 0); +} + +/* + * Class: cvc5_Solver + * Method: getDriverOptions + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_cvc5_Solver_getDriverOptions(JNIEnv* env, + jobject, + jlong pointer) +{ + CVC5_JAVA_API_TRY_CATCH_BEGIN; + Solver* solver = reinterpret_cast<Solver*>(pointer); + DriverOptions* ret = new DriverOptions(solver->getDriverOptions()); + return reinterpret_cast<jlong>(ret); + CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, 0); +} + +/* + * Class: cvc5_Solver * Method: getUnsatAssumptions * Signature: (J)[J */ @@ -1905,6 +1972,62 @@ JNIEXPORT jlongArray JNICALL Java_cvc5_Solver_getUnsatCore(JNIEnv* env, /* * Class: cvc5_Solver + * Method: getDifficulty + * Signature: (J)Ljava/util/Map; + */ +JNIEXPORT jobject JNICALL Java_cvc5_Solver_getDifficulty(JNIEnv* env, + jobject, + jlong pointer) +{ + CVC5_JAVA_API_TRY_CATCH_BEGIN; + Solver* solver = reinterpret_cast<Solver*>(pointer); + std::map<Term, Term> map = solver->getDifficulty(); + // HashMap hashMap = new HashMap(); + jclass hashMapClass = env->FindClass("Ljava/util/HashMap;"); + jmethodID constructor = env->GetMethodID(hashMapClass, "<init>", "()V"); + jobject hashMap = env->NewObject(hashMapClass, constructor); + jmethodID putMethod = env->GetMethodID( + hashMapClass, + "put", + "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + + // Long longObject = new Long(statPointer) + jclass longClass = env->FindClass("Ljava/lang/Long;"); + jmethodID longConstructor = env->GetMethodID(longClass, "<init>", "(J)V"); + + for (const auto& [k, v] : map) + { + // hashmap.put(key, value); + Term* termKey = new Term(k); + Term* termValue = new Term(v); + jobject key = env->NewObject( + longClass, longConstructor, reinterpret_cast<jlong>(termKey)); + jobject value = env->NewObject( + longClass, longConstructor, reinterpret_cast<jlong>(termValue)); + env->CallObjectMethod(hashMap, putMethod, key, value); + } + return hashMap; + CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, nullptr); +} + +/* + * Class: cvc5_Solver + * Method: getProof + * Signature: (J)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_cvc5_Solver_getProof(JNIEnv* env, + jobject, + jlong pointer) +{ + CVC5_JAVA_API_TRY_CATCH_BEGIN; + Solver* solver = reinterpret_cast<Solver*>(pointer); + std::string proof = solver->getProof(); + return env->NewStringUTF(proof.c_str()); + CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, 0); +} + +/* + * Class: cvc5_Solver * Method: getValue * Signature: (JJ)J */ @@ -1940,6 +2063,23 @@ JNIEXPORT jlongArray JNICALL Java_cvc5_Solver_getValue__J_3J( /* * Class: cvc5_Solver + * Method: getModelDomainElements + * Signature: (JJ)[J + */ +JNIEXPORT jlongArray JNICALL Java_cvc5_Solver_getModelDomainElements( + JNIEnv* env, jobject, jlong pointer, jlong sortPointer) +{ + CVC5_JAVA_API_TRY_CATCH_BEGIN; + Solver* solver = reinterpret_cast<Solver*>(pointer); + Sort* sort = reinterpret_cast<Sort*>(sortPointer); + std::vector<Term> terms = solver->getModelDomainElements(*sort); + jlongArray ret = getPointersFromObjects<Term>(env, terms); + return ret; + CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, nullptr); +} + +/* + * Class: cvc5_Solver * Method: isModelCoreSymbol * Signature: (JJ)Z */ @@ -2446,6 +2586,23 @@ JNIEXPORT void JNICALL Java_cvc5_Solver_addSygusConstraint(JNIEnv* env, /* * Class: cvc5_Solver + * Method: addSygusAssume + * Signature: (JJ)V + */ +JNIEXPORT void JNICALL Java_cvc5_Solver_addSygusAssume(JNIEnv* env, + jobject, + jlong pointer, + jlong termPointer) +{ + CVC5_JAVA_API_TRY_CATCH_BEGIN; + Solver* solver = reinterpret_cast<Solver*>(pointer); + Term* term = reinterpret_cast<Term*>(termPointer); + solver->addSygusAssume(*term); + CVC5_JAVA_API_TRY_CATCH_END(env); +} + +/* + * Class: cvc5_Solver * Method: addSygusInvConstraint * Signature: (JJJJJ)V */ @@ -2591,4 +2748,4 @@ JNIEXPORT jlong JNICALL Java_cvc5_Solver_getNullDatatypeDecl(JNIEnv* env, DatatypeDecl* ret = new DatatypeDecl(); return reinterpret_cast<jlong>(ret); CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, 0); -}
\ No newline at end of file +} diff --git a/src/api/java/jni/cvc5_Sort.cpp b/src/api/java/jni/cvc5_Sort.cpp index a2754f032..36ba81249 100644 --- a/src/api/java/jni/cvc5_Sort.cpp +++ b/src/api/java/jni/cvc5_Sort.cpp @@ -978,46 +978,44 @@ JNIEXPORT jint JNICALL Java_cvc5_Sort_getSortConstructorArity(JNIEnv* env, /* * Class: cvc5_Sort - * Method: getBVSize + * Method: getBitVectorSize * Signature: (J)I */ -JNIEXPORT jint JNICALL Java_cvc5_Sort_getBVSize(JNIEnv* env, - jobject, - jlong pointer) +JNIEXPORT jint JNICALL Java_cvc5_Sort_getBitVectorSize(JNIEnv* env, + jobject, + jlong pointer) { CVC5_JAVA_API_TRY_CATCH_BEGIN; Sort* current = reinterpret_cast<Sort*>(pointer); - return static_cast<jint>(current->getBVSize()); + return static_cast<jint>(current->getBitVectorSize()); CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, 0); } /* * Class: cvc5_Sort - * Method: getFPExponentSize + * Method: getFloatingPointExponentSize * Signature: (J)I */ -JNIEXPORT jint JNICALL Java_cvc5_Sort_getFPExponentSize(JNIEnv* env, - jobject, - jlong pointer) +JNIEXPORT jint JNICALL +Java_cvc5_Sort_getFloatingPointExponentSize(JNIEnv* env, jobject, jlong pointer) { CVC5_JAVA_API_TRY_CATCH_BEGIN; Sort* current = reinterpret_cast<Sort*>(pointer); - return static_cast<jint>(current->getFPExponentSize()); + return static_cast<jint>(current->getFloatingPointExponentSize()); CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, 0); } /* * Class: cvc5_Sort - * Method: getFPSignificandSize + * Method: getFloatingPointSignificandSize * Signature: (J)I */ -JNIEXPORT jint JNICALL Java_cvc5_Sort_getFPSignificandSize(JNIEnv* env, - jobject, - jlong pointer) +JNIEXPORT jint JNICALL Java_cvc5_Sort_getFloatingPointSignificandSize( + JNIEnv* env, jobject, jlong pointer) { CVC5_JAVA_API_TRY_CATCH_BEGIN; Sort* current = reinterpret_cast<Sort*>(pointer); - return static_cast<jint>(current->getFPSignificandSize()); + return static_cast<jint>(current->getFloatingPointSignificandSize()); CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, 0); } diff --git a/src/api/parsekinds.py b/src/api/parsekinds.py index 0c39bca6f..0dcc7be68 100644 --- a/src/api/parsekinds.py +++ b/src/api/parsekinds.py @@ -34,7 +34,7 @@ US = '_' NL = '\n' # Expected C++ Enum Declarations -ENUM_START = 'enum CVC5_EXPORT Kind' +ENUM_START = 'enum Kind' ENUM_END = CCB + SC # Comments and Macro Tokens diff --git a/src/api/python/cvc5.pxd b/src/api/python/cvc5.pxd index ef9971c20..02b572120 100644 --- a/src/api/python/cvc5.pxd +++ b/src/api/python/cvc5.pxd @@ -219,6 +219,7 @@ cdef extern from "api/cpp/cvc5.h" namespace "cvc5::api": Term mkRegexpSigma() except + Term mkEmptySet(Sort s) except + Term mkEmptyBag(Sort s) except + + Term mkSepEmp() except + Term mkSepNil(Sort sort) except + Term mkString(const string& s) except + Term mkString(const wstring& s) except + @@ -308,6 +309,7 @@ cdef extern from "api/cpp/cvc5.h" namespace "cvc5::api": bint operator>(const Sort&) except + bint operator<=(const Sort&) except + bint operator>=(const Sort&) except + + bint isNull() except + bint isBoolean() except + bint isInteger() except + bint isReal() except + @@ -321,6 +323,7 @@ cdef extern from "api/cpp/cvc5.h" namespace "cvc5::api": bint isConstructor() except + bint isSelector() except + bint isTester() except + + bint isUpdater() except + bint isFunction() except + bint isPredicate() except + bint isTuple() except + @@ -357,9 +360,9 @@ cdef extern from "api/cpp/cvc5.h" namespace "cvc5::api": vector[Sort] getUninterpretedSortParamSorts() except + string getSortConstructorName() except + size_t getSortConstructorArity() except + - uint32_t getBVSize() except + - uint32_t getFPExponentSize() except + - uint32_t getFPSignificandSize() except + + uint32_t getBitVectorSize() except + + uint32_t getFloatingPointExponentSize() except + + uint32_t getFloatingPointSignificandSize() except + vector[Sort] getDatatypeParamSorts() except + size_t getDatatypeArity() except + size_t getTupleLength() except + diff --git a/src/api/python/cvc5.pxi b/src/api/python/cvc5.pxi index 0f6b54dc6..4627859b9 100644 --- a/src/api/python/cvc5.pxi +++ b/src/api/python/cvc5.pxi @@ -95,7 +95,10 @@ cdef c_hash[c_Term] ctermhash = c_hash[c_Term]() cdef class Datatype: - """Wrapper class for :cpp:class:`cvc5::api::Datatype`.""" + """ + A cvc5 datatype. + Wrapper class for :cpp:class:`cvc5::api::Datatype`. + """ cdef c_Datatype cd cdef Solver solver def __cinit__(self, Solver solver): @@ -114,7 +117,7 @@ cdef class Datatype: def getConstructor(self, str name): """ :param name: the name of the constructor. - :return: a constructor by name. + :return: a constructor by name. """ cdef DatatypeConstructor dc = DatatypeConstructor(self.solver) dc.cdc = self.cd.getConstructor(name.encode()) @@ -151,34 +154,35 @@ cdef class Datatype: return self.cd.getNumConstructors() def isParametric(self): - """:return: whether this datatype is parametric.""" + """:return: True if this datatype is parametric.""" return self.cd.isParametric() def isCodatatype(self): - """:return: whether this datatype corresponds to a co-datatype.""" + """:return: True if this datatype corresponds to a co-datatype.""" return self.cd.isCodatatype() def isTuple(self): - """:return: whether this datatype corresponds to a tuple.""" + """:return: True if this datatype corresponds to a tuple.""" return self.cd.isTuple() def isRecord(self): - """:return: whether this datatype corresponds to a record.""" + """:return: True if this datatype corresponds to a record.""" return self.cd.isRecord() def isFinite(self): - """:return: whether this datatype is finite.""" + """:return: True if this datatype is finite.""" return self.cd.isFinite() def isWellFounded(self): - """:return: whether this datatype is well-founded (see :cpp:func:`Datatype::isWellFounded() <cvc5::api::Datatype::isWellFounded>`).""" + """:return: True if this datatype is well-founded (see :cpp:func:`Datatype::isWellFounded() <cvc5::api::Datatype::isWellFounded>`).""" return self.cd.isWellFounded() def hasNestedRecursion(self): - """:return: whether this datatype has nested recursion (see :cpp:func:`Datatype::hasNestedRecursion() <cvc5::api::Datatype::hasNestedRecursion>`).""" + """:return: True if this datatype has nested recursion (see :cpp:func:`Datatype::hasNestedRecursion() <cvc5::api::Datatype::hasNestedRecursion>`).""" return self.cd.hasNestedRecursion() def isNull(self): + """:return: True if this Datatype is a null object.""" return self.cd.isNull() def __str__(self): @@ -195,7 +199,10 @@ cdef class Datatype: cdef class DatatypeConstructor: - """Wrapper class for :cpp:class:`cvc5::api::DatatypeConstructor`.""" + """ + A cvc5 datatype constructor. + Wrapper class for :cpp:class:`cvc5::api::DatatypeConstructor`. + """ cdef c_DatatypeConstructor cdc cdef Solver solver def __cinit__(self, Solver solver): @@ -270,6 +277,7 @@ cdef class DatatypeConstructor: return term def isNull(self): + """:return: True if this DatatypeConstructor is a null object.""" return self.cdc.isNull() def __str__(self): @@ -286,7 +294,10 @@ cdef class DatatypeConstructor: cdef class DatatypeConstructorDecl: - """Wrapper class for :cpp:class:`cvc5::api::DatatypeConstructorDecl`.""" + """ + A cvc5 datatype constructor declaration. + Wrapper class for :cpp:class:`cvc5::api::DatatypeConstructorDecl`. + """ cdef c_DatatypeConstructorDecl cddc cdef Solver solver @@ -311,6 +322,7 @@ cdef class DatatypeConstructorDecl: self.cddc.addSelectorSelf(name.encode()) def isNull(self): + """:return: True if this DatatypeConstructorDecl is a null object.""" return self.cddc.isNull() def __str__(self): @@ -321,7 +333,10 @@ cdef class DatatypeConstructorDecl: cdef class DatatypeDecl: - """Wrapper class for :cpp:class:`cvc5::api::DatatypeDecl`.""" + """ + A cvc5 datatype declaration. + Wrapper class for :cpp:class:`cvc5::api::DatatypeDecl`. + """ cdef c_DatatypeDecl cdd cdef Solver solver def __cinit__(self, Solver solver): @@ -354,6 +369,7 @@ cdef class DatatypeDecl: return self.cdd.getName().decode() def isNull(self): + """:return: True if this DatatypeDecl is a null object.""" return self.cdd.isNull() def __str__(self): @@ -364,7 +380,10 @@ cdef class DatatypeDecl: cdef class DatatypeSelector: - """Wrapper class for :cpp:class:`cvc5::api::DatatypeSelector`.""" + """ + A cvc5 datatype selector. + Wrapper class for :cpp:class:`cvc5::api::DatatypeSelector`. + """ cdef c_DatatypeSelector cds cdef Solver solver def __cinit__(self, Solver solver): @@ -402,6 +421,7 @@ cdef class DatatypeSelector: return sort def isNull(self): + """:return: True if this DatatypeSelector is a null object.""" return self.cds.isNull() def __str__(self): @@ -412,6 +432,13 @@ cdef class DatatypeSelector: cdef class Op: + """ + A cvc5 operator. + An operator is a term that represents certain operators, + instantiated with its required parameters, e.g., + a term of kind :cpp:enumerator:`BITVECTOR_EXTRACT`. + Wrapper class for :cpp:class:`cvc5::api::Op`. + """ cdef c_Op cop cdef Solver solver def __cinit__(self, Solver solver): @@ -434,18 +461,33 @@ cdef class Op: return cophash(self.cop) def getKind(self): + """ + :return: the kind of this operator. + """ return kind(<int> self.cop.getKind()) def isIndexed(self): + """ + :return: True iff this operator is indexed. + """ return self.cop.isIndexed() def isNull(self): + """ + :return: True iff this operator is a null term. + """ return self.cop.isNull() def getNumIndices(self): + """ + :return: number of indices of this op. + """ return self.cop.getNumIndices() def getIndices(self): + """ + :return: the indices used to create this Op (see :cpp:func:`Op::getIndices() <cvc5::api::Op::getIndices>`). + """ indices = None try: indices = self.cop.getIndices[string]().decode() @@ -468,6 +510,10 @@ cdef class Op: return indices cdef class Grammar: + """ + A Sygus Grammar. + Wrapper class for :cpp:class:`cvc5::api::Grammar`. + """ cdef c_Grammar cgrammar cdef Solver solver def __cinit__(self, Solver solver): @@ -475,46 +521,100 @@ cdef class Grammar: self.cgrammar = c_Grammar() def addRule(self, Term ntSymbol, Term rule): + """ + Add ``rule`` to the set of rules corresponding to ``ntSymbol``. + + :param ntSymbol: the non-terminal to which the rule is added. + :param rule: the rule to add. + """ self.cgrammar.addRule(ntSymbol.cterm, rule.cterm) def addAnyConstant(self, Term ntSymbol): + """ + Allow ``ntSymbol`` to be an arbitrary constant. + + :param ntSymbol: the non-terminal allowed to be constant. + """ self.cgrammar.addAnyConstant(ntSymbol.cterm) def addAnyVariable(self, Term ntSymbol): + """ + Allow ``ntSymbol`` to be any input variable to corresponding synth-fun/synth-inv with the same sort as ``ntSymbol``. + + :param ntSymbol: the non-terminal allowed to be any input variable. + """ self.cgrammar.addAnyVariable(ntSymbol.cterm) def addRules(self, Term ntSymbol, rules): + """ + Add ``ntSymbol`` to the set of rules corresponding to ``ntSymbol``. + + :param ntSymbol: the non-terminal to which the rules are added. + :param rules: the rules to add. + """ cdef vector[c_Term] crules for r in rules: crules.push_back((<Term?> r).cterm) self.cgrammar.addRules(ntSymbol.cterm, crules) cdef class Result: + """ + Encapsulation of a three-valued solver result, with explanations. + Wrapper class for :cpp:class:`cvc5::api::Result`. + """ cdef c_Result cr def __cinit__(self): # gets populated by solver self.cr = c_Result() def isNull(self): + """ + :return: True if Result is empty, i.e., a nullary Result, + and not an actual result returned from a :cpp:func:`Solver::checkSat() <cvc5::api::Solver::checkSat>` (and friends) query. + """ return self.cr.isNull() def isSat(self): + """ + :return: True if query was a satisfiable :cpp:func:`Solver::checkSat() <cvc5::api::Solver::checkSat>` or :cpp:func:`Solver::checkSatAssuming() <cvc5::api::Solver::checkSatAssuming>` query. + """ return self.cr.isSat() def isUnsat(self): + """ + :return: True if query was an usatisfiable :cpp:func:`Solver::checkSat() <cvc5::api::Solver::checkSat>` or :cpp:func:`Solver::checkSatAssuming() <cvc5::api::Solver::checkSatAssuming>` query. + """ return self.cr.isUnsat() def isSatUnknown(self): + """ + :return: True if query was a :cpp:func:`Solver::checkSat() <cvc5::api::Solver::checkSat>` or :cpp:func:`Solver::checkSatAssuming() <cvc5::api::Solver::checkSatAssuming>` query and cvc5 was not able to determine (un)satisfiability. + """ return self.cr.isSatUnknown() def isEntailed(self): + """ + :return: True if corresponding query was an entailed :cpp:func:`Solver::checkEntailed() <cvc5::api::Solver::checkEntailed>` query. + """ return self.cr.isEntailed() def isNotEntailed(self): + """ + :return: True if corresponding query was a :cpp:func:`Solver::checkEntailed() <cvc5::api::Solver::checkEntailed>` query that is not entailed. + """ return self.cr.isNotEntailed() def isEntailmentUnknown(self): + """ + :return: True if query was a :cpp:func:`Solver::checkEntailed() <cvc5::api::Solver::checkEntailed>` query query and cvc5 was not able to determine if it is entailed. + """ return self.cr.isEntailmentUnknown() + + def getUnknownExplanation(self): + """ + :return: an explanation for an unknown query result. + """ + return UnknownExplanation(<int> self.cr.getUnknownExplanation()) def __eq__(self, Result other): return self.cr == other.cr @@ -522,9 +622,6 @@ cdef class Result: def __ne__(self, Result other): return self.cr != other.cr - def getUnknownExplanation(self): - return UnknownExplanation(<int> self.cr.getUnknownExplanation()) - def __str__(self): return self.cr.toString().decode() @@ -533,6 +630,20 @@ cdef class Result: cdef class RoundingMode: + """ + Rounding modes for floating-point numbers. + + For many floating-point operations, infinitely precise results may not be + representable with the number of available bits. Thus, the results are + rounded in a certain way to one of the representable floating-point numbers. + + These rounding modes directly follow the SMT-LIB theory for floating-point + arithmetic, which in turn is based on IEEE Standard 754 :cite:`IEEE754`. + The rounding modes are specified in Sections 4.3.1 and 4.3.2 of the IEEE + Standard 754. + + Wrapper class for :cpp:enum:`cvc5::api::RoundingMode`. + """ cdef c_RoundingMode crm cdef str name def __cinit__(self, int rm): @@ -557,6 +668,9 @@ cdef class RoundingMode: cdef class UnknownExplanation: + """ + Wrapper class for :cpp:enum:`cvc5::api::Result::UnknownExplanation`. + """ cdef c_UnknownExplanation cue cdef str name def __cinit__(self, int ue): @@ -907,9 +1021,10 @@ cdef class Solver: - ``Op mkOp(Kind kind, const string& arg)`` - ``Op mkOp(Kind kind, uint32_t arg)`` - ``Op mkOp(Kind kind, uint32_t arg0, uint32_t arg1)`` + - ``Op mkOp(Kind kind, [uint32_t arg0, ...])`` (used for the TupleProject kind) """ cdef Op op = Op(self) - cdef vector[int] v + cdef vector[uint32_t] v if len(args) == 0: op.cop = self.csolver.mkOp(k.k) @@ -922,7 +1037,10 @@ cdef class Solver: op.cop = self.csolver.mkOp(k.k, <int?> args[0]) elif isinstance(args[0], list): for a in args[0]: - v.push_back((<int?> a)) + if a < 0 or a >= 2 ** 31: + raise ValueError("Argument {} must fit in a uint32_t".format(a)) + + v.push_back((<uint32_t?> a)) op.cop = self.csolver.mkOp(k.k, <const vector[uint32_t]&> v) else: raise ValueError("Unsupported signature" @@ -1050,6 +1168,15 @@ cdef class Solver: term.cterm = self.csolver.mkEmptyBag(s.csort) return term + def mkSepEmp(self): + """Create a separation logic empty term. + + :return: the separation logic empty term + """ + cdef Term term = Term(self) + term.cterm = self.csolver.mkSepEmp() + return term + def mkSepNil(self, Sort sort): """Create a separation logic nil term. @@ -2087,6 +2214,9 @@ cdef class Sort: def __hash__(self): return csorthash(self.csort) + def isNull(self): + return self.csort.isNull() + def isBoolean(self): return self.csort.isBoolean() @@ -2126,6 +2256,9 @@ cdef class Sort: def isTester(self): return self.csort.isTester() + def isUpdater(self): + return self.csort.isUpdater() + def isFunction(self): return self.csort.isFunction() @@ -2278,14 +2411,14 @@ cdef class Sort: def getSortConstructorArity(self): return self.csort.getSortConstructorArity() - def getBVSize(self): - return self.csort.getBVSize() + def getBitVectorSize(self): + return self.csort.getBitVectorSize() - def getFPExponentSize(self): - return self.csort.getFPExponentSize() + def getFloatingPointExponentSize(self): + return self.csort.getFloatingPointExponentSize() - def getFPSignificandSize(self): - return self.csort.getFPSignificandSize() + def getFloatingPointSignificandSize(self): + return self.csort.getFloatingPointSignificandSize() def getDatatypeParamSorts(self): param_sorts = [] diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt index 8c1c2a714..d2c763159 100644 --- a/src/base/CMakeLists.txt +++ b/src/base/CMakeLists.txt @@ -76,5 +76,5 @@ set_source_files_properties( add_library(cvc5base OBJECT ${LIBBASE_SOURCES}) set_target_properties(cvc5base PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_compile_definitions(cvc5base PRIVATE -D__BUILDING_CVC5LIB) +target_compile_definitions(cvc5base PRIVATE -D__BUILDING_CVC5LIB -Dcvc5_obj_EXPORTS) add_dependencies(cvc5base gen-versioninfo gen-tags) diff --git a/src/context/CMakeLists.txt b/src/context/CMakeLists.txt index 00b4d91ee..482dc6acc 100644 --- a/src/context/CMakeLists.txt +++ b/src/context/CMakeLists.txt @@ -35,4 +35,4 @@ set(LIBCONTEXT_SOURCES add_library(cvc5context OBJECT ${LIBCONTEXT_SOURCES}) set_target_properties(cvc5context PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_compile_definitions(cvc5context PRIVATE -D__BUILDING_CVC5LIB) +target_compile_definitions(cvc5context PRIVATE -D__BUILDING_CVC5LIB -Dcvc5_obj_EXPORTS) diff --git a/src/expr/CMakeLists.txt b/src/expr/CMakeLists.txt index e73960676..45ce01edb 100644 --- a/src/expr/CMakeLists.txt +++ b/src/expr/CMakeLists.txt @@ -26,6 +26,8 @@ libcvc5_add_sources( bound_var_manager.h cardinality_constraint.cpp cardinality_constraint.h + codatatype_bound_variable.cpp + codatatype_bound_variable.h emptyset.cpp emptyset.h emptybag.cpp @@ -95,6 +97,8 @@ libcvc5_add_sources( sygus_datatype.h uninterpreted_constant.cpp uninterpreted_constant.h + variadic_trie.cpp + variadic_trie.h ) libcvc5_add_sources(GENERATED diff --git a/src/expr/cardinality_constraint.cpp b/src/expr/cardinality_constraint.cpp index 3841228fb..695f5d4a3 100644 --- a/src/expr/cardinality_constraint.cpp +++ b/src/expr/cardinality_constraint.cpp @@ -56,6 +56,13 @@ std::ostream& operator<<(std::ostream& out, const CardinalityConstraint& cc) << ')'; } +size_t CardinalityConstraintHashFunction::operator()( + const CardinalityConstraint& cc) const +{ + return std::hash<TypeNode>()(cc.getType()) + * IntegerHashFunction()(cc.getUpperBound()); +} + CombinedCardinalityConstraint::CombinedCardinalityConstraint(const Integer& ub) : d_ubound(ub) { diff --git a/src/expr/cardinality_constraint.h b/src/expr/cardinality_constraint.h index a51ba545c..b2bfa836f 100644 --- a/src/expr/cardinality_constraint.h +++ b/src/expr/cardinality_constraint.h @@ -60,10 +60,10 @@ class CardinalityConstraint std::ostream& operator<<(std::ostream& out, const CardinalityConstraint& cc); -using CardinalityConstraintHashFunction = PairHashFunction<TypeNode, - Integer, - std::hash<TypeNode>, - IntegerHashFunction>; +struct CardinalityConstraintHashFunction +{ + size_t operator()(const CardinalityConstraint& cc) const; +}; /** * A combined cardinality constraint, handled in the cardinality extension of diff --git a/src/expr/codatatype_bound_variable.cpp b/src/expr/codatatype_bound_variable.cpp new file mode 100644 index 000000000..a6a9d8e3e --- /dev/null +++ b/src/expr/codatatype_bound_variable.cpp @@ -0,0 +1,113 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Representation of bound variables in codatatype values + */ + +#include "expr/codatatype_bound_variable.h" + +#include <algorithm> +#include <iostream> +#include <sstream> +#include <string> + +#include "base/check.h" +#include "expr/type_node.h" + +using namespace std; + +namespace cvc5 { + +CodatatypeBoundVariable::CodatatypeBoundVariable(const TypeNode& type, + Integer index) + : d_type(new TypeNode(type)), d_index(index) +{ + PrettyCheckArgument(type.isCodatatype(), + type, + "codatatype bound variables can only be created for " + "codatatype sorts, not `%s'", + type.toString().c_str()); + PrettyCheckArgument( + index >= 0, + index, + "index >= 0 required for codatatype bound variable index, not `%s'", + index.toString().c_str()); +} + +CodatatypeBoundVariable::~CodatatypeBoundVariable() {} + +CodatatypeBoundVariable::CodatatypeBoundVariable( + const CodatatypeBoundVariable& other) + : d_type(new TypeNode(other.getType())), d_index(other.getIndex()) +{ +} + +const TypeNode& CodatatypeBoundVariable::getType() const { return *d_type; } +const Integer& CodatatypeBoundVariable::getIndex() const { return d_index; } +bool CodatatypeBoundVariable::operator==( + const CodatatypeBoundVariable& cbv) const +{ + return getType() == cbv.getType() && d_index == cbv.d_index; +} +bool CodatatypeBoundVariable::operator!=( + const CodatatypeBoundVariable& cbv) const +{ + return !(*this == cbv); +} + +bool CodatatypeBoundVariable::operator<( + const CodatatypeBoundVariable& cbv) const +{ + return getType() < cbv.getType() + || (getType() == cbv.getType() && d_index < cbv.d_index); +} +bool CodatatypeBoundVariable::operator<=( + const CodatatypeBoundVariable& cbv) const +{ + return getType() < cbv.getType() + || (getType() == cbv.getType() && d_index <= cbv.d_index); +} +bool CodatatypeBoundVariable::operator>( + const CodatatypeBoundVariable& cbv) const +{ + return !(*this <= cbv); +} +bool CodatatypeBoundVariable::operator>=( + const CodatatypeBoundVariable& cbv) const +{ + return !(*this < cbv); +} + +std::ostream& operator<<(std::ostream& out, const CodatatypeBoundVariable& cbv) +{ + std::stringstream ss; + ss << cbv.getType(); + std::string st(ss.str()); + // must remove delimiting quotes from the name of the type + // this prevents us from printing symbols like |@cbv_|T|_n| + std::string q("|"); + size_t pos; + while ((pos = st.find(q)) != std::string::npos) + { + st.replace(pos, 1, ""); + } + return out << "cbv_" << st.c_str() << "_" << cbv.getIndex(); +} + +size_t CodatatypeBoundVariableHashFunction::operator()( + const CodatatypeBoundVariable& cbv) const +{ + return std::hash<TypeNode>()(cbv.getType()) + * IntegerHashFunction()(cbv.getIndex()); +} + +} // namespace cvc5 diff --git a/src/expr/codatatype_bound_variable.h b/src/expr/codatatype_bound_variable.h new file mode 100644 index 000000000..9af8476d5 --- /dev/null +++ b/src/expr/codatatype_bound_variable.h @@ -0,0 +1,91 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Representation of bound variables in codatatype values + */ + +#include "cvc5_public.h" + +#ifndef CVC5__EXPR__CODATATYPE_BOUND_VARIABLE_H +#define CVC5__EXPR__CODATATYPE_BOUND_VARIABLE_H + +#include <iosfwd> +#include <memory> + +#include "util/integer.h" + +namespace cvc5 { + +class TypeNode; + +/** + * In theory, codatatype values are represented in using a MU-notation form, + * where recursive values may contain free constants indexed by their de Bruijn + * indices. This is sometimes written: + * MU x. (cons 0 x). + * where the MU binder is label for a term position, which x references. + * Instead of constructing an explicit MU binder (which is problematic for + * canonicity), we use de Bruijn indices for representing bound variables, + * whose index indicates the level in which it is nested. For example, we + * represent the above value as: + * (cons 0 @cbv_0) + * In the above value, @cbv_0 is a pointer to its direct parent, so the above + * value represents the infinite list (cons 0 (cons 0 (cons 0 ... ))). + * Another example, the value: + * (cons 0 (cons 1 @cbv_1)) + * @cbv_1 is pointer to the top most node of this value, so this is value + * represents the infinite list (cons 0 (cons 1 (cons 0 (cons 1 ...)))). The + * value: + * (cons 0 (cons 1 @cbv_0)) + * on the other hand represents the lasso: + * (cons 0 (cons 1 (cons 1 (cons 1 ... )))) + * + * This class is used for representing the indexed bound variables in the above + * values. It is considered a constant (isConst is true). However, constants + * of codatatype type are normalized by the rewriter (see datatypes rewriter + * normalizeCodatatypeConstant) to ensure their canonicity, via a variant + * of Hopcroft's algorithm. + */ +class CodatatypeBoundVariable +{ + public: + CodatatypeBoundVariable(const TypeNode& type, Integer index); + ~CodatatypeBoundVariable(); + + CodatatypeBoundVariable(const CodatatypeBoundVariable& other); + + const TypeNode& getType() const; + const Integer& getIndex() const; + bool operator==(const CodatatypeBoundVariable& cbv) const; + bool operator!=(const CodatatypeBoundVariable& cbv) const; + bool operator<(const CodatatypeBoundVariable& cbv) const; + bool operator<=(const CodatatypeBoundVariable& cbv) const; + bool operator>(const CodatatypeBoundVariable& cbv) const; + bool operator>=(const CodatatypeBoundVariable& cbv) const; + + private: + std::unique_ptr<TypeNode> d_type; + const Integer d_index; +}; +std::ostream& operator<<(std::ostream& out, const CodatatypeBoundVariable& cbv); + +/** + * Hash function for codatatype bound variables. + */ +struct CodatatypeBoundVariableHashFunction +{ + size_t operator()(const CodatatypeBoundVariable& cbv) const; +}; + +} // namespace cvc5 + +#endif /* CVC5__UNINTERPRETED_CONSTANT_H */ diff --git a/src/expr/uninterpreted_constant.cpp b/src/expr/uninterpreted_constant.cpp index ef354568d..709ec112f 100644 --- a/src/expr/uninterpreted_constant.cpp +++ b/src/expr/uninterpreted_constant.cpp @@ -31,7 +31,11 @@ UninterpretedConstant::UninterpretedConstant(const TypeNode& type, Integer index) : d_type(new TypeNode(type)), d_index(index) { - //PrettyCheckArgument(type.isSort(), type, "uninterpreted constants can only be created for uninterpreted sorts, not `%s'", type.toString().c_str()); + PrettyCheckArgument(type.isSort(), + type, + "uninterpreted constants can only be created for " + "uninterpreted sorts, not `%s'", + type.toString().c_str()); PrettyCheckArgument(index >= 0, index, "index >= 0 required for uninterpreted constant index, not `%s'", index.toString().c_str()); } diff --git a/src/expr/variadic_trie.cpp b/src/expr/variadic_trie.cpp new file mode 100644 index 000000000..bd27780e9 --- /dev/null +++ b/src/expr/variadic_trie.cpp @@ -0,0 +1,55 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Variadic trie utility + */ + +#include "expr/variadic_trie.h" + +namespace cvc5 { + +bool VariadicTrie::add(Node n, const std::vector<Node>& i) +{ + VariadicTrie* curr = this; + for (const Node& ic : i) + { + curr = &(curr->d_children[ic]); + } + if (curr->d_data.isNull()) + { + curr->d_data = n; + return true; + } + return false; +} + +bool VariadicTrie::hasSubset(const std::vector<Node>& is) const +{ + if (!d_data.isNull()) + { + return true; + } + for (const std::pair<const Node, VariadicTrie>& p : d_children) + { + Node n = p.first; + if (std::find(is.begin(), is.end(), n) != is.end()) + { + if (p.second.hasSubset(is)) + { + return true; + } + } + } + return false; +} + +} // namespace cvc5 diff --git a/src/expr/variadic_trie.h b/src/expr/variadic_trie.h new file mode 100644 index 000000000..aa7ca1e37 --- /dev/null +++ b/src/expr/variadic_trie.h @@ -0,0 +1,54 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Variadic trie utility + */ + +#include "cvc5_private.h" + +#ifndef CVC5__EXPR__VARIADIC_TRIE_H +#define CVC5__EXPR__VARIADIC_TRIE_H + +#include <map> +#include <vector> + +#include "expr/node.h" + +namespace cvc5 { + +/** + * A trie that stores data at undetermined depth. Storing data at + * undetermined depth is in contrast to the NodeTrie (expr/node_trie.h), which + * assumes all data is stored at a fixed depth. + * + * Since data can be stored at any depth, we require both a d_children field + * and a d_data field. + */ +class VariadicTrie +{ + public: + /** the children of this node */ + std::map<Node, VariadicTrie> d_children; + /** the data at this node */ + Node d_data; + /** + * Add data with identifier n indexed by i, return true if data is not already + * stored at the node indexed by i. + */ + bool add(Node n, const std::vector<Node>& i); + /** Is there any data in this trie that is indexed by any subset of is? */ + bool hasSubset(const std::vector<Node>& is) const; +}; + +} // namespace cvc5 + +#endif /* CVC5__EXPR__VARIADIC_TRIE_H */ diff --git a/src/options/language.h b/src/options/language.h index 05756f98f..2bf596124 100644 --- a/src/options/language.h +++ b/src/options/language.h @@ -25,7 +25,7 @@ namespace cvc5 { -enum class CVC5_EXPORT Language +enum class Language { // SPECIAL "NON-LANGUAGE" LANGUAGES HAVE ENUM VALUE < 0 diff --git a/src/options/managed_streams.cpp b/src/options/managed_streams.cpp index 81bb242cf..90090df25 100644 --- a/src/options/managed_streams.cpp +++ b/src/options/managed_streams.cpp @@ -100,34 +100,54 @@ std::istream* openIStream(const std::string& filename) } } // namespace detail -std::ostream* ManagedErr::defaultValue() const { return &std::cerr; } +ManagedErr::ManagedErr() : ManagedStream(&std::cerr, "stderr") {} bool ManagedErr::specialCases(const std::string& value) { if (value == "stderr" || value == "--") { - d_stream.reset(); + d_nonowned = &std::cerr; + d_owned.reset(); + d_description = "stderr"; + return true; + } + else if (value == "stdout") + { + d_nonowned = &std::cout; + d_owned.reset(); + d_description = "stdout"; return true; } return false; } -std::istream* ManagedIn::defaultValue() const { return &std::cin; } +ManagedIn::ManagedIn() : ManagedStream(&std::cin, "stdin") {} bool ManagedIn::specialCases(const std::string& value) { if (value == "stdin" || value == "--") { - d_stream.reset(); + d_nonowned = &std::cin; + d_owned.reset(); + d_description = "stdin"; return true; } return false; } -std::ostream* ManagedOut::defaultValue() const { return &std::cout; } +ManagedOut::ManagedOut() : ManagedStream(&std::cout, "stdout") {} bool ManagedOut::specialCases(const std::string& value) { if (value == "stdout" || value == "--") { - d_stream.reset(); + d_nonowned = &std::cout; + d_owned.reset(); + d_description = "stdout"; + return true; + } + else if (value == "stderr") + { + d_nonowned = &std::cerr; + d_owned.reset(); + d_description = "stderr"; return true; } return false; diff --git a/src/options/managed_streams.h b/src/options/managed_streams.h index 56bb21c2e..cf1820de6 100644 --- a/src/options/managed_streams.h +++ b/src/options/managed_streams.h @@ -50,7 +50,8 @@ template <typename Stream> class ManagedStream { public: - ManagedStream() {} + ManagedStream(Stream* nonowned, std::string description) + : d_nonowned(nonowned), d_description(std::move(description)) {} virtual ~ManagedStream() {} /** @@ -62,11 +63,15 @@ class ManagedStream if (specialCases(value)) return; if constexpr (std::is_same<Stream, std::ostream>::value) { - d_stream.reset(detail::openOStream(value)); + d_nonowned = nullptr; + d_owned.reset(detail::openOStream(value)); + d_description = value; } else if constexpr (std::is_same<Stream, std::istream>::value) { - d_stream.reset(detail::openIStream(value)); + d_nonowned = nullptr; + d_owned.reset(detail::openIStream(value)); + d_description = value; } } @@ -75,12 +80,14 @@ class ManagedStream operator Stream&() const { return *getPtr(); } operator Stream*() const { return getPtr(); } + const std::string& description() const { return d_description; } + protected: - std::shared_ptr<Stream> d_stream; + Stream* d_nonowned; + std::shared_ptr<Stream> d_owned; + std::string d_description = "<null>"; private: - /** Returns the value to be used if d_stream is not set. */ - virtual Stream* defaultValue() const = 0; /** * Check if there is a special case for this value. If so, the implementation * should set d_stream appropriately and return true to skip the default @@ -88,18 +95,18 @@ class ManagedStream */ virtual bool specialCases(const std::string& value) = 0; - /** Return the pointer, either from d_stream of from defaultValue(). */ + /** Return the pointer, either from d_nonowned or d_owned. */ Stream* getPtr() const { - if (d_stream) return d_stream.get(); - return defaultValue(); + if (d_nonowned != nullptr) return d_nonowned; + return d_owned.get(); } }; template <typename Stream> std::ostream& operator<<(std::ostream& os, const ManagedStream<Stream>& ms) { - return os << "ManagedStream"; + return os << ms.description(); } /** @@ -108,7 +115,10 @@ std::ostream& operator<<(std::ostream& os, const ManagedStream<Stream>& ms) */ class ManagedErr : public ManagedStream<std::ostream> { - std::ostream* defaultValue() const override final; + public: + ManagedErr(); + + private: bool specialCases(const std::string& value) override final; }; @@ -118,7 +128,10 @@ class ManagedErr : public ManagedStream<std::ostream> */ class ManagedIn : public ManagedStream<std::istream> { - std::istream* defaultValue() const override final; + public: + ManagedIn(); + + private: bool specialCases(const std::string& value) override final; }; @@ -128,7 +141,10 @@ class ManagedIn : public ManagedStream<std::istream> */ class ManagedOut : public ManagedStream<std::ostream> { - std::ostream* defaultValue() const override final; + public: + ManagedOut(); + + private: bool specialCases(const std::string& value) override final; }; diff --git a/src/options/mkoptions.py b/src/options/mkoptions.py index a8f631de6..57f8b64e6 100644 --- a/src/options/mkoptions.py +++ b/src/options/mkoptions.py @@ -480,17 +480,6 @@ def generate_module_option_names(module): 'static constexpr const char* {name}__name = "{long_name}";', relevant) -def generate_module_setdefaults_decl(module): - res = [] - for option in module.options: - if option.name is None: - continue - funcname = option.name[0].capitalize() + option.name[1:] - res.append('void setDefault{}(Options& opts, {} value);'.format( - funcname, option.type)) - return '\n'.join(res) - - ################################################################################ # for options/<module>.cpp @@ -581,27 +570,6 @@ def generate_module_mode_impl(module): return '\n'.join(res) -TPL_SETDEFAULT_IMPL = '''void setDefault{capname}(Options& opts, {type} value) -{{ - if (!opts.{module}.{name}WasSetByUser) opts.{module}.{name} = value; -}}''' - - -def generate_module_setdefaults_impl(module): - res = [] - for option in module.options: - if option.name is None: - continue - fmt = { - 'capname': option.name[0].capitalize() + option.name[1:], - 'type': option.type, - 'module': module.id, - 'name': option.name, - } - res.append(TPL_SETDEFAULT_IMPL.format(**fmt)) - return '\n'.join(res) - - ################################################################################ # for main/options.cpp @@ -875,11 +843,9 @@ def codegen_module(module, dst_dir, tpls): 'holder_decl': generate_module_holder_decl(module), 'wrapper_functions': generate_module_wrapper_functions(module), 'option_names': generate_module_option_names(module), - 'setdefaults_decl': generate_module_setdefaults_decl(module), # module source 'header': module.header, 'modes_impl': generate_module_mode_impl(module), - 'setdefaults_impl': generate_module_setdefaults_impl(module), } for tpl in tpls: filename = tpl['output'].replace('module', module.filename) diff --git a/src/options/module_template.cpp b/src/options/module_template.cpp index ee5260f34..d2ece3f13 100644 --- a/src/options/module_template.cpp +++ b/src/options/module_template.cpp @@ -28,11 +28,4 @@ namespace cvc5::options { ${modes_impl}$ // clang-format on -namespace ${id}$ -{ -// clang-format off -${setdefaults_impl}$ -// clang-format on -} - } // namespace cvc5::options diff --git a/src/options/module_template.h b/src/options/module_template.h index 61d689b72..d9b591f11 100644 --- a/src/options/module_template.h +++ b/src/options/module_template.h @@ -56,8 +56,6 @@ namespace ${id}$ { // clang-format off ${option_names}$ - -${setdefaults_decl}$ // clang-format on } diff --git a/src/options/options_handler.cpp b/src/options/options_handler.cpp index 93b08a858..ec6c831e4 100644 --- a/src/options/options_handler.cpp +++ b/src/options/options_handler.cpp @@ -387,7 +387,10 @@ void OptionsHandler::checkBvSatSolver(const std::string& option, + " does not support lazy bit-blasting.\n" + "Try --bv-sat-solver=minisat"); } - options::bv::setDefaultBitvectorToBool(*d_options, true); + if (!d_options->bv.bitvectorToBoolWasSetByUser) + { + d_options->bv.bitvectorToBool = true; + } } } diff --git a/src/options/smt_options.toml b/src/options/smt_options.toml index 19862cab2..420496190 100644 --- a/src/options/smt_options.toml +++ b/src/options/smt_options.toml @@ -259,6 +259,7 @@ name = "SMT Layer" category = "common" long = "produce-assertions" type = "bool" + alias = ["interactive-mode"] help = "keep an assertions list (enables get-assertions command)" [[option]] diff --git a/src/parser/CMakeLists.txt b/src/parser/CMakeLists.txt index eed5b53c9..eeae442a4 100644 --- a/src/parser/CMakeLists.txt +++ b/src/parser/CMakeLists.txt @@ -100,7 +100,7 @@ endforeach() add_library(cvc5parser-objs OBJECT ${libcvc5parser_src_files}) set_target_properties(cvc5parser-objs PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_compile_definitions(cvc5parser-objs PUBLIC -D__BUILDING_CVC5PARSERLIB) +target_compile_definitions(cvc5parser-objs PUBLIC -D__BUILDING_CVC5PARSERLIB -Dcvc5_obj_EXPORTS) add_dependencies(cvc5parser-objs ANTLR3_SHARED) target_include_directories(cvc5parser-objs PRIVATE ${ANTLR3_INCLUDE_DIR}) @@ -109,7 +109,11 @@ add_library(cvc5parser-shared SHARED $<TARGET_OBJECTS:cvc5parser-objs>) set_target_properties(cvc5parser-shared PROPERTIES SOVERSION ${CVC5_SOVERSION}) set_target_properties(cvc5parser-shared PROPERTIES OUTPUT_NAME cvc5parser) target_link_libraries(cvc5parser-shared PRIVATE cvc5-shared) -target_link_libraries(cvc5parser-shared PRIVATE ANTLR3_SHARED) +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + target_link_libraries(cvc5parser-shared PRIVATE ANTLR3_STATIC) +else() + target_link_libraries(cvc5parser-shared PRIVATE ANTLR3_SHARED) +endif() install(TARGETS cvc5parser-shared EXPORT cvc5-targets @@ -131,7 +135,7 @@ endif() # unresolved symbols when linking against libcvc5parser. # -Wl,--export-all-symbols makes sure that all symbols are exported when # building a DLL. -if(CVC5_WINDOWS_BUILD) - set_target_properties(cvc5parser +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set_target_properties(cvc5parser-objs PROPERTIES LINK_FLAGS "-Wl,--export-all-symbols") endif() diff --git a/src/parser/parser.cpp b/src/parser/parser.cpp index 84c0bd17c..9f34b5647 100644 --- a/src/parser/parser.cpp +++ b/src/parser/parser.cpp @@ -737,6 +737,36 @@ void Parser::pushScope(bool isUserContext) d_symman->pushScope(isUserContext); } +void Parser::pushGetValueScope() +{ + pushScope(); + // we must bind all relevant uninterpreted constants, which coincide with + // the set of uninterpreted constants that are printed in the definition + // of a model. + std::vector<api::Sort> declareSorts = d_symman->getModelDeclareSorts(); + Trace("parser") << "Push get value scope, with " << declareSorts.size() + << " declared sorts" << std::endl; + for (const api::Sort& s : declareSorts) + { + std::vector<api::Term> elements = d_solver->getModelDomainElements(s); + for (const api::Term& e : elements) + { + // Uninterpreted constants are abstract values, which by SMT-LIB are + // required to be annotated with their type, e.g. (as @uc_Foo_0 Foo). + // Thus, the element is not printed simply as its name. + std::string en = e.toString(); + size_t index = en.find("(as "); + if (index == 0) + { + index = en.find(" ", 4); + en = en.substr(4, index - 4); + } + Trace("parser") << "Get value scope : " << en << " -> " << e << std::endl; + defineVar(en, e); + } + } +} + void Parser::popScope() { d_symman->popScope(); diff --git a/src/parser/parser.h b/src/parser/parser.h index 4b04c77b7..19e5b8531 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -716,6 +716,14 @@ public: */ void pushScope(bool isUserContext = false); + /** Push scope for get-value + * + * This pushes a scope by a call to pushScope that binds all relevant bindings + * required for parsing the SMT-LIB command `get-value`. This includes + * all uninterpreted constant values in user-defined uninterpreted sorts. + */ + void pushGetValueScope(); + void popScope(); virtual void reset(); diff --git a/src/parser/smt2/Smt2.g b/src/parser/smt2/Smt2.g index 478edb651..e3aee250e 100644 --- a/src/parser/smt2/Smt2.g +++ b/src/parser/smt2/Smt2.g @@ -343,7 +343,13 @@ command [std::unique_ptr<cvc5::Command>* cmd] | DECLARE_DATATYPE_TOK datatypeDefCommand[false, cmd] | DECLARE_DATATYPES_TOK datatypesDefCommand[false, cmd] | /* value query */ - GET_VALUE_TOK { PARSER_STATE->checkThatLogicIsSet(); } + GET_VALUE_TOK + { + PARSER_STATE->checkThatLogicIsSet(); + // bind all symbols specific to the model, e.g. uninterpreted constant + // values + PARSER_STATE->pushGetValueScope(); + } ( LPAREN_TOK termList[terms,expr] RPAREN_TOK { cmd->reset(new GetValueCommand(terms)); } | ~LPAREN_TOK @@ -352,6 +358,7 @@ command [std::unique_ptr<cvc5::Command>* cmd] "parentheses?"); } ) + { PARSER_STATE->popScope(); } | /* get-assignment */ GET_ASSIGNMENT_TOK { PARSER_STATE->checkThatLogicIsSet(); } { cmd->reset(new GetAssignmentCommand()); } @@ -793,7 +800,7 @@ smt25Command[std::unique_ptr<cvc5::Command>* cmd] /* echo */ | ECHO_TOK - ( simpleSymbolicExpr[s] + ( str[s, true] { cmd->reset(new EchoCommand(s)); } | { cmd->reset(new EchoCommand()); } ) @@ -1648,7 +1655,7 @@ identifier[cvc5::ParseOp& p] if (!f.getSort().isConstructor()) { PARSER_STATE->parseError( - "Bad syntax for test (_ is X), X must be a constructor."); + "Bad syntax for (_ is X), X must be a constructor."); } // get the datatype that f belongs to api::Sort sf = f.getSort().getConstructorCodomainSort(); @@ -1662,7 +1669,7 @@ identifier[cvc5::ParseOp& p] if (!f.getSort().isSelector()) { PARSER_STATE->parseError( - "Bad syntax for test (_ update X), X must be a selector."); + "Bad syntax for (_ update X), X must be a selector."); } std::string sname = f.toString(); // get the datatype that f belongs to @@ -1673,13 +1680,6 @@ identifier[cvc5::ParseOp& p] // get the updater term p.d_expr = ds.getUpdaterTerm(); } - | TUPLE_SEL_TOK m=INTEGER_LITERAL - { - // we adopt a special syntax (_ tupSel n) - p.d_kind = api::APPLY_SELECTOR; - // put m in expr so that the caller can deal with this case - p.d_expr = SOLVER->mkInteger(AntlrInput::tokenToUnsigned($m)); - } | TUPLE_PROJECT_TOK nonemptyNumeralList[numerals] { // we adopt a special syntax (_ tuple_project i_1 ... i_n) where @@ -1697,9 +1697,10 @@ identifier[cvc5::ParseOp& p] { std::string opName = AntlrInput::tokenText($sym); api::Kind k = PARSER_STATE->getIndexedOpKind(opName); - if (k == api::APPLY_UPDATER) + if (k == api::APPLY_SELECTOR || k == api::APPLY_UPDATER) { - // we adopt a special syntax (_ tuple_update n) for tuple updaters + // we adopt a special syntax (_ tuple_select n) and (_ tuple_update n) + // for tuple selectors and updaters if (numerals.size() != 1) { PARSER_STATE->parseError( @@ -2314,7 +2315,6 @@ FORALL_TOK : 'forall'; CHAR_TOK : { PARSER_STATE->isTheoryEnabled(theory::THEORY_STRINGS) }? 'char'; TUPLE_CONST_TOK: { PARSER_STATE->isTheoryEnabled(theory::THEORY_DATATYPES) }? 'tuple'; -TUPLE_SEL_TOK: { PARSER_STATE->isTheoryEnabled(theory::THEORY_DATATYPES) }? 'tuple_select'; TUPLE_PROJECT_TOK: { PARSER_STATE->isTheoryEnabled(theory::THEORY_DATATYPES) }? 'tuple_project'; HO_ARROW_TOK : { PARSER_STATE->isHoEnabled() }? '->'; diff --git a/src/parser/smt2/smt2.cpp b/src/parser/smt2/smt2.cpp index b186c2b2a..19c3527f7 100644 --- a/src/parser/smt2/smt2.cpp +++ b/src/parser/smt2/smt2.cpp @@ -135,6 +135,11 @@ void Smt2::addDatatypesOperators() { Parser::addOperator(api::APPLY_UPDATER); addOperator(api::DT_SIZE, "dt.size"); + // Notice that tuple operators, we use the generic APPLY_SELECTOR and + // APPLY_UPDATER kinds. These are processed based on the context + // in which they are parsed, e.g. when parsing identifiers. + addIndexedOperator( + api::APPLY_SELECTOR, api::APPLY_SELECTOR, "tuple_select"); addIndexedOperator(api::APPLY_UPDATER, api::APPLY_UPDATER, "tuple_update"); } } @@ -258,14 +263,16 @@ void Smt2::addFloatingPointOperators() { } void Smt2::addSepOperators() { + defineVar("sep.emp", d_solver->mkSepEmp()); + // the Boolean sort is a placeholder here since we don't have type info + // without type annotation + defineVar("sep.nil", d_solver->mkSepNil(d_solver->getBooleanSort())); addOperator(api::SEP_STAR, "sep"); addOperator(api::SEP_PTO, "pto"); addOperator(api::SEP_WAND, "wand"); - addOperator(api::SEP_EMP, "emp"); Parser::addOperator(api::SEP_STAR); Parser::addOperator(api::SEP_PTO); Parser::addOperator(api::SEP_WAND); - Parser::addOperator(api::SEP_EMP); } void Smt2::addCoreSymbols() @@ -288,7 +295,7 @@ void Smt2::addOperator(api::Kind kind, const std::string& name) Debug("parser") << "Smt2::addOperator( " << kind << ", " << name << " )" << std::endl; Parser::addOperator(kind); - operatorKindMap[name] = kind; + d_operatorKindMap[name] = kind; } void Smt2::addIndexedOperator(api::Kind tKind, @@ -302,11 +309,11 @@ void Smt2::addIndexedOperator(api::Kind tKind, api::Kind Smt2::getOperatorKind(const std::string& name) const { // precondition: isOperatorEnabled(name) - return operatorKindMap.find(name)->second; + return d_operatorKindMap.find(name)->second; } bool Smt2::isOperatorEnabled(const std::string& name) const { - return operatorKindMap.find(name) != operatorKindMap.end(); + return d_operatorKindMap.find(name) != d_operatorKindMap.end(); } bool Smt2::isTheoryEnabled(theory::TheoryId theory) const @@ -437,7 +444,7 @@ void Smt2::reset() { d_logicSet = false; d_seenSetLogic = false; d_logic = LogicInfo(); - operatorKindMap.clear(); + d_operatorKindMap.clear(); d_lastNamedTerm = std::pair<api::Term, std::string>(); } @@ -509,7 +516,6 @@ Command* Smt2::setLogic(std::string name, bool fromCommand) if (!strictModeEnabled() && d_logic.hasCardinalityConstraints()) { addOperator(api::CARDINALITY_CONSTRAINT, "fmf.card"); - addOperator(api::CARDINALITY_VALUE, "fmf.card.val"); } } @@ -546,7 +552,7 @@ Command* Smt2::setLogic(std::string name, bool fromCommand) if (d_logic.areTranscendentalsUsed()) { - defineVar("real.pi", d_solver->mkTerm(api::PI)); + defineVar("real.pi", d_solver->mkPi()); addTranscendentalOperators(); } if (!strictModeEnabled()) @@ -672,12 +678,8 @@ Command* Smt2::setLogic(std::string name, bool fromCommand) addFloatingPointOperators(); } - if (d_logic.isTheoryEnabled(theory::THEORY_SEP)) { - // the Boolean sort is a placeholder here since we don't have type info - // without type annotation - defineVar("sep.nil", d_solver->mkSepNil(d_solver->getBooleanSort())); - defineVar("sep.emp", d_solver->mkTerm(api::SEP_EMP)); - + if (d_logic.isTheoryEnabled(theory::THEORY_SEP)) + { addSepOperators(); } @@ -953,6 +955,8 @@ api::Term Smt2::applyParseOp(ParseOp& p, std::vector<api::Term>& args) { // a builtin operator, convert to kind kind = getOperatorKind(p.d_name); + Debug("parser") << "Got builtin kind " << kind << " for name" + << std::endl; } else { @@ -1119,17 +1123,27 @@ api::Term Smt2::applyParseOp(ParseOp& p, std::vector<api::Term>& args) Debug("parser") << "applyParseOp: return uminus " << ret << std::endl; return ret; } - if (kind == api::EQ_RANGE && d_solver->getOption("arrays-exp") != "true") - { - parseError( - "eqrange predicate requires option --arrays-exp to be enabled."); - } if (kind == api::SINGLETON && args.size() == 1) { api::Term ret = d_solver->mkTerm(api::SINGLETON, args[0]); Debug("parser") << "applyParseOp: return singleton " << ret << std::endl; return ret; } + else if (kind == api::CARDINALITY_CONSTRAINT) + { + if (args.size() != 2) + { + parseError("Incorrect arguments for cardinality constraint"); + } + api::Sort sort = args[0].getSort(); + if (!sort.isUninterpretedSort()) + { + parseError("Expected uninterpreted sort for cardinality constraint"); + } + uint64_t ubound = args[1].getUInt32Value(); + api::Term ret = d_solver->mkCardinalityConstraint(sort, ubound); + return ret; + } api::Term ret = d_solver->mkTerm(kind, args); Debug("parser") << "applyParseOp: return default builtin " << ret << std::endl; diff --git a/src/parser/smt2/smt2.h b/src/parser/smt2/smt2.h index fd68732fe..8d8f8febe 100644 --- a/src/parser/smt2/smt2.h +++ b/src/parser/smt2/smt2.h @@ -50,7 +50,7 @@ class Smt2 : public Parser bool d_seenSetLogic; LogicInfo d_logic; - std::unordered_map<std::string, api::Kind> operatorKindMap; + std::unordered_map<std::string, api::Kind> d_operatorKindMap; /** * Maps indexed symbols to the kind of the operator (e.g. "extract" to * BITVECTOR_EXTRACT). diff --git a/src/printer/smt2/smt2_printer.cpp b/src/printer/smt2/smt2_printer.cpp index 32b195be2..ccb2ed2c6 100644 --- a/src/printer/smt2/smt2_printer.cpp +++ b/src/printer/smt2/smt2_printer.cpp @@ -24,6 +24,7 @@ #include "api/cpp/cvc5.h" #include "expr/array_store_all.h" #include "expr/ascription_type.h" +#include "expr/cardinality_constraint.h" #include "expr/datatype_index.h" #include "expr/dtype.h" #include "expr/dtype_cons.h" @@ -331,7 +332,13 @@ void Smt2Printer::toStream(std::ostream& out, out << ss.str(); break; } - + case kind::CARDINALITY_CONSTRAINT: + out << "(_ fmf.card "; + out << n.getConst<CardinalityConstraint>().getType(); + out << " "; + out << n.getConst<CardinalityConstraint>().getUpperBound(); + out << ")"; + break; case kind::EMPTYSET: out << "(as emptyset "; toStreamType(out, n.getConst<EmptySet>().getType()); @@ -659,9 +666,6 @@ void Smt2Printer::toStream(std::ostream& out, break; } - case kind::CARDINALITY_CONSTRAINT: out << "fmf.card "; break; - case kind::CARDINALITY_VALUE: out << "fmf.card.val "; break; - // bv theory case kind::BITVECTOR_CONCAT: case kind::BITVECTOR_AND: @@ -1723,15 +1727,7 @@ void Smt2Printer::toStreamCmdEmpty(std::ostream& out, void Smt2Printer::toStreamCmdEcho(std::ostream& out, const std::string& output) const { - std::string s = output; - // escape all double-quotes - size_t pos = 0; - while ((pos = s.find('"', pos)) != string::npos) - { - s.replace(pos, 1, "\"\""); - pos += 2; - } - out << "(echo \"" << s << "\")" << std::endl; + out << "(echo " << cvc5::quoteString(output) << ')' << std::endl; } /* @@ -1930,14 +1926,9 @@ static void toStream(std::ostream& out, const CommandUnsupported* s, Variant v) #endif /* CVC5_COMPETITION_MODE */ } -static void errorToStream(std::ostream& out, std::string message, Variant v) { - // escape all double-quotes - size_t pos = 0; - while((pos = message.find('"', pos)) != string::npos) { - message.replace(pos, 1, "\"\""); - pos += 2; - } - out << "(error \"" << message << "\")" << endl; +static void errorToStream(std::ostream& out, std::string message, Variant v) +{ + out << "(error " << cvc5::quoteString(message) << ')' << endl; } static void toStream(std::ostream& out, const CommandFailure* s, Variant v) { diff --git a/src/proof/alethe/alethe_post_processor.cpp b/src/proof/alethe/alethe_post_processor.cpp index 6312f3140..ef7735b44 100644 --- a/src/proof/alethe/alethe_post_processor.cpp +++ b/src/proof/alethe/alethe_post_processor.cpp @@ -55,6 +55,79 @@ bool AletheProofPostprocessCallback::update(Node res, switch (id) { + // To keep the original shape of the proof node it is necessary to rederive + // the original conclusion. However, the term that should be printed might + // be different from that conclusion. Thus, it is stored as an additional + // argument in the proof node. Usually, the only difference is an additional + // cl operator or the outmost or operator being replaced by a cl operator. + // + // When steps are added to the proof that have not been there previously, + // it is unwise to add them in the same manner. To illustrate this the + // following counterexample shows the pitfalls of this approach: + // + // (or a (or b c)) (not a) + // --------------------------- RESOLUTION + // (or b c) + // + // is converted into an Alethe proof that should be printed as + // + // (cl a (or b c)) (cl (not a)) + // -------------------------------- RESOLUTION + // (cl (or b c)) + // --------------- OR + // (cl b c) + // + // Here, (cl (or b c)) and (cl b c) cannot correspond to the same proof node + // (or b c). Thus, we build a new proof node using the kind SEXPR + // that is then printed as (cl (or b c)). We denote this wrapping of a proof + // node by using an extra pair of parenthesis, i.e. ((or b c)) is the proof + // node printed as (cl (or b c)). + // + // + // Some proof rules have a close correspondence in Alethe. There are two + // very frequent patterns that, to avoid repetition, are described here and + // referred to in the comments on the specific proof rules below. + // + // The first pattern, which will be called singleton pattern in the + // following, adds the original proof node F with the corresponding rule R' + // of the Alethe calculus and uses the same premises as the original proof + // node (P1:F1) ... (Pn:Fn). However, the conclusion is printed as (cl F). + // + // This means a cvc5 rule R that looks as follows: + // + // (P1:F1) ... (Pn:Fn) + // --------------------- R + // F + // + // is transformed into: + // + // (P1:F1) ... (Pn:Fn) + // --------------------- R' + // (cl F)* + // + // * the corresponding proof node is F + // + // The second pattern, which will be called clause pattern in the following, + // has a disjunction (or G1 ... Gn) as conclusion. It also adds the orignal + // proof node (or G1 ... Gn) with the corresponding rule R' of the Alethe + // calculus and uses the same premises as the original proof node (P1:F1) + // ... (Pn:Fn). However, the conclusion is printed as (cl G1 ... Gn), i.e. + // the or is replaced by the cl operator. + // + // This means a cvc5 rule R that looks as follows: + // + // (P1:F1) ... (Pn:Fn) + // --------------------- R + // (or G1 ... Gn) + // + // Is transformed into: + // + // (P1:F1) ... (Pn:Fn) + // --------------------- R' + // (cl G1 ... Gn)* + // + // * the corresponding proof node is (or G1 ... Gn) + // //================================================= Core rules //======================== Assume and Scope case PfRule::ASSUME: diff --git a/src/proof/proof_node_manager.cpp b/src/proof/proof_node_manager.cpp index a3ef944e0..8b4b332a1 100644 --- a/src/proof/proof_node_manager.cpp +++ b/src/proof/proof_node_manager.cpp @@ -28,7 +28,8 @@ using namespace cvc5::kind; namespace cvc5 { -ProofNodeManager::ProofNodeManager(ProofChecker* pc) : d_checker(pc) +ProofNodeManager::ProofNodeManager(theory::Rewriter* rr, ProofChecker* pc) + : d_rewriter(rr), d_checker(pc) { d_true = NodeManager::currentNM()->mkConst(true); // we always allocate a proof checker, regardless of the proof checking mode @@ -160,14 +161,14 @@ std::shared_ptr<ProofNode> ProofNodeManager::mkScope( computedAcr = true; for (const Node& acc : ac) { - Node accr = theory::Rewriter::rewrite(acc); + Node accr = d_rewriter->rewrite(acc); if (accr != acc) { acr[accr] = acc; } } } - Node ar = theory::Rewriter::rewrite(a); + Node ar = d_rewriter->rewrite(a); std::unordered_map<Node, Node>::iterator itr = acr.find(ar); if (itr != acr.end()) { diff --git a/src/proof/proof_node_manager.h b/src/proof/proof_node_manager.h index 928aabb76..533f6d173 100644 --- a/src/proof/proof_node_manager.h +++ b/src/proof/proof_node_manager.h @@ -28,6 +28,10 @@ namespace cvc5 { class ProofChecker; class ProofNode; +namespace theory { +class Rewriter; +} + /** * A manager for proof node objects. This is a trusted interface for creating * and updating ProofNode objects. @@ -54,7 +58,7 @@ class ProofNode; class ProofNodeManager { public: - ProofNodeManager(ProofChecker* pc = nullptr); + ProofNodeManager(theory::Rewriter* rr, ProofChecker* pc = nullptr); ~ProofNodeManager() {} /** * This constructs a ProofNode with the given arguments. The expected @@ -184,6 +188,8 @@ class ProofNodeManager static ProofNode* cancelDoubleSymm(ProofNode* pn); private: + /** The rewriter */ + theory::Rewriter* d_rewriter; /** The (optional) proof checker */ ProofChecker* d_checker; /** the true node */ diff --git a/src/prop/sat_proof_manager.cpp b/src/prop/sat_proof_manager.cpp index da49a5990..e650473b3 100644 --- a/src/prop/sat_proof_manager.cpp +++ b/src/prop/sat_proof_manager.cpp @@ -348,6 +348,19 @@ void SatProofManager::explainLit(SatLiteral lit, Trace("sat-proof") << push << "SatProofManager::explainLit: Lit: " << lit; Node litNode = getClauseNode(lit); Trace("sat-proof") << " [" << litNode << "]\n"; + // We don't need to explain nodes who are inputs. Note that it's *necessary* + // to avoid attempting such explanations because they can introduce cycles at + // the node level. For example, if a literal l depends on an input clause C + // but a literal l', node-equivalent to C, depends on l, we may have a cycle + // when building the overall SAT proof. + if (d_assumptions.contains(litNode)) + { + Trace("sat-proof") + << "SatProofManager::explainLit: input assumption, ABORT\n"; + return; + } + // We don't need to explain nodes who already have proofs. + // // Note that if we had two literals for (= a b) and (= b a) and we had already // a proof for (= a b) this test would return true for (= b a), which could // lead to open proof. However we should never have two literals like this in @@ -626,7 +639,7 @@ void SatProofManager::finalizeProof(Node inConflictNode, if (it != d_cnfStream->getTranslationCache().end()) { Trace("sat-proof") << it->second << "\n"; - Trace("sat-proof") << "- " << fa << "\n"; + Trace("sat-proof") << " - " << fa << "\n"; continue; } // then it's a clause @@ -638,7 +651,7 @@ void SatProofManager::finalizeProof(Node inConflictNode, Trace("sat-proof") << it->second << " "; } Trace("sat-proof") << "\n"; - Trace("sat-proof") << "- " << fa << "\n"; + Trace("sat-proof") << " - " << fa << "\n"; } } @@ -653,7 +666,7 @@ void SatProofManager::finalizeProof(Node inConflictNode, } // ignore input assumptions. This is necessary to avoid rare collisions // between input clauses and literals that are equivalent at the node - // level. In trying to justify the literal below if, it was previously + // level. In trying to justify the literal below, if it was previously // propagated (say, in a previous check-sat call that survived the // user-context changes) but no longer holds, then we may introduce a // bogus proof for it, rather than keeping it as an input. diff --git a/src/smt/check_models.cpp b/src/smt/check_models.cpp index f148d1018..5d16c12ce 100644 --- a/src/smt/check_models.cpp +++ b/src/smt/check_models.cpp @@ -31,8 +31,7 @@ using namespace cvc5::theory; namespace cvc5 { namespace smt { -CheckModels::CheckModels(Env& e) : d_env(e) {} -CheckModels::~CheckModels() {} +CheckModels::CheckModels(Env& e) : EnvObj(e) {} void CheckModels::checkModel(TheoryModel* m, const context::CDList<Node>& al, @@ -50,6 +49,12 @@ void CheckModels::checkModel(TheoryModel* m, throw RecoverableModalException( "Cannot run check-model on a model with approximate values."); } + Node sepHeap, sepNeq; + if (m->getHeapModel(sepHeap, sepNeq)) + { + throw RecoverableModalException( + "Cannot run check-model on a model with a separation logic heap."); + } theory::SubstitutionMap& sm = d_env.getTopLevelSubstitutions().get(); Trace("check-model") << "checkModel: Check assertions..." << std::endl; @@ -71,7 +76,7 @@ void CheckModels::checkModel(TheoryModel* m, Notice() << "SolverEngine::checkModel(): -- substitutes to " << n << std::endl; - n = Rewriter::rewrite(n); + n = rewrite(n); Notice() << "SolverEngine::checkModel(): -- rewrites to " << n << std::endl; // We look up the value before simplifying. If n contains quantifiers, diff --git a/src/smt/check_models.h b/src/smt/check_models.h index d785b53d5..2b3447010 100644 --- a/src/smt/check_models.h +++ b/src/smt/check_models.h @@ -20,11 +20,10 @@ #include "context/cdlist.h" #include "expr/node.h" +#include "smt/env_obj.h" namespace cvc5 { -class Env; - namespace theory { class TheoryModel; } @@ -34,11 +33,10 @@ namespace smt { /** * This utility is responsible for checking the current model. */ -class CheckModels +class CheckModels : protected EnvObj { public: CheckModels(Env& e); - ~CheckModels(); /** * Check model m against the current set of input assertions al. * @@ -48,10 +46,6 @@ class CheckModels void checkModel(theory::TheoryModel* m, const context::CDList<Node>& al, bool hardFailure); - - private: - /** Reference to the environment */ - Env& d_env; }; } // namespace smt diff --git a/src/smt/command.cpp b/src/smt/command.cpp index 311d6713a..e8ee6d59c 100644 --- a/src/smt/command.cpp +++ b/src/smt/command.cpp @@ -38,6 +38,7 @@ #include "proof/unsat_core.h" #include "smt/dump.h" #include "smt/model.h" +#include "util/smt2_quote_string.h" #include "util/unsafe_interrupt_exception.h" #include "util/utility.h" @@ -302,19 +303,10 @@ void EmptyCommand::toStream(std::ostream& out, /* class EchoCommand */ /* -------------------------------------------------------------------------- */ -EchoCommand::EchoCommand(std::string output) -{ - // escape all double-quotes - size_t pos = 0; - while ((pos = output.find('"', pos)) != string::npos) - { - output.replace(pos, 1, "\"\""); - pos += 2; - } - d_output = '"' + output + '"'; -} +EchoCommand::EchoCommand(std::string output) : d_output(output) {} std::string EchoCommand::getOutput() const { return d_output; } + void EchoCommand::invoke(api::Solver* solver, SymbolManager* sm) { /* we don't have an output stream here, nothing to do */ @@ -325,7 +317,7 @@ void EchoCommand::invoke(api::Solver* solver, SymbolManager* sm, std::ostream& out) { - out << d_output << std::endl; + out << cvc5::quoteString(d_output) << std::endl; Trace("dtview::command") << "* ~COMMAND: echo |" << d_output << "|~" << std::endl; d_commandStatus = CommandSuccess::instance(); @@ -335,6 +327,7 @@ void EchoCommand::invoke(api::Solver* solver, } Command* EchoCommand::clone() const { return new EchoCommand(d_output); } + std::string EchoCommand::getCommandName() const { return "echo"; } void EchoCommand::toStream(std::ostream& out, diff --git a/src/smt/model_blocker.cpp b/src/smt/model_blocker.cpp index cbc388331..e8c1ff07f 100644 --- a/src/smt/model_blocker.cpp +++ b/src/smt/model_blocker.cpp @@ -25,6 +25,8 @@ using namespace cvc5::kind; namespace cvc5 { +ModelBlocker::ModelBlocker(Env& e) : EnvObj(e) {} + Node ModelBlocker::getModelBlocker(const std::vector<Node>& assertions, theory::TheoryModel* m, options::BlockModelsMode mode, diff --git a/src/smt/model_blocker.h b/src/smt/model_blocker.h index 42219e220..5e41de6a3 100644 --- a/src/smt/model_blocker.h +++ b/src/smt/model_blocker.h @@ -22,6 +22,7 @@ #include "expr/node.h" #include "options/smt_options.h" +#include "smt/env_obj.h" namespace cvc5 { @@ -32,9 +33,10 @@ class TheoryModel; /** * A utility for blocking the current model. */ -class ModelBlocker +class ModelBlocker : protected EnvObj { public: + ModelBlocker(Env& e); /** get model blocker * * This returns a disjunction of literals ~L1 V ... V ~Ln with the following @@ -63,7 +65,7 @@ class ModelBlocker * our input. In other words, we do not return ~(x < 0) V ~(w < 0) since the * left disjunct is always false. */ - static Node getModelBlocker( + Node getModelBlocker( const std::vector<Node>& assertions, theory::TheoryModel* m, options::BlockModelsMode mode, diff --git a/src/smt/preprocessor.cpp b/src/smt/preprocessor.cpp index 4b16b9391..3aed58b30 100644 --- a/src/smt/preprocessor.cpp +++ b/src/smt/preprocessor.cpp @@ -150,7 +150,7 @@ Node Preprocessor::simplify(const Node& node) d_env.getPrinter().toStreamCmdSimplify(d_env.getDumpOut(), node); } Node ret = expandDefinitions(node); - ret = theory::Rewriter::rewrite(ret); + ret = rewrite(ret); return ret; } diff --git a/src/smt/proof_manager.cpp b/src/smt/proof_manager.cpp index 2e08ab2df..4b4291075 100644 --- a/src/smt/proof_manager.cpp +++ b/src/smt/proof_manager.cpp @@ -41,7 +41,7 @@ PfManager::PfManager(Env& env) d_pchecker(new ProofChecker( options().proof.proofCheck == options::ProofCheckMode::EAGER, options().proof.proofPedantic)), - d_pnm(new ProofNodeManager(d_pchecker.get())), + d_pnm(new ProofNodeManager(env.getRewriter(), d_pchecker.get())), d_pppg(new PreprocessProofGenerator( d_pnm.get(), env.getUserContext(), "smt::PreprocessProofGenerator")), d_pfpp(nullptr), diff --git a/src/smt/proof_post_processor.cpp b/src/smt/proof_post_processor.cpp index adc15ca2c..04a36c1c0 100644 --- a/src/smt/proof_post_processor.cpp +++ b/src/smt/proof_post_processor.cpp @@ -42,7 +42,7 @@ ProofPostprocessCallback::ProofPostprocessCallback(Env& env, : d_env(env), d_pnm(env.getProofNodeManager()), d_pppg(pppg), - d_wfpm(env.getProofNodeManager()), + d_wfpm(env), d_updateScopedAssumptions(updateScopedAssumptions) { d_true = NodeManager::currentNM()->mkConst(true); @@ -654,7 +654,7 @@ Node ProofPostprocessCallback::expandMacros(PfRule id, // apply transitivity if necessary Node eq = addProofForTrans(tchildren, cdp); - cdp->addStep(args[0], PfRule::EQ_RESOLVE, {children[0], eq}, {}); + cdp->addStep(eq[1], PfRule::EQ_RESOLVE, {children[0], eq}, {}); return args[0]; } else if (id == PfRule::MACRO_RESOLUTION diff --git a/src/smt/solver_engine.cpp b/src/smt/solver_engine.cpp index 911e0a960..fc6ec3915 100644 --- a/src/smt/solver_engine.cpp +++ b/src/smt/solver_engine.cpp @@ -417,10 +417,10 @@ void SolverEngine::setInfo(const std::string& key, const std::string& value) bool SolverEngine::isValidGetInfoFlag(const std::string& key) const { - if (key == "all-statistics" || key == "error-behavior" || key == "name" - || key == "version" || key == "authors" || key == "status" - || key == "reason-unknown" || key == "assertion-stack-levels" - || key == "all-options" || key == "time") + if (key == "all-statistics" || key == "error-behavior" || key == "filename" + || key == "name" || key == "version" || key == "authors" + || key == "status" || key == "time" || key == "reason-unknown" + || key == "assertion-stack-levels" || key == "all-options") { return true; } @@ -455,7 +455,7 @@ std::string SolverEngine::getInfo(const std::string& key) const } if (key == "authors") { - return toSExpr(Configuration::about()); + return toSExpr("the " + Configuration::getName() + " authors"); } if (key == "status") { @@ -1081,7 +1081,7 @@ Node SolverEngine::getValue(const Node& ex) const // AJR : necessary? if (!n.getType().isFunction()) { - n = Rewriter::rewrite(n); + n = d_env->getRewriter()->rewrite(n); } Trace("smt") << "--- getting value of " << n << endl; @@ -1224,7 +1224,8 @@ Result SolverEngine::blockModel() // get expanded assertions std::vector<Node> eassertsProc = getExpandedAssertions(); - Node eblocker = ModelBlocker::getModelBlocker( + ModelBlocker mb(*d_env.get()); + Node eblocker = mb.getModelBlocker( eassertsProc, m, d_env->getOptions().smt.blockModelsMode); Trace("smt") << "Block formula: " << eblocker << std::endl; return assertFormula(eblocker); @@ -1247,7 +1248,8 @@ Result SolverEngine::blockModelValues(const std::vector<Node>& exprs) // get expanded assertions std::vector<Node> eassertsProc = getExpandedAssertions(); // we always do block model values mode here - Node eblocker = ModelBlocker::getModelBlocker( + ModelBlocker mb(*d_env.get()); + Node eblocker = mb.getModelBlocker( eassertsProc, m, options::BlockModelsMode::VALUES, exprs); return assertFormula(eblocker); } diff --git a/src/smt/sygus_solver.cpp b/src/smt/sygus_solver.cpp index 2a1d4e6c6..5db9804c6 100644 --- a/src/smt/sygus_solver.cpp +++ b/src/smt/sygus_solver.cpp @@ -377,7 +377,7 @@ void SygusSolver::checkSynthSolution(Assertions& as) // problem are rewritten to true. If this is not the case, then the // assertions module of the subsolver will complain about assertions // with free variables. - Node ar = theory::Rewriter::rewrite(a); + Node ar = rewrite(a); solChecker->assertFormula(ar); } Result r = solChecker->checkSat(); diff --git a/src/smt/witness_form.cpp b/src/smt/witness_form.cpp index 8e998b9cf..16d297495 100644 --- a/src/smt/witness_form.cpp +++ b/src/smt/witness_form.cpp @@ -16,21 +16,27 @@ #include "smt/witness_form.h" #include "expr/skolem_manager.h" +#include "smt/env.h" #include "theory/rewriter.h" namespace cvc5 { namespace smt { -WitnessFormGenerator::WitnessFormGenerator(ProofNodeManager* pnm) - : d_tcpg(pnm, +WitnessFormGenerator::WitnessFormGenerator(Env& env) + : d_rewriter(env.getRewriter()), + d_tcpg(env.getProofNodeManager(), nullptr, TConvPolicy::FIXPOINT, TConvCachePolicy::NEVER, "WfGenerator::TConvProofGenerator", nullptr, true), - d_wintroPf(pnm, nullptr, nullptr, "WfGenerator::LazyCDProof"), - d_pskPf(pnm, nullptr, "WfGenerator::PurifySkolemProof") + d_wintroPf(env.getProofNodeManager(), + nullptr, + nullptr, + "WfGenerator::LazyCDProof"), + d_pskPf( + env.getProofNodeManager(), nullptr, "WfGenerator::PurifySkolemProof") { } @@ -114,12 +120,12 @@ Node WitnessFormGenerator::convertToWitnessForm(Node t) bool WitnessFormGenerator::requiresWitnessFormTransform(Node t, Node s) const { - return theory::Rewriter::rewrite(t) != theory::Rewriter::rewrite(s); + return d_rewriter->rewrite(t) != d_rewriter->rewrite(s); } bool WitnessFormGenerator::requiresWitnessFormIntro(Node t) const { - Node tr = theory::Rewriter::rewrite(t); + Node tr = d_rewriter->rewrite(t); return !tr.isConst() || !tr.getConst<bool>(); } diff --git a/src/smt/witness_form.h b/src/smt/witness_form.h index 8522d41f0..06d60c1ed 100644 --- a/src/smt/witness_form.h +++ b/src/smt/witness_form.h @@ -25,6 +25,13 @@ #include "proof/proof_generator.h" namespace cvc5 { + +class Env; + +namespace theory { +class Rewriter; +} + namespace smt { /** @@ -37,7 +44,7 @@ namespace smt { class WitnessFormGenerator : public ProofGenerator { public: - WitnessFormGenerator(ProofNodeManager* pnm); + WitnessFormGenerator(Env& env); ~WitnessFormGenerator() {} /** * Get proof for, which expects an equality of the form t = toWitness(t). @@ -85,6 +92,8 @@ class WitnessFormGenerator : public ProofGenerator * Return a proof generator that can prove the given axiom exists. */ ProofGenerator* convertExistsInternal(Node exists); + /** The rewriter we are using */ + theory::Rewriter* d_rewriter; /** The term conversion proof generator */ TConvProofGenerator d_tcpg; /** The nodes we have already added rewrite steps for in d_tcpg */ diff --git a/src/theory/arith/theory_arith.cpp b/src/theory/arith/theory_arith.cpp index 76a0c363d..cf2373b9f 100644 --- a/src/theory/arith/theory_arith.cpp +++ b/src/theory/arith/theory_arith.cpp @@ -193,9 +193,9 @@ void TheoryArith::postCheck(Effort level) if (Theory::fullEffort(level)) { d_arithModelCache.clear(); + std::set<Node> termSet; if (d_nonlinearExtension != nullptr) { - std::set<Node> termSet; updateModelCache(termSet); d_nonlinearExtension->checkFullEffort(d_arithModelCache, termSet); } @@ -204,6 +204,15 @@ void TheoryArith::postCheck(Effort level) // set incomplete d_im.setIncomplete(IncompleteId::ARITH_NL_DISABLED); } + // If we won't be doing a last call effort check (which implies that + // models will be computed), we must sanity check the integer model + // from the linear solver now. We also must update the model cache + // if we did not do so above. + if (d_nonlinearExtension == nullptr) + { + updateModelCache(termSet); + } + sanityCheckIntegerModel(); } } @@ -274,12 +283,6 @@ bool TheoryArith::collectModelValues(TheoryModel* m, updateModelCache(termSet); - if (sanityCheckIntegerModel()) - { - // We added a lemma - return false; - } - // We are now ready to assert the model. for (const std::pair<const Node, Node>& p : d_arithModelCache) { @@ -383,9 +386,9 @@ bool TheoryArith::sanityCheckIntegerModel() Trace("arith-check") << p.first << " -> " << p.second << std::endl; if (p.first.getType().isInteger() && !p.second.getType().isInteger()) { - Assert(false) << "TheoryArithPrivate generated a bad model value for " - "integer variable " - << p.first << " : " << p.second; + Warning() << "TheoryArithPrivate generated a bad model value for " + "integer variable " + << p.first << " : " << p.second; // must branch and bound TrustNode lem = d_bab.branchIntegerVariable(p.first, p.second.getConst<Rational>()); diff --git a/src/theory/arrays/theory_arrays.cpp b/src/theory/arrays/theory_arrays.cpp index 9e69c9f71..93eadde43 100644 --- a/src/theory/arrays/theory_arrays.cpp +++ b/src/theory/arrays/theory_arrays.cpp @@ -296,7 +296,19 @@ Node TheoryArrays::solveWrite(TNode term, bool solve1, bool solve2, bool ppCheck TrustNode TheoryArrays::ppRewrite(TNode term, std::vector<SkolemLemma>& lems) { - // first, see if we need to expand definitions + // first, check for logic exceptions + Kind k = term.getKind(); + if (!options().arrays.arraysExp) + { + if (k == kind::EQ_RANGE) + { + std::stringstream ss; + ss << "Term of kind " << k + << " not supported in default mode, try --arrays-exp"; + throw LogicException(ss.str()); + } + } + // see if we need to expand definitions TrustNode texp = d_rewriter.expandDefinition(term); if (!texp.isNull()) { @@ -309,7 +321,8 @@ TrustNode TheoryArrays::ppRewrite(TNode term, std::vector<SkolemLemma>& lems) d_ppEqualityEngine.addTerm(term); NodeManager* nm = NodeManager::currentNM(); Node ret; - switch (term.getKind()) { + switch (k) + { case kind::SELECT: { // select(store(a,i,v),j) = select(a,j) // IF i != j diff --git a/src/theory/arrays/theory_arrays_type_rules.cpp b/src/theory/arrays/theory_arrays_type_rules.cpp index e1b4813ec..433768e5d 100644 --- a/src/theory/arrays/theory_arrays_type_rules.cpp +++ b/src/theory/arrays/theory_arrays_type_rules.cpp @@ -18,6 +18,7 @@ // for array-constant attributes #include "expr/array_store_all.h" #include "theory/arrays/theory_arrays_rewriter.h" +#include "theory/builtin/theory_builtin_type_rules.h" #include "theory/type_enumerator.h" #include "util/cardinality.h" @@ -249,7 +250,24 @@ bool ArraysProperties::isWellFounded(TypeNode type) Node ArraysProperties::mkGroundTerm(TypeNode type) { - return *TypeEnumerator(type); + Assert(type.getKind() == kind::ARRAY_TYPE); + TypeNode elemType = type.getArrayConstituentType(); + Node elem = elemType.mkGroundTerm(); + if (elem.isConst()) + { + return NodeManager::currentNM()->mkConst(ArrayStoreAll(type, elem)); + } + // Note the distinction between mkGroundTerm and mkGroundValue. While + // an arbitrary value can be obtained by calling the type enumerator here, + // that is wrong for types that are not closed enumerable since it may + // return a term containing values that should not appear in e.g. assertions. + // For example, arrays whose element type is an uninterpreted sort will + // incorrectly introduce uninterpreted sort values if this is done. + // It is currently infeasible to construct an ArrayStoreAll with the element + // type's mkGroundTerm as an argument when that term is not constant. + // Thus, we must simply return a fresh Skolem here, using the same utility + // as that of uninterpreted sorts. + return builtin::SortProperties::mkGroundTerm(type); } TypeNode ArrayPartialSelectTypeRule::computeType(NodeManager* nodeManager, diff --git a/src/theory/booleans/proof_circuit_propagator.cpp b/src/theory/booleans/proof_circuit_propagator.cpp index ed4f81cb3..71fb2a56f 100644 --- a/src/theory/booleans/proof_circuit_propagator.cpp +++ b/src/theory/booleans/proof_circuit_propagator.cpp @@ -223,11 +223,11 @@ std::shared_ptr<ProofNode> ProofCircuitPropagator::xorYFromX(bool negated, } if (x) { - return mkResolution( + return mkNot(mkResolution( mkProof(negated ? PfRule::NOT_XOR_ELIM2 : PfRule::XOR_ELIM2, {assume(negated ? parent.notNode() : Node(parent))}), parent[0], - false); + false)); } return mkNot( mkResolution(mkProof(negated ? PfRule::NOT_XOR_ELIM1 : PfRule::XOR_ELIM1, @@ -396,11 +396,14 @@ std::shared_ptr<ProofNode> ProofCircuitPropagatorBackward::iteIsCase(unsigned c) } if (d_parentAssignment) { - return mkResolution( - mkProof(PfRule::ITE_ELIM2, {assume(d_parent)}), d_parent[c + 1], false); + return mkResolution(mkProof(c == 0 ? PfRule::ITE_ELIM1 : PfRule::ITE_ELIM2, + {assume(d_parent)}), + d_parent[c + 1], + true); } return mkResolution( - mkProof(PfRule::NOT_ITE_ELIM2, {assume(d_parent.notNode())}), + mkProof(c == 0 ? PfRule::NOT_ITE_ELIM1 : PfRule::NOT_ITE_ELIM2, + {assume(d_parent.notNode())}), d_parent[c + 1], false); } diff --git a/src/theory/datatypes/datatypes_rewriter.cpp b/src/theory/datatypes/datatypes_rewriter.cpp index c446504fd..fd957baaa 100644 --- a/src/theory/datatypes/datatypes_rewriter.cpp +++ b/src/theory/datatypes/datatypes_rewriter.cpp @@ -16,12 +16,12 @@ #include "theory/datatypes/datatypes_rewriter.h" #include "expr/ascription_type.h" +#include "expr/codatatype_bound_variable.h" #include "expr/dtype.h" #include "expr/dtype_cons.h" #include "expr/node_algorithm.h" #include "expr/skolem_manager.h" #include "expr/sygus_datatype.h" -#include "expr/uninterpreted_constant.h" #include "options/datatypes_options.h" #include "theory/datatypes/sygus_datatype_utils.h" #include "theory/datatypes/theory_datatypes_utils.h" @@ -729,7 +729,7 @@ Node DatatypesRewriter::collectRef(Node n, else { // a loop - const Integer& i = n.getConst<UninterpretedConstant>().getIndex(); + const Integer& i = n.getConst<CodatatypeBoundVariable>().getIndex(); uint32_t index = i.toUnsignedInt(); if (index >= sk.size()) { @@ -771,7 +771,7 @@ Node DatatypesRewriter::normalizeCodatatypeConstantEqc( { int debruijn = depth - it->second - 1; return NodeManager::currentNM()->mkConst( - UninterpretedConstant(n.getType(), debruijn)); + CodatatypeBoundVariable(n.getType(), debruijn)); } std::vector<Node> children; bool childChanged = false; @@ -798,10 +798,10 @@ Node DatatypesRewriter::replaceDebruijn(Node n, TypeNode orig_tn, unsigned depth) { - if (n.getKind() == kind::UNINTERPRETED_CONSTANT && n.getType() == orig_tn) + if (n.getKind() == kind::CODATATYPE_BOUND_VARIABLE && n.getType() == orig_tn) { unsigned index = - n.getConst<UninterpretedConstant>().getIndex().toUnsignedInt(); + n.getConst<CodatatypeBoundVariable>().getIndex().toUnsignedInt(); if (index == depth) { return orig; diff --git a/src/theory/datatypes/kinds b/src/theory/datatypes/kinds index 41d5ded76..cb3a78cf2 100644 --- a/src/theory/datatypes/kinds +++ b/src/theory/datatypes/kinds @@ -145,4 +145,13 @@ parameterized TUPLE_PROJECT TUPLE_PROJECT_OP 1 \ typerule TUPLE_PROJECT_OP "SimpleTypeRule<RBuiltinOperator>" typerule TUPLE_PROJECT ::cvc5::theory::datatypes::TupleProjectTypeRule +# For representing codatatype values +constant CODATATYPE_BOUND_VARIABLE \ + class \ + CodatatypeBoundVariable \ + ::cvc5::CodatatypeBoundVariableHashFunction \ + "expr/codatatype_bound_variable.h" \ + "the kind of expressions representing bound variables in codatatype constants, which are de Bruijn indexed variables; payload is an instance of the cvc5::CodatatypeBoundVariable class (used in models)" +typerule CODATATYPE_BOUND_VARIABLE ::cvc5::theory::datatypes::CodatatypeBoundVariableTypeRule + endtheory diff --git a/src/theory/datatypes/theory_datatypes.cpp b/src/theory/datatypes/theory_datatypes.cpp index c892ffc11..0cbeaa515 100644 --- a/src/theory/datatypes/theory_datatypes.cpp +++ b/src/theory/datatypes/theory_datatypes.cpp @@ -18,11 +18,11 @@ #include <sstream> #include "base/check.h" +#include "expr/codatatype_bound_variable.h" #include "expr/dtype.h" #include "expr/dtype_cons.h" #include "expr/kind.h" #include "expr/skolem_manager.h" -#include "expr/uninterpreted_constant.h" #include "options/datatypes_options.h" #include "options/quantifiers_options.h" #include "options/smt_options.h" @@ -1290,10 +1290,10 @@ bool TheoryDatatypes::collectModelValues(TheoryModel* m, Node TheoryDatatypes::getCodatatypesValue( Node n, std::map< Node, Node >& eqc_cons, std::map< Node, int >& vmap, int depth ){ std::map< Node, int >::iterator itv = vmap.find( n ); + NodeManager* nm = NodeManager::currentNM(); if( itv!=vmap.end() ){ int debruijn = depth - 1 - itv->second; - return NodeManager::currentNM()->mkConst( - UninterpretedConstant(n.getType(), debruijn)); + return nm->mkConst(CodatatypeBoundVariable(n.getType(), debruijn)); }else if( n.getType().isDatatype() ){ Node nc = eqc_cons[n]; if( !nc.isNull() ){ @@ -1308,7 +1308,7 @@ Node TheoryDatatypes::getCodatatypesValue( Node n, std::map< Node, Node >& eqc_c children.push_back( rv ); } vmap.erase( n ); - return NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, children ); + return nm->mkNode(APPLY_CONSTRUCTOR, children); } } return n; diff --git a/src/theory/datatypes/theory_datatypes_type_rules.cpp b/src/theory/datatypes/theory_datatypes_type_rules.cpp index 503eaf4df..86a11357b 100644 --- a/src/theory/datatypes/theory_datatypes_type_rules.cpp +++ b/src/theory/datatypes/theory_datatypes_type_rules.cpp @@ -18,6 +18,7 @@ #include <sstream> #include "expr/ascription_type.h" +#include "expr/codatatype_bound_variable.h" #include "expr/dtype.h" #include "expr/dtype_cons.h" #include "expr/type_matcher.h" @@ -597,6 +598,13 @@ TypeNode TupleProjectTypeRule::computeType(NodeManager* nm, TNode n, bool check) return nm->mkTupleType(types); } +TypeNode CodatatypeBoundVariableTypeRule::computeType(NodeManager* nodeManager, + TNode n, + bool check) +{ + return n.getConst<CodatatypeBoundVariable>().getType(); +} + } // namespace datatypes } // namespace theory } // namespace cvc5 diff --git a/src/theory/datatypes/theory_datatypes_type_rules.h b/src/theory/datatypes/theory_datatypes_type_rules.h index cf57a6c0d..7cf98aa74 100644 --- a/src/theory/datatypes/theory_datatypes_type_rules.h +++ b/src/theory/datatypes/theory_datatypes_type_rules.h @@ -99,6 +99,12 @@ class TupleProjectTypeRule static TypeNode computeType(NodeManager* nm, TNode n, bool check); }; +class CodatatypeBoundVariableTypeRule +{ + public: + static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check); +}; + } // namespace datatypes } // namespace theory } // namespace cvc5 diff --git a/src/theory/datatypes/type_enumerator.cpp b/src/theory/datatypes/type_enumerator.cpp index 39b48ece9..6528f1052 100644 --- a/src/theory/datatypes/type_enumerator.cpp +++ b/src/theory/datatypes/type_enumerator.cpp @@ -16,6 +16,7 @@ #include "theory/datatypes/type_enumerator.h" #include "expr/ascription_type.h" +#include "expr/codatatype_bound_variable.h" #include "expr/dtype_cons.h" #include "theory/datatypes/datatypes_rewriter.h" #include "theory/datatypes/theory_datatypes_utils.h" @@ -108,8 +109,8 @@ Node DatatypesEnumerator::getTermEnum( TypeNode tn, unsigned i ){ { if (d_child_enum) { - ret = NodeManager::currentNM()->mkConst( - UninterpretedConstant(d_type, d_size_limit)); + NodeManager* nm = NodeManager::currentNM(); + ret = nm->mkConst(CodatatypeBoundVariable(d_type, d_size_limit)); } else { diff --git a/src/theory/incomplete_id.cpp b/src/theory/incomplete_id.cpp index a2a3baa0f..c763fb9a0 100644 --- a/src/theory/incomplete_id.cpp +++ b/src/theory/incomplete_id.cpp @@ -40,6 +40,8 @@ const char* toString(IncompleteId i) case IncompleteId::STRINGS_LOOP_SKIP: return "STRINGS_LOOP_SKIP"; case IncompleteId::STRINGS_REGEXP_NO_SIMPLIFY: return "STRINGS_REGEXP_NO_SIMPLIFY"; + case IncompleteId::SEQ_FINITE_DYNAMIC_CARDINALITY: + return "SEQ_FINITE_DYNAMIC_CARDINALITY"; case IncompleteId::UF_HO_EXT_DISABLED: return "UF_HO_EXT_DISABLED"; case IncompleteId::UF_CARD_DISABLED: return "UF_CARD_DISABLED"; case IncompleteId::UF_CARD_MODE: return "UF_CARD_MODE"; diff --git a/src/theory/incomplete_id.h b/src/theory/incomplete_id.h index 951c2a94f..aaa458d8e 100644 --- a/src/theory/incomplete_id.h +++ b/src/theory/incomplete_id.h @@ -52,6 +52,11 @@ enum class IncompleteId STRINGS_LOOP_SKIP, // we could not simplify a regular expression membership STRINGS_REGEXP_NO_SIMPLIFY, + // incomplete due to sequence of a dynamic finite type (e.g. a type that + // we know is finite, but its exact cardinality is not fixed. For example, + // when finite model finding is enabled, uninterpreted sorts have a + // cardinality that depends on their interpretation in the current model). + SEQ_FINITE_DYNAMIC_CARDINALITY, // HO extensionality axiom was disabled UF_HO_EXT_DISABLED, // UF+cardinality solver was disabled diff --git a/src/theory/quantifiers/conjecture_generator.cpp b/src/theory/quantifiers/conjecture_generator.cpp index b98088a71..d778a679e 100644 --- a/src/theory/quantifiers/conjecture_generator.cpp +++ b/src/theory/quantifiers/conjecture_generator.cpp @@ -245,6 +245,7 @@ Node ConjectureGenerator::getUniversalRepresentative(TNode n, bool add) // now, do instantiation-based merging for each of these terms Trace("thm-ee-debug") << "Merge equivalence classes based on instantiations of terms..." << std::endl; //merge all pending equalities + EntailmentCheck* echeck = d_treg.getEntailmentCheck(); while( !d_upendingAdds.empty() ){ Trace("sg-pending") << "Add " << d_upendingAdds.size() << " pending terms..." << std::endl; std::vector< Node > pending; @@ -256,7 +257,7 @@ Node ConjectureGenerator::getUniversalRepresentative(TNode n, bool add) Trace("thm-ee-add") << "UEE : Add universal term " << t << std::endl; std::vector< Node > eq_terms; //if occurs modulo equality at ground level, it is equivalent to representative of ground equality engine - Node gt = getTermDatabase()->evaluateTerm(t); + Node gt = echeck->evaluateTerm(t); if( !gt.isNull() && gt!=t ){ eq_terms.push_back( gt ); } @@ -1362,7 +1363,8 @@ bool ConjectureGenerator::notifySubstitution( TNode glhs, std::map< TNode, TNode } Trace("sg-cconj-debug") << "Evaluate RHS : : " << rhs << std::endl; //get the representative of rhs with substitution subs - TNode grhs = getTermDatabase()->getEntailedTerm( rhs, subs, true ); + EntailmentCheck* echeck = d_treg.getEntailmentCheck(); + TNode grhs = echeck->getEntailedTerm(rhs, subs, true); Trace("sg-cconj-debug") << "...done evaluating term, got : " << grhs << std::endl; if( !grhs.isNull() ){ if( glhs!=grhs ){ diff --git a/src/theory/quantifiers/ematching/ho_trigger.cpp b/src/theory/quantifiers/ematching/ho_trigger.cpp index a8ab760ce..d2b0b0542 100644 --- a/src/theory/quantifiers/ematching/ho_trigger.cpp +++ b/src/theory/quantifiers/ematching/ho_trigger.cpp @@ -238,6 +238,7 @@ bool HigherOrderTrigger::sendInstantiation(std::vector<Node>& m, InferenceId id) d_lchildren.clear(); d_arg_to_arg_rep.clear(); d_arg_vector.clear(); + EntailmentCheck* echeck = d_treg.getEntailmentCheck(); for (std::pair<const TNode, std::vector<Node> >& ha : ho_var_apps_subs) { TNode var = ha.first; @@ -291,8 +292,7 @@ bool HigherOrderTrigger::sendInstantiation(std::vector<Node>& m, InferenceId id) { if (!d_qstate.areEqual(itf->second, args[k])) { - if (!d_treg.getTermDatabase()->isEntailed( - itf->second.eqNode(args[k]), true)) + if (!echeck->isEntailed(itf->second.eqNode(args[k]), true)) { fixed_vals[k] = Node::null(); } diff --git a/src/theory/quantifiers/ematching/inst_match_generator.cpp b/src/theory/quantifiers/ematching/inst_match_generator.cpp index d8e3b7950..075299583 100644 --- a/src/theory/quantifiers/ematching/inst_match_generator.cpp +++ b/src/theory/quantifiers/ematching/inst_match_generator.cpp @@ -23,6 +23,7 @@ #include "theory/quantifiers/ematching/inst_match_generator_multi_linear.h" #include "theory/quantifiers/ematching/inst_match_generator_simple.h" #include "theory/quantifiers/ematching/pattern_term_selector.h" +#include "theory/quantifiers/ematching/relational_match_generator.h" #include "theory/quantifiers/ematching/var_match_generator.h" #include "theory/quantifiers/instantiate.h" #include "theory/quantifiers/quantifiers_state.h" @@ -618,7 +619,7 @@ InstMatchGenerator* InstMatchGenerator::mkInstMatchGenerator( InstMatchGenerator* init; std::map< Node, InstMatchGenerator * >::iterator iti = pat_map_init.find( pats[pCounter] ); if( iti==pat_map_init.end() ){ - init = new InstMatchGenerator(tparent, pats[pCounter]); + init = getInstMatchGenerator(tparent, q, pats[pCounter]); }else{ init = iti->second; } @@ -645,6 +646,7 @@ InstMatchGenerator* InstMatchGenerator::getInstMatchGenerator(Trigger* tparent, Node q, Node n) { + // maybe variable match generator if (n.getKind() != INST_CONSTANT) { Trace("var-trigger-debug") @@ -672,6 +674,16 @@ InstMatchGenerator* InstMatchGenerator::getInstMatchGenerator(Trigger* tparent, return vmg; } } + Trace("relational-trigger") + << "Is " << n << " a relational trigger?" << std::endl; + // relational triggers + bool hasPol, pol; + Node lit; + if (TriggerTermInfo::isUsableRelationTrigger(n, hasPol, pol, lit)) + { + Trace("relational-trigger") << "...yes, for literal " << lit << std::endl; + return new RelationalMatchGenerator(tparent, lit, hasPol, pol); + } return new InstMatchGenerator(tparent, n); } diff --git a/src/theory/quantifiers/ematching/inst_strategy_e_matching.cpp b/src/theory/quantifiers/ematching/inst_strategy_e_matching.cpp index 30be83ecc..fdb0d0db3 100644 --- a/src/theory/quantifiers/ematching/inst_strategy_e_matching.cpp +++ b/src/theory/quantifiers/ematching/inst_strategy_e_matching.cpp @@ -339,20 +339,18 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ TriggerDatabase::TR_GET_OLD, d_num_trigger_vars[f]); } - if (tr == nullptr) + // if we generated a trigger above, add it + if (tr != nullptr) { - // did not generate a trigger - continue; - } - addTrigger(tr, f); - if (tr->isMultiTrigger()) - { - // only add a single multi-trigger - continue; + addTrigger(tr, f); + if (tr->isMultiTrigger()) + { + // only add a single multi-trigger + continue; + } } // if we are generating additional triggers... - size_t index = 0; - if (index < patTerms.size()) + if (patTerms.size() > 1) { // check if similar patterns exist, and if so, add them additionally unsigned nqfs_curr = 0; @@ -361,7 +359,7 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){ nqfs_curr = d_quant_rel->getNumQuantifiersForSymbol(patTerms[0].getOperator()); } - index++; + size_t index = 1; bool success = true; while (success && index < patTerms.size() && d_is_single_trigger[patTerms[index]]) @@ -527,16 +525,22 @@ bool InstStrategyAutoGenTriggers::generatePatternTerms(Node f) Trace("auto-gen-trigger-debug") << "...required polarity for " << pat << " is " << rpol << ", eq=" << rpoleq << std::endl; + // Currently, we have ad-hoc treatment for relational triggers that + // are not handled by RelationalMatchGen. + bool isAdHocRelationalTrigger = + TriggerTermInfo::isRelationalTrigger(pat) + && !TriggerTermInfo::isUsableRelationTrigger(pat); if (rpol != 0) { Assert(rpol == 1 || rpol == -1); - if (TriggerTermInfo::isRelationalTrigger(pat)) + if (isAdHocRelationalTrigger) { pat = rpol == -1 ? pat.negate() : pat; } else { - Assert(TriggerTermInfo::isAtomicTrigger(pat)); + Assert(TriggerTermInfo::isAtomicTrigger(pat) + || TriggerTermInfo::isUsableRelationTrigger(pat)); if (pat.getType().isBoolean() && rpoleq.isNull()) { if (options().quantifiers.literalMatchMode @@ -575,13 +579,10 @@ bool InstStrategyAutoGenTriggers::generatePatternTerms(Node f) } Trace("auto-gen-trigger-debug") << "...got : " << pat << std::endl; } - else + else if (isAdHocRelationalTrigger) { - if (TriggerTermInfo::isRelationalTrigger(pat)) - { - // consider both polarities - addPatternToPool(f, pat.negate(), num_fv, mpat); - } + // consider both polarities + addPatternToPool(f, pat.negate(), num_fv, mpat); } addPatternToPool(f, pat, num_fv, mpat); } diff --git a/src/theory/quantifiers/ematching/relational_match_generator.cpp b/src/theory/quantifiers/ematching/relational_match_generator.cpp new file mode 100644 index 000000000..8981a7a2d --- /dev/null +++ b/src/theory/quantifiers/ematching/relational_match_generator.cpp @@ -0,0 +1,125 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Relational match generator class. + */ + +#include "theory/quantifiers/ematching/relational_match_generator.h" + +#include "theory/quantifiers/term_util.h" +#include "util/rational.h" + +using namespace cvc5::kind; + +namespace cvc5 { +namespace theory { +namespace quantifiers { +namespace inst { + +RelationalMatchGenerator::RelationalMatchGenerator(Trigger* tparent, + Node rtrigger, + bool hasPol, + bool pol) + : InstMatchGenerator(tparent, Node::null()), + d_vindex(-1), + d_hasPol(hasPol), + d_pol(pol), + d_counter(0) +{ + Assert((rtrigger.getKind() == EQUAL && rtrigger[0].getType().isReal()) + || rtrigger.getKind() == GEQ); + Trace("relational-match-gen") + << "Relational trigger: " << rtrigger << ", hasPol/pol = " << hasPol + << "/" << pol << std::endl; + for (size_t i = 0; i < 2; i++) + { + if (rtrigger[i].getKind() == INST_CONSTANT) + { + d_var = rtrigger[i]; + d_vindex = d_var.getAttribute(InstVarNumAttribute()); + d_rhs = rtrigger[1 - i]; + Assert(!quantifiers::TermUtil::hasInstConstAttr(d_rhs)); + Kind k = rtrigger.getKind(); + d_rel = (i == 0 ? k : (k == GEQ ? LEQ : k)); + break; + } + } + Trace("relational-match-gen") << "...processed " << d_var << " (" << d_vindex + << ") " << d_rel << " " << d_rhs << std::endl; + AlwaysAssert(!d_var.isNull()) + << "Failed to initialize RelationalMatchGenerator"; +} + +bool RelationalMatchGenerator::reset(Node eqc) +{ + d_counter = 0; + return true; +} + +int RelationalMatchGenerator::getNextMatch(Node q, InstMatch& m) +{ + Trace("relational-match-gen") << "getNextMatch, rel match gen" << std::endl; + // try (up to) two different terms + Node s; + Node rhs = d_rhs; + bool rmPrev = m.get(d_vindex).isNull(); + while (d_counter < 2) + { + bool checkPol = false; + if (d_counter == 0) + { + checkPol = d_pol; + } + else + { + Assert(d_counter == 1); + if (d_hasPol) + { + break; + } + // try the opposite polarity + checkPol = !d_pol; + } + NodeManager* nm = NodeManager::currentNM(); + // falsify ( d_var <d_rel> d_rhs ) = checkPol + s = rhs; + if (!checkPol) + { + s = nm->mkNode(PLUS, s, nm->mkConst(Rational(d_rel == GEQ ? -1 : 1))); + } + d_counter++; + Trace("relational-match-gen") + << "...try set " << s << " for " << checkPol << std::endl; + if (m.set(d_qstate, d_vindex, s)) + { + Trace("relational-match-gen") << "...success" << std::endl; + int ret = continueNextMatch(q, m, InferenceId::UNKNOWN); + if (ret > 0) + { + Trace("relational-match-gen") << "...returned " << ret << std::endl; + return ret; + } + Trace("relational-match-gen") << "...failed to gen inst" << std::endl; + // failed + if (rmPrev) + { + m.d_vals[d_vindex] = Node::null(); + } + } + } + return -1; +} + +} // namespace inst +} // namespace quantifiers +} // namespace theory +} // namespace cvc5 diff --git a/src/theory/quantifiers/ematching/relational_match_generator.h b/src/theory/quantifiers/ematching/relational_match_generator.h new file mode 100644 index 000000000..eead2876a --- /dev/null +++ b/src/theory/quantifiers/ematching/relational_match_generator.h @@ -0,0 +1,92 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Relational match generator class. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__THEORY__QUANTIFIERS__RELATIONAL_MATCH_GENERATOR_H +#define CVC5__THEORY__QUANTIFIERS__RELATIONAL_MATCH_GENERATOR_H + +#include "expr/node.h" +#include "theory/quantifiers/ematching/inst_match_generator.h" + +namespace cvc5 { +namespace theory { +namespace quantifiers { +namespace inst { + +/** + * Match generator for relational triggers x ~ t where t is a ground term. + * This match generator tries a small fixed set of terms based on the kind of + * relation and the required polarity of the trigger in the quantified formula. + * + * For example, for quantified formula (forall ((x Int)) (=> (> x n) (P x))), + * we have that (> x n) is a relational trigger with required polarity "true". + * This generator will try the match `x -> n+1` only, where notice that n+1 is + * the canonical term chosen to satisfy x>n. Canonical terms for arithmetic + * relations (~ x n) are in set { n, n+1, n-1 }. + * + * If a relational trigger does not have a required polarity, then up to 2 + * terms are tried, a term that satisfies the relation, and one that does not. + * If (>= x n) is a relational trigger with no polarity, then `x -> n` and + * `x -> n-1` will be generated. + * + * Currently this class handles only equality between real or integer valued + * terms, or inequalities (kind GEQ). It furthermore only considers ground terms + * t for the right hand side of relations. + */ +class RelationalMatchGenerator : public InstMatchGenerator +{ + public: + /** + * @param tparent The parent trigger that this match generator belongs to + * @param rtrigger The relational trigger + * @param hasPol Whether the trigger has an entailed polarity + * @param pol The entailed polarity of the relational trigger. + */ + RelationalMatchGenerator(Trigger* tparent, + Node rtrigger, + bool hasPol, + bool pol); + + /** Reset */ + bool reset(Node eqc) override; + /** Get the next match. */ + int getNextMatch(Node q, InstMatch& m) override; + + private: + /** the variable */ + Node d_var; + /** The index of the variable */ + int64_t d_vindex; + /** the relation kind */ + Kind d_rel; + /** the right hand side */ + Node d_rhs; + /** whether we have a required polarity */ + bool d_hasPol; + /** the required polarity, if it exists */ + bool d_pol; + /** + * The current number of terms we have generated since the last call to reset + */ + size_t d_counter; +}; + +} // namespace inst +} // namespace quantifiers +} // namespace theory +} // namespace cvc5 + +#endif diff --git a/src/theory/quantifiers/ematching/trigger_term_info.cpp b/src/theory/quantifiers/ematching/trigger_term_info.cpp index 600797f4e..f31ec088a 100644 --- a/src/theory/quantifiers/ematching/trigger_term_info.cpp +++ b/src/theory/quantifiers/ematching/trigger_term_info.cpp @@ -70,6 +70,46 @@ bool TriggerTermInfo::isRelationalTriggerKind(Kind k) return k == EQUAL || k == GEQ; } +bool TriggerTermInfo::isUsableRelationTrigger(Node n) +{ + bool hasPol, pol; + Node lit; + return isUsableRelationTrigger(n, hasPol, pol, lit); +} +bool TriggerTermInfo::isUsableRelationTrigger(Node n, + bool& hasPol, + bool& pol, + Node& lit) +{ + // relational triggers (not) (= (~ x t) true|false), where ~ in { =, >= }. + hasPol = false; + pol = n.getKind() != NOT; + lit = pol ? n : n[0]; + if (lit.getKind() == EQUAL && lit[1].getType().isBoolean() + && lit[1].isConst()) + { + hasPol = true; + pol = lit[1].getConst<bool>() ? pol : !pol; + lit = lit[0]; + } + // is it a relational trigger? + if ((lit.getKind() == EQUAL && lit[0].getType().isReal()) + || lit.getKind() == GEQ) + { + // if one side of the relation is a variable and the other side is a ground + // term, we can treat this using the relational match generator + for (size_t i = 0; i < 2; i++) + { + if (lit[i].getKind() == INST_CONSTANT + && !quantifiers::TermUtil::hasInstConstAttr(lit[1 - i])) + { + return true; + } + } + } + return false; +} + bool TriggerTermInfo::isSimpleTrigger(Node n) { Node t = n.getKind() == NOT ? n[0] : n; @@ -105,7 +145,7 @@ int32_t TriggerTermInfo::getTriggerWeight(Node n) { return 0; } - if (isAtomicTrigger(n)) + if (isAtomicTrigger(n) || isUsableRelationTrigger(n)) { return 1; } diff --git a/src/theory/quantifiers/ematching/trigger_term_info.h b/src/theory/quantifiers/ematching/trigger_term_info.h index 753d0850c..3816d0988 100644 --- a/src/theory/quantifiers/ematching/trigger_term_info.h +++ b/src/theory/quantifiers/ematching/trigger_term_info.h @@ -108,6 +108,19 @@ class TriggerTermInfo static bool isRelationalTrigger(Node n); /** Is k a relational trigger kind? */ static bool isRelationalTriggerKind(Kind k); + /** + * Is n a usable relational trigger, which is true if RelationalMatchGenerator + * can process n. + */ + static bool isUsableRelationTrigger(Node n); + /** + * Same as above, but lit / hasPol / pol are updated to the required + * constructor arguments for RelationalMatchGenerator. + */ + static bool isUsableRelationTrigger(Node n, + bool& hasPol, + bool& pol, + Node& lit); /** is n a simple trigger (see inst_match_generator.h)? */ static bool isSimpleTrigger(Node n); /** get trigger weight @@ -116,7 +129,8 @@ class TriggerTermInfo * trigger term n, where the smaller the value, the easier. * * Returns 0 for triggers that are APPLY_UF terms. - * Returns 1 for other triggers whose kind is atomic. + * Returns 1 for other triggers whose kind is atomic, or are usable + * relational triggers. * Returns 2 otherwise. */ static int32_t getTriggerWeight(Node n); diff --git a/src/theory/quantifiers/entailment_check.cpp b/src/theory/quantifiers/entailment_check.cpp new file mode 100644 index 000000000..543414a4e --- /dev/null +++ b/src/theory/quantifiers/entailment_check.cpp @@ -0,0 +1,411 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Implementation of entailment check class. + */ + +#include "theory/quantifiers/entailment_check.h" + +#include "theory/quantifiers/quantifiers_state.h" +#include "theory/quantifiers/term_database.h" + +using namespace cvc5::kind; +using namespace cvc5::context; + +namespace cvc5 { +namespace theory { +namespace quantifiers { + +EntailmentCheck::EntailmentCheck(Env& env, QuantifiersState& qs, TermDb& tdb) + : EnvObj(env), d_qstate(qs), d_tdb(tdb) +{ + d_true = NodeManager::currentNM()->mkConst(true); + d_false = NodeManager::currentNM()->mkConst(false); +} + +EntailmentCheck::~EntailmentCheck() {} + +Node EntailmentCheck::evaluateTerm2(TNode n, + std::map<TNode, Node>& visited, + std::map<TNode, TNode>& subs, + bool subsRep, + bool useEntailmentTests, + bool reqHasTerm) +{ + std::map<TNode, Node>::iterator itv = visited.find(n); + if (itv != visited.end()) + { + return itv->second; + } + Trace("term-db-eval") << "evaluate term : " << n << std::endl; + Node ret = n; + Kind k = n.getKind(); + if (k == FORALL) + { + // do nothing + } + else if (k == BOUND_VARIABLE) + { + std::map<TNode, TNode>::iterator it = subs.find(n); + if (it != subs.end()) + { + if (!subsRep) + { + Assert(d_qstate.hasTerm(it->second)); + ret = d_qstate.getRepresentative(it->second); + } + else + { + ret = it->second; + } + } + } + else if (d_qstate.hasTerm(n)) + { + Trace("term-db-eval") << "...exists in ee, return rep" << std::endl; + ret = d_qstate.getRepresentative(n); + reqHasTerm = false; + } + else if (n.hasOperator()) + { + std::vector<TNode> args; + bool ret_set = false; + for (unsigned i = 0, nchild = n.getNumChildren(); i < nchild; i++) + { + TNode c = evaluateTerm2( + n[i], visited, subs, subsRep, useEntailmentTests, reqHasTerm); + if (c.isNull()) + { + ret = Node::null(); + ret_set = true; + break; + } + else if (c == d_true || c == d_false) + { + // short-circuiting + if ((k == AND && c == d_false) || (k == OR && c == d_true)) + { + ret = c; + ret_set = true; + reqHasTerm = false; + break; + } + else if (k == ITE && i == 0) + { + ret = evaluateTerm2(n[c == d_true ? 1 : 2], + visited, + subs, + subsRep, + useEntailmentTests, + reqHasTerm); + ret_set = true; + reqHasTerm = false; + break; + } + } + Trace("term-db-eval") << " child " << i << " : " << c << std::endl; + args.push_back(c); + } + if (!ret_set) + { + // get the (indexed) operator of n, if it exists + TNode f = d_tdb.getMatchOperator(n); + // if it is an indexed term, return the congruent term + if (!f.isNull()) + { + // if f is congruent to a term indexed by this class + TNode nn = d_tdb.getCongruentTerm(f, args); + Trace("term-db-eval") << " got congruent term " << nn + << " from DB for " << n << std::endl; + if (!nn.isNull()) + { + ret = d_qstate.getRepresentative(nn); + Trace("term-db-eval") << "return rep" << std::endl; + ret_set = true; + reqHasTerm = false; + Assert(!ret.isNull()); + } + } + if (!ret_set) + { + Trace("term-db-eval") << "return rewrite" << std::endl; + // a theory symbol or a new UF term + if (n.getMetaKind() == metakind::PARAMETERIZED) + { + args.insert(args.begin(), n.getOperator()); + } + ret = NodeManager::currentNM()->mkNode(n.getKind(), args); + ret = rewrite(ret); + if (ret.getKind() == EQUAL) + { + if (d_qstate.areDisequal(ret[0], ret[1])) + { + ret = d_false; + } + } + if (useEntailmentTests) + { + if (ret.getKind() == EQUAL || ret.getKind() == GEQ) + { + Valuation& val = d_qstate.getValuation(); + for (unsigned j = 0; j < 2; j++) + { + std::pair<bool, Node> et = val.entailmentCheck( + options::TheoryOfMode::THEORY_OF_TYPE_BASED, + j == 0 ? ret : ret.negate()); + if (et.first) + { + ret = j == 0 ? d_true : d_false; + break; + } + } + } + } + } + } + } + // must have the term + if (reqHasTerm && !ret.isNull()) + { + Kind rk = ret.getKind(); + if (rk != OR && rk != AND && rk != EQUAL && rk != ITE && rk != NOT + && rk != FORALL) + { + if (!d_qstate.hasTerm(ret)) + { + ret = Node::null(); + } + } + } + Trace("term-db-eval") << "evaluated term : " << n << ", got : " << ret + << ", reqHasTerm = " << reqHasTerm << std::endl; + visited[n] = ret; + return ret; +} + +TNode EntailmentCheck::getEntailedTerm2(TNode n, + std::map<TNode, TNode>& subs, + bool subsRep) +{ + Trace("term-db-entail") << "get entailed term : " << n << std::endl; + if (d_qstate.hasTerm(n)) + { + Trace("term-db-entail") << "...exists in ee, return rep " << std::endl; + return n; + } + else if (n.getKind() == BOUND_VARIABLE) + { + std::map<TNode, TNode>::iterator it = subs.find(n); + if (it != subs.end()) + { + Trace("term-db-entail") + << "...substitution is : " << it->second << std::endl; + if (subsRep) + { + Assert(d_qstate.hasTerm(it->second)); + Assert(d_qstate.getRepresentative(it->second) == it->second); + return it->second; + } + return getEntailedTerm2(it->second, subs, subsRep); + } + } + else if (n.getKind() == ITE) + { + for (uint32_t i = 0; i < 2; i++) + { + if (isEntailed2(n[0], subs, subsRep, i == 0)) + { + return getEntailedTerm2(n[i == 0 ? 1 : 2], subs, subsRep); + } + } + } + else + { + if (n.hasOperator()) + { + TNode f = d_tdb.getMatchOperator(n); + if (!f.isNull()) + { + std::vector<TNode> args; + for (size_t i = 0, nchild = n.getNumChildren(); i < nchild; i++) + { + TNode c = getEntailedTerm2(n[i], subs, subsRep); + if (c.isNull()) + { + return TNode::null(); + } + c = d_qstate.getRepresentative(c); + Trace("term-db-entail") << " child " << i << " : " << c << std::endl; + args.push_back(c); + } + TNode nn = d_tdb.getCongruentTerm(f, args); + Trace("term-db-entail") + << " got congruent term " << nn << " for " << n << std::endl; + return nn; + } + } + } + return TNode::null(); +} + +Node EntailmentCheck::evaluateTerm(TNode n, + std::map<TNode, TNode>& subs, + bool subsRep, + bool useEntailmentTests, + bool reqHasTerm) +{ + std::map<TNode, Node> visited; + return evaluateTerm2( + n, visited, subs, subsRep, useEntailmentTests, reqHasTerm); +} + +Node EntailmentCheck::evaluateTerm(TNode n, + bool useEntailmentTests, + bool reqHasTerm) +{ + std::map<TNode, Node> visited; + std::map<TNode, TNode> subs; + return evaluateTerm2(n, visited, subs, false, useEntailmentTests, reqHasTerm); +} + +TNode EntailmentCheck::getEntailedTerm(TNode n, + std::map<TNode, TNode>& subs, + bool subsRep) +{ + return getEntailedTerm2(n, subs, subsRep); +} + +TNode EntailmentCheck::getEntailedTerm(TNode n) +{ + std::map<TNode, TNode> subs; + return getEntailedTerm2(n, subs, false); +} + +bool EntailmentCheck::isEntailed2(TNode n, + std::map<TNode, TNode>& subs, + bool subsRep, + bool pol) +{ + Trace("term-db-entail") << "Check entailed : " << n << ", pol = " << pol + << std::endl; + Assert(n.getType().isBoolean()); + if (n.getKind() == EQUAL && !n[0].getType().isBoolean()) + { + TNode n1 = getEntailedTerm2(n[0], subs, subsRep); + if (!n1.isNull()) + { + TNode n2 = getEntailedTerm2(n[1], subs, subsRep); + if (!n2.isNull()) + { + if (n1 == n2) + { + return pol; + } + else + { + Assert(d_qstate.hasTerm(n1)); + Assert(d_qstate.hasTerm(n2)); + if (pol) + { + return d_qstate.areEqual(n1, n2); + } + else + { + return d_qstate.areDisequal(n1, n2); + } + } + } + } + } + else if (n.getKind() == NOT) + { + return isEntailed2(n[0], subs, subsRep, !pol); + } + else if (n.getKind() == OR || n.getKind() == AND) + { + bool simPol = (pol && n.getKind() == OR) || (!pol && n.getKind() == AND); + for (size_t i = 0, nchild = n.getNumChildren(); i < nchild; i++) + { + if (isEntailed2(n[i], subs, subsRep, pol)) + { + if (simPol) + { + return true; + } + } + else + { + if (!simPol) + { + return false; + } + } + } + return !simPol; + // Boolean equality here + } + else if (n.getKind() == EQUAL || n.getKind() == ITE) + { + for (size_t i = 0; i < 2; i++) + { + if (isEntailed2(n[0], subs, subsRep, i == 0)) + { + size_t ch = (n.getKind() == EQUAL || i == 0) ? 1 : 2; + bool reqPol = (n.getKind() == ITE || i == 0) ? pol : !pol; + return isEntailed2(n[ch], subs, subsRep, reqPol); + } + } + } + else if (n.getKind() == APPLY_UF) + { + TNode n1 = getEntailedTerm2(n, subs, subsRep); + if (!n1.isNull()) + { + Assert(d_qstate.hasTerm(n1)); + if (n1 == d_true) + { + return pol; + } + else if (n1 == d_false) + { + return !pol; + } + else + { + return d_qstate.getRepresentative(n1) == (pol ? d_true : d_false); + } + } + } + else if (n.getKind() == FORALL && !pol) + { + return isEntailed2(n[1], subs, subsRep, pol); + } + return false; +} + +bool EntailmentCheck::isEntailed(TNode n, bool pol) +{ + std::map<TNode, TNode> subs; + return isEntailed2(n, subs, false, pol); +} + +bool EntailmentCheck::isEntailed(TNode n, + std::map<TNode, TNode>& subs, + bool subsRep, + bool pol) +{ + return isEntailed2(n, subs, subsRep, pol); +} + +} // namespace quantifiers +} // namespace theory +} // namespace cvc5 diff --git a/src/theory/quantifiers/entailment_check.h b/src/theory/quantifiers/entailment_check.h new file mode 100644 index 000000000..ecf74fe85 --- /dev/null +++ b/src/theory/quantifiers/entailment_check.h @@ -0,0 +1,156 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Entailment check class + */ + +#include "cvc5_private.h" + +#ifndef CVC5__THEORY__QUANTIFIERS__ENTAILMENT_CHECK_H +#define CVC5__THEORY__QUANTIFIERS__ENTAILMENT_CHECK_H + +#include <map> +#include <vector> + +#include "expr/node.h" +#include "smt/env_obj.h" + +namespace cvc5 { +namespace theory { +namespace quantifiers { + +class QuantifiersState; +class TermDb; + +/** + * Entailment check utility, which determines whether formulas are entailed + * in the current context. The main focus of this class is on UF formulas. + * It works by looking at the term argument trie data structures in term + * database. For details, see e.g. Section 4.1 of Reynolds et al TACAS 2018. + */ +class EntailmentCheck : protected EnvObj +{ + public: + EntailmentCheck(Env& env, QuantifiersState& qs, TermDb& tdb); + ~EntailmentCheck(); + /** evaluate term + * + * Returns a term n' such that n * subs = n' is entailed based on the current + * set of equalities, where ( n * subs ) is term n under the substitution + * subs. + * + * This function may generate new terms. In particular, we typically rewrite + * subterms of n of maximal size (in terms of the AST) to terms that exist + * in the equality engine. + * + * useEntailmentTests is whether to call the theory engine's entailmentTest + * on literals n for which this call fails to find a term n' that is + * equivalent to n, for increased precision. This is not frequently used. + * + * If reqHasTerm, then we require that the returned term is a Boolean + * combination of terms that exist in the equality engine used by this call. + * If no such term is constructable, this call returns null. The motivation + * for setting this to true is to "fail fast" if we require the return value + * of this function to only involve existing terms. This is used e.g. in + * the "propagating instances" portion of conflict-based instantiation + * (quant_conflict_find.h). + * + * @param n The term under consideration + * @param subs The substitution under consideration + * @param subsRep Whether the range of subs are representatives in the current + * equality engine + * @param useEntailmentTests Whether to use entailment tests to show + * n * subs is equivalent to true/false. + * @param reqHasTerm Whether we require the returned term to be a Boolean + * combination of terms known to the current equality engine + * @return the term n * subs evaluates to + */ + Node evaluateTerm(TNode n, + std::map<TNode, TNode>& subs, + bool subsRep, + bool useEntailmentTests = false, + bool reqHasTerm = false); + /** Same as above, without a substitution */ + Node evaluateTerm(TNode n, + bool useEntailmentTests = false, + bool reqHasTerm = false); + /** get entailed term + * + * If possible, returns a term n' such that: + * (1) n' exists in the current equality engine (as specified by the state), + * (2) n = n' is entailed in the current context. + * It returns null if no such term can be found. + * Wrt evaluateTerm, this version does not construct new terms, and + * thus is less aggressive. + */ + TNode getEntailedTerm(TNode n); + /** get entailed term + * + * If possible, returns a term n' such that: + * (1) n' exists in the current equality engine (as specified by the state), + * (2) n * subs = n' is entailed in the current context, where * denotes + * substitution application. + * It returns null if no such term can be found. + * subsRep is whether the substitution maps to terms that are representatives + * according to the quantifiers state. + * Wrt evaluateTerm, this version does not construct new terms, and + * thus is less aggressive. + */ + TNode getEntailedTerm(TNode n, std::map<TNode, TNode>& subs, bool subsRep); + /** is entailed + * Checks whether the current context entails n with polarity pol, based on + * the equality information in the quantifiers state. Returns true if the + * entailment can be successfully shown. + */ + bool isEntailed(TNode n, bool pol); + /** is entailed + * + * Checks whether the current context entails ( n * subs ) with polarity pol, + * based on the equality information in the quantifiers state, + * where * denotes substitution application. + * subsRep is whether the substitution maps to terms that are representatives + * according to in the quantifiers state. + */ + bool isEntailed(TNode n, + std::map<TNode, TNode>& subs, + bool subsRep, + bool pol); + + protected: + /** helper for evaluate term */ + Node evaluateTerm2(TNode n, + std::map<TNode, Node>& visited, + std::map<TNode, TNode>& subs, + bool subsRep, + bool useEntailmentTests, + bool reqHasTerm); + /** helper for get entailed term */ + TNode getEntailedTerm2(TNode n, std::map<TNode, TNode>& subs, bool subsRep); + /** helper for is entailed */ + bool isEntailed2(TNode n, + std::map<TNode, TNode>& subs, + bool subsRep, + bool pol); + /** The quantifiers state object */ + QuantifiersState& d_qstate; + /** Reference to the term database */ + TermDb& d_tdb; + /** boolean terms */ + Node d_true; + Node d_false; +}; /* class EntailmentCheck */ + +} // namespace quantifiers +} // namespace theory +} // namespace cvc5 + +#endif /* CVC5__THEORY__QUANTIFIERS__ENTAILMENT_CHECK_H */ diff --git a/src/theory/quantifiers/expr_miner.cpp b/src/theory/quantifiers/expr_miner.cpp index d648a7a29..fad95612f 100644 --- a/src/theory/quantifiers/expr_miner.cpp +++ b/src/theory/quantifiers/expr_miner.cpp @@ -53,17 +53,23 @@ Node ExprMiner::convertToSkolem(Node n) void ExprMiner::initializeChecker(std::unique_ptr<SolverEngine>& checker, Node query) { + initializeChecker(checker, query, options(), logicInfo()); +} + +void ExprMiner::initializeChecker(std::unique_ptr<SolverEngine>& checker, + Node query, + const Options& opts, + const LogicInfo& logicInfo) +{ Assert (!query.isNull()); if (Options::current().quantifiers.sygusExprMinerCheckTimeoutWasSetByUser) { - initializeSubsolver(checker, - d_env, - true, - options::sygusExprMinerCheckTimeout()); + initializeSubsolver( + checker, opts, logicInfo, true, options::sygusExprMinerCheckTimeout()); } else { - initializeSubsolver(checker, d_env); + initializeSubsolver(checker, opts, logicInfo); } // also set the options checker->setOption("sygus-rr-synth-input", "false"); diff --git a/src/theory/quantifiers/expr_miner.h b/src/theory/quantifiers/expr_miner.h index 3933b9635..702dbf5aa 100644 --- a/src/theory/quantifiers/expr_miner.h +++ b/src/theory/quantifiers/expr_miner.h @@ -85,7 +85,12 @@ class ExprMiner : protected EnvObj * of the argument "query", which is a formula whose free variables (of * kind BOUND_VARIABLE) are a subset of d_vars. */ - void initializeChecker(std::unique_ptr<SolverEngine>& smte, Node query); + void initializeChecker(std::unique_ptr<SolverEngine>& checker, Node query); + /** Also with configurable options and logic */ + void initializeChecker(std::unique_ptr<SolverEngine>& checker, + Node query, + const Options& opts, + const LogicInfo& logicInfo); /** * Run the satisfiability check on query and return the result * (sat/unsat/unknown). diff --git a/src/theory/quantifiers/fmf/full_model_check.cpp b/src/theory/quantifiers/fmf/full_model_check.cpp index 7c1d36ce8..94351274a 100644 --- a/src/theory/quantifiers/fmf/full_model_check.cpp +++ b/src/theory/quantifiers/fmf/full_model_check.cpp @@ -951,10 +951,15 @@ void FullModelChecker::doCheck(FirstOrderModelFmc * fm, Node f, Def & d, Node n Node r = n; if( !n.isConst() ){ TypeNode tn = n.getType(); - if( !fm->hasTerm(n) && tn.isFirstClass() ){ - r = getSomeDomainElement(fm, tn ); + if (!fm->hasTerm(n) && tn.isFirstClass()) + { + // if the term is unknown, we do not assume any value for it + r = Node::null(); + } + else + { + r = fm->getRepresentative(r); } - r = fm->getRepresentative( r ); } Trace("fmc-debug") << "Add constant entry..." << std::endl; d.addEntry(fm, mkCondDefault(fm, f), r); diff --git a/src/theory/quantifiers/instantiate.cpp b/src/theory/quantifiers/instantiate.cpp index f756fcfd1..be04f9404 100644 --- a/src/theory/quantifiers/instantiate.cpp +++ b/src/theory/quantifiers/instantiate.cpp @@ -25,6 +25,7 @@ #include "smt/logic_exception.h" #include "smt/smt_statistics_registry.h" #include "theory/quantifiers/cegqi/inst_strategy_cegqi.h" +#include "theory/quantifiers/entailment_check.h" #include "theory/quantifiers/first_order_model.h" #include "theory/quantifiers/quantifiers_attributes.h" #include "theory/quantifiers/quantifiers_preprocess.h" @@ -183,7 +184,7 @@ bool Instantiate::addInstantiation(Node q, #endif } - TermDb* tdb = d_treg.getTermDatabase(); + EntailmentCheck* ec = d_treg.getEntailmentCheck(); // Note we check for entailment before checking for term vector duplication. // Although checking for term vector duplication is a faster check, it is // included automatically with recordInstantiationInternal, hence we prefer @@ -206,7 +207,7 @@ bool Instantiate::addInstantiation(Node q, { subs[q[0][i]] = terms[i]; } - if (tdb->isEntailed(q[1], subs, false, true)) + if (ec->isEntailed(q[1], subs, false, true)) { Trace("inst-add-debug") << " --> Currently entailed." << std::endl; ++(d_statistics.d_inst_duplicate_ent); @@ -217,6 +218,7 @@ bool Instantiate::addInstantiation(Node q, // check based on instantiation level if (options::instMaxLevel() != -1) { + TermDb* tdb = d_treg.getTermDatabase(); for (Node& t : terms) { if (!tdb->isTermEligibleForInstantiation(t, q)) @@ -409,7 +411,7 @@ bool Instantiate::addInstantiationExpFail(Node q, // will never succeed with 1 variable return false; } - TermDb* tdb = d_treg.getTermDatabase(); + EntailmentCheck* echeck = d_treg.getEntailmentCheck(); Trace("inst-exp-fail") << "Explain inst failure..." << terms << std::endl; // set up information for below std::vector<Node>& vars = d_qreg.d_vars[q]; @@ -445,7 +447,7 @@ bool Instantiate::addInstantiationExpFail(Node q, if (options::instNoEntail()) { Trace("inst-exp-fail") << " check entailment" << std::endl; - success = tdb->isEntailed(q[1], subs, false, true); + success = echeck->isEntailed(q[1], subs, false, true); Trace("inst-exp-fail") << " entailed: " << success << std::endl; } // check whether the instantiation rewrites to the same thing diff --git a/src/theory/quantifiers/quant_conflict_find.cpp b/src/theory/quantifiers/quant_conflict_find.cpp index 361adfd54..323398879 100644 --- a/src/theory/quantifiers/quant_conflict_find.cpp +++ b/src/theory/quantifiers/quant_conflict_find.cpp @@ -52,12 +52,6 @@ QuantInfo::~QuantInfo() { d_var_mg.clear(); } -QuantifiersInferenceManager& QuantInfo::getInferenceManager() -{ - Assert(d_parent != nullptr); - return d_parent->getInferenceManager(); -} - void QuantInfo::initialize( QuantConflictFind * p, Node q, Node qn ) { d_parent = p; d_q = q; @@ -577,30 +571,33 @@ bool QuantInfo::isTConstraintSpurious(QuantConflictFind* p, { if( options::qcfEagerTest() ){ //check whether the instantiation evaluates as expected + EntailmentCheck* echeck = p->getTermRegistry().getEntailmentCheck(); + std::map<TNode, TNode> subs; + for (size_t i = 0, tsize = terms.size(); i < tsize; i++) + { + Trace("qcf-instance-check") << " " << terms[i] << std::endl; + subs[d_q[0][i]] = terms[i]; + } + for (size_t i = 0, evsize = d_extra_var.size(); i < evsize; i++) + { + Node n = getCurrentExpValue(d_extra_var[i]); + Trace("qcf-instance-check") + << " " << d_extra_var[i] << " -> " << n << std::endl; + subs[d_extra_var[i]] = n; + } if (p->atConflictEffort()) { Trace("qcf-instance-check") << "Possible conflict instance for " << d_q << " : " << std::endl; - std::map< TNode, TNode > subs; - for( unsigned i=0; i<terms.size(); i++ ){ - Trace("qcf-instance-check") << " " << terms[i] << std::endl; - subs[d_q[0][i]] = terms[i]; - } - TermDb* tdb = p->getTermDatabase(); - for( unsigned i=0; i<d_extra_var.size(); i++ ){ - Node n = getCurrentExpValue( d_extra_var[i] ); - Trace("qcf-instance-check") << " " << d_extra_var[i] << " -> " << n << std::endl; - subs[d_extra_var[i]] = n; - } - if (!tdb->isEntailed(d_q[1], subs, false, false)) + if (!echeck->isEntailed(d_q[1], subs, false, false)) { Trace("qcf-instance-check") << "...not entailed to be false." << std::endl; return true; } }else{ - Node inst = - getInferenceManager().getInstantiate()->getInstantiation(d_q, terms); - inst = Rewriter::rewrite(inst); - Node inst_eval = p->getTermDatabase()->evaluateTerm( - inst, options::qcfTConstraint(), true); + // see if the body of the quantified formula evaluates to a Boolean + // combination of known terms under the current substitution. We use + // the helper method evaluateTerm from the entailment check utility. + Node inst_eval = echeck->evaluateTerm( + d_q[1], subs, false, options::qcfTConstraint(), true); if( Trace.isOn("qcf-instance-check") ){ Trace("qcf-instance-check") << "Possible propagating instance for " << d_q << " : " << std::endl; for( unsigned i=0; i<terms.size(); i++ ){ @@ -608,6 +605,10 @@ bool QuantInfo::isTConstraintSpurious(QuantConflictFind* p, } Trace("qcf-instance-check") << "...evaluates to " << inst_eval << std::endl; } + // If it is the case that instantiation can be rewritten to a Boolean + // combination of terms that exist in the current context, then inst_eval + // is non-null. Moreover, we insist that inst_eval is not true, or else + // the instantiation is trivially entailed and hence is spurious. if (inst_eval.isNull() || (inst_eval.isConst() && inst_eval.getConst<bool>())) { @@ -1219,11 +1220,11 @@ bool MatchGen::reset_round(QuantConflictFind* p) // d_ground_eval[0] = p->d_false; //} // modified - TermDb* tdb = p->getTermDatabase(); + EntailmentCheck* echeck = p->getTermRegistry().getEntailmentCheck(); QuantifiersState& qs = p->getState(); for (unsigned i = 0; i < 2; i++) { - if (tdb->isEntailed(d_n, i == 0)) + if (echeck->isEntailed(d_n, i == 0)) { d_ground_eval[0] = i==0 ? p->d_true : p->d_false; } @@ -1233,13 +1234,13 @@ bool MatchGen::reset_round(QuantConflictFind* p) } } }else if( d_type==typ_eq ){ - TermDb* tdb = p->getTermDatabase(); + EntailmentCheck* echeck = p->getTermRegistry().getEntailmentCheck(); QuantifiersState& qs = p->getState(); for (unsigned i = 0, size = d_n.getNumChildren(); i < size; i++) { if (!expr::hasBoundVar(d_n[i])) { - TNode t = tdb->getEntailedTerm(d_n[i]); + TNode t = echeck->getEntailedTerm(d_n[i]); if (qs.isInConflict()) { return false; @@ -1289,13 +1290,9 @@ void MatchGen::reset( QuantConflictFind * p, bool tgt, QuantInfo * qi ) { TNode n = qi->getCurrentValue( d_n ); int vn = qi->getCurrentRepVar( qi->getVarNum( n ) ); if( vn==-1 ){ - //evaluate the value, see if it is compatible - //int e = p->evaluate( n ); - //if( ( e==1 && d_tgt ) || ( e==0 && !d_tgt ) ){ - // d_child_counter = 0; - //} - //modified - if( p->getTermDatabase()->isEntailed( n, d_tgt ) ){ + EntailmentCheck* echeck = p->getTermRegistry().getEntailmentCheck(); + if (echeck->isEntailed(n, d_tgt)) + { d_child_counter = 0; } }else{ @@ -2168,8 +2165,8 @@ void QuantConflictFind::checkQuantifiedFormula(Node q, Node inst = qinst->getInstantiation(q, terms); Debug("qcf-check-inst") << "Check instantiation " << inst << "..." << std::endl; - Assert(!getTermDatabase()->isEntailed(inst, true)); - Assert(getTermDatabase()->isEntailed(inst, false) + Assert(!getTermRegistry().getEntailmentCheck()->isEntailed(inst, true)); + Assert(getTermRegistry().getEntailmentCheck()->isEntailed(inst, false) || d_effort > EFFORT_CONFLICT); } // Process the lemma: either add an instantiation or specific lemmas diff --git a/src/theory/quantifiers/quant_conflict_find.h b/src/theory/quantifiers/quant_conflict_find.h index 927a74ff2..d14e281fb 100644 --- a/src/theory/quantifiers/quant_conflict_find.h +++ b/src/theory/quantifiers/quant_conflict_find.h @@ -132,8 +132,6 @@ public: public: QuantInfo(); ~QuantInfo(); - /** Get quantifiers inference manager */ - QuantifiersInferenceManager& getInferenceManager(); std::vector< TNode > d_vars; std::vector< TypeNode > d_var_types; std::map< TNode, int > d_var_num; diff --git a/src/theory/quantifiers/quantifiers_rewriter.cpp b/src/theory/quantifiers/quantifiers_rewriter.cpp index 7c7c7aade..52e90e26e 100644 --- a/src/theory/quantifiers/quantifiers_rewriter.cpp +++ b/src/theory/quantifiers/quantifiers_rewriter.cpp @@ -15,6 +15,7 @@ #include "theory/quantifiers/quantifiers_rewriter.h" +#include "expr/ascription_type.h" #include "expr/bound_var_manager.h" #include "expr/dtype.h" #include "expr/dtype_cons.h" @@ -895,12 +896,25 @@ bool QuantifiersRewriter::getVarElimLit(Node body, const DType& dt = datatypes::utils::datatypeOf(tester); const DTypeConstructor& c = dt[index]; std::vector<Node> newChildren; - newChildren.push_back(c.getConstructor()); + Node cons = c.getConstructor(); + TypeNode tspec; + // take into account if parametric + if (dt.isParametric()) + { + tspec = c.getSpecializedConstructorType(lit[0].getType()); + cons = nm->mkNode( + APPLY_TYPE_ASCRIPTION, nm->mkConst(AscriptionType(tspec)), cons); + } + else + { + tspec = cons.getType(); + } + newChildren.push_back(cons); std::vector<Node> newVars; BoundVarManager* bvm = nm->getBoundVarManager(); - for (unsigned j = 0, nargs = c.getNumArgs(); j < nargs; j++) + for (size_t j = 0, nargs = c.getNumArgs(); j < nargs; j++) { - TypeNode tn = c[j].getRangeType(); + TypeNode tn = tspec[j]; Node rn = nm->mkConst(Rational(j)); Node cacheVal = BoundVarManager::getCacheValue(body, lit, rn); Node v = bvm->mkBoundVar<QRewDtExpandAttribute>(cacheVal, tn); diff --git a/src/theory/quantifiers/sygus/cegis.cpp b/src/theory/quantifiers/sygus/cegis.cpp index c8e14e4bd..fdc0b28e0 100644 --- a/src/theory/quantifiers/sygus/cegis.cpp +++ b/src/theory/quantifiers/sygus/cegis.cpp @@ -39,6 +39,7 @@ Cegis::Cegis(Env& env, SynthConjecture* p) : SygusModule(env, qs, qim, tds, p), d_eval_unfold(tds->getEvalUnfold()), + d_cexClosedEnum(false), d_cegis_sampler(env), d_usingSymCons(false) { @@ -47,11 +48,18 @@ Cegis::Cegis(Env& env, bool Cegis::initialize(Node conj, Node n, const std::vector<Node>& candidates) { d_base_body = n; + d_cexClosedEnum = true; if (d_base_body.getKind() == NOT && d_base_body[0].getKind() == FORALL) { for (const Node& v : d_base_body[0][0]) { d_base_vars.push_back(v); + if (!v.getType().isClosedEnumerable()) + { + // not closed enumerable, refinement lemmas cannot be sent to the + // quantifier-free datatype solver + d_cexClosedEnum = false; + } } d_base_body = d_base_body[0][1]; } @@ -467,14 +475,18 @@ void Cegis::addRefinementLemmaConjunct(unsigned wcounter, void Cegis::registerRefinementLemma(const std::vector<Node>& vars, Node lem) { addRefinementLemma(lem); - // Make the refinement lemma and add it to lems. - // This lemma is guarded by the parent's guard, which has the semantics - // "this conjecture has a solution", hence this lemma states: - // if the parent conjecture has a solution, it satisfies the specification - // for the given concrete point. - Node rlem = - NodeManager::currentNM()->mkNode(OR, d_parent->getGuard().negate(), lem); - d_qim.addPendingLemma(rlem, InferenceId::QUANTIFIERS_SYGUS_CEGIS_REFINE); + // must be closed enumerable + if (d_cexClosedEnum && options().quantifiers.sygusEvalUnfold) + { + // Make the refinement lemma and add it to lems. + // This lemma is guarded by the parent's guard, which has the semantics + // "this conjecture has a solution", hence this lemma states: + // if the parent conjecture has a solution, it satisfies the specification + // for the given concrete point. + Node rlem = NodeManager::currentNM()->mkNode( + OR, d_parent->getGuard().negate(), lem); + d_qim.addPendingLemma(rlem, InferenceId::QUANTIFIERS_SYGUS_CEGIS_REFINE); + } } bool Cegis::usingRepairConst() { return true; } diff --git a/src/theory/quantifiers/sygus/cegis.h b/src/theory/quantifiers/sygus/cegis.h index 8e0fffdd1..8d1f0a1b2 100644 --- a/src/theory/quantifiers/sygus/cegis.h +++ b/src/theory/quantifiers/sygus/cegis.h @@ -119,6 +119,13 @@ class Cegis : public SygusModule std::vector<Node> d_rl_vals; /** all variables appearing in refinement lemmas */ std::unordered_set<Node> d_refinement_lemma_vars; + /** + * Are the counterexamples we are handling in this class of only closed + * enumerable types (see TypeNode::isClosedEnumerable). If this is false, + * then CEGIS refinement lemmas can contain terms that are unhandled by + * theory solvers, e.g. uninterpreted constants. + */ + bool d_cexClosedEnum; /** adds lem as a refinement lemma */ void addRefinementLemma(Node lem); diff --git a/src/theory/quantifiers/sygus/cegis_core_connective.cpp b/src/theory/quantifiers/sygus/cegis_core_connective.cpp index 936310e4e..5a0cf8724 100644 --- a/src/theory/quantifiers/sygus/cegis_core_connective.cpp +++ b/src/theory/quantifiers/sygus/cegis_core_connective.cpp @@ -33,41 +33,6 @@ namespace cvc5 { namespace theory { namespace quantifiers { -bool VariadicTrie::add(Node n, const std::vector<Node>& i) -{ - VariadicTrie* curr = this; - for (const Node& ic : i) - { - curr = &(curr->d_children[ic]); - } - if (curr->d_data.isNull()) - { - curr->d_data = n; - return true; - } - return false; -} - -bool VariadicTrie::hasSubset(const std::vector<Node>& is) const -{ - if (!d_data.isNull()) - { - return true; - } - for (const std::pair<const Node, VariadicTrie>& p : d_children) - { - Node n = p.first; - if (std::find(is.begin(), is.end(), n) != is.end()) - { - if (p.second.hasSubset(is)) - { - return true; - } - } - } - return false; -} - CegisCoreConnective::CegisCoreConnective(Env& env, QuantifiersState& qs, QuantifiersInferenceManager& qim, @@ -595,38 +560,6 @@ bool CegisCoreConnective::Component::addToAsserts(CegisCoreConnective* p, return true; } -void CegisCoreConnective::getModel(SolverEngine& smt, - std::vector<Node>& vals) const -{ - for (const Node& v : d_vars) - { - Node mv = smt.getValue(v); - Trace("sygus-ccore-model") << v << " -> " << mv << " "; - vals.push_back(mv); - } -} - -bool CegisCoreConnective::getUnsatCore( - SolverEngine& smt, - const std::unordered_set<Node>& queryAsserts, - std::vector<Node>& uasserts) const -{ - UnsatCore uc = smt.getUnsatCore(); - bool hasQuery = false; - for (UnsatCore::const_iterator i = uc.begin(); i != uc.end(); ++i) - { - Node uassert = *i; - Trace("sygus-ccore-debug") << " uc " << uassert << std::endl; - if (queryAsserts.find(uassert) != queryAsserts.end()) - { - hasQuery = true; - continue; - } - uasserts.push_back(uassert); - } - return hasQuery; -} - Result CegisCoreConnective::checkSat(Node n, std::vector<Node>& mvs) const { Trace("sygus-ccore-debug") << "...check-sat " << n << "..." << std::endl; @@ -758,7 +691,8 @@ Node CegisCoreConnective::constructSolutionFromPool(Component& ccheck, std::unordered_set<Node> queryAsserts; queryAsserts.insert(ccheck.getFormula()); queryAsserts.insert(d_sc); - bool hasQuery = getUnsatCore(*checkSol, queryAsserts, uasserts); + bool hasQuery = + getUnsatCoreFromSubsolver(*checkSol, queryAsserts, uasserts); // now, check the side condition bool falseCore = false; if (!d_sc.isNull()) @@ -794,7 +728,7 @@ Node CegisCoreConnective::constructSolutionFromPool(Component& ccheck, uasserts.clear(); std::unordered_set<Node> queryAsserts2; queryAsserts2.insert(d_sc); - getUnsatCore(*checkSc, queryAsserts2, uasserts); + getUnsatCoreFromSubsolver(*checkSc, queryAsserts2, uasserts); falseCore = true; } } @@ -838,7 +772,7 @@ Node CegisCoreConnective::constructSolutionFromPool(Component& ccheck, // it does not entail the postcondition, add an assertion that blocks // the current point mvs.clear(); - getModel(*checkSol, mvs); + getModelFromSubsolver(*checkSol, d_vars, mvs); // should evaluate to true Node ean = evaluatePt(an, Node::null(), mvs); Assert(ean.isConst() && ean.getConst<bool>()); diff --git a/src/theory/quantifiers/sygus/cegis_core_connective.h b/src/theory/quantifiers/sygus/cegis_core_connective.h index 3ee631dea..26784f939 100644 --- a/src/theory/quantifiers/sygus/cegis_core_connective.h +++ b/src/theory/quantifiers/sygus/cegis_core_connective.h @@ -22,6 +22,7 @@ #include "expr/node.h" #include "expr/node_trie.h" +#include "expr/variadic_trie.h" #include "smt/env_obj.h" #include "theory/quantifiers/sygus/cegis.h" #include "util/result.h" @@ -33,30 +34,6 @@ class SolverEngine; namespace theory { namespace quantifiers { -/** - * A trie that stores data at undetermined depth. Storing data at - * undetermined depth is in contrast to the NodeTrie (expr/node_trie.h), which - * assumes all data is stored at a fixed depth. - * - * Since data can be stored at any depth, we require both a d_children field - * and a d_data field. - */ -class VariadicTrie -{ - public: - /** the children of this node */ - std::map<Node, VariadicTrie> d_children; - /** the data at this node */ - Node d_data; - /** - * Add data with identifier n indexed by i, return true if data is not already - * stored at the node indexed by i. - */ - bool add(Node n, const std::vector<Node>& i); - /** Is there any data in this trie that is indexed by any subset of is? */ - bool hasSubset(const std::vector<Node>& is) const; -}; - /** CegisCoreConnective * * A sygus module that is specialized in handling conjectures of the form: @@ -336,23 +313,6 @@ class CegisCoreConnective : public Cegis Node d_sc; //-----------------------------------for SMT engine calls /** - * Assuming smt has just been called to check-sat and returned "SAT", this - * method adds the model for d_vars to mvs. - */ - void getModel(SolverEngine& smt, std::vector<Node>& mvs) const; - /** - * Assuming smt has just been called to check-sat and returned "UNSAT", this - * method get the unsat core and adds it to uasserts. - * - * The assertions in the argument queryAsserts (which we are not interested - * in tracking in the unsat core) are excluded from uasserts. - * If one of the formulas in queryAsserts was in the unsat core, then this - * method returns true. Otherwise, this method returns false. - */ - bool getUnsatCore(SolverEngine& smt, - const std::unordered_set<Node>& queryAsserts, - std::vector<Node>& uasserts) const; - /** * Return the result of checking satisfiability of formula n. * If n was satisfiable, then we store the model for d_vars in mvs. */ diff --git a/src/theory/quantifiers/sygus/sygus_grammar_cons.cpp b/src/theory/quantifiers/sygus/sygus_grammar_cons.cpp index 43c958ff9..9b4cfb9e1 100644 --- a/src/theory/quantifiers/sygus/sygus_grammar_cons.cpp +++ b/src/theory/quantifiers/sygus/sygus_grammar_cons.cpp @@ -20,6 +20,7 @@ #include "expr/ascription_type.h" #include "expr/dtype_cons.h" +#include "expr/node_algorithm.h" #include "options/quantifiers_options.h" #include "theory/bv/theory_bv_utils.h" #include "theory/datatypes/sygus_datatype_utils.h" @@ -420,6 +421,8 @@ void CegGrammarConstructor::mkSygusConstantsForType(TypeNode type, { // generate constant array over the first element of the constituent type Node c = type.mkGroundTerm(); + // note that c should never contain an uninterpreted constant + Assert(!expr::hasSubtermKind(UNINTERPRETED_CONSTANT, c)); ops.push_back(c); } else if (type.isRoundingMode()) diff --git a/src/theory/quantifiers/term_database.cpp b/src/theory/quantifiers/term_database.cpp index b1537a390..573cab7bf 100644 --- a/src/theory/quantifiers/term_database.cpp +++ b/src/theory/quantifiers/term_database.cpp @@ -451,366 +451,6 @@ bool TermDb::inRelevantDomain( TNode f, unsigned i, TNode r ) { } } -Node TermDb::evaluateTerm2(TNode n, - std::map<TNode, Node>& visited, - std::vector<Node>& exp, - bool useEntailmentTests, - bool computeExp, - bool reqHasTerm) -{ - std::map< TNode, Node >::iterator itv = visited.find( n ); - if( itv != visited.end() ){ - return itv->second; - } - size_t prevSize = exp.size(); - Trace("term-db-eval") << "evaluate term : " << n << std::endl; - Node ret = n; - if( n.getKind()==FORALL || n.getKind()==BOUND_VARIABLE ){ - //do nothing - } - else if (d_qstate.hasTerm(n)) - { - Trace("term-db-eval") << "...exists in ee, return rep" << std::endl; - ret = d_qstate.getRepresentative(n); - if (computeExp) - { - if (n != ret) - { - exp.push_back(n.eqNode(ret)); - } - } - reqHasTerm = false; - } - else if (n.hasOperator()) - { - std::vector<TNode> args; - bool ret_set = false; - Kind k = n.getKind(); - std::vector<Node> tempExp; - for (unsigned i = 0, nchild = n.getNumChildren(); i < nchild; i++) - { - TNode c = evaluateTerm2(n[i], - visited, - tempExp, - useEntailmentTests, - computeExp, - reqHasTerm); - if (c.isNull()) - { - ret = Node::null(); - ret_set = true; - break; - } - else if (c == d_true || c == d_false) - { - // short-circuiting - if ((k == AND && c == d_false) || (k == OR && c == d_true)) - { - ret = c; - ret_set = true; - reqHasTerm = false; - break; - } - else if (k == ITE && i == 0) - { - ret = evaluateTerm2(n[c == d_true ? 1 : 2], - visited, - tempExp, - useEntailmentTests, - computeExp, - reqHasTerm); - ret_set = true; - reqHasTerm = false; - break; - } - } - if (computeExp) - { - exp.insert(exp.end(), tempExp.begin(), tempExp.end()); - } - Trace("term-db-eval") << " child " << i << " : " << c << std::endl; - args.push_back(c); - } - if (ret_set) - { - // if we short circuited - if (computeExp) - { - exp.clear(); - exp.insert(exp.end(), tempExp.begin(), tempExp.end()); - } - } - else - { - // get the (indexed) operator of n, if it exists - TNode f = getMatchOperator(n); - // if it is an indexed term, return the congruent term - if (!f.isNull()) - { - // if f is congruent to a term indexed by this class - TNode nn = getCongruentTerm(f, args); - Trace("term-db-eval") << " got congruent term " << nn - << " from DB for " << n << std::endl; - if (!nn.isNull()) - { - if (computeExp) - { - Assert(nn.getNumChildren() == n.getNumChildren()); - for (unsigned i = 0, nchild = nn.getNumChildren(); i < nchild; i++) - { - if (nn[i] != n[i]) - { - exp.push_back(nn[i].eqNode(n[i])); - } - } - } - ret = d_qstate.getRepresentative(nn); - Trace("term-db-eval") << "return rep" << std::endl; - ret_set = true; - reqHasTerm = false; - Assert(!ret.isNull()); - if (computeExp) - { - if (n != ret) - { - exp.push_back(nn.eqNode(ret)); - } - } - } - } - if( !ret_set ){ - Trace("term-db-eval") << "return rewrite" << std::endl; - // a theory symbol or a new UF term - if (n.getMetaKind() == metakind::PARAMETERIZED) - { - args.insert(args.begin(), n.getOperator()); - } - ret = NodeManager::currentNM()->mkNode(n.getKind(), args); - ret = rewrite(ret); - if (ret.getKind() == EQUAL) - { - if (d_qstate.areDisequal(ret[0], ret[1])) - { - ret = d_false; - } - } - if (useEntailmentTests) - { - if (ret.getKind() == EQUAL || ret.getKind() == GEQ) - { - Valuation& val = d_qstate.getValuation(); - for (unsigned j = 0; j < 2; j++) - { - std::pair<bool, Node> et = val.entailmentCheck( - options::TheoryOfMode::THEORY_OF_TYPE_BASED, - j == 0 ? ret : ret.negate()); - if (et.first) - { - ret = j == 0 ? d_true : d_false; - if (computeExp) - { - exp.push_back(et.second); - } - break; - } - } - } - } - } - } - } - // must have the term - if (reqHasTerm && !ret.isNull()) - { - Kind k = ret.getKind(); - if (k != OR && k != AND && k != EQUAL && k != ITE && k != NOT - && k != FORALL) - { - if (!d_qstate.hasTerm(ret)) - { - ret = Node::null(); - } - } - } - Trace("term-db-eval") << "evaluated term : " << n << ", got : " << ret - << ", reqHasTerm = " << reqHasTerm << std::endl; - // clear the explanation if failed - if (computeExp && ret.isNull()) - { - exp.resize(prevSize); - } - visited[n] = ret; - return ret; -} - -TNode TermDb::getEntailedTerm2(TNode n, - std::map<TNode, TNode>& subs, - bool subsRep, - bool hasSubs) -{ - Trace("term-db-entail") << "get entailed term : " << n << std::endl; - if (d_qstate.hasTerm(n)) - { - Trace("term-db-entail") << "...exists in ee, return rep " << std::endl; - return n; - }else if( n.getKind()==BOUND_VARIABLE ){ - if( hasSubs ){ - std::map< TNode, TNode >::iterator it = subs.find( n ); - if( it!=subs.end() ){ - Trace("term-db-entail") << "...substitution is : " << it->second << std::endl; - if( subsRep ){ - Assert(d_qstate.hasTerm(it->second)); - Assert(d_qstate.getRepresentative(it->second) == it->second); - return it->second; - } - return getEntailedTerm2(it->second, subs, subsRep, hasSubs); - } - } - }else if( n.getKind()==ITE ){ - for( unsigned i=0; i<2; i++ ){ - if (isEntailed2(n[0], subs, subsRep, hasSubs, i == 0)) - { - return getEntailedTerm2(n[i == 0 ? 1 : 2], subs, subsRep, hasSubs); - } - } - }else{ - if( n.hasOperator() ){ - TNode f = getMatchOperator( n ); - if( !f.isNull() ){ - std::vector< TNode > args; - for( unsigned i=0; i<n.getNumChildren(); i++ ){ - TNode c = getEntailedTerm2(n[i], subs, subsRep, hasSubs); - if( c.isNull() ){ - return TNode::null(); - } - c = d_qstate.getRepresentative(c); - Trace("term-db-entail") << " child " << i << " : " << c << std::endl; - args.push_back( c ); - } - TNode nn = getCongruentTerm(f, args); - Trace("term-db-entail") << " got congruent term " << nn << " for " << n << std::endl; - return nn; - } - } - } - return TNode::null(); -} - -Node TermDb::evaluateTerm(TNode n, - bool useEntailmentTests, - bool reqHasTerm) -{ - std::map< TNode, Node > visited; - std::vector<Node> exp; - return evaluateTerm2(n, visited, exp, useEntailmentTests, false, reqHasTerm); -} - -Node TermDb::evaluateTerm(TNode n, - std::vector<Node>& exp, - bool useEntailmentTests, - bool reqHasTerm) -{ - std::map<TNode, Node> visited; - return evaluateTerm2(n, visited, exp, useEntailmentTests, true, reqHasTerm); -} - -TNode TermDb::getEntailedTerm(TNode n, - std::map<TNode, TNode>& subs, - bool subsRep) -{ - return getEntailedTerm2(n, subs, subsRep, true); -} - -TNode TermDb::getEntailedTerm(TNode n) -{ - std::map< TNode, TNode > subs; - return getEntailedTerm2(n, subs, false, false); -} - -bool TermDb::isEntailed2( - TNode n, std::map<TNode, TNode>& subs, bool subsRep, bool hasSubs, bool pol) -{ - Trace("term-db-entail") << "Check entailed : " << n << ", pol = " << pol << std::endl; - Assert(n.getType().isBoolean()); - if( n.getKind()==EQUAL && !n[0].getType().isBoolean() ){ - TNode n1 = getEntailedTerm2(n[0], subs, subsRep, hasSubs); - if( !n1.isNull() ){ - TNode n2 = getEntailedTerm2(n[1], subs, subsRep, hasSubs); - if( !n2.isNull() ){ - if( n1==n2 ){ - return pol; - }else{ - Assert(d_qstate.hasTerm(n1)); - Assert(d_qstate.hasTerm(n2)); - if( pol ){ - return d_qstate.areEqual(n1, n2); - }else{ - return d_qstate.areDisequal(n1, n2); - } - } - } - } - }else if( n.getKind()==NOT ){ - return isEntailed2(n[0], subs, subsRep, hasSubs, !pol); - }else if( n.getKind()==OR || n.getKind()==AND ){ - bool simPol = ( pol && n.getKind()==OR ) || ( !pol && n.getKind()==AND ); - for( unsigned i=0; i<n.getNumChildren(); i++ ){ - if (isEntailed2(n[i], subs, subsRep, hasSubs, pol)) - { - if( simPol ){ - return true; - } - }else{ - if( !simPol ){ - return false; - } - } - } - return !simPol; - //Boolean equality here - }else if( n.getKind()==EQUAL || n.getKind()==ITE ){ - for( unsigned i=0; i<2; i++ ){ - if (isEntailed2(n[0], subs, subsRep, hasSubs, i == 0)) - { - unsigned ch = ( n.getKind()==EQUAL || i==0 ) ? 1 : 2; - bool reqPol = ( n.getKind()==ITE || i==0 ) ? pol : !pol; - return isEntailed2(n[ch], subs, subsRep, hasSubs, reqPol); - } - } - }else if( n.getKind()==APPLY_UF ){ - TNode n1 = getEntailedTerm2(n, subs, subsRep, hasSubs); - if( !n1.isNull() ){ - Assert(d_qstate.hasTerm(n1)); - if( n1==d_true ){ - return pol; - }else if( n1==d_false ){ - return !pol; - }else{ - return d_qstate.getRepresentative(n1) == (pol ? d_true : d_false); - } - } - }else if( n.getKind()==FORALL && !pol ){ - return isEntailed2(n[1], subs, subsRep, hasSubs, pol); - } - return false; -} - -bool TermDb::isEntailed(TNode n, bool pol) -{ - Assert(d_consistent_ee); - std::map< TNode, TNode > subs; - return isEntailed2(n, subs, false, false, pol); -} - -bool TermDb::isEntailed(TNode n, - std::map<TNode, TNode>& subs, - bool subsRep, - bool pol) -{ - Assert(d_consistent_ee); - return isEntailed2(n, subs, subsRep, true, pol); -} - bool TermDb::isTermActive( Node n ) { return d_inactive_map.find( n )==d_inactive_map.end(); //return !n.getAttribute(NoMatchAttribute()); diff --git a/src/theory/quantifiers/term_database.h b/src/theory/quantifiers/term_database.h index af0b87bd8..0e5c7bc01 100644 --- a/src/theory/quantifiers/term_database.h +++ b/src/theory/quantifiers/term_database.h @@ -176,78 +176,6 @@ class TermDb : public QuantifiersUtil { * equivalence class of r. */ bool inRelevantDomain(TNode f, unsigned i, TNode r); - /** evaluate term - * - * Returns a term n' such that n = n' is entailed based on the equality - * information ee. This function may generate new terms. In particular, - * we typically rewrite subterms of n of maximal size to terms that exist in - * the equality engine specified by ee. - * - * useEntailmentTests is whether to call the theory engine's entailmentTest - * on literals n for which this call fails to find a term n' that is - * equivalent to n, for increased precision. This is not frequently used. - * - * The vector exp stores the explanation for why n evaluates to that term, - * that is, if this call returns a non-null node n', then: - * exp => n = n' - * - * If reqHasTerm, then we require that the returned term is a Boolean - * combination of terms that exist in the equality engine used by this call. - * If no such term is constructable, this call returns null. The motivation - * for setting this to true is to "fail fast" if we require the return value - * of this function to only involve existing terms. This is used e.g. in - * the "propagating instances" portion of conflict-based instantiation - * (quant_conflict_find.h). - */ - Node evaluateTerm(TNode n, - std::vector<Node>& exp, - bool useEntailmentTests = false, - bool reqHasTerm = false); - /** same as above, without exp */ - Node evaluateTerm(TNode n, - bool useEntailmentTests = false, - bool reqHasTerm = false); - /** get entailed term - * - * If possible, returns a term n' such that: - * (1) n' exists in the current equality engine (as specified by the state), - * (2) n = n' is entailed in the current context. - * It returns null if no such term can be found. - * Wrt evaluateTerm, this version does not construct new terms, and - * thus is less aggressive. - */ - TNode getEntailedTerm(TNode n); - /** get entailed term - * - * If possible, returns a term n' such that: - * (1) n' exists in the current equality engine (as specified by the state), - * (2) n * subs = n' is entailed in the current context, where * denotes - * substitution application. - * It returns null if no such term can be found. - * subsRep is whether the substitution maps to terms that are representatives - * according to the quantifiers state. - * Wrt evaluateTerm, this version does not construct new terms, and - * thus is less aggressive. - */ - TNode getEntailedTerm(TNode n, std::map<TNode, TNode>& subs, bool subsRep); - /** is entailed - * Checks whether the current context entails n with polarity pol, based on - * the equality information in the quantifiers state. Returns true if the - * entailment can be successfully shown. - */ - bool isEntailed(TNode n, bool pol); - /** is entailed - * - * Checks whether the current context entails ( n * subs ) with polarity pol, - * based on the equality information in the quantifiers state, - * where * denotes substitution application. - * subsRep is whether the substitution maps to terms that are representatives - * according to in the quantifiers state. - */ - bool isEntailed(TNode n, - std::map<TNode, TNode>& subs, - bool subsRep, - bool pol); /** is the term n active in the current context? * * By default, all terms are active. A term is inactive if: @@ -355,24 +283,6 @@ class TermDb : public QuantifiersUtil { //----------------------------- end implementation-specific /** set has term */ void setHasTerm( Node n ); - /** helper for evaluate term */ - Node evaluateTerm2(TNode n, - std::map<TNode, Node>& visited, - std::vector<Node>& exp, - bool useEntailmentTests, - bool computeExp, - bool reqHasTerm); - /** helper for get entailed term */ - TNode getEntailedTerm2(TNode n, - std::map<TNode, TNode>& subs, - bool subsRep, - bool hasSubs); - /** helper for is entailed */ - bool isEntailed2(TNode n, - std::map<TNode, TNode>& subs, - bool subsRep, - bool hasSubs, - bool pol); /** compute uf eqc terms : * Ensure entries for f are in d_func_map_eqc_trie for all equivalence classes */ diff --git a/src/theory/quantifiers/term_registry.cpp b/src/theory/quantifiers/term_registry.cpp index ab999ad9b..d11fb0b8d 100644 --- a/src/theory/quantifiers/term_registry.cpp +++ b/src/theory/quantifiers/term_registry.cpp @@ -18,6 +18,7 @@ #include "options/base_options.h" #include "options/quantifiers_options.h" #include "options/smt_options.h" +#include "theory/quantifiers/entailment_check.h" #include "theory/quantifiers/first_order_model.h" #include "theory/quantifiers/fmf/first_order_model_fmc.h" #include "theory/quantifiers/ho_term_database.h" @@ -39,6 +40,7 @@ TermRegistry::TermRegistry(Env& env, d_termPools(new TermPools(env, qs)), d_termDb(logicInfo().isHigherOrder() ? new HoTermDb(env, qs, qr) : new TermDb(env, qs, qr)), + d_echeck(new EntailmentCheck(env, qs, *d_termDb.get())), d_sygusTdb(nullptr), d_qmodel(nullptr) { @@ -132,6 +134,11 @@ TermDbSygus* TermRegistry::getTermDatabaseSygus() const return d_sygusTdb.get(); } +EntailmentCheck* TermRegistry::getEntailmentCheck() const +{ + return d_echeck.get(); +} + TermEnumeration* TermRegistry::getTermEnumeration() const { return d_termEnum.get(); diff --git a/src/theory/quantifiers/term_registry.h b/src/theory/quantifiers/term_registry.h index 175d450df..60a87a91f 100644 --- a/src/theory/quantifiers/term_registry.h +++ b/src/theory/quantifiers/term_registry.h @@ -23,6 +23,7 @@ #include "context/cdhashset.h" #include "smt/env_obj.h" +#include "theory/quantifiers/entailment_check.h" #include "theory/quantifiers/sygus/term_database_sygus.h" #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/term_enumeration.h" @@ -83,6 +84,8 @@ class TermRegistry : protected EnvObj TermDb* getTermDatabase() const; /** get term database sygus */ TermDbSygus* getTermDatabaseSygus() const; + /** get entailment check utility */ + EntailmentCheck* getEntailmentCheck() const; /** get term enumeration utility */ TermEnumeration* getTermEnumeration() const; /** get the term pools utility */ @@ -103,6 +106,8 @@ class TermRegistry : protected EnvObj std::unique_ptr<TermPools> d_termPools; /** term database */ std::unique_ptr<TermDb> d_termDb; + /** entailment check */ + std::unique_ptr<EntailmentCheck> d_echeck; /** sygus term database */ std::unique_ptr<TermDbSygus> d_sygusTdb; /** extended model object */ diff --git a/src/theory/rewriter.cpp b/src/theory/rewriter.cpp index 6cb3bf3ff..361d90dcd 100644 --- a/src/theory/rewriter.cpp +++ b/src/theory/rewriter.cpp @@ -32,13 +32,6 @@ using namespace std; namespace cvc5 { namespace theory { -/** Attribute true for nodes that have been rewritten with proofs enabled */ -struct RewriteWithProofsAttributeId -{ -}; -typedef expr::Attribute<RewriteWithProofsAttributeId, bool> - RewriteWithProofsAttribute; - // Note that this function is a simplified version of Theory::theoryOf for // (type-based) theoryOfMode. We expand and simplify it here for the sake of // efficiency. @@ -173,7 +166,6 @@ Node Rewriter::rewriteTo(theory::TheoryId theoryId, Node node, TConvProofGenerator* tcpg) { - RewriteWithProofsAttribute rpfa; #ifdef CVC5_ASSERTIONS bool isEquality = node.getKind() == kind::EQUAL && (!node[0].getType().isBoolean()); @@ -187,7 +179,7 @@ Node Rewriter::rewriteTo(theory::TheoryId theoryId, // Check if it's been cached already Node cached = getPostRewriteCache(theoryId, node); - if (!cached.isNull() && (tcpg == nullptr || node.getAttribute(rpfa))) + if (!cached.isNull() && (tcpg == nullptr || hasRewrittenWithProofs(node))) { return cached; } @@ -217,7 +209,8 @@ Node Rewriter::rewriteTo(theory::TheoryId theoryId, cached = getPreRewriteCache(rewriteStackTop.getTheoryId(), rewriteStackTop.d_node); if (cached.isNull() - || (tcpg != nullptr && !rewriteStackTop.d_node.getAttribute(rpfa))) + || (tcpg != nullptr + && !hasRewrittenWithProofs(rewriteStackTop.d_node))) { // Rewrite until fix-point is reached for(;;) { @@ -256,7 +249,7 @@ Node Rewriter::rewriteTo(theory::TheoryId theoryId, rewriteStackTop.d_node); // If not, go through the children if (cached.isNull() - || (tcpg != nullptr && !rewriteStackTop.d_node.getAttribute(rpfa))) + || (tcpg != nullptr && !hasRewrittenWithProofs(rewriteStackTop.d_node))) { // The child we need to rewrite unsigned child = rewriteStackTop.d_nextChild++; @@ -343,7 +336,7 @@ Node Rewriter::rewriteTo(theory::TheoryId theoryId, if (tcpg != nullptr) { // if proofs are enabled, mark that we've rewritten with proofs - rewriteStackTop.d_original.setAttribute(rpfa, true); + d_tpgNodes.insert(rewriteStackTop.d_original); if (!cached.isNull()) { // We may have gotten a different node, due to non-determinism in @@ -474,5 +467,10 @@ void Rewriter::clearCaches() clearCachesInternal(); } +bool Rewriter::hasRewrittenWithProofs(TNode n) const +{ + return d_tpgNodes.find(n) != d_tpgNodes.end(); +} + } // namespace theory } // namespace cvc5 diff --git a/src/theory/rewriter.h b/src/theory/rewriter.h index f96062b61..d90af4a31 100644 --- a/src/theory/rewriter.h +++ b/src/theory/rewriter.h @@ -159,6 +159,11 @@ class Rewriter { void clearCachesInternal(); + /** + * Has n been rewritten with proofs? This checks if n is in d_tpgNodes. + */ + bool hasRewrittenWithProofs(TNode n) const; + /** The resource manager, for tracking resource usage */ ResourceManager* d_resourceManager; @@ -167,6 +172,12 @@ class Rewriter { /** The proof generator */ std::unique_ptr<TConvProofGenerator> d_tpg; + /** + * Nodes rewritten with proofs. Since d_tpg contains a reference to all + * nodes that have been rewritten with proofs, we can keep only a TNode + * here. + */ + std::unordered_set<TNode> d_tpgNodes; #ifdef CVC5_ASSERTIONS std::unique_ptr<std::unordered_set<Node>> d_rewriteStack = nullptr; #endif /* CVC5_ASSERTIONS */ diff --git a/src/theory/sets/normal_form.h b/src/theory/sets/normal_form.h index eb839e1c0..35d06a510 100644 --- a/src/theory/sets/normal_form.h +++ b/src/theory/sets/normal_form.h @@ -58,7 +58,7 @@ class NormalForm { } /** - * Returns true if n is considered a to be a (canonical) constant set value. + * Returns true if n is considered to be a (canonical) constant set value. * A canonical set value is one whose AST is: * (union (singleton c1) ... (union (singleton c_{n-1}) (singleton c_n)))) * where c1 ... cn are constants and the node identifier of these constants diff --git a/src/theory/smt_engine_subsolver.cpp b/src/theory/smt_engine_subsolver.cpp index 99e3f7768..d22bde370 100644 --- a/src/theory/smt_engine_subsolver.cpp +++ b/src/theory/smt_engine_subsolver.cpp @@ -16,6 +16,7 @@ #include "theory/smt_engine_subsolver.h" +#include "proof/unsat_core.h" #include "smt/env.h" #include "smt/solver_engine.h" #include "smt/solver_engine_scope.h" @@ -137,5 +138,41 @@ Result checkWithSubsolver(Node query, return r; } +void getModelFromSubsolver(SolverEngine& smt, + const std::vector<Node>& vars, + std::vector<Node>& vals) +{ + for (const Node& v : vars) + { + Node mv = smt.getValue(v); + vals.push_back(mv); + } +} + +bool getUnsatCoreFromSubsolver(SolverEngine& smt, + const std::unordered_set<Node>& queryAsserts, + std::vector<Node>& uasserts) +{ + UnsatCore uc = smt.getUnsatCore(); + bool hasQuery = false; + for (UnsatCore::const_iterator i = uc.begin(); i != uc.end(); ++i) + { + Node uassert = *i; + if (queryAsserts.find(uassert) != queryAsserts.end()) + { + hasQuery = true; + continue; + } + uasserts.push_back(uassert); + } + return hasQuery; +} + +void getUnsatCoreFromSubsolver(SolverEngine& smt, std::vector<Node>& uasserts) +{ + std::unordered_set<Node> queryAsserts; + getUnsatCoreFromSubsolver(smt, queryAsserts, uasserts); +} + } // namespace theory } // namespace cvc5 diff --git a/src/theory/smt_engine_subsolver.h b/src/theory/smt_engine_subsolver.h index 0e1af29db..5de65a5d2 100644 --- a/src/theory/smt_engine_subsolver.h +++ b/src/theory/smt_engine_subsolver.h @@ -113,6 +113,31 @@ Result checkWithSubsolver(Node query, bool needsTimeout = false, unsigned long timeout = 0); +//--------------- utilities + +/** + * Assuming smt has just been called to check-sat and returned "SAT", this + * method adds the model for d_vars to mvs. + */ +void getModelFromSubsolver(SolverEngine& smt, + const std::vector<Node>& vars, + std::vector<Node>& mvs); + +/** + * Assuming smt has just been called to check-sat and returned "UNSAT", this + * method get the unsat core and adds it to uasserts. + * + * The assertions in the argument queryAsserts (which we are not interested + * in tracking in the unsat core) are excluded from uasserts. + * If one of the formulas in queryAsserts was in the unsat core, then this + * method returns true. Otherwise, this method returns false. + */ +bool getUnsatCoreFromSubsolver(SolverEngine& smt, + const std::unordered_set<Node>& queryAsserts, + std::vector<Node>& uasserts); +/** Same as above, without query asserts */ +void getUnsatCoreFromSubsolver(SolverEngine& smt, std::vector<Node>& uasserts); + } // namespace theory } // namespace cvc5 diff --git a/src/theory/strings/base_solver.cpp b/src/theory/strings/base_solver.cpp index b675d2444..9396e3e87 100644 --- a/src/theory/strings/base_solver.cpp +++ b/src/theory/strings/base_solver.cpp @@ -17,6 +17,7 @@ #include "theory/strings/base_solver.h" #include "expr/sequence.h" +#include "options/quantifiers_options.h" #include "options/strings_options.h" #include "theory/rewriter.h" #include "theory/strings/theory_strings_utils.h" @@ -539,8 +540,41 @@ void BaseSolver::checkCardinalityType(TypeNode tn, // infinite cardinality, we are fine return; } - // TODO (cvc4-projects #23): how to handle sequence for finite types? - return; + // we check the cardinality class of the type, assuming that FMF is + // disabled. + if (isCardinalityClassFinite(etn.getCardinalityClass(), false)) + { + Cardinality c = etn.getCardinality(); + bool smallCardinality = false; + if (!c.isLargeFinite()) + { + Integer ci = c.getFiniteCardinality(); + if (ci.fitsUnsignedInt()) + { + smallCardinality = true; + typeCardSize = ci.toUnsignedInt(); + } + } + if (!smallCardinality) + { + // if it is large finite, then there is no way we could have + // constructed that many terms in memory, hence there is nothing + // to do. + return; + } + } + else + { + Assert(options().quantifiers.finiteModelFind); + // we are in a case where the cardinality of the type is infinite + // if not FMF, and finite given the Env's option value for FMF. In this + // case, FMF must be true, and the cardinality is finite and dynamic + // (i.e. it depends on the model's finite interpretation for uninterpreted + // sorts). We do not know how to handle this case, we set incomplete. + // TODO (cvc4-projects #23): how to handle sequence for finite types? + d_im.setIncomplete(IncompleteId::SEQ_FINITE_DYNAMIC_CARDINALITY); + return; + } } // for each collection for (unsigned i = 0, csize = cols.size(); i < csize; ++i) diff --git a/src/theory/strings/infer_proof_cons.cpp b/src/theory/strings/infer_proof_cons.cpp index 34597c8be..5eba8663a 100644 --- a/src/theory/strings/infer_proof_cons.cpp +++ b/src/theory/strings/infer_proof_cons.cpp @@ -59,6 +59,11 @@ void InferProofCons::notifyFact(const InferInfo& ii) d_lazyFactMap.insert(ii.d_conc, iic); } +void InferProofCons::notifyLemma(const InferInfo& ii) +{ + d_lazyFactMap[ii.d_conc] = std::make_shared<InferInfo>(ii); +} + bool InferProofCons::addProofTo(CDProof* pf, Node conc, InferenceId infer, diff --git a/src/theory/strings/infer_proof_cons.h b/src/theory/strings/infer_proof_cons.h index 110d231cf..02b266fd7 100644 --- a/src/theory/strings/infer_proof_cons.h +++ b/src/theory/strings/infer_proof_cons.h @@ -69,6 +69,11 @@ class InferProofCons : public ProofGenerator * only for facts that are explained. */ void notifyFact(const InferInfo& ii); + /** + * Same as above, but always overwrites. This is used for lemmas and + * conflicts, which do not necessarily have unique conclusions. + */ + void notifyLemma(const InferInfo& ii); /** * This returns the proof for fact. This is required for using this class as diff --git a/src/theory/strings/inference_manager.cpp b/src/theory/strings/inference_manager.cpp index 0e971fc54..1f531a97c 100644 --- a/src/theory/strings/inference_manager.cpp +++ b/src/theory/strings/inference_manager.cpp @@ -42,7 +42,8 @@ InferenceManager::InferenceManager(Env& env, d_termReg(tr), d_extt(e), d_statistics(statistics), - d_ipc(pnm ? new InferProofCons(context(), pnm, d_statistics) : nullptr) + d_ipc(pnm ? new InferProofCons(context(), pnm, d_statistics) : nullptr), + d_ipcl(pnm ? new InferProofCons(context(), pnm, d_statistics) : nullptr) { NodeManager* nm = NodeManager::currentNM(); d_zero = nm->mkConst(Rational(0)); @@ -279,12 +280,12 @@ void InferenceManager::processConflict(const InferInfo& ii) { Assert(!d_state.isInConflict()); // setup the fact to reproduce the proof in the call below - if (d_ipc != nullptr) + if (d_ipcl != nullptr) { - d_ipc->notifyFact(ii); + d_ipcl->notifyLemma(ii); } // make the trust node - TrustNode tconf = mkConflictExp(ii.d_premises, d_ipc.get()); + TrustNode tconf = mkConflictExp(ii.d_premises, d_ipcl.get()); Assert(tconf.getKind() == TrustNodeKind::CONFLICT); Trace("strings-assert") << "(assert (not " << tconf.getNode() << ")) ; conflict " << ii.getId() << std::endl; @@ -335,11 +336,11 @@ TrustNode InferenceManager::processLemma(InferInfo& ii, LemmaProperty& p) } // ensure that the proof generator is ready to explain the final conclusion // of the lemma (ii.d_conc). - if (d_ipc != nullptr) + if (d_ipcl != nullptr) { - d_ipc->notifyFact(ii); + d_ipcl->notifyLemma(ii); } - TrustNode tlem = mkLemmaExp(ii.d_conc, exp, noExplain, d_ipc.get()); + TrustNode tlem = mkLemmaExp(ii.d_conc, exp, noExplain, d_ipcl.get()); Trace("strings-pending") << "Process pending lemma : " << tlem.getNode() << std::endl; diff --git a/src/theory/strings/inference_manager.h b/src/theory/strings/inference_manager.h index 49f10d7cb..9f4cd1986 100644 --- a/src/theory/strings/inference_manager.h +++ b/src/theory/strings/inference_manager.h @@ -249,8 +249,15 @@ class InferenceManager : public InferenceManagerBuffered ExtTheory& d_extt; /** Reference to the statistics for the theory of strings/sequences. */ SequencesStatistics& d_statistics; - /** Conversion from inferences to proofs */ + /** Conversion from inferences to proofs for facts */ std::unique_ptr<InferProofCons> d_ipc; + /** + * Conversion from inferences to proofs for lemmas and conflicts. This is + * separate from the above proof generator to avoid rare cases where the + * conclusion of a lemma is a duplicate of the conclusion of another lemma, + * or is a fact in the current equality engine. + */ + std::unique_ptr<InferProofCons> d_ipcl; /** Common constants */ Node d_true; Node d_false; diff --git a/src/theory/strings/kinds b/src/theory/strings/kinds index aa95ef2f8..9faa935e1 100644 --- a/src/theory/strings/kinds +++ b/src/theory/strings/kinds @@ -56,6 +56,10 @@ enumerator STRING_TYPE \ "::cvc5::theory::strings::StringEnumerator" \ "theory/strings/type_enumerator.h" +enumerator REGEXP_TYPE \ + "::cvc5::theory::strings::RegExpEnumerator" \ + "theory/strings/regexp_enumerator.h" + constant CONST_STRING \ class \ String \ diff --git a/src/theory/strings/regexp_enumerator.cpp b/src/theory/strings/regexp_enumerator.cpp new file mode 100644 index 000000000..261d0008e --- /dev/null +++ b/src/theory/strings/regexp_enumerator.cpp @@ -0,0 +1,49 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Implementation of enumerator for regular expressions. + */ + +#include "theory/strings/regexp_enumerator.h" + +namespace cvc5 { +namespace theory { +namespace strings { + +RegExpEnumerator::RegExpEnumerator(TypeNode type, TypeEnumeratorProperties* tep) + : TypeEnumeratorBase<RegExpEnumerator>(type), d_senum(type, tep) +{ +} + +RegExpEnumerator::RegExpEnumerator(const RegExpEnumerator& enumerator) + : TypeEnumeratorBase<RegExpEnumerator>(enumerator.getType()), + d_senum(enumerator.d_senum) +{ +} + +Node RegExpEnumerator::operator*() +{ + NodeManager* nm = NodeManager::currentNM(); + return nm->mkNode(kind::STRING_TO_REGEXP, *d_senum); +} + +RegExpEnumerator& RegExpEnumerator::operator++() +{ + ++d_senum; + return *this; +} + +bool RegExpEnumerator::isFinished() { return d_senum.isFinished(); } + +} // namespace strings +} // namespace theory +} // namespace cvc5 diff --git a/src/theory/strings/regexp_enumerator.h b/src/theory/strings/regexp_enumerator.h new file mode 100644 index 000000000..289c8b046 --- /dev/null +++ b/src/theory/strings/regexp_enumerator.h @@ -0,0 +1,59 @@ +/****************************************************************************** + * Top contributors (to current version): + * Andrew Reynolds + * + * This file is part of the cvc5 project. + * + * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS + * in the top-level source directory and their institutional affiliations. + * All rights reserved. See the file COPYING in the top-level source + * directory for licensing information. + * **************************************************************************** + * + * Enumerators for regular expressions. + */ + +#include "cvc5_private.h" + +#ifndef CVC5__THEORY__STRINGS__REGEXP_ENUMERATOR_H +#define CVC5__THEORY__STRINGS__REGEXP_ENUMERATOR_H + +#include <vector> + +#include "expr/node.h" +#include "expr/type_node.h" +#include "theory/strings/type_enumerator.h" + +namespace cvc5 { +namespace theory { +namespace strings { + +/** + * Simple regular expression enumerator, generates only singleton language + * regular expressions from a string enumeration, in other words: + * (str.to_re s1) ... (str.to_re sn) .... + * where s1 ... sn ... is the enumeration for strings. + */ +class RegExpEnumerator : public TypeEnumeratorBase<RegExpEnumerator> +{ + public: + RegExpEnumerator(TypeNode type, TypeEnumeratorProperties* tep = nullptr); + RegExpEnumerator(const RegExpEnumerator& enumerator); + ~RegExpEnumerator() {} + /** get the current term */ + Node operator*() override; + /** increment */ + RegExpEnumerator& operator++() override; + /** is this enumerator finished? */ + bool isFinished() override; + + private: + /** underlying string enumerator */ + StringEnumerator d_senum; +}; + +} // namespace strings +} // namespace theory +} // namespace cvc5 + +#endif /* CVC5__THEORY__STRINGS__TYPE_ENUMERATOR_H */ diff --git a/src/theory/strings/regexp_solver.cpp b/src/theory/strings/regexp_solver.cpp index 614a5e6e0..898c0f1b7 100644 --- a/src/theory/strings/regexp_solver.cpp +++ b/src/theory/strings/regexp_solver.cpp @@ -546,7 +546,10 @@ bool RegExpSolver::checkPDerivative( { std::vector<Node> noExplain; noExplain.push_back(atom); - noExplain.push_back(x.eqNode(d_emptyString)); + if (x != d_emptyString) + { + noExplain.push_back(x.eqNode(d_emptyString)); + } std::vector<Node> iexp = nf_exp; iexp.insert(iexp.end(), noExplain.begin(), noExplain.end()); d_im.sendInference(iexp, noExplain, d_false, InferenceId::STRINGS_RE_DELTA_CONF); diff --git a/src/theory/term_registration_visitor.cpp b/src/theory/term_registration_visitor.cpp index 0ce07c867..27e75fd83 100644 --- a/src/theory/term_registration_visitor.cpp +++ b/src/theory/term_registration_visitor.cpp @@ -92,7 +92,6 @@ bool PreRegisterVisitor::alreadyVisited(TNode current, TNode parent) { || parent.getKind() == kind::SEP_STAR || parent.getKind() == kind::SEP_WAND || (parent.getKind() == kind::SEP_LABEL && current.getType().isBoolean()) - // parent.getKind() == kind::CARDINALITY_CONSTRAINT ) && current != parent) { @@ -193,25 +192,19 @@ void PreRegisterVisitor::preRegisterWithTheory(TheoryEngine* te, << "): adding " << id << std::endl; // This should never throw an exception, since theories should be // guaranteed to be initialized. - // These checks don't work with finite model finding, because it - // uses Rational constants to represent cardinality constraints, - // even though arithmetic isn't actually involved. - if (!options::finiteModelFind()) + if (!te->isTheoryEnabled(id)) { - if (!te->isTheoryEnabled(id)) - { - const LogicInfo& l = te->getLogicInfo(); - LogicInfo newLogicInfo = l.getUnlockedCopy(); - newLogicInfo.enableTheory(id); - newLogicInfo.lock(); - std::stringstream ss; - ss << "The logic was specified as " << l.getLogicString() - << ", which doesn't include " << id - << ", but found a term in that theory." << std::endl - << "You might want to extend your logic to " - << newLogicInfo.getLogicString() << std::endl; - throw LogicException(ss.str()); - } + const LogicInfo& l = te->getLogicInfo(); + LogicInfo newLogicInfo = l.getUnlockedCopy(); + newLogicInfo.enableTheory(id); + newLogicInfo.lock(); + std::stringstream ss; + ss << "The logic was specified as " << l.getLogicString() + << ", which doesn't include " << id + << ", but found a term in that theory." << std::endl + << "You might want to extend your logic to " + << newLogicInfo.getLogicString() << std::endl; + throw LogicException(ss.str()); } } // call the theory's preRegisterTerm method @@ -249,7 +242,6 @@ bool SharedTermsVisitor::alreadyVisited(TNode current, TNode parent) const { || parent.getKind() == kind::SEP_STAR || parent.getKind() == kind::SEP_WAND || (parent.getKind() == kind::SEP_LABEL && current.getType().isBoolean()) - // parent.getKind() == kind::CARDINALITY_CONSTRAINT ) && current != parent) { diff --git a/src/theory/theory_model.cpp b/src/theory/theory_model.cpp index 8c1a17fe1..a5ec0867a 100644 --- a/src/theory/theory_model.cpp +++ b/src/theory/theory_model.cpp @@ -14,6 +14,7 @@ */ #include "theory/theory_model.h" +#include "expr/cardinality_constraint.h" #include "expr/node_algorithm.h" #include "options/quantifiers_options.h" #include "options/smt_options.h" @@ -253,17 +254,12 @@ Node TheoryModel::getModelValue(TNode n) const // special cases if (ret.getKind() == kind::CARDINALITY_CONSTRAINT) { + const CardinalityConstraint& cc = + ret.getOperator().getConst<CardinalityConstraint>(); Debug("model-getvalue-debug") - << "get cardinality constraint " << ret[0].getType() << std::endl; - ret = nm->mkConst(getCardinality(ret[0].getType()).getFiniteCardinality() - <= ret[1].getConst<Rational>().getNumerator()); - } - else if (ret.getKind() == kind::CARDINALITY_VALUE) - { - Debug("model-getvalue-debug") - << "get cardinality value " << ret[0].getType() << std::endl; - ret = nm->mkConst( - Rational(getCardinality(ret[0].getType()).getFiniteCardinality())); + << "get cardinality constraint " << cc.getType() << std::endl; + ret = nm->mkConst(getCardinality(cc.getType()).getFiniteCardinality() + <= cc.getUpperBound()); } // if the value was constant, we return it. If it was non-constant, // we only return it if we an evaluated kind. This can occur if the diff --git a/src/theory/theory_model_builder.cpp b/src/theory/theory_model_builder.cpp index 33dbe9ffa..1a0549a0a 100644 --- a/src/theory/theory_model_builder.cpp +++ b/src/theory/theory_model_builder.cpp @@ -224,7 +224,7 @@ bool TheoryEngineModelBuilder::isExcludedCdtValue( { Trace("model-builder-debug") << " ...matches with " << eqc << " -> " << eqc_m << std::endl; - if (eqc_m.getKind() == kind::UNINTERPRETED_CONSTANT) + if (eqc_m.getKind() == kind::CODATATYPE_BOUND_VARIABLE) { Trace("model-builder-debug") << "*** " << val << " is excluded datatype for " << eqc diff --git a/src/theory/uf/cardinality_extension.cpp b/src/theory/uf/cardinality_extension.cpp index 3c71cdbb9..650ef1d70 100644 --- a/src/theory/uf/cardinality_extension.cpp +++ b/src/theory/uf/cardinality_extension.cpp @@ -17,6 +17,7 @@ #include <sstream> +#include "expr/cardinality_constraint.h" #include "expr/skolem_manager.h" #include "options/smt_options.h" #include "options/uf_options.h" @@ -447,26 +448,28 @@ void Region::debugPrint( const char* c, bool incClique ) { } SortModel::CardinalityDecisionStrategy::CardinalityDecisionStrategy( - Env& env, Node t, Valuation valuation) - : DecisionStrategyFmf(env, valuation), d_cardinality_term(t) + Env& env, TypeNode type, Valuation valuation) + : DecisionStrategyFmf(env, valuation), d_type(type) { } + Node SortModel::CardinalityDecisionStrategy::mkLiteral(unsigned i) { NodeManager* nm = NodeManager::currentNM(); - return nm->mkNode( - CARDINALITY_CONSTRAINT, d_cardinality_term, nm->mkConst(Rational(i + 1))); + Node cco = nm->mkConst(CardinalityConstraint(d_type, Integer(i + 1))); + return nm->mkNode(CARDINALITY_CONSTRAINT, cco); } + std::string SortModel::CardinalityDecisionStrategy::identify() const { return std::string("uf_card"); } -SortModel::SortModel(Node n, +SortModel::SortModel(TypeNode tn, TheoryState& state, TheoryInferenceManager& im, CardinalityExtension* thss) - : d_type(n.getType()), + : d_type(tn), d_state(state), d_im(im), d_thss(thss), @@ -481,7 +484,6 @@ SortModel::SortModel(Node n, d_initialized(thss->userContext(), false), d_c_dec_strat(nullptr) { - d_cardinality_term = n; if (options::ufssMode() == options::UfssMode::FULL) { @@ -489,7 +491,7 @@ SortModel::SortModel(Node n, // We are guaranteed that the decision manager is ready since we // construct this module during TheoryUF::finishInit. d_c_dec_strat.reset(new CardinalityDecisionStrategy( - thss->d_env, n, thss->getTheory()->getValuation())); + thss->d_env, d_type, thss->getTheory()->getValuation())); } } @@ -1342,72 +1344,80 @@ void CardinalityExtension::assertNode(Node n, bool isDecision) if (options::ufssMode() == options::UfssMode::FULL) { if( lit.getKind()==CARDINALITY_CONSTRAINT ){ - TypeNode tn = lit[0].getType(); + const CardinalityConstraint& cc = + lit.getOperator().getConst<CardinalityConstraint>(); + TypeNode tn = cc.getType(); Assert(tn.isSort()); Assert(d_rep_model[tn]); - uint32_t nCard = - lit[1].getConst<Rational>().getNumerator().getUnsignedInt(); - Node ct = d_rep_model[tn]->getCardinalityTerm(); - Trace("uf-ss-debug") << "...check cardinality terms : " << lit[0] << " " << ct << std::endl; - if( lit[0]==ct ){ - if( options::ufssFairnessMonotone() ){ - SortInference* si = d_state.getSortInference(); - Trace("uf-ss-com-card-debug") << "...set master/slave" << std::endl; - if( tn!=d_tn_mono_master ){ - std::map< TypeNode, bool >::iterator it = d_tn_mono_slave.find( tn ); - if( it==d_tn_mono_slave.end() ){ - bool isMonotonic; - if (si != nullptr) + uint32_t nCard = cc.getUpperBound().getUnsignedInt(); + Trace("uf-ss-debug") << "...check cardinality constraint : " << tn + << std::endl; + if (options::ufssFairnessMonotone()) + { + SortInference* si = d_state.getSortInference(); + Trace("uf-ss-com-card-debug") << "...set master/slave" << std::endl; + if (tn != d_tn_mono_master) + { + std::map<TypeNode, bool>::iterator it = d_tn_mono_slave.find(tn); + if (it == d_tn_mono_slave.end()) + { + bool isMonotonic; + if (si != nullptr) + { + isMonotonic = si->isMonotonic(tn); + } + else + { + // if ground, everything is monotonic + isMonotonic = true; + } + if (isMonotonic) + { + if (d_tn_mono_master.isNull()) { - isMonotonic = si->isMonotonic(tn); - }else{ - //if ground, everything is monotonic - isMonotonic = true; + Trace("uf-ss-com-card-debug") + << "uf-ss-fair-monotone: Set master : " << tn << std::endl; + d_tn_mono_master = tn; } - if( isMonotonic ){ - if( d_tn_mono_master.isNull() ){ - Trace("uf-ss-com-card-debug") << "uf-ss-fair-monotone: Set master : " << tn << std::endl; - d_tn_mono_master = tn; - }else{ - Trace("uf-ss-com-card-debug") << "uf-ss-fair-monotone: Set slave : " << tn << std::endl; - d_tn_mono_slave[tn] = true; - } - }else{ - Trace("uf-ss-com-card-debug") << "uf-ss-fair-monotone: Set non-monotonic : " << tn << std::endl; - d_tn_mono_slave[tn] = false; + else + { + Trace("uf-ss-com-card-debug") + << "uf-ss-fair-monotone: Set slave : " << tn << std::endl; + d_tn_mono_slave[tn] = true; } } - } - //set the minimum positive cardinality for master if necessary - if( polarity && tn==d_tn_mono_master ){ - Trace("uf-ss-com-card-debug") << "...set min positive cardinality" << std::endl; - if (!d_min_pos_tn_master_card_set.get() - || nCard < d_min_pos_tn_master_card.get()) + else { - d_min_pos_tn_master_card_set.set(true); - d_min_pos_tn_master_card.set( nCard ); + Trace("uf-ss-com-card-debug") + << "uf-ss-fair-monotone: Set non-monotonic : " << tn + << std::endl; + d_tn_mono_slave[tn] = false; } } } - Trace("uf-ss-com-card-debug") << "...assert cardinality" << std::endl; - d_rep_model[tn]->assertCardinality(nCard, polarity); - //check if combined cardinality is violated - checkCombinedCardinality(); - }else{ - //otherwise, make equal via lemma - if( d_card_assertions_eqv_lemma.find( lit )==d_card_assertions_eqv_lemma.end() ){ - Node eqv_lit = NodeManager::currentNM()->mkNode( CARDINALITY_CONSTRAINT, ct, lit[1] ); - eqv_lit = lit.eqNode( eqv_lit ); - Trace("uf-ss-lemma") << "*** Cardinality equiv lemma : " << eqv_lit << std::endl; - d_im.lemma(eqv_lit, InferenceId::UF_CARD_EQUIV); - d_card_assertions_eqv_lemma[lit] = true; + // set the minimum positive cardinality for master if necessary + if (polarity && tn == d_tn_mono_master) + { + Trace("uf-ss-com-card-debug") + << "...set min positive cardinality" << std::endl; + if (!d_min_pos_tn_master_card_set.get() + || nCard < d_min_pos_tn_master_card.get()) + { + d_min_pos_tn_master_card_set.set(true); + d_min_pos_tn_master_card.set(nCard); + } } } + Trace("uf-ss-com-card-debug") << "...assert cardinality" << std::endl; + d_rep_model[tn]->assertCardinality(nCard, polarity); + // check if combined cardinality is violated + checkCombinedCardinality(); }else if( lit.getKind()==COMBINED_CARDINALITY_CONSTRAINT ){ if( polarity ){ //safe to assume int here - uint32_t nCard = - lit[0].getConst<Rational>().getNumerator().getUnsignedInt(); + const CombinedCardinalityConstraint& cc = + lit.getOperator().getConst<CombinedCardinalityConstraint>(); + uint32_t nCard = cc.getUpperBound().getUnsignedInt(); if (!d_min_pos_com_card_set.get() || nCard < d_min_pos_com_card.get()) { d_min_pos_com_card_set.set(true); @@ -1570,7 +1580,8 @@ Node CardinalityExtension::CombinedCardinalityDecisionStrategy::mkLiteral( unsigned i) { NodeManager* nm = NodeManager::currentNM(); - return nm->mkNode(COMBINED_CARDINALITY_CONSTRAINT, nm->mkConst(Rational(i))); + Node cco = nm->mkConst(CombinedCardinalityConstraint(Integer(i))); + return nm->mkNode(COMBINED_CARDINALITY_CONSTRAINT, cco); } std::string @@ -1581,31 +1592,52 @@ CardinalityExtension::CombinedCardinalityDecisionStrategy::identify() const void CardinalityExtension::preRegisterTerm(TNode n) { - if (options::ufssMode() == options::UfssMode::FULL) + if (options::ufssMode() != options::UfssMode::FULL) { - //initialize combined cardinality - initializeCombinedCardinality(); - - Trace("uf-ss-register") << "Preregister " << n << "." << std::endl; - //shouldn't have to preregister this type (it may be that there are no quantifiers over tn) - TypeNode tn = n.getType(); - std::map< TypeNode, SortModel* >::iterator it = d_rep_model.find( tn ); - if( it==d_rep_model.end() ){ - SortModel* rm = NULL; - if( tn.isSort() ){ - Trace("uf-ss-register") << "Create sort model " << tn << "." << std::endl; - rm = new SortModel(n, d_state, d_im, this); - } - if( rm ){ - rm->initialize(); - d_rep_model[tn] = rm; - //d_rep_model_init[tn] = true; - } - }else{ - //ensure sort model is initialized - it->second->initialize(); + return; + } + // initialize combined cardinality + initializeCombinedCardinality(); + + Trace("uf-ss-register") << "Preregister " << n << "." << std::endl; + // shouldn't have to preregister this type (it may be that there are no + // quantifiers over tn) + TypeNode tn; + if (n.getKind() == CARDINALITY_CONSTRAINT) + { + const CardinalityConstraint& cc = + n.getOperator().getConst<CardinalityConstraint>(); + tn = cc.getType(); + } + else + { + tn = n.getType(); + } + if (!tn.isSort()) + { + return; + } + std::map<TypeNode, SortModel*>::iterator it = d_rep_model.find(tn); + if (it == d_rep_model.end()) + { + SortModel* rm = nullptr; + if (tn.isSort()) + { + Trace("uf-ss-register") << "Create sort model " << tn << "." << std::endl; + rm = new SortModel(tn, d_state, d_im, this); + } + if (rm) + { + rm->initialize(); + d_rep_model[tn] = rm; + // d_rep_model_init[tn] = true; } } + else + { + // ensure sort model is initialized + it->second->initialize(); + } } SortModel* CardinalityExtension::getSortModel(Node n) diff --git a/src/theory/uf/cardinality_extension.h b/src/theory/uf/cardinality_extension.h index 10e9ceb44..4b989a166 100644 --- a/src/theory/uf/cardinality_extension.h +++ b/src/theory/uf/cardinality_extension.h @@ -265,8 +265,6 @@ class CardinalityExtension : protected EnvObj void addCliqueLemma(std::vector<Node>& clique); /** cardinality */ context::CDO<uint32_t> d_cardinality; - /** cardinality lemma term */ - Node d_cardinality_term; /** cardinality literals */ std::map<uint32_t, Node> d_cardinality_literal; /** whether a positive cardinality constraint has been asserted */ @@ -283,7 +281,7 @@ class CardinalityExtension : protected EnvObj void simpleCheckCardinality(); public: - SortModel(Node n, + SortModel(TypeNode tn, TheoryState& state, TheoryInferenceManager& im, CardinalityExtension* thss); @@ -309,7 +307,7 @@ class CardinalityExtension : protected EnvObj /** has cardinality */ bool hasCardinalityAsserted() const { return d_hasCard; } /** get cardinality term */ - Node getCardinalityTerm() const { return d_cardinality_term; } + TypeNode getType() const { return d_type; } /** get cardinality literal */ Node getCardinalityLiteral(uint32_t c); /** get maximum negative cardinality */ @@ -341,15 +339,14 @@ class CardinalityExtension : protected EnvObj class CardinalityDecisionStrategy : public DecisionStrategyFmf { public: - CardinalityDecisionStrategy(Env& env, Node t, Valuation valuation); + CardinalityDecisionStrategy(Env& env, TypeNode type, Valuation valuation); /** make literal (the i^th combined cardinality literal) */ Node mkLiteral(unsigned i) override; /** identify */ std::string identify() const override; - private: - /** the cardinality term */ - Node d_cardinality_term; + /** The type we are considering cardinality constraints for */ + TypeNode d_type; }; /** cardinality decision strategy */ std::unique_ptr<CardinalityDecisionStrategy> d_c_dec_strat; diff --git a/src/theory/uf/ho_extension.cpp b/src/theory/uf/ho_extension.cpp index 096a47c86..fd7cd467e 100644 --- a/src/theory/uf/ho_extension.cpp +++ b/src/theory/uf/ho_extension.cpp @@ -43,13 +43,16 @@ HoExtension::HoExtension(Env& env, Node HoExtension::ppRewrite(Node node) { // convert HO_APPLY to APPLY_UF if fully applied - if (node[0].getType().getNumChildren() == 2) + if (node.getKind() == HO_APPLY) { - Trace("uf-ho") << "uf-ho : expanding definition : " << node << std::endl; - Node ret = getApplyUfForHoApply(node); - Trace("uf-ho") << "uf-ho : ppRewrite : " << node << " to " << ret - << std::endl; - return ret; + if (node[0].getType().getNumChildren() == 2) + { + Trace("uf-ho") << "uf-ho : expanding definition : " << node << std::endl; + Node ret = getApplyUfForHoApply(node); + Trace("uf-ho") << "uf-ho : ppRewrite : " << node << " to " << ret + << std::endl; + return ret; + } } return node; } diff --git a/src/theory/uf/kinds b/src/theory/uf/kinds index e4b946105..0faa5c672 100644 --- a/src/theory/uf/kinds +++ b/src/theory/uf/kinds @@ -17,19 +17,32 @@ typerule APPLY_UF ::cvc5::theory::uf::UfTypeRule variable BOOLEAN_TERM_VARIABLE "Boolean term variable" -operator CARDINALITY_CONSTRAINT 2 "cardinality constraint on sort S: first parameter is (any) term of sort S, second is a positive integer constant k that bounds the cardinality of S" -typerule CARDINALITY_CONSTRAINT ::cvc5::theory::uf::CardinalityConstraintTypeRule - -operator COMBINED_CARDINALITY_CONSTRAINT 1 "combined cardinality constraint; parameter is a positive integer constant k that bounds the sum of the cardinalities of all sorts in the signature" -typerule COMBINED_CARDINALITY_CONSTRAINT ::cvc5::theory::uf::CombinedCardinalityConstraintTypeRule - parameterized PARTIAL_APPLY_UF APPLY_UF 1: "partial uninterpreted function application" typerule PARTIAL_APPLY_UF ::cvc5::theory::uf::PartialTypeRule -operator CARDINALITY_VALUE 1 "cardinality value of sort S: first parameter is (any) term of sort S" -typerule CARDINALITY_VALUE ::cvc5::theory::uf::CardinalityValueTypeRule - operator HO_APPLY 2 "higher-order (partial) function application" typerule HO_APPLY ::cvc5::theory::uf::HoApplyTypeRule +constant CARDINALITY_CONSTRAINT_OP \ + class \ + CardinalityConstraint \ + ::cvc5::CardinalityConstraintHashFunction \ + "expr/cardinality_constraint.h" \ + "the empty set constant; payload is an instance of the cvc5::CardinalityConstraint class" +parameterized CARDINALITY_CONSTRAINT CARDINALITY_CONSTRAINT_OP 0 "a fixed upper bound on the cardinality of an uninterpreted sort" + +typerule CARDINALITY_CONSTRAINT_OP ::cvc5::theory::uf::CardinalityConstraintOpTypeRule +typerule CARDINALITY_CONSTRAINT ::cvc5::theory::uf::CardinalityConstraintTypeRule + +constant COMBINED_CARDINALITY_CONSTRAINT_OP \ + class \ + CombinedCardinalityConstraint \ + ::cvc5::CombinedCardinalityConstraintHashFunction \ + "expr/cardinality_constraint.h" \ + "the empty set constant; payload is an instance of the cvc5::CombinedCardinalityConstraint class" +parameterized COMBINED_CARDINALITY_CONSTRAINT COMBINED_CARDINALITY_CONSTRAINT_OP 0 "a fixed upper bound on the sum of cardinalities of uninterpreted sorts" + +typerule COMBINED_CARDINALITY_CONSTRAINT_OP ::cvc5::theory::uf::CombinedCardinalityConstraintOpTypeRule +typerule COMBINED_CARDINALITY_CONSTRAINT ::cvc5::theory::uf::CombinedCardinalityConstraintTypeRule + endtheory diff --git a/src/theory/uf/symmetry_breaker.cpp b/src/theory/uf/symmetry_breaker.cpp index aa5d9d953..da75d0eea 100644 --- a/src/theory/uf/symmetry_breaker.cpp +++ b/src/theory/uf/symmetry_breaker.cpp @@ -79,12 +79,11 @@ bool SymmetryBreaker::Template::matchRecursive(TNode t, TNode n) { } if(t.getNumChildren() == 0) { - if(t.isConst()) { - Assert(n.isConst()); - Debug("ufsymm:match") << "UFSYMM we have constants, failing match" << endl; + if (!t.isVar()) + { + Debug("ufsymm:match") << "UFSYMM non-variables, failing match" << endl; return false; } - Assert(t.isVar() && n.isVar()); t = find(t); n = find(n); Debug("ufsymm:match") << "UFSYMM variable match " << t << " , " << n << endl; diff --git a/src/theory/uf/theory_uf.cpp b/src/theory/uf/theory_uf.cpp index cd974d3e4..a76591b6e 100644 --- a/src/theory/uf/theory_uf.cpp +++ b/src/theory/uf/theory_uf.cpp @@ -212,7 +212,7 @@ TrustNode TheoryUF::ppRewrite(TNode node, std::vector<SkolemLemma>& lems) Trace("uf-exp-def") << "TheoryUF::ppRewrite: expanding definition : " << node << std::endl; Kind k = node.getKind(); - if (k == kind::HO_APPLY) + if (k == kind::HO_APPLY || (node.isVar() && node.getType().isFunction())) { if (!logicInfo().isHigherOrder()) { @@ -251,7 +251,8 @@ void TheoryUF::preRegisterTerm(TNode node) { Debug("uf") << "TheoryUF::preRegisterTerm(" << node << ")" << std::endl; - if (d_thss != NULL) { + if (d_thss != nullptr) + { d_thss->preRegisterTerm(node); } diff --git a/src/theory/uf/theory_uf_type_rules.cpp b/src/theory/uf/theory_uf_type_rules.cpp index e95c8963e..5b132fc27 100644 --- a/src/theory/uf/theory_uf_type_rules.cpp +++ b/src/theory/uf/theory_uf_type_rules.cpp @@ -18,6 +18,7 @@ #include <climits> #include <sstream> +#include "expr/cardinality_constraint.h" #include "util/rational.h" namespace cvc5 { @@ -63,33 +64,19 @@ TypeNode UfTypeRule::computeType(NodeManager* nodeManager, TNode n, bool check) return fType.getRangeType(); } -TypeNode CardinalityConstraintTypeRule::computeType(NodeManager* nodeManager, - TNode n, - bool check) +TypeNode CardinalityConstraintOpTypeRule::computeType(NodeManager* nodeManager, + TNode n, + bool check) { if (check) { - // don't care what it is, but it should be well-typed - n[0].getType(check); - - TypeNode valType = n[1].getType(check); - if (valType != nodeManager->integerType()) - { - throw TypeCheckingExceptionPrivate( - n, "cardinality constraint must be integer"); - } - if (n[1].getKind() != kind::CONST_RATIONAL) - { - throw TypeCheckingExceptionPrivate( - n, "cardinality constraint must be a constant"); - } - cvc5::Rational r(INT_MAX); - if (n[1].getConst<Rational>() > r) + const CardinalityConstraint& cc = n.getConst<CardinalityConstraint>(); + if (!cc.getType().isSort()) { throw TypeCheckingExceptionPrivate( - n, "Exceeded INT_MAX in cardinality constraint"); + n, "cardinality constraint must apply to uninterpreted sort"); } - if (n[1].getConst<Rational>().getNumerator().sgn() != 1) + if (cc.getUpperBound().sgn() != 1) { throw TypeCheckingExceptionPrivate( n, "cardinality constraint must be positive"); @@ -98,37 +85,35 @@ TypeNode CardinalityConstraintTypeRule::computeType(NodeManager* nodeManager, return nodeManager->booleanType(); } -TypeNode CombinedCardinalityConstraintTypeRule::computeType( +TypeNode CardinalityConstraintTypeRule::computeType(NodeManager* nodeManager, + TNode n, + bool check) +{ + return nodeManager->booleanType(); +} + +TypeNode CombinedCardinalityConstraintOpTypeRule::computeType( NodeManager* nodeManager, TNode n, bool check) { if (check) { - TypeNode valType = n[0].getType(check); - if (valType != nodeManager->integerType()) + const CombinedCardinalityConstraint& cc = + n.getConst<CombinedCardinalityConstraint>(); + if (cc.getUpperBound().sgn() != 1) { throw TypeCheckingExceptionPrivate( - n, "combined cardinality constraint must be integer"); - } - if (n[0].getKind() != kind::CONST_RATIONAL) - { - throw TypeCheckingExceptionPrivate( - n, "combined cardinality constraint must be a constant"); - } - cvc5::Rational r(INT_MAX); - if (n[0].getConst<Rational>() > r) - { - throw TypeCheckingExceptionPrivate( - n, "Exceeded INT_MAX in combined cardinality constraint"); - } - if (n[0].getConst<Rational>().getNumerator().sgn() == -1) - { - throw TypeCheckingExceptionPrivate( - n, "combined cardinality constraint must be non-negative"); + n, "combined cardinality constraint must be positive"); } } return nodeManager->booleanType(); } +TypeNode CombinedCardinalityConstraintTypeRule::computeType( + NodeManager* nodeManager, TNode n, bool check) +{ + return nodeManager->booleanType(); +} + TypeNode PartialTypeRule::computeType(NodeManager* nodeManager, TNode n, bool check) @@ -136,17 +121,6 @@ TypeNode PartialTypeRule::computeType(NodeManager* nodeManager, return n.getOperator().getType().getRangeType(); } -TypeNode CardinalityValueTypeRule::computeType(NodeManager* nodeManager, - TNode n, - bool check) -{ - if (check) - { - n[0].getType(check); - } - return nodeManager->integerType(); -} - TypeNode HoApplyTypeRule::computeType(NodeManager* nodeManager, TNode n, bool check) diff --git a/src/theory/uf/theory_uf_type_rules.h b/src/theory/uf/theory_uf_type_rules.h index d786f5f24..b9451a500 100644 --- a/src/theory/uf/theory_uf_type_rules.h +++ b/src/theory/uf/theory_uf_type_rules.h @@ -37,19 +37,25 @@ class CardinalityConstraintTypeRule static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check); }; +class CardinalityConstraintOpTypeRule +{ + public: + static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check); +}; + class CombinedCardinalityConstraintTypeRule { public: static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check); }; -class PartialTypeRule +class CombinedCardinalityConstraintOpTypeRule { public: static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check); }; -class CardinalityValueTypeRule +class PartialTypeRule { public: static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check); diff --git a/src/util/safe_print.h b/src/util/safe_print.h index e3280bb55..0bc2b0599 100644 --- a/src/util/safe_print.h +++ b/src/util/safe_print.h @@ -47,12 +47,17 @@ namespace cvc5 { +template <size_t N> +void CVC5_EXPORT safe_print(int fd, const char (&msg)[N]); +template <typename T> +void CVC5_EXPORT safe_print(int fd, const T& obj); + /** * Prints arrays of chars (e.g. string literals) of length N. Safe to use in a * signal handler. */ template <size_t N> -void CVC5_EXPORT safe_print(int fd, const char (&msg)[N]) +void safe_print(int fd, const char (&msg)[N]) { ssize_t nb = N - 1; if (write(fd, msg, nb) != nb) { @@ -96,7 +101,7 @@ auto toStringImpl(const T& obj, int) -> decltype(toString(obj)) * @param obj The object to print */ template <typename T> -void CVC5_EXPORT safe_print(int fd, const T& obj) +void safe_print(int fd, const T& obj) { const char* s = toStringImpl(obj, /* prefer the method that uses `toString()` */ 0); diff --git a/src/util/smt2_quote_string.cpp b/src/util/smt2_quote_string.cpp index 82750c48b..e2ab4a74c 100644 --- a/src/util/smt2_quote_string.cpp +++ b/src/util/smt2_quote_string.cpp @@ -42,4 +42,16 @@ std::string quoteSymbol(const std::string& s){ return s; } +std::string quoteString(const std::string& s) { + // escape all double-quotes + std::string output = s; + size_t pos = 0; + while ((pos = output.find('"', pos)) != std::string::npos) + { + output.replace(pos, 1, "\"\""); + pos += 2; + } + return '"' + output + '"'; +} + } // namespace cvc5 diff --git a/src/util/smt2_quote_string.h b/src/util/smt2_quote_string.h index 98b2f89dd..f8be53c0c 100644 --- a/src/util/smt2_quote_string.h +++ b/src/util/smt2_quote_string.h @@ -27,6 +27,11 @@ namespace cvc5 { */ std::string quoteSymbol(const std::string& s); +/** + * SMT-LIB 2 quoting for strings + */ +std::string quoteString(const std::string& s); + } // namespace cvc5 #endif /* CVC5__UTIL__SMT2_QUOTE_STRING_H */ diff --git a/test/api/issue6111.cpp b/test/api/issue6111.cpp index bd7be2ad0..c0366ce23 100644 --- a/test/api/issue6111.cpp +++ b/test/api/issue6111.cpp @@ -27,7 +27,7 @@ int main() solver.setLogic("QF_BV"); Sort bvsort12979 = solver.mkBitVectorSort(12979); Term input2_1 = solver.mkConst(bvsort12979, "intpu2_1"); - Term zero = solver.mkBitVector(bvsort12979.getBVSize(), "0", 10); + Term zero = solver.mkBitVector(bvsort12979.getBitVectorSize(), "0", 10); vector<Term> args1; args1.push_back(zero); diff --git a/test/python/unit/api/test_op.py b/test/python/unit/api/test_op.py index 07a57a5c6..5126a481d 100644 --- a/test/python/unit/api/test_op.py +++ b/test/python/unit/api/test_op.py @@ -86,6 +86,9 @@ def test_get_num_indices(solver): assert 2 == floatingpoint_to_fp_generic.getNumIndices() assert 2 == regexp_loop.getNumIndices() +def test_op_indices_list(solver): + with_list = solver.mkOp(kinds.TupleProject, [4, 25]) + assert 2 == with_list.getNumIndices() def test_get_indices_string(solver): x = Op(solver) diff --git a/test/python/unit/api/test_solver.py b/test/python/unit/api/test_solver.py index db49f8bea..6052a057f 100644 --- a/test/python/unit/api/test_solver.py +++ b/test/python/unit/api/test_solver.py @@ -381,15 +381,6 @@ def test_mk_rounding_mode(solver): solver.mkRoundingMode(pycvc5.RoundTowardZero) -def test_mk_uninterpreted_const(solver): - solver.mkUninterpretedConst(solver.getBooleanSort(), 1) - with pytest.raises(RuntimeError): - solver.mkUninterpretedConst(pycvc5.Sort(solver), 1) - slv = pycvc5.Solver() - with pytest.raises(RuntimeError): - slv.mkUninterpretedConst(solver.getBooleanSort(), 1) - - def test_mk_abstract_value(solver): solver.mkAbstractValue("1") with pytest.raises(ValueError): @@ -653,6 +644,10 @@ def test_mk_regexp_sigma(solver): solver.mkTerm(kinds.StringInRegexp, s, solver.mkRegexpSigma()) +def test_mk_sep_emp(solver): + solver.mkSepEmp(); + + def test_mk_sep_nil(solver): solver.mkSepNil(solver.getBooleanSort()) with pytest.raises(RuntimeError): diff --git a/test/python/unit/api/test_sort.py b/test/python/unit/api/test_sort.py index def539cf4..98cf76d76 100644 --- a/test/python/unit/api/test_sort.py +++ b/test/python/unit/api/test_sort.py @@ -60,6 +60,11 @@ def test_operators_comparison(solver): solver.getIntegerSort() > Sort(solver) solver.getIntegerSort() >= Sort(solver) +def test_is_null(solver): + x = Sort(solver) + assert x.isNull() + x = solver.getBooleanSort() + assert not x.isNull() def test_is_boolean(solver): assert solver.getBooleanSort().isBoolean() @@ -140,6 +145,12 @@ def test_is_tester(solver): assert cons_sort.isTester() Sort(solver).isTester() +def test_is_updater(solver): + dt_sort = create_datatype_sort(solver) + dt = dt_sort.getDatatype() + updater_sort = dt[0][0].getUpdaterTerm().getSort() + assert updater_sort.isUpdater() + Sort(solver).isUpdater() def test_is_function(solver): fun_sort = solver.mkFunctionSort(solver.getRealSort(), @@ -438,26 +449,26 @@ def test_get_uninterpreted_sort_constructor_arity(solver): def test_get_bv_size(solver): bvSort = solver.mkBitVectorSort(32) - bvSort.getBVSize() + bvSort.getBitVectorSize() setSort = solver.mkSetSort(solver.getIntegerSort()) with pytest.raises(RuntimeError): - setSort.getBVSize() + setSort.getBitVectorSize() def test_get_fp_exponent_size(solver): fpSort = solver.mkFloatingPointSort(4, 8) - fpSort.getFPExponentSize() + fpSort.getFloatingPointExponentSize() setSort = solver.mkSetSort(solver.getIntegerSort()) with pytest.raises(RuntimeError): - setSort.getFPExponentSize() + setSort.getFloatingPointExponentSize() def test_get_fp_significand_size(solver): fpSort = solver.mkFloatingPointSort(4, 8) - fpSort.getFPSignificandSize() + fpSort.getFloatingPointSignificandSize() setSort = solver.mkSetSort(solver.getIntegerSort()) with pytest.raises(RuntimeError): - setSort.getFPSignificandSize() + setSort.getFloatingPointSignificandSize() def test_get_datatype_paramsorts(solver): diff --git a/test/regress/CMakeLists.txt b/test/regress/CMakeLists.txt index 907dc22d1..ca9ce349f 100644 --- a/test/regress/CMakeLists.txt +++ b/test/regress/CMakeLists.txt @@ -479,6 +479,7 @@ set(regress_0_tests regress0/datatypes/coda_simp_model.smt2 regress0/datatypes/conqueue-dt-enum-iloop.smt2 regress0/datatypes/data-nested-codata.smt2 + regress0/datatypes/datatype-dump.cvc.smt2 regress0/datatypes/datatype.cvc.smt2 regress0/datatypes/datatype0.cvc.smt2 regress0/datatypes/datatype1.cvc.smt2 @@ -498,6 +499,7 @@ set(regress_0_tests regress0/datatypes/is_test.smt2 regress0/datatypes/issue1433.smt2 regress0/datatypes/issue2838.cvc.smt2 + regress0/datatypes/issue4393-cdt-model.smt2 regress0/datatypes/issue5280-no-nrec.smt2 regress0/datatypes/jsat-2.6.smt2 regress0/datatypes/list-bool.smt2 @@ -557,6 +559,7 @@ set(regress_0_tests regress0/distinct.smtv1.smt2 regress0/dump-unsat-core-full.smt2 regress0/echo.smt2 + regress0/eqrange0.smt2 regress0/eqrange1.smt2 regress0/eqrange2.smt2 regress0/eqrange3.smt2 @@ -765,10 +768,12 @@ set(regress_0_tests regress0/nl/very-simple-unsat.smt2 regress0/opt-abd-no-use.smt2 regress0/options/ast-and-sexpr.smt2 + regress0/options/interactive-mode.smt2 regress0/options/invalid_dump.smt2 regress0/options/set-after-init.smt2 regress0/options/set-and-get-options.smt2 regress0/options/statistics.smt2 + regress0/options/stream-printing.smt2 regress0/parallel-let.smt2 regress0/parser/as.smt2 regress0/parser/bv_arity_smt2.6.smt2 @@ -779,6 +784,7 @@ set(regress_0_tests regress0/parser/force_logic_set_logic.smt2 regress0/parser/force_logic_success.smt2 regress0/parser/issue5163.smt2 + regress0/parser/issue6908-get-value-uc.smt2 regress0/parser/issue7274.smt2 regress0/parser/linear_arithmetic_err1.smt2 regress0/parser/linear_arithmetic_err2.smt2 @@ -828,6 +834,11 @@ set(regress_0_tests regress0/preprocess/preprocess_13.cvc.smt2 regress0/preprocess/preprocess_14.cvc.smt2 regress0/preprocess/preprocess_15.cvc.smt2 + regress0/preprocess/proj-issue304-circuit-prop-xor.smt2 + regress0/preprocess/proj-issue305-circuit-prop-ite-a.smt2 + regress0/preprocess/proj-issue305-circuit-prop-ite-b.smt2 + regress0/preprocess/proj-issue305-circuit-prop-ite-c.smt2 + regress0/preprocess/proj-issue305-circuit-prop-ite-d.smt2 regress0/print_define_fun_internal.smt2 regress0/print_lambda.cvc.smt2 regress0/print_model.cvc.smt2 @@ -844,6 +855,7 @@ set(regress_0_tests regress0/proofs/open-pf-datatypes.smt2 regress0/proofs/open-pf-if-unordered-iff.smt2 regress0/proofs/open-pf-rederivation.smt2 + regress0/proofs/qgu-fuzz-1-bool-sat.smt2 regress0/proofs/scope.smt2 regress0/proofs/trust-subs-eq-open.smt2 regress0/push-pop/boolean/fuzz_12.smt2 @@ -875,6 +887,7 @@ set(regress_0_tests regress0/push-pop/incremental-subst-bug.cvc.smt2 regress0/push-pop/issue1986.smt2 regress0/push-pop/issue2137.min.smt2 + regress0/push-pop/issue6535-inc-solve.smt2 regress0/push-pop/quant-fun-proc-unfd.smt2 regress0/push-pop/real-as-int-incremental.smt2 regress0/push-pop/simple_unsat_cores.smt2 @@ -917,10 +930,12 @@ set(regress_0_tests regress0/quantifiers/issue4576.smt2 regress0/quantifiers/issue5645-dt-cm-spurious.smt2 regress0/quantifiers/issue5693-prenex.smt2 + regress0/quantifiers/issue6475-rr-const.smt2 regress0/quantifiers/issue6603-dt-bool-cegqi.smt2 regress0/quantifiers/issue6838-qpdt.smt2 regress0/quantifiers/issue6996-trivial-elim.smt2 regress0/quantifiers/issue6999-deq-elim.smt2 + regress0/quantifiers/issue7353-var-elim-par-dt.smt2 regress0/quantifiers/lra-triv-gn.smt2 regress0/quantifiers/macro-back-subs-sat.smt2 regress0/quantifiers/macros-int-real.smt2 @@ -964,6 +979,7 @@ set(regress_0_tests regress0/quantifiers/simp-len.smt2 regress0/quantifiers/simp-typ-test.smt2 regress0/quantifiers/ufnia-fv-delta.smt2 + regress0/quantifiers/veqt-delta.smt2 regress0/rec-fun-const-parse-bug.smt2 regress0/rels/addr_book_0.cvc.smt2 regress0/rels/atom_univ2.cvc.smt2 @@ -1499,6 +1515,8 @@ set(regress_1_tests regress1/arith/issue3952-rew-eq.smt2 regress1/arith/issue4985-model-success.smt2 regress1/arith/issue4985b-model-success.smt2 + regress1/arith/issue6774-sanity-int-model.smt2 + regress1/arith/issue7252-arith-sanity.smt2 regress1/arith/issue789.smt2 regress1/arith/miplib3.cvc.smt2 regress1/arith/mod.02.smt2 @@ -1570,6 +1588,7 @@ set(regress_1_tests regress1/constarr3.cvc.smt2 regress1/constarr3.smt2 regress1/cores/issue5604.smt2 + regress1/cores/issue6988-arith-sanity.smt2 regress1/datatypes/acyclicity-sr-ground096.smt2 regress1/datatypes/cee-prs-small-dd2.smt2 regress1/datatypes/dt-color-2.6.smt2 @@ -1638,8 +1657,11 @@ set(regress_1_tests regress1/fmf/issue4225-univ-fun.smt2 regress1/fmf/issue5738-dt-interp-finite.smt2 regress1/fmf/issue6690-re-enum.smt2 + regress1/fmf/issue6744-2-unc-bool-var.smt2 + regress1/fmf/issue6744-3-unc-bool-var.smt2 regress1/fmf/issue916-fmf-or.smt2 regress1/fmf/jasmin-cdt-crash.smt2 + regress1/fmf/ko-bound-set.cvc.smt2 regress1/fmf/loopy_coda.smt2 regress1/fmf/lst-no-self-rev-exp.smt2 regress1/fmf/memory_model-R_cpp-dd.cvc.smt2 @@ -1661,6 +1683,7 @@ set(regress_1_tests regress1/ho/issue4065-no-rep.smt2 regress1/ho/issue4092-sinf.smt2 regress1/ho/issue4134-sinf.smt2 + regress1/ho/issue5741-3.smt2 regress1/ho/NUM638^1.smt2 regress1/ho/NUM925^1.p regress1/ho/soundness_fmf_SYO362^5-delta.p @@ -1679,6 +1702,7 @@ set(regress_1_tests regress1/model-blocker-simple.smt2 regress1/model-blocker-values.smt2 regress1/nl/approx-sqrt.smt2 + regress1/nl/approx-sqrt-unsat.smt2 regress1/nl/arctan2-expdef.smt2 regress1/nl/arrowsmith-050317.smt2 regress1/nl/bad-050217.smt2 @@ -1707,8 +1731,10 @@ set(regress_1_tests regress1/nl/issue3656.smt2 regress1/nl/issue3803-nl-check-model.smt2 regress1/nl/issue3955-ee-double-notify.smt2 + regress1/nl/issue3966-conf-coeff.smt2 regress1/nl/issue4791-llr.smt2 regress1/nl/issue5372-2-no-m-presolve.smt2 + regress1/nl/issue5660-mb-success.smt2 regress1/nl/issue5662-nl-tc.smt2 regress1/nl/issue5662-nl-tc-min.smt2 regress1/nl/metitarski-1025.smt2 @@ -1752,6 +1778,7 @@ set(regress_1_tests regress1/proofs/issue6625-unsat-core-proofs.smt2 regress1/proofs/macro-res-exp-crowding-lit-inside-unit.smt2 regress1/proofs/macro-res-exp-singleton-after-elimCrowd.smt2 + regress1/proofs/qgu-fuzz-1-strings-pp.smt2 regress1/proofs/quant-alpha-eq.smt2 regress1/proofs/sat-trivial-cycle.smt2 regress1/proofs/unsat-cores-proofs.smt2 @@ -1842,6 +1869,7 @@ set(regress_1_tests regress1/quantifiers/cee-npnt-dd.smt2 regress1/quantifiers/cee-os-delta.smt2 regress1/quantifiers/cdt-0208-to.smt2 + regress1/quantifiers/choice-move-delta-relt.smt2 regress1/quantifiers/const.cvc.smt2 regress1/quantifiers/constfunc.cvc.smt2 regress1/quantifiers/ddatv-delta2.smt2 @@ -1887,6 +1915,7 @@ set(regress_1_tests regress1/quantifiers/issue4849-nqe.smt2 regress1/quantifiers/issue5019-cegqi-i.smt2 regress1/quantifiers/issue5279-nqe.smt2 + regress1/quantifiers/issue5288-vts-real-int.smt2 regress1/quantifiers/issue5365-nqe.smt2 regress1/quantifiers/issue5378-witness.smt2 regress1/quantifiers/issue5469-aext.smt2 @@ -1898,9 +1927,16 @@ set(regress_1_tests regress1/quantifiers/issue5506-qe.smt2 regress1/quantifiers/issue5507-qe.smt2 regress1/quantifiers/issue5658-qe.smt2 + regress1/quantifiers/issue5735-subtypes.smt2 + regress1/quantifiers/issue5735-2-subtypes.smt2 regress1/quantifiers/issue5766-wrong-sel-trigger.smt2 regress1/quantifiers/issue5899-qe.smt2 + regress1/quantifiers/issue6607-witness-te.smt2 + regress1/quantifiers/issue6638-sygus-inst.smt2 + regress1/quantifiers/issue6642-em-types.smt2 regress1/quantifiers/issue6699-nc-shadow.smt2 + regress1/quantifiers/issue6775-vts-int.smt2 + regress1/quantifiers/issue6845-nl-lemma-tc.smt2 regress1/quantifiers/issue993.smt2 regress1/quantifiers/javafe.ast.StmtVec.009.smt2 regress1/quantifiers/lia-witness-div-pp.smt2 @@ -1946,10 +1982,12 @@ set(regress_1_tests regress1/quantifiers/quaternion_ds1_symm_0428.fof.smt2 regress1/quantifiers/qs-has-term.smt2 regress1/quantifiers/recfact.cvc.smt2 + regress1/quantifiers/rel-trigger-unusable.smt2 regress1/quantifiers/repair-const-nterm.smt2 regress1/quantifiers/rew-to-0211-dd.smt2 regress1/quantifiers/ricart-agrawala6.smt2 regress1/quantifiers/set-choice-koikonomou.cvc.smt2 + regress1/quantifiers/set3.smt2 regress1/quantifiers/set8.smt2 regress1/quantifiers/seu169+1.smt2 regress1/quantifiers/seq-solved-enum.smt2 @@ -1960,6 +1998,7 @@ set(regress_1_tests regress1/quantifiers/smtlib46f14a.smt2 regress1/quantifiers/smtlibe99bbe.smt2 regress1/quantifiers/smtlibf957ea.smt2 + regress1/quantifiers/stream-x2014-09-18-unsat.smt2 regress1/quantifiers/sygus-infer-nested.smt2 regress1/quantifiers/sygus-inst-nia-psyco-060.smt2 regress1/quantifiers/sygus-inst-ufnia-sat-t3_rw1505.smt2 @@ -2178,6 +2217,7 @@ set(regress_1_tests regress1/strings/issue6101-2.smt2 regress1/strings/issue6132-non-unique-skolem.smt2 regress1/strings/issue6142-repl-inv-rew.smt2 + regress1/strings/issue6184-unsat-core.smt2 regress1/strings/issue6191-replace-all.smt2 regress1/strings/issue6203-1-substr-ctn-strip.smt2 regress1/strings/issue6203-2-re-ccache.smt2 @@ -2194,11 +2234,13 @@ set(regress_1_tests regress1/strings/issue6604-2.smt2 regress1/strings/issue6635-rre.smt2 regress1/strings/issue6653-2-update-c-len.smt2 + regress1/strings/issue6653-3-seq.smt2 regress1/strings/issue6653-4-rre.smt2 regress1/strings/issue6653-rre.smt2 regress1/strings/issue6653-rre-small.smt2 regress1/strings/issue6777-seq-nth-eval-cm.smt2 regress1/strings/issue6913.smt2 + regress1/strings/issue6973-dup-lemma-conc.smt2 regress1/strings/kaluza-fl.smt2 regress1/strings/loop002.smt2 regress1/strings/loop003.smt2 @@ -2250,6 +2292,7 @@ set(regress_1_tests regress1/strings/rev-ex5.smt2 regress1/strings/rew-020618.smt2 regress1/strings/rew-check1.smt2 + regress1/strings/seq-cardinality.smt2 regress1/strings/seq-quant-infinite-branch.smt2 regress1/strings/simple-re-consume.smt2 regress1/strings/stoi-400million.smt2 @@ -2289,6 +2332,7 @@ set(regress_1_tests regress1/sygus/abv.sy regress1/sygus/array-grammar-store.sy regress1/sygus/array_search_5-Q-easy.sy + regress1/sygus/array-uc.sy regress1/sygus/bvudiv-by-2.sy regress1/sygus/cegar1.sy regress1/sygus/cegis-unif-inv-eq-fair.sy @@ -2523,6 +2567,7 @@ set(regress_2_tests regress2/push-pop/DRAGON_4_e2_2799_e3_1915.lus.ic3.1.min.smt2 regress2/quantifiers/AdditiveMethods_AdditiveMethods..ctor.smt2 regress2/quantifiers/cee-event-wrong-sat.smt2 + regress2/quantifiers/exp-trivially-dd3.smt2 regress2/quantifiers/gn-wrong-091018.smt2 regress2/quantifiers/issue3481-unsat1.smt2 regress2/quantifiers/javafe.ast.StandardPrettyPrint.319.smt2 @@ -2701,16 +2746,12 @@ set(regression_disabled_tests ### regress1/bug472.smt2 regress1/datatypes/non-simple-rec-set.smt2 - # TODO: fix models (projects #276) - regress1/fmf/ko-bound-set.cvc.smt2 # results in an assertion failure (see issue #1650). regress1/ho/hoa0102.smt2 # after disallowing solving for terms with quantifiers regress1/ho/nested_lambdas-AGT034^2.smt2 regress1/ho/nested_lambdas-sat-SYO056^1-delta.smt2 regress1/ho/SYO056^1.p - # slow on some builds after changes to tangent planes - regress1/nl/approx-sqrt-unsat.smt2 # times out after update to circuit propagator regress1/nl/dumortier_llibre_artes_ex_5_13.transcendental.k2.smt2 # times out after update to tangent planes @@ -2727,12 +2768,6 @@ set(regression_disabled_tests regress1/quantifiers/macro-subtype-param.smt2 # times out with competition build, ok with other builds: regress1/quantifiers/model_6_1_bv.smt2 - # timeout after changes to nonlinear on PR #5351 - regress1/quantifiers/rel-trigger-unusable.smt2 - # does not terminate/takes a long time when doing a coverage build with LFSC. - regress1/quantifiers/set3.smt2 - # changes to expand definitions, related to trigger selection for selectors - regress1/quantifiers/stream-x2014-09-18-unsat.smt2 # ajreynol: different error messages on production and debug: regress1/quantifiers/subtype-param-unk.smt2 regress1/quantifiers/subtype-param.smt2 @@ -2769,7 +2804,6 @@ set(regression_disabled_tests # previously sygus inference did not apply, now (correctly) times out regress1/sygus/issue3498.smt2 regress2/arith/miplib-opt1217--27.smt2 - regress2/quantifiers/exp-trivially-dd3.smt2 regress2/nl/dumortier-050317.smt2 # timeout on some builds after changes to justification heuristic regress2/nl/nt-lemmas-bad.smt2 diff --git a/test/regress/regress0/datatypes/datatype-dump.cvc.smt2 b/test/regress/regress0/datatypes/datatype-dump.cvc.smt2 index f1aae6bb1..7d5495fcc 100644 --- a/test/regress/regress0/datatypes/datatype-dump.cvc.smt2 +++ b/test/regress/regress0/datatypes/datatype-dump.cvc.smt2 @@ -1,14 +1,11 @@ ; COMMAND-LINE: -o raw-benchmark -; EXPECT: OPTION "incremental" false; -; EXPECT: OPTION "logic" "ALL"; -; EXPECT: DATATYPE -; EXPECT: nat = succ(pred: nat) | zero -; EXPECT: END; -; EXPECT: x : nat; -; EXPECT: QUERY NOT(is_succ(x)) AND NOT(is_zero(x)); +; EXPECT: (set-option :incremental false) +; EXPECT: (set-logic ALL) +; EXPECT: (declare-datatypes ((nat 0)) (((succ (pred nat)) (zero)))) +; EXPECT: (declare-fun x () nat) +; EXPECT: (check-sat-assuming ( (not (and (not ((_ is succ) x)) (not ((_ is zero) x)))) )) ; EXPECT: sat (set-logic ALL) -(set-option :incremental false) (declare-datatypes ((nat 0)) (((succ (pred nat)) (zero)))) (declare-fun x () nat) (check-sat-assuming ( (not (and (not ((_ is succ) x)) (not ((_ is zero) x)))) )) diff --git a/test/regress/regress0/datatypes/issue4393-cdt-model.smt2 b/test/regress/regress0/datatypes/issue4393-cdt-model.smt2 new file mode 100644 index 000000000..950cb61a9 --- /dev/null +++ b/test/regress/regress0/datatypes/issue4393-cdt-model.smt2 @@ -0,0 +1,8 @@ +(set-logic QF_DTLIA) +(set-info :status sat) +(declare-codatatypes ((a 0)) (((b (c Int) (d a))))) +(declare-fun e () a) +(declare-fun f () a) +(assert (distinct f (b 0 f))) +(assert (= e f)) +(check-sat) diff --git a/test/regress/regress0/eqrange0.smt2 b/test/regress/regress0/eqrange0.smt2 new file mode 100644 index 000000000..01713d18f --- /dev/null +++ b/test/regress/regress0/eqrange0.smt2 @@ -0,0 +1,6 @@ +; EXPECT: (error "Term of kind EQ_RANGE not supported in default mode, try --arrays-exp") +; EXIT: 1 +(set-logic QF_AUFLIA) +(declare-const a (Array Int Int)) +(assert (eqrange a a 0 0)) +(check-sat) diff --git a/test/regress/regress0/options/interactive-mode.smt2 b/test/regress/regress0/options/interactive-mode.smt2 new file mode 100644 index 000000000..549fdfd24 --- /dev/null +++ b/test/regress/regress0/options/interactive-mode.smt2 @@ -0,0 +1,10 @@ +; EXPECT: true +; EXPECT: true +; EXPECT: false +; EXPECT: false +(set-option :interactive-mode true) +(get-option :interactive-mode) +(get-option :produce-assertions) +(set-option :produce-assertions false) +(get-option :interactive-mode) +(get-option :produce-assertions)
\ No newline at end of file diff --git a/test/regress/regress0/options/stream-printing.smt2 b/test/regress/regress0/options/stream-printing.smt2 new file mode 100644 index 000000000..21ea85aa1 --- /dev/null +++ b/test/regress/regress0/options/stream-printing.smt2 @@ -0,0 +1,18 @@ +; EXPECT: stdout +; EXPECT: stderr +; EXPECT: stdin +; EXPECT-ERROR: stderr +; EXPECT-ERROR: stdout +; EXPECT-ERROR: stdin + +(get-option :regular-output-channel) +(get-option :diagnostic-output-channel) +(get-option :in) + +(set-option :regular-output-channel stderr) +(set-option :diagnostic-output-channel stdout) +(set-option :in stdin) + +(get-option :regular-output-channel) +(get-option :diagnostic-output-channel) +(get-option :in) diff --git a/test/regress/regress0/parser/issue6908-get-value-uc.smt2 b/test/regress/regress0/parser/issue6908-get-value-uc.smt2 new file mode 100644 index 000000000..b6825fe27 --- /dev/null +++ b/test/regress/regress0/parser/issue6908-get-value-uc.smt2 @@ -0,0 +1,10 @@ +; COMMAND-LINE: --produce-models +; EXPECT: sat +; EXPECT: (((f (as @uc_Foo_0 Foo)) 3)) +(set-logic ALL) +(set-option :produce-models true) +(declare-sort Foo 0) +(declare-fun f (Foo) Int) +(assert (exists ((x Foo)) (= (f x) 3))) +(check-sat) +(get-value ((f @uc_Foo_0))) diff --git a/test/regress/regress0/preprocess/proj-issue304-circuit-prop-xor.smt2 b/test/regress/regress0/preprocess/proj-issue304-circuit-prop-xor.smt2 new file mode 100644 index 000000000..9e4bd285c --- /dev/null +++ b/test/regress/regress0/preprocess/proj-issue304-circuit-prop-xor.smt2 @@ -0,0 +1,8 @@ +; EXPECT: sat +(set-logic ALL) +(set-option :check-proofs true) +(declare-const a Bool) +(declare-const b Bool) +(assert b) +(assert (xor b (not a))) +(check-sat) diff --git a/test/regress/regress0/preprocess/proj-issue305-circuit-prop-ite-a.smt2 b/test/regress/regress0/preprocess/proj-issue305-circuit-prop-ite-a.smt2 new file mode 100644 index 000000000..6760bdf4d --- /dev/null +++ b/test/regress/regress0/preprocess/proj-issue305-circuit-prop-ite-a.smt2 @@ -0,0 +1,10 @@ +; EXPECT: sat +(set-logic ALL) +(set-option :check-proofs true) +(declare-const x Bool) +(declare-const y Bool) +(declare-const z Bool) +(assert y) +(assert (not z)) +(assert (ite x y z)) +(check-sat)
\ No newline at end of file diff --git a/test/regress/regress0/preprocess/proj-issue305-circuit-prop-ite-b.smt2 b/test/regress/regress0/preprocess/proj-issue305-circuit-prop-ite-b.smt2 new file mode 100644 index 000000000..fcb26054e --- /dev/null +++ b/test/regress/regress0/preprocess/proj-issue305-circuit-prop-ite-b.smt2 @@ -0,0 +1,10 @@ +; EXPECT: sat +(set-logic ALL) +(set-option :check-proofs true) +(declare-fun a () Bool) +(declare-fun b () Bool) +(declare-fun c () Bool) +(assert c) +(assert (not b)) +(assert (ite a b c)) +(check-sat)
\ No newline at end of file diff --git a/test/regress/regress0/preprocess/proj-issue305-circuit-prop-ite-c.smt2 b/test/regress/regress0/preprocess/proj-issue305-circuit-prop-ite-c.smt2 new file mode 100644 index 000000000..1765c32f1 --- /dev/null +++ b/test/regress/regress0/preprocess/proj-issue305-circuit-prop-ite-c.smt2 @@ -0,0 +1,10 @@ +; EXPECT: sat +(set-logic ALL) +(set-option :check-proofs true) +(declare-fun a () Bool) +(declare-fun b () Bool) +(declare-fun c () Bool) +(assert (not (ite a b c))) +(assert b) +(assert (not c)) +(check-sat)
\ No newline at end of file diff --git a/test/regress/regress0/preprocess/proj-issue305-circuit-prop-ite-d.smt2 b/test/regress/regress0/preprocess/proj-issue305-circuit-prop-ite-d.smt2 new file mode 100644 index 000000000..b3b19f74b --- /dev/null +++ b/test/regress/regress0/preprocess/proj-issue305-circuit-prop-ite-d.smt2 @@ -0,0 +1,10 @@ +; EXPECT: sat +(set-logic ALL) +(set-option :check-proofs true) +(declare-fun a () Bool) +(declare-fun b () Bool) +(declare-fun c () Bool) +(assert (not (ite a b c))) +(assert c) +(assert (not b)) +(check-sat)
\ No newline at end of file diff --git a/test/regress/regress0/proofs/qgu-fuzz-1-bool-sat.smt2 b/test/regress/regress0/proofs/qgu-fuzz-1-bool-sat.smt2 new file mode 100644 index 000000000..d9b10e2b9 --- /dev/null +++ b/test/regress/regress0/proofs/qgu-fuzz-1-bool-sat.smt2 @@ -0,0 +1,7 @@ +; EXPECT: unsat +(set-logic QF_UF) +(declare-fun b () Bool) +(declare-fun c () Bool) +(declare-fun d () Bool) +(assert (and (or d b) (= c d) (not (ite d c false)) (= (or b d) (= b d)))) +(check-sat)
\ No newline at end of file diff --git a/test/regress/regress0/push-pop/issue6535-inc-solve.smt2 b/test/regress/regress0/push-pop/issue6535-inc-solve.smt2 new file mode 100644 index 000000000..c4a9a770f --- /dev/null +++ b/test/regress/regress0/push-pop/issue6535-inc-solve.smt2 @@ -0,0 +1,9 @@ +; COMMAND-LINE: -i +; EXPECT: sat +(set-logic ALL) +(declare-fun f0_2 (Real Real) Real) +(declare-fun x8 () Real) +(assert (= 0.0 (f0_2 x8 1.0))) +(push) +(assert (= x8 1.0)) +(check-sat) diff --git a/test/regress/regress0/quantifiers/issue6475-rr-const.smt2 b/test/regress/regress0/quantifiers/issue6475-rr-const.smt2 new file mode 100644 index 000000000..11cc56547 --- /dev/null +++ b/test/regress/regress0/quantifiers/issue6475-rr-const.smt2 @@ -0,0 +1,8 @@ +(set-logic ALL) +(set-info :status sat) +(set-option :macros-quant true) +(declare-sort I_fb 0) +(declare-fun fb_arg_0_1 (I_fb) Int) +(declare-fun name!0 (I_fb) Int) +(assert (forall ((?j I_fb)) (! (= (name!0 ?j) (fb_arg_0_1 ?j)) :qid k!9))) +(check-sat) diff --git a/test/regress/regress0/quantifiers/issue7353-var-elim-par-dt.smt2 b/test/regress/regress0/quantifiers/issue7353-var-elim-par-dt.smt2 new file mode 100644 index 000000000..8c9d9eb66 --- /dev/null +++ b/test/regress/regress0/quantifiers/issue7353-var-elim-par-dt.smt2 @@ -0,0 +1,9 @@ +(set-logic ALL) +(set-info :status sat) +(declare-datatype Option (par (T) ((none) (some (extractSome T))))) +(assert + (forall ((x (Option Int))) + (=> (and ((_ is some) x) + (= (extractSome x) 0)) + (= x (some 0))))) +(check-sat) diff --git a/test/regress/regress0/quantifiers/veqt-delta.smt2 b/test/regress/regress0/quantifiers/veqt-delta.smt2 new file mode 100644 index 000000000..dfac015c6 --- /dev/null +++ b/test/regress/regress0/quantifiers/veqt-delta.smt2 @@ -0,0 +1,8 @@ +; COMMAND-LINE: --relational-triggers +; EXPECT: unsat +(set-logic ALL) +(declare-sort S 0) +(declare-fun f () S) +(assert (forall ((? S)) (= ? f))) +(assert (exists ((? S) (v S)) (distinct ? v))) +(check-sat) diff --git a/test/regress/regress1/arith/issue6774-sanity-int-model.smt2 b/test/regress/regress1/arith/issue6774-sanity-int-model.smt2 new file mode 100644 index 000000000..732b709f9 --- /dev/null +++ b/test/regress/regress1/arith/issue6774-sanity-int-model.smt2 @@ -0,0 +1,21 @@ +; COMMAND-LINE: -i -q +; EXPECT: sat +; EXPECT: sat +; EXPECT: sat +; EXPECT: sat +(set-logic ALIRA) +(declare-const x Real) +(declare-fun i () Int) +(declare-fun i1 () Int) +(push) +(assert (< 1 (- i))) +(check-sat) +(pop) +(push) +(assert (or (>= i1 (* 5 (- i))))) +(check-sat) +(pop) +(assert (or (> i1 1) (= x (to_real i)))) +(check-sat) +(assert (not (is_int x))) +(check-sat) diff --git a/test/regress/regress1/arith/issue7252-arith-sanity.smt2 b/test/regress/regress1/arith/issue7252-arith-sanity.smt2 new file mode 100644 index 000000000..dd7a1fa2e --- /dev/null +++ b/test/regress/regress1/arith/issue7252-arith-sanity.smt2 @@ -0,0 +1,14 @@ +; COMMAND-LINE: -q +; EXPECT: sat +(set-logic ALL) +(set-info :status sat) +(declare-fun a () Int) +(declare-fun b () Int) +(declare-fun c () Int) +(declare-fun d () Int) +(declare-fun e () Int) +(declare-fun f () Int) +(declare-fun g () Int) +(assert (> 0 (* a (mod 0 b)))) +(assert (or (and (> (* b d) (* 2 (+ g c))) (= g (- c)) (> (+ e c) 0)) (> f 0))) +(check-sat) diff --git a/test/regress/regress1/cores/issue6988-arith-sanity.smt2 b/test/regress/regress1/cores/issue6988-arith-sanity.smt2 new file mode 100644 index 000000000..622d64375 --- /dev/null +++ b/test/regress/regress1/cores/issue6988-arith-sanity.smt2 @@ -0,0 +1,18 @@ +; COMMAND-LINE: -i -q +; EXPECT: sat +; EXPECT: sat +(set-logic ANIA) +(declare-const x Bool) +(set-option :produce-unsat-cores true) +(declare-fun i () Int) +(declare-fun i5 () Int) +(declare-fun i8 () Int) +(assert (or x (< i5 0))) +(push) +(assert (and (= i8 1) (= i5 (+ 1 i)) (= i8 (+ 1 i)))) +(push) +(check-sat) +(pop) +(pop) +(assert (= i8 (* i8 3 (+ i8 1)))) +(check-sat) diff --git a/test/regress/regress1/fmf/issue5738-dt-interp-finite.smt2 b/test/regress/regress1/fmf/issue5738-dt-interp-finite.smt2 index 50373fde4..6f0fb84f2 100644 --- a/test/regress/regress1/fmf/issue5738-dt-interp-finite.smt2 +++ b/test/regress/regress1/fmf/issue5738-dt-interp-finite.smt2 @@ -1,4 +1,4 @@ -(set-logic UFLIA) +(set-logic UFDTLIA) (set-info :status sat) (set-option :finite-model-find true) (declare-sort a 0) diff --git a/test/regress/regress1/fmf/issue6744-2-unc-bool-var.smt2 b/test/regress/regress1/fmf/issue6744-2-unc-bool-var.smt2 new file mode 100644 index 000000000..024d5b6a3 --- /dev/null +++ b/test/regress/regress1/fmf/issue6744-2-unc-bool-var.smt2 @@ -0,0 +1,7 @@ +(set-logic ALL) +(set-option :finite-model-find true) +(set-info :status sat) +(declare-fun v () Bool) +(declare-fun v2 () Bool) +(assert (exists ((q (Array Bool (Array Bool (Array Int Bool))))) (forall ((q3 (Array Bool (Array Bool (Array Int Bool))))) (xor v2 v2 v v2 (or v2 (not (= q3 q))))))) +(check-sat) diff --git a/test/regress/regress1/fmf/issue6744-3-unc-bool-var.smt2 b/test/regress/regress1/fmf/issue6744-3-unc-bool-var.smt2 new file mode 100644 index 000000000..eb0c35f68 --- /dev/null +++ b/test/regress/regress1/fmf/issue6744-3-unc-bool-var.smt2 @@ -0,0 +1,5 @@ +(set-logic UFC) +(set-info :status sat) +(declare-fun v () Bool) +(assert (and (forall ((q Bool)) (not (xor v (exists ((q Bool)) true) (and (not v) (not q))))))) +(check-sat) diff --git a/test/regress/regress1/ho/issue5741-3.smt2 b/test/regress/regress1/ho/issue5741-3.smt2 new file mode 100644 index 000000000..abc4b76a6 --- /dev/null +++ b/test/regress/regress1/ho/issue5741-3.smt2 @@ -0,0 +1,5 @@ +(set-logic HO_ALL) +(set-info :status sat) +(declare-fun p ((_ BitVec 17) (_ BitVec 16)) Bool) +(assert (p (_ bv0 17) ((_ sign_extend 15) (ite (p (_ bv0 17) (_ bv0 16)) (_ bv1 1) (_ bv0 1))))) +(check-sat) diff --git a/test/regress/regress1/nl/approx-sqrt-unsat.smt2 b/test/regress/regress1/nl/approx-sqrt-unsat.smt2 index 576fb1a67..cda24d098 100644 --- a/test/regress/regress1/nl/approx-sqrt-unsat.smt2 +++ b/test/regress/regress1/nl/approx-sqrt-unsat.smt2 @@ -1,4 +1,4 @@ -; COMMAND-LINE: --nl-ext-tplanes +; COMMAND-LINE: --nl-ext-tplanes --no-check-proofs ; EXPECT: unsat (set-logic QF_NRA) (set-info :status unsat) diff --git a/test/regress/regress1/nl/issue3966-conf-coeff.smt2 b/test/regress/regress1/nl/issue3966-conf-coeff.smt2 new file mode 100644 index 000000000..7bfbf4140 --- /dev/null +++ b/test/regress/regress1/nl/issue3966-conf-coeff.smt2 @@ -0,0 +1,22 @@ +(set-logic QF_UFNIA) +(set-info :status sat) +(set-option :nl-ext-ent-conf true) +(declare-const v0 Bool) +(declare-const v1 Bool) +(declare-const v2 Bool) +(declare-const v3 Bool) +(declare-const i0 Int) +(declare-const i1 Int) +(declare-const i2 Int) +(declare-const i4 Int) +(declare-const i5 Int) +(declare-const i9 Int) +(declare-const i10 Int) +(declare-const i12 Int) +(declare-const i13 Int) +(assert v0) +(declare-sort S0 0) +(declare-const v4 Bool) +(assert (xor v2 v1 (> i12 i2) (and v3 v3) (> i12 i2) v4 v2 v1 v0 v3)) +(assert (xor (<= 52 (div 15 (- i1 84 i0 99 i5))) v4 (=> v4 (>= i5 88)) (> i12 i2) (and v3 v3) (<= 52 (div 15 (- i1 84 i0 99 i5))) v1 (> i12 i2) (distinct i0 615) v0)) +(check-sat) diff --git a/test/regress/regress1/nl/issue5660-mb-success.smt2 b/test/regress/regress1/nl/issue5660-mb-success.smt2 new file mode 100644 index 000000000..6284b0580 --- /dev/null +++ b/test/regress/regress1/nl/issue5660-mb-success.smt2 @@ -0,0 +1,21 @@ +(set-logic QF_AUFNRA) +(set-info :status sat) +(declare-fun r2 () Real) +(declare-fun r9 () Real) +(declare-fun r13 () Real) +(declare-fun ufrb5 (Real Real Real Real Real) Bool) +(declare-fun v3 () Bool) +(declare-fun v4 () Bool) +(declare-fun arr0 () (Array Bool Real)) +(declare-fun arr1 () (Array Bool (Array Bool Real))) +(declare-fun v5 () Bool) +(declare-fun v7 () Bool) +(declare-fun v8 () Bool) +(declare-fun v71 () Bool) +(declare-fun v81 () Bool) +(declare-fun v161 () Bool) +(assert (or v161 (and v3 (not v7)))) +(assert (or v8 (distinct v7 (and v7 v81) (ufrb5 0.0 1.0 0.0 1.0 (/ r13 r9))))) +(assert (or v161 (distinct v71 v8 (= v4 v81)))) +(assert (or v81 (= arr0 (store (select (store arr1 (xor v81 (and (= r2 1.0) (= r13 1))) arr0) v7) v81 (select (store arr0 v5 1.0) false))))) +(check-sat) diff --git a/test/regress/regress1/proofs/qgu-fuzz-1-strings-pp.smt2 b/test/regress/regress1/proofs/qgu-fuzz-1-strings-pp.smt2 new file mode 100644 index 000000000..c0c1f16f7 --- /dev/null +++ b/test/regress/regress1/proofs/qgu-fuzz-1-strings-pp.smt2 @@ -0,0 +1,5 @@ +(set-logic ALL) +(set-info :status unsat) +(declare-fun x () String) +(assert (let ((_let_1 (str.to_re "B"))) (and (str.in_re x (re.++ re.allchar (str.to_re (str.++ "B" "A")))) (str.in_re x (re.* (re.union re.allchar (str.to_re "A")))) (str.in_re x (re.* (re.union re.allchar _let_1))) (str.in_re x (re.* (re.++ re.allchar _let_1)))))) +(check-sat) diff --git a/test/regress/regress1/quantifiers/choice-move-delta-relt.smt2 b/test/regress/regress1/quantifiers/choice-move-delta-relt.smt2 new file mode 100644 index 000000000..a8fd0d498 --- /dev/null +++ b/test/regress/regress1/quantifiers/choice-move-delta-relt.smt2 @@ -0,0 +1,6 @@ +; COMMAND-LINE: --relational-triggers --user-pat=use +; EXPECT: unsat +(set-logic ALL) +(declare-fun F (Int) Bool) +(assert (forall ((v Int)) (! (= (F 0) (< v 0)) :qid |outputbpl.194:24| :pattern ((F 0))))) +(check-sat) diff --git a/test/regress/regress1/quantifiers/issue5288-vts-real-int.smt2 b/test/regress/regress1/quantifiers/issue5288-vts-real-int.smt2 new file mode 100644 index 000000000..b36b8cc94 --- /dev/null +++ b/test/regress/regress1/quantifiers/issue5288-vts-real-int.smt2 @@ -0,0 +1,9 @@ +; COMMAND-LINE: --strings-exp +; EXPECT: unsat +(set-logic ALL) +(set-info :status unsat) +(assert + (forall ((a Int) (b Int)) + (or (< a (/ 3 b (- 2))) + (forall ((c Int)) (or (<= c b) (>= c a)))))) +(check-sat) diff --git a/test/regress/regress1/quantifiers/issue5735-2-subtypes.smt2 b/test/regress/regress1/quantifiers/issue5735-2-subtypes.smt2 new file mode 100644 index 000000000..76a58bdb7 --- /dev/null +++ b/test/regress/regress1/quantifiers/issue5735-2-subtypes.smt2 @@ -0,0 +1,4 @@ +(set-logic ALL) +(set-info :status unsat) +(assert (forall ((a Real)) (exists ((b Int)) (= (exists ((c Int)) (<= a c (+ a 1))) (and (>= b (/ a (+ a 1))) (< 1 (+ a 1))))))) +(check-sat) diff --git a/test/regress/regress1/quantifiers/issue5735-subtypes.smt2 b/test/regress/regress1/quantifiers/issue5735-subtypes.smt2 new file mode 100644 index 000000000..300819007 --- /dev/null +++ b/test/regress/regress1/quantifiers/issue5735-subtypes.smt2 @@ -0,0 +1,6 @@ +(set-logic ALL) +(set-info :status unsat) +(declare-fun a () Bool) +(assert (forall ((b Int) (c Bool) (d Int)) +(or (= d (/ 1 (ite c 9 0))) (<= (ite a 1 b) (/ 1 (ite c 9 0)))))) +(check-sat) diff --git a/test/regress/regress1/quantifiers/issue6607-witness-te.smt2 b/test/regress/regress1/quantifiers/issue6607-witness-te.smt2 new file mode 100644 index 000000000..ff426c139 --- /dev/null +++ b/test/regress/regress1/quantifiers/issue6607-witness-te.smt2 @@ -0,0 +1,5 @@ +; COMMAND-LINE: --no-check-models +(set-logic ALL) +(set-info :status sat) +(assert (exists ((skoY Real)) (forall ((skoX Real)) (or (= 0.0 (* skoY skoY)) (and (< skoY 0.0) (or (= skoY skoX) (= 2 (* skoY skoY)))))))) +(check-sat) diff --git a/test/regress/regress1/quantifiers/issue6638-sygus-inst.smt2 b/test/regress/regress1/quantifiers/issue6638-sygus-inst.smt2 new file mode 100644 index 000000000..b7cc50885 --- /dev/null +++ b/test/regress/regress1/quantifiers/issue6638-sygus-inst.smt2 @@ -0,0 +1,8 @@ +; COMMAND-LINE: --sygus-inst --no-check-models +; EXPECT: sat +(set-logic ALL) +(declare-fun b (Int) Int) +(assert (distinct 0 (ite (exists ((t Int)) (and (forall ((tt Int) (t Int)) (! (or (distinct 0 tt) (> 0 (+ tt t)) (> (+ tt t) 0) (= (b 0) (b t))) :qid k!7)))) 1 (b 0)))) +(check-sat) + +; check-models disabled due to intermediate terms from sygus-inst diff --git a/test/regress/regress1/quantifiers/issue6642-em-types.smt2 b/test/regress/regress1/quantifiers/issue6642-em-types.smt2 new file mode 100644 index 000000000..19dc11227 --- /dev/null +++ b/test/regress/regress1/quantifiers/issue6642-em-types.smt2 @@ -0,0 +1,6 @@ +; COMMAND-LINE: --full-saturate-quant +; EXPECT: unsat +(set-logic ALL) +(declare-fun b () String) +(assert (forall ((c (Seq Int))) (exists ((a String)) (and (= 1 (seq.len c)) (not (= b a)))))) +(check-sat) diff --git a/test/regress/regress1/quantifiers/issue6775-vts-int.smt2 b/test/regress/regress1/quantifiers/issue6775-vts-int.smt2 new file mode 100644 index 000000000..39b92a6ad --- /dev/null +++ b/test/regress/regress1/quantifiers/issue6775-vts-int.smt2 @@ -0,0 +1,19 @@ +; COMMAND-LINE: -i --no-check-models +; EXPECT: sat +; EXPECT: sat +(set-logic NIA) +(declare-const x43799 Bool) +(declare-const x32 Bool) +(declare-fun v11 () Bool) +(declare-fun i2 () Int) +(declare-fun i3 () Int) +(declare-fun i () Int) +(assert (or (< 0 i2) (not x32) (not v11))) +(assert (or x32 (exists ((q Int)) (not (= x32 (> q (abs i3))))))) +(assert (< i2 i)) +(check-sat) +(assert (or (not v11) x43799)) +(assert (= (+ 3 i (* 13 4 5 (abs i3))) (* 157 4 (- 1) (+ 1 1 i2 i)))) +(check-sat) + +; check-models disabled due to intermediate terms from sygus-inst diff --git a/test/regress/regress1/quantifiers/issue6845-nl-lemma-tc.smt2 b/test/regress/regress1/quantifiers/issue6845-nl-lemma-tc.smt2 new file mode 100644 index 000000000..087137653 --- /dev/null +++ b/test/regress/regress1/quantifiers/issue6845-nl-lemma-tc.smt2 @@ -0,0 +1,15 @@ +; COMMAND-LINE: -i +; EXPECT: sat +; EXPECT: sat +(set-logic NIRA) +(declare-const x Bool) +(declare-fun i () Int) +(declare-fun i1 () Int) +(assert (< 1.0 (to_real i))) +(assert (distinct 0 (/ 7 (to_real i)))) +(push) +(assert (or (exists ((q Int)) (= 0 (* i i1))))) +(check-sat) +(pop) +(assert (or (= i1 1) x)) +(check-sat) diff --git a/test/regress/regress1/strings/issue6184-unsat-core.smt2 b/test/regress/regress1/strings/issue6184-unsat-core.smt2 new file mode 100644 index 000000000..6673dc3b9 --- /dev/null +++ b/test/regress/regress1/strings/issue6184-unsat-core.smt2 @@ -0,0 +1,15 @@ +; COMMAND-LINE: --strings-exp +; EXPECT: unsat +(set-logic ALL) +(set-info :status unsat) +(set-option :check-unsat-cores true) +(declare-fun i8 () Int) +(declare-fun st6 () (Set String)) +(declare-fun st8 () (Set String)) +(declare-fun str6 () String) +(declare-fun str7 () String) +(assert (= 0 (card st8))) +(assert (str.in_re str6 (re.opt re.none))) +(assert (str.in_re (str.substr str7 0 i8) re.allchar)) +(assert (xor true (subset st8 st6) (not (= str7 str6)) true)) +(check-sat) diff --git a/test/regress/regress1/strings/issue6653-3-seq.smt2 b/test/regress/regress1/strings/issue6653-3-seq.smt2 new file mode 100644 index 000000000..a48fc7664 --- /dev/null +++ b/test/regress/regress1/strings/issue6653-3-seq.smt2 @@ -0,0 +1,10 @@ +; COMMAND-LINE: -q +; EXPECT: sat +(set-logic QF_SLIA) +(set-info :status sat) +(set-option :strings-lazy-pp false) +(declare-fun z () Int) +(declare-fun a () (Seq Int)) +(assert (not (= (seq.nth a 1) (seq.nth a z)))) +(assert (= z (- 1))) +(check-sat) diff --git a/test/regress/regress1/strings/issue6973-dup-lemma-conc.smt2 b/test/regress/regress1/strings/issue6973-dup-lemma-conc.smt2 new file mode 100644 index 000000000..4c6fe5c62 --- /dev/null +++ b/test/regress/regress1/strings/issue6973-dup-lemma-conc.smt2 @@ -0,0 +1,15 @@ +(set-logic QF_SLIA) +(set-info :status unsat) +(declare-fun a () String) +(assert + (str.in_re "" + (re.++ (re.diff (re.comp re.all) (re.++ (str.to_re a) (re.comp re.all))) + (str.to_re + (ite + (str.in_re "" + (re.++ (str.to_re (ite (str.in_re "" (re.++ (str.to_re a) (re.comp re.all))) a "")) + (re.inter (str.to_re a) + (re.++ (str.to_re a) + (re.comp (re.union (re.++ (str.to_re a) (re.comp re.all)) re.all)))))) + a ""))))) +(check-sat) diff --git a/test/regress/regress1/strings/seq-cardinality.smt2 b/test/regress/regress1/strings/seq-cardinality.smt2 new file mode 100644 index 000000000..93a4985d4 --- /dev/null +++ b/test/regress/regress1/strings/seq-cardinality.smt2 @@ -0,0 +1,11 @@ +(set-logic ALL) +(set-info :status unsat) +(declare-fun x () (Seq (_ BitVec 1))) +(declare-fun y () (Seq (_ BitVec 1))) +(declare-fun z () (Seq (_ BitVec 1))) + +(assert (= (seq.len x) 1)) +(assert (= (seq.len y) 1)) +(assert (= (seq.len z) 1)) +(assert (distinct x y z)) +(check-sat) diff --git a/test/regress/regress1/sygus/array-uc.sy b/test/regress/regress1/sygus/array-uc.sy new file mode 100644 index 000000000..b3d950436 --- /dev/null +++ b/test/regress/regress1/sygus/array-uc.sy @@ -0,0 +1,14 @@ +; COMMAND-LINE: --sygus-out=status +; EXPECT: unsat +(set-logic ALL) +(declare-sort U 0) +(synth-fun f ((x (Array U Int)) (y U)) Bool) + +(declare-var x (Array U Int)) +(declare-var y U) + +(constraint (= (f (store x y 0) y) true)) +(constraint (= (f (store x y 1) y) false)) + +; f can be (= (select x y) 0) +(check-synth) diff --git a/test/regress/run_regression.py b/test/regress/run_regression.py index 32ba6f6c2..edb45e4bd 100755 --- a/test/regress/run_regression.py +++ b/test/regress/run_regression.py @@ -11,36 +11,332 @@ # directory for licensing information. # ############################################################################# ## - """ Runs benchmark and checks for correct exit status and output. """ import argparse +import collections import difflib import os import re import shlex import subprocess import sys +import tempfile import threading class Color: - BLUE = '\033[94m' - GREEN = '\033[92m' - YELLOW = '\033[93m' - RED = '\033[91m' - ENDC = '\033[0m' + BLUE = "\033[94m" + GREEN = "\033[92m" + YELLOW = "\033[93m" + RED = "\033[91m" + ENDC = "\033[0m" + +class Tester: + def __init__(self): + pass -SCRUBBER = 'SCRUBBER:' -ERROR_SCRUBBER = 'ERROR-SCRUBBER:' -EXPECT = 'EXPECT:' -EXPECT_ERROR = 'EXPECT-ERROR:' -EXIT = 'EXIT:' -COMMAND_LINE = 'COMMAND-LINE:' -REQUIRES = 'REQUIRES:' + def applies(self, benchmark_info): + return True + + def run(self, benchmark_info): + exit_code = EXIT_OK + output, error, exit_status = run_benchmark(benchmark_info) + if exit_status == STATUS_TIMEOUT: + exit_code = EXIT_SKIP if skip_timeout else EXIT_FAILURE + print("Timeout - Flags: {}".format(command_line_args)) + elif output != benchmark_info.expected_output: + exit_code = EXIT_FAILURE + print("not ok - Flags: {}".format(benchmark_info.command_line_args)) + print() + print("Standard output difference") + print("=" * 80) + print_diff(output, benchmark_info.expected_output) + print("=" * 80) + print() + print() + if error: + print("Error output") + print("=" * 80) + print_colored(Color.YELLOW, error) + print("=" * 80) + print() + elif error != benchmark_info.expected_error: + exit_code = EXIT_FAILURE + print( + "not ok - Differences between expected and actual output on stderr - Flags: {}".format( + benchmark_info.command_line_args + ) + ) + print() + print("Error output difference") + print("=" * 80) + print_diff(error, benchmark_info.expected_error) + print("=" * 80) + print() + elif exit_status != benchmark_info.expected_exit_status: + exit_code = EXIT_FAILURE + print( + 'not ok - Expected exit status "{}" but got "{}" - Flags: {}'.format( + benchmark_info.expected_exit_status, + exit_status, + benchmark_info.command_line_args, + ) + ) + print() + print("Output:") + print("=" * 80) + print_colored(Color.BLUE, output) + print("=" * 80) + print() + print() + print("Error output:") + print("=" * 80) + print_colored(Color.YELLOW, error) + print("=" * 80) + print() + else: + print("ok - Flags: {}".format(benchmark_info.command_line_args)) + return exit_code + + +################################################################################ +# The testers +# +# Testers use `Tester` as a base class and implement `applies()` and `run()` +# methods. The `applies()` method returns `True` if a tester applies to a given +# benchmark and `run()` runs the actual test. Most testers can invoke the +# `run()` method in the base class, which calls the cvc5 binary with a set of +# arguments and checks the expected output (both stdout and stderr) as well as +# the exit status. +# +# To add a new tester, add a class and add it to the `g_tester` dictionary. +################################################################################ + + +class BaseTester(Tester): + def __init__(self): + pass + + def run(self, benchmark_info): + return super().run(benchmark_info) + + +class UnsatCoreTester(Tester): + def __init__(self): + pass + + def applies(self, benchmark_info): + return ( + benchmark_info.benchmark_ext != ".sy" + and ( + "unsat" in benchmark_info.expected_output.split() + or "entailed" in benchmark_info.expected_output.split() + ) + and "--no-produce-unsat-cores" not in benchmark_info.command_line_args + and "--no-check-unsat-cores" not in benchmark_info.command_line_args + and "--check-unsat-cores" not in benchmark_info.command_line_args + and "sygus-inference" not in benchmark_info.benchmark_content + and "--unconstrained-simp" not in benchmark_info.command_line_args + ) + + def run(self, benchmark_info): + return super().run( + benchmark_info._replace( + command_line_args=benchmark_info.command_line_args + + ["--check-unsat-cores"] + ) + ) + + +class ProofTester(Tester): + def __init__(self): + pass + + def applies(self, benchmark_info): + expected_output_lines = benchmark_info.expected_output.split() + return ( + benchmark_info.benchmark_ext != ".sy" + and ( + "unsat" in benchmark_info.expected_output.split() + or "entailed" in benchmark_info.expected_output.split() + ) + and "--no-produce-proofs" not in benchmark_info.command_line_args + and "--no-check-proofs" not in benchmark_info.command_line_args + and "--check-proofs" not in benchmark_info.command_line_args + ) + + def run(self, benchmark_info): + return super().run( + benchmark_info._replace( + command_line_args=benchmark_info.command_line_args + ["--check-proofs"] + ) + ) + + +class ModelTester(Tester): + def __init__(self): + pass + + def applies(self, benchmark_info): + expected_output_lines = benchmark_info.expected_output.split() + return ( + benchmark_info.benchmark_ext != ".sy" + and ("sat" in expected_output_lines or "unknown" in expected_output_lines) + and "--no-debug-check-models" not in benchmark_info.command_line_args + and "--no-check-models" not in benchmark_info.command_line_args + and "--debug-check-models" not in benchmark_info.command_line_args + ) + + def run(self, benchmark_info): + return super().run( + benchmark_info._replace( + command_line_args=benchmark_info.command_line_args + + ["--debug-check-models"] + ) + ) + + +class SynthTester(Tester): + def __init__(self): + pass + + def applies(self, benchmark_info): + return ( + benchmark_info.benchmark_ext == ".sy" + and "--no-check-synth-sol" not in benchmark_info.command_line_args + and "--sygus-rr" not in benchmark_info.command_line_args + and "--check-synth-sol" not in benchmark_info.command_line_args + ) + + def run(self, benchmark_info): + return super().run( + benchmark_info._replace( + command_line_args=benchmark_info.command_line_args + + ["--check-synth-sol"] + ) + ) + + +class AbductTester(Tester): + def __init__(self): + pass + + def applies(self, benchmark_info): + return ( + benchmark_info.benchmark_ext != ".sy" + and "--no-check-abducts" not in benchmark_info.command_line_args + and "--check-abducts" not in benchmark_info.command_line_args + and "get-abduct" in benchmark_info.benchmark_content + ) + + def run(self, benchmark_info): + return super().run( + benchmark_info._replace( + command_line_args=benchmark_info.command_line_args + ["--check-abducts"] + ) + ) + + +class DumpTester(Tester): + def applies(self, benchmark_info): + return benchmark_info.expected_exit_status == EXIT_OK + + def run(self, benchmark_info): + ext_to_lang = { + ".smt2": "smt2", + ".p": "tptp", + ".sy": "sygus", + } + + tmpf_name = None + with tempfile.NamedTemporaryFile(delete=False) as tmpf: + dump_args = [ + "--parse-only", + "-o", + "raw-benchmark", + f"--output-lang={ext_to_lang[benchmark_info.benchmark_ext]}", + ] + dump_output, _, _ = run_process( + [benchmark_info.cvc5_binary] + + benchmark_info.command_line_args + + dump_args + + [benchmark_info.benchmark_basename], + benchmark_info.benchmark_dir, + benchmark_info.timeout, + ) + + tmpf_name = tmpf.name + tmpf.write(dump_output) + + if not tmpf_name: + return EXIT_FAILURE + + exit_code = super().run( + benchmark_info._replace( + command_line_args=benchmark_info.command_line_args + + [ + "--parse-only", + f"--lang={ext_to_lang[benchmark_info.benchmark_ext]}", + ], + benchmark_basename=tmpf.name, + expected_output="", + ) + ) + os.remove(tmpf.name) + return exit_code + + +g_testers = { + "base": BaseTester(), + "unsat-core": UnsatCoreTester(), + "proof": ProofTester(), + "model": ModelTester(), + "synth": SynthTester(), + "abduct": AbductTester(), + "dump": DumpTester(), +} + +g_default_testers = [ + "base", + "unsat-core", + "proof", + "model", + "synth", + "abduct", +] + +################################################################################ + +BenchmarkInfo = collections.namedtuple( + "BenchmarkInfo", + [ + "wrapper", + "scrubber", + "error_scrubber", + "timeout", + "cvc5_binary", + "benchmark_dir", + "benchmark_basename", + "benchmark_ext", + "benchmark_content", + "expected_output", + "expected_error", + "expected_exit_status", + "command_line_args", + ], +) + +SCRUBBER = "SCRUBBER:" +ERROR_SCRUBBER = "ERROR-SCRUBBER:" +EXPECT = "EXPECT:" +EXPECT_ERROR = "EXPECT-ERROR:" +EXIT = "EXIT:" +COMMAND_LINE = "COMMAND-LINE:" +REQUIRES = "REQUIRES:" EXIT_OK = 0 EXIT_FAILURE = 1 @@ -58,14 +354,12 @@ def print_colored(color, text): def print_diff(actual, expected): """Prints the difference between `actual` and `expected`.""" - for line in difflib.unified_diff(expected.splitlines(), - actual.splitlines(), - 'expected', - 'actual', - lineterm=''): - if line.startswith('+'): + for line in difflib.unified_diff( + expected.splitlines(), actual.splitlines(), "expected", "actual", lineterm="" + ): + if line.startswith("+"): print_colored(Color.GREEN, line) - elif line.startswith('-'): + elif line.startswith("-"): print_colored(Color.RED, line) else: print(line) @@ -78,14 +372,16 @@ def run_process(args, cwd, timeout, s_input=None): output and the exit code of the process. If the process times out, the output and the error output are empty and the exit code is 124.""" - proc = subprocess.Popen(args, - cwd=cwd, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + proc = subprocess.Popen( + args, + cwd=cwd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) - out = '' - err = '' + out = "" + err = "" exit_status = STATUS_TIMEOUT try: if timeout: @@ -106,61 +402,60 @@ def run_process(args, cwd, timeout, s_input=None): def get_cvc5_features(cvc5_binary): """Returns a list of features supported by the cvc5 binary `cvc5_binary`.""" - output, _, _ = run_process([cvc5_binary, '--show-config'], None, None) + output, _, _ = run_process([cvc5_binary, "--show-config"], None, None) if isinstance(output, bytes): output = output.decode() features = [] disabled_features = [] - for line in output.split('\n'): - tokens = [t.strip() for t in line.split(':')] + for line in output.split("\n"): + tokens = [t.strip() for t in line.split(":")] if len(tokens) == 2: key, value = tokens - if value == 'yes': + if value == "yes": features.append(key) - elif value == 'no': + elif value == "no": disabled_features.append(key) return features, disabled_features -def run_benchmark(dump, wrapper, scrubber, error_scrubber, cvc5_binary, - command_line, benchmark_dir, benchmark_filename, timeout): - """Runs cvc5 on the file `benchmark_filename` in the directory - `benchmark_dir` using the binary `cvc5_binary` with the command line - options `command_line`. The output is scrubbed using `scrubber` and - `error_scrubber` for stdout and stderr, respectively. If dump is true, the - function first uses cvc5 to read in and dump the benchmark file and then - uses that as input.""" +def run_benchmark(benchmark_info): + """Runs cvc5 on a benchmark with the given `benchmark_info`. It runs on the + file `benchmark_basename` in the directory `benchmark_dir` using the binary + `cvc5_binary` with the command line options `command_line_args`. The output + is scrubbed using `scrubber` and `error_scrubber` for stdout and stderr, + respectively.""" - bin_args = wrapper[:] - bin_args.append(cvc5_binary) + bin_args = benchmark_info.wrapper[:] + bin_args.append(benchmark_info.cvc5_binary) output = None error = None exit_status = None - if dump: - dump_args = [ - '--parse-only', '-o', 'raw-benchmark', '--output-lang=smt2' - ] - dump_output, _, _ = run_process( - bin_args + command_line + dump_args + [benchmark_filename], - benchmark_dir, timeout) - output, error, exit_status = run_process( - bin_args + command_line + ['--lang=smt2', '-'], benchmark_dir, - timeout, dump_output) - else: - output, error, exit_status = run_process( - bin_args + command_line + [benchmark_filename], benchmark_dir, - timeout) + output, error, exit_status = run_process( + bin_args + + benchmark_info.command_line_args + + [benchmark_info.benchmark_basename], + benchmark_info.benchmark_dir, + benchmark_info.timeout, + ) # If a scrubber command has been specified then apply it to the output. - if scrubber: - output, _, _ = run_process(shlex.split(scrubber), benchmark_dir, - timeout, output) - if error_scrubber: - error, _, _ = run_process(shlex.split(error_scrubber), benchmark_dir, - timeout, error) + if benchmark_info.scrubber: + output, _, _ = run_process( + shlex.split(benchmark_info.scrubber), + benchmark_info.benchmark_dir, + benchmark_info.timeout, + output, + ) + if benchmark_info.error_scrubber: + error, _, _ = run_process( + shlex.split(benchmark_info.error_scrubber), + benchmark_info.benchmark_dir, + benchmark_info.timeout, + error, + ) # Popen in Python 3 returns a bytes object instead of a string for # stdout/stderr. @@ -168,22 +463,28 @@ def run_benchmark(dump, wrapper, scrubber, error_scrubber, cvc5_binary, output = output.decode() if isinstance(error, bytes): error = error.decode() - return (output.strip(), error.strip(), exit_status) - - -def run_regression(check_unsat_cores, check_proofs, dump, use_skip_return_code, - skip_timeout, wrapper, cvc5_binary, benchmark_path, - timeout): - """Determines the expected output for a benchmark, runs cvc5 on it and then - checks whether the output corresponds to the expected output. Optionally - uses a wrapper `wrapper`, tests unsat cores (if check_unsat_cores is true), - checks proofs (if check_proofs is true), or dumps a benchmark and uses that as - the input (if dump is true). `use_skip_return_code` enables/disables - returning 77 when a test is skipped.""" + output = re.sub(r"^[ \t]*", "", output.strip(), flags=re.MULTILINE) + error = re.sub(r"^[ \t]*", "", error.strip(), flags=re.MULTILINE) + return (output, error, exit_status) + + +def run_regression( + testers, + use_skip_return_code, + skip_timeout, + wrapper, + cvc5_binary, + benchmark_path, + timeout, +): + """Determines the expected output for a benchmark, runs cvc5 on it using + all the specified `testers` and then checks whether the output corresponds + to the expected output. Optionally uses a wrapper `wrapper`. + `use_skip_return_code` enables/disables returning 77 when a test is + skipped.""" if not os.access(cvc5_binary, os.X_OK): - sys.exit( - '"{}" does not exist or is not executable'.format(cvc5_binary)) + sys.exit('"{}" does not exist or is not executable'.format(cvc5_binary)) if not os.path.isfile(benchmark_path): sys.exit('"{}" does not exist or is not a file'.format(benchmark_path)) @@ -194,38 +495,34 @@ def run_regression(check_unsat_cores, check_proofs, dump, use_skip_return_code, benchmark_basename = os.path.basename(benchmark_path) benchmark_filename, benchmark_ext = os.path.splitext(benchmark_basename) benchmark_dir = os.path.dirname(benchmark_path) - comment_char = '%' + comment_char = "%" status_regex = None status_to_output = lambda s: s - if benchmark_ext == '.smt': - status_regex = r':status\s*(sat|unsat)' - comment_char = ';' - elif benchmark_ext == '.smt2': - status_regex = r'set-info\s*:status\s*(sat|unsat)' - comment_char = ';' - elif benchmark_ext == '.p': - status_regex = r'% Status\s*:\s*(Theorem|Unsatisfiable|CounterSatisfiable|Satisfiable)' - status_to_output = lambda s: '% SZS status {} for {}'.format( - s, benchmark_filename) - elif benchmark_ext == '.sy': - comment_char = ';' - # Do not check proofs/unsat-cores with .sy files - check_unsat_cores = False - check_proofs = False + if benchmark_ext == ".smt2": + status_regex = r"set-info\s*:status\s*(sat|unsat)" + comment_char = ";" + elif benchmark_ext == ".p": + status_regex = ( + r"% Status\s*:\s*(Theorem|Unsatisfiable|CounterSatisfiable|Satisfiable)" + ) + status_to_output = lambda s: "% SZS status {} for {}".format( + s, benchmark_filename + ) + elif benchmark_ext == ".sy": + comment_char = ";" else: - sys.exit('"{}" must be *.smt2 or *.p or *.sy'.format( - benchmark_basename)) + sys.exit('"{}" must be *.smt2 or *.p or *.sy'.format(benchmark_basename)) benchmark_lines = None - with open(benchmark_path, 'r') as benchmark_file: + with open(benchmark_path, "r") as benchmark_file: benchmark_lines = benchmark_file.readlines() - benchmark_content = ''.join(benchmark_lines) + benchmark_content = "".join(benchmark_lines) # Extract the metadata for the benchmark. scrubber = None error_scrubber = None - expected_output = '' - expected_error = '' + expected_output = "" + expected_error = "" expected_exit_status = None command_lines = [] requires = [] @@ -236,31 +533,31 @@ def run_regression(check_unsat_cores, check_proofs, dump, use_skip_return_code, line = line[1:].lstrip() if line.startswith(SCRUBBER): - scrubber = line[len(SCRUBBER):].strip() + scrubber = line[len(SCRUBBER) :].strip() elif line.startswith(ERROR_SCRUBBER): - error_scrubber = line[len(ERROR_SCRUBBER):].strip() + error_scrubber = line[len(ERROR_SCRUBBER) :].strip() elif line.startswith(EXPECT): - expected_output += line[len(EXPECT):].strip() + '\n' + expected_output += line[len(EXPECT) :].strip() + "\n" elif line.startswith(EXPECT_ERROR): - expected_error += line[len(EXPECT_ERROR):].strip() + '\n' + expected_error += line[len(EXPECT_ERROR) :].strip() + "\n" elif line.startswith(EXIT): - expected_exit_status = int(line[len(EXIT):].strip()) + expected_exit_status = int(line[len(EXIT) :].strip()) elif line.startswith(COMMAND_LINE): - command_lines.append(line[len(COMMAND_LINE):].strip()) + command_lines.append(line[len(COMMAND_LINE) :].strip()) elif line.startswith(REQUIRES): - requires.append(line[len(REQUIRES):].strip()) + requires.append(line[len(REQUIRES) :].strip()) expected_output = expected_output.strip() expected_error = expected_error.strip() # Expected output/expected error has not been defined in the metadata for # the benchmark. Try to extract the information from the benchmark itself. - if expected_output == '' and expected_error == '': + if expected_output == "" and expected_error == "": match = None if status_regex: match = re.findall(status_regex, benchmark_content) if match: - expected_output = status_to_output('\n'.join(match)) + expected_output = status_to_output("\n".join(match)) elif expected_exit_status is None: # If there is no expected output/error and the exit status has not # been set explicitly, the benchmark is invalid. @@ -268,156 +565,77 @@ def run_regression(check_unsat_cores, check_proofs, dump, use_skip_return_code, if expected_exit_status is None: expected_exit_status = 0 - if 'CVC5_REGRESSION_ARGS' in os.environ: - basic_command_line_args += shlex.split( - os.environ['CVC5_REGRESSION_ARGS']) - - if not check_unsat_cores and ('(get-unsat-core)' in benchmark_content - or '(get-unsat-assumptions)' in benchmark_content): - print( - '1..0 # Skipped regression: unsat cores not supported without proof support' - ) - return (EXIT_SKIP if use_skip_return_code else EXIT_OK) + if "CVC5_REGRESSION_ARGS" in os.environ: + basic_command_line_args += shlex.split(os.environ["CVC5_REGRESSION_ARGS"]) for req_feature in requires: is_negative = False if req_feature.startswith("no-"): - req_feature = req_feature[len("no-"):] + req_feature = req_feature[len("no-") :] is_negative = True if req_feature not in (cvc5_features + cvc5_disabled_features): print( - 'Illegal requirement in regression: {}\nAllowed requirements: {}' - .format(req_feature, - ' '.join(cvc5_features + cvc5_disabled_features))) + "Illegal requirement in regression: {}\nAllowed requirements: {}".format( + req_feature, " ".join(cvc5_features + cvc5_disabled_features) + ) + ) return EXIT_FAILURE if is_negative: if req_feature in cvc5_features: - print('1..0 # Skipped regression: not valid with {}'.format( - req_feature)) - return (EXIT_SKIP if use_skip_return_code else EXIT_OK) + print( + "1..0 # Skipped regression: not valid with {}".format(req_feature) + ) + return EXIT_SKIP if use_skip_return_code else EXIT_OK elif req_feature not in cvc5_features: - print('1..0 # Skipped regression: {} not supported'.format( - req_feature)) - return (EXIT_SKIP if use_skip_return_code else EXIT_OK) + print("1..0 # Skipped regression: {} not supported".format(req_feature)) + return EXIT_SKIP if use_skip_return_code else EXIT_OK if not command_lines: - command_lines.append('') + command_lines.append("") + tests = [] + expected_output_lines = expected_output.split() command_line_args_configs = [] for command_line in command_lines: args = shlex.split(command_line) all_args = basic_command_line_args + args - if not check_unsat_cores and ('--check-unsat-cores' in all_args): - print( - '# Skipped command line options ({}): unsat cores not supported without proof support' - .format(all_args)) - continue - if not check_proofs and '--dump-proofs' in all_args: - print( - '# Skipped command line options ({}): proof production not supported' - .format(all_args)) - continue - command_line_args_configs.append(all_args) - expected_output_lines = expected_output.split() - extra_command_line_args = [] - if benchmark_ext == '.sy' and \ - '--no-check-synth-sol' not in all_args and \ - '--sygus-rr' not in all_args and \ - '--check-synth-sol' not in all_args: - all_args += ['--check-synth-sol'] - if ('sat' in expected_output_lines or \ - 'unknown' in expected_output_lines) and \ - '--no-debug-check-models' not in all_args and \ - '--no-check-models' not in all_args and \ - '--debug-check-models' not in all_args: - extra_command_line_args += ['--debug-check-models'] - if 'unsat' in expected_output_lines in expected_output_lines: - if check_unsat_cores and \ - '--no-produce-unsat-cores' not in all_args and \ - '--no-check-unsat-cores' not in all_args and \ - '--check-unsat-cores' not in all_args and \ - 'sygus-inference' not in benchmark_content and \ - '--unconstrained-simp' not in all_args: - extra_command_line_args += ['--check-unsat-cores'] - if check_proofs and \ - '--no-produce-proofs' not in all_args and \ - '--no-check-proofs' not in all_args and \ - '--check-proofs' not in all_args: - extra_command_line_args += ['--check-proofs'] - if '--no-check-abducts' not in all_args and \ - '--check-abducts' not in all_args and \ - 'get-abduct' in benchmark_content: - all_args += ['--check-abducts'] - - # Create a test case for each extra argument - for extra_arg in extra_command_line_args: - command_line_args_configs.append(all_args + [extra_arg]) - - # Run cvc5 on the benchmark with the different option sets and check - # whether the exit status, stdout output, stderr output are as expected. - print('1..{}'.format(len(command_line_args_configs))) - print('# Starting') + benchmark_info = BenchmarkInfo( + wrapper=wrapper, + scrubber=scrubber, + error_scrubber=error_scrubber, + timeout=timeout, + cvc5_binary=cvc5_binary, + benchmark_dir=benchmark_dir, + benchmark_basename=benchmark_basename, + benchmark_ext=benchmark_ext, + benchmark_content=benchmark_content, + expected_output=expected_output, + expected_error=expected_error, + expected_exit_status=expected_exit_status, + command_line_args=all_args, + ) + for tester_name, tester in g_testers.items(): + if tester_name in testers and tester.applies(benchmark_info): + tests.append((tester, benchmark_info)) + + if len(tests) == 0: + print("1..0 # Skipped regression: no tests to run") + return EXIT_SKIP if use_skip_return_code else EXIT_OK + + print("1..{}".format(len(tests))) + print("# Starting") + # Run cvc5 on the benchmark with the different testers and check whether + # the exit status, stdout output, stderr output are as expected. exit_code = EXIT_OK - for command_line_args in command_line_args_configs: - output, error, exit_status = run_benchmark(dump, wrapper, scrubber, - error_scrubber, cvc5_binary, - command_line_args, - benchmark_dir, - benchmark_basename, timeout) - output = re.sub(r'^[ \t]*', '', output, flags=re.MULTILINE) - error = re.sub(r'^[ \t]*', '', error, flags=re.MULTILINE) - if exit_status == STATUS_TIMEOUT: - exit_code = EXIT_SKIP if skip_timeout else EXIT_FAILURE - print('Timeout - Flags: {}'.format(command_line_args)) - elif output != expected_output: - exit_code = EXIT_FAILURE - print('not ok - Flags: {}'.format(command_line_args)) - print() - print('Standard output difference') - print('=' * 80) - print_diff(output, expected_output) - print('=' * 80) - print() - print() - if error: - print('Error output') - print('=' * 80) - print_colored(Color.YELLOW, error) - print('=' * 80) - print() - elif error != expected_error: - exit_code = EXIT_FAILURE - print( - 'not ok - Differences between expected and actual output on stderr - Flags: {}' - .format(command_line_args)) - print() - print('Error output difference') - print('=' * 80) - print_diff(error, expected_error) - print('=' * 80) - print() - elif expected_exit_status != exit_status: + for tester, benchmark_info in tests: + test_exit_code = tester.run(benchmark_info) + if exit_code == EXIT_FAILURE or test_exit_code == EXIT_FAILURE: exit_code = EXIT_FAILURE - print( - 'not ok - Expected exit status "{}" but got "{}" - Flags: {}'. - format(expected_exit_status, exit_status, command_line_args)) - print() - print('Output:') - print('=' * 80) - print_colored(Color.BLUE, output) - print('=' * 80) - print() - print() - print('Error output:') - print('=' * 80) - print_colored(Color.YELLOW, error) - print('=' * 80) - print() else: - print('ok - Flags: {}'.format(command_line_args)) + exit_code = test_exit_code return exit_code @@ -427,40 +645,43 @@ def main(): script.""" parser = argparse.ArgumentParser( - description= - 'Runs benchmark and checks for correct exit status and output.') - parser.add_argument('--dump', action='store_true') - parser.add_argument('--use-skip-return-code', action='store_true') - parser.add_argument('--skip-timeout', action='store_true') - parser.add_argument('--check-unsat-cores', action='store_true', - default=True) - parser.add_argument('--no-check-unsat-cores', dest='check_unsat_cores', - action='store_false') - parser.add_argument('--check-proofs', action='store_true', default=True) - parser.add_argument('--no-check-proofs', dest='check_proofs', - action='store_false') - parser.add_argument('wrapper', nargs='*') - parser.add_argument('cvc5_binary') - parser.add_argument('benchmark') + description="Runs benchmark and checks for correct exit status and output." + ) + parser.add_argument("--use-skip-return-code", action="store_true") + parser.add_argument("--skip-timeout", action="store_true") + parser.add_argument("--tester", choices=g_testers.keys(), action="append") + parser.add_argument("wrapper", nargs="*") + parser.add_argument("cvc5_binary") + parser.add_argument("benchmark") argv = sys.argv[1:] # Append options passed via RUN_REGRESSION_ARGS to argv - if os.environ.get('RUN_REGRESSION_ARGS'): - argv.extend(shlex.split(os.getenv('RUN_REGRESSION_ARGS'))) + if os.environ.get("RUN_REGRESSION_ARGS"): + argv.extend(shlex.split(os.getenv("RUN_REGRESSION_ARGS"))) args = parser.parse_args(argv) cvc5_binary = os.path.abspath(args.cvc5_binary) wrapper = args.wrapper - if os.environ.get('VALGRIND') == '1' and not wrapper: - wrapper = ['libtool', '--mode=execute', 'valgrind'] - - timeout = float(os.getenv('TEST_TIMEOUT', '600')) - - return run_regression(args.check_unsat_cores, args.check_proofs, args.dump, - args.use_skip_return_code, args.skip_timeout, - wrapper, cvc5_binary, args.benchmark, timeout) + if os.environ.get("VALGRIND") == "1" and not wrapper: + wrapper = ["libtool", "--mode=execute", "valgrind"] + + timeout = float(os.getenv("TEST_TIMEOUT", "600")) + + testers = args.tester + if not testers: + testers = g_default_testers + + return run_regression( + testers, + args.use_skip_return_code, + args.skip_timeout, + wrapper, + cvc5_binary, + args.benchmark, + timeout, + ) if __name__ == "__main__": diff --git a/test/unit/api/java/cvc5/SolverTest.java b/test/unit/api/java/cvc5/SolverTest.java index d153b8a91..971be58cc 100644 --- a/test/unit/api/java/cvc5/SolverTest.java +++ b/test/unit/api/java/cvc5/SolverTest.java @@ -19,11 +19,11 @@ import static cvc5.Kind.*; import static cvc5.RoundingMode.*; import static org.junit.jupiter.api.Assertions.*; +import java.math.BigInteger; import java.util.*; import java.util.concurrent.atomic.AtomicReference; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.function.Executable; class SolverTest { @@ -623,6 +623,11 @@ class SolverTest assertDoesNotThrow(() -> d_solver.mkTerm(STRING_IN_REGEXP, s, d_solver.mkRegexpSigma())); } + @Test void mkSepEmp() + { + assertDoesNotThrow(() -> d_solver.mkSepEmp()); + } + @Test void mkSepNil() { assertDoesNotThrow(() -> d_solver.mkSepNil(d_solver.getBooleanSort())); @@ -1341,6 +1346,75 @@ class SolverTest assertThrows(CVC5ApiException.class, () -> d_solver.getOption("asdf")); } + @Test void getOptionNames() + { + String[] names = d_solver.getOptionNames(); + assertTrue(names.length > 100); + assertTrue(Arrays.asList(names).contains("verbose")); + assertFalse(Arrays.asList(names).contains("foobar")); + } + + @Test void getOptionInfo() + { + List<Executable> assertions = new ArrayList<>(); + { + assertions.add( + () -> assertThrows(CVC5ApiException.class, () -> d_solver.getOptionInfo("asdf-invalid"))); + } + { + OptionInfo info = d_solver.getOptionInfo("verbose"); + assertions.add(() -> assertEquals("verbose", info.getName())); + assertions.add( + () -> assertEquals(Arrays.asList(new String[] {}), Arrays.asList(info.getAliases()))); + assertions.add(() -> assertTrue(info.getBaseInfo() instanceof OptionInfo.VoidInfo)); + } + { + // int64 type with default + OptionInfo info = d_solver.getOptionInfo("verbosity"); + assertions.add(() -> assertEquals("verbosity", info.getName())); + assertions.add( + () -> assertEquals(Arrays.asList(new String[] {}), Arrays.asList(info.getAliases()))); + assertions.add( + () -> assertTrue(info.getBaseInfo().getClass() == OptionInfo.NumberInfo.class)); + OptionInfo.NumberInfo<BigInteger> numInfo = + (OptionInfo.NumberInfo<BigInteger>) info.getBaseInfo(); + assertions.add(() -> assertEquals(0, numInfo.getDefaultValue().intValue())); + assertions.add(() -> assertEquals(0, numInfo.getCurrentValue().intValue())); + assertions.add( + () -> assertFalse(numInfo.getMinimum() != null || numInfo.getMaximum() != null)); + assertions.add(() -> assertEquals(info.intValue().intValue(), 0)); + } + assertAll(assertions); + { + OptionInfo info = d_solver.getOptionInfo("random-freq"); + assertEquals(info.getName(), "random-freq"); + assertEquals( + Arrays.asList(info.getAliases()), Arrays.asList(new String[] {"random-frequency"})); + assertTrue(info.getBaseInfo().getClass() == OptionInfo.NumberInfo.class); + OptionInfo.NumberInfo<Double> ni = (OptionInfo.NumberInfo<Double>) info.getBaseInfo(); + assertEquals(ni.getCurrentValue(), 0.0); + assertEquals(ni.getDefaultValue(), 0.0); + assertTrue(ni.getMinimum() != null && ni.getMaximum() != null); + assertEquals(ni.getMinimum(), 0.0); + assertEquals(ni.getMaximum(), 1.0); + assertEquals(info.doubleValue(), 0.0); + } + { + // mode option + OptionInfo info = d_solver.getOptionInfo("output"); + assertions.clear(); + assertions.add(() -> assertEquals("output", info.getName())); + assertions.add( + () -> assertEquals(Arrays.asList(new String[] {}), Arrays.asList(info.getAliases()))); + assertions.add(() -> assertTrue(info.getBaseInfo().getClass() == OptionInfo.ModeInfo.class)); + OptionInfo.ModeInfo modeInfo = (OptionInfo.ModeInfo) info.getBaseInfo(); + assertions.add(() -> assertEquals("NONE", modeInfo.getDefaultValue())); + assertions.add(() -> assertEquals("OutputTag::NONE", modeInfo.getCurrentValue())); + assertions.add(() -> assertTrue(Arrays.asList(modeInfo.getModes()).contains("NONE"))); + } + assertAll(assertions); + } + @Test void getUnsatAssumptions1() { d_solver.setOption("incremental", "false"); @@ -1383,17 +1457,17 @@ class SolverTest assertThrows(CVC5ApiException.class, () -> d_solver.getUnsatCore()); } - @Test void getUnsatCore3() + @Test void getUnsatCoreAndProof() { d_solver.setOption("incremental", "true"); d_solver.setOption("produce-unsat-cores", "true"); + d_solver.setOption("produce-proofs", "true"); Sort uSort = d_solver.mkUninterpretedSort("u"); Sort intSort = d_solver.getIntegerSort(); Sort boolSort = d_solver.getBooleanSort(); Sort uToIntSort = d_solver.mkFunctionSort(uSort, intSort); Sort intPredSort = d_solver.mkFunctionSort(intSort, boolSort); - Term[] unsat_core; Term x = d_solver.mkConst(uSort, "x"); Term y = d_solver.mkConst(uSort, "y"); @@ -1401,21 +1475,21 @@ class SolverTest Term p = d_solver.mkConst(intPredSort, "p"); Term zero = d_solver.mkInteger(0); Term one = d_solver.mkInteger(1); - Term f_x = d_solver.mkTerm(APPLY_UF, f, x); - Term f_y = d_solver.mkTerm(APPLY_UF, f, y); - Term sum = d_solver.mkTerm(PLUS, f_x, f_y); - Term p_0 = d_solver.mkTerm(APPLY_UF, p, zero); + Term f_x = d_solver.mkTerm(Kind.APPLY_UF, f, x); + Term f_y = d_solver.mkTerm(Kind.APPLY_UF, f, y); + Term sum = d_solver.mkTerm(Kind.PLUS, f_x, f_y); + Term p_0 = d_solver.mkTerm(Kind.APPLY_UF, p, zero); Term p_f_y = d_solver.mkTerm(APPLY_UF, p, f_y); - d_solver.assertFormula(d_solver.mkTerm(GT, zero, f_x)); - d_solver.assertFormula(d_solver.mkTerm(GT, zero, f_y)); - d_solver.assertFormula(d_solver.mkTerm(GT, sum, one)); + d_solver.assertFormula(d_solver.mkTerm(Kind.GT, zero, f_x)); + d_solver.assertFormula(d_solver.mkTerm(Kind.GT, zero, f_y)); + d_solver.assertFormula(d_solver.mkTerm(Kind.GT, sum, one)); d_solver.assertFormula(p_0); d_solver.assertFormula(p_f_y.notTerm()); assertTrue(d_solver.checkSat().isUnsat()); - AtomicReference<Term[]> atomic = new AtomicReference<>(); - assertDoesNotThrow(() -> atomic.set(d_solver.getUnsatCore())); - unsat_core = atomic.get(); + Term[] unsat_core = d_solver.getUnsatCore(); + + assertDoesNotThrow(() -> d_solver.getProof()); d_solver.resetAssertions(); for (Term t : unsat_core) @@ -1424,6 +1498,42 @@ class SolverTest } Result res = d_solver.checkSat(); assertTrue(res.isUnsat()); + assertDoesNotThrow(() -> d_solver.getProof()); + } + + @Test void getDifficulty() + { + d_solver.setOption("produce-difficulty", "true"); + // cannot ask before a check sat + assertThrows(CVC5ApiException.class, () -> d_solver.getDifficulty()); + d_solver.checkSat(); + assertDoesNotThrow(() -> d_solver.getDifficulty()); + } + + @Test void getDifficulty2() + { + d_solver.checkSat(); + // option is not set + assertThrows(CVC5ApiException.class, () -> d_solver.getDifficulty()); + } + + @Test void getDifficulty3() throws CVC5ApiException + { + d_solver.setOption("produce-difficulty", "true"); + Sort intSort = d_solver.getIntegerSort(); + Term x = d_solver.mkConst(intSort, "x"); + Term zero = d_solver.mkInteger(0); + Term ten = d_solver.mkInteger(10); + Term f0 = d_solver.mkTerm(GEQ, x, ten); + Term f1 = d_solver.mkTerm(GEQ, zero, x); + d_solver.checkSat(); + Map<Term, Term> dmap = d_solver.getDifficulty(); + // difficulty should map assertions to integer values + for (Map.Entry<Term, Term> t : dmap.entrySet()) + { + assertTrue(t.getKey() == f0 || t.getKey() == f1); + assertTrue(t.getValue().getKind() == Kind.CONST_RATIONAL); + } } @Test void getValue1() @@ -1483,6 +1593,95 @@ class SolverTest assertThrows(CVC5ApiException.class, () -> slv.getValue(x)); } + @Test void getModelDomainElements() + { + d_solver.setOption("produce-models", "true"); + Sort uSort = d_solver.mkUninterpretedSort("u"); + Sort intSort = d_solver.getIntegerSort(); + Term x = d_solver.mkConst(uSort, "x"); + Term y = d_solver.mkConst(uSort, "y"); + Term z = d_solver.mkConst(uSort, "z"); + Term f = d_solver.mkTerm(DISTINCT, x, y, z); + d_solver.assertFormula(f); + d_solver.checkSat(); + assertDoesNotThrow(() -> d_solver.getModelDomainElements(uSort)); + assertTrue(d_solver.getModelDomainElements(uSort).length >= 3); + assertThrows(CVC5ApiException.class, () -> d_solver.getModelDomainElements(intSort)); + } + + @Test void getModelDomainElements2() + { + d_solver.setOption("produce-models", "true"); + d_solver.setOption("finite-model-find", "true"); + Sort uSort = d_solver.mkUninterpretedSort("u"); + Term x = d_solver.mkVar(uSort, "x"); + Term y = d_solver.mkVar(uSort, "y"); + Term eq = d_solver.mkTerm(EQUAL, x, y); + Term bvl = d_solver.mkTerm(BOUND_VAR_LIST, x, y); + Term f = d_solver.mkTerm(FORALL, bvl, eq); + d_solver.assertFormula(f); + d_solver.checkSat(); + assertDoesNotThrow(() -> d_solver.getModelDomainElements(uSort)); + // a model for the above must interpret u as size 1 + assertTrue(d_solver.getModelDomainElements(uSort).length == 1); + } + + @Test void isModelCoreSymbol() + { + d_solver.setOption("produce-models", "true"); + d_solver.setOption("model-cores", "simple"); + Sort uSort = d_solver.mkUninterpretedSort("u"); + Term x = d_solver.mkConst(uSort, "x"); + Term y = d_solver.mkConst(uSort, "y"); + Term z = d_solver.mkConst(uSort, "z"); + Term zero = d_solver.mkInteger(0); + Term f = d_solver.mkTerm(NOT, d_solver.mkTerm(EQUAL, x, y)); + d_solver.assertFormula(f); + d_solver.checkSat(); + assertTrue(d_solver.isModelCoreSymbol(x)); + assertTrue(d_solver.isModelCoreSymbol(y)); + assertFalse(d_solver.isModelCoreSymbol(z)); + assertThrows(CVC5ApiException.class, () -> d_solver.isModelCoreSymbol(zero)); + } + + @Test void getModel() + { + d_solver.setOption("produce-models", "true"); + Sort uSort = d_solver.mkUninterpretedSort("u"); + Term x = d_solver.mkConst(uSort, "x"); + Term y = d_solver.mkConst(uSort, "y"); + Term z = d_solver.mkConst(uSort, "z"); + Term f = d_solver.mkTerm(NOT, d_solver.mkTerm(EQUAL, x, y)); + d_solver.assertFormula(f); + d_solver.checkSat(); + Sort[] sorts = new Sort[] {uSort}; + Term[] terms = new Term[] {x, y}; + assertDoesNotThrow(() -> d_solver.getModel(sorts, terms)); + Term nullTerm = d_solver.getNullTerm(); + Term[] terms2 = new Term[] {x, y, nullTerm}; + assertThrows(CVC5ApiException.class, () -> d_solver.getModel(sorts, terms2)); + } + + @Test void getModel2() + { + d_solver.setOption("produce-models", "true"); + Sort[] sorts = new Sort[] {}; + Term[] terms = new Term[] {}; + assertThrows(CVC5ApiException.class, () -> d_solver.getModel(sorts, terms)); + } + + @Test void getModel3() + { + d_solver.setOption("produce-models", "true"); + Sort[] sorts = new Sort[] {}; + final Term[] terms = new Term[] {}; + d_solver.checkSat(); + assertDoesNotThrow(() -> d_solver.getModel(sorts, terms)); + Sort integer = d_solver.getIntegerSort(); + Sort[] sorts2 = new Sort[] {integer}; + assertThrows(CVC5ApiException.class, () -> d_solver.getModel(sorts2, terms)); + } + @Test void getQuantifierElimination() { Term x = d_solver.mkVar(d_solver.getBooleanSort(), "x"); @@ -2180,6 +2379,20 @@ class SolverTest assertThrows(CVC5ApiException.class, () -> slv.addSygusConstraint(boolTerm)); } + @Test void addSygusAssume() + { + Term nullTerm = d_solver.getNullTerm(); + Term boolTerm = d_solver.mkBoolean(false); + Term intTerm = d_solver.mkInteger(1); + + assertDoesNotThrow(() -> d_solver.addSygusAssume(boolTerm)); + assertThrows(CVC5ApiException.class, () -> d_solver.addSygusAssume(nullTerm)); + assertThrows(CVC5ApiException.class, () -> d_solver.addSygusAssume(intTerm)); + + Solver slv = new Solver(); + assertThrows(CVC5ApiException.class, () -> slv.addSygusAssume(boolTerm)); + } + @Test void addSygusInvConstraint() throws CVC5ApiException { Sort bool = d_solver.getBooleanSort(); @@ -2342,4 +2555,4 @@ class SolverTest + "\"Z\")))", projection.toString()); } -}
\ No newline at end of file +} diff --git a/test/unit/api/java/cvc5/SortTest.java b/test/unit/api/java/cvc5/SortTest.java index f2f9edaed..1ea703268 100644 --- a/test/unit/api/java/cvc5/SortTest.java +++ b/test/unit/api/java/cvc5/SortTest.java @@ -458,28 +458,28 @@ class SortTest assertThrows(CVC5ApiException.class, () -> bvSort.getSortConstructorArity()); } - @Test void getBVSize() throws CVC5ApiException + @Test void getBitVectorSize() throws CVC5ApiException { Sort bvSort = d_solver.mkBitVectorSort(32); - assertDoesNotThrow(() -> bvSort.getBVSize()); + assertDoesNotThrow(() -> bvSort.getBitVectorSize()); Sort setSort = d_solver.mkSetSort(d_solver.getIntegerSort()); - assertThrows(CVC5ApiException.class, () -> setSort.getBVSize()); + assertThrows(CVC5ApiException.class, () -> setSort.getBitVectorSize()); } - @Test void getFPExponentSize() throws CVC5ApiException + @Test void getFloatingPointExponentSize() throws CVC5ApiException { Sort fpSort = d_solver.mkFloatingPointSort(4, 8); - assertDoesNotThrow(() -> fpSort.getFPExponentSize()); + assertDoesNotThrow(() -> fpSort.getFloatingPointExponentSize()); Sort setSort = d_solver.mkSetSort(d_solver.getIntegerSort()); - assertThrows(CVC5ApiException.class, () -> setSort.getFPExponentSize()); + assertThrows(CVC5ApiException.class, () -> setSort.getFloatingPointExponentSize()); } - @Test void getFPSignificandSize() throws CVC5ApiException + @Test void getFloatingPointSignificandSize() throws CVC5ApiException { Sort fpSort = d_solver.mkFloatingPointSort(4, 8); - assertDoesNotThrow(() -> fpSort.getFPSignificandSize()); + assertDoesNotThrow(() -> fpSort.getFloatingPointSignificandSize()); Sort setSort = d_solver.mkSetSort(d_solver.getIntegerSort()); - assertThrows(CVC5ApiException.class, () -> setSort.getFPSignificandSize()); + assertThrows(CVC5ApiException.class, () -> setSort.getFloatingPointSignificandSize()); } @Test void getDatatypeParamSorts() throws CVC5ApiException diff --git a/test/unit/api/solver_black.cpp b/test/unit/api/solver_black.cpp index 19113ae13..8dcb0fde6 100644 --- a/test/unit/api/solver_black.cpp +++ b/test/unit/api/solver_black.cpp @@ -376,15 +376,6 @@ TEST_F(TestApiBlackSolver, mkRoundingMode) ASSERT_NO_THROW(d_solver.mkRoundingMode(RoundingMode::ROUND_TOWARD_ZERO)); } -TEST_F(TestApiBlackSolver, mkUninterpretedConst) -{ - ASSERT_NO_THROW(d_solver.mkUninterpretedConst(d_solver.getBooleanSort(), 1)); - ASSERT_THROW(d_solver.mkUninterpretedConst(Sort(), 1), CVC5ApiException); - Solver slv; - ASSERT_THROW(slv.mkUninterpretedConst(d_solver.getBooleanSort(), 1), - CVC5ApiException); -} - TEST_F(TestApiBlackSolver, mkAbstractValue) { ASSERT_NO_THROW(d_solver.mkAbstractValue(std::string("1"))); @@ -419,6 +410,17 @@ TEST_F(TestApiBlackSolver, mkFloatingPoint) ASSERT_THROW(slv.mkFloatingPoint(3, 5, t1), CVC5ApiException); } +TEST_F(TestApiBlackSolver, mkCardinalityConstraint) +{ + Sort su = d_solver.mkUninterpretedSort("u"); + Sort si = d_solver.getIntegerSort(); + ASSERT_NO_THROW(d_solver.mkCardinalityConstraint(su, 3)); + ASSERT_THROW(d_solver.mkCardinalityConstraint(si, 3), CVC5ApiException); + ASSERT_THROW(d_solver.mkCardinalityConstraint(su, 0), CVC5ApiException); + Solver slv; + ASSERT_THROW(slv.mkCardinalityConstraint(su, 3), CVC5ApiException); +} + TEST_F(TestApiBlackSolver, mkEmptySet) { Solver slv; @@ -606,6 +608,8 @@ TEST_F(TestApiBlackSolver, mkRegexpSigma) d_solver.mkTerm(STRING_IN_REGEXP, s, d_solver.mkRegexpSigma())); } +TEST_F(TestApiBlackSolver, mkSepEmp) { ASSERT_NO_THROW(d_solver.mkSepEmp()); } + TEST_F(TestApiBlackSolver, mkSepNil) { ASSERT_NO_THROW(d_solver.mkSepNil(d_solver.getBooleanSort())); @@ -1234,6 +1238,23 @@ TEST_F(TestApiBlackSolver, getAbduct) ASSERT_EQ(output2, truen); } +TEST_F(TestApiBlackSolver, getAbduct2) +{ + d_solver.setLogic("QF_LIA"); + d_solver.setOption("incremental", "false"); + Sort intSort = d_solver.getIntegerSort(); + Term zero = d_solver.mkInteger(0); + Term x = d_solver.mkConst(intSort, "x"); + Term y = d_solver.mkConst(intSort, "y"); + // Assumptions for abduction: x > 0 + d_solver.assertFormula(d_solver.mkTerm(GT, x, zero)); + // Conjecture for abduction: y > 0 + Term conj = d_solver.mkTerm(GT, y, zero); + Term output; + // Fails due to option not set + ASSERT_THROW(d_solver.getAbduct(conj, output), CVC5ApiException); +} + TEST_F(TestApiBlackSolver, getInterpolant) { d_solver.setLogic("QF_LIA"); @@ -2536,5 +2557,21 @@ TEST_F(TestApiBlackSolver, Output) ASSERT_NE(cvc5::null_os.rdbuf(), d_solver.getOutput("inst").rdbuf()); } + +TEST_F(TestApiBlackSolver, issue7000) +{ + Sort s1 = d_solver.getIntegerSort(); + Sort s2 = d_solver.mkFunctionSort(s1, s1); + Sort s3 = d_solver.getRealSort(); + Term t4 = d_solver.mkPi(); + Term t7 = d_solver.mkConst(s3, "_x5"); + Term t37 = d_solver.mkConst(s2, "_x32"); + Term t59 = d_solver.mkConst(s2, "_x51"); + Term t72 = d_solver.mkTerm(EQUAL, t37, t59); + Term t74 = d_solver.mkTerm(GT, t4, t7); + // throws logic exception since logic is not higher order by default + ASSERT_THROW(d_solver.checkEntailed({t72, t74, t72, t72}), CVC5ApiException); +} + } // namespace test } // namespace cvc5 diff --git a/test/unit/api/sort_black.cpp b/test/unit/api/sort_black.cpp index 757bacad6..d0c755cf7 100644 --- a/test/unit/api/sort_black.cpp +++ b/test/unit/api/sort_black.cpp @@ -61,6 +61,14 @@ TEST_F(TestApiBlackSort, operators_comparison) ASSERT_NO_THROW(d_solver.getIntegerSort() >= Sort()); } +TEST_F(TestApiBlackSort, isNull) +{ + Sort x; + ASSERT_TRUE(x.isNull()); + x = d_solver.getBooleanSort(); + ASSERT_FALSE(x.isNull()); +} + TEST_F(TestApiBlackSort, isBoolean) { ASSERT_TRUE(d_solver.getBooleanSort().isBoolean()); @@ -470,28 +478,28 @@ TEST_F(TestApiBlackSort, getUninterpretedSortConstructorArity) ASSERT_THROW(bvSort.getSortConstructorArity(), CVC5ApiException); } -TEST_F(TestApiBlackSort, getBVSize) +TEST_F(TestApiBlackSort, getBitVectorSize) { Sort bvSort = d_solver.mkBitVectorSort(32); - ASSERT_NO_THROW(bvSort.getBVSize()); + ASSERT_NO_THROW(bvSort.getBitVectorSize()); Sort setSort = d_solver.mkSetSort(d_solver.getIntegerSort()); - ASSERT_THROW(setSort.getBVSize(), CVC5ApiException); + ASSERT_THROW(setSort.getBitVectorSize(), CVC5ApiException); } -TEST_F(TestApiBlackSort, getFPExponentSize) +TEST_F(TestApiBlackSort, getFloatingPointExponentSize) { Sort fpSort = d_solver.mkFloatingPointSort(4, 8); - ASSERT_NO_THROW(fpSort.getFPExponentSize()); + ASSERT_NO_THROW(fpSort.getFloatingPointExponentSize()); Sort setSort = d_solver.mkSetSort(d_solver.getIntegerSort()); - ASSERT_THROW(setSort.getFPExponentSize(), CVC5ApiException); + ASSERT_THROW(setSort.getFloatingPointExponentSize(), CVC5ApiException); } -TEST_F(TestApiBlackSort, getFPSignificandSize) +TEST_F(TestApiBlackSort, getFloatingPointSignificandSize) { Sort fpSort = d_solver.mkFloatingPointSort(4, 8); - ASSERT_NO_THROW(fpSort.getFPSignificandSize()); + ASSERT_NO_THROW(fpSort.getFloatingPointSignificandSize()); Sort setSort = d_solver.mkSetSort(d_solver.getIntegerSort()); - ASSERT_THROW(setSort.getFPSignificandSize(), CVC5ApiException); + ASSERT_THROW(setSort.getFloatingPointSignificandSize(), CVC5ApiException); } TEST_F(TestApiBlackSort, getDatatypeParamSorts) |