diff --git a/.travis.yml b/.travis.yml index 81ddefb8b..a3ffb221e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,15 @@ env: ############################################################################### # Ubuntu 16.04 LTS ############################################################################### + # 64-bit UBSan Debug build + - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug UBSAN_BUILD=1 RUN_UNIT_TESTS=SKIP + # 64-bit ASan Debug build + - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=Debug ASAN_BUILD=1 RUN_UNIT_TESTS=SKIP ASAN_DSO=/usr/lib/clang/3.9/lib/linux/libclang_rt.asan-x86_64.so + # Build for running unit tests under ASan/UBSan + # FIXME: We should really be doing a debug build but the unit tests run too + # slowly when we do that. + - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/clang-3.9 CXX_COMPILER=/usr/bin/clang++-3.9 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo ASAN_BUILD=1 RUN_UNIT_TESTS=BUILD_AND_RUN ASAN_DSO=/usr/lib/clang/3.9/lib/linux/libclang_rt.asan-x86_64.so UBSAN_BUILD=1 RUN_API_EXAMPLES=0 RUN_SYSTEM_TESTS=0 DOTNET_BINDINGS=0 JAVA_BINDINGS=0 PYTHON_BINDINGS=0 + # 64-bit GCC 5.4 RelWithDebInfo - LINUX_BASE=ubuntu_16.04 C_COMPILER=/usr/bin/gcc-5 CXX_COMPILER=/usr/bin/g++-5 TARGET_ARCH=x86_64 Z3_BUILD_TYPE=RelWithDebInfo # 64-bit Clang 3.9 RelWithDebInfo diff --git a/README-CMake.md b/README-CMake.md index 605c14818..0d323e08f 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -270,6 +270,7 @@ The following useful options can be passed to CMake whilst configuring. * ``API_LOG_SYNC`` - BOOL. If set to ``TRUE`` will enable experimental API log sync feature. * ``WARNINGS_AS_ERRORS`` - STRING. If set to ``TRUE`` compiler warnings will be treated as errors. If set to ``False`` compiler warnings will not be treated as errors. If set to ``SERIOUS_ONLY`` a subset of compiler warnings will be treated as errors. +* ``Z3_C_EXAMPLES_FORCE_CXX_LINKER`` - BOOL. If set to ``TRUE`` the C API examples will request that the C++ linker is used rather than the C linker. On the command line these can be passed to ``cmake`` using the ``-D`` option. In ``ccmake`` and ``cmake-gui`` these can be set in the user interface. diff --git a/README.md b/README.md index 701556cc8..70956d439 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ utility is used to install ``Microsoft.Z3.dll`` into the [pkg-config](http://www.freedesktop.org/wiki/Software/pkg-config/) file (``Microsoft.Z3.Sharp.pc``) is also installed which allows the [MonoDevelop](http://www.monodevelop.com/) IDE to find the bindings. Running -``make uninstall`` will remove the dll from the GAC and the pkg-config file. +``make uninstall`` will remove the dll from the GAC and the ``pkg-config`` file. See [``examples/dotnet``](examples/dotnet) for examples. @@ -170,8 +170,8 @@ If you do need to install to a non standard prefix a better approach is to use a [Python virtual environment](https://virtualenv.readthedocs.org/en/latest/) and install Z3 there. Python packages also work for Python3. Under Windows, recall to build inside the Visual C++ native command build environment. -Note that the buit/python/z3 directory should be accessible from where python is used with Z3 -and it depends on libz3.dll to be in the path. +Note that the ``build/python/z3`` directory should be accessible from where python is used with Z3 +and it depends on ``libz3.dll`` to be in the path. ```bash virtualenv venv diff --git a/contrib/ci/Dockerfiles/z3_base_ubuntu32_16.04.Dockerfile b/contrib/ci/Dockerfiles/z3_base_ubuntu32_16.04.Dockerfile index d8a32edea..87e3c8d67 100644 --- a/contrib/ci/Dockerfiles/z3_base_ubuntu32_16.04.Dockerfile +++ b/contrib/ci/Dockerfiles/z3_base_ubuntu32_16.04.Dockerfile @@ -30,6 +30,7 @@ RUN apt-get update && \ libgomp1 \ libomp5 \ libomp-dev \ + llvm-3.9 \ make \ mono-devel \ ninja-build \ @@ -47,4 +48,4 @@ RUN useradd -m user && \ echo 'user ALL=(root) NOPASSWD: ALL' >> /etc/sudoers USER user WORKDIR /home/user - +ENV ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-3.9/bin/llvm-symbolizer diff --git a/contrib/ci/Dockerfiles/z3_base_ubuntu_14.04.Dockerfile b/contrib/ci/Dockerfiles/z3_base_ubuntu_14.04.Dockerfile index c28e59e97..c963ce255 100644 --- a/contrib/ci/Dockerfiles/z3_base_ubuntu_14.04.Dockerfile +++ b/contrib/ci/Dockerfiles/z3_base_ubuntu_14.04.Dockerfile @@ -16,6 +16,7 @@ RUN apt-get update && \ libgmp-dev \ libgomp1 \ lib32gomp1 \ + llvm-3.9 \ make \ mono-devel \ ninja-build \ @@ -32,4 +33,4 @@ RUN useradd -m user && \ echo 'user ALL=(root) NOPASSWD: ALL' >> /etc/sudoers USER user WORKDIR /home/user - +ENV ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-3.9/bin/llvm-symbolizer diff --git a/contrib/ci/Dockerfiles/z3_base_ubuntu_16.04.Dockerfile b/contrib/ci/Dockerfiles/z3_base_ubuntu_16.04.Dockerfile index 98a5a3e09..08686e275 100644 --- a/contrib/ci/Dockerfiles/z3_base_ubuntu_16.04.Dockerfile +++ b/contrib/ci/Dockerfiles/z3_base_ubuntu_16.04.Dockerfile @@ -18,6 +18,7 @@ RUN apt-get update && \ libgomp1 \ libomp5 \ libomp-dev \ + llvm-3.9 \ make \ mono-devel \ ninja-build \ @@ -35,4 +36,4 @@ RUN useradd -m user && \ echo 'user ALL=(root) NOPASSWD: ALL' >> /etc/sudoers USER user WORKDIR /home/user - +ENV ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-3.9/bin/llvm-symbolizer diff --git a/contrib/ci/Dockerfiles/z3_build.Dockerfile b/contrib/ci/Dockerfiles/z3_build.Dockerfile index 07504e6b9..eb2e03a43 100644 --- a/contrib/ci/Dockerfiles/z3_build.Dockerfile +++ b/contrib/ci/Dockerfiles/z3_build.Dockerfile @@ -5,6 +5,7 @@ FROM ${DOCKER_IMAGE_BASE} # Build arguments. This can be changed when invoking # `docker build`. ARG ASAN_BUILD +ARG ASAN_DSO ARG BUILD_DOCS ARG CC ARG CXX @@ -13,8 +14,10 @@ ARG JAVA_BINDINGS ARG NO_SUPPRESS_OUTPUT ARG PYTHON_BINDINGS ARG PYTHON_EXECUTABLE=/usr/bin/python2.7 +ARG RUN_API_EXAMPLES ARG RUN_SYSTEM_TESTS ARG RUN_UNIT_TESTS +ARG SANITIZER_PRINT_SUPPRESSIONS ARG TARGET_ARCH ARG TEST_INSTALL ARG UBSAN_BUILD @@ -32,6 +35,7 @@ ARG Z3_VERBOSE_BUILD_OUTPUT ENV \ ASAN_BUILD=${ASAN_BUILD} \ + ASAN_DSO=${ASAN_DSO} \ BUILD_DOCS=${BUILD_DOCS} \ CC=${CC} \ CXX=${CXX} \ @@ -40,6 +44,8 @@ ENV \ NO_SUPPRESS_OUTPUT=${NO_SUPPRESS_OUTPUT} \ PYTHON_BINDINGS=${PYTHON_BINDINGS} \ PYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} \ + SANITIZER_PRINT_SUPPRESSIONS=${SANITIZER_PRINT_SUPPRESSIONS} \ + RUN_API_EXAMPLES=${RUN_API_EXAMPLES} \ RUN_SYSTEM_TESTS=${RUN_SYSTEM_TESTS} \ RUN_UNIT_TESTS=${RUN_UNIT_TESTS} \ TARGET_ARCH=${TARGET_ARCH} \ @@ -50,6 +56,7 @@ ENV \ USE_OPENMP=${USE_OPENMP} \ Z3_SRC_DIR=${Z3_SRC_DIR} \ Z3_BUILD_DIR=/home/user/z3_build \ + Z3_BUILD_TYPE=${Z3_BUILD_TYPE} \ Z3_CMAKE_GENERATOR=${Z3_CMAKE_GENERATOR} \ Z3_VERBOSE_BUILD_OUTPUT=${Z3_VERBOSE_BUILD_OUTPUT} \ Z3_STATIC_BUILD=${Z3_STATIC_BUILD} \ @@ -62,7 +69,8 @@ ENV \ # Build Z3 RUN mkdir -p "${Z3_SRC_DIR}" && \ - mkdir -p "${Z3_SRC_DIR}/contrib/ci/scripts" + mkdir -p "${Z3_SRC_DIR}/contrib/ci/scripts" && \ + mkdir -p "${Z3_SRC_DIR}/contrib/suppressions/sanitizers" # Deliberately leave out `contrib` ADD /cmake ${Z3_SRC_DIR}/cmake/ ADD /doc ${Z3_SRC_DIR}/doc/ @@ -89,7 +97,13 @@ RUN ${Z3_SRC_DIR}/contrib/ci/scripts/test_z3_docs.sh # Test examples ADD \ /contrib/ci/scripts/test_z3_examples_cmake.sh \ + /contrib/ci/scripts/sanitizer_env.sh \ ${Z3_SRC_DIR}/contrib/ci/scripts/ +ADD \ + /contrib/suppressions/sanitizers/asan.txt \ + /contrib/suppressions/sanitizers/lsan.txt \ + /contrib/suppressions/sanitizers/ubsan.txt \ + ${Z3_SRC_DIR}/contrib/suppressions/sanitizers/ RUN ${Z3_SRC_DIR}/contrib/ci/scripts/test_z3_examples_cmake.sh # Run unit tests diff --git a/contrib/ci/README.md b/contrib/ci/README.md index 99bbcd7a6..bd1c52792 100644 --- a/contrib/ci/README.md +++ b/contrib/ci/README.md @@ -30,8 +30,10 @@ the future. * `JAVA_BINDINGS` - Build and test Java API bindings (`0` or `1`) * `NO_SUPPRESS_OUTPUT` - Don't suppress output of some commands (`0` or `1`) * `PYTHON_BINDINGS` - Build and test Python API bindings (`0` or `1`) +* `RUN_API_EXAMPLES` - Build and run API examples (`0` or `1`) * `RUN_SYSTEM_TESTS` - Run system tests (`0` or `1`) * `RUN_UNIT_TESTS` - Run unit tests (`BUILD_ONLY` or `BUILD_AND_RUN` or `SKIP`) +* `SANITIZER_PRINT_SUPPRESSIONS` - Show ASan/UBSan suppressions (`0` or `1`) * `TARGET_ARCH` - Target architecture (`x86_64` or `i686`) * `TEST_INSTALL` - Test running `install` target (`0` or `1`) * `UBSAN_BUILD` - Do [UndefinedBehaviourSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html) build (`0` or `1`) diff --git a/contrib/ci/scripts/build_z3_cmake.sh b/contrib/ci/scripts/build_z3_cmake.sh index 76fd0fb84..c1014d5d5 100755 --- a/contrib/ci/scripts/build_z3_cmake.sh +++ b/contrib/ci/scripts/build_z3_cmake.sh @@ -22,6 +22,7 @@ set -o pipefail : ${USE_LTO?"USE_LTO must be specified"} : ${Z3_INSTALL_PREFIX?"Z3_INSTALL_PREFIX must be specified"} : ${Z3_WARNINGS_AS_ERRORS?"Z3_WARNINGS_AS_ERRORS must be specified"} +: ${UBSAN_BUILD?"UBSAN_BUILD must be specified"} ADDITIONAL_Z3_OPTS=() @@ -105,6 +106,16 @@ fi # Set compiler flags source ${SCRIPT_DIR}/set_compiler_flags.sh +if [ "X${UBSAN_BUILD}" = "X1" ]; then + # HACK: When building with UBSan the C++ linker + # must be used to avoid the following linker errors. + # undefined reference to `__ubsan_vptr_type_cache' + # undefined reference to `__ubsan_handle_dynamic_type_cache_miss' + ADDITIONAL_Z3_OPTS+=( \ + '-DZ3_C_EXAMPLES_FORCE_CXX_LINKER=ON' \ + ) +fi + # Sanity check if [ ! -e "${Z3_SRC_DIR}/CMakeLists.txt" ]; then echo "Z3_SRC_DIR is invalid" diff --git a/contrib/ci/scripts/ci_defaults.sh b/contrib/ci/scripts/ci_defaults.sh index d8e9376d8..7a4434bd5 100644 --- a/contrib/ci/scripts/ci_defaults.sh +++ b/contrib/ci/scripts/ci_defaults.sh @@ -9,8 +9,13 @@ export DOTNET_BINDINGS="${DOTNET_BINDINGS:-1}" export JAVA_BINDINGS="${JAVA_BINDINGS:-1}" export NO_SUPPRESS_OUTPUT="${NO_SUPPRESS_OUTPUT:-0}" export PYTHON_BINDINGS="${PYTHON_BINDINGS:-1}" +export RUN_API_EXAMPLES="${RUN_API_EXAMPLES:-1}" export RUN_SYSTEM_TESTS="${RUN_SYSTEM_TESTS:-1}" export RUN_UNIT_TESTS="${RUN_UNIT_TESTS:-BUILD_AND_RUN}" +# Don't print suppressions by default because that breaks the Z3 +# regression tests because they don't expect them to appear in Z3's +# output. +export SANITIZER_PRINT_SUPPRESSIONS="${SANITIZER_PRINT_SUPPRESSIONS:-0}" export TARGET_ARCH="${TARGET_ARCH:-x86_64}" export TEST_INSTALL="${TEST_INSTALL:-1}" export UBSAN_BUILD="${UBSAN_BUILD:-0}" @@ -49,6 +54,7 @@ unset PLATFORM # NOTE: The following variables are not set here because # they are specific to the CI implementation # PYTHON_EXECUTABLE +# ASAN_DSO # Z3_SRC_DIR # Z3_BUILD_DIR # Z3_SYSTEM_TEST_DIR diff --git a/contrib/ci/scripts/run_quiet.sh b/contrib/ci/scripts/run_quiet.sh index 5abc910e8..0f49da3be 100644 --- a/contrib/ci/scripts/run_quiet.sh +++ b/contrib/ci/scripts/run_quiet.sh @@ -34,8 +34,8 @@ function run_quiet() { fi # Clean up rm "${STDOUT}" "${STDERR}" - [ $( echo "${OLD_SETTINGS}" | grep -c 'e') -ne 0 ] && set -e - [ $( echo "${OLD_SETTINGS}" | grep -c 'x') -ne 0 ] && set -x + [ "$( echo "${OLD_SETTINGS}" | grep -c 'e')" != "0" ] && set -e + [ "$( echo "${OLD_SETTINGS}" | grep -c 'x')" != "0" ] && set -x return ${EXIT_STATUS} fi } diff --git a/contrib/ci/scripts/sanitizer_env.sh b/contrib/ci/scripts/sanitizer_env.sh new file mode 100644 index 000000000..f1fa87442 --- /dev/null +++ b/contrib/ci/scripts/sanitizer_env.sh @@ -0,0 +1,82 @@ +# This script is intended to be included by other +# scripts and should not be executed directly + +: ${Z3_SRC_DIR?"Z3_SRC_DIR must be specified"} +: ${ASAN_BUILD?"ASAN_BUILD must be specified"} +: ${UBSAN_BUILD?"UBSAN_BUILD must be specified"} + +if [ "X${ASAN_BUILD}" = "X1" ]; then + # Use suppression files + export LSAN_OPTIONS="suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/lsan.txt" + # NOTE: If you get bad stacktraces try using `fast_unwind_on_malloc=0` + # NOTE: `malloc_context_size` controls size of recorded stacktrace for allocations. + # If the reported stacktraces appear incomplete try increasing the value. + export ASAN_OPTIONS="malloc_context_size=100,suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/asan.txt" + + : ${SANITIZER_PRINT_SUPPRESSIONS?"SANITIZER_PRINT_SUPPRESSIONS must be specified"} + if [ "X${SANITIZER_PRINT_SUPPRESSIONS}" = "X1" ]; then + export LSAN_OPTIONS="${LSAN_OPTIONS},print_suppressions=1" + export ASAN_OPTIONS="${ASAN_OPTIONS},print_suppressions=1" + else + export LSAN_OPTIONS="${LSAN_OPTIONS},print_suppressions=0" + export ASAN_OPTIONS="${ASAN_OPTIONS},print_suppressions=0" + fi + + : ${ASAN_SYMBOLIZER_PATH?"ASAN_SYMBOLIZER_PATH must be specified"} + + # Run command without checking for leaks + function run_no_lsan() { + ASAN_OPTIONS="${ASAN_OPTIONS},detect_leaks=0" "${@}" + } + + # Check path to ASan DSO + : ${ASAN_DSO?"ASAN_DSO must be specified"} + if [ ! -e "${ASAN_DSO}" ]; then + echo "ASAN_DSO (${ASAN_DSO}) does not exist" + exit 1 + fi + # FIXME: We'll need to refactor this when we can do UBSan builds + # against a UBSan DSO. + function run_non_native_binding() { + # We need to preload the ASan DSO that libz3 + # will have undefined references to. + # Don't run leak checking because we get lots reported leaks + # in the language runtime (e.g. python). + PLATFORM="$(uname -s)" + case "${PLATFORM}" in + Linux*) + LD_PRELOAD="${ASAN_DSO}" run_no_lsan "${@}" + ;; + Darwin*) + DYLD_INSERT_LIBRARIES="${ASAN_DSO}" run_no_lsan "${@}" + ;; + *) + echo "Unknown platform \"${PLATFORM}\"" + exit 1 + ;; + esac + unset PLATFORM + } +else + # In non-ASan build just run directly + function run_no_lsan() { + "${@}" + } + function run_non_native_binding() { + "${@}" + } +fi + +if [ "X${UBSAN_BUILD}" = "X1" ]; then + # `halt_on_error=1,abort_on_error=1` means that on the first UBSan error + # the program will terminate by calling `abort(). Without this UBSan will + # allow execution to continue. We also use a suppression file. + export UBSAN_OPTIONS="halt_on_error=1,abort_on_error=1,suppressions=${Z3_SRC_DIR}/contrib/suppressions/sanitizers/ubsan.txt" + + : ${SANITIZER_PRINT_SUPPRESSIONS?"SANITIZER_PRINT_SUPPRESSIONS must be specified"} + if [ "X${SANITIZER_PRINT_SUPPRESSIONS}" = "X1" ]; then + export UBSAN_OPTIONS="${UBSAN_OPTIONS},print_suppressions=1" + else + export UBSAN_OPTIONS="${UBSAN_OPTIONS},print_suppressions=0" + fi +fi diff --git a/contrib/ci/scripts/test_z3_examples_cmake.sh b/contrib/ci/scripts/test_z3_examples_cmake.sh index 2eda3de7b..687efebb4 100755 --- a/contrib/ci/scripts/test_z3_examples_cmake.sh +++ b/contrib/ci/scripts/test_z3_examples_cmake.sh @@ -14,6 +14,13 @@ set -o pipefail : ${PYTHON_EXECUTABLE?"PYTHON_EXECUTABLE must be specified"} : ${DOTNET_BINDINGS?"DOTNET_BINDINGS must be specified"} : ${JAVA_BINDINGS?"JAVA_BINDINGS must be specified"} +: ${UBSAN_BUILD?"UBSAN_BUILD must be specified"} +: ${RUN_API_EXAMPLES?"RUN_API_EXAMPLES must be specified"} + +if [ "X${RUN_API_EXAMPLES}" = "X0" ]; then + echo "Skipping run of API examples" + exit 0 +fi # Set compiler flags source ${SCRIPT_DIR}/set_compiler_flags.sh @@ -21,6 +28,9 @@ source ${SCRIPT_DIR}/set_compiler_flags.sh # Set CMake generator args source ${SCRIPT_DIR}/set_generator_args.sh +# Sanitizer environment variables +source ${SCRIPT_DIR}/sanitizer_env.sh + cd "${Z3_BUILD_DIR}" # Build and run C example @@ -38,9 +48,21 @@ run_quiet examples/tptp_build_dir/z3_tptp5 -help # Build an run c_maxsat_example cmake --build $(pwd) --target c_maxsat_example "${GENERATOR_ARGS[@]}" -run_quiet \ - examples/c_maxsat_example_build_dir/c_maxsat_example \ - ${Z3_SRC_DIR}/examples/maxsat/ex.smt +# FIXME: It is known that the maxsat example leaks memory and the +# the Z3 developers have stated this is "wontfix". +# See https://github.com/Z3Prover/z3/issues/1299 +run_no_lsan \ + run_quiet \ + examples/c_maxsat_example_build_dir/c_maxsat_example \ + ${Z3_SRC_DIR}/examples/maxsat/ex.smt + +if [ "X${UBSAN_BUILD}" = "X1" ]; then + # FIXME: We really need libz3 to link against a shared UBSan runtime. + # Right now we link against the static runtime which breaks all the + # non-native language bindings. + echo "FIXME: Can't run other examples when building with UBSan" + exit 0 +fi if [ "X${PYTHON_BINDINGS}" = "X1" ]; then @@ -48,16 +70,21 @@ if [ "X${PYTHON_BINDINGS}" = "X1" ]; then # `all_interval_series.py` produces a lot of output so just throw # away output. # TODO: This example is slow should we remove it from testing? - run_quiet ${PYTHON_EXECUTABLE} python/all_interval_series.py - run_quiet ${PYTHON_EXECUTABLE} python/complex.py - run_quiet ${PYTHON_EXECUTABLE} python/example.py + if [ "X${ASAN_BUILD}" = "X1" -a "X${Z3_BUILD_TYPE}" = "XDebug" ]; then + # Too slow when doing ASan Debug build + echo "Skipping all_interval_series.py under ASan Debug build" + else + run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/all_interval_series.py + fi + run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/complex.py + run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/example.py # FIXME: `hamiltonian.py` example is disabled because its too slow. #${PYTHON_EXECUTABLE} python/hamiltonian.py - run_quiet ${PYTHON_EXECUTABLE} python/marco.py - run_quiet ${PYTHON_EXECUTABLE} python/mss.py - run_quiet ${PYTHON_EXECUTABLE} python/socrates.py - run_quiet ${PYTHON_EXECUTABLE} python/visitor.py - run_quiet ${PYTHON_EXECUTABLE} python/z3test.py + run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/marco.py + run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/mss.py + run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/socrates.py + run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/visitor.py + run_quiet run_non_native_binding ${PYTHON_EXECUTABLE} python/z3test.py fi if [ "X${DOTNET_BINDINGS}" = "X1" ]; then @@ -65,7 +92,7 @@ if [ "X${DOTNET_BINDINGS}" = "X1" ]; then # FIXME: Move compliation step into CMake target mcs ${Z3_SRC_DIR}/examples/dotnet/Program.cs /target:exe /out:dotnet_test.exe /reference:Microsoft.Z3.dll /r:System.Numerics.dll # Run .NET example - run_quiet mono ./dotnet_test.exe + run_quiet run_non_native_binding mono ./dotnet_test.exe fi if [ "X${JAVA_BINDINGS}" = "X1" ]; then @@ -82,6 +109,14 @@ if [ "X${JAVA_BINDINGS}" = "X1" ]; then # Assume Linux for now export LD_LIBRARY_PATH=$(pwd):${LD_LIBRARY_PATH} fi - run_quiet java -cp .:examples/java:com.microsoft.z3.jar JavaExample + if [ "X${ASAN_BUILD}" = "X1" ]; then + # The JVM seems to crash (SEGV) if we pre-load ASan + # so don't run it for now. + echo "Skipping JavaExample under ASan build" + else + run_quiet \ + run_non_native_binding \ + java -cp .:examples/java:com.microsoft.z3.jar JavaExample + fi fi diff --git a/contrib/ci/scripts/test_z3_system_tests.sh b/contrib/ci/scripts/test_z3_system_tests.sh index dfb1084a4..19c179268 100755 --- a/contrib/ci/scripts/test_z3_system_tests.sh +++ b/contrib/ci/scripts/test_z3_system_tests.sh @@ -10,12 +10,17 @@ set -o pipefail : ${PYTHON_BINDINGS?"PYTHON_BINDINGS must be specified"} : ${PYTHON_EXECUTABLE?"PYTHON_EXECUTABLE must be specified"} : ${Z3_SYSTEM_TEST_DIR?"Z3_SYSTEM_TEST_DIR must be specified"} +: ${UBSAN_BUILD?"UBSAN_BUILD must be specified"} if [ "X${RUN_SYSTEM_TESTS}" != "X1" ]; then echo "Skipping system tests" exit 0 fi +# Sanitizer environment variables +SCRIPT_DIR="$( cd ${BASH_SOURCE[0]%/*} ; echo $PWD )" +source ${SCRIPT_DIR}/sanitizer_env.sh + Z3_EXE="${Z3_BUILD_DIR}/z3" Z3_LIB_DIR="${Z3_BUILD_DIR}" @@ -23,7 +28,7 @@ Z3_LIB_DIR="${Z3_BUILD_DIR}" Z3_SYSTEM_TEST_GIT_URL="${Z3_GIT_URL:-https://github.com/Z3Prover/z3test.git}" # Clone repo to destination -mkdir -p "${Z3_SYSTEM_TEST_GIT_URL}" +mkdir -p "${Z3_SYSTEM_TEST_DIR}" git clone "${Z3_SYSTEM_TEST_GIT_URL}" "${Z3_SYSTEM_TEST_DIR}" cd "${Z3_SYSTEM_TEST_DIR}" @@ -48,7 +53,18 @@ fi if [ "X${PYTHON_BINDINGS}" = "X1" ]; then # Run python binding tests - ${PYTHON_EXECUTABLE} scripts/test_pyscripts.py "${Z3_LIB_DIR}" regressions/python/ + if [ "X${UBSAN_BUILD}" = "X1" ]; then + # FIXME: We need to build libz3 with a shared UBSan runtime for the bindings + # to work. + echo "FIXME: Skipping python binding tests when building with UBSan" + elif [ "X${ASAN_BUILD}" = "X1" ]; then + # FIXME: The `test_pyscripts.py` doesn't propagate LD_PRELOAD + # so under ASan the tests fail to run + # to work. + echo "FIXME: Skipping python binding tests when building with ASan" + else + run_non_native_binding ${PYTHON_EXECUTABLE} scripts/test_pyscripts.py "${Z3_LIB_DIR}" regressions/python/ + fi fi # FIXME: Run `scripts/test_cs.py` once it has been modified to support mono diff --git a/contrib/ci/scripts/test_z3_unit_tests_cmake.sh b/contrib/ci/scripts/test_z3_unit_tests_cmake.sh index e1c927f58..60c29556b 100755 --- a/contrib/ci/scripts/test_z3_unit_tests_cmake.sh +++ b/contrib/ci/scripts/test_z3_unit_tests_cmake.sh @@ -13,6 +13,9 @@ set -o pipefail # Set CMake generator args source ${SCRIPT_DIR}/set_generator_args.sh +# Sanitizer environment variables +source ${SCRIPT_DIR}/sanitizer_env.sh + cd "${Z3_BUILD_DIR}" function build_unit_tests() { diff --git a/contrib/ci/scripts/travis_ci_linux_entry_point.sh b/contrib/ci/scripts/travis_ci_linux_entry_point.sh index bd2c9d2d1..731ea9ff0 100755 --- a/contrib/ci/scripts/travis_ci_linux_entry_point.sh +++ b/contrib/ci/scripts/travis_ci_linux_entry_point.sh @@ -84,6 +84,14 @@ if [ -n "${ASAN_BUILD}" ]; then BUILD_OPTS+=("--build-arg" "ASAN_BUILD=${ASAN_BUILD}") fi +if [ -n "${ASAN_DSO}" ]; then + BUILD_OPTS+=("--build-arg" "ASAN_DSO=${ASAN_DSO}") +fi + +if [ -n "${SANITIZER_PRINT_SUPPRESSIONS}" ]; then + BUILD_OPTS+=("--build-arg" "SANITIZER_PRINT_SUPPRESSIONS=${SANITIZER_PRINT_SUPPRESSIONS}") +fi + if [ -n "${UBSAN_BUILD}" ]; then BUILD_OPTS+=("--build-arg" "UBSAN_BUILD=${UBSAN_BUILD}") fi @@ -92,6 +100,10 @@ if [ -n "${TEST_INSTALL}" ]; then BUILD_OPTS+=("--build-arg" "TEST_INSTALL=${TEST_INSTALL}") fi +if [ -n "${RUN_API_EXAMPLES}" ]; then + BUILD_OPTS+=("--build-arg" "RUN_API_EXAMPLES=${RUN_API_EXAMPLES}") +fi + if [ -n "${RUN_SYSTEM_TESTS}" ]; then BUILD_OPTS+=("--build-arg" "RUN_SYSTEM_TESTS=${RUN_SYSTEM_TESTS}") fi diff --git a/contrib/suppressions/README.md b/contrib/suppressions/README.md new file mode 100644 index 000000000..90df39084 --- /dev/null +++ b/contrib/suppressions/README.md @@ -0,0 +1,7 @@ +# Suppression files + +This directory contains suppression files used by various +program analysis tools. + +Suppression files tell a program analysis tool to suppress +various warnings/errors. diff --git a/contrib/suppressions/maintainers.txt b/contrib/suppressions/maintainers.txt new file mode 100644 index 000000000..caa6798c6 --- /dev/null +++ b/contrib/suppressions/maintainers.txt @@ -0,0 +1,3 @@ +# Maintainers + +- Dan Liew (@delcypher) diff --git a/contrib/suppressions/sanitizers/README.md b/contrib/suppressions/sanitizers/README.md new file mode 100644 index 000000000..f76f920b2 --- /dev/null +++ b/contrib/suppressions/sanitizers/README.md @@ -0,0 +1,4 @@ +# Sanitizer supression files + +This directory contains files used to suppress +ASan/LSan/UBSan warnings/errors. diff --git a/contrib/suppressions/sanitizers/asan.txt b/contrib/suppressions/sanitizers/asan.txt new file mode 100644 index 000000000..f058adfe2 --- /dev/null +++ b/contrib/suppressions/sanitizers/asan.txt @@ -0,0 +1 @@ +# AddressSanitizer suppression file diff --git a/contrib/suppressions/sanitizers/lsan.txt b/contrib/suppressions/sanitizers/lsan.txt new file mode 100644 index 000000000..1480b7c09 --- /dev/null +++ b/contrib/suppressions/sanitizers/lsan.txt @@ -0,0 +1,5 @@ +# LeakSanitizer suppression file + +# Ignore Clang OpenMP leaks. +# See https://github.com/Z3Prover/z3/issues/1308 +leak:___kmp_allocate diff --git a/contrib/suppressions/sanitizers/ubsan.txt b/contrib/suppressions/sanitizers/ubsan.txt new file mode 100644 index 000000000..4d19e40be --- /dev/null +++ b/contrib/suppressions/sanitizers/ubsan.txt @@ -0,0 +1,7 @@ +# UndefinedBehavior sanitizer suppression file +# FIXME: UBSan doesn't usually have false positives so we need to fix all of these! + +# Occurs when running tptp example +# See https://github.com/Z3Prover/z3/issues/964 +null:rational.h +null:mpq.h diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index ba7f6ee59..6c50320ed 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -7,6 +7,30 @@ else() set(EXTERNAL_PROJECT_BUILD_ALWAYS_ARG "") endif() +option(Z3_C_EXAMPLES_FORCE_CXX_LINKER + "Force C++ linker when building C example projects" OFF) + +if (Z3_C_EXAMPLES_FORCE_CXX_LINKER) + # HACK: This is a workaround for UBSan. + message(STATUS "Forcing C++ linker to be used when building example C projects") + set(EXTERNAL_C_PROJ_USE_CXX_LINKER_ARG + "-DFORCE_CXX_LINKER=ON" + ) +else() + set(EXTERNAL_C_PROJ_USE_CXX_LINKER_ARG "") +endif() + +if (DEFINED CMAKE_CONFIGURATION_TYPES) + message(WARNING + "Cannot set built type of external project when building with a " + "multi-configuration generator") + set(EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG "") +else() + set(EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG + "-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}" + ) +endif() + ################################################################################ # Build example project using libz3's C API as an external project ################################################################################ @@ -14,7 +38,10 @@ ExternalProject_Add(c_example DEPENDS libz3 # Configure step SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/c" - CMAKE_ARGS "-DZ3_DIR=${CMAKE_BINARY_DIR}" + CMAKE_ARGS + "-DZ3_DIR=${CMAKE_BINARY_DIR}" + "${EXTERNAL_C_PROJ_USE_CXX_LINKER_ARG}" + "${EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG}" # Build step ${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG} BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/c_example_build_dir" @@ -30,7 +57,10 @@ ExternalProject_Add(c_maxsat_example DEPENDS libz3 # Configure step SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/maxsat" - CMAKE_ARGS "-DZ3_DIR=${CMAKE_BINARY_DIR}" + CMAKE_ARGS + "-DZ3_DIR=${CMAKE_BINARY_DIR}" + "${EXTERNAL_C_PROJ_USE_CXX_LINKER_ARG}" + "${EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG}" # Build step ${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG} BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/c_maxsat_example_build_dir" @@ -47,7 +77,9 @@ ExternalProject_Add(cpp_example DEPENDS libz3 # Configure step SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/c++" - CMAKE_ARGS "-DZ3_DIR=${CMAKE_BINARY_DIR}" + CMAKE_ARGS + "-DZ3_DIR=${CMAKE_BINARY_DIR}" + "${EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG}" # Build step ${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG} BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/cpp_example_build_dir" @@ -63,7 +95,9 @@ ExternalProject_Add(z3_tptp5 DEPENDS libz3 # Configure step SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tptp" - CMAKE_ARGS "-DZ3_DIR=${CMAKE_BINARY_DIR}" + CMAKE_ARGS + "-DZ3_DIR=${CMAKE_BINARY_DIR}" + "${EXTERNAL_PROJECT_CMAKE_BUILD_TYPE_ARG}" # Build step ${EXTERNAL_PROJECT_BUILD_ALWAYS_ARG} BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/tptp_build_dir" diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index dd8fa6328..c47a4947a 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -7,6 +7,19 @@ # the C++ standard library in resulting in a link failure. project(Z3_C_EXAMPLE C CXX) cmake_minimum_required(VERSION 2.8.12) + +# Set C version required to C99 +if ("${CMAKE_VERSION}" VERSION_LESS "3.1") + if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR + ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 ") + endif() +else() + set(CMAKE_C_STANDARD_REQUIRED ON) + set(CMAKE_C_STANDARD 99) + set(CMAKE_C_EXTENSIONS OFF) +endif() + find_package(Z3 REQUIRED CONFIG @@ -22,6 +35,17 @@ message(STATUS "Found Z3 ${Z3_VERSION_STRING}") message(STATUS "Z3_DIR: ${Z3_DIR}") add_executable(c_example test_capi.c) + +option(FORCE_CXX_LINKER "Force linker with C++ linker" OFF) +if (FORCE_CXX_LINKER) + # This is a hack for avoiding UBSan linking errors + message(STATUS "Forcing use of C++ linker") + set_target_properties(c_example + PROPERTIES + LINKER_LANGUAGE CXX + ) +endif() + target_include_directories(c_example PRIVATE ${Z3_C_INCLUDE_DIRS}) target_link_libraries(c_example PRIVATE ${Z3_LIBRARIES}) diff --git a/examples/c/test_capi.c b/examples/c/test_capi.c index 62aac110d..53c0caa9b 100644 --- a/examples/c/test_capi.c +++ b/examples/c/test_capi.c @@ -2771,80 +2771,279 @@ void fpa_example() { double_sort = Z3_mk_fpa_sort(ctx, 11, 53); rm_sort = Z3_mk_fpa_rounding_mode_sort(ctx); - // Show that there are x, y s.t. (x + y) = 42.0 (with rounding mode). - s_rm = Z3_mk_string_symbol(ctx, "rm"); - rm = Z3_mk_const(ctx, s_rm, rm_sort); - s_x = Z3_mk_string_symbol(ctx, "x"); - s_y = Z3_mk_string_symbol(ctx, "y"); - x = Z3_mk_const(ctx, s_x, double_sort); - y = Z3_mk_const(ctx, s_y, double_sort); - n = Z3_mk_fpa_numeral_double(ctx, 42.0, double_sort); - - s_x_plus_y = Z3_mk_string_symbol(ctx, "x_plus_y"); - x_plus_y = Z3_mk_const(ctx, s_x_plus_y, double_sort); - c1 = Z3_mk_eq(ctx, x_plus_y, Z3_mk_fpa_add(ctx, rm, x, y)); - - args[0] = c1; - args[1] = Z3_mk_eq(ctx, x_plus_y, n); - c2 = Z3_mk_and(ctx, 2, (Z3_ast*)&args); - - args2[0] = c2; - args2[1] = Z3_mk_not(ctx, Z3_mk_eq(ctx, rm, Z3_mk_fpa_rtz(ctx))); - c3 = Z3_mk_and(ctx, 2, (Z3_ast*)&args2); - - and_args[0] = Z3_mk_not(ctx, Z3_mk_fpa_is_zero(ctx, y)); - and_args[1] = Z3_mk_not(ctx, Z3_mk_fpa_is_nan(ctx, y)); - and_args[2] = Z3_mk_not(ctx, Z3_mk_fpa_is_infinite(ctx, y)); - args3[0] = c3; - args3[1] = Z3_mk_and(ctx, 3, and_args); - c4 = Z3_mk_and(ctx, 2, (Z3_ast*)&args3); - - printf("c4: %s\n", Z3_ast_to_string(ctx, c4)); - Z3_solver_push(ctx, s); - Z3_solver_assert(ctx, s, c4); - check(ctx, s, Z3_L_TRUE); - Z3_solver_pop(ctx, s, 1); - - // Show that the following are equal: - // (fp #b0 #b10000000001 #xc000000000000) - // ((_ to_fp 11 53) #x401c000000000000)) - // ((_ to_fp 11 53) RTZ 1.75 2))) - // ((_ to_fp 11 53) RTZ 7.0))) - - Z3_solver_push(ctx, s); - c1 = Z3_mk_fpa_fp(ctx, - Z3_mk_numeral(ctx, "0", Z3_mk_bv_sort(ctx, 1)), - Z3_mk_numeral(ctx, "1025", Z3_mk_bv_sort(ctx, 11)), + // Show that there are x, y s.t. (x + y) = 42.0 (with rounding mode). + s_rm = Z3_mk_string_symbol(ctx, "rm"); + rm = Z3_mk_const(ctx, s_rm, rm_sort); + s_x = Z3_mk_string_symbol(ctx, "x"); + s_y = Z3_mk_string_symbol(ctx, "y"); + x = Z3_mk_const(ctx, s_x, double_sort); + y = Z3_mk_const(ctx, s_y, double_sort); + n = Z3_mk_fpa_numeral_double(ctx, 42.0, double_sort); + + s_x_plus_y = Z3_mk_string_symbol(ctx, "x_plus_y"); + x_plus_y = Z3_mk_const(ctx, s_x_plus_y, double_sort); + c1 = Z3_mk_eq(ctx, x_plus_y, Z3_mk_fpa_add(ctx, rm, x, y)); + + args[0] = c1; + args[1] = Z3_mk_eq(ctx, x_plus_y, n); + c2 = Z3_mk_and(ctx, 2, (Z3_ast*)&args); + + args2[0] = c2; + args2[1] = Z3_mk_not(ctx, Z3_mk_eq(ctx, rm, Z3_mk_fpa_rtz(ctx))); + c3 = Z3_mk_and(ctx, 2, (Z3_ast*)&args2); + + and_args[0] = Z3_mk_not(ctx, Z3_mk_fpa_is_zero(ctx, y)); + and_args[1] = Z3_mk_not(ctx, Z3_mk_fpa_is_nan(ctx, y)); + and_args[2] = Z3_mk_not(ctx, Z3_mk_fpa_is_infinite(ctx, y)); + args3[0] = c3; + args3[1] = Z3_mk_and(ctx, 3, and_args); + c4 = Z3_mk_and(ctx, 2, (Z3_ast*)&args3); + + printf("c4: %s\n", Z3_ast_to_string(ctx, c4)); + Z3_solver_push(ctx, s); + Z3_solver_assert(ctx, s, c4); + check(ctx, s, Z3_L_TRUE); + Z3_solver_pop(ctx, s, 1); + + // Show that the following are equal: + // (fp #b0 #b10000000001 #xc000000000000) + // ((_ to_fp 11 53) #x401c000000000000)) + // ((_ to_fp 11 53) RTZ 1.75 2))) + // ((_ to_fp 11 53) RTZ 7.0))) + + Z3_solver_push(ctx, s); + c1 = Z3_mk_fpa_fp(ctx, + Z3_mk_numeral(ctx, "0", Z3_mk_bv_sort(ctx, 1)), + Z3_mk_numeral(ctx, "1025", Z3_mk_bv_sort(ctx, 11)), Z3_mk_numeral(ctx, "3377699720527872", Z3_mk_bv_sort(ctx, 52))); - c2 = Z3_mk_fpa_to_fp_bv(ctx, - Z3_mk_numeral(ctx, "4619567317775286272", Z3_mk_bv_sort(ctx, 64)), - Z3_mk_fpa_sort(ctx, 11, 53)); + c2 = Z3_mk_fpa_to_fp_bv(ctx, + Z3_mk_numeral(ctx, "4619567317775286272", Z3_mk_bv_sort(ctx, 64)), + Z3_mk_fpa_sort(ctx, 11, 53)); c3 = Z3_mk_fpa_to_fp_int_real(ctx, Z3_mk_fpa_rtz(ctx), - Z3_mk_numeral(ctx, "2", Z3_mk_int_sort(ctx)), /* exponent */ + Z3_mk_numeral(ctx, "2", Z3_mk_int_sort(ctx)), /* exponent */ Z3_mk_numeral(ctx, "1.75", Z3_mk_real_sort(ctx)), /* significand */ Z3_mk_fpa_sort(ctx, 11, 53)); c4 = Z3_mk_fpa_to_fp_real(ctx, - Z3_mk_fpa_rtz(ctx), - Z3_mk_numeral(ctx, "7.0", Z3_mk_real_sort(ctx)), - Z3_mk_fpa_sort(ctx, 11, 53)); - args3[0] = Z3_mk_eq(ctx, c1, c2); - args3[1] = Z3_mk_eq(ctx, c1, c3); - args3[2] = Z3_mk_eq(ctx, c1, c4); - c5 = Z3_mk_and(ctx, 3, args3); - - printf("c5: %s\n", Z3_ast_to_string(ctx, c5)); - Z3_solver_assert(ctx, s, c5); - check(ctx, s, Z3_L_TRUE); - Z3_solver_pop(ctx, s, 1); - + Z3_mk_fpa_rtz(ctx), + Z3_mk_numeral(ctx, "7.0", Z3_mk_real_sort(ctx)), + Z3_mk_fpa_sort(ctx, 11, 53)); + args3[0] = Z3_mk_eq(ctx, c1, c2); + args3[1] = Z3_mk_eq(ctx, c1, c3); + args3[2] = Z3_mk_eq(ctx, c1, c4); + c5 = Z3_mk_and(ctx, 3, args3); + + printf("c5: %s\n", Z3_ast_to_string(ctx, c5)); + Z3_solver_assert(ctx, s, c5); + check(ctx, s, Z3_L_TRUE); + Z3_solver_pop(ctx, s, 1); + del_solver(ctx, s); Z3_del_context(ctx); } +/** + \brief Demonstrates some basic features of model construction +*/ + +void mk_model_example() { + Z3_context ctx; + Z3_model m; + Z3_sort intSort; + Z3_symbol aSymbol, bSymbol, cSymbol; + Z3_func_decl aFuncDecl, bFuncDecl, cFuncDecl; + Z3_ast aApp, bApp, cApp; + Z3_sort int2intArraySort; + Z3_ast zeroNumeral, oneNumeral, twoNumeral, threeNumeral, fourNumeral; + Z3_sort arrayDomain[1]; + Z3_func_decl cAsFuncDecl; + Z3_func_interp cAsFuncInterp; + Z3_ast_vector zeroArgs; + Z3_ast_vector oneArgs; + Z3_ast cFuncDeclAsArray; + Z3_string modelAsString; + + printf("\nmk_model_example\n"); + ctx = mk_context(); + // Construct empty model + m = Z3_mk_model(ctx); + Z3_model_inc_ref(ctx, m); + + // Create constants "a" and "b" + intSort = Z3_mk_int_sort(ctx); + aSymbol = Z3_mk_string_symbol(ctx, "a"); + aFuncDecl = Z3_mk_func_decl(ctx, aSymbol, + /*domain_size=*/0, + /*domain=*/NULL, + /*range=*/intSort); + aApp = Z3_mk_app(ctx, aFuncDecl, + /*num_args=*/0, + /*args=*/NULL); + bSymbol = Z3_mk_string_symbol(ctx, "b"); + bFuncDecl = Z3_mk_func_decl(ctx, bSymbol, + /*domain_size=*/0, + /*domain=*/NULL, + /*range=*/intSort); + bApp = Z3_mk_app(ctx, bFuncDecl, + /*num_args=*/0, + /*args=*/NULL); + + // Create array "c" that maps int to int. + cSymbol = Z3_mk_string_symbol(ctx, "c"); + int2intArraySort = Z3_mk_array_sort(ctx, + /*domain=*/intSort, + /*range=*/intSort); + cFuncDecl = Z3_mk_func_decl(ctx, cSymbol, + /*domain_size=*/0, + /*domain=*/NULL, + /*range=*/int2intArraySort); + cApp = Z3_mk_app(ctx, cFuncDecl, + /*num_args=*/0, + /*args=*/NULL); + + // Create numerals to be used in model + zeroNumeral = Z3_mk_int(ctx, 0, intSort); + oneNumeral = Z3_mk_int(ctx, 1, intSort); + twoNumeral = Z3_mk_int(ctx, 2, intSort); + threeNumeral = Z3_mk_int(ctx, 3, intSort); + fourNumeral = Z3_mk_int(ctx, 4, intSort); + + // Add assignments to model + // a == 1 + Z3_add_const_interp(ctx, m, aFuncDecl, oneNumeral); + // b == 2 + Z3_add_const_interp(ctx, m, bFuncDecl, twoNumeral); + + // Create a fresh function that represents + // reading from array. + arrayDomain[0] = intSort; + cAsFuncDecl = Z3_mk_fresh_func_decl(ctx, + /*prefix=*/"", + /*domain_size*/ 1, + /*domain=*/arrayDomain, + /*sort=*/intSort); + // Create function interpretation with default + // value of "0". + cAsFuncInterp = + Z3_add_func_interp(ctx, m, cAsFuncDecl, + /*default_value=*/zeroNumeral); + Z3_func_interp_inc_ref(ctx, cAsFuncInterp); + // Add [0] = 3 + zeroArgs = Z3_mk_ast_vector(ctx); + Z3_ast_vector_inc_ref(ctx, zeroArgs); + Z3_ast_vector_push(ctx, zeroArgs, zeroNumeral); + Z3_func_interp_add_entry(ctx, cAsFuncInterp, zeroArgs, threeNumeral); + // Add [1] = 4 + oneArgs = Z3_mk_ast_vector(ctx); + Z3_ast_vector_inc_ref(ctx, oneArgs); + Z3_ast_vector_push(ctx, oneArgs, oneNumeral); + Z3_func_interp_add_entry(ctx, cAsFuncInterp, oneArgs, fourNumeral); + + // Now use the `(_ as_array)` to associate + // the `cAsFuncInterp` with the `cFuncDecl` + // in the model + cFuncDeclAsArray = Z3_mk_as_array(ctx, cAsFuncDecl); + Z3_add_const_interp(ctx, m, cFuncDecl, cFuncDeclAsArray); + + // Print the model + modelAsString = Z3_model_to_string(ctx, m); + printf("Model:\n%s\n", modelAsString); + + // Check the interpretations we expect to be present + // are. + { + Z3_func_decl expectedInterpretations[3] = {aFuncDecl, bFuncDecl, cFuncDecl}; + int index; + for (index = 0; + index < sizeof(expectedInterpretations) / sizeof(Z3_func_decl); + ++index) { + Z3_func_decl d = expectedInterpretations[index]; + if (Z3_model_has_interp(ctx, m, d)) { + printf("Found interpretation for \"%s\"\n", + Z3_ast_to_string(ctx, Z3_func_decl_to_ast(ctx, d))); + } else { + printf("Missing interpretation"); + exit(1); + } + } + } + + { + // Evaluate a + b under model + Z3_ast addArgs[] = {aApp, bApp}; + Z3_ast aPlusB = Z3_mk_add(ctx, + /*num_args=*/2, + /*args=*/addArgs); + Z3_ast aPlusBEval = NULL; + Z3_bool aPlusBEvalSuccess = + Z3_model_eval(ctx, m, aPlusB, + /*model_completion=*/Z3_FALSE, &aPlusBEval); + if (aPlusBEvalSuccess != Z3_TRUE) { + printf("Failed to evaluate model\n"); + exit(1); + } + + { + int aPlusBValue = 0; + Z3_bool getAPlusBValueSuccess = + Z3_get_numeral_int(ctx, aPlusBEval, &aPlusBValue); + if (getAPlusBValueSuccess != Z3_TRUE) { + printf("Failed to get integer value for a+b\n"); + exit(1); + } + printf("Evaluated a + b = %d\n", aPlusBValue); + if (aPlusBValue != 3) { + printf("a+b did not evaluate to expected value\n"); + exit(1); + } + } + } + + { + // Evaluate c[0] + c[1] + c[2] under model + Z3_ast c0 = Z3_mk_select(ctx, cApp, zeroNumeral); + Z3_ast c1 = Z3_mk_select(ctx, cApp, oneNumeral); + Z3_ast c2 = Z3_mk_select(ctx, cApp, twoNumeral); + Z3_ast arrayAddArgs[] = {c0, c1, c2}; + Z3_ast arrayAdd = Z3_mk_add(ctx, + /*num_args=*/3, + /*args=*/arrayAddArgs); + Z3_ast arrayAddEval = NULL; + Z3_bool arrayAddEvalSuccess = + Z3_model_eval(ctx, m, arrayAdd, + /*model_completion=*/Z3_FALSE, &arrayAddEval); + if (arrayAddEvalSuccess != Z3_TRUE) { + printf("Failed to evaluate model\n"); + exit(1); + } + { + int arrayAddValue = 0; + Z3_bool getArrayAddValueSuccess = + Z3_get_numeral_int(ctx, arrayAddEval, &arrayAddValue); + if (getArrayAddValueSuccess != Z3_TRUE) { + printf("Failed to get integer value for c[0] + c[1] + c[2]\n"); + exit(1); + } + printf("Evaluated c[0] + c[1] + c[2] = %d\n", arrayAddValue); + if (arrayAddValue != 7) { + printf("c[0] + c[1] + c[2] did not evaluate to expected value\n"); + exit(1); + } + } + } + + Z3_ast_vector_dec_ref(ctx, oneArgs); + Z3_ast_vector_dec_ref(ctx, zeroArgs); + Z3_func_interp_dec_ref(ctx, cAsFuncInterp); + Z3_model_dec_ref(ctx, m); + Z3_del_context(ctx); +} + /*@}*/ /*@}*/ + + int main() { #ifdef LOG_Z3_CALLS Z3_open_log("z3.log"); @@ -2888,5 +3087,6 @@ int main() { substitute_example(); substitute_vars_example(); fpa_example(); + mk_model_example(); return 0; } diff --git a/examples/maxsat/CMakeLists.txt b/examples/maxsat/CMakeLists.txt index b48e167ea..019243ecf 100644 --- a/examples/maxsat/CMakeLists.txt +++ b/examples/maxsat/CMakeLists.txt @@ -25,6 +25,16 @@ add_executable(c_maxsat_example maxsat.c) target_include_directories(c_maxsat_example PRIVATE ${Z3_C_INCLUDE_DIRS}) target_link_libraries(c_maxsat_example PRIVATE ${Z3_LIBRARIES}) +option(FORCE_CXX_LINKER "Force linker with C++ linker" OFF) +if (FORCE_CXX_LINKER) + # This is a hack for avoiding UBSan linking errors + message(STATUS "Forcing use of C++ linker") + set_target_properties(c_maxsat_example + PROPERTIES + LINKER_LANGUAGE CXX + ) +endif() + if ("${CMAKE_SYSTEM_NAME}" MATCHES "[Ww]indows") # On Windows we need to copy the Z3 libraries # into the same directory as the executable diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 923b948a6..05190f217 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -37,20 +37,20 @@ def init_project_def(): add_lib('nlsat_tactic', ['nlsat', 'sat_tactic', 'arith_tactics'], 'nlsat/tactic') add_lib('subpaving_tactic', ['core_tactics', 'subpaving'], 'math/subpaving/tactic') add_lib('aig_tactic', ['tactic'], 'tactic/aig') - add_lib('solver', ['model', 'tactic']) + add_lib('proofs', ['rewriter', 'util'], 'ast/proofs') + add_lib('solver', ['model', 'tactic', 'proofs']) add_lib('ackermannization', ['model', 'rewriter', 'ast', 'solver', 'tactic'], 'ackermannization') add_lib('interp', ['solver']) add_lib('cmd_context', ['solver', 'rewriter', 'interp']) add_lib('extra_cmds', ['cmd_context', 'subpaving_tactic', 'arith_tactics'], 'cmd_context/extra_cmds') add_lib('smt2parser', ['cmd_context', 'parser_util'], 'parsers/smt2') - add_lib('proof_checker', ['rewriter'], 'ast/proof_checker') add_lib('fpa', ['ast', 'util', 'rewriter', 'model'], 'ast/fpa') add_lib('pattern', ['normal_forms', 'smt2parser', 'rewriter'], 'ast/pattern') add_lib('bit_blaster', ['rewriter', 'rewriter'], 'ast/rewriter/bit_blaster') add_lib('smt_params', ['ast', 'rewriter', 'pattern', 'bit_blaster'], 'smt/params') add_lib('proto_model', ['model', 'rewriter', 'smt_params'], 'smt/proto_model') add_lib('smt', ['bit_blaster', 'macros', 'normal_forms', 'cmd_context', 'proto_model', - 'substitution', 'grobner', 'euclid', 'simplex', 'proof_checker', 'pattern', 'parser_util', 'fpa', 'lp']) + 'substitution', 'grobner', 'euclid', 'simplex', 'proofs', 'pattern', 'parser_util', 'fpa', 'lp']) add_lib('bv_tactics', ['tactic', 'bit_blaster', 'core_tactics'], 'tactic/bv') add_lib('fuzzing', ['ast'], 'test/fuzzing') add_lib('smt_tactic', ['smt'], 'smt/tactic') diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 305395833..52d01e95e 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -72,6 +72,7 @@ IS_FREEBSD=False IS_OPENBSD=False IS_CYGWIN=False IS_CYGWIN_MINGW=False +IS_MSYS2=False VERBOSE=True DEBUG_MODE=False SHOW_CPPS = True @@ -152,6 +153,9 @@ def is_cygwin(): def is_cygwin_mingw(): return IS_CYGWIN_MINGW +def is_msys2(): + return IS_MSYS2 + def norm_path(p): return os.path.expanduser(os.path.normpath(p)) @@ -227,7 +231,7 @@ def rmf(fname): def exec_compiler_cmd(cmd): r = exec_cmd(cmd) - if is_windows() or is_cygwin_mingw(): + if is_windows() or is_cygwin_mingw() or is_cygwin() or is_msys2(): rmf('a.exe') else: rmf('a.out') @@ -606,6 +610,13 @@ elif os.name == 'posix': IS_CYGWIN=True if (CC != None and "mingw" in CC): IS_CYGWIN_MINGW=True + elif os.uname()[0].startswith('MSYS_NT'): + IS_MSYS2=True + if os.uname()[4] == 'x86_64': + LINUX_X64=True + else: + LINUX_X64=False + def display_help(exit_code): print("mk_make.py: Z3 Makefile generator\n") @@ -1229,7 +1240,7 @@ def get_so_ext(): sysname = os.uname()[0] if sysname == 'Darwin': return 'dylib' - elif sysname == 'Linux' or sysname == 'FreeBSD' or sysname == 'OpenBSD': + elif sysname == 'Linux' or sysname == 'FreeBSD' or sysname == 'OpenBSD' or sysname.startswith('MSYS_NT'): return 'so' elif sysname == 'CYGWIN': return 'dll' @@ -1783,6 +1794,8 @@ class JavaDLLComponent(Component): t = t.replace('PLATFORM', 'openbsd') elif IS_CYGWIN: t = t.replace('PLATFORM', 'cygwin') + elif IS_MSYS2: + t = t.replace('PLATFORM', 'win32') else: t = t.replace('PLATFORM', 'win32') out.write(t) @@ -2446,7 +2459,7 @@ def mk_config(): if sysname == 'Darwin': SO_EXT = '.dylib' SLIBFLAGS = '-dynamiclib' - elif sysname == 'Linux': + elif sysname == 'Linux' or sysname.startswith('MSYS_NT'): CXXFLAGS = '%s -D_LINUX_' % CXXFLAGS OS_DEFINES = '-D_LINUX_' SO_EXT = '.so' @@ -2466,10 +2479,10 @@ def mk_config(): SO_EXT = '.so' SLIBFLAGS = '-shared' elif sysname[:6] == 'CYGWIN': - CXXFLAGS = '%s -D_CYGWIN' % CXXFLAGS + CXXFLAGS = '%s -D_CYGWIN' % CXXFLAGS OS_DEFINES = '-D_CYGWIN' - SO_EXT = '.dll' - SLIBFLAGS = '-shared' + SO_EXT = '.dll' + SLIBFLAGS = '-shared' else: raise MKException('Unsupported platform: %s' % sysname) if is64(): diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 33dc34471..5961b2a65 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -67,7 +67,7 @@ add_subdirectory(interp) add_subdirectory(cmd_context) add_subdirectory(cmd_context/extra_cmds) add_subdirectory(parsers/smt2) -add_subdirectory(ast/proof_checker) +add_subdirectory(ast/proofs) add_subdirectory(ast/fpa) add_subdirectory(ast/macros) add_subdirectory(ast/pattern) diff --git a/src/ackermannization/ackr_model_converter.cpp b/src/ackermannization/ackr_model_converter.cpp index a7c021913..3cd86d2f1 100644 --- a/src/ackermannization/ackr_model_converter.cpp +++ b/src/ackermannization/ackr_model_converter.cpp @@ -62,6 +62,10 @@ public: } } + virtual void display(std::ostream & out) { + out << "(ackr-model-converter)\n"; + } + protected: ast_manager & m; const ackr_info_ref info; @@ -144,6 +148,7 @@ void ackr_model_converter::add_entry(model_evaluator & evaluator, else { TRACE("ackr_model", tout << "entry already present\n";); } + } model_converter * mk_ackr_model_converter(ast_manager & m, const ackr_info_ref& info) { diff --git a/src/ackermannization/lackr_model_converter_lazy.cpp b/src/ackermannization/lackr_model_converter_lazy.cpp index 2a7adb839..ffb4cae9d 100644 --- a/src/ackermannization/lackr_model_converter_lazy.cpp +++ b/src/ackermannization/lackr_model_converter_lazy.cpp @@ -48,6 +48,11 @@ public: virtual model_converter * translate(ast_translation & translator) { NOT_IMPLEMENTED_YET(); } + + virtual void display(std::ostream & out) { + out << "(lackr-model-converter)\n"; + } + protected: ast_manager& m; const lackr_model_constructor_ref model_constructor; diff --git a/src/api/api_array.cpp b/src/api/api_array.cpp index a5916e987..5e6764dff 100644 --- a/src/api/api_array.cpp +++ b/src/api/api_array.cpp @@ -34,6 +34,19 @@ extern "C" { Z3_CATCH_RETURN(0); } + Z3_sort Z3_API Z3_mk_array_sort_n(Z3_context c, unsigned n, Z3_sort const* domain, Z3_sort range) { + Z3_TRY; + LOG_Z3_mk_array_sort_n(c, n, domain, range); + RESET_ERROR_CODE(); + vector params; + for (unsigned i = 0; i < n; ++i) params.push_back(parameter(to_sort(domain[i]))); + params.push_back(parameter(to_sort(range))); + sort * ty = mk_c(c)->m().mk_sort(mk_c(c)->get_array_fid(), ARRAY_SORT, params.size(), params.c_ptr()); + mk_c(c)->save_ast_trail(ty); + RETURN_Z3(of_sort(ty)); + Z3_CATCH_RETURN(0); + } + Z3_ast Z3_API Z3_mk_select(Z3_context c, Z3_ast a, Z3_ast i) { Z3_TRY; LOG_Z3_mk_select(c, a, i); @@ -57,6 +70,35 @@ extern "C" { Z3_CATCH_RETURN(0); } + Z3_ast Z3_API Z3_mk_select_n(Z3_context c, Z3_ast a, unsigned n, Z3_ast const* idxs) { + Z3_TRY; + LOG_Z3_mk_select_n(c, a, n, idxs); + RESET_ERROR_CODE(); + ast_manager & m = mk_c(c)->m(); + expr * _a = to_expr(a); + // expr * _i = to_expr(i); + sort * a_ty = m.get_sort(_a); + // sort * i_ty = m.get_sort(_i); + if (a_ty->get_family_id() != mk_c(c)->get_array_fid()) { + SET_ERROR_CODE(Z3_SORT_ERROR); + RETURN_Z3(0); + } + ptr_vector domain; + ptr_vector args; + args.push_back(_a); + domain.push_back(a_ty); + for (unsigned i = 0; i < n; ++i) { + args.push_back(to_expr(idxs[i])); + domain.push_back(m.get_sort(to_expr(idxs[i]))); + } + func_decl * d = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_SELECT, 2, a_ty->get_parameters(), domain.size(), domain.c_ptr()); + app * r = m.mk_app(d, args.size(), args.c_ptr()); + mk_c(c)->save_ast_trail(r); + check_sorts(c, r); + RETURN_Z3(of_ast(r)); + Z3_CATCH_RETURN(0); + } + Z3_ast Z3_API Z3_mk_store(Z3_context c, Z3_ast a, Z3_ast i, Z3_ast v) { Z3_TRY; LOG_Z3_mk_store(c, a, i, v); @@ -82,6 +124,37 @@ extern "C" { Z3_CATCH_RETURN(0); } + Z3_ast Z3_API Z3_mk_store_n(Z3_context c, Z3_ast a, unsigned n, Z3_ast const* idxs, Z3_ast v) { + Z3_TRY; + LOG_Z3_mk_store_n(c, a, n, idxs, v); + RESET_ERROR_CODE(); + ast_manager & m = mk_c(c)->m(); + expr * _a = to_expr(a); + expr * _v = to_expr(v); + sort * a_ty = m.get_sort(_a); + sort * v_ty = m.get_sort(_v); + if (a_ty->get_family_id() != mk_c(c)->get_array_fid()) { + SET_ERROR_CODE(Z3_SORT_ERROR); + RETURN_Z3(0); + } + ptr_vector domain; + ptr_vector args; + args.push_back(_a); + domain.push_back(a_ty); + for (unsigned i = 0; i < n; ++i) { + args.push_back(to_expr(idxs[i])); + domain.push_back(m.get_sort(to_expr(idxs[i]))); + } + args.push_back(_v); + domain.push_back(v_ty); + func_decl * d = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_STORE, 2, a_ty->get_parameters(), domain.size(), domain.c_ptr()); + app * r = m.mk_app(d, args.size(), args.c_ptr()); + mk_c(c)->save_ast_trail(r); + check_sorts(c, r); + RETURN_Z3(of_ast(r)); + Z3_CATCH_RETURN(0); + } + Z3_ast Z3_API Z3_mk_map(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast const* args) { Z3_TRY; LOG_Z3_mk_map(c, f, n, args); @@ -188,6 +261,18 @@ extern "C" { MK_BINARY(Z3_mk_set_subset, mk_c(c)->get_array_fid(), OP_SET_SUBSET, SKIP); MK_BINARY(Z3_mk_array_ext, mk_c(c)->get_array_fid(), OP_ARRAY_EXT, SKIP); + Z3_ast Z3_API Z3_mk_as_array(Z3_context c, Z3_func_decl f) { + Z3_TRY; + LOG_Z3_mk_as_array(c, f); + RESET_ERROR_CODE(); + ast_manager & m = mk_c(c)->m(); + array_util a(m); + app * r = a.mk_as_array(to_func_decl(f)); + mk_c(c)->save_ast_trail(r); + return of_ast(r); + Z3_CATCH_RETURN(0); + } + Z3_ast Z3_mk_set_member(Z3_context c, Z3_ast elem, Z3_ast set) { return Z3_mk_select(c, set, elem); } @@ -222,7 +307,8 @@ extern "C" { CHECK_VALID_AST(t, 0); if (to_sort(t)->get_family_id() == mk_c(c)->get_array_fid() && to_sort(t)->get_decl_kind() == ARRAY_SORT) { - Z3_sort r = reinterpret_cast(to_sort(t)->get_parameter(1).get_ast()); + unsigned n = to_sort(t)->get_num_parameters(); + Z3_sort r = reinterpret_cast(to_sort(t)->get_parameter(n-1).get_ast()); RETURN_Z3(r); } SET_ERROR_CODE(Z3_INVALID_ARG); diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index b60a8c839..0889eee0d 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -249,7 +249,7 @@ extern "C" { params_ref _p; _p.set_bool("proof", true); // this is currently useless - scoped_proof_mode spm(mk_c(c)->m(), PGM_FINE); + scoped_proof_mode spm(mk_c(c)->m(), PGM_ENABLED); scoped_ptr sf = mk_smt_solver_factory(); scoped_ptr m_solver((*sf)(mk_c(c)->m(), _p, true, true, true, ::symbol::null)); m_solver.get()->updt_params(_p); // why do we have to do this? diff --git a/src/api/api_model.cpp b/src/api/api_model.cpp index 5c6023cfc..3e7dfa061 100644 --- a/src/api/api_model.cpp +++ b/src/api/api_model.cpp @@ -255,8 +255,13 @@ extern "C" { LOG_Z3_add_const_interp(c, m, f, a); RESET_ERROR_CODE(); func_decl* d = to_func_decl(f); - model* mdl = to_model_ref(m); - mdl->register_decl(d, to_expr(a)); + if (d->get_arity() != 0) { + SET_ERROR_CODE(Z3_INVALID_ARG); + } + else { + model* mdl = to_model_ref(m); + mdl->register_decl(d, to_expr(a)); + } Z3_CATCH; } diff --git a/src/api/api_opt.cpp b/src/api/api_opt.cpp index e07fa5b2d..a70f3f660 100644 --- a/src/api/api_opt.cpp +++ b/src/api/api_opt.cpp @@ -288,15 +288,16 @@ extern "C" { Z3_optimize opt, std::istream& s) { ast_manager& m = mk_c(c)->m(); - cmd_context ctx(false, &m); - install_opt_cmds(ctx, to_optimize_ptr(opt)); - ctx.set_ignore_check(true); - if (!parse_smt2_commands(ctx, s)) { + scoped_ptr ctx = alloc(cmd_context, false, &m); + install_opt_cmds(*ctx.get(), to_optimize_ptr(opt)); + ctx->set_ignore_check(true); + if (!parse_smt2_commands(*ctx.get(), s)) { + ctx = nullptr; SET_ERROR_CODE(Z3_PARSER_ERROR); return; } - ptr_vector::const_iterator it = ctx.begin_assertions(); - ptr_vector::const_iterator end = ctx.end_assertions(); + ptr_vector::const_iterator it = ctx->begin_assertions(); + ptr_vector::const_iterator end = ctx->end_assertions(); for (; it != end; ++it) { to_optimize_ptr(opt)->add_hard_constraint(*it); } @@ -325,9 +326,6 @@ extern "C" { std::ostringstream strm; strm << "Could not open file " << s; throw default_exception(strm.str()); - - SET_ERROR_CODE(Z3_PARSER_ERROR); - return; } Z3_optimize_from_stream(c, d, is); Z3_CATCH; diff --git a/src/api/api_parsers.cpp b/src/api/api_parsers.cpp index 8613d07e7..fe4aad848 100644 --- a/src/api/api_parsers.cpp +++ b/src/api/api_parsers.cpp @@ -57,7 +57,7 @@ extern "C" { Z3_func_decl const decls[]) { Z3_TRY; LOG_Z3_parse_smtlib_string(c, str, num_sorts, sort_names, sorts, num_decls, decl_names, decls); - std::ostringstream* outs = alloc(std::ostringstream); + scoped_ptr outs = alloc(std::ostringstream); bool ok = false; RESET_ERROR_CODE(); @@ -70,7 +70,7 @@ extern "C" { ok = false; } mk_c(c)->m_smtlib_error_buffer = outs->str(); - dealloc(outs); + outs = nullptr; if (!ok) { mk_c(c)->reset_parser(); SET_ERROR_CODE(Z3_PARSER_ERROR); @@ -90,7 +90,7 @@ extern "C" { LOG_Z3_parse_smtlib_file(c, file_name, num_sorts, sort_names, types, num_decls, decl_names, decls); bool ok = false; RESET_ERROR_CODE(); - std::ostringstream* outs = alloc(std::ostringstream); + scoped_ptr outs = alloc(std::ostringstream); init_smtlib_parser(c, num_sorts, sort_names, types, num_decls, decl_names, decls); mk_c(c)->m_smtlib_parser->set_error_stream(*outs); try { @@ -100,7 +100,7 @@ extern "C" { ok = false; } mk_c(c)->m_smtlib_error_buffer = outs->str(); - dealloc(outs); + outs = nullptr; if (!ok) { mk_c(c)->reset_parser(); SET_ERROR_CODE(Z3_PARSER_ERROR); @@ -263,24 +263,24 @@ extern "C" { Z3_symbol const decl_names[], Z3_func_decl const decls[]) { Z3_TRY; - cmd_context ctx(false, &(mk_c(c)->m())); - ctx.set_ignore_check(true); + scoped_ptr ctx = alloc(cmd_context, false, &(mk_c(c)->m())); + ctx->set_ignore_check(true); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); - mk_c(c)->save_object(v); - + mk_c(c)->save_object(v); for (unsigned i = 0; i < num_decls; ++i) { - ctx.insert(to_symbol(decl_names[i]), to_func_decl(decls[i])); + ctx->insert(to_symbol(decl_names[i]), to_func_decl(decls[i])); } for (unsigned i = 0; i < num_sorts; ++i) { - psort* ps = ctx.pm().mk_psort_cnst(to_sort(sorts[i])); - ctx.insert(ctx.pm().mk_psort_user_decl(0, to_symbol(sort_names[i]), ps)); + psort* ps = ctx->pm().mk_psort_cnst(to_sort(sorts[i])); + ctx->insert(ctx->pm().mk_psort_user_decl(0, to_symbol(sort_names[i]), ps)); } - if (!parse_smt2_commands(ctx, is)) { + if (!parse_smt2_commands(*ctx.get(), is)) { + ctx = nullptr; SET_ERROR_CODE(Z3_PARSER_ERROR); return of_ast_vector(v); } - ptr_vector::const_iterator it = ctx.begin_assertions(); - ptr_vector::const_iterator end = ctx.end_assertions(); + ptr_vector::const_iterator it = ctx->begin_assertions(); + ptr_vector::const_iterator end = ctx->end_assertions(); for (; it != end; ++it) { v->m_ast_vector.push_back(*it); } diff --git a/src/api/api_quant.cpp b/src/api/api_quant.cpp index d64768b3b..e56505e6d 100644 --- a/src/api/api_quant.cpp +++ b/src/api/api_quant.cpp @@ -63,9 +63,11 @@ extern "C" { RESET_ERROR_CODE(); if (!mk_c(c)->m().is_bool(to_expr(body))) { SET_ERROR_CODE(Z3_SORT_ERROR); + return nullptr; } if (num_patterns > 0 && num_no_patterns > 0) { SET_ERROR_CODE(Z3_INVALID_USAGE); + return nullptr; } expr * const* ps = reinterpret_cast(patterns); expr * const* no_ps = reinterpret_cast(no_patterns); @@ -76,7 +78,7 @@ extern "C" { for (unsigned i = 0; i < num_patterns; i++) { if (!v(num_decls, ps[i], 0, 0)) { SET_ERROR_CODE(Z3_INVALID_PATTERN); - return 0; + return nullptr; } } } diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 73ba5e082..ad8a3cc29 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -33,6 +33,9 @@ Revision History: #include "smt/smt_solver.h" #include "smt/smt_implied_equalities.h" #include "solver/smt_logics.h" +#include "cmd_context/cmd_context.h" +#include "parsers/smt2/smt2parser.h" + extern "C" { @@ -121,6 +124,30 @@ extern "C" { Z3_CATCH_RETURN(0); } + void Z3_API Z3_solver_from_file(Z3_context c, Z3_solver s, Z3_string file_name) { + Z3_TRY; + LOG_Z3_solver_from_file(c, s, file_name); + scoped_ptr ctx = alloc(cmd_context, false, &(mk_c(c)->m())); + ctx->set_ignore_check(true); + std::ifstream is(file_name); + if (!is) { + SET_ERROR_CODE(Z3_FILE_ACCESS_ERROR); + return; + } + if (!parse_smt2_commands(*ctx.get(), is)) { + ctx = nullptr; + SET_ERROR_CODE(Z3_PARSER_ERROR); + return; + } + ptr_vector::const_iterator it = ctx->begin_assertions(); + ptr_vector::const_iterator end = ctx->end_assertions(); + for (; it != end; ++it) { + to_solver_ref(s)->assert_expr(*it); + } + to_solver_ref(s)->set_model_converter(ctx->get_model_converter()); + Z3_CATCH; + } + Z3_string Z3_API Z3_solver_get_help(Z3_context c, Z3_solver s) { Z3_TRY; LOG_Z3_solver_get_help(c, s); @@ -452,6 +479,7 @@ extern "C" { unsigned sz = __assumptions.size(); for (unsigned i = 0; i < sz; ++i) { if (!is_expr(__assumptions[i])) { + _assumptions.finalize(); _consequences.finalize(); _variables.finalize(); SET_ERROR_CODE(Z3_INVALID_USAGE); return Z3_L_UNDEF; } @@ -461,6 +489,7 @@ extern "C" { sz = __variables.size(); for (unsigned i = 0; i < sz; ++i) { if (!is_expr(__variables[i])) { + _assumptions.finalize(); _consequences.finalize(); _variables.finalize(); SET_ERROR_CODE(Z3_INVALID_USAGE); return Z3_L_UNDEF; } @@ -481,6 +510,7 @@ extern "C" { } catch (z3_exception & ex) { to_solver_ref(s)->set_reason_unknown(eh); + _assumptions.finalize(); _consequences.finalize(); _variables.finalize(); mk_c(c)->handle_exception(ex); return Z3_L_UNDEF; } @@ -522,69 +552,5 @@ extern "C" { Z3_CATCH_RETURN(0); } - Z3_ast Z3_API Z3_solver_lookahead(Z3_context c, - Z3_solver s, - Z3_ast_vector assumptions, - Z3_ast_vector candidates) { - Z3_TRY; - LOG_Z3_solver_lookahead(c, s, assumptions, candidates); - ast_manager& m = mk_c(c)->m(); - expr_ref_vector _candidates(m), _assumptions(m); - ast_ref_vector const& __candidates = to_ast_vector_ref(candidates); - ast_ref_vector const& __assumptions = to_ast_vector_ref(assumptions); - for (auto & e : __candidates) { - if (!is_expr(e)) { - SET_ERROR_CODE(Z3_INVALID_USAGE); - return 0; - } - _candidates.push_back(to_expr(e)); - } - for (auto & e : __assumptions) { - if (!is_expr(e)) { - SET_ERROR_CODE(Z3_INVALID_USAGE); - return 0; - } - _assumptions.push_back(to_expr(e)); - } - - expr_ref result(m); - unsigned timeout = to_solver(s)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); - unsigned rlimit = to_solver(s)->m_params.get_uint("rlimit", mk_c(c)->get_rlimit()); - bool use_ctrl_c = to_solver(s)->m_params.get_bool("ctrl_c", false); - cancel_eh eh(mk_c(c)->m().limit()); - api::context::set_interruptable si(*(mk_c(c)), eh); - { - scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); - scoped_timer timer(timeout, &eh); - scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); - try { - result = to_solver_ref(s)->lookahead(_assumptions, _candidates); - } - catch (z3_exception & ex) { - mk_c(c)->handle_exception(ex); - return 0; - } - } - mk_c(c)->save_ast_trail(result); - RETURN_Z3(of_ast(result)); - Z3_CATCH_RETURN(0); - } - - Z3_ast_vector Z3_API Z3_solver_get_lemmas(Z3_context c, Z3_solver s) { - Z3_TRY; - LOG_Z3_solver_get_lemmas(c, s); - RESET_ERROR_CODE(); - ast_manager& m = mk_c(c)->m(); - init_solver(c, s); - Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); - mk_c(c)->save_object(v); - expr_ref_vector lemmas(m); - to_solver_ref(s)->get_lemmas(lemmas); - for (expr* e : lemmas) { - v->m_ast_vector.push_back(e); - } - RETURN_Z3(of_ast_vector(v)); - Z3_CATCH_RETURN(0); - } }; diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index eb38c5fa5..7015f6c08 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -140,18 +140,17 @@ namespace z3 { class context { bool m_enable_exceptions; Z3_context m_ctx; - static void Z3_API error_handler(Z3_context /*c*/, Z3_error_code /*e*/) { /* do nothing */ } void init(config & c) { m_ctx = Z3_mk_context_rc(c); m_enable_exceptions = true; - Z3_set_error_handler(m_ctx, error_handler); + Z3_set_error_handler(m_ctx, 0); Z3_set_ast_print_mode(m_ctx, Z3_PRINT_SMTLIB2_COMPLIANT); } void init_interp(config & c) { m_ctx = Z3_mk_interpolation_context(c); m_enable_exceptions = true; - Z3_set_error_handler(m_ctx, error_handler); + Z3_set_error_handler(m_ctx, 0); Z3_set_ast_print_mode(m_ctx, Z3_PRINT_SMTLIB2_COMPLIANT); } @@ -251,6 +250,8 @@ namespace z3 { Example: Given a context \c c, c.array_sort(c.int_sort(), c.bool_sort()) is an array sort from integer to Boolean. */ sort array_sort(sort d, sort r); + sort array_sort(sort_vector const& d, sort r); + /** \brief Return an enumeration sort: enum_names[0], ..., enum_names[n-1]. \c cs and \c ts are output parameters. The method stores in \c cs the constants corresponding to the enumerated elements, @@ -2328,6 +2329,11 @@ namespace z3 { inline sort context::re_sort(sort& s) { Z3_sort r = Z3_mk_re_sort(m_ctx, s); check_error(); return sort(*this, r); } inline sort context::array_sort(sort d, sort r) { Z3_sort s = Z3_mk_array_sort(m_ctx, d, r); check_error(); return sort(*this, s); } + inline sort context::array_sort(sort_vector const& d, sort r) { + array dom(d); + Z3_sort s = Z3_mk_array_sort_n(m_ctx, dom.size(), dom.ptr(), r); check_error(); return sort(*this, s); + } + inline sort context::enumeration_sort(char const * name, unsigned n, char const * const * enum_names, func_decl_vector & cs, func_decl_vector & ts) { array _enum_names(n); for (unsigned i = 0; i < n; i++) { _enum_names[i] = Z3_mk_string_symbol(*this, enum_names[i]); } @@ -2574,11 +2580,32 @@ namespace z3 { a.check_error(); return expr(a.ctx(), r); } + inline expr select(expr const & a, expr_vector const & i) { + check_context(a, i); + array idxs(i); + Z3_ast r = Z3_mk_select_n(a.ctx(), a, idxs.size(), idxs.ptr()); + a.check_error(); + return expr(a.ctx(), r); + } + inline expr store(expr const & a, int i, expr const & v) { return store(a, a.ctx().num_val(i, a.get_sort().array_domain()), v); } inline expr store(expr const & a, expr i, int v) { return store(a, i, a.ctx().num_val(v, a.get_sort().array_range())); } inline expr store(expr const & a, int i, int v) { return store(a, a.ctx().num_val(i, a.get_sort().array_domain()), a.ctx().num_val(v, a.get_sort().array_range())); } + inline expr store(expr const & a, expr_vector const & i, expr const & v) { + check_context(a, i); check_context(a, v); + array idxs(i); + Z3_ast r = Z3_mk_store_n(a.ctx(), a, idxs.size(), idxs.ptr(), v); + a.check_error(); + return expr(a.ctx(), r); + } + + inline expr as_array(func_decl & f) { + Z3_ast r = Z3_mk_as_array(f.ctx(), f); + f.check_error(); + return expr(f.ctx(), r); + } #define MK_EXPR1(_fn, _arg) \ Z3_ast r = _fn(_arg.ctx(), _arg); \ diff --git a/src/api/dotnet/ArraySort.cs b/src/api/dotnet/ArraySort.cs index ddd27785c..47a73ae1f 100644 --- a/src/api/dotnet/ArraySort.cs +++ b/src/api/dotnet/ArraySort.cs @@ -63,6 +63,13 @@ namespace Microsoft.Z3 Contract.Requires(domain != null); Contract.Requires(range != null); } + internal ArraySort(Context ctx, Sort[] domain, Sort range) + : base(ctx, Native.Z3_mk_array_sort_n(ctx.nCtx, (uint)domain.Length, AST.ArrayToNative(domain), range.NativeObject)) + { + Contract.Requires(ctx != null); + Contract.Requires(domain != null); + Contract.Requires(range != null); + } #endregion }; diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index 2bf1c44c5..38ab101b4 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -274,6 +274,20 @@ namespace Microsoft.Z3 return new ArraySort(this, domain, range); } + /// + /// Create a new n-ary array sort. + /// + public ArraySort MkArraySort(Sort[] domain, Sort range) + { + Contract.Requires(domain != null); + Contract.Requires(range != null); + Contract.Ensures(Contract.Result() != null); + + CheckContextMatch(domain); + CheckContextMatch(range); + return new ArraySort(this, domain, range); + } + /// /// Create a new tuple sort. /// @@ -2113,6 +2127,7 @@ namespace Microsoft.Z3 return (ArrayExpr)MkConst(MkSymbol(name), MkArraySort(domain, range)); } + /// /// Array read. /// @@ -2123,8 +2138,8 @@ namespace Microsoft.Z3 /// The node a must have an array sort [domain -> range], /// and i must have the sort domain. /// The sort of the result is range. - /// - /// + /// + /// /// public Expr MkSelect(ArrayExpr a, Expr i) { @@ -2137,6 +2152,30 @@ namespace Microsoft.Z3 return Expr.Create(this, Native.Z3_mk_select(nCtx, a.NativeObject, i.NativeObject)); } + /// + /// Array read. + /// + /// + /// The argument a is the array and args are the indices + /// of the array that gets read. + /// + /// The node a must have an array sort [domain1,..,domaink -> range], + /// and args must have the sort domain1,..,domaink. + /// The sort of the result is range. + /// + /// + /// + public Expr MkSelect(ArrayExpr a, params Expr[] args) + { + Contract.Requires(a != null); + Contract.Requires(args != null && Contract.ForAll(args, n => n != null)); + Contract.Ensures(Contract.Result() != null); + + CheckContextMatch(a); + CheckContextMatch(args); + return Expr.Create(this, Native.Z3_mk_select_n(nCtx, a.NativeObject, AST.ArrayLength(args), AST.ArrayToNative(args))); + } + /// /// Array update. /// @@ -2151,8 +2190,9 @@ namespace Microsoft.Z3 /// on all indices except for i, where it maps to v /// (and the select of a with /// respect to i may be a different value). - /// - /// + /// + /// + /// /// public ArrayExpr MkStore(ArrayExpr a, Expr i, Expr v) { @@ -2167,14 +2207,45 @@ namespace Microsoft.Z3 return new ArrayExpr(this, Native.Z3_mk_store(nCtx, a.NativeObject, i.NativeObject, v.NativeObject)); } + /// + /// Array update. + /// + /// + /// The node a must have an array sort [domain1,..,domaink -> range], + /// args must have sort domain1,..,domaink, + /// v must have sort range. The sort of the result is [domain -> range]. + /// The semantics of this function is given by the theory of arrays described in the SMT-LIB + /// standard. See http://smtlib.org for more details. + /// The result of this function is an array that is equal to a + /// (with respect to select) + /// on all indices except for args, where it maps to v + /// (and the select of a with + /// respect to args may be a different value). + /// + /// + /// + /// + public ArrayExpr MkStore(ArrayExpr a, Expr[] args, Expr v) + { + Contract.Requires(a != null); + Contract.Requires(args != null); + Contract.Requires(v != null); + Contract.Ensures(Contract.Result() != null); + + CheckContextMatch(args); + CheckContextMatch(a); + CheckContextMatch(v); + return new ArrayExpr(this, Native.Z3_mk_store_n(nCtx, a.NativeObject, AST.ArrayLength(args), AST.ArrayToNative(args), v.NativeObject)); + } + /// /// Create a constant array. /// /// /// The resulting term is an array, such that a selecton an arbitrary index /// produces the value v. - /// - /// + /// + /// /// public ArrayExpr MkConstArray(Sort domain, Expr v) { @@ -2194,9 +2265,9 @@ namespace Microsoft.Z3 /// Eeach element of args must be of an array sort [domain_i -> range_i]. /// The function declaration f must have type range_1 .. range_n -> range. /// v must have sort range. The sort of the result is [domain_i -> range]. - /// - /// - /// + /// + /// + /// /// public ArrayExpr MkMap(FuncDecl f, params ArrayExpr[] args) { diff --git a/src/api/dotnet/Solver.cs b/src/api/dotnet/Solver.cs index 40da086ce..9154117ce 100644 --- a/src/api/dotnet/Solver.cs +++ b/src/api/dotnet/Solver.cs @@ -181,6 +181,14 @@ namespace Microsoft.Z3 Native.Z3_solver_assert_and_track(Context.nCtx, NativeObject, constraint.NativeObject, p.NativeObject); } + /// + /// Load solver assertions from a file. + /// + public void FromFile(string file) + { + Native.Z3_solver_from_file(Context.nCtx, NativeObject, file); + } + /// /// Assert a lemma (or multiple) into the solver. /// @@ -275,31 +283,6 @@ namespace Microsoft.Z3 return lboolToStatus(r); } - /// - /// Select a lookahead literal from the set of supplied candidates. - /// - public BoolExpr Lookahead(IEnumerable assumptions, IEnumerable candidates) - { - ASTVector cands = new ASTVector(Context); - foreach (var c in candidates) cands.Push(c); - ASTVector assums = new ASTVector(Context); - foreach (var c in assumptions) assums.Push(c); - return (BoolExpr)Expr.Create(Context, Native.Z3_solver_lookahead(Context.nCtx, NativeObject, assums.NativeObject, cands.NativeObject)); - } - - /// - /// Retrieve set of lemmas that have been inferred by solver. - /// - public BoolExpr[] Lemmas - { - get - { - var r = Native.Z3_solver_get_lemmas(Context.nCtx, NativeObject); - var v = new ASTVector(Context, r); - return v.ToBoolExprArray(); - } - } - /// /// The model of the last Check. /// @@ -370,6 +353,28 @@ namespace Microsoft.Z3 } } + /// + /// Return a set of cubes. + /// + public IEnumerable Cube() + { + int rounds = 0; + while (true) { + BoolExpr r = (BoolExpr)Expr.Create(Context, Native.Z3_solver_cube(Context.nCtx, NativeObject)); + if (r.IsFalse) { + if (rounds == 0) + yield return r; + break; + } + if (r.IsTrue) { + yield return r; + break; + } + ++rounds; + yield return r; + } + } + /// /// Create a clone of the current solver with respect to ctx. /// diff --git a/src/api/java/ArraySort.java b/src/api/java/ArraySort.java index 1574823d1..db4d992ed 100644 --- a/src/api/java/ArraySort.java +++ b/src/api/java/ArraySort.java @@ -56,4 +56,10 @@ public class ArraySort extends Sort super(ctx, Native.mkArraySort(ctx.nCtx(), domain.getNativeObject(), range.getNativeObject())); } + + ArraySort(Context ctx, Sort[] domains, Sort range) + { + super(ctx, Native.mkArraySortN(ctx.nCtx(), domains.length, AST.arrayToNative(domains), + range.getNativeObject())); + } }; diff --git a/src/api/java/Context.java b/src/api/java/Context.java index a0a738fdb..711327dd2 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -224,6 +224,17 @@ public class Context implements AutoCloseable { return new ArraySort(this, domain, range); } + + /** + * Create a new array sort. + **/ + public ArraySort mkArraySort(Sort[] domains, Sort range) + { + checkContextMatch(domains); + checkContextMatch(range); + return new ArraySort(this, domains, range); + } + /** * Create a new string sort **/ @@ -414,7 +425,7 @@ public class Context implements AutoCloseable { * that is passed in as argument is updated with value v, * the remaining fields of t are unchanged. **/ - public Expr MkUpdateField(FuncDecl field, Expr t, Expr v) + public Expr mkUpdateField(FuncDecl field, Expr t, Expr v) throws Z3Exception { return Expr.create (this, @@ -706,7 +717,7 @@ public class Context implements AutoCloseable { } /** - * Mk an expression representing {@code not(a)}. + * Create an expression representing {@code not(a)}. **/ public BoolExpr mkNot(BoolExpr a) { @@ -1679,6 +1690,28 @@ public class Context implements AutoCloseable { i.getNativeObject())); } + /** + * Array read. + * Remarks: The argument {@code a} is the array and + * {@code args} are the indices of the array that gets read. + * + * The node {@code a} must have an array sort + * {@code [domains -> range]}, and {@code args} must have the sorts + * {@code domains}. The sort of the result is {@code range}. + * + * @see #mkArraySort + * @see #mkStore + + **/ + public Expr mkSelect(ArrayExpr a, Expr[] args) + { + checkContextMatch(a); + checkContextMatch(args); + return Expr.create( + this, + Native.mkSelectN(nCtx(), a.getNativeObject(), args.length, AST.arrayToNative(args))); + } + /** * Array update. * Remarks: The node {@code a} must have an array sort @@ -1704,6 +1737,31 @@ public class Context implements AutoCloseable { i.getNativeObject(), v.getNativeObject())); } + /** + * Array update. + * Remarks: The node {@code a} must have an array sort + * {@code [domains -> range]}, {@code i} must have sort + * {@code domain}, {@code v} must have sort range. The sort of the + * result is {@code [domains -> range]}. The semantics of this function + * is given by the theory of arrays described in the SMT-LIB standard. See + * http://smtlib.org for more details. The result of this function is an + * array that is equal to {@code a} (with respect to + * {@code select}) on all indices except for {@code args}, where it + * maps to {@code v} (and the {@code select} of {@code a} + * with respect to {@code args} may be a different value). + * @see #mkArraySort + * @see #mkSelect + + **/ + public ArrayExpr mkStore(ArrayExpr a, Expr[] args, Expr v) + { + checkContextMatch(a); + checkContextMatch(args); + checkContextMatch(v); + return new ArrayExpr(this, Native.mkStoreN(nCtx(), a.getNativeObject(), + args.length, AST.arrayToNative(args), v.getNativeObject())); + } + /** * Create a constant array. * Remarks: The resulting term is an array, such @@ -2104,7 +2162,7 @@ public class Context implements AutoCloseable { /** * Create a range expression. */ - public ReExpr MkRange(SeqExpr lo, SeqExpr hi) + public ReExpr mkRange(SeqExpr lo, SeqExpr hi) { checkContextMatch(lo, hi); return (ReExpr) Expr.create(this, Native.mkReRange(nCtx(), lo.getNativeObject(), hi.getNativeObject())); diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 4379e3fe1..d0273cc24 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -6279,21 +6279,6 @@ class Solver(Z3PPObject): consequences = [ consequences[i] for i in range(sz) ] return CheckSatResult(r), consequences - def lemmas(self): - """Extract auxiliary lemmas produced by solver""" - return AstVector(Z3_solver_get_lemmas(self.ctx.ref(), self.solver), self.ctx) - - def lookahead(self, candidates = None): - """Get lookahead literal""" - if candidates is None: - candidates = AstVector(None, self.ctx) - elif not isinstance(candidates, AstVector): - _cs = AstVector(None, self.ctx) - for c in candidates: - _asms.push(c) - candidates = _cs - return _to_expr_ref(Z3_solver_lookahead(self.ctx.ref(), self.solver, candidates), self.ctx) - def cube(self): """Get set of cubes""" rounds = 0 @@ -6315,11 +6300,11 @@ class Solver(Z3PPObject): def from_file(self, filename): """Parse assertions from a file""" - self.add([f for f in parse_smt2_file(filename)]) + Z3_solver_from_file(self.ctx.ref(), self.solver) def from_string(self, s): """Parse assertions from a string""" - self.add([f for f in parse_smt2_string(s)]) + self.add([f for f in parse_smt2_string(s, ctx=self.ctx)]) def assertions(self): """Return an AST vector containing all added constraints. diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 8615cd7cb..5548f6796 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -1881,6 +1881,17 @@ extern "C" { */ Z3_sort Z3_API Z3_mk_array_sort(Z3_context c, Z3_sort domain, Z3_sort range); + /** + \brief Create an array type with N arguments + + \sa Z3_mk_select_n + \sa Z3_mk_store_n + + def_API('Z3_mk_array_sort_n', SORT, (_in(CONTEXT), _in(UINT), _in_array(1, SORT), _in(SORT))) + */ + Z3_sort Z3_API Z3_mk_array_sort_n(Z3_context c, unsigned n, Z3_sort const * domain, Z3_sort range); + + /** \brief Create a tuple type. @@ -2973,6 +2984,15 @@ extern "C" { */ Z3_ast Z3_API Z3_mk_select(Z3_context c, Z3_ast a, Z3_ast i); + /** + \brief n-ary Array read. + The argument \c a is the array and \c idxs are the indices of the array that gets read. + + def_API('Z3_mk_select_n', AST, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) + + */ + Z3_ast Z3_API Z3_mk_select_n(Z3_context c, Z3_ast a, unsigned n, Z3_ast const* idxs); + /** \brief Array update. @@ -2991,6 +3011,14 @@ extern "C" { */ Z3_ast Z3_API Z3_mk_store(Z3_context c, Z3_ast a, Z3_ast i, Z3_ast v); + /** + \brief n-ary Array update. + + def_API('Z3_mk_store_n', AST, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST), _in(AST))) + + */ + Z3_ast Z3_API Z3_mk_store_n(Z3_context c, Z3_ast a, unsigned n, Z3_ast const* idxs, Z3_ast v); + /** \brief Create the constant array. @@ -3031,6 +3059,15 @@ extern "C" { def_API('Z3_mk_array_default', AST, (_in(CONTEXT), _in(AST))) */ Z3_ast Z3_API Z3_mk_array_default(Z3_context c, Z3_ast array); + + /** + \brief Create array with the same interpretation as a function. + The array satisfies the property (f x) = (select (_ as-array f) x) + for every argument x. + + def_API('Z3_mk_as_array', AST, (_in(CONTEXT), _in(FUNC_DECL))) + */ + Z3_ast Z3_API Z3_mk_as_array(Z3_context c, Z3_func_decl f); /*@}*/ /** @name Sets */ @@ -3854,6 +3891,7 @@ extern "C" { /** \brief Return the domain of the given array sort. + In the case of a multi-dimensional array, this function returns the sort of the first dimension. \pre Z3_get_sort_kind(c, t) == Z3_ARRAY_SORT @@ -6099,6 +6137,13 @@ extern "C" { */ void Z3_API Z3_solver_assert_lemma(Z3_context c, Z3_solver s, Z3_ast a); + /** + \brief load solver assertions from a file. + + def_API('Z3_solver_from_file', VOID, (_in(CONTEXT), _in(SOLVER), _in(STRING))) + */ + void Z3_API Z3_solver_from_file(Z3_context c, Z3_solver s, Z3_string file_name); + /** \brief Return the set of asserted formulas on the solver. @@ -6174,14 +6219,6 @@ extern "C" { Z3_ast_vector variables, Z3_ast_vector consequences); - /** - \brief select a literal from the list of candidate propositional variables to split on. - If the candidate list is empty, then the solver chooses a formula based on its internal state. - - def_API('Z3_solver_lookahead', AST, (_in(CONTEXT), _in(SOLVER), _in(AST_VECTOR), _in(AST_VECTOR))) - */ - - Z3_ast Z3_API Z3_solver_lookahead(Z3_context c, Z3_solver s, Z3_ast_vector assumptions, Z3_ast_vector candidates); /** \brief extract a next cube for a solver. The last cube is the constant \c true or \c false. @@ -6193,18 +6230,6 @@ extern "C" { Z3_ast Z3_API Z3_solver_cube(Z3_context c, Z3_solver s); - - /** - \brief retrieve lemmas from solver state. Lemmas are auxiliary unit literals, - binary clauses and other learned clauses that are below a minimal glue level. - Lemmas that have been retrieved in a previous call may be suppressed from subsequent - calls. - - def_API('Z3_solver_get_lemmas', AST_VECTOR, (_in(CONTEXT), _in(SOLVER))) - */ - - Z3_ast_vector Z3_API Z3_solver_get_lemmas(Z3_context c, Z3_solver s); - /** \brief Retrieve the model for the last #Z3_solver_check or #Z3_solver_check_assumptions diff --git a/src/ast/CMakeLists.txt b/src/ast/CMakeLists.txt index 0a14d9473..4dcdd2a35 100644 --- a/src/ast/CMakeLists.txt +++ b/src/ast/CMakeLists.txt @@ -10,6 +10,7 @@ z3_add_component(ast ast_printer.cpp ast_smt2_pp.cpp ast_smt_pp.cpp + ast_pp_dot.cpp ast_translation.cpp ast_util.cpp bv_decl_plugin.cpp diff --git a/src/ast/array_decl_plugin.cpp b/src/ast/array_decl_plugin.cpp index cb016e263..0cc4f6031 100644 --- a/src/ast/array_decl_plugin.cpp +++ b/src/ast/array_decl_plugin.cpp @@ -242,7 +242,9 @@ func_decl* array_decl_plugin::mk_select(unsigned arity, sort * const * domain) { parameter const* parameters = s->get_parameters(); if (num_parameters != arity) { - m_manager->raise_exception("select requires as many arguments as the size of the domain"); + std::stringstream strm; + strm << "select requires " << num_parameters << " arguments, but was provided with " << arity << " arguments"; + m_manager->raise_exception(strm.str().c_str()); return 0; } ptr_buffer new_domain; // we need this because of coercions. @@ -314,7 +316,7 @@ func_decl * array_decl_plugin::mk_array_ext(unsigned arity, sort * const * domai return 0; } sort * r = to_sort(s->get_parameter(i).get_ast()); - parameter param(s); + parameter param(i); return m_manager->mk_func_decl(m_array_ext_sym, arity, domain, r, func_decl_info(m_family_id, OP_ARRAY_EXT, 1, ¶m)); } @@ -592,3 +594,9 @@ sort * array_util::mk_array_sort(unsigned arity, sort* const* domain, sort* rang params.push_back(parameter(range)); return m_manager.mk_sort(m_fid, ARRAY_SORT, params.size(), params.c_ptr()); } + +func_decl* array_util::mk_array_ext(sort *domain, unsigned i) { + sort * domains[2] = { domain, domain }; + parameter p(i); + return m_manager.mk_func_decl(m_fid, OP_ARRAY_EXT, 1, &p, 2, domains); +} diff --git a/src/ast/array_decl_plugin.h b/src/ast/array_decl_plugin.h index 0704fe56a..911bb3f27 100644 --- a/src/ast/array_decl_plugin.h +++ b/src/ast/array_decl_plugin.h @@ -143,6 +143,7 @@ public: bool is_const(expr* n) const { return is_app_of(n, m_fid, OP_CONST_ARRAY); } bool is_map(expr* n) const { return is_app_of(n, m_fid, OP_ARRAY_MAP); } bool is_as_array(expr * n) const { return is_app_of(n, m_fid, OP_AS_ARRAY); } + bool is_as_array(expr * n, func_decl*& f) const { return is_as_array(n) && (f = get_as_array_func_decl(n), true); } bool is_select(func_decl* f) const { return is_decl_of(f, m_fid, OP_SELECT); } bool is_store(func_decl* f) const { return is_decl_of(f, m_fid, OP_STORE); } bool is_const(func_decl* f) const { return is_decl_of(f, m_fid, OP_CONST_ARRAY); } @@ -182,13 +183,15 @@ public: return mk_const_array(s, m_manager.mk_true()); } + func_decl * mk_array_ext(sort* domain, unsigned i); + sort * mk_array_sort(sort* dom, sort* range) { return mk_array_sort(1, &dom, range); } sort * mk_array_sort(unsigned arity, sort* const* domain, sort* range); - app * mk_as_array(sort * s, func_decl * f) { + app * mk_as_array(func_decl * f) { parameter param(f); - return m_manager.mk_app(m_fid, OP_AS_ARRAY, 1, ¶m, 0, 0, s); + return m_manager.mk_app(m_fid, OP_AS_ARRAY, 1, ¶m, 0, 0, 0); } }; diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 232e7b6d0..a6c4ab8fc 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -768,7 +768,7 @@ func_decl * basic_decl_plugin::mk_compressed_proof_decl(char const * name, basic func_decl * basic_decl_plugin::mk_proof_decl(char const * name, basic_op_kind k, unsigned num_parents, ptr_vector & cache) { if (num_parents >= cache.size()) { - cache.resize(num_parents+1, 0); + cache.resize(num_parents+1); } if (cache[num_parents] == 0) { cache[num_parents] = mk_proof_decl(name, k, num_parents); @@ -805,7 +805,6 @@ func_decl * basic_decl_plugin::mk_proof_decl(char const* name, basic_op_kind k, } func_decl * basic_decl_plugin::mk_proof_decl(basic_op_kind k, unsigned num_parents) { - SASSERT(k == PR_UNDEF || m_manager->proofs_enabled()); switch (static_cast(k)) { // // A description of the semantics of the proof @@ -1356,6 +1355,10 @@ ast_manager::ast_manager(ast_manager const & src, bool disable_proofs): copy_families_plugins(src); } +void ast_manager::update_fresh_id(ast_manager const& m) { + m_fresh_id = std::max(m_fresh_id, m.m_fresh_id); +} + void ast_manager::init() { m_int_real_coercions = true; m_debug_ref_count = false; @@ -2627,7 +2630,7 @@ bool ast_manager::is_fully_interp(sort * s) const { // ----------------------------------- proof * ast_manager::mk_proof(family_id fid, decl_kind k, unsigned num_args, expr * const * args) { - if (m_proof_mode == PGM_DISABLED) + if (proofs_disabled()) return m_undef_proof; return mk_app(fid, k, num_args, args); } @@ -2663,8 +2666,7 @@ proof * ast_manager::mk_goal(expr * f) { } proof * ast_manager::mk_modus_ponens(proof * p1, proof * p2) { - if (m_proof_mode == PGM_DISABLED) - return m_undef_proof; + if (!p1 || !p2) return nullptr; SASSERT(has_fact(p1)); SASSERT(has_fact(p2)); CTRACE("mk_modus_ponens", !(is_implies(get_fact(p2)) || is_iff(get_fact(p2)) || is_oeq(get_fact(p2))), @@ -2685,14 +2687,10 @@ proof * ast_manager::mk_modus_ponens(proof * p1, proof * p2) { } proof * ast_manager::mk_reflexivity(expr * e) { - if (m_proof_mode == PGM_DISABLED) - return m_undef_proof; return mk_app(m_basic_family_id, PR_REFLEXIVITY, mk_eq(e, e)); } proof * ast_manager::mk_oeq_reflexivity(expr * e) { - if (m_proof_mode == PGM_DISABLED) - return m_undef_proof; return mk_app(m_basic_family_id, PR_REFLEXIVITY, mk_oeq(e, e)); } @@ -2706,8 +2704,7 @@ proof * ast_manager::mk_commutativity(app * f) { \brief Given a proof of p, return a proof of (p <=> true) */ proof * ast_manager::mk_iff_true(proof * pr) { - if (m_proof_mode == PGM_DISABLED) - return m_undef_proof; + if (!pr) return pr; SASSERT(has_fact(pr)); SASSERT(is_bool(get_fact(pr))); return mk_app(m_basic_family_id, PR_IFF_TRUE, pr, mk_iff(get_fact(pr), mk_true())); @@ -2717,8 +2714,7 @@ proof * ast_manager::mk_iff_true(proof * pr) { \brief Given a proof of (not p), return a proof of (p <=> false) */ proof * ast_manager::mk_iff_false(proof * pr) { - if (m_proof_mode == PGM_DISABLED) - return m_undef_proof; + if (!pr) return pr; SASSERT(has_fact(pr)); SASSERT(is_not(get_fact(pr))); expr * p = to_app(get_fact(pr))->get_arg(0); @@ -2726,10 +2722,7 @@ proof * ast_manager::mk_iff_false(proof * pr) { } proof * ast_manager::mk_symmetry(proof * p) { - if (m_proof_mode == PGM_DISABLED) - return m_undef_proof; - if (!p) - return p; + if (!p) return p; if (is_reflexivity(p)) return p; if (is_symmetry(p)) @@ -2742,8 +2735,6 @@ proof * ast_manager::mk_symmetry(proof * p) { } proof * ast_manager::mk_transitivity(proof * p1, proof * p2) { - if (m_proof_mode == PGM_DISABLED) - return m_undef_proof; if (!p1) return p2; if (!p2) @@ -2788,8 +2779,6 @@ proof * ast_manager::mk_transitivity(proof * p1, proof * p2, proof * p3, proof * } proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) - return m_undef_proof; SASSERT(num_proofs > 0); proof * r = proofs[0]; for (unsigned i = 1; i < num_proofs; i++) @@ -2798,11 +2787,8 @@ proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs } proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs, expr * n1, expr * n2) { - if (m_proof_mode == PGM_DISABLED) - return m_undef_proof; - if (fine_grain_proofs()) - return mk_transitivity(num_proofs, proofs); - SASSERT(num_proofs > 0); + if (num_proofs == 0) + return nullptr; if (num_proofs == 1) return proofs[0]; DEBUG_CODE({ @@ -2818,8 +2804,6 @@ proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs } proof * ast_manager::mk_monotonicity(func_decl * R, app * f1, app * f2, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) - return m_undef_proof; SASSERT(f1->get_num_args() == f2->get_num_args()); SASSERT(f1->get_decl() == f2->get_decl()); ptr_buffer args; @@ -2829,8 +2813,6 @@ proof * ast_manager::mk_monotonicity(func_decl * R, app * f1, app * f2, unsigned } proof * ast_manager::mk_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) - return m_undef_proof; SASSERT(get_sort(f1) == get_sort(f2)); sort * s = get_sort(f1); sort * d[2] = { s, s }; @@ -2838,8 +2820,6 @@ proof * ast_manager::mk_congruence(app * f1, app * f2, unsigned num_proofs, proo } proof * ast_manager::mk_oeq_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) - return m_undef_proof; SASSERT(get_sort(f1) == get_sort(f2)); sort * s = get_sort(f1); sort * d[2] = { s, s }; @@ -2847,11 +2827,7 @@ proof * ast_manager::mk_oeq_congruence(app * f1, app * f2, unsigned num_proofs, } proof * ast_manager::mk_quant_intro(quantifier * q1, quantifier * q2, proof * p) { - if (m_proof_mode == PGM_DISABLED) - return m_undef_proof; - if (!p) { - return 0; - } + if (!p) return nullptr; SASSERT(q1->get_num_decls() == q2->get_num_decls()); SASSERT(has_fact(p)); SASSERT(is_iff(get_fact(p))); @@ -2859,8 +2835,7 @@ proof * ast_manager::mk_quant_intro(quantifier * q1, quantifier * q2, proof * p) } proof * ast_manager::mk_oeq_quant_intro(quantifier * q1, quantifier * q2, proof * p) { - if (m_proof_mode == PGM_DISABLED) - return m_undef_proof; + if (!p) return nullptr; SASSERT(q1->get_num_decls() == q2->get_num_decls()); SASSERT(has_fact(p)); SASSERT(is_oeq(get_fact(p))); @@ -2868,25 +2843,26 @@ proof * ast_manager::mk_oeq_quant_intro(quantifier * q1, quantifier * q2, proof } proof * ast_manager::mk_distributivity(expr * s, expr * r) { - if (m_proof_mode == PGM_DISABLED) - return m_undef_proof; return mk_app(m_basic_family_id, PR_DISTRIBUTIVITY, mk_eq(s, r)); } proof * ast_manager::mk_rewrite(expr * s, expr * t) { - if (m_proof_mode == PGM_DISABLED) + SASSERT(proofs_enabled()); + if (proofs_disabled()) return m_undef_proof; return mk_app(m_basic_family_id, PR_REWRITE, mk_eq(s, t)); } proof * ast_manager::mk_oeq_rewrite(expr * s, expr * t) { - if (m_proof_mode == PGM_DISABLED) + SASSERT(proofs_enabled()); + if (proofs_disabled()) return m_undef_proof; return mk_app(m_basic_family_id, PR_REWRITE, mk_oeq(s, t)); } proof * ast_manager::mk_rewrite_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + SASSERT(proofs_enabled()); + if (proofs_disabled()) return m_undef_proof; ptr_buffer args; args.append(num_proofs, (expr**) proofs); @@ -2895,37 +2871,43 @@ proof * ast_manager::mk_rewrite_star(expr * s, expr * t, unsigned num_proofs, pr } proof * ast_manager::mk_pull_quant(expr * e, quantifier * q) { - if (m_proof_mode == PGM_DISABLED) + SASSERT(proofs_enabled()); + if (proofs_disabled()) return m_undef_proof; return mk_app(m_basic_family_id, PR_PULL_QUANT, mk_iff(e, q)); } proof * ast_manager::mk_pull_quant_star(expr * e, quantifier * q) { - if (m_proof_mode == PGM_DISABLED) + SASSERT(proofs_enabled()); + if (proofs_disabled()) return m_undef_proof; return mk_app(m_basic_family_id, PR_PULL_QUANT_STAR, mk_iff(e, q)); } proof * ast_manager::mk_push_quant(quantifier * q, expr * e) { - if (m_proof_mode == PGM_DISABLED) + SASSERT(proofs_enabled()); + if (proofs_disabled()) return m_undef_proof; return mk_app(m_basic_family_id, PR_PUSH_QUANT, mk_iff(q, e)); } proof * ast_manager::mk_elim_unused_vars(quantifier * q, expr * e) { - if (m_proof_mode == PGM_DISABLED) + SASSERT(proofs_enabled()); + if (proofs_disabled()) return m_undef_proof; return mk_app(m_basic_family_id, PR_ELIM_UNUSED_VARS, mk_iff(q, e)); } proof * ast_manager::mk_der(quantifier * q, expr * e) { - if (m_proof_mode == PGM_DISABLED) + SASSERT(proofs_enabled()); + if (proofs_disabled()) return m_undef_proof; return mk_app(m_basic_family_id, PR_DER, mk_iff(q, e)); } proof * ast_manager::mk_quant_inst(expr * not_q_or_i, unsigned num_bind, expr* const* binding) { - if (m_proof_mode == PGM_DISABLED) + SASSERT(proofs_enabled()); + if (proofs_disabled()) return m_undef_proof; vector params; for (unsigned i = 0; i < num_bind; ++i) { @@ -2960,7 +2942,8 @@ bool ast_manager::is_rewrite(expr const* e, expr*& r1, expr*& r2) const { } proof * ast_manager::mk_def_axiom(expr * ax) { - if (m_proof_mode == PGM_DISABLED) + SASSERT(proofs_enabled()); + if (proofs_disabled()) return m_undef_proof; return mk_app(m_basic_family_id, PR_DEF_AXIOM, ax); } @@ -3002,7 +2985,7 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro new_lits.push_back(lit); } DEBUG_CODE({ - for (unsigned i = 1; m_proof_mode == PGM_FINE && i < num_proofs; i++) { + for (unsigned i = 1; proofs_enabled() && i < num_proofs; i++) { CTRACE("mk_unit_resolution_bug", !found.get(i, false), for (unsigned j = 0; j < num_proofs; j++) { if (j == i) tout << "Index " << i << " was not found:\n"; @@ -3081,14 +3064,11 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro } proof * ast_manager::mk_hypothesis(expr * h) { - if (m_proof_mode == PGM_DISABLED) - return m_undef_proof; return mk_app(m_basic_family_id, PR_HYPOTHESIS, h); } proof * ast_manager::mk_lemma(proof * p, expr * lemma) { - if (m_proof_mode == PGM_DISABLED) - return m_undef_proof; + if (!p) return p; SASSERT(has_fact(p)); CTRACE("mk_lemma", !is_false(get_fact(p)), tout << mk_ll_pp(p, *this) << "\n";); SASSERT(is_false(get_fact(p))); @@ -3101,7 +3081,7 @@ proof * ast_manager::mk_def_intro(expr * new_def) { } proof * ast_manager::mk_apply_defs(expr * n, expr * def, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (proofs_disabled()) return m_undef_proof; ptr_buffer args; args.append(num_proofs, (expr**) proofs); @@ -3110,10 +3090,7 @@ proof * ast_manager::mk_apply_defs(expr * n, expr * def, unsigned num_proofs, pr } proof * ast_manager::mk_iff_oeq(proof * p) { - if (m_proof_mode == PGM_DISABLED) - return m_undef_proof; - if (!p) - return p; + if (!p) return p; SASSERT(has_fact(p)); SASSERT(is_iff(get_fact(p)) || is_oeq(get_fact(p))); @@ -3137,7 +3114,7 @@ bool ast_manager::check_nnf_proof_parents(unsigned num_proofs, proof * const * p } proof * ast_manager::mk_nnf_pos(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (proofs_disabled()) return m_undef_proof; check_nnf_proof_parents(num_proofs, proofs); ptr_buffer args; @@ -3147,7 +3124,7 @@ proof * ast_manager::mk_nnf_pos(expr * s, expr * t, unsigned num_proofs, proof * } proof * ast_manager::mk_nnf_neg(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (proofs_disabled()) return m_undef_proof; check_nnf_proof_parents(num_proofs, proofs); ptr_buffer args; @@ -3157,7 +3134,7 @@ proof * ast_manager::mk_nnf_neg(expr * s, expr * t, unsigned num_proofs, proof * } proof * ast_manager::mk_nnf_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (proofs_disabled()) return m_undef_proof; ptr_buffer args; args.append(num_proofs, (expr**) proofs); @@ -3166,7 +3143,7 @@ proof * ast_manager::mk_nnf_star(expr * s, expr * t, unsigned num_proofs, proof } proof * ast_manager::mk_skolemization(expr * q, expr * e) { - if (m_proof_mode == PGM_DISABLED) + if (proofs_disabled()) return m_undef_proof; SASSERT(is_bool(q)); SASSERT(is_bool(e)); @@ -3174,7 +3151,7 @@ proof * ast_manager::mk_skolemization(expr * q, expr * e) { } proof * ast_manager::mk_cnf_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (proofs_disabled()) return m_undef_proof; ptr_buffer args; args.append(num_proofs, (expr**) proofs); @@ -3183,7 +3160,7 @@ proof * ast_manager::mk_cnf_star(expr * s, expr * t, unsigned num_proofs, proof } proof * ast_manager::mk_and_elim(proof * p, unsigned i) { - if (m_proof_mode == PGM_DISABLED) + if (proofs_disabled()) return m_undef_proof; SASSERT(has_fact(p)); SASSERT(is_and(get_fact(p))); @@ -3194,7 +3171,7 @@ proof * ast_manager::mk_and_elim(proof * p, unsigned i) { } proof * ast_manager::mk_not_or_elim(proof * p, unsigned i) { - if (m_proof_mode == PGM_DISABLED) + if (proofs_disabled()) return m_undef_proof; SASSERT(has_fact(p)); SASSERT(is_not(get_fact(p))); @@ -3217,7 +3194,7 @@ proof * ast_manager::mk_th_lemma( unsigned num_params, parameter const* params ) { - if (m_proof_mode == PGM_DISABLED) + if (proofs_disabled()) return m_undef_proof; ptr_buffer args; diff --git a/src/ast/ast.h b/src/ast/ast.h index 4eb43d30b..e579e4565 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -121,6 +121,20 @@ public: explicit parameter(unsigned ext_id, bool):m_kind(PARAM_EXTERNAL), m_ext_id(ext_id) {} parameter(parameter const&); + parameter(parameter && other) : m_kind(other.m_kind) { + switch (other.m_kind) { + case PARAM_INT: m_int = other.get_int(); break; + case PARAM_AST: m_ast = other.get_ast(); break; + case PARAM_SYMBOL: m_symbol = other.m_symbol; break; + case PARAM_RATIONAL: m_rational = 0; std::swap(m_rational, other.m_rational); break; + case PARAM_DOUBLE: m_dval = other.m_dval; break; + case PARAM_EXTERNAL: m_ext_id = other.m_ext_id; break; + default: + UNREACHABLE(); + break; + } + } + ~parameter(); parameter& operator=(parameter const& other); @@ -1382,8 +1396,7 @@ public: enum proof_gen_mode { PGM_DISABLED, - PGM_COARSE, - PGM_FINE + PGM_ENABLED }; // ----------------------------------- @@ -1435,6 +1448,8 @@ public: void show_id_gen(); + void update_fresh_id(ast_manager const& other); + protected: reslimit m_limit; small_object_allocator m_alloc; @@ -2074,15 +2089,14 @@ protected: proof * mk_proof(family_id fid, decl_kind k, expr * arg1, expr * arg2); proof * mk_proof(family_id fid, decl_kind k, expr * arg1, expr * arg2, expr * arg3); + proof * mk_undef_proof() const { return m_undef_proof; } + public: bool proofs_enabled() const { return m_proof_mode != PGM_DISABLED; } bool proofs_disabled() const { return m_proof_mode == PGM_DISABLED; } - bool coarse_grain_proofs() const { return m_proof_mode == PGM_COARSE; } - bool fine_grain_proofs() const { return m_proof_mode == PGM_FINE; } proof_gen_mode proof_mode() const { return m_proof_mode; } void toggle_proof_mode(proof_gen_mode m) { m_proof_mode = m; } // APIs for creating proof objects return [undef] - proof * mk_undef_proof() const { return m_undef_proof; } bool is_proof(expr const * n) const { return is_app(n) && to_app(n)->get_decl()->get_range() == m_proof_sort; } diff --git a/src/ast/ast_pp_dot.cpp b/src/ast/ast_pp_dot.cpp new file mode 100644 index 000000000..1dfbe9aae --- /dev/null +++ b/src/ast/ast_pp_dot.cpp @@ -0,0 +1,133 @@ +/*++ + +Abstract: Pretty-printer for proofs in Graphviz format + +--*/ + +#include "util/util.h" +#include "util/map.h" +#include "ast/ast_pp_dot.h" + +// string escaping for DOT +std::string escape_dot(std::string const & s) { + std::string res; + res.reserve(s.size()); // preallocate + for (auto c : s) { + if (c == '\n') + res.append("\\l"); + else + res.push_back(c); + } + return res; +} + +// map from proofs to unique IDs +typedef obj_map expr2id; + +// temporary structure for traversing the proof and printing it +struct ast_pp_dot_st { + ast_manager & m_manager; + std::ostream & m_out; + const ast_pp_dot * m_pp; + unsigned m_next_id; + expr2id m_id_map; + obj_hashtable m_printed; + svector m_to_print; + bool m_first; + + ast_pp_dot_st(const ast_pp_dot * pp, std::ostream & out) : + m_manager(pp->get_manager()), + m_out(out), + m_pp(pp), + m_next_id(0), + m_id_map(), + m_printed(), + m_to_print(), + m_first(true) {} + + ~ast_pp_dot_st() {}; + + void push_term(const expr * a) { m_to_print.push_back(a); } + + void pp_loop() { + // DFS traversal + while (!m_to_print.empty()) { + const expr * a = m_to_print.back(); + m_to_print.pop_back(); + if (!m_printed.contains(a)) { + m_printed.insert(a); + if (m().is_proof(a)) + pp_step(to_app(a)); + else + pp_atomic_step(a); + } + } + } + +private: + + inline ast_manager & m() const { return m_manager; } + + // label for an expression + std::string label_of_expr(const expr * e) const { + expr_ref er((expr*)e, m()); + std::ostringstream out; + out << er << std::flush; + return escape_dot(out.str()); + } + + void pp_atomic_step(const expr * e) { + unsigned id = get_id(e); + m_out << "node_" << id << " [shape=box,color=\"yellow\",style=\"filled\",label=\"" << label_of_expr(e) << "\"] ;" << std::endl; + } + + void pp_step(const proof * p) { + TRACE("pp_ast_dot_step", tout << " :kind " << p->get_kind() << " :num-args " << p->get_num_args() << "\n";); + if (m().has_fact(p)) { + // print result + expr* p_res = m().get_fact(p); // result of proof step + unsigned id = get_id(p); + unsigned num_parents = m().get_num_parents(p); + const char* color = + m_first ? (m_first=false,"color=\"red\"") : num_parents==0 ? "color=\"yellow\"": ""; + m_out << "node_" << id << + " [shape=box,style=\"filled\",label=\"" << label_of_expr(p_res) << "\"" + << color << "]" << std::endl; + // now print edges to parents (except last one, which is the result) + std::string label = p->get_decl()->get_name().str(); + for (unsigned i = 0 ; i < num_parents; ++i) { + expr* parent = m().get_parent(p, i); + // explore parent, also print a link to it + push_term(to_app(parent)); + m_out << "node_" << id << " -> " << "node_" << get_id((expr*)parent) + << "[label=\"" << label << "\"];" << std::endl;; + } + } else { + pp_atomic_step(p); + } + } + + // find a unique ID for this proof + unsigned get_id(const expr * e) { + unsigned id = 0; + if (!m_id_map.find(e, id)) { + id = m_next_id++; + m_id_map.insert(e, id); + } + return id; + } + +}; + +// main printer +std::ostream & ast_pp_dot::pp(std::ostream & out) const { + out << "digraph proof { " << std::endl; + ast_pp_dot_st pp_st(this, out); + pp_st.push_term(m_pr); + pp_st.pp_loop(); + out << std::endl << " } " << std::endl << std::flush; + return out; +} + +std::ostream &operator<<(std::ostream &out, const ast_pp_dot & p) { return p.pp(out); } + diff --git a/src/ast/ast_pp_dot.h b/src/ast/ast_pp_dot.h new file mode 100644 index 000000000..537754e83 --- /dev/null +++ b/src/ast/ast_pp_dot.h @@ -0,0 +1,24 @@ +/*++ + +Abstract: Pretty-printer for proofs in Graphviz format + +--*/ + +#pragma once + +#include +#include "ast_pp.h" + +class ast_pp_dot { + ast_manager & m_manager; + proof * const m_pr; + + public: + ast_pp_dot(proof *pr, ast_manager &m) : m_manager(m), m_pr(pr) {} + ast_pp_dot(proof_ref &e) : m_manager(e.m()), m_pr(e.get()) {} + + std::ostream & pp(std::ostream & out) const; + ast_manager & get_manager() const { return m_manager; } +}; + +std::ostream &operator<<(std::ostream &out, const ast_pp_dot & p); \ No newline at end of file diff --git a/src/ast/ast_pp_util.cpp b/src/ast/ast_pp_util.cpp index 677422b8a..43dccac02 100644 --- a/src/ast/ast_pp_util.cpp +++ b/src/ast/ast_pp_util.cpp @@ -36,7 +36,6 @@ void ast_pp_util::collect(expr_ref_vector const& es) { } void ast_pp_util::display_decls(std::ostream& out) { - smt2_pp_environment_dbg env(m); ast_smt_pp pp(m); unsigned n = coll.get_num_sorts(); for (unsigned i = 0; i < n; ++i) { @@ -46,26 +45,25 @@ void ast_pp_util::display_decls(std::ostream& out) { for (unsigned i = 0; i < n; ++i) { func_decl* f = coll.get_func_decls()[i]; if (f->get_family_id() == null_family_id) { - ast_smt2_pp(out, f, env); - out << "\n"; + ast_smt2_pp(out, f, m_env) << "\n"; } } + SASSERT(coll.get_num_preds() == 0); } void ast_pp_util::display_asserts(std::ostream& out, expr_ref_vector const& fmls, bool neat) { if (neat) { - smt2_pp_environment_dbg env(m); - for (unsigned i = 0; i < fmls.size(); ++i) { + for (expr* f : fmls) { out << "(assert "; - ast_smt2_pp(out, fmls[i], env); + ast_smt2_pp(out, f, m_env); out << ")\n"; } } else { ast_smt_pp ll_smt2_pp(m); - for (unsigned i = 0; i < fmls.size(); ++i) { + for (expr* f : fmls) { out << "(assert "; - ll_smt2_pp.display_expr_smt2(out, fmls[i]); + ll_smt2_pp.display_expr_smt2(out, f); out << ")\n"; } } diff --git a/src/ast/ast_pp_util.h b/src/ast/ast_pp_util.h index 964a862a2..ead31a475 100644 --- a/src/ast/ast_pp_util.h +++ b/src/ast/ast_pp_util.h @@ -20,14 +20,16 @@ Revision History: #define AST_PP_UTIL_H_ #include "ast/decl_collector.h" +#include "ast/ast_smt2_pp.h" class ast_pp_util { ast_manager& m; + smt2_pp_environment_dbg m_env; public: decl_collector coll; - ast_pp_util(ast_manager& m): m(m), coll(m, false) {} + ast_pp_util(ast_manager& m): m(m), m_env(m), coll(m, false) {} void collect(expr* e); @@ -38,6 +40,8 @@ class ast_pp_util { void display_decls(std::ostream& out); void display_asserts(std::ostream& out, expr_ref_vector const& fmls, bool neat = true); + + smt2_pp_environment& env() { return m_env; } }; #endif /* AST_PP_UTIL_H_ */ diff --git a/src/ast/ast_printer.cpp b/src/ast/ast_printer.cpp index b5cf60ee9..9e19e4bfe 100644 --- a/src/ast/ast_printer.cpp +++ b/src/ast/ast_printer.cpp @@ -34,7 +34,7 @@ public: out << f->get_name(); } virtual void pp(sort * s, format_ns::format_ref & r) const { mk_smt2_format(s, env(), params_ref(), r); } - virtual void pp(func_decl * f, format_ns::format_ref & r) const { mk_smt2_format(f, env(), params_ref(), r); } + virtual void pp(func_decl * f, format_ns::format_ref & r) const { mk_smt2_format(f, env(), params_ref(), r, "declare-fun"); } virtual void pp(expr * n, format_ns::format_ref & r) const { sbuffer buf; mk_smt2_format(n, env(), params_ref(), 0, 0, r, buf); diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index e4a875005..bb5359c19 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -31,8 +31,13 @@ using namespace format_ns; #define MAX_INDENT 16 #define SMALL_INDENT 2 -format * smt2_pp_environment::pp_fdecl_name(symbol const & s, unsigned & len) const { +format * smt2_pp_environment::pp_fdecl_name(symbol const & s, unsigned & len, bool is_skolem) const { ast_manager & m = get_manager(); +#if 0 + symbol s1 = m_renaming.get_symbol(s, is_skolem); + len = static_cast(strlen(s1.bare_str())); + return mk_string(m, s1.bare_str()); +#else if (is_smt2_quoted_symbol(s)) { std::string str = mk_smt2_quoted_symbol(s); len = static_cast(str.length()); @@ -44,12 +49,14 @@ format * smt2_pp_environment::pp_fdecl_name(symbol const & s, unsigned & len) co return mk_string(m, str.c_str()); } else if (!s.bare_str()) { + len = 4; return mk_string(m, "null"); } else { len = static_cast(strlen(s.bare_str())); return mk_string(m, s.bare_str()); } +#endif } format * smt2_pp_environment::pp_fdecl_name(func_decl * f, unsigned & len) const { @@ -68,7 +75,7 @@ format * smt2_pp_environment::pp_fdecl_name(func_decl * f, unsigned & len) const } else { symbol s = f->get_name(); - return pp_fdecl_name(s, len); + return pp_fdecl_name(s, len, f->is_skolem()); } } @@ -613,9 +620,9 @@ class smt2_printer { return f; ptr_buffer buf; buf.push_back(f); - for (unsigned i = 0; i < names.size(); i++) { - buf.push_back(pp_simple_attribute(is_pos ? ":lblpos " : ":lblneg ", names[i])); - } + for (symbol const& n : names) + buf.push_back(pp_simple_attribute(is_pos ? ":lblpos " : ":lblneg ", n)); + return mk_seq1(m(), buf.begin(), buf.end(), f2f(), "!"); } @@ -890,8 +897,21 @@ class smt2_printer { } } + void register_var_names(unsigned n) { + unsigned idx = 1; + for (unsigned i = 0; i < n; i++) { + symbol name = next_name("x", idx); + SASSERT(!m_var_names_set.contains(name)); + m_var_names.push_back(name); + m_var_names_set.insert(name); + } + } + void unregister_var_names(quantifier * q) { - unsigned num_decls = q->get_num_decls(); + unregister_var_names(q->get_num_decls()); + } + + void unregister_var_names(unsigned num_decls) { for (unsigned i = 0; i < num_decls; i++) { symbol s = m_var_names.back(); m_var_names.pop_back(); @@ -899,25 +919,28 @@ class smt2_printer { } } - format * pp_var_decls(quantifier * q) { + format * pp_var_args(unsigned num_decls, sort* const* srts) { ptr_buffer buf; - unsigned num_decls = q->get_num_decls(); SASSERT(num_decls <= m_var_names.size()); symbol * it = m_var_names.end() - num_decls; for (unsigned i = 0; i < num_decls; i++, it++) { - format * fs[1] = { m_env.pp_sort(q->get_decl_sort(i)) }; + format * fs[1] = { m_env.pp_sort(srts[i]) }; std::string var_name; if (is_smt2_quoted_symbol (*it)) { var_name = mk_smt2_quoted_symbol (*it); } else { - var_name = it->str ();\ + var_name = it->str (); } buf.push_back(mk_seq1(m(), fs, fs+1, f2f(), var_name.c_str ())); } return mk_seq5(m(), buf.begin(), buf.end(), f2f()); } + format * pp_var_decls(quantifier * q) { + return pp_var_args(q->get_num_decls(), q->get_decl_sorts()); + } + void process_quantifier(quantifier * q, frame & fr) { if (fr.m_idx == 0) { begin_scope(); @@ -1112,7 +1135,7 @@ public: r = m_env.pp_sort(s); } - void operator()(func_decl * f, format_ref & r) { + void operator()(func_decl * f, format_ref & r, char const* cmd) { unsigned arity = f->get_arity(); unsigned len; format * fname = m_env.pp_fdecl_name(f, len); @@ -1124,9 +1147,27 @@ public: } args[1] = mk_seq5(m(), buf.begin(), buf.end(), f2f()); args[2] = m_env.pp_sort(f->get_range()); - r = mk_seq1(m(), args, args+3, f2f(), "declare-fun"); + r = mk_seq1(m(), args, args+3, f2f(), cmd); } + + void operator()(func_decl * f, expr * e, format_ref & r, char const* cmd) { + unsigned arity = f->get_arity(); + unsigned len; + format * fname = m_env.pp_fdecl_name(f, len); + register_var_names(f->get_arity()); + format * args[4]; + args[0] = fname; + args[1] = pp_var_args(f->get_arity(), f->get_domain()); + args[2] = m_env.pp_sort(f->get_range()); + process(e, r); + args[3] = r; + r = mk_seq1(m(), args, args+4, f2f(), cmd); + unregister_var_names(f->get_arity()); + } + + + }; void mk_smt2_format(expr * n, smt2_pp_environment & env, params_ref const & p, @@ -1141,9 +1182,14 @@ void mk_smt2_format(sort * s, smt2_pp_environment & env, params_ref const & p, f pr(s, r); } -void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const & p, format_ref & r) { +void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const & p, format_ref & r, char const* cmd) { smt2_printer pr(env, p); - pr(f, r); + pr(f, r, cmd); +} + +void mk_smt2_format(func_decl * f, expr * e, smt2_pp_environment & env, params_ref const & p, format_ref & r, char const* cmd) { + smt2_printer pr(env, p); + pr(f, e, r, cmd); } void mk_smt2_format(unsigned sz, expr * const* es, smt2_pp_environment & env, params_ref const & p, @@ -1184,17 +1230,29 @@ std::ostream & ast_smt2_pp(std::ostream & out, sort * s, smt2_pp_environment & e return out; } -std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p, unsigned indent) { +std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd) { ast_manager & m = env.get_manager(); format_ref r(fm(m)); sbuffer var_names; - mk_smt2_format(f, env, p, r); + mk_smt2_format(f, env, p, r, cmd); if (indent > 0) r = mk_indent(m, indent, r.get()); pp(out, r.get(), m, p); return out; } +std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd) { + ast_manager & m = env.get_manager(); + format_ref r(fm(m)); + sbuffer var_names; + mk_smt2_format(f, e, env, p, r, cmd); + if (indent > 0) + r = mk_indent(m, indent, r.get()); + pp(out, r.get(), m, p); + return out; +} + + std::ostream & ast_smt2_pp(std::ostream & out, unsigned sz, expr * const* es, smt2_pp_environment & env, params_ref const & p, unsigned indent, unsigned num_vars, char const * var_prefix) { ast_manager & m = env.get_manager(); @@ -1207,6 +1265,15 @@ std::ostream & ast_smt2_pp(std::ostream & out, unsigned sz, expr * const* es, sm return out; } +std::ostream & ast_smt2_pp(std::ostream & out, symbol const& s, bool is_skolem, smt2_pp_environment & env, params_ref const& p) { + unsigned len; + ast_manager & m = env.get_manager(); + format_ref r(fm(m)); + r = env.pp_fdecl_name(s, len, is_skolem); + pp(out, r.get(), m, p); + return out; +} + mk_ismt2_pp::mk_ismt2_pp(ast * t, ast_manager & m, params_ref const & p, unsigned indent, unsigned num_vars, char const * var_prefix): m_ast(t), m_manager(m), @@ -1225,6 +1292,8 @@ mk_ismt2_pp::mk_ismt2_pp(ast * t, ast_manager & m, unsigned indent, unsigned num m_var_prefix(var_prefix) { } + + std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p) { smt2_pp_environment_dbg env(p.m_manager); if (p.m_ast == 0) { @@ -1272,14 +1341,14 @@ std::ostream& operator<<(std::ostream& out, app_ref_vector const& e) { } std::ostream& operator<<(std::ostream& out, func_decl_ref_vector const& e) { - for (unsigned i = 0; i < e.size(); ++i) - out << mk_ismt2_pp(e[i], e.get_manager()) << "\n"; + for (func_decl* f : e) + out << mk_ismt2_pp(f, e.get_manager()) << "\n"; return out; } std::ostream& operator<<(std::ostream& out, sort_ref_vector const& e) { - for (unsigned i = 0; i < e.size(); ++i) - out << mk_ismt2_pp(e[i], e.get_manager()) << "\n"; + for (sort* s : e) + out << mk_ismt2_pp(s, e.get_manager()) << "\n"; return out; } diff --git a/src/ast/ast_smt2_pp.h b/src/ast/ast_smt2_pp.h index 3e8b1aa39..a1a457916 100644 --- a/src/ast/ast_smt2_pp.h +++ b/src/ast/ast_smt2_pp.h @@ -31,10 +31,12 @@ Revision History: #include "ast/dl_decl_plugin.h" #include "ast/seq_decl_plugin.h" #include "ast/datatype_decl_plugin.h" +#include "ast/ast_smt_pp.h" #include "util/smt2_util.h" class smt2_pp_environment { protected: + mutable smt_renaming m_renaming; format_ns::format * mk_neg(format_ns::format * f) const; format_ns::format * mk_float(rational const & val) const; bool is_indexed_fdecl(func_decl * f) const; @@ -61,7 +63,7 @@ public: virtual format_ns::format * pp_string_literal(app * t); virtual format_ns::format * pp_sort(sort * s); virtual format_ns::format * pp_fdecl_ref(func_decl * f); - format_ns::format * pp_fdecl_name(symbol const & fname, unsigned & len) const; + format_ns::format * pp_fdecl_name(symbol const & fname, unsigned & len, bool is_skolem) const; format_ns::format * pp_fdecl_name(func_decl * f, unsigned & len) const; }; @@ -95,12 +97,14 @@ void mk_smt2_format(expr * n, smt2_pp_environment & env, params_ref const & p, unsigned num_vars, char const * var_prefix, format_ns::format_ref & r, sbuffer & var_names); void mk_smt2_format(sort * s, smt2_pp_environment & env, params_ref const & p, format_ns::format_ref & r); -void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const & p, format_ns::format_ref & r); +void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const & p, format_ns::format_ref & r, char const* cmd); std::ostream & ast_smt2_pp(std::ostream & out, expr * n, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = 0); std::ostream & ast_smt2_pp(std::ostream & out, sort * s, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0); -std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0); +std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "declare-fun"); +std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "define-fun"); +std::ostream & ast_smt2_pp(std::ostream & out, symbol const& s, bool is_skolem, smt2_pp_environment & env, params_ref const& p = params_ref()); /** \brief Internal wrapper (for debugging purposes only) diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index 906fd054b..59b66d082 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -45,14 +45,6 @@ symbol smt_renaming::fix_symbol(symbol s, int k) { std::ostringstream buffer; char const * data = s.is_numerical() ? "" : s.bare_str(); - if (data[0] && !data[1]) { - switch (data[0]) { - case '/': data = "op_div"; break; - case '%': data = "op_mod"; break; - default: break; - } - } - if (k == 0 && *data) { if (s.is_numerical()) { return s; @@ -70,14 +62,17 @@ symbol smt_renaming::fix_symbol(symbol s, int k) { return symbol(buffer.str().c_str()); } - if (is_smt2_quoted_symbol(s)) { + if (!s.bare_str()) { + buffer << "null"; + } + else if (is_smt2_quoted_symbol(s)) { buffer << mk_smt2_quoted_symbol(s); } else { buffer << s; } if (k > 0) { - buffer << k; + buffer << "!" << k; } return symbol(buffer.str().c_str()); @@ -124,15 +119,30 @@ bool smt_renaming::all_is_legal(char const* s) { smt_renaming::smt_renaming() { for (unsigned i = 0; i < ARRAYSIZE(m_predef_names); ++i) { symbol s(m_predef_names[i]); - m_translate.insert(s, s); + m_translate.insert(s, sym_b(s, false)); m_rev_translate.insert(s, s); } } - -symbol smt_renaming::get_symbol(symbol s0) { +// Ensure that symbols that are used both with skolems and non-skolems are named apart. +symbol smt_renaming::get_symbol(symbol s0, bool is_skolem) { + sym_b sb; symbol s; - if (m_translate.find(s0, s)) { + if (m_translate.find(s0, sb)) { + if (is_skolem == sb.is_skolem) + return sb.name; + if (sb.name_aux != symbol::null) { + return sb.name_aux; + } + int k = 0; + symbol s1; + do { + s = fix_symbol(s0, k++); + } + while (s == s0 || (m_rev_translate.find(s, s1) && s1 != s0)); + m_rev_translate.insert(s, s0); + sb.name_aux = s; + m_translate.insert(s, sb); return s; } @@ -141,7 +151,7 @@ symbol smt_renaming::get_symbol(symbol s0) { s = fix_symbol(s0, k++); } while (m_rev_translate.contains(s)); - m_translate.insert(s0, s); + m_translate.insert(s0, sym_b(s, is_skolem)); m_rev_translate.insert(s, s0); return s; } @@ -202,7 +212,7 @@ class smt_printer { } void pp_decl(func_decl* d) { - symbol sym = m_renaming.get_symbol(d->get_name()); + symbol sym = m_renaming.get_symbol(d->get_name(), d->is_skolem()); if (d->get_family_id() == m_dt_fid) { datatype_util util(m_manager); if (util.is_recognizer(d)) { @@ -313,7 +323,7 @@ class smt_printer { if (num_sorts > 0) { m_out << "("; } - m_out << m_renaming.get_symbol(s->get_name()); + m_out << m_renaming.get_symbol(s->get_name(), false); if (num_sorts > 0) { for (unsigned i = 0; i < num_sorts; ++i) { m_out << " "; @@ -324,7 +334,7 @@ class smt_printer { return; } else { - sym = m_renaming.get_symbol(s->get_name()); + sym = m_renaming.get_symbol(s->get_name(), false); } visit_params(true, sym, s->get_num_parameters(), s->get_parameters()); } @@ -396,17 +406,17 @@ class smt_printer { else if (m_manager.is_label(n, pos, names) && names.size() >= 1) { m_out << "(! "; pp_marked_expr(n->get_arg(0)); - m_out << (pos?":lblpos":":lblneg") << " " << m_renaming.get_symbol(names[0]) << ")"; + m_out << (pos?":lblpos":":lblneg") << " " << m_renaming.get_symbol(names[0], false) << ")"; } else if (m_manager.is_label_lit(n, names) && names.size() >= 1) { - m_out << "(! true :lblpos " << m_renaming.get_symbol(names[0]) << ")"; + m_out << "(! true :lblpos " << m_renaming.get_symbol(names[0], false) << ")"; } else if (num_args == 0) { if (decl->private_parameters()) { - m_out << m_renaming.get_symbol(decl->get_name()); + m_out << m_renaming.get_symbol(decl->get_name(), decl->is_skolem()); } else { - symbol sym = m_renaming.get_symbol(decl->get_name()); + symbol sym = m_renaming.get_symbol(decl->get_name(), decl->is_skolem()); visit_params(false, sym, decl->get_num_parameters(), decl->get_parameters()); } } @@ -500,7 +510,7 @@ class smt_printer { for (unsigned i = 0; i < q->get_num_decls(); ++i) { sort* s = q->get_decl_sort(i); m_out << "("; - print_bound(m_renaming.get_symbol(q->get_decl_name(i))); + print_bound(m_renaming.get_symbol(q->get_decl_name(i), false)); m_out << " "; visit_sort(s, true); m_out << ") "; @@ -565,7 +575,7 @@ class smt_printer { unsigned num_decls = q->get_num_decls(); if (idx < num_decls) { unsigned offs = num_decls-idx-1; - symbol name = m_renaming.get_symbol(q->get_decl_name(offs)); + symbol name = m_renaming.get_symbol(q->get_decl_name(offs), false); print_bound(name); return; } @@ -807,15 +817,15 @@ public: m_out << ")"; } m_out << "("; - m_out << m_renaming.get_symbol(d->name()); + m_out << m_renaming.get_symbol(d->name(), false); m_out << " "; bool first_constr = true; for (datatype::constructor* f : *d) { if (!first_constr) m_out << " "; else first_constr = false; m_out << "("; - m_out << m_renaming.get_symbol(f->name()); + m_out << m_renaming.get_symbol(f->name(), false); for (datatype::accessor* a : *f) { - m_out << " (" << m_renaming.get_symbol(a->name()) << " "; + m_out << " (" << m_renaming.get_symbol(a->name(), false) << " "; visit_sort(a->range()); m_out << ")"; } diff --git a/src/ast/ast_smt_pp.h b/src/ast/ast_smt_pp.h index dd2a6d753..fd2cd186b 100644 --- a/src/ast/ast_smt_pp.h +++ b/src/ast/ast_smt_pp.h @@ -24,8 +24,10 @@ Revision History: #include "util/map.h" class smt_renaming { + struct sym_b { symbol name; bool is_skolem; symbol name_aux; sym_b(symbol n, bool s): name(n), is_skolem(s) {} sym_b():name(),is_skolem(false) {}}; typedef map symbol2symbol; - symbol2symbol m_translate; + typedef map symbol2sym_b; + symbol2sym_b m_translate; symbol2symbol m_rev_translate; symbol fix_symbol(symbol s, int k); @@ -35,8 +37,8 @@ class smt_renaming { bool all_is_legal(char const* s); public: smt_renaming(); - symbol get_symbol(symbol s0); - symbol operator()(symbol const & s) { return get_symbol(s); } + symbol get_symbol(symbol s0, bool is_skolem = false); + symbol operator()(symbol const & s, bool is_skolem = false) { return get_symbol(s, is_skolem); } }; class ast_smt_pp { diff --git a/src/ast/ast_translation.cpp b/src/ast/ast_translation.cpp index 1bce4bcbe..0599ec91d 100644 --- a/src/ast/ast_translation.cpp +++ b/src/ast/ast_translation.cpp @@ -160,7 +160,7 @@ void ast_translation::mk_func_decl(func_decl * f, frame & fr) { new_fi.set_chainable(fi->is_chainable()); new_fi.set_pairwise(fi->is_pairwise()); new_fi.set_injective(fi->is_injective()); - new_fi.set_skolem(fi->is_skolem()); +/// TBD new_fi.set_skolem(fi->is_skolem()); new_fi.set_idempotent(fi->is_idempotent()); new_f = m_to_manager.mk_func_decl(f->get_name(), diff --git a/src/ast/ast_translation.h b/src/ast/ast_translation.h index b278791d7..886aaf417 100644 --- a/src/ast/ast_translation.h +++ b/src/ast/ast_translation.h @@ -52,6 +52,7 @@ public: ast_translation(ast_manager & from, ast_manager & to, bool copy_plugins = true) : m_from_manager(from), m_to_manager(to) { if (copy_plugins) m_to_manager.copy_families_plugins(m_from_manager); + m_to_manager.update_fresh_id(m_from_manager); } ~ast_translation(); diff --git a/src/ast/bv_decl_plugin.cpp b/src/ast/bv_decl_plugin.cpp index fd176c1e0..04f69b8c6 100644 --- a/src/ast/bv_decl_plugin.cpp +++ b/src/ast/bv_decl_plugin.cpp @@ -863,8 +863,7 @@ app * bv_util::mk_numeral(rational const & val, sort* s) const { } app * bv_util::mk_numeral(rational const & val, unsigned bv_size) const { - parameter p1(val); - parameter p[2] = { p1, parameter(static_cast(bv_size)) }; + parameter p[2] = { parameter(val), parameter(static_cast(bv_size)) }; return m_manager.mk_app(get_fid(), OP_BV_NUM, 2, p, 0, 0); } diff --git a/src/ast/decl_collector.cpp b/src/ast/decl_collector.cpp index e000f43df..0df4b0fc5 100644 --- a/src/ast/decl_collector.cpp +++ b/src/ast/decl_collector.cpp @@ -45,12 +45,14 @@ bool decl_collector::is_bool(sort * s) { } void decl_collector::visit_func(func_decl * n) { - family_id fid = n->get_family_id(); - if (fid == null_family_id) { - if (m_sep_preds && is_bool(n->get_range())) - m_preds.push_back(n); - else - m_decls.push_back(n); + if (!m_visited.is_marked(n)) { + family_id fid = n->get_family_id(); + if (fid == null_family_id) { + if (m_sep_preds && is_bool(n->get_range())) + m_preds.push_back(n); + else + m_decls.push_back(n); + } } } @@ -63,31 +65,29 @@ decl_collector::decl_collector(ast_manager & m, bool preds): } void decl_collector::visit(ast* n) { - ptr_vector todo; - todo.push_back(n); - while (!todo.empty()) { - n = todo.back(); - todo.pop_back(); + m_todo.push_back(n); + while (!m_todo.empty()) { + n = m_todo.back(); + m_todo.pop_back(); if (!m_visited.is_marked(n)) { - m_visited.mark(n, true); switch(n->get_kind()) { case AST_APP: { app * a = to_app(n); for (unsigned i = 0; i < a->get_num_args(); ++i) { - todo.push_back(a->get_arg(i)); + m_todo.push_back(a->get_arg(i)); } - todo.push_back(a->get_decl()); + m_todo.push_back(a->get_decl()); break; } case AST_QUANTIFIER: { quantifier * q = to_quantifier(n); unsigned num_decls = q->get_num_decls(); for (unsigned i = 0; i < num_decls; ++i) { - todo.push_back(q->get_decl_sort(i)); + m_todo.push_back(q->get_decl_sort(i)); } - todo.push_back(q->get_expr()); + m_todo.push_back(q->get_expr()); for (unsigned i = 0; i < q->get_num_patterns(); ++i) { - todo.push_back(q->get_pattern(i)); + m_todo.push_back(q->get_pattern(i)); } break; } @@ -97,9 +97,9 @@ void decl_collector::visit(ast* n) { case AST_FUNC_DECL: { func_decl * d = to_func_decl(n); for (unsigned i = 0; i < d->get_arity(); ++i) { - todo.push_back(d->get_domain(i)); + m_todo.push_back(d->get_domain(i)); } - todo.push_back(d->get_range()); + m_todo.push_back(d->get_range()); visit_func(d); break; } @@ -108,6 +108,7 @@ void decl_collector::visit(ast* n) { default: UNREACHABLE(); } + m_visited.mark(n, true); } } } diff --git a/src/ast/decl_collector.h b/src/ast/decl_collector.h index 053d6df41..0a812bb2c 100644 --- a/src/ast/decl_collector.h +++ b/src/ast/decl_collector.h @@ -36,7 +36,7 @@ class decl_collector { void visit_sort(sort* n); bool is_bool(sort* s); - void visit_func(func_decl* n); + ptr_vector m_todo; public: @@ -44,6 +44,7 @@ public: decl_collector(ast_manager & m, bool preds=true); ast_manager & m() { return m_manager; } + void visit_func(func_decl* n); void visit(ast * n); void visit(unsigned n, expr* const* es); void visit(expr_ref_vector const& es); diff --git a/src/ast/expr_substitution.h b/src/ast/expr_substitution.h index 90154d163..1590f888c 100644 --- a/src/ast/expr_substitution.h +++ b/src/ast/expr_substitution.h @@ -80,6 +80,7 @@ public: m_trail_lim.resize(new_sz); } } + unsigned scope_level() const { return m_trail_lim.size(); } bool empty() const { return m_subst.empty(); } expr* find(expr * e) { proof* pr; expr* d = 0; if (find(e, d, pr)) return d; else return e; } bool find(expr * s, expr * & def, proof * & def_pr) { return m_subst.find(s, def, def_pr); } diff --git a/src/ast/fpa/bv2fpa_converter.cpp b/src/ast/fpa/bv2fpa_converter.cpp index 93bd79571..2f5a7c3c1 100644 --- a/src/ast/fpa/bv2fpa_converter.cpp +++ b/src/ast/fpa/bv2fpa_converter.cpp @@ -250,7 +250,7 @@ bv2fpa_converter::array_model bv2fpa_converter::convert_array_func_interp(model_ am.new_float_fd = m.mk_fresh_func_decl(arity, array_domain.c_ptr(), rng); am.new_float_fi = convert_func_interp(mc, am.new_float_fd, bv_f); am.bv_fd = bv_f; - am.result = arr_util.mk_as_array(f->get_range(), am.new_float_fd); + am.result = arr_util.mk_as_array(am.new_float_fd); return am; } diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index 13a7599a9..220295dad 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -3076,8 +3076,6 @@ void fpa2bv_converter::mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * split_fp(x, sgn, e, s); mk_is_nan(x, x_is_nan); - sort * fp_srt = m.get_sort(x); - expr_ref unspec(m); mk_to_ieee_bv_unspecified(f, num, args, unspec); diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index 247e9dea1..6fc65543d 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -40,7 +40,7 @@ enum nnf_mode { transformation will be in skolem normal form. If a formula is too expensive to be put into NNF, then nested quantifiers and labels are renamed. - + This mode is sufficient when using E-matching. */ NNF_QUANT, /* A subformula is put into NNF if it contains @@ -48,7 +48,7 @@ enum nnf_mode { quantifier. The result of the transformation will be in skolem normal form, and the body of quantifiers will be in NNF. If a ground formula is too expensive to - be put into NNF, then nested quantifiers and labels + be put into NNF, then nested quantifiers and labels are renamed. This mode is sufficient when using Superposition @@ -89,7 +89,7 @@ class skolemizer { } TRACE("skolemizer", tout << "skid: " << q->get_skid() << "\n";); - + expr_ref_vector substitution(m()); unsigned num_decls = q->get_num_decls(); for (unsigned i = num_decls; i > 0; ) { @@ -111,7 +111,7 @@ class skolemizer { substitution.push_back(0); } // - // (VAR num_decls) ... (VAR num_decls+sz-1) + // (VAR num_decls) ... (VAR num_decls+sz-1) // are in positions num_decls .. num_decls+sz-1 // std::reverse(substitution.c_ptr(), substitution.c_ptr() + substitution.size()); @@ -139,7 +139,7 @@ class skolemizer { s(body, substitution.size(), substitution.c_ptr(), r); p = 0; if (m().proofs_enabled()) { - if (q->is_forall()) + if (q->is_forall()) p = m().mk_skolemization(m().mk_not(q), m().mk_not(r)); else p = m().mk_skolemization(q, r); @@ -175,7 +175,7 @@ public: m_cache_pr.insert(q, p); } } - + bool is_sk_hack(expr * p) const { SASSERT(m().is_pattern(p)); if (to_app(p)->get_num_args() != 1) @@ -204,11 +204,11 @@ struct nnf::imp { unsigned m_i:28; unsigned m_pol:1; // pos/neg polarity unsigned m_in_q:1; // true if m_curr is nested in a quantifier - unsigned m_new_child:1; + unsigned m_new_child:1; unsigned m_cache_result:1; unsigned m_spos; // top of the result stack, when the frame was created. - frame(expr_ref& n, bool pol, bool in_q, bool cache_res, unsigned spos): - m_curr(n), + frame(expr_ref && n, bool pol, bool in_q, bool cache_res, unsigned spos): + m_curr(std::move(n)), m_i(0), m_pol(pol), m_in_q(in_q), @@ -216,6 +216,16 @@ struct nnf::imp { m_cache_result(cache_res), m_spos(spos) { } + frame(frame && other): + m_curr(std::move(other.m_curr)), + m_i(other.m_i), + m_pol(other.m_pol), + m_in_q(other.m_in_q), + m_new_child(other.m_new_child), + m_cache_result(other.m_cache_result), + m_spos(other.m_spos) { + } + }; // There are four caches: @@ -223,22 +233,22 @@ struct nnf::imp { #define POS_NQ_CIDX 1 // positive polarity and not nested in a quantifier #define NEG_Q_CIDX 2 // negative polarity and nested in a quantifier #define POS_Q_CIDX 3 // positive polarity and nested in a quantifier - + ast_manager & m_manager; vector m_frame_stack; expr_ref_vector m_result_stack; - + typedef act_cache cache; cache * m_cache[4]; expr_ref_vector m_todo_defs; proof_ref_vector m_todo_proofs; - + // proof generation goodness ---- proof_ref_vector m_result_pr_stack; cache * m_cache_pr[4]; // ------------------------------ - + skolemizer m_skolemizer; // configuration ---------------- @@ -249,7 +259,7 @@ struct nnf::imp { name_exprs * m_name_nested_formulas; name_exprs * m_name_quant; - + unsigned long long m_max_memory; // in bytes imp(ast_manager & m, defined_names & n, params_ref const & p): @@ -292,9 +302,9 @@ struct nnf::imp { m_mode = NNF_FULL; else if (mode_sym == "quantifiers") m_mode = NNF_QUANT; - else + else throw nnf_params_exception("invalid NNF mode"); - + TRACE("nnf", tout << "nnf-mode: " << m_mode << " " << mode_sym << "\n" << _p << "\n";); m_ignore_labels = p.ignore_labels(); @@ -324,12 +334,11 @@ struct nnf::imp { } void push_frame(expr * t, bool pol, bool in_q, bool cache_res) { - expr_ref tr(t, m()); - m_frame_stack.push_back(frame(tr, pol, in_q, cache_res, m_result_stack.size())); + m_frame_stack.push_back(frame(expr_ref(t, m()), pol, in_q, cache_res, m_result_stack.size())); } - static unsigned get_cache_idx(bool pol, bool in_q) { - return static_cast(in_q) * 2 + static_cast(pol); + static unsigned get_cache_idx(bool pol, bool in_q) { + return static_cast(in_q) * 2 + static_cast(pol); } void cache_result(expr * t, bool pol, bool in_q, expr * v, proof * pr) { @@ -339,8 +348,8 @@ struct nnf::imp { m_cache_pr[idx]->insert(t, pr); } - expr * get_cached(expr * t, bool pol, bool in_q) const { - return m_cache[get_cache_idx(pol, in_q)]->find(t); + expr * get_cached(expr * t, bool pol, bool in_q) const { + return m_cache[get_cache_idx(pol, in_q)]->find(t); } proof * get_cached_pr(expr * t, bool pol, bool in_q) const { @@ -368,12 +377,12 @@ struct nnf::imp { return false; } - + void checkpoint() { cooperate("nnf"); if (memory::get_allocation_size() > m_max_memory) throw nnf_exception(Z3_MAX_MEMORY_MSG); - if (m().canceled()) + if (m().canceled()) throw nnf_exception(m().limit().get_cancel_msg()); } @@ -382,11 +391,11 @@ struct nnf::imp { m_frame_stack.back().m_new_child = true; } - void set_new_child_flag(expr * old_t, expr * new_t) { - if (old_t != new_t) - set_new_child_flag(); + void set_new_child_flag(expr * old_t, expr * new_t) { + if (old_t != new_t) + set_new_child_flag(); } - + void skip(expr * t, bool pol) { expr * r = pol ? t : m().mk_not(t); m_result_stack.push_back(r); @@ -448,10 +457,10 @@ struct nnf::imp { if (pol) { if (old_e->get_decl() == new_e->get_decl()) return m().mk_oeq_congruence(old_e, new_e, num_parents, parents); - else + else return m().mk_nnf_pos(old_e, new_e, num_parents, parents); } - else + else return m().mk_nnf_neg(old_e, new_e, num_parents, parents); } @@ -468,7 +477,7 @@ struct nnf::imp { r = m().mk_and(t->get_num_args(), m_result_stack.c_ptr() + fr.m_spos); else r = m().mk_or(t->get_num_args(), m_result_stack.c_ptr() + fr.m_spos); - + m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(r); if (proofs_enabled()) { @@ -520,7 +529,7 @@ struct nnf::imp { r = m().mk_or(2, m_result_stack.c_ptr() + fr.m_spos); else r = m().mk_and(2, m_result_stack.c_ptr() + fr.m_spos); - + m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(r); if (proofs_enabled()) { @@ -554,7 +563,7 @@ struct nnf::imp { default: break; } - + expr * const * rs = m_result_stack.c_ptr() + fr.m_spos; expr * _cond = rs[0]; expr * _not_cond = rs[1]; @@ -574,7 +583,7 @@ struct nnf::imp { } bool is_eq(app * t) const { return m().is_eq(t) || m().is_iff(t); } - + bool process_iff_xor(app * t, frame & fr) { SASSERT(t->get_num_args() == 2); switch (fr.m_i) { @@ -605,7 +614,7 @@ struct nnf::imp { expr * not_rhs = rs[3]; app * r; - if (is_eq(t) == fr.m_pol) + if (is_eq(t) == fr.m_pol) r = m().mk_and(m().mk_or(not_lhs, rhs), m().mk_or(lhs, not_rhs)); else r = m().mk_and(m().mk_or(lhs, rhs), m().mk_or(not_lhs, not_rhs)); @@ -626,7 +635,7 @@ struct nnf::imp { else return process_default(t, fr); } - + bool process_default(app * t, frame & fr) { SASSERT(fr.m_i == 0); if (m_mode == NNF_FULL || t->has_quantifiers() || t->has_labels()) { @@ -636,10 +645,10 @@ struct nnf::imp { m_name_nested_formulas->operator()(t, m_todo_defs, m_todo_proofs, n2, pr2); else m_name_quant->operator()(t, m_todo_defs, m_todo_proofs, n2, pr2); - + if (!fr.m_pol) n2 = m().mk_not(n2); - + m_result_stack.push_back(n2); if (proofs_enabled()) { if (!fr.m_pol) { @@ -666,10 +675,10 @@ struct nnf::imp { expr * arg = m_result_stack.back(); proof * arg_pr = proofs_enabled() ? m_result_pr_stack.back() : 0; - if (m_ignore_labels && !proofs_enabled()) + if (m_ignore_labels && !proofs_enabled()) return true; // the result is already on the stack - + buffer names; bool pos; m().is_label(t, pos, names); @@ -684,7 +693,7 @@ struct nnf::imp { pr = m().mk_transitivity(mk_proof(fr.m_pol, 1, &arg_pr, t, to_app(aux)), m().mk_iff_oeq(m().mk_rewrite(aux, r))); } - } + } else { r = arg; if (proofs_enabled()) { @@ -692,7 +701,7 @@ struct nnf::imp { pr = m().mk_transitivity(p1, arg_pr); } } - + m_result_stack.pop_back(); m_result_stack.push_back(r); if (proofs_enabled()) { @@ -729,7 +738,7 @@ struct nnf::imp { if (m().is_label(t)) { return process_label(t, fr); } - + return process_default(t, fr); } @@ -737,7 +746,7 @@ struct nnf::imp { skip(v, fr.m_pol); return true; } - + bool process_quantifier(quantifier * q, frame & fr) { expr_ref r(m()); proof_ref pr(m()); @@ -757,7 +766,7 @@ struct nnf::imp { if (q->is_forall() == fr.m_pol || !m_skolemize) { expr * new_expr = m_result_stack.back(); proof * new_expr_pr = proofs_enabled() ? m_result_pr_stack.back() : 0; - + ptr_buffer new_patterns; if (q->is_forall() == fr.m_pol) { @@ -773,7 +782,7 @@ struct nnf::imp { // New quantifier has existential force. // So, ignore patterns } - + quantifier * new_q = 0; proof * new_q_pr = 0; if (fr.m_pol) { @@ -786,7 +795,7 @@ struct nnf::imp { if (proofs_enabled()) new_q_pr = m().mk_nnf_neg(q, new_q, 1, &new_expr_pr); } - + m_result_stack.pop_back(); m_result_stack.push_back(new_q); if (proofs_enabled()) { @@ -809,7 +818,7 @@ struct nnf::imp { } return true; } - + void recover_result(expr * t, expr_ref & result, proof_ref & result_pr) { // recover result from the top of the stack. result = m_result_stack.back(); @@ -873,7 +882,7 @@ struct nnf::imp { process(n, r, pr); unsigned old_sz1 = new_defs.size(); unsigned old_sz2 = new_def_proofs.size(); - + for (unsigned i = 0; i < m_todo_defs.size(); i++) { expr_ref dr(m()); proof_ref dpr(m()); @@ -881,7 +890,7 @@ struct nnf::imp { new_defs.push_back(dr); if (proofs_enabled()) { proof * new_pr = m().mk_modus_ponens(m_todo_proofs.get(i), dpr); - new_def_proofs.push_back(new_pr); + new_def_proofs.push_back(new_pr); } } std::reverse(new_defs.c_ptr() + old_sz1, new_defs.c_ptr() + new_defs.size()); @@ -898,7 +907,7 @@ nnf::nnf(ast_manager & m, defined_names & n, params_ref const & p) { nnf::~nnf() { dealloc(m_imp); } - + void nnf::operator()(expr * n, expr_ref_vector & new_defs, proof_ref_vector & new_def_proofs, expr_ref & r, proof_ref & p) { m_imp->operator()(n, new_defs, new_def_proofs, r, p); TRACE("nnf_result", tout << mk_ismt2_pp(n, m_imp->m()) << "\nNNF result:\n" << mk_ismt2_pp(r, m_imp->m()) << "\n";); diff --git a/src/ast/normal_forms/pull_quant.cpp b/src/ast/normal_forms/pull_quant.cpp index 239fd9008..ee618c747 100644 --- a/src/ast/normal_forms/pull_quant.cpp +++ b/src/ast/normal_forms/pull_quant.cpp @@ -229,7 +229,7 @@ struct pull_quant::imp { proofs.push_back(m_manager.mk_pull_quant(arg, to_quantifier(new_arg))); } pull_quant1(to_app(n)->get_decl(), new_args.size(), new_args.c_ptr(), r); - if (m_manager.fine_grain_proofs()) { + if (m_manager.proofs_enabled()) { app * r1 = m_manager.mk_app(to_app(n)->get_decl(), new_args.size(), new_args.c_ptr()); proof * p1 = proofs.empty() ? 0 : m_manager.mk_congruence(to_app(n), r1, proofs.size(), proofs.c_ptr()); proof * p2 = r1 == r ? 0 : m_manager.mk_pull_quant(r1, to_quantifier(r)); @@ -240,7 +240,7 @@ struct pull_quant::imp { expr_ref new_expr(m_manager); pull_quant1(to_quantifier(n)->get_expr(), new_expr); pull_quant1(to_quantifier(n), new_expr, r); - if (m_manager.fine_grain_proofs()) { + if (m_manager.proofs_enabled()) { quantifier * q1 = m_manager.update_quantifier(to_quantifier(n), new_expr); proof * p1 = 0; if (n != q1) { diff --git a/src/ast/pattern/expr_pattern_match.cpp b/src/ast/pattern/expr_pattern_match.cpp index 770832d1f..628c777d3 100644 --- a/src/ast/pattern/expr_pattern_match.cpp +++ b/src/ast/pattern/expr_pattern_match.cpp @@ -179,11 +179,11 @@ expr_pattern_match::compile(expr* q) } if (m_regs.size() <= max_reg) { - m_regs.resize(max_reg+1, 0); + m_regs.resize(max_reg+1); } if (m_bound_dom.size() <= num_bound) { - m_bound_dom.resize(num_bound+1, 0); - m_bound_rng.resize(num_bound+1, 0); + m_bound_dom.resize(num_bound+1); + m_bound_rng.resize(num_bound+1); } instr.m_kind = YIELD; diff --git a/src/ast/pattern/pattern_inference.cpp b/src/ast/pattern/pattern_inference.cpp index 5a1fa473c..60d004347 100644 --- a/src/ast/pattern/pattern_inference.cpp +++ b/src/ast/pattern/pattern_inference.cpp @@ -606,7 +606,7 @@ bool pattern_inference_cfg::reduce_quantifier( result = m.update_quantifier_weight(tmp, new_weight); TRACE("pattern_inference", tout << "found patterns in database, weight: " << new_weight << "\n" << mk_pp(new_q, m) << "\n";); } - if (m.fine_grain_proofs()) + if (m.proofs_enabled()) result_pr = m.mk_rewrite(q, new_q); return true; } @@ -671,7 +671,7 @@ bool pattern_inference_cfg::reduce_quantifier( quantifier_ref new_q(m.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_body), m); if (weight != q->get_weight()) new_q = m.update_quantifier_weight(new_q, weight); - if (m.fine_grain_proofs()) { + if (m.proofs_enabled()) { proof* new_body_pr = m.mk_reflexivity(new_body); result_pr = m.mk_quant_intro(q, new_q, new_body_pr); } @@ -689,7 +689,7 @@ bool pattern_inference_cfg::reduce_quantifier( warning_msg("pulled nested quantifier to be able to find an useable pattern (quantifier id: %s)", q->get_qid().str().c_str()); } new_q = m.update_quantifier(result2, new_patterns.size(), (expr**) new_patterns.c_ptr(), result2->get_expr()); - if (m.fine_grain_proofs()) { + if (m.proofs_enabled()) { result_pr = m.mk_transitivity(new_pr, m.mk_quant_intro(result2, new_q, m.mk_reflexivity(new_q->get_expr()))); } TRACE("pattern_inference", tout << "pulled quantifier:\n" << mk_pp(new_q, m) << "\n";); diff --git a/src/ast/proof_checker/CMakeLists.txt b/src/ast/proofs/CMakeLists.txt similarity index 60% rename from src/ast/proof_checker/CMakeLists.txt rename to src/ast/proofs/CMakeLists.txt index 5c947adec..6eedb0fac 100644 --- a/src/ast/proof_checker/CMakeLists.txt +++ b/src/ast/proofs/CMakeLists.txt @@ -1,6 +1,7 @@ -z3_add_component(proof_checker +z3_add_component(proofs SOURCES proof_checker.cpp + proof_utils.cpp COMPONENT_DEPENDENCIES rewriter -) +) \ No newline at end of file diff --git a/src/ast/proof_checker/proof_checker.cpp b/src/ast/proofs/proof_checker.cpp similarity index 99% rename from src/ast/proof_checker/proof_checker.cpp rename to src/ast/proofs/proof_checker.cpp index afe2baeed..3f9438229 100644 --- a/src/ast/proof_checker/proof_checker.cpp +++ b/src/ast/proofs/proof_checker.cpp @@ -4,10 +4,9 @@ Copyright (c) 2015 Microsoft Corporation --*/ -#include "ast/proof_checker/proof_checker.h" +#include "ast/proofs/proof_checker.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" -// include "spc_decl_plugin.h" #include "ast/ast_smt_pp.h" #include "ast/arith_decl_plugin.h" #include "ast/rewriter/th_rewriter.h" diff --git a/src/ast/proof_checker/proof_checker.h b/src/ast/proofs/proof_checker.h similarity index 100% rename from src/ast/proof_checker/proof_checker.h rename to src/ast/proofs/proof_checker.h diff --git a/src/muz/base/proof_utils.cpp b/src/ast/proofs/proof_utils.cpp similarity index 70% rename from src/muz/base/proof_utils.cpp rename to src/ast/proofs/proof_utils.cpp index 3b2d50fdc..58500cb79 100644 --- a/src/muz/base/proof_utils.cpp +++ b/src/ast/proofs/proof_utils.cpp @@ -1,15 +1,343 @@ - /*++ -Copyright (c) 2015 Microsoft Corporation +Copyright (c) 2017 Arie Gurfinkel + +Module Name: + + proof_utils.cpp + +Abstract: + Utilities to traverse and manipulate proofs + +Author: + Bernhard Gleiss + Arie Gurfinkel + +Revision History: --*/ -#include "muz/base/dl_util.h" -#include "muz/base/proof_utils.h" -#include "ast/ast_smt2_pp.h" +#include "ast/ast_util.h" +#include "ast/ast_pp.h" +#include "ast/proofs/proof_utils.h" +#include "ast/proofs/proof_checker.h" #include "ast/rewriter/var_subst.h" +#include "util/container_util.h" + + +proof_post_order::proof_post_order(proof* root, ast_manager& manager) : m(manager) +{m_todo.push_back(root);} + +bool proof_post_order::hasNext() +{return !m_todo.empty();} + +/* + * iterative post-order depth-first search (DFS) through the proof DAG + */ +proof* proof_post_order::next() +{ + while (!m_todo.empty()) { + proof* currentNode = m_todo.back(); + + // if we haven't already visited the current unit + if (!m_visited.is_marked(currentNode)) { + bool existsUnvisitedParent = false; + + // add unprocessed premises to stack for DFS. + // If there is at least one unprocessed premise, don't compute the result + // for currentProof now, but wait until those unprocessed premises are processed. + for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) { + SASSERT(m.is_proof(currentNode->get_arg(i))); + proof* premise = to_app(currentNode->get_arg(i)); + + // if we haven't visited the current premise yet + if (!m_visited.is_marked(premise)) { + // add it to the stack + m_todo.push_back(premise); + existsUnvisitedParent = true; + } + } + + // if we already visited all parent-inferences, we can visit the inference too + if (!existsUnvisitedParent) { + m_visited.mark(currentNode, true); + m_todo.pop_back(); + return currentNode; + } + } else { + m_todo.pop_back(); + } + } + // we have already iterated through all inferences + return nullptr; +} + class reduce_hypotheses { + ast_manager &m; + // tracking all created expressions + expr_ref_vector m_pinned; + + // cache for the transformation + obj_map m_cache; + + // map from unit literals to their hypotheses-free derivations + obj_map m_units; + + // -- all hypotheses in the the proof + obj_hashtable m_hyps; + + // marks hypothetical proofs + ast_mark m_hypmark; + + + // stack + ptr_vector m_todo; + + void reset() + { + m_cache.reset(); + m_units.reset(); + m_hyps.reset(); + m_hypmark.reset(); + m_pinned.reset(); + } + + bool compute_mark1(proof *pr) + { + bool hyp_mark = false; + // lemmas clear all hypotheses + if (!m.is_lemma(pr)) { + for (unsigned i = 0, sz = m.get_num_parents(pr); i < sz; ++i) { + if (m_hypmark.is_marked(m.get_parent(pr, i))) { + hyp_mark = true; + break; + } + } + } + m_hypmark.mark(pr, hyp_mark); + return hyp_mark; + } + + void compute_marks(proof* pr) { + proof *p; + proof_post_order pit(pr, m); + while (pit.hasNext()) { + p = pit.next(); + if (m.is_hypothesis(p)) { + m_hypmark.mark(p, true); + m_hyps.insert(m.get_fact(p)); + } + else { + bool hyp_mark = compute_mark1(p); + // collect units that are hyp-free and are used as hypotheses somewhere + if (!hyp_mark && m.has_fact(p) && m_hyps.contains(m.get_fact(p))) { + m_units.insert(m.get_fact(p), p); + } + } + } + } + void find_units(proof *pr) + { + // optional. not implemented yet. + } + + void reduce(proof* pf, proof_ref &out) + { + proof *res = NULL; + + m_todo.reset(); + m_todo.push_back(pf); + ptr_buffer args; + bool dirty = false; + + while (!m_todo.empty()) { + proof *p, *tmp, *pp; + unsigned todo_sz; + + p = m_todo.back(); + if (m_cache.find(p, tmp)) { + res = tmp; + m_todo.pop_back(); + continue; + } + + dirty = false; + args.reset(); + todo_sz = m_todo.size(); + for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { + pp = m.get_parent(p, i); + if (m_cache.find(pp, tmp)) { + args.push_back(tmp); + dirty = dirty || pp != tmp; + } else { + m_todo.push_back(pp); + } + } + + if (todo_sz < m_todo.size()) { continue; } + else { m_todo.pop_back(); } + + if (m.is_hypothesis(p)) { + // hyp: replace by a corresponding unit + if (m_units.find(m.get_fact(p), tmp)) { + res = tmp; + } else { res = p; } + } + + else if (!dirty) { res = p; } + + else if (m.is_lemma(p)) { + //lemma: reduce the premise; remove reduced consequences from conclusion + SASSERT(args.size() == 1); + res = mk_lemma_core(args.get(0), m.get_fact(p)); + compute_mark1(res); + } else if (m.is_unit_resolution(p)) { + // unit: reduce untis; reduce the first premise; rebuild unit resolution + res = mk_unit_resolution_core(args.size(), args.c_ptr()); + compute_mark1(res); + } else { + // other: reduce all premises; reapply + if (m.has_fact(p)) { args.push_back(to_app(m.get_fact(p))); } + SASSERT(p->get_decl()->get_arity() == args.size()); + res = m.mk_app(p->get_decl(), args.size(), (expr * const*)args.c_ptr()); + m_pinned.push_back(res); + compute_mark1(res); + } + + SASSERT(res); + m_cache.insert(p, res); + + if (m.has_fact(res) && m.is_false(m.get_fact(res))) { break; } + } + + out = res; + } + + // returns true if (hypothesis (not a)) would be reduced + bool is_reduced(expr *a) + { + expr_ref e(m); + if (m.is_not(a)) { e = to_app(a)->get_arg(0); } + else { e = m.mk_not(a); } + + return m_units.contains(e); + } + + proof *mk_lemma_core(proof *pf, expr *fact) + { + ptr_buffer args; + expr_ref lemma(m); + + if (m.is_or(fact)) { + for (unsigned i = 0, sz = to_app(fact)->get_num_args(); i < sz; ++i) { + expr *a = to_app(fact)->get_arg(i); + if (!is_reduced(a)) + { args.push_back(a); } + } + } else if (!is_reduced(fact)) + { args.push_back(fact); } + + + if (args.size() == 0) { return pf; } + else if (args.size() == 1) { + lemma = args.get(0); + } else { + lemma = m.mk_or(args.size(), args.c_ptr()); + } + proof* res = m.mk_lemma(pf, lemma); + m_pinned.push_back(res); + + if (m_hyps.contains(lemma)) + { m_units.insert(lemma, res); } + return res; + } + + proof *mk_unit_resolution_core(unsigned num_args, proof* const *args) + { + + ptr_buffer pf_args; + pf_args.push_back(args [0]); + + app *cls_fact = to_app(m.get_fact(args[0])); + ptr_buffer cls; + if (m.is_or(cls_fact)) { + for (unsigned i = 0, sz = cls_fact->get_num_args(); i < sz; ++i) + { cls.push_back(cls_fact->get_arg(i)); } + } else { cls.push_back(cls_fact); } + + // construct new resovent + ptr_buffer new_fact_cls; + bool found; + // XXX quadratic + for (unsigned i = 0, sz = cls.size(); i < sz; ++i) { + found = false; + for (unsigned j = 1; j < num_args; ++j) { + if (m.is_complement(cls.get(i), m.get_fact(args [j]))) { + found = true; + pf_args.push_back(args [j]); + break; + } + } + if (!found) { + new_fact_cls.push_back(cls.get(i)); + } + } + + SASSERT(new_fact_cls.size() + pf_args.size() - 1 == cls.size()); + expr_ref new_fact(m); + new_fact = mk_or(m, new_fact_cls.size(), new_fact_cls.c_ptr()); + + // create new proof step + proof *res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), new_fact); + m_pinned.push_back(res); + return res; + } + + // reduce all units, if any unit reduces to false return true and put its proof into out + bool reduce_units(proof_ref &out) + { + proof_ref res(m); + for (auto entry : m_units) { + reduce(entry.get_value(), res); + if (m.is_false(m.get_fact(res))) { + out = res; + return true; + } + res.reset(); + } + return false; + } + + +public: + reduce_hypotheses(ast_manager &m) : m(m), m_pinned(m) {} + + + void operator()(proof_ref &pr) + { + compute_marks(pr); + if (!reduce_units(pr)) { + reduce(pr.get(), pr); + } + reset(); + } +}; + +void reduce_hypotheses(proof_ref &pr) { + ast_manager &m = pr.get_manager(); + class reduce_hypotheses hypred(m); + hypred(pr); + DEBUG_CODE(proof_checker pc(m); + expr_ref_vector side(m); + SASSERT(pc.check(pr, side)); + ); +} + + + +#include "ast/ast_smt2_pp.h" + +class reduce_hypotheses0 { typedef obj_hashtable expr_set; ast_manager& m; // reference for any expression created by the tranformation @@ -85,7 +413,7 @@ class reduce_hypotheses { m_hyprefs.push_back(hyps); inherited = false; } - datalog::set_union(*hyps, *hyps1); + set_union(*hyps, *hyps1); } } } @@ -137,7 +465,7 @@ class reduce_hypotheses { } public: - reduce_hypotheses(ast_manager& m): m(m), m_refs(m) {} + reduce_hypotheses0(ast_manager& m): m(m), m_refs(m) {} void operator()(proof_ref& pr) { proof_ref tmp(m); @@ -416,7 +744,7 @@ public: void proof_utils::reduce_hypotheses(proof_ref& pr) { ast_manager& m = pr.get_manager(); - class reduce_hypotheses reduce(m); + class reduce_hypotheses0 reduce(m); reduce(pr); CTRACE("proof_utils", !is_closed(m, pr), tout << mk_pp(pr, m) << "\n";); } diff --git a/src/ast/proofs/proof_utils.h b/src/ast/proofs/proof_utils.h new file mode 100644 index 000000000..b953c834d --- /dev/null +++ b/src/ast/proofs/proof_utils.h @@ -0,0 +1,238 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + proof_utils.h + +Abstract: + Utilities to traverse and manipulate proofs + +Author: + Bernhard Gleiss + Arie Gurfinkel + Nikolaj Bjorner + +Revision History: + +--*/ + +#ifndef PROOF_UTILS_H_ +#define PROOF_UTILS_H_ +#include "ast/ast.h" +#include "ast/ast_pp.h" +#include "ast/rewriter/bool_rewriter.h" +#include "ast/proofs/proof_checker.h" + +/* + * iterator, which traverses the proof in depth-first post-order. + */ + +class proof_post_order { +public: + proof_post_order(proof* refutation, ast_manager& manager); + bool hasNext(); + proof* next(); + +private: + ptr_vector m_todo; + ast_mark m_visited; // the proof nodes we have already visited + ast_manager& m; +}; + +void reduce_hypotheses(proof_ref &pr); + + +class proof_utils { +public: + /** + \brief reduce the set of hypotheses used in the proof. + */ + static void reduce_hypotheses(proof_ref& pr); + + /** + \brief Check that a proof does not contain open hypotheses. + */ + static bool is_closed(ast_manager& m, proof* p); + + /** + \brief Permute unit resolution rule with th-lemma + */ + static void permute_unit_resolution(proof_ref& pr); + + /** + \brief Push instantiations created in hyper-resolutions up to leaves. + This produces a "ground" proof where leaves are annotated by instantiations. + */ + static void push_instantiations_up(proof_ref& pr); + + +}; + +class elim_aux_assertions { + + static bool matches_fact(expr_ref_vector &args, expr* &match) { + ast_manager &m = args.get_manager(); + expr *fact = args.back(); + for (unsigned i = 0, sz = args.size() - 1; i < sz; ++i) { + expr *arg = args.get(i); + if (m.is_proof(arg) && + m.has_fact(to_app(arg)) && + m.get_fact(to_app(arg)) == fact) { + match = arg; + return true; + } + } + return false; + } + + app_ref m_aux; +public: + elim_aux_assertions(app_ref aux) : m_aux(aux) {} + + void mk_or_core(expr_ref_vector &args, expr_ref &res) + { + ast_manager &m = args.get_manager(); + unsigned j = 0; + for (unsigned i = 0, sz = args.size(); i < sz; ++i) { + if (m.is_false(args.get(i))) { continue; } + if (i != j) { args [j] = args.get(i); } + ++j; + } + SASSERT(j >= 1); + res = j > 1 ? m.mk_or(j, args.c_ptr()) : args.get(0); + } + + void mk_app(func_decl *decl, expr_ref_vector &args, expr_ref &res) + { + ast_manager &m = args.get_manager(); + bool_rewriter brwr(m); + + if (m.is_or(decl)) + { mk_or_core(args, res); } + else if (m.is_iff(decl) && args.size() == 2) + // avoiding simplifying equalities. In particular, + // we don't want (= (not a) (not b)) to be reduced to (= a b) + { res = m.mk_iff(args.get(0), args.get(1)); } + else + { brwr.mk_app(decl, args.size(), args.c_ptr(), res); } + } + + void operator()(ast_manager &m, proof *pr, proof_ref &res) + { + DEBUG_CODE(proof_checker pc(m); + expr_ref_vector side(m); + SASSERT(pc.check(pr, side)); + ); + obj_map cache; + bool_rewriter brwr(m); + + // for reference counting of new proofs + app_ref_vector pinned(m); + + ptr_vector todo; + todo.push_back(pr); + + expr_ref not_aux(m); + not_aux = m.mk_not(m_aux); + + expr_ref_vector args(m); + + while (!todo.empty()) { + app *p, *r; + expr *a; + + p = todo.back(); + if (cache.find(pr, r)) { + todo.pop_back(); + continue; + } + + SASSERT(!todo.empty() || pr == p); + bool dirty = false; + unsigned todo_sz = todo.size(); + args.reset(); + for (unsigned i = 0, sz = p->get_num_args(); i < sz; ++i) { + expr* arg = p->get_arg(i); + if (arg == m_aux.get()) { + dirty = true; + args.push_back(m.mk_true()); + } else if (arg == not_aux.get()) { + dirty = true; + args.push_back(m.mk_false()); + } + // skip (asserted m_aux) + else if (m.is_asserted(arg, a) && a == m_aux.get()) { + dirty = true; + } + // skip (hypothesis m_aux) + else if (m.is_hypothesis(arg, a) && a == m_aux.get()) { + dirty = true; + } else if (is_app(arg) && cache.find(to_app(arg), r)) { + dirty |= (arg != r); + args.push_back(r); + } else if (is_app(arg)) + { todo.push_back(to_app(arg)); } + else + // -- not an app + { args.push_back(arg); } + + } + if (todo_sz < todo.size()) { + // -- process parents + args.reset(); + continue; + } + + // ready to re-create + app_ref newp(m); + if (!dirty) { newp = p; } + else if (m.is_unit_resolution(p)) { + if (args.size() == 2) + // unit resolution with m_aux that got collapsed to nothing + { newp = to_app(args.get(0)); } + else { + ptr_vector parents; + for (unsigned i = 0, sz = args.size() - 1; i < sz; ++i) + { parents.push_back(to_app(args.get(i))); } + SASSERT(parents.size() == args.size() - 1); + newp = m.mk_unit_resolution(parents.size(), parents.c_ptr()); + // XXX the old and new facts should be + // equivalent. The test here is much + // stronger. It might need to be relaxed. + SASSERT(m.get_fact(newp) == args.back()); + pinned.push_back(newp); + } + } else if (matches_fact(args, a)) { + newp = to_app(a); + } else { + expr_ref papp(m); + mk_app(p->get_decl(), args, papp); + newp = to_app(papp.get()); + pinned.push_back(newp); + } + cache.insert(p, newp); + todo.pop_back(); + CTRACE("virtual", + p->get_decl_kind() == PR_TH_LEMMA && + p->get_decl()->get_parameter(0).get_symbol() == "arith" && + p->get_decl()->get_num_parameters() > 1 && + p->get_decl()->get_parameter(1).get_symbol() == "farkas", + tout << "Old pf: " << mk_pp(p, m) << "\n" + << "New pf: " << mk_pp(newp, m) << "\n";); + } + + proof *r; + VERIFY(cache.find(pr, r)); + + DEBUG_CODE( + proof_checker pc(m); + expr_ref_vector side(m); + SASSERT(pc.check(r, side)); + ); + + res = r ; + } +}; + +#endif diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h b/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h index cd66a5124..a80994f6c 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h +++ b/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h @@ -272,7 +272,7 @@ void bit_blaster_tpl::mk_multiplier(unsigned sz, expr * const * a_bits, exp zero = m().mk_false(); vector< expr_ref_vector > pps; - pps.resize(sz, m()); + pps.resize(sz, expr_ref_vector(m())); for (unsigned i = 0; i < sz; i++) { checkpoint(); diff --git a/src/ast/rewriter/fpa_rewriter.cpp b/src/ast/rewriter/fpa_rewriter.cpp index 818336c75..e62c9346f 100644 --- a/src/ast/rewriter/fpa_rewriter.cpp +++ b/src/ast/rewriter/fpa_rewriter.cpp @@ -119,17 +119,15 @@ br_status fpa_rewriter::mk_to_fp(func_decl * f, unsigned num_args, expr * const // BV -> float SASSERT(bvs1 == sbits + ebits); unsynch_mpz_manager & mpzm = m_fm.mpz_manager(); - unsynch_mpq_manager & mpqm = m_fm.mpq_manager(); scoped_mpz sig(mpzm), exp(mpzm); const mpz & sm1 = m_fm.m_powers2(sbits - 1); const mpz & em1 = m_fm.m_powers2(ebits); - scoped_mpq q(mpqm); - mpqm.set(q, r1.to_mpq()); - SASSERT(mpzm.is_one(q.get().denominator())); + const mpq & q = r1.to_mpq(); + SASSERT(mpzm.is_one(q.denominator())); scoped_mpz z(mpzm); - z = q.get().numerator(); + z = q.numerator(); mpzm.rem(z, sm1, sig); mpzm.div(z, sm1, z); diff --git a/src/ast/scoped_proof.h b/src/ast/scoped_proof.h index 0a650ceb7..b2b3d7173 100644 --- a/src/ast/scoped_proof.h +++ b/src/ast/scoped_proof.h @@ -37,7 +37,7 @@ public: class scoped_proof : public scoped_proof_mode { public: - scoped_proof(ast_manager& m): scoped_proof_mode(m, PGM_FINE) {} + scoped_proof(ast_manager& m): scoped_proof_mode(m, PGM_ENABLED) {} }; class scoped_no_proof : public scoped_proof_mode { diff --git a/src/ast/substitution/substitution_tree.cpp b/src/ast/substitution/substitution_tree.cpp index 9befb582f..20d5f1590 100644 --- a/src/ast/substitution/substitution_tree.cpp +++ b/src/ast/substitution/substitution_tree.cpp @@ -256,7 +256,7 @@ void substitution_tree::insert(expr * new_expr) { sort * s = to_var(new_expr)->get_sort(); unsigned id = s->get_decl_id(); if (id >= m_vars.size()) - m_vars.resize(id+1, 0); + m_vars.resize(id+1); if (m_vars[id] == 0) m_vars[id] = alloc(var_ref_vector, m_manager); var_ref_vector * v = m_vars[id]; @@ -277,7 +277,7 @@ void substitution_tree::insert(app * new_expr) { unsigned id = d->get_decl_id(); if (id >= m_roots.size()) - m_roots.resize(id+1, 0); + m_roots.resize(id+1); if (!m_roots[id]) { // there is no tree for the function symbol heading new_expr diff --git a/src/ast/used_vars.cpp b/src/ast/used_vars.cpp index a1cd65feb..a3030f087 100644 --- a/src/ast/used_vars.cpp +++ b/src/ast/used_vars.cpp @@ -58,7 +58,7 @@ void used_vars::process(expr * n, unsigned delta) { if (idx >= delta) { idx = idx - delta; if (idx >= m_found_vars.size()) - m_found_vars.resize(idx + 1, 0); + m_found_vars.resize(idx + 1); m_found_vars[idx] = to_var(n)->get_sort(); } break; diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index 21b66febd..65c8860b1 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -20,6 +20,7 @@ Notes: #include "util/version.h" #include "ast/ast_smt_pp.h" #include "ast/ast_smt2_pp.h" +#include "ast/ast_pp_dot.h" #include "ast/ast_pp.h" #include "ast/array_decl_plugin.h" #include "ast/pp.h" @@ -202,6 +203,26 @@ ATOMIC_CMD(get_proof_cmd, "get-proof", "retrieve proof", { } }); +ATOMIC_CMD(get_proof_graph_cmd, "get-proof-graph", "retrieve proof and print it in graphviz", { + if (!ctx.produce_proofs()) + throw cmd_exception("proof construction is not enabled, use command (set-option :produce-proofs true)"); + if (!ctx.has_manager() || + ctx.cs_state() != cmd_context::css_unsat) + throw cmd_exception("proof is not available"); + proof_ref pr(ctx.m()); + pr = ctx.get_check_sat_result()->get_proof(); + if (pr == 0) + throw cmd_exception("proof is not available"); + if (ctx.well_sorted_check_enabled() && !is_well_sorted(ctx.m(), pr)) { + throw cmd_exception("proof is not well sorted"); + } + + context_params& params = ctx.params(); + const std::string& file = params.m_dot_proof_file; + std::ofstream out(file); + out << ast_pp_dot(pr) << std::endl; +}); + static void print_core(cmd_context& ctx) { ptr_vector core; ctx.get_check_sat_result()->get_unsat_core(core); @@ -840,6 +861,7 @@ void install_basic_cmds(cmd_context & ctx) { ctx.insert(alloc(get_assignment_cmd)); ctx.insert(alloc(get_assertions_cmd)); ctx.insert(alloc(get_proof_cmd)); + ctx.insert(alloc(get_proof_graph_cmd)); ctx.insert(alloc(get_unsat_core_cmd)); ctx.insert(alloc(set_option_cmd)); ctx.insert(alloc(get_option_cmd)); diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 95c030687..06a7cba19 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -43,6 +43,7 @@ Notes: #include "model/model_v2_pp.h" #include "model/model_params.hpp" #include "tactic/tactic_exception.h" +#include "tactic/generic_model_converter.h" #include "solver/smt_logics.h" #include "cmd_context/basic_cmds.h" #include "cmd_context/interpolant_cmds.h" @@ -389,7 +390,7 @@ protected: datalog::dl_decl_util m_dlutil; format_ns::format * pp_fdecl_name(symbol const & s, func_decls const & fs, func_decl * f, unsigned & len) { - format_ns::format * f_name = smt2_pp_environment::pp_fdecl_name(s, len); + format_ns::format * f_name = smt2_pp_environment::pp_fdecl_name(s, len, f->is_skolem()); if (!fs.more_than_one()) return f_name; if (!fs.clash(f)) @@ -399,7 +400,7 @@ protected: format_ns::format * pp_fdecl_ref_core(symbol const & s, func_decls const & fs, func_decl * f) { unsigned len; - format_ns::format * f_name = smt2_pp_environment::pp_fdecl_name(s, len); + format_ns::format * f_name = smt2_pp_environment::pp_fdecl_name(s, len, f->is_skolem()); if (!fs.more_than_one()) return f_name; return pp_signature(f_name, f); @@ -491,6 +492,7 @@ cmd_context::~cmd_context() { finalize_tactic_cmds(); finalize_probes(); reset(true); + m_mc0 = 0; m_solver = 0; m_check_sat_result = 0; } @@ -774,7 +776,6 @@ bool cmd_context::is_func_decl(symbol const & s) const { } void cmd_context::insert(symbol const & s, func_decl * f) { - m_check_sat_result = 0; if (!m_check_logic(f)) { throw cmd_exception(m_check_logic.get_last_error()); } @@ -805,7 +806,6 @@ void cmd_context::insert(symbol const & s, func_decl * f) { } void cmd_context::insert(symbol const & s, psort_decl * p) { - m_check_sat_result = 0; if (m_psort_decls.contains(s)) { throw cmd_exception("sort already defined ", s); } @@ -819,7 +819,6 @@ void cmd_context::insert(symbol const & s, psort_decl * p) { void cmd_context::insert(symbol const & s, unsigned arity, sort *const* domain, expr * t) { expr_ref _t(t, m()); - m_check_sat_result = 0; if (m_builtin_decls.contains(s)) { throw cmd_exception("invalid macro/named expression, builtin symbol ", s); } @@ -864,6 +863,20 @@ void cmd_context::insert(symbol const & s, object_ref * r) { m_object_refs.insert(s, r); } +void cmd_context::model_add(symbol const & s, unsigned arity, sort *const* domain, expr * t) { + if (!m_mc0) m_mc0 = alloc(generic_model_converter, m()); + func_decl_ref fn(m().mk_func_decl(s, arity, domain, m().get_sort(t)), m()); + dictionary::entry * e = m_func_decls.insert_if_not_there2(s, func_decls()); + func_decls & fs = e->get_data().m_value; + fs.insert(m(), fn); + m_mc0->add(fn, t); +} + +void cmd_context::model_del(func_decl* f) { + if (!m_mc0) m_mc0 = alloc(generic_model_converter, m()); + m_mc0->hide(f); +} + void cmd_context::insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector const& ids, expr* e) { expr_ref eq(m()); app_ref lhs(m()); @@ -1066,16 +1079,31 @@ void cmd_context::mk_app(symbol const & s, unsigned num_args, expr * const * arg if (fs.more_than_one()) throw cmd_exception("ambiguous constant reference, more than one constant with the same sort, use a qualified expression (as ) to disumbiguate ", s); func_decl * f = fs.first(); - if (f == 0) + if (f == 0) { throw cmd_exception("unknown constant ", s); + } if (f->get_arity() != 0) throw cmd_exception("invalid function application, missing arguments ", s); result = m().mk_const(f); } else { func_decl * f = fs.find(m(), num_args, args, range); - if (f == 0) - throw cmd_exception("unknown constant ", s); + if (f == 0) { + std::ostringstream buffer; + buffer << "unknown constant " << s << " "; + buffer << " ("; + bool first = true; + for (unsigned i = 0; i < num_args; ++i, first = false) { + if (!first) buffer << " "; + buffer << mk_pp(m().get_sort(args[i]), m()); + } + buffer << ") "; + if (range) buffer << mk_pp(range, m()) << " "; + for (unsigned i = 0; i < fs.get_num_entries(); ++i) { + buffer << "\ndeclared: " << mk_pp(fs.get_entry(i), m()) << " "; + } + throw cmd_exception(buffer.str().c_str()); + } if (well_sorted_check_enabled()) m().check_sort(f, num_args, args); result = m().mk_app(f, num_args, args); @@ -1235,8 +1263,8 @@ void cmd_context::reset(bool finalize) { reset_macros(); reset_func_decls(); restore_assertions(0); - if (m_solver) - m_solver = 0; + m_solver = 0; + m_mc0 = 0; m_scopes.reset(); m_opt = 0; m_pp_env = 0; @@ -1573,6 +1601,7 @@ void cmd_context::display_dimacs() { void cmd_context::display_model(model_ref& mdl) { if (mdl) { + if (m_mc0) (*m_mc0)(mdl); model_params p; if (p.v1() || p.v2()) { std::ostringstream buffer; @@ -1608,7 +1637,9 @@ void cmd_context::validate_check_sat_result(lbool r) { throw cmd_exception("check annotation that says unsat"); #else diagnostic_stream() << "BUG: incompleteness" << std::endl; - exit(ERR_INCOMPLETENESS); + // WORKAROUND: `exit()` causes LSan to be invoked and produce + // many false positives. + _Exit(ERR_INCOMPLETENESS); #endif } break; @@ -1618,7 +1649,9 @@ void cmd_context::validate_check_sat_result(lbool r) { throw cmd_exception("check annotation that says sat"); #else diagnostic_stream() << "BUG: unsoundness" << std::endl; - exit(ERR_UNSOUNDNESS); + // WORKAROUND: `exit()` causes LSan to be invoked and produce + // many false positives. + _Exit(ERR_UNSOUNDNESS); #endif } break; @@ -1776,6 +1809,7 @@ void cmd_context::validate_model() { continue; } TRACE("model_validate", model_smt2_pp(tout, *this, *(md.get()), 0);); + IF_VERBOSE(10, verbose_stream() << "model check failed on: " << mk_pp(a, m()) << "\n";); invalid_model = true; } } @@ -1894,7 +1928,7 @@ void cmd_context::pp(expr * n, format_ns::format_ref & r) const { } void cmd_context::pp(func_decl * f, format_ns::format_ref & r) const { - mk_smt2_format(f, get_pp_env(), params_ref(), r); + mk_smt2_format(f, get_pp_env(), params_ref(), r, "declare-fun"); } void cmd_context::display(std::ostream & out, sort * s, unsigned indent) const { diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index b60f590a9..440c17285 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -23,20 +23,21 @@ Notes: #include #include -#include "ast/ast.h" -#include "ast/ast_printer.h" -#include "cmd_context/pdecl.h" -#include "util/dictionary.h" -#include "solver/solver.h" -#include "ast/datatype_decl_plugin.h" #include "util/stopwatch.h" #include "util/cmd_context_types.h" #include "util/event_handler.h" #include "util/sexpr.h" +#include "util/dictionary.h" +#include "util/scoped_ptr_vector.h" +#include "ast/ast.h" +#include "ast/ast_printer.h" +#include "ast/datatype_decl_plugin.h" +#include "tactic/generic_model_converter.h" +#include "solver/solver.h" +#include "solver/progress_callback.h" +#include "cmd_context/pdecl.h" #include "cmd_context/tactic_manager.h" #include "cmd_context/check_logic.h" -#include "solver/progress_callback.h" -#include "util/scoped_ptr_vector.h" #include "cmd_context/context_params.h" @@ -193,6 +194,7 @@ protected: static std::ostringstream g_error_stream; + generic_model_converter_ref m_mc0; ast_manager * m_manager; bool m_own_manager; bool m_manager_initialized; @@ -322,6 +324,7 @@ public: void set_numeral_as_real(bool f) { m_numeral_as_real = f; } void set_interactive_mode(bool flag) { m_interactive_mode = flag; } void set_ignore_check(bool flag) { m_ignore_check = flag; } + bool ignore_check() const { return m_ignore_check; } void set_exit_on_error(bool flag) { m_exit_on_error = flag; } bool exit_on_error() const { return m_exit_on_error; } bool interactive_mode() const { return m_interactive_mode; } @@ -381,6 +384,8 @@ public: void insert_user_tactic(symbol const & s, sexpr * d); void insert_aux_pdecl(pdecl * p); void insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector const& ids, expr* e); + void model_add(symbol const & s, unsigned arity, sort *const* domain, expr * t); + void model_del(func_decl* f); func_decl * find_func_decl(symbol const & s) const; func_decl * find_func_decl(symbol const & s, unsigned num_indices, unsigned const * indices, unsigned arity, sort * const * domain, sort * range) const; @@ -438,6 +443,8 @@ public: dictionary const & get_macros() const { return m_macros; } + model_converter* get_model_converter() { return m_mc0.get(); } + bool is_model_available() const; double get_seconds() const { return m_watch.get_seconds(); } diff --git a/src/cmd_context/context_params.cpp b/src/cmd_context/context_params.cpp index 9a3339b84..78f4223fc 100644 --- a/src/cmd_context/context_params.cpp +++ b/src/cmd_context/context_params.cpp @@ -111,6 +111,9 @@ void context_params::set(char const * param, char const * value) { else if (p == "trace_file_name") { m_trace_file_name = value; } + else if (p == "dot_proof_file") { + m_dot_proof_file = value; + } else if (p == "unsat_core") { set_bool(m_unsat_core, param, value); } @@ -146,6 +149,7 @@ void context_params::updt_params(params_ref const & p) { m_dump_models = p.get_bool("dump_models", m_dump_models); m_trace = p.get_bool("trace", m_trace); m_trace_file_name = p.get_str("trace_file_name", "z3.log"); + m_dot_proof_file = p.get_str("dot_proof_file", "proof.dot"); m_unsat_core = p.get_bool("unsat_core", m_unsat_core); m_debug_ref_count = p.get_bool("debug_ref_count", m_debug_ref_count); m_smtlib2_compliant = p.get_bool("smtlib2_compliant", m_smtlib2_compliant); @@ -161,6 +165,7 @@ void context_params::collect_param_descrs(param_descrs & d) { d.insert("dump_models", CPK_BOOL, "dump models whenever check-sat returns sat", "false"); d.insert("trace", CPK_BOOL, "trace generation for VCC", "false"); d.insert("trace_file_name", CPK_STRING, "trace out file name (see option 'trace')", "z3.log"); + d.insert("dot_proof_file", CPK_STRING, "file in which to output graphical proofs", "proof.dot"); d.insert("debug_ref_count", CPK_BOOL, "debug support for AST reference counting", "false"); d.insert("smtlib2_compliant", CPK_BOOL, "enable/disable SMT-LIB 2.0 compliance", "false"); collect_solver_param_descrs(d); @@ -192,7 +197,7 @@ void context_params::get_solver_params(ast_manager const & m, params_ref & p, bo ast_manager * context_params::mk_ast_manager() { ast_manager * r = alloc(ast_manager, - m_proof ? PGM_FINE : PGM_DISABLED, + m_proof ? PGM_ENABLED : PGM_DISABLED, m_trace ? m_trace_file_name.c_str() : 0); if (m_smtlib2_compliant) r->enable_int_real_coercions(false); diff --git a/src/cmd_context/context_params.h b/src/cmd_context/context_params.h index c238af556..df62057fe 100644 --- a/src/cmd_context/context_params.h +++ b/src/cmd_context/context_params.h @@ -30,6 +30,7 @@ class context_params { public: bool m_auto_config; bool m_proof; + std::string m_dot_proof_file; bool m_interpolants; bool m_debug_ref_count; bool m_trace; diff --git a/src/cmd_context/interpolant_cmds.cpp b/src/cmd_context/interpolant_cmds.cpp index b5bbea18c..8b9f0ebd8 100644 --- a/src/cmd_context/interpolant_cmds.cpp +++ b/src/cmd_context/interpolant_cmds.cpp @@ -147,7 +147,7 @@ static void compute_interpolant_and_maybe_check(cmd_context & ctx, expr * t, par ast_manager &_m = ctx.m(); // TODO: the following is a HACK to enable proofs in the old smt solver // When we stop using that solver, this hack can be removed - scoped_proof_mode spm(_m,PGM_FINE); + scoped_proof_mode spm(_m,PGM_ENABLED); ctx.params().get_solver_params(_m, p, proofs_enabled, models_enabled, unsat_core_enabled); p.set_bool("proof", true); scoped_ptr sp = (ctx.get_interpolating_solver_factory())(_m, p, true, models_enabled, false, ctx.get_logic()); diff --git a/src/cmd_context/tactic_cmds.cpp b/src/cmd_context/tactic_cmds.cpp index cbc90c62c..cd5eff9a6 100644 --- a/src/cmd_context/tactic_cmds.cpp +++ b/src/cmd_context/tactic_cmds.cpp @@ -200,6 +200,8 @@ public: if (!m_tactic) { throw cmd_exception("check-sat-using needs a tactic argument"); } + if (ctx.ignore_check()) + return; params_ref p = ctx.params().merge_default_params(ps()); tactic_ref tref = using_params(sexpr2tactic(ctx, m_tactic), p); tref->set_logic(ctx.get_logic()); @@ -211,6 +213,7 @@ public: assert_exprs_from(ctx, *g); TRACE("check_sat_using", g->display(tout);); model_ref md; + model_converter_ref mc; proof_ref pr(m); expr_dependency_ref core(m); std::string reason_unknown; @@ -226,7 +229,7 @@ public: cmd_context::scoped_watch sw(ctx); lbool r = l_undef; try { - r = check_sat(t, g, md, result->labels, pr, core, reason_unknown); + r = check_sat(t, g, md, mc, result->labels, pr, core, reason_unknown); ctx.display_sat_result(r); result->set_status(r); if (r == l_undef) { diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index e4c294059..0ad751326 100755 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -57,12 +57,12 @@ typedef ast raw_ast; /** Wrapper around an ast pointer */ class ast_i { - protected: +protected: raw_ast *_ast; - public: +public: raw_ast * const &raw() const {return _ast;} ast_i(raw_ast *a){_ast = a;} - + ast_i(){_ast = 0;} bool eq(const ast_i &other) const { return _ast == other._ast; @@ -86,19 +86,19 @@ class ast_i { /** Reference counting verison of above */ class ast_r : public ast_i { ast_manager *_m; - public: - ast_r(ast_manager *m, raw_ast *a) : ast_i(a) { +public: + ast_r(ast_manager *m, raw_ast *a) : ast_i(a) { _m = m; m->inc_ref(a); } - + ast_r() {_m = 0;} - - ast_r(const ast_r &other) : ast_i(other) { + + ast_r(const ast_r &other) : ast_i(other) { _m = other._m; if (_m) _m->inc_ref(_ast); } - + ast_r &operator=(const ast_r &other) { if(_ast) _m->dec_ref(_ast); @@ -107,12 +107,12 @@ class ast_r : public ast_i { if (_m) _m->inc_ref(_ast); return *this; } - - ~ast_r(){ + + ~ast_r() { if(_ast) _m->dec_ref(_ast); } - + ast_manager *mgr() const {return _m;} }; diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp index c59dd0178..e4730ea63 100755 --- a/src/interp/iz3translate.cpp +++ b/src/interp/iz3translate.cpp @@ -29,6 +29,7 @@ #include "interp/iz3profiling.h" #include "interp/iz3interp.h" #include "interp/iz3proof_itp.h" +#include "ast/ast_pp.h" #include #include @@ -1851,6 +1852,21 @@ public: } break; } + case PR_TRANSITIVITY_STAR: { + // assume the premises are x = y, y = z, z = u, u = v, .. + + ast x = arg(conc(prem(proof,0)),0); + ast y = arg(conc(prem(proof,0)),1); + ast z = arg(conc(prem(proof,1)),1); + res = iproof->make_transitivity(x,y,z,args[0],args[1]); + + for (unsigned i = 2; i < nprems; ++i) { + y = z; + z = arg(conc(prem(proof,i)),1); + res = iproof->make_transitivity(x,y,z,res,args[i]); + } + break; + } case PR_QUANT_INTRO: case PR_MONOTONICITY: { @@ -2029,6 +2045,7 @@ public: break; } default: + IF_VERBOSE(0, verbose_stream() << "Unsupported proof rule: " << expr_ref((expr*)proof.raw(), *proof.mgr()) << "\n";); // pfgoto(proof); // SASSERT(0 && "translate_main: unsupported proof rule"); throw unsupported(); diff --git a/src/interp/iz3translate.h b/src/interp/iz3translate.h index 519a252e0..8ecafbd3a 100755 --- a/src/interp/iz3translate.h +++ b/src/interp/iz3translate.h @@ -36,7 +36,7 @@ class iz3translation : public iz3base { /** This is thrown when the proof cannot be translated. */ struct unsupported: public iz3_exception { - unsupported(): iz3_exception("unsupported") {} + unsupported(): iz3_exception("unsupported") { } }; static iz3translation *create(iz3mgr &mgr, diff --git a/src/math/automata/symbolic_automata_def.h b/src/math/automata/symbolic_automata_def.h index 3be6a1da0..be38a60bc 100644 --- a/src/math/automata/symbolic_automata_def.h +++ b/src/math/automata/symbolic_automata_def.h @@ -24,6 +24,7 @@ Revision History: #include "math/automata/symbolic_automata.h" #include "util/hashtable.h" +#include "util/vector.h" @@ -311,7 +312,7 @@ symbolic_automata::mk_determinstic_param(automaton_t& a, bool flip_accepta s2id.insert(set, p_state_id++); // the index to the initial state is 0 id2s.push_back(set); - svector todo; //States to visit + ::vector todo; //States to visit todo.push_back(set); uint_set state; diff --git a/src/math/interval/interval.h b/src/math/interval/interval.h index bae644fac..db3c5a850 100644 --- a/src/math/interval/interval.h +++ b/src/math/interval/interval.h @@ -162,7 +162,7 @@ private: void checkpoint(); public: - interval_manager(reslimit& lim, C const & c); + interval_manager(reslimit& lim, C && c); ~interval_manager(); numeral_manager & m() const { return m_c.m(); } diff --git a/src/math/interval/interval_def.h b/src/math/interval/interval_def.h index e18d9edda..de3a5fa19 100644 --- a/src/math/interval/interval_def.h +++ b/src/math/interval/interval_def.h @@ -31,7 +31,7 @@ Revision History: // #define TRACE_NTH_ROOT template -interval_manager::interval_manager(reslimit& lim, C const & c): m_limit(lim), m_c(c) { +interval_manager::interval_manager(reslimit& lim, C && c): m_limit(lim), m_c(std::move(c)) { m().set(m_minus_one, -1); m().set(m_one, 1); m_pi_n = 0; diff --git a/src/math/polynomial/algebraic_numbers.cpp b/src/math/polynomial/algebraic_numbers.cpp index 22b50326b..9484c3c83 100644 --- a/src/math/polynomial/algebraic_numbers.cpp +++ b/src/math/polynomial/algebraic_numbers.cpp @@ -2632,10 +2632,14 @@ namespace algebraic_numbers { scoped_mpz neg_n(qm()); qm().set(neg_n, v.numerator()); qm().neg(neg_n); - mpz const coeffs[2] = { neg_n.get(), v.denominator() }; + unsynch_mpz_manager zmgr; + // FIXME: remove these copies + mpz coeffs[2] = { zmgr.dup(neg_n.get()), zmgr.dup(v.denominator()) }; out << "("; upm().display(out, 2, coeffs, "#"); out << ", 1)"; // first root of the polynomial d*# - n + zmgr.del(coeffs[0]); + zmgr.del(coeffs[1]); } else { algebraic_cell * c = a.to_algebraic(); @@ -2678,10 +2682,14 @@ namespace algebraic_numbers { scoped_mpz neg_n(qm()); qm().set(neg_n, v.numerator()); qm().neg(neg_n); - mpz const coeffs[2] = { neg_n.get(), v.denominator() }; + unsynch_mpz_manager zmgr; + // FIXME: remove these copies + mpz coeffs[2] = { zmgr.dup(neg_n.get()), zmgr.dup(v.denominator()) }; out << "(root-obj "; upm().display_smt2(out, 2, coeffs, "x"); out << " 1)"; // first root of the polynomial d*# - n + zmgr.del(coeffs[0]); + zmgr.del(coeffs[1]); } else { algebraic_cell * c = a.to_algebraic(); diff --git a/src/math/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index 80669648e..d6d392148 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -3536,10 +3536,11 @@ namespace polynomial { iccp(p, max_var(p), i, c, pp); } - void pp(polynomial const * p, var x, polynomial_ref & pp) { + polynomial_ref pp(polynomial const * p, var x) { scoped_numeral i(m_manager); - polynomial_ref c(pm()); - iccp(p, x, i, c, pp); + polynomial_ref c(pm()), result(pm()); + iccp(p, x, i, c, result); + return result; } bool is_primitive(polynomial const * p, var x) { @@ -3598,7 +3599,7 @@ namespace polynomial { if (is_zero(rem)) { TRACE("polynomial", tout << "rem is zero...\npp_v: " << pp_v << "\n";); flip_sign_if_lm_neg(pp_v); - pp(pp_v, x, r); + r = pp(pp_v, x); r = mul(d_a, d_r, r); return; } @@ -3849,7 +3850,7 @@ namespace polynomial { TRACE("mgcd", tout << "new combined:\n" << C_star << "\n";); } } - pp(C_star, x, candidate); + candidate = pp(C_star, x); TRACE("mgcd", tout << "candidate:\n" << candidate << "\n";); scoped_numeral lc_candidate(m()); lc_candidate = univ_coeff(candidate, degree(candidate, x)); @@ -4821,10 +4822,9 @@ namespace polynomial { polynomial * mk_x_minus_y(var x, var y) { numeral zero(0); - numeral one(1); numeral minus_one; // It is not safe to initialize with -1 when numeral_manager is GF_2 m_manager.set(minus_one, -1); - numeral as[2] = { one, minus_one }; + numeral as[2] = { numeral(1), std::move(minus_one) }; var xs[2] = { x, y }; return mk_linear(2, as, xs, zero); } @@ -4844,8 +4844,7 @@ namespace polynomial { polynomial * mk_x_plus_y(var x, var y) { numeral zero(0); - numeral one(1); - numeral as[2] = { one, one }; + numeral as[2] = { numeral(1), numeral(1) }; var xs[2] = { x, y }; return mk_linear(2, as, xs, zero); } @@ -6619,8 +6618,8 @@ namespace polynomial { polynomial_ref cf1(pm()); m_wrapper.content(f1, x, cf1); polynomial_ref cf2(pm()); m_wrapper.content(f2, x, cf2); tout << "content(f1): " << cf1 << "\ncontent(f2): " << cf2 << "\n";); - pp(f1, x, f1); - pp(f2, x, f2); + f1 = pp(f1, x); + f2 = pp(f2, x); TRACE("factor", tout << "f1: " << f1 << "\nf2: " << f2 << "\n";); DEBUG_CODE({ polynomial_ref f1f2(pm()); @@ -7150,7 +7149,7 @@ namespace polynomial { } void manager::primitive(polynomial const * p, var x, polynomial_ref & pp) { - m_imp->pp(p, x, pp); + pp = m_imp->pp(p, x); } void manager::icpp(polynomial const * p, var x, numeral & i, polynomial_ref & c, polynomial_ref & pp) { diff --git a/src/math/polynomial/upolynomial_factorization_int.h b/src/math/polynomial/upolynomial_factorization_int.h index 640c5ad5c..ea2b51d8f 100644 --- a/src/math/polynomial/upolynomial_factorization_int.h +++ b/src/math/polynomial/upolynomial_factorization_int.h @@ -45,7 +45,7 @@ namespace upolynomial { for (unsigned i = 0; i < p.size(); ++ i) { numeral p_i; // no need to delete, we keep it pushed in zp_p zp_nm.set(p_i, p[i]); - zp_p.push_back(p_i); + zp_p.push_back(std::move(p_i)); } zp_upm.trim(zp_p); } diff --git a/src/math/simplex/sparse_matrix.h b/src/math/simplex/sparse_matrix.h index dc4ce8695..4edbb2b9d 100644 --- a/src/math/simplex/sparse_matrix.h +++ b/src/math/simplex/sparse_matrix.h @@ -35,7 +35,7 @@ namespace simplex { struct row_entry { numeral m_coeff; var_t m_var; - row_entry(numeral const& c, var_t v): m_coeff(c), m_var(v) {} + row_entry(numeral && c, var_t v) : m_coeff(std::move(c)), m_var(v) {} }; private: @@ -61,7 +61,7 @@ namespace simplex { int m_col_idx; int m_next_free_row_entry_idx; }; - _row_entry(numeral const & c, var_t v): row_entry(c, v), m_col_idx(0) {} + _row_entry(numeral && c, var_t v) : row_entry(std::move(c), v), m_col_idx(0) {} _row_entry() : row_entry(numeral(), dead_id), m_col_idx(0) {} bool is_dead() const { return row_entry::m_var == dead_id; } }; diff --git a/src/math/subpaving/subpaving_t_def.h b/src/math/subpaving/subpaving_t_def.h index 108b6aac3..3574787d8 100644 --- a/src/math/subpaving/subpaving_t_def.h +++ b/src/math/subpaving/subpaving_t_def.h @@ -739,7 +739,7 @@ void context_t::del_sum(polynomial * p) { template var context_t::mk_sum(numeral const & c, unsigned sz, numeral const * as, var const * xs) { - m_num_buffer.reserve(num_vars(), numeral()); + m_num_buffer.reserve(num_vars()); for (unsigned i = 0; i < sz; i++) { SASSERT(xs[i] < num_vars()); nm().set(m_num_buffer[xs[i]], as[i]); diff --git a/src/model/func_interp.cpp b/src/model/func_interp.cpp index 6699ef0e2..e458cc4b0 100644 --- a/src/model/func_interp.cpp +++ b/src/model/func_interp.cpp @@ -117,7 +117,7 @@ bool func_interp::is_fi_entry_expr(expr * e, ptr_vector & args) { (m_arity > 1 && (!m().is_and(c) || to_app(c)->get_num_args() != m_arity))) return false; - args.resize(m_arity, 0); + args.resize(m_arity); for (unsigned i = 0; i < m_arity; i++) { expr * ci = (m_arity == 1 && i == 0) ? c : to_app(c)->get_arg(i); diff --git a/src/muz/base/CMakeLists.txt b/src/muz/base/CMakeLists.txt index ec1ce47c3..6b0334664 100644 --- a/src/muz/base/CMakeLists.txt +++ b/src/muz/base/CMakeLists.txt @@ -10,7 +10,6 @@ z3_add_component(muz dl_rule_transformer.cpp dl_util.cpp hnf.cpp - proof_utils.cpp rule_properties.cpp COMPONENT_DEPENDENCIES aig_tactic diff --git a/src/muz/base/dl_boogie_proof.cpp b/src/muz/base/dl_boogie_proof.cpp index ec999c05e..8f4f9c02d 100644 --- a/src/muz/base/dl_boogie_proof.cpp +++ b/src/muz/base/dl_boogie_proof.cpp @@ -53,7 +53,7 @@ Example from Boogie: #include "muz/base/dl_boogie_proof.h" #include "model/model_pp.h" -#include "muz/base/proof_utils.h" +#include "ast/proofs/proof_utils.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index 97a2c841a..39e044ec3 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -453,7 +453,8 @@ namespace datalog { return new_pred; } - void context::add_rule(expr* rl, symbol const& name, unsigned bound) { + void context::add_rule(expr* rl, symbol const& name, unsigned bound) { + SASSERT(rl); m_rule_fmls.push_back(rl); m_rule_names.push_back(name); m_rule_bounds.push_back(bound); @@ -461,7 +462,7 @@ namespace datalog { void context::flush_add_rules() { datalog::rule_manager& rm = get_rule_manager(); - scoped_proof_mode _scp(m, generate_proof_trace()?PGM_FINE:PGM_DISABLED); + scoped_proof_mode _scp(m, generate_proof_trace()?PGM_ENABLED:PGM_DISABLED); while (m_rule_fmls_head < m_rule_fmls.size()) { expr* fml = m_rule_fmls[m_rule_fmls_head].get(); proof* p = generate_proof_trace()?m.mk_asserted(fml):0; diff --git a/src/muz/base/dl_rule.cpp b/src/muz/base/dl_rule.cpp index 367795c9b..4f832c4c9 100644 --- a/src/muz/base/dl_rule.cpp +++ b/src/muz/base/dl_rule.cpp @@ -141,7 +141,7 @@ namespace datalog { void rule_manager::mk_rule(expr* fml, proof* p, rule_set& rules, symbol const& name) { - scoped_proof_mode _sc(m, m_ctx.generate_proof_trace()?PGM_FINE:PGM_DISABLED); + scoped_proof_mode _sc(m, m_ctx.generate_proof_trace()?PGM_ENABLED:PGM_DISABLED); proof_ref pr(p, m); expr_ref fml1(m); bind_variables(fml, true, fml1); @@ -343,7 +343,7 @@ namespace datalog { } TRACE("dl", tout << rule_expr << "\n";); - scoped_proof_mode _sc(m, m_ctx.generate_proof_trace()?PGM_FINE:PGM_DISABLED); + scoped_proof_mode _sc(m, m_ctx.generate_proof_trace()?PGM_ENABLED:PGM_DISABLED); proof_ref pr(m); if (m_ctx.generate_proof_trace()) { pr = m.mk_asserted(rule_expr); diff --git a/src/muz/base/dl_util.cpp b/src/muz/base/dl_util.cpp index f0439a55c..463b78c4a 100644 --- a/src/muz/base/dl_util.cpp +++ b/src/muz/base/dl_util.cpp @@ -384,6 +384,7 @@ namespace datalog { return alloc(skip_model_converter); } + virtual void display(std::ostream & out) { } }; model_converter* mk_skip_model_converter() { return alloc(skip_model_converter); } @@ -398,6 +399,7 @@ namespace datalog { return alloc(skip_proof_converter); } + virtual void display(std::ostream & out) { out << "(skip-proof-converter)\n"; } }; proof_converter* mk_skip_proof_converter() { return alloc(skip_proof_converter); } @@ -508,10 +510,9 @@ namespace datalog { } void collect_and_transform(const unsigned_vector & src, const unsigned_vector & translation, - unsigned_vector & res) { - unsigned n = src.size(); - for(unsigned i=0; i - void set_intersection(Set1 & tgt, const Set2 & src) { - svector to_remove; - typename Set1::iterator vit = tgt.begin(); - typename Set1::iterator vend = tgt.end(); - for(;vit!=vend;++vit) { - typename Set1::data itm=*vit; - if(!src.contains(itm)) { - to_remove.push_back(itm); - } - } - while(!to_remove.empty()) { - tgt.remove(to_remove.back()); - to_remove.pop_back(); - } - } - - template - void set_difference(Set & tgt, const Set & to_remove) { - typename Set::iterator vit = to_remove.begin(); - typename Set::iterator vend = to_remove.end(); - for(;vit!=vend;++vit) { - typename Set::data itm=*vit; - tgt.remove(itm); - } - } - - template - void set_union(Set1 & tgt, const Set2 & to_add) { - typename Set2::iterator vit = to_add.begin(); - typename Set2::iterator vend = to_add.end(); - for(;vit!=vend;++vit) { - typename Set1::data itm=*vit; - tgt.insert(itm); - } - } - - void idx_set_union(idx_set & tgt, const idx_set & src); - - template - void unite_disjoint_maps(T & tgt, const T & src) { - typename T::iterator it = src.begin(); - typename T::iterator end = src.end(); - for(; it!=end; ++it) { - SASSERT(!tgt.contains(it->m_key)); - tgt.insert(it->m_key, it->m_value); - } - } - - template - void collect_map_range(T & acc, const U & map) { - typename U::iterator it = map.begin(); - typename U::iterator end = map.end(); - for(; it!=end; ++it) { - acc.push_back(it->m_value); - } - } - - - template - void print_container(const T & begin, const T & end, std::ostream & out) { - T it = begin; - out << "("; - bool first = true; - for(; it!=end; ++it) { - if(first) { first = false; } else { out << ","; } - out << (*it); - } - out << ")"; - } - - template - void print_container(const T & cont, std::ostream & out) { - print_container(cont.begin(), cont.end(), out); - } - - template - void print_container(const ref_vector & cont, std::ostream & out) { - print_container(cont.c_ptr(), cont.c_ptr() + cont.size(), out); - } - - template - void print_map(const T & cont, std::ostream & out) { - typename T::iterator it = cont.begin(); - typename T::iterator end = cont.end(); - out << "("; - bool first = true; - for(; it!=end; ++it) { - if(first) { first = false; } else { out << ","; } - out << it->m_key << "->" << it->m_value; - } - out << ")"; - } - - template - unsigned find_index(const It & begin, const It & end, const V & val) { - unsigned idx = 0; - It it = begin; - for(; it!=end; it++, idx++) { - if(*it==val) { - return idx; - } - } - return UINT_MAX; - } - - template - bool containers_equal(const T & begin1, const T & end1, const U & begin2, const U & end2) { - T it1 = begin1; - U it2 = begin2; - for(; it1!=end1 && it2!=end2; ++it1, ++it2) { - if(*it1!=*it2) { - return false; - } - } - return it1==end1 && it2==end2; - } template bool vectors_equal(const T & c1, const U & c2) { @@ -521,6 +399,8 @@ namespace datalog { return true; } + void idx_set_union(idx_set & tgt, const idx_set & src); + template struct default_obj_chash { unsigned operator()(T const& cont, unsigned i) const { diff --git a/src/muz/base/proof_utils.h b/src/muz/base/proof_utils.h deleted file mode 100644 index 0db831c4f..000000000 --- a/src/muz/base/proof_utils.h +++ /dev/null @@ -1,48 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - proof_utils.h - -Abstract: - - Utilities for transforming proofs. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-10-12. - -Revision History: - ---*/ -#ifndef PROOF_UTILS_H_ -#define PROOF_UTILS_H_ - -class proof_utils { -public: - /** - \brief reduce the set of hypotheses used in the proof. - */ - static void reduce_hypotheses(proof_ref& pr); - - /** - \brief Check that a proof does not contain open hypotheses. - */ - static bool is_closed(ast_manager& m, proof* p); - - /** - \brief Permute unit resolution rule with th-lemma - */ - static void permute_unit_resolution(proof_ref& pr); - - /** - \brief Push instantiations created in hyper-resolutions up to leaves. - This produces a "ground" proof where leaves are annotated by instantiations. - */ - static void push_instantiations_up(proof_ref& pr); - - -}; - -#endif diff --git a/src/muz/fp/dl_cmds.cpp b/src/muz/fp/dl_cmds.cpp index 42c614912..2610f821c 100644 --- a/src/muz/fp/dl_cmds.cpp +++ b/src/muz/fp/dl_cmds.cpp @@ -189,11 +189,12 @@ public: m_bound = bound; m_arg_idx++; } - virtual void reset(cmd_context & ctx) { m_dl_ctx->reset(); prepare(ctx); } + virtual void reset(cmd_context & ctx) { m_dl_ctx->reset(); prepare(ctx); m_t = nullptr; } virtual void prepare(cmd_context& ctx) { m_arg_idx = 0; m_name = symbol::null; m_bound = UINT_MAX; } virtual void finalize(cmd_context & ctx) { } virtual void execute(cmd_context & ctx) { + if (!m_t) throw cmd_exception("invalid rule, expected formula"); m_dl_ctx->add_rule(m_t, m_name, m_bound); } }; diff --git a/src/muz/pdr/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp index fd734ea66..37d80e2d6 100644 --- a/src/muz/pdr/pdr_context.cpp +++ b/src/muz/pdr/pdr_context.cpp @@ -41,9 +41,8 @@ Notes: #include "ast/ast_smt2_pp.h" #include "qe/qe_lite.h" #include "ast/ast_ll_pp.h" -#include "ast/proof_checker/proof_checker.h" +#include "ast/proofs/proof_checker.h" #include "smt/smt_value_sort.h" -#include "muz/base/proof_utils.h" #include "muz/base/dl_boogie_proof.h" #include "ast/scoped_proof.h" #include "tactic/core/blast_term_ite_tactic.h" @@ -1825,7 +1824,7 @@ namespace pdr { m_core_generalizers.push_back(alloc(core_multi_generalizer, *this, 0)); } if (!classify.is_bool()) { - m.toggle_proof_mode(PGM_FINE); + m.toggle_proof_mode(PGM_ENABLED); m_fparams.m_arith_bound_prop = BP_NONE; m_fparams.m_arith_auto_config_simplex = true; m_fparams.m_arith_propagate_eqs = false; diff --git a/src/muz/pdr/pdr_farkas_learner.cpp b/src/muz/pdr/pdr_farkas_learner.cpp index 29037f180..4a77d2f5f 100644 --- a/src/muz/pdr/pdr_farkas_learner.cpp +++ b/src/muz/pdr/pdr_farkas_learner.cpp @@ -31,7 +31,7 @@ Revision History: #include "ast/rewriter/th_rewriter.h" #include "ast/ast_ll_pp.h" #include "tactic/arith/arith_bounds_tactic.h" -#include "muz/base/proof_utils.h" +#include "ast/proofs/proof_utils.h" #include "ast/reg_decl_plugins.h" @@ -372,7 +372,7 @@ namespace pdr { farkas_learner::farkas_learner(smt_params& params, ast_manager& outer_mgr) : m_proof_params(get_proof_params(params)), - m_pr(PGM_FINE), + m_pr(PGM_ENABLED), m_constr(0), m_combine_farkas_coefficients(true), p2o(m_pr, outer_mgr), @@ -733,8 +733,8 @@ namespace pdr { } else { expr_set* hyps3 = alloc(expr_set); - datalog::set_union(*hyps3, *hyps); - datalog::set_union(*hyps3, *hyps2); + set_union(*hyps3, *hyps); + set_union(*hyps3, *hyps2); hyps = hyps3; hyprefs.push_back(hyps); } @@ -795,7 +795,7 @@ namespace pdr { case PR_LEMMA: { expr_set* hyps2 = alloc(expr_set); hyprefs.push_back(hyps2); - datalog::set_union(*hyps2, *hyps); + set_union(*hyps2, *hyps); hyps = hyps2; expr* fml = m.get_fact(p); hyps->remove(fml); diff --git a/src/muz/pdr/pdr_generalizers.cpp b/src/muz/pdr/pdr_generalizers.cpp index b17d3f8b6..094379a0b 100644 --- a/src/muz/pdr/pdr_generalizers.cpp +++ b/src/muz/pdr/pdr_generalizers.cpp @@ -81,7 +81,7 @@ namespace pdr { m_gen(n, core0, uses_level1); new_cores.push_back(std::make_pair(core0, uses_level1)); obj_hashtable core_exprs, core1_exprs; - datalog::set_union(core_exprs, core0); + set_union(core_exprs, core0); for (unsigned i = 0; i < old_core.size(); ++i) { expr* lit = old_core[i].get(); if (core_exprs.contains(lit)) { @@ -94,8 +94,8 @@ namespace pdr { if (core1.size() < old_core.size()) { new_cores.push_back(std::make_pair(core1, uses_level1)); core1_exprs.reset(); - datalog::set_union(core1_exprs, core1); - datalog::set_intersection(core_exprs, core1_exprs); + set_union(core1_exprs, core1); + set_intersection(core_exprs, core1_exprs); } } } diff --git a/src/muz/rel/dl_instruction.h b/src/muz/rel/dl_instruction.h index c29681f37..56dd249a5 100644 --- a/src/muz/rel/dl_instruction.h +++ b/src/muz/rel/dl_instruction.h @@ -128,7 +128,7 @@ namespace datalog { void set_reg(reg_idx i, reg_type val) { if (i >= m_registers.size()) { check_overflow(i); - m_registers.resize(i+1,0); + m_registers.resize(i+1); } if (m_registers[i]) { m_registers[i]->deallocate(); diff --git a/src/muz/rel/dl_mk_explanations.cpp b/src/muz/rel/dl_mk_explanations.cpp index fd13978d2..c4fb57eeb 100644 --- a/src/muz/rel/dl_mk_explanations.cpp +++ b/src/muz/rel/dl_mk_explanations.cpp @@ -465,7 +465,7 @@ namespace datalog { unsigned sz = r.get_signature().size(); ptr_vector subst_arg; - subst_arg.resize(sz, 0); + subst_arg.resize(sz); unsigned ofs = sz-1; for (unsigned i=0; iremove(fml); diff --git a/src/muz/spacer/spacer_itp_solver.h b/src/muz/spacer/spacer_itp_solver.h index ec878af00..6fc67a243 100644 --- a/src/muz/spacer/spacer_itp_solver.h +++ b/src/muz/spacer/spacer_itp_solver.h @@ -138,15 +138,15 @@ public: {return m_solver.get_num_assumptions();} virtual expr * get_assumption(unsigned idx) const {return m_solver.get_assumption(idx);} - virtual std::ostream &display(std::ostream &out) const - {m_solver.display(out); return out;} + virtual std::ostream &display(std::ostream &out, unsigned n, expr* const* es) const + { return m_solver.display(out, n, es); } /* check_sat_result interface */ virtual void collect_statistics(statistics &st) const ; virtual void reset_statistics(); virtual void get_unsat_core(ptr_vector &r); - virtual void get_model(model_ref &m) {m_solver.get_model(m);} + virtual void get_model_core(model_ref &m) {m_solver.get_model(m);} virtual proof *get_proof() {return m_solver.get_proof();} virtual std::string reason_unknown() const {return m_solver.reason_unknown();} @@ -174,7 +174,7 @@ public: public: scoped_bg(itp_solver &s) : m_s(s), m_bg_sz(m_s.get_num_bg()) {} ~scoped_bg() - {if(m_s.get_num_bg() > m_bg_sz) { m_s.pop_bg(m_s.get_num_bg() - m_bg_sz); }} + {if (m_s.get_num_bg() > m_bg_sz) { m_s.pop_bg(m_s.get_num_bg() - m_bg_sz); }} }; }; } diff --git a/src/muz/spacer/spacer_legacy_frames.cpp b/src/muz/spacer/spacer_legacy_frames.cpp index 9c302e5fd..679736add 100644 --- a/src/muz/spacer/spacer_legacy_frames.cpp +++ b/src/muz/spacer/spacer_legacy_frames.cpp @@ -23,9 +23,9 @@ #include "ast/ast_smt2_pp.h" #include "ast/ast_ll_pp.h" #include "ast/ast_util.h" -#include "ast/proof_checker/proof_checker.h" +#include "ast/proofs/proof_checker.h" #include "smt/smt_value_sort.h" -#include "muz/base/proof_utils.h" +#include "ast/proofs/proof_utils.h" #include "ast/scoped_proof.h" #include "muz/spacer/spacer_qe_project.h" #include "tactic/core/blast_term_ite_tactic.h" diff --git a/src/muz/spacer/spacer_min_cut.cpp b/src/muz/spacer/spacer_min_cut.cpp deleted file mode 100644 index 22ff81a80..000000000 --- a/src/muz/spacer/spacer_min_cut.cpp +++ /dev/null @@ -1,289 +0,0 @@ -/*++ -Copyright (c) 2017 Arie Gurfinkel - -Module Name: - - spacer_min_cut.cpp - -Abstract: - min cut solver - -Author: - Bernhard Gleiss - -Revision History: - - ---*/ -#include "muz/spacer/spacer_min_cut.h" - -namespace spacer { - - spacer_min_cut::spacer_min_cut() - { - m_n = 2; - - // push back two empty vectors for source and sink - m_edges.push_back(vector>()); - m_edges.push_back(vector>()); - } - - unsigned spacer_min_cut::new_node() - { - return m_n++; - } - - void spacer_min_cut::add_edge(unsigned int i, unsigned int j, unsigned int capacity) - { - if (i >= m_edges.size()) - { - m_edges.resize(i + 1); - } - m_edges[i].insert(std::make_pair(j, 1)); - STRACE("spacer.mincut", - verbose_stream() << "adding edge (" << i << "," << j << ")\n"; - ); - - } - - void spacer_min_cut::compute_min_cut(vector& cut_nodes) - { - if (m_n == 2) - { - return; - } - - m_d.resize(m_n); - m_pred.resize(m_n); - - // compute initial distances and number of nodes - compute_initial_distances(); - - unsigned i = 0; - - while (m_d[0] < m_n) - { - unsigned j = get_admissible_edge(i); - - if (j < m_n) - { - // advance(i) - m_pred[j] = i; - i = j; - - // if i is the sink, augment path - if (i == 1) - { - augment_path(); - i = 0; - } - } - else - { - // retreat - compute_distance(i); - if (i != 0) - { - i = m_pred[i]; - } - } - } - - // split nodes into reachable and unreachable ones - vector reachable(m_n); - compute_reachable_nodes(reachable); - - // find all edges between reachable and unreachable nodes and for each such edge, add corresponding lemma to unsat-core - compute_cut_and_add_lemmas(reachable, cut_nodes); - } - - void spacer_min_cut::compute_initial_distances() - { - vector todo; - vector visited(m_n); - - todo.push_back(0); // start at the source, since we do postorder traversel - - while (!todo.empty()) - { - unsigned current = todo.back(); - - // if we haven't already visited current - if (!visited[current]) { - bool existsUnvisitedParent = false; - - // add unprocessed parents to stack for DFS. If there is at least one unprocessed parent, don't compute the result - // for current now, but wait until those unprocessed parents are processed. - for (unsigned i = 0, sz = m_edges[current].size(); i < sz; ++i) - { - unsigned parent = m_edges[current][i].first; - - // if we haven't visited the current parent yet - if(!visited[parent]) - { - // add it to the stack - todo.push_back(parent); - existsUnvisitedParent = true; - } - } - - // if we already visited all parents, we can visit current too - if (!existsUnvisitedParent) { - visited[current] = true; - todo.pop_back(); - - compute_distance(current); // I.H. all parent distances are already computed - } - } - else { - todo.pop_back(); - } - } - } - - unsigned spacer_min_cut::get_admissible_edge(unsigned i) - { - for (const auto& pair : m_edges[i]) - { - if (pair.second > 0 && m_d[i] == m_d[pair.first] + 1) - { - return pair.first; - } - } - return m_n; // no element found - } - - void spacer_min_cut::augment_path() - { - // find bottleneck capacity - unsigned max = std::numeric_limits::max(); - unsigned k = 1; - while (k != 0) - { - unsigned l = m_pred[k]; - for (const auto& pair : m_edges[l]) - { - if (pair.first == k) - { - if (max > pair.second) - { - max = pair.second; - } - } - } - k = l; - } - - k = 1; - while (k != 0) - { - unsigned l = m_pred[k]; - - // decrease capacity - for (auto& pair : m_edges[l]) - { - if (pair.first == k) - { - pair.second -= max; - } - } - // increase reverse flow - bool already_exists = false; - for (auto& pair : m_edges[k]) - { - if (pair.first == l) - { - already_exists = true; - pair.second += max; - } - } - if (!already_exists) - { - m_edges[k].insert(std::make_pair(l, max)); - } - k = l; - } - } - - void spacer_min_cut::compute_distance(unsigned i) - { - if (i == 1) // sink node - { - m_d[1] = 0; - } - else - { - unsigned min = std::numeric_limits::max(); - - // find edge (i,j) with positive residual capacity and smallest distance - for (const auto& pair : m_edges[i]) - { - if (pair.second > 0) - { - unsigned tmp = m_d[pair.first] + 1; - if (tmp < min) - { - min = tmp; - } - } - } - m_d[i] = min; - } - } - - void spacer_min_cut::compute_reachable_nodes(vector& reachable) - { - vector todo; - - todo.push_back(0); - while (!todo.empty()) - { - unsigned current = todo.back(); - todo.pop_back(); - - if (!reachable[current]) - { - reachable[current] = true; - - for (const auto& pair : m_edges[current]) - { - if (pair.second > 0) - { - todo.push_back(pair.first); - } - } - } - } - } - - void spacer_min_cut::compute_cut_and_add_lemmas(vector& reachable, vector& cut_nodes) - { - vector todo; - vector visited(m_n); - - todo.push_back(0); - while (!todo.empty()) - { - unsigned current = todo.back(); - todo.pop_back(); - - if (!visited[current]) - { - visited[current] = true; - - for (const auto& pair : m_edges[current]) - { - unsigned successor = pair.first; - if (reachable[successor]) - { - todo.push_back(successor); - } - else - { - cut_nodes.push_back(successor); - } - } - } - } - } -} diff --git a/src/muz/spacer/spacer_min_cut.h b/src/muz/spacer/spacer_min_cut.h deleted file mode 100644 index c73a8d3d3..000000000 --- a/src/muz/spacer/spacer_min_cut.h +++ /dev/null @@ -1,53 +0,0 @@ -/*++ -Copyright (c) 2017 Arie Gurfinkel - -Module Name: - - spacer_min_cut.h - -Abstract: - min cut solver - -Author: - Bernhard Gleiss - -Revision History: - - ---*/ - -#ifndef _SPACER_MIN_CUT_H_ -#define _SPACER_MIN_CUT_H_ - -#include "ast/ast.h" -#include "util/vector.h" - -namespace spacer { - - class spacer_min_cut { - public: - spacer_min_cut(); - - unsigned new_node(); - void add_edge(unsigned i, unsigned j, unsigned capacity); - void compute_min_cut(vector& cut_nodes); - - private: - - unsigned m_n; // number of vertices in the graph - - vector > > m_edges; // map from node to all outgoing edges together with their weights (also contains "reverse edges") - vector m_d; // approximation of distance from node to sink in residual graph - vector m_pred; // predecessor-information for reconstruction of augmenting path - vector m_node_to_formula; // maps each node to the corresponding formula in the original proof - - void compute_initial_distances(); - unsigned get_admissible_edge(unsigned i); - void augment_path(); - void compute_distance(unsigned i); - void compute_reachable_nodes(vector& reachable); - void compute_cut_and_add_lemmas(vector& reachable, vector& cut_nodes); - }; -} - -#endif diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp deleted file mode 100644 index 6edb29881..000000000 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ /dev/null @@ -1,332 +0,0 @@ -/*++ -Copyright (c) 2017 Arie Gurfinkel - -Module Name: - - spacer_proof_utils.cpp - -Abstract: - Utilities to traverse and manipulate proofs - -Author: - Bernhard Gleiss - Arie Gurfinkel - -Revision History: - ---*/ - -#include "muz/spacer/spacer_proof_utils.h" -#include "ast/ast_util.h" -#include "ast/ast_pp.h" - -#include "ast/proof_checker/proof_checker.h" - -namespace spacer { - -ProofIteratorPostOrder::ProofIteratorPostOrder(proof* root, ast_manager& manager) : m(manager) -{m_todo.push_back(root);} - -bool ProofIteratorPostOrder::hasNext() -{return !m_todo.empty();} - -/* - * iterative post-order depth-first search (DFS) through the proof DAG - */ -proof* ProofIteratorPostOrder::next() -{ - while (!m_todo.empty()) { - proof* currentNode = m_todo.back(); - - // if we haven't already visited the current unit - if (!m_visited.is_marked(currentNode)) { - bool existsUnvisitedParent = false; - - // add unprocessed premises to stack for DFS. If there is at least one unprocessed premise, don't compute the result - // for currentProof now, but wait until those unprocessed premises are processed. - for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) { - SASSERT(m.is_proof(currentNode->get_arg(i))); - proof* premise = to_app(currentNode->get_arg(i)); - - // if we haven't visited the current premise yet - if (!m_visited.is_marked(premise)) { - // add it to the stack - m_todo.push_back(premise); - existsUnvisitedParent = true; - } - } - - // if we already visited all parent-inferences, we can visit the inference too - if (!existsUnvisitedParent) { - m_visited.mark(currentNode, true); - m_todo.pop_back(); - return currentNode; - } - } else { - m_todo.pop_back(); - } - } - // we have already iterated through all inferences - return NULL; -} - - -class reduce_hypotheses { - ast_manager &m; - // tracking all created expressions - expr_ref_vector m_pinned; - - // cache for the transformation - obj_map m_cache; - - // map from unit literals to their hypotheses-free derivations - obj_map m_units; - - // -- all hypotheses in the the proof - obj_hashtable m_hyps; - - // marks hypothetical proofs - ast_mark m_hypmark; - - - // stack - ptr_vector m_todo; - - void reset() - { - m_cache.reset(); - m_units.reset(); - m_hyps.reset(); - m_hypmark.reset(); - m_pinned.reset(); - } - - bool compute_mark1(proof *pr) - { - bool hyp_mark = false; - // lemmas clear all hypotheses - if (!m.is_lemma(pr)) { - for (unsigned i = 0, sz = m.get_num_parents(pr); i < sz; ++i) { - if (m_hypmark.is_marked(m.get_parent(pr, i))) { - hyp_mark = true; - break; - } - } - } - m_hypmark.mark(pr, hyp_mark); - return hyp_mark; - } - - void compute_marks(proof* pr) - { - proof *p; - ProofIteratorPostOrder pit(pr, m); - while (pit.hasNext()) { - p = pit.next(); - if (m.is_hypothesis(p)) { - m_hypmark.mark(p, true); - m_hyps.insert(m.get_fact(p)); - } else { - bool hyp_mark = compute_mark1(p); - // collect units that are hyp-free and are used as hypotheses somewhere - if (!hyp_mark && m.has_fact(p) && m_hyps.contains(m.get_fact(p))) - { m_units.insert(m.get_fact(p), p); } - } - } - } - void find_units(proof *pr) - { - // optional. not implemented yet. - } - - void reduce(proof* pf, proof_ref &out) - { - proof *res = NULL; - - m_todo.reset(); - m_todo.push_back(pf); - ptr_buffer args; - bool dirty = false; - - while (!m_todo.empty()) { - proof *p, *tmp, *pp; - unsigned todo_sz; - - p = m_todo.back(); - if (m_cache.find(p, tmp)) { - res = tmp; - m_todo.pop_back(); - continue; - } - - dirty = false; - args.reset(); - todo_sz = m_todo.size(); - for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { - pp = m.get_parent(p, i); - if (m_cache.find(pp, tmp)) { - args.push_back(tmp); - dirty = dirty || pp != tmp; - } else { - m_todo.push_back(pp); - } - } - - if (todo_sz < m_todo.size()) { continue; } - else { m_todo.pop_back(); } - - if (m.is_hypothesis(p)) { - // hyp: replace by a corresponding unit - if (m_units.find(m.get_fact(p), tmp)) { - res = tmp; - } else { res = p; } - } - - else if (!dirty) { res = p; } - - else if (m.is_lemma(p)) { - //lemma: reduce the premise; remove reduced consequences from conclusion - SASSERT(args.size() == 1); - res = mk_lemma_core(args.get(0), m.get_fact(p)); - compute_mark1(res); - } else if (m.is_unit_resolution(p)) { - // unit: reduce untis; reduce the first premise; rebuild unit resolution - res = mk_unit_resolution_core(args.size(), args.c_ptr()); - compute_mark1(res); - } else { - // other: reduce all premises; reapply - if (m.has_fact(p)) { args.push_back(to_app(m.get_fact(p))); } - SASSERT(p->get_decl()->get_arity() == args.size()); - res = m.mk_app(p->get_decl(), args.size(), (expr * const*)args.c_ptr()); - m_pinned.push_back(res); - compute_mark1(res); - } - - SASSERT(res); - m_cache.insert(p, res); - - if (m.has_fact(res) && m.is_false(m.get_fact(res))) { break; } - } - - out = res; - } - - // returns true if (hypothesis (not a)) would be reduced - bool is_reduced(expr *a) - { - expr_ref e(m); - if (m.is_not(a)) { e = to_app(a)->get_arg(0); } - else { e = m.mk_not(a); } - - return m_units.contains(e); - } - proof *mk_lemma_core(proof *pf, expr *fact) - { - ptr_buffer args; - expr_ref lemma(m); - - if (m.is_or(fact)) { - for (unsigned i = 0, sz = to_app(fact)->get_num_args(); i < sz; ++i) { - expr *a = to_app(fact)->get_arg(i); - if (!is_reduced(a)) - { args.push_back(a); } - } - } else if (!is_reduced(fact)) - { args.push_back(fact); } - - - if (args.size() == 0) { return pf; } - else if (args.size() == 1) { - lemma = args.get(0); - } else { - lemma = m.mk_or(args.size(), args.c_ptr()); - } - proof* res = m.mk_lemma(pf, lemma); - m_pinned.push_back(res); - - if (m_hyps.contains(lemma)) - { m_units.insert(lemma, res); } - return res; - } - - proof *mk_unit_resolution_core(unsigned num_args, proof* const *args) - { - - ptr_buffer pf_args; - pf_args.push_back(args [0]); - - app *cls_fact = to_app(m.get_fact(args[0])); - ptr_buffer cls; - if (m.is_or(cls_fact)) { - for (unsigned i = 0, sz = cls_fact->get_num_args(); i < sz; ++i) - { cls.push_back(cls_fact->get_arg(i)); } - } else { cls.push_back(cls_fact); } - - // construct new resovent - ptr_buffer new_fact_cls; - bool found; - // XXX quadratic - for (unsigned i = 0, sz = cls.size(); i < sz; ++i) { - found = false; - for (unsigned j = 1; j < num_args; ++j) { - if (m.is_complement(cls.get(i), m.get_fact(args [j]))) { - found = true; - pf_args.push_back(args [j]); - break; - } - } - if (!found) { - new_fact_cls.push_back(cls.get(i)); - } - } - - SASSERT(new_fact_cls.size() + pf_args.size() - 1 == cls.size()); - expr_ref new_fact(m); - new_fact = mk_or(m, new_fact_cls.size(), new_fact_cls.c_ptr()); - - // create new proof step - proof *res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), new_fact); - m_pinned.push_back(res); - return res; - } - - // reduce all units, if any unit reduces to false return true and put its proof into out - bool reduce_units(proof_ref &out) - { - proof_ref res(m); - for (auto entry : m_units) { - reduce(entry.get_value(), res); - if (m.is_false(m.get_fact(res))) { - out = res; - return true; - } - res.reset(); - } - return false; - } - - -public: - reduce_hypotheses(ast_manager &m) : m(m), m_pinned(m) {} - - - void operator()(proof_ref &pr) - { - compute_marks(pr); - if (!reduce_units(pr)) { - reduce(pr.get(), pr); - } - reset(); - } -}; -void reduce_hypotheses(proof_ref &pr) -{ - ast_manager &m = pr.get_manager(); - class reduce_hypotheses hypred(m); - hypred(pr); - DEBUG_CODE(proof_checker pc(m); - expr_ref_vector side(m); - SASSERT(pc.check(pr, side)); - ); -} -} diff --git a/src/muz/spacer/spacer_proof_utils.h b/src/muz/spacer/spacer_proof_utils.h deleted file mode 100644 index f2897f7ec..000000000 --- a/src/muz/spacer/spacer_proof_utils.h +++ /dev/null @@ -1,43 +0,0 @@ -/*++ -Copyright (c) 2017 Arie Gurfinkel - -Module Name: - - spacer_proof_utils.cpp - -Abstract: - Utilities to traverse and manipulate proofs - -Author: - Bernhard Gleiss - Arie Gurfinkel - -Revision History: - ---*/ - -#ifndef _SPACER_PROOF_UTILS_H_ -#define _SPACER_PROOF_UTILS_H_ -#include "ast/ast.h" - -namespace spacer { -/* - * iterator, which traverses the proof in depth-first post-order. - */ -class ProofIteratorPostOrder { -public: - ProofIteratorPostOrder(proof* refutation, ast_manager& manager); - bool hasNext(); - proof* next(); - -private: - ptr_vector m_todo; - ast_mark m_visited; // the proof nodes we have already visited - - ast_manager& m; -}; - - -void reduce_hypotheses(proof_ref &pr); -} -#endif diff --git a/src/muz/spacer/spacer_qe_project.cpp b/src/muz/spacer/spacer_qe_project.cpp index dd6472224..9f4f4e4fe 100644 --- a/src/muz/spacer/spacer_qe_project.cpp +++ b/src/muz/spacer/spacer_qe_project.cpp @@ -66,7 +66,6 @@ class peq { app_ref m_peq; // partial equality application app_ref m_eq; // equivalent std equality using def. of partial eq array_util m_arr_u; - ast_eq_proc m_eq_proc; // for checking if two asts are equal public: static const char* PARTIAL_EQ; @@ -102,7 +101,7 @@ peq::peq (app* p, ast_manager& m): VERIFY (is_partial_eq (p)); SASSERT (m_arr_u.is_array (m_lhs) && m_arr_u.is_array (m_rhs) && - m_eq_proc (m.get_sort (m_lhs), m.get_sort (m_rhs))); + ast_eq_proc() (m.get_sort (m_lhs), m.get_sort (m_rhs))); for (unsigned i = 2; i < p->get_num_args (); i++) { m_diff_indices.push_back (p->get_arg (i)); } @@ -121,7 +120,7 @@ peq::peq (expr* lhs, expr* rhs, unsigned num_indices, expr * const * diff_indice { SASSERT (m_arr_u.is_array (lhs) && m_arr_u.is_array (rhs) && - m_eq_proc (m.get_sort (lhs), m.get_sort (rhs))); + ast_eq_proc() (m.get_sort (lhs), m.get_sort (rhs))); ptr_vector sorts; sorts.push_back (m.get_sort (m_lhs)); sorts.push_back (m.get_sort (m_rhs)); diff --git a/src/muz/spacer/spacer_unsat_core_learner.cpp b/src/muz/spacer/spacer_unsat_core_learner.cpp index 222ca146c..d478b1e8f 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.cpp +++ b/src/muz/spacer/spacer_unsat_core_learner.cpp @@ -41,7 +41,7 @@ void unsat_core_learner::compute_unsat_core(proof *root, expr_set& asserted_b, e // transform proof in order to get a proof which is better suited for unsat-core-extraction proof_ref pr(root, m); - spacer::reduce_hypotheses(pr); + reduce_hypotheses(pr); STRACE("spacer.unsat_core_learner", verbose_stream() << "Reduced proof:\n" << mk_ismt2_pp(pr, m) << "\n"; ); @@ -50,7 +50,7 @@ void unsat_core_learner::compute_unsat_core(proof *root, expr_set& asserted_b, e collect_symbols_b(asserted_b); // traverse proof - ProofIteratorPostOrder it(root, m); + proof_post_order it(root, m); while (it.hasNext()) { proof* currentNode = it.next(); @@ -138,7 +138,7 @@ void unsat_core_learner::compute_unsat_core(proof *root, expr_set& asserted_b, e std::unordered_map id_to_small_id; unsigned counter = 0; - ProofIteratorPostOrder it2(root, m); + proof_post_order it2(root, m); while (it2.hasNext()) { proof* currentNode = it2.next(); diff --git a/src/muz/spacer/spacer_unsat_core_learner.h b/src/muz/spacer/spacer_unsat_core_learner.h index 6ee7c3b37..a7c9f6aa7 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.h +++ b/src/muz/spacer/spacer_unsat_core_learner.h @@ -20,7 +20,7 @@ Revision History: #include "ast/ast.h" #include "muz/spacer/spacer_util.h" -#include "muz/spacer/spacer_proof_utils.h" +#include "ast/proofs/proof_utils.h" namespace spacer { diff --git a/src/muz/spacer/spacer_unsat_core_plugin.cpp b/src/muz/spacer/spacer_unsat_core_plugin.cpp index 0d90d2653..af9230713 100644 --- a/src/muz/spacer/spacer_unsat_core_plugin.cpp +++ b/src/muz/spacer/spacer_unsat_core_plugin.cpp @@ -20,13 +20,13 @@ Revision History: #include "ast/rewriter/bool_rewriter.h" #include "ast/arith_decl_plugin.h" +#include "ast/proofs/proof_utils.h" #include "solver/solver.h" #include "smt/smt_farkas_util.h" #include "smt/smt_solver.h" -#include "muz/spacer/spacer_proof_utils.h" #include "muz/spacer/spacer_matrix.h" #include "muz/spacer/spacer_unsat_core_plugin.h" #include "muz/spacer/spacer_unsat_core_learner.h" @@ -735,7 +735,7 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector cut_nodes; + unsigned_vector cut_nodes; m_min_cut.compute_min_cut(cut_nodes); for (unsigned cut_node : cut_nodes) diff --git a/src/muz/spacer/spacer_unsat_core_plugin.h b/src/muz/spacer/spacer_unsat_core_plugin.h index 743d7af4a..96d03140c 100644 --- a/src/muz/spacer/spacer_unsat_core_plugin.h +++ b/src/muz/spacer/spacer_unsat_core_plugin.h @@ -19,7 +19,7 @@ Revision History: #define _SPACER_UNSAT_CORE_PLUGIN_H_ #include "ast/ast.h" -#include "muz/spacer/spacer_min_cut.h" +#include "util/min_cut.h" namespace spacer { @@ -109,7 +109,7 @@ private: vector m_node_to_formula; // maps each node to the corresponding formula in the original proof - spacer_min_cut m_min_cut; + min_cut m_min_cut; }; } #endif diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index a277c9ed6..83042cd6d 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -72,73 +72,67 @@ namespace spacer { // model_evaluator_util::model_evaluator_util(ast_manager& m) : - m(m), m_mev(NULL) - { reset (NULL); } + m(m), m_mev(nullptr) { + reset (nullptr); + } model_evaluator_util::~model_evaluator_util() {reset (NULL);} -void model_evaluator_util::reset(model* model) -{ + void model_evaluator_util::reset(model* model) { if (m_mev) { dealloc(m_mev); m_mev = NULL; } m_model = model; - if (!m_model) { return; } + if (!m_model) { return; } m_mev = alloc(model_evaluator, *m_model); } - -bool model_evaluator_util::eval(expr *e, expr_ref &result, bool model_completion) -{ + + bool model_evaluator_util::eval(expr *e, expr_ref &result, bool model_completion) { m_mev->set_model_completion (model_completion); try { m_mev->operator() (e, result); return true; - } catch (model_evaluator_exception &ex) { + } + catch (model_evaluator_exception &ex) { (void)ex; TRACE("spacer_model_evaluator", tout << ex.msg () << "\n";); return false; } } - + bool model_evaluator_util::eval(const expr_ref_vector &v, - expr_ref& res, bool model_completion) -{ + expr_ref& res, bool model_completion) { expr_ref e(m); e = mk_and (v); return eval(e, res, model_completion); } - - -bool model_evaluator_util::is_true(const expr_ref_vector &v) -{ + + + bool model_evaluator_util::is_true(const expr_ref_vector &v) { expr_ref res(m); return eval (v, res, false) && m.is_true (res); } - -bool model_evaluator_util::is_false(expr *x) -{ + + bool model_evaluator_util::is_false(expr *x) { expr_ref res(m); return eval(x, res, false) && m.is_false (res); } -bool model_evaluator_util::is_true(expr *x) -{ + + bool model_evaluator_util::is_true(expr *x) { expr_ref res(m); return eval(x, res, false) && m.is_true (res); } - - -void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) -{ + + void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) { ast_manager& m = fml.get_manager(); expr_ref_vector conjs(m); flatten_and(fml, conjs); obj_map diseqs; expr* n, *lhs, *rhs; for (unsigned i = 0; i < conjs.size(); ++i) { - if (m.is_not(conjs[i].get(), n) && - m.is_eq(n, lhs, rhs)) { + if (m.is_not(conjs[i].get(), n) && m.is_eq(n, lhs, rhs)) { if (!m.is_value(rhs)) { std::swap(lhs, rhs); } @@ -155,14 +149,12 @@ void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) expr_ref val(m), tmp(m); proof_ref pr(m); pr = m.mk_asserted(m.mk_true()); - obj_map::iterator it = diseqs.begin(); - obj_map::iterator end = diseqs.end(); - for (; it != end; ++it) { - if (it->m_value >= threshold) { - model.eval(it->m_key, val); - sub.insert(it->m_key, val, pr); - conjs.push_back(m.mk_eq(it->m_key, val)); - num_deleted += it->m_value; + for (auto const& kv : diseqs) { + if (kv.m_value >= threshold) { + model.eval(kv.m_key, val); + sub.insert(kv.m_key, val, pr); + conjs.push_back(m.mk_eq(kv.m_key, val)); + num_deleted += kv.m_value; } } if (orig_size < conjs.size()) { @@ -178,14 +170,17 @@ void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) SASSERT(orig_size <= 1 + conjs.size()); if (i + 1 == orig_size) { // no-op. - } else if (orig_size <= conjs.size()) { + } + else if (orig_size <= conjs.size()) { // no-op - } else { + } + else { SASSERT(orig_size == 1 + conjs.size()); --orig_size; --i; } - } else { + } + else { conjs[i] = tmp; } } @@ -202,9 +197,8 @@ void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) ast_manager& m; public: ite_hoister(ast_manager& m): m(m) {} - - br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) - { + + br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { if (m.is_ite(f)) { return BR_FAILED; } @@ -233,13 +227,12 @@ void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) struct ite_hoister_cfg: public default_rewriter_cfg { ite_hoister m_r; bool rewrite_patterns() const { return false; } - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) - { + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { return m_r.mk_app_core(f, num, args, result); } ite_hoister_cfg(ast_manager & m, params_ref const & p):m_r(m) {} }; - + class ite_hoister_star : public rewriter_tpl { ite_hoister_cfg m_cfg; public: @@ -247,9 +240,8 @@ void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) rewriter_tpl(m, false, m_cfg), m_cfg(m, p) {} }; - -void hoist_non_bool_if(expr_ref& fml) -{ + + void hoist_non_bool_if(expr_ref& fml) { ast_manager& m = fml.get_manager(); scoped_no_proof _sp(m); params_ref p; @@ -266,8 +258,7 @@ void hoist_non_bool_if(expr_ref& fml) bool m_is_dl; bool m_test_for_utvpi; - bool is_numeric(expr* e) const - { + bool is_numeric(expr* e) const { if (a.is_numeral(e)) { return true; } @@ -278,13 +269,11 @@ void hoist_non_bool_if(expr_ref& fml) return false; } - bool is_arith_expr(expr *e) const - { + bool is_arith_expr(expr *e) const { return is_app(e) && a.get_family_id() == to_app(e)->get_family_id(); } - - bool is_offset(expr* e) const - { + + bool is_offset(expr* e) const { if (a.is_numeral(e)) { return true; } @@ -315,47 +304,44 @@ void hoist_non_bool_if(expr_ref& fml) return !is_arith_expr(e); } - bool is_minus_one(expr const * e) const - { - rational r; - return a.is_numeral(e, r) && r.is_minus_one(); + bool is_minus_one(expr const * e) const { + rational r; + return a.is_numeral(e, r) && r.is_minus_one(); } - bool test_ineq(expr* e) const - { + bool test_ineq(expr* e) const { SASSERT(a.is_le(e) || a.is_ge(e) || m.is_eq(e)); SASSERT(to_app(e)->get_num_args() == 2); expr * lhs = to_app(e)->get_arg(0); expr * rhs = to_app(e)->get_arg(1); if (is_offset(lhs) && is_offset(rhs)) - { return true; } + { return true; } if (!is_numeric(rhs)) - { std::swap(lhs, rhs); } + { std::swap(lhs, rhs); } if (!is_numeric(rhs)) - { return false; } + { return false; } // lhs can be 'x' or '(+ x (* -1 y))' if (is_offset(lhs)) - { return true; } + { return true; } expr* arg1, *arg2; if (!a.is_add(lhs, arg1, arg2)) - { return false; } + { return false; } // x if (m_test_for_utvpi) { return is_offset(arg1) && is_offset(arg2); } if (is_arith_expr(arg1)) - { std::swap(arg1, arg2); } + { std::swap(arg1, arg2); } if (is_arith_expr(arg1)) - { return false; } + { return false; } // arg2: (* -1 y) expr* m1, *m2; if (!a.is_mul(arg2, m1, m2)) - { return false; } + { return false; } return is_minus_one(m1) && is_offset(m2); } - bool test_eq(expr* e) const - { + bool test_eq(expr* e) const { expr* lhs, *rhs; VERIFY(m.is_eq(e, lhs, rhs)); if (!a.is_int_real(lhs)) { @@ -370,9 +356,8 @@ void hoist_non_bool_if(expr_ref& fml) !a.is_mul(lhs) && !a.is_mul(rhs); } - - bool test_term(expr* e) const - { + + bool test_term(expr* e) const { if (m.is_bool(e)) { return true; } @@ -490,7 +475,7 @@ bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) * eliminate simple equalities using qe_lite * then, MBP for Booleans (substitute), reals (based on LW), ints (based on Cooper), and arrays */ - void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, +void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, const model_ref& M, bool reduce_all_selects, bool use_native_mbp, bool dont_sub) { diff --git a/src/muz/spacer/spacer_util.h b/src/muz/spacer/spacer_util.h index 546b7df5b..7fb17329e 100644 --- a/src/muz/spacer/spacer_util.h +++ b/src/muz/spacer/spacer_util.h @@ -74,15 +74,6 @@ inline std::ostream& operator<<(std::ostream& out, pp_level const& p) } -struct scoped_watch { - stopwatch &m_sw; - scoped_watch (stopwatch &sw, bool reset=false): m_sw(sw) - { - if(reset) { m_sw.reset(); } - m_sw.start (); - } - ~scoped_watch () {m_sw.stop ();} -}; typedef ptr_vector app_vector; diff --git a/src/muz/spacer/spacer_virtual_solver.cpp b/src/muz/spacer/spacer_virtual_solver.cpp index ebaef14f0..938e8cb94 100644 --- a/src/muz/spacer/spacer_virtual_solver.cpp +++ b/src/muz/spacer/spacer_virtual_solver.cpp @@ -23,7 +23,8 @@ Notes: #include "muz/spacer/spacer_util.h" #include "ast/rewriter/bool_rewriter.h" -#include "ast/proof_checker/proof_checker.h" +#include "ast/proofs/proof_checker.h" +#include "ast/proofs/proof_utils.h" #include "ast/scoped_proof.h" @@ -64,172 +65,11 @@ virtual_solver::~virtual_solver() } namespace { -static bool matches_fact(expr_ref_vector &args, expr* &match) -{ - ast_manager &m = args.get_manager(); - expr *fact = args.back(); - for (unsigned i = 0, sz = args.size() - 1; i < sz; ++i) { - expr *arg = args.get(i); - if (m.is_proof(arg) && - m.has_fact(to_app(arg)) && - m.get_fact(to_app(arg)) == fact) { - match = arg; - return true; - } - } - return false; -} -class elim_aux_assertions { - app_ref m_aux; -public: - elim_aux_assertions(app_ref aux) : m_aux(aux) {} +// TBD: move to ast/proofs/elim_aux_assertions - void mk_or_core(expr_ref_vector &args, expr_ref &res) - { - ast_manager &m = args.get_manager(); - unsigned j = 0; - for (unsigned i = 0, sz = args.size(); i < sz; ++i) { - if (m.is_false(args.get(i))) { continue; } - if (i != j) { args [j] = args.get(i); } - ++j; - } - SASSERT(j >= 1); - res = j > 1 ? m.mk_or(j, args.c_ptr()) : args.get(0); - } - void mk_app(func_decl *decl, expr_ref_vector &args, expr_ref &res) - { - ast_manager &m = args.get_manager(); - bool_rewriter brwr(m); - - if (m.is_or(decl)) - { mk_or_core(args, res); } - else if (m.is_iff(decl) && args.size() == 2) - // avoiding simplifying equalities. In particular, - // we don't want (= (not a) (not b)) to be reduced to (= a b) - { res = m.mk_iff(args.get(0), args.get(1)); } - else - { brwr.mk_app(decl, args.size(), args.c_ptr(), res); } - } - - void operator()(ast_manager &m, proof *pr, proof_ref &res) - { - DEBUG_CODE(proof_checker pc(m); - expr_ref_vector side(m); - SASSERT(pc.check(pr, side)); - ); - obj_map cache; - bool_rewriter brwr(m); - - // for reference counting of new proofs - app_ref_vector pinned(m); - - ptr_vector todo; - todo.push_back(pr); - - expr_ref not_aux(m); - not_aux = m.mk_not(m_aux); - - expr_ref_vector args(m); - - while (!todo.empty()) { - app *p, *r; - expr *a; - - p = todo.back(); - if (cache.find(pr, r)) { - todo.pop_back(); - continue; - } - - SASSERT(!todo.empty() || pr == p); - bool dirty = false; - unsigned todo_sz = todo.size(); - args.reset(); - for (unsigned i = 0, sz = p->get_num_args(); i < sz; ++i) { - expr* arg = p->get_arg(i); - if (arg == m_aux.get()) { - dirty = true; - args.push_back(m.mk_true()); - } else if (arg == not_aux.get()) { - dirty = true; - args.push_back(m.mk_false()); - } - // skip (asserted m_aux) - else if (m.is_asserted(arg, a) && a == m_aux.get()) { - dirty = true; - } - // skip (hypothesis m_aux) - else if (m.is_hypothesis(arg, a) && a == m_aux.get()) { - dirty = true; - } else if (is_app(arg) && cache.find(to_app(arg), r)) { - dirty |= (arg != r); - args.push_back(r); - } else if (is_app(arg)) - { todo.push_back(to_app(arg)); } - else - // -- not an app - { args.push_back(arg); } - - } - if (todo_sz < todo.size()) { - // -- process parents - args.reset(); - continue; - } - - // ready to re-create - app_ref newp(m); - if (!dirty) { newp = p; } - else if (m.is_unit_resolution(p)) { - if (args.size() == 2) - // unit resolution with m_aux that got collapsed to nothing - { newp = to_app(args.get(0)); } - else { - ptr_vector parents; - for (unsigned i = 0, sz = args.size() - 1; i < sz; ++i) - { parents.push_back(to_app(args.get(i))); } - SASSERT(parents.size() == args.size() - 1); - newp = m.mk_unit_resolution(parents.size(), parents.c_ptr()); - // XXX the old and new facts should be - // equivalent. The test here is much - // stronger. It might need to be relaxed. - SASSERT(m.get_fact(newp) == args.back()); - pinned.push_back(newp); - } - } else if (matches_fact(args, a)) { - newp = to_app(a); - } else { - expr_ref papp(m); - mk_app(p->get_decl(), args, papp); - newp = to_app(papp.get()); - pinned.push_back(newp); - } - cache.insert(p, newp); - todo.pop_back(); - CTRACE("virtual", - p->get_decl_kind() == PR_TH_LEMMA && - p->get_decl()->get_parameter(0).get_symbol() == "arith" && - p->get_decl()->get_num_parameters() > 1 && - p->get_decl()->get_parameter(1).get_symbol() == "farkas", - tout << "Old pf: " << mk_pp(p, m) << "\n" - << "New pf: " << mk_pp(newp, m) << "\n";); - } - - proof *r; - VERIFY(cache.find(pr, r)); - - DEBUG_CODE( - proof_checker pc(m); - expr_ref_vector side(m); - SASSERT(pc.check(r, side)); - ); - - res = r ; - } -}; } proof *virtual_solver::get_proof() @@ -349,15 +189,16 @@ void virtual_solver::push_core() m_context.push(); } } -void virtual_solver::pop_core(unsigned n) -{ +void virtual_solver::pop_core(unsigned n) { SASSERT(!m_pushed || get_scope_level() > 0); if (m_pushed) { SASSERT(!m_in_delay_scope); m_context.pop(n); m_pushed = get_scope_level() - n > 0; - } else - { m_in_delay_scope = get_scope_level() - n > 0; } + } + else { + m_in_delay_scope = get_scope_level() - n > 0; + } } void virtual_solver::get_unsat_core(ptr_vector &r) diff --git a/src/muz/spacer/spacer_virtual_solver.h b/src/muz/spacer/spacer_virtual_solver.h index fafaf2020..4e1f43bfc 100644 --- a/src/muz/spacer/spacer_virtual_solver.h +++ b/src/muz/spacer/spacer_virtual_solver.h @@ -80,7 +80,7 @@ public: virtual void get_unsat_core(ptr_vector &r); virtual void assert_expr(expr *e); virtual void collect_statistics(statistics &st) const {} - virtual void get_model(model_ref &m) {m_context.get_model(m);} + virtual void get_model_core(model_ref &m) {m_context.get_model(m);} virtual proof* get_proof(); virtual std::string reason_unknown() const {return m_context.last_failure_as_string();} @@ -94,8 +94,6 @@ public: virtual void reset(); virtual void set_progress_callback(progress_callback *callback) {UNREACHABLE();} - virtual void assert_lemma(expr* e) { NOT_IMPLEMENTED_YET(); } - virtual expr_ref lookahead(const expr_ref_vector &,const expr_ref_vector &) { return expr_ref(m.mk_true(), m); } virtual expr_ref cube() { return expr_ref(m.mk_true(), m); } virtual solver *translate(ast_manager &m, params_ref const &p); @@ -136,6 +134,9 @@ private: void refresh(); + + smt_params &fparams() { return m_fparams; } + public: virtual_solver_factory(ast_manager &mgr, smt_params &fparams); virtual ~virtual_solver_factory(); @@ -146,7 +147,6 @@ public: void collect_param_descrs(param_descrs &r) { /* empty */ } void set_produce_models(bool f) { m_fparams.m_model = f; } bool get_produce_models() { return m_fparams.m_model; } - smt_params &fparams() { return m_fparams; } }; } diff --git a/src/muz/transforms/dl_mk_bit_blast.cpp b/src/muz/transforms/dl_mk_bit_blast.cpp index 3c8045e34..924b12eda 100644 --- a/src/muz/transforms/dl_mk_bit_blast.cpp +++ b/src/muz/transforms/dl_mk_bit_blast.cpp @@ -63,6 +63,8 @@ namespace datalog { return alloc(bit_blast_model_converter, m); } + virtual void display(std::ostream& out) { out << "(bit-blast-model-converter)\n"; } + virtual void operator()(model_ref & model) { for (unsigned i = 0; i < m_new_funcs.size(); ++i) { func_decl* p = m_new_funcs[i].get(); diff --git a/src/muz/transforms/dl_mk_karr_invariants.cpp b/src/muz/transforms/dl_mk_karr_invariants.cpp index 48219eeb9..678c673a8 100644 --- a/src/muz/transforms/dl_mk_karr_invariants.cpp +++ b/src/muz/transforms/dl_mk_karr_invariants.cpp @@ -150,6 +150,8 @@ namespace datalog { return mc; } + virtual void display(std::ostream& out) { out << "(add-invariant-model-converter)\n"; } + private: void mk_body(matrix const& M, expr_ref& body) { expr_ref_vector conj(m); diff --git a/src/muz/transforms/dl_mk_quantifier_abstraction.cpp b/src/muz/transforms/dl_mk_quantifier_abstraction.cpp index b171aaa7c..888e1ebb5 100644 --- a/src/muz/transforms/dl_mk_quantifier_abstraction.cpp +++ b/src/muz/transforms/dl_mk_quantifier_abstraction.cpp @@ -53,6 +53,8 @@ namespace datalog { return alloc(qa_model_converter, m); } + virtual void display(std::ostream& out) { display_add(out, m); } + void insert(func_decl* old_p, func_decl* new_p, expr_ref_vector& sub, sort_ref_vector& sorts, svector const& bound) { m_old_funcs.push_back(old_p); m_new_funcs.push_back(new_p); @@ -81,7 +83,11 @@ namespace datalog { SASSERT(body); } else { - body = m.mk_false(); + expr_ref_vector args(m); + for (unsigned i = 0; i < p->get_arity(); ++i) { + args.push_back(m.mk_var(i, p->get_domain(i))); + } + body = m.mk_app(p, args.size(), args.c_ptr()); } // Create quantifier wrapper around body. diff --git a/src/muz/transforms/dl_mk_scale.cpp b/src/muz/transforms/dl_mk_scale.cpp index 5bc10b957..5eeaaaeca 100644 --- a/src/muz/transforms/dl_mk_scale.cpp +++ b/src/muz/transforms/dl_mk_scale.cpp @@ -99,6 +99,9 @@ namespace datalog { UNREACHABLE(); return 0; } + + virtual void display(std::ostream& out) { out << "(scale-model-converter)\n"; } + }; diff --git a/src/muz/transforms/dl_mk_slice.cpp b/src/muz/transforms/dl_mk_slice.cpp index c094e5520..8b38335e0 100644 --- a/src/muz/transforms/dl_mk_slice.cpp +++ b/src/muz/transforms/dl_mk_slice.cpp @@ -283,6 +283,8 @@ namespace datalog { // this would require implementing translation for the dl_context. return 0; } + + virtual void display(std::ostream& out) { out << "(slice-proof-converter)\n"; } }; class mk_slice::slice_model_converter : public model_converter { @@ -396,6 +398,8 @@ namespace datalog { return 0; } + virtual void display(std::ostream& out) { out << "(slice-model-converter)\n"; } + }; mk_slice::mk_slice(context & ctx): diff --git a/src/opt/maxres.cpp b/src/opt/maxres.cpp index 52972fef1..117b65c5a 100644 --- a/src/opt/maxres.cpp +++ b/src/opt/maxres.cpp @@ -315,14 +315,13 @@ public: void found_optimum() { IF_VERBOSE(1, verbose_stream() << "found optimum\n";); - rational upper(0); + m_lower.reset(); for (unsigned i = 0; i < m_soft.size(); ++i) { m_assignment[i] = is_true(m_soft[i]); if (!m_assignment[i]) { - upper += m_weights[i]; + m_lower += m_weights[i]; } } - SASSERT(upper == m_lower); m_upper = m_lower; m_found_feasible_optimum = true; } @@ -399,10 +398,11 @@ public: void get_current_correction_set(model* mdl, exprs& cs) { cs.reset(); if (!mdl) return; - for (unsigned i = 0; i < m_asms.size(); ++i) { - if (is_false(mdl, m_asms[i].get())) { - cs.push_back(m_asms[i].get()); + for (expr* a : m_asms) { + if (is_false(mdl, a)) { + cs.push_back(a); } + TRACE("opt", expr_ref tmp(m); mdl->eval(a, tmp, true); tout << mk_pp(a, m) << ": " << tmp << "\n";); } TRACE("opt", display_vec(tout << "new correction set: ", cs);); } @@ -511,6 +511,7 @@ public: trace(); if (m_c.num_objectives() == 1 && m_pivot_on_cs && m_csmodel.get() && m_correction_set_size < core.size()) { exprs cs; + TRACE("opt", tout << "cs " << m_correction_set_size << " " << core.size() << "\n";); get_current_correction_set(m_csmodel.get(), cs); m_correction_set_size = cs.size(); if (m_correction_set_size < core.size()) { diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 29470ba18..045dcafd4 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -331,7 +331,7 @@ namespace opt { } } - void context::get_model(model_ref& mdl) { + void context::get_model_core(model_ref& mdl) { mdl = m_model; fix_model(mdl); } @@ -818,7 +818,7 @@ namespace opt { bool is_max = is_maximize(fml, term, orig_term, index); bool is_min = !is_max && is_minimize(fml, term, orig_term, index); if (is_min && get_pb_sum(term, terms, weights, offset)) { - TRACE("opt", tout << "try to convert minimization" << mk_pp(term, m) << "\n";); + TRACE("opt", tout << "try to convert minimization\n" << mk_pp(term, m) << "\n";); // minimize 2*x + 3*y // <=> // (assert-soft (not x) 2) diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index ad40db074..4fec5d452 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -184,7 +184,7 @@ namespace opt { virtual void set_hard_constraints(ptr_vector & hard); virtual lbool optimize(); virtual void set_model(model_ref& _m) { m_model = _m; } - virtual void get_model(model_ref& _m); + virtual void get_model_core(model_ref& _m); virtual void get_box_model(model_ref& _m, unsigned index); virtual void fix_model(model_ref& _m); virtual void collect_statistics(statistics& stats) const; diff --git a/src/opt/opt_solver.cpp b/src/opt/opt_solver.cpp index 69b62083f..3eb435fd7 100644 --- a/src/opt/opt_solver.cpp +++ b/src/opt/opt_solver.cpp @@ -47,8 +47,9 @@ namespace opt { m_dump_benchmarks(false), m_first(true), m_was_unknown(false) { + solver::updt_params(p); m_params.updt_params(p); - if (m_params.m_case_split_strategy == CS_ACTIVITY_DELAY_NEW) { + if (m_params.m_case_split_strategy == CS_ACTIVITY_DELAY_NEW) { m_params.m_relevancy_lvl = 0; } // m_params.m_auto_config = false; @@ -296,7 +297,7 @@ namespace opt { } } - void opt_solver::get_model(model_ref & m) { + void opt_solver::get_model_core(model_ref & m) { m_context.get_model(m); } diff --git a/src/opt/opt_solver.h b/src/opt/opt_solver.h index a3107057a..1f0a518b2 100644 --- a/src/opt/opt_solver.h +++ b/src/opt/opt_solver.h @@ -97,7 +97,7 @@ namespace opt { virtual void pop_core(unsigned n); virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions); virtual void get_unsat_core(ptr_vector & r); - virtual void get_model(model_ref & _m); + virtual void get_model_core(model_ref & _m); virtual proof * get_proof(); virtual std::string reason_unknown() const; virtual void set_reason_unknown(char const* msg); @@ -108,7 +108,6 @@ namespace opt { virtual ast_manager& get_manager() const { return m; } virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes); virtual lbool preferred_sat(expr_ref_vector const& asms, vector& cores); - virtual expr_ref lookahead(expr_ref_vector const& assumptions, expr_ref_vector const& candidates) { return expr_ref(m.mk_true(), m); } virtual expr_ref cube() { return expr_ref(m.mk_true(), m); } void set_logic(symbol const& logic); diff --git a/src/parsers/smt/smtlib_solver.cpp b/src/parsers/smt/smtlib_solver.cpp index b4487cbc5..339be2ddd 100644 --- a/src/parsers/smt/smtlib_solver.cpp +++ b/src/parsers/smt/smtlib_solver.cpp @@ -34,7 +34,7 @@ Revision History: namespace smtlib { solver::solver(): - m_ast_manager(m_params.m_proof ? PGM_FINE : PGM_DISABLED, + m_ast_manager(m_params.m_proof ? PGM_ENABLED : PGM_DISABLED, m_params.m_trace ? m_params.m_trace_file_name.c_str() : 0), m_ctx(0), m_error_code(0) { diff --git a/src/parsers/smt2/CMakeLists.txt b/src/parsers/smt2/CMakeLists.txt index 1467d95c6..022cce2f2 100644 --- a/src/parsers/smt2/CMakeLists.txt +++ b/src/parsers/smt2/CMakeLists.txt @@ -1,5 +1,6 @@ z3_add_component(smt2parser SOURCES + marshal.cpp smt2parser.cpp smt2scanner.cpp COMPONENT_DEPENDENCIES diff --git a/src/muz/spacer/spacer_marshal.cpp b/src/parsers/smt2/marshal.cpp similarity index 71% rename from src/muz/spacer/spacer_marshal.cpp rename to src/parsers/smt2/marshal.cpp index 68c90bd33..ae144e491 100644 --- a/src/muz/spacer/spacer_marshal.cpp +++ b/src/parsers/smt2/marshal.cpp @@ -2,14 +2,14 @@ Copyright (c) 2017 Arie Gurfinkel Module Name: - spacer_marshal.cpp + marshal.cpp Abstract: marshaling and unmarshaling of expressions --*/ -#include "muz/spacer/spacer_marshal.h" +#include "parsers/smt2/marshal.h" #include @@ -18,39 +18,33 @@ Abstract: #include "util/vector.h" #include "ast/ast_smt_pp.h" #include "ast/ast_pp.h" +#include "ast/ast_util.h" -namespace spacer { -std::ostream &marshal(std::ostream &os, expr_ref e, ast_manager &m) -{ +std::ostream &marshal(std::ostream &os, expr_ref e, ast_manager &m) { ast_smt_pp pp(m); pp.display_smt2(os, e); return os; } -std::string marshal(expr_ref e, ast_manager &m) -{ +std::string marshal(expr_ref e, ast_manager &m) { std::stringstream ss; marshal(ss, e, m); return ss.str(); } -expr_ref unmarshal(std::istream &is, ast_manager &m) -{ +expr_ref unmarshal(std::istream &is, ast_manager &m) { cmd_context ctx(false, &m); ctx.set_ignore_check(true); if (!parse_smt2_commands(ctx, is)) { return expr_ref(0, m); } ptr_vector::const_iterator it = ctx.begin_assertions(); ptr_vector::const_iterator end = ctx.end_assertions(); - if (it == end) { return expr_ref(m.mk_true(), m); } unsigned size = static_cast(end - it); - return expr_ref(m.mk_and(size, it), m); + return expr_ref(mk_and(m, size, it), m); } -expr_ref unmarshal(std::string s, ast_manager &m) -{ +expr_ref unmarshal(std::string s, ast_manager &m) { std::istringstream is(s); return unmarshal(is, m); } -} diff --git a/src/muz/spacer/spacer_marshal.h b/src/parsers/smt2/marshal.h similarity index 91% rename from src/muz/spacer/spacer_marshal.h rename to src/parsers/smt2/marshal.h index 95cb8f26a..ebc2c0426 100644 --- a/src/muz/spacer/spacer_marshal.h +++ b/src/parsers/smt2/marshal.h @@ -2,7 +2,7 @@ Copyright (c) 2017 Arie Gurfinkel Module Name: - spacer_marshal.h + marshal.h Abstract: @@ -17,14 +17,11 @@ Abstract: #include "ast/ast.h" -namespace spacer { std::ostream &marshal(std::ostream &os, expr_ref e, ast_manager &m); std::string marshal(expr_ref e, ast_manager &m); expr_ref unmarshal(std::string s, ast_manager &m); expr_ref unmarshal(std::istream &is, ast_manager &m); -} - #endif diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index b8ad7e610..4facbc41c 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -96,6 +96,8 @@ namespace smt2 { symbol m_check_sat; symbol m_define_fun; symbol m_define_const; + symbol m_model_add; + symbol m_model_del; symbol m_declare_fun; symbol m_declare_const; symbol m_define_sort; @@ -444,7 +446,10 @@ namespace smt2 { m_ctx.regular_stream()<< "line " << line << " column " << pos << ": " << escaped(msg, true) << "\")" << std::endl; } if (m_ctx.exit_on_error()) { - exit(1); + // WORKAROUND: ASan's LeakSanitizer reports many false positives when + // calling `exit()` so call `_Exit()` instead which avoids invoking leak + // checking. + _Exit(1); } } @@ -1878,6 +1883,8 @@ namespace smt2 { // the resultant expression is on the top of the stack TRACE("let_frame", tout << "let result expr: " << mk_pp(expr_stack().back(), m()) << "\n";); expr_ref r(m()); + if (expr_stack().empty()) + throw parser_exception("invalid let expression"); r = expr_stack().back(); expr_stack().pop_back(); // remove local declarations from the stack @@ -2179,9 +2186,9 @@ namespace smt2 { next(); } - void parse_define_fun() { + void parse_define(bool is_fun) { SASSERT(curr_is_identifier()); - SASSERT(curr_id() == m_define_fun); + SASSERT(curr_id() == (is_fun ? m_define_fun : m_model_add)); SASSERT(m_num_bindings == 0); next(); check_identifier("invalid function/constant definition, symbol expected"); @@ -2195,7 +2202,10 @@ namespace smt2 { parse_expr(); if (m().get_sort(expr_stack().back()) != sort_stack().back()) throw parser_exception("invalid function/constant definition, sort mismatch"); - m_ctx.insert(id, num_vars, sort_stack().c_ptr() + sort_spos, expr_stack().back()); + if (is_fun) + m_ctx.insert(id, num_vars, sort_stack().c_ptr() + sort_spos, expr_stack().back()); + else + m_ctx.model_add(id, num_vars, sort_stack().c_ptr() + sort_spos, expr_stack().back()); check_rparen("invalid function/constant definition, ')' expected"); // restore stacks & env symbol_stack().shrink(sym_spos); @@ -2208,6 +2218,24 @@ namespace smt2 { next(); } + void parse_define_fun() { + parse_define(true); + } + + void parse_model_add() { + parse_define(false); + } + + void parse_model_del() { + next(); + symbol id = curr_id(); + func_decl * f = m_ctx.find_func_decl(id); + m_ctx.model_del(f); + next(); + check_rparen_next("invalid model-del, ')' expected"); + m_ctx.print_success(); + } + void parse_define_fun_rec() { // ( define-fun-rec hfun_defi ) SASSERT(curr_is_identifier()); @@ -2910,6 +2938,14 @@ namespace smt2 { parse_define_funs_rec(); return; } + if (s == m_model_add) { + parse_model_add(); + return; + } + if (s == m_model_del) { + parse_model_del(); + return; + } parse_ext_cmd(line, pos); } @@ -2941,6 +2977,8 @@ namespace smt2 { m_check_sat("check-sat"), m_define_fun("define-fun"), m_define_const("define-const"), + m_model_add("model-add"), + m_model_del("model-del"), m_declare_fun("declare-fun"), m_declare_const("declare-const"), m_define_sort("define-sort"), diff --git a/src/qe/qe.cpp b/src/qe/qe.cpp index dd54f4441..061f403e3 100644 --- a/src/qe/qe.cpp +++ b/src/qe/qe.cpp @@ -1272,7 +1272,7 @@ namespace qe { family_id fid = p->get_family_id(); SASSERT(fid != null_family_id); if (static_cast(m_plugins.size()) <= fid) { - m_plugins.resize(fid+1,0); + m_plugins.resize(fid+1); } SASSERT(!m_plugins[fid]); m_plugins[fid] = p; diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index 9b3033397..6b3b3a11f 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -105,10 +105,10 @@ namespace qe { rational r; app* alit = to_app(lit); vector > nums; - for (unsigned i = 0; i < alit->get_num_args(); ++i) { - val = eval(alit->get_arg(i)); + for (expr* arg : *alit) { + val = eval(arg); if (!a.is_numeral(val, r)) return false; - nums.push_back(std::make_pair(alit->get_arg(i), r)); + nums.push_back(std::make_pair(arg, r)); } std::sort(nums.begin(), nums.end(), compare_second()); for (unsigned i = 0; i + 1 < nums.size(); ++i) { @@ -168,8 +168,8 @@ namespace qe { } else if (a.is_add(t)) { app* ap = to_app(t); - for (unsigned i = 0; i < ap->get_num_args(); ++i) { - linearize(mbo, eval, mul, ap->get_arg(i), c, fmls, ts, tids); + for (expr* arg : *ap) { + linearize(mbo, eval, mul, arg, c, fmls, ts, tids); } } else if (a.is_sub(t, t1, t2)) { @@ -226,16 +226,16 @@ namespace qe { else if (a.is_mul(t)) { app* ap = to_app(t); r = rational(1); - for (unsigned i = 0; i < ap->get_num_args(); ++i) { - if (!is_numeral(ap->get_arg(i), r1)) return false; + for (expr * arg : *ap) { + if (!is_numeral(arg, r1)) return false; r *= r1; } } else if (a.is_add(t)) { app* ap = to_app(t); r = rational(0); - for (unsigned i = 0; i < ap->get_num_args(); ++i) { - if (!is_numeral(ap->get_arg(i), r1)) return false; + for (expr * arg : *ap) { + if (!is_numeral(arg, r1)) return false; r += r1; } } @@ -297,6 +297,7 @@ namespace qe { opt::model_based_opt mbo; obj_map tids; + expr_ref_vector pinned(m); unsigned j = 0; for (unsigned i = 0; i < fmls.size(); ++i) { expr* fml = fmls[i].get(); @@ -308,6 +309,7 @@ namespace qe { } else { TRACE("qe", tout << mk_pp(fml, m) << "\n";); + pinned.push_back(fml); } } fmls.resize(j); @@ -321,8 +323,7 @@ namespace qe { // return those to fmls. expr_mark var_mark, fmls_mark; - for (unsigned i = 0; i < vars.size(); ++i) { - app* v = vars[i].get(); + for (app * v : vars) { var_mark.mark(v); if (is_arith(v) && !tids.contains(v)) { rational r; @@ -332,17 +333,16 @@ namespace qe { tids.insert(v, mbo.add_var(r, a.is_int(v))); } } - for (unsigned i = 0; i < fmls.size(); ++i) { - fmls_mark.mark(fmls[i].get()); + for (expr* fml : fmls) { + fmls_mark.mark(fml); } - obj_map::iterator it = tids.begin(), end = tids.end(); ptr_vector index2expr; - for (; it != end; ++it) { - expr* e = it->m_key; + for (auto& kv : tids) { + expr* e = kv.m_key; if (!var_mark.is_marked(e)) { mark_rec(fmls_mark, e); } - index2expr.setx(it->m_value, e, 0); + index2expr.setx(kv.m_value, e, 0); } j = 0; unsigned_vector real_vars; @@ -360,8 +360,7 @@ namespace qe { } vars.resize(j); TRACE("qe", tout << "remaining vars: " << vars << "\n"; - for (unsigned i = 0; i < real_vars.size(); ++i) { - unsigned v = real_vars[i]; + for (unsigned v : real_vars) { tout << "v" << v << " " << mk_pp(index2expr[v], m) << "\n"; } mbo.display(tout);); @@ -449,8 +448,8 @@ namespace qe { // extract linear constraints - for (unsigned i = 0; i < fmls.size(); ++i) { - linearize(mbo, eval, fmls[i].get(), fmls, tids); + for (expr * fml : fmls) { + linearize(mbo, eval, fml, fmls, tids); } // find optimal value @@ -459,11 +458,10 @@ namespace qe { // update model to use new values that satisfy optimality ptr_vector vars; - obj_map::iterator it = tids.begin(), end = tids.end(); - for (; it != end; ++it) { - expr* e = it->m_key; + for (auto& kv : tids) { + expr* e = kv.m_key; if (is_uninterp_const(e)) { - unsigned id = it->m_value; + unsigned id = kv.m_value; func_decl* f = to_app(e)->get_decl(); expr_ref val(a.mk_numeral(mbo.get_value(id), false), m); mdl.register_decl(f, val); @@ -509,10 +507,9 @@ namespace qe { void extract_coefficients(opt::model_based_opt& mbo, model_evaluator& eval, obj_map const& ts, obj_map& tids, vars& coeffs) { coeffs.reset(); eval.set_model_completion(true); - obj_map::iterator it = ts.begin(), end = ts.end(); - for (; it != end; ++it) { + for (auto& kv : ts) { unsigned id; - expr* v = it->m_key; + expr* v = kv.m_key; if (!tids.find(v, id)) { rational r; expr_ref val = eval(v); @@ -520,9 +517,9 @@ namespace qe { id = mbo.add_var(r, a.is_int(v)); tids.insert(v, id); } - CTRACE("qe", it->m_value.is_zero(), tout << mk_pp(v, m) << " has coefficeint 0\n";); - if (!it->m_value.is_zero()) { - coeffs.push_back(var(id, it->m_value)); + CTRACE("qe", kv.m_value.is_zero(), tout << mk_pp(v, m) << " has coefficeint 0\n";); + if (!kv.m_value.is_zero()) { + coeffs.push_back(var(id, kv.m_value)); } } } diff --git a/src/sat/ba_solver.cpp b/src/sat/ba_solver.cpp index f963ce620..2a880f918 100644 --- a/src/sat/ba_solver.cpp +++ b/src/sat/ba_solver.cpp @@ -1173,7 +1173,7 @@ namespace sat { break; case justification::CLAUSE: { inc_bound(offset); - clause & c = *(s().m_cls_allocator.get_clause(js.get_clause_offset())); + clause & c = s().get_clause(js); unsigned i = 0; if (consequent != null_literal) { inc_coeff(consequent, offset); @@ -3780,7 +3780,7 @@ namespace sat { break; case justification::CLAUSE: { ineq.reset(offset); - clause & c = *(s().m_cls_allocator.get_clause(js.get_clause_offset())); + clause & c = s().get_clause(js); for (literal l : c) ineq.push(l, offset); break; } diff --git a/src/sat/sat_asymm_branch.cpp b/src/sat/sat_asymm_branch.cpp index 14969adb8..03d121599 100644 --- a/src/sat/sat_asymm_branch.cpp +++ b/src/sat/sat_asymm_branch.cpp @@ -115,6 +115,7 @@ namespace sat { } bool asymm_branch::process(clause & c) { + if (c.is_blocked()) return true; TRACE("asymm_branch_detail", tout << "processing: " << c << "\n";); SASSERT(s.scope_lvl() == 0); SASSERT(s.m_qhead == s.m_trail.size()); diff --git a/src/sat/sat_clause.cpp b/src/sat/sat_clause.cpp index 206dfcf40..43d614e38 100644 --- a/src/sat/sat_clause.cpp +++ b/src/sat/sat_clause.cpp @@ -60,15 +60,15 @@ namespace sat { } bool clause::contains(literal l) const { - for (unsigned i = 0; i < m_size; i++) - if (m_lits[i] == l) + for (literal l2 : *this) + if (l2 == l) return true; return false; } bool clause::contains(bool_var v) const { - for (unsigned i = 0; i < m_size; i++) - if (m_lits[i].var() == v) + for (literal l : *this) + if (l.var() == v) return true; return false; } @@ -87,8 +87,7 @@ namespace sat { } bool clause::satisfied_by(model const & m) const { - for (unsigned i = 0; i < m_size; i++) { - literal l = m_lits[i]; + for (literal l : *this) { if (l.sign()) { if (m[l.var()] == l_false) return true; @@ -197,14 +196,13 @@ namespace sat { if (c.was_removed()) out << "x"; if (c.strengthened()) out << "+"; if (c.is_learned()) out << "*"; + if (c.is_blocked()) out << "b"; return out; } std::ostream & operator<<(std::ostream & out, clause_vector const & cs) { - clause_vector::const_iterator it = cs.begin(); - clause_vector::const_iterator end = cs.end(); - for (; it != end; ++it) { - out << *(*it) << "\n"; + for (clause *cp : cs) { + out << *cp << "\n"; } return out; } @@ -226,12 +224,12 @@ namespace sat { } std::ostream & operator<<(std::ostream & out, clause_wrapper const & c) { - out << "("; - for (unsigned i = 0; i < c.size(); i++) { - if (i > 0) out << " "; - out << c[i]; + if (c.is_binary()) { + out << "(" << c[0] << " " << c[1] << ")"; + } + else { + out << c.get_clause()->id() << ": " << *c.get_clause(); } - out << ")"; return out; } diff --git a/src/sat/sat_clause.h b/src/sat/sat_clause.h index 8fd7b52cf..dd7af64eb 100644 --- a/src/sat/sat_clause.h +++ b/src/sat/sat_clause.h @@ -33,6 +33,10 @@ namespace sat { class clause_allocator; + class clause; + + std::ostream & operator<<(std::ostream & out, clause const & c); + class clause { friend class clause_allocator; friend class tmp_clause; @@ -101,7 +105,6 @@ namespace sat { void set_reinit_stack(bool f) { m_reinit_stack = f; } }; - std::ostream & operator<<(std::ostream & out, clause const & c); std::ostream & operator<<(std::ostream & out, clause_vector const & cs); class bin_clause { @@ -180,6 +183,7 @@ namespace sat { bool contains(literal l) const; bool contains(bool_var v) const; clause * get_clause() const { SASSERT(!is_binary()); return m_cls; } + bool was_removed() const { return !is_binary() && get_clause()->was_removed(); } }; typedef svector clause_wrapper_vector; diff --git a/src/sat/sat_cleaner.cpp b/src/sat/sat_cleaner.cpp index 286cc1661..e52cf139f 100644 --- a/src/sat/sat_cleaner.cpp +++ b/src/sat/sat_cleaner.cpp @@ -137,7 +137,8 @@ namespace sat { SASSERT(s.value(c[0]) == l_undef && s.value(c[1]) == l_undef); if (new_sz == 2) { TRACE("cleanup_bug", tout << "clause became binary: " << c[0] << " " << c[1] << "\n";); - s.mk_bin_clause(c[0], c[1], c.is_learned()); + if (!c.is_blocked()) + s.mk_bin_clause(c[0], c[1], c.is_learned()); s.del_clause(c); } else { diff --git a/src/sat/sat_config.cpp b/src/sat/sat_config.cpp index 08f1b86b2..31304bab5 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -89,7 +89,6 @@ namespace sat { m_local_search = p.local_search(); m_local_search_threads = p.local_search_threads(); m_lookahead_simplify = p.lookahead_simplify(); - m_lookahead_cube = p.lookahead_cube(); m_lookahead_search = p.lookahead_search(); if (p.lookahead_reward() == symbol("heule_schur")) { m_lookahead_reward = heule_schur_reward; @@ -146,9 +145,10 @@ namespace sat { m_minimize_lemmas = p.minimize_lemmas(); m_core_minimize = p.core_minimize(); m_core_minimize_partial = p.core_minimize_partial(); - m_drat_check = p.drat_check(); + m_drat_check_unsat = p.drat_check_unsat(); + m_drat_check_sat = p.drat_check_sat(); m_drat_file = p.drat_file(); - m_drat = (m_drat_check || m_drat_file != symbol("")) && p.threads() == 1; + m_drat = (m_drat_check_unsat || m_drat_file != symbol("") || m_drat_check_sat) && p.threads() == 1; m_dyn_sub_res = p.dyn_sub_res(); // Parameters used in Liang, Ganesh, Poupart, Czarnecki AAAI 2016. diff --git a/src/sat/sat_config.h b/src/sat/sat_config.h index a6db0cddc..2e1d8a145 100644 --- a/src/sat/sat_config.h +++ b/src/sat/sat_config.h @@ -84,7 +84,6 @@ namespace sat { bool m_local_search; bool m_lookahead_search; bool m_lookahead_simplify; - bool m_lookahead_cube; unsigned m_lookahead_cube_cutoff; double m_lookahead_cube_fraction; reward_t m_lookahead_reward; @@ -105,7 +104,8 @@ namespace sat { bool m_core_minimize_partial; bool m_drat; symbol m_drat_file; - bool m_drat_check; + bool m_drat_check_unsat; + bool m_drat_check_sat; bool m_dimacs_display; bool m_dimacs_inprocess_display; diff --git a/src/sat/sat_drat.cpp b/src/sat/sat_drat.cpp index d3864fd8b..fd2b6fa79 100644 --- a/src/sat/sat_drat.cpp +++ b/src/sat/sat_drat.cpp @@ -27,7 +27,10 @@ namespace sat { drat::drat(solver& s): s(s), m_out(0), - m_inconsistent(false) + m_inconsistent(false), + m_check_unsat(false), + m_check_sat(false), + m_check(false) { if (s.m_config.m_drat && s.m_config.m_drat_file != symbol()) { m_out = alloc(std::ofstream, s.m_config.m_drat_file.str().c_str()); @@ -44,6 +47,12 @@ namespace sat { } } + void drat::updt_config() { + m_check_unsat = s.m_config.m_drat_check_unsat; + m_check_sat = s.m_config.m_drat_check_sat; + m_check = m_check_unsat || m_check_sat; + } + std::ostream& operator<<(std::ostream& out, drat::status st) { switch (st) { case drat::status::learned: return out << "l"; @@ -88,7 +97,7 @@ namespace sat { } void drat::append(literal l, status st) { - IF_VERBOSE(10, trace(verbose_stream(), 1, &l, st);); + IF_VERBOSE(20, trace(verbose_stream(), 1, &l, st);); if (st == status::learned) { verify(1, &l); } @@ -100,7 +109,7 @@ namespace sat { void drat::append(literal l1, literal l2, status st) { literal lits[2] = { l1, l2 }; - IF_VERBOSE(10, trace(verbose_stream(), 2, lits, st);); + IF_VERBOSE(20, trace(verbose_stream(), 2, lits, st);); if (st == status::deleted) { // noop // don't record binary as deleted. @@ -131,7 +140,7 @@ namespace sat { void drat::append(clause& c, status st) { unsigned n = c.size(); - IF_VERBOSE(10, trace(verbose_stream(), n, c.begin(), st);); + IF_VERBOSE(20, trace(verbose_stream(), n, c.begin(), st);); if (st == status::learned) { verify(n, c.begin()); @@ -270,7 +279,7 @@ namespace sat { } void drat::verify(unsigned n, literal const* c) { - if (!is_drup(n, c) && !is_drat(n, c)) { + if (m_check_unsat && !is_drup(n, c) && !is_drat(n, c)) { std::cout << "Verification failed\n"; UNREACHABLE(); //display(std::cout); @@ -412,7 +421,7 @@ namespace sat { void drat::add() { if (m_out) (*m_out) << "0\n"; - if (s.m_config.m_drat_check) { + if (m_check_unsat) { SASSERT(m_inconsistent); } } @@ -420,7 +429,7 @@ namespace sat { declare(l); status st = get_status(learned); if (m_out) dump(1, &l, st); - if (s.m_config.m_drat_check) append(l, st); + if (m_check) append(l, st); } void drat::add(literal l1, literal l2, bool learned) { declare(l1); @@ -428,17 +437,17 @@ namespace sat { literal ls[2] = {l1, l2}; status st = get_status(learned); if (m_out) dump(2, ls, st); - if (s.m_config.m_drat_check) append(l1, l2, st); + if (m_check) append(l1, l2, st); } void drat::add(clause& c, bool learned) { TRACE("sat", tout << "add: " << c << "\n";); for (unsigned i = 0; i < c.size(); ++i) declare(c[i]); status st = get_status(learned); if (m_out) dump(c.size(), c.begin(), st); - if (s.m_config.m_drat_check) append(c, get_status(learned)); + if (m_check_unsat) append(c, get_status(learned)); } void drat::add(literal_vector const& lits, svector const& premises) { - if (s.m_config.m_drat_check) { + if (m_check) { switch (lits.size()) { case 0: add(); break; case 1: append(lits[0], status::external); break; @@ -453,7 +462,7 @@ namespace sat { void drat::add(literal_vector const& c) { for (unsigned i = 0; i < c.size(); ++i) declare(c[i]); if (m_out) dump(c.size(), c.begin(), status::learned); - if (s.m_config.m_drat_check) { + if (m_check) { switch (c.size()) { case 0: add(); break; case 1: append(c[0], status::learned); break; @@ -469,20 +478,25 @@ namespace sat { void drat::del(literal l) { if (m_out) dump(1, &l, status::deleted); - if (s.m_config.m_drat_check) append(l, status::deleted); + if (m_check_unsat) append(l, status::deleted); } void drat::del(literal l1, literal l2) { literal ls[2] = {l1, l2}; if (m_out) dump(2, ls, status::deleted); - if (s.m_config.m_drat_check) + if (m_check) append(l1, l2, status::deleted); } void drat::del(clause& c) { TRACE("sat", tout << "del: " << c << "\n";); if (m_out) dump(c.size(), c.begin(), status::deleted); - if (s.m_config.m_drat_check) { + if (m_check) { clause* c1 = s.m_cls_allocator.mk_clause(c.size(), c.begin(), c.is_learned()); append(*c1, status::deleted); } } + + void drat::check_model(model const& m) { + std::cout << "check model on " << m_proof.size() << "\n"; + } + } diff --git a/src/sat/sat_drat.h b/src/sat/sat_drat.h index 2765d104c..64d796839 100644 --- a/src/sat/sat_drat.h +++ b/src/sat/sat_drat.h @@ -52,6 +52,7 @@ namespace sat { vector m_watches; svector m_assignment; bool m_inconsistent; + bool m_check_unsat, m_check_sat, m_check; void dump(unsigned n, literal const* c, status st); void append(literal l, status st); @@ -78,6 +79,8 @@ namespace sat { public: drat(solver& s); ~drat(); + + void updt_config(); void add(); void add(literal l, bool learned); void add(literal l1, literal l2, bool learned); @@ -89,6 +92,8 @@ namespace sat { void del(literal l); void del(literal l1, literal l2); void del(clause& c); + + void check_model(model const& m); }; }; diff --git a/src/sat/sat_lookahead.h b/src/sat/sat_lookahead.h index 56d2e94a1..baf3ed660 100644 --- a/src/sat/sat_lookahead.h +++ b/src/sat/sat_lookahead.h @@ -246,8 +246,8 @@ namespace sat { inline bool is_true(literal l) const { return is_true_at(l, m_level); } inline void set_true(literal l) { m_stamp[l.var()] = m_level + l.sign(); } inline void set_undef(literal l) { m_stamp[l.var()] = 0; } - inline unsigned get_level(literal l) const { return m_stamp[l.var()] & UINT_MAX - 1; } - void set_level(literal d, literal s) { m_stamp[d.var()] = (m_stamp[s.var()] & ~0x1) + d.sign(); } + inline unsigned get_level(literal l) const { return m_stamp[l.var()] & ~0x1; } + void set_level(literal d, literal s) { m_stamp[d.var()] = get_level(s) + d.sign(); } lbool value(literal l) const { return is_undef(l) ? l_undef : is_true(l) ? l_true : l_false; } // set the level within a scope of the search. diff --git a/src/sat/sat_model_converter.cpp b/src/sat/sat_model_converter.cpp index eb60e209a..d46063455 100644 --- a/src/sat/sat_model_converter.cpp +++ b/src/sat/sat_model_converter.cpp @@ -18,11 +18,12 @@ Revision History: --*/ #include "sat/sat_model_converter.h" #include "sat/sat_clause.h" +#include "sat/sat_solver.h" #include "util/trace.h" namespace sat { - model_converter::model_converter() { + model_converter::model_converter(): m_solver(nullptr) { } model_converter::~model_converter() { @@ -50,7 +51,6 @@ namespace sat { } if (!sat) { m[lit.var()] = lit.sign() ? l_false : l_true; - // if (lit.var() == 258007) std::cout << "flip " << lit << " " << m[lit.var()] << " @ " << i << " of " << c << " from overall size " << sz << "\n"; } } } @@ -58,6 +58,8 @@ namespace sat { void model_converter::operator()(model & m) const { vector::const_iterator begin = m_entries.begin(); vector::const_iterator it = m_entries.end(); + bool first = true; + //VERIFY(!m_solver || m_solver->check_clauses(m)); while (it != begin) { --it; SASSERT(it->get_kind() != ELIM_VAR || m[it->var()] == l_undef); @@ -70,14 +72,18 @@ namespace sat { for (literal l : it->m_clauses) { if (l == null_literal) { // end of clause - elim_stack* s = it->m_elim_stack[index]; + elim_stack* st = it->m_elim_stack[index]; if (!sat) { m[it->var()] = var_sign ? l_false : l_true; } - if (s) { - process_stack(m, clause, s->stack()); + if (st) { + process_stack(m, clause, st->stack()); } sat = false; + if (false && first && m_solver && !m_solver->check_clauses(m)) { + display(std::cout, *it) << "\n"; + first = false; + } ++index; clause.reset(); continue; @@ -88,6 +94,8 @@ namespace sat { continue; bool sign = l.sign(); bool_var v = l.var(); + if (v >= m.size()) std::cout << v << " model size: " << m.size() << "\n"; + VERIFY(v < m.size()); if (v == it->var()) var_sign = sign; if (value_at(l, m) == l_true) @@ -95,8 +103,12 @@ namespace sat { else if (!sat && v != it->var() && m[v] == l_undef) { // clause can be satisfied by assigning v. m[v] = sign ? l_false : l_true; - // if (v == 258007) std::cout << "set undef " << v << " to " << m[v] << " in " << clause << "\n"; + // if (first) std::cout << "set: " << l << "\n"; sat = true; + if (false && first && m_solver && !m_solver->check_clauses(m)) { + display(std::cout, *it) << "\n";; + first = false; + } } } DEBUG_CODE({ @@ -217,10 +229,12 @@ namespace sat { it2++; for (; it2 != end; ++it2) { SASSERT(it2->var() != it->var()); + if (it2->var() == it->var()) return false; for (literal l : it2->m_clauses) { CTRACE("sat_model_converter", l.var() == it->var(), tout << "var: " << it->var() << "\n"; display(tout);); SASSERT(l.var() != it->var()); SASSERT(l == null_literal || l.var() < num_vars); + if (it2->var() == it->var()) return false; } } } @@ -229,31 +243,60 @@ namespace sat { } void model_converter::display(std::ostream & out) const { - out << "(sat::model-converter"; + out << "(sat::model-converter\n"; + bool first = true; for (auto & entry : m_entries) { - out << "\n (" << (entry.get_kind() == ELIM_VAR ? "elim" : "blocked") << " " << entry.var(); - bool start = true; - for (literal l : entry.m_clauses) { - if (start) { - out << "\n ("; - start = false; - } - else { - if (l != null_literal) - out << " "; - } - if (l == null_literal) { - out << ")"; - start = true; - continue; - } - out << l; - } - out << ")"; + if (first) first = false; else out << "\n"; + display(out, entry); } out << ")\n"; } + std::ostream& model_converter::display(std::ostream& out, entry const& entry) const { + out << " ("; + switch (entry.get_kind()) { + case ELIM_VAR: out << "elim"; break; + case BLOCK_LIT: out << "blocked"; break; + case CCE: out << "cce"; break; + case ACCE: out << "acce"; break; + } + out << " " << entry.var(); + bool start = true; + unsigned index = 0; + for (literal l : entry.m_clauses) { + if (start) { + out << "\n ("; + start = false; + } + else { + if (l != null_literal) + out << " "; + } + if (l == null_literal) { + out << ")"; + start = true; + elim_stack* st = entry.m_elim_stack[index]; + if (st) { + elim_stackv const& stack = st->stack(); + unsigned sz = stack.size(); + for (unsigned i = sz; i-- > 0; ) { + out << "\n " << stack[i].first << " " << stack[i].second; + } + } + ++index; + continue; + } + out << l; + } + out << ")"; + for (literal l : entry.m_clauses) { + if (l != null_literal) { + if (false && m_solver && m_solver->was_eliminated(l.var())) out << "\neliminated: " << l; + } + } + return out; + } + void model_converter::copy(model_converter const & src) { reset(); m_entries.append(src.m_entries); @@ -276,4 +319,47 @@ namespace sat { return result; } + void model_converter::expand(literal_vector& update_stack) { + sat::literal_vector clause; + for (entry const& e : m_entries) { + unsigned index = 0; + bool var_sign = false; + clause.reset(); + for (literal l : e.m_clauses) { + if (l == null_literal) { + elim_stack* st = e.m_elim_stack[index]; + if (st) { + // clause sizes increase + elim_stackv const& stack = st->stack(); + unsigned sz = stack.size(); + for (unsigned i = 0; i < sz; ++i) { + unsigned csz = stack[i].first; + literal lit = stack[i].second; + BOOL found = false; + unsigned j = 0; + for (j = 0; j < csz; ++j) { + if (clause[j] == lit) { + std::swap(clause[j], clause[0]); + found = true; + break; + } + } + SASSERT(found); + update_stack.append(csz, clause.c_ptr()); + update_stack.push_back(null_literal); + } + } + update_stack.append(clause); + update_stack.push_back(null_literal); + clause.reset(); + continue; + } + clause.push_back(l); + if (l.var() == e.var()) { + std::swap(clause[0], clause.back()); + } + } + } + } + }; diff --git a/src/sat/sat_model_converter.h b/src/sat/sat_model_converter.h index dcbe450a8..8f8b1a41b 100644 --- a/src/sat/sat_model_converter.h +++ b/src/sat/sat_model_converter.h @@ -36,51 +36,66 @@ namespace sat { This is a low-level model_converter. Given a mapping from bool_var to expr, it can be converted into a general Z3 model_converter */ + + class solver; + + static unsigned counter = 0; + class model_converter { public: typedef svector> elim_stackv; + class elim_stack { - unsigned m_refcount; + unsigned m_counter; + unsigned m_refcount; elim_stackv m_stack; + elim_stack(elim_stack const& ); public: elim_stack(elim_stackv const& stack): + m_counter(0), m_refcount(0), m_stack(stack) { + m_counter = ++counter; } ~elim_stack() { } void inc_ref() { ++m_refcount; } - void dec_ref() { if (0 == --m_refcount) dealloc(this); } + void dec_ref() { if (0 == --m_refcount) { dealloc(this); } } elim_stackv const& stack() const { return m_stack; } + unsigned ref_count() const { return m_refcount; } }; - enum kind { ELIM_VAR = 0, BLOCK_LIT }; + enum kind { ELIM_VAR = 0, BLOCK_LIT, CCE, ACCE }; class entry { friend class model_converter; - unsigned m_var:31; - unsigned m_kind:1; - literal_vector m_clauses; // the different clauses are separated by null_literal - sref_vector m_elim_stack; - entry(kind k, bool_var v):m_var(v), m_kind(k) {} + unsigned m_var:30; + unsigned m_kind:2; + literal_vector m_clauses; // the different clauses are separated by null_literal + sref_vector m_elim_stack; + entry(kind k, bool_var v): m_var(v), m_kind(k) {} public: entry(entry const & src): m_var(src.m_var), m_kind(src.m_kind), - m_clauses(src.m_clauses), - m_elim_stack(src.m_elim_stack) { + m_clauses(src.m_clauses) { + m_elim_stack.append(src.m_elim_stack); } bool_var var() const { return m_var; } kind get_kind() const { return static_cast(m_kind); } }; private: vector m_entries; + solver const* m_solver; void process_stack(model & m, literal_vector const& clause, elim_stackv const& stack) const; + std::ostream& display(std::ostream & out, entry const& entry) const; + public: model_converter(); ~model_converter(); + void set_solver(solver const* s) { m_solver = s; } void operator()(model & m) const; model_converter& operator=(model_converter const& other); @@ -100,6 +115,15 @@ namespace sat { void copy(model_converter const & src); void collect_vars(bool_var_set & s) const; unsigned max_var(unsigned min) const; + /* + * \brief expand entries to a list of clauses, such that + * the first literal in each clause is the literal whose + * truth value is updated as follows: + * + * lit0 := lit0 or (~lit1 & ~lit2 & ... & ~lit_k) + * + */ + void expand(literal_vector& update_stack); }; }; diff --git a/src/sat/sat_parallel.cpp b/src/sat/sat_parallel.cpp index 21f98cd6a..7ba1ba7c6 100644 --- a/src/sat/sat_parallel.cpp +++ b/src/sat/sat_parallel.cpp @@ -101,7 +101,7 @@ namespace sat { void parallel::init_solvers(solver& s, unsigned num_extra_solvers) { unsigned num_threads = num_extra_solvers + 1; - m_solvers.resize(num_extra_solvers, 0); + m_solvers.resize(num_extra_solvers); symbol saved_phase = s.m_params.get_sym("phase", symbol("caching")); for (unsigned i = 0; i < num_extra_solvers; ++i) { m_limits.push_back(reslimit()); diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index d803617f5..229bd8a4b 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -28,16 +28,16 @@ def_module_params('sat', ('threads', UINT, 1, 'number of parallel threads to use'), ('dimacs.core', BOOL, False, 'extract core from DIMACS benchmarks'), ('drat.file', SYMBOL, '', 'file to dump DRAT proofs'), - ('drat.check', BOOL, False, 'build up internal proof and check'), + ('drat.check_unsat', BOOL, False, 'build up internal proof and check'), + ('drat.check_sat', BOOL, False, 'build up internal trace, check satisfying model'), ('cardinality.solver', BOOL, False, 'use cardinality solver'), ('pb.solver', SYMBOL, 'circuit', 'method for handling Pseudo-Boolean constraints: circuit (arithmetical circuit), sorting (sorting circuit), totalizer (use totalizer encoding), solver (use SMT solver)'), ('xor.solver', BOOL, False, 'use xor solver'), ('atmost1_encoding', SYMBOL, 'grouped', 'encoding used for at-most-1 constraints grouped, bimander, ordered'), ('local_search_threads', UINT, 0, 'number of local search threads to find satisfiable solution'), ('local_search', BOOL, False, 'use local search instead of CDCL'), - ('lookahead_cube', BOOL, False, 'use lookahead solver to create cubes'), ('lookahead.cube.fraction', DOUBLE, 0.4, 'adaptive fraction to create lookahead cubes. Used when lookahead_cube is true'), - ('lookahead.cube.cutoff', UINT, 0, 'cut-off depth to create cubes. Only enabled when non-zero. Used when lookahead_cube is true.'), + ('lookahead.cube.cutoff', UINT, 10, 'cut-off depth to create cubes. Only enabled when non-zero. Used when lookahead_cube is true.'), ('lookahead_search', BOOL, False, 'use lookahead solver'), ('lookahead.preselect', BOOL, False, 'use pre-selection of subset of variables for branching'), ('lookahead_simplify', BOOL, False, 'use lookahead solver during simplification'), diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index 09cb063f5..5dccdd379 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -90,6 +90,21 @@ namespace sat { inline void simplifier::checkpoint() { s.checkpoint(); } + bool simplifier::single_threaded() const { return s.m_config.m_num_threads == 1; } + + bool simplifier::bce_enabled() const { + return !s.tracking_assumptions() && + !m_learned_in_use_lists && + m_num_calls >= m_bce_delay && + (m_elim_blocked_clauses || m_elim_blocked_clauses_at == m_num_calls || cce_enabled()); + } + bool simplifier::acce_enabled() const { return !s.tracking_assumptions() && !m_learned_in_use_lists && m_num_calls >= m_bce_delay && m_acce; } + bool simplifier::cce_enabled() const { return !s.tracking_assumptions() && !m_learned_in_use_lists && m_num_calls >= m_bce_delay && (m_cce || acce_enabled()); } + bool simplifier::abce_enabled() const { return !m_learned_in_use_lists && m_num_calls >= m_bce_delay && m_abce; } + bool simplifier::bca_enabled() const { return !s.tracking_assumptions() && m_bca && m_learned_in_use_lists && single_threaded(); } + bool simplifier::elim_vars_bdd_enabled() const { return !s.tracking_assumptions() && m_elim_vars_bdd && m_num_calls >= m_elim_vars_bdd_delay && single_threaded(); } + bool simplifier::elim_vars_enabled() const { return !s.tracking_assumptions() && m_elim_vars && single_threaded(); } + void simplifier::register_clauses(clause_vector & cs) { std::stable_sort(cs.begin(), cs.end(), size_lt()); for (clause* c : cs) { @@ -201,18 +216,19 @@ namespace sat { CASSERT("sat_solver", s.check_invariant()); m_need_cleanup = false; m_use_list.init(s.num_vars()); - m_learned_in_use_lists = false; + m_learned_in_use_lists = learned; if (learned) { register_clauses(s.m_learned); - m_learned_in_use_lists = true; } register_clauses(s.m_clauses); - if (!learned && (bce_enabled() || bca_enabled())) + if (bce_enabled() || abce_enabled() || bca_enabled()) { elim_blocked_clauses(); + } - if (!learned) + if (!learned) { m_num_calls++; + } m_sub_counter = m_subsumption_limit; m_elim_counter = m_res_limit; @@ -229,7 +245,7 @@ namespace sat { subsume(); if (s.inconsistent()) return; - if (!learned && elim_vars_enabled() && s.m_config.m_num_threads == 1) + if (!learned && elim_vars_enabled()) elim_vars(); if (s.inconsistent()) return; @@ -457,7 +473,7 @@ namespace sat { literal_vector::iterator l_it = m_bs_ls.begin(); for (; it != end; ++it, ++l_it) { clause & c2 = *(*it); - if (!c2.was_removed() && *l_it == null_literal) { + if (!c2.was_removed() && !c1.is_blocked() && *l_it == null_literal) { // c2 was subsumed if (c1.is_learned() && !c2.is_learned()) c1.unset_learned(); @@ -713,7 +729,7 @@ namespace sat { // should not traverse wlist using iterators, since back_subsumption1 may add new binary clauses there for (unsigned j = 0; j < wlist.size(); j++) { watched w = wlist[j]; - if (w.is_binary_clause()) { + if (w.is_binary_unblocked_clause()) { literal l2 = w.get_literal(); if (l.index() < l2.index()) { m_dummy.set(l, l2, w.is_learned()); @@ -959,8 +975,10 @@ namespace sat { void operator()() { if (s.bce_enabled()) block_clauses(); + if (s.abce_enabled()) + cce(); if (s.cce_enabled()) - cce(); + cce(); if (s.bca_enabled()) bca(); } @@ -1037,7 +1055,7 @@ namespace sat { block_clause(c, l, new_entry); s.m_num_blocked_clauses++; } - s.unmark_all(c); + s.unmark_all(c); } for (clause* c : m_to_remove) s.block_clause(*c); @@ -1053,21 +1071,22 @@ namespace sat { for (watched & w : s.get_wlist(l)) { // when adding a blocked clause, then all non-learned clauses have to be considered for the // resolution intersection. - bool process_bin = adding ? (w.is_binary_clause() && !w.is_learned()) : w.is_binary_unblocked_clause(); + bool process_bin = adding ? w.is_binary_clause() : w.is_binary_unblocked_clause(); if (process_bin) { literal lit = w.get_literal(); - if (s.is_marked(~lit) && lit != ~l) continue; + if (s.is_marked(~lit) && lit != ~l) { + continue; // tautology + } if (!first || s.is_marked(lit)) { inter.reset(); - return false; + return false; // intersection is empty or does not add anything new. } first = false; inter.push_back(lit); } } clause_use_list & neg_occs = s.m_use_list.get(~l); - clause_use_list::iterator it = neg_occs.mk_iterator(); - for (; !it.at_end(); it.next()) { + for (auto it = neg_occs.mk_iterator(); !it.at_end(); it.next()) { bool tautology = false; clause & c = it.curr(); if (c.is_blocked() && !adding) continue; @@ -1087,8 +1106,8 @@ namespace sat { } else { unsigned j = 0; - for (literal lit : inter) - if (c.contains(lit)) + for (literal lit : inter) + if (c.contains(lit)) inter[j++] = lit; inter.shrink(j); if (j == 0) return false; @@ -1098,6 +1117,30 @@ namespace sat { return first; } + bool check_abce_tautology(literal l) { + if (!process_var(l.var())) return false; + for (watched & w : s.get_wlist(l)) { + if (w.is_binary_unblocked_clause()) { + literal lit = w.get_literal(); + if (!s.is_marked(~lit) || lit == ~l) return false; + } + } + clause_use_list & neg_occs = s.m_use_list.get(~l); + for (auto it = neg_occs.mk_iterator(); !it.at_end(); it.next()) { + clause & c = it.curr(); + if (c.is_blocked()) continue; + bool tautology = false; + for (literal lit : c) { + if (s.is_marked(~lit) && lit != ~l) { + tautology = true; + break; + } + } + if (!tautology) return false; + } + return true; + } + /* * C \/ l l \/ lit * ------------------- @@ -1146,59 +1189,101 @@ namespace sat { return false; } - bool cla(literal& blocked) { + bool above_threshold(unsigned sz0) const { + return sz0 * 10 < m_covered_clause.size(); + } + + template + bool cla(literal& blocked, model_converter::kind& k) { bool is_tautology = false; for (literal l : m_covered_clause) s.mark_visited(l); unsigned num_iterations = 0, sz; m_elim_stack.reset(); m_ala_qhead = 0; + k = model_converter::CCE; + unsigned sz0 = m_covered_clause.size(); + + /* + * For blocked clause elimination with asymmetric literal addition (ABCE) + * it suffices to check if one of the original + * literals in the clause is blocked modulo the additional literals added to the clause. + * So we record sz0, the original set of literals in the clause, mark additional literals, + * and then check if any of the first sz0 literals are blocked. + */ + if (s.abce_enabled() && !use_ri) { + add_ala(); + for (unsigned i = 0; i < sz0; ++i) { + if (check_abce_tautology(m_covered_clause[i])) { + blocked = m_covered_clause[i]; + is_tautology = true; + break; + } + } + k = model_converter::BLOCK_LIT; // actually ABCE + for (literal l : m_covered_clause) s.unmark_visited(l); + m_covered_clause.shrink(sz0); + return is_tautology; + } + + /* + * For CCE we add the resolution intersection while checking if the clause becomes a tautology. + * For ACCE, in addition, we add literals. + */ do { do { + if (above_threshold(sz0)) break; ++num_iterations; sz = m_covered_clause.size(); is_tautology = add_cla(blocked); } while (m_covered_clause.size() > sz && !is_tautology); + if (above_threshold(sz0)) break; if (s.acce_enabled() && !is_tautology) { sz = m_covered_clause.size(); add_ala(); + k = model_converter::ACCE; } } while (m_covered_clause.size() > sz && !is_tautology); + // if (is_tautology) std::cout << num_iterations << " " << m_covered_clause.size() << " " << sz0 << " " << is_tautology << " " << m_elim_stack.size() << "\n"; for (literal l : m_covered_clause) s.unmark_visited(l); - // if (is_tautology) std::cout << "taut: " << num_iterations << " " << m_covered_clause.size() << " " << m_elim_stack.size() << "\n"; - return is_tautology; + return is_tautology && !above_threshold(sz0); } // perform covered clause elimination. // first extract the covered literal addition (CLA). // then check whether the CLA is blocked. - bool cce(clause& c, literal& blocked) { + template + bool cce(clause& c, literal& blocked, model_converter::kind& k) { m_covered_clause.reset(); for (literal l : c) m_covered_clause.push_back(l); - return cla(blocked); + return cla(blocked, k); } - bool cce(literal l1, literal l2, literal& blocked) { + template + bool cce(literal l1, literal l2, literal& blocked, model_converter::kind& k) { m_covered_clause.reset(); m_covered_clause.push_back(l1); m_covered_clause.push_back(l2); - return cla(blocked); + return cla(blocked, k); } + template void cce() { insert_queue(); - cce_clauses(); - cce_binary(); + cce_clauses(); + cce_binary(); } + template void cce_binary() { while (!m_queue.empty() && m_counter >= 0) { s.checkpoint(); - process_cce_binary(m_queue.next()); + process_cce_binary(m_queue.next()); } } + template void process_cce_binary(literal l) { literal blocked = null_literal; watch_list & wlist = s.get_wlist(~l); @@ -1206,15 +1291,15 @@ namespace sat { watch_list::iterator it = wlist.begin(); watch_list::iterator it2 = it; watch_list::iterator end = wlist.end(); - + model_converter::kind k; for (; it != end; ++it) { if (!it->is_binary_clause() || it->is_blocked()) { INC(); continue; } literal l2 = it->get_literal(); - if (cce(l, l2, blocked)) { - block_covered_binary(it, l, blocked); + if (cce(l, l2, blocked, k)) { + block_covered_binary(it, l, blocked, k); s.m_num_covered_clauses++; } else { @@ -1225,13 +1310,15 @@ namespace sat { } + template void cce_clauses() { m_to_remove.reset(); literal blocked; + model_converter::kind k; for (clause* cp : s.s.m_clauses) { clause& c = *cp; - if (!c.was_removed() && !c.is_blocked() && cce(c, blocked)) { - block_covered_clause(c, blocked); + if (!c.was_removed() && !c.is_blocked() && cce(c, blocked, k)) { + block_covered_clause(c, blocked, k); s.m_num_covered_clauses++; } } @@ -1242,10 +1329,10 @@ namespace sat { } - void prepare_block_clause(clause& c, literal l, model_converter::entry*& new_entry) { + void prepare_block_clause(clause& c, literal l, model_converter::entry*& new_entry, model_converter::kind k) { TRACE("blocked_clause", tout << "new blocked clause: " << c << "\n";); - if (new_entry == 0) - new_entry = &(mc.mk(model_converter::BLOCK_LIT, l.var())); + if (new_entry == 0 && !s.m_retain_blocked_clauses) + new_entry = &(mc.mk(k, l.var())); m_to_remove.push_back(&c); for (literal lit : c) { if (lit != l && process_var(lit.var())) { @@ -1255,19 +1342,21 @@ namespace sat { } void block_clause(clause& c, literal l, model_converter::entry *& new_entry) { - prepare_block_clause(c, l, new_entry); - mc.insert(*new_entry, c); + prepare_block_clause(c, l, new_entry, model_converter::BLOCK_LIT); + if (!s.m_retain_blocked_clauses) + mc.insert(*new_entry, c); } - void block_covered_clause(clause& c, literal l) { + void block_covered_clause(clause& c, literal l, model_converter::kind k) { model_converter::entry * new_entry = 0; - prepare_block_clause(c, l, new_entry); - mc.insert(*new_entry, m_covered_clause, m_elim_stack); + prepare_block_clause(c, l, new_entry, k); + if (!s.m_retain_blocked_clauses) + mc.insert(*new_entry, m_covered_clause, m_elim_stack); } - void prepare_block_binary(watch_list::iterator it, literal l1, literal blocked, model_converter::entry*& new_entry) { - if (new_entry == 0) - new_entry = &(mc.mk(model_converter::BLOCK_LIT, blocked.var())); + void prepare_block_binary(watch_list::iterator it, literal l1, literal blocked, model_converter::entry*& new_entry, model_converter::kind k) { + if (new_entry == 0 && !s.m_retain_blocked_clauses) + new_entry = &(mc.mk(k, blocked.var())); literal l2 = it->get_literal(); TRACE("blocked_clause", tout << "new blocked clause: " << l2 << " " << l1 << "\n";); if (s.m_retain_blocked_clauses && !it->is_learned()) { @@ -1281,14 +1370,16 @@ namespace sat { } void block_binary(watch_list::iterator it, literal l, model_converter::entry *& new_entry) { - prepare_block_binary(it, l, l, new_entry); - mc.insert(*new_entry, l, it->get_literal()); + prepare_block_binary(it, l, l, new_entry, model_converter::BLOCK_LIT); + if (!s.m_retain_blocked_clauses) + mc.insert(*new_entry, l, it->get_literal()); } - void block_covered_binary(watch_list::iterator it, literal l, literal blocked) { + void block_covered_binary(watch_list::iterator it, literal l, literal blocked, model_converter::kind k) { model_converter::entry * new_entry = 0; - prepare_block_binary(it, l, blocked, new_entry); - mc.insert(*new_entry, m_covered_clause, m_elim_stack); + prepare_block_binary(it, l, blocked, new_entry, k); + if (!s.m_retain_blocked_clauses) + mc.insert(*new_entry, m_covered_clause, m_elim_stack); } void bca() { @@ -1344,7 +1435,7 @@ namespace sat { clause_use_list & neg_occs = s.m_use_list.get(~l); clause_use_list::iterator it = neg_occs.mk_iterator(); - for (; !it.at_end(); it.next()) { + for (; !it.at_end(); it.next()) { clause & c = it.curr(); if (c.is_blocked()) continue; m_counter -= c.size(); @@ -1375,10 +1466,12 @@ namespace sat { stopwatch m_watch; unsigned m_num_blocked_clauses; unsigned m_num_covered_clauses; + unsigned m_num_added_clauses; blocked_cls_report(simplifier & s): m_simplifier(s), m_num_blocked_clauses(s.m_num_blocked_clauses), - m_num_covered_clauses(s.m_num_covered_clauses) { + m_num_covered_clauses(s.m_num_covered_clauses), + m_num_added_clauses(s.m_num_bca) { m_watch.start(); } @@ -1389,6 +1482,8 @@ namespace sat { << (m_simplifier.m_num_blocked_clauses - m_num_blocked_clauses) << " :elim-covered-clauses " << (m_simplifier.m_num_covered_clauses - m_num_covered_clauses) + << " :added-clauses " + << (m_simplifier.m_num_bca - m_num_added_clauses) << mem_stat() << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } @@ -1456,8 +1551,7 @@ namespace sat { */ void simplifier::collect_clauses(literal l, clause_wrapper_vector & r, bool include_blocked) { clause_use_list const & cs = m_use_list.get(l); - clause_use_list::iterator it = cs.mk_iterator(); - for (; !it.at_end(); it.next()) { + for (auto it = cs.mk_iterator(); !it.at_end(); it.next()) { if (!it.curr().is_blocked() || include_blocked) { r.push_back(clause_wrapper(it.curr())); SASSERT(r.back().size() == it.curr().size()); @@ -1639,7 +1733,6 @@ namespace sat { collect_clauses(pos_l, m_pos_cls, false); collect_clauses(neg_l, m_neg_cls, false); - TRACE("resolution_detail", tout << "collecting number of after_clauses\n";); unsigned before_clauses = num_pos + num_neg; unsigned after_clauses = 0; @@ -1658,7 +1751,7 @@ namespace sat { } } TRACE("resolution", tout << "found var to eliminate, before: " << before_clauses << " after: " << after_clauses << "\n";); - + m_elim_counter -= num_pos * num_neg + before_lits; m_elim_counter -= num_pos * num_neg + before_lits; @@ -1703,6 +1796,7 @@ namespace sat { else s.m_stats.m_mk_clause++; clause * new_c = s.m_cls_allocator.mk_clause(m_new_cls.size(), m_new_cls.c_ptr(), false); + if (s.m_config.m_drat) s.m_drat.add(*new_c, true); s.m_clauses.push_back(new_c); m_use_list.insert(*new_c); @@ -1769,6 +1863,7 @@ namespace sat { m_cce = p.cce(); m_acce = p.acce(); m_bca = p.bca(); + m_bce_delay = p.bce_delay(); m_elim_blocked_clauses = p.elim_blocked_clauses(); m_elim_blocked_clauses_at = p.elim_blocked_clauses_at(); m_retain_blocked_clauses = p.retain_blocked_clauses(); diff --git a/src/sat/sat_simplifier.h b/src/sat/sat_simplifier.h index c75e45d79..98cae5398 100644 --- a/src/sat/sat_simplifier.h +++ b/src/sat/sat_simplifier.h @@ -71,9 +71,11 @@ namespace sat { int m_elim_counter; // config + bool m_abce; // block clauses using asymmetric added literals bool m_cce; // covered clause elimination bool m_acce; // cce with asymetric literal addition bool m_bca; // blocked (binary) clause addition. + unsigned m_bce_delay; bool m_elim_blocked_clauses; unsigned m_elim_blocked_clauses_at; bool m_retain_blocked_clauses; @@ -169,12 +171,14 @@ namespace sat { struct blocked_clause_elim; void elim_blocked_clauses(); - bool bce_enabled() const { return m_elim_blocked_clauses || m_elim_blocked_clauses_at == m_num_calls || cce_enabled(); } - bool acce_enabled() const { return m_acce; } - bool cce_enabled() const { return m_cce || acce_enabled(); } - bool bca_enabled() const { return m_bca; } - bool elim_vars_bdd_enabled() const { return m_elim_vars_bdd && m_num_calls >= m_elim_vars_bdd_delay; } - bool elim_vars_enabled() const { return m_elim_vars; } + bool single_threaded() const; // { return s.m_config.m_num_threads == 1; } + bool bce_enabled() const; + bool acce_enabled() const; + bool cce_enabled() const; + bool abce_enabled() const; + bool bca_enabled() const; + bool elim_vars_bdd_enabled() const; + bool elim_vars_enabled() const; unsigned get_num_unblocked_bin(literal l) const; unsigned get_to_elim_cost(bool_var v) const; diff --git a/src/sat/sat_simplifier_params.pyg b/src/sat/sat_simplifier_params.pyg index 6528e6699..6dd00ec83 100644 --- a/src/sat/sat_simplifier_params.pyg +++ b/src/sat/sat_simplifier_params.pyg @@ -2,10 +2,12 @@ def_module_params(module_name='sat', class_name='sat_simplifier_params', export=True, params=(('elim_blocked_clauses', BOOL, False, 'eliminate blocked clauses'), + ('abce', BOOL, BOOL, False, 'eliminate blocked clauses using asymmmetric literals'), ('cce', BOOL, False, 'eliminate covered clauses'), ('acce', BOOL, False, 'eliminate covered clauses using asymmetric added literals'), ('elim_blocked_clauses_at', UINT, 2, 'eliminate blocked clauses only once at the given simplification round'), ('bca', BOOL, False, 'blocked clause addition - add blocked binary clauses'), + ('bce_delay', UINT, 0, 'delay eliminate blocked clauses until simplification round'), ('retain_blocked_clauses', BOOL, False, 'retain blocked clauses for propagation, hide them from variable elimination'), ('blocked_clause_limit', UINT, 100000000, 'maximum number of literals visited during blocked clause elimination'), ('resolution.limit', UINT, 500000000, 'approx. maximum number of literals visited during variable elimination'), @@ -20,6 +22,6 @@ def_module_params(module_name='sat', ('resolution.cls_cutoff2', UINT, 700000000, 'limit2 - total number of problems clauses for the second cutoff of Boolean variable elimination'), ('elim_vars', BOOL, True, 'enable variable elimination using resolution during simplification'), ('elim_vars_bdd', BOOL, True, 'enable variable elimination using BDD recompilation during simplification'), - ('elim_vars_bdd_delay', UINT, 3, 'delay elimination of variables using BDDs until after simplification round'), + ('elim_vars_bdd_delay', UINT, 3, 'delay elimination of variables using BDDs until after simplification round'), ('subsumption', BOOL, True, 'eliminate subsumed clauses'), ('subsumption.limit', UINT, 100000000, 'approx. maximum number of literals visited during subsumption (and subsumption resolution)'))) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 1f0015476..f5b04e646 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -23,14 +23,11 @@ Revision History: #include "util/luby.h" #include "util/trace.h" #include "util/max_cliques.h" +#include "util/gparams.h" // define to update glue during propagation #define UPDATE_GLUE -// define to create a copy of the solver before starting the search -// useful for checking models -// #define CLONE_BEFORE_SOLVING - namespace sat { solver::solver(params_ref const & p, reslimit& l): @@ -64,6 +61,7 @@ namespace sat { m_num_checkpoints = 0; m_simplifications = 0; m_cuber = nullptr; + m_mc.set_solver(nullptr); } solver::~solver() { @@ -734,15 +732,13 @@ namespace sat { case watched::CLAUSE: { if (value(it->get_blocked_literal()) == l_true) { TRACE("propagate_clause_bug", tout << "blocked literal " << it->get_blocked_literal() << "\n"; - clause_offset cls_off = it->get_clause_offset(); - clause & c = *(m_cls_allocator.get_clause(cls_off)); - tout << c << "\n";); + tout << get_clause(it) << "\n";); *it2 = *it; it2++; break; } clause_offset cls_off = it->get_clause_offset(); - clause & c = *(m_cls_allocator.get_clause(cls_off)); + clause & c = get_clause(cls_off); TRACE("propagate_clause_bug", tout << "processing... " << c << "\nwas_removed: " << c.was_removed() << "\n";); if (c[0] == not_l) std::swap(c[0], c[1]); @@ -867,7 +863,7 @@ namespace sat { SASSERT(at_base_lvl()); if (m_config.m_dimacs_display) { display_dimacs(std::cout); - for (unsigned i = 0; i < num_lits; ++lits) { + for (unsigned i = 0; i < num_lits; ++i) { std::cout << dimacs_lit(lits[i]) << " 0\n"; } return l_undef; @@ -875,9 +871,13 @@ namespace sat { if (m_config.m_lookahead_search && num_lits == 0) { return lookahead_search(); } +#if 0 + // deprecated if (m_config.m_lookahead_cube && num_lits == 0) { return lookahead_cube(); } +#endif + if (m_config.m_local_search) { return do_local_search(num_lits, lits); } @@ -886,12 +886,10 @@ namespace sat { return check_par(num_lits, lits); } flet _searching(m_searching, true); -#ifdef CLONE_BEFORE_SOLVING - if (m_mc.empty()) { - m_clone = alloc(solver, m_params); - SASSERT(m_clone); + if (m_mc.empty() && gparams::get().get_bool("model_validate", false)) { + m_clone = alloc(solver, m_params, m_rlimit); + m_clone->copy(*this); } -#endif try { init_search(); if (inconsistent()) return l_false; @@ -1422,6 +1420,7 @@ namespace sat { m_luby_idx = 1; m_gc_threshold = m_config.m_gc_initial; m_restarts = 0; + m_simplifications = 0; m_conflicts_since_init = 0; m_min_d_tk = 1.0; m_search_lvl = 0; @@ -1548,30 +1547,40 @@ namespace sat { m_model[v] = value(v); } TRACE("sat_mc_bug", m_mc.display(tout);); - m_mc(m_model); - TRACE("sat", for (bool_var v = 0; v < num; v++) tout << v << ": " << m_model[v] << "\n";); -// #ifndef _EXTERNAL_RELEASE IF_VERBOSE(10, verbose_stream() << "\"checking model\"\n";); - if (!check_model(m_model)) { + if (!check_clauses(m_model)) { UNREACHABLE(); throw solver_exception("check model failed"); } + + if (m_config.m_drat) m_drat.check_model(m_model); + + m_mc.set_solver(nullptr); + m_mc(m_model); + + + if (!check_clauses(m_model)) { + std::cout << "failure checking clauses on transformed model\n"; + UNREACHABLE(); + throw solver_exception("check model failed"); + } + + TRACE("sat", for (bool_var v = 0; v < num; v++) tout << v << ": " << m_model[v] << "\n";); if (m_clone) { IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "\"checking model (on original set of clauses)\"\n";); if (!m_clone->check_model(m_model)) throw solver_exception("check model failed (for cloned solver)"); } -// #endif } - bool solver::check_model(model const & m) const { + bool solver::check_clauses(model const& m) const { bool ok = true; for (clause const* cp : m_clauses) { clause const & c = *cp; if (!c.satisfied_by(m) && !c.is_blocked()) { - IF_VERBOSE(0, verbose_stream() << "model check failed: " << c << "\n";); + IF_VERBOSE(0, verbose_stream() << "failed clause " << c.id() << ": " << c << "\n";); TRACE("sat", tout << "failed: " << c << "\n"; tout << "assumptions: " << m_assumptions << "\n"; tout << "trail: " << m_trail << "\n"; @@ -1612,10 +1621,16 @@ namespace sat { ok = false; } } + return ok; + } + + bool solver::check_model(model const & m) const { + bool ok = check_clauses(m); if (ok && !m_mc.check_model(m)) { ok = false; TRACE("sat", tout << "model: " << m << "\n"; m_mc.display(tout);); } + IF_VERBOSE(1, verbose_stream() << "model check " << (ok?"OK":"failed") << "\n";); return ok; } @@ -2034,7 +2049,7 @@ namespace sat { process_antecedent(~(js.get_literal2()), num_marks); break; case justification::CLAUSE: { - clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); + clause & c = get_clause(js); unsigned i = 0; if (consequent != null_literal) { SASSERT(c[0] == consequent || c[1] == consequent); @@ -2153,7 +2168,7 @@ namespace sat { process_antecedent_for_unsat_core(~(js.get_literal2())); break; case justification::CLAUSE: { - clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); + clause & c = get_clause(js); unsigned i = 0; if (consequent != null_literal) { SASSERT(c[0] == consequent || c[1] == consequent); @@ -2497,7 +2512,7 @@ namespace sat { } break; case justification::CLAUSE: { - clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); + clause & c = get_clause(js); unsigned i = 0; if (c[0].var() == var) { i = 1; @@ -2624,8 +2639,7 @@ namespace sat { unsigned sz = m_lemma.size(); SASSERT(!is_marked(m_lemma[0].var())); mark(m_lemma[0].var()); - for (unsigned i = m_lemma.size(); i > 0; ) { - --i; + for (unsigned i = m_lemma.size(); i-- > 0; ) { justification js = m_justification[m_lemma[i].var()]; switch (js.get_kind()) { case justification::NONE: @@ -2638,9 +2652,9 @@ namespace sat { update_lrb_reasoned(js.get_literal2()); break; case justification::CLAUSE: { - clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); - for (unsigned i = 0; i < c.size(); ++i) { - update_lrb_reasoned(c[i]); + clause & c = get_clause(js); + for (literal l : c) { + update_lrb_reasoned(l); } break; } @@ -3045,6 +3059,7 @@ namespace sat { m_scc.updt_params(p); m_rand.set_seed(m_config.m_random_seed); m_step_size = m_config.m_step_size_init; + m_drat.updt_config(); } void solver::collect_param_descrs(param_descrs & d) { @@ -3199,7 +3214,7 @@ namespace sat { std::ostream& solver::display_justification(std::ostream & out, justification const& js) const { out << js; if (js.is_clause()) { - out << *(m_cls_allocator.get_clause(js.get_clause_offset())); + out << get_clause(js); } else if (js.is_ext_justification() && m_ext) { m_ext->display_justification(out << " ", js.get_ext_justification_idx()); @@ -3845,11 +3860,11 @@ namespace sat { s |= m_antecedents.find(js.get_literal2().var()); break; case justification::CLAUSE: { - clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); - for (unsigned i = 0; i < c.size(); ++i) { - if (c[i] != lit) { - if (check_domain(lit, ~c[i]) && all_found) { - s |= m_antecedents.find(c[i].var()); + clause & c = get_clause(js); + for (literal l : c) { + if (l != lit) { + if (check_domain(lit, ~l) && all_found) { + s |= m_antecedents.find(l.var()); } else { all_found = false; diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 2c5a04104..e7dae93a2 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -366,6 +366,7 @@ namespace sat { model_converter const & get_model_converter() const { return m_mc; } void set_model(model const& mdl); char const* get_reason_unknown() const { return m_reason_unknown.c_str(); } + bool check_clauses(model const& m) const; literal select_lookahead(literal_vector const& assumptions, bool_var_vector const& vars); lbool cube(bool_var_vector const& vars, literal_vector& lits); @@ -442,6 +443,20 @@ namespace sat { justification const & jst = m_justification[l0.var()]; return !jst.is_clause() || m_cls_allocator.get_clause(jst.get_clause_offset()) != &c; } + + clause& get_clause(watch_list::iterator it) const { + SASSERT(it->get_kind() == watched::CLAUSE); + return get_clause(it->get_clause_offset()); + } + + clause& get_clause(justification const& j) const { + SASSERT(j.is_clause()); + return get_clause(j.get_clause_offset()); + } + + clause& get_clause(clause_offset cls_off) const { + return *(m_cls_allocator.get_clause(cls_off)); + } // ----------------------- // diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 742f9e103..3f97025e9 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -61,6 +61,7 @@ class inc_sat_solver : public solver { proof_converter_ref m_pc; model_converter_ref m_mc; model_converter_ref m_mc0; + model_converter_ref m_sat_mc; expr_dependency_ref m_dep_core; svector m_weights; std::string m_unknown; @@ -76,7 +77,6 @@ public: inc_sat_solver(ast_manager& m, params_ref const& p, bool incremental_mode): m(m), m_solver(p, m.limit()), - m_params(p), m_fmls(m), m_asmsf(m), m_fmls_head(0), @@ -114,6 +114,7 @@ public: if (m_mc0.get()) result->m_mc0 = m_mc0->translate(tr); result->m_internalized = m_internalized; result->m_internalized_converted = m_internalized_converted; + if (mc0()) result->set_model_converter(mc0()->translate(tr)); return result; } @@ -180,7 +181,13 @@ public: m_internalized_converted = false; init_reason_unknown(); - r = m_solver.check(m_asms.size(), m_asms.c_ptr()); + try { + r = m_solver.check(m_asms.size(), m_asms.c_ptr()); + } + catch (z3_exception& ex) { + IF_VERBOSE(10, verbose_stream() << "exception: " << ex.msg() << "\n";); + r = l_undef; + } if (r == l_undef && m_solver.get_config().m_dimacs_display) { for (auto const& kv : m_map) { std::cout << "c " << kv.m_value << " " << mk_pp(kv.m_key, m) << "\n"; @@ -293,7 +300,7 @@ public: r.reset(); r.append(m_core.size(), m_core.c_ptr()); } - virtual void get_model(model_ref & mdl) { + virtual void get_model_core(model_ref & mdl) { if (!m_model.get()) { extract_model(); } @@ -304,51 +311,6 @@ public: return 0; } - virtual expr_ref lookahead(expr_ref_vector const& assumptions, expr_ref_vector const& candidates) { - IF_VERBOSE(1, verbose_stream() << "(sat-lookahead " << candidates.size() << ")\n";); - sat::bool_var_vector vars; - sat::literal_vector lits; - expr_ref_vector lit2expr(m); - lit2expr.resize(m_solver.num_vars() * 2); - m_map.mk_inv(lit2expr); - for (auto c : candidates) { - sat::bool_var v = m_map.to_bool_var(c); - if (v != sat::null_bool_var) { - vars.push_back(v); - } - } - for (auto c : assumptions) { - SASSERT(is_literal(c)); - sat::bool_var v = sat::null_bool_var; - bool sign = false; - expr* e = c; - while (m.is_not(e, e)) { - sign = !sign; - } - if (is_uninterp_const(e)) { - v = m_map.to_bool_var(e); - } - if (v != sat::null_bool_var) { - lits.push_back(sat::literal(v, sign)); - } - else { - IF_VERBOSE(0, verbose_stream() << "WARNING: could not handle " << mk_pp(c, m) << "\n";); - } - } - if (vars.empty()) { - return expr_ref(m.mk_true(), m); - } - sat::literal l = m_solver.select_lookahead(lits, vars); - if (m_solver.inconsistent()) { - IF_VERBOSE(1, verbose_stream() << "(sat-lookahead inconsistent)\n";); - return expr_ref(m.mk_false(), m); - } - if (l == sat::null_literal) { - return expr_ref(m.mk_true(), m); - } - expr_ref result(lit2expr[l.index()].get(), m); - return result; - } virtual expr_ref cube() { if (!m_internalized) { dep2asm_t dep2asm; @@ -380,16 +342,6 @@ public: return mk_and(fmls); } - virtual void get_lemmas(expr_ref_vector & lemmas) { - if (!m_internalized) return; - sat2goal s2g; - goal g(m, false, false, false); - s2g.get_learned(m_solver, m_map, m_params, lemmas); - IF_VERBOSE(1, verbose_stream() << "(sat :lemmas " << lemmas.size() << ")\n";); - // TBD: handle externals properly. - } - - virtual lbool get_consequences_core(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq) { init_preprocess(); TRACE("sat", tout << assumptions << "\n" << vars << "\n";); @@ -491,29 +443,28 @@ public: return m_asmsf[idx]; } + virtual model_converter_ref get_model_converter() const { + const_cast(this)->convert_internalized(); + if (m_internalized && m_internalized_converted) { + model_converter_ref mc = concat(m_mc0.get(), mk_bit_blaster_model_converter(m, m_bb_rewriter->const2bits())); + mc = concat(solver::get_model_converter().get(), mc.get()); + mc = concat(mc.get(), m_sat_mc.get()); + return mc; + } + else { + return solver::get_model_converter(); + } + } + void convert_internalized() { - if (!m_internalized) return; + if (!m_internalized || m_internalized_converted) return; sat2goal s2g; - model_converter_ref mc; - goal g(m, false, false, false); - s2g(m_solver, m_map, m_params, g, mc); - extract_model(); - if (!m_model) { - m_model = alloc(model, m); - } - model_ref mdl = m_model; - if (m_mc) (*m_mc)(mdl); - for (unsigned i = 0; i < mdl->get_num_constants(); ++i) { - func_decl* c = mdl->get_constant(i); - expr_ref eq(m.mk_eq(m.mk_const(c), mdl->get_const_interp(c)), m); - g.assert_expr(eq); - } + m_sat_mc = nullptr; + goal g(m, false, true, false); + s2g(m_solver, m_map, m_params, g, m_sat_mc); m_internalized_fmls.reset(); g.get_formulas(m_internalized_fmls); m_internalized_converted = true; - // if (mc) mc->display(std::cout << "mc"); - // if (m_mc) m_mc->display(std::cout << "m_mc\n"); - // if (m_mc0) m_mc0->display(std::cout << "m_mc0\n"); } void init_preprocess() { diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 208d60386..c70077dc5 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -52,7 +52,7 @@ struct goal2sat::imp { }; ast_manager & m; pb_util pb; - sat::ba_solver* m_ext; + sat::ba_solver* m_ext; svector m_frame_stack; svector m_result_stack; obj_map m_cache; @@ -882,10 +882,6 @@ struct sat2goal::imp { // Wrapper for sat::model_converter: converts it into an "AST level" model_converter. class sat_model_converter : public model_converter { sat::model_converter m_mc; - // TODO: the following mapping is storing a lot of useless information, and may be a performance bottleneck. - // We need to save only the expressions associated with variables that occur in m_mc. - // This information may be stored as a vector of pairs. - // The mapping is only created during the model conversion. expr_ref_vector m_var2expr; filter_model_converter_ref m_fmc; // filter for eliminating fresh variables introduced in the assertion-set --> sat conversion @@ -900,15 +896,30 @@ struct sat2goal::imp { } ast_manager & m() { return m_var2expr.get_manager(); } - - void insert(expr * atom, bool aux) { - m_var2expr.push_back(atom); + + void init(unsigned num_vars) { + m_var2expr.resize(num_vars); + } + + void insert(unsigned v, expr * atom, bool aux) { + VERIFY(!m_var2expr.get(v)); + m_var2expr[v] = atom; if (aux) { SASSERT(is_uninterp_const(atom)); SASSERT(m().is_bool(atom)); m_fmc->insert(to_app(atom)->get_decl()); } } + + void finish() { + sat::literal_vector updates; + m_mc.expand(updates); + for (sat::literal l : updates) { + if (l != sat::null_literal && !m_var2expr.get(l.var())) { + insert(l.var(), m().mk_fresh_const(0, m().mk_bool_sort()), true); + } + } + } virtual void operator()(model_ref & md, unsigned goal_idx) { SASSERT(goal_idx == 0); @@ -928,6 +939,10 @@ struct sat2goal::imp { sat::model sat_md; expr_ref val(m()); for (expr * atom : m_var2expr) { + if (!atom) { + sat_md.push_back(l_undef); + continue; + } ev(atom, val); if (m().is_true(val)) sat_md.push_back(l_true); @@ -944,7 +959,7 @@ struct sat2goal::imp { unsigned sz = m_var2expr.size(); for (sat::bool_var v = 0; v < sz; v++) { expr * atom = m_var2expr.get(v); - if (is_uninterp_const(atom)) { + if (atom && is_uninterp_const(atom)) { func_decl * d = to_app(atom)->get_decl(); lbool new_val = sat_md[v]; if (new_val == l_true) @@ -963,28 +978,57 @@ struct sat2goal::imp { sat_model_converter * res = alloc(sat_model_converter, translator.to()); res->m_fmc = static_cast(m_fmc->translate(translator)); for (expr* e : m_var2expr) - res->m_var2expr.push_back(translator(e)); + res->m_var2expr.push_back(e ? translator(e) : nullptr); return res; } + expr_ref lit2expr(sat::literal l) { + VERIFY(m_var2expr.get(l.var())); + expr_ref result(m_var2expr.get(l.var()), m()); + if (l.sign()) { + result = m().mk_not(result); + } + return result; + } + void display(std::ostream & out) { - out << "(sat-model-converter\n"; - m_mc.display(out); - sat::bool_var_set vars; - m_mc.collect_vars(vars); - out << "(atoms"; - unsigned sz = m_var2expr.size(); - for (unsigned i = 0; i < sz; i++) { - if (vars.contains(i)) { - out << "\n (" << i << "\n " << mk_ismt2_pp(m_var2expr.get(i), m(), 2) << ")"; + sat::literal_vector updates; + m_mc.expand(updates); + sat::literal_vector clause; + expr_ref_vector tail(m()); + expr_ref def(m()); + for (sat::literal l : updates) { + if (l == sat::null_literal) { + sat::literal lit0 = clause[0]; + for (unsigned i = 1; i < clause.size(); ++i) { + tail.push_back(lit2expr(~clause[i])); + } + def = m().mk_or(lit2expr(lit0), mk_and(tail)); + if (lit0.sign()) { + lit0.neg(); + def = m().mk_not(def); + } + display_add(out, m(), to_app(lit2expr(lit0))->get_decl(), def); + clause.reset(); + tail.reset(); + } + else { + clause.push_back(l); } } - out << ")\n"; m_fmc->display(out); - out << ")\n"; } + + virtual void collect(ast_pp_util& visitor) { + m_env = &visitor.env(); + for (expr* e : m_var2expr) if (e) visitor.coll.visit(e); + if (m_fmc) m_fmc->collect(visitor); + } + }; + typedef ref sat_model_converter_ref; + ast_manager & m; expr_ref_vector m_lit2expr; unsigned long long m_max_memory; @@ -1008,83 +1052,80 @@ struct sat2goal::imp { throw tactic_exception(TACTIC_MAX_MEMORY_MSG); } - void init_lit2expr(sat::solver const & s, atom2bool_var const & map, model_converter_ref & mc, bool produce_models) { - ref _mc; - if (produce_models) - _mc = alloc(sat_model_converter, m, s); + void init_lit2expr(sat::solver const & s, atom2bool_var const & map, sat_model_converter_ref & mc) { unsigned num_vars = s.num_vars(); m_lit2expr.resize(num_vars * 2); map.mk_inv(m_lit2expr); - sort * b = m.mk_bool_sort(); - for (sat::bool_var v = 0; v < num_vars; v++) { - checkpoint(); - sat::literal l(v, false); - if (m_lit2expr.get(l.index()) == 0) { - SASSERT(m_lit2expr.get((~l).index()) == 0); - app * aux = m.mk_fresh_const(0, b); - if (_mc) - _mc->insert(aux, true); - m_lit2expr.set(l.index(), aux); - m_lit2expr.set((~l).index(), m.mk_not(aux)); - } - else { - if (_mc) - _mc->insert(m_lit2expr.get(l.index()), false); - SASSERT(m_lit2expr.get((~l).index()) != 0); + if (mc) { + mc->init(num_vars); + for (sat::bool_var v = 0; v < num_vars; v++) { + checkpoint(); + sat::literal l(v, false); + if (m_lit2expr.get(l.index())) { + mc->insert(v, m_lit2expr.get(l.index()), false); + SASSERT(m_lit2expr.get((~l).index())); + } } } - mc = _mc.get(); } - expr * lit2expr(sat::literal l) { + expr * lit2expr(sat_model_converter_ref& mc, sat::literal l) { + if (!m_lit2expr.get(l.index())) { + SASSERT(m_lit2expr.get((~l).index()) == 0); + app * aux = m.mk_fresh_const(0, m.mk_bool_sort()); + if (mc) + mc->insert(l.var(), aux, true); + m_lit2expr.set(l.index(), aux); + m_lit2expr.set((~l).index(), m.mk_not(aux)); + } return m_lit2expr.get(l.index()); } - void assert_pb(goal& r, sat::ba_solver::pb const& p) { + void assert_pb(sat_model_converter_ref& mc, goal& r, sat::ba_solver::pb const& p) { pb_util pb(m); ptr_buffer lits; vector coeffs; for (auto const& wl : p) { - lits.push_back(lit2expr(wl.second)); + lits.push_back(lit2expr(mc, wl.second)); coeffs.push_back(rational(wl.first)); } rational k(p.k()); expr_ref fml(pb.mk_ge(p.size(), coeffs.c_ptr(), lits.c_ptr(), k), m); if (p.lit() != sat::null_literal) { - fml = m.mk_eq(lit2expr(p.lit()), fml); + fml = m.mk_eq(lit2expr(mc, p.lit()), fml); } r.assert_expr(fml); } - void assert_card(goal& r, sat::ba_solver::card const& c) { + void assert_card(sat_model_converter_ref& mc, goal& r, sat::ba_solver::card const& c) { pb_util pb(m); ptr_buffer lits; for (sat::literal l : c) { - lits.push_back(lit2expr(l)); + lits.push_back(lit2expr(mc, l)); } expr_ref fml(pb.mk_at_least_k(c.size(), lits.c_ptr(), c.k()), m); if (c.lit() != sat::null_literal) { - fml = m.mk_eq(lit2expr(c.lit()), fml); + fml = m.mk_eq(lit2expr(mc, c.lit()), fml); } r.assert_expr(fml); } - void assert_xor(goal & r, sat::ba_solver::xor const& x) { + void assert_xor(sat_model_converter_ref& mc, goal & r, sat::ba_solver::xor const& x) { ptr_buffer lits; for (sat::literal l : x) { - lits.push_back(lit2expr(l)); + lits.push_back(lit2expr(mc, l)); } expr_ref fml(m.mk_xor(x.size(), lits.c_ptr()), m); if (x.lit() != sat::null_literal) { - fml = m.mk_eq(lit2expr(x.lit()), fml); + fml = m.mk_eq(lit2expr(mc, x.lit()), fml); } r.assert_expr(fml); } - void assert_clauses(sat::solver const & s, sat::clause_vector const& clauses, goal & r, bool asserted) { + void assert_clauses(sat_model_converter_ref& mc, sat::solver const & s, sat::clause_vector const& clauses, goal & r, bool asserted) { ptr_buffer lits; for (sat::clause* cp : clauses) { checkpoint(); @@ -1093,7 +1134,7 @@ struct sat2goal::imp { unsigned sz = c.size(); if (asserted || m_learned || c.glue() <= s.get_config().m_gc_small_lbd) { for (sat::literal l : c) { - lits.push_back(lit2expr(l)); + lits.push_back(lit2expr(mc, l)); } r.assert_expr(m.mk_or(lits.size(), lits.c_ptr())); } @@ -1110,17 +1151,22 @@ struct sat2goal::imp { r.assert_expr(m.mk_false()); return; } - init_lit2expr(s, map, mc, r.models_enabled()); + ref _mc; + if (r.models_enabled()) { + _mc = alloc(sat_model_converter, m, s); + } + mc = _mc.get(); + init_lit2expr(s, map, _mc); // collect units unsigned num_vars = s.num_vars(); for (sat::bool_var v = 0; v < num_vars; v++) { checkpoint(); switch (s.value(v)) { case l_true: - r.assert_expr(lit2expr(sat::literal(v, false))); + r.assert_expr(lit2expr(_mc, sat::literal(v, false))); break; case l_false: - r.assert_expr(lit2expr(sat::literal(v, true))); + r.assert_expr(lit2expr(_mc, sat::literal(v, true))); break; case l_undef: break; @@ -1131,96 +1177,50 @@ struct sat2goal::imp { s.collect_bin_clauses(bin_clauses, m_learned); for (sat::solver::bin_clause const& bc : bin_clauses) { checkpoint(); - r.assert_expr(m.mk_or(lit2expr(bc.first), lit2expr(bc.second))); + r.assert_expr(m.mk_or(lit2expr(_mc, bc.first), lit2expr(_mc, bc.second))); } // collect clauses - assert_clauses(s, s.clauses(), r, true); + assert_clauses(_mc, s, s.clauses(), r, true); sat::ba_solver* ext = get_ba_solver(s); if (ext) { for (auto* c : ext->constraints()) { switch (c->tag()) { case sat::ba_solver::card_t: - assert_card(r, c->to_card()); + assert_card(_mc, r, c->to_card()); break; case sat::ba_solver::pb_t: - assert_pb(r, c->to_pb()); + assert_pb(_mc, r, c->to_pb()); break; case sat::ba_solver::xor_t: - assert_xor(r, c->to_xor()); + assert_xor(_mc, r, c->to_xor()); break; } } } - //s.display(std::cout); - //r.display(std::cout); + if (_mc) _mc->finish(); } - void add_clause(sat::literal_vector const& lits, expr_ref_vector& lemmas) { + void add_clause(sat_model_converter_ref& mc, sat::literal_vector const& lits, expr_ref_vector& lemmas) { expr_ref_vector lemma(m); for (sat::literal l : lits) { - expr* e = m_lit2expr.get(l.index(), 0); + expr* e = lit2expr(mc, l); if (!e) return; lemma.push_back(e); } lemmas.push_back(mk_or(lemma)); } - void add_clause(sat::clause const& c, expr_ref_vector& lemmas) { + void add_clause(sat_model_converter_ref& mc, sat::clause const& c, expr_ref_vector& lemmas) { expr_ref_vector lemma(m); for (sat::literal l : c) { - expr* e = m_lit2expr.get(l.index(), 0); + expr* e = lit2expr(mc, l); if (!e) return; lemma.push_back(e); } lemmas.push_back(mk_or(lemma)); } - void get_learned(sat::solver const& s, atom2bool_var const& map, expr_ref_vector& lemmas) { - if (s.inconsistent()) { - lemmas.push_back(m.mk_false()); - return; - } - - unsigned num_vars = s.num_vars(); - m_lit2expr.resize(num_vars * 2); - map.mk_inv(m_lit2expr); - - sat::literal_vector lits; - // collect units - for (sat::bool_var v = 0; v < num_vars; v++) { - checkpoint(); - lits.reset(); - switch (s.value(v)) { - case l_true: - lits.push_back(sat::literal(v, false)); - add_clause(lits, lemmas); - break; - case l_false: - lits.push_back(sat::literal(v, false)); - add_clause(lits, lemmas); - break; - case l_undef: - break; - } - } - // collect learned binary clauses - svector bin_clauses; - s.collect_bin_clauses(bin_clauses, true, true); - for (sat::solver::bin_clause const& bc : bin_clauses) { - checkpoint(); - lits.reset(); - lits.push_back(bc.first); - lits.push_back(bc.second); - add_clause(lits, lemmas); - } - // collect clauses - for (sat::clause const* c : s.learned()) { - add_clause(*c, lemmas); - } - } - - }; sat2goal::sat2goal():m_imp(0) { @@ -1249,9 +1249,4 @@ void sat2goal::operator()(sat::solver const & t, atom2bool_var const & m, params proc(t, m, g, mc); } -void sat2goal::get_learned(sat::solver const & t, atom2bool_var const & m, params_ref const& p, expr_ref_vector& lemmas) { - imp proc(lemmas.get_manager(), p); - scoped_set_imp set(this, &proc); - proc.get_learned(t, m, lemmas); -} diff --git a/src/smt/CMakeLists.txt b/src/smt/CMakeLists.txt index 3db66eb3e..fb1997fb4 100644 --- a/src/smt/CMakeLists.txt +++ b/src/smt/CMakeLists.txt @@ -76,7 +76,7 @@ z3_add_component(smt normal_forms parser_util pattern - proof_checker + proofs proto_model simplex substitution diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp index 55b79ae66..8d2f6f2b3 100644 --- a/src/smt/asserted_formulas.cpp +++ b/src/smt/asserted_formulas.cpp @@ -38,7 +38,7 @@ asserted_formulas::asserted_formulas(ast_manager & m, smt_params & p): m_qhead(0), m_macro_manager(m), m_bv_sharing(m), - m_inconsistent(false), + m_inconsistent(false), m_has_quantifiers(false), m_reduce_asserted_formulas(*this), m_distribute_forall(*this), @@ -54,8 +54,8 @@ asserted_formulas::asserted_formulas(ast_manager & m, smt_params & p): m_lift_ite(*this), m_ng_lift_ite(*this), m_find_macros(*this), - m_propagate_values(*this), - m_nnf_cnf(*this), + m_propagate_values(*this), + m_nnf_cnf(*this), m_apply_quasi_macros(*this) { m_macro_finder = alloc(macro_finder, m, m_macro_manager); @@ -68,7 +68,7 @@ asserted_formulas::asserted_formulas(ast_manager & m, smt_params & p): void asserted_formulas::setup() { switch (m_params.m_lift_ite) { case LI_FULL: - m_params.m_ng_lift_ite = LI_NONE; + m_params.m_ng_lift_ite = LI_NONE; break; case LI_CONSERVATIVE: if (m_params.m_ng_lift_ite == LI_CONSERVATIVE) @@ -77,7 +77,7 @@ void asserted_formulas::setup() { default: break; } - + if (m_params.m_relevancy_lvl == 0) m_params.m_relevancy_lemma = false; } @@ -93,7 +93,7 @@ void asserted_formulas::push_assertion(expr * e, proof * pr, vectorget_num_args(); ++i) { expr* arg = to_app(e)->get_arg(i); - proof_ref _pr(m.mk_and_elim(pr, i), m); + proof_ref _pr(m.proofs_enabled() ? m.mk_and_elim(pr, i) : 0, m); push_assertion(arg, _pr, result); } } else if (m.is_not(e, e1) && m.is_or(e1)) { for (unsigned i = 0; i < to_app(e1)->get_num_args(); ++i) { expr* arg = to_app(e1)->get_arg(i); - proof_ref _pr(m.mk_not_or_elim(pr, i), m); + proof_ref _pr(m.proofs_enabled() ? m.mk_not_or_elim(pr, i) : 0, m); expr_ref narg(mk_not(m, arg), m); - push_assertion(narg, _pr, result); - } + push_assertion(narg, _pr, result); + } } else { result.push_back(justified_expr(m, e, pr)); @@ -130,6 +130,7 @@ void asserted_formulas::set_eliminate_and(bool flag) { p.set_bool("eq2ineq", m_params.m_arith_eq2ineq); p.set_bool("gcd_rounding", true); p.set_bool("expand_select_store", true); + p.set_bool("bv_sort_ac", true); m_rewriter.updt_params(p); flush_cache(); } @@ -139,11 +140,9 @@ void asserted_formulas::assert_expr(expr * e, proof * _in_pr) { proof_ref in_pr(_in_pr, m), pr(_in_pr, m); expr_ref r(e, m); - if (inconsistent()) + if (inconsistent()) return; - m_has_quantifiers |= ::has_quantifiers(e); - if (m_params.m_preprocess) { TRACE("assert_expr_bug", tout << r << "\n";); set_eliminate_and(false); // do not eliminate and before nnf. @@ -156,12 +155,15 @@ void asserted_formulas::assert_expr(expr * e, proof * _in_pr) { } TRACE("assert_expr_bug", tout << "after...\n" << r << "\n";); } + + m_has_quantifiers |= ::has_quantifiers(e); + push_assertion(r, pr, m_formulas); TRACE("asserted_formulas_bug", tout << "after assert_expr\n"; display(tout);); } void asserted_formulas::assert_expr(expr * e) { - assert_expr(e, m.mk_asserted(e)); + assert_expr(e, m.proofs_enabled() ? m.mk_asserted(e) : nullptr); } void asserted_formulas::get_assertions(ptr_vector & result) const { @@ -183,7 +185,7 @@ void asserted_formulas::push_scope() { commit(); TRACE("asserted_formulas_scopes", tout << "after push: " << m_scopes.size() << "\n";); } - + void asserted_formulas::pop_scope(unsigned num_scopes) { TRACE("asserted_formulas_scopes", tout << "before pop " << num_scopes << " of " << m_scopes.size() << "\n";); m_bv_sharing.pop_scope(num_scopes); @@ -212,7 +214,7 @@ void asserted_formulas::reset() { bool asserted_formulas::check_well_sorted() const { for (justified_expr const& je : m_formulas) { - if (!is_well_sorted(m, je.get_fml())) return false; + if (!is_well_sorted(m, je.get_fml())) return false; } return true; } @@ -224,20 +226,20 @@ void asserted_formulas::reduce() { return; if (m_qhead == m_formulas.size()) return; - if (!m_params.m_preprocess) - return; - if (m_macro_manager.has_macros()) + if (!m_params.m_preprocess) + return; + if (m_macro_manager.has_macros()) invoke(m_find_macros); - + TRACE("before_reduce", display(tout);); CASSERT("well_sorted", check_well_sorted()); - + set_eliminate_and(false); // do not eliminate and before nnf. if (!invoke(m_propagate_values)) return; if (!invoke(m_find_macros)) return; if (!invoke(m_nnf_cnf)) return; set_eliminate_and(true); - if (!invoke(m_reduce_asserted_formulas)) return; + if (!invoke(m_reduce_asserted_formulas)) return; if (!invoke(m_pull_cheap_ite_trees)) return; if (!invoke(m_pull_nested_quantifiers)) return; if (!invoke(m_lift_ite)) return; @@ -276,14 +278,14 @@ bool asserted_formulas::invoke(simplify_fmls& s) { if (!s.should_apply()) return true; IF_VERBOSE(10, verbose_stream() << "(smt." << s.id() << ")\n";); s(); - IF_VERBOSE(10000, verbose_stream() << "total size: " << get_total_size() << "\n";); - TRACE("reduce_step_ll", ast_mark visited; display_ll(tout, visited);); - CASSERT("well_sorted",check_well_sorted()); - if (inconsistent() || canceled()) { - TRACE("after_reduce", display(tout);); - TRACE("after_reduce_ll", ast_mark visited; display_ll(tout, visited);); + IF_VERBOSE(10000, verbose_stream() << "total size: " << get_total_size() << "\n";); + TRACE("reduce_step_ll", ast_mark visited; display_ll(tout, visited);); + CASSERT("well_sorted",check_well_sorted()); + if (inconsistent() || canceled()) { + TRACE("after_reduce", display(tout);); + TRACE("after_reduce_ll", ast_mark visited; display_ll(tout, visited);); return false; - } + } else { return true; } @@ -301,10 +303,10 @@ void asserted_formulas::display(std::ostream & out) const { void asserted_formulas::display_ll(std::ostream & out, ast_mark & pp_visited) const { if (!m_formulas.empty()) { - for (justified_expr const& f : m_formulas) + for (justified_expr const& f : m_formulas) ast_def_ll_pp(out, m, f.get_fml(), pp_visited, true, false); out << "asserted formulas:\n"; - for (justified_expr const& f : m_formulas) + for (justified_expr const& f : m_formulas) out << "#" << f.get_fml()->get_id() << " "; out << "\n"; } @@ -333,9 +335,9 @@ void asserted_formulas::find_macros_core() { void asserted_formulas::apply_quasi_macros() { TRACE("before_quasi_macros", display(tout);); vector new_fmls; - quasi_macros proc(m, m_macro_manager); - while (proc(m_formulas.size() - m_qhead, - m_formulas.c_ptr() + m_qhead, + quasi_macros proc(m, m_macro_manager); + while (proc(m_formulas.size() - m_qhead, + m_formulas.c_ptr() + m_qhead, new_fmls)) { swap_asserted_formulas(new_fmls); new_fmls.reset(); @@ -349,7 +351,7 @@ void asserted_formulas::nnf_cnf() { vector new_fmls; expr_ref_vector push_todo(m); proof_ref_vector push_todo_prs(m); - + unsigned i = m_qhead; unsigned sz = m_formulas.size(); TRACE("nnf_bug", tout << "i: " << i << " sz: " << sz << "\n";); @@ -364,7 +366,7 @@ void asserted_formulas::nnf_cnf() { CASSERT("well_sorted", is_well_sorted(m, n)); apply_nnf(n, push_todo, push_todo_prs, r1, pr1); CASSERT("well_sorted",is_well_sorted(m, r1)); - pr = m.mk_modus_ponens(pr, pr1); + pr = m.proofs_enabled() ? m.mk_modus_ponens(pr, pr1) : nullptr; push_todo.push_back(r1); push_todo_prs.push_back(pr); @@ -379,7 +381,7 @@ void asserted_formulas::nnf_cnf() { CASSERT("well_sorted",is_well_sorted(m, r1)); if (canceled()) { return; - } + } if (m.proofs_enabled()) pr = m.mk_modus_ponens(push_todo_prs.get(k), pr1); push_assertion(r1, pr, new_fmls); @@ -390,8 +392,8 @@ void asserted_formulas::nnf_cnf() { void asserted_formulas::simplify_fmls::operator()() { vector new_fmls; - unsigned sz = af.m_formulas.size(); - for (unsigned i = af.m_qhead; i < sz; i++) { + unsigned sz = af.m_formulas.size(); + for (unsigned i = af.m_qhead; i < sz; i++) { auto& j = af.m_formulas[i]; expr_ref result(m); proof_ref result_pr(m); @@ -407,8 +409,8 @@ void asserted_formulas::simplify_fmls::operator()() { af.push_assertion(result, result_pr, new_fmls); } if (af.canceled()) return; - } - af.swap_asserted_formulas(new_fmls); + } + af.swap_asserted_formulas(new_fmls); TRACE("asserted_formulas", af.display(tout);); post_op(); } @@ -474,12 +476,12 @@ void asserted_formulas::propagate_values() { unsigned asserted_formulas::propagate_values(unsigned i) { expr_ref n(m_formulas[i].get_fml(), m); - expr_ref new_n(m); - proof_ref new_pr(m); - m_rewriter(n, new_n, new_pr); - if (m.proofs_enabled()) { + expr_ref new_n(m); + proof_ref new_pr(m); + m_rewriter(n, new_n, new_pr); + if (m.proofs_enabled()) { proof * pr = m_formulas[i].get_proof(); - new_pr = m.mk_modus_ponens(pr, new_pr); + new_pr = m.mk_modus_ponens(pr, new_pr); } justified_expr j(m, new_n, new_pr); m_formulas[i] = j; @@ -505,16 +507,16 @@ void asserted_formulas::update_substitution(expr* n, proof* pr) { } if (is_gt(rhs, lhs)) { TRACE("propagate_values", tout << "insert " << mk_pp(rhs, m) << " -> " << mk_pp(lhs, m) << "\n";); - m_scoped_substitution.insert(rhs, lhs, m.mk_symmetry(pr)); + m_scoped_substitution.insert(rhs, lhs, m.proofs_enabled() ? m.mk_symmetry(pr) : nullptr); return; } TRACE("propagate_values", tout << "incompatible " << mk_pp(n, m) << "\n";); } if (m.is_not(n, n1)) { - m_scoped_substitution.insert(n1, m.mk_false(), m.mk_iff_false(pr)); + m_scoped_substitution.insert(n1, m.mk_false(), m.proofs_enabled() ? m.mk_iff_false(pr) : nullptr); } else { - m_scoped_substitution.insert(n, m.mk_true(), m.mk_iff_true(pr)); + m_scoped_substitution.insert(n, m.mk_true(), m.proofs_enabled() ? m.mk_iff_true(pr) : nullptr); } } @@ -556,13 +558,13 @@ bool asserted_formulas::is_gt(expr* lhs, expr* rhs) { } UNREACHABLE(); } - + return false; } void asserted_formulas::compute_depth(expr* e) { ptr_vector todo; - todo.push_back(e); + todo.push_back(e); while (!todo.empty()) { e = todo.back(); unsigned d = 0; @@ -605,8 +607,7 @@ proof * asserted_formulas::get_inconsistency_proof() const { return 0; } - -void asserted_formulas::refine_inj_axiom_fn::simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { +void asserted_formulas::refine_inj_axiom_fn::simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { expr* f = j.get_fml(); if (is_quantifier(f) && simplify_inj_axiom(m, to_quantifier(f), n)) { TRACE("inj_axiom", tout << "simplifying...\n" << mk_pp(f, m) << "\n" << n << "\n";); diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index 497aab8db..96334e3ce 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -1999,7 +1999,7 @@ namespace smt { m_ast_manager(ctx.get_manager()), m_mam(m), m_use_filters(use_filters) { - m_args.resize(INIT_ARGS_SIZE, 0); + m_args.resize(INIT_ARGS_SIZE); } ~interpreter() { diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 06da12569..c6749f678 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -74,7 +74,7 @@ def_module_params(module_name='smt', ('str.fast_length_tester_cache', BOOL, False, 'cache length tester constants instead of regenerating them'), ('str.fast_value_tester_cache', BOOL, True, 'cache value tester constants instead of regenerating them'), ('str.string_constant_cache', BOOL, True, 'cache all generated string constants generated from anywhere in theory_str'), - ('str.use_binary_search', BOOL, False, 'use a binary search heuristic for finding concrete length values for free variables in theory_str (set to False to use linear search)'), + ('str.use_binary_search', BOOL, True, 'use a binary search heuristic for finding concrete length values for free variables in theory_str (set to False to use linear search)'), ('str.binary_search_start', UINT, 64, 'initial upper bound for theory_str binary search'), ('theory_aware_branching', BOOL, False, 'Allow the context to use extra information from theory solvers regarding literal branching prioritization.'), ('str.finite_overlap_models', BOOL, False, 'attempt a finite model search for overlapping variables instead of completely giving up on the arrangement'), diff --git a/src/smt/smt_conflict_resolution.cpp b/src/smt/smt_conflict_resolution.cpp index cb1465d94..80168df18 100644 --- a/src/smt/smt_conflict_resolution.cpp +++ b/src/smt/smt_conflict_resolution.cpp @@ -810,8 +810,6 @@ namespace smt { m_new_proofs.push_back(pr); return pr; } - if (m_manager.coarse_grain_proofs()) - return pr; TRACE("norm_eq_proof", tout << "#" << n1->get_owner_id() << " = #" << n2->get_owner_id() << "\n"; tout << mk_ll_pp(pr, m_manager, true, false);); @@ -1217,7 +1215,7 @@ namespace smt { mk_proof(rhs, c, prs2); while (!prs2.empty()) { proof * pr = prs2.back(); - if (m_manager.fine_grain_proofs()) { + if (m_manager.proofs_enabled()) { pr = m_manager.mk_symmetry(pr); m_new_proofs.push_back(pr); prs1.push_back(pr); diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 8e95e13ac..4535b29c2 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -23,7 +23,7 @@ Revision History: #include "ast/ast_ll_pp.h" #include "util/warning.h" #include "smt/smt_quick_checker.h" -#include "ast/proof_checker/proof_checker.h" +#include "ast/proofs/proof_checker.h" #include "ast/ast_util.h" #include "smt/uses_theory.h" #include "model/model.h" @@ -1286,7 +1286,7 @@ namespace smt { else { if (depth >= m_almost_cg_tables.size()) { unsigned old_sz = m_almost_cg_tables.size(); - m_almost_cg_tables.resize(depth+1, 0); + m_almost_cg_tables.resize(depth+1); for (unsigned i = old_sz; i < depth + 1; i++) m_almost_cg_tables[i] = alloc(almost_cg_table); } @@ -4402,7 +4402,8 @@ namespace smt { subst.push_back(arg); } expr_ref bodyr(m); - var_subst sub(m, false); + var_subst sub(m, true); + TRACE("context", tout << expr_ref(q, m) << " " << subst << "\n";); sub(body, subst.size(), subst.c_ptr(), bodyr); func_decl* f = to_app(fn)->get_decl(); func_interp* fi = alloc(func_interp, m, f->get_arity()); diff --git a/src/smt/smt_justification.cpp b/src/smt/smt_justification.cpp index c8de45644..440da7297 100644 --- a/src/smt/smt_justification.cpp +++ b/src/smt/smt_justification.cpp @@ -129,7 +129,7 @@ namespace smt { if (m_node1 != m_node1->get_root()) { proof * pr = cr.get_proof(m_node1, m_node1->get_root()); - if (pr && m.fine_grain_proofs()) + if (pr && m.proofs_enabled()) pr = m.mk_symmetry(pr); prs.push_back(pr); if (!pr) diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index 9f411b0e6..b37d004f4 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -29,9 +29,8 @@ Notes: namespace smt { - class solver : public solver_na2as { + class smt_solver : public solver_na2as { smt_params m_smt_params; - params_ref m_params; smt::kernel m_context; progress_callback * m_callback; symbol m_logic; @@ -42,44 +41,42 @@ namespace smt { obj_map m_name2assertion; public: - solver(ast_manager & m, params_ref const & p, symbol const & l) : + smt_solver(ast_manager & m, params_ref const & p, symbol const & l) : solver_na2as(m), m_smt_params(p), - m_params(p), m_context(m, m_smt_params), m_minimizing_core(false), m_core_extend_patterns(false), m_core_extend_patterns_max_distance(UINT_MAX), - m_core_extend_nonlocal_patterns(false) { + m_core_extend_nonlocal_patterns(false) { m_logic = l; if (m_logic != symbol::null) m_context.set_logic(m_logic); - smt_params_helper smth(p); - m_core_extend_patterns = smth.core_extend_patterns(); - m_core_extend_patterns_max_distance = smth.core_extend_patterns_max_distance(); - m_core_extend_nonlocal_patterns = smth.core_extend_nonlocal_patterns(); + updt_params(p); } virtual solver * translate(ast_manager & m, params_ref const & p) { ast_translation translator(get_manager(), m); - solver * result = alloc(solver, m, p, m_logic); + smt_solver * result = alloc(smt_solver, m, p, m_logic); smt::kernel::copy(m_context, result->m_context); for (auto & kv : m_name2assertion) result->m_name2assertion.insert(translator(kv.m_key), translator(kv.m_value)); + if (mc0()) + result->set_model_converter(mc0()->translate(translator)); return result; } - virtual ~solver() { + virtual ~smt_solver() { dec_ref_values(get_manager(), m_name2assertion); } virtual void updt_params(params_ref const & p) { + solver::updt_params(p); m_smt_params.updt_params(p); - m_params.copy(p); m_context.updt_params(p); smt_params_helper smth(p); m_core_extend_patterns = smth.core_extend_patterns(); @@ -150,9 +147,9 @@ namespace smt { } struct scoped_minimize_core { - solver& s; + smt_solver& s; expr_ref_vector m_assumptions; - scoped_minimize_core(solver& s) : s(s), m_assumptions(s.m_assumptions) { + scoped_minimize_core(smt_solver& s) : s(s), m_assumptions(s.m_assumptions) { s.m_minimizing_core = true; s.m_assumptions.reset(); } @@ -169,7 +166,7 @@ namespace smt { r.push_back(m_context.get_unsat_core_expr(i)); } - if (m_minimizing_core && smt_params_helper(m_params).core_minimize()) { + if (m_minimizing_core && smt_params_helper(get_params()).core_minimize()) { scoped_minimize_core scm(*this); mus mus(*this); mus.add_soft(r.size(), r.c_ptr()); @@ -186,7 +183,7 @@ namespace smt { add_nonlocal_pattern_literals_to_core(r); } - virtual void get_model(model_ref & m) { + virtual void get_model_core(model_ref & m) { m_context.get_model(m); } @@ -360,22 +357,16 @@ namespace smt { void add_nonlocal_pattern_literals_to_core(ptr_vector & core) { ast_manager & m = get_manager(); - - obj_map::iterator it = m_name2assertion.begin(); - obj_map::iterator end = m_name2assertion.end(); - for (unsigned i = 0; it != end; it++, i++) { - expr_ref name(it->m_key, m); - expr_ref assrtn(it->m_value, m); + for (auto const& kv : m_name2assertion) { + expr_ref name(kv.m_key, m); + expr_ref assrtn(kv.m_value, m); if (!core.contains(name)) { func_decl_set pattern_fds, body_fds; collect_pattern_fds(assrtn, pattern_fds); collect_body_func_decls(assrtn, body_fds); - func_decl_set::iterator pit = pattern_fds.begin(); - func_decl_set::iterator pend= pattern_fds.end(); - for (; pit != pend; pit++) { - func_decl * fd = *pit; + for (func_decl *fd : pattern_fds) { if (!body_fds.contains(fd)) { core.insert(name); break; @@ -388,7 +379,7 @@ namespace smt { }; solver * mk_smt_solver(ast_manager & m, params_ref const & p, symbol const & logic) { - return alloc(smt::solver, m, p, logic); + return alloc(smt::smt_solver, m, p, logic); } class smt_solver_factory : public solver_factory { diff --git a/src/smt/theory_array.cpp b/src/smt/theory_array.cpp index 18fc9f50b..b8f76f9ad 100644 --- a/src/smt/theory_array.cpp +++ b/src/smt/theory_array.cpp @@ -53,18 +53,12 @@ namespace smt { var_data * d2 = m_var_data[v2]; if (!d1->m_prop_upward && d2->m_prop_upward) set_prop_upward(v1); - ptr_vector::iterator it = d2->m_stores.begin(); - ptr_vector::iterator end = d2->m_stores.end(); - for (; it != end; ++it) - add_store(v1, *it); - it = d2->m_parent_stores.begin(); - end = d2->m_parent_stores.end(); - for (; it != end; ++it) - add_parent_store(v1, *it); - it = d2->m_parent_selects.begin(); - end = d2->m_parent_selects.end(); - for (; it != end; ++it) - add_parent_select(v1, *it); + for (enode* n : d2->m_stores) + add_store(v1, n); + for (enode* n : d2->m_parent_stores) + add_parent_store(v1, n); + for (enode* n : d2->m_parent_selects) + add_parent_select(v1, n); TRACE("array", tout << "after merge\n"; display_var(tout, v1);); } @@ -103,16 +97,11 @@ namespace smt { d->m_parent_selects.push_back(s); TRACE("array", tout << mk_pp(s->get_owner(), get_manager()) << " " << mk_pp(get_enode(v)->get_owner(), get_manager()) << "\n";); m_trail_stack.push(push_back_trail(d->m_parent_selects)); - ptr_vector::iterator it = d->m_stores.begin(); - ptr_vector::iterator end = d->m_stores.end(); - for (; it != end; ++it) { - instantiate_axiom2a(s, *it); + for (enode* n : d->m_stores) { + instantiate_axiom2a(s, n); } if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) { - it = d->m_parent_stores.begin(); - end = d->m_parent_stores.end(); - for (; it != end; ++it) { - enode * store = *it; + for (enode* store : d->m_parent_stores) { SASSERT(is_store(store)); if (!m_params.m_array_cg || store->is_cgr()) { instantiate_axiom2b(s, store); @@ -129,27 +118,19 @@ namespace smt { var_data * d = m_var_data[v]; d->m_parent_stores.push_back(s); m_trail_stack.push(push_back_trail(d->m_parent_stores)); - if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) { - ptr_vector::iterator it = d->m_parent_selects.begin(); - ptr_vector::iterator end = d->m_parent_selects.end(); - for (; it != end; ++it) - if (!m_params.m_array_cg || (*it)->is_cgr()) - instantiate_axiom2b(*it, s); - } + if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) + for (enode* n : d->m_parent_selects) + if (!m_params.m_array_cg || n->is_cgr()) + instantiate_axiom2b(n, s); } bool theory_array::instantiate_axiom2b_for(theory_var v) { bool result = false; var_data * d = m_var_data[v]; - ptr_vector::iterator it = d->m_parent_stores.begin(); - ptr_vector::iterator end = d->m_parent_stores.end(); - for (; it != end; ++it) { - ptr_vector::iterator it2 = d->m_parent_selects.begin(); - ptr_vector::iterator end2 = d->m_parent_selects.end(); - for (; it2 != end2; ++it2) - if (instantiate_axiom2b(*it2, *it)) + for (enode* n1 : d->m_parent_stores) + for (enode * n2 : d->m_parent_selects) + if (instantiate_axiom2b(n2, n1)) result = true; - } return result; } @@ -167,10 +148,8 @@ namespace smt { d->m_prop_upward = true; if (!m_params.m_array_delay_exp_axiom) instantiate_axiom2b_for(v); - ptr_vector::iterator it = d->m_stores.begin(); - ptr_vector::iterator end = d->m_stores.end(); - for (; it != end; ++it) - set_prop_upward(*it); + for (enode * n : d->m_stores) + set_prop_upward(n); } } @@ -209,11 +188,9 @@ namespace smt { } d->m_stores.push_back(s); m_trail_stack.push(push_back_trail(d->m_stores)); - ptr_vector::iterator it = d->m_parent_selects.begin(); - ptr_vector::iterator end = d->m_parent_selects.end(); - for (; it != end; ++it) { - SASSERT(is_select(*it)); - instantiate_axiom2a(*it, s); + for (enode * n : d->m_parent_selects) { + SASSERT(is_select(n)); + instantiate_axiom2a(n, s); } if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1) set_prop_upward(s); @@ -374,7 +351,7 @@ namespace smt { final_check_status theory_array::final_check_eh() { m_final_check_idx++; - final_check_status r; + final_check_status r = FC_DONE; if (m_params.m_array_lazy_ieq) { // Delay the creation of interface equalities... The // motivation is too give other theories and quantifier diff --git a/src/smt/theory_array_base.cpp b/src/smt/theory_array_base.cpp index 1f519dfda..472053fdc 100644 --- a/src/smt/theory_array_base.cpp +++ b/src/smt/theory_array_base.cpp @@ -210,17 +210,15 @@ namespace smt { - func_decl_ref_vector * theory_array_base::register_sort(sort * s_array) { unsigned dimension = get_dimension(s_array); func_decl_ref_vector * ext_skolems = 0; if (!m_sort2skolem.find(s_array, ext_skolems)) { + array_util util(get_manager()); ast_manager & m = get_manager(); ext_skolems = alloc(func_decl_ref_vector, m); for (unsigned i = 0; i < dimension; ++i) { - sort * ext_sk_domain[2] = { s_array, s_array }; - parameter p(i); - func_decl * ext_sk_decl = m.mk_func_decl(get_id(), OP_ARRAY_EXT, 1, &p, 2, ext_sk_domain); + func_decl * ext_sk_decl = util.mk_array_ext(s_array, i); ext_skolems->push_back(ext_sk_decl); } m_sort2skolem.insert(s_array, ext_skolems); @@ -617,8 +615,8 @@ namespace smt { m_else_values.reset(); m_parents.reset(); m_parents.resize(num_vars, -1); - m_defaults.resize(num_vars, 0); - m_else_values.resize(num_vars, 0); + m_defaults.resize(num_vars); + m_else_values.resize(num_vars); if (m_use_unspecified_default) return; diff --git a/src/smt/theory_datatype.cpp b/src/smt/theory_datatype.cpp index 616314117..bbed6840d 100644 --- a/src/smt/theory_datatype.cpp +++ b/src/smt/theory_datatype.cpp @@ -620,7 +620,7 @@ namespace smt { sort * s = recognizer->get_decl()->get_domain(0); if (d->m_recognizers.empty()) { SASSERT(m_util.is_datatype(s)); - d->m_recognizers.resize(m_util.get_datatype_num_constructors(s), 0); + d->m_recognizers.resize(m_util.get_datatype_num_constructors(s)); } SASSERT(d->m_recognizers.size() == m_util.get_datatype_num_constructors(s)); unsigned c_idx = m_util.get_recognizer_constructor_idx(recognizer->get_decl()); diff --git a/src/smt/theory_dense_diff_logic_def.h b/src/smt/theory_dense_diff_logic_def.h index 064bdd433..78fb4d03d 100644 --- a/src/smt/theory_dense_diff_logic_def.h +++ b/src/smt/theory_dense_diff_logic_def.h @@ -914,6 +914,8 @@ namespace smt { } verbose_stream() << " + " << m_objective_consts[v] << "\n";); + unsynch_mpq_manager mgr; + unsynch_mpq_inf_manager inf_mgr; unsigned num_nodes = get_num_vars(); unsigned num_edges = m_edges.size(); S.ensure_var(num_nodes + num_edges + m_objectives.size()); @@ -921,8 +923,9 @@ namespace smt { numeral const& a = m_assignment[i]; rational fin = a.get_rational().to_rational(); rational inf = a.get_infinitesimal().to_rational(); - mpq_inf q(fin.to_mpq(), inf.to_mpq()); + mpq_inf q(mgr.dup(fin.to_mpq()), mgr.dup(inf.to_mpq())); S.set_value(i, q); + inf_mgr.del(q); } for (unsigned i = 0; i < num_nodes; ++i) { enode * n = get_enode(i); @@ -933,7 +936,6 @@ namespace smt { } } svector vars; - unsynch_mpq_manager mgr; scoped_mpq_vector coeffs(mgr); coeffs.push_back(mpq(1)); coeffs.push_back(mpq(-1)); @@ -954,8 +956,9 @@ namespace smt { numeral const& w = e.m_offset; rational fin = w.get_rational().to_rational(); rational inf = w.get_infinitesimal().to_rational(); - mpq_inf q(fin.to_mpq(),inf.to_mpq()); + mpq_inf q(mgr.dup(fin.to_mpq()), mgr.dup(inf.to_mpq())); S.set_upper(base_var, q); + inf_mgr.del(q); } unsigned w = num_nodes + num_edges + v; diff --git a/src/smt/theory_diff_logic_def.h b/src/smt/theory_diff_logic_def.h index a7153234c..203dd24d2 100644 --- a/src/smt/theory_diff_logic_def.h +++ b/src/smt/theory_diff_logic_def.h @@ -1107,6 +1107,8 @@ unsigned theory_diff_logic::simplex2edge(unsigned e) { template void theory_diff_logic::update_simplex(Simplex& S) { + unsynch_mpq_manager mgr; + unsynch_mpq_inf_manager inf_mgr; unsigned num_nodes = m_graph.get_num_nodes(); vector > const& es = m_graph.get_all_edges(); S.ensure_var(num_simplex_vars()); @@ -1114,13 +1116,13 @@ void theory_diff_logic::update_simplex(Simplex& S) { numeral const& a = m_graph.get_assignment(i); rational fin = a.get_rational().to_rational(); rational inf = a.get_infinitesimal().to_rational(); - mpq_inf q(fin.to_mpq(), inf.to_mpq()); + mpq_inf q(mgr.dup(fin.to_mpq()), mgr.dup(inf.to_mpq())); S.set_value(node2simplex(i), q); + inf_mgr.del(q); } S.set_lower(node2simplex(get_zero()), mpq_inf(mpq(0), mpq(0))); S.set_upper(node2simplex(get_zero()), mpq_inf(mpq(0), mpq(0))); svector vars; - unsynch_mpq_manager mgr; scoped_mpq_vector coeffs(mgr); coeffs.push_back(mpq(1)); coeffs.push_back(mpq(-1)); @@ -1145,8 +1147,9 @@ void theory_diff_logic::update_simplex(Simplex& S) { numeral const& w = e.get_weight(); rational fin = w.get_rational().to_rational(); rational inf = w.get_infinitesimal().to_rational(); - mpq_inf q(fin.to_mpq(),inf.to_mpq()); + mpq_inf q(mgr.dup(fin.to_mpq()), mgr.dup(inf.to_mpq())); S.set_upper(base_var, q); + inf_mgr.del(q); } else { S.unset_upper(base_var); diff --git a/src/smt/theory_pb.h b/src/smt/theory_pb.h index fdca9ad73..f5b53b713 100644 --- a/src/smt/theory_pb.h +++ b/src/smt/theory_pb.h @@ -363,7 +363,6 @@ namespace smt { // void compile_ineq(ineq& c); void inc_propagations(ineq& c); - unsigned get_compilation_threshold(ineq& c); // // Conflict resolution, cutting plane derivation. diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 44ce804e7..d55484384 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -3466,6 +3466,8 @@ static bool get_arith_value(context& ctx, theory_id afid, expr* e, expr_ref& v) bool theory_seq::get_num_value(expr* e, rational& val) const { context& ctx = get_context(); expr_ref _val(m); + if (!ctx.e_internalized(e)) + return false; enode* next = ctx.get_enode(e), *n = next; do { if (get_arith_value(ctx, m_autil.get_family_id(), next->get_owner(), _val) && m_autil.is_numeral(_val, val) && val.is_int()) { diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 8a141665c..a1f0f9777 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -166,14 +166,18 @@ namespace smt { } } - void theory_str::assert_axiom(expr * e) { + void theory_str::assert_axiom(expr * _e) { if (opt_VerifyFinalCheckProgress) { finalCheckProgressIndicator = true; } - if (get_manager().is_true(e)) return; - TRACE("str", tout << "asserting " << mk_ismt2_pp(e, get_manager()) << std::endl;); + if (get_manager().is_true(_e)) return; context & ctx = get_context(); + ast_manager& m = get_manager(); + TRACE("str", tout << "asserting " << mk_ismt2_pp(_e, m) << std::endl;); + expr_ref e(_e, m); + //th_rewriter rw(m); + //rw(e); if (!ctx.b_internalized(e)) { ctx.internalize(e, false); } @@ -190,13 +194,13 @@ namespace smt { expr * theory_str::rewrite_implication(expr * premise, expr * conclusion) { ast_manager & m = get_manager(); - return m.mk_or(m.mk_not(premise), conclusion); + return m.mk_or(mk_not(m, premise), conclusion); } void theory_str::assert_implication(expr * premise, expr * conclusion) { ast_manager & m = get_manager(); TRACE("str", tout << "asserting implication " << mk_ismt2_pp(premise, m) << " -> " << mk_ismt2_pp(conclusion, m) << std::endl;); - expr_ref axiom(m.mk_or(m.mk_not(premise), conclusion), m); + expr_ref axiom(m.mk_or(mk_not(m, premise), conclusion), m); assert_axiom(axiom); } @@ -315,6 +319,7 @@ namespace smt { m_trail.push_back(node); if (!cut_var_map.contains(baseNode)) { T_cut * varInfo = alloc(T_cut); + m_cut_allocs.push_back(varInfo); varInfo->level = slevel; varInfo->vars[node] = 1; cut_var_map.insert(baseNode, std::stack()); @@ -323,6 +328,7 @@ namespace smt { } else { if (cut_var_map[baseNode].empty()) { T_cut * varInfo = alloc(T_cut); + m_cut_allocs.push_back(varInfo); varInfo->level = slevel; varInfo->vars[node] = 1; cut_var_map[baseNode].push(varInfo); @@ -330,6 +336,7 @@ namespace smt { } else { if (cut_var_map[baseNode].top()->level < slevel) { T_cut * varInfo = alloc(T_cut); + m_cut_allocs.push_back(varInfo); varInfo->level = slevel; cut_vars_map_copy(varInfo->vars, cut_var_map[baseNode].top()->vars); varInfo->vars[node] = 1; @@ -359,6 +366,7 @@ namespace smt { if (!cut_var_map.contains(destNode)) { T_cut * varInfo = alloc(T_cut); + m_cut_allocs.push_back(varInfo); varInfo->level = slevel; cut_vars_map_copy(varInfo->vars, cut_var_map[srcNode].top()->vars); cut_var_map.insert(destNode, std::stack()); @@ -367,6 +375,7 @@ namespace smt { } else { if (cut_var_map[destNode].empty() || cut_var_map[destNode].top()->level < slevel) { T_cut * varInfo = alloc(T_cut); + m_cut_allocs.push_back(varInfo); varInfo->level = slevel; cut_vars_map_copy(varInfo->vars, cut_var_map[destNode].top()->vars); cut_vars_map_copy(varInfo->vars, cut_var_map[srcNode].top()->vars); @@ -540,7 +549,7 @@ namespace smt { context & ctx = get_context(); ast_manager & m = get_manager(); - expr_ref ax1(m.mk_not(ctx.mk_eq_atom(s, mk_string(""))), m); + expr_ref ax1(mk_not(m, ctx.mk_eq_atom(s, mk_string(""))), m); assert_axiom(ax1); { @@ -552,7 +561,7 @@ namespace smt { SASSERT(zero); // build LHS > RHS and assert // we have to build !(LHS <= RHS) instead - expr_ref lhs_gt_rhs(m.mk_not(m_autil.mk_le(len_str, zero)), m); + expr_ref lhs_gt_rhs(mk_not(m, m_autil.mk_le(len_str, zero)), m); SASSERT(lhs_gt_rhs); assert_axiom(lhs_gt_rhs); } @@ -587,7 +596,7 @@ namespace smt { SASSERT(zero); // build LHS > RHS and assert // we have to build !(LHS <= RHS) instead - expr_ref lhs_gt_rhs(m.mk_not(m_autil.mk_le(len_str, zero)), m); + expr_ref lhs_gt_rhs(mk_not(m, m_autil.mk_le(len_str, zero)), m); SASSERT(lhs_gt_rhs); assert_axiom(lhs_gt_rhs); } @@ -1084,7 +1093,7 @@ namespace smt { m_autil.mk_ge(expr->get_arg(1), mk_int(0)), // REWRITE for arithmetic theory: // m_autil.mk_lt(expr->get_arg(1), mk_strlen(expr->get_arg(0))) - m.mk_not(m_autil.mk_ge(m_autil.mk_add(expr->get_arg(1), m_autil.mk_mul(mk_int(-1), mk_strlen(expr->get_arg(0)))), mk_int(0))) + mk_not(m, m_autil.mk_ge(m_autil.mk_add(expr->get_arg(1), m_autil.mk_mul(mk_int(-1), mk_strlen(expr->get_arg(0)))), mk_int(0))) ), m); expr_ref_vector and_item(m); @@ -1125,7 +1134,7 @@ namespace smt { expr_ref_vector innerItems(m); innerItems.push_back(ctx.mk_eq_atom(expr->get_arg(1), mk_concat(ts0, ts1))); innerItems.push_back(ctx.mk_eq_atom(mk_strlen(ts0), mk_strlen(expr->get_arg(0)))); - innerItems.push_back(m.mk_ite(ctx.mk_eq_atom(ts0, expr->get_arg(0)), expr, m.mk_not(expr))); + innerItems.push_back(m.mk_ite(ctx.mk_eq_atom(ts0, expr->get_arg(0)), expr, mk_not(m, expr))); expr_ref then1(m.mk_and(innerItems.size(), innerItems.c_ptr()), m); SASSERT(then1); @@ -1138,7 +1147,7 @@ namespace smt { , m); SASSERT(topLevelCond); - expr_ref finalAxiom(m.mk_ite(topLevelCond, then1, m.mk_not(expr)), m); + expr_ref finalAxiom(m.mk_ite(topLevelCond, then1, mk_not(m, expr)), m); SASSERT(finalAxiom); assert_axiom(finalAxiom); } @@ -1162,7 +1171,7 @@ namespace smt { expr_ref_vector innerItems(m); innerItems.push_back(ctx.mk_eq_atom(expr->get_arg(1), mk_concat(ts0, ts1))); innerItems.push_back(ctx.mk_eq_atom(mk_strlen(ts1), mk_strlen(expr->get_arg(0)))); - innerItems.push_back(m.mk_ite(ctx.mk_eq_atom(ts1, expr->get_arg(0)), expr, m.mk_not(expr))); + innerItems.push_back(m.mk_ite(ctx.mk_eq_atom(ts1, expr->get_arg(0)), expr, mk_not(m, expr))); expr_ref then1(m.mk_and(innerItems.size(), innerItems.c_ptr()), m); SASSERT(then1); @@ -1175,7 +1184,7 @@ namespace smt { , m); SASSERT(topLevelCond); - expr_ref finalAxiom(m.mk_ite(topLevelCond, then1, m.mk_not(expr)), m); + expr_ref finalAxiom(m.mk_ite(topLevelCond, then1, mk_not(m, expr)), m); SASSERT(finalAxiom); assert_axiom(finalAxiom); } @@ -1199,7 +1208,7 @@ namespace smt { if (haystackStr.contains(needleStr)) { assert_axiom(ex); } else { - assert_axiom(m.mk_not(ex)); + assert_axiom(mk_not(m, ex)); } return; } @@ -1260,7 +1269,7 @@ namespace smt { SASSERT(tmpLen); thenItems.push_back(ctx.mk_eq_atom(expr->get_arg(0), mk_concat(x3, x4))); thenItems.push_back(ctx.mk_eq_atom(mk_strlen(x3), tmpLen)); - thenItems.push_back(m.mk_not(mk_contains(x3, expr->get_arg(1)))); + thenItems.push_back(mk_not(m, mk_contains(x3, expr->get_arg(1)))); expr_ref thenBranch(m.mk_and(thenItems.size(), thenItems.c_ptr()), m); SASSERT(thenBranch); @@ -1336,7 +1345,7 @@ namespace smt { expr_ref ite1(m.mk_ite( //m_autil.mk_lt(expr->get_arg(2), zeroAst), - m.mk_not(m_autil.mk_ge(expr->get_arg(2), zeroAst)), + mk_not(m, m_autil.mk_ge(expr->get_arg(2), zeroAst)), ctx.mk_eq_atom(resAst, mk_indexof(expr->get_arg(0), expr->get_arg(1))), ite2 ), m); @@ -1379,7 +1388,7 @@ namespace smt { thenItems.push_back(m_autil.mk_ge(indexAst, mk_int(0))); // args[0] = x1 . args[1] . x2 // x1 doesn't contain args[1] - thenItems.push_back(m.mk_not(mk_contains(x2, expr->get_arg(1)))); + thenItems.push_back(mk_not(m, mk_contains(x2, expr->get_arg(1)))); thenItems.push_back(ctx.mk_eq_atom(indexAst, mk_strlen(x1))); bool canSkip = false; @@ -1397,7 +1406,7 @@ namespace smt { expr_ref tmpLen(m_autil.mk_add(indexAst, mk_int(1)), m); thenItems.push_back(ctx.mk_eq_atom(expr->get_arg(0), mk_concat(x3, x4))); thenItems.push_back(ctx.mk_eq_atom(mk_strlen(x3), tmpLen)); - thenItems.push_back(m.mk_not(mk_contains(x4, expr->get_arg(1)))); + thenItems.push_back(mk_not(m, mk_contains(x4, expr->get_arg(1)))); } //---------------------------- // else branch @@ -1417,104 +1426,6 @@ namespace smt { assert_axiom(finalAxiom); } - void theory_str::instantiate_axiom_Substr(enode * e) { - context & ctx = get_context(); - ast_manager & m = get_manager(); - - app * expr = e->get_owner(); - if (axiomatized_terms.contains(expr)) { - TRACE("str", tout << "already set up Substr axiom for " << mk_pp(expr, m) << std::endl;); - return; - } - axiomatized_terms.insert(expr); - - TRACE("str", tout << "instantiate Substr axiom for " << mk_pp(expr, m) << std::endl;); - - expr_ref substrBase(expr->get_arg(0), m); - expr_ref substrPos(expr->get_arg(1), m); - expr_ref substrLen(expr->get_arg(2), m); - SASSERT(substrBase); - SASSERT(substrPos); - SASSERT(substrLen); - - expr_ref zero(m_autil.mk_numeral(rational::zero(), true), m); - expr_ref minusOne(m_autil.mk_numeral(rational::minus_one(), true), m); - SASSERT(zero); - SASSERT(minusOne); - - expr_ref_vector argumentsValid_terms(m); - // pos >= 0 - argumentsValid_terms.push_back(m_autil.mk_ge(substrPos, zero)); - // pos < strlen(base) - // --> pos + -1*strlen(base) < 0 - argumentsValid_terms.push_back(m.mk_not(m_autil.mk_ge( - m_autil.mk_add(substrPos, m_autil.mk_mul(minusOne, substrLen)), - zero))); - - // len >= 0 - argumentsValid_terms.push_back(m_autil.mk_ge(substrLen, zero)); - - expr_ref argumentsValid(mk_and(argumentsValid_terms), m); - SASSERT(argumentsValid); - ctx.internalize(argumentsValid, false); - - // (pos+len) >= strlen(base) - // --> pos + len + -1*strlen(base) >= 0 - expr_ref lenOutOfBounds(m_autil.mk_ge( - m_autil.mk_add(substrPos, substrLen, m_autil.mk_mul(minusOne, mk_strlen(substrBase))), - zero), m); - SASSERT(lenOutOfBounds); - ctx.internalize(argumentsValid, false); - - // Case 1: pos < 0 or pos >= strlen(base) or len < 0 - // ==> (Substr ...) = "" - expr_ref case1_premise(m.mk_not(argumentsValid), m); - SASSERT(case1_premise); - ctx.internalize(case1_premise, false); - expr_ref case1_conclusion(ctx.mk_eq_atom(expr, mk_string("")), m); - SASSERT(case1_conclusion); - ctx.internalize(case1_conclusion, false); - expr_ref case1(rewrite_implication(case1_premise, case1_conclusion), m); - SASSERT(case1); - - // Case 2: (pos >= 0 and pos < strlen(base) and len >= 0) and (pos+len) >= strlen(base) - // ==> base = t0.t1 AND len(t0) = pos AND (Substr ...) = t1 - expr_ref t0(mk_str_var("t0"), m); - expr_ref t1(mk_str_var("t1"), m); - expr_ref case2_conclusion(m.mk_and( - ctx.mk_eq_atom(substrBase, mk_concat(t0,t1)), - ctx.mk_eq_atom(mk_strlen(t0), substrPos), - ctx.mk_eq_atom(expr, t1)), m); - expr_ref case2(rewrite_implication(m.mk_and(argumentsValid, lenOutOfBounds), case2_conclusion), m); - SASSERT(case2); - - // Case 3: (pos >= 0 and pos < strlen(base) and len >= 0) and (pos+len) < strlen(base) - // ==> base = t2.t3.t4 AND len(t2) = pos AND len(t3) = len AND (Substr ...) = t3 - - expr_ref t2(mk_str_var("t2"), m); - expr_ref t3(mk_str_var("t3"), m); - expr_ref t4(mk_str_var("t4"), m); - expr_ref_vector case3_conclusion_terms(m); - case3_conclusion_terms.push_back(ctx.mk_eq_atom(substrBase, mk_concat(t2, mk_concat(t3, t4)))); - case3_conclusion_terms.push_back(ctx.mk_eq_atom(mk_strlen(t2), substrPos)); - case3_conclusion_terms.push_back(ctx.mk_eq_atom(mk_strlen(t3), substrLen)); - case3_conclusion_terms.push_back(ctx.mk_eq_atom(expr, t3)); - expr_ref case3_conclusion(mk_and(case3_conclusion_terms), m); - expr_ref case3(rewrite_implication(m.mk_and(argumentsValid, m.mk_not(lenOutOfBounds)), case3_conclusion), m); - SASSERT(case3); - - ctx.internalize(case1, false); - ctx.internalize(case2, false); - ctx.internalize(case3, false); - - expr_ref finalAxiom(m.mk_and(case1, case2, case3), m); - SASSERT(finalAxiom); - assert_axiom(finalAxiom); - } - -#if 0 - // rewrite - // requires to add th_rewriter to assert_axiom to enforce normal form. void theory_str::instantiate_axiom_Substr(enode * e) { context & ctx = get_context(); ast_manager & m = get_manager(); @@ -1543,9 +1454,10 @@ namespace smt { argumentsValid_terms.push_back(m_autil.mk_ge(substrPos, zero)); // pos < strlen(base) // --> pos + -1*strlen(base) < 0 - argumentsValid_terms.push_back(m.mk_not(m_autil.mk_ge( + argumentsValid_terms.push_back(mk_not(m, m_autil.mk_ge( m_autil.mk_add(substrPos, m_autil.mk_mul(minusOne, substrLen)), zero))); + // len >= 0 argumentsValid_terms.push_back(m_autil.mk_ge(substrLen, zero)); @@ -1575,6 +1487,7 @@ namespace smt { // Case 3: (pos >= 0 and pos < strlen(base) and len >= 0) and (pos+len) < strlen(base) // ==> base = t2.t3.t4 AND len(t2) = pos AND len(t3) = len AND (Substr ...) = t3 + expr_ref t2(mk_str_var("t2"), m); expr_ref t3(mk_str_var("t3"), m); expr_ref t4(mk_str_var("t4"), m); @@ -1586,11 +1499,22 @@ namespace smt { expr_ref case3_conclusion(mk_and(case3_conclusion_terms), m); expr_ref case3(m.mk_implies(m.mk_and(argumentsValid, m.mk_not(lenOutOfBounds)), case3_conclusion), m); - assert_axiom(case1); - assert_axiom(case2); - assert_axiom(case3); + { + th_rewriter rw(m); + + expr_ref case1_rw(case1, m); + rw(case1_rw); + assert_axiom(case1_rw); + + expr_ref case2_rw(case2, m); + rw(case2_rw); + assert_axiom(case2_rw); + + expr_ref case3_rw(case3, m); + rw(case3_rw); + assert_axiom(case3_rw); + } } -#endif void theory_str::instantiate_axiom_Replace(enode * e) { context & ctx = get_context(); @@ -1625,19 +1549,23 @@ namespace smt { expr_ref tmpLen(m_autil.mk_add(i1, mk_strlen(expr->get_arg(1)), mk_int(-1)), m); thenItems.push_back(ctx.mk_eq_atom(expr->get_arg(0), mk_concat(x3, x4))); thenItems.push_back(ctx.mk_eq_atom(mk_strlen(x3), tmpLen)); - thenItems.push_back(m.mk_not(mk_contains(x3, expr->get_arg(1)))); + thenItems.push_back(mk_not(m, mk_contains(x3, expr->get_arg(1)))); thenItems.push_back(ctx.mk_eq_atom(result, mk_concat(x1, mk_concat(expr->get_arg(2), x2)))); // ----------------------- // false branch expr_ref elseBranch(ctx.mk_eq_atom(result, expr->get_arg(0)), m); + th_rewriter rw(m); + expr_ref breakdownAssert(m.mk_ite(condAst, m.mk_and(thenItems.size(), thenItems.c_ptr()), elseBranch), m); - assert_axiom(breakdownAssert); - - SASSERT(breakdownAssert); + expr_ref breakdownAssert_rw(breakdownAssert, m); + rw(breakdownAssert_rw); + assert_axiom(breakdownAssert_rw); expr_ref reduceToResult(ctx.mk_eq_atom(expr, result), m); - assert_axiom(reduceToResult); + expr_ref reduceToResult_rw(reduceToResult, m); + rw(reduceToResult_rw); + assert_axiom(reduceToResult_rw); } void theory_str::instantiate_axiom_str_to_int(enode * e) { @@ -1679,7 +1607,7 @@ namespace smt { expr_ref tl(mk_str_var("tl"), m); expr_ref conclusion1(ctx.mk_eq_atom(S, mk_concat(hd, tl)), m); expr_ref conclusion2(ctx.mk_eq_atom(mk_strlen(hd), m_autil.mk_numeral(rational::one(), true)), m); - expr_ref conclusion3(m.mk_not(ctx.mk_eq_atom(hd, mk_string("0"))), m); + expr_ref conclusion3(mk_not(m, ctx.mk_eq_atom(hd, mk_string("0"))), m); expr_ref conclusion(m.mk_and(conclusion1, conclusion2, conclusion3), m); SASSERT(premise); SASSERT(conclusion); @@ -1703,7 +1631,7 @@ namespace smt { // axiom 1: N < 0 <==> (str.from-int N) = "" expr * N = ex->get_arg(0); { - expr_ref axiom1_lhs(m.mk_not(m_autil.mk_ge(N, m_autil.mk_numeral(rational::zero(), true))), m); + expr_ref axiom1_lhs(mk_not(m, m_autil.mk_ge(N, m_autil.mk_numeral(rational::zero(), true))), m); expr_ref axiom1_rhs(ctx.mk_eq_atom(ex, mk_string("")), m); expr_ref axiom1(ctx.mk_eq_atom(axiom1_lhs, axiom1_rhs), m); SASSERT(axiom1); @@ -1942,7 +1870,7 @@ namespace smt { // inconsistency check: value if (!can_two_nodes_eq(eqc_nn1, eqc_nn2)) { TRACE("str", tout << "inconsistency detected: " << mk_pp(eqc_nn1, m) << " cannot be equal to " << mk_pp(eqc_nn2, m) << std::endl;); - expr_ref to_assert(m.mk_not(ctx.mk_eq_atom(eqc_nn1, eqc_nn2)), m); + expr_ref to_assert(mk_not(m, ctx.mk_eq_atom(eqc_nn1, eqc_nn2)), m); assert_axiom(to_assert); // this shouldn't use the integer theory at all, so we don't allow the option of quick-return return false; @@ -2155,7 +2083,7 @@ namespace smt { expr_ref implyR11(ctx.mk_eq_atom(mk_strlen(arg1), mk_int(makeUpLenArg1)), m); assert_implication(implyL11, implyR11); } else { - expr_ref neg(m.mk_not(implyL11), m); + expr_ref neg(mk_not(m, implyL11), m); assert_axiom(neg); } } @@ -2226,7 +2154,7 @@ namespace smt { expr_ref implyR11(ctx.mk_eq_atom(mk_strlen(arg0), mk_int(makeUpLenArg0)), m); assert_implication(implyL11, implyR11); } else { - expr_ref neg(m.mk_not(implyL11), m); + expr_ref neg(mk_not(m, implyL11), m); assert_axiom(neg); } } @@ -2757,7 +2685,7 @@ namespace smt { } if (!can_two_nodes_eq(new_nn1, new_nn2)) { - expr_ref detected(m.mk_not(ctx.mk_eq_atom(new_nn1, new_nn2)), m); + expr_ref detected(mk_not(m, ctx.mk_eq_atom(new_nn1, new_nn2)), m); TRACE("str", tout << "inconsistency detected: " << mk_ismt2_pp(detected, m) << std::endl;); assert_axiom(detected); return; @@ -4747,12 +4675,10 @@ namespace smt { bool theory_str::get_arith_value(expr* e, rational& val) const { context& ctx = get_context(); ast_manager & m = get_manager(); - - // safety - if (!ctx.e_internalized(e)) { + // safety + if (!ctx.e_internalized(e)) { return false; - } - + } // if an integer constant exists in the eqc, it should be the root enode * en_e = ctx.get_enode(e); enode * root_e = en_e->get_root(); @@ -5003,7 +4929,7 @@ namespace smt { implyR = boolVar; } else { //implyR = Z3_mk_eq(ctx, boolVar, Z3_mk_false(ctx)); - implyR = m.mk_not(boolVar); + implyR = mk_not(m, boolVar); } } else { // ------------------------------------------------------------------------------------------------ @@ -5032,7 +4958,7 @@ namespace smt { litems.push_back(ctx.mk_eq_atom(substrAst, aConcat)); } //implyR = Z3_mk_eq(ctx, boolVar, Z3_mk_false(ctx)); - implyR = m.mk_not(boolVar); + implyR = mk_not(m, boolVar); break; } } @@ -5071,7 +4997,7 @@ namespace smt { implyR = boolVar; } else { // implyR = Z3_mk_eq(ctx, boolVar, Z3_mk_false(ctx)); - implyR = m.mk_not(boolVar); + implyR = mk_not(m, boolVar); } } @@ -5141,7 +5067,7 @@ namespace smt { litems.push_back(ctx.mk_eq_atom(substrAst, aConcat)); } expr_ref implyLHS(mk_and(litems), m); - expr_ref implyR(m.mk_not(boolVar), m); + expr_ref implyR(mk_not(m, boolVar), m); assert_implication(implyLHS, implyR); break; } @@ -6510,7 +6436,7 @@ namespace smt { if (matchRes) { assert_implication(implyL, boolVar); } else { - assert_implication(implyL, m.mk_not(boolVar)); + assert_implication(implyL, mk_not(m, boolVar)); } } } @@ -6598,7 +6524,7 @@ namespace smt { << arg1_str << "\" + \"" << arg2_str << "\" != \"" << const_str << "\"" << "\n";); expr_ref equality(ctx.mk_eq_atom(concat, str), m); - expr_ref diseq(m.mk_not(equality), m); + expr_ref diseq(mk_not(m, equality), m); assert_axiom(diseq); return; } @@ -6616,7 +6542,7 @@ namespace smt { "\" is longer than \"" << const_str << "\"," << " so cannot be concatenated with anything to form it" << "\n";); expr_ref equality(ctx.mk_eq_atom(newConcat, str), m); - expr_ref diseq(m.mk_not(equality), m); + expr_ref diseq(mk_not(m, equality), m); assert_axiom(diseq); return; } else { @@ -6630,7 +6556,7 @@ namespace smt { << "actually \"" << arg2_str << "\"" << "\n";); expr_ref equality(ctx.mk_eq_atom(newConcat, str), m); - expr_ref diseq(m.mk_not(equality), m); + expr_ref diseq(mk_not(m, equality), m); assert_axiom(diseq); return; } else { @@ -8883,15 +8809,30 @@ namespace smt { if (concat_lhs_haseqc && concat_rhs_haseqc && !var_haseqc) { TRACE("str", tout << "backpropagate into " << mk_pp(var, m) << " = " << mk_pp(concat, m) << std::endl << "LHS ~= " << mk_pp(concat_lhs_str, m) << " RHS ~= " << mk_pp(concat_rhs_str, m) << std::endl;); + zstring lhsString, rhsString; u.str.is_string(concat_lhs_str, lhsString); u.str.is_string(concat_rhs_str, rhsString); zstring concatString = lhsString + rhsString; - expr_ref lhs1(ctx.mk_eq_atom(concat_lhs, concat_lhs_str), m); - expr_ref lhs2(ctx.mk_eq_atom(concat_rhs, concat_rhs_str), m); - expr_ref lhs(m.mk_and(lhs1, lhs2), m); - expr_ref rhs(ctx.mk_eq_atom(concat, mk_string(concatString)), m); - assert_implication(lhs, rhs); + + // special handling: don't assert that string constants are equal to themselves + expr_ref_vector lhs_terms(m); + if (!u.str.is_string(concat_lhs)) { + lhs_terms.push_back(ctx.mk_eq_atom(concat_lhs, concat_lhs_str)); + } + if (!u.str.is_string(concat_rhs)) { + lhs_terms.push_back(ctx.mk_eq_atom(concat_rhs, concat_rhs_str)); + } + + if (lhs_terms.empty()) { + // no assumptions on LHS + expr_ref rhs(ctx.mk_eq_atom(concat, mk_string(concatString)), m); + assert_axiom(rhs); + } else { + expr_ref lhs(mk_and(lhs_terms), m); + expr_ref rhs(ctx.mk_eq_atom(concat, mk_string(concatString)), m); + assert_implication(lhs, rhs); + } backpropagation_occurred = true; } } @@ -9887,6 +9828,21 @@ namespace smt { expr_ref freeVarLen(mk_strlen(freeVar), m); SASSERT(freeVarLen); + { + rational freeVar_len_value; + if (get_len_value(freeVar, freeVar_len_value)) { + TRACE("str", tout << "special case: length of freeVar is known to be " << freeVar_len_value << std::endl;); + expr_ref concreteOption(ctx.mk_eq_atom(indicator, mk_string(freeVar_len_value.to_string().c_str()) ), m); + expr_ref concreteValue(ctx.mk_eq_atom( + ctx.mk_eq_atom(indicator, mk_string(freeVar_len_value.to_string().c_str()) ), + ctx.mk_eq_atom(freeVarLen, m_autil.mk_numeral(freeVar_len_value, true))), m); + expr_ref finalAxiom(m.mk_and(concreteOption, concreteValue), m); + SASSERT(finalAxiom); + m_trail.push_back(finalAxiom); + return finalAxiom; + } + } + expr_ref_vector orList(m); expr_ref_vector andList(m); @@ -10201,6 +10157,16 @@ namespace smt { } refresh_theory_var(firstTester); + { + rational freeVar_len_value; + if (get_len_value(freeVar, freeVar_len_value)) { + TRACE("str", tout << "special case: length of freeVar is known to be " << freeVar_len_value << std::endl;); + midPoint = freeVar_len_value; + upperBound = midPoint * 2; + windowSize = upperBound; + } + } + binary_search_len_tester_stack[freeVar].push_back(firstTester); m_trail_stack.push(binary_search_trail(binary_search_len_tester_stack, freeVar)); binary_search_info new_info(lowerBound, midPoint, upperBound, windowSize); @@ -10464,6 +10430,9 @@ namespace smt { // iterate parents if (standAlone) { // I hope this works! + if (!ctx.e_internalized(freeVar)) { + ctx.internalize(freeVar, false); + } enode * e_freeVar = ctx.get_enode(freeVar); enode_vector::iterator it = e_freeVar->begin_parents(); for (; it != e_freeVar->end_parents(); ++it) { diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 686fcdd57..acac8cad1 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -18,9 +18,12 @@ #define _THEORY_STR_H_ #include "util/trail.h" +#include "util/union_find.h" +#include "util/scoped_ptr_vector.h" #include "ast/ast_pp.h" #include "ast/arith_decl_plugin.h" #include "ast/rewriter/th_rewriter.h" +#include "ast/seq_decl_plugin.h" #include "smt/smt_theory.h" #include "smt/params/theory_str_params.h" #include "smt/proto_model/value_factory.h" @@ -29,8 +32,6 @@ #include #include #include -#include "ast/seq_decl_plugin.h" -#include "util/union_find.h" namespace smt { @@ -292,6 +293,7 @@ protected: bool avoidLoopCut; bool loopDetected; obj_map > cut_var_map; + scoped_ptr_vector m_cut_allocs; expr_ref m_theoryStrOverlapAssumption_term; obj_hashtable variable_set; diff --git a/src/smt/watch_list.h b/src/smt/watch_list.h index 19d3f20a8..1cc29da5a 100644 --- a/src/smt/watch_list.h +++ b/src/smt/watch_list.h @@ -85,6 +85,10 @@ namespace smt { watch_list(): m_data(0) { } + + watch_list(watch_list && other) : m_data(0) { + std::swap(m_data, other.m_data); + } ~watch_list() { destroy(); diff --git a/src/solver/CMakeLists.txt b/src/solver/CMakeLists.txt index 56864a691..1ffdc35e1 100644 --- a/src/solver/CMakeLists.txt +++ b/src/solver/CMakeLists.txt @@ -6,6 +6,7 @@ z3_add_component(solver smt_logics.cpp solver.cpp solver_na2as.cpp + solver_pool.cpp solver2tactic.cpp tactic2solver.cpp COMPONENT_DEPENDENCIES diff --git a/src/solver/check_sat_result.cpp b/src/solver/check_sat_result.cpp index f1bedfc08..1308c52de 100644 --- a/src/solver/check_sat_result.cpp +++ b/src/solver/check_sat_result.cpp @@ -53,7 +53,7 @@ void simple_check_sat_result::get_unsat_core(ptr_vector & r) { r.append(m_core.size(), m_core.c_ptr()); } -void simple_check_sat_result::get_model(model_ref & m) { +void simple_check_sat_result::get_model_core(model_ref & m) { if (m_status != l_false) m = m_model; else diff --git a/src/solver/check_sat_result.h b/src/solver/check_sat_result.h index 38720a5e9..572c4d88d 100644 --- a/src/solver/check_sat_result.h +++ b/src/solver/check_sat_result.h @@ -23,6 +23,7 @@ Notes: #include "util/lbool.h" #include "util/statistics.h" #include "util/event_handler.h" +#include "tactic/model_converter.h" /** \brief Abstract interface for the result of a (check-sat) like command. @@ -40,6 +41,7 @@ class check_sat_result { protected: unsigned m_ref_count; lbool m_status; + model_converter_ref m_mc0; public: check_sat_result():m_ref_count(0), m_status(l_undef) {} virtual ~check_sat_result() {} @@ -54,7 +56,13 @@ public: get_unsat_core(core); r.append(core.size(), core.c_ptr()); } - virtual void get_model(model_ref & m) = 0; + void set_model_converter(model_converter* mc) { m_mc0 = mc; } + model_converter* mc0() { return m_mc0.get(); } + virtual void get_model_core(model_ref & m) = 0; + void get_model(model_ref & m) { + get_model_core(m); + if (m && mc0()) (*mc0())(m); + } virtual proof * get_proof() = 0; virtual std::string reason_unknown() const = 0; virtual void set_reason_unknown(char const* msg) = 0; @@ -80,7 +88,7 @@ struct simple_check_sat_result : public check_sat_result { virtual ast_manager& get_manager() const { return m_proof.get_manager(); } virtual void collect_statistics(statistics & st) const; virtual void get_unsat_core(ptr_vector & r); - virtual void get_model(model_ref & m); + virtual void get_model_core(model_ref & m); virtual proof * get_proof(); virtual std::string reason_unknown() const; virtual void get_labels(svector & r); diff --git a/src/solver/combined_solver.cpp b/src/solver/combined_solver.cpp index a67fd724d..05e75a9a4 100644 --- a/src/solver/combined_solver.cpp +++ b/src/solver/combined_solver.cpp @@ -147,6 +147,7 @@ public: } virtual void updt_params(params_ref const & p) { + solver::updt_params(p); m_solver1->updt_params(p); m_solver2->updt_params(p); updt_local_params(p); @@ -280,10 +281,6 @@ public: return m_solver1->get_num_assumptions() + m_solver2->get_num_assumptions(); } - virtual expr_ref lookahead(expr_ref_vector const& assumptions, expr_ref_vector const& candidates) { - return m_solver1->lookahead(assumptions, candidates); - } - virtual expr_ref cube() { return m_solver1->cube(); } @@ -294,8 +291,8 @@ public: return m_solver2->get_assumption(idx - c1); } - virtual std::ostream& display(std::ostream & out) const { - return m_solver1->display(out); + virtual std::ostream& display(std::ostream & out, unsigned n, expr* const* es) const { + return m_solver1->display(out, n, es); } virtual void collect_statistics(statistics & st) const { @@ -311,7 +308,7 @@ public: m_solver2->get_unsat_core(r); } - virtual void get_model(model_ref & m) { + virtual void get_model_core(model_ref & m) { if (m_use_solver1_results) m_solver1->get_model(m); else diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp index 0f5a72831..de0834b9d 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -16,12 +16,14 @@ Author: Notes: --*/ -#include "solver/solver.h" -#include "model/model_evaluator.h" +#include "util/common_msgs.h" +#include "util/stopwatch.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "ast/ast_pp_util.h" -#include "util/common_msgs.h" +#include "tactic/model_converter.h" +#include "solver/solver.h" +#include "model/model_evaluator.h" unsigned solver::get_num_assertions() const { @@ -34,13 +36,31 @@ expr * solver::get_assertion(unsigned idx) const { return 0; } -std::ostream& solver::display(std::ostream & out) const { +std::ostream& solver::display(std::ostream & out, unsigned n, expr* const* assumptions) const { expr_ref_vector fmls(get_manager()); + stopwatch sw; + sw.start(); + // std::cout << "display 1\n"; get_assertions(fmls); + // std::cout << "display 2 " << sw.get_current_seconds() << "\n"; ast_pp_util visitor(get_manager()); + model_converter_ref mc = get_model_converter(); + // std::cout << "display 3 " << sw.get_current_seconds() << "\n"; + if (mc.get()) { + mc->collect(visitor); + } + // std::cout << "display 4 " << sw.get_current_seconds() << "\n"; visitor.collect(fmls); + // std::cout << "display 5 " << sw.get_current_seconds() << "\n"; + visitor.collect(n, assumptions); visitor.display_decls(out); + // std::cout << "display 6 " << sw.get_current_seconds() << "\n"; visitor.display_asserts(out, fmls, true); + // std::cout << "display 7 " << sw.get_current_seconds() << "\n"; + if (mc.get()) { + mc->display(out); + } + // std::cout << "display 8 " << sw.get_current_seconds() << "\n"; return out; } diff --git a/src/solver/solver.h b/src/solver/solver.h index abd9b01f2..3719b5a6a 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -24,6 +24,7 @@ Notes: #include "util/params.h" class solver; +class model_converter; class solver_factory { public: @@ -43,6 +44,7 @@ public: - results based on check_sat_result API */ class solver : public check_sat_result { + params_ref m_params; public: virtual ~solver() {} @@ -54,7 +56,12 @@ public: /** \brief Update the solver internal settings. */ - virtual void updt_params(params_ref const & p) { } + virtual void updt_params(params_ref const & p) { m_params.copy(p); } + + /** + \brief Retrieve set of parameters set on solver. + */ + virtual params_ref const& get_params() { return m_params; } /** \brief Store in \c r a description of the configuration @@ -94,7 +101,7 @@ public: \brief Add a lemma to the assertion stack. A lemma is assumed to be a consequence of already asserted formulas. The solver is free to ignore lemmas. */ - virtual void assert_lemma(expr * t) = 0; + virtual void assert_lemma(expr * t) {} /** \brief Create a backtracking point. @@ -182,23 +189,18 @@ public: \brief extract a lookahead candidates for branching. */ - virtual expr_ref lookahead(expr_ref_vector const& assumptions, expr_ref_vector const& candidates) = 0; - - /** - \brief extract a lookahead candidates for branching. - */ - virtual expr_ref cube() = 0; - /** - \brief extract learned lemmas. - */ - virtual void get_lemmas(expr_ref_vector& lemmas) {} - /** \brief Display the content of this solver. */ - virtual std::ostream& display(std::ostream & out) const; + virtual std::ostream& display(std::ostream & out, unsigned n = 0, expr* const* assumptions = nullptr) const; + + /** + \brief expose model converter when solver produces partially reduced set of assertions. + */ + + virtual model_converter_ref get_model_converter() const { return m_mc0; } class scoped_push { solver& s; @@ -217,4 +219,6 @@ protected: }; +typedef ref solver_ref; + #endif diff --git a/src/solver/solver2tactic.cpp b/src/solver/solver2tactic.cpp index 4f7b750f8..eea3aa9c5 100644 --- a/src/solver/solver2tactic.cpp +++ b/src/solver/solver2tactic.cpp @@ -122,6 +122,7 @@ public: local_solver->get_model(mdl); mc = model2model_converter(mdl.get()); mc = concat(fmc.get(), mc.get()); + mc = concat(local_solver->mc0(), mc.get()); } in->reset(); result.push_back(in.get()); @@ -150,14 +151,8 @@ public: if (m.canceled()) { throw tactic_exception(Z3_CANCELED_MSG); } - if (in->models_enabled()) { - model_ref mdl; - local_solver->get_model(mdl); - if (mdl) { - mc = model2model_converter(mdl.get()); - mc = concat(fmc.get(), mc.get()); - } - } + mc = local_solver->get_model_converter(); + mc = concat(fmc.get(), mc.get()); in->reset(); unsigned sz = local_solver->get_num_assertions(); for (unsigned i = 0; i < sz; ++i) { diff --git a/src/solver/solver_pool.cpp b/src/solver/solver_pool.cpp new file mode 100644 index 000000000..b2da00bfa --- /dev/null +++ b/src/solver/solver_pool.cpp @@ -0,0 +1,322 @@ +/** +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + solver_pool.cpp + +Abstract: + + Maintain a pool of solvers + +Author: + + Nikolaj Bjorner + +Notes: + +--*/ + +#include "solver/solver_pool.h" +#include "solver/solver_na2as.h" +#include "ast/proofs/proof_utils.h" +#include "ast/ast_util.h" + +class pool_solver : public solver_na2as { + solver_pool& m_pool; + app_ref m_pred; + proof_ref m_proof; + ref m_base; + expr_ref_vector m_assertions; + unsigned m_head; + expr_ref_vector m_flat; + bool m_pushed; + bool m_in_delayed_scope; + unsigned m_dump_counter; + + bool is_virtual() const { return !m.is_true(m_pred); } +public: + pool_solver(solver* b, solver_pool& pool, app_ref& pred): + solver_na2as(pred.get_manager()), + m_pool(pool), + m_pred(pred), + m_proof(m), + m_base(b), + m_assertions(m), + m_head(0), + m_flat(m), + m_pushed(false), + m_in_delayed_scope(false), + m_dump_counter(0) { + if (is_virtual()) { + solver_na2as::assert_expr(m.mk_true(), pred); + } + } + + virtual ~pool_solver() { + if (m_pushed) pop(get_scope_level()); + if (is_virtual()) { + m_pred = m.mk_not(m_pred); + m_base->assert_expr(m_pred); + } + } + + solver* base_solver() { return m_base.get(); } + + virtual solver* translate(ast_manager& m, params_ref const& p) { UNREACHABLE(); return nullptr; } + virtual void updt_params(params_ref const& p) { solver::updt_params(p); m_base->updt_params(p); } + virtual void collect_param_descrs(param_descrs & r) { m_base->collect_param_descrs(r); } + virtual void collect_statistics(statistics & st) const { m_base->collect_statistics(st); } + + virtual void get_unsat_core(ptr_vector & r) { + m_base->get_unsat_core(r); + unsigned j = 0; + for (unsigned i = 0; i < r.size(); ++i) + if (m_pred != r[i]) + r[j++] = r[i]; + r.shrink(j); + } + + virtual unsigned get_num_assumptions() const { + unsigned sz = solver_na2as::get_num_assumptions(); + return is_virtual() ? sz - 1 : sz; + } + + virtual proof * get_proof() { + scoped_watch _t_(m_pool.m_proof_watch); + if (!m_proof.get()) { + elim_aux_assertions pc(m_pred); + m_proof = m_base->get_proof(); + pc(m, m_proof, m_proof); + } + return m_proof; + } + + void internalize_assertions() { + SASSERT(!m_pushed || m_head == m_assertions.size()); + for (unsigned sz = m_assertions.size(); m_head < sz; ++m_head) { + expr_ref f(m); + f = m.mk_implies(m_pred, (m_assertions.get(m_head))); + m_base->assert_expr(f); + } + } + + virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) { + SASSERT(!m_pushed || get_scope_level() > 0); + m_proof.reset(); + scoped_watch _t_(m_pool.m_check_watch); + m_pool.m_stats.m_num_checks++; + + stopwatch sw; + sw.start(); + internalize_assertions(); + lbool res = m_base->check_sat(num_assumptions, assumptions); + sw.stop(); + switch (res) { + case l_true: + m_pool.m_check_sat_watch.add(sw); + m_pool.m_stats.m_num_sat_checks++; + break; + case l_undef: + m_pool.m_check_undef_watch.add(sw); + m_pool.m_stats.m_num_undef_checks++; + break; + default: + break; + } + set_status(res); + + if (false /*m_dump_benchmarks && sw.get_seconds() >= m_pool.fparams().m_dump_min_time*/) { + std::stringstream file_name; + file_name << "virt_solver"; + if (is_virtual()) { file_name << "_" << m_pred->get_decl()->get_name(); } + file_name << "_" << (m_dump_counter++) << ".smt2"; + + std::ofstream out(file_name.str().c_str()); + if (!out) { verbose_stream() << "could not open file " << file_name.str() << " for output\n"; } + + out << "(set-info :status "; + switch (res) { + case l_true: out << "sat"; break; + case l_false: out << "unsat"; break; + case l_undef: out << "unknown"; break; + } + out << ")\n"; + m_base->display(out, num_assumptions, assumptions); + bool first = true; + out << "(check-sat"; + for (unsigned i = 0; i < num_assumptions; ++i) { + out << " " << mk_pp(assumptions[i], m); + } + out << ")"; + out << "(exit)\n"; + ::statistics st; + m_base->collect_statistics(st); + st.update("time", sw.get_seconds()); + st.display_smt2(out); + out.close(); + } + return res; + } + + virtual void push_core() { + SASSERT(!m_pushed || get_scope_level() > 0); + if (m_in_delayed_scope) { + // second push + internalize_assertions(); + m_base->push(); + m_pushed = true; + m_in_delayed_scope = false; + } + + if (!m_pushed) { + m_in_delayed_scope = true; + } + else { + SASSERT(m_pushed); + SASSERT(!m_in_delayed_scope); + m_base->push(); + } + } + + virtual void pop_core(unsigned n) { + SASSERT(!m_pushed || get_scope_level() > 0); + if (m_pushed) { + SASSERT(!m_in_delayed_scope); + m_base->pop(n); + m_pushed = get_scope_level() - n > 0; + } + else { + m_in_delayed_scope = get_scope_level() - n > 0; + } + } + + virtual void assert_expr(expr * e) { + SASSERT(!m_pushed || get_scope_level() > 0); + if (m.is_true(e)) return; + if (m_in_delayed_scope) { + internalize_assertions(); + m_base->push(); + m_pushed = true; + m_in_delayed_scope = false; + } + + if (m_pushed) { + m_base->assert_expr(e); + } + else { + m_flat.push_back(e); + flatten_and(m_flat); + m_assertions.append(m_flat); + m_flat.reset(); + } + } + + virtual void get_model_core(model_ref & _m) { m_base->get_model_core(_m); } + + virtual expr * get_assumption(unsigned idx) const { + return solver_na2as::get_assumption(idx + is_virtual()); + } + + virtual std::string reason_unknown() const { return m_base->reason_unknown(); } + virtual void set_reason_unknown(char const* msg) { return m_base->set_reason_unknown(msg); } + virtual void get_labels(svector & r) { return m_base->get_labels(r); } + virtual void set_progress_callback(progress_callback * callback) { m_base->set_progress_callback(callback); } + + virtual expr_ref cube() { return expr_ref(m.mk_true(), m); } + + virtual ast_manager& get_manager() const { return m_base->get_manager(); } + + void refresh(solver* new_base) { + SASSERT(!m_pushed); + m_head = 0; + m_base = new_base; + } + + void reset() { + SASSERT(!m_pushed); + m_head = 0; + m_assertions.reset(); + m_pool.refresh(m_base.get()); + } +}; + +solver_pool::solver_pool(solver* base_solver, unsigned num_solvers_per_pool): + m_base_solver(base_solver), + m_num_solvers_per_pool(num_solvers_per_pool), + m_num_solvers_in_last_pool(0) +{} + + +ptr_vector solver_pool::get_base_solvers() const { + ptr_vector solvers; + for (solver* s0 : m_solvers) { + pool_solver* s = dynamic_cast(s0); + if (!solvers.contains(s->base_solver())) { + solvers.push_back(s->base_solver()); + } + } + return solvers; +} + +void solver_pool::collect_statistics(statistics &st) const { + ptr_vector solvers = get_base_solvers(); + for (solver* s : solvers) s->collect_statistics(st); + st.update("time.pool_solver.smt.total", m_check_watch.get_seconds()); + st.update("time.pool_solver.smt.total.sat", m_check_sat_watch.get_seconds()); + st.update("time.pool_solver.smt.total.undef", m_check_undef_watch.get_seconds()); + st.update("time.pool_solver.proof", m_proof_watch.get_seconds()); + st.update("pool_solver.checks", m_stats.m_num_checks); + st.update("pool_solver.checks.sat", m_stats.m_num_sat_checks); + st.update("pool_solver.checks.undef", m_stats.m_num_undef_checks); +} + +void solver_pool::reset_statistics() { +#if 0 + ptr_vector solvers = get_base_solvers(); + for (solver* s : solvers) { + s->reset_statistics(); + } +#endif + m_stats.reset(); + m_check_sat_watch.reset(); + m_check_undef_watch.reset(); + m_check_watch.reset(); + m_proof_watch.reset(); +} + +solver* solver_pool::mk_solver() { + ref base_solver; + ast_manager& m = m_base_solver->get_manager(); + if (m_solvers.empty() || m_num_solvers_in_last_pool == m_num_solvers_per_pool) { + base_solver = m_base_solver->translate(m, m_base_solver->get_params()); + m_num_solvers_in_last_pool = 0; + } + else { + base_solver = dynamic_cast(m_solvers.back())->base_solver(); + } + m_num_solvers_in_last_pool++; + std::stringstream name; + name << "vsolver#" << m_solvers.size(); + app_ref pred(m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()), m); + pool_solver* solver = alloc(pool_solver, base_solver.get(), *this, pred); + m_solvers.push_back(solver); + return solver; +} + +void solver_pool::reset_solver(solver* s) { + pool_solver* ps = dynamic_cast(s); + SASSERT(ps); + if (ps) ps->reset(); +} + +void solver_pool::refresh(solver* base_solver) { + ast_manager& m = m_base_solver->get_manager(); + ref new_base = m_base_solver->translate(m, m_base_solver->get_params()); + for (solver* s0 : m_solvers) { + pool_solver* s = dynamic_cast(s0); + if (base_solver == s->base_solver()) { + s->refresh(new_base.get()); + } + } +} diff --git a/src/solver/solver_pool.h b/src/solver/solver_pool.h new file mode 100644 index 000000000..d676ca54d --- /dev/null +++ b/src/solver/solver_pool.h @@ -0,0 +1,69 @@ +/** +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + solver_pool.h + +Abstract: + + Maintain a pool of solvers + +Author: + + Nikolaj Bjorner + Arie Gurfinkel + +Notes: + + This is a revision of spacer_virtual_solver by Arie Gurfinkel + +--*/ +#ifndef SOLVER_POOL_H_ +#define SOLVER_POOL_H_ + +#include "solver/solver.h" +#include "util/stopwatch.h" + +class pool_solver; + +class solver_pool { + + friend class pool_solver; + + struct stats { + unsigned m_num_checks; + unsigned m_num_sat_checks; + unsigned m_num_undef_checks; + stats() { reset(); } + void reset() { memset(this, 0, sizeof(*this)); } + }; + + ref m_base_solver; + unsigned m_num_solvers_per_pool; + unsigned m_num_solvers_in_last_pool; + sref_vector m_solvers; + stats m_stats; + + stopwatch m_check_watch; + stopwatch m_check_sat_watch; + stopwatch m_check_undef_watch; + stopwatch m_proof_watch; + + void refresh(solver* s); + + ptr_vector get_base_solvers() const; + +public: + solver_pool(solver* base_solver, unsigned num_solvers_per_pool); + + void collect_statistics(statistics &st) const; + void reset_statistics(); + + solver* mk_solver(); + + void reset_solver(solver* s); +}; + + +#endif diff --git a/src/solver/tactic2solver.cpp b/src/solver/tactic2solver.cpp index 7d9388f50..dc9f1b25f 100644 --- a/src/solver/tactic2solver.cpp +++ b/src/solver/tactic2solver.cpp @@ -36,8 +36,8 @@ class tactic2solver : public solver_na2as { unsigned_vector m_scopes; ref m_result; tactic_ref m_tactic; + ref m_mc; symbol m_logic; - params_ref m_params; bool m_produce_models; bool m_produce_proofs; bool m_produce_unsat_cores; @@ -64,7 +64,7 @@ public: virtual void collect_statistics(statistics & st) const; virtual void get_unsat_core(ptr_vector & r); - virtual void get_model(model_ref & m); + virtual void get_model_core(model_ref & m); virtual proof * get_proof(); virtual std::string reason_unknown() const; virtual void set_reason_unknown(char const* msg); @@ -77,15 +77,13 @@ public: virtual ast_manager& get_manager() const; - virtual expr_ref lookahead(expr_ref_vector const& assumptions, expr_ref_vector const& candidates) { - ast_manager& m = get_manager(); - return expr_ref(m.mk_true(), m); - } virtual expr_ref cube() { ast_manager& m = get_manager(); return expr_ref(m.mk_true(), m); } + virtual model_converter_ref get_model_converter() const { return m_mc; } + }; ast_manager& tactic2solver::get_manager() const { return m_assertions.get_manager(); } @@ -96,7 +94,7 @@ tactic2solver::tactic2solver(ast_manager & m, tactic * t, params_ref const & p, m_tactic = t; m_logic = logic; - m_params = p; + solver::updt_params(p); m_produce_models = produce_models; m_produce_proofs = produce_proofs; @@ -107,7 +105,7 @@ tactic2solver::~tactic2solver() { } void tactic2solver::updt_params(params_ref const & p) { - m_params.append(p); + solver::updt_params(p); } void tactic2solver::collect_param_descrs(param_descrs & r) { @@ -143,7 +141,7 @@ lbool tactic2solver::check_sat_core(unsigned num_assumptions, expr * const * ass m_result = alloc(simple_check_sat_result, m); m_tactic->cleanup(); m_tactic->set_logic(m_logic); - m_tactic->updt_params(m_params); // parameters are allowed to overwrite logic. + m_tactic->updt_params(get_params()); // parameters are allowed to overwrite logic. goal_ref g = alloc(goal, m, m_produce_proofs, m_produce_models, m_produce_unsat_cores); unsigned sz = m_assertions.size(); @@ -157,12 +155,12 @@ lbool tactic2solver::check_sat_core(unsigned num_assumptions, expr * const * ass } model_ref md; - proof_ref pr(m); + proof_ref pr(m); expr_dependency_ref core(m); std::string reason_unknown = "unknown"; labels_vec labels; try { - switch (::check_sat(*m_tactic, g, md, labels, pr, core, reason_unknown)) { + switch (::check_sat(*m_tactic, g, md, m_mc, labels, pr, core, reason_unknown)) { case l_true: m_result->set_status(l_true); break; @@ -230,9 +228,9 @@ void tactic2solver::get_unsat_core(ptr_vector & r) { } } -void tactic2solver::get_model(model_ref & m) { +void tactic2solver::get_model_core(model_ref & m) { if (m_result.get()) - m_result->get_model(m); + m_result->get_model_core(m); } proof * tactic2solver::get_proof() { diff --git a/src/tactic/CMakeLists.txt b/src/tactic/CMakeLists.txt index e7cfdb644..74379b30f 100644 --- a/src/tactic/CMakeLists.txt +++ b/src/tactic/CMakeLists.txt @@ -3,6 +3,7 @@ z3_add_component(tactic equiv_proof_converter.cpp extension_model_converter.cpp filter_model_converter.cpp + generic_model_converter.cpp goal.cpp goal_num_occurs.cpp goal_shared_occs.cpp diff --git a/src/tactic/arith/degree_shift_tactic.cpp b/src/tactic/arith/degree_shift_tactic.cpp index 6344c4c51..80b7a416c 100644 --- a/src/tactic/arith/degree_shift_tactic.cpp +++ b/src/tactic/arith/degree_shift_tactic.cpp @@ -21,7 +21,7 @@ Revision History: --*/ #include "tactic/tactical.h" #include "tactic/filter_model_converter.h" -#include "tactic/extension_model_converter.h" +#include "tactic/generic_model_converter.h" #include "util/cooperate.h" #include "ast/arith_decl_plugin.h" #include "tactic/core/simplify_tactic.h" @@ -127,9 +127,7 @@ class degree_shift_tactic : public tactic { void visit_args(expr * t, expr_fast_mark1 & visited) { if (is_app(t)) { - unsigned num_args = to_app(t)->get_num_args(); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = to_app(t)->get_arg(i); + for (expr * arg : *to_app(t)) { save_degree(arg, m_one); visit(arg, visited); } @@ -166,11 +164,9 @@ class degree_shift_tactic : public tactic { void display_candidates(std::ostream & out) { out << "candidates:\n"; - obj_map::iterator it = m_var2degree.begin(); - obj_map::iterator end = m_var2degree.end(); - for (; it != end; ++it) { - if (!it->m_value.is_one()) { - out << "POWER: " << it->m_value << "\n" << mk_ismt2_pp(it->m_key, m) << "\n"; + for (auto const& kv : m_var2degree) { + if (!kv.m_value.is_one()) { + out << "POWER: " << kv.m_value << "\n" << mk_ismt2_pp(kv.m_key, m) << "\n"; } } } @@ -189,48 +185,42 @@ class degree_shift_tactic : public tactic { void discard_non_candidates() { m_pinned.reset(); ptr_vector to_delete; - obj_map::iterator it = m_var2degree.begin(); - obj_map::iterator end = m_var2degree.end(); - for (; it != end; ++it) { - if (it->m_value.is_one()) - to_delete.push_back(it->m_key); + for (auto const& kv : m_var2degree) { + if (kv.m_value.is_one()) + to_delete.push_back(kv.m_key); else - m_pinned.push_back(it->m_key); // make sure it is not deleted during simplifications + m_pinned.push_back(kv.m_key); // make sure it is not deleted during simplifications } - ptr_vector::iterator it2 = to_delete.begin(); - ptr_vector::iterator end2 = to_delete.end(); - for (; it2 != end2; ++it2) - m_var2degree.erase(*it2); + for (app* a : to_delete) + m_var2degree.erase(a); } void prepare_substitution(model_converter_ref & mc) { SASSERT(!m_var2degree.empty()); filter_model_converter * fmc = 0; - extension_model_converter * xmc = 0; + generic_model_converter * xmc = 0; if (m_produce_models) { fmc = alloc(filter_model_converter, m); - xmc = alloc(extension_model_converter, m); + xmc = alloc(generic_model_converter, m); mc = concat(fmc, xmc); } - obj_map::iterator it = m_var2degree.begin(); - obj_map::iterator end = m_var2degree.end(); - for (; it != end; ++it) { - SASSERT(it->m_value.is_int()); - SASSERT(it->m_value >= rational(2)); - app * fresh = m.mk_fresh_const(0, it->m_key->get_decl()->get_range()); + for (auto const& kv : m_var2degree) { + SASSERT(kv.m_value.is_int()); + SASSERT(kv.m_value >= rational(2)); + app * fresh = m.mk_fresh_const(0, kv.m_key->get_decl()->get_range()); m_pinned.push_back(fresh); - m_var2var.insert(it->m_key, fresh); + m_var2var.insert(kv.m_key, fresh); if (m_produce_models) { fmc->insert(fresh->get_decl()); - xmc->insert(it->m_key->get_decl(), mk_power(fresh, rational(1)/it->m_value)); + xmc->add(kv.m_key->get_decl(), mk_power(fresh, rational(1)/kv.m_value)); } if (m_produce_proofs) { - expr * s = mk_power(it->m_key, it->m_value); + expr * s = mk_power(kv.m_key, kv.m_value); expr * eq = m.mk_eq(fresh, s); proof * pr1 = m.mk_def_intro(eq); proof * result_pr = m.mk_apply_def(fresh, s, pr1); m_pinned.push_back(result_pr); - m_var2pr.insert(it->m_key, result_pr); + m_var2pr.insert(kv.m_key, result_pr); } } } @@ -267,17 +257,15 @@ class degree_shift_tactic : public tactic { } // add >= 0 constraints for variables with even degree - obj_map::iterator it = m_var2degree.begin(); - obj_map::iterator end = m_var2degree.end(); - for (; it != end; ++it) { - SASSERT(it->m_value.is_int()); - SASSERT(it->m_value >= rational(2)); - if (it->m_value.is_even()) { - app * new_var = m_var2var.find(it->m_key); + for (auto const& kv : m_var2degree) { + SASSERT(kv.m_value.is_int()); + SASSERT(kv.m_value >= rational(2)); + if (kv.m_value.is_even()) { + app * new_var = m_var2var.find(kv.m_key); app * new_c = m_autil.mk_ge(new_var, m_autil.mk_numeral(rational(0), false)); proof * new_pr = 0; if (m_produce_proofs) { - proof * pr = m_var2pr.find(it->m_key); + proof * pr = m_var2pr.find(kv.m_key); new_pr = m.mk_th_lemma(m_autil.get_family_id(), new_c, 1, &pr); } g->assert_expr(new_c, new_pr, 0); diff --git a/src/tactic/arith/elim01_tactic.cpp b/src/tactic/arith/elim01_tactic.cpp index b6fd5eee7..68534c60b 100644 --- a/src/tactic/arith/elim01_tactic.cpp +++ b/src/tactic/arith/elim01_tactic.cpp @@ -113,6 +113,11 @@ public: } return mc; } + + virtual void display(std::ostream & out) { + out << "(elim01-model-converter)\n"; + } + }; diff --git a/src/tactic/arith/eq2bv_tactic.cpp b/src/tactic/arith/eq2bv_tactic.cpp index 255665b56..492b12fcf 100644 --- a/src/tactic/arith/eq2bv_tactic.cpp +++ b/src/tactic/arith/eq2bv_tactic.cpp @@ -107,12 +107,18 @@ class eq2bv_tactic : public tactic { virtual model_converter* translate(ast_translation & translator) { bvmc* v = alloc(bvmc); - obj_map::iterator it = m_map.begin(), end = m_map.end(); - for (; it != end; ++it) { - v->m_map.insert(translator(it->m_key), translator(it->m_value)); + for (auto const& kv : m_map) { + v->m_map.insert(translator(kv.m_key), translator(kv.m_value)); } return v; } + + virtual void display(std::ostream & out) { + for (auto const& kv : m_map) { + out << "(model-set " << kv.m_key->get_name() << " " << kv.m_value->get_name() << ")\n"; + } + } + }; public: diff --git a/src/tactic/bv/bit_blaster_model_converter.cpp b/src/tactic/bv/bit_blaster_model_converter.cpp index b21defd44..d8dbb77a4 100644 --- a/src/tactic/bv/bit_blaster_model_converter.cpp +++ b/src/tactic/bv/bit_blaster_model_converter.cpp @@ -34,11 +34,9 @@ struct bit_blaster_model_converter : public model_converter { ast_manager & m() const { return m_vars.get_manager(); } bit_blaster_model_converter(ast_manager & m, obj_map const & const2bits):m_vars(m), m_bits(m) { - obj_map::iterator it = const2bits.begin(); - obj_map::iterator end = const2bits.end(); - for (; it != end; ++it) { - func_decl * v = it->m_key; - expr * bits = it->m_value; + for (auto const& kv : const2bits) { + func_decl * v = kv.m_key; + expr * bits = kv.m_value; SASSERT(!TO_BOOL || is_app_of(bits, m.get_family_id("bv"), OP_MKBV)); SASSERT(TO_BOOL || is_app_of(bits, m.get_family_id("bv"), OP_CONCAT)); m_vars.push_back(v); @@ -155,17 +153,11 @@ struct bit_blaster_model_converter : public model_converter { unsigned bv_sz = to_app(bs)->get_num_args(); expr_ref_vector args(m()); app_ref result(m()); - for (unsigned j = 0; j < bv_sz; ++j) { - expr * bit = to_app(bs)->get_arg(j); + for (expr * bit : *to_app(bs)) { SASSERT(is_uninterp_const(bit)); func_decl * bit_decl = to_app(bit)->get_decl(); expr * bit_val = old_model.get_const_interp(bit_decl); - if (bit_val != 0) { - args.push_back(bit_val); - } - else { - args.push_back(bit); - } + args.push_back(bit_val ? bit_val : bit); } if (TO_BOOL) { @@ -194,14 +186,10 @@ struct bit_blaster_model_converter : public model_converter { } virtual void display(std::ostream & out) { - out << "(bit-blaster-model-converter"; unsigned sz = m_vars.size(); for (unsigned i = 0; i < sz; i++) { - out << "\n (" << m_vars.get(i)->get_name() << " "; - unsigned indent = m_vars.get(i)->get_name().size() + 4; - out << mk_ismt2_pp(m_bits.get(i), m(), indent) << ")"; - } - out << ")" << std::endl; + display_add(out, m(), m_vars.get(i), m_bits.get(i)); + } } protected: @@ -210,10 +198,10 @@ public: virtual model_converter * translate(ast_translation & translator) { bit_blaster_model_converter * res = alloc(bit_blaster_model_converter, translator.to()); - for (unsigned i = 0; i < m_vars.size(); i++) - res->m_vars.push_back(translator(m_vars[i].get())); - for (unsigned i = 0; i < m_bits.size(); i++) - res->m_bits.push_back(translator(m_bits[i].get())); + for (func_decl * v : m_vars) + res->m_vars.push_back(translator(v)); + for (expr* b : m_bits) + res->m_bits.push_back(translator(b)); return res; } }; diff --git a/src/tactic/bv/bit_blaster_tactic.cpp b/src/tactic/bv/bit_blaster_tactic.cpp index 5c6c43778..308b775a4 100644 --- a/src/tactic/bv/bit_blaster_tactic.cpp +++ b/src/tactic/bv/bit_blaster_tactic.cpp @@ -81,7 +81,7 @@ class bit_blaster_tactic : public tactic { new_pr = m().mk_modus_ponens(pr, new_pr); } if (curr != new_curr) { - change = true; + change = true; TRACE("bit_blaster", tout << mk_pp(curr, m()) << " -> " << mk_pp(new_curr, m()) << "\n";); g->update(idx, new_curr, new_pr, g->dep(idx)); } diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 44f920840..a279e441b 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -11,7 +11,9 @@ Abstract: Author: - Nikolaj Bjorner (nbjorner) 2016-2-12 + Nuno Lopes (nlopes) 2016-2-12 + + Nikolaj Bjorner (nbjorner) --*/ @@ -650,11 +652,11 @@ namespace { return false; if (old == intr) return true; - m_scopes.insert(undo_bound(t1, old, false)); + m_scopes.push_back(undo_bound(t1, old, false)); old = intr; } else { m_bound.insert(t1, b); - m_scopes.insert(undo_bound(t1, interval(), true)); + m_scopes.push_back(undo_bound(t1, interval(), true)); } } return true; @@ -692,7 +694,8 @@ namespace { bool was_updated = true; if (b.is_full() && b.tight) { r = m.mk_true(); - } else if (m_bound.find(t1, ctx)) { + } + else if (m_bound.find(t1, ctx)) { if (ctx.implies(b)) { r = m.mk_true(); } @@ -703,12 +706,15 @@ namespace { r = m.mk_eq(t1, m_bv.mk_numeral(rational(intr.l, rational::ui64()), m.get_sort(t1))); } + else { + was_updated = false; + } } else { was_updated = false; } - CTRACE("bv", was_updated, tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << r << "\n";); + TRACE("bv", tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << r << "\n";); if (sign && was_updated) r = m.mk_not(r); } @@ -801,6 +807,10 @@ namespace { return alloc(dom_bv_bounds_simplifier, m, m_params); } + virtual unsigned scope_level() const { + return m_scopes.size(); + } + }; } diff --git a/src/tactic/bv/bv_bounds_tactic.h b/src/tactic/bv/bv_bounds_tactic.h index 6c2ce60ed..1d6748b27 100644 --- a/src/tactic/bv/bv_bounds_tactic.h +++ b/src/tactic/bv/bv_bounds_tactic.h @@ -11,7 +11,8 @@ Abstract: Author: - Nikolaj Bjorner (nbjorner) 2016-2-12 + Nuno Lopes (nlopes) 2016-2-12 + Nikolaj Bjorner (nbjorner) --*/ @@ -21,8 +22,15 @@ Author: tactic * mk_bv_bounds_tactic(ast_manager & m, params_ref const & p = params_ref()); +tactic * mk_dom_bv_bounds_tactic(ast_manager & m, params_ref const & p = params_ref()); + /* ADD_TACTIC("propagate-bv-bounds", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_bv_bounds_tactic(m, p)") + + + ADD_TACTIC("propagate-bv-bounds-new", "propagate bit-vector bounds by simplifying implied or contradictory bounds.", "mk_dom_bv_bounds_tactic(m, p)") + + */ #endif diff --git a/src/tactic/bv/bvarray2uf_rewriter.cpp b/src/tactic/bv/bvarray2uf_rewriter.cpp index 9b67e7011..b92092739 100644 --- a/src/tactic/bv/bvarray2uf_rewriter.cpp +++ b/src/tactic/bv/bvarray2uf_rewriter.cpp @@ -117,7 +117,7 @@ func_decl_ref bvarray2uf_rewriter_cfg::mk_uf_for_array(expr * e) { if (is_uninterp_const(e)) { if (m_emc) m_emc->insert(to_app(e)->get_decl(), - m_array_util.mk_as_array(m_manager.get_sort(e), bv_f)); + m_array_util.mk_as_array(bv_f)); } else if (m_fmc) m_fmc->insert(bv_f); @@ -193,7 +193,7 @@ br_status bvarray2uf_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr if (is_uninterp_const(e)) { if (m_emc) m_emc->insert(e->get_decl(), - m_array_util.mk_as_array(m_manager.get_sort(e), bv_f)); + m_array_util.mk_as_array(bv_f)); } else if (m_fmc) m_fmc->insert(bv_f); @@ -207,7 +207,7 @@ br_status bvarray2uf_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr q = m_manager.mk_forall(1, sorts, names, body); extra_assertions.push_back(q); - result = m_array_util.mk_as_array(f->get_range(), bv_f); + result = m_array_util.mk_as_array(bv_f); TRACE("bvarray2uf_rw", tout << "result: " << mk_ismt2_pp(result, m_manager) << ")" << std::endl;); res = BR_DONE; @@ -234,7 +234,7 @@ br_status bvarray2uf_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr if (is_bv_array(t)) { // From [1]: For every array term t we create a fresh uninterpreted function f_t. f_t = mk_uf_for_array(t); - result = m_array_util.mk_as_array(m_manager.get_sort(t), f_t); + result = m_array_util.mk_as_array(f_t); res = BR_DONE; } else if (has_bv_arrays) { @@ -274,7 +274,7 @@ br_status bvarray2uf_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr expr * v = args[0]; func_decl_ref f_t(mk_uf_for_array(t), m_manager); - result = m_array_util.mk_as_array(f->get_range(), f_t); + result = m_array_util.mk_as_array(f_t); res = BR_DONE; // Add \forall x . f_t(x) = v @@ -321,7 +321,7 @@ br_status bvarray2uf_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr expr_ref frllx(m_manager.mk_forall(1, sorts, names, body), m_manager); extra_assertions.push_back(frllx); - result = m_array_util.mk_as_array(f->get_range(), f_t); + result = m_array_util.mk_as_array(f_t); res = BR_DONE; } else if (m_array_util.is_store(f)) { @@ -342,7 +342,7 @@ br_status bvarray2uf_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr func_decl_ref f_s(mk_uf_for_array(s), m_manager); func_decl_ref f_t(mk_uf_for_array(t), m_manager); - result = m_array_util.mk_as_array(f->get_range(), f_t); + result = m_array_util.mk_as_array(f_t); res = BR_DONE; sort * sorts[1] = { get_index_sort(f->get_range()) }; diff --git a/src/tactic/bv/dt2bv_tactic.cpp b/src/tactic/bv/dt2bv_tactic.cpp index fe59480e7..2b1aa0fdd 100644 --- a/src/tactic/bv/dt2bv_tactic.cpp +++ b/src/tactic/bv/dt2bv_tactic.cpp @@ -131,10 +131,8 @@ public: for (unsigned i = 0; i < size; ++i) { quick_for_each_expr(proc, visited, g->form(i)); } - obj_hashtable::iterator it = m_non_fd_sorts.begin(), end = m_non_fd_sorts.end(); - for (; it != end; ++it) { - m_fd_sorts.remove(*it); - } + for (sort* s : m_non_fd_sorts) + m_fd_sorts.remove(s); if (!m_fd_sorts.empty()) { ref ext = alloc(extension_model_converter, m); ref filter = alloc(filter_model_converter, m); @@ -152,21 +150,12 @@ public: } expr_ref_vector bounds(m); rw.flush_side_constraints(bounds); - for (unsigned i = 0; i < bounds.size(); ++i) { - g->assert_expr(bounds[i].get()); - } - { - obj_map::iterator it = rw.enum2bv().begin(), end = rw.enum2bv().end(); - for (; it != end; ++it) { - filter->insert(it->m_value); - } - } - { - obj_map::iterator it = rw.enum2def().begin(), end = rw.enum2def().end(); - for (; it != end; ++it) { - ext->insert(it->m_key, it->m_value); - } - } + for (expr* b : bounds) + g->assert_expr(b); + for (auto const& kv : rw.enum2bv()) + filter->insert(kv.m_value); + for (auto const& kv : rw.enum2def()) + ext->insert(kv.m_key, kv.m_value); mc = concat(filter.get(), ext.get()); report_tactic_progress(":fd-num-translated", rw.num_translated()); diff --git a/src/tactic/converter.h b/src/tactic/converter.h index 9de8218c3..89fc7cccd 100644 --- a/src/tactic/converter.h +++ b/src/tactic/converter.h @@ -29,17 +29,18 @@ public: converter():m_ref_count(0) {} virtual ~converter() {} - void inc_ref() { ++m_ref_count; } + void inc_ref() { ++m_ref_count; } + void dec_ref() { --m_ref_count; - if (m_ref_count == 0) + if (m_ref_count == 0) { dealloc(this); + } } virtual void cancel() {} - // for debugging purposes - virtual void display(std::ostream & out) {} + virtual void display(std::ostream & out) = 0; }; template @@ -68,10 +69,8 @@ public: virtual char const * get_name() const = 0; virtual void display(std::ostream & out) { - out << "(" << get_name() << "\n"; m_c1->display(out); m_c2->display(out); - out << ")\n"; } }; @@ -86,10 +85,9 @@ protected: T * translate_core(ast_translation & translator) { T * t1 = m_c1 ? m_c1->translate(translator) : 0; ptr_buffer t2s; - unsigned num = m_c2s.size(); - for (unsigned i = 0; i < num; i++) - t2s.push_back(m_c2s[i] ? m_c2s[i]->translate(translator) : 0); - return alloc(T2, t1, num, t2s.c_ptr(), m_szs.c_ptr()); + for (T* c : m_c2s) + t2s.push_back(c ? c->translate(translator) : 0); + return alloc(T2, t1, m_c2s.size(), t2s.c_ptr(), m_szs.c_ptr()); } public: @@ -105,36 +103,24 @@ public: } virtual ~concat_star_converter() { - unsigned sz = m_c2s.size(); - for (unsigned i = 0; i < sz; i++) { - T * c2 = m_c2s[i]; - if (c2) - c2->dec_ref(); - } + for (T* c : m_c2s) + if (c) c->dec_ref(); } virtual void cancel() { if (m_c1) m_c1->cancel(); - unsigned num = m_c2s.size(); - for (unsigned i = 0; i < num; i++) { - if (m_c2s[i]) - m_c2s[i]->cancel(); - } + for (T* c : m_c2s) + if (c) c->cancel(); } virtual char const * get_name() const = 0; virtual void display(std::ostream & out) { - out << "(" << get_name() << "\n"; if (m_c1) m_c1->display(out); - out << "(\n"; - unsigned num = m_c2s.size(); - for (unsigned i = 0; i < num; i++) - if (m_c2s[i]) - m_c2s[i]->display(out); - out << "))\n"; + for (T* c : m_c2s) + if (c) c->display(out); } }; diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index 3a6d55569..2099eebf0 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -242,7 +242,7 @@ expr_ref dom_simplify_tactic::simplify_ite(app * ite) { r = new_t; } else { - TRACE("tactic", tout << new_c << "\n" << new_t << "\n" << new_e << "\n";); + TRACE("simplify", tout << new_c << "\n" << new_t << "\n" << new_e << "\n";); r = m.mk_ite(new_c, new_t, new_e); } } @@ -441,7 +441,7 @@ ptr_vector const & dom_simplify_tactic::tree(expr * e) { } -// ---------------------- +// --------------------- // expr_substitution_simplifier bool expr_substitution_simplifier::assert_expr(expr * t, bool sign) { diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index 79bc9728c..56eea8d9a 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -83,6 +83,8 @@ class dom_simplifier { virtual void pop(unsigned num_scopes) = 0; virtual dom_simplifier * translate(ast_manager & m) = 0; + + virtual unsigned scope_level() const = 0; }; @@ -93,7 +95,6 @@ class dom_simplify_tactic : public tactic { expr_ref_vector m_trail, m_args; obj_map m_result; expr_dominators m_dominators; - unsigned m_scope_level; unsigned m_depth; unsigned m_max_depth; ptr_vector m_empty; @@ -116,9 +117,9 @@ class dom_simplify_tactic : public tactic { ptr_vector const & tree(expr * e); expr* idom(expr *e) const { return m_dominators.idom(e); } - unsigned scope_level() { return m_scope_level; } - void pop(unsigned n) { SASSERT(n <= m_scope_level); m_scope_level -= n; m_simplifier->pop(n); } - bool assert_expr(expr* f, bool sign) { m_scope_level++; return m_simplifier->assert_expr(f, sign); } + unsigned scope_level() { return m_simplifier->scope_level(); } + void pop(unsigned n) { SASSERT(n <= m_simplifier->scope_level()); m_simplifier->pop(n); } + bool assert_expr(expr* f, bool sign) { return m_simplifier->assert_expr(f, sign); } bool init(goal& g); @@ -126,8 +127,7 @@ public: dom_simplify_tactic(ast_manager & m, dom_simplifier* s, params_ref const & p = params_ref()): m(m), m_simplifier(s), m_params(p), m_trail(m), m_args(m), - m_dominators(m), - m_scope_level(0), m_depth(0), m_max_depth(1024), m_forward(true) {} + m_dominators(m), m_depth(0), m_max_depth(1024), m_forward(true) {} virtual ~dom_simplify_tactic(); @@ -169,6 +169,8 @@ public: virtual void pop(unsigned num_scopes) { m_scoped_substitution.pop(num_scopes); } + virtual unsigned scope_level() const { return m_scoped_substitution.scope_level(); } + virtual dom_simplifier * translate(ast_manager & m) { SASSERT(m_subst.empty()); return alloc(expr_substitution_simplifier, m); diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index 6a38f787e..614b20a10 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -17,7 +17,7 @@ Notes: --*/ #include "tactic/tactical.h" -#include "tactic/extension_model_converter.h" +#include "tactic/generic_model_converter.h" #include "tactic/filter_model_converter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/arith_decl_plugin.h" @@ -34,7 +34,7 @@ class elim_uncnstr_tactic : public tactic { struct imp { // unconstrained vars collector - typedef extension_model_converter mc; + typedef generic_model_converter mc; struct rw_cfg : public default_rewriter_cfg { bool m_produce_proofs; @@ -120,7 +120,7 @@ class elim_uncnstr_tactic : public tactic { SASSERT(uncnstr(v)); SASSERT(to_app(v)->get_num_args() == 0); if (m_mc) - m_mc->insert(to_app(v)->get_decl(), def); + m_mc->add(to_app(v)->get_decl(), def); } void add_defs(unsigned num, expr * const * args, expr * u, expr * identity) { diff --git a/src/tactic/core/pb_preprocess_tactic.cpp b/src/tactic/core/pb_preprocess_tactic.cpp index 6a0d7205b..c52f0526b 100644 --- a/src/tactic/core/pb_preprocess_tactic.cpp +++ b/src/tactic/core/pb_preprocess_tactic.cpp @@ -50,8 +50,8 @@ public: virtual void operator()(model_ref & mdl, unsigned goal_idx) { SASSERT(goal_idx == 0); - for (unsigned i = 0; i < m_const.size(); ++i) { - mdl->register_decl(m_const[i].first->get_decl(), m_const[i].second); + for (auto const& kv : m_const) { + mdl->register_decl(kv.first->get_decl(), kv.second); } } @@ -65,12 +65,18 @@ public: virtual model_converter * translate(ast_translation & translator) { pb_preproc_model_converter* mc = alloc(pb_preproc_model_converter, translator.to()); - for (unsigned i = 0; i < m_const.size(); ++i) { - mc->set_value_p(translator(m_const[i].first), translator(m_const[i].second)); + for (auto const& kv : m_const) { + mc->set_value_p(translator(kv.first), translator(kv.second)); } return mc; } + virtual void display(std::ostream & out) { + for (auto const& kv : m_const) { + out << "(model-set " << mk_pp(kv.first, m) << " " << mk_pp(kv.second, m) << ")\n"; + } + } + private: void set_value_p(app* e, expr* v) { SASSERT(e->get_num_args() == 0); diff --git a/src/tactic/core/split_clause_tactic.cpp b/src/tactic/core/split_clause_tactic.cpp index 7a2df81b5..a120f2910 100644 --- a/src/tactic/core/split_clause_tactic.cpp +++ b/src/tactic/core/split_clause_tactic.cpp @@ -79,6 +79,8 @@ class split_clause_tactic : public tactic { virtual proof_converter * translate(ast_translation & translator) { return alloc(split_pc, translator.to(), translator(m_clause), translator(m_clause_pr)); } + + virtual void display(std::ostream & out) { out << "(split-clause-pc)\n"; } }; public: diff --git a/src/tactic/equiv_proof_converter.h b/src/tactic/equiv_proof_converter.h index 79f5142b2..0ee44aec2 100644 --- a/src/tactic/equiv_proof_converter.h +++ b/src/tactic/equiv_proof_converter.h @@ -47,6 +47,7 @@ public: ast_manager& get_manager() { return m; } + virtual void display(std::ostream & out) {} }; #endif diff --git a/src/tactic/extension_model_converter.cpp b/src/tactic/extension_model_converter.cpp index 38f0100ee..0ae4e1323 100644 --- a/src/tactic/extension_model_converter.cpp +++ b/src/tactic/extension_model_converter.cpp @@ -7,7 +7,7 @@ Module Name: Abstract: - Model converter that introduces eliminated variables in a model. +Model converter that introduces eliminated variables in a model. Author: @@ -16,11 +16,11 @@ Author: Notes: --*/ -#include "tactic/extension_model_converter.h" -#include "model/model_evaluator.h" -#include "ast/ast_smt2_pp.h" -#include "model/model_v2_pp.h" #include "ast/ast_pp.h" +#include "ast/ast_smt2_pp.h" +#include "model/model_evaluator.h" +#include "model/model_v2_pp.h" +#include "tactic/extension_model_converter.h" extension_model_converter::~extension_model_converter() { } @@ -78,20 +78,17 @@ void extension_model_converter::insert(func_decl * v, expr * def) { void extension_model_converter::display(std::ostream & out) { - out << "(extension-model-converter"; for (unsigned i = 0; i < m_vars.size(); i++) { - out << "\n (" << m_vars.get(i)->get_name() << " "; - unsigned indent = m_vars.get(i)->get_name().size() + 4; - out << mk_ismt2_pp(m_defs.get(i), m(), indent) << ")"; + display_add(out, m(), m_vars.get(i), m_defs.get(i)); } - out << ")" << std::endl; } model_converter * extension_model_converter::translate(ast_translation & translator) { extension_model_converter * res = alloc(extension_model_converter, translator.to()); - for (unsigned i = 0; i < m_vars.size(); i++) - res->m_vars.push_back(translator(m_vars[i].get())); - for (unsigned i = 0; i < m_defs.size(); i++) - res->m_defs.push_back(translator(m_defs[i].get())); + for (func_decl* v : m_vars) + res->m_vars.push_back(translator(v)); + for (expr* d : m_defs) + res->m_defs.push_back(translator(d)); return res; } + diff --git a/src/tactic/extension_model_converter.h b/src/tactic/extension_model_converter.h index dbd7a7e3b..7ee6c68f5 100644 --- a/src/tactic/extension_model_converter.h +++ b/src/tactic/extension_model_converter.h @@ -23,12 +23,11 @@ Notes: #include "ast/ast.h" #include "tactic/model_converter.h" - class extension_model_converter : public model_converter { func_decl_ref_vector m_vars; expr_ref_vector m_defs; public: - extension_model_converter(ast_manager & m):m_vars(m), m_defs(m) { + extension_model_converter(ast_manager & m): m_vars(m), m_defs(m) { } virtual ~extension_model_converter(); @@ -44,6 +43,4 @@ public: virtual model_converter * translate(ast_translation & translator); }; - - #endif diff --git a/src/tactic/filter_model_converter.cpp b/src/tactic/filter_model_converter.cpp index 46328186d..7400063e1 100644 --- a/src/tactic/filter_model_converter.cpp +++ b/src/tactic/filter_model_converter.cpp @@ -55,16 +55,20 @@ void filter_model_converter::operator()(svector & labels, unsigned goal_ } void filter_model_converter::display(std::ostream & out) { - out << "(filter-model-converter"; - for (unsigned i = 0; i < m_decls.size(); i++) { - out << " " << m_decls.get(i)->get_name(); + for (func_decl* f : m_decls) { + display_del(out, f); } - out << ")" << std::endl; } model_converter * filter_model_converter::translate(ast_translation & translator) { filter_model_converter * res = alloc(filter_model_converter, translator.to()); - for (unsigned i = 0; i < m_decls.size(); i++) - res->m_decls.push_back(translator(m_decls[i].get())); + for (func_decl* f : m_decls) + res->m_decls.push_back(translator(f)); return res; } + +void filter_model_converter::collect(ast_pp_util& visitor) { + m_env = &visitor.env(); + for (func_decl* f : m_decls) visitor.coll.visit_func(f); +} + diff --git a/src/tactic/filter_model_converter.h b/src/tactic/filter_model_converter.h index b5d6b86f4..2c3361808 100644 --- a/src/tactic/filter_model_converter.h +++ b/src/tactic/filter_model_converter.h @@ -20,11 +20,12 @@ Notes: #define FILTER_MODEL_CONVERTER_H_ #include "tactic/model_converter.h" +#include "ast/ast_pp_util.h" class filter_model_converter : public model_converter { func_decl_ref_vector m_decls; public: - filter_model_converter(ast_manager & m):m_decls(m) {} + filter_model_converter(ast_manager & m): m_decls(m) {} virtual ~filter_model_converter(); @@ -37,7 +38,7 @@ public: virtual void operator()(model_ref & md) { operator()(md, 0); } // TODO: delete virtual void cancel() {} - + virtual void display(std::ostream & out); void insert(func_decl * d) { @@ -45,6 +46,9 @@ public: } virtual model_converter * translate(ast_translation & translator); + + virtual void collect(ast_pp_util& visitor); + }; typedef ref filter_model_converter_ref; diff --git a/src/tactic/horn_subsume_model_converter.h b/src/tactic/horn_subsume_model_converter.h index 1c1ae9feb..74d5882a5 100644 --- a/src/tactic/horn_subsume_model_converter.h +++ b/src/tactic/horn_subsume_model_converter.h @@ -78,6 +78,7 @@ public: ast_manager& get_manager() { return m; } + virtual void display(std::ostream & out) {} }; #endif diff --git a/src/tactic/model_converter.cpp b/src/tactic/model_converter.cpp index 4269946a1..01c7a16bb 100644 --- a/src/tactic/model_converter.cpp +++ b/src/tactic/model_converter.cpp @@ -18,10 +18,45 @@ Notes: --*/ #include "tactic/model_converter.h" #include "model/model_v2_pp.h" +#include "ast/ast_smt2_pp.h" + +/* + * Add or overwrite value in model. + */ +void model_converter::display_add(std::ostream& out, ast_manager& m, func_decl* f, expr* e) const { + VERIFY(m_env); + ast_smt2_pp(out, f, e, *m_env, params_ref(), 0, "model-add") << "\n"; +} + +/* + * A value is removed from the model. + */ +void model_converter::display_del(std::ostream& out, func_decl* f) const { + VERIFY(m_env); + ast_smt2_pp(out << "(model-del ", f->get_name(), f->is_skolem(), *m_env) << ")\n"; +} + +void model_converter::display_add(std::ostream& out, ast_manager& m) { + // default printer for converter that adds entries + model_ref mdl = alloc(model, m); + (*this)(mdl); + for (unsigned i = 0, sz = mdl->get_num_constants(); i < sz; ++i) { + func_decl* f = mdl->get_constant(i); + display_add(out, m, f, mdl->get_const_interp(f)); + } + for (unsigned i = 0, sz = mdl->get_num_functions(); i < sz; ++i) { + func_decl* f = mdl->get_function(i); + func_interp* fi = mdl->get_func_interp(f); + display_add(out, m, f, fi->get_interp()); + } +} + class concat_model_converter : public concat_converter { public: - concat_model_converter(model_converter * mc1, model_converter * mc2):concat_converter(mc1, mc2) {} + concat_model_converter(model_converter * mc1, model_converter * mc2): concat_converter(mc1, mc2) { + VERIFY(m_c1 && m_c2); + } virtual void operator()(model_ref & m) { this->m_c2->operator()(m); @@ -37,13 +72,17 @@ public: this->m_c2->operator()(r, goal_idx); this->m_c1->operator()(r, 0); } - virtual char const * get_name() const { return "concat-model-converter"; } virtual model_converter * translate(ast_translation & translator) { return this->translate_core(translator); } + + virtual void collect(ast_pp_util& visitor) { + this->m_c1->collect(visitor); + this->m_c2->collect(visitor); + } }; model_converter * concat(model_converter * mc1, model_converter * mc2) { @@ -90,9 +129,9 @@ public: // found the model converter that should be used model_converter * c2 = this->m_c2s[i]; if (c2) - c2->operator()(r, goal_idx); + c2->operator()(r, goal_idx); if (m_c1) - this->m_c1->operator()(r, i); + this->m_c1->operator()(r, i); return; } // invalid goal @@ -143,10 +182,10 @@ public: } virtual void operator()(labels_vec & r, unsigned goal_idx) { - r.append(m_labels.size(), m_labels.c_ptr()); + r.append(m_labels.size(), m_labels.c_ptr()); } - virtual void cancel() { + virtual void cancel() { } virtual void display(std::ostream & out) { diff --git a/src/tactic/model_converter.h b/src/tactic/model_converter.h index 5e549344e..f7597be07 100644 --- a/src/tactic/model_converter.h +++ b/src/tactic/model_converter.h @@ -19,14 +19,25 @@ Notes: #ifndef MODEL_CONVERTER_H_ #define MODEL_CONVERTER_H_ +#include "ast/ast_pp_util.h" #include "model/model.h" #include "tactic/converter.h" #include "util/ref.h" class labels_vec : public svector {}; +class smt2_pp_environment; class model_converter : public converter { +protected: + smt2_pp_environment* m_env; + void display_add(std::ostream& out, ast_manager& m, func_decl* f, expr* e) const; + void display_del(std::ostream& out, func_decl* f) const; + void display_add(std::ostream& out, ast_manager& m); + public: + + model_converter(): m_env(0) {} + virtual void operator()(model_ref & m) {} // TODO: delete virtual void operator()(model_ref & m, unsigned goal_idx) { @@ -34,10 +45,12 @@ public: SASSERT(goal_idx == 0); operator()(m); } - virtual void operator()(labels_vec & r, unsigned goal_idx) {} + virtual model_converter * translate(ast_translation & translator) = 0; + + virtual void collect(ast_pp_util& visitor) { m_env = &visitor.env(); } }; typedef ref model_converter_ref; diff --git a/src/tactic/portfolio/bounded_int2bv_solver.cpp b/src/tactic/portfolio/bounded_int2bv_solver.cpp index ec83766a0..195a16fbf 100644 --- a/src/tactic/portfolio/bounded_int2bv_solver.cpp +++ b/src/tactic/portfolio/bounded_int2bv_solver.cpp @@ -33,7 +33,6 @@ Notes: class bounded_int2bv_solver : public solver_na2as { ast_manager& m; - params_ref m_params; mutable bv_util m_bv; mutable arith_util m_arith; mutable expr_ref_vector m_assertions; @@ -53,7 +52,6 @@ public: bounded_int2bv_solver(ast_manager& m, params_ref const& p, solver* s): solver_na2as(m), m(m), - m_params(p), m_bv(m), m_arith(m), m_assertions(m), @@ -63,6 +61,7 @@ public: m_rewriter_ctx(m, p), m_rewriter(m, m_rewriter_ctx) { + solver::updt_params(p); m_bounds.push_back(alloc(bound_manager, m)); } @@ -83,6 +82,7 @@ public: for (func_decl* f : m_bv_fns) result->m_bv_fns.push_back(tr(f)); for (func_decl* f : m_int_fns) result->m_int_fns.push_back(tr(f)); for (bound_manager* b : m_bounds) result->m_bounds.push_back(b->translate(dst_m)); + if (mc0()) result->set_model_converter(mc0()->translate(tr)); return result; } @@ -144,27 +144,26 @@ public: return m_solver->check_sat(num_assumptions, assumptions); } - virtual void updt_params(params_ref const & p) { m_solver->updt_params(p); } + virtual void updt_params(params_ref const & p) { solver::updt_params(p); m_solver->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { m_solver->collect_param_descrs(r); } virtual void set_produce_models(bool f) { m_solver->set_produce_models(f); } virtual void set_progress_callback(progress_callback * callback) { m_solver->set_progress_callback(callback); } virtual void collect_statistics(statistics & st) const { m_solver->collect_statistics(st); } virtual void get_unsat_core(ptr_vector & r) { m_solver->get_unsat_core(r); } - virtual void get_model(model_ref & mdl) { + virtual void get_model_core(model_ref & mdl) { m_solver->get_model(mdl); if (mdl) { extend_model(mdl); filter_model(mdl); } } + virtual model_converter_ref get_model_converter() const { return m_solver->get_model_converter(); } virtual proof * get_proof() { return m_solver->get_proof(); } virtual std::string reason_unknown() const { return m_solver->reason_unknown(); } virtual void set_reason_unknown(char const* msg) { m_solver->set_reason_unknown(msg); } virtual void get_labels(svector & r) { m_solver->get_labels(r); } virtual ast_manager& get_manager() const { return m; } - virtual expr_ref lookahead(expr_ref_vector const& assumptions, expr_ref_vector const& candidates) { flush_assertions(); return m_solver->lookahead(assumptions, candidates); } virtual expr_ref cube() { flush_assertions(); return m_solver->cube(); } - virtual void get_lemmas(expr_ref_vector & lemmas) { flush_assertions(); m_solver->get_lemmas(lemmas); } virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) { return m_solver->find_mutexes(vars, mutexes); } virtual lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) { flush_assertions(); diff --git a/src/tactic/portfolio/enum2bv_solver.cpp b/src/tactic/portfolio/enum2bv_solver.cpp index 6288223fa..b16ba2443 100644 --- a/src/tactic/portfolio/enum2bv_solver.cpp +++ b/src/tactic/portfolio/enum2bv_solver.cpp @@ -20,38 +20,42 @@ Notes: --*/ -#include "solver/solver_na2as.h" -#include "tactic/tactic.h" #include "ast/bv_decl_plugin.h" #include "ast/datatype_decl_plugin.h" -#include "ast/rewriter/enum2bv_rewriter.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" #include "ast/ast_pp.h" #include "model/model_smt2_pp.h" +#include "tactic/tactic.h" +#include "tactic/extension_model_converter.h" +#include "tactic/filter_model_converter.h" #include "tactic/portfolio/enum2bv_solver.h" +#include "solver/solver_na2as.h" +#include "ast/rewriter/enum2bv_rewriter.h" class enum2bv_solver : public solver_na2as { - ast_manager& m; - params_ref m_params; - ref m_solver; - enum2bv_rewriter m_rewriter; + ast_manager& m; + ref m_solver; + enum2bv_rewriter m_rewriter; public: enum2bv_solver(ast_manager& m, params_ref const& p, solver* s): solver_na2as(m), m(m), - m_params(p), m_solver(s), m_rewriter(m, p) { + solver::updt_params(p); } virtual ~enum2bv_solver() {} - virtual solver* translate(ast_manager& m, params_ref const& p) { - return alloc(enum2bv_solver, m, p, m_solver->translate(m, p)); + virtual solver* translate(ast_manager& dst_m, params_ref const& p) { + solver* result = alloc(enum2bv_solver, dst_m, p, m_solver->translate(dst_m, p)); + if (mc0()) { + ast_translation tr(m, dst_m); + result->set_model_converter(mc0()->translate(tr)); + } + return result; } virtual void assert_expr(expr * t) { @@ -85,32 +89,31 @@ public: } virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) { - m_solver->updt_params(m_params); + m_solver->updt_params(get_params()); return m_solver->check_sat(num_assumptions, assumptions); } - virtual void updt_params(params_ref const & p) { m_solver->updt_params(p); } + virtual void updt_params(params_ref const & p) { solver::updt_params(p); m_solver->updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { m_solver->collect_param_descrs(r); } virtual void set_produce_models(bool f) { m_solver->set_produce_models(f); } virtual void set_progress_callback(progress_callback * callback) { m_solver->set_progress_callback(callback); } virtual void collect_statistics(statistics & st) const { m_solver->collect_statistics(st); } virtual void get_unsat_core(ptr_vector & r) { m_solver->get_unsat_core(r); } - virtual void get_model(model_ref & mdl) { + virtual void get_model_core(model_ref & mdl) { m_solver->get_model(mdl); if (mdl) { extend_model(mdl); filter_model(mdl); } } + virtual model_converter_ref get_model_converter() const { return m_solver->get_model_converter(); } virtual proof * get_proof() { return m_solver->get_proof(); } virtual std::string reason_unknown() const { return m_solver->reason_unknown(); } virtual void set_reason_unknown(char const* msg) { m_solver->set_reason_unknown(msg); } virtual void get_labels(svector & r) { m_solver->get_labels(r); } virtual ast_manager& get_manager() const { return m; } virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) { return m_solver->find_mutexes(vars, mutexes); } - virtual expr_ref lookahead(expr_ref_vector const& assumptions, expr_ref_vector const& candidates) { return m_solver->lookahead(assumptions, candidates); } virtual expr_ref cube() { return m_solver->cube(); } - virtual void get_lemmas(expr_ref_vector & lemmas) { m_solver->get_lemmas(lemmas); } virtual lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) { datatype_util dt(m); @@ -120,8 +123,8 @@ public: // ensure that enumeration variables that // don't occur in the constraints // are also internalized. - for (unsigned i = 0; i < vars.size(); ++i) { - expr_ref tmp(m.mk_eq(vars[i], vars[i]), m); + for (expr* v : vars) { + expr_ref tmp(m.mk_eq(v, v), m); proof_ref proof(m); m_rewriter(tmp, tmp, proof); } @@ -129,13 +132,13 @@ public: m_solver->assert_expr(bounds); // translate enumeration constants to bit-vectors. - for (unsigned i = 0; i < vars.size(); ++i) { + for (expr* v : vars) { func_decl* f = 0; - if (is_app(vars[i]) && is_uninterp_const(vars[i]) && m_rewriter.enum2bv().find(to_app(vars[i])->get_decl(), f)) { + if (is_app(v) && is_uninterp_const(v) && m_rewriter.enum2bv().find(to_app(v)->get_decl(), f)) { bvars.push_back(m.mk_const(f)); } else { - bvars.push_back(vars[i]); + bvars.push_back(v); } } lbool r = m_solver->get_consequences(asms, bvars, consequences); @@ -162,20 +165,16 @@ public: void filter_model(model_ref& mdl) { filter_model_converter filter(m); - obj_map::iterator it = m_rewriter.enum2bv().begin(), end = m_rewriter.enum2bv().end(); - for (; it != end; ++it) { - filter.insert(it->m_value); + for (auto const& kv : m_rewriter.enum2bv()) { + filter.insert(kv.m_value); } filter(mdl, 0); } void extend_model(model_ref& mdl) { extension_model_converter ext(m); - obj_map::iterator it = m_rewriter.enum2def().begin(), end = m_rewriter.enum2def().end(); - for (; it != end; ++it) { - ext.insert(it->m_key, it->m_value); - - } + for (auto const& kv : m_rewriter.enum2def()) + ext.insert(kv.m_key, kv.m_value); ext(mdl, 0); } diff --git a/src/tactic/portfolio/parallel_tactic.cpp b/src/tactic/portfolio/parallel_tactic.cpp index 4eb2fbbe9..5762271ba 100644 --- a/src/tactic/portfolio/parallel_tactic.cpp +++ b/src/tactic/portfolio/parallel_tactic.cpp @@ -7,21 +7,37 @@ Module Name: Abstract: - Parallel tactic in the style of Treengeling. - - It assumes a solver that supports good lookaheads. + Parallel tactic based on cubing. Author: Nikolaj Bjorner (nbjorner) 2017-10-9 + Miguel Neves Notes: - + + + A task comprises of a non-empty sequence of cubes, a type and parameters + + If in the cube state, the solver performs the following: + 1. Clone the state with the remaining cubes if there is more than one cube. Re-enqueue the remaining cubes. + 2. Apply simplifications and pre-processing according to configuration. + 3. Cube using the parameter settings prescribed in m_params. + 4. Create a conquer state with the produced cubes. + If in the conquer state, the solver performs the following + 1. Pass the cubes as assumptions and solve each sub-cube with a prescribed resource bound. + 2. Assemble cubes that could not be solved and create a cube state. + + --*/ +#include +#include +#include #include "util/scoped_ptr_vector.h" #include "ast/ast_util.h" +#include "ast/ast_translation.h" #include "solver/solver.h" #include "solver/solver2tactic.h" #include "tactic/tactic.h" @@ -29,175 +45,373 @@ Notes: class parallel_tactic : public tactic { - class solver_state { - params_ref m_params; - scoped_ptr m_manager; - ref m_solver; - expr_ref_vector m_cube; - unsigned m_units; - public: - solver_state(ast_manager* m, solver* s, params_ref const& p): - m_params(p), - m_manager(m), - m_solver(s), - m_cube(s->get_manager()), - m_units(0) {} - void update_units() { - m_units = 0; - statistics st; - m_solver->collect_statistics(st); - std::string units("units"); - for (unsigned i = st.size(); i-- > 0; ) { - if (st.get_key(i) == units) { - m_units = st.get_uint_value(i); - std::cout << "value for " << i << " is " << m_units << "\n"; - break; + enum task_type { cube_task, conquer_task }; + + class solver_state; + + class task_queue { + std::mutex m_mutex; + std::condition_variable m_cond; + ptr_vector m_tasks; + ptr_vector m_active; + unsigned m_num_waiters; + volatile bool m_shutdown; + + void inc_wait() { + std::lock_guard lock(m_mutex); + ++m_num_waiters; + } + + void dec_wait() { + std::lock_guard lock(m_mutex); + --m_num_waiters; + } + + solver_state* try_get_task() { + solver_state* st = nullptr; + std::lock_guard lock(m_mutex); + if (!m_tasks.empty()) { + st = m_tasks.back(); + m_tasks.pop_back(); + m_active.push_back(st); + } + return st; + } + + public: + + task_queue(): + m_num_waiters(0), + m_shutdown(false) {} + + ~task_queue() { reset(); } + + void shutdown() { + if (!m_shutdown) { + m_shutdown = true; + m_cond.notify_all(); + std::lock_guard lock(m_mutex); + for (solver_state* st : m_active) { + st->m().limit().cancel(); } } - } + } - expr_ref cube() { return mk_and(m_cube); } + void add_task(solver_state* task) { + std::lock_guard lock(m_mutex); + m_tasks.push_back(task); + if (m_num_waiters > 0) { + m_cond.notify_one(); + } + } - void add_cube(expr* c) { m_cube.push_back(c); } + solver_state* get_task() { + while (!m_shutdown) { + inc_wait(); + solver_state* st = try_get_task(); + if (st) { + dec_wait(); + return st; + } + { + std::unique_lock lock(m_mutex); + m_cond.wait(lock); + } + dec_wait(); + } + return nullptr; + } - unsigned num_units() const { return m_units; } + void task_done(solver_state* st) { + std::lock_guard lock(m_mutex); + m_active.erase(st); + if (m_tasks.empty() && m_active.empty()) { + m_shutdown = true; + m_cond.notify_all(); + } + } - unsigned cube_size() const { return m_cube.size(); } + void reset() { + for (auto* t : m_tasks) dealloc(t); + for (auto* t : m_active) dealloc(t); + m_tasks.reset(); + m_active.reset(); + } + + std::ostream& display(std::ostream& out) { + std::lock_guard lock(m_mutex); + out << "num_tasks " << m_tasks.size() << " active: " << m_active.size() << "\n"; + for (solver_state* st : m_tasks) { + st->display(out); + } + return out; + } + + }; + + class solver_state { + task_type m_type; // current work role of the task + scoped_ptr m_manager; // ownership handle to ast_manager + expr_ref_vector m_cubes; // set of cubes to process by task + expr_ref_vector m_asserted_cubes; // set of cubes asserted on the current solver + params_ref m_params; // configuration parameters + ref m_solver; // solver state + unsigned m_depth; // number of nested calls to cubing + double m_width; // estimate of fraction of problem handled by state + unsigned m_cube_cutoff; // saved configuration value + double m_cube_fraction; // saved configuration value + unsigned m_restart_max; // saved configuration value + + expr_ref_vector cube_literals(expr* cube) { + expr_ref_vector literals(m()); + if (m().is_and(cube)) { + literals.append(to_app(cube)->get_num_args(), to_app(cube)->get_args()); + } + else { + literals.push_back(cube); + } + return literals; + } + + public: + solver_state(ast_manager* m, solver* s, params_ref const& p, task_type t): + m_type(t), + m_manager(m), + m_cubes(s->get_manager()), + m_asserted_cubes(s->get_manager()), + m_params(p), + m_solver(s), + m_depth(0), + m_width(1.0) + { + m_cube_cutoff = p.get_uint("sat.lookahead.cube.cutoff", 8); + m_cube_fraction = p.get_double("sat.lookahead.cube.fraction", 0.4); + m_restart_max = p.get_uint("sat.restart.max", 10); + } + + ast_manager& m() { return m_solver->get_manager(); } solver& get_solver() { return *m_solver; } solver const& get_solver() const { return *m_solver; } - params_ref const& params() const { return m_params; } - - params_ref& params() { return m_params; } - - solver_state* clone(params_ref const& p, expr* cube) { + solver_state* clone() { + SASSERT(!m_cubes.empty()); ast_manager& m = m_solver->get_manager(); ast_manager* new_m = alloc(ast_manager, m, !m.proof_mode()); - solver* s = m_solver->translate(*new_m, p); - solver_state* st = alloc(solver_state, new_m, s, m_params); - ast_translation translate(m, *new_m); - for (expr* c : m_cube) { - st->m_cube.push_back(translate(c)); - } - expr_ref cube1(translate(cube), *new_m); - st->m_cube.push_back(cube1); - s->assert_expr(cube1); + ast_translation tr(m, *new_m); + solver* s = m_solver->translate(*new_m, m_params); + solver_state* st = alloc(solver_state, new_m, s, m_params, m_type); + for (expr* c : m_cubes) st->m_cubes.push_back(tr(c)); + for (expr* c : m_asserted_cubes) st->m_asserted_cubes.push_back(tr(c)); + st->m_depth = m_depth; + st->m_width = m_width; return st; } - }; -public: - bool operator()(solver_state* s1, solver_state* s2) const { - return s1->num_units() > s2->num_units(); - } + task_type type() const { return m_type; } + + void set_type(task_type t) { m_type = t; } + + expr_ref_vector const& cubes() const { SASSERT(m_type == conquer); return m_cubes; } + + // remove up to n cubes from list of cubes. + expr_ref_vector split_cubes(unsigned n) { + expr_ref_vector result(m()); + while (n-- > 0 && !m_cubes.empty()) { + result.push_back(m_cubes.back()); + m_cubes.pop_back(); + } + return result; + } + + void set_cubes(expr_ref_vector const& c) { + m_cubes.reset(); + m_cubes.append(c); + } + + void inc_depth(unsigned inc) { m_depth += inc; } + + void inc_width(unsigned w) { m_width *= w; } + + double get_width() const { return m_width; } + + unsigned get_depth() const { return m_depth; } + + lbool simplify() { + lbool r = l_undef; + if (m_depth == 1) { + set_simplify_params(true, true); // retain PB, retain blocked + r = get_solver().check_sat(0,0); + if (r != l_undef) return r; + + // copy over the resulting clauses with a configuration that blasts PB constraints + set_simplify_params(false, true); + expr_ref_vector fmls(m()); + get_solver().get_assertions(fmls); + model_converter_ref mc = get_solver().get_model_converter(); + m_solver = mk_fd_solver(m(), m_params); + m_solver->set_model_converter(mc.get()); + m_solver->assert_expr(fmls); + } + set_simplify_params(false, true); // remove PB, retain blocked + r = get_solver().check_sat(0,0); + if (r != l_undef) return r; + set_simplify_params(false, false); // remove any PB, remove blocked + r = get_solver().check_sat(0,0); + return r; + } + + void assert_cube(expr* cube) { + get_solver().assert_expr(cube); + m_asserted_cubes.append(cube_literals(cube)); + } + + lbool solve(expr* cube) { + set_conquer_params(); + expr_ref_vector literals = cube_literals(cube); + return get_solver().check_sat(literals); + } + + void set_cube_params() { + unsigned cutoff = m_cube_cutoff; + double fraction = m_cube_fraction; + if (m_depth == 1 && cutoff > 0) { + fraction = 0; // use fixed cubing at depth 1. + } + else { + cutoff = 0; // use dynamic cubing beyond depth 1 + } + m_params.set_uint ("lookahead.cube.cutoff", cutoff); + m_params.set_double("lookahead.cube.fraction", fraction); + get_solver().updt_params(m_params); + } + + void set_conquer_params() { + m_params.set_bool("lookahead_simplify", false); + m_params.set_uint("restart.max", m_restart_max); + get_solver().updt_params(m_params); + } + + void set_simplify_params(bool pb_simp, bool retain_blocked) { + m_params.set_bool("cardinality.solver", pb_simp); + m_params.set_sym ("pb.solver", pb_simp ? symbol("solver") : symbol("circuit")); + if (m_params.get_uint("inprocess.max", UINT_MAX) == UINT_MAX) + m_params.set_uint("inprocess.max", 2); + m_params.set_bool("lookahead_simplify", true); + m_params.set_uint("restart.max", UINT_MAX); + m_params.set_bool("retain_blocked_clauses", retain_blocked); + get_solver().updt_params(m_params); + } + + bool canceled() { + return m().canceled(); + } + + std::ostream& display(std::ostream& out) { + out << ":depth " << m_depth << " :width " << m_width << "\n"; + out << ":asserted " << m_asserted_cubes.size() << "\n"; + return out; + } + }; + private: ast_manager& m_manager; params_ref m_params; - - // parameters - unsigned m_conflicts_lower_bound; - unsigned m_conflicts_upper_bound; - unsigned m_conflicts_growth_rate; - unsigned m_conflicts_decay_rate; - unsigned m_num_threads; - - double m_progress; - unsigned m_max_conflicts; - statistics m_stats; - - vector m_solvers; + sref_vector m_models; + unsigned m_num_threads; + statistics m_stats; + task_queue m_queue; + std::mutex m_mutex; + double m_progress; + bool m_has_undef; + bool m_allsat; + unsigned m_num_unsat; + int m_exn_code; + std::string m_exn_msg; void init() { - m_conflicts_lower_bound = 1000; - m_conflicts_upper_bound = 10000; - m_conflicts_growth_rate = 150; - m_conflicts_decay_rate = 75; - m_max_conflicts = m_conflicts_lower_bound; + m_num_threads = omp_get_num_procs(); // TBD adjust by possible threads used inside each solver. m_progress = 0; - m_num_threads = 2 * omp_get_num_procs(); // TBD adjust by possible threads used inside each solver. + m_has_undef = false; + m_allsat = false; + m_num_unsat = 0; + m_exn_code = 0; } - unsigned get_max_conflicts() { - return m_max_conflicts; + void close_branch(solver_state& s, lbool status) { + double f = 100.0 / s.get_width(); + { + std::lock_guard lock(m_mutex); + m_progress += f; + } + IF_VERBOSE(1, verbose_stream() << "(tactic.parallel :progress " << m_progress << "% "; + if (status == l_true) verbose_stream() << ":status sat "; + if (status == l_undef) verbose_stream() << ":status unknown "; + verbose_stream() << ":unsat " << m_num_unsat << ")\n";); } - void set_max_conflicts(unsigned c) { - m_max_conflicts = c; - } - - bool should_increase_conflicts() { - return m_progress < 0; - } - - void update_progress(bool b) { - m_progress = 0.9 * m_progress + (b ? 1 : -1); - if (b) { - m_stats.update("closed", 1u); + void report_sat(solver_state& s) { + close_branch(s, l_true); + model_ref mdl; + s.get_solver().get_model(mdl); + if (mdl) { + std::lock_guard lock(m_mutex); + if (&s.m() != &m_manager) { + ast_translation tr(s.m(), m_manager); + mdl = mdl->translate(tr); + } + m_models.push_back(mdl.get()); + } + if (!m_allsat) { + m_queue.shutdown(); } } - int pick_solvers() { - // order solvers by number of units in descending order - for (solver_state* s : m_solvers) s->update_units(); - std::sort(m_solvers.c_ptr(), m_solvers.c_ptr() + m_solvers.size(), *this); - TRACE("parallel_tactic", display(tout);); - return std::min(m_num_threads, m_solvers.size()); + void report_unsat(solver_state& s) { + close_branch(s, l_false); + std::lock_guard lock(m_mutex); + ++m_num_unsat; + } + + void report_undef(solver_state& s) { + m_has_undef = true; + close_branch(s, l_undef); } - int max_num_splits() { - if (m_solvers.empty()) { - return m_num_threads; - } - uint64 max_mem = memory::get_max_memory_size(); - uint64 cur_mem = memory::get_allocation_size(); - uint64 sol_sz = cur_mem / m_solvers.size(); + void cube_and_conquer(solver_state& s) { + ast_manager& m = s.m(); + expr_ref_vector cubes(m), cube(m), hard_cubes(m); - TRACE("parallel_tactic", tout << "max mem: " << max_mem << " cur mem: " << cur_mem << " num solvers: " << m_solvers.size() << "\n";); - if (max_mem <= cur_mem) { - return 0; + switch (s.type()) { + case cube_task: goto cube; + case conquer_task: goto conquer; } - if (cur_mem == 0) { - return m_num_threads; - } - uint64 extra_solvers = (max_mem - cur_mem) / (2 * sol_sz); - if (extra_solvers > m_num_threads) { - return m_num_threads; - } - return static_cast(extra_solvers); - } - void update_max_conflicts() { - if (should_increase_conflicts()) { - set_max_conflicts(std::min(m_conflicts_upper_bound, m_conflicts_growth_rate * get_max_conflicts() / 100)); - } - else { - set_max_conflicts(std::max(m_conflicts_lower_bound, m_conflicts_decay_rate * get_max_conflicts() / 100)); - } - } + cube: + SASSERT(s.type() == cube_task); - lbool simplify(solver_state& s) { - s.params().set_uint("max_conflicts", 10); - s.params().set_bool("lookahead_simplify", true); - s.get_solver().updt_params(s.params()); - lbool is_sat = s.get_solver().check_sat(0,0); - s.params().set_uint("max_conflicts", get_max_conflicts()); - s.params().set_bool("lookahead_simplify", false); - s.get_solver().updt_params(s.params()); - return is_sat; - } + // extract up to one cube and add it. + cube.reset(); + cube.append(s.split_cubes(1)); + SASSERT(cube.size() <= 1); + if (!s.cubes().empty()) m_queue.add_task(s.clone()); + if (!cube.empty()) s.assert_cube(cube.get(0)); + s.inc_depth(1); - lbool cube(solver_state& s) { - ast_manager& m = s.get_solver().get_manager(); - expr_ref_vector cubes(m); - params_ref p; - p.copy(s.params()); - p.set_uint("lookahead.cube.cutoff", 1); - s.get_solver().updt_params(p); - SASSERT(&m == &cubes.get_manager()); + // simplify + switch (s.simplify()) { + case l_undef: break; + case l_true: report_sat(s); return; + case l_false: report_unsat(s); return; + } + if (canceled(s)) return; + + // extract cubes. + cubes.reset(); + s.set_cube_params(); while (true) { expr_ref c = s.get_solver().cube(); VERIFY(c); @@ -205,191 +419,122 @@ private: break; } if (m.is_true(c)) { - cubes.reset(); - return l_undef; + report_undef(s); + return; } cubes.push_back(c); } - IF_VERBOSE(1, verbose_stream() << "cubes: " << cubes << "\n";); + IF_VERBOSE(1, verbose_stream() << "(parallel_tactic :cubes " << cubes.size() << ")\n";); + IF_VERBOSE(10, verbose_stream() << "(parallel_tactic :cubes " << cubes << ")\n";); if (cubes.empty()) { - return l_false; + report_unsat(s); + return; } - for (unsigned j = 1; j < cubes.size(); ++j) { - solver_state* s1 = s.clone(s.params(), cubes[j].get()); - #pragma omp critical (parallel_tactic) - { - m_solvers.push_back(s1); + else { + s.inc_width(cubes.size()); + s.set_cubes(cubes); + s.set_type(conquer_task); + goto conquer; + } + + conquer: + SASSERT(s.type() == conquer_task); + + // extract a batch of cubes + cubes.reset(); + cubes.append(s.split_cubes(conquer_batch_size())); + if (!s.cubes().empty()) m_queue.add_task(s.clone()); + + s.set_conquer_params(); + hard_cubes.reset(); + for (expr * c : cubes) { + switch (s.solve(c)) { + case l_undef: hard_cubes.push_back(c); break; + case l_true: report_sat(s); break; + case l_false: report_unsat(s); break; + } + if (canceled(s)) return; + } + IF_VERBOSE(1, verbose_stream() << "(parallel_tactic :cubes " << cubes.size() << " :hard-cubes " << hard_cubes.size() << ")\n";); + if (hard_cubes.empty()) return; + + s.set_cubes(hard_cubes); + s.set_type(cube_task); + goto cube; + } + + bool canceled(solver_state& s) { + if (s.canceled()) { + m_has_undef = true; + return true; + } + else { + return false; + } + } + + void run_solver() { + try { + while (solver_state* st = m_queue.get_task()) { + cube_and_conquer(*st); + collect_statistics(*st); + m_queue.task_done(st); + if (st->m().canceled()) m_queue.shutdown(); + IF_VERBOSE(1, display(verbose_stream());); + dealloc(st); } } - - expr* cube0 = cubes[0].get(); - s.add_cube(cube0); - s.get_solver().assert_expr(cube0); - return l_undef; - } - - lbool solve(solver_state& s) { - s.params().set_uint("max_conflicts", get_max_conflicts()); - s.get_solver().updt_params(s.params()); - return s.get_solver().check_sat(0, 0); - } - - void remove_unsat(svector& unsat) { - std::sort(unsat.begin(), unsat.end()); - unsat.reverse(); - DEBUG_CODE(for (unsigned i = 0; i + 1 < unsat.size(); ++i) SASSERT(unsat[i] > unsat[i+1]);); - for (int i : unsat) { - m_solvers[i]->get_solver().collect_statistics(m_stats); - dealloc(m_solvers[i]); - for (unsigned j = i + 1; j < m_solvers.size(); ++j) { - m_solvers[j - 1] = m_solvers[j]; + catch (z3_exception& ex) { + IF_VERBOSE(1, verbose_stream() << ex.msg() << "\n";); + m_queue.shutdown(); + std::lock_guard lock(m_mutex); + if (ex.has_error_code()) { + m_exn_code = ex.error_code(); + } + else { + m_exn_msg = ex.msg(); + m_exn_code = -1; } - m_solvers.shrink(m_solvers.size() - 1); - update_progress(true); } - unsat.reset(); } - void get_model(model_ref& mdl, int sat_index) { - SASSERT(sat_index != -1); - m_solvers[sat_index]->get_solver().get_model(mdl); - ast_translation translate(m_solvers[sat_index]->get_solver().get_manager(), m_manager); - mdl = mdl->translate(translate); + void collect_statistics(solver_state& s) { + std::lock_guard lock(m_mutex); + s.get_solver().collect_statistics(m_stats); } lbool solve(model_ref& mdl) { - - { - solver_state& st = *m_solvers[0]; - st.params().set_uint("restart.max", 200); - st.get_solver().updt_params(st.params()); - lbool is_sat = st.get_solver().check_sat(0, 0); - st.params().set_uint("restart.max", UINT_MAX); - st.get_solver().updt_params(st.params()); - switch (is_sat) { - case l_true: - get_model(mdl, 0); - return l_true; - case l_false: - return l_false; - default: - break; - } + vector threads; + for (unsigned i = 0; i < m_num_threads; ++i) + threads.push_back(std::thread([this]() { run_solver(); })); + for (std::thread& t : threads) + t.join(); + m_manager.limit().reset_cancel(); + if (m_exn_code == -1) + throw default_exception(m_exn_msg); + if (m_exn_code != 0) + throw z3_error(m_exn_code); + if (!m_models.empty()) { + mdl = m_models.back(); + return l_true; } - - while (true) { - int sz = pick_solvers(); - - - if (sz == 0) { - return l_false; - } - svector unsat; - int sat_index = -1; - - // Simplify phase. - IF_VERBOSE(1, verbose_stream() << "(solver.parallel :simplify " << sz << ")\n";); - IF_VERBOSE(1, display(verbose_stream()); verbose_stream() << "Number of solvers: " << sz << "\n";); - - #pragma omp parallel for - for (int i = 0; i < sz; ++i) { - lbool is_sat = simplify(*m_solvers[i]); - switch (is_sat) { - case l_false: - #pragma omp critical (parallel_tactic) - { - unsat.push_back(i); - } - break; - case l_true: - sat_index = i; - break; - case l_undef: - break; - } - } - if (sat_index != -1) { - get_model(mdl, sat_index); - return l_true; - } - sz -= unsat.size(); - remove_unsat(unsat); - if (sz == 0) continue; - - // Solve phase. - IF_VERBOSE(1, verbose_stream() << "(solver.parallel :solve " << sz << ")\n";); - IF_VERBOSE(1, display(verbose_stream()); verbose_stream() << "Number of solvers: " << sz << "\n";); - - #pragma omp parallel for - for (int i = 0; i < sz; ++i) { - lbool is_sat = solve(*m_solvers[i]); - switch (is_sat) { - case l_false: - #pragma omp critical (parallel_tactic) - { - unsat.push_back(i); - } - break; - case l_true: - sat_index = i; - break; - case l_undef: - break; - } - } - if (sat_index != -1) { - get_model(mdl, sat_index); - return l_true; - } - sz -= unsat.size(); - remove_unsat(unsat); - - sz = std::min(max_num_splits(), sz); - sz = std::min(static_cast(m_num_threads/2), sz); - if (sz == 0) continue; - - - // Split phase. - IF_VERBOSE(1, verbose_stream() << "(solver.parallel :split " << sz << ")\n";); - IF_VERBOSE(1, display(verbose_stream()); verbose_stream() << "Number of solvers: " << sz << "\n";); - - #pragma omp parallel for - for (int i = 0; i < sz; ++i) { - switch (cube(*m_solvers[i])) { - case l_false: - #pragma omp critical (parallel_tactic) - { - unsat.push_back(i); - } - break; - default: - #pragma omp critical (parallel_tactic) - { - update_progress(false); - } - break; - } - } - - remove_unsat(unsat); - update_max_conflicts(); - } - return l_undef; + if (m_has_undef) + return l_undef; + return l_false; } std::ostream& display(std::ostream& out) { - out << "branches: " << m_solvers.size() << "\n"; - for (solver_state* s : m_solvers) { - out << "cube " << s->cube_size() << " units " << s->num_units() << "\n"; - } m_stats.display(out); + m_queue.display(out); + std::lock_guard lock(m_mutex); + out << "(parallel_tactic :unsat " << m_num_unsat << " :progress " << m_progress << "% :models " << m_models.size() << ")\n"; return out; } - public: + parallel_tactic(ast_manager& m, params_ref const& p) : m_manager(m), m_params(p) { @@ -399,7 +544,8 @@ public: void operator ()(const goal_ref & g,goal_ref_buffer & result,model_converter_ref & mc,proof_converter_ref & pc,expr_dependency_ref & dep) { ast_manager& m = g->m(); solver* s = mk_fd_solver(m, m_params); - m_solvers.push_back(alloc(solver_state, 0, s, m_params)); + solver_state* st = alloc(solver_state, 0, s, m_params, cube_task); + m_queue.add_task(st); expr_ref_vector clauses(m); ptr_vector assumptions; obj_map bool2dep; @@ -434,8 +580,7 @@ public: } void cleanup() { - for (solver_state * s : m_solvers) dealloc(s); - m_solvers.reset(); + m_queue.reset(); init(); } @@ -446,15 +591,20 @@ public: virtual void updt_params(params_ref const & p) { m_params.copy(p); } + virtual void collect_param_descrs(param_descrs & r) { - // TBD + r.insert("conquer_batch_size", CPK_UINT, "(default: 1000) batch size of cubes to conquer"); + } + + unsigned conquer_batch_size() const { + return m_params.get_uint("conquer_batch_size", 1000); } virtual void collect_statistics(statistics & st) const { - for (solver_state const * s : m_solvers) { - s->get_solver().collect_statistics(st); - } st.copy(m_stats); + st.update("par unsat", m_num_unsat); + st.update("par models", m_models.size()); + st.update("par progress", m_progress); } virtual void reset_statistics() { m_stats.reset(); @@ -462,6 +612,8 @@ public: }; + + tactic * mk_parallel_tactic(ast_manager& m, params_ref const& p) { return alloc(parallel_tactic, m, p); } diff --git a/src/tactic/portfolio/pb2bv_solver.cpp b/src/tactic/portfolio/pb2bv_solver.cpp index ee4b14178..33a8fcd46 100644 --- a/src/tactic/portfolio/pb2bv_solver.cpp +++ b/src/tactic/portfolio/pb2bv_solver.cpp @@ -27,7 +27,6 @@ Notes: class pb2bv_solver : public solver_na2as { ast_manager& m; - params_ref m_params; mutable expr_ref_vector m_assertions; mutable ref m_solver; mutable th_rewriter m_th_rewriter; @@ -38,19 +37,24 @@ public: pb2bv_solver(ast_manager& m, params_ref const& p, solver* s): solver_na2as(m), m(m), - m_params(p), m_assertions(m), m_solver(s), m_th_rewriter(m, p), m_rewriter(m, p) { + solver::updt_params(p); } virtual ~pb2bv_solver() {} - virtual solver* translate(ast_manager& m, params_ref const& p) { + virtual solver* translate(ast_manager& dst_m, params_ref const& p) { flush_assertions(); - return alloc(pb2bv_solver, m, p, m_solver->translate(m, p)); + solver* result = alloc(pb2bv_solver, dst_m, p, m_solver->translate(dst_m, p)); + if (mc0()) { + ast_translation tr(m, dst_m); + result->set_model_converter(mc0()->translate(tr)); + } + return result; } virtual void assert_expr(expr * t) { @@ -78,8 +82,8 @@ public: return m_solver->check_sat(num_assumptions, assumptions); } - virtual void updt_params(params_ref const & p) { m_solver->updt_params(p); m_rewriter.updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { m_solver->collect_param_descrs(r); m_rewriter.collect_param_descrs(r); } + virtual void updt_params(params_ref const & p) { solver::updt_params(p); m_rewriter.updt_params(p); m_solver->updt_params(p); } + virtual void collect_param_descrs(param_descrs & r) { m_solver->collect_param_descrs(r); m_rewriter.collect_param_descrs(r);} virtual void set_produce_models(bool f) { m_solver->set_produce_models(f); } virtual void set_progress_callback(progress_callback * callback) { m_solver->set_progress_callback(callback); } virtual void collect_statistics(statistics & st) const { @@ -87,20 +91,19 @@ public: m_solver->collect_statistics(st); } virtual void get_unsat_core(ptr_vector & r) { m_solver->get_unsat_core(r); } - virtual void get_model(model_ref & mdl) { + virtual void get_model_core(model_ref & mdl) { m_solver->get_model(mdl); if (mdl) { filter_model(mdl); } } + virtual model_converter_ref get_model_converter() const { return m_solver->get_model_converter(); } virtual proof * get_proof() { return m_solver->get_proof(); } virtual std::string reason_unknown() const { return m_solver->reason_unknown(); } virtual void set_reason_unknown(char const* msg) { m_solver->set_reason_unknown(msg); } virtual void get_labels(svector & r) { m_solver->get_labels(r); } virtual ast_manager& get_manager() const { return m; } - virtual expr_ref lookahead(expr_ref_vector const& assumptions, expr_ref_vector const& candidates) { flush_assertions(); return m_solver->lookahead(assumptions, candidates); } virtual expr_ref cube() { flush_assertions(); return m_solver->cube(); } - virtual void get_lemmas(expr_ref_vector & lemmas) { flush_assertions(); m_solver->get_lemmas(lemmas); } virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) { return m_solver->find_mutexes(vars, mutexes); } virtual lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) { flush_assertions(); diff --git a/src/tactic/replace_proof_converter.h b/src/tactic/replace_proof_converter.h index b768a18a0..d5cf7dc31 100644 --- a/src/tactic/replace_proof_converter.h +++ b/src/tactic/replace_proof_converter.h @@ -45,6 +45,8 @@ public: // run the replacements the inverse direction. void invert() { m_proofs.reverse(); } + virtual void display(std::ostream & out) {} + }; #endif diff --git a/src/tactic/sls/bvsls_opt_engine.cpp b/src/tactic/sls/bvsls_opt_engine.cpp index e8547fdfd..502bcbde6 100644 --- a/src/tactic/sls/bvsls_opt_engine.cpp +++ b/src/tactic/sls/bvsls_opt_engine.cpp @@ -238,7 +238,7 @@ bool bvsls_opt_engine::what_if( mpz bvsls_opt_engine::find_best_move( ptr_vector & to_evaluate, - mpz score, + mpz & score, unsigned & best_const, mpz & best_value, unsigned & new_bit, diff --git a/src/tactic/sls/bvsls_opt_engine.h b/src/tactic/sls/bvsls_opt_engine.h index 67d9a5d02..9487130d3 100644 --- a/src/tactic/sls/bvsls_opt_engine.h +++ b/src/tactic/sls/bvsls_opt_engine.h @@ -61,7 +61,7 @@ protected: bool what_if(func_decl * fd, const unsigned & fd_inx, const mpz & temp, mpz & best_score, unsigned & best_const, mpz & best_value); - mpz find_best_move(ptr_vector & to_evaluate, mpz score, + mpz find_best_move(ptr_vector & to_evaluate, mpz & score, unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move, mpz const & max_score, expr * objective); diff --git a/src/tactic/sls/sls_tracker.h b/src/tactic/sls/sls_tracker.h index 4ad5c65f4..15f06f096 100644 --- a/src/tactic/sls/sls_tracker.h +++ b/src/tactic/sls/sls_tracker.h @@ -41,7 +41,24 @@ class sls_tracker { struct value_score { value_score() : m(0), value(unsynch_mpz_manager::mk_z(0)), score(0.0), score_prune(0.0), has_pos_occ(0), has_neg_occ(0), distance(0), touched(1) {}; + value_score(value_score && other) : + m(other.m), + value(std::move(other.value)), + score(other.score), + score_prune(other.score_prune), + has_pos_occ(other.has_pos_occ), + has_neg_occ(other.has_neg_occ), + distance(other.distance), + touched(other.touched) {} ~value_score() { if (m) m->del(value); } + void operator=(value_score && other) { + this->~value_score(); + new (this) value_score(std::move(other)); + } + value_score& operator=(value_score& other) { + UNREACHABLE(); + return *this; + } unsynch_mpz_manager * m; mpz value; double score; @@ -50,15 +67,6 @@ class sls_tracker { unsigned has_neg_occ; unsigned distance; // max distance from any root unsigned touched; - value_score & operator=(const value_score & other) { - SASSERT(m == 0 || m == other.m); - if (m) m->set(value, 0); else m = other.m; - m->set(value, other.value); - score = other.score; - distance = other.distance; - touched = other.touched; - return *this; - } }; public: @@ -294,7 +302,7 @@ public: if (!m_scores.contains(n)) { value_score vs; vs.m = & m_mpz_manager; - m_scores.insert(n, vs); + m_scores.insert(n, std::move(vs)); } // Update uplinks @@ -539,7 +547,7 @@ public: rational r_val; unsigned bv_sz; m_bv_util.is_numeral(val, r_val, bv_sz); - mpq q = r_val.to_mpq(); + const mpq& q = r_val.to_mpq(); SASSERT(m_mpz_manager.is_one(q.denominator())); set_value(fd, q.numerator()); } @@ -630,7 +638,7 @@ public: if (m_bv_util.is_bv_sort(s)) return get_random_bv(s); else if (m_manager.is_bool(s)) - return get_random_bool(); + return m_mpz_manager.dup(get_random_bool()); else NOT_IMPLEMENTED_YET(); // This only works for bit-vectors for now. } @@ -653,9 +661,7 @@ public: TRACE("sls", tout << "Abandoned model:" << std::endl; show_model(tout); ); for (entry_point_type::iterator it = m_entry_points.begin(); it != m_entry_points.end(); it++) { - mpz temp = m_zero; - set_value(it->m_value, temp); - m_mpz_manager.del(temp); + set_value(it->m_value, m_zero); } } @@ -931,7 +937,7 @@ public: rational q; if (!m_bv_util.is_numeral(n, q, bv_sz)) NOT_IMPLEMENTED_YET(); - mpq temp = q.to_mpq(); + const mpq& temp = q.to_mpq(); SASSERT(m_mpz_manager.is_one(temp.denominator())); m_mpz_manager.set(result, temp.numerator()); } @@ -1039,7 +1045,6 @@ public: unsigned pos = -1; if (m_ucb) { - value_score vscore; double max = -1.0; // Andreas: Commented things here might be used for track_unsat data structures as done in SLS for SAT. But seems to have no benefit. /* for (unsigned i = 0; i < m_where_false.size(); i++) { @@ -1048,7 +1053,7 @@ public: expr * e = as[i]; if (m_mpz_manager.neq(get_value(e), m_one)) { - vscore = m_scores.find(e); + value_score & vscore = m_scores.find(e); // Andreas: Select the assertion with the greatest ucb score. Potentially add some noise. // double q = vscore.score + m_ucb_constant * sqrt(log((double)m_touched) / vscore.touched); double q = vscore.score + m_ucb_constant * sqrt(log((double)m_touched) / vscore.touched) + m_ucb_noise * get_random_uint(8); diff --git a/src/tactic/tactic.cpp b/src/tactic/tactic.cpp index 6c4ca7476..7610c420a 100644 --- a/src/tactic/tactic.cpp +++ b/src/tactic/tactic.cpp @@ -175,7 +175,7 @@ void exec(tactic & t, goal_ref const & in, goal_ref_buffer & result, model_conve } -lbool check_sat(tactic & t, goal_ref & g, model_ref & md, labels_vec & labels, proof_ref & pr, expr_dependency_ref & core, std::string & reason_unknown) { +lbool check_sat(tactic & t, goal_ref & g, model_ref & md, model_converter_ref& mc, labels_vec & labels, proof_ref & pr, expr_dependency_ref & core, std::string & reason_unknown) { bool models_enabled = g->models_enabled(); bool proofs_enabled = g->proofs_enabled(); bool cores_enabled = g->unsat_core_enabled(); @@ -184,7 +184,6 @@ lbool check_sat(tactic & t, goal_ref & g, model_ref & md, labels_vec & labels, p core = 0; ast_manager & m = g->m(); goal_ref_buffer r; - model_converter_ref mc; proof_converter_ref pc; try { exec(t, g, r, mc, pc, core); @@ -219,9 +218,9 @@ lbool check_sat(tactic & t, goal_ref & g, model_ref & md, labels_vec & labels, p } else { if (models_enabled) { - model_converter2model(m, mc.get(), md); - if (mc) - (*mc)(labels, 0); + model_converter2model(m, mc.get(), md); + if (mc) + (*mc)(labels, 0); } reason_unknown = "incomplete"; return l_undef; diff --git a/src/tactic/tactic.h b/src/tactic/tactic.h index 0e4c61611..93da52366 100644 --- a/src/tactic/tactic.h +++ b/src/tactic/tactic.h @@ -153,7 +153,7 @@ public: #define MK_SIMPLE_TACTIC_FACTORY(NAME, ST) MK_TACTIC_FACTORY(NAME, return ST;) void exec(tactic & t, goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core); -lbool check_sat(tactic & t, goal_ref & g, model_ref & md, labels_vec & labels, proof_ref & pr, expr_dependency_ref & core, std::string & reason_unknown); +lbool check_sat(tactic & t, goal_ref & g, model_ref & md, model_converter_ref& mc, labels_vec & labels, proof_ref & pr, expr_dependency_ref & core, std::string & reason_unknown); // Throws an exception if goal \c in requires proof generation. void fail_if_proof_generation(char const * tactic_name, goal_ref const & in); diff --git a/src/test/im_float_config.h b/src/test/im_float_config.h index 436d9ecaf..3905358ea 100644 --- a/src/test/im_float_config.h +++ b/src/test/im_float_config.h @@ -63,10 +63,4 @@ public: numeral_manager & m() const { return const_cast(m_manager); } }; -template -inline void del_f_interval(im_float_config & cfg, typename im_float_config::interval & a) { - cfg.m().del(a.m_lower); - cfg.m().del(a.m_upper); -} - #endif diff --git a/src/test/interval.cpp b/src/test/interval.cpp index ba218c010..21b25a9be 100644 --- a/src/test/interval.cpp +++ b/src/test/interval.cpp @@ -125,7 +125,8 @@ static bool mk_interval(im_default_config & cfg, interval & a, bool l_inf, bool } #endif -static void mk_random_interval(im_default_config & cfg, interval & a, unsigned magnitude) { +template +static void mk_random_interval(T & cfg, interval & a, unsigned magnitude) { switch (rand()%3) { case 0: // Neg, Neg @@ -195,11 +196,6 @@ static void mk_random_interval(im_default_config & cfg, interval & a, unsigned m } } -static void del_interval(im_default_config & cfg, interval & a) { - cfg.m().del(a.m_lower); - cfg.m().del(a.m_upper); -} - #define BUFFER_SZ 256 static int g_problem_id = 0; static char g_buffer[BUFFER_SZ]; @@ -238,19 +234,18 @@ static void display_lemmas(unsynch_mpq_manager & nm, char const * result_term, static void tst_ ## NAME(unsigned N, unsigned magnitude) { \ reslimit rl; \ unsynch_mpq_manager nm; \ - im_default_config imc(nm); \ - interval_manager im(rl, imc); \ + interval_manager im(rl, nm); \ interval a, b, r; \ \ for (unsigned i = 0; i < N; i++) { \ - mk_random_interval(imc, a, magnitude); \ - mk_random_interval(imc, b, magnitude); \ + mk_random_interval(im, a, magnitude); \ + mk_random_interval(im, b, magnitude); \ interval_deps deps; \ im.NAME(a, b, r, deps); \ \ display_lemmas(nm, RES_TERM, a, b, r, deps); \ } \ - del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); \ + im.del(a); im.del(b); im.del(r); \ } MK_BINARY(mul, "(* a b)"); @@ -260,56 +255,52 @@ MK_BINARY(sub, "(- a b)"); static void tst_neg(unsigned N, unsigned magnitude) { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); interval a, b, r; for (unsigned i = 0; i < N; i++) { - mk_random_interval(imc, a, magnitude); + mk_random_interval(im, a, magnitude); interval_deps deps; im.neg(a, r, deps); display_lemmas(nm, "(- a)", a, b, r, deps); } - del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); + im.del(a); im.del(b); im.del(r); } static void tst_pw_2(unsigned N, unsigned magnitude) { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); interval a, b, r; for (unsigned i = 0; i < N; i++) { - mk_random_interval(imc, a, magnitude); + mk_random_interval(im, a, magnitude); interval_deps deps; im.power(a, 2, r, deps); display_lemmas(nm, "(* a a)", a, b, r, deps); } - del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); + im.del(a); im.del(b); im.del(r); } static void tst_pw_3(unsigned N, unsigned magnitude) { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); interval a, b, r; for (unsigned i = 0; i < N; i++) { - mk_random_interval(imc, a, magnitude); + mk_random_interval(im, a, magnitude); interval_deps deps; im.power(a, 3, r, deps); display_lemmas(nm, "(* a a a)", a, b, r, deps); } - del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); + im.del(a); im.del(b); im.del(r); } static void tst_root_2(unsigned N, unsigned magnitude, unsigned precision) { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); interval a, b, r; scoped_mpq p(nm); p = precision; @@ -317,7 +308,7 @@ static void tst_root_2(unsigned N, unsigned magnitude, unsigned precision) { unsigned i = 0; while (i < N) { - mk_random_interval(imc, a, magnitude); + mk_random_interval(im, a, magnitude); if (!im.lower_is_neg(a)) { i++; interval_deps deps; @@ -325,14 +316,13 @@ static void tst_root_2(unsigned N, unsigned magnitude, unsigned precision) { display_lemmas(nm, "(^ a (/ 1.0 2.0))", a, b, r, deps); } } - del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); + im.del(a); im.del(b); im.del(r); } static void tst_root_3(unsigned N, unsigned magnitude, unsigned precision) { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); interval a, b, r; scoped_mpq p(nm); p = precision; @@ -340,25 +330,24 @@ static void tst_root_3(unsigned N, unsigned magnitude, unsigned precision) { unsigned i = 0; while (i < N) { - mk_random_interval(imc, a, magnitude); + mk_random_interval(im, a, magnitude); i++; interval_deps deps; im.nth_root(a, 3, p, r, deps); display_lemmas(nm, "(^ a (/ 1.0 3.0))", a, b, r, deps); } - del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); + im.del(a); im.del(b); im.del(r); } static void tst_inv(unsigned N, unsigned magnitude) { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); interval a, b, r; for (unsigned i = 0; i < N; i++) { while (true) { - mk_random_interval(imc, a, magnitude); + mk_random_interval(im, a, magnitude); if (!im.contains_zero(a)) break; } @@ -366,20 +355,19 @@ static void tst_inv(unsigned N, unsigned magnitude) { im.inv(a, r, deps); display_lemmas(nm, "(/ 1 a)", a, b, r, deps); } - del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); + im.del(a); im.del(b); im.del(r); } static void tst_div(unsigned N, unsigned magnitude) { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); interval a, b, r; for (unsigned i = 0; i < N; i++) { - mk_random_interval(imc, a, magnitude); + mk_random_interval(im, a, magnitude); while (true) { - mk_random_interval(imc, b, magnitude); + mk_random_interval(im, b, magnitude); if (!im.contains_zero(b)) break; } @@ -387,7 +375,7 @@ static void tst_div(unsigned N, unsigned magnitude) { im.div(a, b, r, deps); display_lemmas(nm, "(/ a b)", a, b, r, deps); } - del_interval(imc, a); del_interval(imc, b); del_interval(imc, r); + im.del(a); im.del(b); im.del(r); } #include "test/im_float_config.h" @@ -396,8 +384,7 @@ static void tst_div(unsigned N, unsigned magnitude) { static void tst_float() { unsynch_mpq_manager qm; mpf_manager fm; - im_float_config ifc(fm); - interval_manager > im(ifc); + interval_manager > im(fm); im_float_config::interval a, b, c; scoped_mpq minus_one_third(qm), one_third(qm), two_third(qm), minus_two_third(qm); qm.set(minus_one_third, -1, 3); @@ -424,15 +411,14 @@ static void tst_float() { im.display(std::cout, c); std::cout << "\n"; - del_f_interval(ifc, a); del_f_interval(ifc, b); del_f_interval(ifc, c); + im.del(a); im.del(b); im.del(r); } #endif void tst_pi() { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); interval r; for (unsigned i = 0; i < 8; i++) { im.pi(i, r); @@ -440,7 +426,7 @@ void tst_pi() { nm.display_decimal(std::cout, im.upper(r), 32); std::cout << "\n"; ENSURE(nm.lt(im.lower(r), im.upper(r))); } - del_interval(imc, r); + im.del(r); } #if 0 diff --git a/src/test/main.cpp b/src/test/main.cpp index 33d0abae5..64f754667 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -10,27 +10,28 @@ #include "util/memory_manager.h" #include "util/gparams.h" + // // Unit tests fail by asserting. // If they return, we assume the unit test succeeds // and print "PASS" to indicate success. // -#define TST(MODULE) { \ - std::string s("test "); \ - s += #MODULE; \ - void tst_##MODULE(); \ - if (do_display_usage) \ - std::cout << #MODULE << "\n"; \ - for (int i = 0; i < argc; i++) \ - if (test_all || strcmp(argv[i], #MODULE) == 0) { \ - enable_trace(#MODULE); \ - enable_debug(#MODULE); \ - timeit timeit(true, s.c_str()); \ - tst_##MODULE(); \ - std::cout << "PASS" << std::endl; \ - } \ -} +#define TST(MODULE) { \ + std::string s("test "); \ + s += #MODULE; \ + void tst_##MODULE(); \ + if (do_display_usage) \ + std::cout << #MODULE << "\n"; \ + for (int i = 0; i < argc; i++) \ + if (test_all || strcmp(argv[i], #MODULE) == 0) { \ + enable_trace(#MODULE); \ + enable_debug(#MODULE); \ + timeit timeit(true, s.c_str()); \ + tst_##MODULE(); \ + std::cout << "PASS" << std::endl; \ + } \ + } #define TST_ARGV(MODULE) { \ std::string s("test "); \ @@ -208,6 +209,7 @@ int main(int argc, char ** argv) { TST(prime_generator); TST(permutation); TST(nlsat); + if (test_all) return 0; TST(ext_numeral); TST(interval); TST(f2n); @@ -223,7 +225,7 @@ int main(int argc, char ** argv) { TST(heap_trie); TST(karr); TST(no_overflow); - TST(memory); + // TST(memory); TST(datalog_parser); TST_ARGV(datalog_parser_file); TST(dl_query); diff --git a/src/test/nlsat.cpp b/src/test/nlsat.cpp index 4d9d4579e..ecf73843f 100644 --- a/src/test/nlsat.cpp +++ b/src/test/nlsat.cpp @@ -698,10 +698,8 @@ static void tst10() { void tst_nlsat() { tst10(); std::cout << "------------------\n"; - exit(0); tst9(); std::cout << "------------------\n"; - exit(0); tst8(); std::cout << "------------------\n"; tst7(); diff --git a/src/test/proof_checker.cpp b/src/test/proof_checker.cpp index adf77e12e..7a9b619cd 100644 --- a/src/test/proof_checker.cpp +++ b/src/test/proof_checker.cpp @@ -4,11 +4,11 @@ Copyright (c) 2015 Microsoft Corporation --*/ -#include "ast/proof_checker/proof_checker.h" +#include "ast/proofs/proof_checker.h" #include "ast/ast_ll_pp.h" void tst_checker1() { - ast_manager m(PGM_FINE); + ast_manager m(PGM_ENABLED); expr_ref a(m); proof_ref p1(m), p2(m), p3(m), p4(m); expr_ref_vector side_conditions(m); diff --git a/src/test/small_object_allocator.cpp b/src/test/small_object_allocator.cpp index cdac0370d..2f08d553e 100644 --- a/src/test/small_object_allocator.cpp +++ b/src/test/small_object_allocator.cpp @@ -18,8 +18,11 @@ void tst_small_object_allocator() { TRACE("small_object_allocator", tout << "p1: " << (void*)p1 << " q1: " << (void*)q1 << " p2: " << (void*)p2 << "\n";); soa.deallocate(13,p1); + soa.deallocate(14,q1); + soa.deallocate(13,p2); char * p3 = new (soa) char[13]; TRACE("small_object_allocator", tout << "p3: " << (void*)p3 << "\n";); + soa.deallocate(13,p3); char * r1 = new (soa) char[1]; char * r2 = new (soa) char[1]; @@ -36,6 +39,10 @@ void tst_small_object_allocator() { r3 = new (soa) char[1]; TRACE("small_object_allocator", tout << "r1: " << (void*)r1 << " r2: " << (void*)r2 << " r3: " << (void*)r3 << " r4: " << (void*)r4 << "\n";); + soa.deallocate(1,r1); + soa.deallocate(1,r2); + soa.deallocate(1,r3); + soa.deallocate(1,r4); (void)r1; (void)r2; (void)r3; diff --git a/src/test/trigo.cpp b/src/test/trigo.cpp index c1b73c423..686800162 100644 --- a/src/test/trigo.cpp +++ b/src/test/trigo.cpp @@ -39,9 +39,8 @@ static void tst_sine_core(std::ostream & out, unsynch_mpq_manager & nm, interval static void tst_sine(std::ostream & out, unsigned N, unsigned k) { unsynch_mpq_manager nm; - im_default_config imc(nm); reslimit rl; - interval_manager im(rl, imc); + interval_manager im(rl, nm); scoped_mpq a(nm); nm.set(a, 0); tst_sine_core(out, nm, im, a, 1); @@ -67,8 +66,7 @@ static void tst_cosine_core(std::ostream & out, unsynch_mpq_manager & nm, interv static void tst_cosine(std::ostream & out, unsigned N, unsigned k) { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); scoped_mpq a(nm); nm.set(a, 0); tst_cosine_core(out, nm, im, a, 1); @@ -100,8 +98,7 @@ template static void tst_float_sine(std::ostream & out, unsigned N, unsigned k) { reslimit rl; fmanager fm; - im_float_config ifc(fm, EBITS, SBITS); - interval_manager > im(rl, ifc); + interval_manager > im(rl, im_float_config(fm, EBITS, SBITS)); _scoped_numeral a(fm); fm.set(a, EBITS, SBITS, static_cast(0)); tst_float_sine_core(out, fm, im, a, 1); @@ -136,8 +133,7 @@ static void tst_mpf_bug() { static void tst_e(std::ostream & out) { reslimit rl; unsynch_mpq_manager nm; - im_default_config imc(nm); - interval_manager im(rl, imc); + interval_manager im(rl, nm); im_default_config::interval r; for (unsigned i = 0; i < 64; i++) { im.e(i, r); @@ -152,8 +148,7 @@ static void tst_e_float(std::ostream & out) { reslimit rl; unsynch_mpq_manager qm; mpf_manager fm; - im_float_config ifc(fm); - interval_manager > im(rl, ifc); + interval_manager > im(rl, fm); scoped_mpq q(qm); im_float_config::interval r; for (unsigned i = 0; i < 64; i++) { @@ -161,7 +156,7 @@ static void tst_e_float(std::ostream & out) { out << fm.to_rational_string(im.lower(r)) << " <= E\n"; out << "E <= " << fm.to_rational_string(im.upper(r)) << "\n"; } - del_f_interval(ifc, r); + im.del(r); } void tst_trigo() { diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 7ed68c89f..85b6f955c 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -28,6 +28,7 @@ z3_add_component(util lbool.cpp luby.cpp memory_manager.cpp + min_cut.cpp mpbq.cpp mpf.cpp mpff.cpp diff --git a/src/util/buffer.h b/src/util/buffer.h index 503788fa0..c64e23d8b 100644 --- a/src/util/buffer.h +++ b/src/util/buffer.h @@ -149,6 +149,13 @@ public: new (m_buffer + m_pos) T(elem); m_pos++; } + + void push_back(T && elem) { + if (m_pos >= m_capacity) + expand(); + new (m_buffer + m_pos) T(std::move(elem)); + m_pos++; + } void pop_back() { if (CallDestructors) { diff --git a/src/util/container_util.h b/src/util/container_util.h new file mode 100644 index 000000000..e114c87b9 --- /dev/null +++ b/src/util/container_util.h @@ -0,0 +1,122 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + container_util.h + +Abstract: + + Useful functions for containers + +Author: + + Krystof Hoder, Nikolaj Bjorner 2017-10-24 + +Revision History: + + Extracted from dl_util.h + +--*/ + +#ifndef CONTAINER_UTIL_H_ +#define CONTAINER_UTIL_H_ + +// ----------------------------------- +// +// container functions +// +// ----------------------------------- + +template + void set_intersection(Set1 & tgt, const Set2 & src) { + svector to_remove; + for (auto const& itm : tgt) + if (!src.contains(itm)) + to_remove.push_back(itm); + while (!to_remove.empty()) { + tgt.remove(to_remove.back()); + to_remove.pop_back(); + } +} + +template +void set_difference(Set & tgt, const Set & to_remove) { + for (auto const& itm : to_remove) + tgt.remove(itm); +} + +template +void set_union(Set1 & tgt, const Set2 & to_add) { + for (auto const& itm : to_add) + tgt.insert(itm); +} + +template +void unite_disjoint_maps(T & tgt, const T & src) { + for (auto const& kv : src) { + SASSERT(!tgt.contains(kv.m_key)); + tgt.insert(kv.m_key, kv.m_value); + } +} + +template +void collect_map_range(T & acc, const U & map) { + for (auto const& kv : map) + acc.push_back(kv.m_value); +} + + +template +void print_container(const T & begin, const T & end, std::ostream & out) { + T it = begin; + out << "("; + bool first = true; + for(; it!=end; ++it) { + if(first) { first = false; } else { out << ","; } + out << (*it); + } + out << ")"; +} + +template +void print_container(const T & cont, std::ostream & out) { + print_container(cont.begin(), cont.end(), out); +} + +template +void print_container(const ref_vector & cont, std::ostream & out) { + print_container(cont.c_ptr(), cont.c_ptr() + cont.size(), out); +} + +template +void print_map(const T & cont, std::ostream & out) { + out << "("; + bool first = true; + for (auto const& kv : cont) { + if (first) { first = false; } else { out << ","; } + out << kv.m_key << "->" << kv.m_value; + } + out << ")"; +} + +template +unsigned find_index(const It & begin, const It & end, const V & val) { + It it = begin; + for (unsigned idx = 0; it != end; it++, idx++) { + if (*it == val) { + return idx; + } + } + return UINT_MAX; +} + +template +bool containers_equal(const T & begin1, const T & end1, const U & begin2, const U & end2) { + T it1 = begin1; + U it2 = begin2; + for (; it1 != end1 && it2 != end2 && *it1 == *it2; ++it1, ++it2) {}; + return it1 == end1 && it2 == end2; +} + +#endif diff --git a/src/util/f2n.h b/src/util/f2n.h index e5e84f6f0..d55b21d3d 100644 --- a/src/util/f2n.h +++ b/src/util/f2n.h @@ -46,6 +46,9 @@ public: m_manager.set(m_one, ebits, sbits, 1); } + f2n(f2n && other) : m_manager(other.m_manager), m_mode(other.m_mode), m_ebits(other.m_ebits), m_sbits(other.m_sbits), + m_tmp1(std::move(other.m_tmp1)), m_one(std::move(other.m_one)) {} + ~f2n() { m().del(m_tmp1); m().del(m_one); diff --git a/src/util/hashtable.h b/src/util/hashtable.h index 020c47b2b..fa9fef180 100644 --- a/src/util/hashtable.h +++ b/src/util/hashtable.h @@ -54,7 +54,7 @@ public: bool is_used() const { return m_state == HT_USED; } T & get_data() { return m_data; } const T & get_data() const { return m_data; } - void set_data(const T & d) { m_data = d; m_state = HT_USED; } + void set_data(T && d) { m_data = std::move(d); m_state = HT_USED; } void set_hash(unsigned h) { m_hash = h; } void mark_as_deleted() { m_state = HT_DELETED; } void mark_as_free() { m_state = HT_FREE; } @@ -187,10 +187,42 @@ protected: } } + static void move_table(entry * source, unsigned source_capacity, entry * target, unsigned target_capacity) { + SASSERT(target_capacity >= source_capacity); + unsigned target_mask = target_capacity - 1; + entry * source_end = source + source_capacity; + entry * target_end = target + target_capacity; + for (entry * source_curr = source; source_curr != source_end; ++source_curr) { + if (source_curr->is_used()) { + unsigned hash = source_curr->get_hash(); + unsigned idx = hash & target_mask; + entry * target_begin = target + idx; + entry * target_curr = target_begin; + for (; target_curr != target_end; ++target_curr) { + SASSERT(!target_curr->is_deleted()); + if (target_curr->is_free()) { + *target_curr = std::move(*source_curr); + goto end; + } + } + for (target_curr = target; target_curr != target_begin; ++target_curr) { + SASSERT(!target_curr->is_deleted()); + if (target_curr->is_free()) { + *target_curr = std::move(*source_curr); + goto end; + } + } + UNREACHABLE(); + end: + ; + } + } + } + void expand_table() { unsigned new_capacity = m_capacity << 1; entry * new_table = alloc_table(new_capacity); - copy_table(m_table, m_capacity, new_table, new_capacity); + move_table(m_table, m_capacity, new_table, new_capacity); delete_table(); m_table = new_table; m_capacity = new_capacity; @@ -202,7 +234,7 @@ protected: if (memory::is_out_of_memory()) return; entry * new_table = alloc_table(m_capacity); - copy_table(m_table, m_capacity, new_table, m_capacity); + move_table(m_table, m_capacity, new_table, m_capacity); delete_table(); m_table = new_table; m_num_deleted = 0; @@ -321,7 +353,7 @@ public: #define INSERT_LOOP_BODY() { \ if (curr->is_used()) { \ if (curr->get_hash() == hash && equals(curr->get_data(), e)) { \ - curr->set_data(e); \ + curr->set_data(std::move(e)); \ return; \ } \ HS_CODE(m_st_collision++;); \ @@ -330,7 +362,7 @@ public: entry * new_entry; \ if (del_entry) { new_entry = del_entry; m_num_deleted--; } \ else { new_entry = curr; } \ - new_entry->set_data(e); \ + new_entry->set_data(std::move(e)); \ new_entry->set_hash(hash); \ m_size++; \ return; \ @@ -342,7 +374,7 @@ public: } \ } ((void) 0) - void insert(data const & e) { + void insert(data && e) { if ((m_size + m_num_deleted) << 2 > (m_capacity * 3)) { // if ((m_size + m_num_deleted) * 2 > (m_capacity)) { expand_table(); @@ -363,6 +395,11 @@ public: UNREACHABLE(); } + void insert(const data & e) { + data tmp(e); + insert(std::move(tmp)); + } + #define INSERT_LOOP_CORE_BODY() { \ if (curr->is_used()) { \ if (curr->get_hash() == hash && equals(curr->get_data(), e)) { \ @@ -375,7 +412,7 @@ public: entry * new_entry; \ if (del_entry) { new_entry = del_entry; m_num_deleted--; } \ else { new_entry = curr; } \ - new_entry->set_data(e); \ + new_entry->set_data(std::move(e)); \ new_entry->set_hash(hash); \ m_size++; \ et = new_entry; \ @@ -393,7 +430,7 @@ public: Return true if it is a new element, and false otherwise. Store the entry/slot of the table in et. */ - bool insert_if_not_there_core(data const & e, entry * & et) { + bool insert_if_not_there_core(data && e, entry * & et) { if ((m_size + m_num_deleted) << 2 > (m_capacity * 3)) { // if ((m_size + m_num_deleted) * 2 > (m_capacity)) { expand_table(); @@ -415,6 +452,11 @@ public: return 0; } + bool insert_if_not_there_core(const data & e, entry * & et) { + data temp(e); + return insert_if_not_there_core(std::move(temp), et); + } + /** \brief Insert the element e if it is not in the table. Return a reference to e or to an object identical to e diff --git a/src/util/min_cut.cpp b/src/util/min_cut.cpp new file mode 100644 index 000000000..b7c30f546 --- /dev/null +++ b/src/util/min_cut.cpp @@ -0,0 +1,232 @@ +/*++ +Copyright (c) 2017 Arie Gurfinkel + +Module Name: + + min_cut.cpp + +Abstract: + min cut solver + +Author: + Bernhard Gleiss + +Revision History: + + +--*/ +#include "util/min_cut.h" +#include "util/trace.h" + +min_cut::min_cut() { + // push back two empty vectors for source and sink + m_edges.push_back(edge_vector()); + m_edges.push_back(edge_vector()); +} + +unsigned min_cut::new_node() { + m_edges.push_back(edge_vector()); + return m_edges.size() - 1; +} + +void min_cut::add_edge(unsigned int i, unsigned int j, unsigned capacity) { + m_edges.reserve(i + 1); + m_edges[i].push_back(edge(j, capacity)); + TRACE("spacer.mincut", tout << "adding edge (" << i << "," << j << ")\n";); +} + +void min_cut::compute_min_cut(unsigned_vector& cut_nodes) { + if (m_edges.size() == 2) { + return; + } + + m_d.resize(m_edges.size()); + m_pred.resize(m_edges.size()); + + // compute initial distances and number of nodes + compute_initial_distances(); + + unsigned i = 0; + + while (m_d[0] < m_edges.size()) { + unsigned j = get_admissible_edge(i); + + if (j < m_edges.size()) { + // advance(i) + m_pred[j] = i; + i = j; + + // if i is the sink, augment path + if (i == 1) { + augment_path(); + i = 0; + } + } + else { + // retreat + compute_distance(i); + if (i != 0) { + i = m_pred[i]; + } + } + } + + // split nodes into reachable and unreachable ones + bool_vector reachable(m_edges.size()); + compute_reachable_nodes(reachable); + + // find all edges between reachable and unreachable nodes and + // for each such edge, add corresponding lemma to unsat-core + compute_cut_and_add_lemmas(reachable, cut_nodes); +} + +void min_cut::compute_initial_distances() { + unsigned_vector todo; + bool_vector visited(m_edges.size()); + + todo.push_back(0); // start at the source, since we do postorder traversel + + while (!todo.empty()) { + unsigned current = todo.back(); + + // if we haven't already visited current + if (!visited[current]) { + bool exists_unvisited_parent = false; + + // add unprocessed parents to stack for DFS. If there is at least + // one unprocessed parent, don't compute the result + // for current now, but wait until those unprocessed parents are processed + for (auto const& edge : m_edges[current]) { + unsigned parent = edge.node; + + // if we haven't visited the current parent yet + if (!visited[parent]) { + // add it to the stack + todo.push_back(parent); + exists_unvisited_parent = true; + } + } + + // if we already visited all parents, we can visit current too + if (!exists_unvisited_parent) { + visited[current] = true; + todo.pop_back(); + + compute_distance(current); // I.H. all parent distances are already computed + } + } + else { + todo.pop_back(); + } + } +} + +unsigned min_cut::get_admissible_edge(unsigned i) { + for (const auto& edge : m_edges[i]) { + if (edge.weight > 0 && m_d[i] == m_d[edge.node] + 1) { + return edge.node; + } + } + return m_edges.size(); // no element found +} + +void min_cut::augment_path() { + // find bottleneck capacity + unsigned max = std::numeric_limits::max(); + unsigned k = 1; + while (k != 0) { + unsigned l = m_pred[k]; + for (const auto& edge : m_edges[l]) { + if (edge.node == k) { + max = std::min(max, edge.weight); + } + } + k = l; + } + + k = 1; + while (k != 0) { + unsigned l = m_pred[k]; + + // decrease capacity + for (auto& edge : m_edges[l]) { + if (edge.node == k) { + edge.weight -= max; + } + } + // increase reverse flow + bool already_exists = false; + for (auto& edge : m_edges[k]) { + if (edge.node == l) { + already_exists = true; + edge.weight += max; + } + } + if (!already_exists) { + m_edges[k].push_back(edge(1, max)); + } + k = l; + } +} + +void min_cut::compute_distance(unsigned i) { + if (i == 1) { // sink node + m_d[1] = 0; + } + else { + unsigned min = std::numeric_limits::max(); + + // find edge (i,j) with positive residual capacity and smallest distance + for (const auto& edge : m_edges[i]) { + if (edge.weight > 0) { + min = std::min(min, m_d[edge.node] + 1); + } + } + m_d[i] = min; + } +} + +void min_cut::compute_reachable_nodes(bool_vector& reachable) { + unsigned_vector todo; + + todo.push_back(0); + while (!todo.empty()) { + unsigned current = todo.back(); + todo.pop_back(); + + if (!reachable[current]) { + reachable[current] = true; + + for (const auto& edge : m_edges[current]) { + if (edge.weight > 0) { + todo.push_back(edge.node); + } + } + } + } +} + +void min_cut::compute_cut_and_add_lemmas(bool_vector& reachable, unsigned_vector& cut_nodes) { + unsigned_vector todo; + bool_vector visited(m_edges.size()); + + todo.push_back(0); + while (!todo.empty()) { + unsigned current = todo.back(); + todo.pop_back(); + + if (!visited[current]) { + visited[current] = true; + + for (const auto& edge : m_edges[current]) { + unsigned successor = edge.node; + if (reachable[successor]) { + todo.push_back(successor); + } + else { + cut_nodes.push_back(successor); + } + } + } + } +} diff --git a/src/util/min_cut.h b/src/util/min_cut.h new file mode 100644 index 000000000..51a330126 --- /dev/null +++ b/src/util/min_cut.h @@ -0,0 +1,63 @@ +/*++ +Copyright (c) 2017 Arie Gurfinkel + +Module Name: + + min_cut.h + +Abstract: + min cut solver + +Author: + Bernhard Gleiss + +Revision History: + + +--*/ + +#ifndef MIN_CUT_H_ +#define MIN_CUT_H_ + +#include "util/vector.h" + + +class min_cut { +public: + min_cut(); + + /* + \brief create a node + */ + unsigned new_node(); + + /* + \brief add an i -> j edge with (unit) capacity + */ + void add_edge(unsigned i, unsigned j, unsigned capacity = 1); + + /* + \brief produce a min cut between source node = 0 and target node = 1. + NB. the function changes capacities on edges. + */ + void compute_min_cut(unsigned_vector& cut_nodes); + +private: + + typedef svector bool_vector; + struct edge { unsigned node; unsigned weight; edge(unsigned n, unsigned w): node(n), weight(w) {} edge(): node(0), weight(0) {} }; + typedef svector edge_vector; + + vector m_edges; // map from node to all outgoing edges together with their weights (also contains "reverse edges") + unsigned_vector m_d; // approximation of distance from node to sink in residual graph + unsigned_vector m_pred; // predecessor-information for reconstruction of augmenting path + + void compute_initial_distances(); + unsigned get_admissible_edge(unsigned i); + void augment_path(); + void compute_distance(unsigned i); + void compute_reachable_nodes(bool_vector& reachable); + void compute_cut_and_add_lemmas(bool_vector& reachable, unsigned_vector& cut_nodes); +}; + +#endif diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index 3218419a9..9e309a726 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -40,12 +40,6 @@ mpf::mpf(unsigned _ebits, unsigned _sbits): set(ebits, sbits); } -mpf::mpf(mpf const & other) { - // It is safe if the mpz numbers are small. - // I need it for resize method in vector. - // UNREACHABLE(); -} - mpf::~mpf() { } diff --git a/src/util/mpf.h b/src/util/mpf.h index 8070768b2..e679be558 100644 --- a/src/util/mpf.h +++ b/src/util/mpf.h @@ -50,7 +50,12 @@ class mpf { public: mpf(); mpf(unsigned ebits, unsigned sbits); - mpf(mpf const & other); + mpf(mpf && other) : + ebits(other.ebits), + sbits(other.sbits), + sign(other.sign), + significand(std::move(other.significand)), + exponent(other.exponent) {} ~mpf(); unsigned get_ebits() const { return ebits; } unsigned get_sbits() const { return sbits; } diff --git a/src/util/mpq.h b/src/util/mpq.h index 5aa3ca083..fd0ae13d4 100644 --- a/src/util/mpq.h +++ b/src/util/mpq.h @@ -31,11 +31,10 @@ class mpq { public: mpq(int v):m_num(v), m_den(1) {} mpq():m_den(1) {} + mpq(mpq && other) : m_num(std::move(other.m_num)), m_den(std::move(other.m_den)) {} void swap(mpq & other) { m_num.swap(other.m_num); m_den.swap(other.m_den); } mpz const & numerator() const { return m_num; } mpz const & denominator() const { return m_den; } - - double get_double() const; }; inline void swap(mpq & m1, mpq & m2) { m1.swap(m2); } @@ -745,6 +744,12 @@ public: reset_denominator(a); } + mpq dup(const mpq & source) { + mpq temp; + set(temp, source); + return temp; + } + void swap(mpz & a, mpz & b) { mpz_manager::swap(a, b); } void swap(mpq & a, mpq & b) { diff --git a/src/util/mpz.h b/src/util/mpz.h index 7001b9a42..f04430e17 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -94,6 +94,9 @@ class mpz { public: mpz(int v):m_val(v), m_ptr(0) {} mpz():m_val(0), m_ptr(0) {} + mpz(mpz && other) : m_val(other.m_val), m_ptr(0) { + std::swap(m_ptr, other.m_ptr); + } void swap(mpz & other) { std::swap(m_val, other.m_val); std::swap(m_ptr, other.m_ptr); @@ -668,6 +671,12 @@ public: } } + void set(mpz & target, mpz && source) { + del(target); + target.m_val = source.m_val; + std::swap(target.m_ptr, source.m_ptr); + } + void set(mpz & a, int val) { del(a); a.m_val = val; @@ -700,6 +709,12 @@ public: void set(mpz & target, unsigned sz, digit_t const * digits); + mpz dup(const mpz & source) { + mpz temp; + set(temp, source); + return temp; + } + void reset(mpz & a) { del(a); a.m_val = 0; diff --git a/src/util/obj_hashtable.h b/src/util/obj_hashtable.h index 189d1e1a0..df279383b 100644 --- a/src/util/obj_hashtable.h +++ b/src/util/obj_hashtable.h @@ -69,6 +69,10 @@ public: m_key(k), m_value(v) { } + key_data(Key * k, Value && v) : + m_key(k), + m_value(std::move(v)) { + } Value const & get_value() const { return m_value; } Key & get_key () const { return *m_key; } unsigned hash() const { return m_key->hash(); } @@ -86,7 +90,7 @@ public: bool is_used() const { return m_data.m_key != reinterpret_cast(0) && m_data.m_key != reinterpret_cast(1); } key_data const & get_data() const { return m_data; } key_data & get_data() { return m_data; } - void set_data(key_data const & d) { m_data = d; } + void set_data(key_data && d) { m_data = std::move(d); } void set_hash(unsigned h) { SASSERT(h == m_data.hash()); } void mark_as_deleted() { m_data.m_key = reinterpret_cast(1); } void mark_as_free() { m_data.m_key = 0; } @@ -137,6 +141,10 @@ public: void insert(Key * const k, Value const & v) { m_table.insert(key_data(k, v)); } + + void insert(Key * const k, Value && v) { + m_table.insert(key_data(k, std::move(v))); + } key_data const & insert_if_not_there(Key * k, Value const & v) { return m_table.insert_if_not_there(key_data(k, v)); diff --git a/src/util/obj_ref.h b/src/util/obj_ref.h index 1aa562a8f..72762ea5b 100644 --- a/src/util/obj_ref.h +++ b/src/util/obj_ref.h @@ -53,6 +53,10 @@ public: inc_ref(); } + obj_ref(obj_ref && other) : m_obj(0), m_manager(other.m_manager) { + std::swap(m_obj, other.m_obj); + } + ~obj_ref() { dec_ref(); } TManager & get_manager() const { return m_manager; } diff --git a/src/util/rational.h b/src/util/rational.h index 803c562ad..392a1982b 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -41,6 +41,7 @@ public: rational() {} rational(rational const & r) { m().set(m_val, r.m_val); } + rational(rational && r) : m_val(std::move(r.m_val)) {} explicit rational(int n) { m().set(m_val, n); } diff --git a/src/util/ref_vector.h b/src/util/ref_vector.h index 469183b76..f340d8886 100644 --- a/src/util/ref_vector.h +++ b/src/util/ref_vector.h @@ -45,6 +45,10 @@ public: typedef T * data; ref_vector_core(Ref const & r = Ref()):Ref(r) {} + + ref_vector_core(ref_vector_core && other) : + Ref(std::move(other)), + m_nodes(std::move(other.m_nodes)) {} ~ref_vector_core() { dec_range_ref(m_nodes.begin(), m_nodes.end()); @@ -63,7 +67,7 @@ public: void resize(unsigned sz) { if (sz < m_nodes.size()) dec_range_ref(m_nodes.begin() + sz, m_nodes.end()); - m_nodes.resize(sz, 0); + m_nodes.resize(sz); } void resize(unsigned sz, T * d) { @@ -80,7 +84,7 @@ public: void reserve(unsigned sz) { if (sz <= m_nodes.size()) return; - m_nodes.resize(sz, 0); + m_nodes.resize(sz); } void shrink(unsigned sz) { @@ -207,6 +211,8 @@ public: this->append(other); } + ref_vector(ref_vector && other) : super(std::move(other)) {} + ref_vector(TManager & m, unsigned sz, T * const * data): super(ref_manager_wrapper(m)) { this->append(sz, data); diff --git a/src/util/scoped_numeral_vector.h b/src/util/scoped_numeral_vector.h index fdf63bf35..cb9a6b4fd 100644 --- a/src/util/scoped_numeral_vector.h +++ b/src/util/scoped_numeral_vector.h @@ -63,8 +63,7 @@ public: unsigned old_sz = this->size(); if (sz <= old_sz) shrink(sz); - typename Manager::numeral zero(0); - svector::resize(sz, zero); + svector::resize(sz, 0); } }; diff --git a/src/util/scoped_ptr_vector.h b/src/util/scoped_ptr_vector.h index a9ef92766..0bd0fd47e 100644 --- a/src/util/scoped_ptr_vector.h +++ b/src/util/scoped_ptr_vector.h @@ -42,7 +42,7 @@ public: bool empty() const { return m_vector.empty(); } void resize(unsigned sz) { if (sz < m_vector.size()) { - for (unsigned i = m_vector.size(); i < sz; i++) + for (unsigned i = m_vector.size(); i-- > sz; ) dealloc(m_vector[i]); m_vector.shrink(sz); } diff --git a/src/util/stopwatch.h b/src/util/stopwatch.h index 7f2ed3245..9ba707af0 100644 --- a/src/util/stopwatch.h +++ b/src/util/stopwatch.h @@ -185,4 +185,15 @@ public: #endif +struct scoped_watch { + stopwatch &m_sw; + scoped_watch (stopwatch &sw, bool reset=false): m_sw(sw) { + if (reset) m_sw.reset(); + m_sw.start(); + } + ~scoped_watch() { + m_sw.stop (); + } +}; + #endif diff --git a/src/util/util.h b/src/util/util.h index 9c2bbf7b9..931ea34b4 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -55,6 +55,7 @@ static_assert(sizeof(int64) == 8, "64 bits"); #ifdef _WINDOWS #define SSCANF sscanf_s #define SPRINTF sprintf_s +#define _Exit exit #else #define SSCANF sscanf #define SPRINTF sprintf @@ -333,7 +334,7 @@ bool compare_arrays(const T * array1, const T * array2, unsigned size) { template void force_ptr_array_size(T & v, unsigned sz) { if (sz > v.size()) { - v.resize(sz, 0); + v.resize(sz); } } diff --git a/src/util/vector.h b/src/util/vector.h index f068481db..d2beacfb4 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -26,6 +26,7 @@ Revision History: #include "util/debug.h" #include +#include #include #include "util/memory_manager.h" #include "util/hash.h" @@ -75,9 +76,27 @@ class vector { UNREACHABLE(); throw default_exception("Overflow encountered when expanding vector"); } - SZ *mem = (SZ*)memory::reallocate(reinterpret_cast(m_data)-2, new_capacity_T); - *mem = new_capacity; - m_data = reinterpret_cast(mem + 2); + SZ *mem, *old_mem = reinterpret_cast(m_data) - 2; +#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5 + if (__has_trivial_copy(T)) { +#else + if (std::is_trivially_copyable::value) { +#endif + mem = (SZ*)memory::reallocate(old_mem, new_capacity_T); + m_data = reinterpret_cast(mem + 2); + } else { + mem = (SZ*)memory::allocate(new_capacity_T); + auto old_data = m_data; + auto old_size = size(); + mem[1] = old_size; + m_data = reinterpret_cast(mem + 2); + for (unsigned i = 0; i < old_size; ++i) { + new (&m_data[i]) T(std::move(old_data[i])); + old_data[i].~T(); + } + memory::deallocate(old_mem); + } + *mem = new_capacity; } } @@ -149,6 +168,10 @@ public: SASSERT(size() == source.size()); } + vector(vector&& other) : m_data(0) { + std::swap(m_data, other.m_data); + } + vector(SZ s, T const * data): m_data(0) { for (SZ i = 0; i < s; i++) { @@ -180,6 +203,16 @@ public: return *this; } + vector & operator=(vector && source) { + if (this == &source) { + return *this; + } + destroy(); + m_data = 0; + std::swap(m_data, source.m_data); + return *this; + } + void reset() { if (m_data) { if (CallDestructors) { @@ -293,6 +326,11 @@ public: m_data[idx] = val; } + void set(SZ idx, T && val) { + SASSERT(idx < size()); + m_data[idx] = std::move(val); + } + T & back() { SASSERT(!empty()); return operator[](size() - 1); @@ -319,6 +357,14 @@ public: reinterpret_cast(m_data)[SIZE_IDX]++; } + void push_back(T && elem) { + if (m_data == 0 || reinterpret_cast(m_data)[SIZE_IDX] == reinterpret_cast(m_data)[CAPACITY_IDX]) { + expand_vector(); + } + new (m_data + reinterpret_cast(m_data)[SIZE_IDX]) T(std::move(elem)); + reinterpret_cast(m_data)[SIZE_IDX]++; + } + void insert(T const & elem) { push_back(elem); } @@ -358,7 +404,8 @@ public: } } - void resize(SZ s, T const & elem=T()) { + template + void resize(SZ s, Args args...) { SZ sz = size(); if (s <= sz) { shrink(s); return; } while (s > capacity()) { @@ -368,8 +415,23 @@ public: reinterpret_cast(m_data)[SIZE_IDX] = s; iterator it = m_data + sz; iterator end = m_data + s; - for(; it != end; ++it) { - new (it) T(elem); + for (; it != end; ++it) { + new (it) T(std::forward(args)); + } + } + + void resize(SZ s) { + SZ sz = size(); + if (s <= sz) { shrink(s); return; } + while (s > capacity()) { + expand_vector(); + } + SASSERT(m_data != 0); + reinterpret_cast(m_data)[SIZE_IDX] = s; + iterator it = m_data + sz; + iterator end = m_data + s; + for (; it != end; ++it) { + new (it) T(); } } @@ -440,10 +502,15 @@ public: return m_data[idx]; } - void reserve(SZ s, T const & d = T()) { + void reserve(SZ s, T const & d) { if (s > size()) resize(s, d); } + + void reserve(SZ s) { + if (s > size()) + resize(s); + } }; template @@ -453,7 +520,12 @@ public: ptr_vector(unsigned s):vector(s) {} ptr_vector(unsigned s, T * elem):vector(s, elem) {} ptr_vector(ptr_vector const & source):vector(source) {} + ptr_vector(ptr_vector && other) : vector(std::move(other)) {} ptr_vector(unsigned s, T * const * data):vector(s, const_cast(data)) {} + ptr_vector & operator=(ptr_vector const & source) { + vector::operator=(source); + return *this; + } }; template @@ -463,7 +535,12 @@ public: svector(SZ s):vector(s) {} svector(SZ s, T const & elem):vector(s, elem) {} svector(svector const & source):vector(source) {} + svector(svector && other) : vector(std::move(other)) {} svector(SZ s, T const * data):vector(s, data) {} + svector & operator=(svector const & source) { + vector::operator=(source); + return *this; + } }; typedef svector int_vector; @@ -500,23 +577,4 @@ struct vector_hash : public vector_hash_tpl > template struct svector_hash : public vector_hash_tpl > {}; -#include -// Specialize vector to be an instance of std::vector instead. -// This will catch any regression of issue #564 and #420. - -template <> -class vector : public std::vector { -public: - vector(vector const& other): std::vector(other) {} - vector(size_t sz, char const* s): std::vector(sz, s) {} - vector() {} - - void reset() { clear(); } - - -}; - - - #endif /* VECTOR_H_ */ -